/* ============================================================
 * Context curation
 *
 * Everything added for the bulk review of proposed context: the
 * per-category toolbar, the selection count in the sticky footer,
 * the wording disclosure on a candidate row, and the single form
 * the open questions answer through.
 *
 * Composes 01_tokens.css and nothing else. No colour, radius,
 * spacing or type value is declared here - there is not a single
 * literal in this file, which is what keeps both themes working
 * without a second palette to maintain.
 *
 * Also declares no width or height on a checkbox, deliberately:
 * 03_components.css sets every checkbox to 1.5rem for WCAG 2.5.8,
 * and the select-all is a checkbox like any other.
 * ============================================================ */

/* ── Per-category toolbar ────────────────────────────────────
 * The select-all of a category, inside the category. The two
 * category sweeps that used to sit here are gone - they were one
 * of four competing levels of approve/reject on this page.
 *
 * This file loads after 05_features.css, which owns .proposal-*,
 * so the overrides below win by cascade rather than by weight. */

/* The Turbo Stream target, and nothing else: it must not draw a box
 * of its own, because one of its three states renders no toolbar at
 * all and an empty bordered strip is the defect this file is here to
 * remove. Same reason #proposal-review-actions is display: contents. */
.proposal-category-actions {
  display: contents;
}

.proposal-category-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding-block: var(--space-3);
  border-block-end: 1px solid var(--border-default);
}

/* The stack zeroes the first row's top padding because nothing is
 * normally above it. With the toolbar there, something is, and the
 * row needs its breathing room back.
 *
 * Through :has, because the toolbar sits inside the display: contents
 * wrapper above and is no longer the stack's own previous sibling -
 * and because the questions panel renders that wrapper empty, so the
 * gap has to depend on a toolbar actually being in it. */
.proposal-category-actions:has(.proposal-category-toolbar) + .proposal-item-stack > *:first-child .proposal-item-row {
  padding-block-start: var(--space-6);
}

.proposal-select-all {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: var(--control-h-sm);
  font-size: var(--text-body-sm);
  cursor: pointer;
}

/* What a finished category says instead of controls. Left where
 * the toolbar was, so arriving on a tab whose count reads 0/11
 * still lands on a sentence rather than on a missing row. */
.proposal-category-settled {
  font-size: var(--text-body-sm);
}

/* ── Telling a sweep apart from its twin ─────────────────────
 * "Aprobar todos" and "Rechazar todos" were the same ghost, the
 * same size and the same border, 118px apart, and one of them
 * builds the project's context while the other throws it away.
 * Reading the label was the only way to tell which was which.
 *
 * The destructive one takes --error-border at rest, which is the
 * hue 03_components.css already gives .button--danger and which
 * the review's own rejected rows carry on their start border. It
 * is an edge and not a fill: the sweep must not out-shout the
 * approve beside it, it must stop being its identical twin.
 *
 * Only the two sweeps. The per-row "Rechazar" keeps the neutral
 * edge 05_features.css gives it - sixty-two red buttons down a
 * page is not emphasis, it is the background.
 *
 * 0-4-0 and 0-3-0: 05_features.css sets the neutral edge at
 * 0-3-0 through `.proposal-bulk-actions .button.button--subtle`,
 * and a rule that only ties would lose nothing here (this file
 * loads later) but would break the moment either file moves. */
.proposal-sweep-reject.button.button--subtle,
.proposal-bulk-actions .proposal-sweep-reject.button.button--subtle {
  border-color: var(--error-border);
}

.proposal-sweep-reject.button.button--subtle:hover,
.proposal-bulk-actions .proposal-sweep-reject.button.button--subtle:hover {
  border-color: var(--error-border);
  background: var(--error-bg);
}

/* Disabled is disabled, whichever button it is. Measured side by
 * side in the footer, the two selection actions carried two
 * different disabled edges - rgb(31,31,31) on the primary,
 * rgb(134,134,137) on the ghost - because the page-scoped rule
 * that gives the ghosts their edge outweighs `button:disabled`
 * and kept applying after the button went dead. Two treatments
 * of one state, on two buttons that are always disabled
 * together. */
.proposal-bulk-actions .button:disabled,
.proposal-bulk-actions .button.button--subtle:disabled,
.proposal-bulk-actions .proposal-sweep-reject.button.button--subtle:disabled {
  border-color: var(--border-default);
  background: var(--bg-subtle);
  color: var(--text-secondary);
}

