:root{
  --primary:#3151af;
  --primary-dark:#28428f;
  --secondary:#f59e0b;
  --primary-soft:rgba(30,58,138,.1);
  --primary-soft-strong:rgba(30,58,138,.18);
  /* Secondary (accent) soft washes — used for chip/tab hover states so the
     interaction colour matches the active fill family. The JS branding
     applier overrides --secondary at runtime but leaves these alone; the
     wash colours are intentionally derived from a static rgba of the
     default accent so they read as a neutral lightening rather than a
     hue-specific tint. Brands that want a strong accent hover can
     override these vars in admin-app.js applyBranding (TODO). */
  --secondary-soft:rgba(245,158,11,.10);
  --secondary-soft-strong:rgba(245,158,11,.18);
  /* Accent at 50% — legacy; form controls use --input-border at rest. */
  --secondary-border:rgba(254,174,6,.50);
  /* Default rest border for input / select / textarea / Tom Select. */
  --input-border:rgba(0,0,0,.30);
  /* Text/icon colour on filled accent (active chip/tab). Default is dark
     warm brown that reads well on the default amber accent; brands with
     a vivid/dark accent (deep blue, red) should override to white via
     Settings → branding. */
  --secondary-contrast:#422006;
  /* Glow shadow used under active chip/tab pills. Centralised so all
     surfaces share the same drop-shadow. */
  --secondary-glow:0 2px 8px rgba(245,158,11,.28);
  /* Focus ring on form controls (3px accent @ 30%). */
  --secondary-ring:0 0 0 3px rgba(245,158,11,.30);
  --bg-page:#edeef2;
  /* Scrollbar — darker wash of page background (follows white-label bg). */
  --scrollbar-track:color-mix(in srgb, var(--bg-page) 88%, #000);
  --scrollbar-thumb:color-mix(in srgb, var(--bg-page) 55%, #000);
  --scrollbar-thumb-hover:color-mix(in srgb, var(--bg-page) 40%, #000);
  /* Sticky nav height — tree pane starts below it. */
  --admin-nav-h:56px;
  --bg-modal:var(--bg-page);  /* modals share the page bg */
  --bg-soft:rgba(255,255,255,.45); /* table headers \u2014 white wash makes header LIGHTER than bg */
  --bg-soft-strong:rgba(255,255,255,.65); /* th hover, sortable hover */
  --bg-soft-row:rgba(255,255,255,.30);   /* tr:hover td (slightly lighter than bg) */
  --side-tabs-bg:var(--secondary);  /* accent rail */
  /* Inactive side-tab labels and hover wash. Derived from --secondary-contrast
     so they always stay legible on the branded accent rail (light contrast on
     vivid accents, dark contrast on pale/amber accents). The active item gets
     a solid white pill on top of the rail and keeps its own dark text. */
  --side-tab-text:color-mix(in srgb, var(--secondary-contrast) 85%, transparent);
  --side-tab-text-hover:var(--secondary-contrast);
  --side-tab-hover-bg:color-mix(in srgb, var(--secondary-contrast) 12%, transparent);
  --side-tab-text-muted:#8d8a82; /* legacy neutral fallback for non-rail labels */
  --border-soft:rgba(0,0,0,.08);    /* hairline on tinted surfaces */
  --border-table:rgba(0,0,0,.05);   /* in-table row dividers, lighter */

  /* ── Typography stack ──────────────────────────────────────
     Roboto / Roboto Mono / Roboto Condensed are served locally from
     /fonts/roboto.css (see admin.html <head>). System fallbacks kick in
     while the WOFF2 is still loading (font-display:swap). */
  --font-sans:'Inter',system-ui,-apple-system,'Segoe UI',sans-serif;
  --font-mono:'JetBrains Mono',ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  --font-condensed:'Fira Sans Condensed','Inter',system-ui,-apple-system,sans-serif;

  /* ── Label / muted-text colour ─────────────────────────────
     Single source of truth for <label>, .info-label, .hint-sm,
     `.muted` and similar secondary text. Previously hard-coded
     #64748b / #475569 (slate-500/600 — readable but visibly cool).
     Default is a neutral near-black so brands with non-blue palettes
     don't get a clashing slate tint; override here to bind it to
     a brand-derived hue if desired (e.g. color-mix(in srgb,var(--primary) 30%,#111)). */
  --label-color:#111111;
  --label-color-strong:#000000;   /* for label text in dense edit forms */
  --label-color-soft:#3a3a3a;     /* for very light muted captions */

  /* ── Tree pane (dark rail) ─────────────────────────────────
     Defaults match slate-900 / slate-800 / slate-200. Branding JS
     overrides these from --primary hue when a custom primary is set
     (see _treeSurfacesFromPrimary in admin-app.js). */
  --tree-pane-bg:#0f172a;
  --tree-pane-bg-elev:#1e293b;
  --tree-pane-text:#e2e8f0;
  --tree-pane-border:rgba(255,255,255,.12);
  --tree-pane-input-bg:rgba(255,255,255,.08);
  --tree-pane-input-bg-focus:rgba(255,255,255,.12);
  --tree-pane-input-border:rgba(255,255,255,.15);
}
/* Page scrolling is the browser default: <html> grows with content and the
   viewport's own scrollbar handles overflow. We deliberately do NOT pin
   html/body to 100vh + overflow-y:auto (would create a nested scroller +
   double scrollbar) and do NOT reserve scrollbar-gutter (the visible jump
   when the scrollbar appears can't be fully hidden with a gutter — the
   gutter shifts content the same amount, just in advance — and the
   reservation produces an empty band on short pages that looks worse). */
body{background:var(--bg-page);color:#0f172a;font-family:var(--font-sans)}
*{box-sizing:border-box;margin:0;padding:0;font-family:var(--font-sans)}
/* Monospace utility — applied via class on cells/spans that show codes,
   UUIDs, timestamps, gateway IDs, etc. (replaces inline font-family:monospace). */
.mono,code,kbd,samp,pre,.font-mono{font-family:var(--font-mono)}
/* Condensed utility — narrower, used for dense labels, captions,
   chip/legend text and table column headers where horizontal space is tight. */
.font-condensed,.cond{font-family:var(--font-condensed)}
/* Slim scrollbars — tinted from page background so white-label bg stays consistent. */
*{scrollbar-width:thin;scrollbar-color:var(--scrollbar-thumb) var(--scrollbar-track)}
*::-webkit-scrollbar{width:8px;height:8px}
*::-webkit-scrollbar-track{background:var(--scrollbar-track);border-radius:999px}
*::-webkit-scrollbar-thumb{background:var(--scrollbar-thumb);border-radius:999px;border:2px solid rgba(0,0,0,0)}
*::-webkit-scrollbar-thumb:hover{background:var(--scrollbar-thumb-hover)}
/* ─── Public-site header (login screen only) ──────────────────────────────
   Mirrors the header used by webPages.ts layout(); colored with brand
   primary so it visually matches the public catalog. */
.public-bar{display:flex;align-items:center;gap:10px;padding:8px 20px;background:var(--primary);color:#fff;box-shadow:0 2px 8px rgba(0,0,0,.08);position:sticky;top:0;z-index:40;min-height:52px}
.pb-brand{display:inline-flex;align-items:center;gap:10px;color:#fff;text-decoration:none;font-weight:700;flex:0 0 auto;min-width:0;margin-right:auto}
.pb-logo{height:32px;width:auto;max-width:160px;object-fit:contain;display:block}
.pb-name{font-size:16px;letter-spacing:.2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-weight:800}
.pb-nav{display:flex;align-items:center;justify-content:center;gap:4px;min-width:0;flex-wrap:wrap}
.pb-link{color:rgba(255,255,255,.92);text-decoration:none;padding:8px 14px;border-radius:999px;font-size:13px;font-weight:600;line-height:1;white-space:nowrap;transition:background .15s,color .15s}
.pb-link:hover{background:rgba(255,255,255,.18);color:#fff}
.pb-link.active{background:rgba(255,255,255,.26)}
.pb-actions{display:flex;align-items:center;gap:8px;flex:0 0 auto;margin-left:auto}
.pb-burger{display:none;background:rgba(255,255,255,.18);color:#fff;border:none;border-radius:999px;width:36px;height:36px;align-items:center;justify-content:center;cursor:pointer;padding:0;font-size:14px}
.pb-burger:hover{background:rgba(255,255,255,.28)}
.pb-lang .pb-lang-btn{background:rgba(255,255,255,.16);color:#fff;border-radius:999px;padding:7px 12px;font-size:12px;font-weight:700;height:36px}
.pb-lang .pb-lang-btn:hover,.pb-lang .pb-lang-btn.open{background:rgba(255,255,255,.28)}
.pb-lang .nav-dropdown-menu{background:#fff;color:#0f172a;right:0;border-radius:12px;padding:4px;box-shadow:0 8px 24px rgba(0,0,0,.20);min-width:160px}
.pb-lang .nav-dropdown-menu .nav-btn{color:#0f172a;background:transparent;border-radius:8px;text-align:left;padding:8px 10px}
.pb-lang .nav-dropdown-menu .nav-btn:hover{background:var(--primary-soft);color:var(--primary)}
/* Below 720px the primary nav collapses behind the burger toggle. */
@media (max-width:720px){
  .pb-burger{display:inline-flex}
  .public-bar{flex-wrap:wrap;row-gap:8px}
  .pb-nav{display:none;order:3;flex-basis:100%;flex-direction:column;align-items:stretch;justify-content:flex-start;gap:2px;background:rgba(0,0,0,.12);border-radius:12px;padding:6px;margin-top:4px}
  .pb-nav.open{display:flex}
  .pb-link{text-align:left;padding:10px 14px}
}

.login-wrap{display:flex;flex-direction:column;justify-content:flex-start;align-items:center;padding:48px 16px;gap:18px}
.login-card{background:#fff;padding:40px;border-radius:16px;box-shadow:0 8px 32px rgba(0,0,0,.1);width:380px;max-width:100%}
.login-card h1{font-size:22px;color:var(--primary);margin-bottom:6px;display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.login-card h1 .login-brand-logo{height:42px;width:auto;max-width:200px;object-fit:contain;display:inline-block;vertical-align:middle;border-radius:6px}
.login-card h1 .login-brand-text{font-weight:700;line-height:1}
.login-card p{font-size:13px;color:var(--label-color-soft);margin-bottom:24px}
.login-card p.login-hint{margin:6px 0 18px}
.login-tabs{display:flex;gap:4px;background:#f1f5f9;border-radius:10px;padding:4px;margin:14px 0 18px}
.login-tab{flex:1;background:transparent;border:none;padding:8px 10px;border-radius:8px;font-size:13px;font-weight:600;color:var(--label-color-soft);cursor:pointer;display:flex;align-items:center;justify-content:center;gap:6px;transition:background .18s,color .18s}
.login-tab:hover{color:var(--secondary);background:var(--secondary-soft)}
.login-tab.active{background:var(--secondary);color:var(--secondary-contrast);box-shadow:var(--secondary-glow)}
.login-pane[hidden]{display:none}
/* Email+password vs Phone+SMS — chip-style toggle (replaces old underline subtabs).
   Uses the accent (--secondary) colour for the active state, mirroring
   the .tabbed-form .tab-btn pattern used in admin modals. */
.login-method-chips{display:flex;flex-wrap:wrap;gap:6px;margin:0 0 12px}
.chip-method{background:#fff;border:1.5px solid var(--border-soft);color:var(--label-color-strong);border-radius:999px;padding:6px 12px;font-size:12px;font-weight:600;cursor:pointer;display:inline-flex;align-items:center;gap:6px;line-height:1;transition:all .15s}
.chip-method:hover{background:var(--secondary-soft);border-color:var(--secondary-soft-strong);color:var(--secondary)}
.chip-method.active{background:var(--secondary);border-color:var(--secondary);color:var(--secondary-contrast);box-shadow:var(--secondary-glow)}
.chip-method.active:hover{filter:brightness(.96)}
.chip-method.active i{color:#fff}
/* Inline App Store / Google Play badge row on the Client login pane —
   uses the same `.store-row` / `.store-btn` / `.store-badge` classes that
   webPages.ts renders for product/point pages, so badges look identical
   across surfaces. Sized small (32px) so they sit comfortably inside the
   login card. */
.store-row{display:flex;gap:10px;margin-top:10px;flex-wrap:wrap;justify-content:center;align-items:center}
.store-btn{display:inline-block;line-height:0;border-radius:8px;overflow:hidden;text-decoration:none;transition:transform .15s,box-shadow .15s}
.store-btn:hover{transform:translateY(-1px);box-shadow:0 4px 10px rgba(15,23,42,.12);filter:none}
.store-badge{display:block;height:32px;width:auto}
/* HTML5 `[hidden]` is `display:none` only in the UA stylesheet; explicit
   `display: flex/inline-flex` on classes like `.btn` overrides it. Force
   the hidden semantics so e.g. the SMS-mode "Send code" / "Login" buttons
   actually swap places instead of stacking. */
[hidden]{display:none!important}
/* Smooth open/close for <details> across the panel.
   Uses CSS animation on the inner content; fallback for browsers without
   `interpolate-size: allow-keywords` is still acceptable (instant close). */
@keyframes bbDetailsSlideDown{
  from{opacity:0;transform:translateY(-4px);max-height:0}
  to  {opacity:1;transform:translateY(0);   max-height:1200px}
}
details > summary{cursor:pointer;list-style:none;-webkit-tap-highlight-color:transparent;transition:color .2s}
details > summary::-webkit-details-marker{display:none}
details > summary::after{content:"\f078";font-family:"Font Awesome 6 Free";font-weight:900;font-size:11px;color:var(--muted);float:right;margin-top:3px;transition:transform .25s ease}
details[open] > summary::after{transform:rotate(180deg)}
details[open] > *:not(summary){animation:bbDetailsSlideDown .28s ease both;overflow:hidden}
label{font-size:12px;font-weight:600;color:var(--label-color);text-transform:uppercase;letter-spacing:.5px;display:block;margin-bottom:5px;font-family:var(--font-condensed)}
input,select,textarea{
  width:100%;padding:2px 8px;
  border:2px solid var(--input-border);
  border-radius:7px;font-size:13px;margin-bottom:8px;outline:none;
  transition:border-color .15s,box-shadow .15s;
  background:#fff;color:#0f172a;
}
.nav-filters input,.nav-filters select,.nav-filters textarea{margin-bottom:0}
/* Inside tables, never carry the form-field margin-bottom — it shifts
   single inline controls (e.g. status-select in the bookings table). */
table input,table select,table textarea{margin-bottom:0}
/* `<label class="btn">` is used as an icon-only file picker in the chat
   composer; the global `label{margin-bottom:5px}` rule pushes it down,
   so wipe it out for buttons. */
label.btn{margin-bottom:0;text-transform:none;letter-spacing:0;font-weight:600;color:inherit;font-size:13px}
input:not([type=checkbox]):not([type=radio]),select{height:30px}
input[type=checkbox],input[type=radio]{
  width:auto;margin:0;padding:0;vertical-align:middle;
  accent-color:var(--secondary);
  border:none;box-shadow:none;
}
input:not([type=checkbox]):not([type=radio]):hover,
select:hover,
textarea:hover{border-color:var(--secondary)}
input:not([type=checkbox]):not([type=radio]):focus,
select:focus,
textarea:focus{
  border-color:var(--secondary);
  box-shadow:var(--secondary-ring);
}
textarea{resize:vertical;min-height:70px;height:auto;padding:6px 9px;line-height:1.4;border-radius:7px}
.btn{padding:6px 12px;border:none;border-radius:7px;cursor:pointer;font-size:13px;font-weight:600;transition:.15s;display:inline-flex;align-items:center;justify-content:center;gap:5px;line-height:1.2;min-height:30px;white-space:nowrap}
.btn:hover{filter:brightness(.93)}
.btn-primary{background:var(--primary);color:#fff}
.btn-green{background:#10b981;color:#fff}
.btn-orange{background:var(--secondary);color:#fff}
.btn-red{background:#ef4444;color:#fff}
.btn-ghost{background:transparent;color:var(--label-color-strong)}
.btn-ghost:hover{background:rgba(15,23,42,.06);filter:none}
/* Segmented toggle group used in filter bars (журнал шлюзу: «Усі» /
   «Тільки помилки»; can be reused elsewhere). Apply `.btn-toggle` to
   each button in the group; add `.active` to the chosen one. Visual
   language reuses the existing secondary accent so it matches sub-tabs. */
.btn-toggle{background:var(--secondary-soft);color:var(--label-color-strong);border:1px solid var(--secondary-soft-strong);font-weight:500}
.btn-toggle:hover{background:var(--secondary-soft-strong);filter:none}
.btn-toggle.active{background:var(--secondary);color:var(--secondary-contrast);border-color:var(--secondary);box-shadow:var(--secondary-glow);font-weight:600}
.btn-toggle.active:hover{background:var(--secondary);filter:brightness(.96)}
.toggle-group{display:inline-flex;border-radius:13px;overflow:hidden}
.toggle-group .btn-toggle{border-radius:0;border-right-width:0}
.toggle-group .btn-toggle:first-child{border-top-left-radius:13px;border-bottom-left-radius:13px;padding-left:12px}
.toggle-group .btn-toggle:last-child{border-right-width:1px;border-top-right-radius:13px;border-bottom-right-radius:13px;padding-right:12px}
/* Destructive icon button used everywhere a trash/remove control sits in
   a list, a card, or a footer-left slot. Replaces ad-hoc inline styles
   (background:transparent;color:#dc2626) and adds a soft hover wash so
   the affordance is consistent across the admin app. */
.btn-trash{background:transparent;color:#dc2626;border-color:transparent}
.btn-trash:hover{background:rgba(220,38,38,.10);color:#b91c1c;filter:none}
.btn-trash:active{background:rgba(220,38,38,.16)}
.btn-trash:disabled{color:#94a3b8;cursor:not-allowed;background:transparent}
.btn-sm{padding:3px 8px;font-size:12px;border-radius:6px;min-height:26px}
.btn-icon{padding:0;min-width:30px;width:30px;height:28px;min-height:28px;font-size:13px;display:inline-flex;align-items:center;justify-content:center}
.btn-icon.btn-fixed{min-width:42px;width:42px;justify-content:center}
.btn-icon .fa-ic{margin:0;width:auto;text-align:center;font-size:14px;line-height:1}
.btn-full{width:100%;justify-content:center}
/* Booking status pill — reused as <span> badge and <select> control.
   Color (bg + fg) is set inline per status; everything else lives here.
   Sized to match .btn (min-height 28px, border-radius 7px) so it visually
   aligns next to action buttons in row clusters and modal headers. No
   borders ever — the inline bg/fg pair already provides enough contrast. */
.status-pill{display:inline-flex;align-items:center;gap:5px;font-size:12px;padding:4px 10px;border:none;border-radius:7px;font-weight:600;line-height:1.2;vertical-align:middle;min-height:28px;box-sizing:border-box}
/* Native <select> variant — opted out of Tom Select so the inline
   background/color is preserved (TS doesn't copy inline styles to its
   wrapper). Same height as <span> variant for visual parity. */
select.status-pill{cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none;padding-right:22px;border:none;margin:0;min-height:28px;height:28px;line-height:1;font-family:inherit;background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'><path fill='none' stroke='%23000' stroke-opacity='.45' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round' d='M1 1l4 4 4-4'/></svg>");background-repeat:no-repeat;background-position:right 8px center}
.status-pill.sp-lg{font-size:13px;padding:5px 10px;min-height:30px}
select.status-pill.sp-lg{height:30px;padding-right:22px;background-position:right 8px center}
/* Muted modifier for pills shown OUTSIDE primary order/prebooking tables —
   e.g. cancelled/expired statuses in the Cells table where the cell-level
   row context (and the cell itself) is the primary subject, not the booking.
   Lowers visual weight without losing the color/icon hint. */
.status-pill.sp-muted{opacity:.55;filter:saturate(.55)}
.status-pill.sp-muted:hover{opacity:.85;filter:saturate(.85)}
/* Icon-only mini variant — for queued/future bookings shown next to the
   primary order pill (chronological queue). Just the icon, same colored
   pill background, fixed square footprint. */
.status-pill.sp-icon{padding:0;width:22px;min-height:22px;height:22px;font-size:11px;justify-content:center;gap:0}
.status-pill.sp-icon .fa-ic{margin:0}
nav{background:var(--primary);padding:4px 24px;display:flex;align-items:center;min-height:56px;height:auto;gap:4px;position:sticky;top:0;z-index:50;flex-wrap:wrap;row-gap:6px}
.nav-brand{color:#fff;font-weight:600;font-size:8px;margin-right:auto;cursor:pointer;user-select:none}
.nav-brand #navBrandText{font-size:17px;}
.nav-brand:hover{opacity:.85}
.nav-brand img{max-height:48px;width:auto;max-width:162px;vertical-align:middle;margin-right:8px;border-radius:4px}
.nav-btn{background:transparent;border:none;color:#fff;padding:8px 12px;border-radius:17px;cursor:pointer;font-size:13px;font-weight:600;transition:.15s}
.nav-btn:hover{background:rgba(255,255,255,.2);color:#fff}
.nav-btn.active{background:#fff;color:#000}
.nav-logout{color:rgb(255 255 255 / 30%) !important;}
.nav-logout:hover{background:rgba(255,255,255,.2)!important;color:#fff!important}
.nav-dropdown{position:relative;display:inline-block}
.nav-dropdown-trigger{background:transparent;border:none;color:#fff;padding:8px 12px;border-radius:17px;cursor:pointer;font-size:13px;font-weight:500;transition:.15s;display:inline-flex;align-items:center;gap:4px}
.nav-dropdown-trigger:hover,.nav-dropdown-trigger.open{background:rgba(255,255,255,.2);color:#fff}
.nav-dropdown-menu{display:none;position:absolute;top:calc(100% + 6px);right:0;background:var(--primary-dark);border-radius:18px;padding:4px;min-width:max-content;white-space:nowrap;z-index:100;box-shadow:0 8px 24px rgba(0,0,0,.35);flex-direction:column;gap:2px}
.nav-dropdown-menu.show{display:flex}
.nav-dropdown-menu .nav-btn{width:100%;text-align:left;border-radius:14px;display:flex;align-items:center;gap:8px;white-space:nowrap}
/* Notification bell dropdown uses the dark primary background — its content
   (bell rows built from JS strings) has no explicit color so it would
   inherit from `body` (dark slate) and become unreadable. Force a light
   palette here, override `.muted` for the timestamp / empty-state text, and
   soften the row separators so they blend with `--primary-dark`. */
#bellDropdownMenu{color:#fff}
#bellDropdownMenu .muted{color:rgba(255,255,255,.72)}
#bellDropdownMenu .bell-item{color:#fff;border-bottom-color:rgba(255,255,255,.12) !important;background:transparent !important}
#bellDropdownMenu .bell-item:hover{background:rgba(255,255,255,.08) !important}
#bellDropdownMenu .bell-item:last-child{border-bottom:0 !important}
/* Per-row "hide" (×) affordance. Kept low-contrast until hover so the
   glyph never competes with the item label; icon-only, no border, so
   it reads as a control rather than a button. */
#bellDropdownMenu .bell-dismiss{background:transparent;border:0;color:rgba(255,255,255,.55);cursor:pointer;padding:2px 6px;border-radius:6px;font-size:12px;line-height:1;align-self:flex-start;margin-left:4px}
#bellDropdownMenu .bell-dismiss:hover{color:#fff;background:rgba(255,255,255,.14)}
#bellDropdownMenu .bell-dismiss:focus-visible{outline:2px solid rgba(255,255,255,.5);outline-offset:1px}
/* Top-nav buttons get a small fixed-width icon column so labels align. */
.nav-btn .fa-ic,.nav-dropdown-trigger .fa-ic{width:14px;font-size:10px;line-height:16px;vertical-align:top;text-align:center;display:inline-block;margin-right:0;flex-shrink:0}
/* Hide leading icons everywhere in the top nav by default — labels read
   fine on their own and the duplicated glyphs added visual noise. Show
   them ONLY when the icon explicitly opts in with `.fa-ic-only` (e.g.
   the refresh button), or for the trailing dropdown chevron.
   NB: `:only-child` is the wrong tool here — a text-node sibling (the
   button's label “Точки”) is not an element child, so an icon
   with sibling text still matches `:only-child`. Hence the explicit class. */
.nav-brand .fa-ic,
.nav-btn .fa-ic,
.nav-dropdown-trigger .fa-ic{display:none}
.nav-btn .fa-ic.fa-ic-only,
.nav-dropdown-trigger .fa-ic.fa-ic-only,
.nav-dropdown-trigger .fa-chevron-down{display:inline-block}
/* Inside the dropdown menu the icons would only add visual noise — hide them. */
.nav-dropdown-menu .nav-btn .fa-ic{display:none}
/* App shell — outer wrapper that used to live on `main`. Now also hosts the
   left tree-pane, so max-width/margin/padding moved out of `main`. */
.app-shell{margin:20px auto;padding:0 16px 40px}
.app-shell>main.content-pane{padding:0;margin:0;max-width:none}
main{max-width:1440px;margin:20px auto;padding:0 16px 40px}
.card{background:#fff;border-radius:18px;padding:18px 20px;box-shadow:0 1px 4px rgba(0,0,0,.06);margin-bottom:16px;overflow:hidden}
.card-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:14px;flex-wrap:wrap;gap:10px;min-height:26px}
.card-head h2{font-size:16px;font-weight:700;display:inline-flex;align-items:center;gap:8px}
/* «+ Add» buttons sit directly to the RIGHT of a card title (and inside
   table heads). Always green with the plus glyph. The btn-add helper class
   normalises this so renderers don't need to spell out btn-green + size +
   inline icon every time. It is self-contained — works whether or not the
   element also carries the base `.btn` class. */
.btn-add{background:#16a34a;color:#fff;border:none;outline:none;cursor:pointer;padding:4px 10px;font-size:12px;font-weight:600;font-family:inherit;border-radius:7px;min-height:26px;display:inline-flex;align-items:center;justify-content:center;gap:5px;line-height:1;white-space:nowrap;transition:background .15s,box-shadow .15s;text-decoration:none;vertical-align:middle}
.btn-add:hover{background:#15803d;filter:none}
.btn-add:focus-visible{box-shadow:0 0 0 2px rgba(22,163,74,.35)}
.btn-add:disabled{background:#94a3b8;cursor:not-allowed;opacity:.7}
.btn-add .fa-ic,.btn-add i{margin:0;font-size:11px}
.card-head h2 .btn-add{margin-left:4px}
/* btn-add inside the .actions cluster of a row should not push against ghost
   icons (which sit at min-height 26px too). */
.actions .btn-add{min-height:26px}
/* Tables on pages: rounded edges via separated borders + first/last row tweaks. */
.card table{width:100%;border-collapse:separate;border-spacing:0;font-size:12.5px;table-layout:auto;border-radius:10px;overflow:hidden}
table{width:100%;border-collapse:collapse;font-size:12.5px;table-layout:auto}
th{background:var(--bg-soft);text-align:left;padding:7px 9px;color:var(--label-color);font-weight:500;border-bottom:1px solid var(--border-soft);white-space:nowrap;font-size:8.5px;text-transform:uppercase;letter-spacing:.3px;font-family:var(--font-condensed)}
/* Sticky header inside the main listing area */
#mainContent table thead th{position:sticky;top:0;z-index:2}
/* Sortable columns (auto-applied to most cells; opt-out via th.no-sort) */
#mainContent table th.sortable{cursor:pointer;user-select:none;position:sticky}
#mainContent table th.sortable:hover{background:var(--bg-soft-strong);color:#0f172a}
#mainContent table th.sortable .sort-arr{display:inline-block;width:10px;margin-left:3px;color:#94a3b8;font-size:9px}
#mainContent table th.sortable.sort-asc .sort-arr,#mainContent table th.sortable.sort-desc .sort-arr{color:#0f172a}
td{padding:6px 9px;border-bottom:1px solid var(--border-table);vertical-align:middle}
td .actions{flex-wrap:nowrap;white-space:nowrap}
/* In modals, the rightmost cell traditionally holds row actions (pencil,
   trash, links). Right-align them so they hug the table edge. */
.modal table td:last-child{text-align:right}
.modal table td:last-child .actions{justify-content:flex-end}
th.num,td.num{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap}
/* Numeric cells render in the mono family so amounts line up vertically even
   when columns mix multi-digit totals. Header `th.num` keeps the default UI
   font (Recoleta) — only the numbers themselves switch. */
td.num{font-family:var(--font-mono)}
.pt-map-google img{width:100%!important;height:300px!important;object-fit:cover;border-radius:8px;border:1px solid #e2e8f0;display:block}
.pt-map-google a{display:block}
tr:last-child td{border:none}
tr:hover td{background:var(--bg-soft-row)}
/* Status pill palette — single source of truth, mirrors bookingStatusBadge
   in admin-app.js so the same status looks identical wherever it's rendered
   (table rows, modal tabs, badges in lists, mobile-app hints). */
.badge{display:inline-block;padding:2px 8px;border-radius:99px;font-size:11px;font-weight:500;line-height:1.4;white-space:nowrap;vertical-align:middle}
.b-green{background:#dcfce7;color:#15803d}      /* active */
.b-red{background:#fee2e2;color:#dc2626}        /* cancelled */
.b-yellow{background:#fef3c7;color:#92400e}     /* pending / awaiting_inspection */
.b-blue{background:#dbeafe;color:#1d4ed8}       /* completed */
.b-purple{background:#ede9fe;color:#6d28d9}     /* awaiting_payment */
.b-gray{background:#e5e7eb;color:#374151}       /* expired / neutral */
.b-orange{background:#ffedd5;color:#9a3412}     /* warning / waitlist */
.b-cyan{background:#e0f2fe;color:#0369a1}       /* scheduled / pre-booking */
.b-black{background:#0f172a;color:#fff}          /* total counts (Points/Items) */
/* Fixed-width count pill — keeps "1" the same width as "0" or "99" so
   columns don't shift between values. Compose with .b-* for color. */
.badge-w{min-width:22px;text-align:center;padding-left:4px;padding-right:4px}
/* Action cluster in any right-aligned cell hugs the right edge. Previously
   this only applied inside modals via `.modal table td:last-child .actions`,
   so list-page action columns relied on `ta-r` (text-align only, no flex). */
td.ta-r .actions{justify-content:flex-end}
/* Cell/item type micro-label — replaces inline `font-size:10px;font-weight:500`
   styles that were scattered across the cells table. Color follows the
   neutral label palette. */
.type-mini{font-size:10px;line-height:1.3;font-weight:400;color:var(--label-color)}
/* Inline no-wrap helper for one-line bits inside otherwise-wrapping cells
   (point names under primary titles, e.g.). Avoids forcing the whole cell. */
.nowrap{white-space:nowrap}
/* Right-aligned tabular numeric cell — used in the prices view-mode where
   the values look better hugging the column edge with mono digits. */
.num-cell{text-align:right;font-variant-numeric:tabular-nums;font-family:var(--font-mono)}
.overlay{display:none;position:fixed;inset:0;background:rgba(15,23,42,.5);z-index:200;justify-content:center;align-items:flex-start;padding:60px 16px 20px;overflow-y:auto}
.overlay.show{display:flex}
.modal{background:var(--bg-modal);border-radius:20px;padding:24px;width:100%;max-width:920px;min-width:min(640px,100%);margin:0 auto}
/* Inline modal — rendered directly inside `.content-pane`, no overlay chrome.
   Full width, no drop shadow, no border-radius on outer edges (per user
   request: "без блоков и скруглений"). Inner .info-card / form-card
   surfaces keep their rounded corners as smaller grouping cues. */
.modal.modal-inline{max-width:none;min-width:0;background:transparent;padding:0;border-radius:0;margin:0}
.modal.modal-inline>h3{background:transparent;padding:6px 4px 12px;margin:0 0 12px;border:none;border-bottom:1px solid var(--border-soft);border-radius:0;font-size:15px}
.modal img,.modal .info-card img{max-width:100%;height:auto;box-sizing:border-box}
.modal .lb-img{display:inline-block;max-width:100%}
.modal .info-card .info-photo-row .lb-img{display:inline-block;width:auto}
/* Branded confirm dialog \u2014 narrower modal with no h3 chrome around it. */
.modal-type-confirm{max-width:440px!important}
.modal-type-confirm h3{margin:0 0 14px;padding:0;border:none;background:transparent;border-radius:0;font-size:16px}
/* Edit-modal footer: previously pinned to bottom (position:sticky). We now
   let it sit naturally at the end of the form — feedback was that the
   fixed bar took screen space without adding value on short modals and
   competed with the side-tabs rail. Save/Cancel still reachable by
   scrolling; long forms can be scrolled with the standard overlay scroll. */
.modal-type-edit>.actions:last-child{margin:14px 0 0;padding:0;border-top:none}
/* Header status-chips slot \u2014 small pill row that the view modal can
   inject between the type-badge and the title for at-a-glance state
   (billing_blocked, hard_blocked, grace_until\u2026). */
.modal h3 .modal-h-chips{display:inline-flex;align-items:center;gap:4px;flex-wrap:wrap;margin-right:6px}
/* Generic `.chip` pill — used in the modal header AND inline in tables
   (e.g. operator row status). Standalone class so it doesn't depend on
   any parent selector. Modifier classes below set the palette. */
.chip{display:inline-flex;align-items:center;gap:4px;border-radius:999px;padding:2px 9px;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.3px;line-height:1.6;white-space:nowrap}
.chip-red{background:#fee2e2;color:#b91c1c}
.chip-orange{background:#ffedd5;color:#9a3412}
.chip-amber{background:#fef3c7;color:#92400e}
.chip-green{background:#dcfce7;color:#15803d}
.chip-blue{background:#dbeafe;color:#1d4ed8}
.chip-gray{background:#e5e7eb;color:#374151}
.modal h3{font-size:17px;font-weight:700;margin:-24px -24px 22px;padding:18px 24px 14px;border-bottom:1px solid var(--border-soft);background:#fff;border-radius:20px 20px 0 0;display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.modal h3 .modal-h-actions{margin-left:auto;display:inline-flex;align-items:center;gap:6px}
.modal h3 .modal-h-actions .btn{height:28px;min-height:28px;padding:0 10px;font-size:12px;font-weight:500;border-radius:7px;display:inline-flex;align-items:center;gap:5px}
.modal h3 .modal-h-actions .btn.btn-icon-only{padding:0;width:28px;min-width:28px;justify-content:center}
.modal h3 .modal-back{background:transparent;border:none;cursor:pointer;color:var(--label-color-soft);font-size:16px;padding:0 6px;height:28px;display:inline-flex;align-items:center;border-radius:7px}
.modal h3 .modal-back:hover{background:rgba(15,23,42,.06);color:#0f172a}
/* Wrap bare tables inside modal in a white card so they match info-card blocks.
   We use a hairline border instead of a drop-shadow because these tables are
   often wrapped in `overflow-x:auto` containers that would clip the shadow. */
.modal table{background:#fff;border-radius:12px;border:1px solid var(--border-soft);overflow:hidden;border-collapse:separate;border-spacing:0}
.modal table th:first-child,.modal table td:first-child{padding-left:14px}
.modal table th:last-child,.modal table td:last-child{padding-right:14px}
.modal .info-card table,.modal table.bare{background:transparent;box-shadow:none;border-radius:0}
/* Edit-form labels: lighter, smaller, not uppercase */
.modal .field>label,.modal .form-row .field>label,.card .field>label,.card .form-row .field>label{font-weight:500;font-size:10px;text-transform:none;letter-spacing:0;color:var(--label-color-soft);margin-bottom:4px;font-family:var(--font-condensed)}
.form-row{display:grid;grid-template-columns:1fr 1fr;gap:12px}
.field{margin-bottom:4px}
.tabbed-form .tabs-bar{display:flex;flex-wrap:wrap;gap:6px;margin:-2px -2px 14px;padding:0 2px}
/* Branded chip-style tab buttons: full pill radius, accent (--secondary)
   fill when active, soft accent wash on hover. Mirrors the .chip
   pattern used in the mobile app so admin tabs read as the same control
   across surfaces. */
.tabbed-form .tab-btn{background:transparent;border:1px solid transparent;padding:5px 12px;font-size:12px;font-weight:500;color:var(--label-color-strong);cursor:pointer;border-radius:999px;white-space:nowrap;transition:background .15s,color .15s,border-color .15s,box-shadow .15s}
.tabbed-form .tab-btn:hover{background:var(--secondary-soft);color:var(--secondary)}
.tabbed-form .tab-btn.active{background:var(--secondary);color:var(--secondary-contrast);border-color:var(--secondary);box-shadow:var(--secondary-glow);font-weight:600}
.tabbed-form .tab-btn.active:hover{background:var(--secondary);color:var(--secondary-contrast);filter:brightness(.96)}
/* Fixed-width icon column inside modal tab buttons so multi-line tab bars
   line up regardless of per-icon glyph width. Mirrors the nav-btn rule.
   Slightly tighter than nav buttons — tabs sit inside cards where space is
   tighter and bigger icons read as visually heavy. */
.tabbed-form .tab-btn .fa-ic{display:none}
/* …unless the icon explicitly opts in (e.g. the Debug “wrench” tab has
   no text label and would render blank without this). Text-node siblings
   defeat `:only-child`, so we require an explicit `.fa-ic-only` class. */
.tabbed-form .tab-btn .fa-ic.fa-ic-only{display:inline-block;margin:0;font-size:13px}
/* Side-rail tab buttons — sit on the accent (--secondary) panel.
   Inactive: text in --secondary-contrast (legible on the branded accent,
   light on vivid accents, dark on pale/amber). Hover: translucent contrast
   wash. Active: solid white pill with dark slate text — mirrors the main
   nav active state and stays readable regardless of accent hue. */
.side-tab{background:transparent;border:none;padding:4px 12px;font-size:13px;font-weight:500;color:var(--side-tab-text);cursor:pointer;border-radius:13px;text-align:left;width:100%;display:block;margin:0;transition:background .15s,color .15s}
.side-tab:hover{background:var(--side-tab-hover-bg);color:var(--side-tab-text-hover)}
.side-tab.active{background:#fff;color:#000;font-weight:600;box-shadow:0 1px 4px rgba(0,0,0,.1)}
/* The side-rail sticks to the top of the scrolling overlay so long forms
   keep the tabs reachable without a fixed-position bar that would block
   modal background clicks. Top offset matches the overlay's top padding
   so the rail snaps under the viewport edge, not behind the page nav. */
.side-tabs-block{background:var(--side-tabs-bg);border-radius:18px;padding:8px;min-width:200px;align-self:flex-start;display:flex;flex-direction:column;gap:2px;position:sticky;top:16px;max-height:calc(100vh - 80px);overflow-y:auto}
/* Counters/badges inside side-tab buttons — translucent contrast on accent
   for inactive items; dark fill on the active white pill. */
.side-tab .badge,.side-tab .count{background:color-mix(in srgb, var(--secondary-contrast) 22%, transparent);color:var(--secondary-contrast)}
.side-tab.active .badge,.side-tab.active .count{background:rgba(0,0,0,.12);color:#000}
.tabbed-form .tab-panel{display:none}
.tabbed-form .tab-panel.active{display:block}
/* Edit modals: wrap each tab panel's content in the same card surface that
   read-only info-card blocks use. Skip wrapping panels that are already
   composed of explicit info-card / form sections to avoid double-padding
   (visual stylesheets place those on a flat background). */
.modal-type-edit .tabbed-form>.tab-panel.active{background:#fff;border:1px solid var(--border-soft);border-radius:18px;padding:16px 18px;box-shadow:0 1px 4px rgba(0,0,0,.04)}
.modal-type-edit .tabbed-form>.tab-panel.active>.info-card:only-child,
.modal-type-edit .tabbed-form>.tab-panel.active>.info-card:first-child:last-child{box-shadow:none;padding:0;margin:0;background:transparent}
.modal-type-edit .tabbed-form>.tab-panel.active>.info-card{box-shadow:none}

/* ─── O: form-card panel + grouped sections (2026-06-24) ───
   Used by create/edit modals that don't have tabs (and as the
   building-block for grouped fields inside any modal). Same white
   surface treatment as the active tab panel above so visually a tabbed
   form and a flat form look like the same control. Authors wrap one
   logical group (e.g. "Клієнт", "Тривалість і сума", "Призначення")
   in its own .form-card so groups are visually separated, NOT shoved
   into a single 2-col grid by accident. */
.form-card{background:#fff;border:1px solid var(--border-soft);border-radius:14px;padding:14px 16px;box-shadow:0 1px 4px rgba(0,0,0,.04);margin-bottom:12px}
.form-card:last-child{margin-bottom:0}
.form-card .form-card-title{font-size:12.5px;font-weight:600;color:var(--label-color-strong);text-transform:none;letter-spacing:0;margin:-2px 0 10px;padding-bottom:6px;border-bottom:1px solid var(--border-soft);display:flex;align-items:center;gap:6px;font-family:var(--font-condensed)}
.form-card .form-card-title .fa-ic{font-size:12px;color:var(--label-color-soft);margin:0}
/* When a form-card sits inside a tab-panel it should stay flat: the panel
   already provides the white surface and shadow. */
.tabbed-form .tab-panel.active>.form-card{box-shadow:none;background:transparent;border:none;border-radius:0;padding:0;margin-bottom:14px}
.tabbed-form .tab-panel.active>.form-card>.form-card-title{margin-top:0}

/* Loose fields inside a create/edit modal that are NOT inside a tabbed
   form, .info-card, .form-card or .tab-panel should still sit on a white
   surface. The wrapper `<form class="form-loose">…</form>` (or the same
   class on a `<div>`) opts a non-tabbed modal into auto-styling without
   touching every field. */
.modal-type-edit .form-loose{background:#fff;border:1px solid var(--border-soft);border-radius:14px;padding:14px 16px;box-shadow:0 1px 4px rgba(0,0,0,.04);margin-bottom:12px}

/* ─── checkbox label spacing ───
   A canonical "checkbox + descriptive label" row. Renders the input on
   the LEFT of a flex line so the label text has breathing room (the
   common bug was admins writing `<input>Text` which gives ZERO gap).
   `.cb-row` works inside `.field`, inside `.form-card`, or alone. */
.cb-row{display:flex;align-items:flex-start;gap:8px;padding:6px 0;line-height:1.35;font-size:13px;cursor:pointer}
.cb-row>input[type=checkbox]{margin:2px 0 0;flex:0 0 16px;width:16px;height:16px;cursor:pointer;accent-color:var(--primary)}
.cb-row>.cb-lbl{flex:1;min-width:0}
.cb-row>.cb-lbl>.cb-title{font-weight:500;color:#0f172a;display:block}
.cb-row>.cb-lbl>.cb-hint{display:block;font-size:11px;color:var(--label-color-soft);margin-top:2px;font-family:var(--font-condensed)}
.cb-row.is-disabled{opacity:.45;cursor:not-allowed}
.cb-row.is-disabled>input{pointer-events:none}

/* Grouped sub-fields under a master toggle. When the master cb is OFF
   the cluster dims and stops accepting input — mirrors the per-tab
   .tab-panel[data-activation="off"] behavior but for a smaller subset. */
.cb-cluster{margin:4px 0 8px 24px}
.cb-cluster[data-activation="off"]{opacity:.45;pointer-events:none;filter:saturate(.6)}

/* Tabular-numeric money/price display. Right-aligned, mono digits, no
   currency suffix (operator-level currency selector lives in settings).
   Use on table cells AND on inline price displays inside cards. */
.price{font-variant-numeric:tabular-nums;font-family:var(--font-mono);text-align:right}
.price-cell{text-align:right;font-variant-numeric:tabular-nums;font-family:var(--font-mono)}

/* ─── tgBinding / Telegram claim modal ───
   Replaces the inline-style soup that was sprinkled across tgBinding,
   tgConnect, and the QR / photo-upload modals. Centralizing the rules lets
   us tweak palette in one place. */
.tg-binding{display:flex;align-items:center;gap:10px;padding:8px 12px;border-radius:8px}
.tg-binding--bound{background:#f0fdf4;border:1px solid #bbf7d0}
.tg-binding--bound .tg-icon{color:#16a34a;font-size:18px}
.tg-binding--bound .tg-title{font-weight:600;color:#065f46;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.tg-binding--unbound{background:#f8fafc;border:1px dashed #cbd5e1}
.tg-binding--unbound .tg-icon{color:#0088cc;font-size:18px}
.tg-binding--unbound .tg-label{flex:1;color:var(--label-color);font-size:13px}
.tg-binding .tg-chat-id{font-size:11px;font-family:var(--font-mono)}

.tg-claim-box{background:#f0f9ff;border:1px solid #bae6fd;border-radius:8px;padding:12px 14px;margin:12px 0}
.tg-claim-box .tg-claim-step-h{font-size:13px;color:#0c4a6e;margin-bottom:8px}
.tg-claim-box ol{margin:0 0 0 18px;padding:0;font-size:13px;color:#0c4a6e;line-height:1.7}
.tg-claim-box code.tg-claim-cmd{background:#e0f2fe;padding:1px 6px;border-radius:4px}
.tg-claim-code-wrap{text-align:center;margin:14px 0}
.tg-claim-code-cap{font-size:11px;text-transform:uppercase;letter-spacing:.5px;margin-bottom:4px}
.tg-claim-code{font-size:28px;font-weight:800;letter-spacing:4px;font-family:var(--font-mono);color:#1e40af;user-select:all}
.tg-claim-status{text-align:center;font-size:13px;color:var(--label-color)}
.info-card{display:flex;flex-direction:column;gap:0;background:#fff;border-radius:14px;padding:10px 16px;box-shadow:0 1px 4px rgba(0,0,0,.06);margin-bottom:14px}
.info-card:last-child{margin-bottom:0}
.info-card .info-photo{display:block;max-width:280px;max-height:180px;width:auto;height:auto;object-fit:cover;border-radius:10px;border:1px solid var(--border-soft);margin-bottom:14px}
.info-card .info-photo-row{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:14px}
.info-card .info-photo-row img{width:64px;height:64px;object-fit:cover;border-radius:8px;border:1px solid var(--border-soft)}
.info-card .info-row{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;padding:8px 0;border-bottom:1px solid var(--border-table);font-size:13px}
.info-card .info-row:last-child{border-bottom:none}
.info-card .info-label{color:var(--label-color);font-weight:400;font-size:12px;flex-shrink:0;min-width:120px;font-family:var(--font-condensed)}
.info-card .info-value{color:#0f172a;text-align:right;word-break:break-word}
.info-card .info-row-block{flex-direction:column;align-items:stretch;gap:4px}
.info-card .info-row-block .info-label{min-width:0}
.info-card .info-row-block .info-value{text-align:left;white-space:pre-wrap}
.info-card .info-section-title{font-size:11px;font-weight:700;color:#94a3b8;text-transform:uppercase;letter-spacing:.5px;margin:14px 0 4px;padding-bottom:4px;border-bottom:1px solid var(--border-soft)}
.info-card .info-section-title:first-child{margin-top:0}
/* Two-column card grid (used for the main "as in app" tab) */
.info-grid{display:grid;grid-template-columns:1fr 1fr;gap:14px 14px;margin-bottom:14px}
.info-grid:last-child{margin-bottom:0}
.info-grid>.info-card{margin:0}
@media (max-width:720px){.info-grid{grid-template-columns:1fr}}
/* Photos preview row inside cards / forms */
.info-thumb-row{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:14px}
.info-thumb-row img{width:84px;height:84px;object-fit:cover;border-radius:10px;border:1px solid #e2e8f0;cursor:zoom-in}
/* Static-map preview cap */
.info-map{display:block;width:100%;max-height:180px;object-fit:cover;border-radius:10px;border:1px solid #e2e8f0;margin-bottom:12px}
.info-map-wrap{margin-bottom:12px}
.info-map-wrap img,.info-map-wrap iframe{max-height:180px;width:100%;object-fit:cover;border-radius:10px;border:1px solid #e2e8f0;display:block}
.info-map-thumb{display:inline-block;width:160px;height:100px;border-radius:10px;border:1px solid #e2e8f0;overflow:hidden;cursor:zoom-in;box-shadow:0 1px 4px rgba(0,0,0,.08);margin-bottom:12px}
.info-map-thumb img{width:100%;height:100%;object-fit:cover;display:block}
/* Photos table inside booking/item card */
.photos-tbl{font-size:12px;width:100%}
.photos-tbl td{vertical-align:middle;padding:5px 8px}
.photos-tbl img{width:32px;height:32px;object-fit:cover;border-radius:6px;border:1px solid #e2e8f0;cursor:zoom-in;display:block}
/* Rounded "checkbox" dot used as table-header glyph for an availability column.
   Matches the filled green dot used in cells / availability toggles. */
.rounded-checkbox{display:inline-block;width:14px;height:14px;border-radius:50%;background:#16a34a;border:2px solid #16a34a;vertical-align:middle;box-shadow:inset 0 0 0 2px #fff}
.rounded-checkbox.muted{background:#94a3b8;border-color:#94a3b8}
.err{color:#ef4444;font-size:13px;margin:4px 0 8px}
/* Don't reserve space for an empty `.err` block (chat composer keeps one
   around even when there's nothing to show). */
.err:empty{display:none;margin:0}
/* Muted text: black @ 55% so it reads on white cards, tinted modal bg,
   soft tables alike. Previously hard-coded slate-400 (#94a3b8) which lost
   contrast on the page bg. Keep the legacy hex on the structural rounded
   checkbox (it's a swatch, not body copy). */
.muted{color:rgba(15,23,42,.55);font-size:13px}
/* Softer variant for sub-labels / hints sitting on white surfaces. */
.muted-2{color:rgba(15,23,42,.45)}
.actions{display:flex;gap:4px;align-items:center}
.sel-inline{width:auto!important;margin:0!important;padding:4px 8px!important;font-size:12px!important;height:26px!important}
/* ── Unified filter row (right-aligned: search + selects + dates + reset) ── */
.filter-row{display:flex;align-items:center;gap:6px;flex-wrap:nowrap;overflow-x:auto;overflow-y:hidden;margin-left:auto}
/* Scoped to native form elements ONLY. Tom Select copies the source
   <select>'s classList onto its outer `.ts-wrapper`, so an unscoped
   `.filter-input` would re-apply border + 26px height + 4px padding
   AROUND the visible `.ts-control` and produce a doubled "block in
   block" look with the chevron crammed against the inner border. */
input.filter-input,select.filter-input,textarea.filter-input{height:30px!important;padding:4px 8px!important;font-size:12px!important;border:2px solid var(--input-border)!important;border-radius:7px!important;margin:0!important;background:#fff;color:#0f172a;outline:none;transition:border-color .15s,box-shadow .15s;width:auto}
input.filter-input:hover,select.filter-input:hover,textarea.filter-input:hover{border-color:var(--secondary)!important}
input.filter-input:focus,select.filter-input:focus,textarea.filter-input:focus{border-color:var(--secondary)!important;box-shadow:var(--secondary-ring)!important}
input.filter-input[type=date]{min-width:120px;max-width:150px}
/* View-mode selector (e.g. Базовий / Компактний / Розширений, or
   Cells "Стан / Ціни"). Visually distinct from filter inputs so users
   don't confuse it with a data-filter knob — it changes how the SAME
   data is rendered, not WHICH rows are shown. Subtle dashed border +
   muted bg communicate "presentation control". */
select.view-mode-select{border-style:dashed!important;background:#f8fafc!important;color:var(--label-color-strong)!important;font-style:italic;padding-right:22px!important}
select.view-mode-select:hover{background:#f1f5f9!important;color:#0f172a!important}
.btn-reset{padding:0!important;width:30px;min-width:30px;height:30px;min-height:30px;border-radius:7px;display:inline-flex;align-items:center;justify-content:center;color:var(--label-color-soft)}
.btn-reset:hover{color:#ef4444}
.photo-thumb{width:40px;height:40px;object-fit:cover;border-radius:6px;border:1.5px solid var(--border-soft)}
.upload-area{border:2px dashed var(--border-soft);border-radius:10px;padding:16px;text-align:center;cursor:pointer;transition:.2s;margin-bottom:14px}
.upload-area:hover{border-color:var(--primary);background:#f0f4ff}
.upload-area input[type=file]{display:none}
.upload-progress{font-size:12px;color:var(--label-color-soft);margin-top:6px;font-family:var(--font-condensed)}
.cat-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:10px}
.cat-card{background:var(--bg-soft);border-radius:10px;padding:14px;display:flex;align-items:center;gap:10px;cursor:pointer;border:2px solid transparent;transition:.15s}
.cat-card:hover{border-color:var(--primary)}
.cat-card .cat-icon{font-size:24px}
.cat-card .cat-name{font-size:13px;font-weight:600}
.cat-card .cat-key{font-size:11px;color:#94a3b8}
/* nav-side filter group */
.nav-filters{display:flex;gap:6px;align-items:center;margin-left:auto}
.nav-filters select{min-width:140px;max-width:180px;height:32px;color:#fff;background:rgba(255,255,255,.15);border:1px solid rgba(255,255,255,.3);border-radius:17px;padding:0 9px;font-size:13px;font-weight:500;cursor:pointer;line-height:30px}
.nav-filters select option{color:#0f172a;background:#fff}
/* Geo picker (point edit modal) */
.geo-picker{display:flex;flex-direction:column;gap:8px;margin:6px 0 10px}
.geo-picker .geo-search{display:flex;gap:6px}
.geo-picker .geo-search input{flex:1;margin:0}
.geo-picker .geo-results{max-height:140px;overflow-y:auto;border:1px solid var(--border-soft);border-radius:7px;background:#fff;display:none}
.geo-picker .geo-results.show{display:block}
.geo-picker .geo-results .geo-hit{padding:6px 10px;cursor:pointer;font-size:12.5px;border-bottom:1px solid #f1f5f9}
.geo-picker .geo-results .geo-hit:hover{background:#f1f5f9}
.geo-picker .geo-map{width:100%;height:260px;border-radius:8px;border:1px solid #e2e8f0;background:#f1f5f9}
.geo-picker .geo-coords{font-size:12px;color:var(--label-color-soft);font-family:var(--font-mono)}
/* stats table */
.stats-table{width:100%;border-collapse:collapse;font-size:13px}
.stats-table th{background:var(--bg-soft);text-align:left;padding:7px 10px;border-bottom:1px solid var(--border-soft);color:var(--label-color);font-weight:500;font-size:8.5px;text-transform:uppercase;letter-spacing:.3px;white-space:nowrap;font-family:var(--font-condensed)}
.stats-table td{padding:7px 10px;border-bottom:1px solid var(--border-table)}
.stats-table tr.stats-sec td{background:var(--bg-soft);font-weight:700;color:#0f172a;padding:10px;font-size:12px;text-transform:uppercase;letter-spacing:.5px;border-bottom:1px solid var(--border-soft)}
.stats-table tbody tr:not(.stats-sec):hover{background:var(--bg-soft-row)}
/* FontAwesome — inline icons */
i.fa-solid,i.fa-regular,i.fa-brands{font-style:normal;color:inherit}
.fa-ic{margin-right:6px;width:1em;text-align:center;display:inline-block}
.fa-ic-dot{font-size:.65em;vertical-align:middle}
.fa-ic-only{margin:0;width:auto}
button .fa-ic:only-child,.btn-icon .fa-ic{margin:0}
/* ─── Toasts (non-blocking notifications) ─── */
#toastBox{position:fixed;top:14px;right:14px;z-index:10000;display:flex;flex-direction:column;gap:8px;max-width:380px;pointer-events:none}.toast{pointer-events:auto;background:#fff;border:1px solid #e2e8f0;border-left-width:4px;border-radius:8px;box-shadow:0 6px 18px rgba(0,0,0,.12);padding:10px 12px;font-size:13.5px;line-height:1.35;display:flex;gap:8px;align-items:flex-start;animation:tIn .18s ease-out}
.toast.t-success{border-left-color:#16a34a}
.toast.t-error{border-left-color:#dc2626}
.toast.t-warn{border-left-color:#d97706}
.toast.t-info{border-left-color:#0369a1}.toast .t-ic{font-size:16px;line-height:1.2}
.toast.t-success .t-ic{color:#16a34a}
.toast.t-error .t-ic{color:#dc2626}
.toast.t-warn .t-ic{color:#d97706}
.toast.t-info .t-ic{color:#0369a1}
.toast .t-body{flex:1;min-width:0;word-wrap:break-word}
.toast .t-undo{background:#f1f5f9;border:1px solid #cbd5e1;border-radius:6px;padding:2px 8px;font-size:12px;cursor:pointer;font-weight:600}
.toast .t-undo:hover{background:#e2e8f0}
.toast .t-close{background:transparent;border:0;cursor:pointer;color:#94a3b8;padding:0 2px;font-size:14px}
.toast .t-close:hover{color:#475569}
.toast.t-leaving{animation:tOut .18s ease-in forwards}
@keyframes tIn{from{opacity:0;transform:translateX(20px)}to{opacity:1;transform:translateX(0)}}
@keyframes tOut{to{opacity:0;transform:translateX(20px)}}
/* ─── KB items: smooth expand/collapse without re-render ─── */
.kb-item .kb-body-wrap{display:grid;grid-template-rows:0fr;transition:grid-template-rows .25s ease}
.kb-item.open .kb-body-wrap{grid-template-rows:1fr}
/* Inner is the overflow clipper — must have NO vertical padding of its own,
   otherwise the padding leaks even when the grid track is 0fr. The visible
   padding lives on the .kb-body-content child instead. */
.kb-item .kb-body-wrap > .kb-body-inner{overflow:hidden;min-height:0;padding:0}
.kb-item .kb-body-content{padding:0 12px 12px 36px}
.kb-item.open .kb-chev{transform:rotate(90deg)}
/* First section heading inside the FAQ list: no extra top margin. */
.kb-list h3.kb-section:first-child{margin-top:0}
/* ─── Command palette (Ctrl/Cmd-K) ─── */
#cpOverlay{position:fixed;inset:0;background:rgba(15,23,42,.5);z-index:11000;display:none;align-items:flex-start;justify-content:center;padding-top:80px}
#cpOverlay.show{display:flex}
#cpBox{background:#fff;border-radius:12px;width:560px;max-width:92vw;max-height:70vh;display:flex;flex-direction:column;box-shadow:0 30px 80px rgba(0,0,0,.35);overflow:hidden}
#cpInput{border:0;outline:0;padding:14px 16px;font-size:15px;border-bottom:1px solid #e2e8f0;width:100%;box-sizing:border-box}
#cpList{overflow:auto;padding:6px 0}
#cpList .cp-item{padding:8px 14px;cursor:pointer;display:flex;gap:10px;align-items:center;font-size:13.5px}
#cpList .cp-item .cp-tag{font-size:10px;background:#e0e7ff;color:#3730a3;border-radius:4px;padding:1px 6px;text-transform:uppercase;font-weight:700}
#cpList .cp-item.cp-active,#cpList .cp-item:hover{background:#f1f5f9}
#cpList .cp-empty{padding:18px;text-align:center;color:#94a3b8;font-size:13px}
#cpList .cp-section{padding:6px 14px 2px;font-size:11px;text-transform:uppercase;font-weight:700;color:#94a3b8;letter-spacing:.5px}
/* ─── J: table view modes ─── */
table.view-compact .col-extra,
table.view-compact .col-meta,
table.view-compact .col-finance{display:none}
table.view-standard .col-extra{display:none}
/* extended view shows everything (no rules needed) */

/* ─── K: utility & semantic classes extracted from inline styles (2026-06-08) ─── */

/* Uppercase section heading inside cards / forms */
.h-section{font-size:13px;font-weight:700;color:var(--label-color);text-transform:uppercase;letter-spacing:.5px;margin-bottom:18px;font-family:var(--font-condensed)}
.h-section--mb-10{margin-bottom:10px}
.h-section--mb-14{margin-bottom:14px}
.h-section--inline{margin:14px 0 8px}
.h-section--inline-tight{margin:14px 0 6px}
.h-section--spaced{margin:18px 0 10px}
.h-section--spaced-tight{margin:18px 0 8px}
.h-section--spaced-medium{margin:18px 0 6px}
.h-section--spaced-loose{margin:18px 0 12px}

/* Coloured tip / hint banner inside cards */
.tip{display:block;border-radius:8px;padding:10px 12px;font-size:12px;line-height:1.45;margin-bottom:14px}
.tip-sm{padding:8px 12px;font-size:11px;margin-bottom:10px}
.tip-info   {background:#f0f9ff;color:#0c4a6e}
.tip-warn   {background:#fff7ed;color:#7c2d12}
.tip-yellow {background:#fef9c3;color:#713f12}
.tip-amber  {background:#fef3c7;color:#92400e}
.tip-primary{background:#eef2ff;color:#3730a3}
.tip-success{background:#f0fdf4;color:#065f46}
.tip-cyan   {background:#ecfeff;color:#155e75}
.tip-neutral{background:#f8fafc;color:#475569}
/* Trailing warn note (margin-top instead of margin-bottom) */
.tip-warn-trailing{background:#fff7ed;color:#7c2d12;border-radius:8px;padding:8px 12px;font-size:12px;margin-top:6px}

/* Inline clickable label (table titles that open a detail modal) */
.link-primary{cursor:pointer;color:var(--primary)}
.link-primary--bold{font-weight:600}

/* Small advisory text under a form label */
.hint-sm{font-size:11px;color:var(--label-color-soft);margin-top:4px;font-family:var(--font-condensed)}

/* Soft divider between payment sub-sections (replaces ad-hoc <hr style>) */
.hr-soft{border:none;border-top:1px solid #e2e8f0;margin:10px 0}

/* Empty-state row inside a card list */
.empty-row{text-align:center;padding:24px 0}

/* Stat tile used in dashboard summary cards */
.stat-tile{flex:1;min-width:120px;background:#f8fafc;border:1px solid var(--border-soft);border-radius:8px;padding:10px 12px}

/* Large numeric metric (used in stat tiles) */
.metric-num{font-size:20px;font-weight:800}

/* Text-colour utilities */
.tx-danger      {color:#dc2626}
.tx-danger-soft {color:#ef4444}
.tx-success     {color:#16a34a}
.tx-success-deep{color:#15803d}
.tx-muted-soft  {color:#94a3b8}
.tx-muted-sub   {color:var(--label-color-soft)}

/* Font-size utilities */
.fs-10{font-size:10px;font-family:var(--font-condensed)}
.fs-11{font-size:11px;font-family:var(--font-condensed)}
.fs-12{font-size:12px}
.fs-13{font-size:13px}
.fs-20{font-size:20px}
/* Compact monospaced date/time stamp used in logs, journals, lists, etc.
   Keeps timestamps narrow & visually distinct so they don't crowd the
   actual content next to them. Use together with the JS helper that
   formats `YYYY-MM-DD HH:MM:SS` for column alignment. */
.dt-mono{font-family:var(--font-mono);font-size:11px;white-space:nowrap;font-variant-numeric:tabular-nums}
.dt-mono-sm{font-family:var(--font-mono);font-size:10px;white-space:nowrap;font-variant-numeric:tabular-nums;color:var(--muted,#64748b)}

/* Font-weight utilities */
.fw-6{font-weight:600}
.fw-7{font-weight:700}
.fw-8{font-weight:800}

/* Text-alignment utilities */
.ta-c{text-align:center}
.ta-r{text-align:right}

/* M147: Таблиця "Шлюзи керування" — щоб довгий текст помилки не стискав
   колонку "Назва" до кількох літер. Назва завжди в один рядок (з ...),
   колонка "Помилка" обмежена за шириною й переноситься першою. */
.tbl-gateways{table-layout:auto;width:100%}
.tbl-gateways th, .tbl-gateways td{vertical-align:top}
.tbl-gateways td:nth-child(2){max-width:240px;white-space:normal;word-break:break-word}
.tbl-gateways td:nth-child(2) b{display:inline-block;max-width:100%}
.tbl-gateways .col-err{max-width:240px;width:240px}

/* Layout utilities */
.nowrap{white-space:nowrap}
.cur-p{cursor:pointer}
.f1{flex:1}
.f2{flex:2}
.f-min0{flex:1;min-width:0}
.fixed-200{flex:0 0 auto;max-width:200px}
.f-row{display:flex}
.f-row-c{display:flex;align-items:center}
.f-row-c-gap-6{display:flex;align-items:center;gap:6px}
.gap-6{gap:6px}
.gap-8{gap:8px}

/* Width utilities (only sizes used 4+ times inline) */
.w-full{width:100%}

/* Margin utilities (only sizes used 4+ times inline) */
.mt-4 {margin-top:4px}
.mt-6 {margin-top:6px}
.mt-8 {margin-top:8px}
.mt-10{margin-top:10px}
.mt-12{margin-top:12px}
.mt-14{margin-top:14px}
.mr-6 {margin-right:6px}
/* Safety net: a checkbox immediately followed by text inside a <label> with
   no class should still have breathing room. Without this rule a bare
   `<label><input type=checkbox>Text</label>` renders glued. New code uses
   .cb-row or .mr-6 explicitly, but legacy markup still benefits. */
label > input[type=checkbox]:first-child{margin-right:6px}
.mb-0 {margin-bottom:0!important}

/* Compact table cells (icon / toggle columns) */
.td-toggle{width:40px;text-align:center}
.td-icon  {width:36px;text-align:center}

/* Bordered data-cell (mini table cell with thin border, padding 5/6×10) */
.cell-bordered  {padding:5px 10px;border:1px solid var(--border)}
.cell-bordered-l{padding:6px 10px;text-align:left;border:1px solid var(--border)}

/* Compact data table (used inside cards: full-width, 12px text, collapsed borders) */
.data-table-sm{font-size:12px;border-collapse:collapse;width:100%}

/* Sub-heading inside a card (gray, 13px, bold) */
.subhead-gray{font-size:13px;font-weight:600;color:#374151;margin-bottom:6px}

/* Inline-flex pill (icon + label, optional clickable) */
.ifx-c-gap-6  {display:inline-flex;align-items:center;gap:6px}
.ifx-c-gap-6-p{display:inline-flex;align-items:center;gap:6px;cursor:pointer}

/* Extra layout / spacing utilities */
.f-row-c-gap-8{display:flex;align-items:center;gap:8px}
.gap-16{gap:16px}
.fwrap{flex-wrap:wrap}
.jc-end{justify-content:flex-end}
.mt-0{margin-top:0}
.w-36 {width:36px}
.w-180{width:180px}
.select-all{user-select:all}
.bg-soft-fb{background:var(--bg-soft,#f1f5f9)}

/* ─── L: edit-modal sidebar-tabs layout (2026-06-11) ───
   Used by editModalSidebarTabs() in admin-app.js. Renders the tab list as
   a vertical accent rail on the LEFT (same look as the settings/docs page
   side-tabs-block) and the active form panel on the RIGHT. Visually heavier
   than the default chip-tab bar but easier to navigate on long forms with
   10+ tabs (operator edit, item edit). */
.tabbed-form.tf-side{display:flex;gap:14px;align-items:flex-start}
.tabbed-form.tf-side > .tabs-bar{display:flex;flex-direction:column;flex-wrap:nowrap;gap:2px;margin:0;padding:8px;background:var(--side-tabs-bg);border-radius:18px;min-width:200px;align-self:flex-start;position:sticky;top:80px;max-height:calc(100vh - 140px);overflow-y:auto}
.tabbed-form.tf-side > .tabs-bar > .tab-btn{background:transparent;border:none;color:var(--side-tab-text);padding:4px 12px;font-size:13px;font-weight:500;border-radius:13px;text-align:left;width:100%;display:flex;align-items:center;gap:8px;line-height:1.25;white-space:normal;box-shadow:none}
.tabbed-form.tf-side > .tabs-bar > .tab-btn:hover{background:var(--side-tab-hover-bg);color:var(--side-tab-text-hover);border-color:transparent}
.tabbed-form.tf-side > .tabs-bar > .tab-btn.active{background:#fff;color:#000;font-weight:600;box-shadow:0 1px 4px rgba(0,0,0,.12);border-color:transparent}
/* M147: вертикальні таби — приховуємо іконки в тих же випадках, як і в горизонтальних (залишається .fa-ic-only override). */
.tabbed-form.tf-side > .tabs-bar > .tab-btn .fa-ic{display:none}
.tabbed-form.tf-side > .tabs-bar > .tab-btn .fa-ic.fa-ic-only{display:inline-block;margin:0;font-size:13px}
/* M147: довідкові ненумеровані/нумеровані списки всередині .fs-13 (tip-блоки шлюзу та інші) — додаємо стандартний padding-left,
   інакше маркери з'їжджають за межі картки/панелі через глобальний reset. */
ul.fs-13, ol.fs-13{padding-left:20px;margin:6px 0 0}
ul.fs-13 li, ol.fs-13 li{margin:2px 0}
/* The form panels live in a right-hand wrapper so the active panel grows
   into available space and inactives stay collapsed without disrupting
   the layout. */
.tabbed-form.tf-side > .tab-panels{flex:1;min-width:0}
.tabbed-form.tf-side > .tab-panels > .tab-panel{display:none}
.tabbed-form.tf-side > .tab-panels > .tab-panel.active{display:block;background:#fff;border:1px solid var(--border-soft);border-radius:18px;padding:16px 18px;box-shadow:0 1px 4px rgba(0,0,0,.04)}
.tabbed-form.tf-side > .tab-panels > .tab-panel.active > .info-card:only-child,
.tabbed-form.tf-side > .tab-panels > .tab-panel.active > .info-card:first-child:last-child{box-shadow:none;padding:0;margin:0;background:transparent}
.tabbed-form.tf-side > .tab-panels > .tab-panel.active > .info-card{box-shadow:none}
/* Mobile fallback: stack the rail on top, full-width pills. */
@media (max-width:720px){
  .tabbed-form.tf-side{flex-direction:column;gap:10px}
  .tabbed-form.tf-side > .tabs-bar{flex-direction:row;flex-wrap:wrap;position:static;max-height:none;overflow:visible;min-width:0;width:100%}
  .tabbed-form.tf-side > .tabs-bar > .tab-btn{width:auto}
}

/* ─── M: per-tab activation checkbox (2026-06-11) ───
   Renders at the TOP of an edit-modal tab panel, ABOVE the cascade tip
   ("Налаштування … порожнє поле = успадкувати …"); when unchecked the
   rest of the panel below dims and stops accepting input. Tri-state
   variant adds a third « inherit » mode; the resolved-inherited state is
   conveyed by the colored dot painted INSIDE the «Успадковано» pill
   (green when inheritance evaluates to ON, red when OFF) — matches the
   active/inactive dot pattern used in tables. */
.activation-bar{display:flex;align-items:center;gap:10px;background:#f8fafc;border:1px solid var(--border-soft);border-radius:10px;padding:8px 12px;margin:-4px -2px 12px}
.activation-bar .lbl{font-weight:600;font-size:13px;color:#0f172a;flex:1;display:flex;flex-direction:column;gap:2px}
.activation-bar .lbl small{font-weight:400;font-size:11px;color:rgba(15,23,42,.55)}
.activation-bar .tristate{display:inline-flex;border:1px solid var(--border-soft);border-radius:8px;background:#fff;overflow:hidden}
.activation-bar .tristate > button{background:transparent;border:none;padding:5px 12px;font-size:12px;font-weight:600;color:var(--label-color-strong);cursor:pointer;display:inline-flex;align-items:center;gap:6px;line-height:1;border-right:1px solid var(--border-soft)}
.activation-bar .tristate > button:last-child{border-right:none}
.activation-bar .tristate > button:hover{background:rgba(15,23,42,.04)}
.activation-bar .tristate > button.on{background:#dcfce7;color:#15803d}
.activation-bar .tristate > button.off{background:#fee2e2;color:#b91c1c}
.activation-bar .tristate > button.inh{background:var(--primary-soft);color:var(--primary)}
/* Colored status dot inside each tristate button (and other inline
   chip-like contexts). Sized down to a clean bullet — inline `color:`
   on each `<i>` carries the green/red tint so the dot stays
   colored even when the button itself is in the inactive grey state. */
.fa-ic-dot{font-size:8px;line-height:1;vertical-align:middle}
/* Bi-state slim variant (single toggle, no inherit). */
.activation-bar .bistate{display:inline-flex;align-items:center;gap:8px}
.activation-bar .bistate input{margin:0;width:18px;height:18px;accent-color:var(--primary)}
/* Block immediately following the activation-bar dims when the panel is
   marked off (data-activation="off"). The activation-bar itself is a
   direct child of .tab-panel so the `> :not(.activation-bar)` selector
   naturally skips it — the bar stays fully interactive and crisp. */
.tab-panel[data-activation="off"] > :not(.activation-bar){opacity:.45;pointer-events:none;filter:saturate(.6)}
.tab-panel[data-activation="off"] .activation-bar{opacity:1;pointer-events:auto;filter:none}

/* ─── N: Tom Select theme integration (2026-06-11) ───
   Tom Select renders its own .ts-wrapper around a hidden <select>. We
   override its default look so single-mode selects read identically to
   our native <input>/<select> (30px tall, 1.5px border, primary focus,
   5px radius). Multi/tag mode keeps the chip pattern from Tom Select's
   default theme but tinted with our primary palette. */
/* Tom Select 2.x enforces `.ts-control:not(.rtl){padding-right:max(
   var(--ts-pr-min),var(--ts-pr-clear-button)+var(--ts-pr-caret))
   !important}` so plain `padding-right:Npx` on `.ts-control` is
   silently ignored. Bump --ts-pr-min instead — that's the variable
   the !important rule reads — so our pseudo-element arrow at right:10
   never gets covered by the selected item text. */
.ts-wrapper.single{--ts-pr-min:22px}
.ts-wrapper{margin-bottom:8px;width:100%}
/* Single: fixed 30px height. dropdown_input hides .items-placeholder
   on open; with height:auto the control collapses a few px and jumps
   the layout. Multi stays height:auto so chips can wrap. */
.ts-wrapper.single .ts-control{min-height:30px;height:30px;padding:2px 8px;border:2px solid var(--input-border);border-radius:7px;font-size:13px;background:#fff;color:#0f172a;box-shadow:none;transition:border-color .15s,box-shadow .15s;line-height:1.3;box-sizing:border-box;align-items:center}
.ts-wrapper.multi .ts-control{min-height:30px;height:auto;padding:2px 8px;border:2px solid var(--input-border);border-radius:7px;font-size:13px;background:#fff;color:#0f172a;box-shadow:none;transition:border-color .15s,box-shadow .15s;line-height:1.3;box-sizing:border-box;align-items:center}
.ts-wrapper.single .ts-control:hover,
.ts-wrapper.multi .ts-control:hover{border-color:var(--secondary)}
.ts-wrapper.single.focus .ts-control,
.ts-wrapper.multi.focus .ts-control{border-color:var(--secondary);box-shadow:var(--secondary-ring)}
/* dropdown_input makes .ts-control the focus node (tabIndex). Kill the
   browser's default blue focus ring on click/re-click — we already show
   focus via border + secondary-ring. */
.ts-wrapper .ts-control,
.ts-wrapper .ts-control:focus,
.ts-wrapper .ts-control:focus-visible{outline:none}
.ts-dropdown .dropdown-input,
.ts-dropdown .dropdown-input:focus,
.ts-dropdown .dropdown-input:focus-visible{outline:none}
/* Tom Select's dropdown_input theme resets border to 1px + drops the
   focus ring while open — that shrinks the box. Keep our 2px focus. */
.ts-wrapper.plugin-dropdown_input.focus.dropdown-active .ts-control{
  border:2px solid var(--secondary);
  box-shadow:var(--secondary-ring);
}
.ts-wrapper.single .ts-control > input{font-size:13px;color:#0f172a;line-height:24px}
.ts-wrapper.single .ts-control > .item{padding:0;color:#0f172a;line-height:24px}
/* The dropdown_input plugin adds <input class="items-placeholder"> to
   the control so the placeholder text remains visible when nothing is
   selected. Style it as ghost text (no border / background, matching
   line-height) so it occupies the SAME box as a selected .item — fixes
   the "placeholder-set selects are taller than the rest" regression.
   When an item is selected TS toggles .has-items on the wrapper; hide
   the placeholder then so the .item is the sole visible child. */
.ts-wrapper.single .ts-control > .items-placeholder{flex:1 1 auto;min-width:0;padding:0;border:0;background:transparent;color:rgba(15,23,42,.55);font-size:13px;line-height:24px;height:24px;font-family:inherit;outline:none;box-shadow:none;cursor:pointer;pointer-events:none}
.ts-wrapper.single .ts-control > .items-placeholder::placeholder{color:rgba(15,23,42,.55);opacity:1}
.ts-wrapper.single.has-items .ts-control > .items-placeholder{display:none}
/* Keep “— всі … —” visible while the menu is open. TS hides the
   placeholder on .dropdown-active, which leaves the control blank. */
.ts-wrapper.plugin-dropdown_input.dropdown-active:not(.has-items) .ts-control > .items-placeholder{display:block!important}
/* Tom Select's `.ts-control > input{min-width:7rem}` reserves ~112px
   for the search field — which the wrapper sizes around. When the
   dropdown opens that reservation snaps in, jumping the wrapper from
   "item width + arrow" to "item width + 112px". Collapse the
   reservation so the wrapper width stays stable across open/close.
   Typing still works (max-width:100% kicks in on focus). */
.ts-wrapper.single .ts-control > input{min-width:0;flex:1 1 0}
.ts-wrapper.single .ts-control:after{content:"\f078";font-family:"Font Awesome 6 Free";font-weight:900;position:absolute;right:10px;top:50%;transform:translateY(-50%);font-size:10px;color:var(--label-color-soft);pointer-events:none;border:none;margin:0;padding:0;width:auto;height:auto}
.ts-wrapper.multi .ts-control{padding:2px 8px}
.ts-wrapper.multi .ts-control > .item{background:var(--primary-soft);color:var(--primary);border-radius:999px;padding:1px 8px;margin:1px 3px 1px 0;font-size:12px;font-weight:600;border:none}
.ts-wrapper.disabled .ts-control,
.ts-wrapper.disabled .ts-control > input{background:#f1f5f9;color:var(--label-color-soft);cursor:not-allowed}
/* Tom Select dropdowns are now rendered as direct children of <body>
   (dropdownParent:'body') so they can escape ancestor `overflow:auto`
   clipping (modal scroll, .filter-row x-scroll, nav-filters, etc.).
   Bump z-index so the menu paints above the modal (z-index:200) and the
   command palette (z-index:11000 — we stay below that, command palette
   has its own focus trap). */
body > .ts-dropdown{z-index:10500}
/* Auto-size body-portal dropdowns to the widest item so long option
   labels don't get a horizontal scrollbar inside the menu. TS sets
   `width: <controlWidth>px` inline when dropdownParent is body — we
   override with !important and let max-content take over. min-width
   keeps it at least as wide as the control via the JS-set
   --ts-min-w (see bbSelectInit). */
body > .ts-dropdown{width:max-content!important;min-width:var(--ts-min-w,160px);max-width:90vw}
.ts-dropdown{border:1px solid var(--border-soft);border-radius:8px;box-shadow:0 8px 24px rgba(15,23,42,.12);font-size:13px;margin-top:4px;background:#fff;padding:4px}
.ts-dropdown .option{padding:6px 10px;border-radius:6px;color:#0f172a}
.ts-dropdown .option.active,
.ts-dropdown .option:hover{background:var(--primary-soft);color:var(--primary)}
.ts-dropdown .option.selected{background:var(--primary-soft-strong);color:var(--primary);font-weight:600}
.ts-dropdown .no-results{padding:8px 12px;color:rgba(15,23,42,.55);font-size:12px;font-style:italic}
.ts-dropdown .create{padding:6px 10px;border-radius:6px;color:#0f172a}
.ts-dropdown .optgroup-header{padding:6px 10px 2px;font-size:10px;color:rgba(15,23,42,.55);text-transform:uppercase;letter-spacing:.4px;font-weight:700}
/* Tree-pane filters: portal dropdown matches the dark rail (secondary accent). */
.ts-dropdown[data-bb-portal-scope="tree"]{
  background:var(--tree-pane-bg-elev,#1e293b);
  border:1px solid var(--tree-pane-border,rgba(255,255,255,.12));
  box-shadow:0 12px 32px rgba(0,0,0,.45);
  color:var(--tree-pane-text,#e2e8f0);
}
.ts-dropdown[data-bb-portal-scope="tree"] .option{color:var(--tree-pane-text,#e2e8f0)}
.ts-dropdown[data-bb-portal-scope="tree"] .option.active,
.ts-dropdown[data-bb-portal-scope="tree"] .option:hover{
  background:var(--secondary-soft);
  color:var(--secondary);
}
.ts-dropdown[data-bb-portal-scope="tree"] .option.selected{
  background:var(--secondary-soft-strong);
  color:var(--secondary);
  font-weight:600;
}
.ts-dropdown[data-bb-portal-scope="tree"] .no-results{color:rgba(255,255,255,.45)}
.ts-dropdown[data-bb-portal-scope="tree"] .create{color:var(--tree-pane-text,#e2e8f0)}
.ts-dropdown[data-bb-portal-scope="tree"] .optgroup-header{color:rgba(255,255,255,.45)}
.ts-dropdown[data-bb-portal-scope="tree"] .dropdown-input-wrap{
  background:var(--tree-pane-bg,#0f172a);
  border-bottom:1px solid var(--tree-pane-border,rgba(255,255,255,.12));
}
.ts-dropdown[data-bb-portal-scope="tree"] .dropdown-input{color:#fff;box-shadow:none!important;border:0!important;outline:none}
.ts-dropdown[data-bb-portal-scope="tree"] .dropdown-input:hover,
.ts-dropdown[data-bb-portal-scope="tree"] .dropdown-input:focus{
  border:0!important;box-shadow:none!important;outline:none;background:transparent;
}
.ts-dropdown[data-bb-portal-scope="tree"] .dropdown-input::placeholder{color:rgba(255,255,255,.45)}
/* Dark scrollbar inside tree dropdown (Firefox + WebKit). */
.ts-dropdown[data-bb-portal-scope="tree"],
.ts-dropdown[data-bb-portal-scope="tree"] .ts-dropdown-content{
  scrollbar-width:thin;
  scrollbar-color:rgba(255,255,255,.28) var(--tree-pane-bg,#0f172a);
}
.ts-dropdown[data-bb-portal-scope="tree"]::-webkit-scrollbar,
.ts-dropdown[data-bb-portal-scope="tree"] .ts-dropdown-content::-webkit-scrollbar{width:8px;height:8px}
.ts-dropdown[data-bb-portal-scope="tree"]::-webkit-scrollbar-track,
.ts-dropdown[data-bb-portal-scope="tree"] .ts-dropdown-content::-webkit-scrollbar-track{background:var(--tree-pane-bg,#0f172a);border-radius:999px}
.ts-dropdown[data-bb-portal-scope="tree"]::-webkit-scrollbar-thumb,
.ts-dropdown[data-bb-portal-scope="tree"] .ts-dropdown-content::-webkit-scrollbar-thumb{background:rgba(255,255,255,.22);border-radius:999px}
.ts-dropdown[data-bb-portal-scope="tree"]::-webkit-scrollbar-thumb:hover,
.ts-dropdown[data-bb-portal-scope="tree"] .ts-dropdown-content::-webkit-scrollbar-thumb:hover{background:rgba(255,255,255,.34)}
/* Search input lives at the top of the dropdown (dropdown_input plugin).
   Styled as a slim search field with a divider below so it visually
   separates from the option list. The input itself has no border —
   the wrap's border-bottom does the framing. */
.ts-dropdown .dropdown-input-wrap{padding:0;margin:-4px -4px 4px;border-bottom:1px solid var(--border-soft);background:#fff;border-radius:8px 8px 0 0}
.ts-dropdown .dropdown-input{display:block;width:100%;padding:8px 12px;border:0;background:transparent;font-size:13px;color:#0f172a;outline:none;box-shadow:none;line-height:18px;font-family:inherit}
.ts-dropdown .dropdown-input::placeholder{color:rgba(15,23,42,.45)}
/* Tom Select inside the filter row (small chips, 26px height). The wrapper
   sizes by content (min-width:0) so a short native `<select>` doesn't
   suddenly become 140px wide and force `.filter-row` to overflow.
   `width:auto` lets inline `style="width:..."` on the source <select>
   still win when set explicitly. --ts-pr-min:18px gives the arrow room
   without padding the wrapper to look like a fat input. */
.filter-row .ts-wrapper{margin:0;width:auto;min-width:0;flex:0 0 auto}
/* Same neutralization for selects rendered directly inside a `.card-head`
   (e.g. cells/items view-mode toggle, which sits outside any .filter-row).
   Without this the default `.ts-wrapper{margin-bottom:8px}` adds dead
   space below the header on pages that have no filter row. */
.card-head .ts-wrapper{margin-bottom:0}
.card-head > select.filter-input{margin-bottom:0}
.filter-row .ts-wrapper.single{--ts-pr-min:18px}
.filter-row .ts-wrapper.single .ts-control{min-height:30px;height:30px;padding:2px 8px;font-size:12px;border-radius:7px}
.filter-row .ts-wrapper.single .ts-control > input,
.filter-row .ts-wrapper.single .ts-control > .item,
.filter-row .ts-wrapper.single .ts-control > .items-placeholder{line-height:24px;height:24px;font-size:12px}
/* When bbSelectInit measured the widest option, it set --ts-min-w on
   the wrapper. Apply it as min-width on the control so the selected
   item never overlaps the chevron and the control naturally widens to
   fit the longest option label. The wrapper also gets min-width via
   JS — but if for some reason the inline style wasn't set, the CSS
   var still cascades down to the control. */
.ts-wrapper.single[style*="--ts-min-w"] .ts-control{min-width:var(--ts-min-w)}
.ts-wrapper.single[style*="--ts-min-w"]{min-width:var(--ts-min-w)}
.filter-row .ts-wrapper.single .ts-control:after{right:6px;font-size:9px}
/* Tom Select inside the dark page header (.nav-filters). Mirrors the
   native rounded chip the area used to have: white text on translucent
   white surface, 17px radius, 32px height. Dropdown menu stays the
   default light theme since it overlays the page. */
.nav-filters .ts-wrapper{margin:0;width:auto;min-width:140px;max-width:200px}
.nav-filters .ts-wrapper.single{--ts-pr-min:24px}
.nav-filters .ts-wrapper.single .ts-control{min-height:32px;height:32px;padding:0 12px;border-radius:17px;font-size:13px;font-weight:500;color:#fff;background:rgba(255,255,255,.15);border:1px solid rgba(255,255,255,.3);box-shadow:none;line-height:30px;cursor:pointer}
.nav-filters .ts-wrapper.single.focus .ts-control{border-color:rgba(255,255,255,.6);background:rgba(255,255,255,.22);box-shadow:none}
.nav-filters .ts-wrapper.single .ts-control > input,
.nav-filters .ts-wrapper.single .ts-control > .item,
.nav-filters .ts-wrapper.single .ts-control > .items-placeholder{color:#fff;line-height:30px;height:30px;font-size:13px;font-weight:500}
.nav-filters .ts-wrapper.single .ts-control > input::placeholder,
.nav-filters .ts-wrapper.single .ts-control > .items-placeholder::placeholder{color:rgba(255,255,255,.7);opacity:1}
.nav-filters .ts-wrapper.single .ts-control:after{color:rgba(255,255,255,.85);right:10px;font-size:10px}
.nav-filters .ts-dropdown{margin-top:6px;min-width:200px}
/* Tom Select placeholder (inherited-resolved hint) */
.ts-wrapper.single .ts-control > input::placeholder{color:rgba(15,23,42,.45);font-style:italic}

/* ─── P: Binding card (locked-binding mini-card in CRUD modals) ───
   Renders a "this thing is bound to that thing" chip-with-image inside
   create/edit modals so the binding (operator/point/cell/item) is
   visible and obviously NOT editable as a free dropdown. Use over a
   `<select>` when the parent is fixed by the page context. */
.binding-strip{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:8px}
.binding-card{display:flex;align-items:center;gap:10px;padding:8px 12px;background:#f8fafc;border:1px solid var(--border-soft);border-radius:10px;min-width:160px;max-width:100%;cursor:default;transition:border-color .12s}
.binding-card.is-clickable{cursor:pointer}
.binding-card.is-clickable:hover{border-color:#94a3b8;background:#f1f5f9}
/* is-nav = the body region navigates to the entity in the left tree. Keeps
   the wrapper's cursor as default (outer padding still shows default), the
   inner .bc-body picks up the pointer cursor so the click target is clear. */
.binding-card.is-nav .bc-body{cursor:pointer}
.binding-card.is-nav:hover{border-color:var(--primary,#94a3b8);background:#f1f5f9}
.binding-card .bc-thumb{flex:0 0 40px;width:40px;height:40px;border-radius:8px;background:#e2e8f0 center/cover no-repeat;display:flex;align-items:center;justify-content:center;color:#64748b;font-size:18px;overflow:hidden}
.binding-card .bc-thumb img{width:100%;height:100%;object-fit:cover;display:block}
.binding-card .bc-body{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px}
.binding-card .bc-kind{font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.4px;color:#94a3b8}
.binding-card .bc-name{font-size:13px;font-weight:600;color:#0f172a;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.binding-card .bc-meta{font-size:11px;color:var(--label-color-soft);font-family:var(--font-condensed);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.binding-card .bc-action{flex:0 0 auto;color:#64748b;font-size:11px;padding:4px 8px;border-radius:6px;background:transparent;border:none;cursor:pointer}
.binding-card .bc-action:hover{background:#e2e8f0;color:#0f172a}

/* ─── Q: Three-dot (kebab) menu ───
   Compact action drawer for table-rows or card-headers. The trigger is
   a circular icon button; the menu pops below with right-alignment.
   Pure CSS for layout; the open/close toggle is done by the JS helper
   (it adds/removes `is-open` on the wrapper). */
.kebab{position:relative;display:inline-block;vertical-align:middle}
.kebab>.kebab-trigger{appearance:none;border:none;background:transparent;color:var(--label-color-soft);width:28px;height:28px;border-radius:50%;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;font-size:14px;padding:0}
.kebab>.kebab-trigger:hover{background:#f1f5f9;color:#0f172a}
.kebab>.kebab-menu{position:absolute;top:calc(100% + 4px);right:0;min-width:180px;background:#fff;border:1px solid var(--border-soft);border-radius:10px;box-shadow:0 4px 14px rgba(15,23,42,.12);padding:4px 0;z-index:50;display:none}
.kebab.is-open>.kebab-menu{display:block}
.kebab>.kebab-menu>button,
.kebab>.kebab-menu>a{display:flex;align-items:center;gap:8px;width:100%;text-align:left;background:transparent;border:none;padding:8px 14px;font-size:13px;color:#0f172a;cursor:pointer;text-decoration:none;font-family:inherit}
.kebab>.kebab-menu>button:hover,
.kebab>.kebab-menu>a:hover{background:#f1f5f9}
.kebab>.kebab-menu>button[disabled]{opacity:.45;cursor:not-allowed}
.kebab>.kebab-menu>.kebab-sep{height:1px;background:var(--border-soft);margin:4px 0}
.kebab>.kebab-menu>.kebab-danger,
.kebab>.kebab-menu>button.kebab-danger{color:#c0392b}
.kebab>.kebab-menu>.kebab-danger:hover,
.kebab>.kebab-menu>button.kebab-danger:hover{background:#fef2f2}

/* ─── R: Split layout (tree-pane + content-pane) ───
   Two-column shell used inside `.app-shell` on the admin dashboard. The
   tree column is resizable (CSS `resize` on the aside), responsive
   (stacks below 900px), and can be fully collapsed by adding `.no-tree`
   to the shell (localStorage `bb_tree_hidden`). Renderers keep writing
   to `#mainContent` (now `.content-pane`) — no per-page changes needed.

   PREVIOUS light tree (kept for rollback reference, 2026-08):
     .tree-pane{background:#fff;border-right:1px solid var(--border-soft)}
     rows: hover bg --secondary-soft + color --secondary;
           active bg --secondary + color --secondary-contrast + glow
     caret/icon: --label-color-soft; active → --secondary-contrast
     search input: bg #fff, border 2px --input-border
     tabs: pill fill --secondary when active
*/
/* Two independent scroll regions.
   * Tree pane is fixed under the nav; it scrolls its own overflow.
   * Content pane keeps document-flow scrolling. */
.split-layout{display:flex;align-items:flex-start;gap:16px;min-height:calc(100vh - 220px);padding-left:calc(var(--tree-w,320px) + 16px);transition:padding-left .18s ease}
.split-layout>.tree-pane{position:fixed;top:var(--admin-nav-h,56px);bottom:0;left:0;width:var(--tree-w,320px);z-index:20;background:var(--tree-pane-bg);color:var(--tree-pane-text);border:none;border-right:1px solid var(--tree-pane-border);border-radius:0;padding:14px 6px 14px 10px;overflow-x:hidden;overflow-y:scroll;overscroll-behavior:contain;scroll-behavior:smooth;transition:transform .18s ease,width .18s ease}
.split-layout>.content-pane{flex:1;min-width:0;overflow:visible;scroll-behavior:smooth}
.split-layout.no-tree{padding-left:16px}
.split-layout.no-tree>.tree-pane{transform:translateX(-100%);pointer-events:none}
/* ─── Tree resize gutter ───
   Thin vertical strip pinned to the right edge of the fixed tree pane.
   Transparent by default (invisible), lights up on hover. Dragging it
   updates `--tree-w`; releasing persists to localStorage. Dragging
   below ~55% of TREE_MIN_W snaps the tree closed. Double-click toggles
   between hidden ↔ default width. */
.tree-gutter{position:fixed;top:var(--admin-nav-h,56px);bottom:0;left:calc(var(--tree-w,320px));width:10px;margin-left:-5px;z-index:22;cursor:col-resize;background:transparent;transition:background .12s ease}
.tree-gutter::before{content:"";position:absolute;top:0;bottom:0;left:4px;width:2px;background:transparent;border-radius:2px;transition:background .12s ease}
.tree-gutter:hover::before,body.tree-dragging .tree-gutter::before{background:var(--secondary,#f59e0b)}
.tree-gutter.is-tree-hidden{width:14px;margin-left:0}
.tree-gutter.is-tree-hidden::before{left:2px;background:var(--secondary-soft-strong,rgba(245,158,11,.18))}
.tree-gutter.is-tree-hidden:hover::before{background:var(--secondary,#f59e0b)}
body.tree-dragging{cursor:col-resize !important;user-select:none}
body.tree-dragging *{user-select:none !important}
/* Tree / main content scrollbars. */
.tree-pane{
  scrollbar-width:thin;
  scrollbar-color:rgba(255,255,255,.28) var(--tree-pane-bg);
}
.tree-pane::-webkit-scrollbar,#mainContent::-webkit-scrollbar{width:8px;height:8px}
#mainContent::-webkit-scrollbar-track{background:transparent}
#mainContent::-webkit-scrollbar-thumb{background:var(--scrollbar-thumb);border-radius:6px}
#mainContent::-webkit-scrollbar-thumb:hover{background:var(--scrollbar-thumb-hover)}
.tree-pane::-webkit-scrollbar-track{background:var(--tree-pane-bg);border-radius:999px}
.tree-pane::-webkit-scrollbar-thumb{background:rgba(255,255,255,.22);border-radius:999px;border:2px solid transparent;background-clip:padding-box}
.tree-pane::-webkit-scrollbar-thumb:hover{background:rgba(255,255,255,.34);background-clip:padding-box}
html{scroll-behavior:smooth}
@media (max-width:900px){
  /* On narrow screens the fixed tree would eat half the viewport — fall
     back to the stacked layout without fixed positioning. */
  .split-layout{flex-direction:column;align-items:stretch;padding-left:0}
  .split-layout>.tree-pane{position:static;top:auto;bottom:auto;left:auto;width:auto;flex:0 0 auto;max-width:none;max-height:40vh;transform:none;border-right:none;border:1px solid var(--tree-pane-border);border-radius:12px;padding:8px 6px}
  .split-layout.no-tree>.tree-pane{display:none;transform:none}
  .tree-gutter{display:none}
}
/* Tree header — small toolbar above the tree body: title + collapse btn. */
.tree-pane .tree-head{display:flex;align-items:center;gap:6px;padding:4px 6px 8px;border-bottom:1px solid var(--tree-pane-border);margin-bottom:6px}
.tree-pane .tree-head-title{flex:1;font-size:10.5px;text-transform:uppercase;letter-spacing:.4px;color:rgba(255,255,255,.55);font-family:var(--font-condensed);font-weight:600}
.tree-pane .tree-head-btn{background:transparent;border:none;color:rgba(255,255,255,.55);cursor:pointer;padding:2px 6px;border-radius:4px;font-size:11px}
.tree-pane .tree-head-btn:hover{background:rgba(255,255,255,.08);color:var(--secondary)}
/* Restore-tree button — persistent floating pill (fixed-position), only
   visible while `bb_tree_hidden==='1'`. Toggled via `hidden` attribute
   in `_toggleTreePane` / `renderTreePane`, so it survives the frequent
   `mainContent.innerHTML=…` re-writes by page renderers. */
.tree-restore-btn{position:fixed;top:var(--admin-nav-h,56px);left:10px;z-index:80;display:inline-flex;align-items:center;gap:6px;background:#fff;border:1px solid var(--border-soft);border-radius:8px;padding:4px 10px;font-size:11px;color:var(--label-color-strong);cursor:pointer;box-shadow:0 1px 4px rgba(0,0,0,.08);margin-top:8px}
.tree-restore-btn[hidden]{display:none}
.tree-restore-btn:hover{background:var(--secondary-soft);color:var(--secondary)}
.tree-pane .tree-node{font-size:13px}
/* Tree rows: no hover/active fill — indication is text colour only. */
.tree-pane .tree-node>.tree-row{display:flex;align-items:center;gap:6px;padding:5px 8px;border-radius:14px;cursor:pointer;user-select:none;color:var(--tree-pane-text);background:transparent;box-shadow:none;transition:color .12s}
.tree-pane .tree-node>.tree-row:hover{background:transparent;color:var(--secondary)}
.tree-pane .tree-node.is-active>.tree-row{background:transparent;color:var(--secondary);font-weight:600;box-shadow:none}
.tree-pane .tree-node.is-active>.tree-row:hover{background:transparent;color:var(--secondary)}
.tree-pane .tree-node>.tree-row>.tree-caret{flex:0 0 16px;width:16px;color:var(--secondary);font-size:13px;line-height:1;transition:transform .12s;text-align:center}
.tree-pane .tree-node.is-leaf>.tree-row>.tree-caret{visibility:hidden}
.tree-pane .tree-node.is-expanded>.tree-row>.tree-caret{transform:rotate(90deg)}
.tree-pane .tree-node.is-active>.tree-row>.tree-caret{color:var(--secondary)}
/* Hide FA icons in the tree; keep photo thumbs (+ empty placeholder). */
.tree-pane .tree-icon:not(.tree-icon-thumb),
.tree-pane .tree-admin-icon{display:none!important}
.tree-pane .tree-icon-thumb{
  display:inline-flex!important;
  flex:0 0 18px;
  width:18px;height:18px;padding:0;
  border-radius:4px;overflow:hidden;
  border:none;
  background:var(--tree-pane-bg-elev);
  align-items:center;justify-content:center;
}
.tree-pane .tree-icon-thumb img{width:100%;height:100%;object-fit:cover;display:block}
.tree-pane .tree-icon-ph{background:rgba(255,255,255,.08);border-style:dashed}
.tree-pane .tree-node>.tree-row>.tree-icon.tree-icon-thumb{flex-basis:18px;width:18px;height:18px}
.tree-pane .tree-node>.tree-row:hover>.tree-caret{color:var(--secondary)}
.tree-pane .tree-node.is-active>.tree-row:hover>.tree-caret{color:var(--secondary)}
.tree-pane .tree-node>.tree-row>.tree-label{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.tree-pane .tree-node>.tree-row>.tree-count{flex:0 0 auto;font-size:10px;background:var(--secondary-soft);color:var(--secondary);padding:1px 7px;border-radius:99px;font-weight:600;font-family:var(--font-mono)}
.tree-pane .tree-node.is-active>.tree-row>.tree-count{background:var(--secondary-soft-strong);color:var(--secondary)}
.tree-pane .tree-node.has-events>.tree-row>.tree-count{background:rgba(239,68,68,.25);color:#fecaca}
.tree-pane .tree-children{margin-left:14px;border-left:1px dashed var(--tree-pane-border);padding-left:6px;display:none}
.tree-pane .tree-node.is-expanded>.tree-children{display:block}
.tree-pane .tree-empty{padding:12px 8px;font-size:12px;color:rgba(255,255,255,.45)}
/* Empty-state row with a small inline "+" button. Used under empty
   Точки/Комірки/Товари branches for one-click quick-create. */
.tree-pane .tree-empty-add{display:flex;align-items:center;gap:6px;padding:6px 8px}
.tree-pane .tree-empty-add .tree-empty-label{flex:1;font-size:11.5px;color:rgba(255,255,255,.45);font-style:italic}
.tree-pane .tree-empty-btn{flex:0 0 auto;background:transparent;border:1px dashed var(--tree-pane-border);color:rgba(255,255,255,.55);cursor:pointer;padding:2px 8px;border-radius:6px;font-size:10px;line-height:1.4;transition:background .12s,color .12s,border-color .12s}
.tree-pane .tree-empty-btn:hover{background:rgba(255,255,255,.08);color:var(--secondary);border-color:var(--secondary);border-style:solid}
/* ─── Sub-nav rows (Комірки / Товари / Замовлення / …) ─── */
.tree-pane .tree-sub{font-size:12.5px}
.tree-pane .tree-sub>.tree-row{display:flex;align-items:center;gap:6px;padding:4px 8px;border-radius:13px;cursor:pointer;user-select:none;color:var(--tree-pane-text);background:transparent;box-shadow:none;transition:color .12s}
.tree-pane .tree-sub>.tree-row:hover{background:transparent;color:var(--secondary)}
.tree-pane .tree-sub.is-active>.tree-row{background:transparent;color:var(--secondary);font-weight:600;box-shadow:none}
.tree-pane .tree-sub.is-active>.tree-row:hover{background:transparent;color:var(--secondary)}
.tree-pane .tree-sub>.tree-row>.tree-caret{flex:0 0 16px;width:16px;color:var(--secondary);font-size:13px;line-height:1;transition:transform .12s;text-align:center}
.tree-pane .tree-sub.is-leaf>.tree-row>.tree-caret{visibility:hidden}
.tree-pane .tree-sub.is-expanded>.tree-row>.tree-caret{transform:rotate(90deg)}
.tree-pane .tree-sub>.tree-row:hover>.tree-caret,
.tree-pane .tree-sub.is-active>.tree-row>.tree-caret,
.tree-pane .tree-sub.is-active>.tree-row:hover>.tree-caret{color:var(--secondary)}
.tree-pane .tree-sub>.tree-row>.tree-label{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.tree-pane .tree-sub>.tree-children{margin-left:14px;border-left:1px dashed var(--tree-pane-border);padding-left:6px;display:none}
.tree-pane .tree-sub.is-expanded>.tree-children{display:block}
.tree-pane .tree-count-static{flex:0 0 auto;font-size:10px;background:var(--secondary-soft);color:var(--secondary);padding:1px 6px;border-radius:99px;font-weight:600;font-family:var(--font-mono);line-height:1.5}
.tree-pane .tree-caret-blank{visibility:hidden}
/* Slimmer variant for lazy-loaded cell/item leaves — the sub-nav they
   sit under is already indented once; drop another line-height notch so
   long lists don't dominate the pane. */
.tree-pane .tree-node-slim{font-size:12px}
.tree-pane .tree-node-slim>.tree-row{padding:3px 8px}
/* Item/cell history leaves under an expanded cell/item node. Current
   binding renders solid; historical rows fade to signal "not now". */
.tree-pane .tree-node.is-history>.tree-row{opacity:.5}
.tree-pane .tree-node.is-history>.tree-row:hover{opacity:.8}
.tree-pane .tree-node.is-current>.tree-row>.tree-label{font-weight:600}
/* Inline session-count pill next to a history label (e.g. "×3"). */
.tree-pane .tree-count-inline{display:inline-block;margin-left:4px;padding:0 5px;font-size:10px;line-height:1.4;font-weight:600;color:rgba(255,255,255,.55);background:rgba(255,255,255,.08);border-radius:8px;vertical-align:1px}
/* Drag & drop item→cell/point moves. Keep tinted outlines for DnD
   affordance (exception to the no-fill rule). */
.tree-pane .tree-node.is-dragging{opacity:.4}
.tree-pane .tree-node.tree-drop-ok>.tree-row{background:rgba(22,163,74,.18);outline:2px dashed #4ade80;outline-offset:-2px}
.tree-pane .tree-node.tree-drop-no>.tree-row{background:rgba(220,38,38,.18);outline:2px dashed #f87171;outline-offset:-2px;cursor:not-allowed}
.tree-pane .tree-node[draggable="true"]>.tree-row{cursor:grab}
.tree-pane .tree-node[draggable="true"]:active>.tree-row{cursor:grabbing}
.tree-pane .tree-empty-sm{padding:3px 8px;font-size:11px;color:rgba(255,255,255,.45)}
.tree-pane .tree-showmore{padding:4px 8px;font-size:11px;color:var(--secondary);cursor:pointer;user-select:none;border-radius:6px;font-weight:600;font-family:var(--font-condensed);letter-spacing:.2px}
.tree-pane .tree-showmore:hover{background:rgba(255,255,255,.06);color:var(--secondary)}
.tree-pane .tree-sub-scoped{font-size:11.5px}
.tree-pane .tree-sub-scoped>.tree-row{padding:2px 8px;color:rgba(255,255,255,.55);background:transparent}
.tree-pane .tree-sub-scoped>.tree-row:hover{background:transparent;color:var(--secondary)}
/* ─── Deep-scope chip (bookings/incidents/cell-events/staff-log) ─── */
/* Shown next to the H2 of a list page when the user drilled to a cell
   or item via the tree. Communicates the active narrow scope + gives a
   one-tap way to widen back to point/operator scope without leaving the
   page. Colour follows the secondary accent to match the tree's active
   row highlight, so it reads as "same context as the tree selection". */
.scope-chip{display:inline-flex;align-items:center;gap:6px;padding:3px 4px 3px 10px;margin-left:10px;background:var(--secondary-soft-strong,rgba(245,158,11,.18));color:var(--secondary-contrast,#422006);border:1px solid var(--secondary-soft,rgba(245,158,11,.35));border-radius:99px;font-size:12px;font-weight:600;font-family:var(--font-condensed);letter-spacing:.2px;vertical-align:middle}
.scope-chip>i{font-size:10.5px;opacity:.85}
.scope-chip .scope-chip-label{white-space:nowrap;max-width:220px;overflow:hidden;text-overflow:ellipsis}
.scope-chip .scope-chip-clear{background:transparent;border:0;color:inherit;cursor:pointer;padding:2px 6px;border-radius:99px;font-size:11px;line-height:1;transition:background .12s}
.scope-chip .scope-chip-clear:hover{background:rgba(0,0,0,.08)}
/* ─── Tree scope-filter block (was topbar .nav-filters) ─── */
/* Static container inside the tree pane; the selects survive
   renderTreePane() calls because only #treeBody is re-rendered. Stacks
   vertically to fit the narrow (~260px) column. Tom Select control
   inherits full width via the shared .filter-input rule. Refresh is
   beside the tree search input, not here. */
.tree-pane .tree-filters{display:flex;flex-direction:column;gap:6px;padding:6px 6px 8px;border-bottom:none;margin-bottom:2px}
.tree-pane .tree-filters .filter-input,.tree-pane .tree-filters .ts-wrapper{width:100%;margin-bottom:0}
/* Dark-rail inputs (search + filter selects / Tom Select) — 2px border. */
.tree-pane .tree-search-input,
.tree-pane .tree-filters input.filter-input,
.tree-pane .tree-filters select.filter-input{
  background:var(--tree-pane-input-bg)!important;
  border:2px solid var(--tree-pane-input-border)!important;
  color:#fff!important;
  box-shadow:none!important;
}
.tree-pane .tree-search-input::placeholder{color:rgba(255,255,255,.45)}
.tree-pane .tree-search-input:hover,
.tree-pane .tree-filters input.filter-input:hover,
.tree-pane .tree-filters select.filter-input:hover{border-color:var(--secondary)!important}
.tree-pane .tree-search-input:focus,
.tree-pane .tree-filters input.filter-input:focus,
.tree-pane .tree-filters select.filter-input:focus{
  background:var(--tree-pane-input-bg-focus)!important;
  border-color:var(--secondary)!important;
  box-shadow:var(--secondary-ring)!important;
}
.tree-pane .tree-filters .ts-wrapper.single .ts-control,
.tree-pane .tree-filters .ts-wrapper.multi .ts-control{
  background:var(--tree-pane-input-bg);
  border:2px solid var(--tree-pane-input-border);
  color:#fff;
  box-shadow:none;
}
.tree-pane .tree-filters .ts-wrapper.single .ts-control:hover,
.tree-pane .tree-filters .ts-wrapper.multi .ts-control:hover{border-color:var(--secondary)}
.tree-pane .tree-filters .ts-wrapper.single.focus .ts-control,
.tree-pane .tree-filters .ts-wrapper.multi.focus .ts-control,
.tree-pane .tree-filters .ts-wrapper.plugin-dropdown_input.focus.dropdown-active .ts-control{
  background:var(--tree-pane-input-bg-focus);
  border:2px solid var(--secondary);
  box-shadow:var(--secondary-ring);
}
.tree-pane .tree-filters .ts-wrapper.single .ts-control{height:30px;min-height:30px}
.tree-pane .tree-filters .ts-wrapper.single .ts-control > input,
.tree-pane .tree-filters .ts-wrapper.single .ts-control > .item{color:#fff}
.tree-pane .tree-filters .ts-wrapper.single .ts-control > .items-placeholder,
.tree-pane .tree-filters .ts-wrapper.single .ts-control > .items-placeholder::placeholder{color:rgba(255,255,255,.45)}
.tree-pane .tree-filters .ts-wrapper.single .ts-control:after{color:rgba(255,255,255,.45)}
/* ─── Tree tabs (Інвентар | Адмін) — translucent on dark rail ─── */
.tree-pane .tree-tabs{display:flex;align-items:stretch;gap:4px;padding:4px;margin:0 4px 8px;background:rgba(255,255,255,.06);border-radius:999px}
.tree-pane .tree-tab{flex:1;background:transparent;border:1px solid transparent;color:rgba(255,255,255,.70);font-size:12px;font-weight:600;padding:6px 10px;border-radius:999px;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;gap:5px;font-family:inherit;transition:background .12s,color .12s,border-color .12s,box-shadow .12s;box-shadow:none}
.tree-pane .tree-tab:hover{background:rgba(255,255,255,.08);color:#fff}
.tree-pane .tree-tab.is-active{background:rgba(255,255,255,.12);color:#fff;border-color:rgba(255,255,255,.15);box-shadow:none}
.tree-pane .tree-tab.is-active:hover{background:rgba(255,255,255,.14);color:#fff;filter:none}
.tree-pane .tree-tab i{font-size:11px;opacity:.85}
/* Tree search + refresh — refresh sits beside the input (not a full row). */
.tree-pane .tree-search{display:flex;align-items:center;gap:4px;margin:0 4px 6px}
.tree-pane .tree-search-field{position:relative;flex:1;min-width:0}
.tree-pane .tree-search-input{width:100%;height:30px;padding:5px 28px 5px 8px;font-size:12px;border-radius:7px;font-family:inherit;outline:none;margin:0;transition:border-color .15s,box-shadow .15s,background .15s}
.tree-pane .tree-search-clear{position:absolute;right:2px;top:50%;transform:translateY(-50%);background:transparent;border:none;color:rgba(255,255,255,.55);cursor:pointer;padding:4px 6px;font-size:10px;border-radius:4px}
.tree-pane .tree-search-clear:hover{background:rgba(255,255,255,.08);color:var(--secondary)}
.tree-pane .tree-search-clear[hidden]{display:none}
.tree-pane .tree-search-refresh{flex:0 0 auto;width:30px;height:30px;padding:0;display:inline-flex;align-items:center;justify-content:center;background:var(--tree-pane-input-bg);border:2px solid var(--tree-pane-input-border);border-radius:7px;color:rgba(255,255,255,.75);cursor:pointer;font-size:12px;transition:background .12s,color .12s,border-color .12s}
.tree-pane .tree-search-refresh:hover{background:var(--tree-pane-input-bg-focus);color:var(--secondary);border-color:var(--secondary)}
/* Search-driven visibility helpers. */
.tree-pane .tree-hidden{display:none !important}
.tree-pane .tree-match>.tree-row>.tree-label{color:var(--secondary)}
/* ─── Admin-tab rows inside the tree pane ─── */
.tree-pane .tree-admin-list{display:flex;flex-direction:column;gap:0}
.tree-pane .tree-admin-section{padding:12px 8px 4px;font-size:10.5px;text-transform:uppercase;letter-spacing:.4px;color:rgba(255,255,255,.45);font-family:var(--font-condensed);font-weight:600}
.tree-pane .tree-admin-section:first-child{padding-top:4px}
.tree-pane .tree-admin-row{display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:13px;cursor:pointer;font-size:13px;color:var(--tree-pane-text);background:transparent;user-select:none;transition:color .1s;box-shadow:none}
.tree-pane .tree-admin-row:hover{background:transparent;color:var(--secondary)}
.tree-pane .tree-admin-row.active{background:transparent;color:var(--secondary);font-weight:600;box-shadow:none}
.tree-pane .tree-admin-row.active:hover{background:transparent;color:var(--secondary)}
.tree-pane .tree-admin-label{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.tree-pane .tree-admin-badge{flex:0 0 auto;background:#ef4444;color:#fff;border-radius:99px;padding:1px 6px;font-size:10px;font-weight:700;font-family:var(--font-mono)}
/* ─── Icon-only topbar buttons (chats, incidents, search, bell, gear) ─── */
/* Compact fixed-width variant used by the notification cluster on the
   right side of the topbar. Keeps the badge legible and prevents the
   nav from wrapping just because a label got translated to a longer
   string. Badge is absolutely positioned so it doesn't shift the icon. */
.nav .nav-icon-btn{padding:6px 10px;min-width:36px;position:relative;display:inline-flex;align-items:center;justify-content:center;gap:0}
.nav .nav-icon-btn i{font-size:14px}
.nav .nav-icon-btn>span[id$="Badge"]{position:absolute;top:0;right:0;transform:translate(35%,-25%);margin-left:0}


/* ─── S: create-booking modal extras (point picker, client typeahead) ─── */
/* 3-column form row for compact triplets like "Type / Sum / Status" and
   "Start / Duration / Unit". Falls back to 2-col on narrow viewports so
   labels don't get crushed. */
.form-row-3{display:grid;grid-template-columns:1fr 1fr 1fr;gap:12px}
@media (max-width:720px){ .form-row-3{grid-template-columns:1fr 1fr} }
/* Locked-binding strip at the top of the order modal (operator/point chips
   + "Змінити точку" button). Keeps the chips on one line on desktop and
   wraps on mobile without breaking the rebind button position. */
.cb-locked-strip{display:flex;align-items:center;gap:10px;flex-wrap:wrap;padding:10px 12px}
.cb-locked-strip .binding-strip{flex:1;min-width:0}

/* Point picker — list+search pre-step shown when ctx has no pointId */
.cb-pick-list{max-height:380px;overflow-y:auto;margin-top:4px;border:1px solid var(--border-soft);border-radius:10px;background:#fff}
.cb-pick-row{display:flex;align-items:center;gap:10px;padding:10px 12px;cursor:pointer;border-bottom:1px solid #f1f5f9;transition:background .1s}
.cb-pick-row:last-child{border-bottom:none}
.cb-pick-row:hover{background:#f8fafc}
.cb-pick-row-main{flex:1;min-width:0}
.cb-pick-row-name{font-size:13px;font-weight:600;color:#0f172a;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.cb-pick-row-meta{font-size:11.5px;color:var(--label-color-soft);font-family:var(--font-condensed);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-top:2px}

/* Client typeahead in the booking modal */
.cb-client-hits{display:flex;flex-direction:column;gap:4px;margin-top:6px}
.cb-client-hit{display:flex;align-items:center;gap:10px;padding:7px 10px;border:1px solid var(--border-soft);border-radius:8px;background:#fff;cursor:pointer;transition:background .1s}
.cb-client-hit:hover{background:#f8fafc;border-color:#94a3b8}
.cb-client-hit .fa-ic{color:#64748b}
.cb-client-hit-main{flex:1;min-width:0}
.cb-client-hit-name{font-size:13px;font-weight:600;color:#0f172a;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.cb-client-hit-sub{font-size:11.5px;color:var(--label-color-soft);font-family:var(--font-condensed);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-top:1px}
.cb-client-empty{font-size:12px;color:var(--label-color-soft);padding:6px 10px;font-family:var(--font-condensed)}
.cb-client-picked{font-size:12.5px;color:#0f172a;padding:7px 10px;background:#ecfdf5;border:1px solid #a7f3d0;border-radius:8px;display:flex;align-items:center;gap:6px}
/* Per-link metadata chips inside a typeahead hit (link, discount, access flags). */
.cb-client-chip{display:inline-block;font-size:10.5px;font-weight:600;line-height:1;padding:2px 6px;border-radius:99px;background:#eef2ff;color:#3730a3;border:1px solid #c7d2fe;margin-left:4px;vertical-align:middle}
.cb-client-chip-link{background:#dbeafe;color:#1d4ed8;border-color:#93c5fd;padding:2px 5px}
.cb-client-chip-ok{background:#dcfce7;color:#166534;border-color:#86efac}
.cb-client-chip-bad{background:#fee2e2;color:#991b1b;border-color:#fca5a5}

/* Cabinet facade — visual locker layout (columns × stacked S/M/L/XL) */
.cab-wrap{margin:0 0 16px;padding:12px 14px 14px;background:#0f172a;border-radius:12px;color:#e2e8f0}
.cab-toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:10px;margin-bottom:6px}
.cab-title{font-weight:700;font-size:13px;letter-spacing:.02em}
.cab-cols-lbl{font-size:12px;display:flex;align-items:center;gap:6px;color:#cbd5e1}
.cab-cols-lbl input{width:64px;padding:4px 8px;border-radius:6px;border:1px solid #334155;background:#1e293b;color:#f8fafc}
.cab-hint{color:#94a3b8!important;margin:0 0 10px}
.cab-facade{display:flex;gap:8px;align-items:flex-start;overflow-x:auto;padding:4px 0 2px}
.cab-col{display:flex;flex-direction:column;gap:6px;min-width:78px;flex:0 0 auto}
.cab-col-num{text-align:center;font-size:11px;font-weight:700;color:#64748b;letter-spacing:.06em}
.cab-cell{border:none;border-radius:8px;color:#fff;display:flex;flex-direction:column;align-items:center;justify-content:center;cursor:pointer;box-shadow:inset 0 1px 0 rgba(255,255,255,.18),0 1px 2px rgba(0,0,0,.25);padding:4px 6px;line-height:1.15;transition:transform .12s,filter .12s}
.cab-cell:hover{transform:translateY(-1px);filter:brightness(1.08)}
.cab-cell-num{font-size:13px;font-weight:700}
.cab-cell-size{font-size:10px;opacity:.85;font-weight:600}
.cab-cell.size-S{height:36px}
.cab-cell.size-M{height:56px}
.cab-cell.size-L{height:80px}
.cab-cell.size-XL{height:108px}
.cab-cell.size-OPEN{height:56px;background:#7c3aed}
.cab-cell.is-busy{box-shadow:inset 0 0 0 2px rgba(15,23,42,.45)}
.cab-cell.is-preview{outline:1px dashed rgba(255,255,255,.55);outline-offset:1px}

.cam-rtsp{font-size:11px;max-width:240px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}
.cam-caps-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:12px}
.cam-live-wrap{position:relative;background:#0f172a;border-radius:12px;min-height:240px;overflow:hidden}
.cam-live-img{display:block;width:100%;max-height:70vh;object-fit:contain;background:#020617}
.cam-live-status{position:absolute;left:12px;bottom:12px;font-size:12px;color:#e2e8f0;background:rgba(15,23,42,.72);padding:4px 8px;border-radius:6px}
.cam-live-status:empty{display:none}
.cam-live-hint{margin:10px 0 0;font-size:12px}

