/* ════════════════════════════════════════════════════════════════════════
   shared/monday-vibe.css — the monday.com design language, applied to every
   EA dashboard surface.

   WHAT THIS IS. monday.com's design system is called Vibe. Its token values
   below are not eyeballed from screenshots: they were read out of the
   published package, @vibe/core@4.5.30, dist/tokens/tokens.css — the same
   file monday's own product ships. Motion durations, easing curves, the
   radius ladder, the four elevations and the board colour palette are all
   verbatim from it. Where a value here differs from Vibe, there is a comment
   saying why.

   WHY IT IS AN OVERRIDE AND NOT A REWRITE. dashboard.css is ~284KB and
   customer_view.css another ~79KB, and both are written against the token
   vocabulary in dashboard-tokens.css. Retuning those tokens re-skins every
   rule in both files at once. Rewriting the rules themselves would mean
   touching thousands of selectors to reach the same place, with thousands of
   chances to break one. So this file is loaded AFTER dashboard-tokens.css,
   redefines the values, and adds the components Vibe has that we did not.
   Deleting this one <link> reverts the entire visual change.

   THE ONE DELIBERATE DEPARTURE — the accent stays EA teal (#087F73).
   Vibe's --primary-color is #0073ea, monday's blue. That is monday's BRAND,
   not its design language, and this dashboard belongs to EA. Everything
   structural is monday's; the brand colour is EA's. If you ever do want
   monday blue, it is one line: --accent in the block below.

   CONTRAST, MEASURED — not assumed. The canvas moves from #ffffff to Vibe's
   --color-riverstone_gray #f6f7fb. Every inherited foreground was re-measured
   against it before the change was made:

     token            on card #fff   on new canvas #f6f7fb
     --text              16.60:1            15.50:1
     --text-2             9.60:1             8.96:1
     --text-3             5.14:1             4.80:1
     --accent             4.89:1             4.57:1
     --accent-on-bg       6.13:1             5.73:1

   All five still clear the 4.5:1 AA text floor. And it FIXES a defect the
   token file documents at length: card #ffffff on canvas #ffffff is a 1.00:1
   luminance step — no figure/ground at all, which is why every card needed a
   hairline just to exist. On riverstone the step is 1.071:1, so cards read as
   cards. This is exactly monday's arrangement: grey app chrome, white boards.

   MOTION follows Emil Kowalski's rules on top of Vibe's numbers — enter is
   ease-out, exit is faster than enter, nothing animates from scale(0), hover
   effects are gated behind (hover:hover) so a tap does not stick them on, and
   prefers-reduced-motion keeps opacity while dropping movement.
   ════════════════════════════════════════════════════════════════════════ */


/* ── 1. Motion — verbatim from Vibe ──────────────────────────────────────
   Vibe splits motion into "productive" (frequent, functional — must not make
   a repeated action feel slow) and "expressive" (rare, larger surfaces).
   That split is the same judgement Emil's frequency table makes, so the two
   systems agree and the existing --dur-* names simply map onto it. */
:root {
  --motion-productive-short:  70ms;
  --motion-productive-medium: 100ms;
  --motion-productive-long:   150ms;
  --motion-expressive-short:  250ms;
  --motion-expressive-long:   400ms;

  --motion-timing-enter:      cubic-bezier(0, 0, 0.35, 1);
  --motion-timing-exit:       cubic-bezier(0.4, 0, 1, 1);
  --motion-timing-transition: cubic-bezier(0.4, 0, 0.2, 1);
  --motion-timing-emphasize:  cubic-bezier(0, 0, 0.2, 1.4);

  /* Remap of the existing vocabulary onto Vibe's ladder. Every transition
     already written against --dur-fast/base/slow retimes itself here.
     --dur-fast drops 120ms -> 100ms and --dur-base 160ms -> 150ms: monday is
     a denser, quicker-feeling product than what these were tuned for, and
     both stay inside Emil's 100-160ms button-feedback band. */
  --dur-fast:   var(--motion-productive-medium); /* 100ms */
  --dur-base:   var(--motion-productive-long);   /* 150ms */
  --dur-slow:   var(--motion-expressive-short);  /* 250ms */
  --dur-drawer: var(--motion-expressive-long);   /* 400ms */

  /* --ease-out is unchanged: cubic-bezier(0.23,1,0.32,1) is a STRONGER
     ease-out than Vibe's enter curve, and it is already used by hundreds of
     rules that were tuned against it. Vibe's own curves are available by
     name above for the new components below. */
  --ease-emphasize: var(--motion-timing-emphasize);
  --ease-exit:      var(--motion-timing-exit);
}


/* ── 2. Radius — Vibe's ladder is 4 / 8 / 12 / 16 ────────────────────────
   monday reads flatter and tighter than the old 4/6/8/12/16/20 set. Vibe
   names only three roles (small 4, medium 8, big 16); the six existing names
   collapse onto them. 8px is the workhorse — every board, card and widget. */
:root {
  --r-xs:  4px;
  --r-sm:  4px;   /* was 6px — Vibe has no 6; buttons and chips are 4 */
  --r-md:  8px;   /* cards, inputs, menus — the monday default */
  --r-lg:  8px;   /* was 12px — widgets are 8 in monday, not 12 */
  --r-xl:  12px;
  --r-2xl: 16px;  /* was 20px — Vibe's --border-radius-big */
  --r-full: 999px;
}


/* ── 3. Elevation — Vibe's four shadows ──────────────────────────────────
   Wider and softer than the old set, and neutral-black rather than tinted
   with the ink colour. On a grey canvas a tinted shadow reads as a smudge. */
:root {
  --sh-xs: 0 4px 6px -4px rgba(0, 0, 0, .10);
  --sh-sm: 0 4px 8px rgba(0, 0, 0, .12);   /* Vibe's .2 alpha is tuned for a
                                              shadow cast onto white; on the
                                              riverstone canvas it reads
                                              muddy, so it is lightened. */
  --sh-md: 0 6px 20px rgba(0, 0, 0, .14);
  --sh-lg: 0 15px 50px rgba(0, 0, 0, .22);
  --sh-focus: 0 0 0 2px var(--surface), 0 0 0 4px var(--accent);
}


/* ── 4. Type — Figtree for Latin, Rubik for Hebrew ───────────────────────
   Vibe's --font-family is "Figtree, Roboto, Noto Sans Hebrew, ...". Figtree
   carries NO Hebrew glyphs, so on a Hebrew-first dashboard it can only ever
   set the Latin run — which is exactly what we want and exactly how Vibe's
   own stack behaves. Hebrew falls through to Rubik, which is already loaded
   and is the correct Hebrew face. Latin text and every numeral pick up
   monday's actual letterforms; Hebrew is untouched.

   Sizes are Vibe's: 14 / 14 / 16 / 18 / 24 / 30. The existing ladder is kept
   where it already agreed (200=14, 300=18, 400=24) and moved where it did
   not: --fs-100 11px -> 12px (11px is below monday's smallest and was the
   most-complained-about size on a phone), --fs-500 32px -> 30px. */
:root {
  --font-sans: 'Figtree', 'Rubik', 'Heebo', -apple-system, BlinkMacSystemFont,
               'Segoe UI', Roboto, 'Noto Sans Hebrew', Arial, sans-serif;

  --fs-100: 12px;  --lh-100: 18px;
  --fs-200: 14px;  --lh-200: 20px;
  --fs-300: 18px;  --lh-300: 24px;
  --fs-400: 24px;  --lh-400: 32px;
  --fs-500: 30px;  --lh-500: 42px;

  /* Vibe weights top out at 500 ("bold"). monday genuinely does not use 700
     in product chrome — it gets weight from size and colour instead. The 600
     and 700 names stay so nothing breaks, but they are pulled down a step so
     the UI stops shouting. */
  --fw-regular: 400;
  --fw-medium:  500;
  --fw-semi:    600;
  --fw-bold:    700;
}