/* ── Wording disclosure ──────────────────────────────────────
 * A candidate row used to print its own words three times: the
 * content, the evidence, and a prefilled textarea holding the
 * content again. Most rows are approved as written, so the third
 * copy is opened by whoever actually wants to change it. */

.proposal-wording-editor > summary {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  width: fit-content;
  min-height: var(--control-h-sm);
  color: var(--link-color);
  font-size: var(--text-body-sm);
  cursor: pointer;
}

/* Open, the summary is no longer the offer - it is the way back. */
.proposal-wording-editor[open] > summary {
  color: var(--text-secondary);
}

.proposal-wording-editor > .field-group {
  padding-block-start: var(--space-2);
}

/* ── Open questions ──────────────────────────────────────────
 * One form for every answer on the panel, one submit. The row's
 * only remaining action of its own is discarding the question,
 * which is a button and not a judgement of wording. */

.proposal-answer-field {
  margin: 0;
}

/* The review column is a grid, so a lone button would stretch across it. */
.proposal-discard-question {
  justify-self: start;
}

.proposal-answers-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-4);
  padding-block-start: var(--space-6);
  margin-block-start: var(--space-6);
  border-block-start: 1px solid var(--border-default);
}

.proposal-answers-note {
  max-width: 52ch;
  font-size: var(--text-body-sm);
}

/* ── The sticky review bar ───────────────────────────────────
 * Seven elements, measured live, of which three could not be
 * reached from any state of that page: a status label reading
 * "Nada seleccionado", two permanently disabled selection
 * actions, two sweeps whose label named no scope, and a link out
 * of the review styled as a sixth button.
 *
 * What is left is things of different kinds - the way out, the
 * progress, the two actions that move the review ("Guardar las
 * respuestas (N)" and the single "Confirmar todo") and, with the
 * audit open, the selection actions - so the exit sits at one end
 * and the actions at the other, instead of peers queueing at one
 * edge. 05_features.css
 * pushes the exit right with an auto margin, which was how it was
 * set apart from the four buttons it used to share a line with;
 * it is the first child now, and the separation is the bar's own. */
.proposal-bulk-actions--footer {
  justify-content: space-between;
}

/* A link and no longer a button: leaving a review commits
 * nothing, and it was the only thing in a row of submits that
 * did not. Sized to the controls beside it so the bar keeps one
 * baseline, and given the tap target a link in a 36px row still
 * owes a thumb. */
.proposal-bulk-actions__leave {
  display: inline-flex;
  align-items: center;
  min-height: var(--control-h-sm);
  margin-inline-start: 0;
  color: var(--link-color);
  font-size: var(--text-body-sm);
}

.proposal-bulk-actions__mutations,
.proposal-bulk-actions__primary {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
}

/* Lo que la barra ofrece siempre: "Guardar las respuestas (N)" cuando hay
 * algo escrito y el único "Confirmar todo". Empujado al final, después del
 * progreso; la selección, cuando la auditoría está abierta, va a su lado. */
.proposal-bulk-actions__primary {
  margin-inline-start: auto;
}

.proposal-bulk-actions__primary .proposal-confirm-all-blocked {
  max-width: 32ch;
  font-size: var(--text-body-sm);
}

/* "12 de 47 revisados": ver cuánto falta sin subir hasta el encabezado. */
.proposal-bulk-actions__progress {
  margin: 0;
  color: var(--text-secondary);
  font-size: var(--text-body-sm);
  font-variant-numeric: tabular-nums;
}


/* ── Markdown inside a chat message ──────────────────────────
 * The model's own heading levels are pushed two steps down at
 * render (see ChatMessagesHelper::MODEL_HEADING_OFFSET), so what
 * used to arrive as h1..h3 now arrives as h3..h6.
 * 05_features.css styles h1, h2 and h3 under this class and
 * stops there, which left every heading past the model's first
 * two levels falling back to the browser's defaults - and the
 * default for h5 and h6 is SMALLER than the body text they
 * introduce.
 *
 * One step under the h3 above them rather than a ramp of four:
 * these are sections inside one message, not a document, and
 * four sizes inside a chat bubble is a hierarchy nobody asked
 * for. Weight is what separates them from the paragraphs.
 *
 * Here and not in 05_features.css because that file belongs to
 * another track for the length of this ticket. It is three
 * rules and a handoff, not a second home for message styling. */
