:root {
  /* ── Surfaces ── neutral canvas; indigo now reads as signal, not background */
  --bg: #F1EFE8;
  --surface: #FFFFFF;
  --surface2: #F7F6F2;
  --surface3: #EDEBE4;

  /* ── Nav ── near-constant across modes (already a dark surface) */
  --nav-bg: #1e2a4a;
  --nav-surface: #263355;
  --nav-border: rgba(255,255,255,0.08);
  --nav-text: rgba(255,255,255,0.6);
  --nav-text-active: #ffffff;

  /* ── Borders ── the separation mechanism. Use 0.5px, not 1px. */
  /* Control boundary. Solved for WCAG 1.4.11's 3:1 against BOTH the
     button face, the rail AND the page. Raised from 0.56 to 0.69 after
     the 3:1 minimum proved invisible in practice: on a --surface2 rail a
     white button has a FACE contrast of 1.08, so the border does 100% of
     the work. At 0.56 that was 3.42 and users could only find the buttons
     by hovering; the one button they COULD see was Delete, whose --red
     border measures 4.47. This clears 4.79 against the rail, just above it.
     3:1 is a compliance floor, not a visible edge. --border2 reaches only
     1.65 against the page -- a divider hairline and a control edge are
     different jobs, do not collapse them back together. */
  --border-control: rgba(44,44,42,0.69);
  --border: rgba(44,44,42,0.14);
  --border2: rgba(44,44,42,0.26);
  --border3: rgba(44,44,42,0.42);

  /* ── Text ── */
  --text: #1F1F1D;
  --text2: #55554F;
  --text3: #8A8A82;

  /* ── Accent ── indigo. Interactive only: links, buttons, active, focus. */
  --accent: #3d5af1;
  --accent2: #2d47d4;
  --accent-dim: rgba(61,90,241,0.1);
  --accent-dim2: rgba(61,90,241,0.05);
  --accent-light: #EEF1FE;

  /* ── Semantic ramps ── colour encodes meaning, never decorates.
     amber = ai/unconfirmed · green = verified · red = conflict/missing
     blue  = project CL / system-derived · text3 = neutral, no opinion
     purple + teal have NO reserved meaning; do not reach for them. */
  --purple: #7c3aed;
  --purple-dim: rgba(124,58,237,0.1);
  --purple-light: #f3effe;
  --amber: #d97706;
  --amber-dim: rgba(217,119,6,0.1);
  --amber-light: #FFF8ED;
  /* Text sitting ON an --amber-dim / --red-dim wash. The base --amber and --red
     are tuned to read on --surface; the 10% wash lifts the background and drops
     them to 2.86:1 and 4.14:1 at badge size. Same class of failure as the D0
     tick (#EF9F27 at 17px on white). Dark mode needs no override — its amber
     (#f5b13d) already clears 6.5:1 on its own wash. */
  --amber-on-tint: #92400e;
  --red: #dc2626;
  --red-dim: rgba(220,38,38,0.1);
  --red-light: #FEF3F2;
  --red-on-tint: #991b1b;
  --green-on-tint: #166534;
  --teal: #0891b2;
  --teal-dim: rgba(8,145,178,0.1);
  --teal-light: #ECFDFF;
  --green: #059669;
  --green-dim: rgba(5,150,105,0.1);
  --green-light: #ECFDF5;
  --blue: #2563eb;
  --blue-dim: rgba(37,99,235,0.1);

  /* ── Brand ── the verified-core tick. The one place brand amber lives. */
  --verified-tick: #E07B14;

  /* ── On-fill ── text sitting ON a saturated fill (accent button, active nav,
     solid badge). Near-constant across modes: the fill stays saturated in dark,
     so its text stays light. NOT the same as --surface. */
  --on-fill: #FFFFFF;

  /* ── Depth ── retired. Flat surfaces, separated by hairline.
     sm/md alias to none so existing references resolve but stop painting.
     --shadow-pop is the ONLY shadow: floating layers only (rail, modal, dropdown). */
  --shadow-sm: none;
  --shadow-md: none;
  --shadow-pop: 0 8px 24px rgba(0,0,0,0.12);

  /* ── Radius ── a scale, not a constant. Uniform radius is the generic tell. */
  --radius-sm: 3px;
  --radius: 4px;
  --radius-md: 6px;
  --radius-lg: 8px;
  --radius-pill: 999px;

  /* ── Type ── display = Space Grotesk (brand) · body = Plex · mono = JetBrains
     Weights 400/500 only. 600/700 reserved for the wordmark. */
  --font-display: 'Space Grotesk', sans-serif;
  --font-body: 'IBM Plex Sans', sans-serif;
  --font-mono: 'JetBrains Mono', monospace;

  --nav-width: 228px;

  /* -- Type scale -- ONE knob: --type-base. Every size below is an offset
     from it, so moving --type-base by 1px moves the whole platform by 1px
     and the hierarchy holds. Deliberately linear offsets, NOT a ratio
     scale: on a ratio scale a 1px nudge to body would throw a 26px page
     title past 40px, which is wrong for a data-dense engineering UI.
     Sizes are mode-independent -- no dark override needed here.
     NEVER write a raw px font-size again; same rule as raw hex. */
  --type-base: 15px;

  --fs-1: calc(var(--type-base) - 3px);   /* 12 - unit suffix, row flags */
  --fs-2: calc(var(--type-base) - 2px);   /* 13 - hints, counts, chips */
  --fs-3: calc(var(--type-base) - 1px);   /* 14 - labels, secondary body */
  --fs-4: var(--type-base);               /* 15 - body, grid value, controls */
  --fs-5: calc(var(--type-base) + 1px);   /* 16 - grid column header */
  --fs-6: calc(var(--type-base) + 3px);   /* 18 - form / section heading */
  --fs-7: calc(var(--type-base) + 7px);   /* 22 - panel title */
  --fs-8: calc(var(--type-base) + 11px);  /* 26 - page title */
  --fs-9: calc(var(--type-base) + 17px);  /* 32 - hero */

  /* -- Density -- the other half of the problem. 12.5px text on 6px
     padding reads cramped whatever the size is. --pad-cell-y is the knob
     for grid row height: drop it to 7px if thousand-row key lists want
     more rows on screen. --pad-cell-x also drives the header's reserved
     space for its filter button, so the two never disagree. */
  --lh-tight: 1.3;
  --lh-body: 1.55;
  --pad-cell-y: 11px;
  --pad-cell-x: 14px;
  --pad-head-y: 10px;
  --pad-control-y: 8px;
  --pad-control-x: 12px;
}

/* ───────────────────────────────────────────────────────────
   DARK THEME
   Applied via <html data-theme="dark"> (set pre-paint, see index.html).
   Only redefines the colour tokens — every component reads these
   variables, so the whole app re-themes from this single block.
   The navy nav is intentionally near-constant (it is already a dark
   surface with white text in both modes); it deepens slightly here.
   ─────────────────────────────────────────────────────────── */
html[data-theme="dark"] {
  /* Surfaces — true neutral, no blue tint */
  --bg: #0F0F0E;
  --surface: #1A1A18;
  --surface2: #232320;
  --surface3: #2C2C2A;

  --nav-bg: #0a0d16;
  --nav-surface: #161c2b;
  --nav-border: rgba(255,255,255,0.07);
  --nav-text: rgba(255,255,255,0.64);
  --nav-text-active: #ffffff;

  --border: rgba(240,240,235,0.13);
  --border-control: rgba(240,240,235,0.53);
  --border2: rgba(240,240,235,0.24);
  --border3: rgba(240,240,235,0.38);

  --text: #F0F0EB;
  --text2: #ABABA3;
  --text3: #75756E;

  --accent: #5e7bff;
  --accent2: #809aff;
  --accent-dim: rgba(94,123,255,0.17);
  --accent-dim2: rgba(94,123,255,0.08);
  --accent-light: #1c2440;

  --purple: #b095fc;
  --purple-dim: rgba(176,149,252,0.17);
  --purple-light: #261d3f;
  --amber: #f5b13d;
  --amber-dim: rgba(245,177,61,0.17);
  --amber-light: #352a14;
  /* Dark's amber already clears 6.5:1 on its own wash, so on-tint == the base.
     Defined explicitly rather than inherited: without this the light value
     (#92400e) would carry over and go near-black on a dark fill. Parity is a
     value rule, not a presence rule. */
  --amber-on-tint: #f5b13d;
  --red: #ff6f6f;
  --red-dim: rgba(255,111,111,0.17);
  --red-light: #361a1d;
  --red-on-tint: #ff6f6f;
  --green-on-tint: #3ddb9e;
  --teal: #34d6bf;
  --teal-dim: rgba(52,214,191,0.17);
  --teal-light: #11302d;
  --green: #3ddb9e;
  --green-dim: rgba(61,219,158,0.17);
  --green-light: #122d26;
  --blue: #66a6f7;
  --blue-dim: rgba(102,166,247,0.17);

  --verified-tick: #F0A83A;
  --on-fill: #FFFFFF;

  --shadow-sm: none;
  --shadow-md: none;
  --shadow-pop: 0 8px 24px rgba(0,0,0,0.5);

  color-scheme: dark;
}

*{box-sizing:border-box;margin:0;padding:0}
html,body{height:100%;background:var(--bg);color:var(--text);font-family:var(--font-body);font-size:var(--fs-4);line-height:1.6;overflow:hidden}

/* LAYOUT */
.app{display:flex;height:100vh;overflow:hidden}
.nav{width:var(--nav-width);background:var(--nav-bg);border-right:none;display:flex;flex-direction:column;flex-shrink:0;overflow:hidden}
.main{flex:1;overflow-y:auto;overflow-x:hidden;display:flex;flex-direction:column}
.app-footer{margin-top:auto;padding:18px 40px;font-size:var(--fs-1);color:var(--text3);font-family:var(--font-mono);text-align:center;border-top:0.5px solid var(--border);letter-spacing:.04em}

/* NAV */
.nav-logo{padding:22px 20px 18px;border-bottom:1px solid var(--nav-border)}
.nav-logo-name{font-family:'IBM Plex Sans',sans-serif;font-size:var(--fs-6);font-style:normal;font-weight:600;color:var(--on-fill);letter-spacing:.04em;text-transform:uppercase}
.nav-logo-tag{font-size:var(--fs-1);color:rgba(255,255,255,0.35);margin-top:3px;font-family:var(--font-mono);letter-spacing:.06em}
.nav-section{padding:18px 16px 6px;font-size:var(--fs-1);font-family:var(--font-mono);color:rgba(255,255,255,0.28);text-transform:uppercase;letter-spacing:.1em}
.nav-item{display:flex;align-items:center;gap:10px;padding:9px 14px;border-radius:8px;margin:1px 10px;cursor:pointer;transition:all .15s;color:var(--nav-text);font-size:var(--fs-4);border:1px solid transparent;font-weight:400}
.nav-item:hover{background:rgba(255,255,255,0.07);color:var(--nav-text-active)}
.nav-item.active{background:rgba(61,90,241,0.35);border-color:rgba(61,90,241,0.5);color:var(--on-fill)}
.nav-icon{width:15px;height:15px;opacity:.7;flex-shrink:0}
.nav-item.active .nav-icon{opacity:1}
.nav-badge{margin-left:auto;background:rgba(255,255,255,0.1);color:rgba(255,255,255,0.5);font-size:var(--fs-1);font-family:var(--font-mono);padding:1px 7px;border-radius:20px}
.nav-item.active .nav-badge{background:rgba(61,90,241,0.4);color:var(--accent-light)}
.nav-bottom{margin-top:auto;padding:14px;border-top:1px solid var(--nav-border)}
.api-status{display:flex;align-items:center;gap:8px;padding:8px 10px;background:rgba(255,255,255,0.06);border-radius:var(--radius);border:1px solid rgba(255,255,255,0.08)}
.api-dot{width:7px;height:7px;border-radius:50%;background:var(--green);flex-shrink:0;box-shadow:none}
.api-dot.off{background:var(--red);box-shadow:none}
.api-label{font-size:var(--fs-1);color:rgba(255,255,255,0.45);font-family:var(--font-mono)}

/* PAGES */
.page{display:none;padding:32px 36px}
.page.active{display:block}
.page#page-viewer{padding:0;max-width:none;height:100%}
.page#page-viewer.active{display:flex;flex-direction:column}
/* The review page is a fixed-height workspace — like the viewer page —
   so the queue table and Drawing pane each scroll inside their own
   bounds rather than the whole page growing with content. We use
   flex-fill rather than height:100% so the .app-footer below us stays
   visible (.main is a flex column containing pages + footer).
   Tag Search shares the same shell mechanics. */
.page#page-review,
.page#page-tag-search{box-sizing:border-box;min-height:0}
.page#page-review.active,
.page#page-tag-search.active{display:flex;flex-direction:column;flex:1 1 auto}

/* PAGE HEADER */
.page-header{margin-bottom:28px}
.page-title{font-family:'IBM Plex Sans',sans-serif;font-size:var(--fs-8);font-style:normal;font-weight:600;color:var(--text);letter-spacing:-.02em;line-height:1.2}
.page-title em{font-style:normal;color:var(--accent);font-weight:600}
.page-sub{font-size:var(--fs-3);color:var(--text2);margin-top:6px;font-weight:400}

/* CARDS */
.card{background:var(--surface);border:0.5px solid var(--border);border-radius:var(--radius-lg);padding:20px 24px;margin-bottom:16px;box-shadow:var(--shadow-sm)}
.card-title{font-size:var(--fs-4);font-weight:600;color:var(--text);margin-bottom:14px;display:flex;align-items:center;gap:8px;letter-spacing:.01em}
.card-title-icon{width:18px;height:18px;background:var(--accent-dim);border-radius:5px;display:flex;align-items:center;justify-content:center;font-size:10px}

/* BUTTONS */
.btn,.vx-btn{display:inline-flex;align-items:center;gap:7px;padding:8px 16px;border-radius:var(--radius);font-size:var(--fs-4);font-family:var(--font-body);cursor:pointer;transition:all .15s;border:1px solid var(--border-control);background:var(--surface);color:var(--text2);font-weight:500;box-shadow:var(--shadow-sm)}
.btn:hover,.vx-btn:hover:not(:disabled){border-color:rgba(61,90,241,0.35);color:var(--accent);background:var(--surface)}
/* .primary / .btn-primary were used in 5 places across 3 files but were never
   defined, so every one of them rendered as a plain button where a primary
   action was intended. Aliased here rather than rewritten in each file, so
   the next one that gets typed works too. */
.btn.accent,.btn.primary,.btn-primary,.vx-btn-primary{background:var(--accent);color:var(--on-fill);border-color:var(--accent);font-weight:600;box-shadow:none}
.btn.accent:hover,.btn.primary:hover,.btn-primary:hover,.vx-btn-primary:hover:not(:disabled){background:var(--accent2);border-color:var(--accent2);box-shadow:none}
.btn.sm,.vx-btn-sm{padding:5px 12px;font-size:var(--fs-2)}
/* Icon-only button. A glyph needs a square-ish box and the body size,
   not the small-button size -- at .sm a gear reads as a speck. */
.btn.icon,.vx-btn-icon{padding:8px 11px;font-size:var(--fs-4);line-height:1}
.btn.danger,.vx-btn-danger{color:var(--red);border-color:rgba(220,38,38,0.25);background:var(--surface)}
.btn.danger:hover,.vx-btn-danger:hover:not(:disabled){background:var(--red-light);border-color:rgba(220,38,38,0.4)}
/* Orange "retry" button — used for failed extraction rows */
.btn.warn{color:var(--amber);border-color:rgba(245,158,11,0.3);background:var(--surface);font-weight:600}
.btn.warn:hover{background:var(--amber-light);border-color:rgba(245,158,11,0.5)}
.btn.warn:disabled{opacity:0.5;cursor:not-allowed}
/* Never opacity. It fades the boundary too: at .35 the control edge drops
   from 3.42:1 to 1.46:1 and the button stops looking like a button. Mute
   the content, keep the shape. */
.btn:disabled,.vx-btn:disabled{opacity:1;cursor:not-allowed;
  color:var(--text3);background:var(--surface);
  border-color:var(--border-control)}
.btn:disabled:hover,.vx-btn:disabled:hover{color:var(--text3);
  background:var(--surface);border-color:var(--border-control)}

/* INPUTS */
/* Note: scoped away from checkbox/radio so width:100% / padding don't bleed
   onto compact controls. Those types pick up only browser defaults plus
   accent-color where relevant. */
input:not([type="checkbox"]):not([type="radio"]),select,textarea{background:var(--surface2);border:0.5px solid var(--border);border-radius:var(--radius);color:var(--text);font-family:var(--font-body);font-size:var(--fs-4);padding:8px 12px;transition:border-color .15s,box-shadow .15s;outline:none;width:100%}
input:focus,select:focus,textarea:focus{border-color:rgba(61,90,241,0.5);box-shadow:0 0 0 3px rgba(61,90,241,0.08);background:var(--surface)}
input::placeholder{color:var(--text3)}
select option{background:var(--surface2);color:var(--text)}
label{font-size:var(--fs-1);color:var(--text2);display:block;margin-bottom:5px;font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.06em;font-weight:500}

/* BADGES */
.badge{display:inline-block;padding:2px 9px;border-radius:20px;font-size:var(--fs-1);font-weight:600;font-family:var(--font-mono)}
.badge-green{background:var(--green-light);color:var(--green)}
.badge-red{background:var(--red-light);color:var(--red)}
.badge-amber{background:var(--amber-light);color:var(--amber)}
.badge-purple{background:var(--purple-light);color:var(--purple)}
.badge-accent{background:var(--accent-light);color:var(--accent)}
.badge-blue{background:var(--blue-dim);color:var(--blue)}

/* STATS ROW */
.stats-row{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:14px;margin-bottom:20px}
.stat-card{background:var(--surface);border:0.5px solid var(--border);border-radius:var(--radius-lg);padding:18px 22px;box-shadow:var(--shadow-sm)}
.stat-val{font-family:'IBM Plex Sans',sans-serif;font-size:var(--fs-9);font-weight:300;font-style:normal;color:var(--text);letter-spacing:-.02em}
.stat-lbl{font-size:var(--fs-1);color:var(--text3);margin-top:2px;font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.07em;font-weight:500}
.stat-card.accent-card{border-color:rgba(61,90,241,0.2);background:var(--accent-light)}
.stat-card.accent-card .stat-val{color:var(--accent)}

/* Admin page stat strip — 4 columns by default, reflows to 2x2 on narrow
   viewports so cards don't squish to unreadable widths. */
.admin-stat-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:14px}
@media (max-width:900px){
  .admin-stat-grid{grid-template-columns:repeat(2,minmax(0,1fr))}
}

/* Admin two-column shell: sidebar (left) + content (right). The shell
   sits inside a parent .card whose padding is set to 0 so the sidebar
   can run flush to the card edges. The right pane gets its own padding
   so content keeps the card's normal breathing room. */
.admin-shell{display:grid;grid-template-columns:200px 1fr;min-height:380px}
.admin-sidebar{
  border-right:0.5px solid var(--border);
  padding:18px 12px;
  background:var(--surface2);
  border-top-left-radius:var(--radius-lg);
  border-bottom-left-radius:var(--radius-lg);
}
.admin-sidebar-label{
  font-size:var(--fs-1);color:var(--text3);font-family:var(--font-mono);
  text-transform:uppercase;letter-spacing:.07em;font-weight:600;
  padding:4px 12px 10px;
}
.admin-nav-item{
  padding:8px 12px;border-radius:var(--radius);font-size:var(--fs-4);
  color:var(--text2);cursor:pointer;user-select:none;
  transition:background .12s, color .12s;margin-bottom:2px;
}
.admin-nav-item:hover{background:rgba(61,90,241,0.06);color:var(--text)}
.admin-nav-item.active{background:var(--accent);color:var(--on-fill);font-weight:500}
.admin-content{padding:22px 26px}
/* Sections — only the .active one is visible. The rest stay in the DOM
   so their elements (form inputs, lists) keep their IDs available for
   the existing loaders even when not on screen. */
.admin-section{display:none}
.admin-section.active{display:block}

/* Enum library editor — list/editor view toggle inside the Enums
   section. Mirrors how the criteria editor uses one card with two
   states (selector vs editor). */
.enum-view{display:none}
.enum-view.enum-view-active{display:block}
.enum-editor-body{margin-top:8px;display:flex;flex-direction:column;gap:14px}
.enum-field{display:flex;flex-direction:column;gap:6px}
.enum-label{font-size:var(--fs-2);font-weight:600;color:var(--text);display:flex;align-items:baseline;gap:8px}
.enum-label .enum-hint{font-size:var(--fs-1);color:var(--text3);font-weight:400}
.enum-input{
  width:100%;padding:8px 12px;border:1px solid var(--border2);
  border-radius:var(--radius);font-size:var(--fs-4);
  font-family:var(--font-body);color:var(--text);background:var(--surface);
  box-sizing:border-box;
}
.enum-input:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px rgba(61,90,241,0.12)}
textarea.enum-input{resize:vertical;min-height:36px;font-family:var(--font-body)}
.enum-err{font-size:var(--fs-1);color:var(--red);min-height:0}
.enum-err:empty{display:none}
.enum-err:not(:empty){margin-top:2px}

/* Chips for value list — small rounded pills with × button to remove.
   Wraps naturally as more values are added. */
.enum-chips{
  display:flex;flex-wrap:wrap;gap:6px;
  padding:8px;border:0.5px solid var(--border);border-radius:var(--radius);
  background:var(--surface2);min-height:36px;align-content:flex-start;
}
.enum-chips:empty::before{
  content:'No values yet';color:var(--text3);font-size:var(--fs-2);
  padding:2px 6px;font-style:italic;
}
.enum-chip{
  display:inline-flex;align-items:center;gap:6px;
  padding:4px 4px 4px 10px;border-radius:14px;
  background:var(--surface);border:0.5px solid var(--border);
  font-size:var(--fs-2);font-family:var(--font-mono);color:var(--text);
}
.enum-chip-remove{
  width:16px;height:16px;border-radius:50%;border:none;
  background:transparent;color:var(--text3);cursor:pointer;
  font-size:var(--fs-4);line-height:1;display:flex;align-items:center;justify-content:center;
  padding:0;
}
.enum-chip-remove:hover{background:var(--red);color:var(--on-fill)}

/* Library list rows — table-ish layout. */
.enum-row{
  display:grid;grid-template-columns:1.4fr 2fr 70px 70px 80px;
  gap:14px;align-items:center;
  padding:10px 12px;border-bottom:0.5px solid var(--border);
  font-size:var(--fs-4);cursor:pointer;
}
.enum-row:hover{background:var(--accent-light)}
.enum-row-head{
  font-size:var(--fs-1);color:var(--text3);font-family:var(--font-mono);
  text-transform:uppercase;letter-spacing:.07em;font-weight:600;
  cursor:default;background:transparent;
}
.enum-row-head:hover{background:transparent}
.enum-row-name{font-family:var(--font-mono);color:var(--text)}
.enum-row-display{color:var(--text)}
.enum-row-count{color:var(--text2);font-variant-numeric:tabular-nums;text-align:right}
.enum-row-empty{padding:24px;text-align:center;color:var(--text3);font-size:var(--fs-4)}
/* Section headers — title left, action button right. Used by every
   section for consistency. */
.admin-section-header{
  display:flex;align-items:center;justify-content:space-between;
  gap:12px;margin-bottom:14px;flex-wrap:wrap;
}
@media (max-width:900px){
  .admin-shell{grid-template-columns:1fr}
  .admin-sidebar{
    border-right:none;border-bottom:0.5px solid var(--border);
    border-radius:var(--radius-lg) var(--radius-lg) 0 0;
    display:flex;gap:6px;flex-wrap:wrap;padding:12px 14px;
  }
  .admin-sidebar-label{display:none}
  .admin-nav-item{margin-bottom:0;padding:6px 12px;border-radius:20px;font-size:var(--fs-2)}
}

/* DASHBOARD */
.welcome-band{background:var(--nav-bg);border:none;border-radius:var(--radius-lg);padding:30px 34px;margin-bottom:20px;position:relative;overflow:hidden;box-shadow:var(--shadow-md)}
.welcome-band::before{content:'';position:absolute;top:-60px;right:-40px;width:240px;height:240px;background:rgba(61,90,241,0.25);border-radius:50%;filter:blur(70px)}
.welcome-band::after{content:'';position:absolute;bottom:-40px;right:120px;width:160px;height:160px;background:rgba(124,58,237,0.2);border-radius:50%;filter:blur(50px)}
.welcome-title{font-family:'IBM Plex Sans',sans-serif;font-size:var(--fs-7);font-style:normal;font-weight:600;color:var(--on-fill);letter-spacing:-.02em;position:relative}
.welcome-title em{font-style:normal;color:var(--accent-light);font-weight:600}
.welcome-sub{font-size:var(--fs-4);color:rgba(255,255,255,0.65);margin-top:8px;max-width:520px;line-height:1.7;position:relative}
.quick-actions{display:flex;gap:10px;margin-top:20px;flex-wrap:wrap;position:relative}
.quick-actions .btn{background:rgba(255,255,255,0.12);border-color:rgba(255,255,255,0.2);color:var(--on-fill);box-shadow:none}
.quick-actions .btn:hover{background:rgba(255,255,255,0.2);border-color:rgba(255,255,255,0.35)}
.quick-actions .btn.accent{background:var(--accent);border-color:var(--accent);color:var(--on-fill);box-shadow:none}
.activity-list{display:flex;flex-direction:column;gap:0}
.activity-item{display:flex;align-items:center;gap:12px;padding:11px 0;border-bottom:0.5px solid var(--border);font-size:var(--fs-4)}
.activity-item:last-child{border-bottom:none}
.activity-icon{width:30px;height:30px;border-radius:8px;display:flex;align-items:center;justify-content:center;font-size:12px;flex-shrink:0;background:var(--accent-light);color:var(--accent)}
.activity-body{flex:1}
.activity-title{color:var(--text);font-weight:500}
.activity-meta{font-size:var(--fs-1);color:var(--text3);margin-top:1px;font-family:var(--font-mono)}
.activity-status{margin-left:auto}

/* CRITERIA / PROFILE */
.profile-selector{display:flex;gap:8px;align-items:center;margin-bottom:20px;flex-wrap:wrap}
.profile-chip{padding:6px 14px;border-radius:20px;font-size:var(--fs-2);border:0.5px solid var(--border);background:var(--surface2);color:var(--text2);cursor:pointer;transition:all .15s;font-family:var(--font-mono);font-weight:500}
.profile-chip:hover{border-color:rgba(61,90,241,0.3);color:var(--accent);background:var(--accent-light)}
.profile-chip.active{background:var(--accent);border-color:var(--accent);color:var(--on-fill);box-shadow:none}
.field-card{border:0.5px solid var(--border);border-radius:var(--radius);margin-bottom:8px;overflow:hidden;transition:border-color .2s;box-shadow:var(--shadow-sm)}
.field-card.fmt-on{border-color:rgba(124,58,237,0.3)}
.field-top{display:grid;grid-template-columns:minmax(0,1.8fr) minmax(0,1fr) minmax(0,0.9fr) auto auto auto 28px;gap:8px;align-items:center;padding:10px 12px;background:var(--surface2)}
.fmt-toggle-wrap{display:flex;align-items:center;gap:6px;white-space:nowrap}
.fmt-lbl{font-size:var(--fs-1);color:var(--text3);font-family:var(--font-mono);font-weight:500}
.fmt-lbl.on{color:var(--purple)}
.tog{width:28px;height:16px;background:var(--border2);border-radius:8px;cursor:pointer;position:relative;border:none;padding:0;flex-shrink:0;transition:background .2s}
.tog.on{background:var(--purple)}
.tog::after{content:'';position:absolute;top:2px;left:2px;width:12px;height:12px;border-radius:50%;background:var(--surface);transition:transform .2s;box-shadow:none}
.tog.on::after{transform:translateX(12px)}
.del-btn{width:26px;height:26px;display:flex;align-items:center;justify-content:center;border:0.5px solid var(--border);border-radius:7px;background:transparent;color:var(--text3);cursor:pointer;font-size:var(--fs-5);flex-shrink:0;transition:all .15s}
.del-btn:hover{background:var(--red-light);color:var(--red);border-color:rgba(220,38,38,0.3)}
.fmt-panel{padding:14px;border-top:1px solid rgba(124,58,237,0.15);background:var(--purple-light)}
.fmt-panel-hdr{font-size:var(--fs-1);color:var(--purple);font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.08em;margin-bottom:10px;display:flex;align-items:center;justify-content:space-between;font-weight:500}
.pattern-list{display:flex;flex-direction:column;gap:5px;margin-bottom:8px}
.pat-item{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,0.8fr) auto auto;gap:6px;align-items:center;background:var(--surface);border:0.5px solid var(--border);border-radius:8px;padding:7px 10px;transition:border-color .15s}
.pat-item.m-pass{border-color:rgba(5,150,105,0.3);background:var(--green-light)}
.pat-item.m-fail{border-color:rgba(220,38,38,0.3);background:var(--red-light)}
.pat-input{font-family:var(--font-mono);font-size:var(--fs-2);border:none;background:transparent;color:var(--accent);padding:0;width:100%;font-weight:500}
.pat-input:focus{outline:none}
.pat-label-input{font-size:var(--fs-1);border:none;background:transparent;color:var(--text2);padding:0;width:100%;font-family:var(--font-body)}
.pat-label-input:focus{outline:none}
.pat-label-input::placeholder{color:var(--text3)}
.pat-res{font-size:var(--fs-1);font-family:var(--font-mono);font-weight:600;padding:2px 8px;border-radius:20px;white-space:nowrap;text-align:center}
.pr-pass{background:var(--green-light);color:var(--green)}
.pr-fail{background:var(--red-light);color:var(--red)}
.pr-idle{background:var(--purple-light);color:var(--purple)}
.pat-del{width:20px;height:20px;border:none;background:transparent;color:var(--text3);cursor:pointer;font-size:var(--fs-4);border-radius:4px;flex-shrink:0;display:flex;align-items:center;justify-content:center}
.pat-del:hover{background:var(--red-light);color:var(--red)}
.test-row{display:flex;gap:8px;align-items:center;padding-top:10px;border-top:1px solid rgba(124,58,237,0.15);margin-top:6px}
.test-lbl{font-size:var(--fs-1);color:var(--purple);white-space:nowrap;font-family:var(--font-mono);font-weight:500}
.test-inp{flex:1;font-family:var(--font-mono);font-size:var(--fs-2);border:1px solid rgba(124,58,237,0.25);background:var(--surface);color:var(--text);border-radius:7px;padding:6px 10px}
.test-inp:focus{outline:none;border-color:rgba(124,58,237,0.5);box-shadow:0 0 0 3px rgba(124,58,237,0.08)}
.overall-res{font-size:var(--fs-1);font-family:var(--font-mono);font-weight:600;padding:6px 12px;border-radius:7px;white-space:nowrap;text-align:center;min-width:90px}
.or-pass{background:var(--green-light);color:var(--green)}
.or-fail{background:var(--red-light);color:var(--red)}
.or-idle{background:var(--purple-light);color:var(--purple)}
.or-partial{background:var(--amber-light);color:var(--amber)}
.col-labels{display:grid;grid-template-columns:minmax(0,1.8fr) minmax(0,1fr) minmax(0,0.9fr) auto auto auto 28px;gap:8px;padding:0 12px;margin-bottom:8px}
.col-lbl{font-size:var(--fs-1);color:var(--text3);font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.07em;font-weight:500}
.presets{display:flex;flex-wrap:wrap;gap:4px;margin-bottom:8px}
.preset-pill{padding:2px 9px;border:1px solid rgba(124,58,237,0.3);border-radius:20px;font-size:var(--fs-1);cursor:pointer;color:var(--purple);font-family:var(--font-mono);background:var(--surface);transition:all .15s;font-weight:500}
.preset-pill:hover{background:var(--purple-light);border-color:rgba(124,58,237,0.5)}
.logic-row{display:flex;align-items:center;gap:8px;margin-bottom:8px}
.logic-lbl{font-size:var(--fs-1);color:var(--purple);white-space:nowrap;font-family:var(--font-mono);font-weight:500}
.logic-sel{width:auto;padding:3px 8px;border:1px solid rgba(124,58,237,0.3);border-radius:7px;background:var(--surface);color:var(--purple);font-size:var(--fs-1);font-family:var(--font-mono)}
.hint-txt{font-size:var(--fs-1);color:var(--text3);line-height:1.5;margin-top:6px;font-family:var(--font-mono)}