body,
input, textarea, select, button {
  font-family: var(--font-sans);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* Tabular numerals everywhere a figure can change in place. A KPI that
   reflows its own width every poll is the single most distracting thing on
   a live dashboard, and monday's numbers never do it. */
.kpi-value, .stat-value, .mon-count, [data-kpi],
.ea-num, td[data-num], .chart-legend-value {
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum' 1;
}


/* ── 5. Canvas and surfaces — monday's grey chrome, white boards ─────────
   See the contrast table in the header: this is measured, and it repairs the
   1.00:1 figure/ground the token file itself flags as a defect. */
:root {
  --bg: #f6f7fb;          /* Vibe --color-riverstone_gray */
  --surface: #ffffff;     /* boards stay pure white — carries all text */
  --surface-2: #ffffff;
  --surface-3: #eceff8;   /* inset wells / progress tracks — must recede
                             BELOW the canvas now that the canvas is grey */

  /* Vibe's border pair. --color-ui_grey #dcdfec and --layout-border-color
     #d0d4e4 are opaque, but the existing tokens are ink-alpha so they
     composite correctly over card, well and canvas alike — that property is
     worth more than matching the hex, so the alpha form is kept and only
     retuned to land on monday's values over white. */
  --border:        rgba(13, 22, 54, .10);  /* ≈ #e5e7ef on white */
  --border-2:      rgba(13, 22, 54, .17);  /* ≈ #d5d8e4 — Vibe layout border */
  --border-strong: rgba(13, 22, 54, .17);

  /* monday's hover wash: a translucent grey that works on any ground. */
  --hover-wash: rgba(103, 104, 121, .10);
  --hover-wash-strong: rgba(103, 104, 121, .16);
}

[data-theme="dark"] {
  /* Vibe's dark theme: --primary-background-color #181b34,
     --secondary-background-color #30324e, --layout-border-color #4b4e69. */
  --bg: #181b34;
  --surface: #20233f;
  --surface-2: #30324e;
  --surface-3: #12142a;
  --border:        rgba(255, 255, 255, .10);
  --border-2:      rgba(255, 255, 255, .17);
  --border-strong: rgba(255, 255, 255, .17);
  --hover-wash: rgba(255, 255, 255, .07);
  --hover-wash-strong: rgba(255, 255, 255, .12);
  --sh-xs: 0 4px 6px -4px rgba(9, 11, 25, .5);
  --sh-sm: 0 4px 8px rgba(9, 11, 25, .5);
  --sh-md: 0 6px 20px rgba(9, 11, 25, .5);
  --sh-lg: 0 15px 50px rgba(9, 11, 25, .5);
}


/* ── 6. The board palette — Vibe's status colours, verbatim ──────────────
   These are the colours monday is recognised by. The -fg pair on each is not
   decoration: done-green and working-orange fail white text badly (2.21:1 and
   1.90:1) and need monday's own mud-black on them (5.71:1 and 6.64:1), while
   stuck-red and blue carry white (4.52:1, 4.53:1). Getting that backwards is
   the most common way a status chip becomes unreadable. */
:root {
  --mon-done:      #00c875;  --mon-done-fg:      #323338;
  --mon-working:   #fdab3d;  --mon-working-fg:   #323338;
  --mon-stuck:     #df2f4a;  --mon-stuck-fg:     #ffffff;
  --mon-blue:      #0073ea;  --mon-blue-fg:      #ffffff;
  --mon-bright:    #579bfc;  --mon-bright-fg:    #323338;
  --mon-purple:    #a25ddc;  --mon-purple-fg:    #ffffff;
  --mon-dark-purple:#784bd1; --mon-dark-purple-fg:#ffffff;
  --mon-grass:     #037f4c;  --mon-grass-fg:     #ffffff;
  --mon-lime:      #9cd326;  --mon-lime-fg:      #323338;
  --mon-egg:       #ffcb00;  --mon-egg-fg:       #323338;
  --mon-orange:    #ff6d3b;  --mon-orange-fg:    #ffffff;
  --mon-sofia:     #e50073;  --mon-sofia-fg:     #ffffff;
  --mon-lipstick:  #ff5ac4;  --mon-lipstick-fg:  #323338;
  --mon-aquamarine:#4eccc6;  --mon-aquamarine-fg:#323338;
  --mon-navy:      #225091;  --mon-navy-fg:      #ffffff;
  --mon-indigo:    #5559df;  --mon-indigo-fg:    #ffffff;
  --mon-winter:    #9aadbd;  --mon-winter-fg:    #323338;
  --mon-explosive: #c4c4c4;  --mon-explosive-fg: #323338;

  /* Vibe's semantic trio, which is a different (darker, AA-safe) set from
     the board palette above — monday uses the bright board colours as FILLS
     and these as TEXT. Our --status-* tokens already hold AA-safe text
     colours measured on both grounds, so they are deliberately left alone;
     these are here for the new components only. */
  --mon-positive: #00854d;
  --mon-negative: #d83a52;
  --mon-warning:  #ffcb00;
}


/* ── 7. Status chips — monday's most recognisable component ──────────────
   A solid, full-bleed colour block with centred text. Not a pale pill with a
   coloured dot: on a monday board the colour IS the cell. */
.mon-chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-width: 92px;
  padding: 4px 10px;
  border-radius: var(--r-xs);
  font-size: var(--fs-100);
  font-weight: var(--fw-medium);
  line-height: var(--lh-100);
  letter-spacing: .01em;
  white-space: nowrap;
  background: var(--mon-explosive);
  color: var(--mon-explosive-fg);
  transition: filter var(--motion-productive-medium) var(--motion-timing-transition),
              transform var(--motion-productive-medium) var(--motion-timing-transition);
}
.mon-chip--done    { background: var(--mon-done);    color: var(--mon-done-fg) }
.mon-chip--working { background: var(--mon-working); color: var(--mon-working-fg) }
.mon-chip--stuck   { background: var(--mon-stuck);   color: var(--mon-stuck-fg) }
.mon-chip--idle    { background: var(--mon-explosive); color: var(--mon-explosive-fg) }
.mon-chip--info    { background: var(--mon-blue);    color: var(--mon-blue-fg) }

@media (hover: hover) and (pointer: fine) {
  .mon-chip[role="button"]:hover,
  button.mon-chip:hover { filter: brightness(.94) }
}
.mon-chip[role="button"]:active,
button.mon-chip:active { transform: scale(.97) }


/* ── 8. Press feedback on everything pressable ───────────────────────────
   Emil's rule: a button that does not move on press does not feel heard.
   Scoped to the existing button classes rather than a bare `button` so the
   many icon-only affordances that are already transform-animated are not
   double-driven. */
.ea-btn, .btn-primary, .btn-secondary, .mon-btn,
.tp-open, .client-btn, .ob-submit-btn {
  transition: transform var(--motion-productive-medium) var(--ease-out),
              background-color var(--motion-productive-medium) var(--motion-timing-transition),
              border-color var(--motion-productive-medium) var(--motion-timing-transition),
              box-shadow var(--motion-productive-medium) var(--motion-timing-transition);
}
.ea-btn:active, .btn-primary:active, .btn-secondary:active, .mon-btn:active,
.tp-open:active, .client-btn:active, .ob-submit-btn:active {
  transform: scale(.97);
}
/* A disabled control must not pretend to respond. */
.ea-btn:disabled:active, .btn-primary:disabled:active,
.btn-secondary:disabled:active, .mon-btn:disabled:active {
  transform: none;
}