.message-card__markdown h4,
.message-card__markdown h5,
.message-card__markdown h6 {
  font-size: var(--text-body);
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-normal);
  line-height: var(--leading-snug);
}

@media (max-width: 1024px) {
  /* Stacked, space-between has nothing to distribute and the two
   * controls would sit apart on separate lines for no reason. */
  .proposal-category-toolbar {
    justify-content: flex-start;
  }

  /* 05_features.css laid the old six-control bar out as a
   * two-column grid, which is what stopped the six from coming
   * out as three rows of two with six different left edges.
   * There are two children now and they are not peers, so the
   * grid has nothing left to equalise: flex puts the exit and
   * the mutation group on their own rows at the gutter, and the
   * two mutations share the row below. */
  .proposal-bulk-actions--footer {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
  }

  .proposal-bulk-actions__mutations,
  .proposal-bulk-actions__primary {
    flex: 1 1 100%;
    justify-content: flex-start;
  }

  /* Stacked, the groups are already on separate rows and the
   * inline margin would only push one of them off the gutter the
   * others share. */
  .proposal-bulk-actions__primary {
    margin-inline-start: 0;
  }
}

/* Verified with live captures at 390x400/600/700: the width breakpoint
 * above stacks the exit, the mutations row and the sweeps row - three
 * rows, fixed regardless of viewport height. On a short viewport (a
 * laptop window that is not maximised, a phone in landscape, 150-200%
 * browser zoom) those three rows plus the footer's own padding and row
 * gaps take up 40-50% of what is visible, and because the footer is
 * sticky it permanently sits over the "Respuesta" textarea and
 * "Descartar la pregunta" of any question in the lower half of the
 * screen, at any scroll position.
 *
 * Tightening the footer's own vertical rhythm is the fix, not hiding a
 * row and not removing a control: every button and the exit link stay
 * in the DOM, keep their label, and keep their full width - only the
 * padding, the gaps between rows, and (see below) the control height
 * give up pixels. */
@media (max-width: 1024px) and (max-height: 700px) {
  .proposal-bulk-actions--footer {
    gap: var(--space-1);
    padding-block: var(--space-1);
  }

  .proposal-bulk-actions__mutations,
  .proposal-bulk-actions__primary {
    gap: var(--space-1);
  }

  /* --control-h-sm (36px) is design.md's own floor, and three rows of it
   * plus the padding/gaps above is most of what a 390x400 viewport has
   * to give - tightening only the spacing (the change above) is not
   * enough to stop the footer covering the textarea beneath it. 30px
   * stays 6px clear of --target-min (24px, the WCAG 2.5.8 minimum the
   * token above answers to in 01_tokens.css), so the target shrinks
   * without dropping below the standard it exists to satisfy - a
   * permanently unreachable control fails a harder requirement than a
   * smaller one does. Scoped to this one short-viewport breakpoint;
   * --control-h-sm itself, and every other consumer of it, is untouched. */
  .proposal-bulk-actions--footer .button,
  .proposal-bulk-actions__leave {
    min-height: 30px;
  }
}

/* ============================================================
   La pestaña de contexto del proyecto
   ============================================================ */

.context-group {
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sm);
  background: var(--bg-surface);
  margin-bottom: var(--space-4);
}

.context-group__header {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-4);
  border-bottom: 1px solid var(--border-default);
}

.context-group__header h3 {
  margin: 0;
  font-size: var(--text-h4);
  font-weight: var(--weight-book);
}

.context-group__header .badge {
  margin-left: auto;
}

.context-group__hint {
  margin: var(--space-3) var(--space-4) 0;
  font-size: var(--text-body-sm);
  color: var(--text-secondary);
}

.context-group__footer {
  padding: var(--space-3) var(--space-4);
  border-top: 1px solid var(--border-default);
}

.context-entry-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

/* minmax(0, 1fr) y no 1fr: la columna del texto tiene que poder encogerse
   por debajo de su contenido para que el markdown largo envuelva en lugar
   de empujar los controles fuera de la fila. */
