/* VerifactuCloud — hand-written, no build step, no external requests. */

:root {
  --canvas: #0b0f19;
  --card: #111827;
  --border: #1f2937;
  --text: #e5e7eb;
  --text-dim: #9ca3af;
  --primary: #3b82f6;
  /* A separate, darker blue for solid buttons. White on --primary is 3.68:1,
     below the 4.5:1 WCAG AA needs for text this size — .btn is weight 550 at
     body size, so it is neither bold nor large and the higher bar applies.
     White on this is 5.17:1.

     It cannot be fixed by darkening --primary itself: that is also the link
     colour on a dark canvas, where darkening REDUCES contrast rather than
     improving it. Links are currently 5.21:1 and must stay that way. */
  --primary-solid: #2563eb;
  --primary-solid-hover: #1d4ed8;
  --ok: #10b981;
  --amber: #f59e0b;

  --wrap: 68rem;
  --gap: 1.5rem;

  --font: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

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

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--canvas);
  color: var(--text);
  font-family: var(--font);
  font-size: clamp(1rem, 0.96rem + 0.2vw, 1.075rem);
  line-height: 1.65;
}

h1, h2, h3 { line-height: 1.2; text-wrap: balance; margin: 0 0 0.5em; }
h1 { font-size: clamp(1.9rem, 1.3rem + 2.6vw, 3.1rem); letter-spacing: -0.02em; }
h2 { font-size: clamp(1.35rem, 1.1rem + 1vw, 1.9rem); letter-spacing: -0.01em; }

p { margin: 0 0 1rem; }
p:last-child { margin-bottom: 0; }

a { color: var(--primary); }
a:hover { color: #93c5fd; }

:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 3px;
  border-radius: 2px;
}

.wrap {
  width: min(100% - 2.5rem, var(--wrap));
  margin-inline: auto;
}

/* Skip link — visible only once focused. */
.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--primary);
  color: #fff;
  padding: 0.6rem 1rem;
  z-index: 10;
}
.skip:focus { left: 0; }

/* Header */
.site-header {
  border-bottom: 1px solid var(--border);
  background: rgba(11, 15, 25, 0.9);
}
.header-inner {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem 1rem;
  align-items: center;
  justify-content: space-between;
  padding-block: 0.9rem;
}
.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  font-weight: 650;
  letter-spacing: -0.01em;
  color: var(--text);
  text-decoration: none;
}
.brand-mark { color: var(--primary); display: inline-flex; }

/* margin-inline-start:auto keeps the flags at the END of the row they land on.
   Without it, a header that wraps leaves them alone on the second line pushed
   to the far left, under the logo, reading as a stray control rather than as
   the thing they sit beside when the row is not full. */
.langs { display: flex; gap: 0.25rem; margin-inline-start: auto; }
.lang {
  font-size: 0.875rem;
  /* The label sits ON the flag now that the flag is crisp rather than blurred,
     and white text crosses the Union Jack's white saltire. At 0.48 opacity that
     band lands near #7a7a7a, which is about 3.1:1 against #e5e7eb -- under the
     4.5:1 WCAG asks for normal text. The shadow buys the contrast back without
     muting the flag, which is the thing being asked for. */
  text-shadow: 0 1px 2px rgba(3, 6, 12, 0.9);
  padding: 0.3rem 0.65rem;
  border-radius: 999px;
  text-decoration: none;
  color: var(--text-dim);
}
.lang:hover { color: var(--text); background: var(--card); }
.lang.current { color: var(--text); background: var(--card); border: 1px solid var(--border); }

/* Each language pill carries its own flag, blurred, behind the word.
   Decoration only: the label is still real text, so the control works with
   styles off and reads the same to a screen reader. The lang attribute is the
   hook — it is on both the link and the current <span>, which is also just
   correct, since "Español" IS Spanish text.

   🔴 GRADIENTS, NOT IMAGES, AND NOT BY PREFERENCE. The CSP is img-src 'self'
   with no data:, so a `url("data:image/svg+xml,...")` background is blocked at
   the edge with no visible error — the pill would simply lose its flag on the
   live site while looking right locally. Anything drawn here has to be drawn in
   CSS. The blur is what makes that affordable: at 5px nobody is measuring the
   Union Jack's diagonals, which is just as well, because these are approximate.

   The flag sits on a ::before rather than on the element so the blur cannot
   touch the text — filter on the element would blur its own children. */
