/*
 * Shared shell stylesheet for the Vantage dashboard: the rail, top bar, scope banner, drawer and
 * the common table/filter/badge/chip primitives module view fragments are expected to reuse.
 * The :root token block below is the canonical palette for the whole panel; module fragments must
 * not define colors outside it. (The old standalone AuctionHouse dashboard it once had to mirror
 * was absorbed into Vantage — no external sync target remains.)
 */
:root {
  color-scheme: dark;
  --bg: oklch(0.16 0.006 265);
  --surface: oklch(0.185 0.006 265);
  --surface-2: oklch(0.215 0.007 265);
  --border: oklch(0.27 0.008 265);
  --border-strong: oklch(0.34 0.01 265);
  --ink: oklch(0.96 0.004 265);
  --ink-2: oklch(0.74 0.008 265);
  --ink-3: oklch(0.60 0.008 265);
  --accent: oklch(0.80 0.13 85);
  --accent-soft: oklch(0.80 0.13 85 / 0.14);
  --accent-ink: oklch(0.26 0.05 85);
  --red: oklch(0.66 0.19 22);
  --green: oklch(0.78 0.13 165);
  --amber: oklch(0.80 0.13 70);
  --blue: oklch(0.72 0.12 245);
  --purple: oklch(0.72 0.15 305);
  /* Data-viz categorical series (charts only — the interactive accent stays gold). These are the
     panel's original chart hues, teal-led, converted to oklch; Insight resolves them at runtime. */
  --viz-teal: oklch(0.78 0.10 187);
  --viz-blue: oklch(0.72 0.11 259);
  --viz-green: oklch(0.79 0.11 163);
  --viz-red: oklch(0.68 0.13 22);
  --viz-amber: oklch(0.76 0.12 74);
  --viz-purple: oklch(0.72 0.12 300);
  --viz-gray: oklch(0.76 0.008 260);
  --t-xs: 0.786rem; --t-sm: 0.857rem; --t-base: 1rem; --t-md: 1.143rem; --t-lg: 1.357rem;
  --r-sm: 6px; --r: 9px; --r-lg: 13px;
  /* Where a sticky table header comes to rest: right under the sticky top bar, whose real height
     shell.js measures into this on load and on resize. */
  --v-sticky-top: 61px;
  --shadow: 0 1px 2px oklch(0 0 0 / 0.3), 0 8px 24px oklch(0 0 0 / 0.28);
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
}
* { box-sizing: border-box; }
/* text-size-adjust: phones otherwise inflate text on rotation to landscape. */
html { font-size: 14px; -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
body {
  margin: 0; min-height: 100dvh; color: var(--ink);
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  -webkit-font-smoothing: antialiased;
  background: radial-gradient(1100px 600px at 78% -12%, oklch(0.80 0.13 85 / 0.10), transparent 68%), var(--bg);
}
.mono { font-family: ui-monospace, "SF Mono", "Cascadia Code", Consolas, monospace; }
/* Wherever the panel shows a module's identity glyph, resolve it from the platform's colour emoji
   font explicitly.
   Why: system-ui does not carry emoji, so each glyph falls through to whatever the browser picks —
   which for the symbol-block ones (⚖ ⚔ 👁 🛡 🗄) is often a monochrome text font, while the ones from
   the pictograph blocks (💎 🏦 🌊) come back in colour. The result is a rail with some icons flat and
   some coloured, for no reason the operator can see. Naming the emoji font, and keeping the U+FE0F
   variation selector on the glyphs that need it, makes the whole column render one way.
   `emoji` is the CSS generic added for exactly this; the named families are the fallback for
   browsers that do not have it yet.
   Deliberately NOT applied to the chrome glyphs — the top bar's ⤓ ↻ ☰ and the empty state's ◎ are
   line symbols, monochrome by intent and consistent with each other. The split is: a module's
   identity is coloured, the panel's own furniture is not. */
.emoji, .nav .ico, .mcard-ico, .lock {
  font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", "Twemoji Mozilla", emoji, sans-serif;
  font-variant-emoji: emoji;
}
button { font: inherit; cursor: pointer; }
.hide { display: none !important; }

/* ---------- APP SHELL ---------- */
#app { display: grid; grid-template-columns: 232px 1fr; min-height: 100dvh; }
aside.rail { border-right: 1px solid var(--border); background: var(--surface);
  display: flex; flex-direction: column; padding: 16px 12px; gap: 4px; position: sticky; top: 0; height: 100dvh; }
.rail .brand { justify-content: flex-start; font-size: 1.12rem; padding: 6px 10px 14px; }
/* Center the 12px mark inside an 18px slot so it matches the nav rows' .ico width: the wordmark
   then aligns with the nav item labels and the mark no longer juts left of the section headers. */
.rail .brand .mark { margin: 0 3px; }
.brand { display: flex; align-items: center; justify-content: center; gap: 10px; font-size: 1.32rem;
  font-weight: 650; letter-spacing: -0.02em; }
.brand .mark { width: 12px; height: 12px; border-radius: 4px;
  background: linear-gradient(135deg, #f5c542, #d9a520); box-shadow: 0 0 0 5px var(--accent-soft); }
.brand span { color: var(--ink-2); font-weight: 500; }
.nav { display: flex; align-items: center; gap: 10px; padding: 9px 10px; border-radius: var(--r);
  color: var(--ink-2); font-size: var(--t-sm); font-weight: 500; border: 1px solid transparent; user-select: none;
  /* Rail items are real <button>s now (keyboard/AT reachable); neutralize the button UA styles. */
  background: none; width: 100%; text-align: left; font-family: inherit; cursor: pointer; }
.nav:hover { background: var(--surface-2); color: var(--ink); }
.nav.active { background: var(--accent-soft); color: var(--ink); border-color: oklch(0.80 0.13 85 / 0.35);
  box-shadow: inset 2px 0 0 var(--accent); }
.nav:active { transform: translateY(0.5px); }
.nav:focus-visible, .btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.nav .ico { width: 18px; text-align: center; opacity: 0.9; }
/* Sixteen modules plus their group headings no longer fit a 100dvh rail on a laptop, so the module
   list is the part that scrolls; the wordmark above and the session line below stay put. min-height:0
   is what lets a flex child shrink below its content and actually scroll. */
#rail-modules { overflow-y: auto; min-height: 0; scrollbar-width: thin; padding-top: 4px; }
/* Accesos is not a module: a hairline separates it at the bottom, the way Resumen is separated at
   the top by being above the list. */
#rail-access { margin-top: 6px; padding-top: 6px; border-top: 1px solid var(--border); }
/* Both are display:flex/block, so the hidden attribute needs help to win. */
.nav[hidden] { display: none !important; }
.nav.locked { opacity: 0.45; cursor: pointer; }
.nav.locked:hover { background: var(--surface-2); opacity: 0.7; color: var(--ink-2); }
.nav .lock { margin-left: auto; font-size: 0.8rem; opacity: 0.7; }
.nav .count { margin-left: auto; font-size: var(--t-xs); color: var(--ink-3); font-variant-numeric: tabular-nums; }
/* Something in the module needs someone (VantageModule.attention()). Amber: a warning, not an error. */
.count.attn { min-width: 20px; height: 20px; padding: 0 6px; border-radius: 999px; display: inline-grid;
  place-items: center; background: var(--amber); color: var(--accent-ink); font-size: var(--t-xs);
  font-weight: 700; font-variant-numeric: tabular-nums; }
.mcard-head .count.attn { margin-left: auto; }
.rail-foot { margin-top: auto; padding: 10px 8px 4px; border-top: 1px solid var(--border);
  font-size: var(--t-xs); color: var(--ink-3); display: flex; align-items: center; gap: 8px; }

main.content { min-width: 0; display: flex; flex-direction: column; }
header.top { display: flex; align-items: center; gap: 14px; padding: 14px 22px;
  border-bottom: 1px solid var(--border); position: sticky; top: 0; background: oklch(0.16 0.006 265 / 0.86);
  backdrop-filter: blur(8px); z-index: 10; }
header.top h1 { margin: 0; font-size: var(--t-md); font-weight: 640; letter-spacing: -0.01em; }
header.top .sub { color: var(--ink-3); font-size: var(--t-sm); }
.spacer { flex: 1; }
.btn { height: 34px; padding: 0 13px; border-radius: var(--r); border: 1px solid var(--border-strong);
  background: var(--surface-2); color: var(--ink-2); font-size: var(--t-sm); display: inline-flex;
  align-items: center; gap: 7px; transition: border-color .16s, color .16s; }
.btn:hover { border-color: var(--accent); color: var(--ink); }
.btn.accent { background: var(--accent); color: var(--accent-ink); border-color: transparent; font-weight: 620; }
.btn.accent:hover { filter: brightness(1.06); }
.btn.ghost { background: transparent; }
.btn:active { transform: translateY(1px); }
.scope-banner { display: flex; align-items: center; gap: 9px; margin: 0; padding: 9px 22px;
  background: oklch(0.72 0.12 245 / 0.12); border-bottom: 1px solid oklch(0.72 0.12 245 / 0.3);
  color: var(--ink-2); font-size: var(--t-sm); }
.scope-banner b { color: var(--ink); }

.wrap { padding: 22px; }

/* Restricted-access placeholder (non-root session opening Accesos). */
.locked-view { max-width: 440px; margin: 56px auto; text-align: center; animation: rise 0.4s var(--ease) both; }
.locked-view-ico { width: 60px; height: 60px; margin: 0 auto 16px; border-radius: 16px; display: grid; place-items: center;
  font-size: 1.7rem; background: var(--surface); border: 1px solid var(--border); }
.locked-view h3 { margin: 0 0 8px; font-size: var(--t-md); font-weight: 640; }
.locked-view p { margin: 0; color: var(--ink-3); font-size: var(--t-sm); line-height: 1.6; }
@keyframes rise { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }

/* Unlock modal: layer another module's token onto the current session. */
.unlock-backdrop { position: fixed; inset: 0; background: oklch(0 0 0 / 0.5); display: grid; place-items: center;
  padding: 24px; z-index: 60; animation: fade 0.15s var(--ease); }
.unlock-modal { width: 360px; max-width: 100%; background: var(--surface); border: 1px solid var(--border);
  border-radius: 16px; padding: 30px 28px; text-align: center; box-shadow: var(--shadow); animation: rise 0.3s var(--ease) both; }
.unlock-modal .brand { justify-content: center; font-size: 1.14rem; gap: 8px; }
.unlock-modal .unlock-name { color: var(--ink); font-weight: 650; }
.unlock-lead { margin: 10px 0 20px; color: var(--ink-2); font-size: var(--t-sm); line-height: 1.5; }
/* Segmented 6-cell code field (token or 2FA code), matching the main login page. */
.code { position: relative; width: 100%; height: 56px; cursor: text; overflow: hidden; margin-bottom: 4px;
  background: var(--bg); border: 1px solid var(--border-strong); border-radius: 11px;
  transition: border-color .16s var(--ease), box-shadow .16s var(--ease); }
.code:focus-within { border-color: var(--accent); box-shadow: 0 0 0 4px var(--accent-soft); }
.code input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; padding: 0; border: 0;
  background: transparent; color: transparent; caret-color: transparent; outline: none; font-size: 16px; }
.cells { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; gap: 9px; pointer-events: none; }
.cell { flex: 0 0 auto; min-width: 15px; text-align: center; line-height: 1;
  font-family: ui-monospace, "SF Mono", "Cascadia Code", Consolas, monospace;
  font-size: 1.7rem; font-weight: 650; font-variant-numeric: tabular-nums; color: var(--ink-3); }
.cell.filled { color: var(--ink); }
.cell.active { color: var(--accent); position: relative; }
.cell.active::after { content: ''; position: absolute; left: 50%; bottom: 9px; width: 15px; height: 2px;
  transform: translateX(-50%); background: var(--accent); border-radius: 1px; animation: blink 1.1s steps(2, start) infinite; }
@keyframes blink { 50% { opacity: 0; } }
.unlock-submit { width: 100%; height: 46px; justify-content: center; margin-top: 12px; }
.unlock-error { min-height: 18px; margin-top: 10px; color: var(--red); font-size: var(--t-sm); }
.unlock-cancel { margin-top: 8px; background: none; border: 0; color: var(--ink-3); font-size: var(--t-sm); cursor: pointer; }
.unlock-cancel:hover { color: var(--ink); }
@keyframes fade { from { opacity: 0; } to { opacity: 1; } }

/* pills / chips */
.pill { display: inline-flex; align-items: center; gap: 6px; padding: 3px 9px; border-radius: 999px;
  font-size: var(--t-xs); font-weight: 600; border: 1px solid var(--border-strong); color: var(--ink-2); white-space: nowrap; }
.pill .d { width: 7px; height: 7px; border-radius: 50%; }
.chip { display: inline-flex; align-items: center; gap: 6px; padding: 3px 9px; border-radius: var(--r-sm);
  font-size: var(--t-xs); font-weight: 600; background: var(--surface-2); border: 1px solid var(--border-strong); }
.chip .d { width: 7px; height: 7px; border-radius: 50%; }
.badge { display: inline-flex; padding: 2px 8px; border-radius: 6px; font-size: var(--t-xs); font-weight: 650; }

/* ---------- SHARED MODULE PRIMITIVES ----------
   Extracted from the module view fragments (each pattern appeared in 3+ modules). New module views
   should use these instead of redefining local copies; module-specific variants override on top. */
.toolbar { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; margin-bottom: 14px; }
.tabs { display: inline-flex; padding: 3px; gap: 2px; background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r); overflow-x: auto; max-width: 100%; scrollbar-width: none; }
.tab { appearance: none; border: 0; background: transparent; color: var(--ink-2); height: 32px; padding: 0 14px;
  border-radius: var(--r-sm); font-size: var(--t-sm); font-weight: 550; white-space: nowrap;
  transition: background .16s var(--ease), color .16s var(--ease); }
.tab:hover { color: var(--ink); }
.tab[aria-selected="true"] { background: color-mix(in oklch, var(--accent) 16%, var(--surface-2)); color: var(--ink);
  box-shadow: inset 0 0 0 1px color-mix(in oklch, var(--accent) 35%, transparent); }
.pager { display: flex; align-items: center; justify-content: center; gap: 14px; margin-top: 16px; }
.pager .info { color: var(--ink-3); font-size: var(--t-sm); }
.empty { display: grid; place-items: center; gap: 10px; padding: 72px 24px; text-align: center; }
.empty .glyph { width: 44px; height: 44px; border-radius: var(--r-lg); display: grid; place-items: center;
  background: var(--surface-2); border: 1px solid var(--border-strong); color: var(--ink-3); font-size: 20px;
  /* ◎ and ⚠ share this slot; pin text presentation so the browser cannot turn one of them into a
     colour emoji and leave the pair mismatched. */
  font-variant-emoji: text; }
.empty h3 { margin: 0; font-size: var(--t-md); }
.empty p { margin: 0; color: var(--ink-3); font-size: var(--t-sm); max-width: 360px; }
/* Tinted status tag: colored via `color` (semantic token) — bg/border derive from currentColor. */
.tag { display: inline-flex; align-items: center; gap: 6px; padding: 2px 9px 2px 7px; border-radius: 999px;
  font-size: var(--t-xs); font-weight: 650; letter-spacing: 0.01em; text-transform: uppercase; white-space: nowrap;
  border: 1px solid color-mix(in oklch, currentColor 30%, transparent);
  background: color-mix(in oklch, currentColor 12%, transparent); color: var(--ink-2); }
.dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex: 0 0 auto; }
dl.detail { display: grid; grid-template-columns: auto 1fr; gap: 12px 16px; align-items: baseline; margin: 14px 0 0; }
dl.detail dt { color: var(--ink-3); font-size: var(--t-xs); text-transform: uppercase; letter-spacing: 0.05em; }
dl.detail dd { margin: 0; color: var(--ink); font-size: var(--t-sm); word-break: break-word; }
.meta { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-top: 8px; color: var(--ink-2); font-size: var(--t-sm); }
.sep { width: 3px; height: 3px; border-radius: 50%; background: var(--ink-3); }
.subhead { margin: 22px 0 10px; color: var(--ink-3); font-size: var(--t-xs); text-transform: uppercase; letter-spacing: 0.05em; }
/* Skeleton shimmer (promoted from the per-module copies). Views build rows of .bar with inline widths. */
.skeleton .bar { height: 10px; border-radius: var(--r-sm);
  background: linear-gradient(90deg, var(--surface-2) 25%, color-mix(in oklch, var(--surface-2) 60%, var(--ink)) 37%, var(--surface-2) 63%);
  background-size: 400% 100%; animation: shimmer 1.4s ease infinite; }