.context-entry {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--space-3);
  align-items: start;
  padding: var(--space-4);
  border-top: 1px solid var(--border-default);
}

.context-entry-list > turbo-frame:first-child .context-entry {
  border-top: 0;
}

.context-entry__body {
  min-width: 0;
}

.context-entry__question {
  margin: 0 0 var(--space-1);
  font-size: var(--text-body-sm);
  color: var(--text-secondary);
}

.context-entry__text > :first-child { margin-top: 0; }
.context-entry__text > :last-child { margin-bottom: 0; }

.context-entry__origin {
  margin: var(--space-2) 0 0;
  font-size: var(--text-caption);
  letter-spacing: var(--tracking-caption);
  color: var(--text-secondary);
}

/* What kind of thing an item or entry is, in the singular: on the review row,
   on the read-back line and here beside the origin. A quiet outline and not a
   hue - the five categories carry no state, and colour on them would compete
   with the approved/rejected badges that do. */
.category-chip {
  display: inline-flex;
  align-items: center;
  min-height: 20px;
  padding: 0 var(--space-2);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sm);
  font-size: var(--text-caption);
  font-weight: var(--weight-medium);
  line-height: var(--leading-snug);
  letter-spacing: var(--tracking-caption);
  color: var(--text-secondary);
  white-space: nowrap;
}

.context-entry__origin .category-chip { margin-right: var(--space-2); }

/* Los dos controles están en el DOM en reposo, no aparecen al hover: el
   sistema pide reservar la geometría del estado en el elemento en reposo
   para que activarlo no mueva nada, y unos íconos que aparecen al pasar el
   mouse hacen saltar la fila entera. Lo que cambia con el hover es el
   contraste, no la existencia.

   .icon-button ya declara su propia transición (background-color, color) y
   un borde transparente reservado; acá se redeclara con border-color
   incluido porque la variante --danger cambia ese borde al pasar el mouse,
   y sin nombrarla en la transición saltaría en lugar de desvanecer. */
.context-entry__actions {
  display: flex;
  gap: var(--space-1);
  flex: none;
}

.context-entry__action {
  transition: color var(--motion-base) var(--ease-standard),
    border-color var(--motion-base) var(--ease-standard),
    background-color var(--motion-base) var(--ease-standard);
}

.context-entry__action--danger:hover {
  color: var(--error-border);
  border-color: var(--error-border);
}

/* button_to genera un form; sin esto cada botón de borrar abriría su propia
   línea al lado del lápiz. */
.context-entry__actions form {
  margin: 0;
  display: contents;
}

/* Sin transición de altura: animar una propiedad de layout en un contenedor
   es lo que el sistema prohíbe, y acá haría saltar toda la lista de abajo. */
.context-entry--editing {
  background: var(--bg-inset);
  grid-template-columns: minmax(0, 1fr);
}

.context-entry-form {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  min-width: 0;
}

.context-entry-form__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.field-group--invalid .field-input {
  border-color: var(--error-border);
}

/* ============================================================
   La revisión: la tarea primero, la lectura después
   ============================================================ */

/* El borde de advertencia y no un fondo saturado: este bloque es lo único de
   la pantalla que pide algo, y el estado se lleva dos veces - el borde más el
   encabezado - nunca solo con un color de fondo. */
.proposal-answer-task {
  border: 1px solid var(--warning-border);
  border-radius: var(--radius-sm);
  background: var(--bg-surface);
  margin-bottom: var(--space-6);
}

.proposal-answer-task__header {
  padding: var(--space-4);
  border-bottom: 1px solid var(--border-default);
  background: var(--warning-bg);
}

.proposal-answer-task__header h2 {
  margin: 0 0 var(--space-1);
  font-size: var(--text-h4);
  font-weight: var(--weight-book);
}

.proposal-answer-task__header p { margin: 0; }

/* "Guardaste N respuestas. Quedan M preguntas sin responder.": el aviso de un
 * guardado parcial, arriba de las preguntas y no en rojo sobre cada una. */
.proposal-answer-task__header .proposal-answer-task__status {
  margin-top: var(--space-3);
  font-size: var(--text-body-sm);
}

.proposal-answer-task__body {
  padding: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}

/* Cada pregunta es una tarea con su propio campo, no un párrafo suelto: en
 * `display: block` el gap no aplica, así que el textarea y "Descartar la
 * pregunta" quedaban pegados, borde contra borde. */
