/* ==========================================================================
   vx-shell.css — the shells of the ONE design system (SPEC.md §4.27–§4.31)

     §4.30  room tokens: html[data-page] → --vx-room / -solid / -ink / -on
     §4.27  public taskbar  .vx-taskbar (+ .vx-navsheet, the phone menu)
     §4.28  page frame      .vx-page, .vx-window--page, .vx-section*, .vx-skip
     §4.29  app shell       .vx-app, .vx-shell, .vx-sidenav*, .vx-sitepick,
                            .vx-sheet-wrap, .vx-sheet, .vx-scene*, .vx-appbar,
                            .vx-tabbar, .vx-deck / .vx-span-*
     §4.31  footer          .vx-footer*
            partial chrome  #boot splash (_lab_boot.html), install guide
                            (_lab_pwa_guide.html)

   Markup lives in main/_base_public.html, main/_base_app.html, _lab_nav.html,
   _lab_appbar.html, _lab_footer.html, _lab_boot.html, _lab_pwa_guide.html.
   The cockpit's dense modifier and cockpit-only pieces are in vx-cockpit.css.

   Tokens only (no colour literals), logical properties only (§7). Viewport
   media queries appear only for the shell breakpoints of §6.1: taskbar
   collapse (1023px here, see §4.27 below), side nav drawer 1023px, phone tab
   bar 640px.

   ⚠️ Containment gotcha. .vx-window and .vx-sheet are size-query containers
   (container-type:inline-size), which also applies LAYOUT containment: every
   position:fixed descendant is positioned against the window/sheet, not the
   viewport, and is clipped by its overflow:clip. Overlays therefore live
   OUTSIDE them (the base templates' after_window / after_sheet blocks) or are
   native <dialog>s (the top layer escapes containment). For the same reason
   the entrance animations below use fill-mode `backwards`: a transform that
   stays applied after the animation would be a second containing block.
   ========================================================================== */


/* ── §4.30 room tokens: room hue = navigation, never status ──────────────── */
/* The default makes a cockpit page without a known room valid (slate), so
   var(--vx-room) never resolves to nothing. Customer pages carry no data-page
   (the cockpit's `support` room is green; the customer's help-desk Support is sky
   with a help glyph — purple + headset is the Front desk product line). */
html[data-page]                { --vx-room:var(--vx-slate);  --vx-room-solid:var(--vx-slate-solid);  --vx-room-ink:var(--vx-slate-ink);  --vx-room-on:var(--vx-on-solid); }
html[data-page="pulse"]        { --vx-room:var(--vx-gold);   --vx-room-solid:var(--vx-gold-solid);   --vx-room-ink:var(--vx-gold-ink);   --vx-room-on:var(--vx-on-bright); }
html[data-page="health"]       { --vx-room:var(--vx-sky);    --vx-room-solid:var(--vx-sky-solid);    --vx-room-ink:var(--vx-sky-ink);    --vx-room-on:var(--vx-on-solid); }
html[data-page="support"]      { --vx-room:var(--vx-green);  --vx-room-solid:var(--vx-green-solid);  --vx-room-ink:var(--vx-green-ink);  --vx-room-on:var(--vx-on-solid); }
html[data-page="parity"]       { --vx-room:var(--vx-slate);  --vx-room-solid:var(--vx-slate-solid);  --vx-room-ink:var(--vx-slate-ink);  --vx-room-on:var(--vx-on-solid); }
html[data-page="money"]        { --vx-room:var(--vx-lime);   --vx-room-solid:var(--vx-lime-solid);   --vx-room-ink:var(--vx-lime-ink);   --vx-room-on:var(--vx-on-bright); }
html[data-page="products"]     { --vx-room:var(--vx-lilac);  --vx-room-solid:var(--vx-lilac-solid);  --vx-room-ink:var(--vx-lilac-ink);  --vx-room-on:var(--vx-on-solid); }
html[data-page="coupons"]      { --vx-room:var(--vx-pink);   --vx-room-solid:var(--vx-pink-solid);   --vx-room-ink:var(--vx-pink-ink);   --vx-room-on:var(--vx-on-solid); }
html[data-page="customers"]    { --vx-room:var(--vx-teal);   --vx-room-solid:var(--vx-teal-solid);   --vx-room-ink:var(--vx-teal-ink);   --vx-room-on:var(--vx-on-solid); }
html[data-page="sales"]        { --vx-room:var(--vx-blue);   --vx-room-solid:var(--vx-blue-solid);   --vx-room-ink:var(--vx-blue-ink);   --vx-room-on:var(--vx-on-solid); }
html[data-page="satisfaction"] { --vx-room:var(--vx-purple); --vx-room-solid:var(--vx-purple-solid); --vx-room-ink:var(--vx-purple-ink); --vx-room-on:var(--vx-on-solid); }
html[data-page="growth"]       { --vx-room:var(--vx-lime);   --vx-room-solid:var(--vx-lime-solid);   --vx-room-ink:var(--vx-lime-ink);   --vx-room-on:var(--vx-on-bright); }
html[data-page="instagram"]    { --vx-room:var(--vx-pink);   --vx-room-solid:var(--vx-pink-solid);   --vx-room-ink:var(--vx-pink-ink);   --vx-room-on:var(--vx-on-solid); }
html[data-page="quality"]      { --vx-room:var(--vx-blue);   --vx-room-solid:var(--vx-blue-solid);   --vx-room-ink:var(--vx-blue-ink);   --vx-room-on:var(--vx-on-solid); }
html[data-page="conformance"]  { --vx-room:var(--vx-lilac);  --vx-room-solid:var(--vx-lilac-solid);  --vx-room-ink:var(--vx-lilac-ink);  --vx-room-on:var(--vx-on-solid); }
html[data-page="fleet"]        { --vx-room:var(--vx-purple); --vx-room-solid:var(--vx-purple-solid); --vx-room-ink:var(--vx-purple-ink); --vx-room-on:var(--vx-on-solid); }
html[data-page] .vx-scene > .vx-glyph{ --vx-h:var(--vx-room); --vx-h-solid:var(--vx-room-solid); --vx-h-ink:var(--vx-room-ink); --vx-h-on:var(--vx-room-on); }