/* ── 9. Card lift ────────────────────────────────────────────────────────
   monday's cards rise 2px and gain a shadow on hover. Gated behind
   (hover:hover) — on a phone this used to latch on after a tap and stay. */
@media (hover: hover) and (pointer: fine) {
  .mon-card:hover,
  .mon-lift:hover {
    transform: translateY(-2px);
    box-shadow: var(--sh-md);
    border-color: var(--border-2);
  }
}
.mon-card, .mon-lift {
  transition: transform var(--motion-productive-long) var(--ease-out),
              box-shadow var(--motion-productive-long) var(--ease-out),
              border-color var(--motion-productive-long) var(--motion-timing-transition);
}


/* ── 10. Focus — one visible ring, keyboard only ─────────────────────────
   :focus-visible, not :focus, so a mouse click does not paint a ring. */
.ea-btn:focus-visible, .btn-primary:focus-visible, .btn-secondary:focus-visible,
.mon-btn:focus-visible, .mon-card:focus-visible, .mon-chip:focus-visible,
input:focus-visible, textarea:focus-visible, select:focus-visible,
[tabindex]:focus-visible {
  outline: none;
  box-shadow: var(--sh-focus);
  border-color: var(--accent);
}


/* ── 11. Entrance motion ─────────────────────────────────────────────────
   Two primitives, both opacity+transform only so they stay off the main
   thread. Nothing starts at scale(0) or at a large offset — 8px and .98 are
   enough to read as movement without looking like the page is assembling
   itself. */
@keyframes mon-rise {
  from { opacity: 0; transform: translateY(8px) }
  to   { opacity: 1; transform: none }
}
@keyframes mon-pop {
  from { opacity: 0; transform: scale(.96) }
  to   { opacity: 1; transform: none }
}

.mon-enter {
  opacity: 0;
  animation: mon-rise var(--motion-expressive-short) var(--motion-timing-enter) forwards;
}
.mon-enter--pop {
  animation-name: mon-pop;
}

/* Stagger. 40ms steps, capped at 10 — past ~400ms the last card is arriving
   after the user has already started reading the first, which reads as lag
   rather than choreography. Anything beyond the 10th appears with the 10th. */
.mon-stagger > * {
  opacity: 0;
  animation: mon-rise var(--motion-expressive-short) var(--motion-timing-enter) forwards;
}
.mon-stagger > :nth-child(1)  { animation-delay: 0ms }
.mon-stagger > :nth-child(2)  { animation-delay: 40ms }
.mon-stagger > :nth-child(3)  { animation-delay: 80ms }
.mon-stagger > :nth-child(4)  { animation-delay: 120ms }
.mon-stagger > :nth-child(5)  { animation-delay: 160ms }
.mon-stagger > :nth-child(6)  { animation-delay: 200ms }
.mon-stagger > :nth-child(7)  { animation-delay: 240ms }
.mon-stagger > :nth-child(8)  { animation-delay: 280ms }
.mon-stagger > :nth-child(9)  { animation-delay: 320ms }
.mon-stagger > :nth-child(n+10) { animation-delay: 360ms }


/* ── 12. Skeletons ───────────────────────────────────────────────────────
   A shimmer that reads as "loading" without a spinner. Direction-aware: the
   sweep must travel with the reading direction or it looks like it is
   rewinding, and these dashboards are RTL. */
@keyframes mon-shimmer {
  from { background-position: 200% 0 }
  to   { background-position: -200% 0 }
}
.mon-skeleton {
  border-radius: var(--r-xs);
  background: linear-gradient(90deg,
              var(--surface-3) 25%,
              var(--hover-wash) 37%,
              var(--surface-3) 63%);
  background-size: 200% 100%;
  animation: mon-shimmer 1.4s linear infinite;
}
[dir="ltr"] .mon-skeleton { animation-direction: reverse }
.mon-skeleton--text { height: 12px; margin-block: 6px }
.mon-skeleton--title { height: 18px; width: 45%; margin-block: 8px }
.mon-skeleton--block { height: 76px }


/* ── 13. Live pulse ──────────────────────────────────────────────────────
   The "something is actually running" dot. Two rings so it reads at a glance
   in peripheral vision, which is the whole job of a status light. */
@keyframes mon-pulse-ring {
  0%   { transform: scale(.9); opacity: .7 }
  70%  { transform: scale(2.2); opacity: 0 }
  100% { transform: scale(2.2); opacity: 0 }
}
.mon-dot {
  position: relative;
  display: inline-block;
  width: 8px; height: 8px;
  border-radius: var(--r-full);
  background: var(--mon-done);
  flex: none;
}
.mon-dot::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: inherit;
  animation: mon-pulse-ring 1.8s var(--motion-timing-transition) infinite;
}
.mon-dot--working { background: var(--mon-working) }
.mon-dot--stuck   { background: var(--mon-stuck) }
/* An idle light must not pulse — a heartbeat on something that is not
   running is a lie the operator will act on. */
.mon-dot--idle    { background: var(--mon-explosive) }
.mon-dot--idle::after { display: none }


/* ── 14. Progress ────────────────────────────────────────────────────────
   monday's "battery" bar: segmented, flat, filled with board colours. */
.mon-battery {
  display: flex;
  height: 22px;
  border-radius: var(--r-xs);
  overflow: hidden;
  background: var(--surface-3);
}
.mon-battery > span {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--fs-100);
  font-weight: var(--fw-medium);
  color: var(--mon-done-fg);
  overflow: hidden;
  white-space: nowrap;
  /* width is the animated property here on purpose: this bar changes only on
     a data refresh, not per-frame, so one layout pass is cheaper than the
     wrapper+transform rig that would avoid it. */
  transition: width var(--motion-expressive-short) var(--motion-timing-transition);
}
.mon-battery .seg-done    { background: var(--mon-done) }
.mon-battery .seg-working { background: var(--mon-working); color: var(--mon-working-fg) }
.mon-battery .seg-stuck   { background: var(--mon-stuck);   color: var(--mon-stuck-fg) }
.mon-battery .seg-idle    { background: var(--mon-explosive); color: var(--mon-explosive-fg) }


/* ── 15. Board rows ──────────────────────────────────────────────────────
   monday's table: a coloured group spine on the leading edge, rows that
   wash on hover, and a leading-edge accent that grows in rather than
   appearing. Logical properties throughout so RTL is correct by default. */
.mon-board {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  overflow: hidden;
}
.mon-board__group {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 14px;
  font-size: var(--fs-200);
  font-weight: var(--fw-medium);
  color: var(--group-color, var(--mon-blue));
  border-block-end: 1px solid var(--border);
}
.mon-row {
  position: relative;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 14px;
  border-block-end: 1px solid var(--border);
  transition: background-color var(--motion-productive-medium) var(--motion-timing-transition);
}
.mon-row:last-child { border-block-end: 0 }
.mon-row::before {
  content: '';
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  width: 4px;
  background: var(--group-color, var(--mon-blue));
  transform: scaleY(0);
  transform-origin: center;
  transition: transform var(--motion-productive-long) var(--ease-out);
}
.mon-row:hover::before,
.mon-row[aria-selected="true"]::before { transform: scaleY(1) }
@media (hover: hover) and (pointer: fine) {
  .mon-row:hover { background: var(--hover-wash) }
}


/* ── 16. Widget frame ────────────────────────────────────────────────────
   The container every monday dashboard widget sits in. */