/* UPLOAD PAGE */
.drop-zone{border:2px dashed var(--border2);border-radius:var(--radius-lg);padding:48px 32px;text-align:center;transition:all .2s;cursor:pointer;margin-bottom:20px;position:relative;background:var(--surface)}
.drop-zone:hover,.drop-zone.drag-over{border-color:rgba(61,90,241,0.5);background:var(--accent-light)}
.drop-icon{font-size:36px;margin-bottom:12px;display:block}
.drop-title{font-family:'IBM Plex Sans',sans-serif;font-size:var(--fs-6);font-weight:500;color:var(--text);margin-bottom:6px}
.drop-sub{font-size:var(--fs-4);color:var(--text2)}
.drop-file-input{position:absolute;inset:0;opacity:0;cursor:pointer;width:100%;height:100%}
.profile-select-row{display:flex;gap:10px;align-items:flex-end;margin-bottom:20px;flex-wrap:wrap}
.queue-list{display:flex;flex-direction:column;gap:6px}
.queue-item{display:flex;align-items:center;gap:12px;padding:12px 16px;background:var(--surface);border:0.5px solid var(--border);border-radius:var(--radius);font-size:var(--fs-4);box-shadow:var(--shadow-sm)}
.queue-item.processing{border-color:rgba(61,90,241,0.3);background:var(--accent-light)}
.queue-item.done-ok{border-color:rgba(5,150,105,0.3);background:var(--green-light)}
.queue-item.done-gap{border-color:rgba(217,119,6,0.3);background:var(--amber-light)}
.queue-item.done-err{border-color:rgba(220,38,38,0.3);background:var(--red-light)}
.queue-name{flex:1;font-family:var(--font-mono);font-size:var(--fs-2);color:var(--text);font-weight:500}
.queue-progress{flex:1;height:4px;background:var(--border);border-radius:2px;overflow:hidden}
.queue-bar{height:100%;background:var(--accent);border-radius:2px;transition:width .4s}
.queue-status{font-size:var(--fs-1);font-family:var(--font-mono);min-width:80px;text-align:right}

/* RESULTS PAGE */
.results-toolbar{display:flex;gap:8px;align-items:center;margin-bottom:16px;flex-wrap:wrap}
.filter-pill{padding:5px 14px;border-radius:20px;font-size:var(--fs-2);border:0.5px solid var(--border);background:var(--surface);color:var(--text2);cursor:pointer;transition:all .15s;font-family:var(--font-mono);font-weight:500;box-shadow:var(--shadow-sm)}
.filter-pill:hover{border-color:rgba(61,90,241,0.3);color:var(--accent)}
.filter-pill.active{background:var(--accent);border-color:var(--accent);color:var(--on-fill);box-shadow:none}
.tbl-wrap{overflow-x:auto;border-radius:var(--radius-lg);border:0.5px solid var(--border);box-shadow:var(--shadow-sm)}
table{width:100%;border-collapse:collapse;font-size:var(--fs-2);background:var(--surface)}
/* Table headers read as STRUCTURE, not as a caption.
   Previously: 10px uppercase mono against ~13px body text, so every column
   name was 30% SMALLER than the values beneath it. On a wide table that is
   what makes a reader lose their place -- nothing on screen says "this is a
   column". Now 13.5px/600 in the body face, one step above the 12.5-13px
   values, with a heavier bottom rule to separate head from body.
   Kept: left alignment, nowrap, the surface2 background. */
th{text-align:left;padding:10px 14px;border-bottom:2px solid var(--border2);color:var(--text);font-weight:600;font-size:var(--fs-4);letter-spacing:-0.01em;background:var(--surface2);white-space:nowrap}
td{padding:10px 14px;border-bottom:0.5px solid var(--border);vertical-align:top;color:var(--text);font-size:var(--fs-2)}
tr:last-child td{border-bottom:none}
tr.row-gap{background:var(--amber-dim)}
tr:hover td{background:var(--surface2)}
.conf-wrap{display:flex;align-items:center;gap:6px}
.conf-bar-outer{width:36px;height:3px;background:var(--border);border-radius:2px;overflow:hidden}
.conf-bar-inner{height:100%;border-radius:2px}
.missing-val{color:var(--text3);font-style:italic;font-size:var(--fs-1)}
.matched-fmt{font-size:var(--fs-1);color:var(--green);font-family:var(--font-mono);margin-top:2px;font-weight:500}

/* GAP REPORT */
.gap-item{border:1px solid rgba(217,119,6,0.2);border-radius:var(--radius);background:var(--amber-dim);padding:14px 16px;margin-bottom:10px;box-shadow:var(--shadow-sm)}
.gap-doc{font-family:var(--font-mono);font-size:var(--fs-2);color:var(--amber);margin-bottom:4px;font-weight:500}
.gap-field{font-size:var(--fs-4);font-weight:600;margin-bottom:6px;color:var(--text)}
.gap-reason{font-size:var(--fs-2);color:var(--text2);margin-bottom:8px;line-height:1.6}
.gap-suggest{font-size:var(--fs-2);color:var(--text2);border-left:2px solid rgba(217,119,6,0.4);padding-left:10px;line-height:1.6}
.gap-type-badge{font-size:var(--fs-1);font-family:var(--font-mono);padding:2px 8px;border-radius:20px;margin-left:8px;font-weight:600}
.gt-missing{background:var(--red-light);color:var(--red)}
.gt-format{background:var(--amber-light);color:var(--amber)}
.gt-cloud{background:var(--purple-light);color:var(--purple)}
.gt-low-conf{background:var(--blue-dim);color:var(--blue)}

/* SETTINGS */
.settings-group{margin-bottom:24px}
.settings-title{font-size:var(--fs-1);color:var(--text3);font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.08em;margin-bottom:12px;padding-bottom:8px;border-bottom:0.5px solid var(--border);font-weight:600}
.settings-row{display:flex;align-items:center;justify-content:space-between;padding:12px 0;border-bottom:0.5px solid var(--border);gap:16px}
.settings-row:last-child{border-bottom:none}
.settings-row-left{flex:1}
.settings-label{font-size:var(--fs-4);font-weight:600;color:var(--text)}
.settings-hint{font-size:var(--fs-2);color:var(--text3);margin-top:2px;line-height:1.5}
.cp-field{position:relative;width:100%}
.cp-field input{width:100%;padding-right:62px}
.cp-toggle{position:absolute;right:6px;top:50%;transform:translateY(-50%);background:transparent;border:none;color:var(--text3);font-size:var(--fs-1);font-family:var(--font-mono);font-weight:500;letter-spacing:.04em;padding:4px 10px;border-radius:4px;cursor:pointer;text-transform:uppercase;transition:color .15s,background .15s}
.cp-toggle:hover{color:var(--accent);background:var(--accent-light)}
.cp-toggle:focus-visible{outline:2px solid var(--accent);outline-offset:1px}
.api-key-row{display:flex;gap:8px;margin-top:10px}
.key-input{font-family:var(--font-mono);font-size:var(--fs-2);flex:1}
.key-status{display:flex;align-items:center;gap:6px;font-size:var(--fs-2);margin-top:6px;font-family:var(--font-mono)}
.key-dot{width:7px;height:7px;border-radius:50%;flex-shrink:0}

/* MODAL */
.modal-overlay{position:fixed;inset:0;background:rgba(15,20,40,0.55);backdrop-filter:blur(4px);display:flex;align-items:center;justify-content:center;z-index:100;padding:20px}
.modal{background:var(--surface);border:0.5px solid var(--border);border-radius:var(--radius-lg);padding:28px;max-width:480px;width:100%;box-shadow:var(--shadow-md)}
.modal-title{font-family:'IBM Plex Sans',sans-serif;font-size:var(--fs-6);font-style:normal;font-weight:600;margin-bottom:8px;color:var(--text);letter-spacing:-.01em}
.modal-sub{font-size:var(--fs-4);color:var(--text2);margin-bottom:20px;line-height:1.6}
.modal-actions{display:flex;gap:8px;justify-content:flex-end;margin-top:20px}
.form-group{margin-bottom:14px}

/* TOAST */
.toast{position:fixed;bottom:24px;right:24px;background:var(--nav-bg);border:1px solid rgba(255,255,255,0.15);border-radius:var(--radius);padding:12px 18px;font-size:var(--fs-4);color:var(--on-fill);z-index:200;transform:translateY(80px);opacity:0;transition:all .25s;max-width:320px;box-shadow:var(--shadow-md)}
.toast.show{transform:translateY(0);opacity:1}
.toast.success{border-color:rgba(52,211,153,0.4);background:var(--green-dim);color:var(--green)}
.toast.error{border-color:rgba(248,113,113,0.4);background:var(--red-dim);color:var(--red)}

/* CLOUD SECTION */
.cloud-criteria-card{border:1px solid rgba(124,58,237,0.2);border-radius:var(--radius);padding:14px 16px;background:var(--purple-light);margin-top:12px}
.cloud-field-row{display:grid;grid-template-columns:minmax(0,1.5fr) minmax(0,1fr) auto;gap:8px;align-items:center;margin-bottom:6px}

/* SCROLLBAR */
::-webkit-scrollbar{width:5px;height:5px}
::-webkit-scrollbar-track{background:transparent}
::-webkit-scrollbar-thumb{background:rgba(99,120,180,0.25);border-radius:10px}
::-webkit-scrollbar-thumb:hover{background:rgba(99,120,180,0.45)}

/* EMPTY STATES */
.empty-state{text-align:center;padding:48px 24px}
.empty-icon{font-size:32px;margin-bottom:12px;opacity:.25}
.empty-title{font-family:'IBM Plex Sans',sans-serif;font-size:var(--fs-5);font-style:normal;font-weight:500;color:var(--text2);margin-bottom:6px}
.empty-sub{font-size:var(--fs-2);color:var(--text3);line-height:1.6}

/* DIVIDER */
.divider{border:none;border-top:0.5px solid var(--border);margin:16px 0}

/* EXTRACTION HINTS */
.hint-tog-wrap{display:flex;align-items:center;gap:6px;white-space:nowrap}
.hint-lbl{font-size:var(--fs-1);color:var(--text3);font-family:var(--font-mono);font-weight:500}
.hint-lbl.on{color:var(--teal)}
.hint-tog{width:28px;height:16px;background:var(--border2);border-radius:8px;cursor:pointer;position:relative;border:none;padding:0;flex-shrink:0;transition:background .2s}
.hint-tog.on{background:var(--teal)}
.hint-tog::after{content:'';position:absolute;top:2px;left:2px;width:12px;height:12px;border-radius:50%;background:var(--surface);transition:transform .2s;box-shadow:none}
.hint-tog.on::after{transform:translateX(12px)}
.hint-panel{padding:12px 14px;border-top:1px solid rgba(8,145,178,0.15);background:var(--teal-light)}
.hint-panel-hdr{font-size:var(--fs-1);color:var(--teal);font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.08em;margin-bottom:10px;font-weight:500}
.hint-pills{display:flex;flex-wrap:wrap;gap:5px;margin-bottom:10px}
.hint-pill{display:inline-flex;align-items:center;gap:5px;padding:4px 10px;border:1px solid rgba(8,145,178,0.25);border-radius:20px;font-size:var(--fs-1);cursor:pointer;color:var(--teal);background:var(--surface);transition:all .15s;font-weight:500}
.hint-pill:hover{background:var(--teal-light);border-color:rgba(8,145,178,0.5)}
.hint-pill.selected{background:var(--teal);color:var(--on-fill);border-color:var(--teal)}
.hint-pill.custom-pill{border-style:dashed;color:var(--text2);border-color:var(--border2)}
.hint-pill.custom-pill:hover{border-color:var(--teal);color:var(--teal)}
.hint-pill.custom-pill.selected{background:var(--surface2);color:var(--text);border-color:var(--teal);border-style:solid}
.hint-pill-icon{width:16px;height:16px;border-radius:3px;display:flex;align-items:center;justify-content:center;font-size:8px;font-weight:700;flex-shrink:0;background:rgba(8,145,178,0.15);color:var(--teal)}
.hint-pill.selected .hint-pill-icon{background:rgba(255,255,255,0.25);color:var(--on-fill)}
.hint-preview{background:var(--surface);border:1px solid rgba(8,145,178,0.2);border-radius:var(--radius);padding:10px 12px;font-size:var(--fs-2);color:var(--text2);line-height:1.6;margin-bottom:8px;min-height:44px}
.hint-preview.empty{color:var(--text3);font-style:italic}
.hint-custom-area{width:100%;min-height:72px;resize:vertical;font-size:var(--fs-2);line-height:1.6;border:1px solid rgba(8,145,178,0.3);border-radius:var(--radius);background:var(--surface);color:var(--text);padding:8px 10px;font-family:var(--font-body)}
.hint-custom-area:focus{outline:none;border-color:rgba(8,145,178,0.6);box-shadow:0 0 0 3px rgba(8,145,178,0.08)}
.login-overlay{position:fixed;inset:0;background:var(--bg);display:flex;align-items:center;justify-content:center;z-index:500;padding:20px}
.login-box{background:var(--surface);border:0.5px solid var(--border);border-radius:16px;padding:40px 44px;width:100%;max-width:400px;box-shadow:var(--shadow-md)}
.login-logo{font-family:'IBM Plex Sans',sans-serif;font-size:var(--fs-7);font-weight:600;color:var(--accent);letter-spacing:.06em;text-transform:uppercase;margin-bottom:4px}
.login-tagline{font-size:var(--fs-1);color:var(--text3);font-family:var(--font-mono);margin-bottom:30px;letter-spacing:.04em}
.login-title{font-size:var(--fs-6);font-weight:600;color:var(--text);margin-bottom:5px;letter-spacing:-.01em}
.login-sub{font-size:var(--fs-4);color:var(--text2);margin-bottom:22px;line-height:1.5}
.login-field{margin-bottom:13px}
.login-field label{font-size:var(--fs-1);color:var(--text2);font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.06em;font-weight:500;display:block;margin-bottom:5px}
.login-field input{width:100%;padding:10px 13px;border:1px solid var(--border2);border-radius:var(--radius);font-size:var(--fs-4);font-family:var(--font-body);color:var(--text);background:var(--surface);outline:none;transition:border-color .15s,box-shadow .15s}
.login-field input:focus{border-color:rgba(61,90,241,0.5);box-shadow:0 0 0 3px rgba(61,90,241,0.08)}
.login-btn{width:100%;padding:11px;background:var(--accent);color:var(--on-fill);border:none;border-radius:var(--radius);font-size:var(--fs-4);font-weight:600;font-family:var(--font-body);cursor:pointer;transition:background .15s;margin-top:8px;box-shadow:none}
.login-btn:hover{background:var(--accent2)}
.login-error{font-size:var(--fs-2);color:var(--red);margin-top:10px;text-align:center;font-family:var(--font-mono);display:none}
.login-footer{margin-top:22px;padding-top:18px;border-top:0.5px solid var(--border);font-size:var(--fs-1);color:var(--text3);text-align:center;line-height:1.6}

/* ── Rules & behaviour panel ─────────────────────── */
.rules-tog-wrap{display:flex;align-items:center;gap:6px;white-space:nowrap}
.rules-lbl{font-size:var(--fs-1);color:var(--text3);font-family:var(--font-mono);font-weight:500}
.rules-lbl.on{color:var(--amber)}
.rules-tog{width:28px;height:16px;background:var(--border2);border-radius:8px;cursor:pointer;position:relative;border:none;padding:0;flex-shrink:0;transition:background .2s}
.rules-tog.on{background:var(--amber)}
.rules-tog::after{content:'';position:absolute;top:2px;left:2px;width:12px;height:12px;border-radius:50%;background:var(--surface);transition:transform .2s;box-shadow:none}
.rules-tog.on::after{transform:translateX(12px)}
.rules-tog.has-data:not(.on){background:var(--amber-light)}

.field-card.rules-on{border-color:rgba(245,158,11,0.25)}
.rules-panel{margin-top:10px;padding:12px;background:var(--bg3,rgba(245,158,11,0.04));border:1px solid rgba(245,158,11,0.15);border-radius:6px;display:flex;flex-direction:column;gap:10px}
.rules-panel-hdr{font-size:var(--fs-1);color:var(--amber);font-family:var(--font-mono);text-transform:uppercase;letter-spacing:0.08em}
.rules-row2{display:grid;grid-template-columns:1fr 1fr;gap:10px}
.rules-field{display:flex;flex-direction:column;gap:4px;font-size:var(--fs-2)}
.rules-field-wide{grid-column:1 / -1}
.rules-field-inline{flex-direction:row;align-items:center;gap:6px}
.rules-lbl2{font-size:var(--fs-1);color:var(--text2)}
.rules-inp{padding:5px 8px;border:0.5px solid var(--border);border-radius:4px;background:var(--surface);font-size:var(--fs-2);font-family:var(--font-mono)}
.rules-sel{padding:5px 8px;border:0.5px solid var(--border);border-radius:4px;background:var(--surface);font-size:var(--fs-2)}
.rules-subsection{display:flex;flex-direction:column;gap:6px}
.rules-subhdr{display:flex;align-items:center;justify-content:space-between;font-size:var(--fs-1);color:var(--text2);gap:10px}
.rules-textarea{padding:6px 8px;border:0.5px solid var(--border);border-radius:4px;background:var(--surface);font-size:var(--fs-2);font-family:var(--font-mono);min-height:70px;resize:vertical}
.rules-list{display:flex;flex-direction:column;gap:4px}
.rules-row{display:grid;grid-template-columns:2fr 1fr 2fr auto;gap:6px;align-items:center}
.rules-unknown{font-size:var(--fs-1);color:var(--text3);padding:6px 8px;background:var(--bg3,var(--surface2));border-radius:4px;border-left:3px solid var(--amber)}
.rules-advanced{margin-top:4px}
.rules-advanced summary{cursor:pointer;font-size:var(--fs-1);color:var(--text2);padding:4px 0}
.rules-json-help{font-size:var(--fs-1);color:var(--text3);margin:4px 0}
.rules-json-area{width:100%;min-height:120px;padding:6px 8px;border:0.5px solid var(--border);border-radius:4px;background:var(--surface);font-size:var(--fs-1);font-family:var(--font-mono)}
.rules-json-err{margin-top:4px;font-size:var(--fs-1);color:var(--red);font-family:var(--font-mono)}

/* ── Overlay layering ─────────────────────────────────────────────────────
   Two named layers, so nobody has to guess a number again.
   --z-overlay  every page-independent modal (the .vx-overlay class below)
   --z-ask      vxAsk / vxConfirm, which must sit ABOVE any overlay because
                it is routinely raised FROM one. These were both a literal
                9000 and only resolved correctly because of DOM order --
                a tie that any future markup move would have broken.
   Add a new overlay by giving it .vx-overlay, not a new number.          */
:root{ --z-overlay:8000; --z-ask:9000; }

/* ── Page-independent overlay ─────────────────────────────────────────────
   ⚠️ MARKUP USING THIS MUST SIT AT THE TOP LEVEL OF <body>. A position:fixed
   element cannot paint while an ancestor is display:none, and every .page is
   display:none unless it is the current one -- so an overlay nested in a page
   opens onto a hidden parent and appears only once that page is navigated to.
   tests/test_overlay_placement.py enforces this.
   Hidden by default; the opener sets an inline display:flex.               */
.vx-overlay{position:fixed;inset:0;z-index:var(--z-overlay);
  background:rgba(16,20,40,0.45);display:none;
  align-items:center;justify-content:center;padding:20px}

/* ── Centred action modal ─────────────────────────────── */
.vx-modal-backdrop{position:fixed;inset:0;background:rgba(15,20,35,0.55);backdrop-filter:blur(2px);display:none;align-items:center;justify-content:center;z-index:var(--z-ask);padding:20px;opacity:0;transition:opacity .15s ease}
.vx-modal-backdrop.show{display:flex;opacity:1}
.vx-modal{background:var(--surface);border-radius:12px;width:100%;max-width:440px;box-shadow:var(--shadow-pop);overflow:hidden;transform:translateY(8px);transition:transform .15s ease}
.vx-modal-backdrop.show .vx-modal{transform:translateY(0)}
.vx-modal-bar{height:3px;background:var(--accent)}
.vx-modal-bar.success{background:var(--green)}
.vx-modal-bar.error{background:var(--red)}
.vx-modal-bar.warning{background:var(--amber)}
.vx-modal-bar.working{background:var(--accent);background-image:linear-gradient(90deg,var(--accent) 0%,rgba(61,90,241,0.3) 50%,var(--accent) 100%);background-size:200% 100%;animation:vx-bar-shimmer 1.2s linear infinite}
@keyframes vx-bar-shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}
.vx-modal-header{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:11px 28px;border-bottom:1px solid var(--surface2)}
.vx-modal-header-trigger{font-size:var(--fs-2);font-weight:600;letter-spacing:.01em;color:var(--text2);text-transform:uppercase;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.vx-modal-header-brand{font-size:var(--fs-4);font-weight:700;color:var(--accent);letter-spacing:.02em;flex:none}
.vx-modal-body{padding:24px 28px 20px}
.vx-modal-icon{display:flex;align-items:center;justify-content:center;width:44px;height:44px;border-radius:50%;margin-bottom:14px;font-size:22px;font-weight:600}
.vx-modal-icon.success{background:var(--green-dim);color:var(--green)}
.vx-modal-icon.error{background:var(--red-dim);color:var(--red)}
.vx-modal-icon.warning{background:var(--amber-dim);color:var(--amber)}
.vx-modal-icon.info{background:var(--blue-dim);color:var(--blue)}
.vx-modal-icon.working{background:var(--accent-dim);color:var(--accent)}
.vx-modal-icon.hidden{display:none}
.vx-modal-spinner{width:22px;height:22px;border:2.5px solid rgba(61,90,241,0.2);border-top-color:var(--accent);border-radius:50%;animation:vx-spin .7s linear infinite}
@keyframes vx-spin{to{transform:rotate(360deg)}}
.vx-modal-title{font-size:var(--fs-6);font-weight:600;color:var(--text);margin-bottom:6px;letter-spacing:-.01em}
.vx-modal-message{font-size:var(--fs-4);color:var(--text2);line-height:1.5;word-wrap:break-word}
.vx-modal-detail{margin-top:12px;padding:10px 12px;background:var(--surface2);border:0.5px solid var(--border);border-radius:6px;font-family:var(--font-mono);font-size:var(--fs-1);color:var(--text3);line-height:1.5;max-height:140px;overflow:auto;word-wrap:break-word;white-space:pre-wrap;display:none}
.vx-modal-detail.show{display:block}
.vx-modal-text-input{width:100%;box-sizing:border-box;font-size:var(--fs-4);padding:9px 12px;border:0.5px solid var(--border);border-radius:6px;background:var(--surface);color:var(--text);font-family:inherit;outline:none;transition:border-color .12s,box-shadow .12s}
.vx-modal-text-input:focus{border-color:var(--accent);box-shadow:0 0 0 3px rgba(61,90,241,0.12)}
select.vx-modal-text-input{cursor:pointer}

/* ── The pick list ────────────────────────────────────────────────────────
   A bulk write the user can see into and untick. Owner, 2026-08-24:
   *"there must be option to choose certain and ignore others as survey
   might be suggesting something not correct 100%."*

   ⚠️ IT SCROLLS ITSELF rather than growing the dialog. A survey can propose
   forty values, and a dialog taller than the window puts its own confirm
   button off-screen -- a control the user cannot reach does not exist. */
.vx-pick-head{display:flex;align-items:center;justify-content:space-between;
  gap:8px;margin-bottom:6px;font-size:var(--fs-1);color:var(--text3)}
.vx-pick-all{border:0.5px solid var(--border-control);border-radius:6px;
  background:var(--surface);color:var(--text2);font-family:inherit;
  font-size:var(--fs-1);padding:2px 9px;cursor:pointer}
.vx-pick-list{max-height:min(46vh,340px);overflow:auto;text-align:left;
  border:0.5px solid var(--border);border-radius:6px;background:var(--surface)}
.vx-pick-row{display:flex;align-items:flex-start;gap:9px;padding:7px 10px;
  border-bottom:0.5px solid var(--border);cursor:pointer}
.vx-pick-row:last-child{border-bottom:none}
.vx-pick-row:hover{background:var(--surface2)}
.vx-pick-row input{margin-top:2px;flex:0 0 auto}
.vx-pick-body{flex:1 1 auto;min-width:0}
.vx-pick-label{font-size:var(--fs-4);color:var(--text);
  font-family:var(--font-mono);word-break:break-all}
/* Where it goes, or what it becomes. */
.vx-pick-sub{font-size:var(--fs-1);color:var(--text3);margin-top:1px}
/* ⚠️ THE EVIDENCE, AND IT IS THE POINT OF THE WHOLE DIALOG. A value read
   from one tag out of eighty is what a misread looks like; one read from
   forty is real. Without this the user is ticking boxes on faith. */
.vx-pick-hint{flex:0 0 auto;font-size:var(--fs-1);color:var(--text3);
  font-family:var(--font-mono);white-space:nowrap;padding-top:2px}
.vx-modal-actions{display:flex;gap:8px;justify-content:flex-end;padding:14px 20px;background:var(--surface2);border-top:0.5px solid var(--border)}
.vx-modal-actions:empty{display:none}
.vx-modal-btn{padding:8px 16px;border-radius:var(--radius);font-size:var(--fs-4);font-weight:500;font-family:var(--font-body);cursor:pointer;border:1px solid transparent;transition:background .12s,border-color .12s}
.vx-modal-btn.primary{background:var(--accent);color:var(--on-fill);border-color:var(--accent)}
.vx-modal-btn.primary:hover{background:var(--accent2)}
.vx-modal-btn.secondary{background:var(--surface);color:var(--text);border-color:var(--border2)}
.vx-modal-btn.secondary:hover{background:var(--surface2)}
.vx-modal-btn.danger{background:var(--red);color:var(--on-fill);border-color:var(--red)}
.vx-modal-btn.danger:hover{background:var(--red)}
.vx-modal-btn.link{background:transparent;color:var(--text3);border:none;font-size:var(--fs-2);padding:8px 10px}
.vx-modal-btn.link:hover{color:var(--text2)}

/* ── Dirty-state indicators (unsaved profile edits) ─── */
.save-profile-btn.is-dirty{background:var(--amber);border-color:var(--amber);color:var(--on-fill);box-shadow:none}
.save-profile-btn.is-dirty:hover{background:var(--amber);border-color:var(--amber)}
.profile-chip.has-unsaved{position:relative}
.profile-chip.has-unsaved::after{content:'';position:absolute;top:4px;right:4px;width:6px;height:6px;border-radius:50%;background:var(--amber);box-shadow:0 0 0 2px var(--surface)}
.criteria-bottom-bar{display:flex;align-items:center;justify-content:space-between;gap:12px;padding-top:16px;margin-top:4px;border-top:0.5px solid var(--border);flex-wrap:wrap}
.criteria-bottom-bar .hint{font-size:var(--fs-1);color:var(--text3);font-family:var(--font-mono)}
.criteria-bottom-bar .btns{display:flex;gap:8px}

/* ── Projects / Packages UI ──────────────────────────────── */

/* Breadcrumb (project detail view) */
.pp-breadcrumb{display:flex;align-items:center;gap:8px;margin-bottom:16px;font-size:var(--fs-4);color:var(--text2)}
.pp-back{display:inline-flex;align-items:center;gap:6px;background:transparent;border:none;color:var(--accent);font-size:var(--fs-4);font-family:var(--font-body);font-weight:500;cursor:pointer;padding:4px 6px;border-radius:var(--radius);transition:background .12s}
.pp-back:hover{background:rgba(61,90,241,0.08)}
.pp-sep{color:var(--text3)}
.pp-current{color:var(--text);font-weight:500}

/* Grid of cards */
.pp-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:12px}