/* ── §4.27 public taskbar: a floating frosted strip inside the 8px gutter ── */
.vx-taskbar{ position:sticky; inset-block-start:var(--vx-gutter); z-index:var(--vx-z-nav);
  box-sizing:border-box; display:flex; align-items:center; gap:4px; block-size:var(--vx-bar-h);
  margin-block:var(--vx-gutter) 0; margin-inline:var(--vx-gutter); padding-inline:8px;
  background:color-mix(in srgb,var(--vx-sheet) 72%,transparent);
  -webkit-backdrop-filter:blur(20px) saturate(1.2); backdrop-filter:blur(20px) saturate(1.2);
  border:1px solid var(--vx-line); border-radius:var(--vx-r-md); box-shadow:var(--vx-shadow-bar); }
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){ .vx-taskbar{ background:var(--vx-sheet); } }
@media (prefers-reduced-transparency:reduce){ .vx-taskbar{ background:var(--vx-sheet); -webkit-backdrop-filter:none; backdrop-filter:none; } }

.vx-taskbar__brand{ display:inline-flex; align-items:center; gap:8px; flex:none; padding-block:4px; padding-inline:8px; margin-inline-end:6px;
  border-radius:var(--vx-r-sm); font:700 .9375rem/1 var(--vx-font); letter-spacing:-.02em; color:var(--vx-ink); text-decoration:none; }
.vx-taskbar__brand img{ inline-size:22px; block-size:22px; flex:none; }
.vx-taskbar__brand:hover{ background:var(--vx-hover); }
[dir="rtl"] .vx-taskbar__brand{ letter-spacing:0; }
/* the wordmark "Vistaria" (nav, footer, app bar, cockpit): Open Runde on an LTR
   page (the page carries it anyway); on an RTL page a 1.6 KB six-letter face
   (vx-base "Vx Wordmark") instead of two 40 KB Latin faces for one word */
.vx-wordmark{ font-family:"Open Runde", "Vx Runde Fallback", "Vx Runde Fallback R", "Vx Runde Fallback DV", system-ui, sans-serif; font-weight:700; }
/* impeccable-disable-next-line design-system-font -- the Open Runde 700 wordmark subset (vx-base) */
[dir="rtl"] .vx-wordmark{ font-family:"Vx Wordmark", system-ui, sans-serif; letter-spacing:0; }   /* no "Open Runde" behind it: a web font in the fallback chain is fetched while the first one loads */

.vx-taskbar__nav{ display:flex; align-items:center; gap:2px; min-inline-size:0; }
.vx-taskbar__link{ display:inline-flex; align-items:center; gap:4px; min-block-size:30px; padding-block:6px; padding-inline:9px;
  border-radius:var(--vx-r-sm); cursor:pointer; white-space:nowrap;
  font:500 .8125rem/1 var(--vx-font); color:var(--vx-text); text-decoration:none;
  transition:background-color var(--vx-dur-fast) var(--vx-ease-standard), color var(--vx-dur-fast) var(--vx-ease-standard); }
.vx-taskbar__link:hover, .vx-dropdown[open] > .vx-taskbar__link{ background:var(--vx-sunken); color:var(--vx-ink); transition:none; }
.vx-taskbar__link:is([aria-current],.is-current){ background:var(--vx-sunken); color:var(--vx-ink); font-weight:600; }
.vx-taskbar__link:focus-visible{ outline:2px solid var(--vx-focus); outline-offset:1px; }
.vx-taskbar__link > .vx-icon{ color:var(--vx-text-3); transition:rotate var(--vx-dur-fast) var(--vx-ease-standard); }
.vx-dropdown[open] > .vx-taskbar__link > .vx-icon{ rotate:180deg; }
.vx-taskbar__menu{ min-inline-size:15rem; }                    /* the Products menu: glyph + name per line */
.vx-taskbar__menu .vx-menu__item{ min-block-size:36px; }

.vx-taskbar__end{ margin-inline-start:auto; display:flex; align-items:center; gap:6px; flex:none; }
.vx-taskbar .vx-btn{ margin-block-end:0; }                     /* the bar's height absorbs the sm ledge (28 + 2 + 1.5 < 44) */
.vx-taskbar .vx-lang .vx-menu{ inset-block-start:calc(100% + 10px); }
.vx-taskbar__burger{ display:none; }
/* Collapse at 1023px, not the spec's 860px: measured with the real catalogs,
   Russian and German links run into the end group at 870px (2026-09-27).
   Between 1024 and 1180px the language button drops its label (globe only). */
@media (max-width:1023px){
  .vx-taskbar__nav, .vx-taskbar .vx-btn--tertiary, .vx-taskbar .vx-lang, .vx-taskbar [data-lab-install]{ display:none; }
  .vx-taskbar__burger{ display:inline-grid; }
}
@media (min-width:1024px) and (max-width:1180px){ .vx-taskbar .vx-lang__btn > span{ display:none; } }
@media (max-width:360px){ .vx-taskbar__brand span{ display:none; } }
/* phones: the end group (theme, CTA, burger) ran past a 390px screen in long
   locales (ru: the burger at 399px). The theme toggle moves into the sheet's foot. */
@media (max-width:419px){ .vx-taskbar__end > .vx-theme-toggle{ display:none; } }