.proposal-answer {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  align-items: flex-start;
}

/* Una raya entre preguntas. Veinte píxeles de aire entre dos bloques que
 * tienen la misma forma se leen como un bloque solo con espacio adentro, y
 * acá cada uno es una cosa distinta que contestar. */
.proposal-answer + .proposal-answer {
  border-top: 1px solid var(--border-subtle, var(--border-default));
  padding-top: var(--space-5);
}

/* Estas preguntas son prosa de verdad - la que abre esta pantalla mide 239
 * caracteres - y sin tope corrían a 1062px, unos 120 caracteres por línea:
 * el doble de la medida de lectura del sistema. */
.proposal-answer__question {
  margin: 0;
  max-width: var(--measure);
}

/* El campo acompaña a la pregunta en vez de cruzar la pantalla entera. Un
 * textarea de 1900px para una respuesta de una línea invita a escribir algo
 * que nadie va a escribir, y rompe la columna que forma con el texto. */
.proposal-answer .field-group {
  width: 100%;
  max-width: var(--measure);
}

/* La pista bajo el campo: neutra al cargar, y la caja de error solo después
 * de un intento (ver _answer_hint). El margen negativo la acerca al campo que
 * describe en vez de dejarla a la distancia de un bloque nuevo. */
.proposal-answer__hint {
  margin: calc(-1 * var(--space-1)) 0 0;
  font-size: var(--text-body-sm);
}

.proposal-answer-task__footer {
  padding: var(--space-4);
  border-top: 1px solid var(--border-default);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  align-items: center;
}

.proposal-answer-task__footer p { margin: 0; }

/* El salto a la lectura por encima de las preguntas: un enlace, con el alto
 * de un control para el pulgar. */
.proposal-jump {
  margin: 0 0 var(--space-2);
  font-size: var(--text-body-sm);
}

.proposal-jump a {
  display: inline-flex;
  align-items: center;
  min-height: var(--control-h-md);
}

.proposal-read-back {
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sm);
  background: var(--bg-surface);
  margin-bottom: var(--space-6);
}

.proposal-read-back__header {
  padding: var(--space-4);
  border-bottom: 1px solid var(--border-default);
}

.proposal-read-back__header h2 {
  margin: 0 0 var(--space-1);
  font-size: var(--text-h4);
  font-weight: var(--weight-book);
}

.proposal-read-back__header p { margin: 0; }

/* La alternativa con la que abre la lectura: "Confirmar todo" o "Revisar uno
 * por uno". Arriba, porque antes la única forma de rechazar un elemento suelto
 * estaba al final de unos 7000px. */
.proposal-read-back__choice {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-3);
  margin-top: var(--space-4);
}

/* Un grupo por categoría, con encabezado y contador. El encabezado es un h3
 * de verdad (rotor, lista de encabezados) aunque visualmente sea una franja
 * chica: lo que separa es la raya y el tono, no el tamaño. */
.proposal-read-back__group + .proposal-read-back__group {
  border-top: 1px solid var(--border-default);
}

.proposal-read-back__group-heading {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--border-default);
  background: var(--bg-subtle);
  font-size: var(--text-body-sm);
  font-weight: var(--weight-strong);
}

.proposal-read-back__group-count {
  font-weight: var(--weight-book);
  font-variant-numeric: tabular-nums;
}

.proposal-read-back__list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.proposal-read-back__row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--space-3);
  align-items: start;
  padding: var(--space-4);
  border-top: 1px solid var(--border-default);
}

.proposal-read-back__list > li:first-child { border-top: 0; }

/* The row's frame (dom_id(item, :read_back)) generates no box, so the text
   and the Corregir button stay the two grid cells of the row they always
   were. The editor that replaces them spans both columns. */
.proposal-read-back__frame { display: contents; }

.proposal-read-back__text { min-width: 0; }
.proposal-read-back__text p { margin: 0; }

.proposal-read-back__category { margin: 0 0 var(--space-1) !important; }

.proposal-read-back__editor {
  grid-column: 1 / -1;
  min-width: 0;
}

/* Aprobar, Rechazar y Corregir en la línea. El formulario de las dos
 * decisiones no genera caja, así que los tres botones son hermanos de una
 * misma fila flexible. */