/* A single project/package card */
.pp-card{background:var(--surface);border:0.5px solid var(--border);border-radius:var(--radius);padding:16px;cursor:pointer;transition:border-color .15s,box-shadow .15s,transform .08s;display:flex;flex-direction:column;gap:10px;min-height:120px}
.pp-card:hover{border-color:var(--accent);box-shadow:none}
.pp-card:active{transform:translateY(1px)}
.pp-card.archived{opacity:0.55}
.pp-card.archived:hover{opacity:0.85}

.pp-card-top{display:flex;align-items:flex-start;justify-content:space-between;gap:10px}
.pp-card-title{font-size:var(--fs-4);font-weight:600;color:var(--text);line-height:1.3;word-break:break-word;min-width:0;flex:1}
.pp-card-sub{font-size:var(--fs-1);color:var(--text3);font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.06em;margin-top:2px}
.pp-card-desc{font-size:var(--fs-2);color:var(--text2);line-height:1.5;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;word-break:break-word}
.pp-card-footer{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-top:auto;padding-top:8px;border-top:0.5px solid var(--border);font-size:var(--fs-1);color:var(--text3);font-family:var(--font-mono)}
.pp-card-count{display:inline-flex;align-items:center;gap:4px;font-weight:500;color:var(--text2)}

/* Active/selected card (used for the currently-open package detail) */
.pp-card.selected{border-color:var(--accent);box-shadow:0 0 0 2px rgba(61,90,241,0.15)}

/* Status chips (for packages) */
.pkg-status-chip{display:inline-flex;align-items:center;padding:3px 9px;border-radius:999px;font-size:var(--fs-1);font-weight:600;font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.06em;border:1px solid transparent}
.pkg-status-draft{background:var(--surface3);color:var(--text3);border-color:var(--border)}
.pkg-status-active{background:var(--blue-dim);color:var(--blue);border-color:var(--blue-dim)}
.pkg-status-complete{background:var(--green-dim);color:var(--green);border-color:var(--green-dim)}
.pkg-status-archived{background:var(--amber-dim);color:var(--amber);border-color:var(--amber-dim)}

/* Empty state for lists */
.pp-empty{text-align:center;padding:40px 20px;color:var(--text3)}
.pp-empty-title{font-size:var(--fs-4);color:var(--text2);font-weight:500;margin-bottom:6px}
.pp-empty-sub{font-size:var(--fs-2);margin-bottom:16px;line-height:1.5;max-width:360px;margin-left:auto;margin-right:auto}

/* Project/package create/edit modal form rows */
.pp-form-row{display:flex;flex-direction:column;gap:5px;margin-bottom:12px}
.pp-form-row label{font-size:var(--fs-1);color:var(--text2);font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.06em;font-weight:500}
.pp-form-row input,.pp-form-row select,.pp-form-row textarea{width:100%;padding:9px 12px;border:1px solid var(--border2);border-radius:var(--radius);font-size:var(--fs-4);font-family:var(--font-body);color:var(--text);background:var(--surface);outline:none;transition:border-color .12s,box-shadow .12s}
.pp-form-row input:focus,.pp-form-row select:focus,.pp-form-row textarea:focus{border-color:rgba(61,90,241,0.5);box-shadow:0 0 0 3px rgba(61,90,241,0.08)}
.pp-form-row textarea{resize:vertical;min-height:60px;font-family:var(--font-body)}
.pp-form-hint{font-size:var(--fs-1);color:var(--text3);margin-top:2px}

/* Indeterminate progress — shimmering bar used before tile count is known */
.extract-progress-indeterminate{
  animation: extract-progress-shimmer 1.2s ease-in-out infinite;
}
@keyframes extract-progress-shimmer{
  0%   { margin-left: 0%;   width: 30%; }
  50%  { margin-left: 35%;  width: 40%; }
  100% { margin-left: 100%; width: 0%;  }
}

/* ── Title block region card (Criteria page) ────── */
.tbr-card{margin-top:14px;padding:12px;background:var(--surface);border:0.5px solid var(--border);border-radius:6px}
.tbr-header{display:flex;align-items:center;justify-content:space-between;gap:12px}
.tbr-title{font-size:var(--fs-4);font-weight:600;color:var(--text1)}
.tbr-sub{font-size:var(--fs-1);color:var(--text3);margin-top:2px}
.tbr-body{margin-top:8px;font-size:var(--fs-2);color:var(--text2);font-family:var(--font-mono)}
.tbr-body .tbr-placeholder{color:var(--text3);font-style:italic}

/* ── Title block region picker modal ────── */
.tbr-modal{position:fixed;inset:0;z-index:1000;display:flex;align-items:center;justify-content:center}
.tbr-modal-backdrop{position:absolute;inset:0;background:rgba(10,15,30,0.55)}
.tbr-modal-panel{position:relative;width:min(1100px,94vw);max-height:94vh;display:flex;flex-direction:column;background:var(--surface);border:0.5px solid var(--border);border-radius:8px;box-shadow:var(--shadow-pop);overflow:hidden}
.tbr-modal-hdr{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;border-bottom:0.5px solid var(--border)}
.tbr-modal-title{font-size:var(--fs-4);font-weight:600;color:var(--text1)}
.tbr-modal-close{background:none;border:none;font-size:var(--fs-7);color:var(--text2);cursor:pointer;line-height:1}
.tbr-modal-toolbar{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:10px 16px;border-bottom:0.5px solid var(--border);font-size:var(--fs-2)}
.tbr-modal-toolbar label{display:flex;align-items:center;gap:8px}
.tbr-modal-lbl{color:var(--text2);font-size:var(--fs-1)}
.tbr-modal-toolbar select{padding:4px 8px;border:0.5px solid var(--border);border-radius:4px;font-size:var(--fs-2);background:var(--surface);color:var(--text);min-width:260px}
.tbr-modal-hint{color:var(--text3);font-size:var(--fs-1)}
.tbr-modal-stage{position:relative;flex:1;min-height:400px;max-height:68vh;overflow:auto;background:var(--nav-surface);display:flex;align-items:center;justify-content:center}
.tbr-modal-placeholder{color:var(--text3);font-size:var(--fs-2)}
#tbr-canvas{display:block;max-width:100%;max-height:100%}
.tbr-rect{position:absolute;border:2px dashed var(--amber);background:rgba(245,158,11,0.14);box-sizing:border-box;cursor:move}
.tbr-handle{position:absolute;width:12px;height:12px;background:var(--amber);border:1px solid var(--surface);box-sizing:border-box;border-radius:2px}
.tbr-h-tl{top:-7px;left:-7px;cursor:nwse-resize}
.tbr-h-tr{top:-7px;right:-7px;cursor:nesw-resize}
.tbr-h-bl{bottom:-7px;left:-7px;cursor:nesw-resize}
.tbr-h-br{bottom:-7px;right:-7px;cursor:nwse-resize}
.tbr-modal-footer{display:flex;align-items:center;justify-content:space-between;padding:10px 16px;border-top:0.5px solid var(--border)}
.tbr-modal-coords{font-size:var(--fs-1);color:var(--text3);font-family:var(--font-mono)}
.tbr-modal-btns{display:flex;gap:8px}


/* ─────────────────────────────────────────────────────────────────── */
/* Criteria editor — two-pane layout (added 2026-04-28)                */
/* ─────────────────────────────────────────────────────────────────── */

.vx-pane-grid{display:grid;grid-template-columns:280px 1fr;gap:14px;align-items:start}
.vx-pane{background:var(--surface);border:0.5px solid var(--border);border-radius:var(--radius-lg);box-shadow:var(--shadow-sm)}
.vx-left-pane{padding:14px;max-height:680px;overflow:auto}
.vx-right-pane{padding:18px 22px;min-height:440px}

.vx-left-hdr{display:flex;justify-content:space-between;align-items:center;margin-bottom:10px}
.vx-left-hdr-title{font-family:var(--font-mono);font-size:var(--fs-1);font-weight:500;text-transform:uppercase;letter-spacing:.07em;color:var(--text3)}
.vx-add-btn{background:transparent;border:1px solid var(--border2);color:var(--text2);padding:4px 10px;border-radius:8px;font-size:var(--fs-2);cursor:pointer;font-family:var(--font-body);transition:all .15s}
.vx-add-btn:hover{border-color:var(--accent);color:var(--accent)}

.vx-field-pill{display:flex;flex-direction:column;padding:10px 12px;border-radius:10px;cursor:pointer;margin-bottom:4px;border:1px solid transparent;transition:background .12s,border-color .12s}
.vx-field-pill:hover{background:var(--accent-dim2)}
.vx-field-pill.selected{background:var(--accent-dim);border-color:rgba(61,90,241,0.25)}
.vx-field-pill-row{display:flex;align-items:center;gap:8px}
.vx-field-pill-name{font-size:var(--fs-4);font-weight:500;flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--text)}
.vx-untitled{color:var(--text3);font-style:italic}
.vx-field-pill-meta{font-size:var(--fs-1);color:var(--text3);margin-top:3px;font-family:var(--font-mono)}
.vx-field-pill.selected .vx-field-pill-meta{color:var(--text2)}

.vx-role-badge{font-size:var(--fs-1);font-family:var(--font-mono);padding:2px 7px;border-radius:8px;font-weight:500;letter-spacing:.04em}
.vx-role-tag{background:var(--teal-dim);color:var(--teal)}
.vx-role-attr{background:var(--purple-dim);color:var(--purple)}
/* Datasheet role badge (Phase 5) — uses the accent palette to read
   distinct from tag (teal) and attribute (purple). */
.vx-role-ds{background:var(--accent-dim, rgba(99,102,241,0.12));color:var(--accent)}
/* System-managed field marker. Shown alongside the role badge on
   fields the platform owns (currently: datasheet_number). Greyer than
   the role badges so it reads as supplementary metadata. */
.vx-role-sys{background:rgba(120,120,120,0.12);color:var(--text2);margin-left:4px}
/* Footer note shown in place of the Delete button for system fields. */
.vx-sys-note{
  font-size:var(--fs-2);color:var(--text3);font-style:italic;
  padding:8px 12px;background:var(--surface2);border:1px dashed var(--border);
  border-radius:6px;
}
/* Read-only role label shown for datasheet profiles in place of the
   Tag/Attribute toggle. Visually similar to a disabled segment so the
   layout stays consistent. */
.vx-role-static{font-size:var(--fs-4);color:var(--text);font-weight:500;padding:6px 10px;background:var(--surface2);border:0.5px solid var(--border);border-radius:6px;display:inline-block}
/* Profile meta row at the top of the criteria editor — document-type
   editor on the left, Delete button on the right. Sits above the
   "Data to capture" header. */
.profile-meta-row{
  display:flex;align-items:flex-end;justify-content:space-between;
  gap:16px;
  padding-bottom:14px;margin-bottom:14px;
  border-bottom:0.5px solid var(--border);
}
.profile-meta-field{display:flex;flex-direction:column;gap:4px;flex:0 1 360px;min-width:200px}
.profile-meta-field label{font-size:var(--fs-1);color:var(--text3);font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.06em;font-weight:500}
.profile-meta-field select{font-size:var(--fs-4);padding:7px 10px;border:1px solid var(--border2);border-radius:var(--radius);background:var(--surface);color:var(--text);font-family:var(--font-body)}

.vx-empty,.vx-detail-empty{color:var(--text3);font-size:var(--fs-4);padding:14px 4px}

.vx-section{margin-bottom:18px}
.vx-section-label{font-family:var(--font-mono);font-size:var(--fs-1);font-weight:500;text-transform:uppercase;letter-spacing:.08em;color:var(--text3);margin-bottom:12px;display:flex;align-items:center;justify-content:space-between}
.vx-section-tog{display:inline-flex;align-items:center}
.vx-tog{width:28px;height:16px;background:var(--border2);border-radius:8px;cursor:pointer;position:relative;border:none;padding:0;transition:background .2s}
.vx-tog.on{background:var(--purple)}
.vx-tog::after{content:'';position:absolute;top:2px;left:2px;width:12px;height:12px;border-radius:50%;background:var(--surface);transition:transform .2s;box-shadow:none}
.vx-tog.on::after{transform:translateX(12px)}
.vx-section-off{font-size:var(--fs-2);color:var(--text3);padding:4px 0}

.vx-row{display:grid;grid-template-columns:130px 1fr;align-items:center;gap:12px;margin-bottom:10px}
.vx-row label{font-size:var(--fs-2);color:var(--text2);font-family:var(--font-body);text-transform:none;letter-spacing:0;margin:0;font-weight:400}
.vx-row input[type=text],.vx-row select,.vx-row textarea{background:var(--surface2);border:0.5px solid var(--border);border-radius:var(--radius);padding:7px 10px;font-size:var(--fs-4);font-family:var(--font-body);color:var(--text);width:100%;outline:none;transition:border-color .15s}
.vx-row input[type=text]:focus,.vx-row select:focus{border-color:var(--accent)}

.vx-seg{display:inline-flex;background:var(--surface2);border:0.5px solid var(--border);border-radius:10px;padding:2px;gap:2px;width:fit-content}
.vx-seg-opt{padding:6px 14px;font-size:var(--fs-2);cursor:pointer;border-radius:8px;color:var(--text2);user-select:none;transition:all .15s}
.vx-seg-opt.on{background:var(--surface);color:var(--text);box-shadow:none;font-weight:500}
.vx-seg-opt:not(.on):hover{color:var(--text)}
/* Disabled state — locks an option from being clicked. Used while a
   popout window is open (the in-page Drawing tab is unavailable until
   the popout closes). pointer-events:none catches the click before
   any onclick handler runs; opacity + cursor sell the disabled state. */
.vx-seg-opt-disabled{opacity:0.40;cursor:not-allowed;pointer-events:none}

.vx-checks{display:flex;gap:18px}
.vx-check{display:flex;align-items:center;gap:7px;font-size:var(--fs-2);color:var(--text2);cursor:pointer;text-transform:none;letter-spacing:0;font-family:var(--font-body);margin:0;font-weight:400}
.vx-check input{accent-color:var(--accent);width:auto;margin:0}

.vx-divider{border:none;border-top:0.5px solid var(--border);margin:18px 0}

.vx-info-bar{background:var(--teal-dim);border-left:2px solid var(--teal);padding:8px 12px;font-size:var(--fs-1);color:var(--text2);border-radius:0 8px 8px 0;margin-bottom:12px;line-height:1.5}

.vx-hint-pills{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:10px}
.vx-hint-pill{padding:5px 11px;border-radius:14px;font-size:var(--fs-2);background:var(--surface2);color:var(--text2);border:0.5px solid var(--border);cursor:pointer;transition:all .15s;user-select:none}
.vx-hint-pill:hover{border-color:var(--teal)}
.vx-hint-pill.selected{background:var(--teal-light);color:var(--teal);border-color:rgba(8,145,178,0.3);font-weight:500}
.vx-textarea{background:var(--surface2);border:0.5px solid var(--border);border-radius:var(--radius);padding:10px 12px;font-size:var(--fs-2);color:var(--text);font-family:var(--font-body);min-height:80px;line-height:1.5;width:100%;resize:vertical;outline:none}
.vx-textarea:focus{border-color:var(--accent)}
.vx-hint-preview{font-size:var(--fs-2);color:var(--text2);padding:10px 12px;background:var(--surface2);border:0.5px solid var(--border);border-radius:var(--radius);line-height:1.5;font-style:italic}
.vx-hint-preview.empty{color:var(--text3)}
.vx-meas-empty{font-size:var(--fs-2);color:var(--text3);padding:8px 10px;background:var(--surface2);border:1px dashed var(--border);border-radius:var(--radius);line-height:1.5}

.vx-pat-list{display:flex;flex-direction:column;gap:5px;margin-bottom:8px}
.vx-pat-row{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,0.8fr) auto auto;gap:6px;align-items:center;background:var(--surface);border:0.5px solid var(--border);border-radius:8px;padding:7px 10px;transition:border-color .15s}
.vx-pat-row.m-pass{border-color:rgba(5,150,105,0.35)}
.vx-pat-row.m-fail{border-color:rgba(220,38,38,0.35)}
.vx-pat-input{font-family:var(--font-mono);font-size:var(--fs-4);border:none;background:transparent;color:var(--text);padding:0;width:100%;outline:none}
.vx-pat-label{font-size:var(--fs-1);border:none;background:transparent;color:var(--text2);padding:0;width:100%;font-family:var(--font-body);outline:none}
.vx-pat-res{font-family:var(--font-mono);font-size:var(--fs-1);font-weight:500;padding:2px 8px;border-radius:6px;letter-spacing:.04em;text-transform:uppercase}
.vx-pat-res.pr-pass{color:var(--green);background:var(--green-light)}
.vx-pat-res.pr-fail{color:var(--red);background:var(--red-light)}
.vx-pat-res.pr-idle{color:var(--text3);background:var(--surface2)}
.vx-pat-del{background:transparent;border:0.5px solid var(--border);border-radius:6px;width:22px;height:22px;cursor:pointer;color:var(--text3);padding:0;line-height:1;font-size:var(--fs-4)}
.vx-pat-del:hover{border-color:var(--red);color:var(--red)}
.vx-add-pattern{background:transparent;border:1px solid rgba(124,58,237,0.3);color:var(--purple);padding:5px 12px;border-radius:8px;font-size:var(--fs-1);cursor:pointer;font-family:var(--font-body);margin-bottom:10px}
.vx-add-pattern:hover{background:var(--purple-light)}
.vx-logic-row{display:flex;align-items:center;gap:8px;font-size:var(--fs-1);color:var(--text2);margin-bottom:10px}
.vx-logic-row select{width:auto;font-size:var(--fs-2);padding:5px 8px;background:var(--surface2);border:0.5px solid var(--border);border-radius:8px;color:var(--text)}
.vx-presets-label{font-size:var(--fs-1);color:var(--text3);font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.06em;margin-bottom:6px}
.vx-presets{display:flex;flex-wrap:wrap;gap:5px;margin-bottom:10px}
.vx-preset-pill{padding:3px 10px;border-radius:14px;font-size:var(--fs-1);background:var(--surface2);color:var(--text2);border:0.5px solid var(--border);cursor:pointer;font-family:var(--font-mono);transition:all .15s}
.vx-preset-pill:hover{border-color:var(--purple);color:var(--purple)}
.vx-dsl-help{font-size:var(--fs-1);color:var(--text3);font-family:var(--font-mono);margin-bottom:10px}
.vx-test-row{display:flex;align-items:center;gap:8px}
.vx-test-inp{flex:1;background:var(--surface2);border:0.5px solid var(--border);border-radius:var(--radius);padding:7px 10px;font-size:var(--fs-4);color:var(--text);outline:none}
.vx-overall{font-family:var(--font-mono);font-size:var(--fs-1);font-weight:500;padding:5px 12px;border-radius:8px;letter-spacing:.04em;text-transform:uppercase}
.vx-overall.or-idle{color:var(--text3);background:var(--surface2)}
.vx-overall.or-pass{color:var(--green);background:var(--green-light)}
.vx-overall.or-partial{color:var(--amber);background:var(--amber-light)}
.vx-overall.or-fail{color:var(--red);background:var(--red-light)}

.vx-enum-chips{display:flex;flex-wrap:wrap;gap:6px;align-items:center;padding:8px;background:var(--surface2);border:0.5px solid var(--border);border-radius:var(--radius);min-height:38px}
.vx-chip{background:var(--surface);border:1px solid rgba(124,58,237,0.3);padding:3px 9px 3px 11px;font-size:var(--fs-2);border-radius:14px;color:var(--purple);font-family:var(--font-mono);display:inline-flex;align-items:center;gap:5px}
.vx-chip-x{color:var(--text3);cursor:pointer;font-size:var(--fs-4);line-height:1}
.vx-chip-x:hover{color:var(--red)}
.vx-chip-input{background:transparent;border:none;outline:none;font-size:var(--fs-2);color:var(--text);padding:3px 6px;min-width:140px;flex:1;font-family:var(--font-body)}

/* Enum library binding row — replaces inline chip editor on the
   criteria page. Source of truth is the admin Enums page; this row
   lets the user pick which library a field uses, and previews the
   library's current values as read-only chips. */
.vx-enum-bind{display:flex;flex-direction:column;gap:8px;width:100%}
.vx-enum-bind-select{
  width:100%;padding:7px 10px;border:1px solid var(--border2);
  border-radius:var(--radius);font-size:var(--fs-4);background:var(--surface);
  color:var(--text);font-family:var(--font-body);
}
.vx-enum-bind-select:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px rgba(61,90,241,0.12)}
.vx-enum-preview{
  display:flex;flex-wrap:wrap;gap:6px;padding:8px;
  background:var(--surface2);border:0.5px solid var(--border);
  border-radius:var(--radius);min-height:34px;align-items:center;
}
.vx-chip-readonly{
  background:var(--surface);border:0.5px solid var(--border);
  padding:3px 11px;font-size:var(--fs-2);border-radius:14px;
  color:var(--text2);font-family:var(--font-mono);cursor:default;
}
.vx-enum-preview-empty{font-size:var(--fs-2);color:var(--text3);font-style:italic;padding:0 6px}
.vx-enum-manage-link{font-size:var(--fs-1)}
.vx-enum-manage-link a{color:var(--accent);text-decoration:none}
.vx-enum-manage-link a:hover{text-decoration:underline}
.vx-enum-bind-hint,
.vx-enum-bind-empty{font-size:var(--fs-1);color:var(--text3);line-height:1.5}
.vx-enum-bind-empty a{color:var(--accent);text-decoration:none}
.vx-enum-bind-empty a:hover{text-decoration:underline}

.vx-advanced-row{display:flex;align-items:center;justify-content:space-between;padding:10px 14px;background:var(--surface2);border:0.5px solid var(--border);border-radius:var(--radius);cursor:pointer;transition:border-color .15s}
.vx-advanced-row:hover{border-color:var(--amber)}
.vx-advanced-label{font-size:var(--fs-1);color:var(--text2);font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.07em}
.vx-chev{color:var(--text3);font-size:var(--fs-2)}

.vx-detail-footer{margin-top:24px;padding-top:14px;border-top:0.5px solid var(--border);display:flex;justify-content:flex-end}
.vx-del-field-btn{background:transparent;border:0.5px solid var(--border);color:var(--text3);padding:6px 14px;border-radius:8px;font-size:var(--fs-2);cursor:pointer;font-family:var(--font-body);transition:all .15s}
.vx-del-field-btn:hover{border-color:var(--red);color:var(--red);background:var(--red-light)}

/* ─────────────────────────────────────────────────────────────────── */
/* Help system — side drawer (added 2026-04-28)                        */
/* ─────────────────────────────────────────────────────────────────── */

.vx-help-nav-btn{display:flex;align-items:center;gap:8px;background:transparent;border:1px solid rgba(255,255,255,0.12);color:rgba(255,255,255,0.55);padding:8px 12px;border-radius:8px;width:100%;cursor:pointer;font-family:var(--font-body);font-size:var(--fs-2);transition:all .15s;margin-bottom:6px}
.vx-help-nav-btn:hover{background:rgba(255,255,255,0.05);color:rgba(255,255,255,0.85);border-color:rgba(255,255,255,0.2)}
.vx-help-nav-btn svg{width:14px;height:14px;flex-shrink:0}

.vx-help-overlay{position:fixed;inset:0;background:rgba(15,21,40,0.4);z-index:998;opacity:0;pointer-events:none;transition:opacity .2s}
.vx-help-overlay.open{opacity:1;pointer-events:auto}

.vx-help-drawer{position:fixed;top:0;right:0;width:520px;max-width:92vw;height:100vh;background:var(--surface);box-shadow:none;z-index:999;display:flex;flex-direction:column;transform:translateX(100%);transition:transform .25s ease-out}
.vx-help-drawer.open{transform:translateX(0)}

.vx-help-hdr{display:flex;align-items:flex-start;justify-content:space-between;padding:18px 22px 14px;border-bottom:0.5px solid var(--border)}
.vx-help-title{font-size:var(--fs-6);font-weight:500;color:var(--text);font-family:'IBM Plex Sans',sans-serif}
.vx-help-sub{font-size:var(--fs-1);color:var(--text3);margin-top:3px;font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.06em}
.vx-help-close{background:transparent;border:none;color:var(--text3);font-size:var(--fs-7);line-height:1;cursor:pointer;padding:0 4px;border-radius:6px;transition:all .15s}
.vx-help-close:hover{color:var(--red);background:var(--red-light)}

.vx-help-body{display:grid;grid-template-columns:160px 1fr;flex:1;min-height:0}
.vx-help-nav{padding:14px 8px 14px 14px;border-right:0.5px solid var(--border);overflow:auto;background:var(--surface2)}
.vx-help-nav-section{font-family:var(--font-mono);font-size:var(--fs-1);font-weight:500;text-transform:uppercase;letter-spacing:.07em;color:var(--text3);padding:8px 10px 4px}
.vx-help-nav-link{display:block;padding:7px 10px;font-size:var(--fs-2);color:var(--text2);cursor:pointer;border-radius:7px;margin-bottom:1px;transition:all .12s;line-height:1.35}
.vx-help-nav-link:hover{background:var(--surface);color:var(--text)}
.vx-help-nav-link.active{background:var(--accent-dim);color:var(--accent);font-weight:500}

.vx-help-content{padding:18px 24px;overflow:auto;font-size:var(--fs-4);line-height:1.65;color:var(--text)}
.vx-help-content h2{font-size:var(--fs-6);font-weight:500;color:var(--text);margin:0 0 12px;letter-spacing:-.01em;font-family:'IBM Plex Sans',sans-serif}
.vx-help-content h3{font-size:var(--fs-4);font-weight:500;color:var(--text);margin:18px 0 6px;font-family:'IBM Plex Sans',sans-serif}
.vx-help-content p{margin:0 0 10px;color:var(--text2)}
.vx-help-content ul{margin:0 0 12px;padding-left:18px;color:var(--text2)}
.vx-help-content li{margin-bottom:4px}
.vx-help-content code{font-family:var(--font-mono);font-size:var(--fs-1);background:var(--surface2);padding:1px 6px;border-radius:5px;color:var(--text);border:0.5px solid var(--border)}
.vx-help-content .help-callout{background:var(--teal-dim);border-left:2px solid var(--teal);padding:10px 14px;border-radius:0 8px 8px 0;margin:12px 0;font-size:var(--fs-2);color:var(--text2)}
.vx-help-content .help-warn{background:var(--amber-dim);border-left:2px solid var(--amber);padding:10px 14px;border-radius:0 8px 8px 0;margin:12px 0;font-size:var(--fs-2);color:var(--text2)}
.vx-help-content .help-example{background:var(--surface2);border:0.5px solid var(--border);border-radius:8px;padding:12px 14px;margin:10px 0;font-size:var(--fs-2)}
.vx-help-content .help-example .label{font-family:var(--font-mono);font-size:var(--fs-1);font-weight:500;text-transform:uppercase;letter-spacing:.06em;color:var(--text3);margin-bottom:4px}
.vx-help-content .help-example.good{border-left:3px solid var(--green)}
.vx-help-content .help-example.good .label{color:var(--green)}
.vx-help-content .help-example.bad{border-left:3px solid var(--red)}
.vx-help-content .help-example.bad .label{color:var(--red)}
.vx-help-content .help-meta{display:inline-block;font-family:var(--font-mono);font-size:var(--fs-1);font-weight:400;color:var(--text3);background:var(--surface2);border:0.5px solid var(--border);padding:1px 7px;border-radius:8px;margin-left:8px;vertical-align:1px;letter-spacing:.02em}
.vx-help-content .help-figure{margin:14px 0;padding:14px;background:var(--surface2);border:0.5px solid var(--border);border-radius:8px;text-align:center}
.vx-help-content .help-figure svg{display:block;margin:0 auto;max-width:100%;height:auto}
.vx-help-content .help-figure-caption{margin-top:10px;font-size:var(--fs-1);color:var(--text3);font-style:italic;line-height:1.5}
.vx-help-content .help-anchor{display:block;position:relative;top:-8px;height:0;overflow:hidden;visibility:hidden}

.vx-help-inline{display:inline-flex;align-items:center;justify-content:center;width:14px;height:14px;border:1px solid var(--border2);border-radius:50%;color:var(--text3);font-size:9px;cursor:pointer;font-family:var(--font-mono);font-weight:500;margin-left:6px;background:transparent;padding:0;line-height:1;transition:all .15s;vertical-align:middle}
.vx-help-inline:hover{border-color:var(--accent);color:var(--accent)}

/* ─────────────────────────────────────────────────────────────────── */
/* Review queue page (added 2026-04-28)                                */
/* ─────────────────────────────────────────────────────────────────── */

.rv-stat{background:var(--surface);border:0.5px solid var(--border);border-radius:10px;padding:9px 14px;font-size:var(--fs-1);color:var(--text2);font-family:var(--font-mono)}
.rv-stat strong{color:var(--text);font-weight:500;font-size:var(--fs-4)}
.rv-stat-flagged strong{color:var(--amber)}
.rv-stat-low strong{color:var(--red)}

.rv-grid{display:grid;grid-template-columns:1fr 420px;gap:12px;align-items:start}
.rv-queue-pane{padding:0;overflow:hidden;max-height:680px;overflow-y:auto}
.rv-detail-pane{padding:18px 20px}

