/* =========================================================================
   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.
   ========================================================================= */

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

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

body {
  margin: 0;
  background: var(--feedo-paper-2);
  color: var(--feedo-ink);
  font-family: var(--feedo-sans);
  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;
}

: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-1);
  letter-spacing: -0.005em;
}
h3 {
  font-size: var(--feedo-step-0);
}

p {
  margin: 0;
}

code {
  /* 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. */
.page[data-width="wide"] {
  --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 ---- */
.topbar {
  padding-block: var(--feedo-space-xs);
  padding-inline: var(--feedo-space-s-m);
  background: var(--feedo-paper);
  border-block-end: 1px solid var(--feedo-line);
}
.topbar__brand {
  font-size: var(--feedo-step-0);
  font-weight: 800;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--feedo-accent);
}
.topbar__nav a {
  font-size: var(--feedo-step--1);
  font-weight: 700;
  color: var(--feedo-muted);
}
.topbar__nav a:hover {
  color: var(--feedo-accent);
  text-decoration: none;
}
.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);
}

/* 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);
}
.auth h1 {
  font-size: var(--feedo-step-2);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--feedo-accent);
}
.auth__sub {
  margin-block: var(--feedo-space-3xs) var(--feedo-space-m);
  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;
}
.meter__lead {
  font-weight: 800;
}
/* 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. */
.meter[data-density="compact"] {
  min-inline-size: 10rem;
}
.meter[data-density="compact"] .meter__legend {
  white-space: nowrap;
}
.meter[data-density="compact"] .meter__bar {
  block-size: 4px;
  max-inline-size: 9rem;
}
.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);
}
/* 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);
}

/* ---- 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);
}

/* =========================================================================
   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. */
.agent-access {
  --flow-space: var(--feedo-space-s);
  padding: var(--feedo-space-m) var(--feedo-space-s-m);
}
.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 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;
}
