/* =========================================================================
   Feedo — application chrome (login, dashboard, admin), in CUBE order:
   global, then Composition, Utility, Block, Exception.

   Almost all of the styling happens above the blocks. By the time a block is
   written, the global layer and the composition primitives have already
   decided its type, colour and rhythm — which is why the blocks below are
   mostly a handful of contextual lines each.

   Shares the warm design language of the injected feedback layer so the whole
   product reads as one identity. The tool stays quiet and precise; the
   client's prototype previews are where the boldness lives.
   ========================================================================= */

/* =========================================================================
   FUENTES
   Sólo las carga el panel. La capa que se inyecta en el prototipo del cliente
   usa el stack del sistema a propósito: no le sumamos descargas a una página
   que no es nuestra, y ahí Feedo tiene que pasar desapercibido.
   ========================================================================= */

@font-face {
  font-family: 'Gabarito';
  font-style: normal;
  font-weight: 400 900; /* variable: un archivo cubre todos los pesos */
  font-display: swap;
  src: url('/assets/fonts/gabarito-var.woff2') format('woff2');
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/assets/fonts/plex-mono-400.woff2') format('woff2');
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/assets/fonts/plex-mono-600.woff2') format('woff2');
}

/* =========================================================================
   GLOBAL
   ========================================================================= */

*,
*::before,
*::after {
  box-sizing: border-box;
}