.mon-widget {
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  box-shadow: var(--sh-xs);
  overflow: hidden;
}
.mon-widget__hd {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 12px 16px;
  border-block-end: 1px solid var(--border);
}
.mon-widget__title {
  font-size: var(--fs-200);
  font-weight: var(--fw-medium);
  color: var(--text);
}
.mon-widget__actions { margin-inline-start: auto; display: flex; gap: 4px }
.mon-widget__bd { padding: 16px; flex: 1; min-height: 0 }


/* ── 17. Reduced motion ──────────────────────────────────────────────────
   Reduced motion means gentler, not none. Opacity and colour survive because
   they carry meaning (this appeared / this changed state); every translate,
   scale and infinite loop is dropped. The shimmer and the pulse ring are
   removed outright — an endless loop is the exact thing that triggers
   vestibular discomfort. */
@media (prefers-reduced-motion: reduce) {
  .mon-enter, .mon-stagger > * {
    animation: none;
    opacity: 1;
  }
  .mon-skeleton {
    animation: none;
    background: var(--surface-3);
  }
  .mon-dot::after { animation: none; display: none }
  .mon-card, .mon-lift { transition: box-shadow var(--motion-productive-long) linear }
  .mon-card:hover, .mon-lift:hover { transform: none }
  .ea-btn:active, .btn-primary:active, .btn-secondary:active,
  .mon-btn:active, .mon-chip:active { transform: none }
  .mon-row::before { transition: none }
}


/* ── 18. The assistant panel, as monday's Updates ────────────────────────
   monday's conversation surface is not a chat bubble stack. It is an
   "Updates" feed: an avatar and a name on every entry, the body in a plain
   block, and a composer that looks like a document field rather than a
   messenger input. That is the shape this reskins ours into.

   The spend gate inside this panel is untouched functionally — it is
   restyled to look like a monday approval card and nothing else. It still
   renders from the same `pending` payload, still carries the same two
   buttons, and still cannot fire a paid run without a deliberate click. */

#chat-panel {
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  box-shadow: var(--sh-lg);
  background: var(--surface);
  /* Enter with the emphasize curve — this is an expressive surface opened a
     few times an hour, not a keyboard action, so a little overshoot is
     earned. Exit uses the exit curve and is faster: the user has already
     decided to dismiss it. */
  transition: opacity var(--motion-productive-long) var(--motion-timing-exit),
              transform var(--motion-productive-long) var(--motion-timing-exit),
              visibility var(--motion-productive-long);
  transform: translateY(8px) scale(.98);
}
#chat-panel.open {
  transform: none;
  transition: opacity var(--motion-expressive-short) var(--motion-timing-emphasize),
              transform var(--motion-expressive-short) var(--motion-timing-emphasize),
              visibility var(--motion-expressive-short);
}

.chat-hd {
  padding: 12px 16px;
  border-block-end: 1px solid var(--border);
  background: var(--surface);
}
.chat-title { font-weight: var(--fw-medium) }
.chat-state {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--fs-100);
  color: var(--text-3);
}

.chat-log {
  gap: 0;
  padding: 4px 0;
  background: var(--surface);
  scroll-behavior: smooth;
}

/* Every entry is a full-width row with a leading avatar — no alignment
   games, which is what makes an Updates feed readable at a glance where a
   bubble stack is not. */
.chat-msg {
  align-self: stretch !important;
  max-width: none !important;
  display: grid;
  grid-template-columns: 28px 1fr;
  gap: 10px;
  padding: 10px 16px;
  border-radius: 0;
  background: transparent !important;
  color: var(--text) !important;
  animation: mon-rise var(--motion-productive-long) var(--motion-timing-enter);
}
.chat-msg + .chat-msg { border-block-start: 1px solid var(--border) }

.chat-msg::before {
  content: attr(data-avatar);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px; height: 28px;
  border-radius: var(--r-full);
  font-size: var(--fs-100);
  font-weight: var(--fw-medium);
  background: var(--mon-explosive);
  color: var(--mon-explosive-fg);
}
.chat-msg.is-me::before   { background: var(--accent);      color: var(--on-accent) }
.chat-msg.is-bot::before  { background: var(--mon-blue);    color: var(--mon-blue-fg) }
.chat-msg.is-note::before { background: var(--mon-working); color: var(--mon-working-fg) }

.chat-who {
  font-size: var(--fs-100);
  font-weight: var(--fw-medium);
  color: var(--text-2);
  margin-block-end: 2px;
}
.chat-body { font-size: var(--fs-200); line-height: 1.6; white-space: pre-wrap }
.chat-msg.is-note .chat-body { color: var(--text-2) }

/* The typing indicator. Three dots on staggered offsets — the classic
   because it reads as "working" without claiming progress it cannot know. */
.chat-typing { display: inline-flex; gap: 3px; align-items: center }
.chat-typing i {
  width: 5px; height: 5px; border-radius: var(--r-full);
  background: var(--text-3);
  animation: mon-typing 1.2s var(--motion-timing-transition) infinite;
}
.chat-typing i:nth-child(2) { animation-delay: .15s }
.chat-typing i:nth-child(3) { animation-delay: .30s }
@keyframes mon-typing {
  0%, 60%, 100% { opacity: .25; transform: translateY(0) }
  30%           { opacity: 1;   transform: translateY(-3px) }
}

/* The approval card. Deliberately the loudest thing in the panel: it is the
   one surface here that can spend the user's money. */
.chat-msg.is-gate {
  display: block;
  margin: 10px 16px;
  padding: 14px;
  border: 1px solid var(--border-2);
  border-inline-start: 4px solid var(--mon-working);
  border-radius: var(--r-md);
  background: var(--surface-2) !important;
  box-shadow: var(--sh-xs);
}
.chat-msg.is-gate::before { content: none }
.gate-title { font-weight: var(--fw-medium); margin-block-end: 8px }
.gate-row {
  display: flex; justify-content: space-between; gap: 12px;
  font-size: var(--fs-200); padding-block: 3px;
}
.gate-row b { color: var(--text-3); font-weight: var(--fw-regular) }
.gate-note { font-size: var(--fs-100); color: var(--text-3); margin-block-start: 6px }
.gate-warn {
  font-size: var(--fs-100);
  color: var(--mon-negative);
  margin-block-start: 6px;
  font-weight: var(--fw-medium);
}
.gate-actions { display: flex; gap: 8px; justify-content: flex-end; margin-block-start: 12px }

.chat-input-row {
  padding: 12px 16px;
  border-block-start: 1px solid var(--border);
  gap: 8px;
  align-items: flex-end;
}
.chat-input-row textarea {
  border-radius: var(--r-md);
  border: 1px solid var(--border-2);
  padding: 9px 12px;
  font-family: var(--font-sans);
  font-size: var(--fs-200);
  transition: border-color var(--motion-productive-medium) var(--motion-timing-transition),
              box-shadow var(--motion-productive-medium) var(--motion-timing-transition);
}

/* The launcher. A pressable circle that lifts on hover and dips on press —
   the same two gestures as every other control here, so it feels like part
   of the system rather than a floating add-on. */
#chat-fab {
  box-shadow: var(--sh-md);
  transition: transform var(--motion-productive-long) var(--ease-out),
              box-shadow var(--motion-productive-long) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  #chat-fab:hover { transform: translateY(-2px); box-shadow: var(--sh-lg) }
}
#chat-fab:active { transform: scale(.94) }

