﻿/* ==========================================================================
   Voice AI by Kinetiq — Premium RTL design system
   Palette sampled from the Kinetiq logo.
   ========================================================================== */

:root {
    /* ═══ THE SUBSTRATE (2026-08-04) ═══════════════════════════════════════════════════
       A six-specialist design audit counted what this file ACTUALLY contained rather than
       what :root claimed: 36 font sizes across 103 declarations with ZERO referencing a
       token, 30 spacing values across 289 with two, 18 radius values bypassing the three
       tokens 27 times, and 76 distinct colours under 26 colour tokens — including two
       "success greens" two hex digits apart doing the same job 525 lines from each other.

       That is the answer to "לא מסודר" and to "אני לא רואה שהשתנה משהו": nothing lined up
       between components because nothing shared an increment, and nothing was decisively
       larger than anything else because five "card title" sizes differed by 0.3px. Neither
       is fixable by eye. The token blocks below are the fix.

         --n-*    ONE cool neutral ramp. The ground left #EEEEEE — the only fully achromatic
                  grey in the product, which made every cool surface above it read warm.
                  --n-6 is EVERY control boundary: 3.16:1, clearing WCAG 1.4.11, which the
                  old --line at 1.34:1 never did — including every switch's OFF track.
         --*-ink  The ink weight of a status, separate from its fill. A fill sits under a
                  white glyph at 3:1; a word sits on the page and must clear 4.5:1. Deriving
                  them per component is how two identical greens got into one file.
         --s1..7  One 4px scale. 1px and 2px are deliberately NOT on it — at that size the
                  value is optical correction, not spacing, and snapping it is a visible
                  misalignment rather than a tidier one.
         --r      One corner for everything with an edge. There were six on a single page.
         --t-*    Seven steps, with the body→title gap widened on purpose. Deduplicating the
                  five title sizes onto ~1rem would have been tidier and invisible.

       TWO RULES FOR EDITING THIS BLOCK, both learned the hard way on 2026-08-04:
       1. A bulk find-and-replace must never run over :root. One did, and left
          `--ok-ink: var(--ok-ink)` — a self-reference, which CSS treats as guaranteed-
          invalid, so every status word fell back to inherited grey while the file parsed
          perfectly and nothing warned.
       2. If you stash comments to protect them from a rewrite, restore in a LOOP. A single
          re.sub pass does not re-scan its own replacements, so comments nested inside a
          stashed :root came back as literal placeholder text sitting in a declaration block. */

    --kq-cyan-rgb: 0, 173, 181;
    --kq-ink-rgb: 34, 40, 49;

    --kq-cyan: #00ADB5;
    --kq-cyan-bright: #1AC4CC;
    --kq-azure: #0E8F96;
    --kq-blue: #00ADB5;
    --kq-blue-600: #0E8F96;
    --kq-indigo: #222831;
    --kq-violet: #00ADB5;

    --grad-brand: linear-gradient(135deg, #00ADB5 0%, #0E8F96 100%);
    --grad-brand-soft: linear-gradient(135deg, rgba(0,173,181,.13), rgba(0,173,181,.07));
    --grad-sidebar: linear-gradient(185deg, #222831 0%, #393E46 100%);
    --grad-success: linear-gradient(135deg, #18BE66, #4BC140);
    --grad-violet: linear-gradient(135deg, #1AC4CC, #00ADB5);

    --n-0: #FFFFFF;    --n-1: #FAFBFC;    --n-2: #EDF0F3;    --n-3: #E4E8ED;    --n-4: #D9DEE5;    --n-5: #C3CAD3;    --n-6: #868F9C;    --n-7: #656E7B;    --n-8: #545D69;    --n-9: #38414C;
    --ink: #14191F;
    --ink-2: var(--n-9);
    --muted: var(--n-8);
    --line-strong: var(--n-6);
    --row-hover: var(--n-3);
    --line: var(--n-4);
    --line-2: var(--n-4);
    --bg: var(--n-1);
    --card: var(--n-0);
    --panel: var(--n-0);

    --ok: #16a34a;
    --ok-bg: #e7f8ee;
    --warn: #d97706;
    --warn-bg: #fdf1e1;
    --danger: #dc2626;
    --danger-bg: #fdecec;
    --info: #2563A8;
    --info-bg: #E7EEF7;

    --ok-ink: #0D7A3E;    --warn-ink: #B45309;    --danger-ink: #B3261E;    --kq-cyan-soft: color-mix(in srgb, var(--kq-cyan) 12%, var(--n-0));

    --shadow-sm: 0 1px 2px rgba(34, 40, 49, .06), 0 1px 3px rgba(34, 40, 49, .06);
    --shadow: 0 6px 20px rgba(34, 40, 49, .09);
    --shadow-lg: 0 18px 50px rgba(34, 40, 49, .16);
    --ring: 0 0 0 4px rgba(0, 173, 181, .20);

    --link: color-mix(in srgb, var(--kq-cyan) 62%, #000);


    /* DENSITY PASS 2026-08-05 (owner: "תקטין לי את כל הדפים בממשק בהתאם להקטנה שביצענו בתפריט").
       The rail was tightened first and he asked for the rest of the product to follow it.
       Done at the TOKENS, not rule by rule: 298 declarations reference these seven values, so a
       scale change here moves the whole interface at once and keeps it internally consistent —
       which is the entire reason the substrate was built. Editing paddings screen by screen is
       how the 30-spacing-value mess it replaced came about in the first place.
       ~0.85 of the old scale. --s1 stays 4px: it is the atom the others are multiples of, and
       below 4 a gap stops reading as deliberate and starts reading as a mistake. */
    --s1: 4px;
    --s2: 7px;
    --s3: 10px;
    --s4: 14px;
    --s5: 20px;
    --s6: 28px;
    --s7: 40px;

    /* ── GLASS (owner 2026-08-05: "תעשה לי זכוכית, ככה בכל הממשק בכל מקום קומפלט") ────
       Three things make a surface read as glass, and only the third is the obvious one:
         1. a GROUND WORTH BLURRING. Glass over flat grey is a grey box. body carries a very
            faint two-hue wash — 7% teal, 5% indigo, gone by 65% — enough to give the panels
            something to be in front of, quiet enough for a tool people stare at all day.
         2. AN EDGE THAT CATCHES LIGHT. The inset white hairline along the top is what the eye
            reads as a bevel. Without it a translucent panel just looks faded.
         3. the blur itself, which is the cheapest part visually and by far the most expensive
            to render.
       --glass-bg is 78% and not lower on purpose: text sits on this, and every point of
       transparency is a point of contrast lost. */
    --glass-bg: color-mix(in srgb, #fff 68%, transparent);
    --glass-brd: rgba(255,255,255,.55);
    --glass-shine: inset 0 1px 0 rgba(255,255,255,.75);
    --glass-shadow: 0 1px 2px rgba(20,25,31,.05), 0 16px 36px -18px rgba(20,25,31,.30);
    --glass-blur: saturate(1.6) blur(18px);

    --r: 6px;
    --r-pill: 999px;
    --r-circle: 50%;
    --radius: var(--r);
    --radius-sm: var(--r);
    --radius-xs: var(--r);

    /* Type comes down more gently than spacing — ~0.93. Whitespace can lose an eighth without
       anyone noticing; text cannot, and Hebrew loses legibility faster than Latin because it
       has no ascenders or descenders to identify a letter by. The two largest steps drop
       furthest: the dashboard figures were dominating screens they only summarise. */
    --t-xs: .68rem;    --t-sm: .75rem;    --t-md: .82rem;    --t-base: .89rem;    --t-lg: 1.06rem;    --t-xl: 1.35rem;    --t-2xl: 1.95rem;
    --sidebar-w: 244px;

    --kq-x: 1;

    --t-fast: 120ms;
    --t-base: 200ms;
    --t-slow: 320ms;
    --e-out: cubic-bezier(.22, .61, .36, 1);
    --e-in: cubic-bezier(.55, 0, .68, .53);
    --e-standard: cubic-bezier(.4, 0, .2, 1);
}

/* THE REDUCED-MOTION GUARD. Nothing in this stylesheet honoured prefers-reduced-motion —
   the only such rule anywhere was inside vendored bootstrap. For a person who has asked
   their operating system to stop moving things, an interface that animates anyway is not a
   style choice, it is a symptom trigger.
   Motion is reduced to a hair rather than removed: a 0.01ms transition still fires its
   transitionend, so anything waiting on that event keeps working, and a state change still
   reads as a change rather than a jump-cut. */
@media (prefers-reduced-motion: reduce) {
    /* NOT the blanket `transition-duration: .01ms` reset. That kills the safe half too — an
       opacity fade carries no motion and is exactly what should survive — and it turns every
       state change into a jump-cut, which removes the causality cue the animation was there
       to give. Instead: keep the colour and opacity transitions, drop the travel. */
    *, *::before, *::after {
        transition-property: opacity, color, background-color, border-color, box-shadow,
                             outline-color, fill, stroke !important;
        animation-duration: var(--t-fast) !important;
        animation-iteration-count: 1 !important;
        scroll-behavior: auto !important;
    }
    /* the handful of places that displace an element on hover or press — decoration, so it goes */
    .btn:hover, .btn:active, .card:hover, .nav__link:hover, .bnav label:hover,
    .stat:hover, .tool-item:hover { transform: none !important; }
}

[dir="rtl"] { --kq-x: -1; }

/* ==========================================================================
   Page transitions
   ========================================================================== */
/* Clicking a rail item blinks the whole window white today — the navy rail, the logo and
   the top bar all disappear and come back byte-identical. Pinning the shell with a
   view-transition-name and giving its group a 0s duration makes it HOLD STILL across a real
   HTTP navigation, so only the content cross-fades. Pure CSS: `script-src 'self'` never sees
   it, and browsers without support just navigate the way they do now.
   The exit is one step shorter than the enter and opacity-only — on the way out the user has
   already decided, and every millisecond of outro is dead time they paid for. */
@view-transition { navigation: auto; }

.sidebar { view-transition-name: kq-rail; }
.topbar  { view-transition-name: kq-topbar; }
::view-transition-group(kq-rail),
::view-transition-group(kq-topbar) { animation-duration: 0s; }

::view-transition-old(root) { animation: kq-vt-out  90ms var(--e-in)  both; }
::view-transition-new(root) { animation: kq-vt-in  180ms var(--e-out) both; }
@keyframes kq-vt-out { to   { opacity: 0; } }
@keyframes kq-vt-in  { from { opacity: 0; translate: 0 6px; } }

/* Travel is the vestibular trigger, opacity is not — keep the fade, null the movement. The
   global reduced-motion block whitelists transition-property only; a keyframe still travels,
   so it has to be said again here. */
@media (prefers-reduced-motion: reduce) {
    ::view-transition-old(root),
    ::view-transition-new(root) { animation-duration: var(--t-fast); translate: none; }
}

/* The top bar is sticky, and nothing set scroll-padding — so every #anchor, every
   scrollIntoView and every focus() after a validation failure landed the target UNDERNEATH
   73px of frosted white. The user sees the ring vanish and assumes focus was lost. */
/* AUTHORITATIVE, NOT OBSERVED. This was a measurement of what the topbar happened to come
   out to, and the density pass of 2026-08-05 made it 61px while this still said 73 — so the
   bot editor's sticky header, which pins itself to this number, floated 12px below the bar
   with a band of page content scrolling through the gap. The topbar now TAKES its height
   from this token, so the number cannot drift from the thing it describes again. */
:root { --kq-topbar-h: 61px; }
html { scroll-padding-block-start: calc(var(--kq-topbar-h) + 12px); }

* { box-sizing: border-box; }

/* ═══ TYPEFACES — SELF-HOSTED (owner chose this 2026-08-04) ════════════════════════════
   Was Heebo + Rubik off Google Fonts. Two problems, one of them not cosmetic:

   1. That pair is the default free Google Hebrew pairing — the Hebrew equivalent of Inter +
      Poppins, what every template and every generated mockup ships — and the market research
      named it the single loudest "AI-made" tell in the product.
   2. Typography was the ONLY system in this application still loading from a third party,
      while everything else runs under script-src 'self'. Two preconnects and a stylesheet
      link on every page, to a host we do not control, that can see every visitor.

   ASSISTANT for text (owner chose it 2026-08-05 from a live five-way specimen). It is the face
   most serious Israeli products set their interfaces in, and the reason is the reason he picked
   it: even colour and generous counters make it legible at 13px without asking to be noticed.
   "לא מקצועי מספיק" in a dense admin UI is nearly always a request for a QUIETER face, not a more
   characterful one.

   ASSISTANT IS THE ONLY FACE IN THE PRODUCT, digits included (owner 2026-08-05). IBM Plex Mono
   carried the figures until then; see the note at the @font-face block for what that cost and
   why he took it. OFL.

   ONE VARIABLE FILE PER SUBSET, not four static weights: Assistant ships variable, so 400
   through 700 come from 7KB of Hebrew instead of four separate downloads. Two files total.

   Hebrew and Latin are separate files by unicode-range so a browser fetches only what the
   page actually contains: the Hebrew subset is 7KB for every weight. font-display:swap, because
   a console that is unreadable for 200ms is worse than one that reflows once. */
@font-face { font-family: "Assistant"; font-weight: 200 800; font-style: normal; font-display: swap;
             src: url("/fonts/assistant-he.woff2") format("woff2");
             unicode-range: U+0590-05FF, U+200F, U+FB1D-FB4F; }
@font-face { font-family: "Assistant"; font-weight: 200 800; font-style: normal; font-display: swap;
             src: url("/fonts/assistant-latin.woff2") format("woff2");
             unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+2000-206F, U+20AA, U+20AC, U+2122, U+2212; }
/* THE FIGURES FACE IS GONE (owner 2026-08-05: "שגם הפונט של המספרים בכל הממשק
   יהיה ASSISTANT"). IBM Plex Mono used to carry every digit so that columns of
   timestamps and durations aligned. Assistant has NO tabular figures — measured on
   the live journal, its "1" is 9.02px against "0" at 9.84px, and font-variant-numeric
   changes nothing because the font ships no `tnum` feature. Those columns are now
   slightly ragged, and that is a trade the owner made knowingly in exchange for one
   typeface across the whole interface.
   The tabular-nums declarations are deliberately LEFT IN PLACE: they cost nothing,
   they document the intent, and they start working the day a face that supports
   them is chosen. */

html { -webkit-text-size-adjust: 100%; }

/* FORM CONTROLS DO NOT INHERIT TYPE. Browsers set their own family on button/input/select, so a
   control that misses .btn or .control renders in Arial while the page around it is Assistant —
   and several did: the icon buttons and the user menu. Found by listing every distinct first
   font-family on a live page rather than by reading the sheet, because the sheet looked right.
   Declared once, on the elements, so no future control can be born without it. */
button, input, select, textarea, optgroup { font: inherit; }

body {
    margin: 0;
    font-family: "Assistant", "Segoe UI", system-ui, -apple-system, sans-serif;
    /* THE WASH IS FIXED, NOT SCROLLED. A gradient that scrolls with the page makes every
       panel's blur shift as you move, which reads as the surfaces sliding over each other —
       a nice trick on a landing page and a distraction in a console. */
    background: #eef1f5;
    background-image:
        radial-gradient(1200px 700px at 92% -6%, rgba(0,173,181,.30), transparent 60%),
        radial-gradient(1000px 640px at 4% 2%, rgba(67,56,202,.20), transparent 58%),
        radial-gradient(900px 700px at 55% 108%, rgba(0,173,181,.14), transparent 60%);
    background-attachment: fixed;
    color: var(--ink);
    font-size: 14px;
    line-height: 1.6;
    /* NOT antialiased. It thins strokes, and Hebrew recognition depends on stroke weight far
       more than Latin does — there are no ascenders and descenders to identify a letter by. */
    text-rendering: optimizeLegibility;
}

h1, h2, h3, h4, h5, .display {
    font-family: "Assistant", sans-serif;
    color: var(--ink);
    font-weight: 600;
    /* no negative tracking: Hebrew has no optical gaps to close and tightening
       shears the letterforms */
    margin: 0 0 .4em;
}

a { color: var(--link); text-decoration: none; }
a:hover { color: var(--kq-indigo); }

.muted { color: var(--muted); }
.text-center { text-align: center; }
.small { font-size: var(--t-sm); }

/* ==========================================================================
   App shell
   ========================================================================== */
.app { display: flex; min-height: 100vh; }

.sidebar {
    width: var(--sidebar-w);
    flex: 0 0 var(--sidebar-w);
    /* THE RAIL JOINS THE MATERIAL BUT KEEPS ITS CONTRAST. Nav labels are light-on-dark, so
       the dark stays dominant at 94% — not the 68% the panels use. Enough translucency for
       the wash to tint its edge and for it to belong to the same world; not enough for page
       content to show through and start competing with the labels. */
    background: linear-gradient(185deg, rgba(34,40,49,.94) 0%, rgba(57,62,70,.94) 100%);
    -webkit-backdrop-filter: saturate(1.4) blur(20px);
    backdrop-filter: saturate(1.4) blur(20px);
    border-inline-start: 1px solid rgba(255,255,255,.08);
    color: #cdd5db;
    position: fixed;
    inset-block: 0;
    inset-inline-start: 0; /* RTL: right edge */
    display: flex;
    flex-direction: column;
    z-index: 40;
    box-shadow: var(--shadow-lg);
}

.sidebar__brand {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--s4) var(--s4) var(--s3);
    border-bottom: 1px solid rgba(255,255,255,.09);
}
.sidebar__logo { width: 150px; max-width: 100%; height: auto; display: block; margin: 0 auto; }

.sidebar__scope {
    margin: var(--s3) var(--s3) var(--s1);
    padding: var(--s2) var(--s3);
    background: rgba(255,255,255,.06);
    border: 1px solid rgba(255,255,255,.08);
    border-radius: var(--radius-sm);
    font-size: var(--t-sm);
}
.sidebar__scope .label { color: var(--n-6); font-size: var(--t-xs); }
.sidebar__scope .value { color: #fff; font-weight: 600; font-size: var(--t-base); margin-top: 2px; }

/* impersonation (platform owner acting inside a tenant) */
.imp-banner {
    margin: var(--s4) var(--s4) var(--s1);
    padding: var(--s3);
    background: linear-gradient(135deg, rgba(252,149,47,.20), rgba(252,138,30,.14));
    border: 1px solid rgba(252,149,47,.45);
    border-radius: var(--radius-sm);
}
.imp-banner__label { display: flex; align-items: center; gap: var(--s2); color: #ffd9ab; font-size: var(--t-xs); font-weight: 600; }
.imp-banner__label svg { width: 15px; height: 15px; }
.imp-banner__tenant { color: #c2cad2; font-size: var(--t-xs); margin-top: var(--s2); }
.imp-banner__name { color: #fff; font-weight: 700; font-size: var(--t-base); font-family: "Assistant", sans-serif; line-height: 1.2; }
.imp-banner__exit {
    display: flex; align-items: center; justify-content: center; gap: var(--s2); width: 100%;
    margin-top: var(--s3); padding: var(--s2) var(--s3); border-radius: var(--radius-xs); cursor: pointer;
    background: #fff; color: #b4620c; border: 0; font: inherit; font-weight: 600; font-size: var(--t-md);
    transition: background var(--t-fast) var(--e-standard);
}
.imp-banner__exit:hover { background: #fff4e7; }
.imp-banner__exit svg { width: 16px; height: 16px; }

/* RAIL DENSITY (owner 2026-08-05: "שהכפתורים בתפריט הראשי יהיו פחות גדולים שיכנסו יותר כפתורים
   בלי שיפתח SCROLL"). Each row was 12px padding + a 20px icon + 12px = 44px, plus a 4px gap —
   48px per item. Ten items plus the brand block and the scope card overflowed a laptop viewport,
   so the last items in the rail were reachable only by scrolling a menu, which is the one thing
   a menu is supposed to save you from.
   Now 34px per row on a 2px gap: 36px per item, a quarter off, and the whole rail fits.
   Density is not the opposite of professional here — it is what dense admin products look like;
   what read as unprofessional was a navigation that ran out of room. */
.nav { padding: var(--s2); display: flex; flex-direction: column; gap: 2px; overflow-y: auto; }
.nav__section { color: var(--n-6); font-size: var(--t-xs); padding: var(--s3) var(--s2) var(--s1); }
.nav__link {
    display: flex; align-items: center; gap: var(--s2);
    padding: var(--s2) var(--s3);
    border-radius: var(--radius-xs);
    color: #c2cad2;
    font-size: var(--t-md);
    font-weight: 500;
    transition: background-color var(--t-fast) var(--e-standard),
                color var(--t-fast) var(--e-standard),
                border-color var(--t-fast) var(--e-standard);
    position: relative;
}
.nav__link svg { width: 18px; height: 18px; flex: 0 0 18px; opacity: .9; }

.nav__link.active { background: rgba(255,255,255,.14); color: #fff; box-shadow: inset 0 0 0 1px rgba(255,255,255,.08); }
.nav__link.active::before {
    content: ""; position: absolute; inset-inline-start: 0; top: 18%; bottom: 18%;
    width: 3px; border-radius: var(--r); background: linear-gradient(var(--kq-cyan-bright), var(--kq-violet));
}

.sidebar__foot { margin-top: auto; padding: var(--s2); border-top: 1px solid rgba(255,255,255,.09); }

.main {
    flex: 1;
    min-width: 0;
    margin-inline-start: var(--sidebar-w);
    display: flex; flex-direction: column;
}

.topbar {
    position: sticky; top: 0; z-index: 30;
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--s4);
    height: var(--kq-topbar-h); box-sizing: border-box;
    padding: var(--s3) var(--s6);
    background: color-mix(in srgb, #fff 72%, transparent);
    -webkit-backdrop-filter: var(--glass-blur);
    backdrop-filter: var(--glass-blur);
    border-bottom: 1px solid var(--glass-brd);
    box-shadow: var(--glass-shine);
}
.topbar h1 { font-size: var(--t-lg); margin: 0; }
.topbar .crumb { color: var(--muted); font-size: var(--t-sm); }

.content { padding: var(--s5) var(--s6) var(--s7); max-width: 1200px; width: 100%; }

/* user menu */
.usermenu { position: relative; }
.usermenu__btn {
    display: flex; align-items: center; gap: var(--s2);
    background: #fff; border: 1px solid var(--line);
    border-radius: var(--r-pill); padding: var(--s1) var(--s2) var(--s1) var(--s3); cursor: pointer;
    transition: background-color var(--t-fast) var(--e-standard),
                color var(--t-fast) var(--e-standard),
                border-color var(--t-fast) var(--e-standard);
}
.usermenu__btn:hover { box-shadow: var(--shadow-sm); border-color: #cdd2d8; }
.usermenu__btn .who { display: flex; flex-direction: column; text-align: start; line-height: 1.2; }
.usermenu__btn .who .n { font-weight: 600; font-size: var(--t-md); }
.usermenu__btn .who .r { font-size: var(--t-xs); color: var(--muted); }
.usermenu__panel {
    position: absolute; inset-inline-end: 0; top: calc(100% + 8px);
    background: color-mix(in srgb, #fff 82%, transparent);
    -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur); border: 1px solid var(--glass-brd); border-radius: var(--radius-sm);
    box-shadow: var(--shadow-lg); min-width: 220px; padding: var(--s2); display: none;
}
.usermenu.open .usermenu__panel { display: block; }
.usermenu__panel a, .usermenu__panel button {
    display: flex; align-items: center; gap: var(--s2); width: 100%;
    padding: var(--s2) var(--s3); border-radius: var(--radius-xs);
    background: none; border: 0; cursor: pointer; color: var(--ink); font: inherit; text-align: start;
}

.usermenu__sep { height: 1px; background: var(--line); margin: var(--s1) var(--s1); }

/* avatar */
.avatar {
    width: 38px; height: 38px; border-radius: var(--r-circle);
    display: inline-flex; align-items: center; justify-content: center;
    background: var(--grad-brand); color: #fff; font-weight: 700; font-size: var(--t-md);
    font-family: "Assistant", sans-serif; flex: 0 0 auto; box-shadow: var(--shadow-sm);
}
.avatar.lg { width: 52px; height: 52px; font-size: var(--t-lg); }
.avatar.sm { width: 30px; height: 30px; font-size: var(--t-xs); }

/* ==========================================================================
   Cards, stats
   ========================================================================== */
.card {
    background: var(--glass-bg);
    -webkit-backdrop-filter: var(--glass-blur);
    backdrop-filter: var(--glass-blur);
    border: 1px solid var(--glass-brd);
    border-radius: calc(var(--r) + 4px);
    box-shadow: var(--glass-shine), var(--glass-shadow);
}
.card__head {
    display: flex; align-items: center; justify-content: space-between; gap: var(--s3);
    padding: var(--s4) var(--s5); border-bottom: 1px solid var(--line-2);
}
.card__head h2, .card__head h3 { margin: 0; font-size: var(--t-lg); display: inline-flex; align-items: center; gap: var(--s2); }
.card__head h2 svg, .card__head h3 svg { flex: 0 0 auto; }
.card__body { padding: var(--s5); }
.card__body.tight { padding: 0; }

.grid { display: grid; gap: var(--s4); }
.grid.cols-2 { grid-template-columns: repeat(2, 1fr); }
.grid.cols-3 { grid-template-columns: repeat(3, 1fr); }
.grid.cols-4 { grid-template-columns: repeat(4, 1fr); }

.stat {
    background: var(--glass-bg); border: 1px solid var(--glass-brd);
    border-radius: var(--radius); padding: var(--s4); box-shadow: var(--shadow-sm);
    position: relative; overflow: hidden;
}
.stat__icon {
    width: 46px; height: 46px; border-radius: var(--r);
    display: flex; align-items: center; justify-content: center; color: #fff; margin-bottom: var(--s3);
}
.stat__icon svg { width: 24px; height: 24px; }
.stat__icon.blue { background: var(--grad-brand); }
.stat__icon.green { background: var(--grad-success); }
.stat__icon.orange { background: var(--grad-violet); }
.stat__icon.cyan { background: var(--grad-brand); }
.stat__value { font-family: "Assistant", sans-serif; font-size: var(--t-2xl); font-weight: 700; line-height: 1; }
.stat__label { color: var(--muted); font-size: var(--t-md); margin-top: var(--s1); }

/* ==========================================================================
   Buttons
   ========================================================================== */
.btn {
    display: inline-flex; align-items: center; justify-content: center; gap: var(--s2);
    font: inherit; font-weight: 600; font-size: var(--t-md);
    padding: var(--s2) var(--s4); border-radius: var(--radius-xs);
    border: 1px solid transparent; cursor: pointer;
    position: relative;
    /* box-shadow is deliberately NOT in this list. Its blur radius is re-rasterised every
       frame and cannot be composited, so a hover over a toolbar of buttons repaints the
       whole strip; the shadow moved to the ::after layer below, where only opacity moves.
       `translate` rather than `transform` so the press composes with anything else. */
    transition: translate    var(--t-fast) var(--e-standard),
                background   var(--t-fast) var(--e-standard),
                border-color var(--t-fast) var(--e-standard),
                color        var(--t-fast) var(--e-standard);
    white-space: nowrap; text-decoration: none;
}
/* Down at 0ms, back up over --t-fast. A transitioned press-down reads as lag on the one
   interaction where latency is most obvious — every save button in the product. */
.btn:active { transition-duration: 0ms; translate: 0 1px; }
.btn::after {
    content: ""; position: absolute; inset: 0; border-radius: inherit;
    pointer-events: none; opacity: 0;
    transition: opacity var(--t-fast) linear;
}
.btn-primary::after { box-shadow: 0 10px 22px rgba(var(--kq-cyan-rgb), .38); }
.btn-success::after { box-shadow: 0 10px 22px rgba(24, 190, 102, .34); }
.btn svg { width: 18px; height: 18px; }

.btn-primary { background: var(--grad-brand); color: #fff; box-shadow: 0 6px 16px rgba(var(--kq-cyan-rgb), .30); }

.btn-ghost { background: color-mix(in srgb, #fff 60%, transparent); border-color: var(--line); color: var(--ink-2); }

.btn-success { background: var(--grad-success); color: #fff; box-shadow: 0 6px 16px rgba(24,190,102,.26); }

.btn-danger { background: #fff; border-color: #f3c9c9; color: var(--danger); }

.btn-warn { background: #fff; border-color: #f2d7ac; color: var(--warn); }

.btn-sm { padding: var(--s2) var(--s3); font-size: var(--t-sm); border-radius: var(--r); }
.btn-block { width: 100%; }
.btn[disabled] { opacity: .55; cursor: not-allowed; }
.btn-lg { padding: var(--s3) var(--s5); font-size: var(--t-base); }

/* CENTRING IS NOT INHERITED FROM .btn. Every icon button in the product is `class="btn-icon"`
   WITHOUT `btn` — so the `display:inline-flex; align-items:center; justify-content:center` that
   .btn declares never reached any of them. The glyph sat wherever the default inline flow put
   it: baseline-aligned, and nudged off-centre by the descender space under it. Invisible on the
   34px desktop button, obvious on the 40px hamburger (owner 2026-08-06: "לא ממוקם באמצע").
   Declared here rather than by adding `btn` to every call site: this is what the class is for,
   and a markup fix would have to be repeated on each of them and remembered on the next one. */
.btn-icon {
    width: 34px; height: 34px; padding: 0; border-radius: var(--r);
    background: #fff; border: 1px solid var(--line); color: var(--ink-2);
    display: inline-flex; align-items: center; justify-content: center;
    flex: none; line-height: 0;
}
.btn-icon > svg { display: block; }

/* ==========================================================================
   Tables
   ========================================================================== */
.table-wrap { overflow-x: auto; }
table.tbl { width: 100%; border-collapse: collapse; }
table.tbl th {
    text-align: start; font-size: var(--t-sm);
    color: var(--n-8); font-weight: 500; padding: var(--s2) var(--s4) var(--s3); border-bottom: 1px solid var(--n-5);
    white-space: nowrap;
}

/* Sort-link headers (call journal) look exactly like every other th — muted, not link-teal
   (owner screenshot 2026-07-22); the accent appears only on hover / the active sort arrow. */
table.tbl th a {
    color: var(--muted); text-decoration: none;
}
table.tbl th a:hover {
    color: var(--kq-azure);
}
table.tbl td { padding: var(--s3) var(--s4); vertical-align: middle; }
/* The row separator is drawn on the ROW, not on each cell. A cell that is itself a flex
   container (.row-actions) is pulled out of table-cell layout, so it sizes to its own content
   instead of the row — its cell border then landed above the neighbouring cells' and the
   separator looked stepped whenever another cell was taller (a role <select>, say).
   Bordering the row makes the line span the full width at the row's true bottom regardless. */
/* colour only, and no lift: a 1px translate on a 40px row makes a 50-row log shimmer
   like water as the pointer crosses it */
table.tbl tbody tr { transition: background-color var(--t-fast) linear; border-bottom: 1px solid var(--line-2); }

table.tbl tbody tr:last-child { border-bottom: 0; }
.cell-user { display: flex; align-items: center; gap: var(--s3); }
.cell-user .n { font-weight: 600; }
.cell-user .e { font-size: var(--t-sm); color: var(--muted); }
.row-actions { display: flex; gap: var(--s1); justify-content: flex-end; align-items: center; }

/* Sortable table headers: clickable, with an arrow that appears on the active column. */
table.tbl-sortable th[data-sort-key] { cursor: pointer; user-select: none; }
table.tbl-sortable th[data-sort-key]:hover { color: var(--ink-2); }
table.tbl-sortable th[data-sort-key]::after {
    content: "↕"; margin-inline-start: var(--s1); opacity: .3; font-size: .9em;
}
table.tbl-sortable th[data-sort-dir="asc"]::after { content: "↑"; opacity: 1; color: var(--kq-cyan); }
table.tbl-sortable th[data-sort-dir="desc"]::after { content: "↓"; opacity: 1; color: var(--kq-cyan); }

/* ==========================================================================
   Tabs (in-page section switch, e.g. the credential vault)
   ========================================================================== */
.tabs { display: flex; gap: var(--s1); margin-bottom: var(--s4); border-bottom: 1px solid var(--line); }
.tab {
    display: inline-flex; align-items: center; gap: var(--s2);
    padding: var(--s2) var(--s4); margin-bottom: -1px;
    font-weight: 600; font-size: var(--t-base); color: var(--muted); text-decoration: none;
    border-bottom: 2px solid transparent;
    transition: color var(--t-fast) var(--e-standard), border-color var(--t-fast) var(--e-standard);
}
.tab svg { width: 17px; height: 17px; }

.tab.active { color: var(--kq-cyan); border-bottom-color: var(--kq-cyan); }
.tab .count {
    font-size: var(--t-xs); font-weight: 600; color: var(--muted);
    background: var(--bg); border-radius: var(--r-pill); padding: 1px var(--s2); line-height: 1.6;
}
.tab.active .count { color: var(--kq-cyan); }

/* Search box with an icon inside the field (magnifier at the leading edge). */
.search-box { position: relative; }
.search-box .control { padding-inline-start: var(--s6); }
.search-box .search-icon {
    position: absolute; inset-inline-start: 13px; top: 50%; transform: translateY(-50%);
    color: var(--muted); pointer-events: none; display: inline-flex;
}
.search-box .search-icon svg { width: 17px; height: 17px; }

/* Tool parameters editor rows */
.params-rows { display: flex; flex-direction: column; gap: var(--s2); margin-bottom: var(--s2); }
.param-row { display: grid; grid-template-columns: 150px 128px 1fr auto auto; gap: var(--s2); align-items: center; }
.param-row .control { margin: 0; }
.param-row .checkbox { white-space: nowrap; }
@media (max-width: 720px) { .param-row { grid-template-columns: 1fr 1fr; } }

/* Tools list on the bot editor */
.tool-item { display: flex; align-items: center; justify-content: space-between; gap: var(--s3); padding: var(--s3) var(--s3); border: 1px solid var(--line); border-radius: var(--radius-sm); margin-bottom: var(--s2); }
.tool-item .tool-meta { min-width: 0; }
.tool-item .tool-name { font-weight: 600; display: flex; align-items: center; gap: var(--s2); }
.tool-item .tool-method { font-size: var(--t-xs); font-weight: 700; padding: 2px var(--s2); border-radius: var(--r); background: var(--grad-brand-soft); color: var(--kq-blue-600); letter-spacing: .04em; }
.tool-item .tool-desc { color: var(--muted); font-size: var(--t-md); margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 520px; }
.tool-item .tool-lead { display: flex; align-items: center; gap: var(--s3); min-width: 0; }

/* Circular service badge — marks an HTTP/API item (a credential, a connection, or a tool). */
.svc-icon {
    display: inline-flex; align-items: center; justify-content: center;
    width: 34px; height: 34px; border-radius: var(--r-circle); flex-shrink: 0;
    background: var(--kq-cyan); color: #fff;
}
.svc-icon svg { width: 19px; height: 19px; }
.svc-icon.sm { width: 28px; height: 28px; }
.svc-icon.sm svg { width: 16px; height: 16px; }

/* ==========================================================================
   Badges / chips
   ========================================================================== */
.badge {
    display: inline-flex; align-items: center; gap: var(--s1);
    font-size: var(--t-xs); font-weight: 600; padding: var(--s1) var(--s3); border-radius: var(--r-pill); line-height: 1.4;
}
.badge .dot { width: 7px; height: 7px; border-radius: var(--r-circle); background: currentColor; }
.badge-ok { color: var(--ok); background: var(--ok-bg); }
.badge-danger { color: var(--danger); background: var(--danger-bg); }
.badge-warn { color: var(--warn); background: var(--warn-bg); }
.badge-info { color: var(--info); background: var(--info-bg); }
.badge-muted { color: var(--muted); background: #e7e9ec; }
/* Premium feature marker — a warm gold gradient, distinct from the status colours. */
.badge-premium {
    color: #7a4e00; background: linear-gradient(135deg, #ffe89e, #f5b83d);
    font-size: var(--t-xs); font-weight: 800; letter-spacing: .06em; padding: var(--s1) var(--s2);
    box-shadow: 0 1px 2px rgba(180,120,0,.25);
}
.nav-premium { margin-inline-start: auto; transform: scale(.9); }
.chip-role { font-size: var(--t-xs); font-weight: 600; padding: var(--s1) var(--s2); border-radius: var(--r); background: var(--grad-brand-soft); color: var(--kq-indigo); }
.chip-role.admin { background: rgba(var(--kq-cyan-rgb), .14); color: var(--kq-azure); }

/* ==========================================================================
   Forms
   ========================================================================== */
.form-row { margin-bottom: var(--s4); }
.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s4) var(--s4); }
.grid-method-url { display: grid; grid-template-columns: 130px 1fr; gap: var(--s4) var(--s4); align-items: start; }
label.lbl { display: block; font-weight: 600; font-size: var(--t-md); margin-bottom: var(--s2); color: var(--ink-2); }
label.lbl .req { color: var(--danger); }
.control, input.control, select.control {
    width: 100%; padding: var(--s3) var(--s3); font: inherit; color: var(--ink);
    background: color-mix(in srgb, #fff 62%, transparent); border: 1px solid color-mix(in srgb, var(--n-6) 70%, transparent); border-radius: var(--radius-xs);
    transition: border-color var(--t-fast) var(--e-standard), box-shadow var(--t-fast) var(--e-standard);
}
.control:focus { outline: none; border-color: var(--kq-blue); box-shadow: var(--ring); }
/* `.control:focus` is (0,2,0) and the global `:focus-visible` is (0,1,0), so that
   `outline: none` was winning on KEYBOARD focus too — leaving every input in the app,
   the login form included, with only --ring, which composites to #ccecee over white:
   1.23:1. Same specificity, placed after, so the keyboard gets a ring and a mouse
   click still does not. */
.control:focus-visible {
    outline: 2px solid var(--ink); outline-offset: 1px;
    box-shadow: 0 0 0 2px #fff, var(--ring);
}
/* 2.65:1 before — a placeholder is text and has to be readable, not a hint of one */
.control::placeholder { color: #6a7280; }
.hint { font-size: var(--t-sm); color: var(--muted); margin-top: var(--s1); }

/* Country select with flag */
.cselect { position: relative; }
.cselect__btn { display: flex; align-items: center; gap: var(--s2); width: 100%; text-align: right; cursor: pointer; }
.cselect__btn .cselect__name { flex: 1; }
.cselect__flag { display: inline-flex; flex: none; }
.cselect__chev { flex: none; color: var(--muted); display: inline-flex; }
.cselect__chev svg { width: 16px; height: 16px; }
.cselect.open .cselect__chev { transform: rotate(180deg); }
.cselect__list { position: absolute; top: calc(100% + 4px); inset-inline: 0; z-index: 30; margin: 0; padding: var(--s1);
    list-style: none; background: color-mix(in srgb, #fff 82%, transparent);
    -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur); border: 1px solid var(--glass-brd); border-radius: var(--r);
    box-shadow: 0 12px 34px -14px rgba(15, 23, 42, .28); max-height: 280px; overflow: auto; }
.cselect__opt { display: flex; align-items: center; gap: var(--s2); padding: var(--s2) var(--s3); border-radius: var(--r); cursor: pointer; }
.cselect__opt:hover { background: var(--line-2); }
.field-error { color: var(--danger); font-size: var(--t-sm); margin-top: var(--s1); }
.input-pw { position: relative; }
/* Reserve room for the eye toggle (sits at the physical-left in this RTL app) so
   the password text — RTL or LTR (dir="ltr") — never runs under it. */
.input-pw .control { padding-inline-end: var(--s7); }
.input-pw .toggle {
    position: absolute; inset-inline-end: 6px; top: 50%; transform: translateY(-50%);
    background: none; border: 0; cursor: pointer; color: var(--muted); padding: var(--s1); border-radius: var(--r);
}
.input-pw .toggle:hover { background: var(--bg); color: var(--ink); }

/* The in-house icon set and its turquoise accent were reverted 2026-08-03 (owner: "תחזיר
   את האימוג'ים הרגילים"). The icons are plain single-colour strokes again, so the accent
   variable and the per-surface flips that went with it are gone rather than left inert. */

.checkbox { display: inline-flex; align-items: center; gap: var(--s2); cursor: pointer; font-size: var(--t-md); }
/* RADIOS ONLY. This used to read `.checkbox input`, which also sized the checkboxes and then
   fought the switch rules below on equal specificity — whichever came last in the file won. */
.checkbox input[type="radio"] { width: 18px; height: 18px; accent-color: var(--kq-blue); }

/* ── Toggles ─────────────────────────────────────────────────────────────────────────
   Every checkbox on the platform is drawn as a switch (owner 2026-08-01: "שכל הצ'קבוקסים
   יהפכו להיות מתגים"). Applied to the ELEMENT, not to an opt-in class, so a checkbox added
   anywhere later is a switch too without anyone having to remember.
   type="checkbox" ONLY: the same .checkbox wrapper also holds RADIO inputs — the transfer
   insistence levels, the address mode — and an exclusive choice drawn as three switches reads
   as three independent settings. .ci-advtoggle is excluded because it is not a control at all;
   it is a hidden CSS mechanism behind the advanced-filter button. */
input[type="checkbox"]:not(.ci-advtoggle) {
    appearance: none; -webkit-appearance: none;
    position: relative; flex: 0 0 auto; display: inline-block;
    width: 40px; height: 22px; border-radius: var(--r-pill); margin: 0;
    /* The OFF track is a CONTROL BOUNDARY, and WCAG 1.4.11 wants 3:1 for one. It was --line
       at 1.34:1 on a white card and 1.15:1 on the page ground — functionally invisible, on
       the most-flipped control in the product. A switch nobody can see the off-state of is a
       switch whose state has to be guessed. --line-strong measures 3.10:1. */
    background: var(--line-2); border: 1px solid var(--line-strong);
    cursor: pointer; vertical-align: middle;
    transition: background var(--t-base) var(--e-out), border-color var(--t-base) var(--e-out);
}
/* the knob. inset-inline-start, not left: in RTL the switch must start on the right and
   travel left, and a hard-coded `left` would run the wrong way on every screen we have. */
/* The knob travels with `translate`, not `inset-inline-start`. An inset animation re-runs
   LAYOUT on every frame; every checkbox on the platform is a switch, so this is the most
   flipped control in the product and the one worth putting on the compositor.
   38px containing block − 16px knob − 2px inset = 18px of travel, signed by --kq-x so it
   starts at the inline-start edge and moves the right way in RTL. */
input[type="checkbox"]:not(.ci-advtoggle)::after {
    content: ""; position: absolute; top: 50%; inset-inline-start: 2px;
    width: 16px; height: 16px; border-radius: var(--r-circle); background: #fff;
    box-shadow: 0 1px 3px rgba(0, 0, 0, .3);
    transform: translateY(-50%);
    translate: 0;
    transition: translate var(--t-base) var(--e-out);
}
input[type="checkbox"]:not(.ci-advtoggle):checked {
    background: var(--kq-cyan, #00ADB5); border-color: var(--kq-cyan, #00ADB5);
}
input[type="checkbox"]:not(.ci-advtoggle):checked::after { translate: calc(18px * var(--kq-x)); }
input[type="checkbox"]:not(.ci-advtoggle):disabled { opacity: .45; cursor: not-allowed; }
/* No focus rule here on purpose: a teal ring measures 2.75:1 on white, and this one was more
   specific than the global two-tone ring, so switches would have kept the failing colour. */
/* a switch is wider than the 18px box it replaced, so a label that hung its text off the old
   width needs the flex row to keep the two apart rather than let the text ride the track */
.checkbox > input[type="checkbox"] { margin-inline-end: 0; }

/* ── THE ONE DOCUMENTED OPT-OUT FROM THE SWITCH RULE (owner 2026-08-06: "שבעמוד התחברות
   לממשק יהיה צ'קבוקס בזכור אותי ולא מתג") ────────────────────────────────────────────────
   Every checkbox on the platform is a switch, and that stays true — the rule above still
   targets the ELEMENT, so anything added later is a switch without anyone remembering. This is
   an explicit opt-out, the same shape as .ci-advtoggle, not a hole in the rule.
   The reason it is right here: a switch says "this setting is now on", something that takes
   effect the moment you flip it. «זכור אותי» does nothing until the form is submitted — it is a
   choice ABOUT the submission, like the fields above it, and a switch on a login form promises an
   immediacy the control does not have.
   appearance:auto and not a hand-drawn box: the native checkbox is the one control every user
   already recognises, and accent-color tints it without taking over its rendering. */
input[type="checkbox"].kq-plain {
    appearance: auto; -webkit-appearance: auto;
    width: 18px; height: 18px; border-radius: 0;
    background: none; border: 0;
    accent-color: var(--kq-blue);
    transition: none; cursor: pointer;
}
/* the knob belongs to the track this control no longer has */
input[type="checkbox"].kq-plain::after { content: none; }
.form-row.narrow { max-width: 260px; }

/* Grouped settings sections + capability rows (voice-engine settings screen) */
.section-head { display: flex; align-items: flex-start; gap: var(--s3); margin-bottom: var(--s4); }
.section-head .t { font-size: var(--t-base); font-weight: 700; }
.section-head .d { font-size: var(--t-md); color: var(--muted); margin-top: var(--s1); }
.section-sep { height: 1px; background: var(--line); margin: var(--s1) 0 var(--s4); }
.feature { padding: var(--s4) 0; border-bottom: 1px solid var(--line-2); }
.feature:first-of-type { padding-top: var(--s1); }
.feature:last-of-type { border-bottom: 0; padding-bottom: var(--s1); }
.feature > .checkbox { font-size: var(--t-base); font-weight: 600; }
.feature .d { font-size: var(--t-md); color: var(--muted); margin: var(--s1) 0 0; padding-inline-start: var(--s5); }
.feature .form-row { margin: var(--s3) 0 0; padding-inline-start: var(--s5); }

/* ==========================================================================
   Alerts
   ========================================================================== */
.alert {
    display: flex; align-items: flex-start; gap: var(--s3);
    padding: var(--s3) var(--s4); border-radius: var(--radius-sm); font-size: var(--t-md); margin-bottom: var(--s4);
    border: 1px solid transparent;
}
.alert svg { width: 20px; height: 20px; flex: 0 0 20px; margin-top: 1px; }
.alert-ok { background: var(--ok-bg); color: var(--ok-ink); border-color: #bce7cd; }
.alert-danger { background: var(--danger-bg); color: #a81f1f; border-color: #f4cccc; }
.alert-info { background: var(--info-bg); color: #0a6a70; border-color: #b6e2e4; }
.alert-warn { background: var(--warn-bg); color: #9a5a06; border-color: #f2d7ac; }
.alert ul { margin: var(--s1) 0 0; padding-inline-start: var(--s4); }

/* ==========================================================================
   Page header + misc
   ========================================================================== */
/* The hairline under the header is the dashboard's separator device, applied to the shared
   page header so every screen divides the same way. One device, used everywhere: the
   moment there are two, they compete and the page reads busy again. */
.page-head { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--s4);
             margin-bottom: var(--s5); padding-block-end: var(--s4); flex-wrap: wrap;
             border-block-end: 1px solid var(--line); }
.page-head .t { font-size: var(--t-xl); margin: 0; }
.page-head .d { color: var(--muted); margin: var(--s1) 0 0; }

.empty { text-align: center; padding: var(--s7) var(--s4); color: var(--muted); }
.empty svg { width: 54px; height: 54px; opacity: .4; margin-bottom: var(--s3); }

.divider { height: 1px; background: var(--line); margin: var(--s5) 0; }

.kv { display: grid; grid-template-columns: max-content 1fr; gap: var(--s2) var(--s4); font-size: var(--t-base); }
.kv dt { color: var(--muted); }
.kv dd { margin: 0; font-weight: 500; }

.tag-copy { font-family: "Assistant", ui-monospace, "SFMono-Regular", Menlo, monospace; font-size: var(--t-sm); background: var(--bg); padding: 2px var(--s2); border-radius: var(--r); border: 1px solid var(--line); }

/* password meter */
.pw-meter { height: 6px; border-radius: var(--r); background: var(--line); overflow: hidden; margin-top: var(--s2); }
.pw-meter > span { display: block; height: 100%; width: 0; transition: width var(--t-base) var(--e-out), background var(--t-base) var(--e-out); }

/* ==========================================================================
   Auth (login / change password) split screen
   ========================================================================== */
.auth {
    min-height: 100vh; display: grid; grid-template-columns: 1.05fr .95fr;
    background: var(--bg);
}
.auth__brand {
    /* same dark identity as the sidebar/menu */
    background: var(--grad-sidebar);
    color: #cdd5db; position: relative; overflow: hidden;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    text-align: center; padding: var(--s7);
}
.auth__brand::after {
    content: ""; position: absolute; inset: 0;
    background:
        radial-gradient(620px 320px at 12% 12%, rgba(var(--kq-cyan-rgb), .22), transparent 60%),
        radial-gradient(520px 320px at 90% 88%, rgba(var(--kq-cyan-rgb), .12), transparent 55%);
    opacity: 1;
}
.auth__brand > * { position: relative; z-index: 1; }
.auth__hero { display: flex; flex-direction: column; align-items: center; }
.auth__hero img { width: min(80%, 460px); height: auto; filter: drop-shadow(0 12px 30px rgba(0,0,0,.35)); }
.auth__hero-name { font-family: "Assistant", sans-serif; font-weight: 700; color: #fff; font-size: 4.4rem; line-height: 1; letter-spacing: -.02em; }
.auth__hero-sub { font-size: var(--t-lg); letter-spacing: .3em; text-transform: uppercase; color: #cdd5db; margin-top: var(--s3); }

.auth__panel { display: flex; align-items: center; justify-content: center; padding: var(--s6); }
.auth__card { width: 100%; max-width: 400px; }
.auth__card h1 { font-size: var(--t-xl); margin-bottom: var(--s1); }
.auth__card .lead { color: var(--muted); margin-bottom: var(--s5); }
.auth__mobilelogo { display: none; align-items: center; justify-content: center; margin-bottom: var(--s5); }
.auth__mobilelogo img { width: 200px; height: auto; }
.auth__mobilelogo .name { font-family: "Assistant", sans-serif; font-weight: 700; font-size: var(--t-xl); }

/* ==========================================================================
   Responsive
   ========================================================================== */
.menu-toggle { display: none; }
@media (max-width: 980px) {
    .auth { grid-template-columns: 1fr; }
    .auth__brand { display: none; }
    .auth__mobilelogo { display: flex; }
    .grid.cols-4 { grid-template-columns: repeat(2, 1fr); }
    .grid.cols-3 { grid-template-columns: 1fr; }
    .grid.cols-2 { grid-template-columns: 1fr; }
    .form-grid { grid-template-columns: 1fr; }
    .grid-method-url { grid-template-columns: 1fr; }
}
@media (max-width: 820px) {
    .sidebar { transform: translateX(100%); transition: transform var(--t-base) var(--e-out); }
    .sidebar.open { transform: translateX(0); }
    .main { margin-inline-start: 0; }
    .menu-toggle { display: inline-flex; }
    .content { padding: var(--s4) var(--s4) var(--s6); }
    .topbar { padding: var(--s3) var(--s4); }
}

/* scrim for mobile sidebar */
.scrim { display: none; position: fixed; inset: 0; background: rgba(12,8,45,.4); z-index: 35; }
.scrim.show { display: block; }

/* ─────────── monthly call summary (tenant dashboard) ─────────── */
.msum-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--s3); flex-wrap: wrap; margin-bottom: var(--s4); }
.msum-title { display: flex; align-items: flex-start; gap: var(--s3); }
.msum-title .t { font-size: var(--t-base); font-weight: 700; margin: 0; }
.msum-title .d { color: var(--muted); font-size: var(--t-md); margin: 2px 0 0; }
.msum-nav { display: flex; align-items: center; gap: var(--s1); background: var(--bg); border: 1px solid var(--line); border-radius: var(--r-pill); padding: var(--s1); }
.msum-nav-btn { display: flex; align-items: center; justify-content: center; width: 30px; height: 30px; border-radius: var(--r-circle); font-size: var(--t-lg); line-height: 1; color: var(--ink); text-decoration: none; transition: background .15s, color .15s; }
.msum-nav-btn:hover { background: var(--grad-brand); color: #fff; }
.msum-nav-btn.disabled { color: var(--line); pointer-events: none; }
.msum-month { min-width: 118px; text-align: center; font-weight: 700; font-size: var(--t-base); padding: 0 var(--s2); }
.msum-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s3); }
.msum-tile { position: relative; overflow: hidden; background: var(--grad-brand-soft); border: 1px solid var(--line); border-radius: var(--r); padding: var(--s4) var(--s4) var(--s4); text-align: center; }
.msum-tile::before { content: ''; position: absolute; inset-inline-start: 0; top: 0; bottom: 0; width: 4px; background: var(--grad-brand); border-radius: 0 3px 3px 0; }
.msum-ico { display: inline-flex; align-items: center; justify-content: center; width: 40px; height: 40px; border-radius: var(--r); margin-bottom: var(--s2); }
.msum-ico svg { width: 20px; height: 20px; }
.msum-ico.in   { background: var(--info-bg); color: var(--info); }
.msum-ico.out  { background: var(--ok-bg); color: var(--ok); }
.msum-ico.time { background: var(--warn-bg); color: var(--warn); }
.msum-val { font-size: var(--t-2xl); font-weight: 800; letter-spacing: -.02em; font-variant-numeric: tabular-nums slashed-zero; font-family: "Assistant", ui-monospace, monospace; color: var(--ink); line-height: 1.15; }
.msum-lbl { color: var(--muted); font-size: var(--t-md); font-weight: 600; margin-top: 2px; }
.msum-note { color: var(--muted); font-size: var(--t-sm); margin: var(--s3) 2px 0; }
@media (max-width: 640px) { .msum-grid { grid-template-columns: 1fr; } .msum-head { flex-direction: column; } }

/* ==========================================================================
   Dashboard — statistics, charts, breakdowns
   ========================================================================== */
.delta { display: inline-flex; align-items: center; gap: var(--s1); margin-top: var(--s2); font-size: var(--t-xs); font-weight: 700; padding: var(--s1) var(--s2); border-radius: var(--r-pill); }
.delta.up   { background: var(--ok-bg); color: var(--ok); }
.delta.down { background: var(--danger-bg); color: var(--danger); }

.dash-substats { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s3); margin-top: var(--s3); }
.sub-stat { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s2); background: var(--bg); border: 1px solid var(--line); border-radius: var(--r); padding: var(--s3) var(--s3); }
.sub-stat-lbl { color: var(--muted); font-size: var(--t-sm); font-weight: 600; }
.sub-stat-val { font-size: var(--t-lg); font-weight: 800; font-variant-numeric: tabular-nums slashed-zero; font-family: "Assistant", ui-monospace, monospace; color: var(--ink); white-space: nowrap; }

.dash-two { display: grid; grid-template-columns: 3fr 2fr; gap: var(--s4); margin-top: var(--s4); align-items: stretch; }
.dash-two.alt { grid-template-columns: 2fr 3fr; }
.chart-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s2); margin-bottom: var(--s3); }
.chart-head.pad { padding: var(--s4) var(--s4) 0; margin-bottom: var(--s1); }
.chart-head .t { font-size: var(--t-base); font-weight: 700; margin: 0; }
.chart-scroll { overflow-x: auto; }
.chart-svg { width: 100%; height: auto; display: block; }
.chart-svg .chart-xlabel { font-size: var(--t-xs); fill: var(--muted); font-family: inherit; }
.chart-svg .chart-vlabel { font-size: var(--t-xs); font-weight: 700; fill: var(--ink-2); font-family: inherit; font-variant-numeric: tabular-nums slashed-zero; font-family: "Assistant", ui-monospace, monospace; }
.chart-svg rect { transition: opacity var(--t-fast) var(--e-standard); }
.chart-svg rect:hover { opacity: 1; }
.empty.slim { padding: var(--s5) var(--s2); }

.bshare-list { display: flex; flex-direction: column; gap: var(--s3); }
.bshare-top { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s2); margin-bottom: var(--s1); }
.bshare-name { font-weight: 600; font-size: var(--t-md); color: var(--ink); }
.bshare-nums { color: var(--muted); font-size: var(--t-sm); white-space: nowrap; font-variant-numeric: tabular-nums slashed-zero; font-family: "Assistant", ui-monospace, monospace; }
.bshare-bar { height: 7px; border-radius: var(--r-pill); background: var(--bg); border: 1px solid var(--line); overflow: hidden; }
.bshare-bar span { display: block; height: 100%; border-radius: var(--r-pill); background: var(--grad-brand); min-width: 4px; }

.recent-tbl td { padding-top: var(--s2); padding-bottom: var(--s2); }

@media (max-width: 900px) { .dash-two, .dash-two.alt { grid-template-columns: 1fr; } .dash-substats { grid-template-columns: 1fr; } }

/* DID quick-assign on the bot list — a flag-inside country-select; picking saves */
/* While a picker is open, the table's scroll container must not clip its list. */
.table-wrap:has([data-cselect].open) { overflow: visible; }
.did-assign .cselect { min-width: 195px; max-width: 250px; }
.did-assign .cselect__btn { padding: var(--s2) var(--s3); font-size: var(--t-md); }
.did-assign .cselect__list { max-height: 220px; overflow-y: auto; }
.cselect__opt[data-disabled] { opacity: .45; cursor: not-allowed; }
.cselect__opt .opt-note { margin-inline-start: auto; font-size: var(--t-xs); font-weight: 600; color: var(--warn); white-space: nowrap; }

/* ───────────── לוח בקרה v2 (owner 2026-07-22): editorial-clean, no icons ───────────── */

@media (max-width: 900px) {
}

/* ── שדות חובה (חוק 2026-07-24): כוכבית על lbl req בלבד; שדה רשות ללא סימון ── */
.lbl.req::after { content: " *"; color: #d64545; font-weight: 700; }

/* ── סיכומי שיחות — תצוגת כרטיסיות (2026-07-24, סבב 2: היררכיה ורווחים לפי הבעלים) ── */
.ci-grid { display: grid; grid-template-columns: 1fr; gap: var(--s3); }
.ci-card { background: var(--panel, #fff); border: 1px solid var(--line, var(--n-4)); border-radius: var(--r);
           padding: var(--s4) var(--s5); display: flex; flex-direction: column;
           transition: box-shadow var(--t-fast) var(--e-standard); }
.ci-card:hover { box-shadow: 0 4px 18px rgba(14, 143, 150, .10); }
/* העיקר בימין: מתקשר + בוט; המשני בשמאל: כיוון + תאריך-שעה */
.ci-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--s3); }
.ci-who { display: flex; flex-direction: column; gap: 2px; }
.ci-caller { font-weight: 700; font-size: var(--t-lg); letter-spacing: .01em; }
.ci-bot { color: var(--muted, var(--n-8)); font-size: var(--t-sm); }
.ci-meta { display: flex; flex-direction: column; align-items: flex-end; gap: var(--s1); }
.ci-when { color: var(--muted, var(--n-8)); font-size: var(--t-xs); font-variant-numeric: tabular-nums slashed-zero; font-family: "Assistant", ui-monospace, monospace; white-space: nowrap; }
.ci-summary { line-height: 1.65; font-size: var(--t-md); margin: var(--s3) 0 0;
              padding-top: var(--s3); border-top: 1px solid var(--line, var(--n-4)); }
/* כל שדה בשורה נפרדת */
.ci-fields { display: flex; flex-direction: column; gap: var(--s2); font-size: var(--t-md);
             background: var(--bg, var(--n-1)); border-radius: var(--r); padding: var(--s3) var(--s3); margin-top: var(--s3); }
.ci-field { display: flex; gap: var(--s2); align-items: baseline; }
.ci-k { color: var(--kq-azure, #0E8F96); font-weight: 600; white-space: nowrap; }
.ci-k::after { content: ":"; }
.ci-v { overflow-wrap: anywhere; line-height: 1.5; }
.ci-foot { display: flex; gap: var(--s2); flex-wrap: wrap; margin-top: var(--s3); }

/* ── סיכומי שיחות: סרגל סינון + פאנל מתקדם מתקפל (CSS בלבד, בלי JS) ── */
/* ═══ THE FILTER BAR, AS A TOOLBAR (owner 2026-08-03: "שיהיה הרבה יותר יפה ומקצועי") ═══
   It used to be loose controls floating on the page background between the heading and the
   table, with the advanced panel opening as a SECOND bordered box below it — so turning on a
   filter grew a second object on the page instead of extending the first one.
   Now it is one surface. The panel opens inside it, under a hairline, and the applied filters
   are listed back as chips so you can see what the list is filtered by WITHOUT opening the
   panel. That was the real gap: with the panel shut, a date range or a sentiment filter was
   invisible, and a short result set looked like the whole journal. */
.ci-filterbar {
    margin-bottom: var(--s3); padding: var(--s3) var(--s3);
    background: var(--glass-bg); border: 1px solid var(--glass-brd); border-radius: var(--radius-sm);
    box-shadow: var(--glass-shine), var(--glass-shadow);
}
.ci-filterrow { display: flex; gap: var(--s2); align-items: center; flex-wrap: wrap; }
/* Toolbar controls stand down to the height of a small button. A 44px field is right in a form,
   where it is the thing you came to fill in; in a strip of buttons it is just the loudest. */
.ci-filterrow .control { padding: var(--s2) var(--s3); font-size: var(--t-md); border-radius: var(--r); }
.ci-filterrow .search-box .control { padding-inline-start: var(--s6); }
.ci-filterrow .search-icon svg { width: 15px; height: 15px; }
/* The search sits at the far edge of the filter row, the same edge on every tab (owner
   2026-08-03: "באותה שורה של הסינון בצד שמאל"). It is logical, not left: in RTL the auto
   margin pushes it to the left end, and the same rule would send it right in an LTR tenant.
   When the row is too narrow to hold both, the auto margin is dropped rather than left to
   strand the field mid-line — it takes the next line whole. */
.ci-filterrow .search-box { margin-inline-start: auto; flex: 0 1 320px; min-width: 220px; }
@media (max-width: 760px) {
    .ci-filterrow .search-box { margin-inline-start: 0; flex: 1 1 100%; }
}
.ci-advtoggle { position: absolute; opacity: 0; pointer-events: none; }
.ci-advtoggle:checked + label { background: var(--kq-azure, #0E8F96); color: #fff; border-color: var(--kq-azure, #0E8F96); }
.ci-advpanel { display: none; }
.ci-advtoggle:checked ~ .ci-advpanel,
.ci-filterbar:has(.ci-advtoggle:checked) .ci-advpanel {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 220px)); gap: var(--s3);
    margin-top: var(--s3); padding-top: var(--s3); border-top: 1px solid var(--line-2);
}
.ci-advfield .lbl { font-size: var(--t-sm); margin-bottom: var(--s1); display: block; }

/* Applied filters, said back. Each chip drops its own filter and keeps the rest — which is
   what makes the row usable: narrowing down is easy on any interface, and backing out one step
   is the part people cannot do without re-picking everything from scratch. */
.ci-chips { display: flex; gap: var(--s2); align-items: center; flex-wrap: wrap;
            margin-top: var(--s3); padding-top: var(--s3); border-top: 1px solid var(--line-2); }
.ci-chips__lbl { font-size: var(--t-xs); color: var(--muted); font-weight: 600; }
.ci-chip {
    display: inline-flex; align-items: center; gap: var(--s2);
    padding: var(--s1) var(--s2) var(--s1) var(--s3); border-radius: var(--r-pill);
    background: var(--bg); border: 1px solid var(--line);
    font-size: var(--t-sm); color: var(--ink-2); text-decoration: none;
    transition: border-color var(--t-fast) var(--e-standard),
                background-color var(--t-fast) var(--e-standard);
}
.ci-chip b { font-weight: 650; color: var(--ink); }
/* the × is drawn, not typed: a font glyph would sit off-centre and change size with the face */
.ci-chip svg { width: 13px; height: 13px; color: var(--muted); flex: none; }
@media (hover: hover) {
    .ci-chip:hover { background: var(--danger-bg); border-color: #f3c9c9; }
    .ci-chip:hover svg { color: var(--danger); }
}

/* ═══ THE CALL JOURNAL (owner 2026-08-03: "הרבה יותר יפה ומובן") ═══════════════════════
   What was wrong was not the styling, it was that the row spent its weight on the wrong things.
   Two of the seven columns — direction and status — held the SAME value on essentially every
   row, each drawn as a filled pill, so the two loudest marks in the table were the two carrying
   the least information. Meanwhile the timestamp, which is what the journal is ordered by and
   what a reader scans for, was set in the quietest style on the row.
   So: the exceptions get the colour, the constants get an icon, and the time gets the weight. */
/* ═══ «קו» — THE STYLISTIC LAYER, ON THIS ONE SCREEN (2026-08-04) ═════════════════════
   A telephone line is a קו; a hairline rule is a קו. This screen is a board of lines that
   were answered or missed, so it is built from ONE primitive — the rule — and everything
   else is stripped until the rule is all that holds the page together. No card, no box, no
   shadow: the journal sits directly on the sheet and structure comes from alignment.

   Onto that silence, one inversion: THE FIGURE IS THE ONLY INK-BLACK THING HERE. Times,
   phone numbers and durations are set in --ink; every word — column headers, the bot's name,
   the "אל" label, the status — recedes to --n-8. The Hebrew becomes annotation and the data
   comes forward, which is the hierarchy an operations console needs and the exact inversion
   of the bold-label/grey-value pattern every generated dashboard ships.

   THE STEP IS THE WHOLE POINT, AND IT WAS NEARLY TOO SMALL. The proposal originally put the
   chrome at --n-9 #38414C: against --ink #14191F that is 1.74:1, weaker than the 3.4:1 of
   the cliché it claims to invert, i.e. imperceptible — the exact failure the owner has
   rejected twice. --n-8 #545D69 gives 2.70:1, and the mono face carries the rest.

   Scoped to .log-block on purpose. It goes no further until it has been looked at. */
.log-block { background: none; }
.calltbl { width: 100%; border-collapse: collapse; }
.calltbl th {
    /* no uppercase, no tracking: Hebrew is caseless, so text-transform is a no-op that
       leaves only the letter-spacing behind — and Hebrew must never be tracked. */
    text-align: start; font-size: var(--t-sm);
    color: var(--n-8); font-weight: 500; padding: var(--s2) var(--s4) var(--s3);
    border-bottom: 1px solid var(--n-5); white-space: nowrap;
}
.calltbl th a { color: inherit; text-decoration: none; }
@media (hover: hover) { .calltbl th a:hover { color: var(--ink-2); } }
/* 8px, not the 14px a form-like table would use: two of the six cells are two lines tall, so the
   padding is added to a 34px cell rather than a 20px one and the row grows twice as fast as the
   number suggests. */
.calltbl td { padding: var(--s3) var(--s4); border-bottom: 1px solid var(--n-4); vertical-align: middle;
              color: var(--n-8); }
.calltbl tr:last-child > td { border-bottom: 0; }
@media (hover: hover) { .calltbl tbody tr[data-calllog-row]:hover > td { background: var(--row-hover); } }

/* WHEN — the sort key of the whole screen, so it reads first. Time over date, because within a
   journal you are almost always placing a call inside a day you already know. */
.log-when { line-height: 1.2; }
.log-when .h { color: var(--ink); font-weight: 500; font-variant-numeric: tabular-nums slashed-zero; font-family: "Assistant", ui-monospace, monospace; }
.log-when .d { display: block; font-size: var(--t-xs); color: var(--n-8); font-variant-numeric: tabular-nums slashed-zero; font-family: "Assistant", ui-monospace, monospace; }

/* WHO — both ends of the call, which is the whole point of a phone record and was half missing
   (owner: "שיופיע גם המספר טלפון שחוייג לא רק שחייג"). Labelled rather than arrowed: an arrow
   has to be read against the page direction before it means anything, and this interface is RTL
   with LTR phone numbers inside it — two directions in one glyph is one too many. */
.log-who { display: grid; grid-template-columns: max-content minmax(0, 1fr);
           gap: 0 var(--s2); line-height: 1.25; }
.log-who dt { font-size: var(--t-xs); color: var(--n-8); align-self: center; }
.log-who dd { margin: 0; font-variant-numeric: tabular-nums slashed-zero; font-family: "Assistant", ui-monospace, monospace; }
.log-who .from { color: var(--ink); font-weight: 500; }
.log-who .to { color: var(--n-8); font-size: var(--t-sm); }
.log-num { display: inline-flex; align-items: center; gap: var(--s1); }
/* a duration is a figure: ink, the figure face, tabular so the column aligns */
.log-dur { color: var(--ink); font-variant-numeric: tabular-nums slashed-zero;
           font-family: "Assistant", ui-monospace, monospace; }

/* the direction mark rides with the numbers instead of owning a column of identical pills */
.log-dir { display: inline-flex; color: var(--n-6); }
.log-dir svg { width: 15px; height: 15px; }
.log-dir.is-out { color: var(--info); }
.log-dir.is-web { color: var(--kq-indigo, #3f4a8a); }

/* RESULT — an answered call is the norm and gets a quiet tick; a missed one is the exception a
   reader is scanning for, and it is the only thing in the row allowed to be red. */
.log-res { display: inline-flex; align-items: center; gap: var(--s1); font-size: var(--t-sm); white-space: nowrap; }
.log-res svg { width: 15px; height: 15px; }
.log-res.ok { color: var(--n-8); }
.log-res.miss { color: var(--danger-ink); font-weight: 600; }

.log-foot {
    color: var(--n-8);
    display: flex; align-items: center; justify-content: center; gap: var(--s3);
    padding: var(--s3) var(--s4); border-top: 1px solid var(--line); font-size: var(--t-md); color: var(--muted);
}

/* ── לוח פלטפורמה: קוביות קיבולת פר-טננט (2026-07-25) ── */
/* min(340px, 100%) and not a bare 340px: a fixed track floor cannot shrink, so on a 360px phone
   (332px of content) the grid came out wider than the screen and the PAGE scrolled sideways. */
.tn-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(340px, 100%), 1fr)); gap: var(--s4); }
.tn-card { background: var(--panel, #fff); border: 1px solid var(--line, var(--n-4)); border-radius: var(--r);
           padding: var(--s4) var(--s4); display: flex; flex-direction: column; gap: var(--s3);
           border-inline-start-width: 4px; }
.tn-card.sev-red { border-inline-start-color: #d64545; }
.tn-card.sev-yellow { border-inline-start-color: #d97706; }
.tn-card.sev-ok { border-inline-start-color: var(--kq-azure, #0E8F96); }
.tn-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--s2); }
.tn-name { font-weight: 700; font-size: var(--t-lg); color: var(--ink, #1c2126); text-decoration: none; }
.tn-name:hover { color: var(--kq-azure, #0E8F96); }
.tn-chips { display: flex; gap: var(--s1); flex-wrap: wrap; justify-content: flex-end; }
.tn-metric { display: flex; flex-direction: column; gap: var(--s1); }
.tn-metric-row { display: flex; justify-content: space-between; align-items: baseline; gap: var(--s2); }
.tn-lbl { color: var(--muted, var(--n-8)); font-size: var(--t-sm); font-weight: 600; }
.tn-val { font-size: var(--t-md); font-variant-numeric: tabular-nums slashed-zero; font-family: "Assistant", ui-monospace, monospace; }
.tn-bar { height: 6px; background: var(--line, var(--n-4)); border-radius: var(--r); overflow: hidden; }
.tn-bar span { display: block; height: 100%; border-radius: var(--r); }
.tn-bar .b-ok { background: var(--kq-azure, #0E8F96); }
.tn-bar .b-yellow { background: #d97706; }
.tn-bar .b-red { background: #d64545; }
.tn-reco { font-size: var(--t-sm); line-height: 1.45; color: var(--muted, var(--n-8)); }
.tn-reco.r-yellow { color: var(--warn-ink); font-weight: 600; }
.tn-reco.r-red { color: #b91c1c; font-weight: 600; }
@media (max-width: 640px) { .tn-grid { grid-template-columns: 1fr; } }

/* ── מיתוג מפיץ: בורר צבעים ותצוגת לוגו (2026-07-26) ──
   בורר צבעים בלי JS: רדיו מוסתר + label כדוגמית. ה-CSP חוסם סקריפט בשורה,
   ולכן הבחירה כולה מתבצעת ב-CSS דרך :checked + סלקטור אחים. */
.sw-row { display: flex; flex-wrap: wrap; gap: var(--s2); align-items: center; }
.sw-row input[type="radio"] { position: absolute; opacity: 0; pointer-events: none; }
.sw {
    width: 30px; height: 30px; border-radius: var(--r); cursor: pointer; display: block;
    border: 2px solid var(--line-2, var(--n-4)); box-shadow: inset 0 0 0 2px #fff;
    transition: transform var(--t-fast) var(--e-standard), border-color var(--t-fast) var(--e-standard);
}
.sw:hover { transform: translateY(-1px); }
.sw-row input[type="radio"]:checked + .sw { border-color: var(--ink, #222831); transform: translateY(-1px); }
.sw-row input[type="radio"]:focus-visible + .sw { box-shadow: var(--ring); }
/* דוגמית "ללא צבע מותאם" — משבצת שקופה עם קו אלכסוני */
.sw.sw-none {
    background: linear-gradient(135deg, #fff 45%, #d64545 45%, #d64545 55%, #fff 55%);
}
.brand-logo-box {
    display: flex; align-items: center; gap: var(--s3); padding: var(--s3) var(--s3);
    background: var(--ink, #222831); border-radius: var(--r);
}
.brand-logo-box img { max-height: 40px; max-width: 190px; }
.brand-logo-box .brand-logo-none { color: #fff; opacity: .6; font-size: var(--t-md); }

/* ==========================================================================
   Keyboard focus — LAST IN THE FILE ON PURPOSE
   ========================================================================== */
/* This block sat near the top and lost every fight it mattered in: `:focus-visible` and `.btn`
   have the same specificity, so the later rule won and the white halo never appeared on the one
   control most worth focusing. Specificity hacks would have hidden the problem; source order
   states it. */
/* Focus ring. Only `.control` had any focus styling in the whole stylesheet; every button,
   link, nav item, tab and row action showed nothing at all, so someone navigating by keyboard
   had no way to know where they were — they were tabbing blind through a screen that can delete
   a bot. `:focus-visible` and not `:focus`, so a mouse click does not leave a ring behind.
   The outline follows the element's own border-radius, so it needs no per-component rules. */
/* TWO-TONE, because one colour cannot clear 3:1 on every surface this app has. Measured:
   a teal ring is 2.75:1 on a card, 2.37:1 on the page background and 1.00:1 on a primary
   button — invisible on the very control most worth focusing. A dark outline with a white
   halo between it and the element always has one half that contrasts: 14.8:1 on a card,
   14.8:1 on the navy rail (via the halo), 5.4:1 on a teal button, 4.8:1 on danger red.
   `outline` and not box-shadow alone, because forced-colors mode discards box-shadow. */
:focus-visible {
    outline: 2px solid var(--ink);
    outline-offset: 2px;
    box-shadow: 0 0 0 2px #fff;
}

/* ==========================================================================
   Windows High Contrast / forced-colors
   ========================================================================== */
/* There was not one forced-colors rule in this file. The mode throws box-shadow away and
   keeps outline — so the ring above survives, but anything drawn only with a shadow does
   not — and it replaces `background`, which renders every progress bar empty. */
@media (forced-colors: active) {
    :focus-visible,
    .control:focus-visible { outline: 3px solid Highlight; outline-offset: 2px; box-shadow: none; }
    .card, .control, .badge, .chip, .btn { border: 1px solid CanvasText; }
}

/* ==========================================================================
   Hover — behind a capability query on purpose
   ========================================================================== */
/* An ungated :hover STICKS after a tap on a touch device: a rail item tapped to navigate
   stays highlighted on the next page, so the rail shows two items looking active, and two
   tapped table rows both look selected. :active and :focus-visible stay outside the query —
   a touch user needs both. */
@media (hover: hover) {
    .btn-primary:hover { color: #fff; }
    .btn-ghost:hover { border-color: #cdd2d8; background: var(--bg); color: var(--ink); }
    .btn-success:hover { color: #fff; }
    .btn-danger:hover { background: var(--danger-bg); }
    .btn-warn:hover { background: var(--warn-bg); }
    .btn-icon:hover { background: var(--bg); border-color: #cdd2d8; }
    .nav__link:hover { background: rgba(255,255,255,.09); color: #fff; }
    .usermenu__panel a:hover, .usermenu__panel button:hover { background: var(--bg); }
    .tab:hover { color: var(--ink-2); }
    table.tbl tbody tr:hover { background: var(--row-hover); }
    .btn-primary:hover::after,
    .btn-success:hover::after { opacity: 1; }
}

/* ==========================================================================
   Busy state + screen-reader utilities
   ========================================================================== */
/* Scoped to .btn, not to [aria-busy] globally: aria-busy is a legitimate attribute on
   tables and live regions, and dimming their children would be wrong. */
.btn[aria-busy="true"] { position: relative; pointer-events: none; }
.btn[aria-busy="true"] > * { opacity: .25; transition: opacity var(--t-fast) linear; }
.btn[aria-busy="true"]::after {
    content: ""; position: absolute; inset: 0; margin: auto;   /* centred with no transform,
                                                                 so it stays RTL-neutral */
    inline-size: 16px; block-size: 16px; border-radius: var(--r-circle);
    border: 2px solid currentColor; border-block-start-color: transparent;
    opacity: 0; box-shadow: none;
    /* The 300ms delay needs no JS timer: below that threshold the spinner never becomes
       visible, so a fast save shows nothing instead of a two-frame strobe that reads as a
       glitch. Once it does appear it stays for the rest of the round trip. */
    animation: kq-spin 600ms linear 300ms infinite, kq-reveal 1ms linear 300ms forwards;
}
@keyframes kq-spin { to { rotate: 360deg; } }
@keyframes kq-reveal { to { opacity: 1; } }

/* A default submit button that is never seen but is always FIRST in tree order — see the
   note on the bot-create form. Not display:none: a display:none default button is skipped
   by some engines, which puts the bug straight back. */
.kq-default-submit {
    position: absolute; inline-size: 1px; block-size: 1px; padding: 0; border: 0;
    overflow: hidden; clip-path: inset(50%); pointer-events: none;
}
.sr-only {
    position: absolute; inline-size: 1px; block-size: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* ==========================================================================
   Voice-bot cards
   ========================================================================== */
/* The bots list is cards, not a table (owner 2026-08-01). A table is the right shape when rows
   are compared column by column; a tenant has a handful of bots and reads each one as an object
   — its number, its voice, whether it is answering — so each bot gets a card.
   auto-fill and not auto-fit: with two bots, auto-fit would stretch them to half the screen
   each; auto-fill keeps the card its own size and lets the grid stay left-over. */
/* min(336px, 100%) and not a bare 336px: a fixed track floor cannot shrink, so on a 360px phone
   (332px of content) the card was wider than the screen and the PAGE scrolled sideways. The
   desktop intent is unchanged — wherever there is room for 336px, the track is still 336px. */
.botgrid {
    display: grid; gap: var(--s4);
    grid-template-columns: repeat(auto-fill, minmax(min(336px, 100%), 1fr));
    align-items: start;
}
/* Same treatment as a dashboard block (owner 2026-08-03: "תעצב לי באותו סגנון"): one hairline,
   the small radius, NO shadow. A shadow lifts a card off the page and makes a grid of them read
   as a pile of objects; a hairline just draws its edge. */
.botcard {
    display: flex; flex-direction: column; gap: var(--s3);
    padding: var(--s4) var(--s4);
    background: var(--glass-bg); border: 1px solid var(--glass-brd); border-radius: var(--radius-sm);
    /* NOT hidden: the number picker opens an absolutely-positioned list that would be clipped
       by the card it sits in. */
    overflow: visible;
    transition: border-color var(--t-fast) var(--e-standard);
    box-shadow: var(--glass-shine), var(--glass-shadow);
}
@media (hover: hover) { .botcard:hover { border-color: var(--line-strong); } }
/* THE CARD, RECOMPOSED (owner 2026-08-03: "משהו בעיצוב לא מסתדר לי").
   The head now holds exactly two things — the name, which opens the card, and the dial, which
   closes it. Everything that was competing with them moved down. One loud element per card. */
.botcard.is-off { background: color-mix(in srgb, var(--ink) 4%, var(--card)); }
.botcard.is-off .botcard__name { color: var(--muted); }
.botcard__toggle { display: flex; flex: none; margin-inline-start: auto; }

/* NEUTRAL (owner 2026-08-03: "שהכפתורים יהיו בצבע ניטרלי").
   The circle is now made of the same material as the four buttons under it — white, one hairline,
   a dark glyph — just round. The state is no longer painted on the control at all; it is carried
   by the words, which now say it in full. That separates the two jobs cleanly: the circle tells
   you what your click will DO, the line under it tells you where the bot IS. Nothing on the card
   has to be read twice to work out which of the two it means.
   The edge is --line-strong, the one neutral that clears the 3:1 a control boundary needs — a
   hairline in --line would leave a white circle on a white card with no visible edge at all. */
.botdial {
    flex: none; display: inline-flex; align-items: center; justify-content: center;
    width: 38px; height: 38px; border-radius: var(--r-circle);
    padding: 0; cursor: pointer;
    color: var(--ink);
    background: var(--card);
    border: 1px solid var(--line-strong);
    transition: background var(--t-fast) var(--e-standard),
                border-color var(--t-fast) var(--e-standard);
}
.botdial svg { width: 20px; height: 20px; }
/* A bot with no number cannot be switched on. Faded and not-allowed rather than hidden: the
   control still has to be there, because its absence would read as "this bot has no switch"
   instead of "this switch needs something first" — and the title says which. */
.botdial[disabled] { opacity: .4; cursor: not-allowed; background: var(--card); }
span.botdial { cursor: default; margin-inline-start: auto; }
button.botdial:active { transition-duration: 0ms; translate: 0 1px; }
@media (hover: hover) {
    .botdial:hover { background: color-mix(in srgb, var(--ink) 7%, var(--card));
                     border-color: var(--ink-2); }
}

/* THE ONLY PLACE THE STATE IS SAID NOW — so it says it in full and in colour, and it is the
   one thing on the card carrying a hue. #0d7a3e and #b45309 are the ink weights of the green
   and the amber: the fills they are derived from reach only 3.30:1 and 3.19:1 as text on the
   card, and a caption has to clear 4.5:1. Both of these clear 5:1. */
.botcard__state { font-weight: 600; color: var(--ok-ink, var(--ok-ink)); }
.botcard__state.is-off { color: var(--warn-ink); }
.botcard__state .dot { width: 7px; height: 7px; border-radius: var(--r-circle); background: currentColor;
                       display: inline-block; }

/* the rule under the name mirrors .dash-block h2 — the card is divided the same way a
   dashboard block is */
.botcard__head { display: flex; align-items: center; gap: var(--s3);
                 padding-block-end: var(--s3); border-block-end: 1px solid var(--line-2); }
.botcard__name { font-weight: 650; font-size: var(--t-base); line-height: 1.3; min-width: 0;
                 overflow-wrap: anywhere; }
.botcard__head .badge { margin-inline-start: auto; flex: none; }

.botcard__did { min-width: 0; }
.botcard__did .cselect { min-width: 0; max-width: none; }

.botcard__meta {
    display: flex; flex-wrap: wrap; gap: var(--s1) var(--s3);
    font-size: var(--t-sm); color: var(--muted);
}
.botcard__meta > span { display: inline-flex; align-items: center; gap: var(--s1); }
.botcard__meta svg { width: 15px; height: 15px; }

/* GRID, not flex, and that is the whole fix for "כל הכפתורים באותו גודל". With flex the five
   actions came out 67/67/57/57/57 even at `flex: 1 1 0` on every child, because two of them are
   the button itself and three are a <form> wrapping one — two different content-sizing paths,
   and content still leaks into the result. `grid-auto-columns: 1fr` sizes the tracks from the
   container alone, so the count can be three, four or five and every column is identical. */
.botcard__actions {
    display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
    gap: var(--s1); align-items: stretch;
    padding-block-start: var(--s3); margin-block-start: auto;
    border-block-start: 1px solid var(--line-2);
}
/* Each action is a form or a link, so the FLEX CHILD is the wrapper, not the button — sizing
   the button alone would have done nothing. */
/* a form is a grid item like any other; its button fills it */
.botcard__actions > form { display: flex; min-width: 0; }
.botcard__actions > .botbtn { min-width: 0; }

/* Icon over a caption (owner 2026-08-01: "תוסיף מלל בקטן מתחת לכל אימוגי... ותעשה לי אותם יותר
   גדולים ומובנים"). An icon-only row was compact but it asked the user to remember five
   pictograms; a two-line button is bigger, says what it does, and still fits five across
   because the captions are one word each. The full sentence stays on the title and the
   aria-label, which also name the bot. */
.botcard__actions .botbtn {
    width: 100%; height: auto;
    flex-direction: column; gap: var(--s1);
    padding: var(--s2) var(--s1);
    font-size: var(--t-xs); font-weight: 600; line-height: 1.2;
    color: var(--ink-2);
    background: #fff; border: 1px solid var(--line);
    text-align: center;
}
.botcard__actions .botbtn svg { width: 20px; height: 20px; }
/* one line, clipped rather than wrapped: a caption that wraps makes its button taller than
   the four beside it, and equal size was the whole point */
.botcard__actions .botbtn > span { display: block; width: 100%; white-space: nowrap;
                                   overflow: hidden; text-overflow: ellipsis; }

/* THE STATE ACTIONS LOOK LIKE EVERY OTHER ACTION (owner 2026-08-03: "תסיר את המסגרת האדומה או
   הירוקה"). The coloured frame was the last thing separating השהיה/הפעלה from their neighbours;
   with it gone the row is five identical controls and the caption carries the meaning. This
   also removes the row's only source of uneven visual weight. */
.botcard__actions .btn-danger.botbtn,
.botcard__actions .btn-success.botbtn {
    background: #fff; border-color: var(--line); color: var(--ink-2);
}
/* NO SHADOW ON A CARD ACTION, from either source (owner 2026-08-03: "תסיר את הצל הירוק
   מהכפתור הפעלה"). `.btn-success` carries a resting green glow on the box itself and the
   hover shadow lives on ::after; the card overrode the background and the border but not
   either shadow, so the activate button still floated in a green haze while the four beside
   it sat flat. Both are switched off here. */
.botcard__actions .botbtn,
.botcard__actions .botbtn::after { box-shadow: none; }

/* ==========================================================================
   Dashboard — minimalist, but with structure
   ========================================================================== */
/* Rebuilt 2026-08-03: five card-framed panels, two charts, a per-bot bar list and six metrics
   became three numbers, one bar, one chart and one list.
   Then the frames came off entirely and it read as unsorted (owner: "זה נראה לא מסודר") — with
   nothing but space between them, four sections at four different widths have no edges for the
   eye to line up on. Structure is back, but as ONE device used everywhere: a hairline. No
   shadows, no fills, no second border weight. A rule under the header, rules between the
   figures, a rule around each block. That is the whole system. */
/* Identical to .page-head by intent — the dashboard is not a special case, it was simply the
   first screen to get the rule. Kept as its own selector only because its month navigator wants
   baseline alignment rather than flex-end. */
.dash-head {
    display: flex; align-items: baseline; justify-content: space-between; gap: var(--s4);
    flex-wrap: wrap; margin-bottom: 0;
    padding-block-end: var(--s4); border-block-end: 1px solid var(--line);
}
.dash-head h1 { font-size: var(--t-xl); margin: 0; letter-spacing: -.015em; }
.dash-month { display: inline-flex; align-items: center; gap: var(--s1); font-size: var(--t-base);
              color: var(--muted); }
.dash-month > span:not(.dash-mbtn) { min-width: 8.5ch; text-align: center; }
.dash-mbtn { display: inline-flex; align-items: center; justify-content: center;
             width: 30px; height: 30px; border-radius: var(--r); color: var(--muted); }
.dash-mbtn svg { width: 17px; height: 17px; }
.dash-mbtn.is-off { opacity: .3; pointer-events: none; }

/* The figures sit in one strip divided by hairlines — the columns give the eye a grid to
   read across, which is exactly what the frameless version lacked. */
.dash-figs {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    border-block-end: 1px solid var(--line); margin-bottom: var(--s5);
}
.dash-fig {
    display: flex; flex-direction: column; gap: 2px; min-width: 0;
    padding: var(--s4) var(--s5);
    border-inline-start: 1px solid var(--line);
}
.dash-fig:first-child { border-inline-start: 0; padding-inline-start: 0; }
.dash-fig .k { font-size: var(--t-sm); color: var(--muted); }
.dash-fig .v { font-size: var(--t-2xl); font-weight: 700; line-height: 1.1; letter-spacing: -.03em;
               font-variant-numeric: tabular-nums slashed-zero; font-family: "Assistant", ui-monospace, monospace; }
.dash-fig .d { font-size: var(--t-sm); color: var(--muted); display: flex; flex-wrap: wrap;
               align-items: baseline; gap: var(--s1); }
.dash-fig .d em { font-style: normal; }
/* Direction is not valence: more calls is good for one tenant and a cost for another, so the
   delta is never painted red or green — it is a fact, in the same ink as the sentence that
   qualifies it. The previous screen coloured a −98% alarm red on the 3rd of the month. */
.dash-fig .d.up::before, .dash-fig .d.down::before { color: var(--ink-2); font-weight: 700; }

.dash-quota { max-width: 520px; margin-bottom: var(--s5); }
.dash-quota__row { display: flex; justify-content: space-between; gap: var(--s3);
                   font-size: var(--t-md); color: var(--muted); margin-bottom: var(--s2); }
.dash-quota__row b { color: var(--ink); font-variant-numeric: tabular-nums slashed-zero; font-family: "Assistant", ui-monospace, monospace; }
.dash-quota__track { height: 5px; border-radius: var(--r-pill); background: var(--line-2); overflow: hidden; }
.dash-quota__track > span { display: block; height: 100%; border-radius: var(--r-pill);
                            background: var(--kq-cyan); }
.dash-quota__track > span.is-high { background: var(--warn); }
.dash-quota__track > span.is-full { background: var(--danger); }
.dash-quota__warn { margin: var(--s2) 0 0; font-size: var(--t-sm); color: var(--danger); }

/* An outline, not a card: one hairline, the shared radius, no shadow and no fill, so the
   block has an edge without becoming a panel competing with its own contents. */
.dash-block {
    border: 1px solid var(--glass-brd); border-radius: var(--radius-sm);
    background: var(--glass-bg); padding: var(--s4) var(--s4); margin-bottom: var(--s4);
    box-shadow: var(--glass-shine), var(--glass-shadow);
}
.dash-block h2 {
    display: flex; align-items: baseline; justify-content: space-between; gap: var(--s3);
    font-size: var(--t-sm); font-weight: 600; color: var(--muted); margin: 0 0 var(--s3);
    padding-block-end: var(--s3); border-block-end: 1px solid var(--line-2);
}
.dash-block h2 a { font-size: var(--t-sm); font-weight: 500; display: inline-flex;
                   align-items: center; gap: var(--s1); }
.dash-block h2 a svg { width: 14px; height: 14px; }
.dash-empty { margin: 0; padding: var(--s5) 0; color: var(--muted); font-size: var(--t-md); }

.dash-chart { display: block; height: 132px; width: 100%; }
.dash-chart__x { font-size: var(--t-xs); fill: var(--muted); }

/* the call list: rows separated by a hairline, nothing else */
.dash-calls { list-style: none; margin: 0; padding: 0; }
.dash-calls li {
    display: grid; align-items: center; gap: var(--s1) var(--s3);
    grid-template-columns: max-content minmax(0, 1fr) minmax(0, 1fr) max-content max-content;
    padding: var(--s3) 0; border-block-end: 1px solid var(--line-2); font-size: var(--t-md);
}
.dash-calls li:last-child { border-block-end: 0; padding-block-end: 0; }
.dash-calls .t { color: var(--muted); font-size: var(--t-sm); font-variant-numeric: tabular-nums slashed-zero; font-family: "Assistant", ui-monospace, monospace; }
.dash-calls .n { font-variant-numeric: tabular-nums slashed-zero; font-family: "Assistant", ui-monospace, monospace; overflow: hidden; text-overflow: ellipsis;
                 white-space: nowrap; }
.dash-calls .b { color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dash-calls .d { color: var(--muted); font-variant-numeric: tabular-nums slashed-zero; font-family: "Assistant", ui-monospace, monospace; }
.dash-calls .s { font-size: var(--t-sm); color: var(--muted); }
.dash-calls .s.is-miss { color: var(--danger); }
/* ── THE OPS BRIEF (owner chose this layout from five specimens, 2026-08-06) ────────
   Shipped first as flat columns under a hairline — the page's OLD chrome wearing the new
   architecture — and it did not look like the specimen he approved. The specimen's point is
   that the four figures are FOUR OBJECTS and the action list is a CONTAINED PANEL, so the eye
   parses "figures / jobs / detail" without reading a word. Restored here, in glass rather than
   the specimen's plain 1px border, because the material is already settled platform-wide.

   Scoped to .dash-kpis, NOT to .dash-fig: campaign and contact detail pages use .dash-fig for
   their own inline stat rows and must keep the flat treatment. */
.dash-kpis {
    display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--s3);
    border-block-end: 0; margin-bottom: var(--s4);
}
.dash-kpis .dash-fig,
.dash-kpis .dash-fig:first-child {
    border: 1px solid var(--glass-brd); border-radius: var(--radius-sm);
    background: var(--glass-bg); box-shadow: var(--glass-shine), var(--glass-shadow);
    padding: var(--s3) var(--s4);
}
@media (max-width: 980px) { .dash-kpis { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 520px) { .dash-kpis { grid-template-columns: 1fr; } }

/* "דורש טיפול" — a panel, not loose rows. Jobs are read top to bottom, so the rows are a
   flex column with air between them rather than a ruled table: rules would make it read as
   data, and this is the one block on the page that is not data.
   The link is pushed to the far edge by auto margin, so the calls to action line up in one
   column whatever the titles do. Colour is a 7px dot and nothing else — severity has to
   survive being scanned at arm's length, and a tinted row would fight the figures above. */
.dash-todo {
    border: 1px solid var(--glass-brd); border-radius: var(--radius-sm);
    background: var(--glass-bg); box-shadow: var(--glass-shine), var(--glass-shadow);
    padding: var(--s4); margin-bottom: var(--s4);
}
.dash-todo h2 {
    font-size: var(--t-sm); font-weight: 600; color: var(--muted);
    letter-spacing: .05em; margin: 0 0 var(--s3);
}
.dash-todo ul {
    list-style: none; margin: 0; padding: 0;
    display: flex; flex-direction: column; gap: var(--s3);
}
.dash-todo li { display: flex; align-items: center; gap: var(--s2); min-width: 0; }
.dash-todo .dot {
    width: 7px; height: 7px; flex: 0 0 7px; border-radius: 50%; background: var(--n-6);
}
.dash-todo .t-bad  .dot { background: var(--danger-ink); }
.dash-todo .t-warn .dot { background: var(--warn-ink); }
/* The live dot is the only thing on the page that is happening AS YOU LOOK AT IT; the halo
   is what makes it read as a state rather than a bullet. */
.dash-todo .t-live .dot {
    background: var(--kq-cyan);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--kq-cyan) 22%, transparent);
}
.dash-todo .ttl { font-weight: 600; white-space: nowrap; }
.dash-todo .det {
    color: var(--muted); font-size: var(--t-sm); min-width: 0;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.dash-todo a { margin-inline-start: auto; font-size: var(--t-sm); white-space: nowrap; }
.dash-clear {
    display: flex; align-items: center; gap: var(--s2);
    color: var(--muted); font-size: var(--t-sm); margin: 0;
}
.dash-clear svg { width: 16px; height: 16px; color: var(--ok-ink); }

/* A live count is the one figure here that is TRUE RIGHT NOW rather than month-to-date, so it
   gets the brand colour to say so. Zero stays neutral — colouring a nought trains the eye to
   ignore the colour. */
.dash-fig.is-live .v { color: var(--kq-cyan); }


@media (max-width: 720px) {
    /* FOUR tracks, not three. The row has five children — time, number, agent, duration and,
       on a missed call only, the status word — and this block hides exactly one of them. Three
       tracks therefore had to hold four items, so «לא נענתה» dropped onto a second line and only
       the missed calls in the list came out ragged, which reads as a rendering fault rather than
       a deliberate difference. Answered rows never showed it, which is why it survived. */
    .dash-calls li { grid-template-columns: max-content minmax(0, 1fr) max-content max-content; }
    .dash-calls .b { display: none; }
    .dash-fig { border-inline-start: 0; padding-inline: 0; }
    .dash-todo .det { display: none; }
}

/* ═══ DASHBOARD, REBUILT (owner 2026-08-04: "הרבה יותר מרשים והרבה יותר מובן") ═════════
   Three blocks that were computed on every load and never rendered — the answered/missed
   split, the hour-of-day profile and the per-bot share — now have somewhere to land. */

.dash-grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s4); }
@media (max-width: 900px) { .dash-grid2 { grid-template-columns: 1fr; } }

/* WHEN DOES THE PHONE RING — 24 columns, one per hour. The shape is the message, so the
   bars carry no labels; the axis under them is enough to place a peak. */
.dash-hours { display: grid; grid-template-columns: repeat(24, 1fr); gap: 2px;
              align-items: end; height: 108px; }
.dash-hours .hr { display: block; height: var(--h); min-height: 2px;
                  background: var(--kq-cyan); border-radius: 2px 2px 0 0; }
.dash-hours .hr.is-zero { background: var(--n-4); }
.dash-hours__ax { display: flex; justify-content: space-between; margin-top: var(--s1);
                  font-size: var(--t-xs); color: var(--n-8);
                  font-variant-numeric: tabular-nums;
                  font-family: "Assistant", ui-monospace, monospace; }

.dash-quota__row { display: flex; justify-content: space-between; align-items: baseline;
                   font-size: var(--t-sm); margin-bottom: var(--s2); }
.dash-quota__row b { color: var(--ink); font-variant-numeric: tabular-nums slashed-zero;
                     font-family: "Assistant", ui-monospace, monospace; }

/* ═══ CONTACT TIMELINE — one number's whole history (2026-08-05) ══════════════════════ */
.ct-item { padding: var(--s3) 0; border-bottom: 1px solid var(--n-4); }
.ct-item:last-child { border-bottom: 0; }
.ct-head { display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap; }
.ct-when { color: var(--ink); font-variant-numeric: tabular-nums slashed-zero;
           font-family: "Assistant", ui-monospace, monospace; }
.ct-dur { color: var(--ink); font-variant-numeric: tabular-nums slashed-zero;
          font-family: "Assistant", ui-monospace, monospace; margin-inline-start: auto; }
.ct-sum { margin: var(--s2) 0 0; color: var(--n-8); line-height: 1.6; max-width: 80ch; }
.ct-tx { max-height: 320px; overflow-y: auto; background: var(--n-2);
         border: 1px solid var(--n-4); border-radius: var(--r);
         padding: var(--s3); max-width: 720px; margin-top: var(--s3); }

/* The retry detail is hidden until retries are switched on — checkbox + sibling, no JS (CSP).
   IMPORTANT: this must NOT draw its own toggle. There is a global rule making every checkbox in
   the product a switch, so a custom ::before track here rendered a SECOND, knobless one on top
   of the real control — which is what the owner saw. The input keeps the house switch styling
   and only drives the disclosure. */
.cm-retrypanel { display: none; }
/* :has() and not the sibling combinator — the switch sits inside a row wrapper so it is no
   longer a sibling of the panel, and `~` silently matches nothing. */
.field:has(.cm-retrytoggle:checked) .cm-retrypanel {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 220px)); gap: var(--s3);
    margin-top: var(--s3); padding-top: var(--s3); border-top: 1px solid var(--n-4);
}
.cm-retryrow { display: flex; align-items: center; gap: var(--s2); cursor: pointer; }

/* A divider BEFORE the first figure separates nothing from something — and inside a card it
   lands right on the frame, which is what the owner saw. The rule belongs between items only.
   Fixed globally: the dashboard had the same stray line, just against the page edge where it
   read as intentional. */
.dash-fig:first-child { border-inline-start: 0; }
/* Nested in a card the strip also needs to clear the frame; edge-to-edge only reads right when
   the strip IS the edge, as it is on the dashboard. */
.card .dash-figs, .card__body .dash-figs { padding-inline: var(--s2); }

/* ── CSS-ONLY MODAL ──────────────────────────────────────────────────────────────────────
   Driven by a hidden checkbox and a <label for>, because the CSP is script-src 'self' and an
   inline handler is silently dropped — a JS dialog here would look right in source and never
   open. The label IS the button, so it stays keyboard-reachable via the input.
   The backdrop is also a label, so clicking outside closes it without a line of script. */
.kq-modal-t { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.kq-modal-t:not(:checked) ~ .kq-modal { display: none; }
.kq-modal {
    position: fixed; inset: 0; z-index: 90; display: grid; place-items: center;
    padding: var(--s5); background: rgba(20, 24, 28, .5);
}
.kq-modal-bg { position: absolute; inset: 0; cursor: default; }
.kq-modal-card {
    position: relative; width: min(1040px, 100%); max-height: 82vh; overflow: auto;
    background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-sm);
    box-shadow: var(--shadow-lg); padding: var(--s5);
}
.kq-modal-x {
    position: sticky; top: 0; float: inline-start; cursor: pointer;
    background: var(--card); border-radius: var(--r-pill); padding: 2px 8px; line-height: 1.6;
}
.kq-modal-t:focus-visible ~ .kq-modal .kq-modal-card { outline: 2px solid var(--kq-cyan); }

/* ==========================================================================
   THE PHONE PASS (owner 2026-08-06: "שיתאים לטלפונים הניידים באופן מושלם")
   ==========================================================================
   ONE APPENDED BLOCK, on purpose. There is no git in this project, so a mobile pass spread
   through 1689 lines of stylesheet could not be undone; everything below can be deleted in one
   cut and the desktop product is exactly what it was.

   MEASURED, NOT IMAGINED. Every rule here answers something observed at a real 390px viewport
   (a same-origin srcdoc frame against the live app, which is the only way this environment
   renders narrow — window resizing reports success and silently does nothing). The numbers in
   the comments are what the browser reported, not estimates.

   The starting point was better than expected: of thirteen tenant screens measured, eleven had
   ZERO horizontal overflow and the off-canvas drawer already worked. So this is a repair list,
   not a rebuild. */

/* ── 1. THE SHRINK FIX — the single defect behind every page that scrolled sideways ───────
   A grid item's automatic minimum size is its MIN-CONTENT, not zero. So one wide child — the
   31-day chart at min-width:403px, a <pre> code block at 591px — sets a floor the track cannot
   go below, and that floor propagates up through .content to <html>.
   The overflow:auto already on those children does not help: it stops THEM from spilling, but
   their intrinsic width is still what the parent grid measures.
   Measured: /Admin rendered 447px wide inside a 375px viewport (+72), /Admin/ApiKeys/Docs +230.
   min-width:0 releases the floor, and the scroll container then does the job it was added for.
   A correctness fix at every width, which is why it is not inside a media query. */
.dash-grid2 > *,
.dash-two > *,
.dash-block { min-width: 0; }

/* The other half of the same defect — a grid TRACK floor wider than a small phone — is fixed at
   the two declarations themselves (.botgrid, .tn-grid) rather than overridden from down here.
   An override would leave the original 336px sitting in the file looking authoritative, and the
   next person to read that line would believe it. mobile-test.sh scans for the pattern, so a new
   one cannot be introduced without the guard saying so. */

/* ── 2. VIEWPORT UNITS ────────────────────────────────────────────────────────────────────
   100vh on a phone is the viewport WITHOUT the browser's own chrome, so a full-height shell is
   taller than what can actually be seen and its bottom sits under the URL bar. dvh tracks the
   visible height. Declared twice: the vh line is the fallback, and a browser that understands
   dvh simply takes the second. */
.app { min-height: 100vh; min-height: 100dvh; }
.auth { min-height: 100vh; min-height: 100dvh; }

/* ── 3. TOUCH SCROLLING FOR EVERY HORIZONTAL SCROLLER ─────────────────────────────────────
   A table that scrolls sideways is the honest answer for six columns of call data on a phone —
   but only if the gesture feels native and does not drag the PAGE along once the table reaches
   its end. Overscroll chaining is what makes a nested scroller feel broken. */
.table-wrap, .chart-scroll, .ct-tx {
    -webkit-overflow-scrolling: touch;
    overscroll-behavior-x: contain;
}

/* ══ PHONES AND SMALL TABLETS ═════════════════════════════════════════════════════════════
   820px is the breakpoint the drawer already uses. Reusing it rather than inventing a second
   one keeps "mobile" a single idea in this file. */
@media (max-width: 820px) {

    /* ── 4. iOS SAFARI ZOOMS THE WHOLE PAGE when a focused field is under 16px ────────────
       Not a preference — it is unconditional in Safari, and it leaves the user zoomed in with
       the layout wider than the screen after the keyboard closes. Measured on the call journal:
       the agent filter and the search box both computed to 13.1px, so tapping either threw the
       page out of alignment before a single character was typed.
       The toolbar rules that made them 13.1px are (0,2,0), so they are restated here rather
       than relying on a bare element selector that would lose to them. */
    input:not([type="checkbox"]):not([type="radio"]),
    select, textarea,
    .control, input.control, select.control,
    .ci-filterrow .control,
    .search-box .control { font-size: 16px; }

    /* ── 5. TAP TARGETS ──────────────────────────────────────────────────────────────────
       Measured before this: .btn 35px, .btn-icon 34px, a sort-header link 15px. A finger is not
       a mouse pointer, and the sort links in particular were unhittable without zooming. Height
       only — the padding tokens stay, so nothing about the desktop rhythm leaks in here. */
    .btn { min-height: 40px; }
    .btn-sm { min-height: 36px; }
    .btn-icon { width: 40px; height: 40px; }
    .botdial { width: 42px; height: 42px; }
    /* the header link is inline, so it has no height of its own to grow — padding gives it one */
    table.tbl th a, .calltbl th a { display: inline-block; padding-block: var(--s2); }
    /* a row of actions that cannot fit stacks instead of squeezing its buttons to nothing */
    .row-actions { flex-wrap: wrap; justify-content: flex-start; }

    /* ── 6. THE TOP BAR MUST NEVER BE THE THING THAT BREAKS ──────────────────────────────
       It fits today, but only because the current user's name is short. Nothing was stopping a
       long one from pushing the page title out of the bar: a flex item's default min-width is
       auto, so the name refuses to shrink and something else has to give way. */
    .topbar { padding-inline: var(--s4); gap: var(--s3); }
    .topbar > div:first-child { min-width: 0; }
    .topbar h1 { font-size: var(--t-base); min-width: 0;
                 overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .usermenu__btn { max-width: 46vw; }
    .usermenu__btn .who { min-width: 0; }
    .usermenu__btn .who .n,
    .usermenu__btn .who .r { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

    /* ── 7. DIALOGS ─────────────────────────────────────────────────────────────────────
       s5 of backdrop padding on a 390px screen spends 40px of the width on nothing. */
    .kq-modal { padding: var(--s3); }
    .kq-modal-card { padding: var(--s4); max-height: 88vh; max-height: 88dvh; }

    /* ── 8. COMPONENTS THAT ASSUME A WIDE COLUMN ────────────────────────────────────────*/
    /* a tool's description was one clipped line at max-width:520px — on a phone that ellipsis
       landed after about three words, so the list said nothing about any of the tools */
    .tool-item { flex-wrap: wrap; }
    .tool-item .tool-desc { white-space: normal; max-width: none; }
    .tool-item .tool-lead { min-width: 0; }
    .form-row.narrow { max-width: none; }
    /* a sticky save bar needs the safe-area inset on a notched phone, or the button it holds
       sits underneath the home indicator */
    .savebar { padding-bottom: max(12px, env(safe-area-inset-bottom)); }

    /* ── 10. THE CONVERSATION BUILDER'S ROWS ─────────────────────────────────────────────
       Found by an adversarial audit, NOT by looking — and it could not have been found by
       looking, because these rows live in the ההנחיות panel, which is display:none until its
       radio is checked. A hidden element has no layout, so every width measurement of the
       editor reported it clean.
       The audit called it page-level overflow. It is not — measured at 390px the row fits in
       305px and the page does not scroll. What actually happens is worse to use and easier to
       miss: the three tracks resolve to 59px / 76px / 76px, so each field shows about four
       characters of a Hebrew sentence. The rule row is 95px / 123px.
       ONE COLUMN, and !important is the correct tool exactly once here: the template is an
       inline style attribute on four elements (both server-rendered rows and both prototypes,
       which voicebot-form.js clones with cloneNode(true) — so the inline copy is carried into
       every row the + button adds). An author !important beats a normal inline declaration,
       which is why this fixes the cloned rows too without touching the markup or the script.
       NO min-width:0 here: .control is width:100%, which is a definite size suggestion, so the
       automatic minimum is already suppressed — that is why the fields shrank to 59px instead
       of forcing the page wide. Adding it would be cargo cult.
       justify-self on the delete button because a grid item stretches by default, and a
       full-width «הסרה» directly under the last field is an easy mis-tap on a destructive
       control. */
    .kq-step, .kq-rule { grid-template-columns: 1fr !important; }
    .kq-step .kq-row-del, .kq-rule .kq-row-del { justify-self: start; }

    /* an email or an API path has no space to break at, so one long value sets a column floor
       nothing else can shrink past. Mobile only — desktop columns are wide enough that breaking
       mid-word would just look careless. */
    table.tbl td { overflow-wrap: anywhere; }

    /* the browser-test bar: a nowrap label plus a fixed 150px field in a flex row with no wrap */
    .wtc-ask { flex-wrap: wrap; }
    .wtc-ask .in { width: auto; flex: 1 1 140px; min-width: 0; }
}

/* ══ PHONES PROPER ════════════════════════════════════════════════════════════════════════ */
@media (max-width: 560px) {
    /* ── 9. THE DASHBOARD FIGURES STAY TWO ACROSS ───────────────────────────────────────
       The existing rule dropped .dash-kpis to ONE column at 520px, which turned the four
       headline numbers into 440px of stacked cards — the entire first screen of the product
       spent on four figures, with «דורש טיפול», the only actionable thing on the page, pushed
       below the fold. Two across is 176px per card, which the figures fit comfortably; the
       value steps down one size so a four-digit number cannot force a wrap. */
    .dash-kpis { grid-template-columns: repeat(2, 1fr); }
    .dash-kpis .dash-fig .v { font-size: var(--t-xl); }

    /* a key/value list has no room for two columns; the label goes above its value */
    .kv { grid-template-columns: 1fr; gap: 0; }
    .kv dt { margin-top: var(--s3); }
    .kv dt:first-of-type { margin-top: 0; }

    /* five controls across a 362px row is 60px each — the tool parameter editor stops being
       usable long before that. One field per line. */
    .param-row { grid-template-columns: 1fr; }

    .page-head .t { font-size: var(--t-lg); }
}

/* Only below this does one column beat two — at 360px a 2-up card is still 166px, which holds
   "1:06" and its caption, so the stack is reserved for genuinely tiny screens. */
@media (max-width: 340px) {
    .dash-kpis { grid-template-columns: 1fr; }
}

/* ══════════════════════════════════════════════════════════════════════════════════════════
   TABLES BECOME CARDS (owner 2026-08-06: "טבלאות לא מוצגות בכלל טוב בטלפון... תשנה את זה
   למבנה של כרטיסיות")
   ══════════════════════════════════════════════════════════════════════════════════════════
   Horizontal scroll was the honest first answer and it is not good enough. A six-column journal
   in a 347px scroller makes you drag sideways for the one value you came for, and by the time
   you get there the cell that identified the row — the time, the name — has scrolled off the
   other edge. You cannot read across a row you cannot see the start of.

   So below 820px a row stops being a row and becomes a card: every value on its own line, next
   to its column name, with nothing to scroll. Above 820px not one of these rules applies and the
   tables are exactly what they were.

   .is-cardable IS ADDED BY kinetiq.js, and that is deliberate — see the note there. The label
   beside each value is `attr(data-label)`, and that attribute only exists once the script has
   copied it out of the table's own <thead>. Keying the layout on a class the script adds means
   a scripting failure leaves a scrolling TABLE rather than a stack of anonymous values. */
@media (max-width: 820px) {

    /* the wrapper has nothing left to scroll, and while it is still a scroll container it clips
       the number picker's dropdown */
    .table-wrap:has(table.tbl.is-cardable) { overflow-x: visible; }

    table.tbl.is-cardable,
    table.tbl.is-cardable > tbody,
    table.tbl.is-cardable > tbody > tr,
    table.tbl.is-cardable > tbody > tr > td { display: block; width: auto; }

    /* the header's job has moved into the cells */
    table.tbl.is-cardable > thead { display: none; }

    table.tbl.is-cardable > tbody > tr {
        border: 1px solid var(--line); border-radius: var(--radius-sm);
        background: var(--card); padding: var(--s3) var(--s4);
        margin-bottom: var(--s3);
    }
    /* the base rule strips the last row's border to close a ruled list; as a card it needs its
       own edge like every other card */
    table.tbl.is-cardable > tbody > tr:last-child { border-bottom: 1px solid var(--line); }

    table.tbl.is-cardable > tbody > tr > td {
        display: grid; grid-template-columns: minmax(84px, 34%) minmax(0, 1fr);
        gap: var(--s3); align-items: baseline;
        /* justify-items, and it is not cosmetic: a grid item STRETCHES by default, so every
           badge and every switch became a full-width bar with its text stranded at one end —
           a «פעיל» pill rendered as a 200px green slab. start makes each value take its own
           width again, which is what made them read as chips in the table. */
        justify-items: start;
        padding: var(--s2) 0; border: 0; border-bottom: 1px solid var(--line-2);
    }
    table.tbl.is-cardable > tbody > tr > td:last-child { border-bottom: 0; padding-bottom: 0; }
    table.tbl.is-cardable > tbody > tr > td:first-child { padding-top: 0; }

    table.tbl.is-cardable > tbody > tr > td::before {
        content: attr(data-label);
        color: var(--muted); font-size: var(--t-sm); font-weight: 600;
        overflow-wrap: anywhere;
    }
    /* An actions column has an empty header, so it gets no label row and takes the full width —
       the buttons are the only thing in the product that says what it does by itself. */
    table.tbl.is-cardable > tbody > tr > td[data-label=""] { grid-template-columns: minmax(0, 1fr); }
    table.tbl.is-cardable > tbody > tr > td[data-label=""]::before { content: none; }
    /* .row-actions is a class ON the td, so the card rule above turned the actions cell into a
       one-column grid and stacked עריכה above חסום, each on its own line. It has to go back to
       being the flex row it already is everywhere else — restated at a specificity that beats
       the card rule rather than by weakening the card rule for every other cell. */
    table.tbl.is-cardable > tbody > tr > td.row-actions {
        display: flex; flex-wrap: wrap; justify-content: flex-start;
        align-items: center; gap: var(--s2);
    }

    /* A colspan panel — the API-key editor, a campaign expander — is not a data row. The script
       marks it rather than labelling its contents with whatever column happened to be first. */
    table.tbl.is-cardable > tbody > tr.is-wide-row { border: 0; padding: 0; margin: 0; background: none; }
    table.tbl.is-cardable > tbody > tr.is-wide-row > td { display: block; padding: 0; border: 0; }
    table.tbl.is-cardable > tbody > tr.is-wide-row > td::before { content: none; }

    /* ── THE CALL JOURNAL GETS A REAL CARD, NOT THE GENERIC ONE ─────────────────────────────
       .calltbl is not .tbl and never gets the label treatment, because it does not need it: its
       cells already say what they are. The time is a time, the numbers carry «אל», the duration
       is a duration and the result is a word. Running the generic layout over it would print
       «מתי» beside a clock — a label that adds nothing and costs a line each.
       Instead the six cells are placed as a small grid, in the order a person reads a call
       record: WHEN it happened and HOW it ended on one line, WHO on the next, then the agent and
       how long it ran, then the way in. */
    .log-block .table-wrap { overflow-x: visible; }
    .calltbl, .calltbl > tbody, .calltbl > tbody > tr, .calltbl > tbody > tr > td { display: block; }
    .calltbl > thead { display: none; }

    .calltbl > tbody > tr[data-calllog-row] {
        display: grid; grid-template-columns: minmax(0, 1fr) auto;
        gap: var(--s2) var(--s3); align-items: center;
        border: 1px solid var(--line); border-radius: var(--radius-sm);
        background: var(--card); padding: var(--s3) var(--s4); margin-bottom: var(--s3);
    }
    .calltbl > tbody > tr[data-calllog-row] > td { padding: 0; border: 0; }

    .calltbl > tbody > tr > td:nth-child(1) { grid-column: 1; grid-row: 1; }   /* מתי   */
    .calltbl > tbody > tr > td:nth-child(5) { grid-column: 2; grid-row: 1;
                                              justify-self: end; }             /* תוצאה */
    .calltbl > tbody > tr > td:nth-child(2) { grid-column: 1 / -1; grid-row: 2;
                                              padding-block: var(--s2);
                                              border-block: 1px solid var(--line-2); } /* שיחה */
    .calltbl > tbody > tr > td:nth-child(3) { grid-column: 1; grid-row: 3; }   /* סוכן  */
    .calltbl > tbody > tr > td:nth-child(4) { grid-column: 2; grid-row: 3;
                                              justify-self: end; }             /* משך   */
    .calltbl > tbody > tr > td:nth-child(6) { grid-column: 1 / -1; grid-row: 4;
                                              text-align: start !important; }  /* פרטים */
    .calltbl > tbody > tr > td:nth-child(6) .btn { width: 100%; }

    /* the time reads as one line on a card rather than a stacked column heading */
    .calltbl .log-when { display: flex; align-items: baseline; gap: var(--s2); }
    .calltbl .log-when .d { display: inline; font-size: var(--t-sm); }

    /* the expandable detail panel hangs under its card, still full width */
    .calltbl > tbody > tr[data-calllog-detail] > td {
        border-radius: var(--radius-sm); margin-bottom: var(--s3);
    }
}