.proposal-read-back__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-2);
}

.proposal-read-back__decide { display: contents; }

/* Aprobar con el borde fuerte que 05_features.css ya le da a Rechazar: sin
 * esto el secundario quedaba más tenue que el sutil de al lado, y la
 * decisión que construye el contexto parecía la menor de las dos. */
.proposal-read-back__approve.button.button--secondary {
  border-color: var(--border-strong);
  font-weight: var(--weight-medium);
}

/* Los controles que agregó este rediseño llevan 40px de alto y no los 36 del
 * tamaño compacto: son las acciones más repetidas de la pantalla - una por
 * línea - y las que se tocan con el pulgar en un teléfono. */
.proposal-review-page :is(.proposal-read-back__actions, .proposal-read-back__choice, .proposal-bulk-actions__primary, .proposal-dialog__actions, .proposal-reject-all) .button.button--compact {
  min-height: var(--control-h-md);
}

/* A 390px el texto necesita el ancho entero: las acciones bajan debajo de
 * la frase y se alinean con ella. */
@media (max-width: 640px) {
  .proposal-read-back__row { grid-template-columns: minmax(0, 1fr); }
  .proposal-read-back__actions { justify-content: flex-start; }
}

/* Confirmar todo does not carry what is typed in an open Corregir, so while
   one is open every trigger of it - the one over the read-back and the one in
   the sticky bar - gives way to a note saying what to do first. */
.proposal-confirm-all-blocked { display: none; margin: 0; }

.proposal-review-page:has(.proposal-read-back__editor) .proposal-confirm-all { display: none; }
.proposal-review-page:has(.proposal-read-back__editor) .proposal-confirm-all-blocked { display: block; }

.proposal-audit {
  border-top: 1px solid var(--border-default);
  margin-bottom: var(--space-6);
}

.proposal-audit > summary {
  padding: var(--space-3) 0;
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--text-secondary);
  font-size: var(--text-body-sm);
}

.proposal-audit > summary::-webkit-details-marker { display: none; }
.proposal-audit > summary:hover { color: var(--text-primary); }

/* .proposal-audit no es .disclosure - trae su propio marcado, así que el
   giro del chevron de 03_components.css (que apunta a .disclosure[open])
   no lo alcanza. Mismo comportamiento, repetido bajo el selector propio. */
.proposal-audit > summary .icon {
  transition: transform var(--motion-base) var(--ease-standard);
}

.proposal-audit[open] > summary .icon--chevron {
  transform: rotate(90deg);
}

.proposal-audit__body { padding-top: var(--space-4); }

/* The footer's selection actions and sweeps act on the audit's checkboxes, so
   they follow the <details> the reader actually has open - not a server guess
   about it. The server always renders them; a folded audit hides them here.
   A read-back editor with unsaved text hides them too: approve_all carries
   nothing typed there, so sweeping then approved the old sentence and dropped
   the correction without a word. The same goes for the audit's own decisions:
   a category sweep redraws the whole read-back, and a row's Aprobar/Rechazar
   on the item being corrected takes its line away - both with the typed text
   in it. They come back the moment the correction is saved or cancelled. */
.proposal-review-page:has(> .proposal-audit:not([open])) .proposal-bulk-actions__mutations,
.proposal-review-page:has(.proposal-read-back__editor) :is(.proposal-bulk-actions__mutations, .proposal-item-row .proposal-action-row) {
  display: none;
}

/* La barra fija tapaba el elemento que recibía el foco después de aprobar:
 * el siguiente "Rechazar" quedaba debajo y el clic caía en la barra.
 * proposal-review publica el alto real de la barra (cambia con el ancho y con
 * lo que ofrece), y el navegador lo respeta al llevar el foco a la vista. */
html:has(.proposal-review-page) {
  scroll-padding-bottom: calc(var(--proposal-bar-height, 0px) + var(--space-4));
}

/* "Rechazar toda la revisión", fuera de la barra: una salida para una
 * interpretación que no sirve, no un paso del flujo. Al final de la página,
 * con su explicación y su propia confirmación. */
.proposal-reject-all {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3) var(--space-5);
  margin-bottom: var(--space-6);
  padding-top: var(--space-4);
  border-top: 1px solid var(--border-default);
}