/* the phone menu (the burger's panel). A SIBLING of the taskbar, never a child:
   the bar's backdrop-filter would make it the containing block of this fixed
   sheet. ui.js (§5.4 item 10) un-hides it, toggles .is-open and inserts the
   scrim before it. From 1024px it turns absolute + hidden, which is also how
   ui.js's resize guard knows to close it (it closes non-fixed drawers). */
.vx-navsheet{ position:fixed; z-index:var(--vx-z-nav); inset-inline:var(--vx-gutter); inset-block-start:calc(var(--vx-bar-h) + var(--vx-gutter) * 2);
  box-sizing:border-box; display:grid; gap:2px; max-block-size:calc(100dvh - var(--vx-bar-h) - var(--vx-gutter) * 3); overflow:auto; overscroll-behavior:contain;
  padding:6px; background:var(--vx-sheet); border:1px solid var(--vx-line); border-radius:var(--vx-r-lg); box-shadow:var(--vx-shadow-menu);
  transform-origin:top; animation:vx-menu-in 160ms var(--vx-ease-out-expo) backwards; }
.vx-navsheet .vx-menu__item{ min-block-size:44px; font-size:.9375rem; }      /* touch targets */
.vx-navsheet .vx-menu__label{ padding-block-start:10px; }
.vx-navsheet__foot{ display:flex; flex-wrap:wrap; align-items:center; gap:8px; padding-block:10px 4px; padding-inline:6px;
  margin-block-start:4px; border-block-start:1px solid var(--vx-line-soft); }
.vx-navsheet__foot .vx-btn{ margin-block-end:0; }
.vx-navsheet .vx-lang .vx-menu{ inset-block-start:auto; inset-block-end:calc(100% + 6px); }
@media (min-width:1024px){ .vx-navsheet{ position:absolute; display:none !important; } }


/* ── §4.28 page frame on the desk (public, report, blog, docs, legal, auth) ─ */
.vx-page{ display:block; padding-block:12px 0; padding-inline:var(--vx-gutter); }
.vx-window--page{ max-inline-size:var(--vx-w-sheet); margin-inline:auto; }
.vx-window--page > .vx-window__body{ padding:0; }
/* The page window never springs (DESIGN.md One Spring Rule): it is the page,
   not a small thing appearing, and a pop from opacity 0 held the largest paint
   back on every load. _base_public.html no longer adds --pop; a page that
   still carries it (lab_offline.html) gets no animation either. */
.vx-window--page.vx-window--pop{ animation:none; }
.vx-window--page > .vx-window__bar .vx-window__title{ display:block; }   /* a block, not inline-flex: a long title ends in an ellipsis instead of being cut mid-word (R9) */
.vx-window--page > .vx-window__bar > .vx-glyph{ flex:none; }
.vx-page > * + *{ margin-block-start:16px; }                        /* a second window / desk card under the first */

.vx-section{ padding-block:48px; padding-inline:clamp(16px,5cqi,64px); }
.vx-section + .vx-section{ border-block-start:1px solid var(--vx-line-soft); }
.vx-section--hero{ padding-block:56px 48px; }
.vx-section--tight{ padding-block:32px; }
.vx-section__head{ display:grid; gap:8px; max-inline-size:44rem; margin-block-end:24px; }
.vx-section__sub{ font:500 .9375rem/1.6 var(--vx-font); color:var(--vx-text-2); }
@container window (max-width:640px){   /* 640, the same edge as every other phone rule (it was 639) */
  .vx-section{ padding-block:36px; }
  .vx-section--hero{ padding-block:32px 36px; }
}

/* ── §6.7 the task frame: one recipe for the auth and transactional pages
   (_base_task.html) and the cockpit's login and gate (F12). Not centred:
   inside the 1200px frame the task card sits at the inline-start (26rem); from
   a 900px frame a quieter "what happens next" column (22rem) follows it.
     .vx-task > .vx-task__grid > .vx-card.vx-task__card + .vx-card.vx-card--dashed.vx-task__next
   Inside the card: __head (h1 + one line), __row (a line with its action at
   the end), __back (a tertiary "back" keycap above the head), __stack (stacked
   keycaps; the gold one keeps its ledge room). */
.vx-task{ container:task / inline-size; max-inline-size:var(--vx-w-sheet); margin-inline:auto; padding-block:clamp(16px, 6vh, 56px) 24px; }
.vx-task__grid{ display:grid; gap:16px; align-items:start; }
@container task (min-width:900px){
  .vx-task__grid{ grid-template-columns:var(--vx-w-narrow) minmax(0, 22rem); column-gap:32px; }
}
.vx-task__card{ display:grid; gap:16px; max-inline-size:var(--vx-w-narrow); padding:clamp(20px, 4cqi, 28px); }
.vx-task__card > *{ min-inline-size:0; }
.vx-task__card .vx-form .vx-btn--block{ margin-block-start:4px; }
.vx-task__head{ display:grid; gap:6px; }
.vx-task__row{ display:flex; flex-wrap:wrap; align-items:center; justify-content:space-between; gap:8px; }
.vx-task__row > .vx-btn{ margin-block-end:0; }
.vx-task__back{ justify-self:start; margin-block:-6px -4px; margin-inline-start:-8px; }
.vx-task__stack{ display:grid; gap:8px; }
.vx-task__stack > .vx-btn{ margin-block-end:0; }
.vx-task__stack > .vx-btn--primary{ margin-block-end:4px; }
.vx-task__next{ display:grid; gap:12px; }
.vx-task__next .vx-steps{ grid-template-columns:minmax(0, 1fr); gap:8px; }
.vx-task__next .vx-step{ padding-block:12px; padding-inline:14px; gap:4px; }
.vx-task__next ol:not(.vx-steps){ margin:0; padding-inline-start:1.25em; display:grid; gap:8px; }
.vx-task__next ol:not(.vx-steps):lang(fa){ list-style-type:persian; }   /* the page's own digits on list markers, as to_persian_digits does for text */