body {
  margin: 0;
  /* El lienzo, no el papel. Feedo tiene que desaparecer detrás del diseño que se
     está juzgando: un fondo con tono le tiñe la percepción de los colores del
     cliente, y en una herramienta de revisión eso es un sesgo y no un gusto.
     El papel sigue existiendo como SUPERFICIE —panel, card, diálogo—, apoyado
     sobre el lienzo. */
  background: var(--feedo-canvas);
  color: var(--feedo-ink);
  font-family: var(--feedo-brand);
  font-size: var(--feedo-step-0);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

a {
  color: var(--feedo-accent);
  text-decoration: none;
}
a:hover {
  text-decoration: underline;
}
/* Un enlace rodeado de texto no puede distinguirse sólo por el color: quien no
   separa este azul del gris del párrafo no lo ve. Dentro de prosa va
   subrayado siempre; los enlaces sueltos de navegación no lo necesitan porque
   su lugar ya dice qué son. */
p a,
li a,
dd a {
  text-decoration: underline;
  text-underline-offset: 0.18em;
}

:focus-visible {
  outline: 2px solid var(--feedo-accent);
  outline-offset: 2px;
  border-radius: 6px;
}

h1,
h2,
h3 {
  margin: 0;
  font-weight: 800;
  line-height: 1.15;
  text-wrap: balance;
}
h1 {
  font-size: var(--feedo-step-3);
  letter-spacing: -0.01em;
}
h2 {
  font-size: var(--feedo-step-2);
  letter-spacing: -0.015em;
}
h3 {
  font-size: var(--feedo-step-0);
}

/* La bajada que sigue a un título: una frase, más grande que el cuerpo y en
   tinta plena. Es lo que se lee cuando alguien barre la página sin detenerse;
   si hace falta más detalle, va en el párrafo siguiente y en gris. */
.lede {
  font-size: var(--feedo-step-1);
  line-height: 1.35;
  color: var(--feedo-ink);
  text-wrap: balance;
}

p {
  margin: 0;
}

code {
  font-family: var(--feedo-mono);
  /* Relative, so it tracks whatever size it sits in. Held close to 1 because
     it compounds: at 0.86em inside a 0.8rem cell the repo slug landed at
     10.7px, below anything readable. */
  font-size: 0.94em;
  font-variant-numeric: tabular-nums;
}

/* =========================================================================
   COMPOSITION — how things sit next to each other. No colour, no type.
   Each primitive takes a custom property so a block can retune its spacing
   without redefining the layout.
   ========================================================================= */

/* Vertical rhythm between siblings, without margins on the children. */
.flow > * + * {
  margin-block-start: var(--flow-space, var(--feedo-space-m));
}

/* Evenly spaced vertical list. */
.stack {
  display: grid;
  gap: var(--stack-space, var(--feedo-space-s));
}

/* Things in a row that wrap when they must. */
.cluster {
  display: flex;
  flex-wrap: wrap;
  align-items: var(--cluster-align, center);
  gap: var(--cluster-space, var(--feedo-space-2xs));
}
.cluster[data-align="end"] {
  justify-content: flex-end;
}

/* Two groups pushed apart, stacking when there is no room. */
.repel {
  display: flex;
  flex-wrap: wrap;
  align-items: var(--repel-align, center);
  justify-content: space-between;
  gap: var(--repel-space, var(--feedo-space-s));
}

/* The page's horizontal frame. */
.wrapper {
  max-inline-size: var(--wrapper-max, 64rem);
  margin-inline: auto;
  padding-inline: var(--feedo-space-s-m);
}

/* Prose that should not run wider than it can be read. */
.measure {
  max-inline-size: var(--measure-max, var(--feedo-measure));
}

/* =========================================================================
   UTILITY — one job each, always through a token.
   ========================================================================= */

.visually-hidden {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.text-muted {
  color: var(--feedo-muted);
}

.text-meta {
  font-size: var(--feedo-step--1);
  color: var(--feedo-muted);
}

.text-label {
  font-size: var(--feedo-step--2);
  font-weight: 800;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--feedo-muted);
}

.nowrap {
  white-space: nowrap;
}

/* =========================================================================
   BLOCKS
   ========================================================================= */

/* ---- the page itself ----
   .wrapper only frames horizontally; the vertical breathing room is this
   page's decision, not the primitive's. */
.page {
  padding-block: var(--feedo-space-l) var(--feedo-space-2xl);
}
/* 64rem is a reading measure. A five-column table of projects is not prose and
   does not fit in one; data surfaces get their own frame. Prose inside them is
   still capped by .measure. */
[data-width="wide"] {
  --wrapper-max: 76rem;
}

/* La barra tiene UN ancho, siempre, y no el de la página que la lleva.
   Antes heredaba el `data-width` de cada pantalla para alinear el logo con el
   contenido de abajo. El resultado era que la marca cambiaba de lugar al
   navegar: 64rem en «Cómo funciona», 76rem en el panel, y el logo saltaba entre
   dos pantallas del mismo producto. Que el cromo se quede quieto importa más
   que alinearlo con una columna de prosa.

   Va en la hoja y no en un atributo a propósito: así ninguna página puede
   cambiarlo sin querer. */
.topbar .wrapper {
  --wrapper-max: 76rem;
}

/* ---- skip link ---- */
.skip-link {
  position: absolute;
  inset-block-start: 0;
  inset-inline-start: 0;
  z-index: 10;
  transform: translateY(-120%);
  padding: var(--feedo-space-2xs) var(--feedo-space-s);
  background: var(--feedo-accent);
  color: var(--feedo-accent-ink);
  font-weight: 700;
  border-end-end-radius: var(--feedo-radius-s);
}
.skip-link:focus-visible {
  transform: translateY(0);
  text-decoration: none;
}

/* ---- top bar ---- */
/* ---- la marca ----
   El calderón (¶) con un ojo. Se dimensiona en em para que acompañe al texto
   con el que está compuesto, sin números sueltos por cada tamaño. */
.mark {
  inline-size: 1.45em;
  block-size: 1.45em;
  flex: none;
  display: block;
}
/* Dos capas, porque las dos animaciones usan transform y sobre un mismo
   elemento la segunda pisaría a la primera: el grupo mira, los ojos parpadean.
   Como corren sueltas, a veces coinciden — y eso es justo lo que las hace
   parecer vivas en vez de programadas. */
.mark__eyes {
  transform-box: fill-box;
  transform-origin: center;
  animation: feedo-look 9s ease-in-out infinite;
}
.mark__eye {
  /* Negro porque es contenido de una máscara: ahí el negro es el agujero. El ojo
     no tiene color propio — muestra el fondo, y así contrasta con el cuerpo de la
     marca sea el fondo claro u oscuro. */
  fill: #000;
  /* fill-box, o el origen del transform sería el del viewBox y el ojo
     se cerraría contra el borde del SVG en vez de sobre sí mismo. */
  transform-box: fill-box;
  transform-origin: center;
  animation: feedo-blink 4.5s ease-in-out infinite;
}
/* Lo único que convierte el símbolo en alguien. Breve: tiene que notarse de
   reojo, no pedir atención mientras se trabaja. */
@keyframes feedo-blink {
  0%, 90%, 100% { transform: scaleY(1); }
  94%           { transform: scaleY(0.08); }
}
/* Mira a un lado, se queda un momento, vuelve. Las pausas largas en el centro
   son lo que da la sensación de atención: un ojo que se mueve sin parar es un
   tic, no una mirada. El desplazamiento va en unidades del viewBox, así que
   escala solo con el símbolo. */
@keyframes feedo-look {
  0%, 20%   { transform: translateX(0); }
  27%, 40%  { transform: translateX(3.5px); }
  47%, 63%  { transform: translateX(0); }
  70%, 83%  { transform: translateX(-3.5px); }
  90%, 100% { transform: translateX(0); }
}
@media (prefers-reduced-motion: reduce) {
  .mark__eyes,
  .mark__eye { animation: none; }
}

/* El borde y el fondo cruzan la ventana entera; el contenido lo encuadra el
   .wrapper de adentro, el mismo que usa el main. */
/* La barra es AZUL DE MARCA en todo el producto.
   Pasó por dos intentos y los dos fallaban por la misma razón. En papel, el cromo
   era casi el mismo crema con el que trabaja media clientela: no se sabía dónde
   termina la herramienta y dónde empieza lo que se está juzgando. En tinta se
   distinguía, pero un gris oscuro no es de nadie — podía ser de Feedo o del
   prototipo.
   El azul de la marca sí es de alguien. Es inconfundiblemente Feedo, así que no
   se puede confundir con el diseño de ningún cliente, y el blanco encima da
   7.2:1. El acento a pleno acá no compite con nada: en el lienzo blanco de abajo
   sigue significando identidad y acción, y en la barra significa "esto es la
   herramienta". */
.topbar {
  padding-block: var(--feedo-space-xs);
  background: var(--feedo-accent);
  color: var(--feedo-accent-ink);
  border-block-end: 1px solid transparent;
}

/* Todo lo que va sobre el acento va en blanco: es el único color que la paleta
   admite encima suyo. */
.topbar a,
.marco__vista {
  color: var(--feedo-accent-ink);
}

/* La marca en blanco, con los ojos calados: al ser agujeros muestran el azul del
   fondo, así que dentro del blanco siguen leyéndose como ojos. Es la misma razón
   por la que el dock ya usaba máscara — su encabezado cambia de color según el
   veredicto. */
.topbar__brand .mark,
.marco__marca .mark {
  color: var(--feedo-accent-ink);
}

.topbar__tagline {
  color: color-mix(in srgb, var(--feedo-accent-ink) 72%, transparent);
  border-inline-start-color: color-mix(in srgb, var(--feedo-accent-ink) 32%, transparent);
}

.topbar__session {
  border-inline-start-color: color-mix(in srgb, var(--feedo-accent-ink) 32%, transparent);
}

/* El «Salir» y el «Cómo funciona» sobre tinta: contorno claro en vez de la
   pastilla de papel, que sobre oscuro grita más que el contenido. */
.topbar .btn[data-variant="quiet"],
.topbar__help {
  color: var(--feedo-accent-ink);
  border-color: color-mix(in srgb, var(--feedo-accent-ink) 44%, transparent);
  background: transparent;
}

.topbar .btn[data-variant="quiet"]:hover,
.topbar__help:hover {
  color: var(--feedo-accent);
  background: var(--feedo-accent-ink);
  border-color: var(--feedo-accent-ink);
}

/* El foco sobre el acento no puede ser el acento. */
.topbar :focus-visible {
  outline-color: var(--feedo-accent-ink);
}
/* Caja mixta y no versalitas espaciadas: "Feedo" tiene que leerse como alguien
   que te acompaña, y las versalitas con tracking leen a placa corporativa. */
.topbar__brand {
  display: inline-flex;
  align-items: center;
  gap: var(--feedo-space-3xs);
  font-size: var(--feedo-step-1);
  font-weight: 800;
  letter-spacing: -0.03em;
  /* Sobre el azul de la barra, el logotipo escrito va en blanco: es texto y
     tiene que leerse antes que pertenecer. El calderón mantiene su forma y los
     ojos calados muestran el azul del fondo. */
  color: var(--feedo-accent-ink);
}
/* La bajada acompaña al wordmark, separada por una regla: dice qué es esto sin
   competir con la marca. Desaparece cuando no hay lugar — es contexto, no
   navegación, y nadie la necesita para operar. */
.topbar__tagline {
  padding-inline-start: var(--feedo-space-2xs);
  border-inline-start: 1px solid color-mix(in srgb, var(--feedo-accent-ink) 32%, transparent);
  font-size: var(--feedo-step--1);
  color: color-mix(in srgb, var(--feedo-accent-ink) 76%, transparent);
}
@media (max-width: 40rem) {
  .topbar__tagline {
    display: none;
  }
}

/* Tres cosas de distinta naturaleza, agrupadas como tales: Ayuda es
   navegación; el nombre y Salir son la sesión. La separación entre los dos
   grupos es lo que evita leerlos como una fila de tres botones iguales. */
.topbar__nav {
  display: flex;
  align-items: center;
  gap: var(--feedo-space-s);
}
.topbar__session {
  display: flex;
  align-items: center;
  gap: var(--feedo-space-2xs);
  padding-inline-start: var(--feedo-space-s);
  border-inline-start: 1px solid color-mix(in srgb, var(--feedo-accent-ink) 32%, transparent);
}
.topbar__user {
  font-size: var(--feedo-step--1);
  color: color-mix(in srgb, var(--feedo-accent-ink) 88%, transparent);
}
.topbar__nav a {
  display: inline-flex;
  align-items: center;
  gap: var(--feedo-space-3xs);
  font-size: var(--feedo-step--1);
  font-weight: 700;
  /* Blanco, no el acento: sobre la barra azul el acento es el fondo, y este
     enlace quedaba en 1:1 — invisible. Va acá, en el bloque del componente, y no
     en un override más arriba: con la misma especificidad gana el que va después,
     así que un override arriba no override nada. */
  color: var(--feedo-accent-ink);
}
.topbar__nav a svg {
  inline-size: 1.05em;
  block-size: 1.05em;
  flex: none;
}
/* El color ya distingue al enlace del texto gris de al lado, así que el hover
   suma el subrayado en vez de cambiar de tono otra vez. */
.topbar__nav a:hover {
  text-decoration: underline;
  text-underline-offset: 0.18em;
}
.topbar__nav a[aria-current="page"] {
  color: var(--feedo-ink);
}
.topbar form {
  margin: 0;
}

/* ---- page heading ---- */
.page-head {
  --flow-space: var(--feedo-space-2xs);
  margin-block-end: var(--feedo-space-m);
}
.page-head .meter {
  --flow-space: var(--feedo-space-xs);
}
/* The bar belongs to the sentence under it — running it the full container
   width would read as a page rule, not as this project's progress. */
.page-head .meter__bar {
  max-inline-size: 34rem;
}

.backlink {
  margin-block-end: var(--feedo-space-xs);
  font-size: var(--feedo-step--1);
  font-weight: 600;
}

.empty-state {
  max-inline-size: 46ch;
  margin-block-start: var(--feedo-space-m);
  padding-block: var(--feedo-space-m);
  color: var(--feedo-muted);
}

/* ---- buttons ----
   The neutral outline is the default on purpose: routine controls (log out,
   re-sync) must not read as the loudest thing on the page. Emphasis is opt-in
   through data-variant, which only the form that creates something wears. */
button,
.btn {
  font: inherit;
  font-size: var(--feedo-step--1);
  font-weight: 700;
  /* Its own leading. Inheriting a container's line-height makes a button as
     tall as whatever text happens to surround it — inside a table row that is
     1.6 lines of leading, and the control grows by a third for no reason. */
  line-height: 1.25;
  cursor: pointer;
  border: 1px solid var(--feedo-line);
  background: var(--feedo-paper);
  color: var(--feedo-ink);
  padding: var(--feedo-space-2xs) var(--feedo-space-xs);
  border-radius: var(--feedo-radius-pill);
  transition: border-color 0.15s, background 0.15s, color 0.15s;
}
button:hover,
.btn:hover {
  border-color: var(--feedo-accent);
  color: var(--feedo-accent);
}

.btn[data-variant="primary"] {
  background: var(--feedo-accent);
  color: var(--feedo-accent-ink);
  border-color: transparent;
}
.btn[data-variant="primary"]:hover {
  background: color-mix(in srgb, var(--feedo-accent) 85%, black);
  border-color: transparent;
  color: var(--feedo-accent-ink);
}
/* Inactivo tiene que VERSE inactivo. El botón de publicar llevaba el atributo
   —el navegador ignoraba el clic— pero se veía igual que uno activo: fondo del
   acento, texto blanco, cursor de mano. Un botón que invita a apretarlo y no
   hace nada se lee como una función rota, no como una que todavía no
   corresponde. Va después de las variantes para ganarles a todas. */
.btn:disabled,
.btn[data-variant="primary"]:disabled {
  background: var(--feedo-paper-2);
  color: var(--feedo-muted);
  border-color: var(--feedo-line);
  cursor: not-allowed;
}

/* Destructive, in two states. At rest it is the quietest control in the row —
   a delete that competes for attention gets clicked by accident. Armed, it is
   the loudest thing on the page, because by then that is exactly what the next
   click does. */
.btn[data-variant="danger"] {
  padding: var(--feedo-space-3xs) var(--feedo-space-2xs);
  background: transparent;
  border-color: transparent;
  color: var(--feedo-muted);
}
.btn[data-variant="danger"]:hover {
  background: color-mix(in srgb, var(--feedo-danger) 8%, var(--feedo-paper));
  border-color: color-mix(in srgb, var(--feedo-danger) 35%, transparent);
  color: var(--feedo-danger);
}
.btn[data-variant="danger-armed"] {
  display: inline-flex;
  align-items: center;
  gap: var(--feedo-space-3xs);
  background: var(--feedo-danger);
  border-color: var(--feedo-danger);
  color: #fff;
}
.btn[data-variant="danger-armed"]:hover {
  background: color-mix(in srgb, var(--feedo-danger) 85%, black);
  border-color: transparent;
  color: #fff;
}
.btn svg {
  inline-size: 1em;
  block-size: 1em;
  flex: none;
}
/* While a destructive action is armed, the row shows only that decision. */
.actions__row[data-confirming] > *:not(form[data-confirm]) {
  display: none;
}
/* A form holding an armed control also holds its Cancelar. */
.actions__row form {
  display: flex;
  align-items: center;
  gap: var(--feedo-space-3xs);
}
.btn[aria-busy="true"] {
  opacity: 0.75;
  cursor: progress;
}

.btn[data-variant="quiet"] {
  padding: var(--feedo-space-3xs) var(--feedo-space-2xs);
  font-size: var(--feedo-step--1);
  color: var(--feedo-muted);
  background: transparent;
}
.btn[data-variant="quiet"]:hover {
  background: var(--feedo-paper);
}

/* ---- auth (the front door) ---- */
.auth {
  max-inline-size: 24rem;
  margin-block: clamp(2rem, 9vh, 6rem);
  margin-inline: auto;
  padding: var(--feedo-space-m-l);
  background: var(--feedo-paper);
  border: 1px solid var(--feedo-line);
  border-radius: var(--feedo-radius);
  box-shadow: var(--feedo-shadow-md);
}
/* El descriptor es parte de la marca, no una línea suelta debajo: cuelga del
   wordmark, no del isotipo, y se mueve con el conjunto. El tamaño del símbolo
   va en em para que acompañe al bloque entero. */
.auth__brand {
  display: flex;
  align-items: center;
  gap: var(--feedo-space-2xs);
  margin: 0 0 var(--feedo-space-m);
  font-size: var(--feedo-step-3);
  color: var(--feedo-accent);
}
.auth__brand .mark {
  inline-size: 1.65em;
  block-size: 1.65em;
}
.auth__brand-text {
  display: grid;
  gap: 0.04em;
}
.auth__brand-name {
  font-weight: 800;
  letter-spacing: -0.035em;
  line-height: 1;
}
/* Subordinado sin discusión: la mitad del cuerpo, sin peso y en gris. Si
   compitiera, el conjunto se leería como dos títulos en vez de una marca. */
.auth__brand-sub {
  font-size: 0.5em;
  font-weight: 500;
  letter-spacing: 0;
  line-height: 1.25;
  color: var(--feedo-muted);
}
.auth__error {
  padding: var(--feedo-space-2xs) var(--feedo-space-xs);
  border-radius: var(--feedo-radius-s);
  border: 1px solid color-mix(in srgb, var(--feedo-danger) 30%, transparent);
  background: color-mix(in srgb, var(--feedo-danger) 10%, var(--feedo-paper));
  color: var(--feedo-danger);
  font-size: var(--feedo-step--1);
  font-weight: 600;
}

/* Local-only shortcuts under the login form. Deliberately drab: this is
   scaffolding, and it must not read as part of the product. */
.dev-shortcuts {
  display: flex;
  flex-wrap: wrap;
  gap: var(--feedo-space-2xs) var(--feedo-space-s);
  margin-block-start: var(--feedo-space-m);
  padding-block-start: var(--feedo-space-s);
  border-block-start: 1px dashed var(--feedo-line);
  font-size: var(--feedo-step--1);
}
.dev-shortcuts__label {
  inline-size: 100%;
  font-size: var(--feedo-step--2);
  font-weight: 800;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--feedo-muted);
}

