/* Andamio default theme. Dependency-free, driven by brand design tokens
   (CSS variables injected in the layout <head>). WCAG 2.2 AA: visible focus,
   AA contrast from tokens, adequate target sizes, no color-only signals. */

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  font-family: var(--font-sans);
  color: var(--color-text);
  background: var(--color-bg);
  line-height: 1.5;
}
.container { width: 100%; max-width: 1100px; margin: 0 auto; padding: 0 1rem; }

/* ── Focus: always visible, high-contrast ── */
:focus-visible { outline: 3px solid var(--color-focus); outline-offset: 2px; }
a { color: var(--color-primary); }
a:hover { text-decoration: underline; }

/* ── Skip link ── */
.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 1000;
  background: var(--color-primary); color: var(--color-primary-text);
  padding: .6rem 1rem; border-radius: var(--radius);
}
.skip-link:focus { left: .5rem; top: .5rem; }

.visually-hidden {
  position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ── Header / nav ── */
.app-header { background: var(--color-surface); border-bottom: 1px solid var(--color-border); }
/* Full-width bar: the nav content spans edge to edge (brand hard left,
   actions hard right); only the page body keeps the centered .container. */
.navbar { display: flex; align-items: center; gap: 1rem; padding: .5rem 1rem; flex-wrap: wrap; }
.navbar-brand { display: inline-flex; align-items: center; }
/* color drives the inlined default logo's currentColor wordmark, so it recolors
   with the theme (light/dark) exactly like body text. */
.navbar-logo { display: block; color: var(--color-text); }
/* Default-mark wordmark: REAL text (not SVG <text> in a fixed viewBox, which
   clipped long brand names) so any name renders whole and recolors with the
   theme like body text. */
.navbar-wordmark { font-weight: 700; font-size: 1.25rem; margin-left: .5rem; color: var(--color-text); white-space: nowrap; }
/* Optional client dark-mode logo (brand.logo_dark): ship both <img>s and show
   the right one per theme — mirrors the color tokens' behaviour so it follows
   the system preference AND the in-page manual toggle. */
.navbar-logo.logo-dark { display: none; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .navbar-logo.logo-light { display: none; }
  :root:not([data-theme="light"]) .navbar-logo.logo-dark { display: block; }
}
:root[data-theme="dark"] .navbar-logo.logo-light { display: none; }
:root[data-theme="dark"] .navbar-logo.logo-dark { display: block; }
.nav-links { list-style: none; display: flex; gap: .25rem; margin: 0; padding: 0; }
.nav-links a { display: inline-block; padding: .5rem .75rem; border-radius: var(--radius); text-decoration: none; color: var(--color-text); }
.nav-links a:hover, .nav-links a:focus-visible { background: var(--color-bg); }
.nav-actions { margin-left: auto; display: flex; align-items: center; gap: .5rem; }
.nav-user { color: var(--color-muted); font-size: .9rem; }
.nav-toggle { display: none; background: none; border: 1px solid var(--color-border); border-radius: var(--radius); font-size: 1.2rem; padding: .3rem .6rem; color: var(--color-text); min-height: 40px; min-width: 40px; }

.badge { display: inline-block; background: var(--color-surface); border: 1px solid var(--color-border); border-radius: 999px; padding: .05rem .5rem; font-size: .75rem; color: var(--color-muted); }
/* Role badges (nav user chip): one color per known role so staff and admin
   are distinguishable at a glance; the role text always ships too (no
   color-only signal). Unknown roles keep the neutral .badge. AA pairs for
   light mode here, dark-mode pairs alongside the other dark overrides. */
.badge-role-admin { background: #f5f3ff; border-color: #7c3aed; color: #4c1d95; }
.badge-role-staff { background: #ecfeff; border-color: var(--color-info); color: #164e63; }

/* ── Buttons ── */
.btn { display: inline-flex; align-items: center; justify-content: center; gap: .4rem; min-height: 40px; padding: .5rem .9rem; border-radius: var(--radius); border: 1px solid transparent; font: inherit; cursor: pointer; text-decoration: none; }
.btn-primary { background: var(--color-primary); color: var(--color-primary-text); border-color: var(--color-primary); }
.btn-primary:hover { filter: brightness(.94); text-decoration: none; }
.btn-ghost { background: transparent; color: var(--color-text); border-color: var(--color-border); }
.btn-danger { background: var(--color-danger); color: #fff; border-color: var(--color-danger); }
.btn:focus-visible { outline: 3px solid var(--color-focus); outline-offset: 2px; }
form.inline { display: inline; }

/* ── Admin list toolbar (create action + exports at opposite ends) ── */
.list-toolbar { display: flex; align-items: center; gap: .75rem; flex-wrap: wrap; margin: 0 0 1rem; }
.list-toolbar-actions { display: flex; gap: .5rem; flex-wrap: wrap; margin-left: auto; }

/* ── Main / content ── */
.main { padding: 1.5rem 0 3rem; min-height: 60vh; }
h1 { font-size: 1.6rem; margin: 0 0 1rem; }
h2 { font-size: 1.25rem; }

/* ── Alerts ── */
.alert { padding: .75rem 1rem; border-radius: var(--radius); border: 1px solid; margin-bottom: 1rem; }
.alert-success { background: #ecfdf3; border-color: var(--color-success); color: #14532d; }
.alert-danger  { background: #fef2f2; border-color: var(--color-danger); color: #7f1d1d; }
.alert-info    { background: #ecfeff; border-color: var(--color-info); color: #164e63; }
.alert-warning { background: #fefce8; border-color: var(--color-warning); color: #713f12; }

/* ── Forms ── */
.field { margin-bottom: 1rem; max-width: 32rem; }
.field label { display: block; font-weight: 600; margin-bottom: .3rem; }
.field .req { color: var(--color-danger); font-weight: 400; }
.field input, .field select, .field textarea {
  width: 100%; min-height: 40px; padding: .5rem .6rem; font: inherit;
  color: var(--color-text); background: var(--color-bg);
  border: 1px solid var(--color-border); border-radius: var(--radius);
}
.field input[type="checkbox"] { width: auto; min-height: auto; }
.field.has-error input, .field.has-error select, .field.has-error textarea { border-color: var(--color-danger); }
.field-help { color: var(--color-muted); font-size: .85rem; margin: .3rem 0 0; }
.field-error { color: var(--color-danger); font-size: .9rem; margin: .3rem 0 0; font-weight: 600; }
.error-summary { border: 2px solid var(--color-danger); background: #fef2f2; border-radius: var(--radius); padding: .75rem 1rem; margin-bottom: 1.5rem; color: #7f1d1d; }
.error-summary-title { margin: 0 0 .5rem; font-size: 1.05rem; }
.error-summary a { color: #7f1d1d; }

/* ── Tables ── */
.table-wrap { overflow-x: auto; }
table.data { width: 100%; border-collapse: collapse; }
table.data th, table.data td { text-align: left; padding: .6rem .75rem; border-bottom: 1px solid var(--color-border); }
table.data thead th { border-bottom: 2px solid var(--color-border); }
.card { background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius); padding: 1.25rem; }
.stack > * + * { margin-top: 1rem; }
.row-actions { display: flex; gap: .5rem; }

/* ── Paginator (long admin lists; bars above and below the table) ──
   Ships [hidden] and app.js unhides it — without JS the full list renders and
   no empty bar shows. Neutral + primary tokens only, so dark mode re-tints it
   with the palette and AA contrast comes from the token pairs. */
.paginator { display: flex; align-items: center; justify-content: space-between; gap: .75rem; flex-wrap: wrap; margin: .75rem 0; }
/* display:flex would defeat the hidden attribute (pre-JS state). */
.paginator[hidden] { display: none; }
.paginator-range { color: var(--color-muted); font-size: .9rem; }
.paginator-pages { list-style: none; display: flex; flex-wrap: wrap; gap: .25rem; margin: 0; padding: 0; }
.paginator-pages button {
  min-width: 40px; min-height: 40px; padding: .35rem .6rem; font: inherit; cursor: pointer;
  color: var(--color-text); background: var(--color-surface);
  border: 1px solid var(--color-border); border-radius: var(--radius);
}
.paginator-pages button:hover:not(:disabled) { background: var(--color-bg); }
/* The current page is marked with aria-current="page" (state, not color-only:
   the pressed look pairs with the aria state and the range text). */
.paginator-pages button[aria-current="page"] {
  background: var(--color-primary); color: var(--color-primary-text); border-color: var(--color-primary);
}
.paginator-pages button:disabled { color: var(--color-muted); cursor: default; }
.paginator-gap { display: inline-flex; align-items: center; justify-content: center; min-width: 40px; min-height: 40px; color: var(--color-muted); }

/* ── Footer ── */
/* margin-top leaves room for the clock pill's overhanging half;
   position:relative anchors it. */
.app-footer { border-top: 1px solid var(--color-border); background: var(--color-surface); color: var(--color-muted); padding: 1.5rem 0; margin-top: 2.5rem; font-size: .9rem; position: relative; }
/* ── Clock: pill floating centered on the footer's top edge ──
   Neutral tokens only (surface/text/border), so dark mode re-tints it with the
   palette — AA contrast comes from the tokens, same pair as cards/header. */
.clock-pill {
  position: absolute; top: 0; left: 50%; transform: translate(-50%, -50%);
  display: inline-flex; align-items: center; gap: .4rem;
  background: var(--color-surface); color: var(--color-text);
  border: 1px solid var(--color-border); border-radius: 999px;
  padding: .3rem .9rem; line-height: 1.2; white-space: nowrap;
  font-size: .85rem; box-shadow: 0 2px 10px rgba(0, 0, 0, .15);
}
/* display:inline-flex would defeat the hidden attribute (pre-JS state). */
.clock-pill[hidden] { display: none; }
.clock-pill svg { flex: none; }
.clock-time { font-weight: 600; }
/* Full-width bar: uncap the footer's inner .container to match the nav;
   the horizontal padding comes from .container's own padding. */
.app-footer .container { max-width: none; }
.footer-contact span { margin-right: 1rem; }

/* ── Sticky table header (long lists) ──
   The header (and a totals footer) stay put while a long list scrolls. The
   scroll becomes the CONTAINER's, not the page's: position:sticky anchors to
   the nearest scrolling ancestor, and .table-wrap already is one (overflow-x:
   auto makes the Y axis auto too). Anchoring to the page would mean dropping
   that overflow, and with it the horizontal scroll that small screens need
   (WCAG 1.4.10 Reflow). Being container-relative, top is 0 and never depends
   on the header's height, which changes with width. Opt-in: add .table-sticky
   to the wrapper. */
.table-sticky.table-wrap { max-height: calc(100vh - 16rem); overflow-y: auto; }
.table-sticky table.data thead th,
.table-sticky table.data tfoot th,
.table-sticky table.data tfoot td {
  position: sticky;
  z-index: 2;
  /* Own background: without it the rows show THROUGH the header as they pass. */
  background: var(--color-surface);
}
.table-sticky table.data thead th { top: 0; box-shadow: inset 0 -2px 0 var(--color-border); }
.table-sticky table.data tfoot th,
.table-sticky table.data tfoot td { bottom: 0; box-shadow: inset 0 2px 0 var(--color-border); }
/* No inner scroll on short viewports: with little height a sticky header eats
   half of what's visible. */
@media (max-height: 30rem) {
  .table-sticky.table-wrap { max-height: none; }
}

/* ── System diagnostics (/admin/diagnostico) ──
   The "terminal" is deliberately dark in BOTH themes: it is a literal quote of
   the console output and reads as one. That is why its colors are fixed rather
   than tokens — and they are AA-verified against #10151f: text #e2e8f0
   (13.4:1), ✓ #4ade80 (9.8:1), ⚠ #fbbf24 (11.3:1), ✗ #f87171 (6.4:1), step
   name #93c5fd (8.1:1). State is never color-only: the ✓/⚠/✗ mark and the
   badge text carry it. */
.diag-intro { color: var(--color-muted); max-width: 70ch; }
.diag-caja { padding: 0; overflow: hidden; margin-bottom: 1.25rem; }
.diag-cabecera {
  display: flex; flex-wrap: wrap; align-items: center; gap: .5rem;
  padding: .75rem 1rem; border-bottom: 1px solid var(--color-border);
}
.diag-titulo { font-size: 1rem; margin: 0; flex: 1 1 12rem; }
.diag-badge {
  font-size: .8rem; font-weight: 600; padding: .25rem .6rem;
  border-radius: var(--radius); border: 1px solid;
}
.diag-badge-ok    { background: #f0fdf4; color: #14532d; border-color: #15803d; }
.diag-badge-aviso { background: #fffbeb; color: #713f12; border-color: #a16207; }
.diag-badge-error { background: #fef2f2; color: #7f1d1d; border-color: #b91c1c; }
.terminal { background: #10151f; }
.terminal-barra {
  padding: .35rem 1rem; font-size: .75rem; letter-spacing: .02em;
  color: #94a3b8; background: #1b2230; border-bottom: 1px solid #2b3446;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
.terminal-salida {
  margin: 0; padding: 1rem; max-height: 26rem; overflow: auto;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: .8125rem; line-height: 1.55; color: #e2e8f0;
  white-space: pre; tab-size: 4;
}
.term-ok     { color: #4ade80; }
.term-aviso  { color: #fbbf24; }
.term-error  { color: #f87171; }
.term-nombre { color: #93c5fd; font-weight: 600; }
/* Focus has to be visible on the dark ground too, not just the light one. */
.terminal-salida:focus-visible { outline: 3px solid #93c5fd; outline-offset: -3px; }

/* ── Charts (App\Support\Chart: server-rendered inline SVG, no library) ──
   Structural colours come from the tokens so the chart follows the theme; the
   SERIES colours are baked into the SVG by the PHP side, where they are picked
   for >=3:1 against the ground in both themes (WCAG 1.4.11). Colour is never
   the only signal: each series also carries its own marker shape and dash
   pattern, the legend repeats the name, and the data table below carries the
   numbers. */
.chart { margin: 0 0 1.5rem; }
.chart-title { font-weight: 600; margin-bottom: .5rem; }
.chart-svg { width: 100%; height: auto; max-height: 22rem; display: block; }
.chart-grid { stroke: var(--color-border); stroke-width: 1; }
.chart-zero { stroke: var(--color-muted); stroke-width: 1.5; }
.chart-axis { fill: var(--color-muted); font-size: 12px; font-family: var(--font-sans); }
.chart-line { fill: none; stroke-width: 2.5; stroke-linejoin: round; stroke-linecap: round; }
/* The marker's rim is the page ground, so points stay readable where lines cross. */
.chart-dot { stroke: var(--color-surface); stroke-width: 1.5; }
.chart-legend {
  list-style: none; display: flex; flex-wrap: wrap; gap: .35rem 1rem;
  margin: .5rem 0 .75rem; padding: 0; font-size: .9rem;
}
.chart-legend li { display: inline-flex; align-items: center; gap: .4rem; }
/* The swatch repeats the series' SHAPE, not just its colour — that is what
   makes the legend usable in greyscale or photocopied. */
.chart-swatch { width: .8rem; height: .8rem; flex: none; background: var(--chart-color); }
.chart-swatch-circle { border-radius: 50%; }
.chart-swatch-square { border-radius: 2px; }
.chart-swatch-triangle { background: none; width: 0; height: 0; border-left: .45rem solid transparent; border-right: .45rem solid transparent; border-bottom: .8rem solid var(--chart-color); }
.chart-swatch-diamond { border-radius: 2px; transform: rotate(45deg); }
.chart-data caption { text-align: left; color: var(--color-muted); font-size: .85rem; padding-bottom: .35rem; }

/* ── Small screens ── */
@media (max-width: 720px) {
  .nav-toggle { display: inline-block; }
  .nav-links { flex-basis: 100%; flex-direction: column; display: none; }
  .nav-links.open { display: flex; }
  .terminal-salida { font-size: .75rem; }
}

/* ── Dark mode (AA palette) — respects system + manual toggle ──
   Neutral tokens (bg/surface/text/primary/focus) are re-tinted via custom
   properties above. The semantic tokens (danger/success/warning/info) are
   NOT re-tinted globally: they stay at their light-mode (700-level) value
   because .btn-danger etc. pair them with a fixed white foreground that
   only has AA contrast against a dark, saturated color — lightening the
   token for dark mode would break those buttons. Instead, the specific
   places that put a semantic color as text/border directly against the
   dark page/box background get their own AA-verified dark values below. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --color-bg: #0f172a; --color-surface: #1e293b; --color-text: #f1f5f9;
    --color-muted: #cbd5e1; --color-border: #334155;
    --color-primary: #60a5fa; --color-primary-text: #0b1220; --color-focus: #93c5fd;
  }
  :root:not([data-theme="light"]) .field .req,
  :root:not([data-theme="light"]) .field-error { color: #f87171; }
  :root:not([data-theme="light"]) .field.has-error input,
  :root:not([data-theme="light"]) .field.has-error select,
  :root:not([data-theme="light"]) .field.has-error textarea { border-color: #f87171; }
  :root:not([data-theme="light"]) .alert-success { background:#052e1a; color:#bbf7d0; border-color:#4ade80; }
  :root:not([data-theme="light"]) .alert-danger,
  :root:not([data-theme="light"]) .error-summary { background:#3f1d1d; color:#fecaca; border-color:#f87171; }
  :root:not([data-theme="light"]) .alert-warning { background:#422006; color:#fde68a; border-color:#fbbf24; }
  :root:not([data-theme="light"]) .alert-info { background:#083344; color:#a5f3fc; border-color:#22d3ee; }
  :root:not([data-theme="light"]) .error-summary a { color: #fecaca; }
  :root:not([data-theme="light"]) .badge-role-admin { background:#2e1065; color:#ddd6fe; border-color:#a78bfa; }
  :root:not([data-theme="light"]) .badge-role-staff { background:#083344; color:#a5f3fc; border-color:#22d3ee; }
  /* The diagnostics badges sit on the dark card, not inside the terminal:
     they follow the theme. (The terminal itself stays dark on purpose.) */
  :root:not([data-theme="light"]) .diag-badge-ok    { background:#052e1a; color:#bbf7d0; border-color:#4ade80; }
  :root:not([data-theme="light"]) .diag-badge-aviso { background:#422006; color:#fde68a; border-color:#fbbf24; }
  :root:not([data-theme="light"]) .diag-badge-error { background:#3f1d1d; color:#fecaca; border-color:#f87171; }
}
:root[data-theme="dark"] {
  --color-bg: #0f172a; --color-surface: #1e293b; --color-text: #f1f5f9;
  --color-muted: #cbd5e1; --color-border: #334155;
  --color-primary: #60a5fa; --color-primary-text: #0b1220; --color-focus: #93c5fd;
}
:root[data-theme="dark"] .field .req,
:root[data-theme="dark"] .field-error { color: #f87171; }
:root[data-theme="dark"] .field.has-error input,
:root[data-theme="dark"] .field.has-error select,
:root[data-theme="dark"] .field.has-error textarea { border-color: #f87171; }
:root[data-theme="dark"] .alert-success { background:#052e1a; color:#bbf7d0; border-color:#4ade80; }
:root[data-theme="dark"] .alert-danger,
:root[data-theme="dark"] .error-summary { background:#3f1d1d; color:#fecaca; border-color:#f87171; }
:root[data-theme="dark"] .alert-warning { background:#422006; color:#fde68a; border-color:#fbbf24; }
:root[data-theme="dark"] .alert-info { background:#083344; color:#a5f3fc; border-color:#22d3ee; }
:root[data-theme="dark"] .error-summary a { color: #fecaca; }
:root[data-theme="dark"] .badge-role-admin { background:#2e1065; color:#ddd6fe; border-color:#a78bfa; }
:root[data-theme="dark"] .badge-role-staff { background:#083344; color:#a5f3fc; border-color:#22d3ee; }
:root[data-theme="dark"] .diag-badge-ok    { background:#052e1a; color:#bbf7d0; border-color:#4ade80; }
:root[data-theme="dark"] .diag-badge-aviso { background:#422006; color:#fde68a; border-color:#fbbf24; }
:root[data-theme="dark"] .diag-badge-error { background:#3f1d1d; color:#fecaca; border-color:#f87171; }