/* skip link: clipped, never `left:-9999px` (the RTL scroll origin turns that
   into 10,000px of sideways scroll) */
.vx-skip{ position:absolute; inset-block-start:0; inset-inline-start:0; inline-size:1px; block-size:1px; overflow:hidden;
  clip-path:inset(50%); white-space:nowrap; z-index:200; }
.vx-skip:focus{ inset-block-start:8px; inset-inline-start:8px; inline-size:auto; block-size:auto; overflow:visible; clip-path:none;
  padding-block:8px; padding-inline:12px; background:var(--vx-action-face); color:var(--vx-on-action);
  border:1.5px solid var(--vx-action-edge); border-radius:var(--vx-r-md); font:700 .875rem/1.2 var(--vx-font); text-decoration:none; }
.vx-skip:focus-visible{ outline:2px solid var(--vx-focus); outline-offset:2px; }


/* ── §4.29 app shell (customer dashboard and its siblings) ──────────────── */
html:has(> body.vx-app){ background-color:var(--vx-chrome); }
.vx-app{ background:var(--vx-chrome); font-size:.875rem; line-height:1.25rem; }
.vx-shell{ display:grid; grid-template-columns:var(--vx-nav-w) minmax(0,1fr); min-block-size:100dvh; }

.vx-sidenav{ position:sticky; inset-block-start:0; box-sizing:border-box; block-size:100dvh; min-inline-size:0;
  display:flex; flex-direction:column; gap:8px; padding:8px; background:var(--vx-chrome); }
.vx-sidenav__head{ display:flex; align-items:center; gap:4px; block-size:40px; padding-inline:2px; flex:none; }
.vx-sidenav__head .vx-taskbar__brand{ margin-inline-end:0; }
.vx-sidenav__nav{ display:grid; align-content:start; gap:1px; flex:1 1 auto; min-block-size:0; overflow-y:auto; overscroll-behavior:contain;
  scrollbar-width:thin; margin-inline:-2px; padding-inline:2px; }
.vx-sidenav__label{ padding-block:12px 4px; padding-inline:8px; font:600 .6875rem/1 var(--vx-font);
  letter-spacing:var(--vx-track-label); text-transform:var(--vx-case-label); color:var(--vx-text-3); }
.vx-sidenav__item{ box-sizing:border-box; display:flex; align-items:center; gap:8px; min-block-size:32px; padding-block:0; padding-inline:6px;
  border:1px solid transparent; border-radius:var(--vx-r-md); font:500 .875rem/1.2 var(--vx-font); color:var(--vx-text-2); text-decoration:none;
  transition:background-color var(--vx-dur-fast) var(--vx-ease-standard), color var(--vx-dur-fast) var(--vx-ease-standard); }
