/* =====================================================================================================
   VAULT — YTranscript "Vault" file manager · Increment 1
   Finder × Spacedrive × Linear. Dark-first, strict-CSP (style-src 'self'): ZERO inline styles — every
   dynamic value is written via element.style.setProperty() from vault.js; everything static lives here.
   Cascade Layers keep specificity flat as the sheet grows. Scoped entirely under `.vlt`.
   ===================================================================================================== */

@font-face { font-family:'Comfortaa'; src:url('fonts/comfortaa-400.woff2') format('woff2'); font-weight:400; font-display:swap; }
@font-face { font-family:'Comfortaa'; src:url('fonts/comfortaa-700.woff2') format('woff2'); font-weight:700; font-display:swap; }
@font-face { font-family:'Atkinson Hyperlegible'; src:url('fonts/atkinson-400.woff2') format('woff2'); font-weight:400; font-display:swap; }

@layer tokens, layout, components, state;

/* ============================== TOKENS ============================== */
@layer tokens {
  .vlt {
    /* --- one knob retints the whole surface stack (Spacedrive-235 charcoal, blue-tinted) --- */
    --hue: 235;
    --accent-h: 268; --accent-s: 88%; --accent-l: 68%;      /* YTranscript violet */
    --accent: var(--accent-h) var(--accent-s) var(--accent-l);

    /* surfaces — HSL triplets so every one gets free alpha via hsl(var(--x) / a) */
    --sidebar:  var(--hue) 16% 7.5%;
    --app:      var(--hue) 15% 10.5%;
    --box:      var(--hue) 14% 14.5%;
    --overlay:  var(--hue) 16% 13%;
    --hover:    var(--hue) 14% 18.5%;
    --selected: var(--accent-h) 42% 22%;
    --line:     var(--hue) 12% 21%;
    --frame:    var(--hue) 12% 27%;

    /* ink hierarchy */
    --ink:       var(--hue) 16% 95%;
    --ink-dull:  var(--hue) 11% 68%;
    --ink-faint: var(--hue) 9%  50%;

    /* status */
    --ok: 150 58% 52%; --warn: 40 92% 60%; --err: 2 78% 63%; --busy: var(--accent);

    /* type */
    --font-display: 'Comfortaa', ui-rounded, system-ui, sans-serif;
    --font-body:    'Atkinson Hyperlegible', system-ui, sans-serif;
    --tnum: "tnum" 1;

    /* grid + shape.
       R267 §L1 DENSITY — 44px is a CEILING, never a target. The keyholder, verbatim: "no larger than 44px,
       not 44px — buttons should and could be as less than 44px as possible for functionality and aesthetics".
       The envelope is therefore 24px <= interactive target <= 44px, biased SMALL, where 24px is WCAG 2.2
       SC 2.5.8 Target Size (Minimum) at AA (44x44 is AAA, SC 2.5.5) — not zero.
       `--thumb: 40px` inside a `--row-h: 34px` row was the quiet contradiction that made the declared row
       height fiction: 40 + 4 + 4 = 48, so EVERY vault row measured 48px on a mouse and 34 never happened.
       26px is the largest thumb a 34px row can actually hold, so the declared number becomes the real one
       and ~14px per row comes back — which is the whole ask: fit more. */
    --u: 8px; --row-h: 34px; --thumb: 26px; --radius: 12px;
    --tap-floor: 24px;        /* WCAG 2.2 SC 2.5.8 (AA). Nothing interactive in this sheet goes under it. */

    /* FLUID TYPE — one modular scale (~1.11 step), clamped, so type shrinks WITH the controls instead of
       forcing them back open ("fonts should adjust accordingly"). No breakpoint pile: five tokens, every
       size a `clamp()`. `vw` and not `cqi` on purpose — these are declared ON the container element itself
       (`.vlt` sets `container: vlt / inline-size`), and an element cannot resolve container units against
       its own container, so `cqi` here would silently fall back to viewport units anyway. */
    --fs-2xs: clamp(9.5px,  9.1px + 0.12vw, 10.5px);
    --fs-xs:  clamp(10.5px, 10.1px + 0.15vw, 11.5px);
    --fs-sm:  clamp(11.5px, 11.1px + 0.15vw, 12.5px);
    --fs-md:  clamp(12.5px, 12.1px + 0.18vw, 13.5px);
    --fs-lg:  clamp(13px,   12.5px + 0.20vw, 14px);
    --col-w: 100%;            /* single-column base; container queries widen it */
    --insp-w: 308px;

    /* motion */
    --ease-swift: cubic-bezier(.2, .8, .2, 1);
    --ease-out: cubic-bezier(.16, 1, .3, 1);

    color-scheme: dark;
    container: vlt / inline-size;
  }

  /* LIGHT — attribute override wins over prefers-color-scheme; both provided */
  :root[data-scheme="light"] .vlt {
    --sidebar:  var(--hue) 28% 95%;
    --app:      var(--hue) 30% 98.5%;
    --box:      0 0% 100%;
    --overlay:  0 0% 100%;
    --hover:    var(--hue) 26% 93%;
    --selected: var(--accent-h) 62% 92%;
    --line:     var(--hue) 20% 87%;
    --frame:    var(--hue) 18% 80%;
    --ink:      var(--hue) 24% 13%;
    --ink-dull: var(--hue) 14% 36%;
    --ink-faint:var(--hue) 12% 50%;
    --accent-l: 58%;
    color-scheme: light;
  }
  @media (prefers-color-scheme: light) {
    :root:not([data-scheme]) .vlt {
      --sidebar: var(--hue) 28% 95%; --app: var(--hue) 30% 98.5%; --box: 0 0% 100%;
      --overlay: 0 0% 100%; --hover: var(--hue) 26% 93%; --selected: var(--accent-h) 62% 92%;
      --line: var(--hue) 20% 87%; --frame: var(--hue) 18% 80%;
      --ink: var(--hue) 24% 13%; --ink-dull: var(--hue) 14% 36%; --ink-faint: var(--hue) 12% 50%;
      --accent-l: 58%; color-scheme: light;
    }
  }
}

/* ============================== LAYOUT ============================== */
@layer layout {
  /* full-viewport host used by the test harness */
  .vlt-host { position: fixed; inset: 0; }

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

  .vlt {
    display: flex; flex-direction: column;
    block-size: 100%; min-block-size: 0; inline-size: 100%;
    overflow: hidden;
    background:
      radial-gradient(120% 80% at 100% 0%, hsl(var(--accent-h) 40% 18% / .18), transparent 60%),
      hsl(var(--app));
    color: hsl(var(--ink));
    font-family: var(--font-body);
    font-size: var(--fs-lg); line-height: 1.4;
    -webkit-font-smoothing: antialiased;
  }

  /* --- top bar --- */
  .vlt-top {
    flex: 0 0 auto;
    display: flex; align-items: center; gap: var(--u);
    padding: calc(var(--u) * .75) var(--u);
    min-block-size: 48px;
    border-block-end: 1px solid hsl(var(--line));
    background: hsl(var(--overlay) / .74);
    -webkit-backdrop-filter: blur(20px) saturate(1.35);
    backdrop-filter: blur(20px) saturate(1.35);
  }
  .vlt-top__nav { display: flex; align-items: center; gap: 6px; min-inline-size: 0; flex: 1 1 auto; }
  .vlt-top__tools { display: flex; align-items: center; gap: 6px; flex: 0 0 auto; }

  /* --- body: explorer + inspector --- */
  .vlt-body {
    flex: 1 1 auto; min-block-size: 0;
    display: grid; grid-template-columns: minmax(0, 1fr);
    position: relative;                     /* containing block for the details-rail resize handle */
  }
/* R71 (§66 C6): the explorer is now its OWN inline-size container. Every rule that sizes a pane's INSIDES
   (Miller column width, the preview column's 30cqi, the list's min track) was keyed to `@container vlt` —
   the ROOT — so a 280px pane inside a 1400px Vault still rendered 226px columns, a 360px preview column and
   a 640px list, and the user saw the "name-less left sliver" the CSS comments claim was already fixed. It
   was fixed for the root width only, never for a pane. `30cqi` re-resolves against the pane for free once
   this declaration exists. */
  /* R71 (§66 C6) — REVERTED, deliberately, and recorded rather than quietly dropped. Making the explorer an
     inline-size CONTAINER is the right fix for "a 280px pane still renders 226px columns", but
     `container-type` also applies `contain: layout style`, and that changed hit-testing for the pane's
     descendants: vault-dualpane's cross-pane drag could no longer click a row in pane A. It breaks a
     shipped contract, so it does not ship on a hunch. The full diagnosis is in solution.md §66.6. */
  .vlt-explorer { min-inline-size: 0; min-block-size: 0; position: relative; }
  .vlt-columns { display: flex; block-size: 100%; overflow-x: auto; overflow-y: hidden; scroll-behavior: smooth; scroll-snap-type: x proximity; }
  /* R289 follow-up 2: `--col-w-self` is written ONLY on a column the user has actually dragged
     (vault-resize.js). Untouched columns keep inheriting the responsive container-query `--col-w`, so the
     layout still adapts by itself and a drag is a deliberate pin, not a permanent opt-out. */
  .vlt-col {
    flex: 0 0 var(--col-w-self, var(--col-w)); max-inline-size: 100%; min-inline-size: 0; scroll-snap-align: start;
    block-size: 100%; display: flex; flex-direction: column;
    border-inline-end: 1px solid hsl(var(--line));
    position: relative;                      /* containing block for the resize handle */
  }

  /* ---- the draggable pane borders (R289 follow-up 2) --------------------------------------------------
     Before this, `.vdk-resizer` was the only resizable border in the product; every column border and the
     details rail were fixed. The handle straddles the border so the grab feels like the border itself. */
  .vlt-rsz {
    position: absolute; inset-block: 0; inline-size: 7px; z-index: 4;
    cursor: col-resize; touch-action: none; background: transparent;
  }
  .vlt-rsz--end { inset-inline-end: -4px; }
  .vlt-rsz--start { inset-inline-start: -4px; }
  .vlt-rsz::before {
    content: ""; position: absolute; inset-block: 0; inset-inline-start: 3px; inline-size: 2px;
    background: transparent; transition: background .12s ease;
  }
  .vlt-rsz:hover::before, .vlt-rsz:focus-visible::before { background: hsl(var(--accent)); }
  .vlt-rsz:focus-visible { outline: 2px solid hsl(var(--accent)); outline-offset: -1px; border-radius: 3px; }
  /* ASL Law 5 — a finger needs a real target; widen the slab without moving the visual border */
  @media (pointer: coarse) { .vlt-rsz { inline-size: 16px; } .vlt-rsz--end { inset-inline-end: -8px; } .vlt-rsz--start { inset-inline-start: -8px; } }
  @media (prefers-reduced-motion: reduce) { .vlt-rsz::before { transition: none; } }

  /* R289 f/u 2 — the details-rail handle sits on the GRID BOUNDARY inside .vlt-body. Mounting it inside
     .vlt-inspector looked right and did not work: that aside is `overflow-y: auto`, so an absolutely
     positioned child at inset-inline-start:-4px is clipped out of existence and scrolls with the content.
     Measured before the move: the element existed, toHaveCount passed, and hover() timed out. */
  /* NB: this element also carries `--start` for its drag MATH, whose `inset-inline-start: -4px` would
     otherwise win the placement and park the handle at the body's left edge, 532px from the border it
     is supposed to be (measured: handle x 426 vs inspector x 958). Clear it explicitly. */
  .vlt-rsz--insp { inset-inline-start: auto; inset-inline-end: var(--insp-w, 288px); margin-inline-end: -3px; }
  .vlt[data-inspector="off"] .vlt-rsz--insp { display: none; }

  .vlt-col__list { flex: 1 1 auto; overflow-y: auto; overflow-x: hidden; padding: 6px; }
  .vlt-list { display: none; block-size: 100%; overflow: auto; }

  .vlt-inspector { display: none; min-block-size: 0; }

  /* base = mobile: single visible column, no inspector rail (folds into the preview column) */
  .vlt-columns .vlt-col:not(:last-child) { display: none; }

  /* ≥600px — two columns + a compact inspector rail.
     NB: a container can't respond to its OWN @container query, so --col-w / --insp-w are set on
     DESCENDANTS (.vlt-explorer / .vlt-body) and inherit down to .vlt-col / the grid. */
  /* R71 (§66 C6): the INSPECTOR and the body grid are root-level chrome and stay keyed to `vlt`. Column
     width, the preview column and which columns are shown are PANE-internal and are keyed to `vltpane`. */
  @container vlt (min-width: 600px) {
    .vlt-body { --insp-w: 276px; grid-template-columns: minmax(0, 1fr) var(--insp-w); }
    .vlt-inspector { display: flex; flex-direction: column; overflow-y: auto;
      border-inline-start: 1px solid hsl(var(--line)); position: relative; }
  }
  @container vlt (min-width: 600px) {
    .vlt-explorer { --col-w: 230px; }
    .vlt-col--preview { flex: 0 0 var(--col-w-self, clamp(240px, 30cqi, 360px)); }
    .vlt-columns .vlt-col:not(:last-child) { display: flex; }
  }
  /* ≥900px — 3+ columns + full inspector. Widths trimmed so a root›folder›preview drill + the inspector fit the
     library card without horizontal overflow (which used to leave the first column a name-less left sliver). */
  @container vlt (min-width: 900px) { .vlt-explorer { --col-w: 226px; } }
  @container vlt (min-width: 900px) { .vlt-body { --insp-w: 288px; } }
  /* inspector hidden by the toggle */
  .vlt[data-inspector="off"] .vlt-body { grid-template-columns: minmax(0, 1fr); }
  .vlt[data-inspector="off"] .vlt-inspector { display: none; }

  /* view switch: columns vs list — keyed off EACH PANE's own data-view (#V3 dual-pane: the two explorers can
     show different views; the root's data-view stays as a mirror of the ACTIVE pane for outside consumers). */
  .vlt-explorer[data-view="list"] .vlt-columns { display: none; }
  .vlt-explorer[data-view="list"] .vlt-list { display: block; }
  .vlt-explorer[data-view="columns"] .vlt-list { display: none; }
}