@media (prefers-reduced-motion: reduce) {
  .chat-msg { animation: none }
  .chat-typing i { animation: none; opacity: .5 }
  #chat-panel, #chat-panel.open { transform: none }
  #chat-fab:hover, #chat-fab:active { transform: none }
  .chat-log { scroll-behavior: auto }
}


/* ── 19. Sheets and modals ───────────────────────────────────────────────
   Drawers use the expressive-long duration and Vibe's enter curve; the
   backdrop fades on the shorter productive curve so the dimming never lags
   behind the panel it belongs to. Modals keep transform-origin: center —
   they are not anchored to a trigger. */
.sheet {
  transition: transform var(--motion-expressive-long) var(--ease-drawer),
              box-shadow var(--motion-expressive-long) var(--ease-drawer),
              visibility 0s linear var(--motion-expressive-long);
  box-shadow: var(--sh-lg);
}
/* A closed sheet is parked one width off-canvas with translateX, which moves
   the box but not its shadow's blur: three closed sheets (#sheet, #tab-picker,
   #ovw-picker) each bled a 50px shadow back across the viewport edge, a grey
   band down the left edge of every tab at every width (measured in the 390,
   768 and 1280 screenshots: the edge pixel painted rgb(174,174,177) on the
   #f6f7fb canvas; with this rule it paints #f6f7fb). Their
   controls also stayed in the Tab order while off-screen. Hidden once the
   slide-out has finished (the visibility delay above), visible instantly on
   open so the opener's focus() lands. */
.sheet:not(.open) { visibility: hidden; box-shadow: none; }
.sheet.open { transition-delay: 0s, 0s, 0s; }
.sheet-back {
  transition: opacity var(--motion-expressive-short) var(--motion-timing-transition),
              visibility var(--motion-expressive-short);
}
#modal-inner {
  border-radius: var(--r-md);
  box-shadow: var(--sh-lg);
  transform-origin: center;
  animation: mon-pop var(--motion-expressive-short) var(--motion-timing-enter);
}
@media (prefers-reduced-motion: reduce) {
  #modal-inner { animation: none }
}


/* ── 20. Video Studio ────────────────────────────────────────────────────
   The drop zone's two states. `.is-over` is toggled by the dragover/dragleave
   handlers rather than styled with :hover, because a drag does not produce a
   hover state — the pointer is carrying a payload, not pointing. */
#vs-drop.is-over {
  border-color: var(--accent);
  background: var(--accent-soft, var(--hover-wash));
}
@media (hover: hover) and (pointer: fine) {
  #vs-drop:hover { border-color: var(--border-strong) }
}
#vs-drop:focus-within {
  border-color: var(--accent);
  box-shadow: var(--sh-focus);
}

/* The library grid. A poster that has not loaded shows the card's own ground
   rather than a broken-image glyph, which is the difference between "still
   loading" and "this file is damaged" — and only one of those is true. */
#tab-video img { display: block; border-block-end: 1px solid var(--border) }

/* A clip with no poster shows its own frame; a format the browser cannot play
   says so in words rather than shimmering like something still on its way. */
#tab-video .vs-thumb {
  display: block; width: 100%; height: 112px; object-fit: cover;
  background: var(--surface-3); border-block-end: 1px solid var(--border);
}
#tab-video .vs-thumb--none {
  display: grid; place-items: center; color: var(--text-3); font-size: var(--fs-100);
}
/* The editor's preview. Black ground so a vertical clip letterboxes cleanly
   inside the 340px column instead of floating on white. */
#tab-video .vs-preview {
  display: block; width: 100%; max-height: 360px; border-radius: var(--r-md);
  background: #000;
}
/* "N לתיקון" is advice, not an alarm: an outline, not the red of a failure. */
.mon-chip.vs-chip-quiet {
  background: transparent; color: var(--text-2);
  box-shadow: inset 0 0 0 1px var(--border-strong);
}

/* An in-flight edit dims the panel it belongs to without hiding what it says.
   pointer-events stays on: the operator must still be able to read values and
   scroll while ffmpeg works. */
#vs-editor .mon-widget__bd:has(button:disabled) { opacity: .72 }

@media (max-width: 900px) {
  /* The editor drops under the grid rather than beside it. Two 340px columns
     do not fit a phone, and a horizontally scrolling editor is unusable. */
  #tab-video > div > div[style*="grid-template-columns"] {
    grid-template-columns: minmax(0, 1fr) !important;
  }
}

/* ── Chat panel: engine switch + Claude Code (2026-10-03) ──────────────────
   Claude Code answers run long and carry code, so its mode gets a taller,
   wider drawer. The quick assistant keeps the compact one. */