.rv-row{display:grid;grid-template-columns:120px 1fr auto;gap:12px;align-items:center;padding:12px 16px;border-bottom:0.5px solid var(--border);cursor:pointer;transition:background .12s}
.rv-row:last-child{border-bottom:none}
.rv-row:hover{background:var(--accent-dim2)}
.rv-row.selected{background:var(--accent-dim);border-left:3px solid var(--accent);padding-left:13px}
.rv-row.reviewed{opacity:.6}
.rv-sev{display:flex;justify-content:flex-start}
.rv-main{min-width:0}
.rv-tag-value{font-size:var(--fs-4);font-weight:500;color:var(--text)}
.rv-row-meta{font-size:var(--fs-1);color:var(--text3);margin-top:2px;font-family:var(--font-mono)}
.rv-row-reason{font-size:var(--fs-1);color:var(--text2);margin-top:4px;line-height:1.4;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.rv-conf{font-family:var(--font-mono);font-size:var(--fs-1);color:var(--text2);text-align:right;white-space:nowrap}
.rv-conf-num{display:block;font-size:var(--fs-4);color:var(--text);font-weight:500}
.rv-conf-lbl{font-size:var(--fs-1);color:var(--text3);text-transform:uppercase;letter-spacing:.06em}

.rv-pill{font-size:var(--fs-1);font-family:var(--font-mono);padding:3px 8px;border-radius:8px;font-weight:500;letter-spacing:.05em;text-align:center;display:inline-block}
.rv-pill-flagged{background:var(--amber-dim);color:var(--amber)}
.rv-pill-low{background:var(--red-dim);color:var(--red)}
.rv-pill-confirmed{background:var(--green-dim);color:var(--green)}
.rv-pill-rejected{background:var(--red-dim);color:var(--red)}

.rv-detail-hdr{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:14px;padding-bottom:12px;border-bottom:0.5px solid var(--border)}
.rv-dh-tag{font-size:var(--fs-6);font-weight:500;color:var(--text)}
.rv-dh-meta{font-size:var(--fs-1);color:var(--text3);font-family:var(--font-mono);margin-top:3px}

.rv-item-card{background:var(--surface2);border:0.5px solid var(--border);border-radius:10px;padding:12px 14px;margin-bottom:10px;border-left-width:3px}
.rv-item-flagged{border-left-color:var(--amber)}
.rv-item-low{border-left-color:var(--red)}
.rv-item-confirmed{border-left-color:var(--green);opacity:.85}
.rv-item-corrected{border-left-color:var(--blue);opacity:.85}
.rv-item-rejected{border-left-color:var(--red);opacity:.7}
.rv-item-high{border-left-color:rgba(99,120,180,0.3)}

.rv-item-hdr{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:6px}
.rv-item-name{font-size:var(--fs-1);color:var(--text3);font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.06em}
.rv-item-val{font-size:var(--fs-4);font-weight:500;color:var(--text);margin-bottom:4px}
.rv-item-orig{font-size:var(--fs-1);color:var(--text3);font-family:var(--font-mono);margin-bottom:4px;text-decoration:line-through}
.rv-item-foot{font-size:var(--fs-1);color:var(--text3);font-family:var(--font-mono);display:flex;gap:10px;align-items:center;margin-bottom:6px}
.rv-item-reason{color:var(--text2);font-family:var(--font-body);line-height:1.4}
.rv-remarks{width:100%;font-size:var(--fs-1);padding:6px 9px;border:0.5px solid var(--border);border-radius:6px;background:var(--surface);font-family:var(--font-body);outline:none;resize:vertical;min-height:32px;color:var(--text);margin-top:6px}
.rv-remarks:focus{border-color:var(--accent)}

.rv-actions{display:flex;gap:6px;margin-top:8px;align-items:center}
.rv-correct-input{flex:1;font-size:var(--fs-4);padding:5px 9px;border:1px solid var(--accent);border-radius:6px;background:var(--surface);color:var(--text);font-family:var(--font-mono);outline:none}
.rv-act{font-size:var(--fs-1);padding:5px 12px;border-radius:7px;border:1px solid var(--border2);background:var(--surface);color:var(--text2);cursor:pointer;font-family:var(--font-body);font-weight:500;transition:all .15s}
.rv-act:hover{border-color:var(--text);color:var(--text)}
.rv-act-confirm:hover{border-color:var(--green);color:var(--green);background:var(--green-light)}
.rv-act-correct:hover{border-color:var(--blue);color:var(--blue);background:var(--blue-dim)}
.rv-act-reject:hover{border-color:var(--red);color:var(--red);background:var(--red-light)}

.rv-detail-footer{margin-top:18px;padding-top:14px;border-top:0.5px solid var(--border);display:flex;gap:8px;justify-content:space-between}

.rv-viewer-inline{margin-top:14px;height:520px;border-radius:14px;overflow:hidden;border:0.5px solid var(--border);position:relative;background:var(--nav-bg)}

/* Show-on-drawing pulse — draws attention when a new tag is selected */
@keyframes rv-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(217,119,6,0.55); }
  50%      { box-shadow: 0 0 0 6px rgba(217,119,6,0); }
}
.rv-show-pulse {
  animation: rv-pulse 1.6s ease-in-out infinite;
  border-color: var(--amber) !important;
  color: var(--amber) !important;
}
.rv-show-pulse:hover {
  animation: none;
  background: var(--amber-light);
}

/* =============================================================================
   Phase A — Review property grid + lock banner
   Replaces the rv-item-card stack with a two-column grid:
     col 1: property name (label, sub-label, badge)
     col 2: value (editable on click) + meta + remarks
     col 3: action buttons
   Designed to reuse existing tokens (--surface2, --border, --amber, --green,
   --red, --blue) so the review page keeps a consistent look across the
   queue / detail panel.
   ============================================================================= */

/* Lock banner — shown above the grid when drawing or package is signed off */
.rv-lock-banner {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 14px;
  margin: 0 0 14px 0;
  background: var(--amber-dim);
  border: 1px solid var(--amber);
  border-radius: 10px;
  font-size:var(--fs-2);
  color: var(--text);
}
.rv-lock-icon { font-size: 14px; }
.rv-lock-text { line-height: 1.4; }

/* Toolbar above the grid: show-all toggle + count */
.rv-grid-toolbar {
  display: flex; justify-content: space-between; align-items: center;
  padding: 8px 14px; margin-bottom: 4px;
  border-bottom: 0.5px solid var(--border);
}
.rv-toggle {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: var(--fs-2); color: var(--text2);
  cursor: pointer; user-select: none;
  /* Override the global label{} rule — this is an inline switch, not a
     form-field caption. Display:inline-flex also overrides display:block
     from the global, otherwise the toggle would force a line of its own. */
  font-family: var(--font-body);
  text-transform: none;
  letter-spacing: normal;
  font-weight: 400;
  margin-bottom: 0;
  white-space: nowrap;
}
.rv-toggle input { margin: 0; flex: 0 0 auto; }
.rv-grid-meta {
  font-size:var(--fs-1); color: var(--text3);
  font-family: var(--font-mono);
  letter-spacing: .04em;
}

/* Grid container + header row */
.rv-prop-grid {
  display: flex; flex-direction: column;
  border: 0.5px solid var(--border);
  border-radius: 10px;
  overflow: hidden;
  background: var(--surface2);
}
.rv-prop-grid-head {
  display: grid;
  grid-template-columns: minmax(140px, 1fr) minmax(180px, 2fr) auto;
  gap: 0;
  padding: 8px 14px;
  background: var(--surface);
  border-bottom: 0.5px solid var(--border);
  font-size:var(--fs-1);
  font-family: var(--font-mono);
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--text3);
  font-weight: 500;
}
.rv-prop-col-name, .rv-prop-col-val, .rv-prop-col-act { padding: 0 6px; }
.rv-prop-col-act { text-align: right; }

/* Empty state */
.rv-prop-empty {
  padding: 28px 16px;
  text-align: center;
  color: var(--text3);
  font-size:var(--fs-2);
}

/* Each row */
.rv-prop-row {
  display: grid;
  grid-template-columns: minmax(140px, 1fr) minmax(180px, 2fr) auto;
  gap: 0;
  padding: 12px 14px;
  border-top: 0.5px solid var(--border);
  border-left: 3px solid transparent;
  align-items: start;
  transition: background .12s;
}
.rv-prop-row:first-of-type { border-top: 0; }  /* directly below the header */
.rv-prop-row:hover { background: rgba(255,255,255,.02); }

/* Row state — left edge colour matches the current state.
   Mirrors the legacy rv-item-* convention so reviewers retain muscle memory. */
.rv-prop-row-flagged   { border-left-color: var(--amber); }
.rv-prop-row-low       { border-left-color: var(--red); }
.rv-prop-row-high      { border-left-color: rgba(99,120,180,0.30); }
.rv-prop-row-confirmed { border-left-color: var(--green);  opacity: .9; }
.rv-prop-row-corrected { border-left-color: var(--blue);   opacity: .9; }
.rv-prop-row-rejected  { border-left-color: var(--red);    opacity: .75; }

/* Column 1: property name */
.rv-prop-name { padding-right: 12px; }
.rv-prop-name-main {
  font-size:var(--fs-4); font-weight: 500;
  color: var(--text); margin-bottom: 2px;
}
.rv-prop-name-sub {
  font-size:var(--fs-1);
  color: var(--text3);
  font-family: var(--font-mono);
  text-transform: uppercase; letter-spacing: .05em;
  margin-bottom: 6px;
}
.rv-prop-badge { margin-top: 4px; }

/* Column 2: value */
.rv-prop-val { padding-right: 12px; min-width: 0; }
.rv-prop-val-text {
  font-size:var(--fs-4); color: var(--text);
  word-break: break-word;
  padding: 2px 4px;
  border-radius: 5px;
  transition: background .12s;
}
.rv-prop-val-text:hover {
  background: rgba(255,255,255,.04);
}
.rv-prop-empty-val {
  color: var(--text3); font-style: italic; font-size:var(--fs-4);
}
.rv-prop-orig {
  font-size:var(--fs-1); color: var(--text3);
  font-family: var(--font-mono);
  text-decoration: line-through;
  margin-top: 4px;
}
.rv-prop-meta {
  font-size:var(--fs-1); color: var(--text3);
  font-family: var(--font-mono);
  display: flex; gap: 10px; align-items: center;
  margin-top: 6px;
}

/* Column 3: actions */
.rv-actions-grid {
  display: flex; flex-direction: column; gap: 4px;
  align-items: stretch; min-width: 110px;
}
.rv-actions-grid .rv-act:disabled {
  opacity: .4; cursor: not-allowed;
}
.rv-actions-grid .rv-act:disabled:hover {
  border-color: var(--border2); color: var(--text2);
  background: var(--surface);
}

/* Inline-edit input — sits in column 2 during an edit */
.rv-correct-input {
  width: 100%;
  padding: 6px 10px;
  font-size:var(--fs-4);
  font-family: var(--font-body);
  background: var(--surface);
  border: 1px solid var(--blue);
  border-radius: 6px;
  color: var(--text);
  box-sizing: border-box;
}
.rv-correct-input:focus { outline: 2px solid var(--blue); outline-offset: -1px; }

/* Remarks field — hidden by default, revealed during inline edit */
.rv-remarks {
  width: 100%; box-sizing: border-box;
  margin-top: 6px;
  padding: 6px 8px;
  font-size:var(--fs-2);
  font-family: var(--font-body);
  background: var(--surface);
  border: 0.5px solid var(--border);
  border-radius: 6px;
  color: var(--text);
  resize: vertical; min-height: 32px;
}

/* Mobile / narrow layout — stack columns vertically, action row last.
   Threshold matches typical sidebar-collapsed widths. */
@media (max-width: 720px) {
  .rv-prop-grid-head { display: none; }
  .rv-prop-row {
    grid-template-columns: 1fr;
    gap: 8px;
  }
  .rv-prop-name, .rv-prop-val, .rv-prop-act {
    padding-right: 0;
  }
  .rv-actions-grid { flex-direction: row; }
}

/* Phase A "clean" pill — used when 'Show all' surfaces high-confidence
   attributes that were previously hidden. Subtle, since these don't need
   review; just informational. */
.rv-pill-clean {
  background: rgba(99,120,180,0.10);
  color: var(--text3);
  border: 1px solid rgba(99,120,180,0.20);
}

/* =============================================================================
   Phase A — Compact property grid (revised)
   Replaces the cards-style grid with one-line rows: Property | Value | Acts.
   ============================================================================= */

/* Compact detail header — just a small caption + Show on drawing button. */
.rv-detail-hdr-compact {
  padding: 12px 14px;
  border-bottom: 0.5px solid var(--border);
  display: flex; justify-content: space-between; align-items: center;
  gap: 12px;
}
.rv-detail-title {
  font-size:var(--fs-2);
  font-family: var(--font-mono);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text2);
  font-weight: 500;
}

/* Section markers — used when both 'Needs review' and 'Other' groups
   are present, otherwise omitted to avoid useless headers. */
.rv-prop-sect {
  padding: 4px 14px;
  background: var(--surface2);
  font-size:var(--fs-1);
  font-family: var(--font-mono);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text3);
  border-bottom: 0.5px solid var(--border);
  border-top: 0.5px solid var(--border);
  display: flex; justify-content: space-between; align-items: center;
}
.rv-prop-sect:first-child { border-top: 0; }

/* Override the older row layout — three columns. Long labels wrap to
   a second line; row height grows to fit. */
.rv-prop-grid > .rv-prop-row {
  display: grid;
  grid-template-columns: minmax(0, 130px) minmax(0, 1fr) 84px;
  align-items: center;
  border-top: 0.5px solid var(--border);
  border-bottom: 0;
  border-left: 0;            /* override the old left-edge tint stripe */
  min-height: 32px;
  padding: 6px 14px;
  transition: background .08s;
}
.rv-prop-grid > .rv-prop-row:first-child { border-top: 0; }
.rv-prop-grid > .rv-prop-row:hover { background: var(--surface2); }

/* Editing row — allow it to grow */
.rv-prop-grid > .rv-prop-row.rv-prop-row-editing {
  height: auto;
  padding: 6px 14px;
}

/* Subtle amber tint for actionable rows */
.rv-prop-grid > .rv-prop-row-actionable {
  background: rgba(217,119,6,0.05);
}
.rv-prop-grid > .rv-prop-row-actionable:hover {
  background: rgba(217,119,6,0.10);
}

/* Ghost rows — profile_field present, but no source has a value. Use
   a muted blue-grey tint to distinguish from amber "needs review" rows.
   The value cell shows "(not extracted)" and the only action is Correct
   (which creates the tag_attributes row server-side). */
.rv-prop-grid > .rv-prop-row-ghost {
  background: rgba(100,116,139,0.06);
}
.rv-prop-grid > .rv-prop-row-ghost:hover {
  background: rgba(100,116,139,0.12);
}
.rv-prop-grid > .rv-prop-row-ghost .rv-prop-empty-val {
  font-style: italic;
  color: var(--text3);
}

/* Property name cell — wraps to multiple lines when long. */
.rv-prop-grid > .rv-prop-row .rv-prop-name {
  font-size:var(--fs-2);
  color: var(--text2);
  padding: 0;
  display: block;
  min-width: 0;
  white-space: normal;
  overflow-wrap: anywhere;
  word-break: break-word;
  line-height: 1.3;
}
/* Same wrap behaviour for the inner wrapper Tag Search uses. */
.rv-prop-grid > .rv-prop-row .rv-prop-name .rv-prop-name-main {
  white-space: normal;
  overflow-wrap: anywhere;
  word-break: break-word;
  min-width: 0;
  max-width: 100%;
  margin-bottom: 0;
  line-height: 1.3;
}
.rv-prop-grid > .rv-prop-row-actionable .rv-prop-name {
  font-weight: 500;
  color: var(--text);
}

/* Value cell — wraps to align with the (possibly two-line) name cell. */
.rv-prop-grid > .rv-prop-row .rv-prop-val {
  font-size:var(--fs-4);
  color: var(--text);
  white-space: normal;
  overflow-wrap: anywhere;
  word-break: break-word;
  padding: 0 4px;
  line-height: 1.3;
}
.rv-prop-grid > .rv-prop-row .rv-prop-val .rv-prop-empty-val {
  color: var(--text3);
  font-style: italic;
}
/* State decorations on value cell */
.rv-prop-grid > .rv-prop-row .rv-prop-val.rv-val-confirmed-mark::after {
  content: ' ✓';
  color: var(--green);
  font-weight: 600;
}
.rv-prop-grid > .rv-prop-row .rv-prop-val.rv-val-rejected-mark::after {
  content: ' ✗';
  color: var(--red);
  font-weight: 600;
}
.rv-prop-grid > .rv-prop-row .rv-prop-val.rv-val-corrected-mark {
  color: var(--blue);
}

/* Actions cell — flex row, justified to right */
.rv-prop-grid > .rv-prop-row .rv-prop-acts,
.rv-prop-grid > .rv-prop-row .rv-prop-acts-empty {
  display: flex;
  gap: 2px;
  justify-self: end;
}

/* Icon buttons — replace the old text buttons. 24×24 click target with
   a 14×14 SVG inside. Coloured on hover per action. */
.rv-icon-btn {
  width: 24px; height: 24px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid transparent;
  background: transparent;
  border-radius: 4px;
  cursor: pointer;
  color: var(--text3);
  transition: all .12s;
  padding: 0;
}
.rv-icon-btn svg { width: 14px; height: 14px; }
.rv-icon-btn:hover            { background: var(--surface3); }
.rv-icon-btn.rv-ic-confirm:hover {
  color: var(--green); border-color: var(--green); background: var(--green-light);
}
.rv-icon-btn.rv-ic-correct:hover {
  color: var(--blue);  border-color: var(--blue);  background: var(--blue-dim, #dbeafe);
}
.rv-icon-btn.rv-ic-reject:hover {
  color: var(--red);   border-color: var(--red);   background: var(--red-light);
}
.rv-icon-btn.rv-ic-cancel:hover {
  color: var(--text);  border-color: var(--border2); background: var(--surface3);
}
.rv-icon-btn:disabled { opacity: 0.35; cursor: not-allowed; }
.rv-icon-btn:disabled:hover { background: transparent; border-color: transparent; color: var(--text3); }

/* Inline-edit input — sits in the value cell */
.rv-prop-grid > .rv-prop-row.rv-prop-row-editing .rv-prop-val {
  overflow: visible;
}
.rv-prop-grid > .rv-prop-row .rv-correct-input {
  width: 100%;
  padding: 4px 8px;
  font-size:var(--fs-4);
  font-family: var(--font-body);
  background: var(--surface);
  border: 1px solid var(--accent, var(--blue));
  border-radius: 4px;
  color: var(--text);
  box-sizing: border-box;
  outline: none;
}
.rv-prop-grid > .rv-prop-row .rv-correct-input:focus {
  outline: 2px solid var(--accent, var(--blue));
  outline-offset: -1px;
}

/* Empty-state message */
.rv-prop-grid > .rv-prop-empty {
  padding: 28px 16px;
  text-align: center;
  color: var(--text3);
  font-size:var(--fs-2);
}

/* Hide the old Property/Value/Action header row from the previous layout —
   the new layout uses a small caption + section markers instead. */
.rv-prop-grid > .rv-prop-grid-head { display: none; }

/* Mobile / narrow tweaks — keep one-line layout but shrink the action cell */
@media (max-width: 480px) {
  .rv-prop-grid > .rv-prop-row {
    grid-template-columns: 110px 1fr 70px;
  }
}

/* =============================================================================
   Phase A v2 — Hamburger nav (default hidden, overlay mode, optional pin)
   ============================================================================= */

/* Default nav state: completely hidden off the left edge.
   The .app keeps display:flex but the nav is translated out so the
   main content fills the viewport. */
.nav {
  position: fixed;
  top: 0; left: 0;
  height: 100vh;
  z-index: 80;
  transform: translateX(-100%);
  transition: transform .22s ease;
  box-shadow: 0 0 0 rgba(0,0,0,0);
}

/* OPEN (unpinned overlay) — slide in, drop a shadow */
html.nav-open .nav {
  transform: translateX(0);
  box-shadow:none;
}

/* PINNED — nav permanent, page resizes around it. Override the fixed
   positioning with relative so it takes layout space again. */
html.nav-pinned .nav {
  position: relative;
  transform: translateX(0);
  box-shadow: none;
}

/* Mask — visible only when nav-open AND not pinned. Catches outside-clicks. */
.nav-mask {
  position: fixed; inset: 0;
  background: rgba(15,17,22,0.35);
  z-index: 70;
  opacity: 0;
  pointer-events: none;
  transition: opacity .18s ease;
}
html.nav-open:not(.nav-pinned) .nav-mask {
  opacity: 1;
  pointer-events: auto;
}

/* Hamburger button — top-left, always reachable. Hidden when pinned
   (since the pinned nav is already showing). */
.nav-hamburger {
  position: fixed;
  top: 14px; left: 14px;
  width: 36px; height: 36px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--surface, var(--surface));
  /* Floats over the page, so it needs a real edge AND lift. At 0.5px
     --border this was 1.29:1 against the canvas -- invisible. */
  border: 1px solid var(--border-control);
  box-shadow: var(--shadow-pop);
  border-radius: 8px;
  color: var(--text);
  cursor: pointer;
  z-index: 90;                    /* above nav (80) so it's always clickable */
  transition: background .15s, border-color .15s, transform .22s ease, opacity .18s ease;
  box-shadow:none;
}
.nav-hamburger:hover {
  background: var(--surface3, var(--surface2));
  border-color: var(--border2, #d4d7de);
}
.nav-hamburger svg { width: 16px; height: 16px; }
html.nav-open .nav-hamburger {
  /* Slide the button to the right edge of the open nav so it stays
     "anchored" to the active surface. */
  transform: translateX(var(--nav-width));
}
html.nav-pinned .nav-hamburger {
  /* When pinned, hide the hamburger — the user has the nav permanently. */
  opacity: 0; pointer-events: none;
  transform: translateX(var(--nav-width));
}

/* Pin button — sits at the top of the open nav. Toggles pinned state. */
.nav-pinrow {
  display: flex; justify-content: flex-end;
  padding: 8px 10px 0;
}
.nav-pin {
  width: 28px; height: 28px;
  background: transparent;
  border: 1px solid rgba(255,255,255,0.15);
  border-radius: 6px;
  color: rgba(255,255,255,0.65);
  cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  transition: all .12s;
}
.nav-pin:hover {
  background: rgba(255,255,255,0.08);
  color:var(--on-fill);
  border-color: rgba(255,255,255,0.30);
}
.nav-pin svg { width: 14px; height: 14px; }
html.nav-pinned .nav-pin {
  background: rgba(91,109,238,0.30);
  border-color: rgba(91,109,238,0.60);
  color:var(--on-fill);
}

/* Adjust the .main padding so the hamburger doesn't overlap page content
   when nav is hidden. The hamburger is 36+14*2 = 64px from the left edge. */
.main { padding-left: 0; }
html:not(.nav-pinned) .page-header,
html:not(.nav-pinned) .welcome-band {
  padding-left: 64px;            /* leave room for the hamburger */
}

/* =============================================================================
   Phase A v2 — Review workbench layout
   ============================================================================= */

/* Top toolbar — replaces the old multi-row scope/field/show/viewer block.
   Just Show segmented (left) and Mode segmented (right). */
.rv-topbar {
  padding: 10px 16px !important;
  margin-bottom: 12px;
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px;
}
.rv-topbar-left, .rv-topbar-right {
  display: flex; align-items: center; gap: 10px;
}
.rv-tb-label {
  font-size:var(--fs-1); color: var(--text3);
  font-family: var(--font-mono); text-transform: uppercase;
  letter-spacing: 0.06em;
}

/* Three-pane shell. Override the older .rv-grid two-pane layout.
   The shell flex-fills the review page (which is itself viewport-bound),
   so each column gets a definite height. The queue table scrolls inside;
   the inspector / drawing pane scroll inside their own pane. */
.rv-shell {
  display: grid;
  grid-template-columns: 200px minmax(0, 1fr) minmax(380px, 480px);
  gap: 12px;
  align-items: stretch;
  flex: 1 1 auto;
  min-height: 0;        /* lets grid children shrink below their content size */
  overflow: hidden;     /* belt-and-braces — children handle their own scroll */
}

/* Sidebar */
.rv-side {
  padding: 14px 16px !important;
  /* Was: align-self:start; position:sticky; top:12px — those tricks were
     a workaround for the un-bounded shell. With the shell now flex-bound
     to the page, the sidebar simply stretches. Internal overflow handles
     unusually long filter lists. */
  overflow-y: auto;
  min-height: 0;
}
.rv-side-h3 {
  font-size:var(--fs-1); color: var(--text3);
  margin: 0 0 8px;
  font-family: var(--font-mono);
  text-transform: uppercase; letter-spacing: 0.06em;
  font-weight: 500;
}
.rv-side-h3:not(:first-child) { margin-top: 16px; }
.rv-side-list {
  display: flex; flex-direction: column; gap: 2px;
}
.rv-side-row {
  display: flex; align-items: center; gap: 8px;
  font-size: var(--fs-4); color: var(--text2);
  padding: 4px 6px;
  border-radius: 4px;
  cursor: pointer;
  user-select: none;
  margin: 0;
  /* Override the global label{} rule (line ~109) which forces uppercase /
     mono / letter-spacing on every <label>. Sidebar rows are <label>
     elements but they are interactive list items, not form-field captions. */
  font-family: var(--font-body);
  text-transform: none;
  letter-spacing: normal;
  font-weight: 400;
}
.rv-side-row:hover { background: var(--surface3, var(--surface2)); }
.rv-side-row input { margin: 0; flex: 0 0 auto; }
/* Label span flexes to fill, count clings to the right and never wraps.
   Explicit .rv-side-label class instead of the fragile :nth-child(2)
   selector — works regardless of child order, whitespace text nodes,
   or wrapping. The legacy :nth-child(2) is kept as a fallback for any
   row that hasn't been updated yet. */
.rv-side-row > .rv-side-label,
.rv-side-row > span:nth-child(2) {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.rv-side-empty {
  font-size:var(--fs-1);
  color: var(--text3);
  font-style: italic;
  padding: 4px 6px;
}
.rv-side-count {
  font-family: var(--font-mono);
  font-size:var(--fs-1); color: var(--text3);
  flex: 0 0 auto;
  white-space: nowrap;
  margin-left: auto;
  /* Fixed-width column so counts in different filter sections line up
     vertically (3 digits + a little breathing room). */
  min-width: 28px;
  text-align: right;
}
.rv-side-row.rv-side-all {
  border-bottom: 0.5px solid var(--border);
  padding-bottom: 6px;
  margin-bottom: 4px;
  font-weight: 500;
  color: var(--text);
}

/* Centre column: queue table */
.rv-table {
  padding: 0 !important;
  display: flex; flex-direction: column;
  overflow: hidden;
  min-width: 0;
}
.rv-table-tools {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 14px;
  border-bottom: 0.5px solid var(--border);
  flex-wrap: wrap;
}
.rv-table-tools input[type="search"] {
  flex: 1; max-width: 320px; min-width: 180px;
  font-size:var(--fs-4);
}
.rv-table-count {
  font-size:var(--fs-2);
  color: var(--text3);
  font-family: var(--font-mono);
  margin-left: auto;
}
.rv-cols-wrap { position: relative; }
.rv-cols-btn {
  display: inline-flex; align-items: center; gap: 6px;
  font-size:var(--fs-2);
  padding: 5px 12px;
  border-radius: 6px;
  border: 0.5px solid var(--border);
  background: var(--surface, var(--surface));
  color: var(--text2);
  cursor: pointer;
  transition: all .12s;
}
.rv-cols-btn:hover {
  border-color: var(--text2);
  color: var(--text);
}
.rv-cols-icon { font-size: 14px; line-height: 1; }
.rv-cols-menu {
  position: absolute;
  top: 100%; right: 0;
  margin-top: 6px;
  background: var(--surface, var(--surface));
  border: 0.5px solid var(--border);
  border-radius: 8px;
  box-shadow:none;
  min-width: 240px;
  z-index: 20;
  padding: 6px;
  display: none;
}
.rv-cols-menu.open { display: block; }
.rv-cols-menu h4 {
  font-size:var(--fs-1); color: var(--text3);
  text-transform: uppercase; letter-spacing: 0.06em;
  font-family: var(--font-mono);
  padding: 8px 10px 4px;
  margin: 0; font-weight: 500;
}
.rv-col-item {
  display: flex; align-items: center; gap: 10px;
  padding: 6px 10px;
  border-radius: 4px;
  font-size:var(--fs-4);
  cursor: pointer;
  user-select: none;
}
.rv-col-item input { flex: 0 0 auto; }
.rv-col-item > span { flex: 0 0 auto; }
.rv-col-item > span:last-child { flex: 1 1 auto; min-width: 0; }
.rv-col-item:hover { background: var(--surface2); }
.rv-col-item.fixed { color: var(--text3); cursor: default; }
.rv-col-item.fixed:hover { background: transparent; }
.rv-col-handle {
  color: var(--text3); font-family: var(--font-mono);
  font-size:var(--fs-2); cursor: grab;
}
.rv-col-handle.disabled { opacity: 0.3; cursor: default; }
.rv-col-item.drag-over-before { box-shadow: inset 0 2px 0 0 var(--accent); }
.rv-col-item.drag-over-after  { box-shadow: inset 0 -2px 0 0 var(--accent); }
.rv-cols-foot {
  border-top: 0.5px solid var(--border);
  padding: 8px 10px; margin-top: 4px;
  display: flex; justify-content: space-between;
  font-size:var(--fs-1); color: var(--text3);
}
.rv-cols-foot button {
  background: transparent; border: 0; color: var(--accent);
  cursor: pointer; font-size:var(--fs-2); padding: 0;
}
.rv-cols-foot button:hover { text-decoration: underline; }

.rv-table-scroll { overflow: auto; flex: 1; }

.rv-tbl {
  width: 100%; border-collapse: collapse;
  font-size:var(--fs-4);
  table-layout: fixed;
}
.rv-tbl thead th {
  background: var(--surface2);
  text-align: left;
  padding: var(--pad-head-y) var(--pad-cell-x);
  font-size:var(--fs-5);
  font-family: var(--font-body);
  text-transform: none; letter-spacing: -0.01em;
  color: var(--text); font-weight: 600;
  border-bottom: 0.5px solid var(--border);
  user-select: none;
  position: sticky; top: 0; z-index: 1;
  overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
}
/* NOTE: do NOT set `position: relative` here — it would override the
   `position: sticky` set on `.rv-tbl thead th` above (same specificity,
   later rule wins). The .rv-tbl-resize child uses position:absolute with
   explicit top/right/bottom offsets and a sticky parent is also a
   containing block for absolutely-positioned descendants, so this works. */
.rv-tbl thead th.draggable { cursor: grab; }
.rv-tbl thead th.draggable:hover { background: var(--surface3); color: var(--text2); }
.rv-tbl thead th.dragging { opacity: 0.4; }
.rv-tbl thead th.drop-target-before { box-shadow: inset 3px 0 0 0 var(--accent); }
.rv-tbl thead th.drop-target-after  { box-shadow: inset -3px 0 0 0 var(--accent); }
.rv-tbl-resize {
  position: absolute;
  top: 0; right: -3px; bottom: 0;
  width: 6px;
  cursor: col-resize;
  z-index: 2;
}
.rv-tbl-resize:hover, .rv-tbl-resize.active { background: var(--accent); }

.rv-tbl tbody tr {
  border-bottom: 0.5px solid var(--border);
  cursor: pointer;
  transition: background .1s;
}
.rv-tbl tbody tr:hover { background: var(--surface2); }
.rv-tbl tbody tr.selected { background: var(--accent-light, var(--accent-dim)); }
.rv-tbl tbody tr.selected td:first-child {
  border-left: 3px solid var(--accent);
  padding-left: 9px;
}
.rv-tbl td {
  padding: 10px 12px;
  vertical-align: top;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.rv-tbl td.rv-cell-tag {
  font-family: var(--font-mono);
  font-weight: 500;
  color: var(--text);
}
.rv-tbl td.rv-cell-tag { white-space: nowrap; }
.rv-tag-ic { margin-right: 8px; vertical-align: middle; }
.rv-tag-txt { vertical-align: middle; }
.rv-tbl td.rv-cell-muted   { color: var(--text3); }
.rv-tbl td.rv-cell-conf    { font-family: var(--font-mono); font-size:var(--fs-2); color: var(--text2); }
.rv-tbl td.rv-cell-reason  { color: var(--text3); font-size:var(--fs-2); }

/* Right pane is the existing inspector — already set up. We just make sure
   it slots into the new shell layout cleanly. The shell bounds its height,
   so internal overflow scrolls naturally — no more sticky/calc tricks. */
.rv-detail-pane {
  overflow: auto;
  min-height: 0;
}

/* ──────────────────────────────────────────────────────────────────────────
   Drawing-mode pane (F4 — Phase 1)

   Layout:
     The shell normally is  [200px | 1fr | 380–480px]  (sidebar | queue | inspector).
     In Drawing mode it becomes  [200px | minmax(0,360px) | 1fr]
     so the queue narrows and the right pane (now the drawing pane) takes
     the rest. The .rv-shell-drawing class on .rv-shell triggers the swap.

     The inspector pane (.rv-detail-pane) is hidden via display:none and
     the drawing pane (.rv-drawing-pane) is shown — both are siblings in
     the shell grid so swapping one for the other doesn't reflow the queue.

   Pane structure:
     .rv-drawing-pane
       .rv-draw-toolbar     — dark bar with Zoom / Overlay / Add / Relocate
       .rv-drawing-canvas   — grows to fill; hosts viewer + overlay (Phase 2)
       .rv-drawing-footer   — one-line status strip for the selected tag
   ────────────────────────────────────────────────────────────────────────── */

.rv-shell-drawing {
  /* Queue narrows, drawing pane takes everything else. */
  grid-template-columns: 200px minmax(0, 360px) minmax(0, 1fr) !important;
}

.rv-drawing-pane {
  /* Card padding cleared — toolbar/footer already provide their own
     visual chrome and we want the canvas to fill edge-to-edge. */
  padding: 0 !important;
  display: flex;
  flex-direction: column;
  /* No min-height — the shell row provides height bound now.
     The canvas inside still has min-height:360px as a usability floor. */
  min-height: 0;
  overflow: hidden;
}

/* Toolbar — dark bar at top of the pane */
.rv-draw-toolbar {
  display: flex;
  align-items: center;
  gap: 0;
  padding: 8px 14px;
  background: var(--nav-surface);
  color:var(--on-fill);
  border-top-left-radius: inherit;
  border-top-right-radius: inherit;
  font-size:var(--fs-2);
  flex: 0 0 auto;
}
.rv-draw-tool {
  /* Reset the shared .btn baseline — these are inline-text-style buttons */
  background: transparent;
  border: 0;
  color: var(--border2);
  font-family: var(--font-body);
  font-size:var(--fs-2);
  font-weight: 500;
  padding: 4px 8px;
  border-radius: 4px;
  cursor: pointer;
  transition: background .12s, color .12s;
}
.rv-draw-tool:hover:not(:disabled) {
  background: rgba(255,255,255,0.08);
  color:var(--on-fill);
}
.rv-draw-tool:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}
.rv-draw-tool-on {
  /* Active state — used for "Overlay on" when overlay is enabled */
  color:var(--on-fill);
  font-weight: 600;
}
.rv-draw-tool-sep {
  color: rgba(255,255,255,0.25);
  padding: 0 2px;
  user-select: none;
}

/* Active-drawing label, right-aligned at the end of the toolbar.
   Pushed to the right with auto-margin; truncates with ellipsis on
   narrow pane widths so the toolbar buttons remain usable. */
.rv-draw-label {
  margin-left: auto;
  padding: 4px 10px;
  color: var(--border2);
  font-size:var(--fs-1);
  font-weight: 500;
  letter-spacing: 0.01em;
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  background: rgba(255,255,255,0.06);
  border-radius: 4px;
  max-width: 240px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  user-select: text;          /* let users copy the drawing number */
}
.rv-draw-label[data-empty="true"] {
  /* Faded look when no drawing is loaded yet */
  opacity: 0.5;
  font-style: italic;
}

/* Canvas area — grows to fill, striped placeholder until Phase 2 */
.rv-drawing-canvas {
  flex: 1 1 auto;
  position: relative;          /* viewer + overlay both absolute-positioned */
  background:
    repeating-linear-gradient(135deg,
      var(--surface3) 0px, var(--surface3) 12px,
      var(--surface) 12px, var(--surface) 24px);
  /* No fixed min-height — the flex parent (rv-drawing-pane) is bounded by
     the shell row, and we want the canvas to shrink with it on short
     viewports rather than push the footer below the fold. */
  min-height: 0;
  overflow: hidden;
}
.rv-drawing-canvas-empty {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  color: var(--text3);
  font-size:var(--fs-4);
  pointer-events: none;
}

/* Footer — one-line status strip showing selected tag's identity */
.rv-drawing-footer {
  flex: 0 0 auto;
  padding: 10px 14px;
  border-top: 0.5px solid var(--border);
  background: var(--surface, var(--surface));
  font-size:var(--fs-2);
  color: var(--text2);
  display: flex;
  align-items: center;
  gap: 12px;
  white-space: nowrap;
  overflow-x: auto;
}
.rv-drawing-footer-empty {
  color: var(--text3);
}
.rv-drawing-footer-tagid {
  font-family: var(--font-mono);
  font-weight: 600;
  color: var(--text);
}
.rv-drawing-footer-prop {
  display: inline-flex;
  gap: 4px;
}
.rv-drawing-footer-prop-label {
  color: var(--text3);
}
.rv-drawing-footer-prop-value {
  color: var(--text);
  font-weight: 500;
}

/* Responsive — collapse sidebar on narrower widths.
   In Drawing mode at <1280px, keep the same proportional split but smaller. */
@media (max-width: 1280px) {
  .rv-shell { grid-template-columns: 180px minmax(0, 1fr) minmax(340px, 420px); }
  .rv-shell-drawing {
    grid-template-columns: 180px minmax(0, 320px) minmax(0, 1fr) !important;
  }
}
@media (max-width: 1024px) {
  /* On narrow screens the columns stack vertically. We give up the
     viewport-bounded layout in that case — the page scrolls naturally
     instead, which is more usable on small screens. */
  .page#page-review,
  .page#page-tag-search { height: auto; }
  .page#page-review.active,
  .page#page-tag-search.active { display: block; }
  .rv-shell {
    grid-template-columns: 1fr;
    flex: 0 0 auto;
    overflow: visible;
  }
  .rv-shell-drawing { grid-template-columns: 1fr !important; }
  .rv-side {
    overflow: visible;
  }
  .rv-detail-pane, .rv-drawing-pane {
    overflow: visible;
  }
}

/* ──────────────────────────────────────────────────────────────────────
   Tag Search — minimal additions on top of shared .rv-* shell.

   The page is structurally identical to Review: 3-column grid with a
   property grid in the right pane by default. The header-level Drawing
   toggle swaps the right pane to the embedded viewer (using the same
   .rv-shell-drawing column reflow Review uses).

   Inside the drawing pane, an Inspector/Viewer toggle in the toolbar
   swaps the canvas content between the PDF viewer and the spider
   relationship view (#ts-inspect-host).
   ────────────────────────────────────────────────────────────────────── */

#ts-inspect-host { background: var(--surface); }
#ts-drawing-canvas { position: relative; }

/* Property grid card — strip the default .card padding so the title bar
   meets the card edges; the rv-prop-* and rv-detail-* classes provide
   their own internal padding (same as Review). */
#ts-detail-pane {
  padding: 0 !important;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
#ts-detail-body {
  flex: 1 1 auto;
  overflow: auto;
  min-height: 0;
}
/* The grid itself keeps its border (matches Review). Give it a small
   inset so the bordered table doesn't touch the card edge. */
#ts-detail-body .rv-prop-grid {
  margin: 12px;
}
/* Selection strip + grid-title section both live INSIDE the grid card
   now (Phase 3 unification). Strip the inline strip's separate border
   so it sits flush with the rows below — the grid border handles the
   outer frame. */
