/*
 * Expedientes, stone world. Two patterns ported from pretor.ar:
 *
 * - .marco__grid / .marco__card for the case list: --stone-card cards separated by a 1px gap that
 *   reads as a hairline rule rather than as a gutter. See the note on .marco__grid for why the rule
 *   is drawn by the card and not by the container.
 * - .sec__table for movimientos and plazos: a real table above 920px, stacked cards below it, each
 *   td labelled by its own data-label rather than by a column header a phone has no room for.
 */

/* The hairlines are drawn by the CARDS, not by the container.
 *
 * This grid used to paint --line on itself and let a 1px gap show it through, which reads as a
 * hairline rule only while every cell is filled. A firm with one expediente got a card on the left
 * third and the container background as a bare slab across the rest, and any partial last row does
 * the same thing one row tall. An outset 1px shadow per card cannot do that: an empty cell has no
 * card, so it draws nothing. Adjacent shadows meet inside the 1px gap and render as one rule.
 *
 * auto-fit rather than a fixed column count for the same reason: a lone card fills the row instead
 * of leaving two tracks standing. min(100%, 320px) keeps the track from overflowing a 360px phone,
 * which is the width this is designed at. */
.marco__grid {
  list-style: none;
  margin: 1.5rem 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr));
  gap: 1px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
}

.marco__card {
  background: var(--stone-card);
  box-shadow: 0 0 0 1px var(--line);
  padding: clamp(1.4rem, 3vw, 2rem);
}

.marco__title {
  font-family: var(--serif);
  font-weight: 500;
  font-size: var(--step-1);
  line-height: 1.25;
  margin: 0 0 .5rem;
}

.marco__title a {
  color: var(--ink);
  text-decoration: none;
}

.marco__title a:hover {
  color: var(--oxblood);
}

.marco__ref {
  font-family: var(--mono);
  font-size: var(--step--1);
  color: var(--muted);
  margin: 0 0 .9rem;
}

/* Ported from .agent__tag: an uppercase pill in the accent colour, for a status word core gave us. */
.status-tag {
  display: inline-block;
  font-family: var(--sans);
  font-size: .68rem;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--oxblood);
  border: 1px solid color-mix(in srgb, var(--oxblood) 40%, transparent);
  border-radius: 999px;
  padding: .3em .8em;
}

/* Empty and error states: a bordered card rather than a bare paragraph, so "we have nothing to
   show you" and "we could not reach core" each read as a real answer instead of a missing one. */
.state-card {
  margin: 1.5rem 0;
  padding: clamp(2rem, 5vw, 3rem);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--stone-card);
  text-align: center;
  color: var(--muted);
}

.state-card .icon {
  --icon-size: 1.75rem;
  --icon-color: var(--oxblood-soft);
  margin-bottom: .9rem;
}

.state-card p {
  margin: 0;
  max-width: 42ch;
  margin-inline: auto;
}

/*
 * Sincronización de expedientes: one card per judicial portal.
 *
 * Separate cards rather than one row of buttons, because the portals are not interchangeable: each
 * has its own credentials, its own freshness and its own reason for being quiet, and the question a
 * client actually has ("why is there nothing from MEV?") is answered per portal or not at all.
 *
 * Its own grid rather than .marco__grid: those cards are hairline-joined into one slab, which is
 * right for a list of one kind of thing and wrong here, where each portal is a separate standing
 * report. These stay discrete cards with real gaps between them.
 */
.sync-panel {
  margin: 1.5rem 0 2.5rem;
}

.sync-panel__heading {
  font-size: var(--step-1);
  margin: 0;
}

.sync-panel__intro {
  color: var(--muted);
  font-size: var(--step--1);
  margin: .4rem 0 0;
  max-width: 60ch;
}

/* min(100%, 260px) so the track never overflows the 360px phone this is designed at: below that
   width it is one column, and the cards stack. */
.sync-panel__grid {
  list-style: none;
  margin: 1.2rem 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
  gap: .8rem;
}