@keyframes shimmer { 0% { background-position: 100% 0; } 100% { background-position: -100% 0; } }
/* KPI tile (one implementation for insight/treasury/gemstore analytics; modules may override the grid). */
.kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 12px; margin-bottom: 14px; }
.kpi { background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-lg); padding: 14px 16px; }
.kpi .k { font-size: var(--t-xs); text-transform: uppercase; letter-spacing: 0.04em; color: var(--ink-3); }
.kpi .v { font-size: var(--t-lg); font-weight: 650; margin-top: 5px; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
.kpi .sub { font-size: var(--t-xs); color: var(--ink-3); margin-top: 3px; }
/* Labeled filter: wrap a toolbar control in .f with a visible .fl label so field identity survives typing.
   .fgroup brackets controls that form one logical unit (e.g. X/Z/Radio coordinates). */
.f { display: flex; flex-direction: column; gap: 5px; }
/* Same trap as .filters[hidden]: the display:flex above would beat the hidden attribute. */
.f[hidden] { display: none; }
.f > .fl { font-size: 0.72rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; color: var(--ink-3); }
.fgroup { display: flex; gap: 6px; align-items: flex-end; padding: 6px; margin: -6px 0;
  border: 1px dashed var(--border); border-radius: var(--r); }

/* OVERVIEW grid (module summary cards) */
.ov-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(268px, 100%), 1fr)); gap: 14px; }
.mcard { background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-lg);
  padding: 16px; display: flex; flex-direction: column; gap: 12px;
  transition: border-color .16s var(--ease), transform .16s var(--ease), box-shadow .16s var(--ease);
  cursor: pointer; animation: rise 0.4s var(--ease) both;
  /* Cards are real <button>s (keyboard/AT reachable); neutralize UA styles. */
  font: inherit; color: inherit; text-align: left; width: 100%; }