.vx-sidenav__item:hover{ background:var(--vx-hover); color:var(--vx-ink); transition:none; }
.vx-sidenav__item[aria-current="page"]{ background:var(--vx-card); border-color:var(--vx-line); color:var(--vx-ink); font-weight:600; box-shadow:0 1px 0 var(--vx-ledge); }
.vx-sidenav__item:focus-visible{ outline:2px solid var(--vx-focus); outline-offset:1px; }
.vx-sidenav__item > span:not([class]){ min-inline-size:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.vx-sidenav__item .vx-count{ margin-inline-start:auto; border-color:var(--vx-chrome); }
.vx-sidenav__foot{ margin-block-start:auto; flex:none; display:flex; align-items:center; gap:4px; padding-block-start:8px; border-block-start:1px solid var(--vx-line-soft); }
.vx-sidenav__foot .vx-btn{ margin-block-end:0; }
.vx-sidenav__foot .vx-lang{ min-inline-size:0; }
.vx-sidenav__foot .vx-lang > summary{ max-inline-size:100%; }
.vx-sidenav__foot .vx-lang__btn > span{ min-inline-size:0; overflow:hidden; text-overflow:ellipsis; }
.vx-sidenav__foot > .vx-acct{ margin-inline-start:auto; }

/* site picker (sidenav, when the page passes sidenav_sites) */
.vx-sitepick{ display:block; flex:none; }
.vx-sitepick > summary.vx-btn{ --vx-btn-edge:var(--vx-line); --vx-btn-edge-hover:var(--vx-line-strong); --vx-btn-face:var(--vx-card);
  justify-content:flex-start; gap:8px; padding-inline:8px; }
.vx-sitepick__label{ min-inline-size:0; overflow:hidden; text-overflow:ellipsis; font-weight:600; color:var(--vx-ink); }
.vx-sitepick__label--none{ font-weight:500; color:var(--vx-text-2); }   /* an account-wide page: no site is selected */
.vx-sitepick > summary > .vx-icon:last-child{ margin-inline-start:auto; color:var(--vx-text-3); transition:rotate var(--vx-dur-fast) var(--vx-ease-standard); }
.vx-sitepick[open] > summary > .vx-icon:last-child{ rotate:180deg; }
.vx-sitepick > .vx-menu{ inset-inline:0; min-inline-size:0; }

/* account menu (sidenav foot, legacy top bar): an avatar keycap-less button */
.vx-acct > summary{ display:inline-flex; align-items:center; gap:4px; padding:2px; padding-inline-end:4px; border:1px solid transparent;
  border-radius:var(--vx-r-md); cursor:pointer; color:var(--vx-text-2); }
.vx-acct > summary:hover{ background:var(--vx-hover); color:var(--vx-ink); }
.vx-acct > summary:focus-visible{ outline:2px solid var(--vx-focus); outline-offset:1px; }
.vx-acct > summary > .vx-icon{ transition:rotate var(--vx-dur-fast) var(--vx-ease-standard); }
.vx-acct[open] > summary > .vx-icon{ rotate:180deg; }
.vx-acct__who{ padding-block:8px 9px; padding-inline:10px; margin-block-end:4px; border-block-end:1px solid var(--vx-line-soft);
  font:500 .75rem/1.3 var(--vx-font); color:var(--vx-text-3); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }

/* the sheet: the page's content surface, one step up from the chrome */
.vx-sheet-wrap{ box-sizing:border-box; padding:6px; padding-inline-start:0; min-inline-size:0; }
.vx-sheet{ display:block; box-sizing:border-box; min-block-size:calc(100dvh - 12px); background:var(--vx-desk); border:1px solid var(--vx-line);
  border-radius:var(--vx-r-lg); overflow:clip; container:sheet / inline-size;
  animation:vx-fade-in var(--vx-dur-fast) var(--vx-ease-out) backwards; }  /* backwards: no transform stays applied */
.vx-scene{ position:sticky; inset-block-start:0; z-index:var(--vx-z-sticky); box-sizing:border-box;
  display:flex; flex-wrap:wrap; align-items:center; gap:8px 12px; min-block-size:var(--vx-scene-h); padding-block:10px; padding-inline:24px;
  background:color-mix(in srgb,var(--vx-desk) 92%,transparent); -webkit-backdrop-filter:blur(12px); backdrop-filter:blur(12px);
  border-block-end:1px solid transparent; transition:border-color var(--vx-dur-panel); }
@media (prefers-reduced-transparency:reduce){ .vx-scene{ background:var(--vx-desk); -webkit-backdrop-filter:none; backdrop-filter:none; } }
.vx-scene.is-stuck{ border-block-end-color:var(--vx-line); }
.vx-scene > .vx-glyph{ flex:none; }
.vx-scene__text{ display:grid; gap:1px; min-inline-size:0; flex:1 1 16rem; }
.vx-scene__title{ margin:0; font:600 1.125rem/1.4 var(--vx-font); letter-spacing:0; color:var(--vx-ink); }
.vx-scene__desc{ margin:0; font:500 .8125rem/1.4 var(--vx-font); color:var(--vx-text-2); max-inline-size:48rem; }
.vx-scene__actions{ margin-inline-start:auto; display:flex; flex-wrap:wrap; align-items:center; gap:8px; }
.vx-scene__actions .vx-btn{ margin-block-end:0; }
[dir="rtl"] .vx-scene__title{ line-height:1.5; }
[dir="rtl"] .vx-scene__desc{ font-size:.84375rem; line-height:1.6; }
.vx-scene__tabs{ box-sizing:border-box; padding-inline:24px; margin-inline:0; position:sticky; inset-block-start:var(--vx-scene-h-live, var(--vx-scene-h));
  z-index:calc(var(--vx-z-sticky) - 1); background:var(--vx-desk); }
/* _base_app.html puts the tabs INSIDE the scene (its last row), so they stick
   with it whatever the scene's height (a wrapped description makes it taller
   than --vx-scene-h, and a separately sticky row would slide under it). The
   tab strip's own hairline is then the scene's bottom edge. */
.vx-scene > .vx-scene__tabs{ position:static; z-index:auto; flex:1 0 100%; margin-inline:-24px; padding-inline:24px; background:none; }
.vx-scene:has(> .vx-scene__tabs){ padding-block-end:0; border-block-end:0; }
.vx-scene__body{ box-sizing:border-box; display:grid; gap:16px; max-inline-size:var(--vx-w-app); padding-block:16px 64px; padding-inline:24px; }
/* a sub-page's way back (My team / northwind.example, Support / a ticket):
   a breadcrumb above the scene, in the base's sheet_top block, on the
   scene's own gutters. It scrolls away; the scene stays. */
.vx-sheet > .vx-crumbs{ padding-block:12px 0; padding-inline:24px; }
@container sheet (max-width:720px){
  .vx-sheet > .vx-crumbs{ padding-inline:12px; }
  .vx-scene, .vx-scene__tabs{ padding-inline:12px; }
  .vx-scene > .vx-scene__tabs{ margin-inline:-12px; }
  .vx-scene__body{ padding-block:12px 64px; padding-inline:12px; }   /* the tab bar's extra room is added only where it shows (≤640, below) */
}

/* < 1024px: the side nav becomes a drawer; a compact bar replaces it */
.vx-appbar{ display:none; }
.vx-appbar__title{ min-inline-size:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font:600 .875rem/1.2 var(--vx-font); color:var(--vx-text-2); }
@media (max-width:1023px){
  .vx-shell{ grid-template-columns:minmax(0,1fr); }
  .vx-sidenav{ position:fixed; z-index:var(--vx-z-modal); inset-block:0; inset-inline-start:0; inline-size:min(280px,86vw); block-size:auto;
    border-inline-end:1px solid var(--vx-line); box-shadow:var(--vx-shadow-float);
    translate:calc(-105% * var(--vx-dir)) 0; visibility:hidden;
    transition:translate var(--vx-dur-open) var(--vx-ease-out-expo), visibility 0s linear var(--vx-dur-open); }
  .vx-sidenav.is-open{ translate:0 0; visibility:visible; transition:translate var(--vx-dur-open) var(--vx-ease-out-expo), visibility 0s; }
  .vx-appbar{ display:flex; align-items:center; gap:8px; box-sizing:border-box; block-size:48px; padding-inline:8px;
    background:var(--vx-chrome); border-block-end:1px solid var(--vx-line); position:sticky; inset-block-start:0;
    z-index:calc(var(--vx-z-nav) - 2); }                          /* under the drawer's scrim (z-nav − 1) */
  .vx-appbar .vx-taskbar__brand{ margin-inline-end:0; }
  .vx-sheet-wrap{ padding:0; }
  .vx-sheet{ border:0; border-radius:0; min-block-size:calc(100dvh - 48px); }
  .vx-scene{ inset-block-start:48px; }
  .vx-scene__tabs{ inset-block-start:calc(48px + var(--vx-scene-h-live, var(--vx-scene-h))); }
}
/* Reduced motion makes EVERY transition 1ms (vx-base: `*{transition-duration:1ms
   !important}`, and transition-property defaults to all), so each link in the
   drawer "transitions" its inherited visibility from hidden for 1ms after the
   drawer opens — and ui.js's focus() into it silently failed (focus stayed on
   the burger, so the focus trap never engaged). Nothing in the drawer needs
   to animate when motion is reduced. */
@media (max-width:1023px) and (prefers-reduced-motion:reduce){ .vx-sidenav, .vx-sidenav *{ transition-duration:0s !important; } }
/* The drawer's focus trap (ui.js) wraps from its LAST focusable element. A
   closed dropdown's menu still has layout boxes in Chrome (::details-content
   is only content-visibility:hidden), so "Log out" inside the closed account
   menu counted as the last stop, was never reached, and Tab walked out of the
   open drawer into the page behind it. Closed side-nav menus are display:none;
   the account summary carries tabindex=0 so the trap counts it (_lab_appbar). */
.vx-sidenav .vx-dropdown:not([open]) > .vx-menu{ display:none; }

/* Focus and anchors land clear of the sticky chrome (WCAG 2.4.11): ui.js §10
   measures what is stuck at the top (app bar, scene, tabs, a preview band)
   and at the bottom (the phone tab bar, a sticky save bar) and writes both
   as inline scroll-padding on <html>. These are the no-JS estimates. */
html:has(> body.vx-app .vx-scene){ scroll-padding-block-start:calc(var(--vx-scene-h) + 16px); }
@media (max-width:1023px){ html:has(> body.vx-app .vx-scene){ scroll-padding-block-start:calc(48px + var(--vx-scene-h) + 16px); } }
@media (max-width:640px){ html:has(> body.vx-app > .vx-tabbar){ scroll-padding-block-end:calc(52px + 16px + env(safe-area-inset-bottom)); } }

/* the phone tab bar */
.vx-tabbar{ display:none; }
@media (max-width:640px){
  .vx-tabbar{ position:fixed; z-index:calc(var(--vx-z-nav) - 2); inset-inline:0; inset-block-end:0; display:grid; grid-auto-flow:column; grid-auto-columns:1fr;
    padding-block-end:env(safe-area-inset-bottom); background:var(--vx-chrome); border-block-start:1px solid var(--vx-line); }
  .vx-tabbar a{ display:grid; justify-items:center; gap:2px; padding-block:8px 6px; padding-inline:4px; font:600 .6875rem/1.2 var(--vx-font);
    color:var(--vx-text-2); text-decoration:none; }
  .vx-tabbar a .vx-icon{ inline-size:20px; block-size:20px; }
  .vx-tabbar a[aria-current="page"]{ color:var(--vx-accent-ink); }
  .vx-tabbar a:focus-visible{ outline:2px solid var(--vx-focus); outline-offset:-2px; }
  [dir="rtl"] .vx-tabbar a{ font-size:.75rem; }
  .vx-app:has(.vx-tabbar) .vx-scene__body{ padding-block-end:calc(88px + env(safe-area-inset-bottom)); }
  .vx-app:has(.vx-tabbar){ --vx-tabbar-h:calc(52px + env(safe-area-inset-bottom)); }   /* sticky save bars ride above it; ui.js §10 writes the MEASURED height on .vx-shell (the estimate left a 1–2px seam) */
}

/* the compact top bar: the PLAIN `{% include 'main/_lab_appbar.html' %}` of a
   page not yet on _base_app.html (manager, property, seeker setup). Visible
   at every width; it reads as the public taskbar's sibling. */
.vx-appbar.vx-appbar--static{ display:flex; position:sticky; inset-block-start:var(--vx-gutter); z-index:var(--vx-z-nav);
  box-sizing:border-box; align-items:center; gap:4px; block-size:var(--vx-bar-h); margin-block:var(--vx-gutter) 16px; padding-inline:6px;
  background:color-mix(in srgb,var(--vx-sheet) 72%,transparent); -webkit-backdrop-filter:blur(20px) saturate(1.2); backdrop-filter:blur(20px) saturate(1.2);
  border:1px solid var(--vx-line); border-radius:var(--vx-r-md); box-shadow:var(--vx-shadow-bar); }
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){ .vx-appbar--static{ background:var(--vx-sheet); } }
@media (prefers-reduced-transparency:reduce){ .vx-appbar--static{ background:var(--vx-sheet); -webkit-backdrop-filter:none; backdrop-filter:none; } }
.vx-appbar--static .vx-taskbar__brand{ margin-inline-end:2px; }
.vx-appbar__nav{ display:flex; align-items:center; gap:2px; min-inline-size:0; overflow-x:auto; scrollbar-width:none; }
.vx-appbar__nav::-webkit-scrollbar{ display:none; }
.vx-appbar__nav .vx-taskbar__link{ gap:6px; }
.vx-appbar__end{ margin-inline-start:auto; display:flex; align-items:center; gap:4px; flex:none; }
@media (max-width:640px){
  .vx-appbar--static .vx-taskbar__brand span{ display:none; }
  .vx-appbar__nav .vx-taskbar__link{ padding-inline:6px; }
}
@media (max-width:420px){ .vx-appbar__nav .vx-taskbar__link > .vx-glyph{ display:none; } }