.sync-portal {
  display: flex;
  flex-direction: column;
  gap: .6rem;
  padding: 1.2rem;
  background: var(--stone-card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.sync-portal__name {
  font-family: var(--serif);
  font-size: var(--step-0);
  font-weight: 500;
  margin: 0;
}

.sync-portal__status {
  display: flex;
  align-items: center;
  gap: .45rem;
  font-family: var(--mono);
  font-size: var(--step--1);
  margin: 0;
}

/* The state is carried by a word first and a colour second: the status label is always rendered, so
   the dot is decoration and a client who cannot separate the hues loses nothing. */
.sync-portal__dot {
  width: .5rem;
  height: .5rem;
  border-radius: 50%;
  flex: none;
  background: var(--muted);
}

.sync-portal[data-status="ok"] .sync-portal__dot {
  background: color-mix(in srgb, green 55%, var(--ink));
}

.sync-portal[data-status="failed"] .sync-portal__dot {
  background: var(--oxblood);
}

.sync-portal[data-status="failed"] {
  border-color: color-mix(in srgb, var(--oxblood) 40%, var(--line));
}

/* A sweep in flight: the one state that is about something happening rather than something that
   happened, so it is the only one that moves. The word carries it either way, the same rule the dot
   already follows, which is why the pulse is the first thing to go when a client asks for less
   motion. */
.sync-portal[data-status="running"] .sync-portal__dot {
  background: var(--gold);
  animation: sync-pulse 1.4s var(--ease) infinite;
}

@keyframes sync-pulse {
  0%, 100% { opacity: .3 }
  50% { opacity: 1 }
}

@media (prefers-reduced-motion: reduce) {
  .sync-portal[data-status="running"] .sync-portal__dot {
    animation: none;
  }
}

.sync-portal__reason {
  font-size: var(--step--1);
  color: var(--oxblood);
  margin: 0;
}

.sync-portal__facts {
  display: flex;
  flex-direction: column;
  gap: .5rem;
  margin: 0;
}

.sync-portal__facts dt {
  font-family: var(--mono);
  font-size: .68rem;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--muted);
}

.sync-portal__facts dd {
  margin: .1rem 0 0;
  font-size: var(--step--1);
}

/* Pushed to the bottom edge so the buttons line up across cards of unequal height: a portal with a
   failure reason is taller than one without, and a ragged row of buttons reads as misalignment
   rather than as content. */
.sync-portal__button {
  margin-top: auto;
  align-self: flex-start;
}

/* The show page's title, reference, status and jurisdiction, as a compact definition list. */
.expediente-meta {
  margin: .75rem 0 0;
  display: flex;
  flex-wrap: wrap;
  column-gap: 1.5rem;
  row-gap: .5rem;
}

.expediente-meta > div {
  display: flex;
  flex-direction: column;
  gap: .2rem;
}

.expediente-meta dt {
  font-family: var(--mono);
  font-size: .7rem;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--muted);
}

.expediente-meta dd {
  margin: 0;
  font-size: var(--step-0);
}

.expediente-actions {
  margin: 1.5rem 0 2rem;
}

/* Partes, as a definition list rather than a plain <li>. */
.expediente-parties {
  margin: .75rem 0 0;
  display: flex;
  flex-direction: column;
  gap: .8rem;
}

.expediente-party {
  padding: .8rem 0;
  border-bottom: 1px solid var(--line);
}

.expediente-party dt {
  font-weight: 600;
}

.expediente-party dd {
  margin: .2rem 0 0;
  color: var(--muted);
  font-size: var(--step--1);
}

/* Ported from .sec__table: stacked cards below 920px, a real table above it. The mobile layout
   stacks label over value rather than placing them side by side, because a movimiento description
   is prose and a side-by-side row is exactly what forces the horizontal scroll this exists to
   avoid. */
.sec__table-wrap {
  margin-top: 1rem;
}

.sec__table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--step--1);
}

.sec__table thead {
  display: none;
}

.sec__table tr {
  display: block;
  background: var(--stone-card);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 1rem 1.2rem;
  margin-bottom: .8rem;
}

.sec__table th[scope="row"] {
  display: block;
  font-family: var(--sans);
  font-weight: 600;
  font-size: var(--step-0);
  color: var(--ink);
  text-align: left;
  margin: 0 0 .7rem;
}

.sec__table td {
  display: flex;
  flex-direction: column;
  gap: .15rem;
  padding: .4rem 0;
  color: var(--ink);
}

.sec__table td:empty {
  display: none;
}

.sec__table td::before {
  content: attr(data-label);
  font-family: var(--mono);
  font-size: .68rem;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--muted);
}

/*
 * Plazos: how a deadline was derived, under the row it belongs to.
 *
 * Stacked rather than inline, because the rule line is three facts joined by separators and a phone
 * would wrap it mid-citation otherwise.
 */