.mcard:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.mcard:hover { border-color: var(--border-strong); transform: translateY(-3px);
  box-shadow: 0 10px 30px oklch(0 0 0 / 0.28); }
.mcard.locked { opacity: 0.5; cursor: pointer; }
.mcard.locked:hover { transform: translateY(-2px); opacity: 0.75; border-color: var(--border-strong); box-shadow: none; }
.mcard-head { display: flex; align-items: center; gap: 12px; }
.mcard-headtext { flex: 1; min-width: 0; }
.mcard-lock { font-size: 1rem; opacity: 0.7; }
.mcard-ico { width: 34px; height: 34px; border-radius: 9px; display: grid; place-items: center;
  font-size: 1.1rem; background: var(--surface-2); border: 1px solid var(--border-strong); }
.mcard-title { font-weight: 620; font-size: var(--t-base); }
.mcard-sub { color: var(--ink-3); font-size: var(--t-xs); }
.mcard-metrics { display: flex; gap: 18px; }
.metric .n { font-size: 1.42rem; font-weight: 680; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
.metric .k { font-size: var(--t-xs); color: var(--ink-3); }
.mcard-foot { display: flex; align-items: center; justify-content: space-between;
  font-size: var(--t-xs); color: var(--ink-3); border-top: 1px solid var(--border); padding-top: 10px; }

/* filters + table (faithful to current module panels). Every <select> in a module panel — not just
   the ones inside .filters — gets the house look: soft border, small radius, and a custom flat
   chevron instead of the native OS dropdown arrow (matches how the standalone panels styled all
   selects globally). */
/* Controls align on their bottom edge: labeled .f columns are taller than bare buttons/selects,
   so centering left the Buscar button floating mid-bar. */
.filters { display: flex; flex-wrap: wrap; gap: 10px; align-items: flex-end; margin-bottom: 16px; }
/* The hidden attribute must win over the display:flex above (equal specificity, later rule would lose),
   so a toggled-off filter bar (e.g. Argus' inactive Detecciones/Papelera tab) actually hides instead of
   showing both bars at once. */
.filters[hidden] { display: none; }
/* The shared Periodo control wraps its select and its two custom-range fields so a bar with two of
   them keeps each pair together; display:contents lets the three cells still lay out as siblings. */
.v-period-group { display: contents; }
/* Same trap as .filters[hidden]: display:contents above would beat the hidden attribute, so a tab
   that does not offer a period (OrderHouse's "Activos") could not hide the control. */
.v-period-group[hidden] { display: none; }
select, .filters input { height: 34px; background: var(--surface); color: var(--ink);
  border: 1px solid var(--border); border-radius: var(--r-sm); padding: 0 11px; font: inherit;
  font-size: var(--t-sm); outline: none; transition: border-color .16s var(--ease), box-shadow .16s var(--ease); }
select { appearance: none; padding-right: 30px;
  background-image: linear-gradient(45deg, transparent 50%, var(--ink-3) 50%),
                    linear-gradient(135deg, var(--ink-3) 50%, transparent 50%);
  background-position: calc(100% - 15px) 15px, calc(100% - 10px) 15px;
  background-size: 5px 5px, 5px 5px; background-repeat: no-repeat; }
select:focus, .filters input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
/* ---------- ACTIVE-FILTER CHIPS ----------
   One chip per filter the operator has actually set, each removing its own. Ledger reaches eleven
   filters in one flat bar with nothing saying which are on; this is the answer to that. Absent
   entirely when nothing is filtered, so the simple case costs no vertical space. */
[data-v-chips] { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: -6px 0 14px; }
[data-v-chips][hidden] { display: none; }
.v-chip { padding-right: 4px; gap: 5px; }
.v-chip-k { color: var(--ink-3); font-weight: 600; }
.v-chip-k::after { content: ':'; }
.v-chip-x { border: 0; background: none; color: var(--ink-3); font-size: var(--t-xs); line-height: 1;
  padding: 2px 4px; border-radius: var(--r-sm); cursor: pointer; }
.v-chip-x:hover { color: var(--ink); background: var(--surface); }
.v-chip-x:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.v-chip-clear { height: 26px; padding: 0 10px; font-size: var(--t-xs); border-color: transparent; }

/* Custom Periodo range: revealed only when the select is on "Personalizado…". */
.v-period-custom input { height: 34px; background: var(--surface); color: var(--ink);
  border: 1px solid var(--border); border-radius: var(--r-sm); padding: 0 9px; font: inherit;
  font-size: var(--t-sm); outline: none; color-scheme: dark; }
.v-period-custom input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }

/* ---------- TABLE TOOLS: column picker + sort ---------- */
.table-tools { display: flex; align-items: center; gap: 12px; margin-bottom: 8px; }
.v-cols { position: relative; }
.v-cols-btn { height: 28px; padding: 0 10px; font-size: var(--t-xs); }
.v-cols-menu { position: absolute; z-index: 20; top: calc(100% + 6px); left: 0; min-width: 190px;
  background: var(--surface); border: 1px solid var(--border-strong); border-radius: var(--r);
  box-shadow: var(--shadow); padding: 8px; display: flex; flex-direction: column; gap: 2px; }
.v-cols-menu[hidden] { display: none; }
.v-col-opt { display: flex; align-items: center; gap: 8px; padding: 5px 7px; border-radius: var(--r-sm);
  font-size: var(--t-sm); color: var(--ink-2); cursor: pointer; user-select: none; }
.v-col-opt:hover { background: var(--surface-2); color: var(--ink); }
.v-col-opt input { accent-color: var(--accent); }
/* Honest about what a client-side sort covers; see bindSorting() in shell.js. */
.v-sort-note { color: var(--ink-3); font-size: var(--t-xs); }
.v-sort-note[hidden] { display: none; }
/* The column picker toggles cells with the hidden attribute; table cells need telling. */
th[hidden], td[hidden] { display: none !important; }
thead th.sortable { cursor: pointer; user-select: none; }
thead th.sortable:hover { color: var(--ink-2); }
thead th.sortable:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
thead th.sorted-asc::after { content: ' ↑'; color: var(--accent); }
thead th.sorted-desc::after { content: ' ↓'; color: var(--accent); }