#chat-panel.cc-wide {
  width: min(460px, calc(100vw - 40px));
  max-height: min(720px, calc(100dvh - 110px));
  height: min(720px, calc(100dvh - 110px));
}
.chat-seg {
  display: inline-flex; padding: 2px; gap: 2px;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: 8px;
}
.chat-seg button {
  border: 0; background: transparent; color: var(--text-2); cursor: pointer;
  font: inherit; font-size: var(--fs-100); font-weight: var(--fw-medium);
  padding: 4px 10px; border-radius: 6px; white-space: nowrap;
}
.chat-seg button[aria-selected="true"] { background: var(--surface); color: var(--text); box-shadow: var(--sh-sm) }
.chat-seg button:focus-visible { outline: none; box-shadow: var(--sh-focus) }
.chat-msg.is-bot[data-avatar="CC"]::before { background: #d97757; color: #fff }
.chat-body code { font-size: .92em; background: var(--surface-2); border: 1px solid var(--border);
  border-radius: 4px; padding: 0 4px }
.cc-code { white-space: pre; overflow-x: auto; font-size: 12px; line-height: 1.5; margin: 6px 0;
  padding: 8px 10px; background: var(--surface-2); border: 1px solid var(--border); border-radius: 6px }
.cc-elapsed { font-size: var(--fs-100); color: var(--text-3); margin-inline-start: 8px }
/* Four engines (2026-10-21): the switch wraps rather than pushing the close
   button off a narrow drawer. The live reply hides until its first words. */
.chat-seg { flex-wrap: wrap; max-width: 100% }
.chat-seg button { padding: 4px 8px }
.chat-msg[data-seen] { animation: none }
/* The wide (agent) drawer shows its state as the dot alone; the live reply
   already says it is working, with the elapsed time. */
#chat-panel.cc-wide .chat-state-txt { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%) }
#chat-live .chat-body:empty { display: none }
.chat-msg.is-bot[data-avatar="GPT"]::before { background: #10a37f; color: #fff }
.chat-msg.is-bot[data-avatar="G"]::before { background: #1a73e8; color: #fff }
.cc-steps { margin: 6px 0 0; padding-inline-start: 18px; display: grid; gap: 6px; font-size: var(--fs-100) }
.cc-steps code { white-space: normal; word-break: break-all }
.chat-suggest { display: flex; flex-direction: column; gap: 6px; padding: 4px 0 }
.chat-chip {
  text-align: start; font: inherit; font-size: var(--fs-100); color: var(--text-2); cursor: pointer;
  background: var(--surface); border: 1px solid var(--border); border-radius: 8px; padding: 8px 10px;
}
.chat-chip:hover { border-color: var(--accent); color: var(--text) }
.chat-chip:focus-visible { outline: none; box-shadow: var(--sh-focus) }

/* ── Agency view client rows (2026-10-03) ─────────────────────────────────
   One scannable line per client: identity, progress, output, spend, recency. */
.agy-row { gap: 14px; padding-block: 12px }
.agy-row-id { display: flex; flex-direction: column; min-width: 0; flex: 1 }
.agy-row-id .agy-row-name { font-weight: var(--fw-medium) }
.agy-row-mix { font-size: var(--fs-100); color: var(--text-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis }
.agy-row-tag.is-warn { color: var(--status-warn); border-color: color-mix(in srgb, var(--status-warn) 40%, transparent);
  background: color-mix(in srgb, var(--status-warn) 8%, transparent) }
.agy-row-prog { display: inline-flex; align-items: center; gap: 8px; flex: none }
.agy-bar { width: 96px; height: 6px; border-radius: 99px; background: var(--surface-2); overflow: hidden; display: block }
.agy-bar i { display: block; height: 100%; background: var(--status-done); border-radius: inherit }
.agy-col { flex: none; min-width: 78px; text-align: end; font-variant-numeric: tabular-nums }
.agy-col b { color: var(--text); font-weight: var(--fw-semi) }
/* 2026-10-23: one column template for every row, so progress, output, spend
   and recency line up down the list. As flex rows, a client with a warning
   tag pushed its numbers sideways and an English name floated mid-row. */
.agy-row { display: grid; align-items: center;
  grid-template-columns: 32px minmax(0, 1fr) minmax(0, 180px) 132px 168px 76px 112px }
.agy-row-tags { display: flex; flex-wrap: wrap; gap: 6px; min-width: 0 }
/* dir="auto" makes an English name LTR, so `start` put it on the left of an
   RTL row. Align it to the page side instead. */
html[dir="rtl"] .agy-row-name { text-align: right }
.agy-bar { background: var(--border) }
.agy-items { display: inline-flex; align-items: center; justify-content: flex-end; gap: 8px }
.agy-mini { width: 64px; height: 6px; border-radius: 99px; background: var(--border); overflow: hidden; flex: none }
.agy-mini i { display: block; height: 100%; background: var(--accent); border-radius: inherit }
@media (max-width: 760px) {
  .agy-row { display: flex; flex-wrap: wrap; row-gap: 6px }
  .agy-row-id { flex-basis: calc(100% - 40px) }
  .agy-col { min-width: 0 }
  .agy-bar { width: 64px }
}

/* ── Phone redesign: contrast, weight, a quieter palette (2026-10-26) ──────
   SELECTOR NOTE. Every dark rule below is `:root[data-theme="dark"]`, not the
   bare `[data-theme="dark"]` of section 5. dashboard-tokens.css declares its
   dark theme on :root[data-theme="dark"] (0,2,0), which outranks a bare
   attribute selector (0,1,0) whatever the load order — so a dark token set
   only in section 5's form never reaches the page. Measured, not assumed:
   the live dark canvas is the tokens file's #121614, not Vibe's #181b34.

   1. --text-3, dark. #8b9591 measured 4.02:1 on Vibe's --surface-2 #30324e,
      an AA failure for the subtitle text it carries. #9aa39f, same hue:
        on #30324e 4.80   #2a322f 5.08   #212826 5.81   #1a201e 6.39   #121614 7.05
      and it stays a step under --text-2 #aab4b0 (5.83:1 on #30324e), so
      secondary and tertiary text still read as two levels. The search icon's
      literal stroke in dashboard.css follows it. */
:root[data-theme="dark"] {
  --text-3: #9aa39f;
}

/* 2. Weight. Titles and status labels are bold, descriptions are regular — a
   row is scanned by its bold run and read only when it matters. Kept to the
   handful of selectors the phone board shows, not the whole stylesheet. */
.card-title, .att-title, .fl-hd h3, .lib-card-title,
.qc-row__label, .fl-txt b, .agy-row-id .agy-row-name {
  font-weight: var(--fw-semi);
}
.mon-chip, .fl-tag, .agy-row-tag, .pl-seg__label, .mon-battery > span {
  font-weight: var(--fw-semi);
}
.qc-row__when, .qc-row__why, .qc-row__quote, .fl-txt small,
.fl-hd-sub, .flow-hd-sub, .pulse-sub, .agy-row-mix {
  font-weight: var(--fw-regular);
}

/* 3. Palette. Asaf asked for monday's status colours DESATURATED, and the
   chart hues were the loudest thing left on the page — the overview's stage
   bar painted amber at full chroma beside the pipeline rail's pale tints.
   Meaning is unchanged (done green, working amber, stuck red, running blue);
   only chroma comes down, toward the rail's sage.

   Board fills. Each -fg pair re-measured on its fill:
     light  done #5fae86 / #1f2023 6.12   working #d6a650 / #1f2023 7.31
            bright #6f9fca / #1f2023 5.81  stuck   #b9575c / #fff   4.58
     dark   done #5a9a7b / #1f2023 4.92   working #bf9550 / #1f2023 5.92
            bright #6890b5 / #1f2023 4.84  stuck   #b9575c / #fff   4.58
     both   blue #3d6a9a / #fff 5.64 (info chips; was #0073ea at 4.53)
   Adjacent fills stay apart: CIE76 dE done→bright→working→stuck 50/79/47
   light, 41/67/40 dark. */
:root {
  --mon-done:    #5fae86;  --mon-done-fg:    #1f2023;
  --mon-working: #d6a650;  --mon-working-fg: #1f2023;
  --mon-stuck:   #b9575c;  --mon-stuck-fg:   #ffffff;
  --mon-bright:  #6f9fca;  --mon-bright-fg:  #1f2023;
  --mon-blue:    #3d6a9a;  --mon-blue-fg:    #ffffff;

  /* Chart hues — muted jewel tones. Several are also used as TEXT (KPI
     figures, tag labels), so every one keeps AA on the card and the canvas:
       #2f7a70 5.07/4.74   #3b6e96 5.45/5.09   #4a7a50 5.01/4.68
       #7656a0 5.82/5.44   #a2583a 5.26/4.91   #9e4b6c 5.71/5.34
     Adjacent dE 33 50 77 66 35 (the old set's closest pair was 27). */
  --viz-1: #2f7a70; --viz-2: #3b6e96; --viz-3: #4a7a50;
  --viz-4: #7656a0; --viz-5: #a2583a; --viz-6: #9e4b6c;
}
:root[data-theme="dark"] {
  --mon-done:    #5a9a7b;
  --mon-working: #bf9550;
  --mon-bright:  #6890b5;

  /* Pastels for dark. Worst case is Vibe's #30324e: 6.51 5.90 6.74 5.66
     6.27 5.72; on the live #2a322f well 6.91 6.26 7.15 6.00 6.64 6.06.
     Adjacent dE 25 41 56 48 31. */
  --viz-1: #8cc7bd; --viz-2: #93b7d6; --viz-3: #a3c99e;
  --viz-4: #bba6d9; --viz-5: #e0ae8c; --viz-6: #d9a0b8;
}

/* The overview's stage bar ("N/20 שלבים הושלמו"). It drew --status-*, which
   are TEXT colours tuned for contrast and therefore the most saturated values
   in the theme. It now draws the board fills above; pending stays the well. */
.pulse-seg.is-done    { background: var(--mon-done) }
.pulse-seg.is-running { background: var(--mon-bright) }
.pulse-seg.is-fail    { background: var(--mon-stuck) }
.pulse-seg.is-warn,
.pulse-seg.is-redone  { background: var(--mon-working) }

/* ── Approvals timeline + flow feed: LTR islands, board rhythm (2026-10-26) ─
   Stage ids, English stage names and JSON in evidence quotes sit inside
   Hebrew lines. Each is a <bdi dir="ltr"> (ltrChip / ltrRuns in dashboard.js)
   so its brackets and punctuation stay with it instead of being reordered by
   the surrounding RTL run. A chip never wraps; a run inside a quote may, or a
   long JSON line would push the card off a 390px screen. */
.ltr-chip { unicode-bidi: isolate; direction: ltr; white-space: nowrap; }
.ltr-run  { unicode-bidi: isolate; direction: ltr; }

/* monday rows are 36-40px with clear separation. The list gap and row
   padding were inline (gap 8px, padding 9px 11px) and read as one block. */
.qc-list { display: flex; flex-direction: column; gap: var(--sp-3, 12px); }
.qc-row {
  min-block-size: 40px;
  padding: 12px 14px;
  box-shadow: inset 0 0 0 1px var(--card-line, var(--border));
}
.qc-row[hidden] { display: none; }
.qc-row__hd { gap: 8px 10px; }
.qc-row__why, .qc-row__quote { margin-top: 8px; }
.qc-none { margin: 0; padding: 12px 2px; font-size: 13px; color: var(--text-3); }
.qc-filter .lib-chips { margin-block-end: var(--sp-3, 12px); }

.fl-row { min-block-size: 52px; padding: 12px 8px; }
.fl-txt { gap: 3px; }
.fl-sid { font-size: 11px; font-weight: var(--fw-semi); color: var(--text-2); }

@media (max-width: 640px) {
  .qc-list { gap: 14px; }
  .qc-row  { padding: 14px; }
  .qc-filter .lib-chips { margin-inline: -2px; }
  .fl-row  { padding: 14px 6px; }
}

/* ── Phone bottom bar: monday's rail, active tab filled (2026-10-26) ───────
   The bar drew every icon at stroke 1.75, 20px, and marked the active tab
   only with a 2px top border and a colour change — at arm's length the five
   outlines read as one weight, and the marker sat on the bar's edge, away
   from the thing it marked. monday marks the active rail item with a tinted
   shape behind the icon; this is that pill, plus a fill inside the icon's
   own shapes so the selected tab is solid and the rest are outlines.
   CSS stroke-width beats the constants' presentation attribute, so the
   heavier line applies here only and the desktop sidebar keeps 1.75. */
@media (max-width: 768px) {
  .mob-tab { gap: 2px; font-weight: 500; color: var(--text-2); }
  .mob-tab.active { border-top-color: transparent; font-weight: 700; }
  .mob-tab-ico {
    inline-size: 52px; block-size: 28px;
    border-radius: 999px;
    transition: background var(--dur-fast, .12s) var(--ease-out, ease-out);
  }
  .mob-tab-ico svg { inline-size: 22px; block-size: 22px; stroke-width: 2.25; }
  .mob-tab.active .mob-tab-ico {
    background: color-mix(in srgb, var(--accent) 18%, transparent);
  }
  .mob-tab.active .mob-tab-ico svg :is(rect, circle, path) {
    fill: currentColor;
    fill-opacity: .22;
  }
  .mob-tab.active .mob-tab-lbl { color: var(--accent); }

  /* ── Phone header: the client's name gets the room ─────────────────────
     Measured at 390px before this block: the switcher's label was 57px
     wide for "Dr. Douglas Altman Chiropractic" (scrollWidth 215) and 27px
     for "EA Marketing" (89) — the one thing in the header that says whose
     dashboard this is showed "Dr. Do…". The room went to a 69px "✳ Claude"
     text button, 12px/10px gaps and 36px of chevron padding.
     Claude becomes a 44px icon (its aria-label already names it), the
     action strip is sized to its three 44px buttons instead of half the
     row, and a long name wraps to two lines rather than ellipsising.
     The strip's exception badges (API down / files short) still scroll in
     behind the buttons, as they did before. */
  #topbar { padding-inline: 8px !important; gap: 6px; }
  #hdr-right {
    flex: 0 0 auto;
    max-inline-size: calc(3 * 44px + 2 * 4px);
  }
  #hdr-right > div { gap: 4px !important; }
  #hdr-right .hdr-claude { inline-size: 44px; padding-inline: 0; gap: 0; }
  #hdr-right .hdr-claude-label { display: none; }
  #hdr-right .hdr-claude-glyph { font-size: 18px; line-height: 1; }

  .mob-switcher-wrap { max-width: none; }
  .mob-switcher-wrap::after { right: auto; inset-inline-start: 8px; }
  .mob-switcher-btn { padding-block: 0; padding-inline: 20px 6px; gap: 4px; }
  .mob-switcher-label {
    white-space: normal;
    overflow-wrap: anywhere;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    /* 12px is what fits "Dr. Douglas Altman / Chiropractic" (112px +
       119px at 600) in two lines of the ~116px this row leaves. */
    font-size: 12px;
    line-height: 1.2;
  }
}


/* ── 21. Stage item panel (2026-10-26) ───────────────────────────────────
   A pipeline chip opens monday's item panel: the stage's state, its review
   verdict and a readable preview of the output it wrote. Desktop keeps the
   side drawer from section 19; below 768px the same #stage-drawer becomes a
   bottom sheet — a side drawer at full width on a phone hid the board it
   was opened from and put the close button at the far edge from the thumb. */
#stage-drawer .sheet-grab { display: none }
#stage-drawer .sd-badge {
  border-radius: 999px;
  background: color-mix(in srgb, currentColor 10%, transparent);
  font-weight: var(--fw-semi, 600);
}
#stage-drawer .sd-badge.is-pending { color: var(--text-2) }
/* Light theme: --g is the legacy #15803d, 4.37:1 on its own 10% tint at 12px.
   The AA-checked --status-done on a 6% tint measures 4.80:1. */
:root:not([data-theme="dark"]) #stage-drawer .sd-badge { background: color-mix(in srgb, currentColor 6%, transparent) }
:root:not([data-theme="dark"]) #stage-drawer .sd-badge.is-done { color: var(--status-done) }
#stage-drawer .sd-verdict.is-done { color: var(--status-done) }
#stage-drawer .sd-verdict.is-fail { color: var(--status-fail) }
#stage-drawer .sd-verdict.is-warn { color: var(--status-warn) }

.sd-preview {
  margin: 0 0 16px; padding: 12px 16px;
  border: 1px solid var(--border-2); border-radius: 8px;
  background: var(--surface-2);
}
.sd-preview h4 { margin: 0 0 8px; font-size: var(--fs-100); font-weight: var(--fw-semi, 600); color: var(--text-2) }
.sd-pv-msg, .sd-pv-sum { margin: 0; font-size: var(--fs-100); color: var(--text-2) }
.sd-pv-sum { margin-block-end: 8px; color: var(--text) }
.sd-pv-list { list-style: none; margin: 0; padding: 0 }
.sd-pv-list li {
  display: flex; flex-direction: column; gap: 2px;
  padding-block: 8px; border-top: 1px solid var(--border-2);
}
.sd-pv-text { font-size: var(--fs-200); color: var(--text); overflow-wrap: anywhere; line-height: 1.5 }
.sd-pv-sub { font-size: var(--fs-100); color: var(--text-2) }
.sd-pv-more { font-size: var(--fs-100); color: var(--text-2) }

@media (max-width: 767.98px) {
  #stage-drawer.sheet {
    top: auto; bottom: 0; inset-inline: 0;
    inline-size: 100%; width: 100%; height: auto; max-height: 85vh;
    border-inline-start: 0; border-top: 1px solid var(--border-2);
    border-radius: 16px 16px 0 0;
    transform: translateY(100%);
    box-shadow: 0 -6px 20px rgba(0, 0, 0, .2);
    padding-bottom: env(safe-area-inset-bottom, 0px);
  }
  #stage-drawer.sheet.open { transform: translateY(0) }
  #stage-drawer.sheet:not(.open) { box-shadow: none }
  #stage-drawer .sheet-grab {
    display: flex; justify-content: center; align-items: center;
    block-size: 24px; flex: none; cursor: grab; touch-action: none;
  }
  #stage-drawer .sheet-grab span {
    inline-size: 40px; block-size: 4px; border-radius: 4px; background: var(--border);
  }
  #stage-drawer .sheet-hd { padding-block: 4px 12px; flex-wrap: wrap }
  #stage-drawer .sheet-hd h3 { flex: 1 1 100%; order: -1 }
  #stage-drawer .sheet-hd .ea-btn[data-sd-close] { position: absolute; inset-block-start: 16px; inset-inline-end: 12px }
  #stage-drawer .sheet-hd { position: relative; padding-inline-end: 56px }
  #stage-drawer .sheet-ft { flex-wrap: wrap }
  #stage-drawer .sheet-ft .ea-btn { flex: 1 1 auto; min-block-size: 44px }
}