/* ---- fields ---- */
.field {
  display: grid;
  gap: var(--feedo-space-3xs);
  font-size: var(--feedo-step--2);
  font-weight: 800;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--feedo-muted);
}
.field input,
.field select {
  font: inherit;
  font-size: var(--feedo-step-0);
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--feedo-ink);
  padding: var(--feedo-space-2xs) var(--feedo-space-xs);
  border: 1px solid var(--feedo-line);
  border-radius: var(--feedo-radius-s);
  background: var(--feedo-paper-2);
}
.field input:focus-visible,
.field select:focus-visible {
  outline: 2px solid var(--feedo-accent);
  outline-offset: 1px;
  border-color: var(--feedo-accent);
}
/* El campo de contraseña con su botón de ver. Lo arma el script; sin JS el
   input queda tal cual, sin un botón muerto al costado. */
.password-field {
  position: relative;
  display: grid;
}
.password-field input {
  /* Lugar para el botón, para que el texto nunca quede debajo. */
  padding-inline-end: 2.75rem;
}
.password-field__toggle {
  position: absolute;
  inset-block: 1px;
  inset-inline-end: 1px;
  display: grid;
  place-items: center;
  inline-size: 2.5rem;
  padding: 0;
  border: 0;
  border-radius: var(--feedo-radius-s);
  background: none;
  color: var(--feedo-muted);
  cursor: pointer;
}
.password-field__toggle:hover {
  color: var(--feedo-ink);
}
.password-field__toggle svg {
  inline-size: 1.25rem;
  block-size: 1.25rem;
}

.field-hint {
  font-size: var(--feedo-step--1);
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  color: var(--feedo-muted);
}
.field-hint code {
  padding: 0.05rem 0.3rem;
  border-radius: 5px;
  background: var(--feedo-paper-2);
}

.form {
  --stack-space: var(--feedo-space-s);
  max-inline-size: 34rem;
}
.form button {
  justify-self: start;
}

/* ---- review meter ----
   The bar is a summary of the legend beside it, never the only carrier of the
   value; screen readers get the same sentence through aria-label. */
.meter {
  --stack-space: var(--feedo-space-3xs);
}
.meter__bar {
  display: flex;
  block-size: 6px;
  border-radius: var(--feedo-radius-pill);
  overflow: hidden;
  background: color-mix(in srgb, var(--feedo-ink) 8%, transparent);
}
.meter__seg {
  display: block;
  block-size: 100%;
}
.meter__seg[data-tone="approved"] {
  background: var(--feedo-approve);
}
.meter__seg[data-tone="commented"] {
  background: var(--feedo-comment);
}
.meter__seg[data-tone="pending"] {
  background: color-mix(in srgb, var(--feedo-ink) 14%, transparent);
}
.meter__legend {
  font-size: var(--feedo-step--1);
  color: var(--feedo-muted);
}
.meter__legend strong,
.meter__lead {
  color: var(--feedo-ink);
  font-variant-numeric: tabular-nums;
  font-weight: 800;
}
/* Nada pendiente y nada comentado: el número dejó de contar lo que falta y pasó
   a contar lo que está hecho, así que se viste del color que en este sistema ya
   significa aprobado. */
.meter__lead[data-tone="done"] {
  color: var(--feedo-approve);
}
/* In a table row the legend leads and the bar drops to the second line, so it
   should read as a quantity rather than as a rule across the column. */
/* La barra mide lo mismo que la leyenda que la explica.
   Tenía un max-inline-size de 9rem, un ancho fijo sin relación con el texto de
   al lado: en una celda angosta no se notaba, pero en la tarjeta del cliente
   —que es ancha— la leyenda ocupaba 224px y la barra se cortaba en 144. Se leía
   como una barra incompleta, que es justo lo que una barra de progreso no puede
   permitirse decir por accidente.
   El bloque toma el ancho de su contenido y la barra se estira a ese ancho, así
   coinciden siempre, diga lo que diga el texto. */
.meter[data-density="compact"] {
  min-inline-size: 10rem;
  inline-size: fit-content;
}
.meter[data-density="compact"] .meter__legend {
  white-space: nowrap;
}
.meter[data-density="compact"] .meter__bar {
  block-size: 4px;
}
.meter[data-empty] {
  font-size: var(--feedo-step--1);
  color: var(--feedo-muted);
}

/* ---- dashboard: project list ---- */
.project-list {
  list-style: none;
  padding: 0;
  margin: 0;
}
.project-card {
  --repel-space: var(--feedo-space-s) var(--feedo-space-m);
  position: relative;
  padding: var(--feedo-space-s) var(--feedo-space-s-m);
  background: var(--feedo-paper);
  border: 1px solid var(--feedo-line);
  border-radius: var(--feedo-radius);
  box-shadow: var(--feedo-shadow-sm);
  transition: box-shadow 0.18s, transform 0.18s, border-color 0.18s;
}
.project-card:hover {
  box-shadow: var(--feedo-shadow-md);
  transform: translateY(-2px);
}
.project-card:focus-within {
  border-color: var(--feedo-accent);
}
.project-card__id {
  display: grid;
  gap: var(--feedo-space-4xs);
  min-inline-size: 0;
}
/* The whole card is the target — the hover lift promised it, so the hit area
   has to keep the promise. One link in the accessibility tree, not three. */
.project-card__link {
  color: var(--feedo-ink);
  font-size: var(--feedo-step-0);
  font-weight: 800;
}
.project-card__link::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: var(--feedo-radius);
}
.project-card__link:hover {
  text-decoration: none;
  color: var(--feedo-accent);
}
.project-card__repo {
  font-size: var(--feedo-step--1);
  color: var(--feedo-muted);
}
/* Fixed column so the bar starts at the same x on every card and still reads
   left to right, the direction the progress actually runs. */
.project-card__status {
  display: grid;
  gap: var(--feedo-space-3xs);
  min-inline-size: 14rem;
  text-align: end;
}
.project-card__synced {
  font-size: var(--feedo-step--1);
  color: var(--feedo-muted);
}
.project-card__unsynced {
  color: var(--feedo-danger);
  font-weight: 600;
}

/* ---- notices ---- */
.notice {
  padding: var(--feedo-space-2xs) var(--feedo-space-xs);
  margin-block-end: var(--feedo-space-m);
  border-radius: var(--feedo-radius-s);
  font-size: var(--feedo-step--1);
  font-weight: 600;
}
.notice[data-tone="ok"] {
  color: var(--feedo-approve);
  background: color-mix(in srgb, var(--feedo-approve) 12%, var(--feedo-paper));
  border: 1px solid color-mix(in srgb, var(--feedo-approve) 30%, transparent);
}
.notice[data-tone="err"] {
  color: var(--feedo-danger);
  background: color-mix(in srgb, var(--feedo-danger) 10%, var(--feedo-paper));
  border: 1px solid color-mix(in srgb, var(--feedo-danger) 30%, transparent);
}

/* ---- panels ---- */
.panel {
  margin-block-end: var(--feedo-space-m);
  background: var(--feedo-paper);
  border: 1px solid var(--feedo-line);
  border-radius: var(--feedo-radius);
  box-shadow: var(--feedo-shadow-sm);
  padding: var(--feedo-space-m);
}
/* La persiana ocupa el alto de la sala. Es una pantalla y no un aviso al paso:
   arrinconada arriba a la izquierda se lee como un error, y lo que dice no es
   que algo salió mal sino que hay trabajo en curso. */
.persiana {
  min-height: 60vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: flex-start;
  gap: var(--feedo-space-s);
  max-width: var(--feedo-measure);
  margin-inline: auto;
}
.persiana h1 {
  margin: 0;
}

/* Las pantallas que entran (o no) en una publicación. Nombres de archivo, así
   que van en mono y no se parten: cortar `checkout.html` a la mitad obliga a
   reconstruirlo con la vista. */
.chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--feedo-space-3xs);
}
.chip {
  font-family: var(--feedo-mono);
  font-size: var(--feedo-step--1);
  padding: var(--feedo-space-4xs) var(--feedo-space-2xs);
  border: 1px solid var(--feedo-line);
  border-radius: var(--feedo-radius-pill);
  background: var(--feedo-canvas);
  color: var(--feedo-muted);
  white-space: nowrap;
}
/* Las que entran: en el azul de la marca, que es el mismo con el que el cliente
   las va a ver marcadas del otro lado. */
.chip[data-in] {
  border-color: var(--feedo-accent);
  color: var(--feedo-accent);
  background: var(--feedo-canvas);
}

