/* GreenConnect — inbox styles.

   Conventions used across this file and settings.css:
     * Icons are Material Symbols Rounded, rendered by <span class="ms">name</span>.
     * Motion uses the --dur-* / --ease-* tokens below. Only transform and opacity
       are animated, so nothing here can trigger layout during an animation.
     * Every animation is switched off by the prefers-reduced-motion block at the
       bottom of the file. Add new motion above it, not after it.
*/
:root{
  /* Every colour below is an M3 role from m3-tokens.css (generated from the
     shared design/tokens/tokens.json), so light and dark both follow the theme.
     These names stay so the rest of this file and inbox.js need not change. */
  --brand:var(--md-sys-color-primary);
  --brand-dark:color-mix(in srgb,var(--md-sys-color-primary) 85%,var(--md-sys-color-on-surface));
  --brand-tint:var(--md-sys-color-secondary-container);
  --brand-tint-2:color-mix(in srgb,var(--md-sys-color-secondary-container) 85%,var(--md-sys-color-on-secondary-container));
  --on-brand-tint:var(--md-sys-color-on-secondary-container);
  --out-bubble:var(--md-sys-color-chat-bubble-out);
  --out-bubble-b:color-mix(in srgb,var(--md-sys-color-chat-bubble-out) 90%,var(--md-sys-color-on-chat-bubble-out));
  --in-bubble:var(--md-sys-color-chat-bubble-in);
  --chat-bg:var(--md-sys-color-chat-wallpaper);
  --panel:var(--md-sys-color-surface);
  --raised:var(--md-sys-color-surface-container-lowest); /* cards, modals, inputs on a panel */
  --line:var(--md-sys-color-outline-variant);
  --line-soft:var(--md-sys-color-surface-container-high);
  --ink:var(--md-sys-color-on-surface);
  --muted:var(--md-sys-color-on-surface-variant);
  --faint:var(--md-sys-color-outline);
  --hover:var(--md-sys-color-surface-container);
  --amber:var(--md-sys-color-warning);
  --danger:var(--md-sys-color-error);
  --unread:var(--md-sys-color-primary);
  --tick-read:var(--md-sys-color-tick-read);

  /* foreground on brand surfaces (header, avatar, send, badge) */
  --on-brand:var(--md-sys-color-on-primary);

  /* status pills — the 24h window state. Green is the brand, never "success". */
  --pill-open-bg:color-mix(in srgb,var(--md-sys-color-tertiary) 16%,var(--md-sys-color-surface));
  --pill-open-ink:var(--md-sys-color-tertiary);
  --pill-soon-bg:var(--md-sys-color-warning-container);    /* ink: --amber */
  --pill-closed-bg:var(--md-sys-color-surface-container-highest); /* ink: --muted */

  /* closed-window banner and inline error fill */
  --banner-bg:var(--md-sys-color-warning-container);
  --banner-ink:var(--md-sys-color-on-warning-container);
  --error-bg:var(--md-sys-color-error-container);          /* ink: --danger */

  /* snackbars (toasts) are inverse surfaces in M3 */
  --toast-bg:var(--md-sys-color-inverse-surface);
  --toast-ink:var(--md-sys-color-inverse-on-surface);
  --toast-accent:var(--md-sys-color-inverse-primary);
  --toast-error-bg:color-mix(in srgb,var(--md-sys-color-error) 70%,var(--md-sys-color-inverse-surface));

  /* translucent layers that read right on any surface, in either scheme */
  --scrim:color-mix(in srgb,var(--md-sys-color-scrim) 40%,transparent);
  --tint-04:color-mix(in srgb,var(--md-sys-color-on-surface) 5%,transparent);
  --tint-08:color-mix(in srgb,var(--md-sys-color-on-surface) 8%,transparent);
  --on-brand-08:color-mix(in srgb,var(--md-sys-color-on-primary) 16%,transparent);

  /* date chip over the chat wallpaper, and the wallpaper's dot texture */
  --chip-bg:color-mix(in srgb,var(--md-sys-color-surface-container-high) 88%,transparent);
  --chat-dot:color-mix(in srgb,var(--md-sys-color-primary) 6%,transparent);

  /* notes drawer — a warm paper tint */
  --notes-bg:color-mix(in srgb,var(--md-sys-color-warning-container) 22%,var(--md-sys-color-surface));
  --notes-line:color-mix(in srgb,var(--md-sys-color-warning) 25%,var(--md-sys-color-surface));

  /* template category chips */
  --cat-utility-bg:var(--pill-open-bg); --cat-utility-ink:var(--pill-open-ink);
  --cat-marketing-bg:light-dark(#fde7ef,#4a1f33); --cat-marketing-ink:light-dark(#a13460,#ffb0cd);
  --cat-auth-bg:var(--md-sys-color-info-container); --cat-auth-ink:var(--md-sys-color-on-info-container);

  /* The un-hyphenated duration names this stylesheet (and shell / thread /
     menus) has always used. m3-tokens.css (generated) only defines
     --md-sys-motion-duration-short-3 etc., so these resolved to nothing: every
     animation and transition built on them was invalid and never ran. */
  --md-sys-motion-duration-short1:var(--md-sys-motion-duration-short-1);
  --md-sys-motion-duration-short2:var(--md-sys-motion-duration-short-2);
  --md-sys-motion-duration-short3:var(--md-sys-motion-duration-short-3);
  --md-sys-motion-duration-short4:var(--md-sys-motion-duration-short-4);
  --md-sys-motion-duration-medium1:var(--md-sys-motion-duration-medium-1);
  --md-sys-motion-duration-medium2:var(--md-sys-motion-duration-medium-2);
  --md-sys-motion-duration-medium3:var(--md-sys-motion-duration-medium-3);
  --md-sys-motion-duration-medium4:var(--md-sys-motion-duration-medium-4);
  --md-sys-motion-duration-long1:var(--md-sys-motion-duration-long-1);
  /* Roboto Flex loads with display=swap; until it does, a local face with
     Roboto's metrics stands in, so the swap doesn't move a line. */
  --md-ref-typeface-brand:"Roboto Flex","Roboto Flex Fallback",Roboto,system-ui,-apple-system,"Segoe UI",sans-serif;

  /* motion — M3 easing and durations (m3-tokens.css). Nothing bouncy; this is a work tool. */
  --dur-fast:var(--md-sys-motion-duration-short3);
  --dur:var(--md-sys-motion-duration-short4);
  --dur-slow:var(--md-sys-motion-duration-medium1);
  --ease:var(--md-sys-motion-easing-emphasized-decelerate);  /* things arriving */
  --ease-in:var(--md-sys-motion-easing-emphasized-accelerate); /* things leaving */
  --ease-emphasized:var(--md-sys-motion-easing-emphasized);
  --ease-spring:cubic-bezier(.34,1.56,.64,1); /* overshoot spring settle */

  /* gendered avatar tints (approved): cool-blue / muted rose / grey-green */
  --av-male-bg:light-dark(#e4effb,#1d3550);   --av-male-ink:light-dark(#2f79c8,#9ccaff);
  --av-female-bg:light-dark(#fce1ee,#4a1f33); --av-female-ink:light-dark(#c2407f,#ffb0cd);
  --av-neutral-bg:var(--md-sys-color-surface-container-highest); --av-neutral-ink:var(--md-sys-color-on-surface-variant);

  --radius:var(--md-sys-shape-corner-medium);
  --radius-sm:var(--md-sys-shape-corner-small);
  /* Motion travel scales with --md-sys-motion-distance (0 under reduced motion),
     so slides become plain fades instead of being cut. */
  --dist-s:calc(4px * var(--md-sys-motion-distance));
  --dist:calc(8px * var(--md-sys-motion-distance));
  --dist-l:calc(16px * var(--md-sys-motion-distance));
  /* M3 state-layer opacities */
  --state-hover:var(--md-sys-state-hover-opacity);
  --state-press:var(--md-sys-state-pressed-opacity);
  --shadow-1:var(--md-sys-elevation-level1);
  --shadow-2:var(--md-sys-elevation-level2);
  --shadow-3:var(--md-sys-elevation-level3);
  --focus:0 0 0 3px color-mix(in srgb,var(--md-sys-color-primary) 40%,transparent);
}
*{box-sizing:border-box}
/* The browser's own [hidden]{display:none} loses to any class that sets display,
   so the modal and the drawers would sit open on top of the inbox from the
   moment the page loads. Everything here toggles via the hidden attribute, so
   it has to win. */
[hidden]{display:none !important}
/* Roboto's metrics on a local face (Arial / Helvetica): what the text sets in
   until Roboto Flex arrives, so nothing reflows when it does. */
@font-face{font-family:"Roboto Flex Fallback";src:local("Arial"),local("Helvetica Neue"),local("Helvetica");
  size-adjust:100.3%;ascent-override:92.77%;descent-override:24.41%;line-gap-override:0%}
html,body{margin:0;height:100%}
body{font:var(--md-sys-typescale-body-medium);letter-spacing:var(--md-sys-typescale-body-medium-tracking);
  color:var(--ink);background:var(--md-sys-color-surface-dim);
  -webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}
button{font-family:inherit;letter-spacing:inherit;cursor:pointer;color:inherit}
input,textarea,select{font-family:inherit;letter-spacing:inherit;color:inherit}

/* ---------- M3 state layer ----------
   Hover and press tint a control with its own content colour (currentColor) at
   the M3 opacities, on a ::before so the control's fill never changes. Any
   control that wants it gets class "sl" or is listed here. */
.icon-btn,.btn,.scope,.conv-item,.menu-item,.sal,.dir-row,.tmpl,.canned-item,.acct-result,
.mention-item,.bill-doc,.dir-num,.attach,.send,.compose-btn,.sl{position:relative;isolation:isolate}
.icon-btn::before,.btn::before,.scope::before,.conv-item::before,.menu-item::before,.sal::before,
button.dir-row::before,.tmpl::before,.canned-item::before,.acct-result::before,.mention-item::before,
.bill-doc::before,.dir-num::before,.attach::before,.send::before,.compose-btn::before,.sl::before{
  content:"";position:absolute;inset:0;border-radius:inherit;background:currentColor;opacity:0;
  pointer-events:none;z-index:-1;transition:opacity var(--dur-fast) var(--md-sys-motion-easing-standard)}
.icon-btn:hover::before,.btn:hover::before,.scope:hover::before,.conv-item:hover::before,
.menu-item:hover::before,.sal:hover::before,button.dir-row:hover:not(:disabled)::before,.tmpl:hover::before,
.canned-item:hover::before,.acct-result:hover::before,.mention-item:hover::before,.bill-doc:hover::before,
.dir-num:hover::before,.attach:hover::before,.send:hover::before,.compose-btn:hover::before,.sl:hover::before{
  opacity:var(--state-hover)}
.icon-btn:active::before,.btn:active::before,.scope:active::before,.conv-item:active::before,
.menu-item:active::before,.sal:active::before,button.dir-row:active::before,.tmpl:active::before,
.canned-item:active::before,.dir-num:active::before,.attach:active::before,.send:active::before,
.compose-btn:active::before,.sl:active::before{opacity:var(--state-press)}
.btn:disabled::before,.icon-btn:disabled::before,.send:disabled::before{opacity:0}

/* Keyboard users get a ring; mouse users don't. :focus-visible does that split
   for us, so we can style it hard without it firing on every click. */
:focus{outline:none}
:focus-visible{outline:none;box-shadow:var(--focus);border-radius:8px}

/* ---------- Material Symbols ---------- */
.ms{
  /* !important: a container's `font` shorthand (body-medium etc.) must never
     swap the icon font out and show the ligature name as text. */
  font-family:"Material Symbols Rounded" !important;
  font-weight:400;font-style:normal;line-height:1;letter-spacing:normal;
  text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;
  direction:ltr;font-size:20px;-webkit-font-feature-settings:"liga";
  -webkit-font-smoothing:antialiased;user-select:none;flex:0 0 auto;
  font-variation-settings:"FILL" 0,"wght" 400,"GRAD" 0,"opsz" 20;
}
.ms.fill{font-variation-settings:"FILL" 1,"wght" 400,"GRAD" 0,"opsz" 20}
.ms.xs{font-size:15px}
.ms.sm{font-size:18px}
.ms.lg{font-size:24px;font-variation-settings:"FILL" 0,"wght" 400,"GRAD" 0,"opsz" 24}
.ms.xl{font-size:40px;font-variation-settings:"FILL" 0,"wght" 300,"GRAD" 0,"opsz" 40}

/* One icon-button primitive, used everywhere. 40px here, 44px on touch (below). */
.icon-btn{display:inline-grid;place-items:center;width:40px;height:40px;padding:0;
  border:0;border-radius:var(--md-sys-shape-corner-full);background:transparent;color:var(--muted);
  text-decoration:none;flex:0 0 auto;
  transition:color var(--dur-fast) var(--ease),transform var(--dur-fast) var(--ease)}
.icon-btn .ms{font-size:24px;font-variation-settings:"FILL" 0,"wght" 400,"GRAD" 0,"opsz" 24}
.icon-btn:active{transform:scale(.92)}
.icon-btn:disabled{opacity:.38;cursor:default;transform:none}

/* ---------- auth ----------
   M3: the sign-in card is a surface-container-low card with extra-large corners
   on the surface; outlined text fields and a filled button. */
.auth-wrap{min-height:100vh;min-height:100dvh;display:grid;place-items:center;
  background:var(--md-sys-color-surface-container);padding:20px}
.auth-card{background:var(--md-sys-color-surface);border-radius:var(--md-sys-shape-corner-extra-large);
  padding:32px 28px 28px;width:min(400px,92vw);
  animation:card-in var(--md-sys-motion-duration-medium2) var(--md-sys-motion-easing-emphasized-decelerate) both}
@keyframes card-in{from{opacity:0;transform:translateY(var(--dist-l))}to{opacity:1;transform:none}}
.auth-brand{display:flex;flex-direction:column;align-items:center;text-align:center;gap:12px;margin-bottom:24px}
.auth-brand h1{margin:0;font:var(--md-sys-typescale-headline-small);color:var(--ink)}
.auth-brand p{margin:4px 0 0;color:var(--muted);font:var(--md-sys-typescale-body-medium)}
.auth-card form{display:flex;flex-direction:column;gap:16px}
.auth-card label{display:flex;flex-direction:column;gap:6px;font:var(--md-sys-typescale-body-small);
  letter-spacing:var(--md-sys-typescale-body-small-tracking);color:var(--muted)}
.auth-card label:focus-within{color:var(--brand)}
.auth-card input{height:56px;padding:0 16px;border:1px solid var(--md-sys-color-outline);
  border-radius:var(--md-sys-shape-corner-extra-small);background:transparent;color:var(--ink);
  font:var(--md-sys-typescale-body-large);letter-spacing:var(--md-sys-typescale-body-large-tracking);
  transition:border-color var(--dur-fast) var(--ease),box-shadow var(--dur-fast) var(--ease)}
.auth-card input:hover{border-color:var(--ink)}
.auth-card input:focus{border-color:var(--brand);box-shadow:inset 0 0 0 1px var(--brand)}
/* Browser autofill paints its own fill; keep the field on the card's surface. */
.auth-card input:-webkit-autofill{-webkit-text-fill-color:var(--ink);
  -webkit-box-shadow:inset 0 0 0 100px var(--md-sys-color-surface);caret-color:var(--ink)}
.auth-card input:-webkit-autofill:focus{-webkit-box-shadow:inset 0 0 0 1px var(--brand),inset 0 0 0 100px var(--md-sys-color-surface)}
.auth-card button{position:relative;isolation:isolate;overflow:hidden;background:var(--brand);color:var(--on-brand);
  border:0;height:40px;padding:0 24px;border-radius:var(--md-sys-shape-corner-full);margin-top:8px;
  font:var(--md-sys-typescale-label-large);letter-spacing:var(--md-sys-typescale-label-large-tracking);
  transition:transform var(--dur-fast) var(--ease),box-shadow var(--dur-fast) var(--ease)}
.auth-card button::before{content:"";position:absolute;inset:0;background:currentColor;opacity:0;z-index:-1;
  transition:opacity var(--dur-fast) var(--md-sys-motion-easing-standard)}
.auth-card button:hover{box-shadow:var(--shadow-1)}
.auth-card button:hover::before{opacity:var(--state-hover)}
.auth-card button:active{transform:scale(.97)}
.auth-card button:active::before{opacity:var(--state-press)}
.auth-error{background:var(--error-bg);color:var(--md-sys-color-on-error-container);padding:12px 16px;
  border-radius:var(--md-sys-shape-corner-medium);font:var(--md-sys-typescale-body-medium);
  margin-bottom:16px;display:flex;align-items:center;gap:12px;
  animation:slide-down var(--dur) var(--ease) both}
.auth-note{margin:0 0 16px;color:var(--muted);font:var(--md-sys-typescale-body-medium);line-height:1.55}
.auth-note strong{color:var(--ink);font-weight:600}
.auth-back{display:inline-flex;align-items:center;justify-content:center;gap:8px;margin-top:4px;min-height:40px;
  padding:0 12px;border-radius:var(--md-sys-shape-corner-full);align-self:center;
  color:var(--brand);font:var(--md-sys-typescale-label-large);text-decoration:none}
.auth-back:hover{background:color-mix(in srgb,var(--brand) 8%,transparent)}

.auth-logo{width:56px;height:56px;object-fit:contain;display:block;
  animation:logo-in var(--md-sys-motion-duration-long1) var(--md-sys-motion-easing-emphasized-decelerate) both}
@keyframes logo-in{from{opacity:0;transform:scale(calc(1 - .3 * var(--md-sys-motion-distance)))}to{opacity:1;transform:none}}
.error-body{margin:0 0 24px;color:var(--muted);font:var(--md-sys-typescale-body-large);text-align:center}
.auth-card>.btn{display:flex;width:fit-content;margin:0 auto}
.logo-mark{background:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container);
  font:var(--md-sys-typescale-title-medium);border-radius:var(--md-sys-shape-corner-medium);
  width:44px;height:44px;display:grid;place-items:center;letter-spacing:.5px}
.logo-mark.sm{width:30px;height:30px;border-radius:var(--md-sys-shape-corner-small);font:var(--md-sys-typescale-label-medium)}
.logo-mark.lg{width:72px;height:72px;border-radius:var(--md-sys-shape-corner-large);font:var(--md-sys-typescale-headline-small)}

/* ---------- app shell ---------- */
.app{display:grid;grid-template-columns:360px 1fr;height:100vh;height:100dvh;background:var(--panel);
  max-width:1500px;margin:0 auto;box-shadow:var(--shadow-1);position:relative}
.pane{min-width:0;height:100vh;height:100dvh;display:flex;flex-direction:column}
.list-pane{border-right:1px solid var(--line);background:var(--panel);position:relative}

/* M3 small top app bar: surface at rest, surface-container once the list
   scrolls under it (.scrolled, set by inbox.js). No shadow, no brand fill. */
.list-head{display:flex;align-items:center;justify-content:space-between;gap:4px;
  min-height:64px;padding:8px 4px 8px 16px;background:var(--panel);color:var(--ink);
  transition:background-color var(--dur) var(--md-sys-motion-easing-standard)}
.list-pane.scrolled .list-head{background:var(--md-sys-color-surface-container)}
.brand{display:flex;align-items:center;gap:12px;min-width:0;font:var(--md-sys-typescale-title-large)}
.brand span{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.me{display:flex;align-items:center;gap:0}
.me-name{font:var(--md-sys-typescale-body-small);opacity:.9;margin:0 4px 0 6px;max-width:110px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.logout-form{display:flex;margin:0}

/* Filter chips. The selected fill is one element (.scope-ind, placed by
   inbox.js) that slides from chip to chip; until it is placed the active chip
   carries its own fill. */
.scopes{display:flex;gap:8px;padding:4px 16px 8px;overflow-x:auto;scrollbar-width:none;
  -webkit-overflow-scrolling:touch;position:relative;flex:none}
.scopes::-webkit-scrollbar{display:none}
.scope{border:1px solid var(--line);background:transparent;color:var(--muted);height:32px;padding:0 16px;
  border-radius:var(--md-sys-shape-corner-small);white-space:nowrap;display:inline-flex;align-items:center;gap:8px;
  font:var(--md-sys-typescale-label-large);letter-spacing:var(--md-sys-typescale-label-large-tracking);
  transition:color var(--dur-fast) var(--ease),border-color var(--dur-fast) var(--ease),
             padding var(--dur) var(--ease-emphasized),transform var(--dur-fast) var(--ease)}
.scope:active{transform:scale(.96)}
.scope.active{border-color:transparent;color:var(--on-brand-tint);padding-left:8px;
  background:var(--brand-tint)}
.scope.active::after{content:"check";font-family:"Material Symbols Rounded";font-size:18px;line-height:1;
  order:-1;animation:check-in var(--dur) var(--ease) both}
@keyframes check-in{from{opacity:0;transform:scale(.4)}to{opacity:1;transform:none}}
.scopes.sliding .scope.active{background:transparent}
.scope-ind{position:absolute;left:0;top:4px;height:32px;border-radius:var(--md-sys-shape-corner-small);
  background:var(--brand-tint);pointer-events:none;z-index:0;
  transition:transform var(--md-sys-motion-duration-medium2) var(--ease-emphasized),
             width var(--md-sys-motion-duration-medium2) var(--ease-emphasized)}
.scopes .scope{z-index:1}

/* M3 search bar */
.search{padding:4px 16px 8px;position:relative;flex:none}
.search .ms{position:absolute;left:32px;top:calc(50% - 2px);transform:translateY(-50%);color:var(--muted);
  pointer-events:none;font-size:24px;transition:color var(--dur-fast) var(--ease)}
.search input{width:100%;height:48px;border:0;border-radius:var(--md-sys-shape-corner-full);padding:0 16px 0 56px;
  font:var(--md-sys-typescale-body-large);letter-spacing:var(--md-sys-typescale-body-large-tracking);
  background:var(--md-sys-color-surface-container-high);color:var(--ink);
  transition:background var(--dur-fast) var(--ease),box-shadow var(--dur-fast) var(--ease)}
.search input::placeholder{color:var(--muted)}
.search input:hover{background:var(--md-sys-color-surface-container-highest)}
.search input:focus{background:var(--md-sys-color-surface-container-highest);box-shadow:inset 0 0 0 2px var(--brand)}
.search:focus-within .ms{color:var(--brand)}
.search input::-webkit-search-cancel-button{cursor:pointer}

/* M3 list items, 72dp, no rules between rows. The open thread is the selected
   item: a secondary-container fill on a rounded shape inset from the edges. */
.conv-list{flex:1;overflow-y:auto;overscroll-behavior:contain;-webkit-overflow-scrolling:touch;padding:0 8px 88px}
.conv-item{display:flex;gap:16px;padding:12px 12px 12px 8px;min-height:72px;cursor:pointer;align-items:center;
  border-radius:var(--md-sys-shape-corner-large);color:var(--ink);
  transition:background-color var(--dur) var(--ease)}
.conv-item.active{background:var(--brand-tint);color:var(--on-brand-tint)}
.conv-item.active .conv-preview,.conv-item.active .conv-time{color:var(--on-brand-tint)}
.conv-item.escalated .conv-name::after{content:"priority_high";font-family:"Material Symbols Rounded";
  font-size:16px;color:var(--danger);vertical-align:-3px;margin-left:4px}
/* New rows (inbox.js adds .entering) rise in, staggered by --i. */
.conv-item.entering{animation:row-in var(--md-sys-motion-duration-medium2) var(--md-sys-motion-easing-emphasized-decelerate) both;
  animation-delay:calc(min(var(--i,0),10) * 30ms)}
@keyframes row-in{from{opacity:0;transform:translateY(var(--dist))}to{opacity:1;transform:none}}
.avatar{flex:0 0 48px;height:48px;border-radius:50%;display:grid;place-items:center;
  font:var(--md-sys-typescale-title-medium)}
.avatar.sm{flex-basis:40px;height:40px;font:var(--md-sys-typescale-title-small)}
.conv-main{flex:1;min-width:0}

.brand-logo{width:32px;height:32px;flex:0 0 auto;object-fit:contain;display:block}
.brand-word{white-space:nowrap;color:var(--ink)}
/* "New chat": an M3 extended FAB (primary container) at the foot of the list.
   It shrinks to the icon while the list scrolls down (.fab-collapsed) and
   extends again on the way up. */
.compose-btn{position:absolute;right:16px;bottom:calc(16px + env(safe-area-inset-bottom));z-index:5;
  height:56px;min-width:56px;padding:0 20px 0 16px;border:0;border-radius:var(--md-sys-shape-corner-large);
  display:inline-flex;align-items:center;gap:12px;overflow:hidden;
  background:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container);
  box-shadow:var(--shadow-3);font:var(--md-sys-typescale-label-large);letter-spacing:var(--md-sys-typescale-label-large-tracking);
  transition:padding var(--dur) var(--ease-emphasized),gap var(--dur) var(--ease-emphasized),
             box-shadow var(--dur-fast) var(--ease),transform var(--dur-fast) var(--ease)}
.compose-btn .ms{font-size:24px}
.compose-btn .fab-label{max-width:120px;white-space:nowrap;
  transition:max-width var(--dur) var(--ease-emphasized),opacity var(--dur-fast) var(--ease)}
.compose-btn:hover{box-shadow:var(--shadow-3),var(--shadow-1)}
.compose-btn:active{transform:scale(.96)}
.list-pane.fab-collapsed .compose-btn{padding:0 16px;gap:0}
.list-pane.fab-collapsed .compose-btn .fab-label{max-width:0;opacity:0}
.contacts-open .compose-btn{display:none}
.kebab-btn{color:var(--muted)}
.menu-wrap{position:relative}
.kebab-dot{position:absolute;top:8px;right:8px;width:8px;height:8px;border-radius:50%;
  background:var(--md-sys-color-error);box-shadow:0 0 0 2px var(--panel)}
/* M3 menu: surface-container, extra-small corners, level-2 shadow; it unfolds
   from its anchor corner. */
.menu-dropdown{position:absolute;right:0;top:calc(100% + 4px);z-index:60;min-width:224px;
  background:var(--md-sys-color-surface-container);color:var(--ink);border:0;
  border-radius:var(--md-sys-shape-corner-extra-small);box-shadow:var(--shadow-2);padding:8px 0;
  transform-origin:top right;
  animation:menu-in var(--md-sys-motion-duration-medium1) var(--md-sys-motion-easing-emphasized-decelerate) both}
@keyframes menu-in{from{opacity:0;transform:scaleY(.6) translateY(calc(-1 * var(--dist-s)))}to{opacity:1;transform:none}}
.menu-user{display:flex;align-items:center;gap:12px;padding:8px 16px 12px;
  border-bottom:1px solid var(--line);margin-bottom:8px}
.menu-avatar{display:inline-grid;place-items:center;width:40px;height:40px;border-radius:50%;
  background:var(--av-neutral-bg);color:var(--av-neutral-ink)}
.menu-avatar .ms{font-size:24px}
.menu-user .me-name{opacity:1;color:var(--ink);font:var(--md-sys-typescale-title-small);margin:0;max-width:150px}
.menu-item{display:flex;align-items:center;gap:12px;width:100%;min-height:48px;text-align:left;background:none;
  border:0;border-radius:0;padding:0 16px 0 12px;color:var(--ink);
  font:var(--md-sys-typescale-label-large);letter-spacing:var(--md-sys-typescale-label-large-tracking);
  cursor:pointer;text-decoration:none}
.menu-item .ms{font-size:24px;color:var(--muted)}
.menu-item .bell-count{position:static;margin-left:auto;background:var(--md-sys-color-error);color:var(--md-sys-color-on-error);
  border-radius:var(--md-sys-shape-corner-full);min-width:16px;height:16px;padding:0 4px;font:var(--md-sys-typescale-label-small);
  display:grid;place-items:center;box-shadow:none}
.menu-logout{margin:0}
/* The header assign menu: the kebab menu's look, anchored under the owner pill.
   Unassigned, "Take this" and its caret read as one split button. */
.assign-wrap{position:relative;display:inline-flex}
.assign-wrap.split #assignBtn{border-top-right-radius:0;border-bottom-right-radius:0;padding-right:12px}
.assign-more{width:32px;padding:0;justify-content:center;border-left:0;
  border-top-left-radius:0;border-bottom-left-radius:0}
.assign-menu{max-height:min(60vh,420px);overflow-y:auto;min-width:200px}
.assign-menu .menu-item{min-height:40px}
.assign-menu .menu-item>span{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0}
.menu-sep{height:1px;background:var(--line);margin:8px 0}
.menu-empty{padding:8px 16px;color:var(--muted);font:var(--md-sys-typescale-body-small)}
/* Gendered avatars — a head-and-shoulders bust SVG, tinted by salutation/gender.
   The SVG uses currentColor, so the .g-* colour drives head + shoulders. */
.avatar{background:var(--av-neutral-bg);color:var(--av-neutral-ink)}
.avatar .bust{width:66%;height:66%;display:block}
.avatar.sm .bust{width:70%;height:70%}
.avatar.g-male{background:var(--av-male-bg);color:var(--av-male-ink)}
.avatar.g-female{background:var(--av-female-bg);color:var(--av-female-ink)}
.avatar.g-neutral{background:var(--av-neutral-bg);color:var(--av-neutral-ink)}
/* M3 ripple: grows from the press point on filled and tonal buttons and the FAB
   (inbox.js adds the span). */
.btn,.compose-btn,.send{overflow:hidden}
.ripple{position:absolute;border-radius:50%;background:currentColor;opacity:.12;transform:scale(0);
  animation:ripple var(--md-sys-motion-duration-long1) var(--md-sys-motion-easing-standard-decelerate);pointer-events:none}
@keyframes ripple{to{transform:scale(2.2);opacity:0}}
.conv-top{display:flex;justify-content:space-between;align-items:baseline;gap:8px}
.conv-name{font:var(--md-sys-typescale-body-large);letter-spacing:var(--md-sys-typescale-body-large-tracking);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.conv-item.unread .conv-name{font-weight:600}
.conv-time{font:var(--md-sys-typescale-label-small);letter-spacing:var(--md-sys-typescale-label-small-tracking);
  color:var(--muted);flex:0 0 auto}
.conv-item.unread .conv-time{color:var(--brand)}
.conv-bottom{display:flex;justify-content:space-between;align-items:center;gap:8px;margin-top:2px}
/* The icon and the text are siblings in a flex row, so the truncation has to sit
   on the text span itself — text-overflow does nothing on a flex container, and
   the preview gets hard-clipped mid-word instead of ellipsised. */
.conv-preview{font:var(--md-sys-typescale-body-medium);letter-spacing:var(--md-sys-typescale-body-medium-tracking);
  color:var(--muted);display:flex;align-items:center;gap:4px;min-width:0;overflow:hidden}
.conv-item.unread .conv-preview{color:var(--ink)}
.conv-preview .txt{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0}
.conv-preview .ms{font-size:16px;color:var(--muted)}
/* "Draft:" in place of the last message while a reply waits in that chat. */
.conv-preview .draft-label{color:var(--brand);font-weight:500}
/* The outbound status tick sits as a SIBLING before .conv-preview inside this
   flex line — never inside .conv-preview, so the `.conv-preview .ms` rule can't
   recolour it. .conv-line shrinks (min-width:0) so .txt still truncates. */
.conv-line{display:flex;align-items:center;gap:4px;min-width:0;flex:1;overflow:hidden}
/* A search hit in place of the preview: a quiet "in message · 12 Oct" label, then
   the snippet with the matched text in bold. */
.conv-hit{flex:none;font:var(--md-sys-typescale-label-small);letter-spacing:var(--md-sys-typescale-label-small-tracking);
  color:var(--muted);background:var(--md-sys-color-surface-container-high);border-radius:var(--md-sys-shape-corner-full);
  padding:0 6px;line-height:18px;white-space:nowrap}
.conv-preview .txt b{font-weight:600;color:var(--ink)}
.conv-line .tick{color:var(--muted)}
.conv-line .tick.read{color:var(--tick-read)}
.conv-line .tick.failed{color:var(--danger)}
/* Who owns the chat ("You" / a first name): muted, one line, and it gives way
   (ellipsis, capped width) before the time, chip or unread badge ever move. */
.conv-owner{font:var(--md-sys-typescale-label-small);letter-spacing:var(--md-sys-typescale-label-small-tracking);
  color:var(--muted);flex:0 1 auto;min-width:0;max-width:7.5em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.conv-item.active .conv-owner{color:var(--on-brand-tint)}
.tick.tick-lg .ms{font-size:17px}
.badge{background:var(--unread);color:var(--on-brand);border-radius:var(--md-sys-shape-corner-full);min-width:20px;height:20px;
  padding:0 6px;font:var(--md-sys-typescale-label-small);display:grid;place-items:center;flex:0 0 auto;
  animation:pop var(--dur) var(--ease) both}
@keyframes pop{from{transform:scale(.6);opacity:0}to{transform:scale(1);opacity:1}}
.dot{width:8px;height:8px;border-radius:50%;flex:0 0 auto}
.dot.open{background:var(--md-sys-color-tertiary)}.dot.soon{background:var(--amber)}.dot.closed{background:var(--faint)}

/* ---------- conversation ---------- */
/* Chat wallpaper: the Green Home SVG tiles over the wallpaper colour; the dot
   grid stays as the layer beneath, so it is the fallback if the SVG can't load.
   The art has a dark twin (darker strokes) chosen with the theme. */
:root{--chat-art:url("/static/img/chat-bg-greenhome.6527664f399f.svg")}
:root[data-theme="dark"]{--chat-art:url("/static/img/chat-bg-greenhome-dark.7eda4f82000c.svg")}
@media (prefers-color-scheme:dark){:root:not([data-theme="light"]){--chat-art:url("/static/img/chat-bg-greenhome-dark.7eda4f82000c.svg")}}
.conv-pane{background-color:var(--chat-bg);
  background-image:var(--chat-art),
    radial-gradient(var(--chat-dot) 1px,transparent 1px);
  background-repeat:repeat,repeat;
  background-size:auto,22px 22px}
.empty-state{margin:auto;text-align:center;color:var(--muted);display:flex;flex-direction:column;
  align-items:center;gap:10px;padding:24px;max-width:340px;
  animation:fade-in var(--dur-slow) var(--ease) both}
@keyframes fade-in{from{opacity:0}to{opacity:1}}
.empty-state p{margin:6px 0 0;font:var(--md-sys-typescale-title-medium);color:var(--ink)}
.empty-state span{font:var(--md-sys-typescale-body-medium)}
.conv-pane.empty .conversation{display:none}
/* ...and the mirror of that: once a thread is open the placeholder has to go, or
   it sits above the conversation and pushes the composer off the bottom. */
.conv-pane:not(.empty) .empty-state{display:none}
.conversation{display:flex;flex-direction:column;flex:1;width:100%;min-height:0}

/* The thread's top app bar: surface-container (as the app's thread header),
   a hairline to the wallpaper below. */
.conv-head{display:flex;align-items:center;gap:8px;padding:8px 8px 8px 12px;
  background:var(--md-sys-color-surface-container);border-bottom:1px solid var(--line);min-height:64px}
.back{display:none}
.conv-title{flex:1;min-width:0}
.conv-title .name{font:var(--md-sys-typescale-title-medium);letter-spacing:var(--md-sys-typescale-title-medium-tracking);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.conv-title .sub{font:var(--md-sys-typescale-body-small);letter-spacing:var(--md-sys-typescale-body-small-tracking);
  color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.conv-actions{display:flex;align-items:center;gap:4px}
/* M3 status pills: 24px, small corners, label-medium. */
.pill,.window-pill{font:var(--md-sys-typescale-label-medium);letter-spacing:var(--md-sys-typescale-label-medium-tracking);
  height:24px;padding:0 8px;border-radius:var(--md-sys-shape-corner-small);white-space:nowrap;
  display:inline-flex;align-items:center;gap:4px;
  transition:background var(--dur) var(--ease),color var(--dur) var(--ease)}
.pill .ms,.window-pill .ms{font-size:16px}
.window-pill.open{background:var(--pill-open-bg);color:var(--pill-open-ink)}
.window-pill.soon{background:var(--pill-soon-bg);color:var(--md-sys-color-on-warning-container)}
.window-pill.closed{background:var(--pill-closed-bg);color:var(--muted)}
/* M3 common buttons. .btn is tonal (secondary container); .primary filled;
   .ghost outlined; .text text; .small the 32px compact size. Hover and press
   are the state layer (above) plus a slight press scale. */
.btn{border:0;border-radius:var(--md-sys-shape-corner-full);height:40px;padding:0 24px;
  font:var(--md-sys-typescale-label-large);letter-spacing:var(--md-sys-typescale-label-large-tracking);
  background:var(--brand-tint);color:var(--on-brand-tint);display:inline-flex;align-items:center;justify-content:center;
  gap:8px;white-space:nowrap;text-decoration:none;
  transition:box-shadow var(--dur-fast) var(--ease),color var(--dur-fast) var(--ease),
             border-color var(--dur-fast) var(--ease),transform var(--dur-fast) var(--ease)}
.btn:has(>.ms:first-child){padding-left:16px}
.btn:active{transform:scale(.97)}
.btn:disabled{background:color-mix(in srgb,var(--ink) 12%,transparent);color:color-mix(in srgb,var(--ink) 38%,transparent);
  cursor:default;transform:none;box-shadow:none}
.btn .ms{font-size:18px}
.btn.ghost{background:transparent;color:var(--brand);border:1px solid var(--md-sys-color-outline)}
.btn.ghost:disabled{background:transparent;border-color:color-mix(in srgb,var(--ink) 12%,transparent)}
.btn.text{background:transparent;color:var(--brand);padding:0 12px}
.btn.text:disabled{background:transparent}
.btn.primary{background:var(--brand);color:var(--on-brand)}
.btn.primary:hover{box-shadow:var(--shadow-1)}
.btn.small{height:32px;padding:0 16px;font:var(--md-sys-typescale-label-large)}
.btn.small:has(>.ms:first-child){padding-left:12px}
.btn.assigned{background:var(--brand);color:var(--on-brand);border-color:transparent}

/* Instant, never smooth: opening a chat lands on its newest message (or a search
   hit) with no visible scroll. inbox.js keeps the view anchored itself (images and
   cards that size late, a growing composer), so the browser's own anchoring is off;
   only explicit jumps (a quoted reply) animate, by asking for it. */
.thread{flex:1;overflow-y:auto;overscroll-behavior:contain;-webkit-overflow-scrolling:touch;
  padding:16px 8% 12px;display:flex;flex-direction:column;gap:4px;scroll-behavior:auto;
  overflow-anchor:none}
/* M3 chat bubbles (as the app): medium corners with the seated top corner at
   extra-small, no tail; body text at 15/21. */
.bubble{max-width:74%;padding:6px 10px 6px 12px;border-radius:var(--md-sys-shape-corner-medium);
  font:var(--md-sys-typescale-body-large);font-size:15px;line-height:21px;letter-spacing:.2px;
  position:relative;word-wrap:break-word;white-space:pre-wrap;
  animation:msg-in var(--md-sys-motion-duration-medium1) var(--md-sys-motion-easing-emphasized-decelerate) both}
/* Bubbles slide in from their own side and fade (8px; 0 under reduced motion). */
@keyframes msg-in{from{opacity:0;transform:translateX(var(--from-x,0)) translateY(var(--dist-s))}to{opacity:1;transform:none}}
.bubble.in{--from-x:calc(-1 * var(--dist));background:var(--in-bubble);color:var(--ink);align-self:flex-start;
  border-top-left-radius:var(--md-sys-shape-corner-extra-small);box-shadow:0 1px 1px rgb(0 0 0 / .06)}
.bubble.out{--from-x:var(--dist);background:var(--out-bubble);color:var(--md-sys-color-on-chat-bubble-out);align-self:flex-end;
  border-top-right-radius:var(--md-sys-shape-corner-extra-small)}
.bubble .meta{display:flex;gap:4px;align-items:center;justify-content:flex-end;
  font:var(--md-sys-typescale-label-small);letter-spacing:var(--md-sys-typescale-label-small-tracking);
  color:var(--muted);margin-top:2px}
.bubble.out .meta{color:color-mix(in srgb,var(--md-sys-color-on-chat-bubble-out) 70%,transparent)}
/* A status tick that changes (sent → delivered → read) pops into its new state. */
.tick.changed .ms{animation:tick-pop var(--md-sys-motion-duration-medium1) var(--md-sys-motion-easing-emphasized-decelerate)}
@keyframes tick-pop{from{transform:scale(.4);opacity:.2}to{transform:none;opacity:1}}
.bubble.tmpl{box-shadow:inset 3px 0 0 var(--brand)}
.bubble.auto{box-shadow:inset 3px 0 0 var(--amber)}
/* The message bubble's `.tmpl` modifier collides by name with the template
   PICKER's option class `.tmpl` (a flex-row card: display:flex;padding:12px;
   cursor:pointer). That rule was leaking onto the outbound invoice/quotation
   bubble and laying the document card, body and Pay-Now button out as horizontal
   columns (body wrapping in a narrow middle column). Re-assert the normal
   vertical bubble here — higher specificity than `.tmpl`, so it wins, and the
   picker modal is untouched. The WhatsApp-Desktop order (full-width document
   card → full-width body → full-width CTA) then falls out of block flow. */
.bubble.tmpl{display:block;padding:7px 11px 6px;cursor:auto;align-items:normal;gap:0}
.tmpl-tag{font:var(--md-sys-typescale-label-small);color:var(--brand);
  letter-spacing:var(--md-sys-typescale-label-small-tracking);margin-bottom:4px;display:flex;align-items:center;gap:4px}
.tmpl-tag .ms{font-size:13px}
.tmpl-tag.auto{color:var(--amber)}
/* Template URL buttons rendered inside an outbound bubble. A resolved one is a
   real link; an unresolved one is a muted, non-clickable chip. */
.tmpl-btn{display:flex;align-items:center;justify-content:center;gap:6px;margin:6px -10px -6px -12px;
  height:40px;border-top:1px solid color-mix(in srgb,currentColor 14%,transparent);
  color:var(--md-sys-color-info);font:var(--md-sys-typescale-label-large);text-decoration:none;
  border-radius:0 0 var(--md-sys-shape-corner-medium) var(--md-sys-shape-corner-medium);
  transition:background var(--dur-fast) var(--ease)}
a.tmpl-btn:hover{background:color-mix(in srgb,var(--md-sys-color-info) 8%,transparent)}
.tmpl-btn .ms{font-size:15px}
.tmpl-btn.disabled{background:var(--hover);color:var(--muted);font-weight:500;cursor:default}
.tick{display:inline-flex;align-items:center}
.tick .ms{font-size:15px}
.tick.read{color:var(--tick-read)}
.tick.failed{color:var(--danger);gap:3px;font-weight:600}
/* Queued: the clock breathes, so "still going" is legible without reading it. */
.tick.queued .ms{animation:breathe 1.6s ease-in-out infinite}
@keyframes breathe{0%,100%{opacity:.45}50%{opacity:1}}
.day-sep{align-self:center;background:var(--md-sys-color-surface-container-high);color:var(--muted);
  font:var(--md-sys-typescale-label-medium);letter-spacing:var(--md-sys-typescale-label-medium-tracking);
  padding:4px 12px;border-radius:var(--md-sys-shape-corner-small);margin:8px 0}

/* composer */
.composer{background:var(--md-sys-color-surface-container);padding:8px 12px 12px;position:relative}
.window-closed-banner{background:var(--banner-bg);color:var(--banner-ink);font:var(--md-sys-typescale-body-medium);
  padding:8px 8px 8px 16px;border-radius:var(--md-sys-shape-corner-medium);margin-bottom:8px;display:flex;align-items:center;
  justify-content:space-between;gap:12px;animation:slide-down var(--dur) var(--ease) both}
@keyframes slide-down{from{opacity:0;transform:translateY(calc(-1 * var(--dist-s)))}to{opacity:1;transform:none}}
.composer-row{display:flex;gap:8px;align-items:flex-end}
.composer-row.disabled{opacity:.38;pointer-events:none}
/* M3 filled field on the composer bar, extra-large corners as the app. */
#input{flex:1;border:0;border-radius:var(--md-sys-shape-corner-extra-large);padding:12px 20px;min-height:48px;
  font:var(--md-sys-typescale-body-large);letter-spacing:var(--md-sys-typescale-body-large-tracking);
  resize:none;max-height:140px;background:var(--md-sys-color-surface-container-highest);color:var(--ink);
  transition:box-shadow var(--dur-fast) var(--ease)}
#input::placeholder{color:var(--muted)}
#input:focus{box-shadow:inset 0 0 0 2px var(--brand)}
/* Send: a 48dp primary button with large corners (the app's composer). */
.send{flex:0 0 48px;height:48px;border-radius:var(--md-sys-shape-corner-large);border:0;
  background:var(--brand);color:var(--on-brand);display:grid;place-items:center;
  transition:transform var(--dur-fast) var(--ease),background var(--dur) var(--ease)}
.send:active{transform:scale(.92)}
.send:disabled{background:color-mix(in srgb,var(--ink) 12%,transparent);color:color-mix(in srgb,var(--ink) 38%,transparent);
  cursor:default;transform:none}
.send .ms{font-size:24px}

/* Sending indicator — three dots under the composer while a send is in flight.
   It is the honest signal: the row has left the browser, the worker has it. */
.sending-hint{display:flex;align-items:center;gap:8px;font:var(--md-sys-typescale-body-small);color:var(--muted);
  padding:6px 4px 0;animation:fade-in var(--dur-fast) var(--ease) both}
.dots{display:inline-flex;gap:3px}
/* "Keep unread for <owner>": a quiet line above the composer row, the
   sending hint's type and colour; the box takes the brand accent when on. */
.keep-unread{display:inline-flex;align-items:center;gap:6px;padding:0 4px 6px;cursor:pointer;
  font:var(--md-sys-typescale-body-small);color:var(--muted);user-select:none;max-width:100%}
.keep-unread>span{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0}
.keep-unread input{margin:0;width:14px;height:14px;accent-color:var(--brand);cursor:pointer}
.keep-unread:has(input:checked){color:var(--ink)}
.dots i{width:5px;height:5px;border-radius:50%;background:var(--brand);opacity:.35;
  animation:dot 1.1s ease-in-out infinite}
.dots i:nth-child(2){animation-delay:.15s}
.dots i:nth-child(3){animation-delay:.3s}
@keyframes dot{0%,100%{opacity:.25;transform:translateY(0)}50%{opacity:1;transform:translateY(-2px)}}

/* drawers */
/* M3 side sheet (modal, on the right): surface-container-low, large leading
   corners, level-1 shadow; it slides in from the edge. */
.ctx-drawer{position:absolute;top:0;right:0;width:360px;height:100%;background:var(--md-sys-color-surface-container-low);
  border-radius:var(--md-sys-shape-corner-large) 0 0 var(--md-sys-shape-corner-large);z-index:20;
  display:flex;flex-direction:column;overflow:hidden;box-shadow:var(--shadow-1);
  animation:drawer-in var(--md-sys-motion-duration-medium2) var(--md-sys-motion-easing-emphasized-decelerate) both}
@keyframes drawer-in{from{opacity:0;transform:translateX(var(--dist-l))}to{opacity:1;transform:none}}
.ctx-drawer header{display:flex;justify-content:space-between;align-items:center;gap:8px;
  padding:8px 8px 8px 24px;background:transparent;color:var(--ink);min-height:64px;
  font:var(--md-sys-typescale-title-large)}
/* The header was brand green; its controls now sit on the sheet's surface. */
.ctx-drawer header .icon-btn{color:var(--muted)}
.ctx-drawer header .link-btn{color:var(--brand);opacity:1}
.ctx-drawer header .link-btn:hover{background:color-mix(in srgb,var(--brand) 8%,transparent)}
.ctx-body{padding:8px 24px 24px;overflow-y:auto;font:var(--md-sys-typescale-body-medium);-webkit-overflow-scrolling:touch}
.ctx-row{padding:12px 0;border-bottom:1px solid var(--line)}
.ctx-row .k{font:var(--md-sys-typescale-label-medium);color:var(--muted);letter-spacing:var(--md-sys-typescale-label-medium-tracking)}
.ctx-row .v{margin-top:2px;font:var(--md-sys-typescale-body-large)}
.ctx-empty{color:var(--muted);font:var(--md-sys-typescale-body-medium);text-align:center;margin-top:32px}
/* Customer drawer: editable contact card on top, green-pms context below. Reuses
   the .ctx-drawer shell/keyframes/tokens; no new motion. */
.drawer-scroll{flex:1;overflow-y:auto;-webkit-overflow-scrolling:touch;font-size:14px}
.contact-card{padding:14px 16px;border-bottom:1px solid var(--line)}
.contact-field{display:flex;flex-direction:column;gap:5px;margin-bottom:12px}
.contact-field .k{font:var(--md-sys-typescale-title-small);color:var(--brand);letter-spacing:var(--md-sys-typescale-title-small-tracking)}
.contact-field input{border:1px solid var(--md-sys-color-outline);border-radius:var(--md-sys-shape-corner-extra-small);
  padding:0 16px;height:48px;font:var(--md-sys-typescale-body-large);background:transparent;transition:border-color var(--dur-fast) var(--ease),
  background var(--dur-fast) var(--ease),box-shadow var(--dur-fast) var(--ease)}
.contact-field input:focus{border-color:var(--brand);background:transparent;box-shadow:inset 0 0 0 1px var(--brand)}
.salutation-pick{display:flex;gap:6px}
/* Sir / Madam: M3 filter chips (single choice). */
.sal{border:1px solid var(--line);background:transparent;color:var(--muted);border-radius:var(--md-sys-shape-corner-small);
  height:32px;padding:0 16px;font:var(--md-sys-typescale-label-large);cursor:pointer;
  display:inline-flex;align-items:center;gap:8px;
  transition:background var(--dur) var(--ease),color var(--dur-fast) var(--ease),
             border-color var(--dur-fast) var(--ease),padding var(--dur) var(--ease-emphasized)}
.sal.on{background:var(--brand-tint);color:var(--on-brand-tint);border-color:transparent;padding-left:8px}
.sal.on::after{content:"check";font-family:"Material Symbols Rounded";font-size:18px;line-height:1;order:-1;
  animation:check-in var(--dur) var(--ease) both}
.contact-save{display:flex;align-items:center;justify-content:flex-end;gap:10px;margin-top:2px}
.contact-status{font:var(--md-sys-typescale-body-small);color:var(--muted)}
.ctx-context{padding:12px 16px 16px}
.ctx-context-head{font:var(--md-sys-typescale-title-small);color:var(--brand);letter-spacing:var(--md-sys-typescale-title-small-tracking);
  margin-bottom:2px}
/* The whole left identity region (avatar + name + phone) opens the customer
   panel; the right-side action cluster is a separate element, unaffected. */
.head-id{display:flex;align-items:center;gap:12px;flex:1;min-width:0;cursor:pointer;
  border:0;background:none;text-align:left;padding:4px 8px;margin:-4px -8px;
  border-radius:var(--md-sys-shape-corner-full);transition:background var(--dur-fast) var(--ease)}
.head-id:hover{background:color-mix(in srgb,var(--ink) 8%,transparent)}
.head-id:focus-visible{outline:none;box-shadow:var(--focus)}
/* Linked-accounts section in the drawer, and the header relation badge. Reuses
   the drawer tokens; no new motion. */
.accounts-card{padding:12px 16px;border-bottom:1px solid var(--line)}
.accounts-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}
.accounts-head .k{font:var(--md-sys-typescale-title-small);color:var(--brand);letter-spacing:var(--md-sys-typescale-title-small-tracking)}
.add-acct-btn{background:none;border:0;color:var(--brand);font:var(--md-sys-typescale-label-large);cursor:pointer;
  height:32px;padding:0 12px;border-radius:var(--md-sys-shape-corner-full)}
.add-acct-btn:hover{background:color-mix(in srgb,var(--brand) 8%,transparent)}
.account-list{display:flex;flex-direction:column;gap:8px}
.acct-empty{font:var(--md-sys-typescale-body-medium);color:var(--muted);padding:4px 0}
.acct-row{display:flex;align-items:flex-start;gap:12px;padding:12px;border:1px solid var(--line);
  border-radius:var(--md-sys-shape-corner-medium);background:var(--panel)}
.acct-row.primary{border-color:transparent;background:var(--brand-tint);color:var(--on-brand-tint)}
.acct-star,.acct-unlink{background:none;border:0;cursor:pointer;color:var(--faint);padding:2px;line-height:1}
.acct-row.primary .acct-star{color:var(--brand)}
.acct-unlink:hover{color:var(--danger)}
.acct-main{flex:1;min-width:0;display:flex;flex-direction:column;gap:3px}
.acct-name{font:var(--md-sys-typescale-body-large);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.acct-addr{font:var(--md-sys-typescale-body-small);color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.acct-relation{font:var(--md-sys-typescale-body-medium);height:32px;padding:0 8px;border:1px solid var(--md-sys-color-outline);
  border-radius:var(--md-sys-shape-corner-extra-small);background:transparent;margin-top:4px;max-width:180px}
/* Billing card (Zoho invoices / payments / quotes). Neutral throughout; amber
   only for "N days overdue". Reuses the drawer tokens. */
.billing-card{padding:12px 16px;border-bottom:1px solid var(--line)}
.bill-total{display:flex;justify-content:space-between;align-items:baseline;gap:12px;
  padding:4px 0 12px;font:var(--md-sys-typescale-label-large);color:var(--muted)}
.bill-total b{font:var(--md-sys-typescale-headline-small);color:var(--ink);font-variant-numeric:tabular-nums}
.bill-sec-title{font:var(--md-sys-typescale-title-small);color:var(--brand);letter-spacing:var(--md-sys-typescale-title-small-tracking);margin:10px 0 6px}
.bill-list{display:flex;flex-direction:column;gap:6px}
.bill-row{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;width:100%;
  padding:12px;border:0;border-radius:var(--md-sys-shape-corner-medium);background:var(--md-sys-color-surface-container);
  text-align:left;font:inherit;color:inherit}
.bill-doc{cursor:pointer}
.bill-doc:focus-visible{outline:none;box-shadow:var(--focus)}
.bill-main{display:flex;flex-direction:column;gap:2px;min-width:0}
.bill-num{font:var(--md-sys-typescale-body-large)}
.bill-sub{font:var(--md-sys-typescale-body-small);color:var(--muted)}
.bill-amt{font:var(--md-sys-typescale-title-small);white-space:nowrap;font-variant-numeric:tabular-nums}
/* Status pills as the approved mockup: overdue red, due amber, paid/other neutral. */
.bill-chip{align-self:flex-start;margin-top:4px;font:var(--md-sys-typescale-label-small);height:20px;padding:0 8px;
  display:inline-flex;align-items:center;border-radius:var(--md-sys-shape-corner-small);
  background:var(--md-sys-color-surface-container-highest);color:var(--muted)}
.bill-chip.overdue{background:var(--error-bg);color:var(--md-sys-color-on-error-container)}
.bill-sec{margin-top:10px}
.bill-sec summary{cursor:pointer;font:var(--md-sys-typescale-title-small);color:var(--brand);
  padding:4px 0;list-style-position:inside}
.bill-sec[open] summary{margin-bottom:6px}
.bill-count{color:var(--muted);margin-left:4px}
.bill-unavailable{display:flex;align-items:center;justify-content:space-between;gap:8px;
  font:var(--md-sys-typescale-body-medium);color:var(--muted);padding:4px 0}
.bill-retry{background:none;border:0;color:var(--brand);font:var(--md-sys-typescale-label-large);cursor:pointer;
  height:32px;padding:0 12px;border-radius:var(--md-sys-shape-corner-full)}
.bill-retry:hover{background:color-mix(in srgb,var(--brand) 8%,transparent)}
.bill-skel{display:flex;flex-direction:column;gap:8px;padding:4px 0}
.bill-skel-line{height:12px;border-radius:6px;
  background:linear-gradient(90deg,var(--line-soft) 25%,var(--md-sys-color-surface-container-lowest) 37%,var(--line-soft) 63%);
  background-size:400% 100%;animation:shimmer 1.4s ease infinite}
.bill-skel-line.w40{width:40%}
.bill-skel-line.w70{width:70%}
/* Billing actions: tick due invoices -> one payment request; send a quote. */
.bill-item{display:flex;align-items:flex-start;gap:8px}
.bill-item-main{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}
.bill-pick{margin-top:14px;width:18px;height:18px;accent-color:var(--brand);flex:none;cursor:pointer}
.bill-pick-spacer{width:18px;flex:none}
.bill-mini{align-self:flex-end;background:none;border:1px solid var(--md-sys-color-outline);color:var(--brand);
  font:var(--md-sys-typescale-label-large);cursor:pointer;height:32px;padding:0 16px;
  border-radius:var(--md-sys-shape-corner-full);transition:background var(--dur-fast) var(--ease)}
.bill-mini:hover{background:color-mix(in srgb,var(--brand) 8%,transparent)}
.bill-chip.warn{background:var(--pill-soon-bg);color:var(--md-sys-color-on-warning-container)}
/* Payment reminders: the button spins while greenhome checks and GreenConnect sends. */
.bill-mini:disabled{cursor:default;opacity:.7}
.bill-spin{display:inline-block;width:12px;height:12px;margin-right:6px;vertical-align:-2px;
  border:2px solid color-mix(in srgb,currentColor 30%,transparent);border-top-color:currentColor;
  border-radius:var(--md-sys-shape-corner-full);animation:bill-spin .8s linear infinite}
@keyframes bill-spin{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion:reduce){.bill-spin{animation-duration:2.4s}}
.bill-remind-actions{align-self:flex-end;display:flex;align-items:center;gap:8px}
.bill-remind-choice{height:32px;padding:0 8px;border:1px solid var(--md-sys-color-outline-variant);
  border-radius:var(--md-sys-shape-corner-small);background:var(--md-sys-color-surface);color:var(--ink);
  font:var(--md-sys-typescale-label-medium)}
.bill-remind-note{align-self:flex-end;font:var(--md-sys-typescale-body-small);color:var(--muted)}
.bill-remind-note.error{color:var(--md-sys-color-error)}
/* The selection bar rises in from the bottom of the drawer. */
.bill-actions{position:sticky;bottom:0;display:flex;align-items:center;justify-content:space-between;
  gap:8px;margin-top:8px;padding:8px 8px 8px 16px;border:0;border-radius:var(--md-sys-shape-corner-large);
  background:var(--md-sys-color-surface-container-high);box-shadow:var(--shadow-2);
  font:var(--md-sys-typescale-label-large);color:var(--ink);
  animation:slide-up var(--md-sys-motion-duration-medium2) var(--md-sys-motion-easing-emphasized-decelerate) both}
.bill-confirm{overflow-y:auto}
.bill-confirm-docs{display:flex;flex-direction:column;gap:6px;padding:8px 16px}
.bill-preview-label{font:var(--md-sys-typescale-title-small);color:var(--brand);padding:8px 24px 4px}
.bill-preview{margin:0 24px;padding:10px 12px;border-radius:var(--md-sys-shape-corner-medium);
  border-top-right-radius:var(--md-sys-shape-corner-extra-small);background:var(--out-bubble);
  font:var(--md-sys-typescale-body-medium);white-space:pre-wrap;color:var(--md-sys-color-on-chat-bubble-out)}
.account-picker{margin-top:10px}
.account-picker input{width:100%;border:1px solid var(--line);border-radius:var(--radius-sm);
  padding:8px 10px;font-size:14px;background:var(--hover);
  transition:border-color var(--dur-fast) var(--ease),background var(--dur-fast) var(--ease),
             box-shadow var(--dur-fast) var(--ease)}
.account-picker input:focus{border-color:var(--brand);background:transparent;box-shadow:inset 0 0 0 1px var(--brand)}
.account-results{margin-top:6px;display:flex;flex-direction:column;gap:2px;max-height:210px;overflow-y:auto}
.acct-result{display:flex;flex-direction:column;gap:2px;text-align:left;background:none;border:0;
  border-radius:var(--md-sys-shape-corner-small);padding:8px 12px;cursor:pointer}

/* modal */
/* M3 dialog: surface-container-high, extra-large corners, level-3 shadow; it
   grows in from slightly smaller over a 32% scrim (emphasized decelerate). */
.modal-back{position:fixed;inset:0;background:color-mix(in srgb,var(--md-sys-color-scrim) 32%,transparent);
  display:grid;place-items:center;z-index:40;padding:16px;
  animation:fade-in var(--md-sys-motion-duration-short4) var(--md-sys-motion-easing-standard) both}
.modal{background:var(--md-sys-color-surface-container-high);border-radius:var(--md-sys-shape-corner-extra-large);
  width:min(560px,96vw);max-height:86vh;display:flex;flex-direction:column;overflow:hidden;box-shadow:var(--shadow-3);
  transform-origin:top center;
  animation:dialog-in var(--md-sys-motion-duration-medium1) var(--md-sys-motion-easing-emphasized-decelerate) both}
/* One open motion for every dialog (this, Make it yours, the viewer's
   panels): a fade and a slight scale up, 250ms emphasized-decelerate. */
@keyframes dialog-in{from{opacity:0;transform:scale(.96)}to{opacity:1;transform:none}}
.modal header{display:flex;justify-content:space-between;align-items:center;gap:8px;
  padding:16px 12px 8px 24px;font:var(--md-sys-typescale-headline-small);color:var(--ink)}
.tmpl-list{overflow-y:auto;padding:6px}
.tmpl{display:flex;gap:12px;padding:12px 16px;border-radius:var(--md-sys-shape-corner-medium);cursor:pointer;align-items:flex-start}
.tmpl input{margin-top:4px;accent-color:var(--brand)}
.tmpl-name{font:var(--md-sys-typescale-title-small);display:flex;gap:8px;align-items:center;flex-wrap:wrap}
.tmpl-body{font:var(--md-sys-typescale-body-medium);color:var(--muted);margin-top:4px}
.cat{font:var(--md-sys-typescale-label-small);height:20px;padding:0 8px;display:inline-flex;align-items:center;
  border-radius:var(--md-sys-shape-corner-small);text-transform:capitalize}
.cat.utility{background:var(--cat-utility-bg);color:var(--cat-utility-ink)}.cat.marketing{background:var(--cat-marketing-bg);color:var(--cat-marketing-ink)}.cat.authentication{background:var(--cat-auth-bg);color:var(--cat-auth-ink)}
.tmpl-vars{padding:0 24px}
.tmpl-vars input{width:100%;margin:6px 0;height:56px;padding:0 16px;border:1px solid var(--md-sys-color-outline);background:transparent;
  border-radius:var(--md-sys-shape-corner-extra-small);font:var(--md-sys-typescale-body-large);
  transition:border-color var(--dur-fast) var(--ease),box-shadow var(--dur-fast) var(--ease)}
.tmpl-vars input:focus{border-color:var(--brand);box-shadow:inset 0 0 0 1px var(--brand)}
.tmpl-upload-status{font:var(--md-sys-typescale-body-small);color:var(--muted);margin:2px 0 6px;min-height:16px}
#tmplSend{margin:16px 24px 24px;align-self:flex-end}
.tmpl-empty{font:var(--md-sys-typescale-body-medium);color:var(--muted);text-align:center;padding:24px 16px;margin:0}
/* Pre-send confirm gate: the human checks the resolved recipient/amount/document. */
.tmpl-confirm-head{font:var(--md-sys-typescale-title-medium);padding:16px 24px 4px}
.tmpl-confirm-body{margin:0;padding:4px 24px;display:grid;grid-template-columns:auto 1fr;
  gap:8px 16px;font:var(--md-sys-typescale-body-medium)}
.tmpl-confirm-body dt{color:var(--muted);white-space:nowrap}
.tmpl-confirm-body dd{margin:0;color:var(--ink);word-break:break-word}
.tmpl-confirm-actions{display:flex;gap:8px;justify-content:flex-end;padding:24px}
/* new-conversation compose modal — same tokens as the template picker above */
.compose-field{display:block;padding:16px 24px 0}
.compose-label{display:block;font:var(--md-sys-typescale-body-small);color:var(--muted);margin-bottom:6px}
.compose-field input{width:100%;height:56px;padding:0 16px;border:1px solid var(--md-sys-color-outline);background:transparent;
  border-radius:var(--md-sys-shape-corner-extra-small);font:var(--md-sys-typescale-body-large);
  transition:border-color var(--dur-fast) var(--ease),box-shadow var(--dur-fast) var(--ease)}
.compose-field input:focus{border-color:var(--brand);box-shadow:inset 0 0 0 1px var(--brand)}
.compose-hint{font:var(--md-sys-typescale-body-small);color:var(--muted);margin:8px 24px 0}
.compose-warn{display:flex;gap:12px;align-items:flex-start;font:var(--md-sys-typescale-body-medium);
  color:var(--md-sys-color-on-warning-container);background:var(--md-sys-color-warning-container);
  border-radius:var(--md-sys-shape-corner-medium);margin:12px 24px 0;padding:12px 16px}
#composeStart{margin:16px 24px 24px;align-self:flex-end}

/* ---------- contacts: list view, contact drawer, new-contact modal ---------- */
/* The chat list and the contacts list share the left pane; whichever is shown
   takes the remaining height so its list scrolls on its own. */
.chats-view,.contacts-view{flex:1;min-height:0;display:flex;flex-direction:column}
#contactsNavBtn.active{color:var(--brand);background:var(--brand-tint)}
.contacts-bar{display:flex;align-items:center;gap:8px;padding:4px 16px 4px 4px;min-height:56px}
.contacts-title{flex:1;font:var(--md-sys-typescale-title-large)}
.contact-letter{position:sticky;top:0;z-index:2;background:var(--panel);
  font:var(--md-sys-typescale-title-small);color:var(--brand);padding:12px 12px 4px}
.contacts-more{display:block;margin:12px auto}
.contacts-empty .btn{margin-top:4px}
.contacts-loading{padding:16px 14px}
/* Inline text button for names inside sentences ("Already saved for X"). */
.link-inline{background:none;border:0;padding:0;font:inherit;font-weight:500;color:var(--brand);
  cursor:pointer;text-align:left}
.link-inline:hover{text-decoration:underline}
.modal-error.inline{margin:8px 0 0}
.modal-error .bill-mini{align-self:auto}
.person-drawer{z-index:21}
.person-drawer textarea{border:1px solid var(--line);border-radius:var(--radius-sm);padding:9px 11px;
  font:inherit;font-size:14px;background:var(--hover);resize:vertical;min-height:58px;
  transition:border-color var(--dur-fast) var(--ease),background var(--dur-fast) var(--ease),
             box-shadow var(--dur-fast) var(--ease)}
.person-drawer textarea:focus{outline:none;border-color:var(--brand);background:transparent;box-shadow:inset 0 0 0 1px var(--brand)}
.num-icon{color:var(--faint);padding:2px;line-height:1}
.num-actions{display:flex;gap:10px;margin-top:2px}
.person-add-number{display:flex;gap:6px;margin-top:10px}
.person-add-number input,.nc-num input{flex:1;min-width:0;border:1px solid var(--line);
  border-radius:var(--radius-sm);padding:8px 10px;font-size:14px;background:var(--hover);
  transition:border-color var(--dur-fast) var(--ease),background var(--dur-fast) var(--ease),
             box-shadow var(--dur-fast) var(--ease)}
.person-add-number input:focus,.nc-num input:focus{outline:none;border-color:var(--brand);
  background:var(--on-brand);box-shadow:var(--focus)}
.person-add-number input[type="tel"]{flex:1.3}
.person-danger{padding:14px 16px 20px;display:flex;justify-content:flex-end}
.btn.ghost.danger{color:var(--danger)}
.btn.ghost.danger:hover{border-color:var(--danger);color:var(--danger);background:var(--error-bg)}
/* conversation drawer: "Contact: <name>" or "Save as contact" */
.ctx-person:empty{display:none}
.ctx-person{padding:10px 16px;border-bottom:1px solid var(--line);font-size:13.5px}
.ctx-person-row{display:flex;align-items:center;gap:8px}
.ctx-person-row>.ms{color:var(--faint)}
.ctx-person-none{flex:1;color:var(--muted)}
/* new contact modal */
.nc-body{overflow-y:auto}
.nc-numbers{display:flex;flex-direction:column;gap:6px;margin-bottom:6px}
.nc-num{display:flex;gap:6px;align-items:center}
.nc-num .nc-number{flex:1.3}
#ncSave{margin:14px 16px 16px;justify-content:center}
#ncGender{margin-top:2px}
/* import dry run */
.import-body{overflow-y:auto;padding:4px 0}
.import-count{font-size:13.5px;margin:12px 16px 6px;line-height:1.5}
.import-sample{display:flex;flex-direction:column;margin:0 16px;border:1px solid var(--line);
  border-radius:var(--radius-sm);max-height:240px;overflow-y:auto}
.import-row{display:flex;justify-content:space-between;gap:10px;padding:7px 10px;
  border-bottom:1px solid var(--line-soft)}
.import-row:last-child{border-bottom:0}
/* new-chat directory results and the forward picker share one row style */
.dir-results{overflow-y:auto;padding:4px 16px 0;max-height:46vh}
.dir-results:empty{display:none}
.dir-sec{margin-bottom:4px}
.dir-row{display:flex;align-items:flex-start;gap:16px;width:100%;padding:12px;border:0;
  border-radius:var(--md-sys-shape-corner-medium);background:none;text-align:left;font:inherit;color:inherit}
.dir-row>.ms{color:var(--muted);margin-top:1px;font-size:24px}
button.dir-row{cursor:pointer}
button.dir-row:focus-visible{outline:none;box-shadow:var(--focus)}
button.dir-row:disabled{cursor:default;opacity:.6}
.dir-start .dir-name{color:var(--brand-dark)}
.dir-main{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
.dir-name{font:var(--md-sys-typescale-body-large);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.dir-sub{font:var(--md-sys-typescale-body-medium);color:var(--muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.dir-nums{display:flex;flex-direction:column;gap:2px;margin-top:3px}
.dir-num{display:flex;align-items:center;gap:8px;background:none;border:1px solid var(--line);
  border-radius:var(--md-sys-shape-corner-small);height:32px;padding:0 12px 0 8px;font:var(--md-sys-typescale-label-large);
  color:var(--ink);cursor:pointer;text-align:left}
.dir-num .ms{color:var(--brand);font-size:18px}
.dir-num-act{margin-left:auto;color:var(--brand);font-weight:600}
.fwd-list{overflow-y:auto;padding:4px 10px 12px}
.fwd-target>.ms:last-child{color:var(--brand);align-self:center}
.fwd-closed{align-self:center;font:var(--md-sys-typescale-label-small);padding:2px 8px;border-radius:var(--md-sys-shape-corner-small);
  background:var(--pill-closed-bg);color:var(--muted);white-space:nowrap}
/* per-message action: Forward (images, videos, documents) */
.msg-fwd{margin-right:auto;display:inline-flex;align-items:center;gap:2px;background:none;border:0;
  padding:0 4px 0 0;font:inherit;font-size:10.5px;color:var(--faint);cursor:pointer;
  transition:color var(--dur-fast) var(--ease)}
.msg-fwd:hover{color:var(--brand)}
.msg-fwd:focus-visible{outline:none;box-shadow:var(--focus);border-radius:4px}

/* ---------- list / thread states ---------- */
.list-empty{color:var(--muted);font:var(--md-sys-typescale-body-medium);text-align:center;padding:48px 24px;
  display:flex;flex-direction:column;align-items:center;gap:8px;
  animation:fade-in var(--dur-slow) var(--ease) both}
.list-empty .ms{color:var(--muted);opacity:.6}
.list-empty strong{color:var(--ink);font:var(--md-sys-typescale-title-medium)}
.list-foot{background:var(--banner-bg);color:var(--banner-ink);font:var(--md-sys-typescale-label-medium);text-align:center;padding:8px;
  display:flex;align-items:center;justify-content:center;gap:6px;
  animation:slide-up var(--dur) var(--ease) both}
@keyframes slide-up{from{opacity:0;transform:translateY(var(--dist))}to{opacity:1;transform:none}}
.list-foot .ms{font-size:15px;animation:spin 1.4s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}
.load-more{align-self:center;border:0;background:var(--md-sys-color-surface-container-high);
  color:var(--brand);font:var(--md-sys-typescale-label-large);height:32px;padding:0 16px;
  border-radius:var(--md-sys-shape-corner-small);box-shadow:var(--shadow-1);
  margin-bottom:8px;transition:box-shadow var(--dur-fast) var(--ease),transform var(--dur-fast) var(--ease)}
.load-more:hover{box-shadow:var(--shadow-2)}
.load-more:active{transform:scale(.97)}

/* ---------- skeletons ---------- */
/* Shown for the first paint only. A skeleton that matches the real row's shape
   stops the list jumping when the data lands. */
.skel-item{display:flex;gap:16px;padding:12px 12px 12px 8px;min-height:72px;align-items:center}
.skel{background:linear-gradient(90deg,var(--line-soft) 25%,var(--md-sys-color-surface-container-lowest) 37%,var(--line-soft) 63%);
  background-size:400% 100%;animation:shimmer 1.4s ease infinite;border-radius:6px}
@keyframes shimmer{from{background-position:100% 0}to{background-position:0 0}}
.skel-avatar{width:40px;height:40px;border-radius:50%;flex:0 0 40px}
.skel-lines{flex:1;display:flex;flex-direction:column;gap:7px}
.skel-line{height:10px}
.skel-line.w60{width:60%}.skel-line.w85{width:85%}.skel-line.w40{width:40%}
.skel-bubble{height:38px;border-radius:var(--radius);max-width:70%}
.skel-bubble.in{align-self:flex-start;width:56%}
.skel-bubble.out{align-self:flex-end;width:44%}
.skel-bubble.tall{height:56px}

/* ---------- toasts ---------- */
/* Top-right, under the conversation header. Deliberately NOT bottom-centre: that
   is where the composer lives, and a toast that covers the message box while
   someone is mid-reply is worse than no toast at all. It is also where the
   on-screen keyboard lands on a phone. */
.toasts{position:fixed;right:16px;top:74px;z-index:60;
  display:flex;flex-direction:column;gap:8px;align-items:flex-end;pointer-events:none;
  width:min(380px,calc(100vw - 32px))}
/* M3 snackbar: inverse surface, extra-small corners, level-3 shadow. */
.toast{background:var(--toast-bg);color:var(--toast-ink);border-radius:var(--md-sys-shape-corner-extra-small);
  min-height:48px;padding:12px 16px;font:var(--md-sys-typescale-body-medium);
  display:flex;align-items:center;gap:12px;box-shadow:var(--shadow-3);pointer-events:auto;
  width:100%;cursor:pointer;
  animation:toast-in var(--md-sys-motion-duration-medium1) var(--md-sys-motion-easing-emphasized-decelerate) both}
@keyframes toast-in{from{opacity:0;transform:translateY(calc(-1 * var(--dist)))}to{opacity:1;transform:none}}
.toast.leaving{animation:toast-out var(--md-sys-motion-duration-short3) var(--md-sys-motion-easing-emphasized-accelerate) both}
@keyframes toast-out{to{opacity:0;transform:translateY(calc(-1 * var(--dist-s)))}}
.toast .ms{font-size:24px;flex:0 0 auto}
.toast-body{flex:1;min-width:0}
.toast-title{font:var(--md-sys-typescale-title-small);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.toast-sub{font:var(--md-sys-typescale-body-small);opacity:.8;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-top:2px}
.toast.error{background:var(--toast-error-bg)}
.toast.error .ms{color:var(--toast-ink)}
.toast.mention .ms{color:var(--toast-accent)}
.toast.escalation{background:var(--toast-error-bg)}
.toast.escalation .ms{color:var(--toast-ink)}

/* ---------- attachments in the thread ---------- */
.bubble .body{display:block}
.media-img{display:block;max-width:100%;width:260px;max-height:320px;object-fit:cover;
  border-radius:8px;margin-bottom:4px;cursor:zoom-in;background:var(--line-soft);
  transition:filter var(--dur-fast) var(--ease)}
.media-img:hover{filter:brightness(.96)}
video.media-img{cursor:default;object-fit:contain}
.media-audio{display:block;width:250px;max-width:100%;margin-bottom:4px}
.media-doc{display:flex;align-items:center;gap:12px;text-decoration:none;color:inherit;
  background:var(--tint-04);border-radius:var(--md-sys-shape-corner-small);padding:8px 12px;margin-bottom:4px;
  transition:background var(--dur-fast) var(--ease)}
.media-doc:hover{background:var(--tint-08)}
/* An uploaded header with no stored bytes yet — same row, not clickable. */
.media-doc.no-link{cursor:default}
.media-doc.no-link:hover{background:var(--tint-04)}
.doc-icon{color:var(--brand-dark)}
/* A contact card the customer shared: the doc row's surface, the template
   buttons' action style. */
.contact-card{background:var(--tint-04);border-radius:var(--md-sys-shape-corner-small);
  margin-bottom:4px;width:260px;max-width:100%;overflow:hidden;white-space:normal}
.card-head{display:flex;align-items:center;gap:12px;padding:8px 12px}
.card-acts{display:flex;border-top:1px solid color-mix(in srgb,currentColor 14%,transparent)}
.card-btn{flex:1;display:flex;align-items:center;justify-content:center;gap:6px;height:36px;
  background:none;border:0;cursor:pointer;color:var(--md-sys-color-info);
  font:var(--md-sys-typescale-label-large);white-space:nowrap;
  transition:background var(--dur-fast) var(--ease)}
.card-btn+.card-btn{border-left:1px solid color-mix(in srgb,currentColor 14%,transparent)}
.card-btn:hover{background:color-mix(in srgb,var(--md-sys-color-info) 8%,transparent)}
.card-btn:disabled{color:var(--muted);cursor:default}
.card-btn .ms{font-size:15px}
.card-numbers{list-style:none;margin:0;padding:4px 24px 12px;display:flex;flex-direction:column;gap:12px}
.card-numbers li{display:flex;align-items:center;justify-content:space-between;gap:12px}
#cardAction{display:flex;flex-direction:column}
#cardAction .btn{margin:14px 16px 16px;justify-content:center}
.doc-meta{display:flex;flex-direction:column;min-width:0}
.doc-name{font:var(--md-sys-typescale-title-small);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:190px}
.doc-size{font:var(--md-sys-typescale-body-small);color:var(--muted)}
.media-pending{font-size:12.5px;color:var(--muted);padding:12px 0;display:flex;align-items:center;gap:7px}
.msg-error{font-size:11.5px;color:var(--danger);margin-top:4px;white-space:normal;
  display:flex;align-items:flex-start;gap:5px}
.msg-error .ms{font-size:14px;margin-top:1px}

/* ---------- composer additions ---------- */
.composer-error,.modal-error{background:var(--error-bg);color:var(--md-sys-color-on-error-container);
  font:var(--md-sys-typescale-body-medium);padding:8px 16px;border-radius:var(--md-sys-shape-corner-medium);margin-bottom:8px;display:flex;
  align-items:center;gap:7px;animation:slide-down var(--dur) var(--ease) both}
.modal-error{margin:0 24px 4px}
.attach-preview{display:flex;align-items:center;justify-content:space-between;gap:12px;
  background:var(--brand-tint);color:var(--on-brand-tint);font:var(--md-sys-typescale-body-medium);
  padding:4px 4px 4px 16px;border-radius:var(--md-sys-shape-corner-medium);margin-bottom:8px;
  animation:slide-down var(--dur) var(--ease) both}
.attach-preview span{display:flex;align-items:center;gap:7px;min-width:0}
.attach-preview span span{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.attach{flex:0 0 48px;height:48px;border-radius:50%;border:0;background:transparent;
  display:grid;place-items:center;color:var(--muted);transition:transform var(--dur-fast) var(--ease)}
.attach .ms{font-size:24px}
.attach:active{transform:scale(.92)}
.attach:disabled{opacity:.5;cursor:default;transform:none}

/* ---------- header: settings + notification bell ---------- */
.bell{position:relative}
.bell-count{position:absolute;top:2px;right:2px;background:var(--md-sys-color-error);color:var(--md-sys-color-on-error);
  border-radius:var(--md-sys-shape-corner-full);min-width:16px;height:16px;padding:0 4px;font:var(--md-sys-typescale-label-small);
  display:grid;place-items:center;
  animation:pop var(--dur) var(--ease) both}
.note-count{background:var(--brand);color:var(--on-brand);border-radius:var(--md-sys-shape-corner-full);padding:0 4px;
  min-width:16px;height:16px;display:inline-grid;place-items:center;font:var(--md-sys-typescale-label-small);
  position:absolute;top:2px;right:2px}
.waiting-pill{font:var(--md-sys-typescale-label-medium);height:24px;padding:0 8px;border-radius:var(--md-sys-shape-corner-small);
  white-space:nowrap;background:var(--pill-soon-bg);color:var(--md-sys-color-on-warning-container);display:inline-flex;align-items:center;gap:4px}
.waiting-pill .ms{font-size:16px}
.waiting-pill.escalated{background:var(--error-bg);color:var(--md-sys-color-on-error-container)}
.link-btn{background:none;border:0;color:var(--on-brand);font-size:12px;font-weight:500;opacity:.85;
  padding:6px 8px;border-radius:8px;transition:background var(--dur-fast) var(--ease),opacity var(--dur-fast) var(--ease)}
.link-btn:hover{opacity:1;background:var(--on-brand-08)}

/* ---------- quick replies ---------- */
.canned-pop{position:absolute;bottom:calc(100% - 6px);left:12px;right:12px;max-width:520px;
  background:var(--md-sys-color-surface-container);border:0;border-radius:var(--md-sys-shape-corner-medium);margin-bottom:8px;
  box-shadow:var(--shadow-2);overflow:hidden;z-index:15;
  animation:pop-up var(--dur) var(--ease) both}
@keyframes pop-up{from{opacity:0;transform:translateY(var(--dist)) scaleY(calc(1 - .1 * var(--md-sys-motion-distance)))}to{opacity:1;transform:none}}
.canned-head{font:var(--md-sys-typescale-title-small);color:var(--brand);padding:12px 16px 8px;
  display:flex;align-items:center;gap:8px}
.canned-head .ms{font-size:14px;color:var(--brand)}
.canned-list{max-height:240px;overflow-y:auto;overscroll-behavior:contain}
.canned-item{padding:8px 16px;min-height:56px;cursor:pointer}
.canned-item.on{background:var(--brand-tint);color:var(--on-brand-tint)}
.canned-key{font-family:var(--md-ref-typeface-mono);font-size:12px;
  color:var(--brand);font-weight:600;margin-right:8px}
.canned-title{font:var(--md-sys-typescale-body-large)}
.canned-title em{font-style:normal;font:var(--md-sys-typescale-label-small);color:var(--muted);margin-left:6px}
.canned-body{display:block;font:var(--md-sys-typescale-body-medium);color:var(--muted);margin-top:2px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.canned-empty{font:var(--md-sys-typescale-body-medium);color:var(--muted);padding:16px;margin:0;text-align:center}

/* ---------- internal notes ---------- */
.notes-drawer{background:var(--notes-bg)}
.notes-warn{background:var(--banner-bg);color:var(--banner-ink);font:var(--md-sys-typescale-body-small);margin:0 16px;padding:12px 16px;
  border-radius:var(--md-sys-shape-corner-medium);display:flex;align-items:center;gap:12px}
.notes-warn .ms{font-size:16px;flex:0 0 auto}
.notes-body{flex:1;overflow-y:auto;overscroll-behavior:contain;padding:12px 14px;
  display:flex;flex-direction:column;gap:9px}
.note-card{background:var(--md-sys-color-surface-container-lowest);border:0;box-shadow:inset 3px 0 0 var(--amber);
  border-radius:var(--md-sys-shape-corner-medium);padding:12px 16px;
  animation:msg-in var(--dur) var(--ease) both}
.note-head{display:flex;justify-content:space-between;gap:8px;font:var(--md-sys-typescale-label-small);color:var(--muted);margin-bottom:4px}
.note-head span:first-child{font:var(--md-sys-typescale-label-medium);color:var(--ink)}
.note-body{font:var(--md-sys-typescale-body-medium);white-space:pre-wrap;word-wrap:break-word}
.mention{background:var(--brand-tint);color:var(--on-brand-tint);border-radius:var(--md-sys-shape-corner-extra-small);
  padding:0 4px;font-weight:500}
.notes-compose{border-top:1px solid var(--line);padding:10px 14px;position:relative;
  padding-bottom:calc(10px + env(safe-area-inset-bottom))}
#noteInput{width:100%;border:1px solid var(--md-sys-color-outline);border-radius:var(--md-sys-shape-corner-extra-small);padding:12px 16px;
  font:var(--md-sys-typescale-body-large);resize:vertical;max-height:140px;background:transparent;color:var(--ink);
  transition:border-color var(--dur-fast) var(--ease),box-shadow var(--dur-fast) var(--ease)}
#noteInput:focus{border-color:var(--amber);box-shadow:0 0 0 3px color-mix(in srgb,var(--amber) 22%,transparent)}
#noteSend{width:100%;margin-top:8px;justify-content:center}
.mention-pop{position:absolute;bottom:100%;left:16px;right:16px;background:var(--md-sys-color-surface-container);
  border:0;border-radius:var(--md-sys-shape-corner-extra-small);overflow:hidden;margin-bottom:6px;padding:8px 0;
  box-shadow:var(--shadow-2);z-index:15;animation:pop-up var(--dur-fast) var(--ease) both}
.mention-item{padding:0 16px;min-height:48px;display:flex;align-items:center;cursor:pointer;
  font:var(--md-sys-typescale-label-large);color:var(--ink)}
.mention-item span{font:var(--md-sys-typescale-body-small);color:var(--muted);margin-left:8px}

/* ---------- notifications ---------- */
.notif-item{padding:12px 0;border-bottom:1px solid var(--line);cursor:pointer;
  display:flex;gap:16px;transition:opacity var(--dur) var(--ease)}
.notif-item .ms{color:var(--brand);margin-top:1px;font-size:24px}
.notif-item.read{opacity:.5}
.notif-main{min-width:0;flex:1}
.notif-title{font:var(--md-sys-typescale-title-small)}
.notif-item.escalation .notif-title,.notif-item.escalation .ms{color:var(--danger)}
.notif-item.mention .notif-title,.notif-item.mention .ms{color:var(--brand)}
.notif-body{font:var(--md-sys-typescale-body-medium);color:var(--muted);margin-top:2px}
.notif-time{font:var(--md-sys-typescale-label-small);color:var(--muted);margin-top:4px}

/* ---------- responsive: single-pane mobile app view ---------- */
@media(max-width:820px){
  /* The parked conversation pane sits a full screen-width to the right, which
     otherwise doubles the document's scrollWidth and lets the whole page be
     dragged sideways. Clip it. `clip` rather than `hidden` so the vertical axis
     is not silently turned into a scroll container; `hidden` is the fallback for
     browsers without it. */
  .app{grid-template-columns:1fr;overflow-x:hidden;overflow-x:clip}
  /* The conversation pane rides in over the list. Transform-only, so the slide
     never reflows the thread underneath it. */
  .conv-pane{position:absolute;inset:0;z-index:10;transform:translateX(100%);
    transition:transform var(--md-sys-motion-duration-medium4) var(--md-sys-motion-easing-emphasized-decelerate);will-change:transform}
  .app.show-conv .conv-pane{transform:translateX(0)}
  .app.show-conv .list-pane{display:none}
  .back{display:inline-grid}
  .ctx-drawer{width:100%;border-radius:0}
  .thread{padding:14px 4% 10px}
  .bubble{max-width:88%}
  .media-img{width:100%}
  .conv-head{padding:8px 6px 8px 4px;gap:6px}
  .conv-title .name{font-size:15.5px}
  /* Take/resolve collapse to icon-only rather than disappearing. Hiding them
     outright left a phone unable to claim or close a chat at all, which is most
     of the job. The window pill keeps its text — it is the one thing that
     changes what you're allowed to send. */
  .conv-actions .btn .lbl{display:none}
  .conv-actions .btn{width:44px;height:44px;padding:0;justify-content:center;border-radius:50%}
  .conv-actions .btn .ms{font-size:20px}
  .conv-actions{gap:0}
  .waiting-pill{display:none}
  .window-pill{padding:0 8px}
  /* Comfortable thumb targets everywhere. */
  .icon-btn{width:48px;height:48px}
  /* The composer's three tools give the field room on a phone. */
  .composer-row{gap:4px}
  .attach{flex-basis:40px;width:40px}
  #input{padding:12px 16px}
  #input::placeholder{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .canned-pop,.mention-pop{left:8px;right:8px}
  .canned-item{padding:12px 13px}
  .toasts{left:12px;right:12px;width:auto;top:calc(70px + env(safe-area-inset-top))}
  /* iOS zooms the page when a font under 16px gets focus, and never zooms back */
  #input,.search input,.tmpl-vars input,.auth-card input,#noteInput{font-size:16px}
  .compose-field input,.person-add-number input,.nc-num input,.person-drawer textarea{font-size:16px}
  .person-add-number{flex-wrap:wrap}
  /* keep the composer and the list header clear of the notch and home indicator */
  .list-head{padding-top:calc(8px + env(safe-area-inset-top))}
  .conv-head{padding-top:calc(8px + env(safe-area-inset-top))}
  .ctx-drawer header{padding-top:calc(10px + env(safe-area-inset-top))}
  .composer{padding-bottom:calc(10px + env(safe-area-inset-bottom))}
  .conv-list{padding-bottom:env(safe-area-inset-bottom)}
}

/* Narrow phones: the brand wordmark is the first thing that can go. */
@media(max-width:400px){
  .brand span{display:none}
  .me-name{display:none}
}

/* ---------- reduced motion ----------
   Someone who has asked their OS for less movement gets a static app: no slides,
   no shimmer, no breathing ticks. Transitions collapse to ~0 rather than being
   removed outright, so state changes still land instantly and correctly. */
@media(prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:.001ms !important;
    animation-iteration-count:1 !important;
    transition-duration:.001ms !important;
    scroll-behavior:auto !important;
  }
  .thread{scroll-behavior:auto}
  .conv-pane{transition:none}
  .ripple{display:none}
}

/* ---------- thread fade-through (View Transitions) ----------
   inbox.js wraps a thread switch in document.startViewTransition (wide screens,
   motion allowed) with html.vt-thread set: the page stays put and only the
   conversation pane fades through (old out 90ms, new in 210ms from 92%). */
.conv-pane{view-transition-name:conv}
html.vt-thread::view-transition-group(root){animation:none}
html.vt-thread::view-transition-old(root),html.vt-thread::view-transition-new(root){animation:none}
html.vt-thread::view-transition-old(conv){animation:vt-out var(--md-sys-motion-duration-short2) var(--md-sys-motion-easing-emphasized-accelerate) both}
html.vt-thread::view-transition-new(conv){animation:vt-in var(--md-sys-motion-duration-medium1) var(--md-sys-motion-easing-emphasized-decelerate) 90ms both}
@keyframes vt-out{to{opacity:0}}
/* A theme or palette change (personalise.js, settings-prefs.js add
   html.vt-theme): the whole page cross-fades its colours in 200ms. */
html.vt-theme::view-transition-group(*),html.vt-theme::view-transition-old(*),html.vt-theme::view-transition-new(*){
  animation-duration:var(--md-sys-motion-duration-short4);animation-timing-function:var(--md-sys-motion-easing-standard)}
/* Reduced motion: no View Transition animates (the scripts also skip them). */
@media (prefers-reduced-motion:reduce){
  ::view-transition-group(*),::view-transition-old(*),::view-transition-new(*){animation:none !important}
}
@keyframes vt-in{from{opacity:0;transform:scale(.92)}to{opacity:1;transform:none}}

/* ---------- lifecycle phase ----------
   Only the two phases that ask something of the reader get a chip: nobody owns
   this, or we're waiting on them. "In progress" and "resolved" are the normal
   cases and a chip on every row would be wallpaper. */
.phase-chip{font:var(--md-sys-typescale-label-small);letter-spacing:var(--md-sys-typescale-label-small-tracking);
  height:20px;padding:0 8px;display:inline-flex;align-items:center;border-radius:var(--md-sys-shape-corner-small);
  white-space:nowrap;flex:0 0 auto}
.phase-chip.unassigned{background:var(--error-bg);color:var(--md-sys-color-on-error-container)}
.phase-chip.waiting_on_customer{background:var(--pill-closed-bg);color:var(--muted)}

/* ============================================================================
   In-app media viewer (viewer.js). A light, WhatsApp-style photo/document view:
   a WHITE stage with dark chrome, GreenConnect tokens only. Two modes share this
   look — .gcv-image is the gallery (images-only filmstrip, opens ~175%),
   .gcv-pdf is the document viewer (no filmstrip, page controls). All motion
   collapses under the global prefers-reduced-motion block above; the
   container-transform is JS-guarded too.
   ============================================================================ */
.gcv{position:fixed;inset:0;z-index:1000}
.gcv[hidden]{display:none}
/* Scrim and shell are each fixed to the viewport (not sized by .gcv), so the
   overlay always fills the screen whatever the content height — a short PDF no
   longer leaves the inbox showing underneath. */
.gcv-scrim{position:fixed;inset:0;background:var(--panel);
  animation:gcv-scrim var(--dur) var(--ease) both}
@keyframes gcv-scrim{from{opacity:0}to{opacity:1}}
.gcv-shell{position:fixed;inset:0;display:flex;flex-direction:column;
  color:var(--ink);min-width:0;transform-origin:center}
body.gcv-lock{overflow:hidden}

/* top app bar */
.gcv-top{display:flex;align-items:center;gap:12px;padding:10px 14px;
  border-bottom:1px solid var(--line-soft)}
.gcv-id{min-width:0;margin-left:2px}
.gcv-id-name{font-weight:600;font-size:14px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.gcv-id-sub{font-size:12px;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.gcv-file{flex:1;text-align:center;font-size:13px;color:var(--muted);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;padding:0 8px}
.gcv-actions{display:flex;align-items:center;gap:8px;position:relative}
.gcv-download{display:inline-flex;align-items:center;gap:6px;text-decoration:none;
  background:var(--brand-tint);color:var(--brand-dark);border-radius:20px;
  padding:8px 14px;font-size:13px;font-weight:600;
  transition:transform var(--dur-fast) var(--ease-emphasized),filter var(--dur-fast) var(--ease)}
.gcv-download:hover{filter:brightness(1.03)}
.gcv-download:active{transform:scale(.96)}
.gcv-download .ms{font-size:18px}

/* circular icon buttons with a state layer + spring press (M3) */
.gcv-icon{position:relative;display:inline-flex;align-items:center;justify-content:center;
  width:40px;height:40px;border:0;border-radius:50%;background:transparent;color:var(--ink);
  cursor:pointer;overflow:hidden;
  transition:transform var(--dur-fast) var(--ease-spring)}
.gcv-icon::before{content:"";position:absolute;inset:0;border-radius:50%;background:currentColor;
  opacity:0;transition:opacity var(--dur-fast) var(--ease)}
.gcv-icon:hover::before{opacity:.08}
.gcv-icon:active{transform:scale(.9)}
.gcv-icon:active::before{opacity:.14}
.gcv-icon:disabled{opacity:.32;cursor:default}
.gcv-icon .ms{font-size:24px;position:relative}

/* details popover */
.gcv-details{position:absolute;top:46px;right:0;background:var(--panel);color:var(--ink);
  border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow-3);
  padding:10px 12px;min-width:210px;z-index:5;
  animation:dialog-in var(--dur) var(--ease) both}
.gcv-det-row{display:flex;justify-content:space-between;gap:16px;font-size:13px;padding:4px 0}
.gcv-det-row span{color:var(--muted)}
.gcv-det-row b{font-weight:600;max-width:130px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* body + V5 side arrows (vertically centered on the stage) */
.gcv-body{position:relative;flex:1;display:flex;align-items:center;justify-content:center;min-height:0}
.gcv-nav{position:absolute;top:50%;transform:translateY(-50%);z-index:3;
  width:48px;height:48px;border:1px solid var(--line);border-radius:50%;cursor:pointer;
  background:var(--panel);color:var(--ink);display:flex;align-items:center;justify-content:center;
  box-shadow:var(--shadow-2);
  transition:background var(--dur-fast) var(--ease),transform var(--dur-fast) var(--ease-spring)}
.gcv-nav:hover{background:var(--hover)}
.gcv-nav:active{transform:translateY(-50%) scale(.9)}
.gcv-nav:disabled{opacity:0;pointer-events:none}
.gcv-nav .ms{font-size:30px}
.gcv-prev{left:14px}
.gcv-next{right:14px}

/* stage + media */
.gcv-stage{flex:1;height:100%;display:flex;align-items:center;justify-content:center;
  overflow:hidden;padding:8px 68px;outline:none;min-width:0}
.gcv-loading,.gcv-fail{display:flex;flex-direction:column;align-items:center;gap:10px;color:var(--muted);font-size:14px}
.gcv-loading .ms{font-size:34px;animation:gcv-spin 1s linear infinite}
.gcv-fail .ms{font-size:38px;color:var(--danger)}
@keyframes gcv-spin{to{transform:rotate(360deg)}}

.gcv-img-wrap{max-width:100%;max-height:100%;display:flex;align-items:center;justify-content:center;
  cursor:grab;touch-action:none}
.gcv-img-wrap.zoomed{cursor:grab}
.gcv-img{max-width:100%;max-height:78vh;object-fit:contain;border-radius:6px;
  box-shadow:var(--shadow-2);
  transition:transform var(--dur) var(--ease-emphasized);will-change:transform}
.gcv-img.fill{max-height:none;height:78vh;object-fit:cover}
.gcv-img.actual{max-width:none;max-height:none}

.gcv-pdf{max-height:80vh;overflow:auto;display:flex;justify-content:center;
  background:var(--md-sys-color-surface-container-highest);border-radius:8px;padding:16px}
.gcv-pdf-canvas{box-shadow:var(--shadow-3);background:#fff;height:auto} /* the page itself: documents are never themed */

/* toolbar */
.gcv-tools{display:flex;align-items:center;justify-content:space-between;gap:12px;
  padding:8px 16px;flex-wrap:wrap;border-top:1px solid var(--line-soft)}
.gcv-tool-left,.gcv-tool-right{display:flex;align-items:center;gap:6px}
.gcv-pct,.gcv-page{font-size:13px;color:var(--muted);font-variant-numeric:tabular-nums;min-width:52px;text-align:center}
.gcv-seg{display:inline-flex;background:var(--hover);border-radius:20px;padding:3px}
.gcv-seg button{border:0;background:transparent;color:var(--muted);font-size:12px;font-weight:600;
  padding:6px 12px;border-radius:16px;cursor:pointer;
  transition:background var(--dur-fast) var(--ease),color var(--dur-fast) var(--ease)}
.gcv-seg button.on{background:var(--brand);color:var(--on-brand)}

/* filmstrip — image gallery only; hidden in the PDF (document) viewer */
.gcv-film{display:flex;gap:8px;padding:12px 16px 16px;overflow-x:auto;scrollbar-width:thin;
  border-top:1px solid var(--line-soft)}
.gcv-pdf .gcv-film{display:none}
.gcv-thumb{flex:0 0 auto;width:54px;height:54px;border:0;padding:0;border-radius:10px;
  overflow:hidden;cursor:pointer;background:var(--hover);
  display:flex;align-items:center;justify-content:center;
  outline:2px solid transparent;outline-offset:2px;
  transition:outline-color var(--dur-fast) var(--ease),transform var(--dur-fast) var(--ease-spring)}
.gcv-thumb img{width:100%;height:100%;object-fit:cover}
.gcv-thumb-glyph .ms{font-size:26px;color:var(--muted)}
.gcv-thumb.on{outline-color:var(--brand)}
.gcv-thumb:active{transform:scale(.93)}

/* Inline voice-note player (in-bubble, not the viewer). WhatsApp-familiar:
   a round play/pause, a scrubbable progress track and a running time — styled
   with GreenConnect tokens. Bytes stream through the gated route (viewer.js is
   not involved). Motion collapses under the global reduced-motion block. */
.audio-player{display:flex;align-items:center;gap:10px;padding:4px 2px;
  min-width:220px;max-width:min(320px,64vw)}
.audio-play{flex:0 0 auto;width:38px;height:38px;border:0;border-radius:50%;
  background:var(--brand);color:var(--on-brand);cursor:pointer;
  display:flex;align-items:center;justify-content:center;
  transition:transform var(--dur-fast) var(--ease-spring),filter var(--dur-fast) var(--ease)}
.audio-play:hover{filter:brightness(1.06)}
.audio-play:active{transform:scale(.9)}
.audio-play .ms{font-size:22px}
.audio-track{flex:1;height:4px;border-radius:3px;position:relative;cursor:pointer;
  background:color-mix(in srgb,var(--brand) 18%,transparent)}
.audio-fill{position:absolute;left:0;top:0;height:100%;width:0;border-radius:3px;
  background:var(--brand);transition:width .1s linear}
.audio-time{flex:0 0 auto;font-size:12px;color:var(--muted);
  font-variant-numeric:tabular-nums;min-width:34px;text-align:right}
.audio-player.loading .audio-play{opacity:.6}
.audio-player.playing .audio-play{box-shadow:0 0 0 4px color-mix(in srgb,var(--brand) 16%,transparent)}
.audio-player.failed .audio-time{color:var(--danger)}

@media (max-width:640px){
  .gcv-stage{padding:6px 56px}
  .gcv-nav{width:42px;height:42px}
  .gcv-file{display:none}
}

/* confirm.js: the destructive-action dialog (M3 basic dialog). */
.confirm-dialog{width:min(400px,92vw)}
.confirm-text{margin:0;padding:24px 24px 0;font:var(--md-sys-typescale-body-large);color:var(--ink)}

/* ---------------------------------------------------------------- replies & reactions
   Stage 5. The quote sits at the top of the bubble (a tinted card with a leading
   accent bar, as WhatsApp); reactions hang off the bubble's bottom edge; hover
   (or focus) reveals reply / react beside the bubble. */
.bubble .quote{display:flex;flex-direction:column;gap:1px;width:100%;text-align:left;
  margin:0 0 4px;padding:4px 8px 5px 10px;border:0;border-radius:var(--md-sys-shape-corner-small);
  background:color-mix(in srgb,currentColor 7%,transparent);color:inherit;font:inherit;cursor:pointer;
  box-shadow:inset 3px 0 0 var(--md-sys-color-primary);white-space:normal;position:relative;overflow:hidden;
  animation:quote-in var(--md-sys-motion-duration-medium1) var(--md-sys-motion-easing-emphasized-decelerate) both}
.bubble .quote.q-in{box-shadow:inset 3px 0 0 var(--md-sys-color-tertiary)}
.bubble .quote.missing{cursor:default;flex-direction:row;align-items:center;gap:6px;color:var(--muted)}
.bubble .quote::before{content:"";position:absolute;inset:0;background:currentColor;opacity:0;
  transition:opacity var(--dur-fast) var(--md-sys-motion-easing-standard)}
.bubble .quote:hover::before{opacity:.08}
.bubble .quote:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:1px}
.quote-who{font:var(--md-sys-typescale-label-medium);color:var(--md-sys-color-primary)}
.quote.q-in .quote-who{color:var(--md-sys-color-tertiary)}
.quote-text{font:var(--md-sys-typescale-body-medium);opacity:.85;display:-webkit-box;
  -webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
@keyframes quote-in{from{opacity:0;transform:translateY(calc(-1 * var(--dist-s)))}to{opacity:1;transform:none}}

.bubble.has-rx{margin-bottom:14px}
.rx-row{position:absolute;bottom:-14px;display:flex;gap:2px;padding:1px 5px;
  border-radius:var(--md-sys-shape-corner-full);background:var(--md-sys-color-surface-container-highest);
  box-shadow:0 0 0 2px var(--md-sys-color-surface-container),0 1px 2px rgb(0 0 0 / .12);
  font-size:15px;line-height:22px;white-space:nowrap}
.bubble.in .rx-row{left:8px}
.bubble.out .rx-row{right:8px}
.rx-row .rx.pending{opacity:.55}
.rx-row.pop{animation:rx-pop var(--md-sys-motion-duration-medium2) var(--md-sys-motion-easing-emphasized-decelerate)}
@keyframes rx-pop{0%{transform:scale(calc(1 - .6 * var(--md-sys-motion-distance)));opacity:0}
  60%{transform:scale(calc(1 + .15 * var(--md-sys-motion-distance)));opacity:1}100%{transform:none}}

.msg-acts{position:absolute;top:50%;display:flex;gap:2px;opacity:0;pointer-events:none;
  transform:translateY(-50%);transition:opacity var(--dur-fast) var(--md-sys-motion-easing-standard)}
.bubble.in .msg-acts{left:100%;padding-left:6px}
.bubble.out .msg-acts{right:100%;padding-right:6px;flex-direction:row-reverse}
.bubble:hover .msg-acts,.bubble:focus-within .msg-acts{opacity:1;pointer-events:auto}
.thread.window-closed .msg-acts [data-reply],.thread.window-closed .msg-acts [data-react]{display:none}
.msg-act{display:inline-grid;place-items:center;width:30px;height:30px;border:0;padding:0;
  border-radius:50%;background:var(--md-sys-color-surface-container-high);color:var(--muted);cursor:pointer;
  position:relative;overflow:hidden}
.msg-act .ms{font-size:18px}
.msg-act:hover{color:var(--ink)}
@media (hover:none){.msg-acts{display:none}}

/* Swipe-to-reply (touch): the bubble follows the finger, a reply glyph fades in
   behind it, and it springs back on release. */
.bubble{transition:transform var(--md-sys-motion-duration-medium2) var(--md-sys-motion-easing-emphasized-decelerate)}
.bubble.swiping::after{content:"reply";font-family:"Material Symbols Rounded";position:absolute;
  left:-34px;top:50%;width:26px;height:26px;margin-top:-13px;display:grid;place-items:center;
  border-radius:50%;background:var(--md-sys-color-surface-container-high);color:var(--md-sys-color-primary);
  font-size:18px;opacity:var(--swipe,0);transform:scale(calc(.6 + .4 * var(--swipe,0)))}

/* Jump-to-original / search-hit highlight: thread.css (.bubble.flash::after). */

.react-bar{position:fixed;z-index:60;display:flex;gap:2px;padding:4px;
  border-radius:var(--md-sys-shape-corner-full);background:var(--md-sys-color-surface-container-high);
  box-shadow:var(--md-sys-elevation-level2,0 2px 6px rgb(0 0 0 / .2));
  animation:rxbar-in var(--md-sys-motion-duration-medium1) var(--md-sys-motion-easing-emphasized-decelerate) both}
.react-bar[hidden]{display:none}
@keyframes rxbar-in{from{opacity:0;transform:scale(calc(1 - .2 * var(--md-sys-motion-distance))) translateY(var(--dist-s))}to{opacity:1;transform:none}}
.rx-opt{width:40px;height:40px;border:0;border-radius:50%;background:transparent;font-size:24px;
  line-height:1;cursor:pointer;transition:transform var(--dur-fast) var(--md-sys-motion-easing-standard)}
.rx-opt:hover,.rx-opt:focus-visible{transform:scale(1.18);background:var(--md-sys-color-surface-container-highest);outline:none}
.rx-opt.on{background:var(--md-sys-color-secondary-container)}

.reply-preview{display:flex;align-items:center;gap:8px;margin:0 0 6px;padding:6px 4px 6px 12px;
  border-radius:var(--md-sys-shape-corner-medium);background:var(--md-sys-color-surface-container-high);
  box-shadow:inset 3px 0 0 var(--md-sys-color-primary);
  animation:reply-in var(--md-sys-motion-duration-medium1) var(--md-sys-motion-easing-emphasized-decelerate) both}
.reply-preview.leaving{animation:reply-out var(--md-sys-motion-duration-short3) var(--md-sys-motion-easing-emphasized-accelerate) both}
@keyframes reply-in{from{opacity:0;transform:translateY(var(--dist))}to{opacity:1;transform:none}}
@keyframes reply-out{to{opacity:0;transform:translateY(var(--dist))}}
.reply-quote{flex:1;min-width:0;display:flex;flex-direction:column}
.reply-who{font:var(--md-sys-typescale-label-medium);color:var(--md-sys-color-primary)}
.reply-text{font:var(--md-sys-typescale-body-medium);color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.reply-old{font:var(--md-sys-typescale-label-small);color:var(--md-sys-color-warning,var(--amber));margin-top:2px}

/* ---------------------------------------------------------------- saved contacts
   The contact drawer's title (display name: typed → Zoho → WhatsApp → number)
   and the "Zoho customer with this phone number" suggestion (link to confirm). */
.contact-title{font:var(--md-sys-typescale-title-medium);color:var(--ink);margin:0 0 8px;
  overflow-wrap:anywhere}
.acct-suggest{margin:4px 0 8px;padding:8px 10px;border-radius:var(--md-sys-shape-corner-medium);
  background:var(--md-sys-color-secondary-container);color:var(--md-sys-color-on-secondary-container);
  animation:card-in var(--md-sys-motion-duration-medium2) var(--md-sys-motion-easing-emphasized-decelerate) both}
.acct-suggest[hidden]{display:none}
.acct-suggest-head{display:flex;align-items:center;gap:6px;font:var(--md-sys-typescale-label-large);margin-bottom:4px}
.acct-suggest-row{display:flex;align-items:center;gap:8px;padding:4px 0}
.acct-suggest-row .acct-main{flex:1;min-width:0}
.acct-suggest-row .acct-addr{color:inherit;opacity:.8}
.person-drawer .billing-card{margin-top:12px}
#billNumber{font:inherit;padding:6px 8px;border-radius:var(--md-sys-shape-corner-small);
  border:1px solid var(--md-sys-color-outline);background:var(--md-sys-color-surface-container-highest);
  color:var(--ink);max-width:100%}

/* Message info: a side pane (the .ctx-drawer shell), M3 Expressive cards. */
.info-drawer{border-radius:28px 0 0 28px}
.info-body{display:flex;flex-direction:column;gap:14px;padding:4px 16px 24px;overflow:auto}
.info-card{background:var(--md-sys-color-surface-container-lowest);border-radius:24px;padding:14px 16px;margin:0}
.info-who{display:flex;align-items:center;gap:14px}
.info-av{width:40px;height:40px;border-radius:14px;background:var(--md-sys-color-primary);color:var(--md-sys-color-on-primary);
  display:grid;place-items:center;font-weight:700;flex:none}
.info-av.sm{width:24px;height:24px;border-radius:12px;font-size:12px}
.info-col{display:flex;flex-direction:column}
.info-cap{font-size:12px;letter-spacing:.5px;color:var(--muted);font-weight:500}
.info-name{font-size:17px;font-weight:600}
.info-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px 16px}
.info-cell{display:flex;flex-direction:column}
.info-k{font-size:13px;color:var(--muted)}
.info-k.info-read{color:var(--tick-read);font-weight:600}
.info-t{font-size:15px;font-weight:600}
.info-chip{align-self:flex-start;display:inline-flex;align-items:center;gap:8px;height:32px;padding:0 12px 0 4px;border-radius:16px;
  background:var(--md-sys-color-secondary-container);color:var(--md-sys-color-on-secondary-container);font-size:14px;font-weight:500}
.info-note{margin:0;font-size:13px;color:var(--muted)}
.info-err{color:var(--md-sys-color-error)}
.info-h{margin:6px 4px 0;font-size:14px;letter-spacing:.4px;font-weight:600;color:var(--md-sys-color-primary)}
.info-rx{list-style:none;padding:6px 16px;display:flex;flex-direction:column}
.info-rx li{display:flex;align-items:center;gap:12px;min-height:48px}
.info-rx-e{width:36px;height:36px;border-radius:18px;background:var(--md-sys-color-surface-container-highest);display:grid;place-items:center;font-size:20px;flex:none}
.info-rx-e.ours{background:var(--md-sys-color-secondary-container)}
.info-rx-e.off{background:transparent;border:1.5px dashed var(--md-sys-color-outline);color:var(--md-sys-color-outline)}
.info-rx-t{flex:1;font-size:14px}
.info-rx-t strong{font-weight:600}
.info-rx-at{font-size:13px;color:var(--muted)}
.msg-act.msg-info{border-radius:12px}

/* Settings → Team: a one-time backup sign-in code, shown once. */
.backup-code { font-size: 28px; line-height: 36px; letter-spacing: .12em; font-weight: 600; margin: 12px 0 0; }

/* Template picker: the compose area scrolls inside the dialog (header stays),
   so a long template list and its fields are always reachable. */
#templateModal #tmplCompose,#templateModal #tmplConfirm{flex:1 1 auto;min-height:0;overflow-y:auto;
  display:flex;flex-direction:column}
#templateModal .tmpl-list{overflow:visible;flex:none}
#templateModal #tmplCompose>*,#templateModal #tmplConfirm>*{flex-shrink:0}   /* nothing squeezed: the area scrolls instead */