/* ============================== COMPONENTS ============================== */
@layer components {
  /* R267 §L1 — THIS RESET WAS EATING EVERY CONTROL'S DECLARED TYPE SIZE, and that is half of why the
     toolbar buttons were over the ceiling. `.vlt button` is (0,1,1); every control rule in this sheet is a
     single class, (0,1,0). Same layer, higher specificity → `font: inherit` won, and `.vlt-tool { font-size:
     12px }` / `.vlt-vbtn` 12px / `.vlt-menu__item` 13px all COMPUTED 14px (measured via getComputedStyle,
     not inferred). Combined with the `overflow-wrap: anywhere` this subtree inherits from the shell, the
     word "Details" then broke MID-WORD onto three lines and `button.vlt-tool--dual` measured 88.4px tall at
     320px — a wrap defect wearing a font bug's clothes.
     `:where()` contributes ZERO specificity, which is what a reset should contribute: it still beats the UA
     stylesheet (author origin), and it can no longer outrank the design. */
  :where(.vlt button) { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }

  /* breadcrumb */
  .vlt-crumbs { display: flex; align-items: center; gap: 2px; min-inline-size: 0; overflow: hidden;
    font-family: var(--font-display); }
  .vlt-crumb {
    display: inline-flex; align-items: center; max-inline-size: 40ch; flex: 0 0 auto;   /* path crumbs keep their full name */
    padding: 4px 8px; border-radius: 8px; color: hsl(var(--ink-dull));
    font-size: 13px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    transition: background .14s, color .14s;
  }
  .vlt-crumb:hover { background: hsl(var(--hover)); color: hsl(var(--ink)); }
  .vlt-crumb:last-child { color: hsl(var(--ink)); flex: 0 1 auto; min-inline-size: 0; }   /* only the current leaf ellipsizes when space is tight */
  .vlt-crumb__sep { color: hsl(var(--ink-faint)); font-size: 12px; padding-inline: 1px; user-select: none; }
  .vlt-back {
    flex: 0 0 auto; inline-size: 32px; block-size: 32px; border-radius: 8px;
    display: none; place-items: center; font-size: 20px; color: hsl(var(--ink-dull));
    border: 1px solid hsl(var(--line));
  }
  .vlt-back:hover { background: hsl(var(--hover)); color: hsl(var(--ink)); }
  /* back button is a mobile affordance only: shown when narrow AND not at the root (no [hidden]) */
  @container vlt (max-width: 599.98px) { .vlt-back:not([hidden]) { display: grid; } }

  /* view switch (segmented) */
  .vlt-viewswitch { display: inline-flex; padding: 3px; gap: 2px; border-radius: 10px;
    background: hsl(var(--box)); border: 1px solid hsl(var(--line)); }
  .vlt-vbtn {
    display: inline-flex; align-items: center; gap: 5px; padding: 4px 10px; border-radius: 7px;
    font-family: var(--font-display); font-size: var(--fs-xs); font-weight: 600; color: hsl(var(--ink-dull));
    transition: background .14s, color .14s;
    /* the shell sets `overflow-wrap: anywhere` on this subtree for the long-word overflow gate; a control
       label is not prose and must never take it up. One line, always. */
    white-space: nowrap;
  }
  .vlt-vbtn:hover { color: hsl(var(--ink)); }
  .vlt-ic { display: inline-grid; place-items: center; inline-size: 14px; block-size: 14px; }

  .vlt-tool {
    display: inline-flex; align-items: center; justify-content: center; gap: 6px;
    padding: 5px 10px; border-radius: 8px;
    font-family: var(--font-display); font-size: var(--fs-xs); font-weight: 600; color: hsl(var(--ink-dull));
    border: 1px solid transparent;
    white-space: nowrap;              /* see .vlt-vbtn — measured 88.4px tall at 320px without this */
  }
  .vlt-tool:hover { background: hsl(var(--hover)); color: hsl(var(--ink)); }

  /* column header */
  .vlt-col__head {
    flex: 0 0 auto; display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
    padding: 9px 12px 7px; border-block-end: 1px solid hsl(var(--line));
    font-family: var(--font-display); font-size: 10.5px; font-weight: 700; letter-spacing: .12em;
    text-transform: uppercase; color: hsl(var(--ink-faint));
  }
  .vlt-col__head b { color: hsl(var(--ink-dull)); font-weight: 700; }
  .vlt-col__count { font-variant-numeric: tabular-nums; }

  /* rows */
  .vlt-row {
    position: relative; display: grid; align-items: center; gap: 8px;
    /* R267 V2b: track 1 is the CHECKBOX COLUMN (`.vlt-cbx` on a file, the inert `.vlt-cbx__gap` on a folder,
       so the name never steps left). `auto` rather than a fixed width because the control grows to 44px
       under a coarse pointer and the track must follow it. */
    grid-template-columns: auto var(--thumb) minmax(0, 1fr) auto auto;
    min-block-size: var(--row-h); padding: 4px 8px 4px 6px; margin-block-end: 1px;
    border-radius: 9px; color: hsl(var(--ink)); scroll-margin: 8px;
    content-visibility: auto; contain-intrinsic-size: auto var(--row-h);
  }
  .vlt-row__thumb {
    inline-size: var(--thumb); block-size: var(--thumb); border-radius: 8px;
    display: grid; place-items: center; font-size: 17px; user-select: none;
    background: linear-gradient(150deg, hsl(var(--hue) 14% 24%), hsl(var(--hue) 16% 17%));
    border: 1px solid hsl(var(--line)); color: hsl(var(--ink-dull));
  }
  .vlt-row[data-kind="folder"] .vlt-row__thumb { color: hsl(var(--accent-h) 70% 74%);
    background: linear-gradient(150deg, hsl(var(--accent-h) 30% 26%), hsl(var(--accent-h) 24% 17%)); }
  .vlt-row__name { min-inline-size: 0; font-size: var(--fs-md); white-space: nowrap; overflow: hidden;
    text-overflow: ellipsis; }
  .vlt-row__meta { font-size: var(--fs-xs); color: hsl(var(--ink-faint)); font-variant-numeric: tabular-nums;
    white-space: nowrap; }
  .vlt-row__chev { color: hsl(var(--ink-faint)); font-size: 15px; inline-size: 12px; text-align: center;
    opacity: .6; }
  .vlt-row[data-kind="file"] .vlt-row__chev { visibility: hidden; }

  /* presence dots */
  .vlt-pres { display: inline-flex; gap: 3px; align-items: center; }
  .vlt-dot { inline-size: 7px; block-size: 7px; border-radius: 50%; background: hsl(var(--ink-faint) / .35);
    border: 1px solid hsl(var(--line)); }
  .vlt-dot[data-on="1"][data-loc="browser"] { background: hsl(var(--accent)); border-color: transparent; }
  .vlt-dot[data-on="1"][data-loc="disk"]    { background: hsl(200 70% 58%); border-color: transparent; }
  .vlt-dot[data-on="1"][data-loc="server"]  { background: hsl(var(--ok)); border-color: transparent; }

  /* tag chips */
  .vlt-chip {
    display: inline-flex; align-items: center; gap: 5px; padding: 2px 9px; border-radius: 999px;
    font-size: 11.5px; font-weight: 600; color: hsl(var(--tag-h, 268) 42% 82%);
    background: hsl(var(--tag-h, 268) 44% 30% / .5); border: 1px solid hsl(var(--tag-h, 268) 46% 50% / .5);
  }
  .vlt-chip::before { content: ""; inline-size: 7px; block-size: 7px; border-radius: 50%;
    background: hsl(var(--tag-h, 268) 62% 60%); }
  :root[data-scheme="light"] .vlt-chip { color: hsl(var(--tag-h, 268) 55% 32%);
    background: hsl(var(--tag-h, 268) 70% 92%); border-color: hsl(var(--tag-h, 268) 55% 70%); }
  /* E3: a clickable auto-tag chip (→ Ask lens) reads as interactive */
  /* a clickable chip is a real target: 20px of painted pill was under the WCAG 2.2 SC 2.5.8 (AA) 24px floor.
     A min-block-size rather than more padding, so the pill keeps its light proportions. */
  button.vlt-chip { cursor: pointer; font-family: inherit; font-size: 11.5px; min-block-size: 24px; }
  .vlt-chip--tag:hover { filter: brightness(1.12); box-shadow: 0 0 0 2px hsl(var(--tag-h, 268) 50% 55% / .4); }
  .vlt-chip--tag:focus-visible { outline: 2px solid hsl(var(--accent)); outline-offset: 2px; }

  /* ---- preview column (the Finder killer move: transcript-forward leaf preview) ---- */
  .vlt-col--preview { background: hsl(var(--box) / .5); }
  .vlt-preview { display: flex; flex-direction: column; gap: 12px; padding: 16px 14px; overflow-y: auto;
    block-size: 100%; }
  .vlt-preview__hero {
    aspect-ratio: 16 / 10; border-radius: var(--radius); overflow: hidden; position: relative;
    display: grid; place-items: center; background: linear-gradient(160deg, hsl(var(--hue) 16% 20%),
    hsl(var(--hue) 18% 12%)); border: 1px solid hsl(var(--line));
  }
  .vlt-preview__glyph { font-size: 44px; opacity: .9; }
  .vlt-preview__hero img { inline-size: 100%; block-size: 100%; object-fit: cover; }

  /* CSS waveform — the transcript-native signature (deterministic bars set via setProperty) */
  .vlt-wave { position: absolute; inset-inline: 10px; inset-block-end: 10px; block-size: 34px;
    display: flex; align-items: flex-end; gap: 2px; pointer-events: none; }
  .vlt-wave .vlt-bar { flex: 1 1 0; block-size: calc(6px + (var(--h, .4) * 28px)); border-radius: 2px;
    background: hsl(0 0% 100% / .5); }
  .vlt-wave .vlt-bar[data-play="1"] { background: hsl(var(--accent)); }

  .vlt-preview__name { font-family: var(--font-display); font-weight: 700; font-size: 16px;
    line-height: 1.25; word-break: break-word; }
  .vlt-preview__meta { font-size: 12px; color: hsl(var(--ink-dull)); font-variant-numeric: tabular-nums;
    margin-block-start: -6px; }
  .vlt-preview__snip {
    font-size: 13px; line-height: 1.55; color: hsl(var(--ink-dull));
    padding: 11px 12px; border-radius: 10px; background: hsl(var(--app) / .6);
    border: 1px solid hsl(var(--line)); border-inline-start: 2px solid hsl(var(--accent) / .7);
    max-block-size: 40vh; overflow-y: auto;
  }
  .vlt-preview__snip.is-empty { color: hsl(var(--ink-faint)); font-style: italic; border-inline-start-color: hsl(var(--line)); }
  /* D3: persisted link description (smaller than the title, scrolls past a few lines) + the source address */
  .vlt-preview__desc { margin: -2px 0 0; font-size: 12.5px; line-height: 1.5; color: hsl(var(--ink-dull));
    max-block-size: 5.4em; overflow-y: auto; overscroll-behavior: contain; white-space: pre-wrap; overflow-wrap: anywhere; }
  .vlt-preview__sumlbl { margin-block-start: 4px; font-size: 10.5px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: hsl(var(--ink-dull)); }
  /* #neural-summary: an OPTIONAL on-device AI upgrade — cutting-edge, mobile-first, formatted, scrollable. */
  .vlt-nsum { margin-block-start: 8px; }
  .vlt-nsum__btn { display: inline-flex; align-items: center; gap: 7px; min-block-size: 40px; padding: 8px 15px;
    border-radius: 999px; border: 1px solid color-mix(in oklch, hsl(var(--accent)), transparent 55%); cursor: pointer;
    background: color-mix(in oklch, hsl(var(--accent)), transparent 88%); color: hsl(var(--accent));
    font: 700 12.5px/1 var(--font, system-ui); transition: background .15s, transform .08s; }
  .vlt-nsum__btn:hover { background: color-mix(in oklch, hsl(var(--accent)), transparent 80%); }
  .vlt-nsum__btn:active { transform: scale(.97); }
  .vlt-nsum__ico { font-size: 14px; }
  .vlt-nsum__out { margin-block-start: 8px; font-size: 12.5px; line-height: 1.5; color: hsl(var(--ink));
    max-block-size: 13em; overflow-y: auto; overscroll-behavior: contain;
    border: 1px solid hsl(var(--edge)); border-radius: 14px; padding: 11px 13px;
    background: color-mix(in oklch, hsl(var(--accent)), transparent 95%); }
  .vlt-nsum__out[hidden] { display: none; }
  .vlt-nsum__out.is-loading, .vlt-nsum__out.is-note { display: flex; align-items: center; gap: 9px; color: hsl(var(--ink-dull)); }
  .vlt-nsum__spin { flex: none; inline-size: 15px; block-size: 15px; border-radius: 50%;
    border: 2px solid color-mix(in oklch, hsl(var(--accent)), transparent 62%); border-top-color: hsl(var(--accent));
    animation: vlt-nsum-spin .8s linear infinite; }
  @keyframes vlt-nsum-spin { to { transform: rotate(360deg); } }
  @media (prefers-reduced-motion: reduce) { .vlt-nsum__spin { animation: none; } .vlt-nsum__btn:active { transform: none; } }
  .vlt-nsum__tag { font-size: 10px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
    color: hsl(var(--accent)); margin-block-end: 6px; }
  .vlt-nsum__list { margin: 0; padding-inline-start: 17px; display: grid; gap: 6px; }
  .vlt-nsum__list li { padding-inline-start: 2px; overflow-wrap: anywhere; }
  /* #summary tabs: Summary | 🧠 Neural — pill segmented control, mobile-first */
  .vlt-sum { margin-block-start: 6px; }
  .vlt-sum__tabs { display: inline-flex; gap: 2px; padding: 2px; border-radius: 999px; margin-block-end: 7px;
    background: color-mix(in oklch, hsl(var(--ink)), transparent 92%); }
  .vlt-sum__tab { border: 0; background: transparent; cursor: pointer; padding: 6px 13px; min-block-size: 34px;
    border-radius: 999px; font: 700 12px/1 var(--font, system-ui); color: hsl(var(--ink-dull)); transition: background .15s, color .15s; }
  .vlt-sum__tab.is-on { background: hsl(var(--accent)); color: #fff; }
  .vlt-sum__pane[hidden] { display: none; }
  .vlt-nsum__head { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-block-end: 6px; }
  .vlt-nsum__refresh { border: 1px solid hsl(var(--edge)); background: transparent; color: hsl(var(--accent)); cursor: pointer;
    padding: 5px 10px; min-block-size: 32px; border-radius: 999px; font: 700 11px/1 var(--font, system-ui); }
  .vlt-nsum__refresh:hover { background: color-mix(in oklch, hsl(var(--accent)), transparent 88%); }
  .vlt-preview__link { display: block; margin-block-start: 2px; font-size: 12px; color: hsl(var(--accent));
    max-inline-size: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .vlt-preview__link:hover { text-decoration: underline; }
  .vlt-preview__acts { display: flex; flex-wrap: wrap; gap: 6px; }

  .vlt-act {
    display: inline-flex; align-items: center; gap: 6px; padding: 7px 12px; border-radius: 9px;
    font-size: 12.5px; font-weight: 600; color: hsl(var(--ink)); background: hsl(var(--box));
    border: 1px solid hsl(var(--frame)); transition: background .14s, border-color .14s, transform .06s;
  }
  .vlt-act:hover { background: hsl(var(--hover)); }
  .vlt-act:active { transform: translateY(1px); }
  .vlt-act--primary { background: hsl(var(--accent)); border-color: transparent; color: hsl(var(--hue) 30% 8%); }
  .vlt-act--primary:hover { background: hsl(var(--accent-h) var(--accent-s) calc(var(--accent-l) + 5%)); }
  .vlt-act--danger:hover { color: hsl(var(--err)); border-color: hsl(var(--err) / .6); }

  /* ---- inspector rail (Details / manage) ---- */
  .vlt-inspector { background: hsl(var(--overlay) / .6);
    -webkit-backdrop-filter: blur(20px) saturate(1.2); backdrop-filter: blur(20px) saturate(1.2); }
  .vlt-insp__head { flex: 0 0 auto; display: flex; align-items: center; justify-content: space-between;
    padding: 11px 14px; border-block-end: 1px solid hsl(var(--line));
    font-family: var(--font-display); font-size: 11px; font-weight: 700; letter-spacing: .12em;
    text-transform: uppercase; color: hsl(var(--ink-faint)); }
  .vlt-insp__body { flex: 1 1 auto; overflow-y: auto; padding: 14px; display: flex; flex-direction: column; gap: 14px; }
  .vlt-insp__ident { display: flex; align-items: center; gap: 11px; }
  .vlt-insp__thumb { inline-size: 46px; block-size: 46px; border-radius: 10px; flex: 0 0 auto;
    display: grid; place-items: center; font-size: 21px; border: 1px solid hsl(var(--line));
    background: linear-gradient(150deg, hsl(var(--hue) 14% 24%), hsl(var(--hue) 16% 16%)); }
  .vlt-insp__name { font-family: var(--font-display); font-weight: 700; font-size: 14px; line-height: 1.25;
    word-break: break-word; }
  .vlt-insp__kind { font-size: 11.5px; color: hsl(var(--ink-faint)); text-transform: capitalize; }

  .vlt-meta { display: grid; grid-template-columns: auto 1fr; gap: 6px 14px; margin: 0; }
  .vlt-meta dt { font-size: 11.5px; color: hsl(var(--ink-faint)); }
  .vlt-meta dd { margin: 0; font-size: 12.5px; color: hsl(var(--ink)); text-align: end;
    font-variant-numeric: tabular-nums; word-break: break-word; }

  .vlt-insp__label { font-family: var(--font-display); font-size: 10px; font-weight: 700; letter-spacing: .12em;
    text-transform: uppercase; color: hsl(var(--ink-faint)); }
  .vlt-presrow { display: flex; align-items: center; gap: 12px; }
  .vlt-presrow .vlt-presitem { display: inline-flex; align-items: center; gap: 6px; font-size: 12px;
    color: hsl(var(--ink-dull)); }
  .vlt-presrow .vlt-dot { inline-size: 9px; block-size: 9px; }
  .vlt-chips { display: flex; flex-wrap: wrap; gap: 6px; }
  .vlt-insp__acts { display: flex; flex-wrap: wrap; gap: 6px; margin-block-start: 2px; }

  .vlt-insp__empty, .vlt-col__empty {
    margin: auto; padding: 28px 20px; text-align: center; color: hsl(var(--ink-faint));
    display: flex; flex-direction: column; align-items: center; gap: 8px;
  }
  .vlt-insp__empty .vlt-emoji, .vlt-col__empty .vlt-emoji { font-size: 26px; opacity: .7; }
  .vlt-col__empty { font-size: 12.5px; }

  /* ---- list view (subgrid-aligned, dense) ---- */
  /* WAVE 2 F2: the 640px floor WAS the horizontal scrollbar. It out-sized the fluid template below and
     forced .vlt-list to overflow before the tracks ever got a chance to shrink. The template now carries its
     own (smaller) floor in its minmax() mins, so this one has nothing left to say. */
  .vlt-list__inner { min-inline-size: 0; }
  .vlt-list__head, .vlt-list__row {
    display: grid;
    /* P1 R289/§282 J1: [size] replaced [lang] ("999.9 MB" / "12 items" in tabular figures).
       ---------------------------------------------------------------------------------------------------
       R289/§282 WAVE 2, FINDING 2 — THE TEMPLATE IS FLUID BECAUSE THE LIST IS NOT AS WIDE AS IT LOOKS.
       MEASURED, not reasoned. At 1440x900 the list is 528px, and it is 528px at 1194x834 and at 2560x1440
       too — `.lib-card` caps at max-inline-size 1080px, so every desktop viewport lands on the SAME chain:
         card 1080 → .lib-vault/.vdk-wrap 1030 → (−224 dock rail) .vlt 804 → (−276 inspector) .vlt-list 528.
       The old template was FIXED: 200 + 84 + 90 + 90 + 130 + 60 + 5x12 gap + 32 padding = 746 asked of a
       528px box — scrollWidth 730 vs clientWidth 528, a 202px overflow, with Date's right edge at 1088 and
       Where's at 1160 against a list right edge of 958. Both columns were simply GONE off-screen; the user
       saw four columns and no scrollbar hint. Widening the viewport could not fix it (2560 measures the
       same 528) and collapsing the rail only papered over the default state.
       So every track is now FLEXIBLE with a px floor:
         • the min is the column's measured worst-case CONTENT width (row content, not the header — the
           header ellipsises through .vlt-lc__hd, a row of tabular figures cannot). Measured max-content at
           1440x900: dur 46, size 49, words 43, date 79, pres 27; floors carry headroom over each.
         • the max is an fr, so the SAME template also breathes when the list is wide (inspector off, rail
           collapsed, a future wider card) instead of pinning six columns at desktop sizes forever.
         • no `auto`/`min-content` track anywhere: .vlt-list__head and every .vlt-list__row are SEPARATE
           grids, so a content-dependent track would resolve differently per row and the columns would stop
           lining up (and `content-visibility: auto` rows, whose content is skipped, would resolve
           differently again). px floors + fr keep all of them content-independent and identical.
       Track COUNT is unchanged (6) and no grid child was added or removed, so the positional narrow-hide
       band further down this file (`:nth-child(2)` / `:nth-child(n+4)`, the <=600px @container rule) still
       points at the same cells — re-audited by measurement at 390x844 and 320x568, both engines. */
    /* ---------------------------------------------------------------------------------------------
       R289/§282 WAVE 3 — THE NAME COLUMN. Wave 2's floors were right about the OVERFLOW and wrong about
       the BUDGET: with every meta track clamped to its floor, Name is not a track at all, it is the
       REMAINDER — `name = available - sum(other floors)`. So wave 2 pinned it to 144px, and after the
       R267 checkbox (26) + the thumb (26) + 2x10px inner gaps that is 72px of filename. MEASURED at
       1194/1440/1920/2560, chromium AND webkit: namesClipped 4/4, "zeta clip.mp3" (13 chars) truncated,
       "DURATION" ellipsized in its own header (hd clientWidth 50 vs scrollWidth 67), and the 9-digit
       Words value clipped from its LEFT (right-aligned + overflow:hidden shows ",234,567" - a WRONG
       NUMBER, not a shortened one). Three levers, in order of how much they returned:
         gap 12 -> 8 ................ +20px (5 gutters). Right-aligned tabular columns read fine at 8.
         padding 16 -> 12 ........... +8px  (matches the <=600px band, which already used 12).
         header type 10.5px/.1em -> 10px/.04em ... let [dur] fall from the 71px "DURATION" needs at the
           old tracking to 64, and [pres] from 50 to 48. MEASURED off-screen in-page, both engines:
           "DURATION" 63.0 -> 56.0, "WHERE" 44.3 -> 37.4. Sizing a track to an uppercase TRACKED label
           instead of to its value is what ellipsized Duration; the cheaper fix is the tracking.
       Floors are now max(measured content, measured label) with the label measured at the NEW type:
         dur 64 (label 60 > content 46) · size 50 (content 49) · words 62 (content 62: "1,234,567" needs
         62, wave 2's 54 clipped it) · date 80 (content 79) · pres 48 (label 44 > content 27 - the three
         presence dots; the column is NOT empty, it just has no textContent).
       Sum 304 of 464 available (528 - 24 padding - 40 gaps) -> Name 160px -> 92px of filename, up from
       72. Every fr is unchanged, so the template still breathes when the list is wide. */
    grid-template-columns:
      [name]  minmax(92px, 3.2fr)
      [dur]   minmax(64px, 1.05fr)
      [size]  minmax(50px, .62fr)
      [words] minmax(62px, .70fr)
      [date]  minmax(80px, .95fr)
      [pres]  minmax(48px, .72fr);
    align-items: center; gap: 8px; padding: 0 12px;
  }
  .vlt-list__head {
    position: sticky; inset-block-start: 0; z-index: 1; block-size: 34px;
    /* WAVE 3: 10.5px/.1em -> 10px/.04em. The tracking was pure cost: it is charged to EVERY track's
       floor and paid for out of the Name column. Measured, both engines: "DURATION" 63.0 -> 56.0px,
       "WHERE" 44.3 -> 37.4px, i.e. ~11px of list width returned to the filename for a change no one
       reads as smaller type. Caps + display face are unchanged, so the header still reads as a header. */
    font-family: var(--font-display); font-size: 10px; font-weight: 700; letter-spacing: .04em;
    text-transform: uppercase; color: hsl(var(--ink-faint));
    background: hsl(var(--overlay) / .92); border-block-end: 1px solid hsl(var(--line));
    -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px);
  }
  .vlt-list__head span:not(:first-child), .vlt-list__row > :not(.vlt-lc--name) { text-align: end;
    justify-content: flex-end; }
  .vlt-list__row {
    min-block-size: calc(var(--row-h) + 8px); border-block-end: 1px solid hsl(var(--line) / .5);
    color: hsl(var(--ink)); content-visibility: auto; contain-intrinsic-size: auto 42px;
  }
  /* WAVE 3: inner gap 10 -> 8. Two gaps sit between the checkbox, the icon and the name, so this is
     4px of filename, and it lines the name cell up with the new 8px column gutter. */
  .vlt-lc--name { display: flex; align-items: center; gap: 6px; min-inline-size: 0; }   /* f/u 2c: 8px -> 6px, two seams before the filename */
  /* WAVE 2 F2: `flex: 0 0 auto` — MEASURED. `.vlt-lc--name` is a flex row and the thumb had no flex
     shorthand, so it was shrinkable: a long file name squeezed the 26px icon down to a 9px sliver at
     1440x900 (and to 17-20px even with the inspector off and the rail collapsed, i.e. this predates the
     fluid template — the narrower name track only made it obvious). The name cell must degrade by
     ELLIPSISING `.vlt-lc__t`, which already owns `min-inline-size: 0` + text-overflow, never by crushing
     the icon that tells you what kind of thing the row is. The checkbox beside it is already `flex: 0 0
     auto` (R267 V2b) — this makes the icon agree with it. */
  .vlt-lc--name .vlt-row__thumb { flex: 0 0 auto; inline-size: var(--thumb); block-size: var(--thumb); font-size: 13px; }
  .vlt-lc--name .vlt-lc__t { min-inline-size: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    font-size: var(--fs-md); }
  /* WAVE 2 F2: `min-inline-size: 0` is load-bearing, not tidiness. A grid item's automatic minimum size is
     its min-content size, and these cells are `white-space: nowrap` — so a long value (a 9-digit word count,
     a long-form locale date) would blow its own track wider than the fluid track allows and re-open the
     horizontal overflow the template above just closed. `overflow: hidden` clips instead, so no value can
     ever spill past the list's right edge again. Normal values fit their floors, so nothing visible changes. */
  .vlt-lc { font-size: var(--fs-sm); color: hsl(var(--ink-dull)); font-variant-numeric: tabular-nums; white-space: nowrap;
    min-inline-size: 0; overflow: hidden; }
  .vlt-lc--pres { display: flex; justify-content: flex-end; min-inline-size: 0; }
  .vlt-list__inner > .vlt-list__empty { display: flex; grid-column: 1 / -1; }
  /* P1 (R289/§282 J1): the Size cell — .vlt-lc already sets tabular-nums; restated so the contract survives
     any future .vlt-lc edit. Right-align comes from the `> :not(.vlt-lc--name)` rule above. */
  .vlt-lc--size { font-variant-numeric: tabular-nums; }
  /* P1 (R289/§282 J2): sortable headers — a real <button> inside every columnheader (APG sortable-table).
     Font/case/color inherit from the head; the head's text-align:end / justify-content:flex-end rules place
     the button. The cell stretches so the button's hit area is the FULL header height — 34px fine pointer,
     ≥var(--ytx-tap, 36px) coarse via the R267-measured head rule at the bottom of this file (the head grows
     to hold its controls; a 44px control inside the sticky head is the measured first-row tap-eater). */
  .vlt-list__head [role="columnheader"]:not(.vlt-lc--name) { display: flex; align-items: stretch; align-self: stretch; min-inline-size: 0; }
  .vlt-lc__sort {
    /* WAVE 3: gap 4 -> 3, padding 0 2px -> 0 1px. Both are charged to every header floor. */
    display: inline-flex; align-items: center; gap: 3px;
    margin: 0; border: 0; padding: 0 1px; background: none; border-radius: 6px;
    font: inherit; letter-spacing: inherit; text-transform: inherit; color: inherit;
    cursor: pointer; min-inline-size: 0; max-inline-size: 100%;
  }
  .vlt-lc__sort:hover { color: hsl(var(--ink)); }
  .vlt-lc__sort .vlt-lc__hd { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  /* the sorted column reads as “on”: full-ink label + the chevron direction indicator (the ONLY column that has one) */
  .vlt-list__head [aria-sort] .vlt-lc__sort { color: hsl(var(--ink)); }
  /* WAVE 3 - THE SORTED COLUMN MUST NOT SHRINK ITS OWN LABEL. MEASURED: with the chevron IN FLOW, sorting
     by Duration ellipsized "DURATION" by 7px and sorting by Where ellipsized "WHERE" by 5px (chromium and
     webkit identical) - the indicator ate the label it was indicating. Paying for it in the track floors
     instead costs ~12px of the Name column on every column and in every state, for a glyph that is absent
     five times out of six. So the chevron leaves the flow and sits in the 8px gutter on the header's start
     side: `inset-inline-end: 100%` on a `position: relative` button. The gutter is dead space - every meta
     column is right-aligned, so no value ever reaches its own start edge - and .75em + 1px of margin is
     7.5+1 = 8.5px against an 8px gutter, so the arrow lands in the gutter and not on the neighbour. Start-
     side overflow adds nothing to scrollWidth, so this cannot re-open FINDING 2.
     The NAME header keeps its chevron IN FLOW: that button is start-aligned and shares its cell with the
     R267 select-all checkbox, so "before the button" is on top of the checkbox. It has the room (label
     32px in a 160px track). */
  .vlt-lc__dir { flex: none; inline-size: .8em; block-size: .8em; }
  .vlt-list__head [role="columnheader"]:not(.vlt-lc--name) .vlt-lc__sort { position: relative; }
  .vlt-list__head [role="columnheader"]:not(.vlt-lc--name) .vlt-lc__dir {
    position: absolute; inset-inline-end: 100%; inset-block-start: 50%; transform: translateY(-50%);
    inline-size: .75em; block-size: .75em; margin-inline-end: 1px;
  }

  /* ---- context menu ---- */
  /* R267 — REACH. Two measured defects, both fixed here:
     (1) LAYER. `z-index: auto` painted the menu at layer 0 in tree order, so EVERYTHING outranked it —
         its own .vlt-bulk (30) and .vlt-toast (40), #chrome (100), the dock (200), #liveBar (210),
         #toast (260). vault.js now raises it into the TOP LAYER via `popover` (the estate's idiom:
         index.html:35/51, card-acts.js:68, install-video.js:97). Top layer sits OUTSIDE the stacking
         tree, so it cannot be trapped by an ancestor stacking context; `.vlt` is measurably NOT one today
         (`container-type: inline-size` -> `contain: none`), but a z-index would depend on that staying
         true. `.is-topz` is the no-popover fallback only (see vault.js raiseMenu).
     (2) BAND. --vlt-reach-* is the REACHABLE band: the viewport minus the fixed chrome bar and minus the
         dock's REAL measured height (--dock-h, published by dock.js's ResizeObserver — never a hard-coded
         104px) plus the bottom safe area. `max-block-size` makes a 17-item menu SHRINK and scroll instead
         of overflowing it (floating-ui `size.ts`); vault.js clamps the x/y into the same band. */
  .vlt-menu {
    --vlt-dock: 0px;
    --vlt-reach-top: calc(var(--chrome-h, 56px) + var(--sat, 0px) + 8px);
    --vlt-reach-bot: calc(var(--vlt-dock) + max(var(--sab, 0px), env(safe-area-inset-bottom, 0px)) + 8px);
    position: fixed; margin: 0; inset: auto; min-inline-size: 194px; padding: 6px 6px 0;
    border-radius: 12px; border: 1px solid hsl(var(--frame));
    background: hsl(var(--overlay) / .9); color: hsl(var(--ink));
    -webkit-backdrop-filter: blur(24px) saturate(1.3); backdrop-filter: blur(24px) saturate(1.3);
    box-shadow: 0 24px 60px -22px hsl(var(--hue) 40% 3% / .8), 0 0 0 .5px hsl(0 0% 100% / .04) inset;
    left: var(--vlt-menu-x, 0px); top: var(--vlt-menu-y, 0px);
    max-block-size: var(--vlt-menu-maxh, calc(100dvh - var(--vlt-reach-top) - var(--vlt-reach-bot)));
    overflow-y: auto; overscroll-behavior: contain;
  }
  body.has-dock .vlt-menu { --vlt-dock: var(--dock-h, 104px); }
  /* Layer for a browser with no Popover API: above #toast (260), below the call scene (9991) and the camera
     overlay (9999), which are full-screen takeovers where the Vault is not interactive. MEASURED that this
     actually escapes `.vlt`: `container-type: inline-size` computes `contain: none` and is NOT a stacking
     context in this Chromium, and a z-index:300 menu does win the hit test over the dock (200). It is still
     the fallback, not the mechanism — one `transform`/`filter` added to an ancestor would silently undo it,
     which the top layer cannot suffer. This is also the ONLY path on which `.is-anchored` below is used:
     popover + `position-area` lays out correctly but does not hit-test (see vault.js openMenuAnchored). */
  .vlt-menu.is-topz { z-index: 300; }
  .vlt-menu:not(.is-open) { display: none; }
  /* CSS Anchor Positioning path (progressive enhancement; JS positioner is the universal fallback) */
  .vlt-menu.is-anchored {
    left: auto; top: auto;
    position-area: block-end span-inline-end;
    position-try-fallbacks: flip-block, flip-inline;
    margin-block-start: 4px;
  }
  /* R267-R2 — DESTRUCTIVE FOOTER. R267 made the menu bounded + scrollable, which stopped it painting
     off-screen, but 16 items x 44px (coarse) = 727px of content against a 260px band still opened with
     Delete 469px BELOW the fold at 844x390 — the same "I cannot reach Delete", one layer inward. Making the
     menu taller is not available: the band IS the constraint. So the destructive action leaves the scroll
     flow and sticks to the bottom of the scrollport. `position: sticky` inside the menu's own `overflow-y`
     scroller — no JS, no measurement, and the DOM order is untouched so roving/End still land on it last.
     The menu's block-end padding moves ONTO this element so the footer can sit flush with the scrollport
     edge; without that the scrolled body shows through the 6px strip beneath it. */
  .vlt-menu__foot {
    position: sticky; inset-block-end: 0; z-index: 1;
    margin-block-start: 5px; padding-block: 5px 6px;
    border-block-start: 1px solid hsl(var(--line));
    background: hsl(var(--overlay));
  }
  .vlt-menu[data-kind="protected"] .vlt-menu__foot { display: none; }        /* nothing left in it to pin */
  .vlt-menu[data-kind="protected"] { padding-block-end: 6px; }               /* ...so restore the padding */
  .vlt-menu__item {
    display: flex; align-items: center; gap: 10px; inline-size: 100%; padding: 7px 10px; border-radius: 8px;
    font-size: var(--fs-md); color: hsl(var(--ink)); text-align: start;
  }
  .vlt-menu__item .vlt-mi__ic { inline-size: 16px; text-align: center; opacity: .8; }
  .vlt-menu__item .vlt-kbd { margin-inline-start: auto; font-size: 11px; color: hsl(var(--ink-faint));
    font-family: var(--font-body); }
  /* R267-R2 — `outline: none` used to live on this rule. See the UNLAYERED focus-ring block at the end of
     this file for why the replacement ring cannot live in a layer. */
  .vlt-menu__item:hover, .vlt-menu__item:focus-visible { background: hsl(var(--accent) / .9);
    color: hsl(var(--hue) 30% 8%); }
  .vlt-menu__item:hover .vlt-kbd, .vlt-menu__item:focus-visible .vlt-kbd { color: hsl(var(--hue) 30% 20%); }
  .vlt-menu__item--danger { color: hsl(var(--err)); }
  .vlt-menu__item--danger:hover, .vlt-menu__item--danger:focus-visible { background: hsl(var(--err) / .92);
    color: #fff; }
  .vlt-menu__sep { block-size: 1px; margin: 5px 6px; background: hsl(var(--line)); border: 0; }
  /* protected folder hides mutating items */
  .vlt-menu[data-kind="protected"] [data-cmd="ctx-rename"],
  .vlt-menu[data-kind="protected"] [data-cmd="ctx-delete"],
  .vlt-menu[data-kind="protected"] [data-cmd="ctx-move"],
  .vlt-menu[data-kind="protected"] .vlt-menu__sep--mut { display: none; }
  .vlt-menu[data-kind="folder"] [data-cmd="ctx-export"],
  .vlt-menu[data-kind="folder"] [data-cmd="ctx-notebook"],   /* R33: a folder is not a source */
  .vlt-menu[data-kind="folder"] [data-cmd="ctx-infographic"],   /* R55: a folder has no transcript */
  .vlt-menu[data-kind="folder"] [data-cmd="ctx-play"] { display: none; }

  /* R289/§282 f/u 2 — THE TRASH VERB SET. Default: the three trash rows are hidden everywhere, so no other
     menu grew a verb. Then each trash kind reveals exactly its own. Hiding by data-kind (rather than
     building a second menu) is why Trash inherits the shipped menu's placement, top-layer and reach work. */
  .vlt-menu [data-cmd="ctx-restore"],
  .vlt-menu [data-cmd="ctx-purge"],
  .vlt-menu [data-cmd="ctx-emptytrash"] { display: none; }

  .vlt-menu[data-kind="trashed"] [data-cmd="ctx-restore"],
  .vlt-menu[data-kind="trashed"] [data-cmd="ctx-purge"] { display: flex; }
  .vlt-menu[data-kind="trashroot"] [data-cmd="ctx-emptytrash"] { display: flex; }

  /* a deleted item cannot be opened, shared, renamed, moved, tagged or deleted-again */
  .vlt-menu[data-kind="trashed"] [data-cmd="ctx-open"],
  .vlt-menu[data-kind="trashed"] [data-cmd="ctx-play"],
  .vlt-menu[data-kind="trashed"] [data-cmd="ctx-notebook"],
  .vlt-menu[data-kind="trashed"] [data-cmd="ctx-infographic"],
  .vlt-menu[data-kind="trashed"] [data-cmd="ctx-share"],
  .vlt-menu[data-kind="trashed"] [data-cmd="ctx-export"],
  .vlt-menu[data-kind="trashed"] [data-cmd="ctx-dlmd"],
  .vlt-menu[data-kind="trashed"] [data-cmd="ctx-dljson"],
  .vlt-menu[data-kind="trashed"] [data-cmd="ctx-savemedia"],
  .vlt-menu[data-kind="trashed"] [data-cmd="ctx-editthumb"],
  .vlt-menu[data-kind="trashed"] [data-cmd="ctx-tag"],
  .vlt-menu[data-kind="trashed"] [data-cmd="ctx-addfav"],
  .vlt-menu[data-kind="trashed"] [data-cmd="ctx-rename"],
  .vlt-menu[data-kind="trashed"] [data-cmd="ctx-move"],
  .vlt-menu[data-kind="trashed"] .vlt-menu__foot,
  .vlt-menu[data-kind="trashroot"] [data-cmd="ctx-open"],
  .vlt-menu[data-kind="trashroot"] [data-cmd="ctx-play"],
  .vlt-menu[data-kind="trashroot"] [data-cmd="ctx-notebook"],
  .vlt-menu[data-kind="trashroot"] [data-cmd="ctx-infographic"],
  .vlt-menu[data-kind="trashroot"] [data-cmd="ctx-share"],
  .vlt-menu[data-kind="trashroot"] [data-cmd="ctx-export"],
  .vlt-menu[data-kind="trashroot"] [data-cmd="ctx-dlmd"],
  .vlt-menu[data-kind="trashroot"] [data-cmd="ctx-dljson"],
  .vlt-menu[data-kind="trashroot"] [data-cmd="ctx-savemedia"],
  .vlt-menu[data-kind="trashroot"] [data-cmd="ctx-editthumb"],
  .vlt-menu[data-kind="trashroot"] [data-cmd="ctx-tag"],
  .vlt-menu[data-kind="trashroot"] [data-cmd="ctx-addfav"],
  .vlt-menu[data-kind="trashroot"] [data-cmd="ctx-rename"],
  .vlt-menu[data-kind="trashroot"] [data-cmd="ctx-move"],
  .vlt-menu[data-kind="trashroot"] .vlt-menu__foot { display: none; }
  .vlt-menu[data-kind="trashed"], .vlt-menu[data-kind="trashroot"] { padding-block-end: 6px; }

  /* the origin cell reads as text, not as presence dots */
  .vlt-lc--origin { display: flex; align-items: center; min-inline-size: 0; }
  .vlt-lc--origin .vlt-lc__t { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; color: hsl(var(--ink-dull)); }

  /* R289/§282 f/u 2b — THE TRASH BULK BAR. Reproduced defect: with 15 rows selected in Trash the bar
     offered "Open as notebook / Move… / Save to disk / Delete", and Delete soft-deleted already-deleted
     rows — nothing happened and it toasted "Moved 15 to Trash". Inside Trash the bar now carries only the
     verbs that mean something there. Outside Trash the two trash verbs do not exist. */
  .vlt-bulk__b--trash { display: none; }
  .vlt[data-trash] .vlt-bulk__b--trash { display: inline-flex; }
  .vlt[data-trash] .vlt-bulk [data-cmd="bulk-notebook"],
  .vlt[data-trash] .vlt-bulk [data-cmd="bulk-move"],
  .vlt[data-trash] .vlt-bulk [data-cmd="bulk-disk"],
  .vlt[data-trash] .vlt-bulk [data-cmd="bulk-delete"] { display: none; }
  .vlt-bulk__b--danger { color: hsl(var(--danger, 0 70% 60%)); }

  /* ---- quick look stub ---- */
  .vlt-quicklook { border: 0; padding: 0; max-inline-size: min(680px, 92vw); inline-size: 100%;
    border-radius: 16px; background: hsl(var(--overlay)); color: hsl(var(--ink));
    box-shadow: 0 40px 100px -30px hsl(var(--hue) 50% 2% / .8); }
  .vlt-quicklook::backdrop { background: hsl(var(--hue) 40% 3% / .62); -webkit-backdrop-filter: blur(6px);
    backdrop-filter: blur(6px); }
  .vlt-ql__stage { aspect-ratio: 16 / 9; display: grid; place-items: center; gap: 10px;
    background: linear-gradient(160deg, hsl(var(--hue) 18% 18%), hsl(var(--hue) 20% 10%));
    border-radius: 16px 16px 0 0; }
  .vlt-ql__glyph { font-size: 54px; opacity: .85; }
  .vlt-ql__hint { font-size: 12px; color: hsl(var(--ink-faint)); }
  .vlt-ql__body { padding: 16px 18px 20px; display: flex; flex-direction: column; gap: 6px; }
  .vlt-ql__name { font-family: var(--font-display); font-weight: 700; font-size: 16px; }
  .vlt-ql__meta { font-size: 12.5px; color: hsl(var(--ink-dull)); font-variant-numeric: tabular-nums; }
  .vlt-ql__snip { margin-block-start: 8px; font-size: 13px; line-height: 1.55; color: hsl(var(--ink-dull));
    max-block-size: 34vh; overflow-y: auto; }
  .vlt-ql__close { position: absolute; inset-block-start: 10px; inset-inline-end: 10px; inline-size: 30px;
    block-size: 30px; border-radius: 50%; background: hsl(0 0% 0% / .4); color: #fff; display: grid;
    place-items: center; font-size: 15px; }

  /* inline rename */
  .vlt-rename { font: inherit; font-size: 13.5px; inline-size: 100%; min-inline-size: 0; padding: 2px 6px;
    border-radius: 6px; color: hsl(var(--ink)); background: hsl(var(--app));
    border: 1px solid hsl(var(--accent)); outline: none; }

  /* toast */
  .vlt-toast {
    position: fixed; inset-block-end: 20px; inset-inline: 0; margin-inline: auto; inline-size: max-content;
    max-inline-size: 82vw; padding: 9px 15px; border-radius: 999px; z-index: 40;
    background: hsl(var(--overlay) / .96); color: hsl(var(--ink)); font-size: 13px;
    border: 1px solid hsl(var(--frame)); box-shadow: 0 18px 40px -18px hsl(var(--hue) 40% 3% / .7);
    opacity: 0; transform: translateY(8px); pointer-events: none;
    transition: opacity .2s, transform .2s;
  }
  .vlt-toast.is-on { opacity: 1; transform: translateY(0); }
}

/* ============================== STATE ============================== */
@layer state {
  /* hover / selection on rows */
  .vlt-row:hover { background: hsl(var(--hover) / .7); }
  .vlt-list__row:hover { background: hsl(var(--hover) / .6); }

  .vlt-row[aria-selected="true"] {
    background: hsl(var(--selected));
    box-shadow: inset 2px 0 0 hsl(var(--accent)), inset 0 0 0 1px hsl(var(--accent) / .25);
  }
  .vlt-row[aria-selected="true"] .vlt-row__meta { color: hsl(var(--ink-dull)); }
  .vlt-list__row[aria-selected="true"] {
    background: hsl(var(--selected)); box-shadow: inset 2px 0 0 hsl(var(--accent));
  }
  /* an open folder (drilled) reads slightly lit even when focus is elsewhere */
  .vlt-row.is-open:not([aria-selected="true"]) { background: hsl(var(--hover) / .55); }

  /* protected-folder denial micro-animation */
  .vlt-deny { animation: vlt-deny .34s var(--ease-swift); }
  @keyframes vlt-deny { 25% { transform: translateX(-5px); } 50% { transform: translateX(5px); } 75% { transform: translateX(-3px); } }

  /* view switch active radio */
  .vlt-vbtn[aria-checked="true"] { background: hsl(var(--accent)); color: hsl(var(--hue) 30% 8%); }
  .vlt-vbtn[aria-checked="true"]:hover { color: hsl(var(--hue) 30% 8%); }
  .vlt-tool[aria-pressed="true"] { background: hsl(var(--accent) / .16); color: hsl(var(--ink));
    border-color: hsl(var(--accent) / .5); }

  /* the active column carries a faint accent wash + focus ring on its focused row */
  .vlt-col:has(:focus-visible) { background: hsl(var(--accent-h) 30% 20% / .06); }
  .vlt :focus-visible { outline: 2px solid hsl(var(--accent)); outline-offset: -1px; border-radius: 8px; }
  .vlt-row:focus-visible, .vlt-list__row:focus-visible { outline-offset: -2px; }

  /* empty explorer hint via :has() (zero JS state toggling) */
  .vlt-col__list:not(:has(.vlt-row)) .vlt-col__empty { display: flex; }
  .vlt-col__empty { display: none; }

  /* view-transition slide for a drilled/removed column (desktop + mobile) */
  ::view-transition-group(vlt-col) { animation-duration: .34s; }
  ::view-transition-new(vlt-col) { animation: vlt-col-in .34s var(--ease-out); }
  ::view-transition-old(vlt-col) { animation: vlt-col-out .26s var(--ease-swift); }
  @keyframes vlt-col-in  { from { opacity: 0; transform: translateX(26px); } }
  @keyframes vlt-col-out { to   { opacity: 0; transform: translateX(26px); } }

  /* reduced motion / transparency / forced colors */
  @media (prefers-reduced-motion: reduce) {
    .vlt, .vlt * { scroll-behavior: auto !important; }
    ::view-transition-group(*), ::view-transition-new(*), ::view-transition-old(*) { animation: none !important; }
    .vlt-toast { transition: none; }
  }
  @media (prefers-reduced-transparency: reduce) {
    .vlt-top, .vlt-inspector, .vlt-menu, .vlt-list__head {
      -webkit-backdrop-filter: none; backdrop-filter: none; background-color: hsl(var(--overlay));
    }
  }
  @media (forced-colors: active) {
    .vlt-row[aria-selected="true"], .vlt-list__row[aria-selected="true"] { outline: 2px solid Highlight; }
    .vlt-dot[data-on="1"] { background: Highlight; }
  }
}

/* #fix: List view responsive — on narrow containers collapse the 6-col grid to name + ONE meta column (was a fixed 640px grid that side-scrolled at 390). Unlayered so it wins over the base @layer rule.
   P1 (R289/§282 J1) re-balanced the band: the surviving column is now SIZE (child 3), not Duration — the
   builder's ruling that Size survives narrow better than Words/Duration. NOTE: the row's dur/words/date cells
   are plain `.vlt-lc` (only size carries a modifier), so hide by POSITION — a class-based hide left them
   un-hidden and they wrapped onto implicit grid rows (the 3-line-per-row bug). Children: 1=name 2=dur 3=size
   4=words 5=date 6=pres 7=⋯ → keep 1+3, hide 2 and 4+. Hidden grid children generate no boxes, so name→[name]
   and size→[size]. The ⋯ (.vlt-row__more, child 7, position:absolute) is re-shown by the LATER unlayered
   hover/coarse rules exactly as it was under the old n+3 band — same specificity (0,2,0) as before. */
@container vlt (max-width: 599.98px) {
  .vlt-list__inner { min-inline-size: 0; }
  .vlt-list__head, .vlt-list__row { grid-template-columns: [name] minmax(0, 1fr) [size] auto; gap: 10px; padding: 0 12px; }
  .vlt-list__row > :nth-child(2), .vlt-list__row > :nth-child(n+4),
  .vlt-list__head span:nth-child(2), .vlt-list__head span:nth-child(n+4) { display: none; }
}

/* R289/§282 WAVE 2, FINDING 2 — THE MIDDLE BAND. MEASURED, by a viewport sweep on the real served app.
   The narrow band above is keyed to `.vlt`, but the LIST is `.vlt` MINUS the 276px inspector rail, so the
   six-column set switched on at `.vlt` 600 into a list of only 324px. Sweep, fine pointer, dock rail in its
   default expanded state (chromium; webkit identical): vw 940 -> .vlt 611 -> list 335, overflow 121px with
   Date AND Where off-screen; vw 1000 -> list 392, overflow 64; vw 1024 -> list 416, overflow 40 with Where
   off-screen. 1024x768 is a real laptop and a real tablet-landscape, and the fluid template alone could not
   save it: six columns cannot honestly live in 335px (their measured content floors alone total 380px).
   So the column SET steps down before the columns start clipping - Name + Size + Date, the same ruling the
   narrow band already made one step further down (Size survives narrow better than Duration/Words; Date is
   the Finder default). Six columns return at `.vlt` >= 788px, where the list is >= 512px: enough for the
   380px of floors plus 5x12px gaps plus the 16px/48px trailing reservation a COARSE pointer takes for the
   always-visible per-row `...` (vault.css `@media (pointer: coarse)`). Measured desktop `.vlt` is 804px, so
   the six-column set is 16px clear of the threshold on every viewport this app can produce (the card caps
   at 1080px, so 1440, 1194 and 2560 all land on the same 804/528).
   INSPECTOR-AWARE ON PURPOSE: with the inspector toggled off the list IS the full `.vlt` width, six columns
   fit from 600px up, and stepping down would be a lie. A container query cannot see that state, so the
   selector does - `.vlt:not([data-inspector="off"])` composes with the query because the SUBJECT
   (.vlt-list__head / .vlt-list__row) is a descendant of the `vlt` container. Verified by measurement:
   inspector-off at vw 940 keeps all six columns with zero overflow.
   POSITIONAL HIDE, RE-AUDITED (the band above documents why): children are 1=name 2=dur 3=size 4=words
   5=date 6=pres 7=`...`(absolute). Keep 1+3+5, hide 2+4+6 -> name lands on [name], size on [size], date on
   [date] because hidden grid children generate no boxes. Track COUNT and child COUNT are both unchanged by
   this wave, so the <=600px band's `:nth-child(2)` / `:nth-child(n+4)` still point at the same cells; the
   two ranges are disjoint (<=599.98 vs 600-787.98) so they can never both apply. */
@container vlt (min-width: 600px) and (max-width: 787.98px) {
  .vlt:not([data-inspector="off"]) .vlt-list__head,
  .vlt:not([data-inspector="off"]) .vlt-list__row {
    /* WAVE 3: floors follow the six-column set above (size 52->50, date 82->80) so one measurement
       governs both bands. */
    grid-template-columns: [name] minmax(92px, 3.2fr) [size] minmax(50px, .62fr) [date] minmax(80px, .95fr);
  }
  .vlt:not([data-inspector="off"]) .vlt-list__row > :nth-child(2),
  .vlt:not([data-inspector="off"]) .vlt-list__row > :nth-child(4),
  .vlt:not([data-inspector="off"]) .vlt-list__row > :nth-child(6),
  .vlt:not([data-inspector="off"]) .vlt-list__head span:nth-child(2),
  .vlt:not([data-inspector="off"]) .vlt-list__head span:nth-child(4),
  .vlt:not([data-inspector="off"]) .vlt-list__head span:nth-child(6) { display: none; }
}

/* PD-1 (R289/§282, parked → CLOSED under R302/§295) — A TOUCH USER COULD NOT READ THEIR OWN FILENAMES.
   MEASURED by the lane's own gate (r289-w2-f2-sweep.spec.js C5) at coarse 1194x834, 1366x768 and
   1440x900: name room 50px against that gate's own 82px bar for a 14-character filename — so "Mid file
   2.m4a", "alpha take.mp4" and "zeta clip.mp3" were all clipped on the product's core file screen.
   THE FIX IS TO SPEND THE ONE COLUMN A TOUCH USER CANNOT USE ANYWAY. On a COARSE pointer the [pres]
   column paints three same-colour presence dots whose meaning is only decodable by HOVERING, which a
   finger cannot do (the lane recorded this as PD-10). Dropping it hands its 48px floor plus an 8px
   gutter to [name]. The presence facts are NOT lost — they remain in the inspector (.vlt-presrow).
   NO TRACK VALUE IS RETUNED: with [pres] gone the existing minmax floors resolve Name to 180px, which
   is the parked ledger's own proven arithmetic (track space 436 − floors 256 = 180).
   min-width 788px is the six-column threshold the middle band documents above, so the ranges cannot
   overlap: both narrower bands already hide child 6 positionally, and the positional-hide discipline
   the wave-3 comment declares load-bearing is preserved. Fine-pointer desktop is untouched — a mouse
   user can hover the dots, so they keep the column. The ⋯ button is absolutely positioned inside the
   coarse padding-inline-end reservation, not in this track, so it does not move.
   NOT IN SCOPE: C7 monotonicity (PD-3) is a different defect — the bands are keyed on the .vlt
   container while the list is .vlt minus the inspector. It stays red on purpose until PD-3 lands. */
@media (pointer: coarse) {
  @container vlt (min-width: 788px) {
    .vlt-list__head, .vlt-list__row {
      grid-template-columns:
        [name]  minmax(92px, 3.2fr)
        [dur]   minmax(64px, 1.05fr)
        [size]  minmax(50px, .62fr)
        [words] minmax(62px, .70fr)
        [date]  minmax(80px, .95fr);
    }
    .vlt-list__row > :nth-child(6),
    .vlt-list__head span:nth-child(6) { display: none; }
  }
}

/* #restore: preview-column source poster (YouTube/IG or custom image) + edit-image affordance */
.vlt-preview__hero { position: relative; overflow: hidden; }
.vlt-preview__img { position: absolute; inset: 0; inline-size: 100%; block-size: 100%; object-fit: cover; }
.vlt-preview__hero.has-img > :not(.vlt-preview__img):not(.vlt-preview__editimg) { display: none; }
.vlt-preview__editimg { position: absolute; inset-block-start: 8px; inset-inline-end: 8px; inline-size: 30px; block-size: 30px; border-radius: 8px; border: 1px solid hsl(var(--line)); background: hsl(var(--overlay) / .82); -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); color: hsl(var(--ink)); cursor: pointer; display: grid; place-items: center; z-index: 2; font-size: 14px; }
.vlt-preview__editimg:hover { border-color: hsl(var(--accent)); }

/* #restore: file-row source poster / custom image */
.vlt-row__thumb.has-img { padding: 0; overflow: hidden; background: none; }
.vlt-row__thumb-img { inline-size: 100%; block-size: 100%; object-fit: cover; border-radius: inherit; display: block; }

/* #V2 multi-select + drag-to-move + bulk action bar */
.vlt { position: relative; }
.vlt-row.is-sel, .vlt-list__row.is-sel { background: hsl(var(--selected) / .6); box-shadow: inset 2px 0 0 hsl(var(--accent)); }
.vlt-row.is-dragging, .vlt-list__row.is-dragging { opacity: .45; }
.vlt-row.vlt-drop, .vlt-crumb.vlt-drop { outline: 2px solid hsl(var(--accent)); outline-offset: -2px; background: hsl(var(--accent-h) 60% 50% / .2); border-radius: 8px; }
.vlt-bulk { position: absolute; inset-block-end: 14px; inset-inline: 0; margin-inline: auto; inline-size: max-content; max-inline-size: calc(100% - 24px); display: flex; align-items: center; gap: 8px; padding: 8px 10px 8px 14px; border-radius: 13px; background: hsl(var(--overlay) / .96); border: 1px solid hsl(var(--frame)); box-shadow: 0 18px 44px -18px hsl(var(--hue) 40% 3% / .85); z-index: 30; -webkit-backdrop-filter: blur(18px); backdrop-filter: blur(18px); overflow-x: auto; }
.vlt-bulk[hidden] { display: none; }
.vlt-bulk__n { font-weight: 700; font-size: 13px; margin-inline-end: 4px; white-space: nowrap; color: hsl(var(--ink)); }
.vlt-bulk__b { padding: 6px 12px; border-radius: 8px; border: 1px solid hsl(var(--line)); background: hsl(var(--box)); color: hsl(var(--ink)); font-size: 13px; white-space: nowrap; cursor: pointer; }
.vlt-bulk__b:hover { border-color: hsl(var(--accent)); }
/* R33: "Open as notebook" is the headline bulk action, so it reads as the primary one */
.vlt-bulk__b--primary { border-color: hsl(var(--accent)); background: hsl(var(--accent) / .16); font-weight: 600; }
.vlt-bulk__b--primary:hover { background: hsl(var(--accent) / .26); }
.vlt-bulk__b--danger:hover { border-color: hsl(var(--err)); color: hsl(var(--err)); }
.vlt-bulk__b--ghost { border-color: transparent; color: hsl(var(--ink-dull)); }

/* ===== R83/§76: file-manager touch parity ===============================================================
   MEASURED gaps (tests/playwright/r83-vault-touch.spec.js): file rows exposed NO visible per-file action on
   touch (every action hid behind an undiscoverable long-press), rows fought the iOS long-press with native
   text-selection/callout, and multi-select was gated behind Ctrl/Shift-click (unreachable on touch). These
   rules are UNLAYERED so they win over @layer components AND the unlayered narrow-list hide above. */
.vlt-row, .vlt-list__row { -webkit-touch-callout: none; user-select: none; }   /* D3: stop fighting the iOS long-press */
.vlt-list__row { position: relative; }                                          /* anchor for the absolute ⋯ (columns .vlt-row is already relative) */

/* D1: the per-row ⋯. position:absolute → immune to the list grid template AND its implicit tracks. Fine
   pointers: hidden until the row is hovered/focused, so the dense desktop list stays clean. It opens the
   SAME anchored menu that long-press and right-click already open (data-cmd="menu"). */
.vlt-row__more {
  position: absolute; inset-inline-end: 6px; inset-block: 0; block-size: 100%; margin-block: auto;
  display: none; place-items: center; inline-size: 30px; border: 0; border-radius: 8px;
  background: transparent; color: hsl(var(--ink-dull)); cursor: pointer; z-index: 4;
}
.vlt-row__more > svg, .vlt-row__more svg { inline-size: 18px; block-size: 18px; }
/* fine pointer: revealed ONLY on row hover/focus (Finder/Drive). display-toggle (not opacity) keeps the
   button entirely out of layout at rest, so the dense desktop list is uncluttered AND it is never counted as
   a data column. The hover selectors outrank the narrow `:nth-child(n+3){display:none}` rule above. */
.vlt-row:hover > .vlt-row__more, .vlt-list__row:hover > .vlt-row__more,
.vlt-row:focus-within > .vlt-row__more, .vlt-list__row:focus-within > .vlt-row__more,
.vlt-row__more:focus-visible { display: grid; }
.vlt-row__more:hover { background: hsl(var(--hover) / .9); color: hsl(var(--ink)); }
.vlt-row__more:focus-visible { outline: 2px solid hsl(var(--accent)); outline-offset: -2px; }

/* Coarse pointer (touch): the ⋯ is ALWAYS visible at a full 44px target — long-press is undiscoverable and
   there is no right-click. `display:grid` here is later in source than the narrow-list hide, so it wins even
   where trailing children are hidden by position. Reserve trailing room so it never overlaps the name/meta. */
@media (pointer: coarse) {
  .vlt-row > .vlt-row__more, .vlt-list__row > .vlt-row__more { display: grid; inset-inline-end: 2px; }
  /* R267 §L1 — THE ⋯ WIDTH IS NOW PER-VIEW, and the `min-block-size: 44px` that used to sit here is gone.
     It was a second declaration of a number mobile.css already owns: its blanket
     `button:not(...){min-block-size:...}` is (0,5,1) and outranks anything this sheet can say without
     `!important`, so the copy here could only ever agree or lie. One declaration site: the shell's token,
     retuned per-component just below.
     WIDTH is this sheet's to set — mobile.css's `min-inline-size` allowlist deliberately excludes this
     button — and it takes the same icon-square number on both axes. That also hands 12px back to the file
     NAME, the cell that was starving (measured: a 17.6px name track at 320px). */
  .vlt-row > .vlt-row__more, .vlt-list__row > .vlt-row__more { inline-size: var(--ytx-tap-ico, 32px); }
  .vlt-list__row, .vlt-list__head { padding-inline-end: 48px; }
  /* `padding-block: 0` is measured, not stylistic. Once the name stopped wrapping, the row's tallest child
     was the checkbox TARGET, and 4px+4px of block padding on top of it was the entire remaining excess —
     the row measured 52px with a 44px target. Dropping the padding removes nothing visible (the target is
     transparent and centres its own visual) and lets the row sit on the shell's token. The 26px thumb and
     the type still centre in the track. */
  .vlt-row { padding-inline-end: 36px; padding-block: 0; gap: 7px; }   /* 32px ⋯ + 2px inset + 2px slack */
  /* THE ROW HEIGHT COMES FROM THE SHELL'S TOKEN, NOT FROM A NUMBER TYPED HERE. mobile.css now publishes the
     estate's tap envelope as `--ytx-tap` (36px, text-bearing controls) / `--ytx-tap-ico` (32px, icon
     squares) / `--ytx-tap-ceil` (44px, the ceiling), and documents `--ytx-tap` as the hook a component uses
     to retune its own floor from its OWN stylesheet. Consuming it is how this sheet stays one number with
     the shell instead of becoming a second, competing 44px layer. The fallback covers mobile.css not being
     loaded (the vault-only test harness).
     Measured drop for the row: 179px -> 36px at 320x568, 52px -> 36px at 390x844. Seven rows now ask for
     252px of scroll where they asked for 1,253px inside a 568px viewport. */
  .vlt-row, .vlt-list__row { min-block-size: var(--ytx-tap, 36px); }
  /* the ⋯ is an icon-only square, so it takes the icon token on BOTH axes — set as a variable so the
     shell's own blanket floor reads the smaller number rather than being out-specified by a fight. */
  .vlt-row__more { --ytx-tap: var(--ytx-tap-ico, 32px); }
  .vlt-bulk__b { display: inline-flex; align-items: center; }   /* D5b; height is the shell's token */
}

/* D5: "Select" (touch multi-select entry) is file-only — hide it for folders/protected, matching selToggle. */
.vlt-menu[data-kind="folder"] [data-cmd="ctx-select"],
.vlt-menu[data-kind="protected"] [data-cmd="ctx-select"] { display: none; }
/* ===== end R83/§76 ===================================================================================== */

/* ===== R267/§260 V2b — THE CHECKBOX COLUMN ================================================================
   The keyholder asked for two doors: modifier keys "**or** have the ability to select check mark multiple
   files". A phone has no ⌘, no Ctrl and no Shift, so the modifier door does not exist there at all — this is
   the one a finger can open. Markup: vault.js cbxEl() / cbxAllEl(); behaviour: onCbxChange().

   THE TARGET IS COMPOSED, NOT GROWN. govuk-frontend checkboxes/_mixin.scss:5-7 states it as arithmetic —
   the 44px target is a 40px visual plus a 4px gutter — and :26-29 / :55-56 build it by sizing the INVISIBLE
   native <input> to the whole target and laying it over the smaller visual. Same technique here with a 22px
   visual, because a 44px box drawn into a dense file list would dominate rows it only annotates, while a
   bare `<input type=checkbox>` measures ~13x13 and is a miss on a phone. The input stays a real native
   control the whole time: focusable, announced, Space-toggleable, `indeterminate` readable as "mixed".
   `touch-action: manipulation` (_mixin.scss:47) drops the 300ms double-tap delay.

   THESE RULES LIVE HERE, NOT IN mobile.css. That file owns the app-wide `@media (pointer: coarse)` block and
   belongs to another owner; declaring a second 44px layer for selectors it already covers is how two files
   end up disagreeing about one number. Everything below is scoped to `.vlt-cbx*` (plus the two name cells it
   shares a row with), so there is exactly one declaration site per selector. Hand-over list in the report. */
.vlt-cbx {
  /* R289/§282 f/u 2c (builder: "the selection check mark is way too large and is taking too much space and
     has too much space around it - re-layout that section to be minimum in size").
     MEASURED BEFORE (fine pointer, 1440x900): target 26px, visual 17px, an 8px seam, then a 26px thumb and
     another 8px seam - the filename text did not start until 68px into a 160px name cell, i.e. the leading
     chrome ate 42% of the column the file manager exists to show.
     NOW: 24px target / 15px visual / 6px seams -> text starts at ~60px. 24px is the FLOOR, not a taste
     choice: WCAG 2.2 SC 2.5.8 (AA) requires 24x24 CSS px for a pointer target, and the composed-target
     technique means the invisible <input> IS the target. Going smaller trades that AA criterion, so it is a
     decision to take deliberately rather than something to shave silently. Coarse pointers are untouched
     (32px, declared once at :1134). */
  --cbx-target: 24px;
  --cbx-visual: 15px;
  position: relative; flex: 0 0 auto;
  display: inline-grid; place-items: center;
  inline-size: var(--cbx-target); block-size: var(--cbx-target);
  margin: 0; cursor: pointer;
  touch-action: manipulation; -webkit-tap-highlight-color: transparent;
}
/* The inert twin on a folder row. `sel` holds FILE ids only, so a folder has nothing to tick — but without a
   spacer of the same width the name column would step left on every folder and the checkbox column would
   stop being a column. `pointer-events: none` so the width it occupies never eats the folder's drill-in
   click, and it is NOT `.vlt-cbx`, so vault.js's click guard skips it too. */
.vlt-cbx__gap { flex: 0 0 auto; display: inline-block; inline-size: var(--cbx-target, 24px); block-size: 1px; pointer-events: none; }
/* the whole target, transparent, ON TOP (_mixin.scss:26-29 — "allow the input to sit above the label,
   enabling its proper detection when exploring by touch or using automation tools") */
.vlt-cbx__i {
  position: absolute; inset: 0; z-index: 1;
  inline-size: 100%; block-size: 100%; margin: 0; padding: 0;
  opacity: 0; cursor: pointer; touch-action: manipulation;
}
/* The box the eye sees. `position: relative; display: block` on purpose, NOT a flex/grid centring container:
   the header box lives inside `.vlt-list__head`, whose `span:not(:first-child) { text-align: end;
   justify-content: flex-end }` reaches every descendant span — and both of those properties are inert on a
   block box with absolutely-positioned marks, so the mark cannot be shoved off-centre by a rule that has
   nothing to do with it. The marks are sized in `calc()` off --cbx-visual rather than in %, because a
   percentage inside an auto-sized track resolves against an indefinite size and can collapse to nothing. */
.vlt-cbx__box {
  position: relative; display: block; pointer-events: none;
  inline-size: var(--cbx-visual); block-size: var(--cbx-visual);
  border: 1.5px solid hsl(var(--ink-faint)); border-radius: 4px;
  background: hsl(var(--box)); color: hsl(var(--app));
  transition: background-color .12s ease, border-color .12s ease;
}
.vlt-cbx__box::before, .vlt-cbx__box::after {
  content: ""; position: absolute; inset-block-start: 50%; inset-inline-start: 50%; opacity: 0;
}
/* the check: a box with a border on two sides (└), rotated 45° — _mixin.scss "✔ Check mark". No emoji, no
   icon font, no extra SVG on a row that may render a thousand times. */
.vlt-cbx__box::before {
  inline-size: calc(var(--cbx-visual) * .26); block-size: calc(var(--cbx-visual) * .5);
  border: solid currentColor; border-width: 0 2px 2px 0;
  transform: translate(-50%, -58%) rotate(45deg);
}
/* the mixed mark (—) */
.vlt-cbx__box::after {
  inline-size: calc(var(--cbx-visual) * .55); block-size: 2px; border-radius: 2px;
  background: currentColor; transform: translate(-50%, -50%);
}
.vlt-cbx__i:checked ~ .vlt-cbx__box { background: hsl(var(--accent)); border-color: hsl(var(--accent)); }
.vlt-cbx__i:checked ~ .vlt-cbx__box::before { opacity: 1; }
/* INDETERMINATE WINS, and is declared after `:checked` on purpose: an input can carry both, and a header
   showing a tick while only 3 of 10 rows are selected is the exact lie the tri-state exists to prevent. */
.vlt-cbx__i:indeterminate ~ .vlt-cbx__box { background: hsl(var(--accent)); border-color: hsl(var(--accent)); }
.vlt-cbx__i:indeterminate ~ .vlt-cbx__box::before { opacity: 0; }
.vlt-cbx__i:indeterminate ~ .vlt-cbx__box::after { opacity: 1; }
.vlt-cbx:hover .vlt-cbx__box { border-color: hsl(var(--accent)); }
.vlt-cbx__i:focus-visible ~ .vlt-cbx__box { outline: 2px solid hsl(var(--accent)); outline-offset: 2px; }
/* the header box sits in the Name cell; pin it back to start against the head's `text-align: end` rules */
.vlt-list__head .vlt-lc--name { justify-content: flex-start; text-align: start; }
.vlt-list__head .vlt-lc__hd { text-align: start; }
/* columns view: name + select-all as one group, so the head's space-between still throws the count to the far edge */
.vlt-col__headmain { display: flex; align-items: center; gap: 8px; min-inline-size: 0; }
.vlt-col__headmain b { min-inline-size: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* `.vlt-list__head` is STICKY at the top of `.vlt-list`, so anything that scrolls a row into view — the
   roving keyboard focus, a tool's actionability scroll — parks it exactly UNDER the header and the row's own
   controls become unclickable. `.vlt-row` in columns view already carries `scroll-margin: 8px` for the same
   family of reason; the List view had none at all. Reserve the header's height. */
.vlt-list__row { scroll-margin-block-start: 40px; }

@media (forced-colors: active) {
  /* the visual box is painted with background/border, both of which forced-colors overrides — restate it in
     system colours or the tick disappears entirely in High Contrast (r129-forced-colors.spec.js's remit) */
  .vlt-cbx__box { border-color: ButtonText; background: ButtonFace; color: HighlightText; forced-color-adjust: none; }
  .vlt-cbx__i:checked ~ .vlt-cbx__box,
  .vlt-cbx__i:indeterminate ~ .vlt-cbx__box { background: Highlight; border-color: Highlight; }
}
@media (prefers-reduced-motion: reduce) { .vlt-cbx__box { transition: none; } }

@media (pointer: coarse) {
  /* THE ONE PLACE THIS CONTROL'S TARGET IS DECLARED — and it is now the shell's icon-square token, 32px,
     not 44px. The composed-target technique is unchanged and is the reason shrinking is safe: the target is
     the invisible native <input> (govuk _mixin.scss:26-29), the visual is a separate, smaller box, and the
     two are independent. 32px is 8px clear of the WCAG 2.2 SC 2.5.8 (AA) 24px floor and 12px under the
     ceiling, which is what "as less than 44px as possible" asks for; the visual comes down with it (22 ->
     20px) so the proportion the govuk arithmetic depends on is kept.
     This is also what let the ROW reach 36px: a 44px target inside it made 44px the floor for every file
     row in the product, which was the single largest fixed cost in the list. */
  .vlt-cbx { --cbx-target: var(--ytx-tap-ico, 32px); --cbx-visual: 20px; }
  .vlt-cbx__gap { inline-size: var(--ytx-tap-ico, 32px); }
  /* MEASURED, by the live tap probe, not reasoned about: `.vlt-list__head` is `block-size: 34px` and
     `position: sticky; z-index: 1`, so a 44px control inside it hangs 5px past the header's own box — and
     because the header is the higher layer, those 5px sat ON TOP of the FIRST file row's checkbox and ate
     every tap aimed at it ("…from <div class="vlt-list__head"> subtree intercepts pointer events", forever).
     The control was in the DOM, correctly sized, and unreachable. The header has to grow to hold what it
     contains; 34px stays the floor for a fine pointer, where the box is 26px and fits. */
  .vlt-list__head { block-size: auto; min-block-size: var(--ytx-tap, 36px); }
  .vlt-list__row { scroll-margin-block-start: 42px; }   /* the header is taller here — so is the reservation */
  /* R267 §L1 — THIS RULE IS THE 179px ROW, and it is reverted. It let a filename wrap rather than collide
     with the checkbox (govuk _mixin.scss:42/:212). The reasoning was sound; the arithmetic underneath it
     was not. MEASURED at 320x568 coarse, on the live row: the grid resolved to
     `44px 40px 17.5938px 24.4062px 27px` — a 44px checkbox, a 40px thumb, a 24px meta cell, 27px of
     presence dots, 4x9px of gap and 54px of padding had already spent 225px of a 243px row, leaving the
     NAME 17.6px. `overflow-wrap: anywhere` (inherited by this whole subtree from the shell's long-word
     gate) then broke "Ceiling probe 0.mp3" at roughly one character per line: 171px of text, a 179px row,
     and seven of them needing 1,253px of scroll inside a 568px viewport.
     Capping the height would only have clipped it — the content was genuinely that tall. The fix is the
     width budget (the 26px thumb, the 32px ⋯, 7px gaps, the presence dots yielding below 420px) plus this:
     a file row is a single line that ellipsizes, which is what every file manager does and what "fit more"
     requires. `overflow-wrap: normal` is restated rather than assumed, because `anywhere` arrives by
     inheritance and would otherwise still apply the moment anything reintroduces wrapping. */
  .vlt-list__row .vlt-lc__t, .vlt-row__name {
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; overflow-wrap: normal;
  }
}
/* ===== end R267/§260 V2b ================================================================================ */

/* #V3 workspace tabs */
.vlt-tabsbar { flex: 0 0 auto; display: flex; align-items: stretch; gap: 3px; padding: 5px 8px 0; overflow-x: auto; background: hsl(var(--sidebar) / .55); border-block-end: 1px solid hsl(var(--line)); scrollbar-width: thin; }
.vlt-tab { display: inline-flex; align-items: center; gap: 7px; max-inline-size: 210px; padding: 7px 10px; border: 1px solid transparent; border-block-end: none; border-start-start-radius: 9px; border-start-end-radius: 9px; color: hsl(var(--ink-dull)); font-family: var(--font-display); font-size: 12.5px; white-space: nowrap; cursor: pointer; }
.vlt-tab.is-active { background: hsl(var(--app)); border-color: hsl(var(--line)); color: hsl(var(--ink)); }
.vlt-tab__ico { opacity: .55; font-size: 11px; }
.vlt-tab__t { overflow: hidden; text-overflow: ellipsis; max-inline-size: 155px; }
.vlt-tab__x { position: relative; inline-size: 17px; block-size: 17px; border-radius: 5px; display: grid; place-items: center; font-size: 10px; color: hsl(var(--ink-faint)); flex: none; }
/* R267 §L1 — a 17x17 close button is 7px under the WCAG 2.2 SC 2.5.8 (AA) floor. It sits inside a tab whose
   whole height is ~31px, so GROWING the box would grow the tab strip; the hit area is composed instead —
   govuk _mixin.scss:26-29, and the same pseudo-element idiom mobile.css already uses for `.th-dots`. The
   glyph keeps its 17px, the finger gets 25px, and the tab strip does not move. */
.vlt-tab__x::after { content: ""; position: absolute; inset: -4px; border-radius: 7px; }
.vlt-tab__x:hover { background: hsl(var(--hover)); color: hsl(var(--ink)); }
.vlt-tab--new { color: hsl(var(--ink-dull)); font-size: 17px; padding: 0 12px; max-inline-size: none; }
.vlt-tab--new:hover, .vlt-tab:hover:not(.is-active) { color: hsl(var(--ink)); }

/* #V4 Gallery/Timeline surface — hosted per pane, shown for that pane's media views (#V3 pane-scoped data-view) */
.vlt-media { display: none; block-size: 100%; min-block-size: 0; position: relative; }
.vlt-explorer[data-view="gallery"] .vlt-columns, .vlt-explorer[data-view="timeline"] .vlt-columns,
.vlt-explorer[data-view="gallery"] .vlt-list, .vlt-explorer[data-view="timeline"] .vlt-list { display: none; }
.vlt-explorer[data-view="gallery"] .vlt-media, .vlt-explorer[data-view="timeline"] .vlt-media { display: block; }

/* #V5 real Quick Look — media player + poster + prev/next paging + scrollable transcript */
.vlt-ql__media { position: relative; inline-size: 100%; block-size: 100%; display: grid; place-items: center; overflow: hidden; background: #000; }
.vlt-ql__player { max-inline-size: 100%; max-block-size: 100%; display: block; }
.vlt-ql__media[data-media="video"] .vlt-ql__player { inline-size: 100%; block-size: 100%; object-fit: contain; }
.vlt-ql__media[data-media="audio"] { background: linear-gradient(160deg, hsl(var(--hue) 16% 16%), hsl(var(--hue) 18% 9%)); }
.vlt-ql__media[data-media="audio"] .vlt-ql__player { inline-size: 88%; block-size: auto; }
.vlt-ql__poster { inline-size: 100%; block-size: 100%; object-fit: cover; }
.vlt-ql__nav { position: absolute; inset-block-start: 50%; transform: translateY(-50%); inline-size: 42px; block-size: 42px; border-radius: 999px; background: hsl(var(--overlay) / .78); border: 1px solid hsl(var(--frame)); color: hsl(var(--ink)); font-size: 24px; line-height: 1; display: grid; place-items: center; z-index: 4; cursor: pointer; -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); }
.vlt-ql__prev { inset-inline-start: 12px; }
.vlt-ql__next { inset-inline-end: 12px; }
.vlt-ql__nav:hover { border-color: hsl(var(--accent)); }
.vlt-quicklook[data-single] .vlt-ql__nav { display: none; }
.vlt-ql__snip { max-block-size: 32vh; overflow-y: auto; }

/* #V5 Quick Look audio: waveform poster behind the controls (not a black void) */
.vlt-ql__media[data-media="audio"] { background-image: var(--ql-bg, none); background-size: cover; background-position: center; }
.vlt-ql__media[data-media="audio"]::before { content: ""; position: absolute; inset: 0; background: linear-gradient(hsl(var(--hue) 30% 6% / .3), hsl(var(--hue) 30% 6% / .72)); }
.vlt-ql__media[data-media="audio"] .vlt-ql__player { position: absolute; inset-inline: 6%; inset-block-end: 16px; inline-size: 88%; z-index: 2; }

/* #V6 transcript-native rows — a dim first-line transcript snippet under the file name */
.vlt-row__main { min-inline-size: 0; display: flex; flex-direction: column; justify-content: center; gap: 1px; overflow: hidden; }
.vlt-row__snip { font-size: var(--fs-2xs); line-height: 1.3; color: hsl(var(--ink-faint)); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* R267 §L1 — was `+ 13px` = 47px, i.e. 3px OVER the ceiling on every transcript-bearing row, which the
   demo fixture simply never rendered. The two lines it has to hold measure 18.9px (name, --fs-md/1.4) +
   13.7px (snip, --fs-2xs/1.3) + 1px gap + 8px padding = 41.6px, so +10px is the honest reservation and it
   lands exactly ON the 44px ceiling rather than past it. */
.vlt-row.has-snip { min-block-size: calc(var(--row-h) + 10px); }
.vlt-row[aria-selected="true"] .vlt-row__snip { color: hsl(var(--ink-dull)); }

/* #fix mobile: keep the Back button + breadcrumb visible — the 4-button view switch (Columns/List/Gallery/
   Timeline) was consuming the whole top bar on phones and clipping navigation. Icon-only + scrollable switch,
   shrinkable tools, so Back + crumbs always have room. */
@container vlt (max-width: 599.98px) {
  /* Workspace TABS are a desktop multi-pane power feature — on phones they only add a cluttered row and the
     "Library / Session" tab titles read as overlapping. Hide them; the back button + breadcrumb handle navigation. */
  .vlt-tabsbar { display: none; }
  .vlt-vbtn > span:not(.vlt-ic) { display: none; }
  .vlt-vbtn { padding: 5px 8px; gap: 0; }
  .vlt-viewswitch { max-inline-size: 100%; overflow-x: auto; scrollbar-width: none; }
  .vlt-viewswitch::-webkit-scrollbar { display: none; }
  /* STACK the header on phones: the breadcrumb collapses to a back button + the CURRENT location only (the whole
     squeezed trail was crushing crumbs mid-word), and the view tools drop to their own full-width row below. */
  .vlt-top { flex-wrap: wrap; row-gap: 8px; align-items: stretch; }
  .vlt-top__nav { flex: 1 1 100%; min-inline-size: 0; }
  /* the tool labels are `white-space: nowrap` now, so the row can no longer relieve pressure by wrapping a
     button — it scrolls instead, the same contract `.vlt-viewswitch` above already uses. Without this the
     pressure would land on the PAGE and re-break the R83 no-horizontal-overflow gate. */
  .vlt-top__tools { flex: 1 1 100%; min-inline-size: 0; gap: 4px; justify-content: flex-start;
    overflow-x: auto; scrollbar-width: none; }
  .vlt-top__tools::-webkit-scrollbar { display: none; }
  .vlt-crumbs { flex: 1 1 auto; }
  .vlt-crumbs .vlt-crumb:not(:last-child), .vlt-crumbs .vlt-crumb__sep { display: none; }   /* back button goes up a level */
  .vlt-crumbs .vlt-crumb:last-child { display: block; max-inline-size: 100%; font-size: 15px; font-weight: 700; padding-inline-start: 2px; overflow: hidden; text-overflow: ellipsis; }
}

/* R267 §L1 — THE SHORT-VIEWPORT BAND (the vault's half; the library view has the matching block).
   At 844x390 the vault's own chrome ate 143px of a 390px viewport — a 41px workspace tab strip, a 53px top
   bar and a 49px column header — and the first file row started at y=386, one pixel below the fold.
   The tab strip is already hidden below a 600px container as "a desktop multi-pane power feature"; a 390px
   -tall window is the same judgement on the other axis, so it goes there too and the back button plus the
   breadcrumb carry navigation, exactly as they do on a phone. The top bar and the column header give up
   padding only — no target changes size, so the ceiling and the WCAG AA floor are both untouched. */
@media (max-height: 520px) {
  .vlt-tabsbar { display: none; }
  .vlt-top { min-block-size: 40px; padding-block: 4px; }
  .vlt-col__head { padding: 3px 12px; }
}

/* R267 §L1 — THE PRESENCE DOTS YIELD FIRST on a genuinely narrow pane. They cost 27px of track plus a gap
   in a row that had 17.6px left for the filename, and they are a per-row ANNOTATION, not an action: the
   same three states are spelled out, with labels, in the inspector's `.vlt-presrow`. Below 420px of pane
   the name gets that space back. A container query, never a viewport one — a 380px pane inside a 1400px
   window is just as tight as a 380px phone, which is the whole reason `.vlt` is a container. */
@container vlt (max-width: 419.98px) {
  .vlt-row > .vlt-pres { display: none; }
}

/* =====================================================================================================
   #V3 DUAL-PANE — two independent explorer panes + a draggable/keyboard divider.
   Landscape/desktop: side by side (ratio = --vlt-ratio % for pane A, min ~280px each).
   Portrait/narrow (<900cqi): the panes STACK and the divider flips horizontal.
   ===================================================================================================== */
/* R69 (§64.2): `.vlt-panes` is POSITIONED and had no overflow, so anything that outgrew it painted OVER
   the Details rail instead of being contained. `clip` (not `hidden`) contains it without creating a scroll
   container — which would have broken the sticky `.vlt-list__head`. */
.vlt-panes { display: flex; min-inline-size: 0; min-block-size: 0; position: relative; overflow: clip; }
.vlt-explorer { flex: 1 1 auto; }
.vlt:not([data-dual]) .vlt-pane--b { display: none; }
.vlt-divider { display: none; }
/* R69 (§64.2) — WHY THE PANES BROKE THE DESIGN AT THE LIMIT.
   Pane A was `flex-shrink: 0` with a percentage basis while pane B carried a hard 280px floor, so the flex
   line's own minimum was `ratio%·W + 12 + 280`. Whenever that exceeded W — drag near the edge, then narrow
   the window, toggle Details (a ±288px step), or cross the stacked/side-by-side regime — flexbox could not
   resolve it and pane B was pushed straight out of its track and over the Details rail. The drag-time
   clamp could not save it because NOTHING re-clamped afterwards.
   Making pane A shrinkable turns that from a clamp you must remember to re-apply into a layout INVARIANT:
   when the box is too small the space comes out of A, and overflow is arithmetically impossible. */
/* 280px is the SHIPPED floor (vault-dualpane.spec.js asserts it) — this release changes only whether
   overflow is possible, never how small a pane may get. Lowering the floor belongs to the collapse work,
   where it drops to the rail width deliberately rather than as a side effect. */
.vlt { --vlt-pane-min: 280px; }
/* `0 1`: never GROW past the chosen ratio (grow:1 made pane A swallow the free space, so Home could not
   shrink it), but always able to SHRINK — which is what makes overflow arithmetically impossible. */
.vlt[data-dual] .vlt-pane--a { flex: 0 1 calc(var(--vlt-ratio, 50) * 1%); min-inline-size: min(var(--vlt-pane-min), 45%); }
.vlt[data-dual] .vlt-pane--b { flex: 1 1 0; min-inline-size: min(var(--vlt-pane-min), 45%); }
.vlt[data-dual] .vlt-divider { display: block; flex: 0 0 12px; position: relative; cursor: col-resize;
  touch-action: none; background: transparent; z-index: 2; }
.vlt-divider::before { content: ""; position: absolute; inset-block: 0; inset-inline-start: 5px; inline-size: 2px;
  background: hsl(var(--line)); }
.vlt-divider::after { content: ""; position: absolute; inset-block: 0; inset-inline: -8px; }   /* ≥28px hit area (44px on touch below) */
.vlt-divider:hover::before, .vlt-divider:focus-visible::before { background: hsl(var(--accent)); inline-size: 3px; }
.vlt-divider:focus-visible { outline: 2px solid hsl(var(--accent)); outline-offset: -1px; border-radius: 4px; }
@media (pointer: coarse) {
  .vlt-divider::after { inset-inline: -10px; }           /* 32px touch slab (12px visual + 10px each side) */
  /* R267 §L1 — `.vlt-tool--dual { min-block-size: 44px }` removed from here. mobile.css already grants
     `.vlt-tool` a 44px coarse block-size (and its blanket `button` rule grants it twice over), so this was
     a second declaration site for one number — exactly the duplication that lets two files drift apart.
     What actually made this button 88.4px tall at 320px was mid-word wrapping, fixed at `.vlt-tool`. */
}
/* the ACTIVE pane is visibly marked (dual only): an accent top bar + a faint wash */
.vlt[data-dual] .vlt-explorer.is-active { box-shadow: inset 0 2px 0 hsl(var(--accent)); background: hsl(var(--accent-h) 40% 30% / .06); }
@media (forced-colors: active) { .vlt[data-dual] .vlt-explorer.is-active { outline: 2px solid Highlight; outline-offset: -2px; } }
/* blank-area cross-pane drop target (drag from one pane over the other pane's empty space) */
.vlt[data-dual] .vlt-explorer.vlt-drop { outline: 2px dashed hsl(var(--accent)); outline-offset: -4px; background: hsl(var(--accent-h) 40% 50% / .08); }
/* portrait / narrow container: STACK the panes; the divider goes horizontal. Threshold 900: below it the
   panes area (container minus the ~280px inspector rail) can no longer give BOTH panes their ~280px minimum. */
@container vlt (max-width: 899.98px) {
  .vlt[data-dual] .vlt-panes { flex-direction: column; }
  .vlt[data-dual] .vlt-pane--a { flex-basis: calc(var(--vlt-ratio, 50) * 1%); min-inline-size: 0; min-block-size: 110px; }
  .vlt[data-dual] .vlt-pane--b { min-inline-size: 0; min-block-size: 110px; }
  .vlt[data-dual] .vlt-divider { cursor: row-resize; }
  .vlt[data-dual] .vlt-divider::before { inset-block: auto; inset-inline: 0; inset-block-start: 5px; inline-size: auto; block-size: 2px; }
  .vlt[data-dual] .vlt-divider::after { inset-inline: 0; inset-block: -8px; }
}

/* #V3 a11y: visually-hidden live region (active-pane / dual announcements) */
.vlt-sr { position: absolute; inline-size: 1px; block-size: 1px; margin: -1px; padding: 0; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap; border: 0; }

/* #Amend-V3 conflict dialog — Overwrite / Skip / Keep both / Rename (+ apply-to-all); 44px targets */
.vlt-conflict { border: 0; padding: 0; inline-size: min(460px, 92vw); border-radius: 16px;
  background: hsl(var(--overlay)); color: hsl(var(--ink));
  box-shadow: 0 40px 100px -30px hsl(var(--hue) 50% 2% / .8); }
.vlt-conflict::backdrop { background: hsl(var(--hue) 40% 3% / .62); -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px); }
.vlt-cf { padding: 18px; display: flex; flex-direction: column; gap: 12px; font-family: var(--font-body); }
.vlt-cf__title { display: flex; align-items: center; gap: 8px; font-family: var(--font-display); font-weight: 700; font-size: 15px; }
.vlt-cf__msg { margin: 0; font-size: 13px; line-height: 1.5; color: hsl(var(--ink-dull)); overflow-wrap: anywhere; }
.vlt-cf__count { font-size: 11.5px; color: hsl(var(--ink-faint)); font-variant-numeric: tabular-nums; }
.vlt-cf__count:empty { display: none; }
.vlt-cf__ren { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.vlt-cf__ren[hidden] { display: none; }
.vlt-cf__input { flex: 1 1 160px; min-inline-size: 0; font: inherit; font-size: 13.5px; padding: 8px 10px;
  min-block-size: var(--ytx-tap, 36px); border-radius: 9px; color: hsl(var(--ink)); background: hsl(var(--app));
  border: 1px solid hsl(var(--accent)); outline: none; }
.vlt-cf__all { display: flex; align-items: center; gap: 9px; font-size: 12.5px; color: hsl(var(--ink-dull));
  min-block-size: var(--ytx-tap, 36px); cursor: pointer; }
.vlt-cf__all[hidden] { display: none; }
/* 17px was under the WCAG 2.2 SC 2.5.8 (AA) 24px floor. This checkbox has a wrapping label, but the label is
   a whole flex ROW — composing the target over it (govuk _mixin.scss:26-29) would put an invisible input
   across the entire dialog width, so the native control takes the floor itself. */
.vlt-cf__allcb { inline-size: 24px; block-size: 24px; accent-color: hsl(var(--accent)); }
.vlt-cf__acts { display: flex; flex-wrap: wrap; gap: 8px; }
/* `.vlt-cf__b { min-block-size: 44px }` removed: `.vlt-act` buttons already take the shell's `--ytx-tap`
   floor, and a 44px copy here could only ever restate or contradict it. */
.vlt-cf__b--ghost { border-color: transparent; color: hsl(var(--ink-dull)); }

/* =====================================================================================================
   #50 no-emoji — every former text glyph (thumbs, chevrons, empty states, menu icons, tab icons, the
   Quick Look chrome) is now an inline SVG from icons.js. Each icon renders at 1em, so it inherits the
   font-size ALREADY set on its host above (.vlt-row__thumb 17px, .vlt-preview__glyph 44px, …) and the
   existing sizing keeps working. These rules only fix what a text glyph got for free: baseline sitting
   and centering inside a fixed-size box.
   ===================================================================================================== */
.vlt .ico { flex: none; }
/* icons that sit INSIDE a text run (the "Open original" link, the Summary/Neural tabs, Refresh) */
.vlt-preview__link .ico, .vlt-sum__tab .ico, .vlt-nsum__refresh .ico { vertical-align: -.14em; }
/* icons that ARE the element — the span is the box, the SVG is its whole content */
.vlt-row__thumb, .vlt-insp__thumb, .vlt-preview__glyph, .vlt-ql__glyph,
.vlt-tab__ico, .vlt-mi__ic, .vlt-emoji, .vlt-row__chev, .vlt-crumb__sep {
  display: grid; place-items: center; line-height: 0;
}
.vlt-row__chev .ico { inline-size: 12px; block-size: 12px; }         /* the chevron box is 12px wide but the font-size is 15px */
.vlt-mi__ic { block-size: 16px; }                                    /* menu icon gutter stays 16×16; the icon inherits the item's 13px */
.vlt-cf__title .ico { inline-size: 15px; block-size: 15px; }         /* #Amend-V3 dialog title icon */
.vlt-tab--new { display: inline-grid; place-items: center; }

/* ===== R71 (§66 C1-C5): THE PER-PANE HEADER AND THE COLLAPSED RAIL ==================================
   The builder asked for panes that go down to the smallest the UI allows and then SNAP collapsed, with a
   button "up top" that expands them again. There was no per-pane header at all before this — every control
   lived in one shared bar — so the affordance had nowhere to belong. Now it belongs to the pane.
   ADAPTIVE SURFACE LAW: 44px targets under coarse pointers (LAW 5), reduced motion is OFF not shortened
   (LAW 6), and the rail is a container query on the PANE, never the viewport (LAW 2). */
/* EXPANDED: the header must steal NO layout — displacing the pane's content pushed rows out of the
   clickable box and broke the shipped cross-pane drag (vault-dualpane caught it). So while expanded it is
   an overlay carrying just the collapse control; when COLLAPSED it becomes the static rail below. */
.vlt-pane__head {
  position: absolute; z-index: 3;
  inset-block-start: 2px; inset-inline-end: 2px;
  display: flex; align-items: center; gap: 6px;
  padding-inline: 2px;
  border-radius: 8px;
  background: color-mix(in oklab, var(--paper, #fff) 82%, transparent);
  backdrop-filter: blur(4px);
}
.vlt-pane__head .vlt-pane__title { display: none; }        /* the label belongs to the RAIL, not the overlay */
.vlt-pane__title {
  flex: 1 1 auto; min-inline-size: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: .72rem; letter-spacing: .06em; text-transform: uppercase; opacity: .62;
}
.vlt-pane__fold {
  flex: none; display: inline-grid; place-items: center;
  inline-size: 30px; block-size: 30px;
  border: 0; border-radius: 8px; background: none; color: inherit; cursor: pointer; opacity: .7;
}
.vlt-pane__fold:hover, .vlt-pane__fold:focus-visible { opacity: 1; background: color-mix(in oklab, transparent, currentColor 10%); }
.vlt-pane__fold svg { inline-size: 15px; block-size: 15px; }
/* LAW 5 — a coarse pointer gets a real 44px target */
@media (pointer: coarse) {
  /* LAW 5 said 44x44; the keyholder's clarification makes 44 the ceiling, so an icon-only square takes the
     shell's icon token instead. 32px, both axes, 8px clear of the AA floor. */
  .vlt-pane__fold { inline-size: var(--ytx-tap-ico, 32px); block-size: var(--ytx-tap-ico, 32px); }
  .vlt-pane__head { min-block-size: var(--ytx-tap-ico, 32px); }
}
/* the header is chrome for the SPLIT — a single pane doesn't need it */
.vlt:not([data-dual]) .vlt-pane__head { display: none; }

/* ---- the collapsed rail --------------------------------------------------------------------------- */
.vlt { --vlt-rail-w: 36px; }   /* the rail is exactly wide enough for the fold button's 32px square + slop */
.vlt[data-collapse="a"] .vlt-pane--a,
.vlt[data-collapse="b"] .vlt-pane--b {
  /* the floor MUST be overridden or the pane simply re-inflates to --vlt-pane-min */
  display: flex; flex-direction: column;
  flex: 0 0 var(--vlt-rail-w) !important;
  min-inline-size: 0 !important;
  overflow: hidden;
}
/* the surviving pane takes the room */
.vlt[data-collapse="a"] .vlt-pane--b,
.vlt[data-collapse="b"] .vlt-pane--a { flex: 1 1 auto !important; min-inline-size: 0 !important; }
/* a collapsed pane shows ONLY its header, turned into a vertical rail */
.vlt[data-collapse="a"] .vlt-pane--a > :not(.vlt-pane__head),
.vlt[data-collapse="b"] .vlt-pane--b > :not(.vlt-pane__head) { display: none; }
.vlt[data-collapse="a"] .vlt-pane--a .vlt-pane__head,
.vlt[data-collapse="b"] .vlt-pane--b .vlt-pane__head {
  position: static; border-radius: 0; backdrop-filter: none;
  background: color-mix(in oklab, transparent, currentColor 4%);
  flex-direction: column; justify-content: flex-start; gap: 10px;
  block-size: 100%; padding-block: 8px; padding-inline: 0;
  border-block-end: 0; border-inline-end: 1px solid hsl(var(--line));
}
.vlt[data-collapse="a"] .vlt-pane--a .vlt-pane__title,
.vlt[data-collapse="b"] .vlt-pane--b .vlt-pane__title {
  display: block; writing-mode: vertical-rl; text-orientation: mixed;
  flex: 0 1 auto; max-block-size: 12rem;
}
/* the divider is meaningless while a pane is collapsed — the rail button is the way back */
.vlt[data-collapse] .vlt-divider { display: none !important; }

/* stacked regime: the rail is a horizontal strip, not a column */
@container vlt (max-width: 899.98px) {
  .vlt[data-collapse="a"] .vlt-pane--a,
  .vlt[data-collapse="b"] .vlt-pane--b { flex: 0 0 var(--vlt-rail-w) !important; min-block-size: 0 !important; }
  .vlt[data-collapse="a"] .vlt-pane--a .vlt-pane__head,
  .vlt[data-collapse="b"] .vlt-pane--b .vlt-pane__head {
    flex-direction: row; block-size: auto; inline-size: 100%;
    border-inline-end: 0; border-block-end: 1px solid hsl(var(--line));
  }
  .vlt[data-collapse="a"] .vlt-pane--a .vlt-pane__title,
  .vlt[data-collapse="b"] .vlt-pane--b .vlt-pane__title { writing-mode: horizontal-tb; }
}

@media (prefers-reduced-motion: reduce) {
  .vlt-pane__fold, .vlt-pane__head { transition: none !important; animation: none !important; }
}
@media (forced-colors: active) {
  .vlt-pane__head { border-color: CanvasText; }
  .vlt-pane__fold:focus-visible { outline: 2px solid Highlight; outline-offset: 2px; }
}

/* ===== R71 (§66 P1): the preview hero is a real control ============================================ */
.vlt-preview__zoom {
  display: block; inline-size: 100%; block-size: 100%;
  padding: 0; border: 0; background: none; color: inherit; cursor: zoom-in;
  position: relative;
}
.vlt-preview__zoom:focus-visible { outline: 2px solid hsl(var(--accent, 210 90% 55%)); outline-offset: 2px; }

/* ===== R267-R2: the FOCUS RING on the keyboard path to Delete ========================================
   MEASURED (CDP CSS.getMatchedStylesForNode on a keyboard-focused `.vlt-menu__item`, forcePseudoState
   focus-visible): the item computed `outline: 3px none` — i.e. NO indicator — in BOTH normal and
   `forced-colors: active`, so focused and unfocused were indistinguishable on the only path a keyboard user
   has to Delete. axe cannot see this; only a computed-style read can.

   THREE rules matched, and all three failed:
     @layer components  .vlt-menu__item:focus-visible   outline: none                    (now removed)
     @layer state       .vlt :focus-visible             outline: 2px solid hsl(var(--accent))
     UNLAYERED (chrome) :focus-visible                  outline: 2px solid var(--accent); offset 2px
   The unlayered rule beats BOTH layers — an unlayered declaration outranks every named layer regardless of
   specificity — and inside `.vlt` it is POISONED: vault.css redefines `--accent` as a bare HSL triplet
   (`268 88% 68%`), so `solid var(--accent)` is invalid-at-computed-value-time and the whole `outline`
   shorthand falls back to its initial value: width `medium` (3px), style `none`. That is the measured
   `3px none` exactly. Note this poisoning hits EVERY focusable inside `.vlt`, not just this menu — reported
   to the owner of `.vlt :focus-visible`; only the `.vlt-menu` region is repaired here.

   So the replacement must be UNLAYERED too (a layered rule can never win) and must name a colour that
   resolves in this scope. Estate pattern: chrome.css:894/937 (.pal-input) — outline + a forced-colors
   `Highlight` twin. `outline-offset: -2px` draws the ring INSIDE the item, so it never collides with the
   adjacent item; the colour is the ink tone that contrasts with that item's own focus FILL (an accent ring
   on an accent fill would be invisible). Measured contrast: 5.3:1 on the accent fill, 5.5:1 on the danger
   fill — both past the 3:1 that SC 1.4.11 asks of a non-text indicator. */
.vlt-menu__item:focus-visible { outline: 2px solid hsl(var(--ink)); outline-offset: -2px; }
.vlt-menu__item--danger:focus-visible { outline-color: hsl(var(--hue) 30% 8%); }
@media (forced-colors: active) {
  .vlt-menu__item:focus-visible, .vlt-menu__item--danger:focus-visible {
    outline: 2px solid Highlight; outline-offset: -2px; }
  .vlt-menu__foot { border-block-start-color: CanvasText; }
}