/* Flush: the table draws its own edges, so the panel only pads the heading
   and lets rows run the full width. It scrolls rather than clips — `hidden`
   here silently cut the last column off, which is how a Sincronizar button
   ends up unreachable instead of merely inconvenient. */
.panel[data-flush] {
  padding: 0;
  overflow-x: auto;
}
.panel__title {
  padding: var(--feedo-space-s) var(--feedo-space-s-m) var(--feedo-space-xs);
  font-size: var(--feedo-step-0);
  letter-spacing: 0.02em;
}

/* ---- tables ---- */
.table {
  inline-size: 100%;
  border-collapse: collapse;
}
/* Top, not middle: once any cell wraps to two lines, centring every cell
   independently floats each column to its own vertical position and the row
   loses its baseline. Aligned to the top they all start on the same line. */
.table th,
.table td {
  text-align: start;
  padding: var(--feedo-space-xs) var(--feedo-space-2xs);
  border-block-end: 1px solid var(--feedo-line);
  vertical-align: top;
}
.table thead th {
  font-size: var(--feedo-step--2);
  font-weight: 800;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--feedo-muted);
}
.table tbody th {
  font-weight: 700;
}
.table tr:last-child td,
.table tr:last-child th {
  border-block-end: 0;
}
.panel[data-flush] .table th:first-child,
.panel[data-flush] .table td:first-child {
  padding-inline-start: var(--feedo-space-s-m);
}
.panel[data-flush] .table th:last-child,
.panel[data-flush] .table td:last-child {
  padding-inline-end: var(--feedo-space-s-m);
}
.table tbody tr:hover {
  background: color-mix(in srgb, var(--feedo-paper-2) 45%, transparent);
}

/* ---- the row's type scale ----
   A row gets two sizes and no more. Line one carries each column's primary
   value at --row-lead; line two carries supporting detail at --row-sub. Seven
   elements at five different sizes is what made this table unreadable. */
.table tbody tr {
  --row-lead: var(--feedo-step-0);
  --row-sub: var(--feedo-step--1);
  --row-line: calc(var(--feedo-step-0) * 1.6);
}
.table tbody th,
.table tbody td {
  font-size: var(--row-lead);
  line-height: var(--row-line);
}
/* Scoped to .table so it out-specifies the cell rule above, which would
   otherwise hold every cell at --row-lead. */
.table .cell-meta {
  font-size: var(--row-sub);
  color: var(--feedo-muted);
  white-space: nowrap;
}
.table .actions a,
.table .meter[data-density="compact"] .meter__legend {
  font-size: var(--row-sub);
}
/* Elements taller than a line of text sit centred on line one rather than
   hanging below it — the cells are top-aligned, so they need the offset. */
.table .avatar,
.table .identity,
.table .actions__row {
  margin-block-start: calc((var(--row-line) - 2rem) / 2);
}
/* The flex layout goes on an inner wrapper, never on the cell itself:
   display:flex takes a <td> out of the table model, so it stops stretching to
   the row height and its bottom border lands above everyone else's. And no
   vertical-align override here — a stray `middle` used to out-specify the
   shared `top` and drop the actions below the row's first line. */
.actions__row {
  --cluster-space: var(--feedo-space-xs);
  /* The cluster primitive wraps by default, which is right almost everywhere
     and wrong here: a row of actions that breaks onto two lines steals width
     from every other column and makes the row taller than its content. */
  flex-wrap: nowrap;
}
.actions__row .btn {
  white-space: nowrap;
}
.actions__row form {
  margin: 0;
}

/* Two-line identity cell: the human name leads, the machine key follows. */
.view-cell {
  min-inline-size: 15rem;
}
/* A column of twenty accent-coloured links is a wall. The name reads as text;
   the link colour is earned on hover, where it also confirms the target. */
.view-cell__title {
  display: block;
  font-weight: 700;
  line-height: var(--row-line, 1.6);
  color: var(--feedo-ink);
}
a.view-cell__title:hover {
  color: var(--feedo-accent);
}

/* ---- rows that are links ----
   Same trick as the dashboard card: the name is the link and a stretched
   pseudo-element extends its hit area over the whole row, so there is still
   exactly one link in the accessibility tree rather than a row of duplicates.
   Opt-in per table, because a row full of selectable text — the feedback
   read-out, where the comments live — should not be one big click target. */
.table[data-rows="linked"] tbody tr {
  position: relative;
}
.table[data-rows="linked"] a.view-cell__title::after {
  content: "";
  position: absolute;
  inset: 0;
}
/* Anything you can actually operate has to sit above that area, or the row
   swallows it — including the disc, whose tooltip names the client. */
.table[data-rows="linked"] .actions__row,
.table[data-rows="linked"] .avatar {
  position: relative;
  z-index: 1;
}
/* One line, never split. "owner/repo · branch" is a single coordinate; letting
   the branch fall to its own line orphans it and makes the row taller than the
   information in it. If it truly does not fit it truncates, and the full
   string is on the title attribute. */
.view-cell__meta {
  display: block;
  margin-block-start: var(--feedo-space-4xs);
  line-height: 1.35;
  font-size: var(--row-sub, var(--feedo-step--1));
  font-weight: 400;
  color: var(--feedo-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.view-cell__stale {
  margin-inline-start: var(--feedo-space-3xs);
  padding: 1px 7px;
  border-radius: var(--feedo-radius-pill);
  background: color-mix(in srgb, var(--feedo-danger) 12%, var(--feedo-paper));
  color: var(--feedo-danger);
  font-size: var(--feedo-step--2);
  font-weight: 700;
}
.comment-cell {
  max-inline-size: 34rem;
}

/* repo · branch is a coordinate, not a status. No fill and no colour — two
   chips of different hues made the least important thing in the row the
   loudest, and pushed the branch onto a line of its own. One quiet line. */
.view-cell__meta code,
.project-card__repo code {
  background: none;
  padding: 0;
  color: inherit;
}
.ref-branch::before {
  content: "·";
  margin-inline: var(--feedo-space-4xs);
  font-weight: 700;
}

/* ---- identity ----
   Initials disc. The violet is free to mean identity again now that
   "commented" has its own hue, so the person marker wears the brand colour. */
.avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: 2rem;
  block-size: 2rem;
  flex: none;
  border-radius: 50%;
  background: color-mix(in srgb, var(--feedo-accent) 12%, var(--feedo-paper));
  color: var(--feedo-accent);
  font-size: var(--feedo-step--2);
  font-weight: 800;
  letter-spacing: 0.01em;
  line-height: 1;
  vertical-align: top;
  cursor: default;
}
.identity {
  --cluster-space: var(--feedo-space-2xs);
  display: inline-flex;
  flex-wrap: nowrap;
}

/* ---- disclosures ---- */
.disclosure {
  background: var(--feedo-paper);
  border: 1px solid var(--feedo-line);
  border-radius: var(--feedo-radius);
  box-shadow: var(--feedo-shadow-sm);
}
.disclosure__summary {
  display: flex;
  align-items: center;
  gap: var(--feedo-space-2xs);
  padding: var(--feedo-space-s) var(--feedo-space-s-m);
  font-size: var(--feedo-step-0);
  font-weight: 800;
  cursor: pointer;
  list-style: none;
}
.disclosure__summary::-webkit-details-marker {
  display: none;
}
.disclosure__summary::before {
  content: "+";
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: 1.35rem;
  block-size: 1.35rem;
  border-radius: var(--feedo-radius-pill);
  background: var(--feedo-paper-2);
  color: var(--feedo-accent);
  line-height: 1;
}
.disclosure[open] .disclosure__summary::before {
  content: "−";
}
.disclosure[open] .disclosure__summary {
  border-block-end: 1px solid var(--feedo-line);
}
.disclosure .form {
  padding: var(--feedo-space-m) var(--feedo-space-s-m);
}
/* El nombre se lleva el ancho que sobra para que el contador quede pegado al
   borde derecho: en el medio del renglón se lee como parte del título. */
.disclosure__nombre {
  flex: 1;
  min-inline-size: 0;
}
/* Lo único que se sabe del bloque sin abrirlo. Va en el acento y no en rojo:
   no hay nada roto —el proyecto anda igual—, hay algo sin enchufar. Se apaga
   solo: cuando no falta nada, este span no se imprime. */
.disclosure__falta {
  font-size: var(--feedo-step--1);
  font-weight: 700;
  color: var(--feedo-accent);
  background: color-mix(in srgb, var(--feedo-accent) 12%, var(--feedo-canvas));
  border-radius: var(--feedo-radius-pill);
  padding: var(--feedo-space-4xs) var(--feedo-space-2xs);
  white-space: nowrap;
}

/* ---- ajustes que se tocan una vez ----
   Dos gestos de alta —la credencial del agente y el aviso del repo— que antes
   eran dos paneles del mismo peso que el historial y la tabla, arriba de todo.
   Adentro del mismo desplegable dejan de competir por la primera pantalla, y la
   regla entre ellos alcanza para separarlos: son dos, no una lista. */
.ajustes {
  padding: var(--feedo-space-m) var(--feedo-space-s-m);
  display: grid;
  gap: var(--feedo-space-m);
}
/* `min-inline-size: 0` y no por prolijidad: adentro va el instructivo del agente,
   un <pre> de 90 columnas. Una celda de grilla mide `auto` por defecto, o sea que
   se estira hasta que su contenido entra — y en 320px eso empujaba la página
   entera 500px hacia la derecha. Con 0, el <pre> vuelve a scrollear solo. */
.ajuste {
  --flow-space: var(--feedo-space-s);
  min-inline-size: 0;
}
.ajuste + .ajuste {
  padding-block-start: var(--feedo-space-m);
  border-block-start: 1px solid var(--feedo-line);
}
/* Siguen siendo <h2> —el orden del documento no cambió— pero ya no titulan una
   sección de la página: titulan un ajuste adentro de un cajón cerrado, y a
   cuerpo de h2 gritaban desde adentro. */
.ajuste__titulo {
  font-size: var(--feedo-step-1);
  letter-spacing: -0.01em;
}

/* ---- status badges (shared review vocabulary) ---- */
.badge {
  display: inline-flex;
  align-items: center;
  padding: 3px 10px;
  border-radius: var(--feedo-radius-pill);
  font-size: var(--feedo-step--2);
  font-weight: 800;
  letter-spacing: 0.01em;
  white-space: nowrap;
}
.badge[data-state="approved"] {
  background: var(--feedo-approve);
  color: var(--feedo-approve-ink);
}
.badge[data-state="commented"] {
  background: var(--feedo-comment);
  color: var(--feedo-comment-ink);
}
.badge[data-state="pending"] {
  background: var(--feedo-paper-2);
  color: var(--feedo-muted);
  border: 1px solid var(--feedo-line);
}
/* Vistas con respuesta publicada que el cliente todavía no volvió a mirar. El
   acento de la marca y no el de comentario: no es algo que él dijo, es algo que
   le contestamos. Mismo color que el aviso de arriba, que anuncia lo mismo. */
.badge[data-state="recheck"] {
  background: var(--feedo-accent-soft);
  color: var(--feedo-accent);
}

/* =========================================================================
   EXCEPTIONS — structural only. There are no type or spacing breakpoints
   anywhere in this project; the token scale is fluid and handles that.
   ========================================================================= */

/* A five-column admin table cannot shrink below its longest repo slug, so it
   stops being a table: each row becomes a labelled block. The header row is
   kept for assistive tech via the per-cell data-label.
   The switch happens at 68rem, not at a phone width, because that is where the
   five columns stop fitting — and below it the alternative is scrolling
   sideways to reach Sincronizar, which is a worse trade than a taller row. */
@media (max-width: 68rem) {
  .table[data-stacked] thead {
    position: absolute;
    inline-size: 1px;
    block-size: 1px;
    overflow: hidden;
    clip-path: inset(50%);
  }
  .table[data-stacked] tr {
    display: grid;
    gap: var(--feedo-space-3xs);
    padding: var(--feedo-space-s) var(--feedo-space-s-m);
    border-block-end: 1px solid var(--feedo-line);
  }
  .table[data-stacked] tr:last-child {
    border-block-end: 0;
  }
  .table[data-stacked] th,
  .table[data-stacked] td {
    display: block;
    padding: 0;
    border: 0;
  }
  /* The edge padding above is set with :first-child / :last-child, which
     out-specifies a bare element override — match the shape to win it back. */
  .panel[data-flush] .table[data-stacked] th:first-child,
  .panel[data-flush] .table[data-stacked] td:first-child,
  .panel[data-flush] .table[data-stacked] th:last-child,
  .panel[data-flush] .table[data-stacked] td:last-child {
    padding-inline: 0;
  }
  /* A pending row has nothing to say in most columns. Stacked, those cells
     become two labelled lines of em-dash each; drop them and the row reads as
     what it is. */
  .table[data-stacked] td[data-empty] {
    display: none;
  }
  /* Unstacked the meter is a column; stacked it would run the whole row and
     read as a divider rather than as a quantity. */
  .table[data-stacked] .meter {
    max-inline-size: 18rem;
  }
  .table[data-stacked] td[data-label]::before {
    content: attr(data-label);
    display: block;
    margin-block-end: var(--feedo-space-4xs);
    font-size: var(--feedo-step--2);
    font-weight: 800;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--feedo-muted);
  }
  .table[data-stacked] .actions__row {
    justify-content: flex-start;
    margin-block-start: var(--feedo-space-3xs);
  }
  .comment-cell {
    max-inline-size: none;
  }
  .view-cell {
    min-inline-size: 0;
  }

  .project-card {
    flex-direction: column;
    align-items: stretch;
  }
  .project-card__status {
    justify-items: start;
    text-align: start;
  }
}