.deadline-source,
.deadline-rule {
  display: block;
}

.deadline-source {
  font-family: var(--mono);
  font-size: .68rem;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--muted);
}

.deadline-rule {
  font-size: var(--step--1);
}

/*
 * Urgency, from core's own band, so the colour here and the alert the client gets on Telegram never
 * disagree. Carried by a left rule rather than a background: these rows become table rows above
 * 920px, where a filled cell would fight the hairline grid.
 *
 * The band is never the only signal. Every row states its días restantes in words, so a client who
 * cannot separate the hues reads the same answer.
 */
.sec__table tr[data-urgency="overdue"],
.sec__table tr[data-urgency="today"],
.sec__table tr[data-urgency="critical"] {
  border-left: 3px solid var(--oxblood);
}

.sec__table tr[data-urgency="soon"] {
  border-left: 3px solid var(--oxblood-soft);
}

.sec__table tr[data-urgency="overdue"] td[data-label="Restante"],
.sec__table tr[data-urgency="today"] td[data-label="Restante"],
.sec__table tr[data-urgency="critical"] td[data-label="Restante"] {
  color: var(--oxblood);
  font-weight: 600;
}

@media (width >= 920px) {
  .sec__table thead {
    display: table-header-group;
  }

  .sec__table tbody {
    display: table-row-group;
  }

  .sec__table tr {
    display: table-row;
    background: none;
    border: 0;
    padding: 0;
    margin: 0;
  }

  .sec__table th,
  .sec__table td {
    display: table-cell;
    padding: .8rem 1rem;
    border-bottom: 1px solid var(--line);
    vertical-align: top;
  }

  .sec__table thead th {
    font-family: var(--mono);
    font-size: .72rem;
    font-weight: 500;
    letter-spacing: .04em;
    color: var(--muted);
    text-align: left;
    border-bottom: 1px solid var(--oxblood);
  }

  .sec__table th[scope="row"] {
    font-size: var(--step--1);
    font-weight: 500;
    margin: 0;
  }

  .sec__table td {
    display: table-cell;
  }

  .sec__table td::before {
    display: none;
  }

  /* The stacked-card rule above hangs on the <tr>, and the table layout here resets tr borders to
     zero. So the urgency mark moves onto the row's first cell, which is the only element still
     drawing a left edge once the cards become rows. */
  .sec__table tr[data-urgency] th[scope="row"] {
    border-left: 3px solid transparent;
    padding-left: .7rem;
  }

  .sec__table tr[data-urgency="overdue"] th[scope="row"],
  .sec__table tr[data-urgency="today"] th[scope="row"],
  .sec__table tr[data-urgency="critical"] th[scope="row"] {
    border-left-color: var(--oxblood);
  }

  .sec__table tr[data-urgency="soon"] th[scope="row"] {
    border-left-color: var(--oxblood-soft);
  }
}

/* The archivo controls on a movimiento row: two links (the original the tribunal filed, and the
   portal's own PDF conversion) plus the portal's declared file kind.
 *
 * Laid out as an inline row that WRAPS rather than a fixed pair, because the kind is a portal string
 * of unknown length and this cell is the narrowest column in the table on a laptop. Wrapping keeps
 * the two links side by side and drops the kind to its own line, instead of squeezing the links.
 *
 * Ordinary links, styled as links: they are two plain GETs that stream a file, so making them look
 * like buttons would promise an interaction they do not have (no state, no confirmation, no undo). */
.movement__archivo {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  column-gap: .75rem;
  row-gap: .25rem;
}

.movement__archivo-link {
  white-space: nowrap;
}

/* Not a badge: the kind is a fact about the file, not a status, and a lawyer reads it to know what
   is about to open. Quiet enough that the two actions stay the emphasis of the cell. */
.movement__archivo-kind {
  color: var(--ink-soft, var(--ink));
  font-size: var(--step--1, .85em);
  text-transform: uppercase;
  letter-spacing: .04em;
}

/* Above the stacked-card breakpoint the cell is a real table cell again, and `display: flex` on a
   <td> takes it out of the table layout, so the column loses its alignment with the header. */
@media (min-width: 920px) {
  .movement__archivo {
    display: table-cell;
  }

  .movement__archivo .movement__archivo-link + .movement__archivo-link {
    margin-left: .75rem;
  }

  .movement__archivo .movement__archivo-kind {
    margin-left: .75rem;
  }
}