.proposal-reject-all__heading {
  margin: 0 0 var(--space-1);
  font-size: var(--text-body);
  font-weight: var(--weight-strong);
}

.proposal-reject-all p { margin: 0; max-width: 60ch; }

/* Las confirmaciones de la página, en el lugar del confirm() del navegador.
 * Los tokens del modal del sistema (.app-modal en 03_components.css): la pareja
 * de superposición, la sombra de diálogo y el scrim plano. */
.proposal-dialog {
  width: min(calc(100vw - 2 * var(--space-4)), var(--alert-dialog-w));
  max-height: min(90vh, 40rem);
  padding: var(--space-6);
  border: 1px solid var(--border-overlay);
  border-radius: var(--radius-sm);
  background: var(--bg-overlay);
  color: var(--text-primary);
  box-shadow: var(--shadow-dialog);
}

.proposal-dialog::backdrop { background: var(--scrim); }

.proposal-dialog h2 {
  margin: 0 0 var(--space-3);
  font-size: var(--text-h4);
  font-weight: var(--weight-book);
}

.proposal-dialog__body {
  display: grid;
  gap: var(--space-2);
}

.proposal-dialog__body p { margin: 0; }

/* La advertencia lleva borde y fondo de aviso, no solo color. */
.proposal-dialog__warning {
  padding: var(--space-3);
  border: 1px solid var(--warning-border);
  border-radius: var(--radius-sm);
  background: var(--warning-bg);
}

.proposal-dialog__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: var(--space-2);
  margin-top: var(--space-6);
}

/* El cierre: 05_features.css le da el tinte de éxito. Acá, el encabezado que
 * ahora nombra el estado ("Contexto listo") y el anillo cuando recibe el foco
 * al terminar la última decisión - lo recibe por script, y sin esto un foco
 * programático no se ve en todos los navegadores. */
.proposal-closing__heading {
  margin: 0;
  font-size: var(--text-h4);
  font-weight: var(--weight-book);
}

/* El paso siguiente mientras quedan preguntas: el mismo bloque, con margen
 * antes de la sección secundaria que lo sigue. */
.proposal-closing--partial {
  margin-bottom: var(--space-6);
}

.proposal-closing:focus-visible,
.proposal-closing:focus {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

/* ============================================================
   Qué falta para estimar
   ============================================================ */

.readiness-ladder {
  list-style: none;
  margin: 0 0 var(--space-6);
  padding: 0;
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sm);
  background: var(--bg-inset);
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
}

.readiness-step {
  padding: var(--space-4);
  border-right: 1px solid var(--border-default);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  min-width: 0;
}

.readiness-step:last-child { border-right: 0; }

.readiness-step__top {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.readiness-step__mark {
  width: 18px;
  height: 18px;
  border-radius: var(--radius-full);
  flex: none;
  display: grid;
  place-items: center;
  font-size: var(--text-caption);
  line-height: 1;
  border: 1px solid var(--border-default);
  color: var(--text-secondary);
}

/* Sin text-transform: uppercase. "Responde lo pendiente" y "Confirma lo
   interpretado" pasan las ~20 letras que la regla de mayúsculas del sistema
   pone como techo - de ahí para arriba las mayúsculas cuestan las formas de
   palabra que un lector escanea, así que este rótulo toma el tamaño y el
   tracking del eyebrow sin el caso. */
.readiness-step__label {
  font-size: var(--text-eyebrow);
  letter-spacing: var(--tracking-eyebrow);
  color: var(--text-secondary);
}

.readiness-step__value { margin: 0; }
.readiness-step__sub,
.readiness-step__hint {
  margin: 0;
  font-size: var(--text-body-sm);
  color: var(--text-secondary);
}

/* Un semáforo y no una escalera: cada indicador pinta su propio estado, y
 * ninguno se apaga porque otro esté antes. Antes había --done, --current y
 * --waiting (con opacidad), que es lo que hacía leer los cuatro como pasos.
 * La marca lleva el estado con forma - el ícono, o el borde punteado del
 * vacío - y con color, nunca solo con color. */
.readiness-step--ok .readiness-step__mark {
  background: var(--success-bg);
  border-color: var(--success-border);
  color: var(--success-border);
}

.readiness-step--attention .readiness-step__mark {
  background: var(--warning-bg);
  border-color: var(--warning-border);
  color: var(--text-primary);
}

/* --border-strong y no el borde por defecto: la marca vacía no lleva ícono,
 * así que el borde punteado ES la forma, y con --border-default (1.18:1 en
 * claro) casi no se veía. --border-strong da 4.83:1 en claro y 5.45:1 en
 * oscuro, por encima del 3:1 de un elemento gráfico. */
.readiness-step--empty .readiness-step__mark {
  border-style: dashed;
  border-color: var(--border-strong);
}

/* Generándose: sin color de estado, porque no pide nada - el ícono que gira
 * es la forma, y se queda quieto con reduced-motion (ver .icon--spin). */
.readiness-step--working .readiness-step__mark { color: var(--text-secondary); }

/* Lo que pide algo lleva el valor en peso fuerte: es la cifra que esta
 * pantalla existe para reportar. Con más de uno esperando pueden ser dos, y
 * está bien - son dos cosas que hacer, no una que elegir. */
.readiness-step--attention .readiness-step__label { color: var(--text-primary); }

.readiness-step--attention .readiness-step__value { font-weight: var(--weight-strong); }

/* Forma y color, nunca solo color. */
.readiness-blocker {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-body-sm);
  padding: 2px var(--space-2);
  border: 1px solid var(--warning-border);
  border-radius: var(--radius-xs);
  background: var(--warning-bg);
  color: var(--text-primary);
}