/* ── the 12-column deck (dashboard gap 16, cockpit 12 via --dense) ───────── */
.vx-deck{ display:grid; grid-template-columns:repeat(12,minmax(0,1fr)); gap:16px; }
.vx-deck > *{ min-inline-size:0; }
.vx-span-12{ grid-column:span 12; }
.vx-span-8 { grid-column:span 8; }
.vx-span-7 { grid-column:span 7; }
.vx-span-6 { grid-column:span 6; }
.vx-span-5 { grid-column:span 5; }
.vx-span-4 { grid-column:span 4; }
.vx-span-3 { grid-column:span 3; }
@container sheet (max-width:1100px){
  .vx-span-8, .vx-span-7, .vx-span-6, .vx-span-5{ grid-column:span 12; }
  .vx-span-4, .vx-span-3{ grid-column:span 6; }
}
@container sheet (max-width:640px){
  .vx-span-4, .vx-span-3{ grid-column:span 12; }
}


/* ── §4.31 footer: a sunken band on the desk, outside the window ─────────── */
.vx-footer{ box-sizing:border-box; margin-block:24px var(--vx-gutter); margin-inline:var(--vx-gutter);
  padding-block:32px 16px; padding-inline:clamp(16px,4vw,48px);
  background:var(--vx-sunken); border:1px solid var(--vx-line); border-radius:var(--vx-r-lg); }