/* The way to a first sweep: one select and one button, in place of the four cards that used to say
   nothing about portals the firm has never used. Inside the replaced region, so a portal that has
   just had its first sweep moves from here into the grid on the same poll. */
.sync-panel__start {
  display: flex;
  flex-wrap: wrap;
  align-items: end;
  gap: .75rem;
  margin-block-start: 1.25rem;
}

.sync-panel__start-label {
  flex-basis: 100%;
  color: var(--color-text-subtle);
}

.sync-panel__empty {
  color: var(--color-text-subtle);
}

/* ---------------------------------------------------------------------------------------------
   PRE-66: the case-status colours, the tabs, and the cross-case movement feed.
   --------------------------------------------------------------------------------------------- */

/* One pill per state instead of one pill for every state. The word is still what carries the
   meaning — the label is always rendered — so a client who cannot separate the hues loses nothing,
   which is the rule .sync-portal__dot already follows. Oxblood stays the default, so a status core
   invents later still renders as a pill rather than as unstyled text. */
.status-tag[data-status="active"] {
  color: color-mix(in srgb, green 55%, var(--ink));
  border-color: color-mix(in srgb, green 35%, transparent);
}

.status-tag[data-status="stalled"] {
  color: color-mix(in srgb, var(--gold) 70%, var(--ink));
  border-color: color-mix(in srgb, var(--gold) 45%, transparent);
}

/* Archived and closed are both "nothing is expected of you here", so both go quiet rather than
   competing with the cases that still need work. */
.status-tag[data-status="archived"],
.status-tag[data-status="closed"] {
  color: var(--muted);
  border-color: var(--line);
}

/* Links, not a JavaScript tablist: aria-current is the state, and it is what the styling hangs off,
   so the open tab is said once and read by both a screen reader and the eye. */
.expediente-tabs {
  display: flex;
  gap: .25rem;
  margin: 1.5rem 0 1rem;
  border-bottom: 1px solid var(--line);
  overflow-x: auto;
}

.expediente-tabs__tab {
  flex: none;
  padding: .6rem 1rem;
  font-family: var(--sans);
  font-size: var(--step-0);
  color: var(--muted);
  text-decoration: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  transition: color .2s var(--ease), border-color .2s var(--ease);
}

.expediente-tabs__tab:hover {
  color: var(--ink);
}

.expediente-tabs__tab[aria-current="page"] {
  color: var(--ink);
  font-weight: 600;
  border-bottom-color: var(--oxblood);
}

.feed {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: .75rem;
}

.feed__truncated {
  font-size: var(--step--1);
  color: var(--muted);
  margin: 0 0 .75rem;
}

/* A card per movement rather than a table row: this list mixes expedientes, so each entry has to
   name its own case before it says anything else, and that does not fit a column. */
.feed-movement {
  padding: 1rem 1.1rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--stone-card);
}

.feed-movement__case {
  font-family: var(--serif);
  font-size: var(--step-0);
  margin: 0 0 .35rem;
}

.feed-movement__case a {
  color: var(--ink);
  text-decoration: none;
}

.feed-movement__case a:hover {
  text-decoration: underline;
}

.feed-movement__meta {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  align-items: baseline;
  font-family: var(--mono);
  font-size: var(--step--1);
  color: var(--muted);
  margin: 0 0 .5rem;
}

.feed-movement__kind {
  text-transform: uppercase;
  letter-spacing: .06em;
}

.feed-movement__description {
  margin: 0;
  color: var(--ink);
}

.feed-movement__source {
  display: inline-block;
  margin-top: .5rem;
  font-family: var(--mono);
  font-size: var(--step--1);
  color: var(--muted);
  overflow-wrap: anywhere;
}

/* ---------------------------------------------------------------------------------------------
   PRE-66: "Avisos a tus clientes" on Tu cuenta. A capability the firm does not have yet, shown
   rather than hidden, so the state is legible instead of absent.
   --------------------------------------------------------------------------------------------- */

.channel-soon {
  margin-left: .6rem;
  vertical-align: middle;
  background: color-mix(in srgb, var(--gold) 22%, transparent);
  color: color-mix(in srgb, var(--gold) 70%, var(--ink));
  border: 1px solid color-mix(in srgb, var(--gold) 40%, transparent);
}

.channel-list {
  list-style: none;
  margin: 1rem 0 0;
  padding: 0;
  display: grid;
  gap: .75rem;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
}

.channel-card {
  padding: 1rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--stone-card);
}