/* ONE vertical scroll on the page, never two.
   The table used to scroll inside its own box, which is the classic way to make `thead th` stick —
   sticky resolves against the nearest scrolling ancestor. It worked, and it cost more than it gave:
   a second scrollbar next to the page's own, and a list that jumped back to its first row every time
   "Cargar más" replaced the rows (a scrollport's scrollTop resets when its content is rewritten).
   So the wrap does not scroll vertically at all. The page is the only thing that scrolls, the header
   sticks against the VIEWPORT instead — offset by --v-sticky-top so it lands under the top bar rather
   than beneath it — and appending rows leaves the operator exactly where they were reading.
   Horizontal is the one case that still needs its own scrollport (Ledger's eleven columns do not fit
   a laptop), and `overflow` cannot be split per axis: declaring overflow-x alone computes overflow-y
   to auto too. So it is applied only to the tables that are actually too wide, by measurement — see
   markWideTables() in shell.js. Those tables lose the sticky header; every other table keeps it. */
/* contain: inline-size keeps the table's width out of the wrap's own intrinsic size. Without it a wrap
   inside a grid or flex card hands its table's full width up to the track, the card grows past the
   screen on a phone, and markWideTables() — which compares the table with the wrap — sees nothing too
   wide. With it the wrap is always as wide as its container and a long table overflows it, as intended.
   width: 100% because a contained box has no content width of its own to shrink-wrap to, so in a
   flex row it would otherwise collapse to nothing. */