#ts-detail-body .ts-prop-selection-inline {
  border-bottom: 0.5px solid var(--border);
}
/* Title row at the top of the grid — explicitly larger and bolder so it
   reads as the pane TITLE, not just a section divider. Applies to both
   Tag Search (.ts-prop-grid-title) and Review (.rv-grid-title-row). */
#ts-detail-body .ts-prop-grid-title,
#rv-items .rv-grid-title-row {
  background: var(--surface);
  font-size:var(--fs-4);
  letter-spacing: 0.10em;
  font-weight: 700;
  color: var(--text);
  padding: 10px 14px;
}
/* Left-align both columns inside the Tag-Search property grid.
   Review's variant uses inline form controls + a 'Show all' header
   that nudges things differently; for Tag Search we want a clean
   read-only key/value table — labels and values flush-left. */
#ts-detail-body .rv-prop-row {
  justify-items: start;
  text-align: left;
}
#ts-detail-body .rv-prop-name,
#ts-detail-body .rv-prop-val,
#ts-detail-body .rv-prop-name-main,
#ts-detail-body .rv-prop-val-text {
  text-align: left;
  justify-self: start;
}

/* Read-only "selected tag + category" strip — sits where Review's
   "Show all attributes" toolbar lives, replaces it. */
.ts-prop-selection {
  padding: 12px 14px;
  border-bottom: 0.5px solid var(--border);
  background: var(--surface2);
}
.ts-prop-tag {
  font-size:var(--fs-4);
  font-weight: 600;
  color: var(--text);
  font-family: var(--font-mono);
  word-break: break-all;
  line-height: 1.3;
}
.ts-prop-cat {
  font-size:var(--fs-1);
  color: var(--text3);
  font-family: var(--font-mono);
  text-transform: uppercase;
  letter-spacing: .04em;
  margin-top: 3px;
}


/* ─────────────────────────────────────────────────────────────────────
   Phase 6 — Datasheet review queue
   ─────────────────────────────────────────────────────────────────── */

/* Sidebar status filter rows (used for datasheet status list — shares
   the existing #rv-status-filters container). */
.rv-side-filter-row {
  display: flex; align-items: center; gap: 8px;
  padding: 4px 0;
  font-size:var(--fs-2);
  cursor: pointer;
}
.rv-side-filter-row input[type="checkbox"] { margin: 0; }
.rv-side-filter-lbl { flex: 1; color: var(--text2); }
.rv-side-filter-count {
  font-family: var(--font-mono);
  font-size:var(--fs-1);
  color: var(--text3);
  min-width: 20px;
  text-align: right;
}

/* Status pill in queue + detail toolbar. One swatch per state. */
.rv-ds-status {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 10px;
  font-size:var(--fs-1);
  font-family: var(--font-mono);
  font-weight: 500;
  letter-spacing: .04em;
  text-transform: uppercase;
}
.rv-ds-status-extracted  { background: var(--accent-dim, rgba(99,102,241,0.12)); color: var(--accent); }
.rv-ds-status-queued     { background: rgba(245,158,11,0.10); color: var(--amber); }
.rv-ds-status-extracting { background: rgba(245,158,11,0.18); color: var(--amber); }
.rv-ds-status-failed     { background: rgba(220,38,38,0.10);  color: var(--red); }
.rv-ds-status-confirmed  { background: rgba(16,185,129,0.10); color: var(--green, var(--green)); }
.rv-ds-status-rejected   { background: rgba(107,114,128,0.12); color: var(--text3); }
.rv-ds-status-uploaded   { background: var(--surface2); color: var(--text3); }

/* Multi-mode "needs split" badge in the queue table. Loud on purpose
   — these rows can't be reviewed until the user splits them. */
.rv-ds-split-badge {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 6px;
  background: var(--amber-light, rgba(245,158,11,0.15));
  color: var(--amber);
  border-radius: 4px;
  font-family: var(--font-mono);
  font-size:var(--fs-1);
  font-weight: 600;
  letter-spacing: .04em;
}

.rv-ds-pending { color: var(--amber); font-weight: 500; }
.rv-ds-muted   { color: var(--text3); }

.rv-ds-row { cursor: pointer; }
.rv-ds-row.selected { background: var(--accent-light, var(--accent-dim)); }
.rv-ds-row.selected td:first-child {
  border-left: 3px solid var(--accent);
  padding-left: 9px;
}

/* Detail-pane header for a selected datasheet. */
.rv-ds-detail-toolbar {
  padding: 14px 16px;
  border-bottom: 0.5px solid var(--border);
  background: var(--surface);
}
.rv-ds-detail-title {
  font-size:var(--fs-4); font-weight: 600;
  font-family: var(--font-mono);
  color: var(--text);
  word-break: break-all;
  line-height: 1.3;
  margin-bottom: 6px;
}
.rv-ds-detail-meta {
  display: flex; gap: 10px; align-items: center; flex-wrap: wrap;
  font-size:var(--fs-2);
  color: var(--text2);
}

/* Per-attribute review state — left edge stripe. */
.rv-prop-grid > .rv-prop-row.rv-ds-state-unreviewed {
  border-left: 3px solid var(--amber);
  padding-left: 11px;
}
.rv-prop-grid > .rv-prop-row.rv-ds-state-corrected {
  border-left: 3px solid var(--accent);
  padding-left: 11px;
  background: rgba(99,102,241,0.04);
}
.rv-prop-grid > .rv-prop-row.rv-ds-state-rejected {
  border-left: 3px solid var(--red);
  padding-left: 11px;
  opacity: .65;
}
.rv-prop-grid > .rv-prop-row.rv-ds-state-confirmed {
  border-left: 3px solid var(--green, var(--green));
  padding-left: 11px;
}

/* Subtitle line: "AI said: ..." */
.rv-ds-attr-sub {
  font-size:var(--fs-1);
  color: var(--text3);
  font-family: var(--font-mono);
  margin-top: 2px;
}

/* Compact action buttons for the per-attribute confirm/edit/reject. */
.rv-ds-act {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px;
  padding: 0;
  border: 0.5px solid var(--border);
  background: var(--surface);
  border-radius: 4px;
  color: var(--text2);
  font-size:var(--fs-4); line-height: 1;
  cursor: pointer;
  transition: all .1s;
}
.rv-ds-act + .rv-ds-act { margin-left: 4px; }
.rv-ds-act:hover { background: var(--surface2); }
.rv-ds-act-confirm:hover { color: var(--green, var(--green)); border-color: rgba(16,185,129,0.4); }
.rv-ds-act-edit:hover    { color: var(--accent); border-color: var(--accent); }
.rv-ds-act-reject:hover  { color: var(--red); border-color: rgba(220,38,38,0.4); }

/* Banner at the top of detail when sheet is closed (confirmed/rejected). */
.rv-locked-banner {
  padding: 10px 16px;
  background: var(--amber-light, rgba(245,158,11,0.10));
  color: var(--amber);
  font-size:var(--fs-2);
  border-bottom: 1px solid rgba(245,158,11,0.25);
}