.channel-card__name {
  font-family: var(--serif);
  font-size: var(--step-0);
  margin: 0 0 .35rem;
}

/* The word first, the colour second — the same rule .sync-portal__dot follows, for the same
   reason: the status label is always rendered, so the dot is decoration. */
.channel-card__status {
  display: flex;
  align-items: center;
  gap: .45rem;
  font-family: var(--mono);
  font-size: var(--step--1);
  color: var(--muted);
  margin: 0;
}

.channel-card__dot {
  width: .5rem;
  height: .5rem;
  border-radius: 50%;
  flex: none;
  background: var(--muted);
}

.channel-card[data-status="configured"] .channel-card__dot {
  background: color-mix(in srgb, green 55%, var(--ink));
}

.channel-card__reason {
  font-size: var(--step--1);
  color: var(--muted);
  margin: .5rem 0 0;
}

/* Inert on purpose, and it looks it. Styling a disabled control to look pressable is how a lawyer
   ends up clicking at a feature that does not exist and concluding the app is broken. */
.channel-card__connect {
  margin-top: .75rem;
}

.channel-card__connect:disabled {
  opacity: .5;
  cursor: not-allowed;
}

/* The sample thread. A figure with a real caption rather than a bare div, because the caption is
   load-bearing: it is what stops the mock reading as a conversation that happened. */
.channel-preview {
  margin: 1.25rem 0 0;
}

.channel-preview__caption {
  font-size: var(--step--1);
  color: var(--muted);
  margin-bottom: .5rem;
}

.channel-preview__thread {
  padding: .9rem;
  border: 1px dashed var(--line);
  border-radius: var(--radius);
  display: flex;
  flex-direction: column;
  gap: .5rem;
}

.channel-preview__bubble {
  margin: 0;
  padding: .55rem .8rem;
  border-radius: var(--radius-sm);
  font-size: var(--step--1);
  max-width: 85%;
}

.channel-preview__bubble--them {
  align-self: flex-start;
  background: var(--stone-2);
  color: var(--ink);
}

.channel-preview__bubble--us {
  align-self: flex-end;
  background: color-mix(in srgb, green 12%, var(--stone-card));
  border: 1px solid color-mix(in srgb, green 25%, transparent);
  color: var(--ink);
}

/* ---------------------------------------------------------------------------------------------
   PRE-66: "Publicación de escritos" on the expediente page.
   --------------------------------------------------------------------------------------------- */

.filings__list {
  list-style: none;
  margin: 0 0 1.25rem;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: .75rem;
}

.filing {
  padding: 1rem 1.1rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--stone-card);
}

.filing__title {
  font-family: var(--serif);
  font-size: var(--step-0);
  margin: 0 0 .35rem;
}

.filing__meta {
  display: flex;
  flex-wrap: wrap;
  gap: .6rem;
  font-family: var(--mono);
  font-size: var(--step--1);
  color: var(--muted);
  margin: 0;
}

/* A filing that failed is the one state the lawyer has to act on: the escrito is still unpresented
   and the plazo is still running. The word carries it; the border is the second signal. */
.filing[data-status="failed"] {
  border-color: color-mix(in srgb, var(--oxblood) 40%, var(--line));
}

.filing[data-status="failed"] .filing__status {
  color: var(--oxblood);
}

.filing[data-status="submitted"] .filing__status {
  color: color-mix(in srgb, green 55%, var(--ink));
}

.filing__cargo,
.filing__reason {
  font-family: var(--mono);
  font-size: var(--step--1);
  margin: .5rem 0 0;
}

.filing__cargo {
  color: var(--muted);
}

.filing__reason {
  color: var(--oxblood);
}

.filings__empty {
  color: var(--muted);
  margin: 0 0 1.25rem;
}

/* Which portals could publish at all. A quiet list rather than cards: it is the fine print under
   the escritos, not the subject of the section. */
.filings__portals {
  list-style: none;
  margin: 0;
  padding: 0;
  border-top: 1px solid var(--line);
}

.filings__portal {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: .5rem;
  padding: .6rem 0;
  border-bottom: 1px solid var(--line);
  font-size: var(--step--1);
}

.filings__portal-name {
  color: var(--ink);
}

.filings__portal-state {
  color: var(--muted);
  text-align: right;
}

.filings__portal[data-available="true"] .filings__portal-state {
  color: color-mix(in srgb, green 55%, var(--ink));
}