.table-wrap { border: 1px solid var(--border); border-radius: var(--r-lg); background: var(--surface);
  overflow: visible; contain: inline-size; width: 100%; }
/* No max-height here on purpose: with nothing constraining the height there is nothing to scroll
   vertically, so this adds a horizontal scrollbar and no second vertical one. */
.table-wrap.is-wide { overflow-x: auto; overscroll-behavior-x: contain; }
table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: var(--t-sm); }
thead th { text-align: left; padding: 11px 14px; font-size: var(--t-xs); font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.03em; color: var(--ink-3); background: var(--surface-2);
  border-bottom: 1px solid var(--border); position: sticky; top: var(--v-sticky-top, 0); z-index: 2; }
/* Losing the sticky header has to be said out loud, because sticky does not stop being sticky just
   because the wrap cannot scroll vertically. Once .is-wide adds overflow-x (and overflow-y computes to
   auto with it), the wrap becomes the nearest scrollport, so top: var(--v-sticky-top) resolves against
   IT rather than the viewport: the header parks 61px below its own start, on top of the first row.
   Static is what "those tables lose the sticky header" actually means. */
.table-wrap.is-wide thead th { position: static; }
tbody td { padding: 11px 14px; border-bottom: 1px solid var(--border); color: var(--ink-2); }
tbody tr:last-child td { border-bottom: 0; }
tbody tr.clickable:hover { background: var(--surface-2); cursor: pointer; }
/* Rows are focusable (shell.js stamps tabindex) — give keyboard users the same affordance as hover. */
tbody tr.clickable:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; background: var(--surface-2); }
/* The cell itself: every module writes <td class="name">. */
td.name { color: var(--ink); font-weight: 550; }
/* House rule: every column is left-aligned under its (left) header — numeric columns included. Keep
   the tabular monospace on numbers, just don't right-align them. */