.lang {
  position: relative;
  isolation: isolate;
  overflow: hidden;
}
.lang::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  opacity: 0.34;

  /* Soft EDGES, crisp middle. A filter: blur() would smear the whole flag --
     which it did until 2026-09-08, and at 4px the Union Jack read as grey
     mush. A mask fades the flag out towards the rim instead, so the bands stay
     legible where there is room for them and dissolve where they would collide
     with the pill's border. -webkit- first: Safari still needs the prefix. */
  -webkit-mask-image: radial-gradient(ellipse 74% 78% at 50% 50%, #000 45%, transparent 100%);
  mask-image: radial-gradient(ellipse 74% 78% at 50% 50%, #000 45%, transparent 100%);
}
.lang:hover::before, .lang.current::before { opacity: 0.48; }

/* Rojigualda: red, double-height yellow, red. */
.lang[lang="es"]::before {
  background-image: linear-gradient(
    to bottom,
    #AA151B 0 25%, #F1BF00 25% 75%, #AA151B 75% 100%);
}

/* Union Jack, in the order CSS paints it: last listed is furthest back. Red
   cross over white cross over the white saltire over the blue field. The red
   saltire's counterchange is not attempted — it survives neither this size nor
   the blur. */
.lang[lang="en"]::before {
  background-image:
    linear-gradient(to bottom, transparent 44%, #C8102E 44% 56%, transparent 56%),
    linear-gradient(to right,  transparent 46%, #C8102E 46% 54%, transparent 54%),
    linear-gradient(to bottom, transparent 37%, #FFF 37% 63%, transparent 63%),
    linear-gradient(to right,  transparent 42%, #FFF 42% 58%, transparent 58%),
    linear-gradient(to bottom right, transparent 46%, #FFF 46% 54%, transparent 54%),
    linear-gradient(to bottom left,  transparent 46%, #FFF 46% 54%, transparent 54%),
    linear-gradient(#012169, #012169);
}

/* A blurred flag under 0.875rem text is a contrast risk, and reduced-transparency
   is the setting people turn on for exactly this. Honour it by removing the
   decoration rather than dimming it. */
@media (prefers-reduced-transparency: reduce) {
  .lang::before { display: none; }
  .lang { text-shadow: none; }
}

/* Section navigation. A plain wrapped link row, not a sticky bar with a
   toggle: the site has no JavaScript, and a row that wraps onto a second line
   on a narrow screen costs nothing and cannot break. TODO item 20 covers
   making it sticky with a checkbox mobile menu if it is ever worth it. */
.site-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 0.15rem 0.25rem;
  margin-inline-end: auto;
}
/* 0.55rem rather than 0.7rem: the nav went from seven entries to eight on
   2026-09-08, when Product split into Billing App and Partner API. This buys
   the row about 40px. It does NOT stop the header wrapping — the container is
   capped at --wrap, so no screen is ever wide enough to help — and the wrap is
   fine, since this row was always allowed to wrap. Vertical padding is
   untouched, so the hit target is the height it always was. */
.nav-link {
  font-size: 0.9rem;
  padding: 0.35rem 0.55rem;
  border-radius: 0.4rem;
  text-decoration: none;
  color: var(--text-dim);
  white-space: nowrap;
}
.nav-link:hover { color: var(--text); background: var(--card); }
/* External nav entries sit slightly apart and carry an arrow, so leaving the
   site is visible before the click rather than surprising after it. */
.nav-external { display: inline-flex; align-items: center; gap: 0.3rem; }
.nav-external .ext { opacity: 0.7; }
.nav-external:hover .ext { opacity: 1; }

.api-docs-link { margin-top: 1.6rem; }
.api-docs-link a {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  color: var(--primary);
  text-decoration: none;
  font-weight: 550;
}
.api-docs-link a:hover { text-decoration: underline; }

.nav-link.current {
  color: var(--text);
  background: var(--card);
  box-shadow: inset 0 -2px 0 var(--primary);
}

/* The first section of a subpage carries its <h1>, so it needs the breathing
   room the home page gets from the hero. */
.page-lead { padding-top: 0.5rem; }
.page-lead h1 {
  font-size: clamp(1.6rem, 1.2rem + 2vw, 2.3rem);
  letter-spacing: -0.02em;
  margin-bottom: 0.4rem;
}

/* Portal cards: the home page's job once the detail moved onto its own pages.
   The whole card is a target, but only the heading is a real link, so the
   accessible name stays the section name rather than the whole paragraph. */
.card-portal { position: relative; }
.card-portal h3 { margin: 0 0 0.35rem; font-size: 1.05rem; }
.card-portal h3 a { color: var(--text); text-decoration: none; }
.card-portal h3 a::after { content: ""; position: absolute; inset: 0; }
.card-portal:hover { border-color: color-mix(in srgb, var(--primary) 45%, var(--border)); }
.card-portal:hover h3 a { color: var(--primary); }
.card-portal h3 a:focus-visible { outline: 2px solid var(--primary); outline-offset: 3px; }

/* API samples. These are the one place on the site where content is genuinely
   wider than a phone, so the block scrolls inside itself rather than pushing
   the page sideways — check-widths enforces that it never does the latter. */
.api-step { margin-top: 1.6rem; }
.api-step h3 { font-size: 1.02rem; margin-bottom: 0.3rem; }
.api-step > p { color: var(--text-dim); max-width: 65ch; margin-bottom: 0.7rem; }
.api pre {
  overflow-x: auto;
  max-width: 100%;
  margin: 0 0 0.7rem;
  padding: 0.85rem 1rem;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 0.5rem;
}
.api pre code {
  font-family: var(--mono);
  font-size: 0.82rem;
  line-height: 1.55;
  color: var(--text);
  white-space: pre;
}
.states { margin: 0 0 0.8rem; max-width: 70ch; }
.states dt { margin-top: 0.6rem; }
.states dt code {
  font-family: var(--mono);
  font-size: 0.82rem;
  color: var(--primary);
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 0.35rem;
  padding: 0.1rem 0.4rem;
}
.states dd { margin: 0.25rem 0 0; color: var(--text-dim); font-size: 0.94rem; }


/* Sections */
section { padding-block: clamp(2.5rem, 1.5rem + 4vw, 5rem); }

.hero { padding-bottom: clamp(2rem, 1rem + 3vw, 4rem); }
.status-badge {
  display: inline-block;
  font-size: 0.8125rem;
  color: var(--text-dim);
  border: 1px solid var(--border);
  background: var(--card);
  border-radius: 999px;
  padding: 0.3rem 0.85rem;
  margin-bottom: 1.25rem;
}
.lede {
  font-size: clamp(1.05rem, 1rem + 0.5vw, 1.3rem);
  color: var(--text-dim);
  max-width: 46ch;
}
.hero-note {
  margin-top: 1.5rem;
  font-size: 0.9rem;
  color: var(--amber);
}

/* Deadlines */
.deadlines { border-top: 1px solid var(--border); }
.deadline-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
}
.deadline-list li {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 0.6rem;
  padding: 1.1rem 1.25rem;
}
.date {
  display: block;
  font-family: var(--mono);
  font-size: 0.95rem;
  color: var(--amber);
  margin-bottom: 0.35rem;
}
.who { color: var(--text-dim); font-size: 0.925rem; }

.fineprint {
  margin-top: 1.5rem;
  font-size: 0.85rem;
  color: var(--text-dim);
  max-width: 70ch;
}

/* Status + callout */
.status { border-top: 1px solid var(--border); }
.status p { max-width: 70ch; color: var(--text-dim); }
.callout {
  margin-top: 1.75rem;
  border: 1px solid color-mix(in srgb, var(--amber) 35%, var(--border));
  border-left-width: 3px;
  border-left-color: var(--amber);
  background: var(--card);
  border-radius: 0.6rem;
  padding: 1.25rem 1.4rem;
}
.callout p { color: var(--text); max-width: 70ch; }

/* Buttons */
.actions { display: flex; flex-wrap: wrap; gap: 0.75rem; margin-top: 1.75rem; }
.btn {
  display: inline-block;
  padding: 0.65rem 1.25rem;
  border-radius: 0.5rem;
  font-weight: 550;
  text-decoration: none;
  border: 1px solid transparent;
}
.btn-primary { background: var(--primary-solid); color: #fff; }
.btn-primary:hover { background: var(--primary-solid-hover); color: #fff; }
.btn-ghost { border-color: var(--border); color: var(--text); background: var(--card); }
.btn-ghost:hover { border-color: var(--text-dim); color: var(--text); }

.section-note { color: var(--text-dim); max-width: 65ch; margin-bottom: 2rem; }
.emphasis { color: var(--text); font-weight: 550; }

/* Card grids — features and roadmap */
.card-grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
}
.card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 0.6rem;
  padding: 1.25rem 1.35rem;
}
/* Card and column headings size by role, not by tag: on a subpage they are
   <h2> under the page <h1>, on the home page <h3> under a section <h2>. The
   heading level has to follow the document outline; the size must not. */
.card h2, .card h3 { font-size: 1.02rem; margin-bottom: 0.4rem; }
.card p { color: var(--text-dim); font-size: 0.94rem; }
.icon {
  display: inline-flex;
  color: var(--primary);
  margin-bottom: 0.75rem;
}

/* Roadmap cards are visually distinct so they can never read as shipped. */
.card-roadmap { border-style: dashed; }
.pill {
  display: inline-block;
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--amber);
  border: 1px solid color-mix(in srgb, var(--amber) 45%, var(--border));
  border-radius: 999px;
  padding: 0.15rem 0.6rem;
  margin-bottom: 0.7rem;
}

/* How it works */
.steps {
  list-style: none;
  counter-reset: step;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 1.25rem;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
}
.steps li { counter-increment: step; }
.steps h3::before {
  content: counter(step);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.6rem;
  height: 1.6rem;
  margin-right: 0.6rem;
  border-radius: 50%;
  background: var(--primary);
  color: #fff;
  font-size: 0.85rem;
}
.steps h3 { display: flex; align-items: center; font-size: 1.02rem; }
.steps p { color: var(--text-dim); font-size: 0.94rem; }

/* Tested / not yet */
.two-col {
  display: grid;
  gap: 1.5rem;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
}
.col h2, .col h3 { font-size: 1.02rem; }
.ticks, .dots { list-style: none; margin: 0; padding: 0; }
.ticks li, .dots li {
  position: relative;
  padding-left: 1.6rem;
  margin-bottom: 0.6rem;
  color: var(--text-dim);
  font-size: 0.94rem;
}
.ticks li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.45em;
  width: 0.7rem;
  height: 0.38rem;
  border-left: 2px solid var(--ok);
  border-bottom: 2px solid var(--ok);
  transform: rotate(-45deg);
}
.dots li::before {
  content: "";
  position: absolute;
  left: 0.1rem;
  top: 0.55em;
  width: 0.55rem;
  height: 0.55rem;
  border: 2px solid var(--text-dim);
  border-radius: 50%;
}

/* Pricing */
.price {
  font-size: clamp(1.6rem, 1.2rem + 1.6vw, 2.3rem);
  font-weight: 650;
  letter-spacing: -0.02em;
  margin-bottom: 0.5rem;
}
.provisional {
  color: var(--amber);
  max-width: 65ch;
  font-size: 0.94rem;
}
.pricing p:last-child { color: var(--text-dim); max-width: 65ch; }
.includes { max-width: 60ch; font-size: 1.02rem; }

/* The .todo treatment lived here: a marginal note for values we had not decided
   yet. All three are now answered, so the class has no users and is removed
   rather than left as dead CSS. If an unknown ever needs disclosing again, the
   honest pattern is in git — a body-font note with an amber left rule, not a
   monospace warning box, which is what made the first version read as
   unfinished rather than candid. */


/* FAQ — <details> works with JavaScript disabled. */
.faq details {
  border-bottom: 1px solid var(--border);
  padding: 0.9rem 0;
}
.faq summary {
  cursor: pointer;
  font-weight: 550;
  list-style-position: outside;
}
.faq summary::marker { color: var(--text-dim); }
.faq details p { color: var(--text-dim); margin-top: 0.6rem; }

/* Contact */
.contact-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 1rem; }
.contact-list .who { display: block; color: var(--text-dim); font-size: 0.88rem; }

/* Legal pages */
.prose { max-width: 46rem; }
.prose h2 { margin-top: 2rem; }
.prose ul { padding-left: 1.25rem; color: var(--text-dim); }
.prose li { margin-bottom: 0.35rem; }

/* Footer */
.site-footer {
  border-top: 1px solid var(--border);
  padding-block: 2.5rem;
  font-size: 0.875rem;
  color: var(--text-dim);
}
.site-footer .entity { color: var(--text); }
.disclaimer { max-width: 75ch; }
.footer-links { display: flex; flex-wrap: wrap; gap: 1rem; margin-top: 1rem; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
}

/* ---------------------------------------------------------------------------
   Dashboard

   Only /dashboard uses anything below. It is in this file rather than a second
   stylesheet because both containers already serve this one, and a second file
   would mean a second content hash to keep in step for the sake of ~2 KB.

   There is no JavaScript here either, so every state below is a real HTML
   state: :invalid comes from the input's own constraints, and the error and
   notice blocks are rendered by the server or are not on the page at all.
--------------------------------------------------------------------------- */

.panel {
  max-width: 34rem;
  margin: 0 auto;
}
.panel-wide { max-width: 48rem; }

.form { margin: 1.5rem 0 0; }

.field { margin: 0 0 1.1rem; }
.field label {
  display: block;
  margin: 0 0 0.35rem;
  font-weight: 550;
}
.field input {
  width: 100%;
  padding: 0.6rem 0.7rem;
  font: inherit;
  color: var(--text);
  background: var(--canvas);
  border: 1px solid var(--border);
  border-radius: 8px;
}
.field input:hover { border-color: var(--text-dim); }
/* :focus-visible does not fire for a click into a text field in every browser,
   and a text field with no visible focus is unusable by keyboard. */
.field input:focus { outline: 2px solid var(--primary); outline-offset: 1px; }

.field .hint {
  display: block;
  margin: 0.35rem 0 0;
  color: var(--text-dim);
  font-size: 0.9rem;
}

/* Both messages carry a word as well as a colour: colour alone is not a
   distinction for a reader who cannot see it (WCAG 1.4.1). role="alert" on the
   error is what a screen reader announces after a rejected form. */
.alert, .notice {
  margin: 0 0 1.25rem;
  padding: 0.8rem 1rem;
  border: 1px solid;
  border-radius: 10px;
}
.alert {
  border-color: #7f1d1d;
  background: #1f1113;
  color: #fecaca;
}
.notice {
  border-color: #14532d;
  background: #0f1a14;
  color: #bbf7d0;
}
.alert strong, .notice strong { display: block; }

/* The verification banner. Amber rather than red: an unverified account is not
   an error, it is a step that has not happened yet. */
.banner {
  margin: 0 0 1.75rem;
  padding: 1rem 1.15rem;
  border: 1px solid #78350f;
  border-radius: 12px;
  background: #1c1409;
}
.banner h2 { font-size: 1.1rem; margin: 0 0 0.4rem; color: #fde68a; }
.banner p { color: var(--text); }
.banner form { margin: 0.75rem 0 0; }

.facts {
  margin: 1.25rem 0 0;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--card);
  padding: 0.35rem 1.1rem;
}
.facts div {
  display: flex;
  flex-wrap: wrap;
  gap: 0 1rem;
  justify-content: space-between;
  padding: 0.7rem 0;
  border-bottom: 1px solid var(--border);
}
.facts div:last-child { border-bottom: 0; }
.facts dt { color: var(--text-dim); margin: 0; }
.facts dd { margin: 0; font-weight: 550; overflow-wrap: anywhere; }

.form-links {
  margin: 1.25rem 0 0;
  color: var(--text-dim);
}
.form-links a { margin-right: 1rem; }

/* Obligados: the lifecycle states, the counts, and the table.

   The table is the reason scripts/check-widths.sh learned to measure this
   surface (TODO 74). Tabular data does not reflow into a phone, so it scrolls
   inside its own box rather than dragging the whole page sideways with it. */

.badge {
  display: inline-block;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 0.15rem 0.6rem;
  white-space: nowrap;
}
/* Each carries its own word as well as its colour, so the distinction survives
   for a reader who cannot see the difference (WCAG 1.4.1). */
.badge-registered { color: var(--text-dim); }
.badge-authorized { color: #fcd34d; border-color: #78350f; }
.badge-active     { color: #6ee7b7; border-color: #14532d; }
.badge-suspended  { color: #fca5a5; border-color: #7f1d1d; }
/* A state this build has no word for: its own name, and nothing implied. */
.badge-unknown    { color: var(--text-dim); border-style: dashed; }

/* The pre-flight reason, under the badge. It is prose, not a second badge: a
   partner has to read it to know whether the taxpayer is waiting on them or on
   us, and a three-letter chip cannot carry that difference.

   🔴 NO COLOUR SAYS "YOUR FAULT". `note-locked` and `note-retrying` are things
   we owe the partner, not errors they made, so neither gets the red that
   `.badge-suspended` uses — red beside a row invites someone to re-register a
   taxpayer who is already enrolled. Only the wait on their own action is
   highlighted at all. */
.row-note {
  display: block;
  margin-top: 0.35rem;
  max-width: 34em;
  font-size: 0.8rem;
  line-height: 1.45;
  text-transform: none;
  letter-spacing: 0;
  color: var(--text-dim);
}
.row-note.note-waiting { color: #fcd34d; }
.row-note.note-unknown { font-style: italic; }

.counts {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin: 1.25rem 0 0;
  padding: 0;
  list-style: none;
}
.counts li {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--card);
  padding: 0.5rem 0.8rem;
}
.counts .count { font-variant-numeric: tabular-nums; font-weight: 600; }

.table-wrap {
  margin: 1.5rem 0 0;
  overflow-x: auto;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--card);
}
/* tabindex makes the scroll box reachable by keyboard; without a visible focus
   ring that is a trap rather than a feature. */
.table-wrap:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }

.table-wrap table {
  width: 100%;
  border-collapse: collapse;
  /* Below this the columns crush rather than scroll, which is the failure the
     wrapper exists to prevent. */
  min-width: 30rem;
}
.table-wrap th, .table-wrap td {
  text-align: left;
  padding: 0.7rem 1rem;
  border-bottom: 1px solid var(--border);
  vertical-align: top;
}
.table-wrap thead th {
  font-size: 0.85rem;
  color: var(--text-dim);
  font-weight: 600;
  white-space: nowrap;
}
.table-wrap tbody tr:last-child td { border-bottom: 0; }
.table-wrap code { font-family: var(--mono); font-size: 0.92em; }

.states-explainer h2 { font-size: 1.15rem; margin: 0 0 0.75rem; }
.states-list { margin: 0 0 1rem; }
.states-list div {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem 0.9rem;
  padding: 0.45rem 0;
}
.states-list dt { margin: 0; }
.states-list dd { margin: 0; flex: 1 1 18rem; color: var(--text); }

/* Invoices */

.badge-i-queued  { color: var(--text-dim); }
.badge-i-sending { color: #93c5fd; border-color: #1e3a8a; }
.badge-i-filed   { color: #6ee7b7; border-color: #14532d; }
/* `unknown` is not a stage, it is a dead end that needs a human: an attempt was
   made and its outcome is undetermined, and it is deliberately NOT retried
   because the record may already be in AEAT's ledger. */
.badge-i-unknown { color: #fcd34d; border-color: #78350f; }
/* A rejection has no state of its own — the record returns to `queued` to be
   rebuilt and refiled — so this is the only place it is visible. */
.badge-rejected  { color: #fca5a5; border-color: #7f1d1d; }

.attempts {
  font-size: 0.8rem;
  color: var(--text-dim);
  white-space: nowrap;
}

.filters {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: 1rem 0 0;
}
.filter {
  font-size: 0.875rem;
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 0.25rem 0.8rem;
  text-decoration: none;
  color: var(--text-dim);
  background: var(--card);
}
.filter:hover { color: var(--text); border-color: var(--text-dim); }
.filter.current { color: var(--text); border-color: var(--primary); }

.pager {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
  margin: 1rem 0 0;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}

/* A SHA-256 huella is 64 characters and will not wrap on its own, which is
   exactly how a table starts scrolling the whole page sideways. */
.hash {
  overflow-wrap: anywhere;
  word-break: break-all;
  font-size: 0.85em;
}

.qr {
  display: block;
  max-width: 100%;
  height: auto;
  background: #fff;
  padding: 0.5rem;
  border-radius: 8px;
}

/* The app links — the invoicing app and the partner dashboard. Visually
   distinct because they are not pages about the product: they are the product,
   each served by a different container. Outlined rather than solid: they sit in
   a nav of plain text links, and a filled button there would shout louder than
   the page's own call to action. */
.nav-app {
  border: 1px solid color-mix(in srgb, var(--primary) 55%, var(--border));
  border-radius: 999px;
  padding: 0.2rem 0.75rem;
  color: var(--primary);
}
.nav-app:hover { border-color: var(--primary); color: #93c5fd; }
.nav-app.current { border-color: var(--primary); color: var(--text); }

/* .nav-app .nav-tag lived here from 2026-08-26 to 2026-09-08, dimming the
   "pronto" caveat on the facturas pill. It went out with the flag and the
   dictionary key the day the app opened. Nothing replaces it: both app pills
   are now the same thing, which is the point. */