@media (prefers-reduced-motion: reduce) {
  .project-card,
  button,
  .btn {
    transition: none;
  }
  .project-card:hover {
    transform: none;
  }
}

/* ---- agent access ----
   A credential and a command to paste. Monospace because both are literal:
   a stray character in either and the agent gets a 401 with no clue why.
   El contenedor lo pone ahora `.ajuste`: el acceso del agente dejó de ser un
   desplegable propio y comparte cajón con la sincronización. */
.snippet {
  margin: 0;
  padding: var(--feedo-space-xs) var(--feedo-space-s);
  border-radius: var(--feedo-radius-s);
  background: var(--feedo-ink);
  color: var(--feedo-paper);
  font-size: var(--feedo-step--1);
  line-height: 1.6;
  overflow-x: auto;
}
.snippet code {
  background: none;
  padding: 0;
  color: inherit;
  white-space: pre;
}

/* El briefing del agente entra entero acá — token, endpoint, contrato y cómo
   recorrerlo — para que se copie de una sola vez. Es largo, así que se acota y
   hace scroll; por eso lleva tabindex: un contenedor que scrollea tiene que
   poder alcanzarse y recorrerse con el teclado, no sólo con la rueda. */
.snippet[data-long] {
  max-block-size: 24rem;
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* El ciclo del producto en tres pasos. Numerados porque el orden es real: sin
   vistas publicadas no hay nada que revisar, y sin revisión no hay devolución.
   El número se dibuja con un contador en vez de dejar la viñeta del <ol>, para
   que tenga el peso de un dato y no el de una lista de compras. */
/* Las dos mitades del problema, lado a lado. En paralelo y no en lista, porque
   ninguna viene antes que la otra: son el mismo hueco visto desde cada punta. */
.duo {
  display: grid;
  gap: var(--feedo-space-s);
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  margin-block-start: var(--feedo-space-s);
}
.duo__lado {
  display: grid;
  gap: var(--feedo-space-3xs);
  align-content: start;
  padding: var(--feedo-space-s);
  border-radius: var(--feedo-radius);
  background: var(--feedo-paper-2);
}
.duo__lado h3 {
  margin: 0;
  font-size: var(--feedo-step-0);
  font-weight: 800;
}
.duo__lado p {
  margin: 0;
  font-size: var(--feedo-step--1);
  line-height: 1.5;
  color: var(--feedo-muted);
}

.ciclo {
  list-style: none;
  counter-reset: paso;
  /* Aire de más contra el encabezado: la regla superior de la primera columna
     queda si no tan cerca del h2 que parece subrayarlo. */
  margin: var(--feedo-space-s) 0 0;
  padding: 0;
  display: grid;
  gap: var(--feedo-space-m) var(--feedo-space-l);
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
}
/* Cada paso es una pieza cerrada. Van sobre el papel más oscuro para separarse
   del panel que las contiene, que ya es claro: si compartieran fondo, las tres
   se leerían como un solo bloque de texto en columnas. */
.ciclo__paso {
  counter-increment: paso;
  display: grid;
  gap: var(--feedo-space-2xs);
  align-content: start;
  padding: var(--feedo-space-s);
  border-radius: var(--feedo-radius);
  background: var(--feedo-paper-2);
}
/* Relleno y no sólo color: el número es el ancla visual de cada paso y a este
   tamaño un dígito suelto se pierde entre el título y el texto. */
.ciclo__paso::before {
  content: counter(paso);
  inline-size: 2.1rem;
  block-size: 2.1rem;
  display: grid;
  place-items: center;
  border-radius: var(--feedo-radius-pill);
  background: var(--feedo-accent);
  color: var(--feedo-accent-ink);
  font-size: var(--feedo-step--1);
  font-weight: 800;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.ciclo__paso h3 {
  margin: 0;
  font-size: var(--feedo-step-1);
  font-weight: 800;
  line-height: 1.2;
  text-wrap: balance;
}
.ciclo__paso p {
  margin: 0;
  font-size: var(--feedo-step--1);
  line-height: 1.5;
  color: var(--feedo-muted);
}

/* El curl y su botón de copiar: el bloque manda, el botón no compite. */
.snippet-row {
  --cluster-space: var(--feedo-space-2xs);
  flex-wrap: nowrap;
  align-items: stretch;
}
.snippet-row .snippet {
  flex: 1;
  min-inline-size: 0;
}
.snippet-row .btn {
  flex: none;
  align-self: center;
}

/* =========================================================================
   EL MARCO — una vista del prototipo.
   Ver openspec/changes/viewport-switcher/.

   Hasta acá una vista ERA el prototipo, así que sólo se podía ver al ancho de
   la ventana. Ahora el prototipo vive adentro de un documento de Feedo y esto
   es la sala donde se lo mira.
   ========================================================================= */

/* El fondo de la sala, elegido por proyecto.
   No es gusto: es contraste. Un prototipo claro sobre lienzo blanco se funde con
   la sala y quien revisa deja de ver dónde termina el diseño que vino a juzgar.
   La barra NO cambia: el azul es la identidad de la herramienta y se lee sobre
   los dos, y si también cambiara volveríamos a que el cromo se confunda con el
   prototipo. */
.feedo-marco.fondo-oscuro {
  background: var(--feedo-ink);
}

/* Sobre tinta, el borde claro es el que separa el lienzo del fondo. */
.marco[data-fondo="oscuro"] .marco__lienzo {
  border-color: color-mix(in srgb, var(--feedo-paper) 26%, transparent);
}

.feedo-marco .page {
  padding-block: var(--feedo-space-s) var(--feedo-space-m);
}

/* El lienzo necesita todo el ancho; el resto de la página, no.
   `--wrapper-max: none` estaba en TODOS los `.wrapper`, así que también estiraba
   la barra superior y la vista dejaba de parecerse al menú — misma app, dos
   encabezados distintos. Ahora sólo se suelta el `main`: la barra de Feedo sigue
   encajada como en el resto del producto, y el lienzo es lo único que se
   desborda. */
.feedo-marco .page {
  --wrapper-max: none;
  /* Menos aire que una página de contenido, pero aire. En cero el lienzo quedaba
     pegado a la barra y las dos cosas se leían como una sola pieza; el prototipo
     necesita que se vea dónde empieza. */
  padding-block: var(--feedo-space-s) var(--feedo-space-m);
}

/* El cromo oscuro ya no es de esta pantalla: es de todo el producto. Ver
   `.topbar` arriba. Lo que empezó como una excepción del marco resultó ser la
   regla, porque el argumento —el cromo tiene que ser inconfundiblemente el
   cromo— no era de una vista sino de la herramienta. */

/* La barra de la vista ES `.topbar`, la misma clase que usa el resto del
   producto — no una barra propia que copie sus medidas.
   Copiarlas fue el primer intento y es justamente lo que divergía: un padding
   distinto acá y la vista dejaba de tener el alto del listado. Reusar la clase
   hace que el ancho y el alto coincidan por construcción, y que un cambio futuro
   en el cromo llegue a las dos pantallas al mismo tiempo. */

/* La marca queda como ancla a casa, sin el logotipo escrito: en una barra densa
   el calderón solo ya se reconoce, y las palabras que sobran son alto que le
   falta al prototipo. */
.marco__marca {
  display: inline-flex;
  align-items: center;
  color: var(--feedo-accent);
  padding-inline-end: var(--feedo-space-2xs);
  border-inline-end: 1px solid var(--feedo-line);
}

/* El calderón, al tamaño con el que se compone en toda la app: la marca no
   cambia de cuerpo según la pantalla. */
.marco__marca .mark {
  inline-size: 1.45em;
  block-size: 1.45em;
}

/* La barra se alinea con el contenido del menú: venir del menú y entrar a una
   vista no debería mover de lugar el título ni la salida.

   Lo hace envolviéndose en `.wrapper`, la primitiva que ya usa la barra de
   arriba, y no con un max-width propio. Rederivar la caja a mano la dejaba 27px
   corrida —justo un gutter— porque el padding de la primitiva no entraba en la
   cuenta. Cuando ya existe la pieza que centra, usarla es también la única forma
   de que las dos coincidan siempre. */



.marco {
  display: grid;
  gap: var(--feedo-space-xs);
}

/* `.wrapper` centra con `margin-inline: auto` y un max-width, y eso funciona en
   flujo normal —un bloque con `width: auto` llena y después topa—. Como ítem de
   grid no: con márgenes automáticos el ítem deja de estirarse y se resuelve a
   `fit-content`, así que la barra se encogía a su contenido y quedaba centrada
   en vez de repartida a lo ancho de la caja. Devolverle el estirón es una línea. */
/* `.wrapper` centra con márgenes automáticos, que en flujo normal llenan y
   después topan; como ítem de grid, en cambio, resuelven a `fit-content` y la
   barra se encogía a su contenido. Dentro de `.topbar` esto ya no hace falta
   —el nav es el ítem y su wrapper vuelve al flujo normal—, pero la regla queda
   para cualquier `.wrapper` que se cuelgue directo del marco. */
.marco > .wrapper {
  inline-size: 100%;
}

.marco__barra {
  align-items: center;
  gap: var(--feedo-space-s);
}

/* La salida de la vista. Vive en la barra y no en el dock: el dock se puede
   ocultar, y quien lo ocultaba se quedaba encerrado en la vista. Además navegar
   no es tema del panel de feedback. */
.marco__volver {
  display: inline-flex;
  align-items: center;
  gap: 0.2em;
  /* Azul y subrayado al pasar, igual que el «Volver» del menú. Un enlace que
     lleva al mismo lugar no puede verse distinto según de dónde lo mires. */
  color: var(--feedo-accent);
  text-decoration: underline;
  text-underline-offset: 0.18em;
  font-size: var(--feedo-step--1);
  font-weight: 600;
  white-space: nowrap;
}

.marco__volver .marco__icono {
  inline-size: 1.05rem;
  block-size: 1.05rem;
}

.marco__identidad {
  min-inline-size: 0;
  gap: var(--feedo-space-2xs) var(--feedo-space-s);
}

.marco__vista {
  margin: 0;
  color: var(--feedo-paper);
  font-weight: 700;
  font-size: var(--feedo-step-1);
  /* El título viene del <title> del cliente y puede ser largo. */
  min-inline-size: 0;
  overflow-wrap: anywhere;
}

/* ---- el conmutador ---- */

/* El conmutador vive en la barra, que es oscura, así que su tratamiento es de
   fondo oscuro: la pastilla del grupo es un claro translúcido y los iconos van en
   papel. Quedaron con los colores del fondo claro cuando el cromo pasó a tinta, y
   los inactivos se volvieron casi invisibles. */
.marco__anchos {
  gap: var(--feedo-space-3xs);
  /* Sin padding vertical: el alto de la barra lo tiene que fijar el contenido de
     siempre, no este grupo. Ver el comentario de `.marco__ancho`. */
  padding: 0 var(--feedo-space-3xs);
  background: color-mix(in srgb, var(--feedo-accent-ink) 16%, transparent);
  border-radius: var(--feedo-radius-pill);
  flex-wrap: nowrap;
}

/* El rótulo del grupo. Nombra qué hacen los tres iconos sin repetirlo en cada
   uno: la etiqueta va una vez y las siluetas hacen el resto. */
.marco__rotulo {
  padding-inline: var(--feedo-space-2xs) var(--feedo-space-3xs);
  font-size: var(--feedo-step--2);
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  /* Blanco pleno, no un blanco al 76%.
     Este rótulo se apoya sobre la pastilla del grupo, que es más clara que la
     barra, así que el porcentaje que alcanzaba sobre el azul pleno acá daba
     3.6:1 — por debajo de AA para 12px en negrita. Cuando el cromo pasó de tinta
     a azul traduje los porcentajes en vez de volver a medir, y esto es lo que se
     escapó. */
  color: var(--feedo-accent-ink);
}

/* Dos cosas que se peleaban, y las dos importan.
   El área de toque tiene que ser de 44px: un control de 24px es un control para
   mouse nada más. Y esta barra tiene que medir exactamente lo mismo que la del
   listado, o entrar a una vista mueve el contenido para abajo.
   Se resuelven con márgenes negativos: el botón mide 44px de alto real y aporta
   32px al layout, así que el área de toque desborda hacia el padding de la barra
   —que está vacío— sin empujar su alto. */
.marco__ancho {
  display: inline-grid;
  place-items: center;
  inline-size: 2.75rem;
  block-size: 2.75rem;
  margin-block: -0.375rem;
  border-radius: var(--feedo-radius-pill);
  text-decoration: none;
}

/* Gana a `.topbar a`, que pinta los enlaces de la barra en azul no-foto: acá el
   icono no es un enlace de texto, es un control de un grupo, y el azul lo haría
   parecer el activo. */
.topbar .marco__ancho {
  color: color-mix(in srgb, var(--feedo-accent-ink) 82%, transparent);
}

.topbar .marco__ancho:hover {
  color: var(--feedo-accent-ink);
  text-decoration: none;
}

/* El activo se marca con la pastilla clara y la tinta encima: sobre oscuro, un
   cambio de color de trazo no alcanza para decir cuál está puesto. */
.topbar .marco__ancho[aria-current="true"] {
  background: var(--feedo-accent-ink);
  color: var(--feedo-accent);
}

/* El toggle de fondo va pegado al grupo de medidas pero separado por un hueco:
   son dos decisiones distintas —cuán ancho y sobre qué fondo— y agruparlas en la
   misma pastilla las haría leer como una sola. */
.marco__controles {
  gap: var(--feedo-space-2xs);
  flex-wrap: nowrap;
}

.topbar .marco__fondo {
  background: color-mix(in srgb, var(--feedo-accent-ink) 16%, transparent);
  color: var(--feedo-accent-ink);
}

.topbar .marco__fondo:hover {
  background: var(--feedo-accent-ink);
  color: var(--feedo-accent);
}

/* Volver a ver el recorrido guiado.
   Es un <button> entre <a>: el botón nativo trae fondo, borde y tipografía
   propios que sus hermanos no tienen, y sin resetearlos quedaba una pastilla
   clara con el glifo casi blanco encima — es decir, invisible. */
.marco__ayuda {
  padding: 0;
  border: 0;
  font: inherit;
  cursor: pointer;
}
.topbar .marco__ayuda {
  background: color-mix(in srgb, var(--feedo-accent-ink) 16%, transparent);
  color: var(--feedo-accent-ink);
}
.topbar .marco__ayuda:hover {
  background: var(--feedo-accent-ink);
  color: var(--feedo-accent);
}

/* El mismo control en el panel, sobre fondo claro. Comparte el glifo con la
   barra de la vista a propósito: configurar el fondo y cambiarlo tienen que
   verse como la misma decisión, no como dos cosas distintas. */
.fondo-switch {
  gap: 2px;
  padding: 2px;
  background: var(--feedo-paper-2);
  border-radius: var(--feedo-radius-pill);
  flex-wrap: nowrap;
}

.fondo-switch__op {
  display: inline-grid;
  place-items: center;
  inline-size: 2rem;
  block-size: 1.75rem;
  padding: 0;
  border: 0;
  border-radius: var(--feedo-radius-pill);
  background: transparent;
  color: var(--feedo-muted);
  cursor: pointer;
}

.fondo-switch__op:hover {
  color: var(--feedo-ink);
}

.fondo-switch__op[aria-current="true"] {
  background: var(--feedo-paper);
  color: var(--feedo-accent);
  box-shadow: var(--feedo-shadow-sm);
}

.fondo-switch__op .marco__icono {
  inline-size: 1rem;
  block-size: 1rem;
}

.marco__icono {
  inline-size: 1.15rem;
  block-size: 1.15rem;
}


/* El activo es el único con fondo: el conmutador tiene que poder leerse de un
   vistazo, y tres pastillas iguales no dicen en cuál estás. */
.marco__ancho[aria-current="true"] {
  background: var(--feedo-paper);
  color: var(--feedo-ink);
  box-shadow: var(--feedo-shadow-sm);
}


/* ---- el lienzo ---- */

.marco__lienzo {
  /* El borde marca dónde termina la ventana simulada y empieza la sala. Sobre
     blanco tiene que ser más firme que una línea de sistema: si el prototipo
     también es claro, un borde tenue desaparece y el layout parece roto en vez
     de acotado. */
  display: block;
  inline-size: var(--marco-ancho, 100%);
  max-inline-size: 100%;
  margin-inline: auto;
  block-size: min(80vh, 900px);
  border: 1px solid color-mix(in srgb, var(--feedo-ink) 20%, transparent);
  border-radius: var(--feedo-radius);
  background: #fff;
  box-shadow: var(--feedo-shadow-lg);
}

/* A ancho completo la sombra sobra: no hay ventana que insinuar, es la sala
   entera. Y con más ancho conviene más alto, porque la página se lee como la
   página y no como una tira. */
/* A ancho completo el borde alcanza y la sombra sobra: no hay ventana que
   insinuar, es la sala entera. */
.marco[data-fluido] .marco__lienzo {
  box-shadow: var(--feedo-shadow-sm);
  border-radius: var(--feedo-radius-s);
  block-size: min(86vh, 1200px);
}

@media (max-width: 45rem) {
  .marco__barra {
    flex-direction: column;
    align-items: flex-start;
  }
  /* En un teléfono de verdad no hay lugar para simular otro teléfono más
     angosto: el lienzo toma lo que hay. */
  .marco__lienzo {
    inline-size: 100%;
  }
}

/* Un sync que falló, dicho en la tabla. No es un adorno: es la única señal de
   que el prototipo que ve el cliente quedó viejo, y de por qué. */
.sync-error {
  display: block;
  margin-block-start: var(--feedo-space-3xs);
  color: var(--feedo-danger);
  font-size: var(--feedo-step--2);
  line-height: 1.35;
}

/* La ayuda del campo del token: son varias frases y una lista, no la línea
   suelta que era antes. */
.field-hint p {
  margin-block: 0 var(--feedo-space-3xs);
}
.field-hint ul {
  margin-block: 0 var(--feedo-space-3xs);
  padding-inline-start: var(--feedo-space-s);
}
.field-hint li {
  margin-block-end: var(--feedo-space-4xs);
}
.token-link {
  font-weight: 700;
}

/* Quién terminó de revisar y está esperando. Es lo único del panel que le pide
   algo al estudio, así que va arriba y con el acento de la marca — no en rojo:
   no es un error, es un turno. */
.esperando-trabajo {
  border-color: var(--feedo-accent);
  box-shadow: 0 0 0 3px var(--feedo-accent-soft);
}
.esperando-trabajo__lista {
  margin: 0;
  padding-inline-start: var(--feedo-space-s);
}
.esperando-trabajo__lista li + li {
  margin-block-start: var(--feedo-space-3xs);
}
.badge[data-state="returned"] {
  background: var(--feedo-accent-soft);
  color: var(--feedo-accent);
}
/* Listo para publicar: es una acción disponible, no una espera. Va con el verde
   de aprobado, que en este sistema ya significa "esto está resuelto". */
.badge[data-state="publish"] {
  background: var(--feedo-approve);
  color: var(--feedo-approve-ink);
}

/* Novedades para el cliente: el estudio respondió y publicó. Mismo tratamiento
   que el aviso del admin —es la misma pelota, del otro lado de la cancha— y el
   mismo acento de la vista con cambios, para que el color signifique lo mismo
   en las dos pantallas. */
.novedades {
  border-color: var(--feedo-accent);
  box-shadow: 0 0 0 3px var(--feedo-accent-soft);
}
.novedades__lista {
  margin: 0;
  padding-inline-start: var(--feedo-space-s);
}
.novedades__lista li + li {
  margin-block-start: var(--feedo-space-3xs);
}

/* La tarjeta compone su estado en grilla, y un badge en una celda de grilla se
   estira al ancho de la columna: la marca dejaba de leerse como marca y pasaba
   a leerse como barra. Toma su ancho natural y se alinea con el resto de la
   columna, que va a la derecha. */
.project-card__status .badge {
  justify-self: end;
}

/* El nombre del proyecto y de quién es el turno, en una línea. La marca estaba
   en la columna de revisión, apilada bajo la barra: ahí se leía como parte de
   la medición, cuando dice algo distinto —la barra mide cuánto se revisó, la
   marca dice quién tiene la pelota. */
.view-cell__head {
  display: flex;
  align-items: center;
  gap: var(--feedo-space-2xs);
  flex-wrap: wrap;
}
/* Acá la marca acompaña al nombre, no encabeza una celda: con el alto de
   siempre pesaba más que el proyecto que califica. El padding era lo de menos
   —a 11px de tipografía, el interlineado heredado le daba 33px de alto contra
   los 29px del nombre— así que la caja se ajusta a su propio texto. */
.view-cell__head .badge {
  padding: 2px 8px;
  line-height: 1.4;
}

/* El bloque de progreso acompaña la alineación de su columna, que va a la
   derecha con la fecha. Al pasar a fit-content dejó de llenarla, y sin esto
   quedaba flotando a la izquierda de todo lo demás. */
.project-card__status .meter {
  justify-self: end;
}

/* El historial del proyecto, en dos niveles.
   Un carril vertical atraviesa la lista entera y cada hito es un nodo posado
   encima: eso es lo que hace que se lea como una línea de tiempo y no como una
   tabla de log. Los renglones ya no llevan filete —el carril es la estructura, y
   dos estructuras superpuestas es una de más—, así que lo que separa un hito del
   siguiente es el aire, y el aire se reparte según cuánto pesa cada uno.

   El detalle vive adentro del hito, plegado, y se abre sólo lo que hace falta.
   Sin JavaScript: son <details> nativos.

   Tres pesos, y el nodo es el que los canta antes que el texto:
     · fuerte  (publicado, terminó) → disco lleno y de color: mueven el turno
     · medio   (pidió, contestó, alta) → disco con aro
     · de fondo (sync) → disco chico y apagado, ruido que uno saltea */
.timeline {
  /* Ancho de la columna del carril. Es constante para TODOS los hitos aunque el
     disco cambie de tamaño: si variara, el carril zigzaguearía. */
  --nodo: 2rem;
  --disco: 2rem;
  --icono: 1.1rem;
  --fila-pad: var(--feedo-space-2xs-xs);
  /* Alto de la primera línea del hecho. Con esto se centra el disco contra esa
     línea y no contra el bloque entero, que es lo que descuadra el nodo cuando
     el hito tiene dos renglones. */
  --linea-1: 1.5em;

  list-style: none;
  margin: 0;
  padding: 0;
  font-size: var(--feedo-step-0);
}
.timeline__item {
  position: relative;
}
/* El carril. Va sobre el <li> y no sobre la fila, así que también corre a lo
   largo del detalle abierto: esa continuidad es lo que dice que lo de adentro
   pertenece al hito y no es un hito más. */
.timeline__item::before {
  content: "";
  position: absolute;
  inset-block: 0;
  inset-inline-start: calc(var(--nodo) / 2 - 1px);
  inline-size: 2px;
  background: var(--feedo-line);
}
/* El carril empieza y termina en el centro de su nodo: arriba no hay nada más
   nuevo, abajo no hay nada más viejo, y un tramo suelto prometería lo contrario. */
.timeline__item:first-child::before {
  inset-block-start: calc(var(--fila-pad) + var(--linea-1) / 2);
}
.timeline__item:last-child::before {
  inset-block-end: calc(100% - var(--fila-pad) - var(--linea-1) / 2);
}
.timeline__fila {
  display: grid;
  /* La cuarta columna se reserva aunque el hito no se abra: si sólo existiera
     donde hay flecha, las fechas de los hitos sin detalle quedarían corridas
     respecto de las demás y la columna de la derecha dejaría de ser una columna. */
  grid-template-columns: var(--nodo) minmax(0, 1fr) auto 1rem;
  gap: var(--feedo-space-3xs) var(--feedo-space-xs);
  align-items: start;
  padding-block: var(--fila-pad);
  /* El realce del hover se pasa un poco del texto para que el blanco de la fila
     se sienta clickeable entero, sin correr el carril de lugar. */
  padding-inline: var(--feedo-space-2xs);
  margin-inline: calc(-1 * var(--feedo-space-2xs));
  border-radius: var(--feedo-radius-s);
}
/* El hito con detalle es un resumen desplegable: se comporta como algo que se
   puede abrir, y el que no tiene nada adentro no finge que sí. */
summary.timeline__fila {
  cursor: pointer;
  list-style: none;
}
summary.timeline__fila::-webkit-details-marker {
  display: none;
}
summary.timeline__fila:hover {
  background: color-mix(in srgb, var(--feedo-accent) 7%, transparent);
}
summary.timeline__fila:focus-visible {
  outline: 2px solid var(--feedo-accent);
  outline-offset: 2px;
}

/* ---- el nodo sobre el carril ---- */
.timeline__nodo {
  position: relative;
  z-index: 1;
  justify-self: center;
  display: grid;
  place-items: center;
  inline-size: var(--disco);
  block-size: var(--disco);
  /* Centrado contra la primera línea del hecho, no contra el tope de la fila. */
  margin-block-start: calc((var(--linea-1) - var(--disco)) / 2);
  border: 1px solid var(--feedo-line);
  border-radius: var(--feedo-radius-pill);
  /* Opaco a propósito: el carril le pasa por detrás y tiene que cortarse ahí. */
  background: var(--feedo-canvas);
  color: var(--feedo-ink);
}
.timeline__icono {
  inline-size: var(--icono);
  block-size: var(--icono);
}
/* Los dos hitos que mueven el turno: disco lleno, y el texto en negrita. Son los
   que uno busca cuando reconstruye una vuelta, y tienen que encontrarse sin leer. */
.timeline__item[data-tipo="publicacion"],
.timeline__item[data-tipo="devuelto"] {
  --disco: 2.2rem;
  --icono: 1.2rem;
}
.timeline__item[data-tipo="publicacion"] .timeline__nodo {
  background: var(--feedo-approve);
  border-color: var(--feedo-approve);
  color: var(--feedo-approve-ink);
}
.timeline__item[data-tipo="devuelto"] .timeline__nodo {
  background: var(--feedo-accent);
  border-color: var(--feedo-accent);
  color: var(--feedo-accent-ink);
}
.timeline__item[data-tipo="publicacion"] .timeline__texto,
.timeline__item[data-tipo="devuelto"] .timeline__texto {
  font-weight: 700;
}
/* Lo que dijo el cliente lleva su color de siempre, el mismo con el que aparece
   marcado en la tabla de vistas y del otro lado del prototipo. */
.timeline__item[data-tipo="comentarios"] .timeline__nodo {
  background: color-mix(in srgb, var(--feedo-comment) 12%, var(--feedo-canvas));
  border-color: color-mix(in srgb, var(--feedo-comment) 40%, transparent);
  color: var(--feedo-comment);
}
.timeline__item[data-tipo="comentarios"] .timeline__texto,
.timeline__item[data-tipo="respuestas"] .timeline__texto {
  font-weight: 600;
}
/* El aro punteado dice lo que dice el texto: está escrito y todavía no salió.
   Es el único hito que puede quedar a medio camino, y se ve desde lejos. */
.timeline__item[data-tipo="respuestas"] .timeline__nodo {
  border-style: dashed;
  border-color: color-mix(in srgb, var(--feedo-accent) 50%, transparent);
  color: var(--feedo-accent);
}
.timeline__item[data-tipo="alta"] .timeline__nodo {
  background: var(--feedo-paper-2);
  border-color: transparent;
  color: var(--feedo-muted);
}
/* ---- el hito que todavía no pasó ----
   Publicar era un panel suelto arriba del historial. Es, en realidad, el próximo
   renglón de esta lista: apretar el botón escribe el hito «Cambios publicados»
   justo acá. Por eso comparte nodo, carril, icono y tamaño con el hito en que se
   va a convertir — cuando pase, nada salta de lugar.

   Lo que lo distingue son dos cosas y ninguna es decorativa:
     · el aro punteado, que en esta línea de tiempo ya significa «escrito y
       todavía sin salir» (lo usa «el agente respondió»);
     · el papel blanco, que es la superficie de lo vivo. Todo lo demás en esta
       columna es papel crema, o sea: pasado. */
.timeline__item[data-tipo="pendiente"] {
  --disco: 2.2rem;
  --icono: 1.2rem;
  /* El alto del renglón del título, con el que se centra el nodo. Es el mismo
     1.5em que usa el resto de la lista, pero acá el nodo además tiene que bajar
     lo que la tarjeta padea por arriba. */
  --tarjeta-pad: var(--feedo-space-s);
}
/* Dos columnas y no cuatro: la tarjeta no tiene fecha —no pasó— ni flecha —no se
   abre—, y reservar esas columnas le comía el ancho a los chips. */
.timeline__item[data-tipo="pendiente"] .timeline__fila {
  grid-template-columns: var(--nodo) minmax(0, 1fr);
}
.timeline__item[data-tipo="pendiente"] .timeline__nodo {
  border-style: dashed;
  border-color: var(--feedo-line);
  color: var(--feedo-muted);
  margin-block-start: calc(var(--tarjeta-pad) + (var(--linea-1) - var(--disco)) / 2);
}
/* Con algo para publicar el nodo toma el verde con el que ya se dibuja
   «Cambios publicados»: es el color del hito que está a un clic de existir. */
.timeline__item[data-tipo="pendiente"][data-listo] .timeline__nodo {
  border-color: var(--feedo-approve);
  color: var(--feedo-approve);
}
/* El carril arranca en el centro de ESTE nodo, que quedó más abajo que el de una
   fila normal por el padding de la tarjeta. Sin esto sobra un tramo de carril
   arriba, que prometería un hito más nuevo que no existe. */
.timeline__item[data-tipo="pendiente"]:first-child::before {
  inset-block-start: calc(var(--fila-pad) + var(--tarjeta-pad) + var(--linea-1) / 2);
}
/* Corta donde cortan sus párrafos. Los otros hitos llegan hasta el borde porque
   apoyan la fecha ahí; éste no tiene fecha, y estirado hasta el final dejaba
   media tarjeta vacía a la derecha del botón. */
.proximo {
  --flow-space: var(--feedo-space-2xs);
  max-inline-size: calc(var(--feedo-measure) + 2 * var(--tarjeta-pad));
  padding: var(--tarjeta-pad);
  background: var(--feedo-canvas);
  border: 1px solid var(--feedo-line);
  border-radius: var(--feedo-radius-s);
  min-inline-size: 0;
}
.timeline__item[data-tipo="pendiente"][data-listo] .proximo {
  border-color: color-mix(in srgb, var(--feedo-approve) 35%, transparent);
}
/* Mismo alto de renglón que el resto de la lista, para que el nodo se centre
   contra el título con la misma cuenta que usa cualquier otro hito. */
.proximo__titulo {
  line-height: 1.5;
}
/* El aire antes del botón es mayor que el que separa los párrafos: es lo que
   marca dónde termina la previa y empieza la decisión. */
.proximo form {
  margin-block-start: var(--feedo-space-s);
}

/* El sync es el ruido de fondo: pasa muchas veces y casi nunca es lo que uno vino
   a buscar. Disco chico y todo apagado; conserva el cuerpo de letra para no
   romper el ritmo de la columna, pero deja de competir. */
.timeline__item[data-tipo="sync"] {
  --disco: 1.5rem;
  --icono: 0.95rem;
}
.timeline__item[data-tipo="sync"] .timeline__nodo {
  background: var(--feedo-paper-2);
  border-color: transparent;
  color: var(--feedo-muted);
}
.timeline__item[data-tipo="sync"] .timeline__texto {
  color: var(--feedo-muted);
}
/* El detalle del sync es un commit: se lee carácter por carácter, así que va en
   mono como cualquier otro identificador del proyecto. */
.timeline__item[data-tipo="sync"] .timeline__detalle {
  font-family: var(--feedo-mono);
}

/* ---- el hecho ---- */
.timeline__cuerpo {
  display: flex;
  flex-direction: column;
  gap: var(--feedo-space-4xs);
  min-inline-size: 0;
}
/* `break-word` y no `anywhere`: `anywhere` parte palabras aunque quepan, y en una
   columna angosta dejaba «encabezado» cortado en «encabe / zado». Así sólo se
   parte lo que de verdad no entra, como el link larguísimo de un comentario. */
.timeline__texto {
  line-height: 1.5;
  overflow-wrap: break-word;
}
.timeline__detalle {
  font-size: var(--feedo-step--1);
  color: var(--feedo-muted);
}
.timeline__cuando {
  font-size: var(--feedo-step--1);
  color: var(--feedo-muted);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.timeline__fila > .timeline__cuando {
  grid-column: 3;
  /* La fecha se apoya en la misma línea que el hecho aunque sea más chica. */
  margin-block-start: calc((var(--linea-1) - 1.5em) / 2);
}
.timeline__flecha {
  grid-column: 4;
  inline-size: 1rem;
  block-size: 1rem;
  color: var(--feedo-muted);
  margin-block-start: calc((var(--linea-1) - 1rem) / 2);
  transition: transform 0.15s ease;
}
details[open] > .timeline__fila .timeline__flecha {
  transform: rotate(90deg);
}

/* ---- el detalle, adentro del hito ---- */
/* Sangrado hasta donde arranca el hecho de arriba y sobre su propio papel: la
   sangría dice de quién es y el cambio de superficie dice que se está mirando
   adentro de algo, no un renglón más de la lista. */
.timeline__hijos {
  list-style: none;
  margin: var(--feedo-space-4xs) 0 var(--feedo-space-xs);
  margin-inline-start: calc(var(--nodo) + var(--feedo-space-xs));
  padding: var(--feedo-space-2xs) var(--feedo-space-xs);
  background: var(--feedo-paper-2);
  border-radius: var(--feedo-radius-s);
  display: grid;
  gap: var(--feedo-space-2xs);
}
.timeline__hijo {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 0 var(--feedo-space-xs);
}
.timeline__hijo + .timeline__hijo {
  padding-block-start: var(--feedo-space-2xs);
  border-block-start: 1px solid var(--feedo-line);
}
/* La vista es un nombre de archivo y no se parte: cortar `mis-cursos.html` a la
   mitad obliga a reconstruirlo. Mismo criterio que los chips de publicación. */
.timeline__vista {
  grid-column: 1;
  font-family: var(--feedo-mono);
  font-size: var(--feedo-step--1);
  color: var(--feedo-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Lo dicho es lo único que se lee entero acá adentro, así que conserva el cuerpo
   del hito; lo que baja de jerarquía es todo lo que lo rodea. Se lleva el ancho
   completo —la vista y la fecha se reparten el renglón de arriba— porque
   compartir columna con la fecha lo dejaba en una palabra por línea. */
.timeline__dicho {
  grid-column: 1 / -1;
  line-height: 1.5;
  overflow-wrap: break-word;
}
.timeline__hijo .timeline__cuando {
  grid-row: 1;
  grid-column: 2;
}

@media (prefers-reduced-motion: reduce) {
  .timeline__flecha {
    transition: none;
  }
}

@media (max-width: 40rem) {
  /* En pantalla angosta la fecha baja debajo del hecho: cuatro columnas parten el
     texto en una palabra por línea. El carril y la sangría no se tocan — son lo
     que sostiene la lectura, y achicarlos acá la rompe justo donde más cuesta. */
  .timeline__fila {
    grid-template-columns: var(--nodo) minmax(0, 1fr) auto;
  }
  .timeline__fila > .timeline__cuando {
    grid-row: 2;
    grid-column: 2;
    margin-block-start: 0;
  }
  .timeline__flecha {
    grid-row: 1;
    grid-column: 3;
  }
  /* La tarjeta del próximo hito respira menos: en 320px lo que le sobra al aire
     le falta a los chips, que son nombres de archivo y no se parten. El nodo y
     el arranque del carril se acomodan solos — los dos se calculan desde acá. */
  .timeline__item[data-tipo="pendiente"] {
    --tarjeta-pad: var(--feedo-space-xs);
  }
  /* Adentro del hito pasa lo mismo: el nombre de la vista y la fecha no entran
     juntos en un renglón de 320px sin comerse uno al otro, así que se apilan. */
  .timeline__hijo {
    grid-template-columns: minmax(0, 1fr);
  }
  .timeline__hijo .timeline__cuando {
    grid-row: auto;
    grid-column: 1;
  }
  .timeline__vista {
    white-space: normal;
    overflow-wrap: break-word;
  }
}