td.num { text-align: left; font-variant-numeric: tabular-nums; font-family: ui-monospace, Consolas, monospace; }
/* Kept as a no-op (left) so existing th/td class="right" markup needs no churn while everything stays
   left-aligned. */
.right { text-align: left; }
/* Tabular monospace WITHOUT forcing right-alignment — for date / relative-time columns that read
   left-aligned under a left header but still want aligned, evenly-spaced digits. */
.tnum { font-variant-numeric: tabular-nums; font-family: ui-monospace, Consolas, monospace; }
.muted { color: var(--ink-3); }
/* Bare `dim` utility: some fragments (accountwipe, treasury) tag cells class="dim" expecting the muted
   ink, but only insight defined it locally — so those cells never dimmed. Define it once here, same value
   as .muted. Module-scoped classes like .ag-dim/.ax-dim are a different token and are unaffected. */
.dim { color: var(--ink-3); }

/* access / token admin panels (Task 10 fills the content, styles shared here)
   minmax(0, …) and min-width: 0, not a bare 1fr: a 1fr track never shrinks below its content's
   min-content width, so a long table stretched the column — and the page — instead of overflowing its
   own wrap. Worse, markWideTables() then measured a wrap as wide as its table and never marked it. */
.grid2 { display: grid; grid-template-columns: 340px minmax(0, 1fr); gap: 20px; align-items: start; }
.grid2 > * { min-width: 0; }
.rail-toggle { display: none; }
.visually-hidden { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
@media (max-width: 900px) {
  .grid2 { grid-template-columns: minmax(0, 1fr); }
  #app { grid-template-columns: minmax(0, 1fr); }
  /* The rail becomes an overlay drawer instead of vanishing: without this the phone had no navigation. */
  aside.rail { display: none; }
  .rail-toggle { display: inline-flex; }
  #app.rail-open aside.rail { display: flex; position: fixed; inset: 0 auto 0 0; width: 260px; max-width: 84vw;
    z-index: 55; box-shadow: var(--shadow); }
  /* A scrim behind the open rail. It belongs to #app, so a tap on it reaches #app itself and shell.js
     closes the rail — before this the only way out was picking an entry, as the rail covers the ☰. */
  #app.rail-open::after { content: ''; position: fixed; inset: 0; z-index: 54; background: oklch(0 0 0 / 0.5);
    animation: fade 0.15s var(--ease); }
}
/* The filter fold button only exists for phones (below); on wider screens every bar stays open. */
.v-filters-toggle { display: none; }
/* Phones. Everything above is laid out for a laptop first; these rules only take space back and stop a
   fixed width from pushing the page sideways. */
@media (max-width: 600px) {
  header.top { gap: 8px; padding: 10px 14px; }
  /* The title alone names the view; the subtitle wrapped it onto four lines beside four buttons. */
  header.top .sub { display: none; }
  .wrap { padding: 14px; }
  .kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
  .kpi { padding: 12px; }
  .kpi .v { font-size: var(--t-md); overflow-wrap: anywhere; }
  .filters > *, .f, .fgroup { min-width: 0; max-width: 100%; }
  .filters input, .filters select, .f input, .f select { max-width: 100%; }
  .fgroup { flex-wrap: wrap; }
  .table-tools, .pager { flex-wrap: wrap; }
  .unlock-backdrop { padding: 16px; }
  /* Long filter bars fold behind a button (foldFilterBars() in shell.js). */
  .v-filters-toggle { display: inline-flex; margin-bottom: 12px; }
  .v-filters-toggle[aria-expanded="true"] { border-color: var(--accent); color: var(--ink); }
  .v-filters-toggle[hidden] { display: none; }
  .filters.v-foldable:not(.v-open) { display: none; }
}
/* Touch input: honor the 44px target floor without loosening the desktop density. */
@media (pointer: coarse) {
  .btn, .tab, select, .filters input { min-height: 44px; }
  .nav { padding: 12px 10px; }
  /* iOS zooms the page into any field typed in below 16px and leaves it zoomed. */
  select, textarea, input:not([type="checkbox"]):not([type="radio"]) { font-size: 16px; }
}
.panel { background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-lg); padding: 18px; }
/* After the base rule on purpose: inside the phones block above, this later .panel won the padding back. */
@media (max-width: 600px) { .panel { padding: 14px; } }
.panel h3 { margin: 0 0 4px; font-size: var(--t-base); font-weight: 620; }
.panel p.hint { margin: 0 0 16px; font-size: var(--t-xs); color: var(--ink-3); line-height: 1.5; }
.field { margin-bottom: 14px; }
.field > label { display: block; font-size: var(--t-xs); font-weight: 600; text-transform: uppercase;
  letter-spacing: 0.04em; color: var(--ink-3); margin-bottom: 7px; }