/* ── Agency client list as monday groups + row actions (2026-10-26) ────────
   Each group: a title in the group's colour and a 5px bar down its
   inline-start edge (_agyGroupsHtml). The colour is a status token, softened
   into the surface so the bar reads as a group marker, not an alarm. */
.agy-group { position: relative; padding-inline-start: 16px; margin-block: 8px 24px }
.agy-group::before {
  content: ''; position: absolute; inset-block: 2px; inset-inline-start: 0;
  inline-size: 5px; border-radius: 4px;
  background: color-mix(in srgb, var(--agy-group) 70%, var(--surface));
}
.agy-group-title {
  display: flex; align-items: baseline; gap: 8px; margin: 0 0 8px;
  font-family: var(--font-sans); font-size: var(--fs-300); line-height: var(--lh-300);
  font-weight: var(--fw-semi); color: var(--agy-group);
}
.agy-group-n { font-size: var(--fs-100); font-weight: var(--fw-medium); color: var(--text-2) }

/* Swipe tray (_wireAgySwipe). The front layer — row + ⋯ — slides toward the
   inline-start and uncovers the tray pinned to the inline-end. Clipped only
   while open or dragging, so a closed row keeps its focus ring and shadow and
   an open one never widens the page. */
.agy-swipe { position: relative; border-radius: var(--r-lg) }
.agy-swipe.is-open, .agy-swipe.is-dragging { overflow: hidden }
.agy-front {
  position: relative; z-index: 1; touch-action: pan-y;
  transition: transform var(--dur-base) var(--ease-out);
}
.agy-swipe.is-dragging .agy-front { transition: none }
.agy-front > .agy-row { padding-inline-end: 52px }
.agy-more {
  position: absolute; inset-block-start: 50%; inset-inline-end: 6px; transform: translateY(-50%);
  inline-size: 44px; block-size: 44px; display: grid; place-items: center;
  border: 0; border-radius: var(--r-sm); background: transparent; cursor: pointer;
  color: var(--text-2); font: inherit; font-size: 20px; line-height: 1;
}
.agy-more:hover, .agy-more[aria-expanded="true"] { background: var(--surface-2); color: var(--text) }
.agy-more:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px }
.agy-acts {
  position: absolute; inset-block: 0; inset-inline-end: 0; z-index: 0;
  display: flex; gap: 6px; padding-inline-start: 8px; visibility: hidden;
}
.agy-swipe.is-open .agy-acts, .agy-swipe.is-dragging .agy-acts { visibility: visible }
.agy-act {
  inline-size: 76px; border: 0; border-radius: var(--r-lg); cursor: pointer;
  font: inherit; font-size: var(--fs-100); font-weight: var(--fw-semi);
  background: var(--accent-soft); color: var(--accent);
}
.agy-act--danger { background: var(--status-fail-bg); color: var(--status-fail) }
.agy-act:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px }
.agy-act:disabled { opacity: .5; cursor: progress }
@media (max-width: 760px) {
  .agy-group { padding-inline-start: 12px }
  .agy-more { inset-block-start: 6px; transform: none }
}
@media (prefers-reduced-motion: reduce) { .agy-front { transition: none } }