.vx-footer__inner{ max-inline-size:var(--vx-w-sheet); margin-inline:auto; }
.vx-footer__grid{ display:grid; grid-template-columns:1.4fr repeat(3,minmax(0,1fr)); gap:24px; }
@media (max-width:760px){ .vx-footer__grid{ grid-template-columns:1fr 1fr; } .vx-footer__brand{ grid-column:1 / -1; } }
@media (max-width:420px){ .vx-footer__grid{ grid-template-columns:1fr; } }
.vx-footer__brand{ min-inline-size:0; }
.vx-footer__brand .vx-taskbar__brand{ padding-inline:0; margin-inline:0; }
.vx-footer__brand .vx-taskbar__brand:hover{ background:none; }
.vx-footer__brand p{ margin-block:8px 12px; font:500 .8125rem/1.5 var(--vx-font); color:var(--vx-text-2); max-inline-size:22rem; }
.vx-footer__tools{ display:flex; flex-wrap:wrap; align-items:center; gap:6px; }
.vx-footer__tools .vx-btn{ margin-block-end:0; }
.vx-footer__tools .vx-theme-toggle{ border-color:var(--vx-line); }
.vx-footer h2{ margin-block:0 8px; margin-inline:0; font:700 .8125rem/1.2 var(--vx-font); letter-spacing:0; color:var(--vx-ink); }
.vx-footer ul{ list-style:none; margin:0; padding:0; }
.vx-footer a:not(.vx-btn,.vx-menu__item,.vx-taskbar__brand){ display:block; padding-block:3px; font:500 .875rem/1.4 var(--vx-font); color:var(--vx-text-2); text-decoration:none; }
.vx-footer a:not(.vx-btn,.vx-menu__item,.vx-taskbar__brand):hover{ color:var(--vx-ink); text-decoration:underline; }
.vx-footer a:focus-visible{ outline:2px solid var(--vx-focus); outline-offset:2px; border-radius:var(--vx-r-sm); }
.vx-footer__topics{ display:flex; flex-wrap:wrap; align-items:baseline; gap:4px 16px; margin-block-start:24px; padding-block-start:14px;
  border-block-start:1px solid var(--vx-line-soft); }
.vx-footer__topics h2{ margin:0; margin-inline-end:4px; }
.vx-footer .vx-footer__topics a:not(.vx-btn){ display:inline; font-size:.8125rem; }
.vx-footer__base{ display:flex; flex-wrap:wrap; gap:8px 16px; align-items:center; margin-block-start:24px; padding-block-start:14px;
  border-block-start:1px dashed var(--vx-line); font:500 .8125rem/1.4 var(--vx-font); color:var(--vx-text-3); }
.vx-footer .vx-lang .vx-menu{ inset-block-start:auto; inset-block-end:calc(100% + 6px); inset-inline-end:auto; inset-inline-start:0; }


/* ── boot splash (_lab_boot.html): a small window on the desk ──────────────
   Shown only on an installed-app launch or the first page of a session; the
   partial's own script removes it (hard cap 3s). Above everything else. */
.vx-boot{ position:fixed; inset:0; z-index:calc(var(--vx-z-tooltip) + 30); display:grid; place-items:center; padding:24px;
  transition:opacity 420ms var(--vx-ease-standard), visibility 0s linear 420ms; }
.vx-boot.gone{ opacity:0; visibility:hidden; pointer-events:none; }
.vx-boot__card{ inline-size:min(20rem,100%); animation:vx-pop-in var(--vx-dur-open) var(--vx-ease-back) backwards; }
.vx-boot__card .vx-window__bar{ gap:8px; }
.vx-boot__card .vx-window__bar img{ inline-size:18px; block-size:18px; flex:none; }
.vx-boot__body{ display:grid; gap:12px; justify-items:start; padding:20px; }
.vx-boot__step{ margin:0; min-block-size:1.5em; font:500 .875rem/1.5 var(--vx-font); color:var(--vx-text-2); }
.vx-boot__body > .vx-meter{ inline-size:100%; }
.vx-boot__body > .vx-meter > i{ transition-duration:240ms; }


