:root {
  --bg: #f6f7f9;
  --surface: #ffffff;
  --border: #dfe3e8;
  --text: #1b1f24;
  --muted: #6b7480;
  --accent: #0b5fff;
  --ok: #157347;
  --warn: #a86a00;
  --err: #b02a37;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 15px/1.55 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}

.wrap { max-width: 1080px; margin: 0 auto; padding: 0 20px; }

.topbar {
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  margin-bottom: 28px;
}
.topbar .wrap { display: flex; align-items: center; gap: 16px; height: 56px; }
.brand { font-weight: 600; text-decoration: none; color: var(--text); }
.topbar nav { margin-left: auto; display: flex; gap: 18px; }
.topbar nav a { color: var(--muted); text-decoration: none; }
.topbar nav a:hover { color: var(--accent); }

.env-badge {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: .5px;
  background: #fff3cd;
  color: var(--warn);
  border: 1px solid #ffe69c;
  border-radius: 4px;
  padding: 2px 7px;
}

h1 { font-size: 22px; margin: 0 0 18px; }

.table {
  width: 100%;
  border-collapse: collapse;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  overflow: hidden;
}
.table th, .table td {
  text-align: left;
  padding: 10px 14px;
  border-bottom: 1px solid var(--border);
}
.table th { background: #fafbfc; font-weight: 600; font-size: 13px; color: var(--muted); }
.table tr:last-child td { border-bottom: none; }
.button-params { table-layout: fixed; }
.button-params th, .button-params td { overflow-wrap: anywhere; }

.empty {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 40px;
  text-align: center;
  color: var(--muted);
}

.btn {
  display: inline-block;
  background: var(--accent);
  color: #fff;
  text-decoration: none;
  padding: 8px 16px;
  border-radius: 6px;
  border: none;
  cursor: pointer;
  font-size: 14px;
}
.btn:hover { filter: brightness(.93); }

.btn-ghost {
  background: transparent;
  color: var(--accent);
  border: 1px solid var(--border);
}
.btn-ghost:hover { background: #eef3ff; filter: none; }

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 22px;
  margin-bottom: 20px;
}
.card h2 { font-size: 16px; margin: 0 0 12px; }
.card .row { display: flex; gap: 12px; align-items: center; }

/* Label-above-input forms: upload, login, configuración. Opt-in via
   `.form-stacked`, NOT a bare `form` selector. These rules assume a vertical
   stack, and every horizontal form in the app (the topbar logout, the in-table
   retry, the `.filters` row, the button pairs in `.card .row`) had to opt back
   out of them one by one before the class existed. Adding the class to a new
   stacked form is a deliberate act; inheriting it by accident was not. */
.form-stacked label {
  display: block;
  font-size: 13px;
  font-weight: 600;
  margin: 14px 0 5px;
}
.form-stacked label:first-child { margin-top: 0; }
.form-stacked input[type="text"],
.form-stacked input[type="password"],
.form-stacked input[type="file"] {
  width: 100%;
  max-width: 420px;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  font: inherit;
  background: #fff;
}
.form-stacked button { margin-top: 18px; }

.alert {
  border-radius: 6px;
  padding: 12px 16px;
  margin-bottom: 18px;
  border: 1px solid;
}
.alert-err  { background: #fdf2f3; border-color: #f3c4c9; color: var(--err); }
.alert-warn { background: #fff8e6; border-color: #ffe1a3; color: var(--warn); }

.stats { display: flex; gap: 14px; flex-wrap: wrap; margin-bottom: 22px; }
.stat {
  flex: 1 1 140px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-left: 4px solid var(--muted);
  border-radius: 8px;
  padding: 14px 18px;
  font-size: 13px;
  color: var(--muted);
}
.stat .n { display: block; font-size: 26px; font-weight: 600; color: var(--text); }
.stat.ok   { border-left-color: var(--ok); }
.stat.warn { border-left-color: var(--warn); }
.stat.err  { border-left-color: var(--err); }

.confirm { border-left: 4px solid var(--accent); }

.sample { max-width: 260px; margin-bottom: 14px; }

code {
  background: #f0f2f5;
  border-radius: 4px;
  padding: 1px 5px;
  font-size: 13px;
}

ul.muted { padding-left: 20px; }
ul.muted li { margin-bottom: 5px; }

.muted { color: var(--muted); }
footer.wrap { margin: 40px auto 24px; }

/* ---- Phase 2: campaign monitoring -------------------------------------- */

.table th.num, .table td.num { text-align: right; font-variant-numeric: tabular-nums; }

/* Status pills. The colour carries the same information as the label, so the
   label is never omitted — colour alone is not readable for everyone. */
.pill {
  display: inline-block;
  font-size: 12px;
  padding: 2px 9px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: #f2f4f7;
  color: var(--muted);
  white-space: nowrap;
}
.pill-delivered,
.pill-completed          { background: #e7f5ec; border-color: #b9e0c6; color: var(--ok); }
.pill-read               { background: #e6f0ff; border-color: #b9d2ff; color: var(--accent); }
.pill-failed,
.pill-cancelled,
.pill-completed_with_errors { background: #fdf2f3; border-color: #f3c4c9; color: var(--err); }
.pill-sending,
.pill-queued,
.pill-accepted,
.pill-sent               { background: #fff8e6; border-color: #ffe1a3; color: var(--warn); }
/* Deliberately warn, not err: a scheduled retry has not failed definitively,
   and colouring it red would have the operator chasing rows the system is
   already going to handle by itself tomorrow morning. */
.pill-pending_retry      { background: #fff8e6; border-color: #ffe1a3; color: var(--warn); }

/* The §9 attempt history, tucked under the recipient it belongs to. */
.subrow > td             { border-top: 0; padding-top: 0; }
.attempt                 { white-space: nowrap; }

.filters { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin: 0 0 16px; }

/* Label and field side by side, the opposite of `.form-stacked`. */
.filters label {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

/* Date pickers get the same skin as the stacked text inputs, minus the
   100%/420px width, which would break the row. */
.filters input[type="date"] {
  padding: 7px 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  font: inherit;
  background: #fff;
}

.chip {
  font-size: 13px;
  padding: 4px 12px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--muted);
  text-decoration: none;
}
.chip:hover { border-color: var(--accent); color: var(--accent); }
.chip-on { background: var(--accent); border-color: var(--accent); color: #fff; }

.pager { display: flex; align-items: center; gap: 14px; margin: 16px 0 0; }

.card h2 { font-size: 16px; margin: 0 0 12px; }
.card { margin-bottom: 22px; }

/* Header image preview. Capped so a 4000px banner does not dominate the page,
   but large enough to actually check it is the right artwork. */
.header-preview {
  max-width: 420px;
  max-height: 240px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: #fff;
}

.alert-ok { background: #e7f5ec; border-color: #b9e0c6; color: var(--ok); }
.alert ul { margin: 8px 0 4px; padding-left: 20px; }

/* Same shape as .env-badge, but red: an unauthenticated webhook should read as
   a warning, not as an environment label. */
.env-badge.danger {
  background: #fdf2f3;
  border-color: #f3c4c9;
  color: var(--err);
}

/* Per-row retry button: a one-button form that has to sit inside a cell rather
   than start its own block. */
.table form { display: inline; margin: 0; }
.table td.actions { text-align: right; white-space: nowrap; }

.btn-sm { padding: 3px 10px; font-size: 12px; }

/* Retrying a hopeless error code is allowed but discouraged: the operator may
   know the number was registered yesterday. De-emphasised, never disabled, and
   the reason lives in the title attribute rather than only in the colour. */
.btn-muted {
  background: transparent;
  color: var(--muted);
  border: 1px solid var(--border);
}
.btn-muted:hover { background: #f2f4f7; color: var(--text); filter: none; }

/* --- Auth ---------------------------------------------------------------- */
/* Sits after nav, which carries margin-left:auto, so the chip lands hard right. */
.user-chip {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 13px;
  color: var(--muted);
}
.user-chip form { margin: 0; }

.card-narrow { max-width: 460px; margin-left: auto; margin-right: auto; }
.card-narrow h1 { font-size: 20px; margin: 0 0 12px; }

/* --- WYSIWYG (static/js/wysiwyg.js) -------------------------------------- */
/* Progressive enhancement over a <textarea>. The textarea stays the form
   control and is only hidden, so with no JavaScript the field is still a
   perfectly usable box showing *negrita* — a format WhatsApp users read
   fluently. Nothing here is load-bearing for correctness. */
.wysiwyg { margin-bottom: 6px; }
.wysiwyg-toolbar {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 8px;
  border: 1px solid var(--border);
  border-bottom: none;
  border-radius: 8px 8px 0 0;
  background: #fafbfc;
}
.wysiwyg-hint { margin-left: auto; font-size: 12px; }
.wysiwyg-surface {
  min-height: 170px;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: 0 0 8px 8px;
  background: var(--surface);
  font-size: 14px;
  line-height: 1.5;
  white-space: pre-wrap;   /* newlines are meaningful in a WhatsApp message */
  overflow-wrap: anywhere;
}
.wysiwyg-surface:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px #e6f0ff;
}
.wysiwyg-count { margin: 4px 0 0; font-size: 12px; }
.wysiwyg-over { color: var(--err); font-weight: 600; }

/* The message as WhatsApp will show it. Monospace deliberately: this is the
   stored markup, and seeing the literal asterisks is the point. */
.wa-preview {
  margin: 0;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: #f0f7f1;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 13px;
  line-height: 1.5;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* A checkbox and its label on one line. Forms elsewhere in this app are all
   label-above-input, which reads wrong for a boolean. */
.check {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 400;
  margin-top: 14px;
}
.check input { width: auto; margin: 0; }

/* ---- §8: campaign progress -------------------------------------------- */

/* Nine tiles, not six. `.stats` is `flex: 1 1 140px`, which at 1080px wraps
   nine into a ragged 6+3 with the last row stretched to full width. This block
   gets its own class rather than changing `.stats` globally, because the same
   page also carries a two-tile cost block and a two-tile auto-reply block that
   the flex layout suits perfectly well. */
.stats-progress {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 14px;
}
.stats-progress .stat { flex: none; }

/* A subtotal is not a state. "Fallidos" is the two failure tiles added
   together, so it is set apart — dashed, not solid — to stop anyone adding it
   into the total when checking the numbers. */
.stat-subtotal { border-left-style: dashed; }
.stat-subtotal small { display: block; font-size: 11px; text-transform: uppercase; letter-spacing: .4px; }

/* The tile whose label needed a parenthetical: "Entregados (sin leer)". The
   dotted underline is the standard "there is a tooltip here" affordance, and
   the explanation is in the title attribute, never in the colour alone. */
.stat-note { cursor: help; }

/* The stacked bar. Segments are laid out in lifecycle order and share the pill
   colours, so a segment, its tile and a row's status pill are the same colour
   for the same thing. */
.progress {
  display: flex;
  height: 10px;
  border-radius: 999px;
  overflow: hidden;
  background: #eceff3;
  border: 1px solid var(--border);
  margin: 0 0 8px;
}
.progress-seg {
  /* Transitioned so a poll tick reads as movement rather than a jump. Kept
     short: this is feedback, not an animation. */
  transition: width .4s ease;
  min-width: 0;
}
.seg-pendientes           { background: #d7dbe0; }
.seg-enviados             { background: #ffd88a; }
.seg-entregados           { background: #7cc79a; }
.seg-leidos               { background: var(--accent); }
.seg-pendientes_reintento { background: var(--warn); }
.seg-fallidos_definitivos { background: var(--err); }
.seg-omitidos             { background: #b9bfc7; }

.progress-caption { margin: 0 0 18px; font-size: 13px; }
/* Deliberately understated: a permanent "· actualizando" is a status line, not
   a notification, and it must not compete with the numbers it describes. */
.progress-live { opacity: .7; font-size: 12px; }

/* Prefers-reduced-motion: the width still changes, it just stops sliding. */
@media (prefers-reduced-motion: reduce) {
  .progress-seg { transition: none; }
}