.pending-tray {
  list-style: none;
  margin: 0;
  padding: 0;
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sm);
}

.pending-tray__row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  align-items: center;
  padding: var(--space-4);
  border-top: 1px solid var(--border-default);
}

.pending-tray__row:first-child { border-top: 0; }

.pending-tray__name {
  min-width: 0;
  flex: 1 1 16ch;
}

.pending-tray__name p { margin: 0; }

.pending-tray__sub {
  font-size: var(--text-body-sm);
  color: var(--text-secondary);
}

@media (max-width: 760px) {
  .readiness-ladder { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .readiness-step:nth-child(2) { border-right: 0; }
  .readiness-step:nth-child(1),
  .readiness-step:nth-child(2) { border-bottom: 1px solid var(--border-default); }
}

/* ── El indicador, accionable ──────────────────────────────
 *
 * Un paso con algo que hacer es un enlace, y el área de clic es la tarjeta
 * entera: el <a> toma el padding del paso y se estira a todo su alto.
 *
 * NO con `display: contents` más un ::after estirado, que fue el primer
 * intento: un elemento sin caja no renderiza sus pseudo-elementos, así que el
 * ::after no existía y la tarjeta no respondía al clic. El system spec no lo
 * vio porque rack_test encuentra el <a> sin mirar el CSS. */

.readiness-step--actionable {
  padding: 0;
}

.readiness-step__link {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-4);
  height: 100%;
  color: inherit;
  text-decoration: none;
}

/* Lo que interactúa tiene que parecer que interactúa. */
.readiness-step--actionable:hover {
  background: var(--bg-subtle);
}

.readiness-step__link:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: -2px;
}

/* La invitación: qué hacer, debajo de qué pasa. `margin-top: auto` la deja
 * pegada al pie de la tarjeta, así que las cuatro columnas la alinean entre
 * sí aunque el paso de arriba tenga más líneas. */
/* Texto corrido con el chevron en línea, y no una fila flex: con dos columnas
 * a 390px "Generar la estimación" parte en dos líneas, y en flex el <span>
 * tomaba todo el ancho y dejaba el chevron solo contra el borde derecho. En
 * línea, el chevron sigue a la última palabra, que no se separa de él. */
.readiness-step__go {
  display: block;
  margin: var(--space-1) 0 0;
  color: var(--link-color);
  font-size: var(--text-body-sm);
}

.readiness-step__go-tail { white-space: nowrap; }
.readiness-step__go-tail .icon { margin-left: var(--space-1); }


/* El chevron se corre al pasar por la tarjeta: nombra la propiedad, dura lo
 * de una interacción de alta frecuencia y desaparece con reduced-motion,
 * porque el token va a 0ms. */
.readiness-step__go .icon {
  transition: transform var(--motion-fast) var(--ease-out);
}

.readiness-step--actionable:hover .readiness-step__go .icon {
  transform: translateX(2px);
}