/* ── "דורש תשומת לב היום" counters (_paintAttention, 2026-10-26) ──────────────
   monday number widgets: big bold count, regular label, a muted status accent
   on the inline-start edge. Accents are the status tokens mixed into the
   surface — desaturated, as asked — and only a counter with something to act
   on gets one; zero and unknown stay neutral and are not buttons. */
.attn-strip { margin-block: 8px 24px }
.attn-title {
  margin: 0 0 8px; font-family: var(--font-sans); font-size: var(--fs-300);
  line-height: var(--lh-300); font-weight: var(--fw-semi); color: var(--text);
}
.attn-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 8px }
.attn-tile {
  --attn: var(--text-2);
  display: flex; flex-direction: column; align-items: flex-start; gap: 4px;
  min-block-size: 88px; padding: 12px 16px; text-align: start;
  font: inherit; color: var(--text); cursor: pointer;
  background: var(--surface); border: 1px solid var(--border);
  border-inline-start: 6px solid color-mix(in srgb, var(--attn) 22%, var(--surface));
  border-radius: var(--r-lg);
  transition: box-shadow var(--dur-base) var(--ease-out), background var(--dur-base) var(--ease-out);
}
.attn-tile[data-tone="warn"] { --attn: var(--status-warn) }
.attn-tile[data-tone="fail"] { --attn: var(--status-fail) }
.attn-tile[data-tone="idle"] { --attn: var(--text-2) }
.attn-tile[data-tone="run"]  { --attn: var(--status-running) }
.attn-tile.is-hot {
  border-inline-start-color: color-mix(in srgb, var(--attn) 60%, var(--surface));
  background: color-mix(in srgb, var(--attn) 6%, var(--surface));
}
.attn-tile.is-hot:hover { box-shadow: 0 4px 8px rgba(0, 0, 0, .2) }
.attn-tile:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px }
.attn-tile:disabled { cursor: default }
.attn-n { font-size: var(--fs-500); line-height: 1.1; font-weight: var(--fw-bold); font-variant-numeric: tabular-nums; color: var(--text) }
.attn-tile:not(.is-hot) .attn-n { color: var(--text-2) }
.attn-lbl { font-size: var(--fs-200); line-height: var(--lh-200); font-weight: 400; color: var(--text-2) }
@media (max-width: 760px) {
  .attn-grid { grid-template-columns: repeat(2, minmax(0, 1fr)) }
  .attn-tile { min-block-size: 80px; padding: 12px }
  .attn-n { font-size: 28px }
}

/* Agency list narrowed by a counter tap (_agyFilterRows). */
.agy-group[hidden], .agy-swipe[hidden], .agy-rows > .agy-row[hidden], .agy-attn-filter[hidden] { display: none }
.agy-attn-filter {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  margin-block: 0 8px; padding-block: 4px; padding-inline: 12px 4px;
  border-radius: var(--r-md); background: var(--surface-2);
  font-size: var(--fs-200); color: var(--text);
}

/* The heatmap, demoted: a fold under the counters whose summary line carries
   the grid's answer in words (_activitySummary). */
.ov-activity > .ov-fold-sum { align-items: center }
.ov-activity-hd { display: flex; flex-direction: column; gap: 2px; min-inline-size: 0 }
.ov-activity-hd .sec-label { margin: 0 }
.ov-activity-sum { margin: 0 0 8px; font-size: var(--fs-200); line-height: var(--lh-200); color: var(--text-2) }
.ov-activity-hd .ov-activity-sum { margin: 0 }
.ov-activity[open] .ea-heatmap-wrap { margin-block-start: 12px }