/* ── install guide (_lab_pwa_guide.html): a dialog.vx-modal ─────────────── */
.vx-pwg .vx-modal__head{ align-items:flex-start; }
.vx-pwg__head{ display:grid; gap:6px; justify-items:start; min-inline-size:0; }
.vx-pwg__sub{ margin:0; font:500 .875rem/1.5 var(--vx-font); color:var(--vx-text-2); }
.vx-pwg__steps{ display:none; list-style:none; margin:0; padding:0; counter-reset:vx-pwg; }
.vx-pwg__steps.on{ display:grid; }
.vx-pwg__step{ display:flex; gap:12px; align-items:flex-start; padding-block:10px; border-block-end:1px solid var(--vx-line-soft); }
.vx-pwg__step:last-child{ border-block-end:0; }
.vx-pwg__num{ counter-increment:vx-pwg; flex:none; inline-size:24px; block-size:24px; display:grid; place-items:center; border-radius:var(--vx-r-full);
  font:700 .75rem/1 var(--vx-font-figures); font-variant-numeric:tabular-nums; color:var(--vx-on-accent); background:var(--vx-accent); }
.vx-pwg__num::before{ content:counter(vx-pwg); }
:lang(fa) .vx-pwg__num::before{ content:counter(vx-pwg, persian); }   /* same rule as localized_digits: Persian digits for fa only */
.vx-pwg__txt{ padding-block-start:2px; font:500 .875rem/1.6 var(--vx-font); color:var(--vx-text); }
[dir="rtl"] .vx-pwg__txt{ line-height:1.8; }
.vx-pwg__gl{ display:inline-grid; place-items:center; vertical-align:-6px; inline-size:24px; block-size:24px; margin-inline:2px;
  color:var(--vx-accent-ink); background:var(--vx-sunken); border:1px solid var(--vx-line); border-radius:var(--vx-r-sm); }
.vx-pwg__gl svg{ inline-size:13px; block-size:13px; }
.vx-pwg__note{ margin-block-start:12px; }
.vx-pwg__note:empty{ display:none; }
.vx-pwg__ok{ margin-block-start:12px; }

/* Phones: a stuck scene header (title, description, actions, tabs) ate ~145px
   of a 844px screen under the app bar. Once stuck, the description yields —
   the title, the page's action and the tabs stay — and they share ONE row:
   the title takes what is left (one line, ellipsis: a long domain no longer
   wraps mid-word) and every action but the gold one shrinks to its icon. Its
   words stay its accessible name, and they are back as soon as the scene
   unsticks. (My team stuck at 390px: 124px → 88px; a property: 121px → 52px.)
   A landscape phone (≤500px tall) gets the same compact header (R3: the
   sticky chrome covered 40–60% of an 844×390 screen). */
@media (max-width:640px), (max-height:500px){
  .vx-scene.is-stuck .vx-scene__desc{ display:none; }
  .vx-scene.is-stuck .vx-scene__text{ flex-basis:auto; }
  /* the customer app only (the cockpit's scene keeps its own phone rules) */
  .vx-app:not(.vx-cockpit) .vx-scene.is-stuck .vx-scene__text{ flex:1 1 0; min-inline-size:5rem; }
  .vx-app:not(.vx-cockpit) .vx-scene.is-stuck .vx-scene__title{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  /* a domain title (<bdi dir=ltr>) loses its END, not its start, in an RTL page too */
  .vx-app:not(.vx-cockpit) .vx-scene.is-stuck .vx-scene__title > bdi{ display:inline-block; max-inline-size:100%; vertical-align:bottom; overflow:hidden; text-overflow:ellipsis; }
  .vx-app:not(.vx-cockpit) .vx-scene.is-stuck .vx-scene__actions{ flex-wrap:nowrap; }
  .vx-app:not(.vx-cockpit) .vx-scene.is-stuck .vx-scene__actions > .vx-btn:not(.vx-btn--primary):has(> .vx-icon){ --vx-btn-fs:0px; --vx-btn-px:8px; gap:0; }
  .vx-app:not(.vx-cockpit) .vx-scene.is-stuck .vx-scene__actions > .vx-btn:not(.vx-btn--primary):has(> .vx-icon) > :not(.vx-icon){
    position:absolute; inline-size:1px; block-size:1px; overflow:hidden; clip-path:inset(50%); white-space:nowrap; }
}

/* In-page "you are here" (a sidebar that scroll-spies the page's own sections,
   e.g. the product pages): the same card treatment as the current page. */
.vx-sidenav__item[aria-current="location"]{ background:var(--vx-card); border-color:var(--vx-line); color:var(--vx-ink); font-weight:600; box-shadow:0 1px 0 var(--vx-ledge); }

/* ── print: the sheet's content, none of the chrome ─────────────────────────
   (a printed scan report, docs page or invoice view). No taskbar, nav sheet,
   side nav, tab bar or footer; the window loses its float; nothing sticks. */
@media print{
  .vx-taskbar, .vx-navsheet, .vx-footer, .vx-window__actions, .vx-sidenav, .vx-appbar, .vx-tabbar, .vx-skip,
  .vx-toasts, .vx-scrim, .vx-boot, .vx-theme-toggle, [data-lab-install]{ display:none !important; }
  html, body, .vx-desk, body.vx-desk, .vx-app, .vx-sheet{ background:none !important; }
  body.vx-desk::before{ display:none; }
  .vx-page{ padding:0; }
  .vx-window{ box-shadow:none; border:0; border-radius:0; }
  .vx-shell{ display:block; }
  .vx-sheet-wrap{ padding:0; }
  .vx-sheet{ border:0; border-radius:0; min-block-size:0; }
  .vx-scene, .vx-scene__tabs{ position:static; backdrop-filter:none; -webkit-backdrop-filter:none; }
}