/* Phase 6 — Datasheet upload page: recent-uploads row. */
.up-ds-row {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 14px;
  border: 0.5px solid var(--border);
  border-radius: 6px;
  margin-bottom: 6px;
  background: var(--surface);
  font-size:var(--fs-4);
}
.up-ds-row-name {
  flex: 1; min-width: 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.up-ds-prof-chip {
  flex-shrink: 0;
  padding: 2px 8px;
  background: var(--bg2, rgba(60, 90, 241, 0.08));
  color: var(--text2);
  border: 0.5px solid var(--border);
  border-radius: 10px;
  font-size:var(--fs-1);
  font-family: var(--font-mono, ui-monospace, monospace);
  max-width: 220px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Stage 4-redux — discrepancy badge in tag rows. */
.rv-discrepancy-badge {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  margin-left: 6px;
  padding: 1px 7px;
  background: var(--amber-dim, rgba(245, 158, 11, 0.18));
  color: var(--amber, #92400E);
  border: 1px solid var(--amber, #d97706);
  border-radius: 8px;
  font-size:var(--fs-1);
  font-weight: 700;
  font-family: var(--font-mono, ui-monospace, monospace);
  white-space: nowrap;
  cursor: help;
}

/* Stage 6: disagreement indicator next to field name when sources disagree.
   Source badges (D/S/I) were removed in favour of group headers in
   rvRenderConsolidatedGroups — the section header tells the user the
   source of every row beneath it. */
.rv-disagree-badge {
  display: inline-block;
  margin-left: 6px;
  padding: 0 5px;
  background: rgba(245, 158, 11, 0.18);
  color:var(--amber);
  border: 1px solid rgba(245, 158, 11, 0.40);
  border-radius: 4px;
  font-size:var(--fs-1);
  line-height: 1.4;
  vertical-align: middle;
  cursor: help;
  white-space: nowrap;
}

.rv-override-badge {
  display: inline-block;
  margin-left: 6px;
  padding: 0 5px;
  background: var(--accent-dim);
  color: var(--accent2);
  border: 1px solid rgba(61, 90, 241, 0.40);
  border-radius: 4px;
  font-size:var(--fs-1);
  line-height: 1.4;
  vertical-align: middle;
  cursor: help;
  white-space: nowrap;
}

/* ── PBS setup workflow stepper (mirrors revision-compare's rail) ──────── */
.pbs-step-layout{
  display:grid;
  grid-template-columns:220px minmax(0,1fr);
  gap:16px;
  margin-top:16px;
  align-items:start;
}
@media (max-width:820px){
  .pbs-step-layout{ grid-template-columns:1fr; }
}
.pbs-step-rail{
  background:var(--surface);
  border:0.5px solid var(--border);
  border-radius:8px;
  padding:14px 12px;
  position:sticky;
  top:16px;
}
.pbs-step-rail-label{
  font-size:var(--fs-1);text-transform:uppercase;letter-spacing:.05em;
  color:var(--text3);margin-bottom:14px;
}
.pbs-step{ display:flex;align-items:flex-start;gap:10px;padding:4px 0;cursor:default; }
.pbs-step-clickable{ cursor:pointer; }
.pbs-step-clickable:hover .pbs-step-title{ color:var(--accent); }
.pbs-step-num{
  width:24px;height:24px;border-radius:50%;display:flex;align-items:center;
  justify-content:center;font-size:var(--fs-2);font-weight:600;flex-shrink:0;
  background:var(--surface2);color:var(--text3);border:1.5px solid var(--border2);
  box-sizing:border-box;
}
.pbs-step-current .pbs-step-num{ background:var(--accent-dim);color:var(--accent);border-color:var(--accent); }
.pbs-step-done .pbs-step-num{ background:var(--green);color:var(--on-fill);border-color:var(--green); }
.pbs-step-check{ font-style:normal;font-size:var(--fs-4);line-height:1; }
.pbs-step-body{ flex:1;min-width:0; }
.pbs-step-title{ font-size:var(--fs-4);font-weight:500;color:var(--text); }
.pbs-step-future .pbs-step-title{ color:var(--text2); }
.pbs-step-sub{ font-size:var(--fs-1);color:var(--text3);margin-top:2px;word-wrap:break-word; }
.pbs-step-sub-placeholder{ color:var(--text3);opacity:.55; }
.pbs-step-connector{
  border-left:1.5px solid var(--border2);height:14px;margin-left:11px;
}
.pbs-step-connector-done{ border-left-color:var(--green); }
.pbs-step-main{ min-width:0; }

/* PBS setup — completed (collapsed) state */
.pbs-setup-done{ display:flex;flex-direction:column;align-items:flex-start; }
.pbs-setup-done-row{ display:flex;align-items:center;gap:8px; }
.pbs-setup-done-check{
  width:22px;height:22px;border-radius:50%;background:var(--green);color:var(--on-fill);
  display:inline-flex;align-items:center;justify-content:center;font-size:var(--fs-4);
}
.pbs-setup-done-label{ font-size:var(--fs-4);font-weight:500;color:var(--text); }
/* When more than one step-pane is visible (completed view), space them out */
.pbs-step-main > .pbs-step-pane + .pbs-step-pane{ margin-top:16px; }

/* Shared adaptive PBS scope filter (Upload/Review/Tag Search/Compare) */
.vx-pbs-filter-row{ display:flex;flex-direction:column;gap:4px; }
.vx-pbs-filter-label{ font-size:var(--fs-1);color:var(--text3);text-transform:uppercase;letter-spacing:.04em; }
.vx-pbs-filter-select{ font-size:var(--fs-4);padding:6px 10px;border:0.5px solid var(--border);border-radius:6px;background:var(--surface);color:var(--text); }

/* Shared PBS tree picker (Upload scope selector, later other pages) */
.vx-tp{ border:0.5px solid var(--border);border-radius:8px;background:var(--surface);padding:8px 6px;max-height:340px;overflow:auto;font-size:var(--fs-4); }
.vx-tp-caret{ display:inline-block;width:16px;cursor:pointer;color:var(--text3);user-select:none;text-align:center; }
.vx-tp-caret-blank{ display:inline-block;width:16px; }
.vx-tp-root{ display:flex;align-items:center;gap:6px;padding:5px 4px;font-weight:500;color:var(--text); }
.vx-tp-root-ico,.vx-tp-pkg-ico{ font-size:13px; }
.vx-tp-node{ display:flex;align-items:center;gap:4px;padding:4px 4px;color:var(--text2); }
.vx-tp-node-code{ font-family:var(--font-mono);color:var(--accent);font-size:var(--fs-2); }
.vx-tp-node-desc{ color:var(--text2); }
.vx-tp-pkg{ display:flex;align-items:center;gap:6px;padding:4px 4px;cursor:pointer;border-radius:5px; }
.vx-tp-pkg:hover{ background:var(--surface2); }
.vx-tp-pkg-sel{ background:var(--accent-dim); }
.vx-tp-pkg-name{ color:var(--text); }
.vx-tp-pkg-status{ font-size:var(--fs-1);color:var(--text3);font-family:var(--font-mono); }
.vx-tp-unassigned .vx-tp-node-code{ color:var(--text3); }
.vx-tp-warn-badge{ font-size:var(--fs-1);background:var(--amber);color:var(--on-fill);border-radius:8px;padding:0 6px;margin-left:6px; }
.vx-tp-note{ font-size:var(--fs-1);color:var(--text3);font-style:italic;padding:3px 0; }
.vx-tp-empty{ font-size:var(--fs-2);color:var(--text3);padding:4px 0; }

/* Upload: tree picker (left) + drop zone (right) side by side */
.upload-pick-row{ display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:16px;align-items:start;margin:10px 0 4px 0; }
@media (max-width:860px){ .upload-pick-row{ grid-template-columns:1fr; } }

/* ── Upload & Scan: where you work on the left, what you have on the right.
   The picker and drop zone were 50/50 ACROSS THE TOP with the list beneath
   at full width, so the list began ~450px down and could not fit whatever
   height it was allowed. 30/70, and the left column is sticky so uploading a
   sheet and watching it land no longer means scrolling back up.
   ⚠️ `align-items:start` is what lets the sticky child work — a stretched
   grid item is already as tall as the row and has nothing to stick within. */
/* ── Who is signed in, on every page ──────────────────────────────────────
   Fixed rather than in the flow: this must not cost every page a strip of
   vertical height, which is the space the Upload & Scan layout just spent a
   package recovering.
   ⚠️ z-index sits BELOW modals and dialogs (which start at 1000). An identity
   chip floating over a confirmation dialog would be worse than one nobody can
   find. */
.vx-whoami{ position:fixed;top:10px;right:16px;z-index:60;
  display:flex;align-items:center;gap:8px;
  padding:3px 10px;border-radius:999px;
  border:1px solid var(--border);background:var(--surface);
  box-shadow:var(--shadow-sm);pointer-events:none;max-width:38vw; }
.vx-whoami-name{ font-size:var(--fs-2);color:var(--text2);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis; }
/* The role is the part that explains a 403, so it is the part that stands out. */
.vx-whoami-role{ font-size:var(--fs-1);font-family:var(--font-mono);
  text-transform:uppercase;letter-spacing:.04em;
  color:var(--text3);border-left:1px solid var(--border);padding-left:8px;
  white-space:nowrap; }
@media (max-width:760px){
  .vx-whoami{ top:6px;right:8px;max-width:52vw; }
  .vx-whoami-name{ display:none; }
}

/* The project picker sits in the Kind bar rather than owning a row. */
.upload-kind-project{ display:flex;align-items:center;gap:8px;margin-left:auto; }
.upload-kind-project-label{ font-size:11px;color:var(--text3);
  text-transform:uppercase;letter-spacing:.04em; }
.upload-kind-project select{ max-width:320px;min-width:180px; }
@media (max-width:760px){
  .upload-kind-project{ margin-left:0;width:100%; }
  .upload-kind-project select{ min-width:0;width:100%; }
}

.upload-split{ display:grid;grid-template-columns:minmax(260px,30%) minmax(0,1fr);
  gap:18px;align-items:start;margin:10px 0 4px 0; }
.upload-side{ position:sticky;top:12px;min-width:0; }
.upload-main{ min-width:0; }
/* ⚠️ A PBS TREE SCROLLS SIDEWAYS RATHER THAN WRAPPING. A wrapped node in a
   narrow column reads far worse than a clipped one — the indentation stops
   meaning depth the moment a label runs onto a second line. */
.upload-side #upload-tree-host{ max-height:38vh;overflow:auto;white-space:nowrap; }
.upload-side .drop-zone{ margin:0 0 14px 0;padding:28px 16px;min-height:0; }
.upload-side > label{ display:block; }
/* Below this the two columns are both too narrow to be useful, so they stack
   — the same idiom as .upload-pick-row above, at the width this layout
   actually needs. */
@media (max-width:1100px){
  .upload-split{ grid-template-columns:1fr; }
  .upload-side{ position:static; }
  .upload-side #upload-tree-host{ max-height:none; }
}
.upload-pick-tree{ min-width:0; }
.upload-pick-drop{ min-width:0;display:flex;flex-direction:column; }
/* Drop zone fills the column; top edge aligns with the tree (the tree has
   a 'Location' label above it, so nudge the drop zone down to match). */
.upload-pick-drop .drop-zone{ margin:18px 0 0 0;min-height:160px;display:flex;flex-direction:column;align-items:center;justify-content:center; }
.vx-tp-pkg-detail{ font-size:var(--fs-2);color:var(--text2);padding:2px 0 6px 0; }
.vx-tp-pkg-detail strong{ color:var(--text); }
.vx-tp-proj-name{ font-weight:500;color:var(--text); }
.vx-tp-proj-name:hover{ color:var(--accent); }
.vx-tp-root-name:hover{ color:var(--accent); }
/* =============================================================================
 * Project Shell (Spec A v2) — permanent two-tier module bar + image tiles.
 * Appended block. Matches Veridex tokens: --nav-bg var(--nav-bg), IBM Plex, .card.
 *
 * The bar REPLACES the old hamburger/slide-out nav. It is always visible and
 * takes layout space (the page sits to its right). Two tiers:
 *   .vx-shell  -> flex row wrapper around [bar][page-area]
 *   .vx-bar    -> tier 1 (icon rail) + tier 2 (page panel), side by side
 * ========================================================================== */

.vx-shell { display: flex; align-items: stretch; flex: 1 1 auto; min-width: 0; height: 100vh; }

/* The bar is a fixed overlay, hidden off the left edge by default. A toggle
   button slides it in; clicking the page (mask) slides it out. */
.vx-bar {
  display: flex;
  flex-shrink: 0;
  height: 100vh;
  position: fixed;
  top: 0; left: 0;
  z-index: 80;
  transform: translateX(-100%);
  transition: transform .22s ease;
  box-shadow: 0 0 0 rgba(0,0,0,0);
}
html.vx-bar-open .vx-bar {
  transform: translateX(0);
  box-shadow:none;
}

/* Toggle button — top-left, always reachable, above the bar. */
.vx-bar-toggle {
  position: fixed;
  top: 14px; left: 14px;
  width: 38px; height: 38px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--surface, var(--surface));
  border: 0.5px solid var(--border);
  border-radius: 9px;
  color: var(--text);
  cursor: pointer;
  z-index: 90;
  transition: transform .22s ease, background .15s, border-color .15s;
  box-shadow:none;
}
.vx-bar-toggle:hover { background: var(--surface3, var(--surface2)); }
.vx-bar-toggle svg { width: 17px; height: 17px; }
html.vx-bar-open .vx-bar-toggle { transform: translateX(268px); }

/* Click-anywhere-to-close mask, visible only while the bar is open. */
.vx-bar-mask {
  position: fixed; inset: 0;
  background: rgba(15,17,22,0.35);
  z-index: 70;
  opacity: 0; pointer-events: none;
  transition: opacity .18s ease;
}
html.vx-bar-open .vx-bar-mask { opacity: 1; pointer-events: auto; }

.vx-page-area {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.vx-page-area .main { flex: 1 1 auto; min-height: 0; }

/* Bar brand header (VERIDEX + tagline), top of the page panel. */
.vx-brand {
  padding: 18px 14px 14px;
  border-bottom: 1px solid var(--nav-border);
  margin: -16px -12px 12px;
}
.vx-brand-name {
  font-family: 'IBM Plex Sans', sans-serif;
  font-size:var(--fs-6); font-weight: 600; color:var(--on-fill);
  letter-spacing: .04em; text-transform: uppercase;
}
.vx-brand-tag {
  font-size:var(--fs-1); color: rgba(255,255,255,0.35);
  margin-top: 3px; font-family: var(--font-mono); letter-spacing: .06em;
}

.vx-rail {
  width: 60px;
  background: var(--nav-surface);
  border-right: 1px solid var(--nav-border);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 14px 0 12px;
  overflow-y: auto;
}
.vx-rail-btn {
  position: relative;
  width: 42px; height: 42px;
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 10px;
  color: rgba(255,255,255,0.55);
  cursor: pointer;
  transition: all .14s;
  flex-shrink: 0;
}
.vx-rail-btn svg { width: 19px; height: 19px; }
.vx-rail-btn:hover { background: rgba(255,255,255,0.08); color:var(--on-fill); }
.vx-rail-btn.active {
  background: rgba(91,109,238,0.30);
  border-color: rgba(91,109,238,0.55);
  color:var(--on-fill);
}
.vx-rail-btn.soon { opacity: 0.85; }
.vx-rail-soon {
  position: absolute; top: 7px; right: 7px;
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--amber);
}
.vx-rail-div {
  width: 26px; height: 1px;
  background: var(--nav-border);
  margin: 5px 0;
  flex-shrink: 0;
}
.vx-rail-spacer { flex: 1 1 auto; min-height: 12px; }

.vx-panel {
  width: 208px;
  background: var(--nav-bg, var(--nav-bg));
  border-right: 1px solid var(--nav-border);
  display: flex;
  flex-direction: column;
  padding: 16px 12px;
  overflow-y: auto;
}
.vx-panel-head {
  font-size:var(--fs-1);
  font-family: var(--font-mono);
  color: rgba(255,255,255,0.45);
  text-transform: uppercase;
  letter-spacing: .09em;
  padding: 2px 8px 12px;
}
.vx-panel-list { display: flex; flex-direction: column; gap: 2px; }
.vx-page-btn {
  text-align: left;
  padding: 9px 12px;
  border: 1px solid transparent;
  border-radius: 8px;
  background: transparent;
  color: rgba(255,255,255,0.62);
  font-size:var(--fs-4);
  cursor: pointer;
  transition: all .14s;
  font-family: inherit;
}
.vx-page-btn:hover { background: rgba(255,255,255,0.07); color:var(--on-fill); }
.vx-page-btn.active {
  background: rgba(91,109,238,0.22);
  border-color: rgba(91,109,238,0.40);
  color:var(--on-fill);
}
.vx-panel-soon {
  font-size:var(--fs-2);
  color: rgba(255,255,255,0.45);
  padding: 8px;
  line-height: 1.5;
}

.nav-hamburger, .nav-mask { display: none !important; }

/* ── Project-page image tiles ─────────────────────────────────────────── */
#vx-module-tiles { margin: 16px 0 20px; }
.vx-tiles-head {
  font-size:var(--fs-1);
  font-family: var(--font-mono);
  color: var(--text3);
  text-transform: uppercase;
  letter-spacing: .1em;
  margin-bottom: 12px;
}
.vx-tiles-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 16px;
}
.vx-tile {
  position: relative;
  height: 168px;
  border-radius: 14px;
  overflow: hidden;
  border: 0.5px solid var(--border);
  background:var(--nav-bg);
  transition: transform .16s, box-shadow .16s, border-color .16s;
}
.vx-tile.clickable { cursor: pointer; }
.vx-tile.clickable:hover {
  transform: translateY(-2px);
  box-shadow:none;
  border-color: rgba(91,109,238,0.5);
}
.vx-tile.soon { filter: saturate(0.85); }
.vx-tile-img {
  position: absolute; inset: 0;
  background-size: cover;
  background-position: center;
}
.vx-tile-scrim {
  position: absolute; inset: 0;
  background: linear-gradient(to top,
    rgba(13,18,32,0.92) 0%, rgba(13,18,32,0.45) 45%, rgba(13,18,32,0.10) 100%);
}
.vx-tile-ico {
  position: absolute; top: 12px; left: 12px;
  width: 34px; height: 34px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 9px;
  background: rgba(20,28,52,0.6);
  border: 1px solid rgba(255,255,255,0.14);
  color:var(--on-fill);
}
.vx-tile-ico svg { width: 18px; height: 18px; }
.vx-tile-dot {
  position: absolute; top: 16px; right: 14px;
  width: 8px; height: 8px; border-radius: 50%;
  background:var(--green);
  box-shadow: 0 0 0 3px rgba(52,211,153,0.18);
}
.vx-tile-badge {
  position: absolute; top: 13px; right: 12px;
  font-size:var(--fs-1);
  font-family: var(--font-mono);
  padding: 3px 9px;
  border-radius: 11px;
}
.vx-tile-badge.soon {
  color: var(--amber);
  background: rgba(251,191,36,0.18);
  border: 1px solid rgba(251,191,36,0.32);
}
.vx-tile-foot {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: 12px 14px 13px;
}
.vx-tile-title {
  color:var(--on-fill); font-size:var(--fs-5); font-weight: 600;
  letter-spacing: -.01em;
}
.vx-tile-desc {
  color: rgba(255,255,255,0.62);
  font-size:var(--fs-1);
  margin-top: 2px;
  line-height: 1.4;
}
.vx-tile-actions { display: flex; gap: 6px; margin-top: 10px; }
.vx-tile-off {
  font-size:var(--fs-1); color: rgba(255,255,255,0.5);
  font-family: var(--font-mono);
}
.vx-tile-err { font-size:var(--fs-2); color: var(--red, #ef4444); margin-bottom: 10px; }

@media (max-width: 720px) {
  .vx-panel { width: 168px; }
  .vx-tiles-grid { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 520px) {
  .vx-tiles-grid { grid-template-columns: 1fr; }
}

/* ── Theme segmented control (Settings → Appearance) ──────── */
.vx-theme-seg{display:inline-flex;background:var(--surface2);border:0.5px solid var(--border);border-radius:var(--radius);padding:3px;gap:2px}
.vx-theme-opt{display:inline-flex;align-items:center;gap:7px;padding:7px 14px;border:none;background:transparent;color:var(--text2);font-family:var(--font-body);font-size:var(--fs-4);font-weight:500;border-radius:calc(var(--radius) - 3px);cursor:pointer;transition:background .15s,color .15s}
.vx-theme-opt:hover{color:var(--text)}
.vx-theme-opt.active{background:var(--surface);color:var(--accent);box-shadow:var(--shadow-sm)}
.vx-theme-ic{font-size:var(--fs-4);line-height:1}
/* ── Quick theme toggle (nav bottom) ─────────────────────── */
.vx-theme-quick{display:flex;align-items:center;justify-content:center;width:30px;height:30px;border-radius:8px;border:1px solid rgba(255,255,255,0.12);background:rgba(255,255,255,0.04);color:rgba(255,255,255,0.7);cursor:pointer;font-size:var(--fs-5);line-height:1;transition:background .15s}
.vx-theme-quick:hover{background:rgba(255,255,255,0.1);color:var(--on-fill)}

/* ============================================================
   Veridex Inbox (per-user mailbox) + rail unread badge
   Item 8.1 workflow engine. Theme-token based (light/dark safe).
   ============================================================ */
.vx-rail-btn { position: relative; }
.vx-rail-badge {
  position: absolute; top: 4px; right: 4px; min-width: 16px; height: 16px;
  padding: 0 4px; border-radius: 8px; background: var(--amber);
  color:var(--on-fill); font-size:var(--fs-1); font-weight: 700; line-height: 16px;
  text-align: center; box-shadow: 0 0 0 2px var(--rail-bg, #1a2140);
}
.vx-inbox-wrap { max-width: 760px; margin: 0 auto; padding: 8px 0; }
.vx-inbox-tabs { float: right; display: inline-flex; gap: 6px; }
.vx-inbox-intro {
  font-size:var(--fs-4); color: var(--text2); margin: 4px 0 14px;
}
.vx-inbox-item {
  border: 0.5px solid var(--border); border-radius: var(--radius, 8px);
  padding: 12px 14px; margin-bottom: 10px; background: var(--surface);
}
.vx-inbox-item.vx-inbox-unread { border-left: 3px solid var(--accent); }
.vx-inbox-meta {
  display: flex; align-items: center; gap: 8px; margin-bottom: 4px;
}
.vx-inbox-kind {
  font-size:var(--fs-1); font-weight: 700; text-transform: uppercase;
  letter-spacing: .04em; padding: 2px 7px; border-radius: 10px;
  background: var(--surface2); color: var(--text2);
}
.vx-inbox-kind-approval_request { background: var(--accent-dim); color: var(--accent); }
.vx-inbox-status { font-size:var(--fs-1); color: var(--text2); }
.vx-inbox-subject { font-size:var(--fs-4); font-weight: 600; color: var(--text); }
.vx-inbox-bodytext { font-size:var(--fs-4); color: var(--text); margin-top: 4px; }
.vx-inbox-detail {
  font-size:var(--fs-2); color: var(--text2); margin-top: 4px;
  padding: 6px 8px; background: var(--surface2); border-radius: 6px;
  display: inline-block;
}
.vx-inbox-acts { margin-top: 10px; display: flex; gap: 8px; align-items: center; }
.vx-inbox-busy { font-size:var(--fs-2); color: var(--text2); }

/* ============================================================
   Workflow config UI (item 15) — Admin + Settings
   ============================================================ */
.wf-card { max-width: 620px; }
.wf-h { font-size:var(--fs-4); font-weight: 600; color: var(--text); margin-bottom: 4px; }
.wf-hint, .wf-source { font-size:var(--fs-2); color: var(--text2); margin-bottom: 14px; }
.wf-source b { color: var(--text); }
.wf-toggle {
  display: flex; align-items: center; gap: 10px; cursor: pointer;
  padding: 10px 0; font-size:var(--fs-4); color: var(--text);
}
.wf-toggle input { width: 16px; height: 16px; accent-color: var(--accent); }
.wf-toggle-txt { user-select: none; }
.wf-row { margin: 10px 0 4px; }
.wf-label { font-size:var(--fs-2); font-weight: 600; color: var(--text); margin-bottom: 5px; }
.wf-sublabel { font-size:var(--fs-1); color: var(--text2); margin-top: 4px; line-height: 1.5; }
.wf-select {
  border: 0.5px solid var(--border); border-radius: 6px; padding: 6px 9px;
  font-size:var(--fs-4); color: var(--text); background: var(--surface); min-width: 180px;
}
.wf-fieldset {
  border-left: 2px solid var(--accent-dim); padding-left: 14px; margin: 8px 0;
}
.wf-dim { opacity: 0.45; pointer-events: none; }
.wf-appr-grid {
  display: flex; flex-direction: column; gap: 6px; margin-top: 8px;
  max-height: 220px; overflow-y: auto; padding: 4px 0;
}
.wf-appr {
  display: flex; align-items: center; gap: 8px; font-size:var(--fs-4); color: var(--text);
  cursor: pointer;
}
.wf-appr input { accent-color: var(--accent); }
.wf-actions { display: flex; align-items: center; gap: 10px; margin-top: 18px; }
.wf-busy { font-size:var(--fs-2); color: var(--text2); }

/* Multi-stage approval editor (Tier-1 workflow chains) */
.wf-stage {
  border: 0.5px solid var(--border); border-radius: var(--radius, 8px);
  padding: 10px 12px; margin-bottom: 10px; background: var(--surface2);
}
.wf-stage-head { display: flex; align-items: center; gap: 10px; }
.wf-stage-num {
  font-size:var(--fs-1); font-weight: 700; text-transform: uppercase; letter-spacing: .05em;
  color: var(--accent); background: var(--accent-dim); padding: 3px 8px; border-radius: 10px;
  white-space: nowrap;
}
.wf-stage-label {
  flex: 1; border: 0.5px solid var(--border); border-radius: 6px; padding: 5px 8px;
  font-size:var(--fs-4); color: var(--text); background: var(--surface);
}
.wf-stage-move { display: inline-flex; gap: 4px; }
.wf-mini {
  border: 0.5px solid var(--border); background: var(--surface); color: var(--text);
  width: 26px; height: 26px; border-radius: 6px; cursor: pointer; font-size:var(--fs-4); line-height: 1;
}
.wf-mini:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
.wf-mini:disabled { opacity: .35; cursor: default; }
.wf-mini-x:hover:not(:disabled) { border-color: var(--red, #dc2626); color: var(--red, #dc2626); }
.wf-stage-body { margin-top: 10px; }

/* ============================================================
   Reports hub (Option A: two-pane) + data grid
   ============================================================ */
.rep-shell { display:flex; gap:0; height:calc(100vh - 160px); min-height:480px;
  border:0.5px solid var(--border); border-radius:var(--radius-lg); overflow:hidden; }
.rep-side { width:248px; flex:none; border-right:0.5px solid var(--border);
  background:var(--surface2); overflow-y:auto; padding:12px; }
.rep-main { flex:1; min-width:0; overflow:auto; padding:18px 20px; position:relative; }
.rep-empty, .rep-empty-sm { color:var(--text2); font-size:var(--fs-4); padding:24px 8px; text-align:center; }
.rep-empty-sm { padding:12px 4px; }

/* B1 — the drawing survey. Data is mono (D-9), same as the Reports hub:
   file names, types and counts are data, and this is the same product. */
.vxs-intro { padding:18px 2px; max-width:640px; }
.vxs-intro p { margin:0 0 10px; color:var(--text); font-size:var(--fs-4);
  line-height:1.6; }
.vxs-fine { color:var(--text2); font-size:var(--fs-2); }
/* A minority family that is probably a different object type. Warn
   colour, not error: the row is a real candidate and the person may
   still want it — it is a finding, not a refusal. */
.vxdc-minor { color:var(--warn-text, #8a5a00); }
/* The forced re-read. A deliberate act with its cost stated, so it is laid
   out as a labelled choice rather than a bare tick beside a button: the
   explanation is the point, and a tooltip nobody opens would not be one. */
.vxs-reread { display:grid; grid-template-columns:auto 1fr; gap:2px 8px;
  align-items:start; margin:14px 0 0; padding:10px 12px;
  border:1px solid var(--border-control); border-radius:var(--radius);
  background:var(--surface2); max-width:560px; cursor:pointer; }
.vxs-reread input { margin:2px 0 0; }
.vxs-reread-t { color:var(--text); font-size:var(--fs-3); font-weight:600; }
.vxs-reread-f { grid-column:2; color:var(--text2); font-size:var(--fs-2);
  line-height:1.5; }
.vxs-run { display:flex; align-items:center; gap:12px; margin:4px 0 16px; }
.vxs-bar { flex:1; height:4px; background:var(--surface2);
  border-radius:var(--radius-pill); overflow:hidden; }
.vxs-bar-f { height:100%; background:var(--accent); transition:width .2s; }
.vxs-tbl { width:100%; border-collapse:collapse; margin-bottom:18px;
  font-size:var(--fs-2); }
/* THE PLATFORM HEADER, not the caption. See the table-shell block at the
   foot of this file for why this rule changed and what it used to say. */
.vxs-tbl th { text-align:left; font-size:var(--fs-5); font-weight:600;
  font-family:var(--font-body); text-transform:none; letter-spacing:-0.01em;
  color:var(--text); background:var(--surface2);
  padding:var(--pad-head-y) var(--pad-cell-x); white-space:nowrap;
  border-bottom:2px solid var(--border2); }
.vxs-tbl td { padding:var(--pad-cell-y) var(--pad-cell-x);
  border-bottom:0.5px solid var(--border);
  color:var(--text); font-family:var(--font-mono); font-size:var(--fs-3); }
.vxs-tbl td.num, .vxs-tbl th.num { text-align:right; }
.vxs-err { color:var(--red-on-tint); }
.vxs-done { color:var(--text2); font-size:var(--fs-1);
  font-family:var(--font-mono); }
.vxs-cap { font-size:var(--fs-1); color:var(--text2); margin-top:-8px;
  margin-bottom:16px; }

/* B1 detail — a surveyed file expands to show what the AI actually read:
   its title block, the sampled objects/labels, and any notes. The row is
   clickable; a caret rotates when open. */
.vxs-tbl-files tr.vxs-row { cursor:pointer; }
.vxs-tbl-files tr.vxs-row:hover td { background:var(--surface2); }
.vxs-tbl-files tr.vxs-row.open td { background:var(--surface2); }
.vxs-caret { display:inline-block; width:14px; color:var(--text2);
  font-size:9px; transition:transform .15s; }
.vxs-caret.open { transform:rotate(90deg); }
.vxs-d-row > td { padding:0 !important; border-bottom:0.5px solid var(--border); }
.vxs-d { padding:14px 16px 16px 34px; background:var(--surface2);
  display:flex; flex-wrap:wrap; gap:22px; font-family:var(--font-mono); }
.vxs-d-sec { min-width:220px; flex:1; }
.vxs-d-h { font-size:var(--fs-1); text-transform:uppercase; letter-spacing:.07em;
  color:var(--text2); margin-bottom:7px; font-weight:500; }
.vxs-d-tb { border-collapse:collapse; font-size:var(--fs-2); }
.vxs-d-tb td { padding:3px 10px 3px 0; vertical-align:top; color:var(--text); }
.vxs-d-tb td.vxs-d-k { color:var(--text2); text-transform:capitalize;
  white-space:nowrap; }
.vxs-d-objs { list-style:none; margin:0; padding:0; columns:2; column-gap:22px;
  font-size:var(--fs-2); }
.vxs-d-objs li { color:var(--text); padding:2px 0; break-inside:avoid;
  display:flex; justify-content:space-between; gap:8px; }
.vxs-d-kind { color:var(--text2); font-size:var(--fs-1); }
.vxs-d-more { font-size:var(--fs-1); color:var(--text2); margin-bottom:7px; }
.vxs-d-notes { font-size:var(--fs-2); color:var(--text); line-height:1.5;
  max-width:520px; }
.vxs-d-empty { font-size:var(--fs-2); color:var(--text2); padding:2px 0; }

/* B1 past runs — the landing list of saved survey runs, so a refresh recovers
   instead of wiping. Sits below the "Choose a folder" action. */
.vxs-runs { margin-top:22px; max-width:720px; }
.vxs-runs-h { font-size:var(--fs-1); text-transform:uppercase; letter-spacing:.07em;
  color:var(--text2); font-family:var(--font-mono); font-weight:500;
  margin-bottom:8px; }
.vxs-runs-empty { font-size:var(--fs-2); color:var(--text2); padding:6px 0; }
.vxs-runlist { display:flex; flex-direction:column;
  border:0.5px solid var(--border); border-radius:8px; overflow:hidden; }
.vxs-runrow { display:flex; align-items:baseline; gap:12px; width:100%;
  text-align:left; padding:11px 14px; background:var(--surface);
  border:0; border-bottom:0.5px solid var(--border); cursor:pointer;
  font-family:var(--font-mono); font-size:var(--fs-2); color:var(--text); }
/* The tick sits OUTSIDE the run button — a checkbox nested in a button is a
   control inside a control. The wrapper carries the row's border so the list
   still reads as one list. */
.vxs-runrow-wrap { display:flex; align-items:stretch;
  border-bottom:0.5px solid var(--border); background:var(--surface); }
.vxs-runrow-wrap:last-child { border-bottom:0; }
.vxs-runrow-wrap:hover { background:var(--surface2); }
.vxs-runrow-ck { display:flex; align-items:center; padding:0 4px 0 12px;
  cursor:pointer; }
.vxs-runrow-wrap .vxs-runrow { border-bottom:0; background:none; }
.vxs-runs-bar { display:flex; align-items:center; gap:10px; margin-bottom:8px;
  padding:8px 12px; border:0.5px solid var(--border2);
  border-radius:var(--radius-md,8px); background:var(--surface2); }
.vxs-runs-n { flex:1; font-size:var(--fs-2); color:var(--text2); }
.vxs-runrow:last-child { border-bottom:0; }
.vxs-runrow:hover { background:var(--surface2); }
.vxs-runrow-name { font-weight:500; flex:1; }
.vxs-runrow-meta { color:var(--text2); font-size:var(--fs-1); }
.vxs-runrow-err { color:var(--red-on-tint); margin-left:8px; }
.vxs-runrow-when { color:var(--text2); font-size:var(--fs-1); white-space:nowrap;
  min-width:130px; text-align:right; }
.vxs-done-head { display:flex; align-items:center; justify-content:space-between;
  gap:12px; margin-bottom:14px; }
.vxs-done-title { font-family:var(--font-mono); font-size:var(--fs-4);
  color:var(--text); font-weight:500; }

/* C3 — the ask box. Above the catalogue: a question is how most people
   would rather start, and burying it under a list of reports they have to
   read first defeats the point. */
.rep-ask { display:flex; gap:6px; align-items:flex-start; margin-bottom:10px; }
.rep-ask-in { flex:1; min-width:0; resize:vertical; padding:7px 9px;
  border:0.5px solid var(--border2); border-radius:var(--radius-md);
  background:var(--surface); color:var(--text); font:inherit; font-size:var(--fs-4);
  line-height:1.45; }
.rep-ask-in::placeholder { color:var(--text3); }
/* Renders inside .rep-main (--surface), where --red-on-tint over --red-dim
   measures 7.11 light / 4.98 dark — passes. Worth stating because it does NOT
   pass on --surface2 (4.47 dark): if this error is ever moved into the side
   pane, it fails. Measured, not eyeballed. */
.rep-askerr { font-size:var(--fs-2); color:var(--red-on-tint);
  background:var(--red-dim); border-radius:var(--radius-md); padding:9px 11px;
  margin-bottom:10px; line-height:1.5; }

.rep-search input { width:100%; margin-bottom:12px; }
.rep-group { margin-bottom:14px; }
.rep-group-h { font-size:var(--fs-1); text-transform:uppercase; letter-spacing:.07em;
  color:var(--text2); margin:6px 4px 6px; font-family:var(--font-mono);
  font-weight:500; }
.rep-item { display:flex; align-items:center; gap:8px; padding:7px 9px; border-radius:8px;
  font-size:var(--fs-4); color:var(--text); cursor:pointer; }
.rep-item:hover { background:var(--surface); }
.rep-item.on { background:var(--accent-dim,rgba(61,90,241,.1)); color:var(--accent); font-weight:500; }
.rep-item-ic { font-size:var(--fs-4); opacity:.7; width:14px; text-align:center; }

.rep-head { display:flex; justify-content:space-between; align-items:center; margin-bottom:14px; }
/* D-9: Space Grotesk for display. Matches the app's other headings. */
.rep-title { font-size:var(--fs-6); font-weight:600; color:var(--text);
  font-family:var(--font-display); letter-spacing:-.01em; }
.rep-actions { display:flex; gap:8px; }

/* Report inputs panel (params with source=input, e.g. version pickers) */
.rep-inputs { border:0.5px solid var(--border); border-radius:var(--radius-lg,10px);
  padding:12px 14px; margin-bottom:16px; background:var(--surface2); }
.rep-inputs-h { font-size:var(--fs-2); font-weight:600; color:var(--text2);
  text-transform:uppercase; letter-spacing:.04em; margin-bottom:10px; }
.rep-inputs-grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(220px,1fr));
  gap:10px 16px; }
.rep-in-row { display:flex; flex-direction:column; gap:4px; }
.rep-in-lbl { font-size:var(--fs-2); color:var(--text); }
.rep-in-req { color:var(--red); }
.rep-in { font-size:var(--fs-4); padding:5px 8px; border:0.5px solid var(--border);
  border-radius:6px; background:var(--surface,var(--surface)); color:var(--text); width:100%; }
.rep-in-bool { display:inline-flex; align-items:center; gap:6px; font-size:var(--fs-4);
  color:var(--text); }
.rep-in-bool input { width:auto; }
.rep-inputs-actions { margin-top:12px; display:flex; justify-content:flex-end; }

.rep-stats { display:flex; gap:10px; flex-wrap:wrap; margin-bottom:16px; }
.rep-stat { background:var(--surface2); border-radius:var(--radius-md,8px); padding:9px 14px; min-width:90px; }
/* D-9: data is mono. The app renders every stat this way (.stat-lbl,
   .enum-row-name, .pat-input) — 138 --font-mono uses across this sheet. The
   Reports hub had ZERO, which is most of why it read as generic beside the
   rest of Veridex: typographically it was a website, not the product. */
.rep-stat-l { font-size:var(--fs-1); color:var(--text2); font-family:var(--font-mono);
  text-transform:uppercase; letter-spacing:.07em; font-weight:500; }
.rep-stat-v { font-size:var(--fs-7); font-weight:600; color:var(--text);
  font-family:var(--font-mono); letter-spacing:-.01em; margin-top:2px; }

.rep-chart { border:0.5px solid var(--border); border-radius:var(--radius-lg); padding:14px 16px; margin-bottom:16px; }
.rep-chart-t { font-size:var(--fs-4); font-weight:600; margin-bottom:12px; color:var(--text); }
.rep-bars { display:flex; gap:14px; align-items:flex-end; height:160px; }
.rep-bar-col { display:flex; flex-direction:column; align-items:center; gap:6px; flex:1; height:100%; justify-content:flex-end; }
.rep-bar { width:70%; max-width:46px; background:var(--green); border-radius:4px 4px 0 0; min-height:2px; }
.rep-bar-x { font-size:var(--fs-1); color:var(--text2); text-align:center; }
.rep-donutwrap { display:flex; gap:20px; align-items:center; }
.rep-donut { width:130px; height:130px; border-radius:50%; flex:none;
  -webkit-mask:radial-gradient(circle 42px at center, transparent 98%, #000 100%);
  mask:radial-gradient(circle 42px at center, transparent 98%, #000 100%); }
.rep-legend { display:flex; flex-direction:column; gap:6px; font-size:var(--fs-2); color:var(--text); }
.rep-leg { display:flex; align-items:center; gap:8px; }
.rep-leg-d { width:10px; height:10px; border-radius:3px; }

.rep-gridwrap { }
.rep-gridtools { margin-bottom:8px; }
.rep-gridtools input { max-width:240px; }
.rep-grid { overflow:auto; max-height:calc(100vh - 280px); }
.rep-table { width:100%; border-collapse:collapse; font-size:var(--fs-2); }
/* Sticky header: stays fixed on vertical scroll, but because it lives inside
   the horizontally-scrolling .rep-grid it moves with its column on horizontal
   scroll. box-shadow draws the divider (border-bottom is unreliable under
   position:sticky with border-collapse). */
.rep-table thead th { position:sticky; top:0; z-index:2; }
.rep-table th { position:relative; text-align:left; padding:8px 10px; background:var(--surface2);
  border-bottom:0.5px solid var(--border); color:var(--text); white-space:nowrap; user-select:none;
  box-shadow:inset 0 -1px 0 var(--border); }
.rep-th-l { cursor:pointer; }
.rep-th-grip { position:absolute; right:0; top:0; width:6px; height:100%; cursor:col-resize; }
.rep-th-grip:hover { background:var(--accent); opacity:.4; }
.rep-table td { padding:7px 10px; border-bottom:0.5px solid var(--border); color:var(--text); }
.rep-table tbody tr:hover { background:var(--surface2); }
.rep-num { text-align:right; font-variant-numeric:tabular-nums; }
.rep-pill { font-size:var(--fs-1); padding:2px 8px; border-radius:10px; background:var(--surface2);
  border:0.5px solid var(--border); color:var(--text2,var(--text)); white-space:nowrap; }
.rep-norows { text-align:center; color:var(--text2); padding:18px; }
.rep-count { font-size:var(--fs-1); color:var(--text2); margin-top:8px; }

.rep-colmenu { position:absolute; top:54px; right:20px; z-index:20; background:var(--surface);
  border:0.5px solid var(--border); border-radius:var(--radius-md,8px); box-shadow:none;
  padding:10px; min-width:180px; max-height:300px; overflow:auto; }
.rep-colmenu-h { font-size:var(--fs-1); text-transform:uppercase; letter-spacing:.04em;
  color:var(--text2); margin-bottom:8px; }
.rep-colopt { display:flex; align-items:center; gap:8px; font-size:var(--fs-4); color:var(--text);
  padding:4px 2px; cursor:pointer; }

/* ── Quarantine lens (Review page) — fixed-height scrollable table ── */
.qz-head { margin-bottom:10px; }
.qz-head-title { font-weight:600; font-size:var(--fs-5); color:var(--text); }
.qz-count { display:inline-block; min-width:20px; text-align:center; margin-left:6px;
  font-size:var(--fs-2); font-weight:600; color:var(--text2,var(--text3));
  background:var(--surface2,var(--nav-surface)); border:0.5px solid var(--border);
  border-radius:10px; padding:0 7px; }
.qz-head-sub { font-size:var(--fs-2); color:var(--text3); margin-top:4px; max-width:760px; }

.qz-bulk { display:flex; align-items:center; justify-content:space-between;
  gap:12px; padding:8px 12px; margin-bottom:8px; border:0.5px solid var(--border);
  border-radius:8px; background:var(--surface,#141b27); opacity:.55; }
.qz-bulk.on { opacity:1; border-color:var(--accent,#4a78d6); }
.qz-bulk-count { font-size:var(--fs-2); color:var(--text2,var(--text3)); font-weight:500; }
.qz-bulk-actions { display:flex; gap:6px; }

.qz-tablewrap { max-height:60vh; overflow:auto; border:0.5px solid var(--border);
  border-radius:8px; }
.qz-tbl { width:100%; border-collapse:collapse; font-size:var(--fs-4); }
.qz-tbl thead th { position:sticky; top:0; z-index:1; background:var(--surface2,var(--nav-surface));
  text-align:left; font-weight:600; font-size:var(--fs-5); color:var(--text);
  text-transform:none; letter-spacing:-0.01em;
  padding:var(--pad-head-y) var(--pad-cell-x);
  border-bottom:2px solid var(--border2); }
.qz-tbl tbody td { padding:7px 10px; border-bottom:0.5px solid var(--border); vertical-align:middle; }
.qz-tbl tbody tr:hover { background:var(--surface-hover,rgba(255,255,255,.03)); }
.qz-c-chk { width:34px; text-align:center; }
.qz-c-val { min-width:200px; }
.qz-c-fk { width:120px; color:var(--text3); font-size:var(--fs-2); }
.qz-c-reason { width:130px; }
.qz-c-act { width:200px; text-align:right; white-space:nowrap; }
.qz-val { font-family:var(--font-mono,monospace); font-weight:600; color:var(--text); }
.qz-reason { font-size:var(--fs-1); color:var(--amber,#d08a3a); }
.qz-rel { color:var(--red,#d05a5a); font-weight:700; }
.qz-origin { font-size:var(--fs-1); color:var(--text3); font-style:italic;
  border:0.5px solid var(--border); border-radius:8px; padding:0 5px; margin-left:4px; }
.btn.xs { font-size:var(--fs-1); padding:3px 8px; margin-left:4px; }
.btn.sm { font-size:var(--fs-2); padding:5px 11px; }
/* Ghost stays scoped to the SMALL sizes it was written for. Broadening it to
   every .btn.ghost caught five buttons that had always been solid and made
   them near-invisible on a light surface.

   ⚠️⚠️ THE BOUNDARY IS --border-control, AND THIS IS THE FIFTH TIME THE OWNER
   HAS REPORTED A GHOST AS "JUST TEXT" (3 Aug, 11 Aug twice, 13 Aug twice).
   Four of those I fixed as instances. This time I measured the variant
   itself, and it fails the standard this platform already adopted:

     --border2      0.26 alpha -> 1.67:1 light, 2.07:1 dark
     --border-control            5.15:1 light, 5.13:1 dark

   WCAG 1.4.11 asks 3:1 for the boundary of a user-interface component.
   --border2 was chosen over --border on the argument that "a hairline is a
   divider, not the only edge a control has" -- correct as far as it went, and
   1.67:1 is still not an edge anybody can see. A CONTROL WHOSE BOUNDARY IS
   BELOW 3:1 IS NOT A QUIETER BUTTON, IT IS A CAPTION.

   ⚠️ WHAT THIS COSTS, said plainly: with a visible boundary, a ghost now
   looks close to a plain `.btn` on a white surface -- both are outlined, and
   only the background differs (transparent vs --surface), which shows on a
   tinted panel and not on the page. The hierarchy that still reads at a
   glance is `.accent` (filled) against everything else. Whether `.ghost`
   earns its place as a separate variant at all is worth deciding; that is a
   platform decision, not one to make inside a bug fix. */
.btn.xs.ghost, .btn.sm.ghost { background:transparent;
  border:0.5px solid var(--border-control); color:var(--text2,var(--text3)); }

/* ── Tag validity policy (Settings) ── */
.tp-row { display:flex; align-items:center; gap:10px; }
.tp-enable { justify-content:space-between; padding:10px 12px; border:0.5px solid var(--border);
  border-radius:8px; margin-bottom:14px; }
.tp-switch { display:flex; align-items:center; gap:8px; font-size:var(--fs-4); cursor:pointer; }
.tp-state { font-size:var(--fs-1); font-weight:700; padding:1px 8px; border-radius:10px; }
.tp-state.on { color:var(--green); background:var(--green-dim); }
.tp-state.off { color:var(--text3); background:var(--surface2,var(--nav-surface)); }
.tp-sec { padding:12px 0; border-top:0.5px solid var(--border); }
.tp-sec-h { font-weight:600; font-size:var(--fs-4); margin-bottom:3px; }
.tp-sec-hint { font-size:var(--fs-2); color:var(--text3); margin-bottom:8px; }
.tp-pats { display:flex; flex-direction:column; gap:5px; margin-bottom:8px; }
.tp-pat { display:flex; align-items:center; justify-content:space-between; gap:10px;
  padding:5px 9px; background:var(--surface2,var(--nav-surface)); border:0.5px solid var(--border); border-radius:6px; }
.tp-pat-rx { font-family:var(--font-mono,monospace); font-size:var(--fs-2); color:var(--text); }
.tp-empty { font-size:var(--fs-2); color:var(--text3); font-style:italic; margin-bottom:8px; }
.tp-addrow, .tp-testrow { display:flex; align-items:center; gap:8px; margin-top:6px; }
.tp-addrow input, .tp-testrow input { flex:0 0 320px; max-width:320px; padding:5px 9px;
  border:0.5px solid var(--border); border-radius:6px; background:var(--bg); color:var(--text);
  font-family:var(--font-mono,monospace); font-size:var(--fs-2); }
.tp-test-res { font-size:var(--fs-2); font-weight:500; }
.tp-test-res.ok { color:var(--green); }
.tp-test-res.no { color:var(--amber,#d08a3a); }
.tp-radio, .tp-check { display:flex; align-items:center; gap:8px; font-size:var(--fs-4); padding:3px 0; cursor:pointer; }
.tp-nums { display:flex; gap:16px; margin-top:8px; flex-wrap:wrap; }
.tp-nums label { font-size:var(--fs-2); color:var(--text2,var(--text3)); display:flex; align-items:center; gap:6px; }
.tp-nums input { width:64px; padding:4px 6px; border:0.5px solid var(--border); border-radius:6px;
  background:var(--bg); color:var(--text); font-size:var(--fs-2); }
.tp-actions { display:flex; align-items:center; gap:10px; margin-top:14px; padding-top:12px;
  border-top:0.5px solid var(--border); }
.tp-busy { font-size:var(--fs-2); color:var(--text3); }

/* ═══════════════════════════════════════════════════════════
   D0 DESIGN SYSTEM — component vocabulary
   Flat surfaces · hairline separation · radius scale · lowercase mono
   labels. No raw hex below this line — tokens only, both themes.
   ═══════════════════════════════════════════════════════════ */

/* ── System label: lowercase mono. The difference between software that
      talks to you and software that reports state. Labels/status only —
      never prose, buttons, or page titles. ── */
.d0-label{font-family:var(--font-mono);font-size:var(--fs-1);font-weight:400;
  text-transform:lowercase;letter-spacing:.02em;color:var(--text3)}

/* ── Display type ── */
.d0-title{font-family:var(--font-display);font-size:var(--fs-7);font-weight:500;
  letter-spacing:-.02em;color:var(--text)}
.d0-heading{font-family:var(--font-display);font-size:var(--fs-6);font-weight:500;
  letter-spacing:-.01em;color:var(--text)}

/* ── Card: flat. Separation by hairline, never shadow. Header gets a rule,
      not padding. ── */
.d0-card{background:var(--surface);border:0.5px solid var(--border);
  border-radius:var(--radius-md);box-shadow:none}
.d0-card-header{padding:10px 14px;border-bottom:0.5px solid var(--border);
  font-family:var(--font-display);font-size:var(--fs-4);font-weight:500;color:var(--text);
  display:flex;align-items:center;justify-content:space-between;gap:10px}
.d0-card-body{padding:12px 14px}

/* ── Metric card: no border, display font on the number ── */
.d0-metric{background:var(--surface2);border-radius:var(--radius-md);padding:14px}
.d0-metric-cap{font-family:var(--font-mono);font-size:var(--fs-1);text-transform:lowercase;
  color:var(--text3);margin-bottom:4px}
.d0-metric-val{font-family:var(--font-display);font-size:var(--fs-8);font-weight:500;
  color:var(--text);letter-spacing:-.02em}

/* ── Tag: outlined, lowercase mono. Filled pills are a consumer pattern;
      outlined labels are what engineering tools use. ── */
.d0-tag{display:inline-flex;align-items:center;gap:5px;font-family:var(--font-mono);
  font-size:var(--fs-1);text-transform:lowercase;letter-spacing:.02em;padding:2px 8px;
  border-radius:var(--radius-sm);border:0.5px solid currentColor;background:transparent}
.d0-tag.is-accent{color:var(--accent)}
.d0-tag.is-verified{color:var(--green)}
.d0-tag.is-ai{color:var(--amber)}
.d0-tag.is-conflict{color:var(--red)}
.d0-tag.is-cl{color:var(--blue)}
.d0-tag.is-neutral{color:var(--text3)}

/* ── Data grid: hairline rows, no zebra, mono values ── */
.d0-grid{width:100%;border-collapse:collapse;font-size:var(--fs-4)}
.d0-grid th{font-family:var(--font-mono);font-size:var(--fs-1);font-weight:400;
  text-transform:lowercase;letter-spacing:.02em;color:var(--text3);text-align:left;
  padding:8px 12px;border-bottom:0.5px solid var(--border2)}
.d0-grid td{padding:9px 12px;border-bottom:0.5px solid var(--border);color:var(--text)}
.d0-grid td.d0-val{font-family:var(--font-mono);color:var(--text)}
.d0-grid td.d0-key{color:var(--text2)}
.d0-grid tr:last-child td{border-bottom:none}
.d0-grid tbody tr:hover{background:var(--surface2);cursor:pointer}
.d0-grid-tickcol{width:34px;padding-left:14px;padding-right:0}

/* ── Verified core tick: the ambient grid marker. The nested-V from the
      logo. The ONE place brand amber lives in the product — it is a signal
      state, not decoration, so it never collides with indigo (interactive). ── */
.d0-tick{width:17px;height:17px;display:block}
.d0-tick .v{fill:none;stroke:var(--text3);stroke-width:1.6;opacity:.85}
.d0-tick .tick{fill:none;stroke:var(--verified-tick);stroke-width:2.8;
  stroke-linecap:round;stroke-linejoin:round}
.d0-tick.is-unconfirmed .tick{stroke:var(--text3);stroke-width:1.6;stroke-dasharray:2 2}
.d0-tick.is-missing .tick{display:none}

/* ── Lineage rail: THE signature element. Slide-out from any row, in any
      view. Milestone events only — field edits collapse behind them.
      Actionable: a lineage you can't act on is a museum. ── */
.d0-rail{position:fixed;top:0;right:0;bottom:0;width:340px;background:var(--surface);
  border-left:0.5px solid var(--border2);border-radius:var(--radius-lg) 0 0 var(--radius-lg);
  box-shadow:var(--shadow-pop);z-index:180;display:flex;flex-direction:column;
  transform:translateX(100%);transition:transform .22s ease;overflow:hidden}
.d0-rail.is-open{transform:translateX(0)}
.d0-rail-header{padding:12px 16px;border-bottom:0.5px solid var(--border);
  display:flex;align-items:center;justify-content:space-between;gap:10px;flex-shrink:0}
.d0-rail-title{font-family:var(--font-display);font-size:var(--fs-4);font-weight:500;color:var(--text)}
.d0-rail-body{flex:1;overflow-y:auto;padding:16px 0}
.d0-rail-track{position:relative;padding-left:16px}
.d0-rail-spine{position:absolute;left:23px;top:6px;bottom:6px;width:1px;
  background:var(--border3)}
.d0-rail-event{position:relative;padding:0 16px 18px 22px}
.d0-rail-event:last-child{padding-bottom:0}
.d0-rail-dot{position:absolute;left:4px;top:3px;width:7px;height:7px;border-radius:50%;
  background:var(--text3)}
.d0-rail-dot.is-cl{background:var(--blue)}
.d0-rail-dot.is-ai{background:var(--amber)}
.d0-rail-dot.is-issued{background:var(--text3)}
.d0-rail-dot.is-submitted{background:var(--green)}
.d0-rail-dot.is-approved{background:var(--green)}
.d0-rail-dot.is-conflict{background:var(--red)}
.d0-rail-ev-title{font-size:var(--fs-2);font-weight:500;color:var(--text)}
.d0-rail-ev-meta{font-family:var(--font-mono);font-size:var(--fs-1);text-transform:lowercase;
  color:var(--text3);margin-top:1px}
/* collapsed field-level detail — 300 edits render as 5 milestones, not 300 rows */
.d0-rail-more{margin-top:6px;font-family:var(--font-mono);font-size:var(--fs-1);
  text-transform:lowercase;color:var(--accent);background:none;border:none;padding:0;
  cursor:pointer}
.d0-rail-more:hover{text-decoration:underline}
.d0-rail-detail{margin-top:6px;padding-left:8px;border-left:0.5px solid var(--border)}
.d0-rail-detail-row{font-family:var(--font-mono);font-size:var(--fs-1);color:var(--text2);
  padding:2px 0;display:flex;justify-content:space-between;gap:8px}
/* actionable event — resolve from the rail, don't navigate away */
.d0-rail-actions{display:flex;gap:6px;margin-top:8px}
.d0-rail-btn{font-family:var(--font-mono);font-size:var(--fs-1);text-transform:lowercase;
  padding:3px 9px;border-radius:var(--radius-sm);border:0.5px solid var(--border2);
  background:transparent;color:var(--text2);cursor:pointer}
.d0-rail-btn:hover{border-color:var(--accent);color:var(--accent)}
.d0-rail-btn.is-danger:hover{border-color:var(--red);color:var(--red)}
.d0-rail-scrim{position:fixed;inset:0;background:rgba(0,0,0,0.28);z-index:179;
  opacity:0;pointer-events:none;transition:opacity .22s ease}
.d0-rail-scrim.is-open{opacity:1;pointer-events:auto}

/* ── Provenance chip: supporting role. Carries the SOURCE, which the tick
      can't. Used where width allows. tick -> chip -> rail = three densities,
      one vocabulary. ── */
.d0-chip{display:inline-flex;align-items:center;gap:4px;font-family:var(--font-mono);
  font-size:var(--fs-1);text-transform:lowercase;padding:2px 7px;border-radius:var(--radius-sm);
  border:0.5px solid currentColor;background:transparent}

/* ── Filter bar ── */
.d0-filterbar{display:flex;align-items:center;gap:10px;padding:8px 0 12px;
  border-bottom:0.5px solid var(--border);margin-bottom:12px}

/* ── Empty state: names the space, verb CTA. Not "nothing here yet". ── */
.d0-empty{padding:40px 20px;text-align:center}
.d0-empty-title{font-family:var(--font-display);font-size:var(--fs-5);font-weight:500;
  color:var(--text);margin-bottom:5px}
.d0-empty-body{font-size:var(--fs-4);color:var(--text2);margin-bottom:14px}

/* ── Chart frame: consistent axis/label treatment wrapping both bespoke SVG
      and the themed library. ── */
.d0-chart{background:var(--surface);border:0.5px solid var(--border);
  border-radius:var(--radius-md);padding:14px}
.d0-chart-title{font-family:var(--font-display);font-size:var(--fs-4);font-weight:500;
  color:var(--text);margin-bottom:2px}
.d0-chart-sub{font-family:var(--font-mono);font-size:var(--fs-1);text-transform:lowercase;
  color:var(--text3);margin-bottom:12px}
.d0-axis{font-family:var(--font-mono);font-size:var(--fs-1);fill:var(--text3)}

/* ── A1: mandatory-field filter count ── */
.vd-mx-mandcount{font-family:var(--font-mono);font-size:var(--fs-1);
  text-transform:lowercase;color:var(--text3);white-space:nowrap}

/* ── A1: validation overlay ── red = failed, amber = warning, green = passed.
   Green only where THIS import validated the cell and found nothing; an
   untouched cell stays neutral, because absence of a finding is not a pass. */
.vd-mx-cell.vd-mx-err,.vd-ds2-val.vd-ds2-err{background:var(--red-dim);
  box-shadow:inset 2px 0 0 var(--red)}
.vd-mx-cell.vd-mx-warn,.vd-ds2-val.vd-ds2-warn{background:var(--amber-dim);
  box-shadow:inset 2px 0 0 var(--amber)}
.vd-mx-cell.vd-mx-ok,.vd-ds2-val.vd-ds2-ok{background:var(--green-dim);
  box-shadow:inset 2px 0 0 var(--green)}
.vd-ds2-flag{margin-left:5px;font-weight:500;cursor:help}
.vd-ds2-flag-error{color:var(--red)}
.vd-ds2-flag-warning{color:var(--amber)}
/* Legend + jump-to-issue strip shown after an import. */
.vd-mx-legend{display:flex;align-items:center;gap:12px;flex-wrap:wrap;
  padding:7px 10px;margin:6px 0 10px;border:0.5px solid var(--border);
  border-radius:var(--radius);background:var(--surface2)}
.vd-mx-key{display:inline-flex;align-items:center;gap:5px;
  font-family:var(--font-mono);font-size:var(--fs-1);text-transform:lowercase;
  color:var(--text2)}
.vd-mx-sw{width:11px;height:11px;border-radius:2px;flex-shrink:0}
.vd-mx-sw.err{background:var(--red-dim);box-shadow:inset 2px 0 0 var(--red)}
.vd-mx-sw.warn{background:var(--amber-dim);box-shadow:inset 2px 0 0 var(--amber)}
.vd-mx-sw.ok{background:var(--green-dim);box-shadow:inset 2px 0 0 var(--green)}

/* ── A1: structural verdict banner. Ranks above the colour legend — "your
   workbook is the wrong shape" outranks "these values are wrong". ── */
.vd-mx-struct{padding:10px 12px;margin:6px 0 10px;border-radius:var(--radius);
  font-size:var(--fs-2);line-height:1.55}
.vd-mx-struct.is-bad{background:var(--red-dim);
  border:0.5px solid var(--red);color:var(--text)}
.vd-mx-struct.is-unknown{background:var(--amber-dim);
  border:0.5px solid var(--amber);color:var(--text)}
.vd-mx-struct-list{font-family:var(--font-mono);font-size:var(--fs-1);
  color:var(--text2);margin:5px 0}
.vd-mx-struct-warn{margin-top:5px;color:var(--text2)}

/* ══════════════════════════════════════════════════════════════════════════
   VERIDEX INSIGHTS — chart engine (vx-chart.js v1)

   APPEND THIS BLOCK TO styles.css, AND BUMP styles.css TO v67 IN index.html.

   The series colours below are the ONLY place chart colour is defined. The
   engine reads --vxc-1..8 and never contains a hex value, so a chart cannot
   be unreadable in dark mode without this file being wrong -- which is the
   point. Do not add hex to vx-chart.js.

   Series colours are assigned by fixed SLOT, never by rank, so filtering a
   chart never repaints it a different colour.
   ══════════════════════════════════════════════════════════════════════════ */

:root {
  --vxc-1: #2a78d6;
  --vxc-2: #eb6834;
  --vxc-3: #1baf7a;
  --vxc-4: #eda100;
  --vxc-5: #e87ba4;
  --vxc-6: #4a3aa7;
  --vxc-7: #008300;
  --vxc-8: #e34948;
  --vxc-neutral: #B4B2A9;
  --vxc-grid: #E1E0D9;
  --vxc-axis: #C3C2B7;
}

html[data-theme="dark"] {
  --vxc-1: #3987e5;
  --vxc-2: #d95926;
  --vxc-3: #199e70;
  --vxc-4: #c98500;
  --vxc-5: #d55181;
  --vxc-6: #9085e9;
  --vxc-7: #008300;
  --vxc-8: #e66767;
  --vxc-neutral: #5F5E5A;
  --vxc-grid: #2C2C2A;
  --vxc-axis: #383835;
}

/* ── host + plot ───────────────────────────────────────────────────────── */
.vxc-host { width: 100%; }
.vxc-plot { width: 100%; }
.vxc-svg { width: 100%; height: auto; display: block; overflow: visible; }
.vxc-plot-radial .vxc-svg { max-width: 190px; margin: 0 auto; }

.vxc-empty {
  font-size:var(--fs-2);
  color: var(--text3);
  padding: 28px 8px;
  text-align: center;
}

/* ── chrome: gridlines, baseline, tick + category labels ─────────────────
   Recessive on purpose. Chrome orients the eye; it must never compete with
   the data. Text wears text tokens, never a series colour. */
.vxc-grid   { stroke: var(--vxc-grid); stroke-width: 1; shape-rendering: crispEdges; }
.vxc-axis   { stroke: var(--vxc-axis); stroke-width: 1; shape-rendering: crispEdges; }
/* 12px, up from 10/11. An axis label nobody can read is chrome, not
   information -- and these are the labels that tell you WHICH bar is which. */
.vxc-tick   { font-family: var(--font-body); font-size:var(--fs-2); fill: var(--text3); }
.vxc-cat    { font-family: var(--font-body); font-size:var(--fs-2); fill: var(--text2);
              font-weight: 500; }

/* ── marks ─────────────────────────────────────────────────────────────── */
.vxc-mark      { transition: opacity 120ms ease; }
.vxc-mark:hover { opacity: 0.82; }
.vxc-clickable { cursor: pointer; }

.vxc-line { stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.vxc-area { opacity: 0.10; }
.vxc-dot  { stroke: var(--surface); stroke-width: 2; }

/* Dimmed state for Phase 1 cross-filter: clicking one mark mutes the rest.
   Defined now so the interaction layer has nothing to invent later. */
.vxc-mark.vxc-dim { opacity: 0.22; }

/* ── legend ────────────────────────────────────────────────────────────── */
/* The legend is a flex ITEM of the tile body, which is itself a flex column.
   Without flex:none it gets compressed to nothing when the SVG below asks for
   space -- the legend does not disappear, it is squashed to zero height, which
   looks identical and is much harder to diagnose. */
.vxc-legend {
  display: flex; flex-wrap: wrap; gap: 5px 14px;
  margin: 0 0 10px;
  padding: 0;
  flex: none;
  font-size:var(--fs-2); color: var(--text2);
  line-height: 1.3;
}
.vxc-leg { display: inline-flex; align-items: center; gap: 6px; }
.vxc-leg-s {
  width: 10px; height: 10px; border-radius: 2px; flex: none;
}

/* ── tooltip ───────────────────────────────────────────────────────────────
   Appended to <body>, so it is positioned in viewport coordinates and cannot
   be clipped by a tile's overflow. */
.vxc-tip {
  position: fixed; display: none; z-index: 9999;
  pointer-events: none;
  background: var(--surface);
  border: 0.5px solid var(--border2);
  border-radius: var(--radius);
  box-shadow: var(--shadow-pop);
  padding: 7px 10px;
  font-family: var(--font-body);
  max-width: 240px;
}
.vxc-tip-d { font-size:var(--fs-1); color: var(--text3); margin-bottom: 2px; }
.vxc-tip-v { font-size:var(--fs-2); color: var(--text); font-weight: 500; }

/* ── print / PDF ───────────────────────────────────────────────────────────
   Charts must survive a black-and-white print: the dashed second series and
   the legend carry the meaning, not the colour alone. */
@media print {
  .vxc-tip { display: none !important; }
  .vxc-mark:hover { opacity: 1; }
}


/* ══════════════════════════════════════════════════════════════════════════
   VERIDEX INSIGHTS — dashboard canvas (vx-canvas.js v1)

   APPEND THIS BLOCK TO styles.css, BELOW THE CHART ENGINE BLOCK.
   Then bump styles.css to v68 in index.html.

   Tiles are absolutely positioned by the JS in pixels derived from grid
   units; this file styles them but never places them. Nothing here should
   set top/left/width/height on .vxq-tile -- that is the canvas's job, and a
   CSS override would silently fight the drag maths.
   ══════════════════════════════════════════════════════════════════════════ */

.vxq-canvas {
  position: relative;
  width: 100%;
  min-height: 120px;
}

/* ── tile shell ────────────────────────────────────────────────────────── */
.vxq-tile {
  position: absolute;
  background: var(--surface);
  border: 0.5px solid var(--border);
  border-radius: var(--radius-md);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  box-sizing: border-box;
}

.vxq-tile-h {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 8px 10px 6px;
  flex: none;
}

.vxq-tile-t {
  font-family: var(--font-body);
  /* 13.5px. A tile title names what you are looking at; at 12px it read as a
     caption on the chart rather than a heading over it. */
  font-size:var(--fs-4);
  font-weight: 600;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.vxq-tile-b {
  flex: 1 1 auto;
  min-height: 0;
  padding: 0 10px 10px;
  overflow: hidden;
}

/* A KPI tile is short and its number is the whole point, so it centres
   rather than top-aligning like a chart. */
.vxq-tile-kpi .vxq-tile-b {
  display: flex;
  align-items: center;
}

.vxq-tile-err {
  font-size:var(--fs-1);
  color: var(--text3);
  padding: 14px 4px;
  text-align: center;
}

/* ── edit affordances ────────────────────────────────────────────────────
   Hidden until hover so a dashboard in normal use reads as a document, not
   as a builder. They remain in the DOM (not display:none on a parent) so
   keyboard focus can still reach them. */
.vxq-tile-tools {
  display: flex;
  gap: 2px;
  opacity: 0;
  transition: opacity 120ms ease;
}
.vxq-tile:hover .vxq-tile-tools,
.vxq-tile-tools:focus-within {
  opacity: 1;
}

.vxq-ico {
  background: none;
  border: 0;
  cursor: pointer;
  font-size: 12px;
  line-height: 1;
  padding: 3px 5px;
  border-radius: var(--radius-sm);
  color: var(--text3);
}
.vxq-ico:hover { background: var(--surface3); color: var(--text); }
.vxq-ico:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.vxq-draggable { cursor: grab; }
body.vxq-dragging,
body.vxq-dragging .vxq-draggable { cursor: grabbing; }

/* While dragging, suppress text selection across the page -- otherwise a
   drag paints a selection over every tile it crosses. */
body.vxq-dragging {
  user-select: none;
  -webkit-user-select: none;
}

/* Resize grip, bottom-right corner. */
.vxq-grip {
  position: absolute;
  right: 0;
  bottom: 0;
  width: 14px;
  height: 14px;
  cursor: nwse-resize;
  opacity: 0;
  transition: opacity 120ms ease;
}
.vxq-grip::after {
  content: '';
  position: absolute;
  right: 3px;
  bottom: 3px;
  width: 6px;
  height: 6px;
  border-right: 1.5px solid var(--border3);
  border-bottom: 1.5px solid var(--border3);
}
.vxq-tile:hover .vxq-grip { opacity: 1; }

/* ── add tile ──────────────────────────────────────────────────────────── */
.vxq-add {
  position: absolute;
  left: 0;
  background: none;
  border: 1px dashed var(--border2);
  border-radius: var(--radius-md);
  color: var(--text3);
  font-family: var(--font-body);
  font-size:var(--fs-2);
  padding: 8px 14px;
  cursor: pointer;
}
.vxq-add:hover {
  border-color: var(--accent);
  color: var(--accent);
  background: var(--accent-dim2);
}
.vxq-add:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* ── print / PDF ───────────────────────────────────────────────────────────
   Edit chrome must never reach a printed page. */
@media print {
  .vxq-tile-tools,
  .vxq-grip,
  .vxq-add { display: none !important; }
  .vxq-tile {
    break-inside: avoid;
    border-color: var(--border2);
  }
}


/* ══════════════════════════════════════════════════════════════════════════
   VERIDEX INSIGHTS — runtime slicer bar (vx-slicer.js v1)

   APPEND THIS BLOCK TO styles.css, BELOW THE CANVAS BLOCK.
   Then bump styles.css to v69 in index.html.
   ══════════════════════════════════════════════════════════════════════════ */

.vxf-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  padding: 10px 12px;
  background: var(--surface2);
  border: 0.5px solid var(--border);
  border-radius: var(--radius-md);
  margin-bottom: 12px;
}

.vxf-lead {
  font-family: var(--font-mono);
  font-size:var(--fs-1);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text3);
  margin-right: 2px;
}

/* ── chips ─────────────────────────────────────────────────────────────── */
.vxf-chipwrap { position: relative; display: inline-flex; align-items: center; }

.vxf-chip {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  background: var(--surface);
  border: 0.5px solid var(--border2);
  border-radius: var(--radius);
  padding: 5px 10px;
  font-family: var(--font-body);
  font-size:var(--fs-2);
  color: var(--text2);
  cursor: pointer;
  max-width: 260px;
}
.vxf-chip:hover { border-color: var(--border3); }
.vxf-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* An ACTIVE slicer must be obvious at a glance: a dashboard read from a
   filtered state without noticing the filter is the core reporting error. */
.vxf-chip.vxf-on {
  border-color: var(--accent);
  background: var(--accent-dim2);
  color: var(--text);
}

.vxf-chip-l { color: var(--text3); flex: none; }
.vxf-chip.vxf-on .vxf-chip-l { color: var(--accent); }
.vxf-chip-v {
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.vxf-caret { font-size:9px; color: var(--text3); flex: none; }

.vxf-x {
  background: none;
  border: 0;
  cursor: pointer;
  font-size:var(--fs-1);
  line-height: 1;
  padding: 4px 5px;
  margin-left: -2px;
  border-radius: var(--radius-sm);
  color: var(--text3);
}
.vxf-x:hover { color: var(--red); background: var(--red-dim); }
.vxf-x:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.vxf-clear {
  background: none;
  border: 0;
  cursor: pointer;
  font-family: var(--font-body);
  font-size:var(--fs-2);
  color: var(--accent);
  padding: 5px 6px;
  border-radius: var(--radius-sm);
}
.vxf-clear:hover { background: var(--accent-dim); }
.vxf-clear:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* ── dropdown panel ────────────────────────────────────────────────────────
   The only floating layer here, so it carries the one permitted shadow. */
.vxf-panel {
  position: absolute;
  top: calc(100% + 5px);
  left: 0;
  z-index: 60;
  min-width: 190px;
  max-width: 280px;
  background: var(--surface);
  border: 0.5px solid var(--border2);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-pop);
  padding: 7px;
}

.vxf-search,
.vxf-date {
  width: 100%;
  box-sizing: border-box;
  font-family: var(--font-body);
  font-size:var(--fs-2);
  color: var(--text);
  background: var(--surface2);
  border: 0.5px solid var(--border2);
  border-radius: var(--radius);
  padding: 6px 8px;
  margin-bottom: 6px;
}
.vxf-search:focus,
.vxf-date:focus { outline: none; border-color: var(--accent); }

.vxf-lbl {
  display: block;
  font-family: var(--font-mono);
  font-size:var(--fs-1);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text3);
  margin-bottom: 3px;
}

/* Capped height with its own scroll: a discipline list can be long, and the
   alternative is a panel taller than the viewport. This is the one place
   nested scrolling is correct -- it is a menu, not content. */
.vxf-list {
  max-height: 240px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.vxf-opt {
  display: flex;
  align-items: center;
  gap: 7px;
  width: 100%;
  text-align: left;
  background: none;
  border: 0;
  cursor: pointer;
  font-family: var(--font-body);
  font-size:var(--fs-2);
  color: var(--text);
  padding: 6px 7px;
  border-radius: var(--radius-sm);
}
.vxf-opt:hover { background: var(--surface3); }
.vxf-opt:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.vxf-opt-on { color: var(--accent); font-weight: 600; }

.vxf-tick {
  width: 11px;
  flex: none;
  font-size:var(--fs-1);
  color: var(--accent);
}

.vxf-none {
  font-size:var(--fs-1);
  color: var(--text3);
  padding: 10px 6px;
  text-align: center;
}

/* ── coverage warning ──────────────────────────────────────────────────────
   Amber, not red: the dashboard is not broken, it is partially filtered. Red
   would train the user to dismiss it. Full-width so it sits below the chips
   rather than being mistaken for one. */
.vxf-warn {
  flex-basis: 100%;
  display: flex;
  align-items: flex-start;
  gap: 7px;
  font-family: var(--font-body);
  font-size:var(--fs-1);
  line-height: 1.45;
  color: var(--amber-on-tint);
  background: var(--amber-light);
  border: 0.5px solid var(--amber-dim);
  border-radius: var(--radius);
  padding: 7px 9px;
  margin-top: 2px;
}
.vxf-warn-i {
  flex: none;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--amber);
  color: var(--on-fill);
  font-size:var(--fs-1);
  font-weight: 700;
  line-height: 14px;
  text-align: center;
}

/* ── print / PDF ───────────────────────────────────────────────────────────
   A printed dashboard MUST still say what it was filtered by -- otherwise the
   PDF is a set of numbers with no stated scope. Chips and the warning print;
   only the interactive affordances are dropped. */
@media print {
  .vxf-panel,
  .vxf-x,
  .vxf-clear,
  .vxf-caret { display: none !important; }
  .vxf-bar { border-color: var(--border2); }
}


/* ══════════════════════════════════════════════════════════════════════════
   VERIDEX INSIGHTS — dashboard (vx-dashboard.js)

   REPLACE the existing dashboard block at the end of styles.css with this
   one, then bump styles.css in index.html.
   ══════════════════════════════════════════════════════════════════════════ */

/* NOT sticky -- see the note further down about .rep-main. The dashboard
   scrolls as one document, so nothing can end up hidden behind the title. */
.vxd-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 12px;
}

.vxd-title {
  font-family: var(--font-display);
  font-size:var(--fs-7);
  font-weight: 700;
  color: var(--text);
  letter-spacing: -0.01em;
}

.vxd-actions { display: flex; gap: 6px; }

/* ── KPI tile ──────────────────────────────────────────────────────────────
   The number is the tile. Everything else is subordinate to it, which is why
   the value is the only element with display weight. */
.vxd-kpi {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 2px;
  width: 100%;
}

.vxd-kpi-v {
  font-family: var(--font-display);
  /* 32px, up from 26. A KPI is read across a meeting room, not from a laptop
     at arm's length -- it is the one number on the page that must survive a
     projector. */
  font-size:var(--fs-9);
  font-weight: 700;
  line-height: 1.05;
  color: var(--text);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}

.vxd-kpi-d {
  font-size:var(--fs-2);
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}
/* Direction is set per tile (a rising orphan count is bad), so these classes
   carry the JUDGEMENT, not the arrow direction. */
.vxd-kpi-d.good { color: var(--green); }
.vxd-kpi-d.bad  { color: var(--red); }
.vxd-kpi-d.flat { color: var(--text3); }

.vxd-kpi-sub {
  font-size:var(--fs-2);
  color: var(--text3);
  font-variant-numeric: tabular-nums;
}

/* ── chart tile ────────────────────────────────────────────────────────────
   The chart fills the tile it was given. Height comes from the tile's grid
   rows, so a resized tile redraws at the new size rather than scrolling. */
.vxd-chart {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-height: 0;
  /* The SVG keeps its own aspect ratio. Forcing height:100% on it squashes the
     plot until the rotated category labels fall outside the tile's clip -- the
     labels look "missing" when they are merely off-canvas. Let it size from
     its width and give the tile enough rows instead. */
  overflow: visible;
}
.vxd-chart .vxc-svg {
  width: 100%;
  height: auto;
  max-height: 100%;
  overflow: visible;
}

/* ── table tile ────────────────────────────────────────────────────────── */
.vxd-tablewrap {
  height: calc(100% - 18px);
  overflow: auto;
}
.vxd-tablewrap .rep-table { font-size:var(--fs-2); }
.vxd-tablewrap thead th {
  position: sticky;
  top: 0;
  background: var(--surface);
  z-index: 1;
}
.vxd-tablefoot {
  font-size:var(--fs-1);
  color: var(--text3);
  padding-top: 4px;
}

/* ── tile states ───────────────────────────────────────────────────────────
   Three distinct states, deliberately worded differently. "Loading" is
   temporary, "note" is a real answer (there is nothing to show, and why), and
   "err" is a failure. Collapsing them into one grey message is how a broken
   tile gets mistaken for an empty one. */
.vxd-load,
.vxd-note,
.vxd-err {
  font-size:var(--fs-2);
  line-height: 1.5;
  padding: 12px 4px;
  text-align: center;
}
.vxd-load { color: var(--text3); }
.vxd-note { color: var(--text2); }
.vxd-err  { color: var(--red-on-tint); }

/* ── print / PDF ───────────────────────────────────────────────────────────
   A printed dashboard keeps its numbers and its filter chips; only the
   controls go. */
@media print {
  .vxd-actions { display: none !important; }
  .vxd-tablewrap { overflow: visible; height: auto; }
}

/* ── delete control on a saved report row ──────────────────────────────────
   Owner-requested: deletable while still in development. Hidden until the row
   is hovered or focused, so a list of reports does not read as a list of
   delete buttons. Focus-within keeps it keyboard-reachable. */
.rep-item { position: relative; }

.rep-del {
  margin-left: auto;
  background: none;
  border: 0;
  cursor: pointer;
  font-size:var(--fs-1);
  line-height: 1;
  padding: 4px 5px;
  border-radius: var(--radius-sm);
  color: var(--text3);
  opacity: 0;
  transition: opacity 120ms ease;
  flex: none;
}
.rep-item:hover .rep-del,
.rep-del:focus-visible { opacity: 1; }
.rep-del:hover { color: var(--red); background: var(--red-dim); }
.rep-del:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* ── layout clearance ──────────────────────────────────────────────────────
   The canvas positions its tiles absolutely inside a container whose height
   it sets itself, so anything above it must occupy real flow height or the
   tiles ride up over it -- which is what a collapsed slicer bar looks like. */
#vxd-canvas {
  position: relative;
  z-index: 1;
  clear: both;
}

/* A chart tile's body gets no bottom padding: the SVG's own bottom margin is
   already sized for the labels, and stacking the two wastes the space the
   labels need. */
.vxq-tile:not(.vxq-tile-kpi) .vxq-tile-b {
  padding-bottom: 4px;
}

/* ── the dashboard lives inside .rep-main, which scrolls ──────────────────
   .rep-main is `overflow:auto; position:relative; padding:18px 20px`.

   An earlier version made the header and slicer bar STICKY so the filters
   stayed visible while scrolling. That was wrong here, and the failure is
   worth recording: `position:relative` on .rep-main makes it the containing
   block for sticky children, so their `top` offsets against the PADDING BOX,
   not the visible pane. A negative `top` (to cancel the padding) parks the
   header just above the viewport, and the slicer -- pinned beneath a header
   whose height it has to guess -- ends up behind it. Two sticky siblings that
   must agree on each other's height is a constraint nobody can maintain.

   The dashboard now scrolls as one document. The filter bar is at the top
   where it is read, and nothing can hide behind anything. */
#vxd-slicer {
  position: relative;   /* stacking context only -- the dropdown opens over the
                           tiles below, which are themselves positioned */
  z-index: 5;
  margin-bottom: 12px;
}

#vxd-slicer .vxf-bar {
  min-height: 40px;
  box-sizing: border-box;
  margin-bottom: 0;
}

/* ── drill-through (Phase 1) ───────────────────────────────────────────────
   A slide-over panel, appended to <body> rather than into the dashboard.
   .rep-main is a scroll container with `overflow:auto`, so a panel rendered
   inside it would be clipped by the pane and scroll away with the tiles --
   the same constraint that ruled out sticky positioning above. */
.vxd-drill {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  width: min(760px, 88vw);
  z-index: 400;
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border-left: 0.5px solid var(--border2);
  box-shadow: var(--shadow-pop);
}

.vxd-drill-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 16px;
  border-bottom: 0.5px solid var(--border);
  flex: none;
}

.vxd-drill-t {
  font-family: var(--font-display);
  font-size:var(--fs-5);
  font-weight: 600;
  color: var(--text);
}

/* The panel scrolls, not the page behind it. */
.vxd-drill-scroll {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
  padding: 0 4px;
}
.vxd-drill-scroll .rep-table { font-size:var(--fs-2); }
.vxd-drill-scroll thead th {
  position: sticky;
  top: 0;
  background: var(--surface);
  z-index: 1;
}

.vxd-drill-foot {
  flex: none;
  padding: 8px 16px;
  font-size:var(--fs-2);
  color: var(--text3);
  border-top: 0.5px solid var(--border);
}

/* Amber, matching the slicer's coverage warning: the rows ARE correct, they
   are simply narrower in scope than the dashboard's filter bar implies. Red
   would say "broken", and the user would stop reading the numbers. */
.vxd-drill-warn {
  flex: none;
  margin: 10px 12px 6px;
  padding: 8px 10px;
  font-size:var(--fs-2);
  line-height: 1.45;
  color: var(--amber-on-tint);
  background: var(--amber-light);
  border: 0.5px solid var(--amber-dim);
  border-radius: var(--radius);
}

.vxd-drill .vxd-load,
.vxd-drill .vxd-note,
.vxd-drill .vxd-err { padding: 32px 16px; }

/* A KPI you can open must look like one -- but quietly. The number is the
   subject; a button chrome around it would compete with the figure itself. */
.vxd-clickable { cursor: pointer; border-radius: var(--radius-sm); }
.vxd-clickable:hover .vxd-kpi-v { color: var(--accent); }
.vxd-clickable:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

@media print {
  .vxd-drill { display: none !important; }
}

/* ── text tile ─────────────────────────────────────────────────────────────
   A note on a dashboard carries what the numbers cannot: a caveat, a
   deadline, an exclusion. It reads as prose, not as a caption. */
.vxd-text {
  font-size:var(--fs-4);
  line-height: 1.55;
  color: var(--text2);
  white-space: pre-wrap;
  overflow: auto;
  height: 100%;
}

/* ── provenance (Phase 3) ──────────────────────────────────────────────────
   Sits OVER the drill panel, narrower and inset, so the rows stay visible
   behind it. The two are one train of thought -- "which rows" then "what does
   this count" -- and replacing the first with the second would lose the
   context the reader just opened. */
.vxd-prov {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  width: min(480px, 78vw);
  z-index: 420;
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border-left: 0.5px solid var(--border2);
  box-shadow: var(--shadow-pop);
}

.vxd-prov-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
  padding: 14px 16px 20px;
}

/* The figure, restated at the top. The reader arrived here from a number;
   showing it again anchors everything below to the thing they clicked. */
.vxd-prov-fig {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 4px 10px;
  padding-bottom: 12px;
  margin-bottom: 14px;
  border-bottom: 0.5px solid var(--border);
}
.vxd-prov-v {
  font-family: var(--font-display);
  font-size:var(--fs-9);
  font-weight: 700;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}
.vxd-prov-frac {
  font-size:var(--fs-4);
  color: var(--text2);
  font-variant-numeric: tabular-nums;
}
.vxd-prov-when { font-size:var(--fs-2); color: var(--text3); }

.vxd-prov-s { margin-bottom: 14px; }
.vxd-prov-h {
  font-family: var(--font-mono);
  font-size:var(--fs-1);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text3);
  margin-bottom: 5px;
}
.vxd-prov-s p {
  margin: 0;
  font-size:var(--fs-4);
  line-height: 1.55;
  color: var(--text);
}
.vxd-prov-ul {
  margin: 0;
  padding-left: 17px;
  font-size:var(--fs-4);
  line-height: 1.55;
  color: var(--text);
}
.vxd-prov-ul li { margin-bottom: 5px; }

/* Amber and boxed. A caveat folded in with the rules reads as just another
   definition; the whole point is that it is a known imprecision the reader
   must weigh. */
.vxd-prov-caveat {
  background: var(--amber-light);
  border: 0.5px solid var(--amber-dim);
  border-radius: var(--radius);
  padding: 10px 12px;
  margin-bottom: 14px;
  font-size:var(--fs-2);
  color: var(--amber-on-tint);
}
.vxd-prov-caveat b { display: block; margin-bottom: 4px; }
.vxd-prov-caveat .vxd-prov-ul { color: var(--amber-on-tint); }

/* Names the function that computes the figure. A declared provenance can go
   stale like any comment; this is how a reader checks it in one step. */
.vxd-prov-fn {
  margin-top: 16px;
  padding-top: 10px;
  border-top: 0.5px solid var(--border);
  font-size:var(--fs-1);
  color: var(--text3);
}
.vxd-prov-fn code {
  font-family: var(--font-mono);
  font-size:var(--fs-1);
  color: var(--text2);
}

.vxd-drill-tools { display: flex; align-items: center; gap: 8px; }

@media print {
  .vxd-prov { display: none !important; }
}

/* ── baseline comparison (Phase 3) ─────────────────────────────────────────
   Reuses .vxd-drill for its shell: same shape, same behaviour, and a second
   near-identical panel definition would drift from it. */
.vxd-cmp-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
  padding: 12px 16px 20px;
}

.vxd-cmp-pick {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  font-size:var(--fs-2);
  color: var(--text2);
  margin-bottom: 12px;
}
.vxd-cmp-sel {
  font-family: var(--font-body);
  font-size:var(--fs-2);
  color: var(--text);
  background: var(--surface2);
  border: 0.5px solid var(--border2);
  border-radius: var(--radius);
  padding: 5px 8px;
}
.vxd-cmp-sel:focus { outline: none; border-color: var(--accent); }

.vxd-cmp-head {
  font-size:var(--fs-4);
  color: var(--text);
  background: var(--surface2);
  border-radius: var(--radius);
  padding: 9px 12px;
  margin-bottom: 12px;
}
.vxd-cmp-head .good { color: var(--green); }
.vxd-cmp-head .bad { color: var(--red); }
.vxd-cmp-flat { color: var(--text2); }

.vxd-cmp-tbl { font-size:var(--fs-2); }
.vxd-cmp-tbl td { vertical-align: top; }
.vxd-cmp-d { font-variant-numeric: tabular-nums; font-weight: 600; }

/* Colour lands on the CHANGE cell only. Tinting the whole row would make a
   table of five metrics read as an alarm board. */
.vxd-cmp-tbl tr.good .vxd-cmp-d { color: var(--green); }
.vxd-cmp-tbl tr.bad .vxd-cmp-d { color: var(--red); }
.vxd-cmp-tbl tr.na td { color: var(--text3); }

.vxd-cmp-na { font-weight: 400; font-size:var(--fs-1); }

/* A moving denominator is the first thing to check on a moving ratio:
   coverage can "improve" purely because entries were deleted. */
.vxd-cmp-flag {
  display: inline-block;
  margin-left: 6px;
  font-size:var(--fs-1);
  padding: 1px 6px;
  border-radius: var(--radius-pill);
  color: var(--amber-on-tint);
  background: var(--amber-light);
  border: 0.5px solid var(--amber-dim);
  cursor: help;
}

@media print {
  #vxd-cmp-panel { display: none !important; }
}

/* ── governed reports (Phase 3) ────────────────────────────────────────────
   Reuses .vxd-drill for the shell, like the comparison panel. */
.vxd-cert-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
  padding: 14px 16px 20px;
}

.vxd-cert-form { display: flex; flex-direction: column; gap: 8px; }
.vxd-cert-form .btn { align-self: flex-start; margin-top: 2px; }

/* States the freeze BEFORE the button, not after. Someone about to put their
   name to a set of numbers should read what that means first. */
.vxd-cert-hint {
  font-size:var(--fs-2);
  line-height: 1.5;
  color: var(--text2);
  background: var(--surface2);
  border-radius: var(--radius);
  padding: 9px 11px;
}

.vxd-cert-row {
  padding: 9px 0;
  border-bottom: 0.5px solid var(--border);
  font-size:var(--fs-4);
  color: var(--text);
}
.vxd-cert-meta {
  font-size:var(--fs-1);
  color: var(--text3);
  margin-top: 2px;
}

.vxd-cert-state {
  display: inline-block;
  font-size:var(--fs-1);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 2px 7px;
  border-radius: var(--radius-pill);
  margin-right: 6px;
  background: var(--surface3);
  color: var(--text2);
}
/* Only two states get colour: approved (this is signed) and rejected (it is
   not). Pending, withdrawn and superseded are neutral facts, and colouring
   five states turns a history list into a traffic light nobody reads. */
.vxd-cert-state.s-approved {
  background: var(--green-dim);
  color: var(--green-on-tint);
}
.vxd-cert-state.s-rejected {
  background: var(--red-dim);
  color: var(--red-on-tint);
}

@media print {
  #vxd-cert-panel { display: none !important; }
}


/* ══════════════════════════════════════════════════════════════════════════
   VERIDEX INSIGHTS — tile editor (vx-tile-editor.js)

   APPEND THIS BLOCK TO styles.css, BELOW THE DASHBOARD BLOCK.
   ══════════════════════════════════════════════════════════════════════════ */

/* Fixed to the viewport, appended to <body>. The dashboard lives inside
   .rep-main, which clips and scrolls -- a modal rendered in there would be
   trapped in the pane. Same constraint that shaped the drill panel. */
.vxt-back {
  position: fixed;
  inset: 0;
  z-index: 500;
  background: rgba(0, 0, 0, 0.32);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}

.vxt-modal {
  width: min(520px, 100%);
  max-height: min(88vh, 720px);
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 0.5px solid var(--border2);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-pop);
}

.vxt-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 16px;
  border-bottom: 0.5px solid var(--border);
  flex: none;
}

.vxt-title {
  font-family: var(--font-display);
  font-size:var(--fs-5);
  font-weight: 600;
  color: var(--text);
}

.vxt-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
  padding: 14px 16px;
}

.vxt-f { margin-bottom: 14px; }

.vxt-l {
  display: block;
  font-size:var(--fs-2);
  color: var(--text2);
  margin-bottom: 5px;
}

.vxt-in {
  width: 100%;
  box-sizing: border-box;
  font-family: var(--font-body);
  font-size:var(--fs-4);
  color: var(--text);
  background: var(--surface2);
  border: 0.5px solid var(--border2);
  border-radius: var(--radius);
  padding: 8px 10px;
}
.vxt-in:focus { outline: none; border-color: var(--accent); }
textarea.vxt-in { resize: vertical; line-height: 1.5; }

.vxt-row { display: flex; gap: 12px; }
.vxt-row .vxt-f { flex: 1; }
.vxt-num { width: 100%; }

.vxt-check {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size:var(--fs-4);
  color: var(--text);
  margin-bottom: 14px;
  cursor: pointer;
}

/* Shown where a control WOULD be, when the chosen number cannot support it.
   An explanation in the control's own place beats a disabled dropdown, which
   says "not now" without saying why. */
.vxt-off {
  font-size:var(--fs-2);
  line-height: 1.5;
  color: var(--text3);
  background: var(--surface2);
  border: 0.5px solid var(--border);
  border-radius: var(--radius);
  padding: 9px 11px;
}

.vxt-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 16px;
  border-top: 0.5px solid var(--border);
  flex: none;
}

.vxt-hint {
  font-size:var(--fs-2);
  line-height: 1.4;
  color: var(--text3);
  flex: 1;
  min-width: 0;
}

/* Amber, not red: an incomplete tile is not an error, it is a form the user
   has not finished. Red here would read as "you broke something". */
.vxt-warn { color: var(--amber-on-tint); }

.vxt-actions { display: flex; gap: 6px; flex: none; }

@media print {
  .vxt-back { display: none !important; }
}


/* ══════════════════════════════════════════════════════════════════════
   SHARED BUTTON SYSTEM
   Supersedes .ed-btn / .vxp-btn / .vxpg-btn / .vmr-btn / .del-btn. Add a
   variant here; never a sixth per-module button class.
   ══════════════════════════════════════════════════════════════════════ */
/* .vx-btn is an ALIAS of .btn, not a second system. The shared declarations
   live on .btn above -- 26 files already use it -- and .vx-btn is grouped into
   those selectors so the two can never drift. Only genuinely new variants
   belong here. */
.vx-btn:focus-visible{outline:2px solid var(--accent);outline-offset:1px}
.vx-btn svg{flex-shrink:0}
.vx-btn-quiet{border-color:transparent;background:none;box-shadow:none}
.vx-btn-quiet:hover:not(:disabled){background:var(--accent-dim2)}
/* Toggle state for a button that reflects something switched on (grouping,
   filter row). Was .ed-btn.is-on; belongs to the shared system now. */
.vx-btn.is-on{border-color:var(--accent);color:var(--accent);
  background:var(--accent-dim2)}
.vx-btn-sm + .vx-btn-sm{margin-left:6px}
@media (prefers-reduced-motion: reduce){.vx-btn{transition:none}}

/* ══════════════════════════════════════════════════════════════════════
   CONTEXTUAL ACTION RAIL  (see vx-action-rail.js)
   Sits to the LEFT of the table, inside the content area -- not against the
   window edge, so it never becomes a second left column when the nav is
   pinned. Under 900px it lies down into a strip above the table.
   ══════════════════════════════════════════════════════════════════════ */
.vx-arailwrap{display:flex;gap:16px;align-items:flex-start}
.vx-arailwrap > .vx-arail{flex:0 0 190px}
.vx-arailwrap > .vx-arailbody{flex:1;min-width:0}
.vx-arail{display:flex;flex-direction:column;gap:6px;padding:12px;
  border:1px solid var(--border);border-radius:var(--radius-md);
  background:var(--surface2)}
.vx-arail-btn{width:100%;justify-content:flex-start;text-align:left}
/* The same rail as a toolbar across the top of a table. Opt-in via
   vxActionRail.mount({layout:'bar'}) — the column above stays the default,
   because a thirty-column key list wants its actions beside it, not over it.
   Buttons size to their label and wrap; the hint moves to the end of the row
   rather than under a border. */
.vx-arail.vx-arail-bar{flex-direction:row;flex-wrap:wrap;align-items:center;
  gap:8px;padding:8px 10px}
.vx-arail.vx-arail-bar > .vx-arail-btn{width:auto;text-align:center;
  justify-content:center}
.vx-arail.vx-arail-bar > .vx-arail-hint{border-top:none;margin:0 0 0 auto;
  padding:0 0 0 8px}
.vx-arail-hint{font-size:var(--fs-2);color:var(--text3);
  line-height:var(--lh-body);margin-top:2px;padding-top:8px;
  border-top:1px solid var(--border);word-break:break-word}
@media (max-width:900px){
  .vx-arailwrap{flex-direction:column}
  .vx-arailwrap > .vx-arail{flex:none;width:100%;flex-direction:row;
    flex-wrap:wrap;align-items:center;box-sizing:border-box}
  .vx-arail-btn{width:auto}
  .vx-arail-hint{border-top:none;margin:0;padding:0 0 0 8px;margin-left:auto}
}

/* ══════════════════════════════════════════════════════════════════════
   SELECTABLE ROWS
   The platform selection convention. Only the review workbench had one
   (.rv-tbl tbody tr.selected); this is the shared version.
   ══════════════════════════════════════════════════════════════════════ */
/* ══════════════════════════════════════════════════════════════════════
   SWITCH
   The platform had no toggle component -- every on/off control was a raw
   checkbox. The input underneath stays a checkbox so existing handlers and
   data-attributes are untouched; role="switch" makes it announce correctly.
   Also opts out of the bare label{} rule, which would otherwise render the
   caption in uppercase mono.
   ══════════════════════════════════════════════════════════════════════ */
.vx-switch{display:inline-flex;align-items:center;gap:8px;cursor:pointer;
  font-family:var(--font-body);font-size:var(--fs-4);color:var(--text2);
  text-transform:none;letter-spacing:normal;font-weight:400;margin-bottom:0;
  white-space:nowrap}
.vx-switch input{position:absolute;width:1px;height:1px;opacity:0;
  margin:0;pointer-events:none}
.vx-switch-track{position:relative;flex:0 0 auto;width:34px;height:20px;
  border-radius:10px;background:var(--surface2);
  border:1px solid var(--border-control);
  transition:background .15s,border-color .15s}
.vx-switch-thumb{position:absolute;top:2px;left:2px;width:14px;height:14px;
  border-radius:50%;background:var(--text3);
  transition:transform .15s,background .15s}
.vx-switch input:checked + .vx-switch-track{background:var(--accent);
  border-color:var(--accent)}
.vx-switch input:checked + .vx-switch-track .vx-switch-thumb{
  transform:translateX(14px);background:var(--on-fill)}
.vx-switch input:focus-visible + .vx-switch-track{outline:2px solid var(--accent);
  outline-offset:2px}
.vx-switch input:disabled + .vx-switch-track{opacity:.5;cursor:not-allowed}
@media (prefers-reduced-motion: reduce){
  .vx-switch-track,.vx-switch-thumb{transition:none}
}

.vx-selectable tbody tr{cursor:pointer}
.vx-selectable tbody tr:focus-visible{outline:2px solid var(--accent);
  outline-offset:-2px}
.vx-selectable tbody tr.is-selected > td{background:var(--accent-light)}
.vx-selectable tbody tr.is-selected > td:first-child{
  box-shadow:inset 3px 0 0 0 var(--accent)}

/* ══════════════════════════════════════════════════════════════════════
   STANDARD COLUMN HEADER
   Sentence case, ONE step above the value, heavier. Deliberately NOT the
   10px uppercase caption used by .vxs-tbl / .rep-table: at thirty columns
   a caption-sized header stops reading as structure and people lose their
   place in the grid.
   ══════════════════════════════════════════════════════════════════════ */
.vx-th{text-align:left;font-weight:600;font-size:var(--fs-5);color:var(--text);
  padding:var(--pad-head-y) var(--pad-cell-x);white-space:nowrap;
  letter-spacing:-0.01em;border-bottom:2px solid var(--border2);
  background:var(--surface2)}

/* ══════════════════════════════════════════════════════════════════════
   THE TABLE SHELL — one standard, taken from the Key lists grid
   ══════════════════════════════════════════════════════════════════════
   Owner, 2026-08-13: "Tables in the page must be scrollable, which
   header/column don't scroll. The column names and their sizes aren't
   aligned to Veridex standard, pls check Key lists table for reference
   (for font, symbol size, style) and update across the board using that
   as a basis."

   ⚠️ THE STANDARD ALREADY EXISTED AND WAS OPTED OUT OF. The bare `th{}`
   rule near the top of this file is the Key lists header — --fs-5 / 600,
   body face, surface2, a 2px rule. `.vxs-tbl th` then overrode it back to
   the 10px uppercase mono caption it was written to replace. A standard
   a module can silently undo with one more specific selector is not a
   standard, which is why `platform.cjs` now fails on that shape.

   What was genuinely MISSING is the shell: `.ed-gridwrap` on the key list
   scrolls itself and pins its header, and nothing else on the platform
   could without copying it. That is this block.

     .vx-tablewrap        a table that scrolls ITSELF, header pinned
     .vx-tablewrap.tall   for a long list — a taller ceiling
     .vx-tablewrap.short  for a summary — a shorter one

   A header cell only sticks if the container scrolls, so the two belong
   together and are shipped together.
   ══════════════════════════════════════════════════════════════════════ */
/* ══════════════════════════════════════════════════════════════════════
   ⚠️⚠️ WHAT WAS HERE WAS WRONG AND IS DELETED. In package 306 I added
   `.ed-gridwrap` and `.ed-mapacts` rules to this file, on the finding that
   `.ed-gridwrap` "has no rule anywhere in this codebase". That finding was
   false. It has one — in the style block `vx-ed-page.js` injects at runtime,
   which is where the whole `.ed-*` family lives and which this file's own
   comment above never mentions.

   Two consequences worth recording:

     * my rule never took effect at all. The injected block is appended later,
       so it wins the cascade. The fix I shipped and explained did nothing.
     * a class defined in two stylesheets is the same "two definitions of one
       idea" fault this codebase keeps paying for, in CSS form.

   `.ed-mapacts` went with it: those controls moved onto the shared action
   rail in package 308 and the class has no user left.

   ⚠️ THE `.ed-*` FAMILY LIVES IN `vx-ed-page.js`. Anything Engineering Data
   needs goes there, next to its siblings, not here.
   ══════════════════════════════════════════════════════════════════════ */

.vx-tablewrap{overflow:auto;max-height:min(62vh,640px);
  border:1px solid var(--border);border-radius:var(--radius-lg);
  background:var(--surface);margin-bottom:18px}
.vx-tablewrap.tall{max-height:min(76vh,900px)}
.vx-tablewrap.short{max-height:min(40vh,380px)}
.vx-tablewrap > table{margin-bottom:0;border-collapse:separate;
  border-spacing:0}
/* z-index 3: above a sticky first column (2) and any cell shadow (1). */
.vx-tablewrap thead th{position:sticky;top:0;z-index:3;
  background:var(--surface2)}
/* The wrapper draws the outer edge, so the table must not draw a second
   one inside it — two rules 1px apart is the "freelanced" look. */
.vx-tablewrap > table > thead > tr > th{border-top:none}
.vx-tablewrap > table > tbody > tr:last-child > td{border-bottom:none}

/* THE SURVEY TABLES JOIN THE STANDARD — the change is made IN the original
   `.vxs-tbl th` rule above, not bolted on here. A second rule for the same
   selector is how a stylesheet ends up with 51 duplicated selectors, which
   this platform has already paid to clean up once. */
/* The detail panel's inner tables are a definition list in table clothing,
   not a grid: they keep their caption header and their tighter density. */
.vxs-d-tb th{font-size:var(--fs-1);font-weight:500;text-transform:uppercase;
  letter-spacing:.07em;color:var(--text2);font-family:var(--font-mono);
  padding:6px 10px 6px 0;background:none;border-bottom:0.5px solid var(--border)}
.vxs-d-tb td{padding:3px 10px 3px 0;font-size:var(--fs-2)}