.seg { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; }
.opt { padding: 9px 10px; border-radius: var(--r); border: 1px solid var(--border-strong);
  background: var(--surface-2); font-size: var(--t-sm); color: var(--ink-2); display: flex; align-items: center;
  gap: 8px; user-select: none; }
.opt.sel { border-color: var(--accent); background: var(--accent-soft); color: var(--ink); }
.opt .box { width: 15px; height: 15px; border-radius: 4px; border: 1px solid var(--border-strong);
  display: grid; place-items: center; font-size: 0.7rem; }
.opt.sel .box { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
select.full, input.full { width: 100%; height: 36px; background-color: var(--bg); color: var(--ink);
  border: 1px solid var(--border-strong); border-radius: var(--r); padding: 0 11px; font: inherit; font-size: var(--t-sm); outline: none; }
select.full { padding-right: 30px; }
textarea.full { width: 100%; min-height: 76px; resize: vertical; background-color: var(--bg); color: var(--ink);
  border: 1px solid var(--border-strong); border-radius: var(--r); padding: 9px 11px; font: inherit;
  font-size: var(--t-sm); line-height: 1.5; outline: none; }
input.full:focus, select.full:focus, textarea.full:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
/* After the .full rules on purpose: select.full and textarea.full outrank the plain touch rule above, and
   iOS zooms the page into any field typed in below 16px. */
@media (pointer: coarse) { select.full, textarea.full, input.full { font-size: 16px; } }
.minted { margin-top: 6px; padding: 16px; border-radius: var(--r); border: 1px dashed var(--accent);
  background: var(--accent-soft); text-align: center; }
.minted .tok { font-family: ui-monospace, Consolas, monospace; font-size: 1.9rem; font-weight: 680;
  letter-spacing: 0.12em; color: var(--ink); }
.minted .meta { margin-top: 8px; font-size: var(--t-xs); color: var(--ink-2); }
.empty-mint { color: var(--ink-3); font-size: var(--t-sm); text-align: center; padding: 26px 0; }
/* A panel whose title block shares its row with an action; the action drops below on a narrow screen. */
.panel-head { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 4px 12px; }
.panel-head > div { flex: 1 1 260px; min-width: 0; }
/* Linking an authenticator app (Accesos → Vincular 2FA). The QR keeps a white quiet zone whatever the
   theme: phone cameras read dark-on-light, and the dark surface around it would read as part of it. */
.totp-qr { width: 196px; max-width: 100%; margin: 4px auto 16px; padding: 6px; border-radius: var(--r);
  background: oklch(1 0 0); }
.totp-qr svg { display: block; width: 100%; height: auto; }
.totp-key { padding: 10px 12px; border-radius: var(--r); border: 1px solid var(--border-strong); background: var(--bg);
  color: var(--ink); font-size: var(--t-sm); letter-spacing: 0.06em; word-break: break-all; }

/* drawer */
.backdrop { position: fixed; inset: 0; background: oklch(0 0 0 / 0.5); opacity: 0; pointer-events: none;
  transition: opacity .2s var(--ease); z-index: 40; }
.backdrop.show { opacity: 1; pointer-events: auto; }
.drawer { position: fixed; top: 0; right: 0; height: 100dvh; width: 400px; max-width: 92vw;
  background: var(--surface); border-left: 1px solid var(--border); box-shadow: var(--shadow);
  transform: translateX(100%); transition: transform .24s var(--ease); z-index: 50; padding: 22px; overflow-y: auto; }
.drawer.show { transform: none; }
/* After the base rule on purpose, as with .panel: in the phones block it lost to the rule above. */
@media (max-width: 600px) { .drawer { width: 100%; max-width: 100%; padding: 18px 16px; } }
.drawer h3 { margin: 0 0 2px; font-size: var(--t-md); }
/* Drawer header (title block + close button). Every module fragment hand-rolled this row with the
   same inline styles; V.drawerHeader() emits these classes instead. */
.drawer-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.drawer-sub { margin-top: 6px; font-size: var(--t-sm); }
.drawer-close { height: 30px; padding: 0 10px; }
.drawer .row { display: flex; justify-content: space-between; gap: 12px; padding: 10px 0;
  border-bottom: 1px solid var(--border); font-size: var(--t-sm); }
.drawer .row .k { color: var(--ink-3); }
.drawer .row .v { color: var(--ink); text-align: right; }

@media (prefers-reduced-motion: reduce) { * { transition-duration: 0.01ms !important; animation: none !important; } }
