/* ============================================================================
   Kylosys Suite — design system
   Values are derived from Kylosys-Suite-UI.pdf. The deck mockup is 1125pt wide
   and its type resolves to a clean 1.28 factor, so the app canvas is 1440px and
   every measurement below is the deck's pt value x 1.28.
   ============================================================================ */

:root{
  /* brand palette (deck p14 "UI kit") */
  --plum:#2a1e3a;
  --pickup:#1e9e64;
  --deep:#0e7a4c;
  --sunny:#ffb938;
  --coral:#f26b5b;
  --oat:#fff6ea;
  --mist:#e9f4ec;
  --blush:#ffd9cc;

  /* surfaces + lines */
  --sand:#f3ede2;
  --cream:#fff6dd;
  --amber:#fff0cc;
  --lilac:#ede6f6;
  --paper:#fffcf6;          /* hover / inset surface */
  --line:#ede3d3;
  --line-2:#cfc4b3;

  /* text */
  --ink:#2a1e3a;
  --muted:#5b5166;
  --muted-2:#9a90a4;
  --nav:#cfc4da;
  --nav-label:#8e82a0;
  --pill:#3d2f50;

  /* semantic */
  --ok:#0e7a4c;
  --ok-soft:#e9f4ec;
  --warn:#7a5200;
  --warn-soft:#fff0cc;
  --bad:#a6352a;
  --bad-soft:#fde3df;

  /* One rise, two sheets. The Terminal's panel and the phone's app drawer both come up
     from the bottom edge, and they move with these three values rather than with two
     copies of them: "the drawer rises the way the Terminal does" is then one definition
     and cannot drift into two. `.term` and the phone block's `.lch__pop` are the two
     readers; the reduced-motion block at the foot of this file replaces the travel with
     a fade for both. */
  --rise-in:280ms cubic-bezier(.2,.8,.2,1);   /* a sheet arriving from below */
  --rise-out:200ms ease-in;                   /* and going back down */
  --veil:200ms ease-in;                       /* the scrim that comes with it */
  --gold:#ffe3a0;

  /* radii — deck p14: 12 / 16 / 20 / 26 / pill */
  --r-1:15.4px;   /* 12 */
  --r-2:20.5px;   /* 16 */
  --r-3:25.6px;   /* 20 */
  --r-4:33.3px;   /* 26 */
  --r-ico:8.5px;
  --r-pill:999px;

  /* layout — the app rail and the context sidebar, at the deck's own widths */
  --rail:76px;
  --side:236px;
  --pad:28px;
  --gap:14.3px;
  /* 64px: the deck's own bar, read off where its controls sit — the search field is
     38px tall at frame-rel y 13.3 and the account badge 36px at y 14, which is a
     centred 64px strip and not a centred 60px one. */
  --topbar:64px;

  --font-display:"Bricolage Grotesque",system-ui,sans-serif;
  --font-body:"DM Sans",system-ui,sans-serif;
  --font-mono:"DM Mono",ui-monospace,monospace;

  --shadow-1:0 1px 2px rgba(42,30,58,.05);
  --shadow-2:0 12px 32px rgba(42,30,58,.14);
}

*,*::before,*::after{box-sizing:border-box}
html,body{margin:0;padding:0}
body{
  font-family:var(--font-body);
  font-size:15.4px;
  line-height:1.45;
  color:var(--ink);
  background:var(--sand);
  -webkit-font-smoothing:antialiased;
}
h1,h2,h3,h4,p,figure,ul,ol{margin:0}
ul,ol{padding:0;list-style:none}
a{color:inherit;text-decoration:none}
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer}
img{max-width:100%;display:block}
:focus-visible{outline:2.5px solid var(--pickup);outline-offset:2px;border-radius:4px}

/* ============================================================================
   Shell — the app switcher
   The deck's frame is 1440px wide, so its design pixels are this app's own CSS
   pixels: a 76px rail of products in #1F1530, a 236px sidebar of the current
   app's pages in #2A1E3A, and the main column beside them. The rail is built by
   nav.js from the app registry, so an app that is switched off takes its own
   tile with it.
   ============================================================================ */
.app{display:grid;grid-template-columns:var(--rail) var(--side) minmax(0,1fr);min-height:100vh}

/* --- the app rail --------------------------------------------------------- */
.navrail{
  background:#1f1530;color:#b9aecb;
  display:flex;flex-direction:column;align-items:center;
  position:sticky;top:0;height:100vh;padding:18px 0;z-index:30;
}
/* The brand is the k mark: 40x40 r12 #FFF6EA holding a 26px green k, which is
   what the deck draws at the head of the rail. The wordmark has no room in a
   76px column and is not what stands there. */
.navrail__brand{
  width:40px;height:40px;border-radius:12px;background:#fff6ea;color:#0e7a4c;
  display:grid;place-items:center;flex:none;
  font-family:var(--font-display);font-weight:700;font-size:26px;line-height:1;
  padding-bottom:4px;
}
/* The list of apps gives up its own height before the foot does. An eighth product was
   added on 2026-09-30 and its tile pushed the launcher, Settings and the account button
   below the fold at a 720px-tall window — where `height:100vh` with `position:sticky`
   leaves nowhere to scroll to. Measured by four `app-workspace` cases timing out on
   `#meSwitch`: the locator resolved, the element was "visible, enabled and stable", and
   the click still failed with "element is outside of the viewport". The tiles keep the
   deck's 70px pitch and 40px box; only a window too short to hold them scrolls. */
.navrail__apps{
  display:flex;flex-direction:column;align-items:center;gap:12.5px;margin-top:22px;width:100%;
  flex:0 1 auto;min-height:0;overflow-y:auto;overflow-x:hidden;scrollbar-width:none;
}
.navrail__apps::-webkit-scrollbar{width:0;height:0}
/* One row per app: a 40x40 tile on a 70px pitch, label under it. The active row
   carries a 4x28 white bar on the rail's own left edge and a white 700 label. */
.navrail__tile{position:relative;width:100%;display:flex;flex-direction:column;align-items:center;gap:7px;color:#b9aecb}
.navrail__ico{
  width:40px;height:40px;border-radius:12px;display:grid;place-items:center;
  color:#fff;opacity:.82;transition:opacity .12s ease;
  box-shadow:0 1px 2px rgba(0,0,0,.2);
}
.navrail__tile:hover .navrail__ico,.navrail__tile:focus-visible .navrail__ico{opacity:1}
.navrail__name{font-size:10.5px;font-weight:500;line-height:1;white-space:nowrap}
.navrail__bar{position:absolute;left:0;top:6px;width:4px;height:28px;border-radius:0 4px 4px 0;background:#fff;opacity:0}
.navrail__tile.is-on .navrail__ico{opacity:1}
.navrail__tile.is-on .navrail__name{color:#fff;font-weight:700}
.navrail__tile.is-on .navrail__bar{opacity:1}
.navrail__tile[hidden]{display:none}
.navrail__tip{
  position:absolute;left:calc(100% + 6px);top:8px;white-space:nowrap;z-index:5;
  background:#2a1e3a;color:var(--oat);font-size:12px;font-weight:600;
  padding:6px 10px;border-radius:8px;box-shadow:var(--shadow-2);
  opacity:0;pointer-events:none;transition:opacity .12s ease;
}
.navrail__tile:hover .navrail__tip{opacity:1}
.navrail__foot{margin-top:auto;display:flex;flex-direction:column;align-items:center;gap:10px}
.navrail__btn{width:40px;height:40px;padding:0;border-radius:12px;display:grid;place-items:center;color:#cfc6dd;flex:none;position:relative}
.navrail__btn:hover,.navrail__btn.is-on{background:#3d2f50;color:var(--oat)}
.navrail__btn--me{width:36px;height:36px}
.navrail__btn--me .av{width:36px;height:36px;font-size:12.4px}
.navrail__dot{position:absolute;right:0;bottom:0;width:13px;height:13px;border-radius:50%;background:#2bc37a;border:2.5px solid #1f1530}
/* The count a rail tile can carry — the ticket queue's, written by mobile.js,
   which used to badge the phone's tab bar: that bar's place is this rail's now. */
.navrail__badge{
  position:absolute;top:-4px;right:14px;min-width:16px;height:16px;padding:0 4px;
  border-radius:var(--r-pill);background:var(--coral);color:#fff;
  font-size:10px;font-weight:700;place-items:center;
}
.navrail__badge:not([hidden]){display:grid}

/* --- the context sidebar -------------------------------------------------- */
.side{
  background:var(--plum);
  display:flex;flex-direction:column;
  position:sticky;top:0;height:100vh;min-width:0;
}
/* Header: the app's own 32px tile, its 19px name, and the 26px chevron button
   that opens the launcher. */
.side__app{display:flex;align-items:center;gap:10px;margin:24px 20px 0}
.side__appico{width:32px;height:32px;border-radius:10px;display:grid;place-items:center;color:#fff;flex:none}
.side__appico svg{display:block}
.side__appname{flex:1;min-width:0;font-size:19px;font-weight:700;color:var(--oat);line-height:1.1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.side__chev{width:26px;height:26px;border-radius:8px;background:#3d2f50;color:var(--oat);display:grid;place-items:center;flex:none}
.side__chev:hover{background:#4b3a64}
/* The divider sits at the deck's own 74px and the first row 15px under it — the
   rows are stacked 40px apart with no gap and no section label, which is what the
   deck draws and what puts Contacts at frame-rel y 89. */
.side__nav{flex:1;overflow-y:auto;padding:15px 14px 0;margin-top:18px;border-top:1px solid rgba(255,255,255,.08)}
.side__nav::-webkit-scrollbar{width:0}
/* A page of the app you are in: 208x40, r12, an 18px icon and a 14.5px label
   42px in from the item's own left edge. */
.nav-item{
  display:flex;align-items:center;gap:12px;
  height:40px;padding:0 12px;border-radius:12px;
  color:#e6dff0;font-size:14.5px;font-weight:500;
}
.nav-item:hover{background:rgba(255,255,255,.06);color:var(--oat)}
.nav-item.is-on{background:var(--pill);color:#fff;font-weight:600}
/* The user-agent's own [hidden]{display:none} loses to any author rule that sets
   display, and .nav-item sets display:flex — so hiding a nav item did nothing until
   this existed. After .is-on, so a screen that is both hidden and current stays
   hidden. */
.nav-item[hidden]{display:none}
.nav-item svg{flex:none;width:18px;height:18px}
.nav-item .n-badge{
  margin-left:auto;font-size:11px;font-weight:700;
  background:var(--mist);color:var(--deep);
  height:18px;min-width:18px;padding:0 6px;
  border-radius:var(--r-pill);display:grid;place-items:center;
}
.nav-item .n-badge--live{background:var(--pickup);color:#fff}

/* The foot: the collapse control, then the workspace switcher — 208x59, r14,
   with the business's own 30px circle and its plan under the name. */
.side__foot{padding:16px 14px 20px;display:flex;flex-direction:column;gap:5px}
.side__collapse{
  display:flex;align-items:center;gap:12px;height:40px;padding:0 12px;border-radius:12px;
  font-size:14.5px;font-weight:500;color:var(--nav-label);text-align:left;
}
.side__collapse svg{flex:none;transform:rotate(180deg)}
.side__collapse:hover{background:rgba(255,255,255,.06);color:#e6dff0}
.side__biz{
  width:100%;height:59px;display:flex;align-items:center;gap:10px;
  background:var(--pill);border-radius:14px;padding:0 12px;
  color:var(--oat);text-align:left;
}
.side__biz:hover{background:#463757}
/* The business's own badge: deep green with its initials, or its logo — the
   switcher is the workspace's, so it does not wear the person's amber. */
.side__biz .av{width:30px;height:30px;font-size:11px;background:var(--deep);color:var(--oat)}
/* The chip's badge is a <span> too — `#bizAv`, the first child of the button — so
   naming every span in here reached it and set `display:block` on it. That beat
   `.av`'s `display:grid` on specificity, 0,1,1 against 0,1,0, so the letters sat at
   the left-hand edge of the circle and a little above centre: measured in a 24px
   badge, 4.76px off across and 4.5px off down, at both widths. The block is named
   now, and the badge keeps the rule that makes it a circle. */
.side__biz__text{display:block;flex:1;min-width:0}
.side__biz__text b{display:block;font-size:14px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.side__biz__text span{display:block;font-size:12px;color:#cfc6dd;margin-top:2px}
.side__biz svg{margin-left:auto;flex:none}

/* --- the launcher --------------------------------------------------------- */
/* A 640x586 popover 16px right of the rail and level with the top bar's foot,
   over a scrim that covers the sidebar and the main column and leaves the rail
   undimmed — the deck's own arrangement. It fades and scales in over 160ms. */
.lch{position:fixed;inset:0;z-index:70}
.lch[hidden]{display:none}
.lch__scrim{position:absolute;left:var(--rail);top:0;right:0;bottom:0;background:rgba(31,21,48,.38)}
/* The popover's own padding is per part rather than on the box: the search field
   and the footer sit 26px in, while the tile grid starts at 21.3px so that its four
   149.3px columns put a 64px tile at popover-rel x 64 — the deck's own grid. */
.lch__pop{
  position:absolute;left:calc(var(--rail) + 16px);top:60px;
  width:640px;max-width:calc(100vw - var(--rail) - 32px);
  background:#fff;border-radius:22px;box-shadow:0 28px 70px rgba(31,21,48,.35);
  padding:22px 0 0;display:flex;flex-direction:column;
  transform:scale(.96);opacity:0;transition:opacity .16s ease-out,transform .16s ease-out;
}
.lch.is-open .lch__pop{transform:none;opacity:1}
/* The popover is focusable so a phone can put the focus on the dialog itself rather than in
   the search field, where it would raise the keyboard on open. It is a container, not a
   control, so it draws no ring: `:focus-visible` does not match a programmatic focus here,
   and this keeps a browser that disagrees from ringing the whole sheet. */
.lch__pop:focus{outline:0}
.lch__head{padding:0 26px}
.lch__field{
  display:flex;align-items:center;gap:12px;height:44px;border-radius:14px;
  background:var(--sand);padding:0 14px;color:var(--nav-label);cursor:text;
}
.lch__field input{flex:1;min-width:0;border:0;outline:0;background:none;font:inherit;font-size:15px;color:var(--ink)}
.lch__body{padding:0 21.3px;overflow:auto;max-height:calc(100vh - var(--topbar) - 150px)}
/* One group fills a row; the two groups that share one are side by side. */
.lch__row{display:grid;grid-template-columns:minmax(0,1fr)}
.lch__row--pair{grid-template-columns:repeat(2,minmax(0,1fr))}
.lch__label{
  font-size:11px;font-weight:700;letter-spacing:.09em;text-transform:uppercase;
  color:var(--nav-label);padding-left:8px;margin:8px 0 12px;line-height:14px;
}
/* The first group's label sits 20px under the search field; the ones after a row of
   tiles sit 8px under it, which is what puts every row on the deck's 150px pitch. */
.lch__body > .lch__row:first-child .lch__label{margin-top:20px}
.lch__group[hidden],.lch__row[hidden]{display:none}
.lch__tiles{display:grid;grid-template-columns:repeat(4,minmax(0,1fr))}
.lch__tiles--2{grid-template-columns:repeat(2,minmax(0,1fr))}
.lch__tile{display:flex;flex-direction:column;align-items:center;text-align:center;padding:0 4px 11px;border-radius:16px}
.lch__tile:hover .lch__ico{filter:brightness(1.05)}
.lch__tile[hidden]{display:none}
.lch__ico{position:relative;width:64px;height:64px;border-radius:18px;display:grid;place-items:center;color:#fff;box-shadow:0 8px 18px rgba(31,21,48,.18)}
.lch__new{
  position:absolute;top:-5px;right:-8px;background:#e4572e;color:#fff;
  font-size:9px;font-weight:700;letter-spacing:.04em;
  border-radius:var(--r-pill);padding:3px 6px;
}
.lch__name{margin-top:8px;font-size:13px;font-weight:600;color:var(--ink);line-height:1.2}
.lch__desc{margin-top:3px;font-size:11px;color:var(--nav-label);line-height:1.3}
.lch__none{padding:26px 16px;text-align:center;color:var(--nav-label);font-size:13.5px}
.lch__none[hidden]{display:none}
.lch__foot{display:flex;align-items:center;gap:12px;padding:14px 26px 17px;border-top:1px solid var(--line);margin-top:8px}
.lch__manage{color:var(--deep);font-size:13.5px;font-weight:600}
.lch__manage:hover{text-decoration:underline}
.lch__esc{margin-left:auto;font-size:13.5px;color:var(--nav-label)}

/* Every control the switcher adds takes the deck's own focus ring: 2px #FFC857,
   which reads on all three of its backgrounds — the rail, the sidebar and the
   white popover. */
.navrail__brand:focus-visible,.navrail__tile:focus-visible,.navrail__btn:focus-visible,
.nav-item:focus-visible,.side__chev:focus-visible,.side__collapse:focus-visible,
.side__biz:focus-visible,.crumb__app:focus-visible,
.lch__tile:focus-visible,.lch__manage:focus-visible{
  outline:2px solid #ffc857;outline-offset:2px;
}
.lch__tile:focus-visible{border-radius:16px}

/* The page is the deck's own cream (#F3EDE2) and the top bar sits on it rather
   than on a white strip of its own: measured at frame-rel y 30, the whole bar is
   #f3ede2 between the breadcrumb, the search field and + New, with no rule under
   it — which is why the border that used to be here is gone. */
.main{display:flex;flex-direction:column;min-width:0;background:var(--sand);min-height:100vh}

/* --- the sidebar as an overlay --------------------------------------------
   Two ways to get here, one set of rules: an app rail is all the navigation a
   window narrower than the deck's own canvas can hold (1200px and below, which
   is where the rail plus the main column stop leaving the sidebar room), and a
   reader who has collapsed it at any width. Either way the sidebar becomes a
   panel beside the rail, a rail tile or the burger peeks it, and the scrim
   covers what it is over — nav.js puts `is-overlay` on `.app`. */
.app.is-overlay{grid-template-columns:var(--rail) minmax(0,1fr)}
/* Folded away with `left`, not `translateX`: a transform makes its element the
   containing block for every `position:fixed` descendant, so anything fixed inside the
   sidebar would be dragged off the screen with it. The account menu is fixed, and this
   is the trap that put it 124px off the left-hand edge of a phone.
   The folded panel is a full 236px off the screen and not merely 76px — parked at
   `--rail - --side` its right-hand 76px sat *under* the rail as a later positioned
   element, which covered the rail's own tiles, launcher, gear and avatar and made four
   buttons unclickable at every width from 1200 down.

   THE DRAWER STARTS UNDER THE TOP BAR, and that is a door rather than a number. Measured with
   the panel open at 900, 1000 and 1199px, `document.elementFromPoint` at the burger's own
   centre -- (109,32) and (121,32) -- answered `#scrim`, whose box began at the rail (76px) and
   so covered the top bar's left end; and once the panel had finished arriving it was `#side`
   itself, a full 100vh. So the one control that opened this drawer could not be pressed to
   close it, and the drawer had exactly one door: its scrim. Both layers now begin at
   `--topbar`, which leaves the bar -- and the burger in it -- above them. The rail was already
   live there for the same reason: the scrim starts at the rail, not at 0. */
.app.is-overlay .side{
  position:fixed;left:calc(0px - var(--side));top:var(--topbar);width:var(--side);
  height:calc(100vh - var(--topbar));z-index:60;
  transition:left .2s ease;
}
.app.is-overlay .side.is-open{left:var(--rail);box-shadow:var(--shadow-2)}
.app.is-overlay .scrim{position:fixed;inset:var(--topbar) 0 0 var(--rail);background:rgba(31,21,48,.44);z-index:50}
.app.is-overlay .scrim[hidden]{display:none}
.app.is-overlay .burger{display:grid}

/* --- the breadcrumb -------------------------------------------------------- */
/* The deck's top bar names where you are: the app at 15px #6B5E80, a slash, and
   the page at 20px 700. #pageTitle stays the page's own name alone — the screens
   write it as they open, and the tab title is built from it. */
.crumb{display:flex;align-items:baseline;gap:8px;min-width:0}
.crumb__app{font-size:15px;font-weight:500;color:#6b5e80;flex:none}
.crumb__app:hover{color:var(--ink)}
.crumb__sep{color:var(--line-2);font-size:15px}

/* The deck's own bar: a 64px strip whose controls are centred in it — the field at
   frame-rel y 13.3 and the account badge at y 14 are a centred 64px bar, not a 60px
   one — with 14.7px between the groups. The deck puts a ⌘K keycap against the field's
   own right edge; it is gone (see index.html) so the field itself ends the group. */
.topbar{
  display:flex;align-items:center;gap:14.7px;
  height:var(--topbar);flex:none;
  padding:0 var(--pad);
  position:sticky;top:0;background:var(--sand);z-index:20;
}
.topbar__title{font-weight:700;font-size:20px;letter-spacing:-.01em;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.topbar__jump{margin-left:auto;display:flex;align-items:center;gap:.5px}
/* + New, at the deck's own 76x38 r19 and drawn as the deck draws it — the plus is
   the character, not an icon, which is what makes the box 76 wide. Scoped to the top
   bar rather than moving every .btn on the site. */
.topbar .btn{height:38px;padding:0 17.75px;font-size:14px;font-weight:500;border-radius:var(--r-pill)}
/* 253.7x37, r18.5 on #FBF8F2 — the deck's own field. The deck's ⌘K keycap beside it was
   removed at the operator's request, so the field's own box is unchanged and the group's
   left edge moved right: measured at 966.1 against the deck's 922.5, 43.6 short of the
   keycap's own 44.6 because + New draws 1.5 wider than the deck says. */
.topbar__search{
  display:flex;align-items:center;gap:9px;
  width:253.7px;height:37px;padding:0 14px;
  background:var(--paper);border:1px solid var(--line);border-radius:var(--r-pill);
  color:var(--muted-2);font-size:14px;
}
.topbar__search span{flex:1;text-align:left;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.topbar__bell{
  position:relative;width:34.6px;height:34.6px;border-radius:var(--r-1);
  display:grid;place-items:center;color:var(--ink);flex:none;
}
.topbar__bell:hover{background:var(--paper)}
.topbar__bell i{
  position:absolute;top:5.5px;right:5.5px;width:8.7px;height:8.7px;
  border-radius:50%;background:var(--coral);
}
.kbd{
  font-family:var(--font-mono);font-size:11px;color:var(--muted-2);
  background:#fff;border:1px solid var(--line);border-radius:8px;
  height:29.4px;padding:0 11px;display:inline-flex;align-items:center;gap:2px;
}
.status{
  display:flex;align-items:center;gap:6.4px;height:27.9px;padding:0 11.5px;
  background:var(--mist);border-radius:var(--r-pill);
  color:var(--deep);font-size:12.5px;font-weight:600;white-space:nowrap;
}
.status i{width:8.7px;height:8.7px;border-radius:50%;background:var(--pickup);flex:none}
/* The account button in the top-right corner. `padding:0` is not decoration: this badge
   shows the business's logo now, and a button keeps the user agent's own `1px 6px` padding,
   which `.av__img` — 100% of the CONTENT box — would be measured against. Measured before
   the fix, with the rule absent: a 34.6px circle holding a 22.59x32.59 picture. The letters
   it showed before sat centred either way, which is why nothing else changes. */
.topbar__me{
  width:36px;height:36px;border-radius:50%;background:var(--deep);padding:0;
  color:var(--oat);font-family:var(--font-display);font-weight:800;font-size:15px;
  display:grid;place-items:center;flex:none;
}

.view{padding:19px var(--pad) 30px}
.view[hidden]{display:none}
.view__head{display:flex;align-items:flex-start;gap:var(--gap);margin-bottom:0}
.view__head h2{font-family:var(--font-display);font-weight:800;font-size:26.9px;line-height:1.1;letter-spacing:-.02em}
.view__sub{color:var(--muted);font-size:15.4px;margin-top:6px}
.view__acts{margin-left:auto;display:flex;gap:8px}

/* ============================================================================
   Buttons
   ============================================================================ */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:8px;
  height:34.6px;padding:0 17.3px;border-radius:var(--r-pill);
  font-size:13.4px;font-weight:700;white-space:nowrap;
  border:1px solid transparent;transition:background .15s,border-color .15s;
}
.btn--primary{background:var(--deep);color:#fff}
.btn--primary:hover{background:#0b6640}
.btn--sec{background:#fff;border-color:var(--line);color:var(--ink)}
.btn--sec:hover{background:var(--paper)}
.btn--quiet{background:transparent;color:var(--muted);padding:0 11.5px}
.btn--quiet:hover{background:var(--paper);color:var(--ink)}
.btn--celebrate{background:var(--sunny);color:var(--plum)}
.btn--celebrate:hover{background:#ffc95f}
.btn--bad{background:var(--coral);color:#fff}
.btn--sm{height:29.7px;padding:0 13px;font-size:12.5px;border-radius:var(--r-pill)}
.btn--xs{height:24px;padding:0 10px;font-size:11.5px}
.btn--ghost{background:var(--paper);border-color:var(--line-2);color:var(--ink)}
.btn--block{width:100%}

/* ============================================================================
   Badges, chips, avatars
   ============================================================================ */
.badge{
  display:inline-flex;align-items:center;gap:5px;
  height:21.1px;padding:0 9.6px;border-radius:var(--r-pill);
  font-size:12px;font-weight:700;
}
.badge--ok{background:var(--ok-soft);color:var(--ok)}
.badge--bad{background:var(--bad-soft);color:var(--bad)}
.badge--warn{background:var(--amber);color:var(--warn)}
.badge--sun{background:var(--sunny);color:var(--plum)}
.badge--live{background:var(--pickup);color:#fff}
.badge--mut{background:var(--sand);color:var(--muted)}
.badge--plum{background:var(--plum);color:var(--oat)}

.av{
  width:28.8px;height:28.8px;border-radius:50%;flex:none;
  display:grid;place-items:center;
  font-family:var(--font-display);font-weight:800;font-size:10.6px;color:var(--plum);
  background:var(--mist);
}
.av--lg{width:36.5px;height:36.5px;font-size:12.8px}
.av--xl{width:57.6px;height:57.6px;font-size:19.2px}
.av--sm{width:24px;height:24px;font-size:9.6px}
.av--a{background:var(--mist)}      .av--b{background:var(--blush)}
.av--c{background:var(--amber)}     .av--d{background:var(--lilac)}
.av--e{background:var(--gold)}      .av--f{background:var(--sand)}
.av--sun{background:var(--sunny)}
/* A logo drawn inside a badge. `inherit` is what keeps it round: the rule above is
   an id-free class on the container, and a picture that only filled the box would be
   a square inside a circle. */
.av__img{width:100%;height:100%;object-fit:cover;border-radius:inherit;display:block}
.av-stack{display:flex}
.av-stack .av+.av{margin-left:-7.5px;box-shadow:0 0 0 2px #fff}

.dot{width:8.7px;height:8.7px;border-radius:50%;flex:none;background:var(--pickup)}
.dot--bad{background:var(--coral)}
.dot--away{background:var(--muted-2)}
.dot--warn{background:var(--sunny)}

/* ============================================================================
   Cards & panels
   ============================================================================ */
.card{
  background:#fff;border:1px solid var(--line);border-radius:var(--r-1);
  padding:14.1px 17.7px;min-width:0;
}
.card--flush{padding:0;overflow:hidden}
.card__head{display:flex;align-items:center;gap:9.3px;margin-bottom:7.9px}
.card__head h3{font-family:var(--font-display);font-weight:800;font-size:16.3px;letter-spacing:-.01em}
.card__head svg{width:11.5px;height:11.5px;color:var(--ink);flex:none}
.card__head .badge{margin-left:auto}
.card__link{
  margin-left:auto;color:var(--ok);font-size:12.5px;font-weight:600;
  display:inline-flex;align-items:center;gap:6px;
}
.card__link:hover{text-decoration:underline}
.card__sub{color:var(--muted-2);font-size:12px}
.card__body{padding:14.1px 17.7px}

.grid{display:grid;gap:var(--gap)}
.grid--4{grid-template-columns:repeat(4,minmax(0,1fr))}
.grid--3{grid-template-columns:repeat(3,minmax(0,1fr))}
.grid--2{grid-template-columns:repeat(2,minmax(0,1fr))}
.stack{display:flex;flex-direction:column;gap:var(--gap)}
.row{display:flex;align-items:center;gap:11.5px}

/* ============================================================================
   Stat tiles
   ============================================================================ */
.stat{
  background:#fff;border:1px solid var(--line);border-radius:var(--r-1);
  padding:13.9px 17.8px;min-height:113.3px;
}
.stat__top{display:flex;align-items:center;gap:9.9px}
.stat__ico{width:36.5px;height:36.5px;border-radius:var(--r-ico);display:grid;place-items:center;flex:none}
.stat__label{color:var(--muted);font-size:13.4px;font-weight:600}
.stat__row{display:flex;align-items:center;gap:9.9px;margin-top:12.5px}
.stat__val{font-family:var(--font-display);font-weight:800;font-size:30.7px;letter-spacing:-.02em;line-height:1}
.stat__hint{color:var(--muted-2);font-size:12px}

/* ============================================================================
   Lists, rows, tables
   ============================================================================ */
.list{display:flex;flex-direction:column}
.list>*+*{border-top:1px solid var(--line)}
.li{display:flex;align-items:center;gap:11.5px;padding:11px 0;min-width:0}
.li--pad{padding:11px 17.7px}
.li:hover{background:var(--paper)}
.li__main{min-width:0;flex:1}
.li__t{font-size:13.4px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.li__s{color:var(--muted-2);font-size:12px;margin-top:2px}
.li__meta{color:var(--muted-2);font-family:var(--font-mono);font-size:12px;flex:none}
.li__end{margin-left:auto;flex:none;display:flex;align-items:center;gap:8px}

.tbl{width:100%;border-collapse:collapse}
.tbl th{
  text-align:left;font-family:var(--font-mono);font-size:10.6px;letter-spacing:.09em;
  color:var(--muted-2);font-weight:400;padding:0 0 10px;border-bottom:1px solid var(--line);
}
.tbl td{padding:12.5px 0;border-bottom:1px solid var(--line);font-size:13.4px;vertical-align:middle}
.tbl tr:hover td{background:var(--paper)}
.tbl .num{font-family:var(--font-mono);font-size:12px;color:var(--muted-2)}
.tbl__t{font-weight:600;color:var(--ink)}
.tbl__t:hover{color:var(--deep);text-decoration:underline}

/* queue / inbox rows */
.q-row{
  display:flex;align-items:center;gap:11.5px;
  padding:9.6px;border-radius:var(--r-1);
  border:1px solid transparent;min-height:55.7px;
}
.q-row--hot{background:var(--cream)}
.q-row--plain{background:var(--paper);border-color:var(--line-2)}
.q-row .li__t{font-size:13.4px}

/* ============================================================================
   Messages / conversation
   ============================================================================ */
.thread{display:flex;flex-direction:column;gap:14px}
.msg{max-width:78%;padding:11.5px 14.1px;border-radius:var(--r-2);font-size:13.9px;line-height:1.5}
.msg--in{background:var(--paper);border:1px solid var(--line);border-bottom-left-radius:6px;align-self:flex-start}
.msg--out{background:var(--plum);color:var(--oat);border-bottom-right-radius:6px;align-self:flex-end}
.msg--note{
  background:var(--amber);border:1px dashed #e0c98a;color:var(--warn);
  border-radius:var(--r-1);align-self:stretch;max-width:100%;font-size:12.5px;
}
.msg__meta{font-size:11.5px;color:var(--muted-2);margin-bottom:4px}
/* One text's own Delete, in the line that already says when it arrived. Always drawn, and
   small: a phone has no hover to reveal it with, so a control that only appeared under a
   pointer would be no control at all at 390px. */
.msg__del{float:right;margin-left:8px;padding:0 4px;border:0;background:none;cursor:pointer;
  font:inherit;font-size:11.5px;color:var(--muted-2);text-decoration:underline}
.msg__del:hover{color:var(--bad)}
.msg:focus-within .msg__del,.msg__del:focus{color:var(--bad)}
.composer{
  border:1px solid var(--line);border-radius:var(--r-2);padding:11.5px 14.1px;
  display:flex;align-items:center;gap:11.5px;background:#fff;
}
.composer input{border:0;outline:0;flex:1;font:inherit;font-size:13.9px;background:none}

/* ============================================================================
   Forms
   ============================================================================ */
.field{display:block}
.field span{display:block;font-size:12px;font-weight:600;color:var(--muted);margin-bottom:6px}
.input,.select,.textarea{
  width:100%;height:36.5px;padding:0 13px;
  background:#fff;border:1px solid var(--line);border-radius:var(--r-1);
  font:inherit;font-size:13.4px;color:var(--ink);
}
.textarea{height:auto;min-height:86px;padding:11.5px 13px;resize:vertical}
.select{appearance:none;background-image:linear-gradient(45deg,transparent 50%,var(--muted-2) 50%),linear-gradient(135deg,var(--muted-2) 50%,transparent 50%);background-position:calc(100% - 17px) 16px,calc(100% - 12px) 16px;background-size:5px 5px;background-repeat:no-repeat}
.input:focus,.select:focus,.textarea:focus{border-color:var(--pickup);outline:0;box-shadow:0 0 0 3px var(--mist)}
.toggle{
  width:41px;height:23px;border-radius:var(--r-pill);background:var(--line);
  position:relative;flex:none;transition:background .15s;
}
.toggle::after{
  content:"";position:absolute;top:2.5px;left:2.5px;width:18px;height:18px;border-radius:50%;
  background:#fff;transition:transform .15s;box-shadow:var(--shadow-1);
}
.toggle[aria-pressed="true"]{background:var(--pickup)}
.toggle[aria-pressed="true"]::after{transform:translateX(18px)}
.seg{display:inline-flex;background:var(--sand);border-radius:var(--r-pill);padding:3px;gap:2px}
.seg button{padding:6.5px 14px;border-radius:var(--r-pill);font-size:12.5px;font-weight:600;color:var(--muted)}
.seg button[aria-pressed="true"]{background:#fff;color:var(--ink);box-shadow:var(--shadow-1)}
.check{display:flex;align-items:center;gap:11.5px;font-size:13.4px}
.check i{
  width:15.4px;height:15.4px;border-radius:50%;border:1.6px solid var(--line-2);flex:none;
}
.check.is-done i{background:var(--deep);border-color:var(--deep);position:relative}
.check.is-done i::after{
  content:"";position:absolute;left:4.4px;top:3.2px;width:4.6px;height:7.6px;
  border:2px solid #fff;border-top:0;border-left:0;transform:rotate(42deg);
}
.check.is-done span{color:var(--muted-2);text-decoration:line-through}

/* ============================================================================
   Multi-pane screens (inbox, ticket, contact, call)
   ============================================================================ */
.view--flush{padding:0;height:calc(100vh - var(--topbar));overflow:hidden}
.view--scroll{padding:19px var(--pad) 30px}
.split{display:grid;height:100%;min-height:0}
.split--inbox{grid-template-columns:296.6px minmax(0,1fr) 297.6px}
.split--2{grid-template-columns:minmax(0,1fr) 341px}
.split--phone{grid-template-columns:minmax(0,1fr) 381px}
.pane{min-width:0;min-height:0;overflow-y:auto;display:flex;flex-direction:column}
/* A pane is the scroller, so a card inside one must not be squashed to fit it. A flex item
   shrinks by default and `.tablecard` hides its own vertical overflow, so a long log had
   nowhere to go: measured at 1440x900 with 60 rows, the card was 566px tall holding 4,042px
   of rows, and a wheel over it moved nothing - not the card, not the pane, not the page.
   `flex:none` gives the card its content's height and lets the pane do the scrolling it was
   already built for. The Helpdesk queue and every Settings pane holding a table are the same
   chain and were clipped the same way. */
.pane > .tablecard{flex:none}
.pane--list{background:var(--paper);border-right:1px solid var(--line)}
.pane--side{background:var(--paper);border-left:1px solid var(--line)}
.pane--plain{background:#fff;border-left:1px solid var(--line)}

/* filter chips */
.chips{display:flex;flex-wrap:wrap;gap:5.5px;padding:15.3px 11.5px 12px}
.chip{
  height:26.9px;padding:0 12.5px;border-radius:10px;background:#f1ece4;
  color:var(--muted);font-size:12px;font-weight:700;
  display:inline-flex;align-items:center;gap:6px;white-space:nowrap;
}
.chip.is-on{background:var(--mist);color:var(--deep)}

/* conversation list */
.conv{padding:0 11.5px 11.5px;display:flex;flex-direction:column;gap:0}
.conv__i{
  display:flex;gap:11.5px;padding:11.5px;border-radius:11.4px;
  align-items:flex-start;cursor:pointer;text-align:left;width:100%;
}
.conv__i:hover{background:#f6f1e8}
.conv__i.is-on{background:var(--mist)}
.conv__i .av{width:38.4px;height:38.4px;font-size:12.5px;margin-top:1px}
.conv__m{min-width:0;flex:1}
.conv__m b{display:block;font-size:13.9px;font-weight:700;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.conv__m span{display:block;color:var(--muted);font-size:12.5px;margin-top:2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.conv__e{display:flex;flex-direction:column;align-items:flex-end;gap:5px;flex:none}
.conv__e time{color:var(--muted-2);font-size:11.5px}
.conv__tags{display:flex;gap:5px;align-items:center}
.conv__ico{color:var(--muted-2);display:flex;gap:4px;align-items:center}
/* Which way a call went, at the left of its row. At 14px the colour is most of the
   signal, so the arrow is drawn in it rather than beside it. Two classes rather than
   one, because the colour is the whole difference and a rule that set both would have
   to be read to be believed. */
.conv__ico.call-dir--in{color:var(--ok)}
.conv__ico.call-dir--out{color:var(--bad)}

/* thread */
.conv-head{
  display:flex;align-items:center;gap:11.5px;flex:none;
  padding:17.3px 23.1px;border-bottom:1px solid var(--line);
}
.conv-head .av{width:42.2px;height:42.2px;font-size:14.4px}
.conv-head h2{font-family:var(--font-display);font-weight:800;font-size:16.3px;line-height:1.1}
.conv-head p{font-family:var(--font-mono);font-size:12px;color:var(--muted);margin-top:3px}
.conv-head .acts{margin-left:auto;display:flex;gap:5.5px}
.conv-body{flex:1;overflow-y:auto;padding:23.1px;display:flex;flex-direction:column;gap:15.4px}
.conv-foot{flex:none;border-top:1px solid var(--line);padding:11.5px 23.1px 17.3px}
.daybar{display:flex;justify-content:center}
.daybar span{
  background:#f1ece4;border-radius:7.8px;height:21.1px;padding:0 11.5px;
  display:inline-flex;align-items:center;font-size:12px;font-weight:600;color:var(--muted);
}
.msg{max-width:409px;padding:12.2px 15.3px;border-radius:15.4px;font-size:13.9px;line-height:1.55}
.msg--in{background:var(--sand);align-self:flex-start;color:var(--ink)}
.msg--out{background:var(--plum);color:var(--oat);align-self:flex-end}
.msg--full{max-width:100%;align-self:stretch}
.msg--rec{
  background:var(--cream);border-radius:13.9px;align-self:stretch;max-width:100%;
  padding:13.4px 15.3px;display:flex;gap:11.5px;align-items:flex-start;
}
.msg--rec .play{
  width:29.7px;height:29.7px;border-radius:50%;background:var(--deep);color:#fff;
  display:grid;place-items:center;flex:none;
}
.msg--note{
  background:var(--amber);border-radius:13.9px;align-self:stretch;max-width:100%;
  padding:11.5px 15.3px;font-size:13px;color:var(--warn);
}
.msg__t{font-size:11.5px;color:var(--muted-2);margin-bottom:5px}
.composer-tabs{display:flex;gap:17.5px;margin-bottom:9.6px}
.composer-tabs button{font-size:12px;font-weight:700;color:var(--muted);display:inline-flex;gap:6px;align-items:center}
.composer-tabs button.is-on{color:var(--deep)}
.composer-row{display:flex;gap:11.5px;align-items:center}
.composer-row .input{height:42.2px;border-radius:15.4px}
.tpl{display:flex;gap:5.5px;margin-top:9.6px;flex-wrap:wrap}
.tpl button{
  font-size:11.5px;font-weight:600;color:var(--muted);background:var(--paper);
  border:1px solid var(--line);border-radius:var(--r-pill);padding:5px 11.5px;
}

/* contact side panel */
.side-head{display:flex;flex-direction:column;align-items:center;text-align:center;padding:19px 17.7px 14.1px}
.side-head .av{width:69.1px;height:69.1px;font-size:23px}
.side-head h3{font-family:var(--font-display);font-weight:800;font-size:20.2px;margin-top:9.6px;line-height:1.1}
.side-head p{color:var(--muted);font-size:13px;margin-top:5px}
.tagrow{display:flex;gap:5.5px;justify-content:center;flex-wrap:wrap;margin-top:11.5px}
.tag{height:21.1px;padding:0 10.5px;border-radius:7.8px;display:inline-flex;align-items:center;font-size:12px;font-weight:700}
.tag--ok{background:var(--mist);color:var(--deep)}
.tag--vip{background:var(--lilac);color:#553c85}
.tag--mut{background:#f1ece4;color:var(--muted)}
.side-sec{padding:0 17.7px 17.7px}
.side-sec h4{
  font-family:var(--font-display);font-weight:800;font-size:14.4px;
  margin:0 0 11.5px;display:flex;align-items:center;gap:8px;
}
.side-card{background:#fff;border:1px solid var(--line);border-radius:15.1px;padding:13.4px 17.7px}
.mini{display:flex;justify-content:space-between;gap:11.5px;font-size:13px;padding:6.9px 0}
.mini dt{color:var(--muted-2)}
.mini dd{margin:0;text-align:right}
.tl{display:flex;flex-direction:column;gap:0}
.tl li{display:flex;gap:9.6px;align-items:center;font-size:12.5px;color:var(--muted);padding:5.5px 0}

/* ============================================================================
   Live call + call log
   ============================================================================ */
.split--live{grid-template-columns:263.7px minmax(0,1fr) 300px}
.rail{background:var(--paper);border-right:1px solid var(--line);padding:17.3px 11.5px;display:flex;flex-direction:column;gap:15.4px}
.rail h3{
  font-family:var(--font-display);font-weight:800;font-size:16.3px;
  padding:0 5.8px;display:flex;align-items:center;gap:8px;
}
.rail h3 .badge{margin-left:auto}
.qcard{
  background:#fff;border:1px solid var(--line);border-radius:var(--r-1);
  padding:11.5px;display:flex;gap:9.6px;align-items:center;
}
.qcard--hot{background:var(--cream)}
.qcard .av{width:34.6px;height:34.6px;font-size:11.5px}
.qcard .li__t{font-size:13.4px}
.linecard{
  background:#fff;border:1px solid var(--line);border-radius:var(--r-1);
  padding:11.5px 13.4px;display:flex;align-items:center;gap:9.6px;
}
.linecard b{font-size:13.4px;display:block}
.linecard small{display:block;font-family:var(--font-mono);font-size:11.5px;color:var(--muted-2);margin-top:2px}
.teamrow{display:flex;align-items:center;gap:9.6px;padding:7.5px 5.8px}
.teamrow .av{width:24px;height:24px;font-size:9.6px}
.teamrow b{font-size:13.4px;font-weight:600;flex:1}

.livecard{
  background:var(--plum);color:var(--oat);border-radius:var(--r-2);
  padding:23.1px;display:flex;flex-direction:column;gap:17.3px;
}
.livecard__t{font-family:var(--font-mono);font-size:12px;letter-spacing:.08em;color:var(--nav)}
.livecard__clock{font-family:var(--font-display);font-weight:800;font-size:42.2px;color:var(--sunny);line-height:1}
.livecard__who{display:flex;align-items:center;gap:17.3px}
.livecard__who h2{font-family:var(--font-display);font-weight:800;font-size:32.6px;color:var(--oat);line-height:1.05}
.livecard__who p{color:var(--nav);font-size:15.4px;margin-top:5px}
.livecard__who .av{width:57.6px;height:57.6px;font-size:19.2px}
.ctl{display:flex;flex-wrap:wrap;gap:9.6px}
.ctl button{
  display:flex;flex-direction:column;align-items:center;gap:6px;
  background:rgba(255,255,255,.1);color:var(--oat);border-radius:15.4px;
  padding:11.5px 13.4px;min-width:69.1px;font-size:12px;font-weight:600;
}
.ctl button:hover{background:rgba(255,255,255,.2)}
.ctl button.is-end{background:var(--coral);color:#fff}
.tr{display:flex;gap:9.6px;align-items:flex-start;padding:6.9px 0;font-size:13.4px}
.tr time{font-family:var(--font-mono);font-size:11px;color:var(--muted-2);width:34.6px;flex:none;padding-top:2px}
.tr b{flex:none;width:34.6px}
.tr--jo b{color:var(--ok)}
.tr--dev b{color:#7b5ea7}
.notes li{display:flex;gap:9.6px;font-size:13.4px;padding:6.9px 0}
.notes li::before{
  content:"";width:5.8px;height:5.8px;border-radius:50%;
  background:var(--line-2);margin-top:8px;flex:none;
}
/* The number is an <input> in the live page, not the <div> the copies in
   `_backup/_suite/` still ship, so it needs the house field's box drawn for it.
   Without one the browser draws its own: no radius, 2px of padding and the user
   agent's border, square, beside a keypad of 15.4px-rounded keys. Its height is
   the keypad's own so the field and the keys sit on one rhythm. */
.dialnum{
  width:100%;height:52.8px;padding:0 15.4px;
  background:#fff;border:1px solid var(--line);border-radius:var(--r-1);
  font-family:var(--font-mono);font-weight:500;font-size:25px;
  text-align:right;letter-spacing:.02em;color:var(--ink);
}
.dialnum:focus{border-color:var(--pickup);outline:0;box-shadow:0 0 0 3px var(--mist)}
/* The hint is not a number, so it gets neither the number's size nor its right
   edge: at 25px, right-aligned, "The number to call" was a clipped shout. */
.dialnum::placeholder{color:var(--muted-2);font-size:15.4px;letter-spacing:0;text-align:left}
.pane--dialer{background:var(--paper);border-left:1px solid var(--line);padding:17.3px 23.1px;gap:17.3px}
.vmcard{background:#fff;border:1px solid var(--line);border-radius:var(--r-1);padding:13.4px}
.vmcard b{font-size:13px;display:block}
.vmcard p{font-size:12px;color:var(--muted);margin-top:5px}

/* ============================================================================
   Tables with toolbars (contacts, tickets, calls)
   ============================================================================ */
.toolbar{display:flex;align-items:center;gap:8px;margin-bottom:14.1px;flex-wrap:wrap}
.toolbar .input{width:236px;height:34.6px}
.toolbar .spacer{margin-left:auto}
.tablecard{background:#fff;border:1px solid var(--line);border-radius:var(--r-1);
  overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch}
.tablecard .tbl th{padding:13.4px 17.7px;background:var(--paper)}
.tablecard .tbl td{padding:13.4px 17.7px}
.tablecard .tbl th:first-child,.tablecard .tbl td:first-child{padding-left:23.1px}
.tablecard .tbl th:last-child,.tablecard .tbl td:last-child{padding-right:23.1px}
.tablecard .tbl tr:last-child td{border-bottom:0}
.cellwho{display:flex;align-items:center;gap:11.5px}
.cellwho b{font-weight:700}

/* bulk action bar + pager */
.bulkbar{
  background:var(--plum);color:var(--oat);border-radius:var(--r-1);
  display:flex;align-items:center;gap:17.3px;padding:11.5px 17.7px;font-size:13.4px;
  flex-wrap:wrap;
}
.bulkbar b{font-weight:700}
.bulkbar button{
  color:var(--oat);font-size:13.4px;display:inline-flex;gap:6px;align-items:center;opacity:.92;
}
.bulkbar button:hover{opacity:1;text-decoration:underline}
.bulkbar .spacer{margin-left:auto}
.pager{display:flex;align-items:center;justify-content:space-between;color:var(--muted);font-size:12.5px;padding:14.1px 0 0}
.pager div{display:flex;gap:11.5px}

/* contact profile */
.profile-head{display:flex;align-items:flex-start;gap:17.3px;flex-wrap:wrap}
.profile-head .av{width:55.7px;height:55.7px;font-size:22px}
.profile-head h2{font-family:var(--font-display);font-weight:800;font-size:30.7px;line-height:1.05}
.profile-head .sub{color:var(--muted);font-size:15.4px;margin-top:6px}
.tl2 li{display:flex;gap:14.1px;padding:14.1px 0;border-top:1px solid var(--line)}
.tl2 li:first-child{border-top:0;padding-top:6px}
.tl2__i{
  width:29.7px;height:29.7px;border-radius:50%;background:var(--mist);color:var(--deep);
  display:grid;place-items:center;flex:none;
}
.tl2__b{flex:1;min-width:0}
.tl2__h{display:flex;align-items:baseline;gap:11.5px;flex-wrap:wrap}
.tl2__h b{font-size:13.9px}
.tl2__h time{margin-left:auto;font-size:12px;color:var(--muted-2);font-family:var(--font-mono)}
.tl2__b p{font-size:13px;color:var(--muted);margin-top:5px}
.tl2__m{
  margin-top:9.6px;font-size:12px;font-weight:700;color:var(--muted);
  display:inline-flex;gap:6px;align-items:center;
}
.tl2__m:hover{color:var(--ink)}
.note-box{
  border:1px solid var(--line);border-radius:var(--r-2);
  padding:12.5px 15.3px;display:flex;align-items:center;gap:11.5px;flex-wrap:wrap;
}
.note-box input{border:0;outline:0;flex:1;min-width:200px;font:inherit;font-size:13.9px;background:none}
.tabs2{display:flex;gap:17.3px;border-bottom:1px solid var(--line);margin:17.3px 0 0;flex-wrap:wrap}
.tabs2 button{padding:0 0 11.5px;font-size:13.4px;font-weight:600;color:var(--muted-2);border-bottom:2px solid transparent}
.tabs2 button.is-on{color:var(--ink);border-bottom-color:var(--plum)}

/* ============================================================================
   Kanban (deals)
   ============================================================================ */
.kanban{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:14.4px;align-items:start}
.kcol{background:var(--paper);border:1px solid var(--line);border-radius:var(--r-1);padding:12.5px}
.kcol__h{display:flex;align-items:center;gap:8px;margin-bottom:11.5px}
.kcol__h b{font-family:var(--font-display);font-size:14.4px;font-weight:800}
.kcol__h span{color:var(--muted-2);font-family:var(--font-mono);font-size:12px}
.kcol__h em{margin-left:auto;font-style:normal;font-family:var(--font-mono);font-size:12px;color:var(--muted)}
.kcard{background:#fff;border:1px solid var(--line);border-radius:var(--r-1);padding:12.5px;margin-bottom:9.6px}
.kcard b{display:block;font-size:13.9px;font-weight:700}
.kcard .amt{font-family:var(--font-display);font-weight:800;font-size:16.3px;margin-top:6px}
.kcard__h{display:flex;align-items:center;gap:8px}
.kcard__h b{flex:1;font-size:13.9px}
.kcard__s{font-size:12.5px;color:var(--muted);margin-top:4px}
.kcard__r{display:flex;align-items:baseline;gap:8px;margin-top:9.6px}
.kcard__r b{font-family:var(--font-display);font-weight:800;font-size:17.3px}
.kcard__r span{margin-left:auto;font-size:12px;color:var(--muted)}
.kcard__chip{
  display:inline-flex;margin-top:9.6px;font-size:12px;font-weight:600;
  color:var(--deep);background:var(--mist);border-radius:var(--r-pill);padding:4px 10.5px;
}
.kcard__won{color:var(--ok);font-size:12px;font-weight:700;display:inline-flex;gap:5px;align-items:center}
.kadd{font-size:12.5px;font-weight:600;color:var(--muted-2);padding:9.6px;width:100%;text-align:left;border-radius:11.4px}
.kadd:hover{background:#fff;color:var(--ink)}

/* progress + horizontal bars */
.bar{background:var(--sand);border-radius:var(--r-pill);overflow:hidden;display:block;height:9.6px}
.bar i{display:block;height:100%;background:var(--pickup);border-radius:var(--r-pill)}
.bar--a i{background:#8a6fb8}
.hbar__r > span:first-child{display:flex;flex-direction:column;gap:2px}

/* ticket detail */
.thead{display:flex;align-items:center;gap:11.5px;flex-wrap:wrap}
.thead h1{font-family:var(--font-display);font-weight:800;font-size:25px;line-height:1.15;margin:11.5px 0 0}
.thread{overflow-y:auto;padding:6px 0 14.1px}
.tmsg{display:flex;gap:17.3px;padding:14.1px 0}
.tmsg>.av{width:21px;height:21px;font-size:8.6px;margin-top:3px}
.tmsg__b{font-size:13.9px;line-height:1.65;margin-top:6px}
.tmsg__h{font-size:15.4px;font-weight:700}
.tmsg__h span{font-size:12.5px;color:var(--muted-2);font-weight:400}
.tmsg--note{margin-left:15.4px;padding-left:23.1px;border-left:2px solid var(--amber)}
.tmsg--note .tmsg__h span{color:var(--warn)}
.rec{background:var(--cream);border-radius:13.9px;padding:9.6px 15.3px;margin-top:9.6px;font-size:13px}
.composer2{border:1px solid var(--line);border-radius:var(--r-2);padding:12.5px 17.7px;flex:none;background:#fff}
.composer2__tabs{display:flex;gap:17.3px;font-size:12px;font-weight:700;flex-wrap:wrap}
.composer2__tabs button{color:var(--muted)}
.composer2__tabs button.is-on{color:var(--deep)}
.composer2__tabs .r{margin-left:auto;display:flex;gap:17.3px}
.composer2 textarea{
  border:0;outline:0;width:100%;font:inherit;font-size:13.9px;line-height:1.6;
  resize:none;margin-top:11.5px;background:none;color:var(--ink);height:61px;
}
.prop{display:flex;justify-content:space-between;align-items:center;gap:11.5px;padding:9.6px 0;border-bottom:1px solid var(--line)}
.prop:last-child{border-bottom:0}
.prop dt{font-size:11.5px;color:var(--muted-2)}
.prop dd{margin:0;font-size:13px;font-weight:600;display:flex;align-items:center;gap:6px}
.sla{display:flex;justify-content:space-between;font-size:12.5px;padding:6px 0}
.sla b{font-family:var(--font-mono);font-weight:400}

/* reports */
.chartcard{background:#fff;border:1px solid var(--line);border-radius:var(--r-1);padding:17.7px}
.chartcard h3{font-family:var(--font-display);font-weight:800;font-size:16.3px}
.lb{display:flex;align-items:center;gap:11.5px;padding:9.6px 0;border-bottom:1px solid var(--line);font-size:13.4px}
.lb:last-child{border-bottom:0}
.lb .rk{font-family:var(--font-mono);font-size:12px;color:var(--muted-2);width:20px}
.lb .av{width:25px;height:25px;font-size:9.6px}
.lb b{margin-left:auto;font-family:var(--font-display);font-size:15.4px}
.chart{display:flex;gap:11.5px;margin-top:17.3px}
.chart__y{
  display:flex;flex-direction:column;justify-content:space-between;
  font-size:11.6px;color:var(--muted-2);font-family:var(--font-mono);
  padding-bottom:22px;text-align:right;flex:none;
}
.bars{flex:1}
.bars .pair{flex:1;display:flex;align-items:flex-end;gap:3px;width:100%}
.bars .pair i{flex:1}
.frail{background:var(--paper);border-right:1px solid var(--line);padding:17.3px 11.5px;display:flex;flex-direction:column}
.frail__g{
  font-family:var(--font-mono);font-size:10.6px;color:var(--nav-label);
  letter-spacing:.09em;padding:13.4px 11.5px 6px;
}
.frail button{
  display:flex;align-items:center;gap:8px;padding:7.5px 11.5px;border-radius:10px;
  font-size:13.4px;font-weight:600;color:var(--muted);width:100%;text-align:left;
}
.frail button b{margin-left:auto;font-family:var(--font-mono);font-size:12px;font-weight:400;color:var(--muted-2)}
.frail button:hover{background:#f6f1e8}
.frail button.is-on{background:var(--mist);color:var(--deep)}
.frail__stats{margin-top:23.1px;background:#fff;border:1px solid var(--line);border-radius:var(--r-1);padding:13.4px}
.frail__stats .mini{padding:4px 0}
/* The settings rail and the settings pane share one screen, and the pages in the
   pane are cards with no outer spacing of their own — so the pane carries the
   gutter. 17.3px on top lines the first card up with the rail's own heading. */
#settingsPane{padding:17.3px var(--pad) 30px}
.prio{display:inline-flex;align-items:center;gap:5px;font-weight:700;font-size:12px}
.prio--urgent{color:var(--bad)} .prio--high{color:var(--warn)}
.prio--normal{color:var(--muted)} .prio--low{color:var(--muted-2)}
.via{display:inline-flex;align-items:center;gap:5px;color:var(--muted-2);font-size:12px}

/* ============================================================================
   Phone / dialer
   ============================================================================ */
.dialpad{display:grid;grid-template-columns:repeat(3,1fr);gap:9.6px;max-width:264px}
.dialpad button{
  height:52.8px;border-radius:15.4px;background:var(--paper);border:1px solid var(--line);
  font-family:var(--font-display);font-size:19.2px;font-weight:800;
}
.dialpad button:hover{background:var(--mist)}
.dialpad button small{display:block;font-family:var(--font-body);font-size:9.6px;font-weight:600;color:var(--muted-2);letter-spacing:.1em}
.callcard{
  background:var(--plum);color:var(--oat);border-radius:var(--r-2);
  padding:23.1px;display:flex;flex-direction:column;gap:17.3px;
}
.callcard .av{width:69.1px;height:69.1px;font-size:23px}
.wave{display:flex;align-items:center;gap:3px;height:34.6px}
.wave i{width:3px;border-radius:2px;background:var(--pickup);height:30%}
.wave i:nth-child(3n){height:70%}
.wave i:nth-child(3n+1){height:45%}
.wave i:nth-child(4n){height:100%}
.wave i:nth-child(5n){height:60%}
.callctl{display:flex;gap:11.5px}
.callctl button{
  width:46.1px;height:46.1px;border-radius:50%;background:rgba(255,255,255,.12);
  color:var(--oat);display:grid;place-items:center;
}
.callctl button:hover{background:rgba(255,255,255,.2)}
.callctl button.is-end{background:var(--coral);color:#fff;width:auto;padding:0 20.5px;border-radius:var(--r-pill);gap:8px}

/* ============================================================================
   Charts (reports)
   ============================================================================ */
.bars{display:flex;align-items:flex-end;gap:8px;height:168px}
.bars>div{flex:1;display:flex;flex-direction:column;justify-content:flex-end;align-items:center;gap:6px;height:100%}
.bars i{width:100%;border-radius:6px 6px 0 0;background:var(--mist);display:block}
.bars i.g{background:var(--pickup)}
.bars i.p{background:#8a6fb8}
.bars i.a{background:var(--sunny)}
.bars small{font-size:10.6px;color:var(--muted-2);font-family:var(--font-mono)}
.legend{display:flex;gap:14.4px;flex-wrap:wrap;margin-top:11.5px}
.legend span{display:inline-flex;align-items:center;gap:6px;font-size:12px;color:var(--muted)}
.legend i{width:9.6px;height:9.6px;border-radius:3px}
.donut{width:132px;height:132px;border-radius:50%;flex:none;position:relative}
.donut::after{content:"";position:absolute;inset:26px;border-radius:50%;background:#fff}
.hbar{display:flex;flex-direction:column;gap:11.5px}
.hbar__r{display:grid;grid-template-columns:110px minmax(0,1fr) 48px;gap:11.5px;align-items:center;font-size:12.5px}
.hbar__r .bar{height:9.6px}
.hbar__r b{text-align:right;font-family:var(--font-mono);font-size:12px}

/* ============================================================================
   Flow builder (settings)
   ============================================================================ */
.flow{display:flex;flex-direction:column;gap:0;align-items:flex-start}
.node{
  background:#fff;border:1px solid var(--line);border-radius:var(--r-1);
  padding:12.5px 15.3px;min-width:264px;position:relative;
}
.node b{font-size:13.9px;font-weight:700;display:block}
.node small{display:block;color:var(--muted-2);font-size:11.5px;margin-top:3px}
.node--start{border-color:var(--pickup);background:var(--mist)}
.node--end{background:var(--plum);color:var(--oat);border-color:var(--plum)}
.node--end small{color:var(--nav)}
.connector{width:2px;height:19.2px;background:var(--line-2);margin-left:29.7px}
.branch{margin-left:29.7px;padding-left:23.1px;border-left:2px dashed var(--line-2);display:flex;flex-direction:column;gap:11.5px;margin-top:11.5px}
.branch__lbl{font-family:var(--font-mono);font-size:11.5px;color:var(--muted-2)}
.flowcanvas{padding:17.3px 23.1px;overflow-y:auto}
.flowrow{display:grid;grid-template-columns:minmax(0,1fr) 300px;gap:23.1px;align-items:start;width:100%}
.noderow{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:14.4px;width:100%}
.node--c{align-items:center;text-align:center}
.node--c b,.node--c small{text-align:center}
.node__h{display:flex;align-items:center;justify-content:space-between;gap:11.5px}
.node__h small{margin:0}
.tipbox{
  background:var(--amber);border-radius:var(--r-1);padding:13.4px;
  font-size:13px;color:var(--warn);margin-top:17.3px;
}
.tipbox b{font-weight:700}
.toggle{
  width:36.5px;height:21.1px;border-radius:var(--r-pill);background:var(--line-2);
  position:relative;flex:none;transition:background .15s;
}
.toggle::after{
  content:"";position:absolute;top:2.4px;left:2.4px;width:16.3px;height:16.3px;
  border-radius:50%;background:#fff;transition:transform .15s;
}
/* This block is the one that wins — it comes later than the other .toggle
   definition, so its geometry is what is on screen. Both state markers are
   honoured here because the two halves of the app disagree about which one
   they set: the router writes aria-pressed, and screens that predate it write
   is-on. Keying only one of them left the knob sitting still. */
.toggle.is-on,.toggle[aria-pressed="true"]{background:var(--pickup)}
.toggle.is-on::after,.toggle[aria-pressed="true"]::after{transform:translateX(15.4px)}
.avmini{display:inline-flex;align-items:center;gap:5px;background:var(--mist);color:var(--deep);
  border-radius:var(--r-pill);padding:2px 8px;font-size:12px;font-weight:700}
.avmini i{width:15.4px;height:15.4px;border-radius:50%;background:var(--sunny);color:var(--plum);
  display:grid;place-items:center;font-size:7.7px;font-style:normal;font-weight:800}

/* ============================================================================
   Mobile frames (screen 13)
   ============================================================================ */
.phones{display:flex;gap:34.6px;justify-content:center;align-items:flex-start;flex-wrap:wrap}
.phone{
  width:264px;background:var(--plum);border-radius:33.3px;padding:9.6px;
  box-shadow:var(--shadow-2);flex:none;
}
.phone__s{background:#fff;border-radius:25.6px;overflow:hidden;height:558px;display:flex;flex-direction:column}
.phone__bar{height:29.7px;display:flex;align-items:center;justify-content:center;flex:none}
.phone__bar i{width:69.1px;height:5.8px;border-radius:3px;background:rgba(255,255,255,.35)}
.phone__body{flex:1;overflow:hidden;padding:0}
.phone__nav{display:flex;justify-content:space-around;padding:9.6px 0;border-top:1px solid var(--line)}
.phone__top{flex:1;overflow:hidden;padding:14.1px}
.pstat{
  display:flex;justify-content:space-between;align-items:center;
  padding:8px 14.1px 4px;font-size:12px;font-weight:700;flex:none;
}
.pstat span{letter-spacing:.06em;font-size:10.6px}
.phone__nav{font-size:10.6px;font-weight:700;color:var(--muted-2)}
.phone__nav b{color:var(--ink)}
.phone + .mcap,.mcap{text-align:center;font-family:var(--font-display);font-weight:800;font-size:16.3px;margin-top:17.3px}
.mstat{display:block;font-size:11.5px;color:var(--muted);font-weight:600}
.mstat + b{font-family:var(--font-display);font-size:23px;font-weight:800;display:block;margin-top:2px}
.mcard{
  background:var(--paper);border:1px solid var(--line);border-radius:11.4px;
  padding:8px 11.5px;font-size:12.5px;margin-top:8px;line-height:1.45;
}
.mlist>*{
  display:flex;gap:8px;align-items:center;padding:8px 0;
  border-bottom:1px solid var(--line);font-size:12.5px;
}
.mlist>*:last-child{border-bottom:0}
.mlist .m{flex:1;min-width:0}
.mlist b{display:block;font-size:13px;font-weight:700}
.mlist .m span{display:block;color:var(--muted);font-size:11.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.mlist time{color:var(--muted-2);font-size:10.6px}
.mlist .av{width:24px;height:24px;font-size:9.6px}
.mphone-head{display:flex;align-items:center;gap:8px;font-size:11.5px;padding:12.5px 14.1px;border-bottom:1px solid var(--line)}
.mphone-head .mono{color:var(--muted-2)}

/* ============================================================================
   UI kit (screen 14)
   ============================================================================ */
.kitgrid{display:grid;gap:14.4px}
.kitbox{background:#fff;border:1px solid var(--line);border-radius:var(--r-1);padding:17.7px}
.kitbox h4{
  font-family:var(--font-display);font-size:16.3px;font-weight:800;
  color:var(--ink);margin-bottom:12.5px;letter-spacing:0;
}
.stack{display:flex;flex-direction:column;gap:14.4px}
.iconrow{display:grid;grid-template-columns:repeat(6,minmax(0,1fr));gap:11.5px}
.iconrow span{
  display:flex;flex-direction:column;align-items:center;gap:6px;
  font-size:10.6px;color:var(--muted-2);border:1px solid var(--line);
  border-radius:11.4px;padding:11.5px 6px;
}
.iconrow svg{width:16px;height:16px;color:var(--ink)}
.swatches{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:11.5px}
.swatch{border-radius:11.4px;overflow:hidden;border:1px solid var(--line)}
.swatch i{display:block;height:52.8px}
.swatch b{display:block;padding:8px 9.6px;font-size:11.5px}
.swatch small{display:block;padding:0 9.6px 9.6px;font-family:var(--font-mono);font-size:10.6px;color:var(--muted-2)}
.btn--celebrate{background:var(--sunny);color:var(--plum)}
.btn--end{background:var(--coral);color:#fff}
.rbox{
  display:inline-flex;align-items:center;justify-content:center;background:var(--paper);
  border:1px solid var(--line);font-family:var(--font-mono);font-size:11.5px;
  color:var(--muted);width:61px;height:44.2px;
}
.rbox--1{border-radius:12px} .rbox--2{border-radius:16px}
.rbox--3{border-radius:20px} .rbox--4{border-radius:26px}
.rbox--p{border-radius:999px;width:100px}
.kitline{display:flex;align-items:center;gap:17.3px;flex-wrap:wrap;font-size:13.4px;color:var(--muted)}
.selcard{border:2px solid var(--pickup);background:var(--mist);border-radius:var(--r-1);padding:11.5px 15.3px;font-size:13.4px}
.emptycard{border:1px dashed var(--line-2);border-radius:var(--r-1);padding:17.3px;text-align:center;color:var(--muted-2);font-size:13px}
.kv{display:flex;justify-content:space-between;gap:14px;font-size:13.4px;padding:7.5px 0}
.kv dt{color:var(--muted-2)}
.kv dd{margin:0;font-weight:600;text-align:right}
.note{background:var(--mist);border-radius:var(--r-1);padding:11.5px 14.1px;font-size:12.5px;color:var(--deep)}
.warnbox{background:var(--bad-soft);border-radius:var(--r-1);padding:11.5px 14.1px;font-size:12.5px;color:var(--bad)}
.tabs{display:flex;gap:20px;border-bottom:1px solid var(--line);margin-bottom:14px}
.tabs a{padding:0 0 11.5px;font-size:13.4px;font-weight:600;color:var(--muted-2);border-bottom:2px solid transparent}
.tabs a.is-on{color:var(--ink);border-bottom-color:var(--plum)}
.avatar-lg{width:57.6px;height:57.6px;border-radius:50%;display:grid;place-items:center;font-family:var(--font-display);font-weight:800;font-size:19.2px;color:var(--plum);background:var(--amber);flex:none}
.empty{text-align:center;padding:40px 20px;color:var(--muted-2)}
.empty b{display:block;color:var(--ink);font-family:var(--font-display);font-size:16.3px;margin-bottom:6px}
.bar{height:7px;border-radius:var(--r-pill);background:var(--sand);overflow:hidden}
.bar i{display:block;height:100%;border-radius:var(--r-pill);background:var(--pickup)}
.spark{display:flex;align-items:flex-end;gap:6px;height:52px}
.spark i{flex:1;background:var(--mist);border-radius:4px 4px 0 0;min-height:4px}
.spark i.is-on{background:var(--pickup)}

/* ============================================================================
   Responsive — the deck's canvas is 1440; below that we fold the sidebar and
   let dense grids collapse so nothing ever scrolls sideways.
   ============================================================================ */
@media (max-width:1180px){
  .grid--4{grid-template-columns:repeat(2,minmax(0,1fr))}
}
/* The app switcher's own folding is in `.app.is-overlay` above, which nav.js sets
   at this width and at any width the reader has collapsed the sidebar at — one set
   of rules rather than a second copy in a media block. */
@media (max-width:980px){
  :root{--pad:16px}
  .grid--2,.grid--3{grid-template-columns:minmax(0,1fr)}
  .topbar__search{width:auto;flex:1;min-width:0}
  .topbar__search span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .status span{display:none}
}
@media (max-width:620px){
  .grid--4{grid-template-columns:minmax(0,1fr)}
  .view__head{flex-direction:column;align-items:stretch}
  .view__acts{margin-left:0}
  .topbar{gap:8px}
  .hide-sm{display:none!important}
}
.burger{
  display:none;width:34.6px;height:34.6px;border-radius:var(--r-1);
  place-items:center;border:1px solid var(--line);flex:none;
}

/* ============================================================================
   Command palette + popover menus
   ============================================================================ */
.pal{
  position:fixed;inset:0;z-index:80;background:rgba(42,30,58,.34);
  display:flex;justify-content:center;align-items:flex-start;padding:88px 16px 16px;
}
.pal[hidden]{display:none}
.pal__box{
  width:min(560px,100%);background:#fff;border-radius:var(--r-2);
  box-shadow:var(--shadow-2);overflow:hidden;
}
.pal__field{
  display:flex;align-items:center;gap:11.5px;padding:14.1px 17.7px;
  border-bottom:1px solid var(--line);color:var(--muted-2);
}
.pal__field input{flex:1;border:0;outline:0;font:inherit;font-size:15.4px;color:var(--ink);background:none}
.pal__list{max-height:340px;overflow:auto;padding:6px}
.pal__list button{
  width:100%;display:flex;align-items:center;gap:11.5px;
  padding:10.5px 12.8px;border-radius:var(--r-1);
  font-size:13.9px;font-weight:600;text-align:left;color:var(--ink);
}
.pal__list button svg{margin-left:auto;color:var(--muted-2)}
/* Which app a palette entry belongs to, now that ⌘K reaches every app's pages. */
.pal__app{margin-left:auto;color:var(--nav-label);font-size:11.5px;font-weight:600}
.pal__app + svg{margin-left:8px}
.pal__list button:hover{background:var(--paper)}
.pal__none{padding:16px;color:var(--muted-2);font-size:13.4px;text-align:center}

.menu{
  position:fixed;z-index:90;min-width:236px;background:#fff;
  border:1px solid var(--line);border-radius:var(--r-2);
  box-shadow:var(--shadow-2);padding:6px;overflow:hidden;
}
.menu button{
  width:100%;display:flex;align-items:center;gap:10.5px;
  padding:9.6px 11.5px;border-radius:var(--r-1);text-align:left;
  font-size:13.4px;font-weight:600;color:var(--ink);
}
.menu button:hover{background:var(--paper)}
.menu button.is-on{background:var(--mist);color:var(--deep)}
.menu button small{display:block;font-weight:400;font-size:11.5px;color:var(--muted-2)}
.menu button svg:last-child{margin-left:auto;color:var(--deep)}
.menu__foot{
  border-top:1px solid var(--line);margin-top:6px;padding-top:11.5px!important;
  color:var(--deep)!important;
}


/* ============================================================================
   Account menu (real session from /api/auth/me)
   ============================================================================ */
/* Pinned to the rail's own 15.4px gutters rather than sized off --side. That coupling
   was fine while every width's --side was a real column width, and wrong as soon as one
   width set --side:0px: calc(0px - 30.8px) is a negative width, and what a browser makes
   of it is the padding — a 16px-wide menu, measured, inside a 264px drawer. The two
   forms agree at every other width (215px rail: 184.2px either way). */
/* The account menu is anchored to the avatar on the app rail, in the viewport's own
   coordinates, because that is where the button that opens it lives: beside the rail at
   the foot of a desktop window, and above the bar at the foot of a phone. */
.acct{
  position:fixed;left:calc(var(--rail) + 16px);bottom:24px;width:236px;
  background:#fff;border-radius:var(--r-2);
  box-shadow:var(--shadow-2);padding:8px;z-index:70;
}
.acct[hidden]{display:none}
.acct__head{padding:8px 10px 11px;border-bottom:1px solid var(--line);margin-bottom:6px}
.acct__head b{display:block;font-size:13.4px}
.acct__head span{display:block;font-size:11.4px;color:var(--muted);margin-top:3px;word-break:break-all;line-height:1.35}
.acct__item{
  display:block;width:100%;padding:9px 10px;border-radius:var(--r-1);
  font-size:13.4px;font-weight:600;color:var(--ink);text-align:left;
}
.acct__item:hover{background:var(--paper)}
.acct__item--bad{color:var(--bad)}
/* Same reason as .nav-item[hidden]: .acct__item sets display:block, which outranks the
   user agent's [hidden] rule. This is what keeps the operator console out of a
   customer's menu rather than merely marked hidden. */
.acct__item[hidden]{display:none}

/* ============================================================================
   Empty states — what a real customer sees before they have any data
   ============================================================================ */
.ky-empty{
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  text-align:center;padding:44px 24px;gap:5px;
}
.ky-empty__mark{
  width:46px;height:46px;border-radius:var(--r-2);
  background:var(--mist);color:var(--deep);
  display:flex;align-items:center;justify-content:center;margin-bottom:9px;
}
.ky-empty__mark svg{width:20px;height:20px}
.ky-empty h3{font-family:var(--font-display);font-size:16.4px;font-weight:700;color:var(--ink)}
.ky-empty p{font-size:13.4px;color:var(--muted);max-width:40ch;line-height:1.5}
.ky-empty__acts{display:flex;gap:9px;margin-top:13px;flex-wrap:wrap;justify-content:center}
.ky-empty__hint{font-size:11.6px;color:var(--muted-2);margin-top:9px}
.ky-emptyrow td{padding:32px 12px;text-align:center;color:var(--muted);font-size:13.4px;line-height:1.5}
.ky-emptyrow b{display:block;color:var(--ink);font-family:var(--font-display);font-size:14.4px;margin-bottom:3px}

/* ============================================================================
   Products (Settings → Products) — what this workspace shows
   ============================================================================ */
.prods{display:flex;flex-direction:column}
.prod{display:flex;align-items:center;gap:14px;padding:13.4px 0;border-bottom:1px solid var(--line)}
.prod:last-child{border-bottom:0}
.prod__main{flex:1;min-width:0}
.prod__main b{display:block;font-family:var(--font-display);font-size:14.4px;font-weight:700}
.prod__main small{display:block;color:var(--muted-2);font-size:12px;margin-top:2px}
.prod__side{display:flex;align-items:center;gap:10px;flex:none}
.prod__word{font-size:11.6px;font-weight:700;color:var(--muted);min-width:44px;text-align:right}
.prod__plan{
  background:var(--sand);color:var(--muted);border-radius:var(--r-pill);
  padding:3.4px 10px;font-size:11.6px;font-weight:700;white-space:nowrap;
}
.prod__none{margin:0;font-size:13px;color:var(--muted-2)}
.prod__foot{margin:13.4px 0 0;font-size:11.6px;color:var(--muted-2);line-height:1.5}
/* The Products switch is deliberately not `.toggle`. That one's state is set by the
   router on any document click, and this one carries its own aria-checked and is saved
   the moment it is pressed: two writers for one attribute is how a switch comes to show
   the opposite of what is stored. Same geometry, so it looks like every other switch. */
.psw{
  width:36.5px;height:21.1px;border-radius:var(--r-pill);background:var(--line-2);
  position:relative;flex:none;transition:background .15s;
}
.psw::after{
  content:"";position:absolute;top:2.4px;left:2.4px;width:16.3px;height:16.3px;
  border-radius:50%;background:#fff;transition:transform .15s;
}
.psw[aria-checked="true"]{background:var(--pickup)}
.psw[aria-checked="true"]::after{transform:translateX(15.4px)}
@media (max-width:760px){
  /* The word beside the switch is the only thing that says which way it is set in
     words rather than in position, so it keeps its room on a phone instead of letting
     the switch be pushed off the end of the row. */
  .prod{gap:10px}
  .prod__word{min-width:38px;font-size:11px}
  .prod__main small{font-size:11.6px}
}

/* ============================================================================
   Account area (profile, business, security, API keys)
   ============================================================================ */
.acc{max-width:940px}
.acc__head{margin-bottom:20px}
.acc__head h2{font-family:var(--font-display);font-size:23px;font-weight:700;letter-spacing:-.01em}
.acc__head p{font-size:13.8px;color:var(--muted);margin-top:5px}
.acc__tabs{display:flex;gap:3px;border-bottom:1px solid var(--line);margin-bottom:22px;overflow-x:auto}
.acc__tab{
  padding:9px 14px;font-size:13.4px;font-weight:600;color:var(--muted);
  border-bottom:2px solid transparent;white-space:nowrap;
}
.acc__tab:hover{color:var(--ink)}
.acc__tab.is-on{color:var(--deep);border-bottom-color:var(--pickup)}
.acc__pane[hidden]{display:none}
.acc__grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px}
@media (max-width:760px){.acc__grid{grid-template-columns:minmax(0,1fr)}}
.acc__card{background:#fff;border:1px solid var(--line);border-radius:var(--r-2);padding:20px}
.acc__card--wide{grid-column:1/-1}
.acc__card h3{font-family:var(--font-display);font-size:15.4px;font-weight:700;margin-bottom:3px}
.acc__card > p.sub{font-size:12.6px;color:var(--muted);margin-bottom:15px;line-height:1.5}
.acc__rows{display:flex;flex-direction:column;gap:13px}
.acc__row{display:flex;align-items:center;justify-content:space-between;gap:14px;
  padding:11px 0;border-bottom:1px solid var(--line);font-size:13.4px}
.acc__row:last-child{border-bottom:0}
.acc__row b{font-weight:600}
.acc__row small{display:block;color:var(--muted);font-size:11.8px;margin-top:2px}
.acc__acts{display:flex;gap:9px;margin-top:16px;flex-wrap:wrap;align-items:center}
.acc__msg{margin-top:13px}
/* The logo card: the picture on the left, the way to change it on the right, and the
   two stack at a phone width because the frame is fixed and the form is not. */
.acc__logo{display:flex;gap:16px;align-items:flex-start;flex-wrap:wrap}
/* `grid-template-rows:100%` is what holds the picture inside the frame. An auto row is
   sized by its own content, so a square logo drawn 130px wide made a 130.5px row inside
   this 74px frame and `overflow:hidden` cut it off half way down; a 100% row is the
   frame's own height, which is definite, so the picture is scaled to fit it instead. */
.acc__logo__frame{flex:none;width:132px;height:74px;border:1px solid var(--line);
  border-radius:var(--r-2);display:grid;grid-template-rows:100%;place-items:center;
  overflow:hidden;background:#fff}
.acc__logo__frame[data-empty]{border-style:dashed}
.acc__logo__img{width:100%;height:100%;object-fit:contain;display:block}
.acc__logo__initials{font-family:var(--font-display);font-weight:700;font-size:20px;color:var(--muted)}
.acc__logo__form{flex:1 1 240px;min-width:0}
.acc__foot{font-size:11.8px;color:var(--muted-2);margin-top:14px;line-height:1.55}
.acc__key{
  font-family:var(--font-mono);font-size:12.4px;background:var(--paper);
  border:1px solid var(--line);border-radius:var(--r-1);padding:11px 12px;
  word-break:break-all;display:block;margin-top:6px;color:var(--ink);
}
.acc__pill{display:inline-flex;align-items:center;gap:5px;font-size:11.4px;font-weight:600;
  padding:3px 8px;border-radius:var(--r-pill);background:var(--mist);color:var(--deep)}
.acc__pill--off{background:var(--sand);color:var(--muted)}
.acc__label{display:block;font-size:12.4px;font-weight:600;color:var(--ink);margin-bottom:6px}
.acc__hint{display:block;font-size:11.4px;color:var(--muted-2);margin-top:6px}
/* A field the carriers refused, and the sentence they answered with. The border is the part
   that can be seen from the top of a long form; the message is the API's own, under the box
   it is about rather than in a toast at the top of the page. Both hang off aria-invalid, so
   the markup says which field is at fault and the stylesheet only says what that looks
   like. */
.input[aria-invalid="true"],.select[aria-invalid="true"],.textarea[aria-invalid="true"]{border-color:var(--bad)}
.field__err{display:block;font-size:11.4px;color:var(--bad);margin-top:6px}

/* Inline messages inside the account area (mirrors the auth pages) */
.a-msg{margin-top:13px;padding:10px 13px;border-radius:var(--r-1);font-size:12.8px;line-height:1.5}
.a-msg--ok{background:var(--mist);color:var(--deep)}
.a-msg--err{background:var(--blush);color:#8a2b1d}
.a-msg--info{background:var(--sand);color:var(--muted)}
.a-msg b{display:block;margin-bottom:5px}
.a-msg .acc__key{margin-top:8px}

/* ============================================================================
   Mobile hardening
   Nothing in the user area may push the page sideways: long emails, key
   prefixes, device strings and unbroken endpoint paths all have to wrap.
   ============================================================================ */
.acc{width:100%}
.acc__head h2,.acc__head p{overflow-wrap:anywhere}
.acc__tabs{scrollbar-width:none;-webkit-overflow-scrolling:touch}
.acc__tabs::-webkit-scrollbar{height:0}
.acc__tab{flex:none;white-space:nowrap}
.acc__card{overflow-wrap:anywhere}
.acc__rows{min-width:0}
.acc__row{flex-wrap:wrap;align-items:flex-start}
.acc__row > *{min-width:0;overflow-wrap:anywhere}
.acc__row > :last-child{margin-left:auto;text-align:right}
.acc__key,.acc__msg,.a-msg{max-width:100%;overflow-wrap:anywhere}
.acc input,.acc select,.acc textarea,
.quickadd input,.quickadd select,
.tlform input,.tlform select{min-width:0;max-width:100%}
.prof__head h2,.prof__head p,.tl__body b,.tl__body span,.dealcard b{overflow-wrap:anywhere}

@media (max-width:620px){
  .acc__card{padding:15px}
  .acc__row{flex-direction:column;gap:2px}
  .acc__row > :last-child{margin-left:0;text-align:left}
  .acc__acts{gap:7px}
  .acc__acts .btn{flex:1 1 auto;justify-content:center}
  .prof__head h2{font-size:19px}
  .prof__acts{margin-left:0;width:100%}
  .tlform .field{min-width:100%}
  .tlform .select{width:100%}
  .quickadd__grid{grid-template-columns:minmax(0,1fr)}
  .quickadd{padding:14px}
  .board{grid-auto-columns:minmax(210px,1fr)}
  .topbar{gap:6px}
  .topbar__title{font-size:17.5px}
  .topbar__jump{flex:1;min-width:0}
  .toolbar{flex-wrap:wrap}
  .seg{flex-wrap:wrap}
}

@media (max-width:760px){
  /* wide data tables scroll inside their card instead of squashing the page */
  .tablecard .tbl{min-width:640px}
}

/* ============================================================================
   CRM — deal pipeline board and quick-add forms
   ============================================================================ */
.board{display:grid;grid-auto-flow:column;grid-auto-columns:minmax(238px,1fr);gap:12px;overflow-x:auto;padding-bottom:8px}
.board__col{background:var(--paper);border:1px solid var(--line);border-radius:var(--r-2);padding:12px;min-height:130px}
.board__head{display:flex;align-items:center;justify-content:space-between;gap:8px;
  font-size:11.6px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:var(--muted);margin-bottom:10px}
.board__head b{background:#fff;border:1px solid var(--line);border-radius:var(--r-pill);padding:1px 7px;font-size:10.6px;color:var(--ink)}
.board__sum{font-family:var(--font-mono);font-size:10.6px;color:var(--muted-2);text-transform:none;letter-spacing:0}
.dealcard{background:#fff;border:1px solid var(--line);border-radius:var(--r-1);padding:11px 12px;margin-bottom:9px}
.dealcard b{display:block;font-size:13.4px;font-weight:600;color:var(--ink)}
.dealcard span{display:block;font-size:11.6px;color:var(--muted);margin-top:4px}
.dealcard em{display:block;font-family:var(--font-mono);font-size:12.4px;color:var(--deep);margin-top:8px;font-style:normal}
.quickadd{border:1px solid var(--line);background:#fff;border-radius:var(--r-2);padding:16px;margin-bottom:14px}
.quickadd h3{font-family:var(--font-display);font-size:14.4px;font-weight:700;margin-bottom:12px}
.quickadd__grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(190px,100%),1fr));gap:12px}
.quickadd[hidden]{display:none}

/* contact profile + activity timeline */
.prof{display:grid;gap:17px}
.prof__head{display:flex;align-items:center;gap:14px;flex-wrap:wrap}
.prof__head h2{font-family:var(--font-display);font-weight:800;font-size:22px;letter-spacing:-.01em}
.prof__head p{color:var(--muted);font-size:13px;margin-top:2px}
.prof__acts{margin-left:auto;display:flex;gap:8px;flex-wrap:wrap}
/* One column, deliberately: every card on a customer's page is a list of rows — Details is
   fourteen of them, Tickets puts a reference, a subject and a status on each line — so
   `repeat(auto-fit,minmax(min(285px,100%),1fr))` made a wide window draw THREE columns and
   squeezed those lists into ~347px of a 1072px page (measured 2026-09-30, both hosts, at
   1440px, and unchanged at every width down to one column). The two cards that already
   spanned the grid — Tasks (`acc__card--wide`) and Activity (`grid-column:1/-1`) — are what
   made the narrow ones look wrong rather than intentional. `.acc__card--wide` stays: the
   account panes still use it in their own two-column grid. A phone is unaffected, because
   below 760px `mobile.js` turns this page into four tabs rather than a wall of cards. */
.prof__grid{display:grid;grid-template-columns:minmax(0,1fr);gap:16px;align-items:start}
.timeline{display:grid;gap:2px}
.tl{display:flex;gap:11px;padding:11px 0;border-bottom:1px solid var(--line);align-items:flex-start}
.tl:last-child{border-bottom:0}
.tl__kind{flex:none;width:29px;height:29px;border-radius:50%;display:grid;place-items:center;
  background:var(--mist);color:var(--deep);font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.02em}
.tl__body{min-width:0}
.tl__body b{display:block;font-size:13.4px;font-weight:600}
.tl__body span{display:block;font-size:12.4px;color:var(--muted);margin-top:3px;line-height:1.5}
.tl__when{flex:none;font-family:var(--font-mono);font-size:11px;color:var(--muted-2);margin-left:auto;padding-left:10px}
.tlform{display:flex;gap:8px;flex-wrap:wrap;align-items:flex-end;margin-top:13px;padding-top:13px;border-top:1px solid var(--line)}
.tlform .field{flex:1;min-width:170px}
.tlform .select{flex:none;width:120px}

/* moving a deal between stages */
.dealcard{cursor:pointer}
.dealcard__moves{display:flex;flex-wrap:wrap;gap:5px;margin-top:10px;padding-top:10px;border-top:1px dashed var(--line)}
.dealcard__moves button{font-size:10.6px;padding:3px 8px;border-radius:var(--r-pill);
  border:1px solid var(--line);background:#fff;color:var(--ink);cursor:pointer}
.dealcard__moves button:hover{border-color:var(--deep);color:var(--deep)}
.dealcard__moves button[disabled]{opacity:.45;cursor:default}
/* The one control in a deal's moves area that is not a move, and the phone sheet's own row for
   it: both are the destructive one, so both say so in the same colour. */
.dealcard__moves .dealcard__del{color:var(--bad);border-color:var(--bad-soft);margin-left:auto}
.sheet__row--del{color:var(--bad)}
/* A note's Delete sits after the date it was written, which is already pushed right by
   `margin-left:auto` on the timestamp. */
.tl__del{flex:none;margin-left:8px}
/* A person's own Remove, under the email that identifies them in the same cell. */
.team__drop{margin-top:6px;margin-left:0}

/* ------------------------------------------- record editor, custom, tasks */
.editor{border:1px solid var(--line);background:#fff;border-radius:var(--r-2);padding:20px}
.quickadd .editor{border:0;padding:0;background:none}
.editor__head{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:16px}
.editor__head h2{margin:0;font-size:19px}
.editor__acts{margin-left:auto;display:flex;gap:8px;flex-wrap:wrap}
.editgroup{margin:0 0 18px;min-width:0}
.editgroup:last-of-type{margin-bottom:6px}
.editgroup__t{margin:0 0 8px;font-size:12.5px;letter-spacing:.04em;text-transform:uppercase;color:var(--muted)}
.field--wide{grid-column:1/-1}
.field--check{display:flex;flex-direction:row;align-items:center;gap:8px}
.field--check .acc__hint{margin:0}
.field--tight{max-width:190px}
.acc__label--gap{margin-top:16px}
.acc__note{margin:4px 0 0;font-size:14px;line-height:1.55;color:var(--muted);overflow-wrap:anywhere}
.customrows{display:flex;flex-direction:column;gap:8px}
.customrow{display:flex;gap:8px;align-items:center;flex-wrap:wrap;min-width:0}
.customrow .input{flex:1 1 170px;min-width:0}
.customrow .input--key{flex:0 1 220px}
.tasklist{display:flex;flex-direction:column;margin-bottom:14px}
.taskitem{display:flex;align-items:flex-start;gap:10px;padding:9px 0;border-bottom:1px solid var(--line);min-width:0}
.taskitem:last-child{border-bottom:0}
.taskitem__box{margin:3px 0 0;flex:0 0 auto}
.taskitem__body{display:flex;flex-direction:column;flex:1;min-width:0}
.taskitem__body b{font-weight:600;overflow-wrap:anywhere}
.taskitem__body small{color:var(--muted);font-size:12.5px}
.taskitem--done .taskitem__body b{text-decoration:line-through;color:var(--muted)}
.taskitem .btn{flex:0 0 auto}
.taskform{display:flex;gap:8px;align-items:flex-end;flex-wrap:wrap}
.taskform .field{flex:1 1 210px;min-width:0}
.taskform .btn{flex:0 0 auto}
@media (max-width:620px){
  .editor{padding:15px}
  .editor__acts{margin-left:0;width:100%}
  .editor__acts .btn{flex:1 1 auto}
  .customrow .input,.customrow .input--key{flex:1 1 100%}
  .taskform .field,.taskform .btn{flex:1 1 100%}
  .field--tight{max-width:none}
}

/* ------------------------------------------------------------- helpdesk */
.hd{display:flex;flex-direction:column;gap:16px;min-width:0}
.hd__head{display:flex;align-items:flex-start;gap:14px;flex-wrap:wrap;min-width:0}
.hd__title{min-width:0}
.hd__title h2{margin:0;font-family:var(--font-display);font-weight:800;font-size:21.1px;overflow-wrap:anywhere}
.hd__meta{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-top:8px}
.hd__acts{margin-left:auto;display:flex;gap:8px;flex-wrap:wrap}
.hd__grid{display:grid;grid-template-columns:minmax(0,1fr) 300px;gap:16px;align-items:start;min-width:0}
.hd__main,.hd__side{display:flex;flex-direction:column;gap:14px;min-width:0}
.hd-id{font-family:var(--font-mono);font-size:12px;font-weight:700;color:var(--muted)}
.hd-sub{display:block;font-weight:600;overflow-wrap:anywhere}
.hd-sub__who{display:block;color:var(--muted);font-size:12.5px;margin-top:3px;overflow-wrap:anywhere}
.hd-dim{color:var(--muted);font-size:12.5px}
.hd-count{display:inline-block;margin-left:7px;font-size:11.5px;font-weight:700;color:var(--muted)}
.hdthread{display:flex;flex-direction:column;gap:12px;min-width:0}
.hmsg{border:1px solid var(--line);border-radius:var(--r-1);padding:12px 14px;background:#fff;min-width:0}
.hmsg--out{background:var(--mist);border-color:transparent}
.hmsg--note{background:var(--sunny);border-color:transparent}
.hmsg__head{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:7px}
.hmsg__head b{font-size:13.4px}
.hmsg__kind{font-size:11.5px;font-weight:600;color:var(--muted)}
.hmsg__when{margin-left:auto;font-size:11.5px;color:var(--muted-2)}
.hmsg__body{font-size:13.6px;line-height:1.6;overflow-wrap:anywhere}
.hdcomposer,.hdnew,.hdreplies{border:1px solid var(--line);background:#fff;border-radius:var(--r-2);padding:16px;margin-bottom:14px}
.hdcomposer{display:flex;flex-direction:column;gap:10px}
.hdcomposer__mode{align-self:flex-start}
.hdnew__head{display:flex;align-items:center;gap:10px;margin-bottom:12px}
.hdnew__head h3{margin:0;font-size:17px}
.hdnew__head .btn{margin-left:auto}
.hdnew__mode{margin-bottom:12px}
.hdnew__form .acc__acts{margin-top:12px}
.hdprops{display:flex;flex-direction:column;gap:9px}
.hdreplies__list{margin-bottom:12px}
.hdreplies__body{flex:1;min-width:0;color:var(--muted);font-size:12.5px;overflow-wrap:anywhere}
.hdreplies__form{display:flex;gap:8px;flex-wrap:wrap;align-items:flex-end}
.hdreplies__form .field{flex:1 1 170px;min-width:0}
.hd-name{margin:2px 0 9px;font-weight:700;font-size:15px}
.select--sm{padding:5px 8px;font-size:12.5px}
@media (max-width:900px){.hd__grid{grid-template-columns:minmax(0,1fr)}}
@media (max-width:620px){
  .hd__acts{margin-left:0;width:100%}
  .hd__acts .btn{flex:1 1 auto}
  .hdcomposer,.hdnew,.hdreplies{padding:13px}
  .hdreplies__form .field{flex:1 1 100%}
  .hdnew__head{flex-wrap:wrap}
}

/* ==========================================================================
   Phone shell — implements Kylosys-Mobile-App.pdf
   Breakpoints: <768 phone · 768–1099 tablet · >=1100 desktop.
   Rules: 16px page gutter, cards 12–14px padding, tap targets >= 44x44 with
   8px between them, body text 14–15px, inputs 16px, safe-area aware tab bar,
   no horizontal page scroll (chip rows scroll instead).
   ========================================================================== */
.fab,.chiprow,.cardlist,.sheetwrap,.searchscreen{display:none}

/* --- tablet and narrow desktop -------------------------------------------
   Nothing here any more. The 72px icon-only strip that used to live at 981–1099px
   is gone: at every width below the deck's own canvas the app rail *is* the
   icon-only column, and the sidebar is an overlay beside it — see
   `.app.is-overlay` in the shell block. That block also replaced a bare
   max-width:1099 rule which, before it, reached the off-canvas drawer and hid
   every name, label and chip in it. */

/* --- phone --------------------------------------------------------------- */
/* The deck's own narrowest phone reading: three columns under 360px rather than four. The
   launcher is named in the selector on purpose — the four-column grid lives inside the phone
   block, and two rules of equal weight are decided by which comes last in the file, not by
   which media query is narrower. Naming the container makes this one win where it applies. */
@media (max-width:359px){
  .lch .lch__tiles,.lch .lch__tiles--2{grid-template-columns:repeat(3,minmax(0,1fr))}
}

/* --- what only a phone has ------------------------------------------------
   The mobile deck's own containers, built by nav.js and by markup in index.html: the
   header's app button, the page tabs, the bottom tab bar, the page menu sheet and the tab
   bar editor. Each gives itself a `display` inside the phone block below, so each needs
   this rule to be absent above 767px — an author `display` beats the user agent's
   `[hidden]{display:none}`, which is why they are named one by one. */
.mph__app,.mtabs,.tabbar,.msheet,.medit,.lch__title,.lch__close,.lch__foot--phone{display:none}

@media (max-width:767px){
  :root{--pad:16px}
  /* The phone's own shell, to the mobile deck (Kylosys_Mobile_Nav.pdf): the app rail
     becomes the bottom tab bar, the context sidebar becomes the page tabs and the page
     menu sheet, and the app launcher takes the whole screen. Nothing above this width
     changes — every rule here is inside this block, and every element it needs is built
     by nav.js and switched off above 767px. */
  /* One column, and `.is-overlay` named as well: from here up the rail is hidden and
     `#tabbar` is `position:fixed`, so the main column is the app's only grid item. With
     `.app.is-overlay`'s own two columns still in force — it outranks a bare `.app`
     whatever the order in the file — the body landed in the 76px first column: measured
     at 390px, the whole app was 76px wide and 6231px tall. */
  .app,.app.is-overlay{grid-template-columns:minmax(0,1fr)}
  /* The rail is the desktop's column and has no phone form. Everything it carries that a
     phone still needs has a home elsewhere: the apps and the launcher in the tab bar, the
     account and Settings in the launcher's own foot, the pages in the sheet. */
  .navrail,.app.is-overlay .side,.app.is-overlay .scrim{display:none}

  /* --- the bottom tab bar (the rail's phone form) ---------------------------
     92px on an iPhone: 64px of content and the home-indicator inset under it. Five equal
     slots — four pinned apps and the launcher, which is always last.
     Its z-index is the deck's own order of the three takeovers: below the launcher (70),
     which covers the whole screen including this bar, and above the editor (60), which
     leaves the bar showing at the foot of the screen as the live preview of its own lists.
     The page menu sheet (80) is over all of them. */
  .tabbar{
    position:fixed;left:0;right:0;bottom:0;z-index:65;
    display:flex;align-items:stretch;
    height:calc(64px + env(safe-area-inset-bottom));
    padding:0 6.35px env(safe-area-inset-bottom);
    background:#1f1530;
  }
  .tabbar__slot{
    position:relative;flex:1;min-width:0;min-height:48px;min-width:48px;
    display:flex;flex-direction:column;align-items:center;gap:5px;padding:11px 0 0;
    background:none;border:0;font:inherit;text-decoration:none;color:#b9aecb;cursor:pointer;
  }
  /* The 26x3 bar on the active slot's top edge. */
  .tabbar__bar{position:absolute;top:0;left:50%;transform:translateX(-50%);
    width:26px;height:3px;border-radius:0 0 3px 3px;background:transparent}
  .tabbar__slot.is-on .tabbar__bar{background:#fff}
  .tabbar__ico{width:32px;height:32px;border-radius:10px;flex:none;display:grid;place-items:center;
    color:#fff;opacity:.6}
  .tabbar__slot.is-on .tabbar__ico{opacity:1}
  .tabbar__ico--apps{background:#3d2f50;opacity:1}
  .tabbar__svg{display:block}
  .tabbar__name{font-size:10.5px;font-weight:500;line-height:1}
  .tabbar__slot.is-on .tabbar__name{color:#fff;font-weight:700}
  /* The badge hangs off the tile's own top-right corner, and its 2px rail-coloured ring is
     what keeps it legible where it overlaps the tile or its neighbour. */
  .tabbar__badge{
    position:absolute;top:7px;left:50%;margin-left:11px;min-width:17px;height:17px;padding:0 4px;
    border-radius:9px;background:#e4572e;color:#fff;font-size:10px;font-weight:700;font-style:normal;
    display:grid;place-items:center;box-shadow:0 0 0 2px #1f1530;
  }
  .tabbar__badge[hidden]{display:none}

  /* --- the header (M1, part 1) --------------------------------------------
     52px: the app you are in on the left, search, notifications and the avatar on the
     right. The crumb, the burger and + New are the desktop's; the deck's phone header has
     none of them, and the app button it does have opens the page menu sheet. */
  .topbar{height:calc(52px + env(safe-area-inset-top));gap:10.6px;padding:env(safe-area-inset-top) 16px 0;
    top:0;transition:top .18s ease}
  .mph__app{display:flex;align-items:center;gap:8px;height:44px;padding:0 3px;
    min-width:0;background:none;border:0;font:inherit;color:var(--ink);cursor:pointer}
  .mph__ico{width:30px;height:30px;border-radius:9px;flex:none;display:grid;place-items:center;color:#fff}
  .mph__svg{display:block}
  .mph__name{font-size:21px;font-weight:700;letter-spacing:-.01em;color:#2a1e3a;
    white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .mph__chev{display:grid;place-items:center;color:#6b5e80}
  .crumb,.topbar__jump .kbd,#newBtn,.burger{display:none!important}
  .topbar__jump{margin-left:auto;flex:none}
  /* The deck's two 38px circles and the 36px avatar, 10.6px apart. */
  .topbar__search,.topbar__bell{width:38px;height:38px;flex:none;padding:0;border-radius:50%;
    position:relative;background:#fbf8f2;border:1px solid #e4dccd;color:var(--ink);display:grid;place-items:center}
  .topbar__search span{display:none}
  .topbar__me{width:36px;height:36px;font-size:13px;position:relative}

  /* --- the page tabs (M1, part 2) -----------------------------------------
     52px under the header: the current app's pages as 34px pills on one sideways-scrolling
     row. nav.js hides the whole strip where the app has a single page. */
  .mtabs{position:sticky;top:calc(52px + env(safe-area-inset-top));z-index:19;height:52px;display:flex;
    align-items:center;background:var(--sand);transition:top .18s ease}
  .mtabs[hidden]{display:none}
  .mtabs__row{display:flex;align-items:center;gap:9px;width:100%;padding:0 16px;
    overflow-x:auto;scrollbar-width:none;-webkit-overflow-scrolling:touch}
  .mtabs__row::-webkit-scrollbar{display:none}
  .mtab{position:relative;flex:none;display:inline-flex;align-items:center;gap:6px;height:34px;padding:0 14px;
    border-radius:17px;background:#fbf8f2;border:1px solid #e4dccd;color:var(--ink);
    font-size:14px;font-weight:500;text-decoration:none;white-space:nowrap}
  .mtab.is-on{background:#2a1e3a;border-color:#2a1e3a;color:#fff;font-weight:600}
  .mtab__badge{font-style:normal;font-size:11px;font-weight:700;border-radius:999px;padding:1px 6px;
    background:#e9f4ec;color:#0e7a4c}
  .mtab.is-on .mtab__badge{background:#3d2f50;color:#fff}

  /* --- the page menu sheet (M2) -------------------------------------------
     A bottom sheet over a scrim, above the tab bar the way the deck draws it: white, a
     28px top radius, and the workspace switcher at its foot (nav.js moves the sidebar's
     own button in here, so it keeps its name, its badge and its handler). */
  .msheet{display:block;position:fixed;inset:0;z-index:80}
  .msheet[hidden]{display:none}
  .msheet__scrim{position:absolute;inset:0;background:rgba(31,21,48,.45);opacity:1;
    transition:opacity var(--veil)}
  .msheet.is-leaving .msheet__scrim{opacity:0}
  .msheet:not([hidden]) .msheet__scrim{animation:sheet-veil var(--veil)}
  /* THE RISE IS AN ANIMATION, AND THAT IS A GUARD RATHER THAN A STYLE. A transition needs a value
     change in a task AFTER the un-hide, or the browser coalesces the pair into one style pass and
     paints the panel at its destination -- so every opener has to remember to flush (`void
     el.offsetHeight`) and to add the class that moves it. There are FIVE openers for these two
     panels (`openSheet`, the quick task form, the ticket's Details sheet, the deal's Move sheet
     and the filter sheet in helpdesk.js), and three of them un-hid the host themselves: measured
     on staging when the rise landed, each of those three parked its panel a screen below the
     reader, with its own Close button unpressable. An animation starts off the display change
     itself, so a sixth opener cannot get this wrong.
     The way OUT is a transition on `is-leaving`, because a class change on a displayed element is
     an ordinary transition -- and because the panel's closed position is then a declaration a
     test can read (`is-leaving` on a displayed wrapper, which the coalescing works FOR here).
     The panel is hidden one paint after that travel ends; see the drawer's block above. */
  .msheet__panel{
    position:absolute;left:0;right:0;bottom:0;max-height:85%;overflow-y:auto;
    background:#fff;border-radius:28px 28px 0 0;
    padding:10px 15.4px calc(18px + env(safe-area-inset-bottom));
    box-shadow:0 -12px 32px rgba(31,21,48,.22);
    transform:none;transition:transform var(--rise-out);
  }
  .msheet.is-leaving .msheet__panel{transform:translateY(100%)}
  .msheet:not([hidden]) .msheet__panel{animation:sheet-rise var(--rise-in)}
  /* On its way out the sheet takes no finger, the way the drawer and the Terminal's do: it is
     off the screen but an opacity of zero still receives a press. */
  .msheet.is-leaving{pointer-events:none}
  @keyframes sheet-rise{from{transform:translateY(100%)}}
  @keyframes sheet-veil{from{opacity:0}}
  .msheet__grip{width:40px;height:5px;border-radius:3px;background:#d8cfc0;margin:0 auto 9px}
  .msheet__head{display:flex;align-items:center;gap:10px;padding:10px 3.4px 12px;border-bottom:1px solid #efe8dc}
  .msheet__ico{width:36px;height:36px;border-radius:11px;flex:none;display:grid;place-items:center;color:#fff}
  .msheet__name{font-size:20px;font-weight:700}
  .msheet__switch{margin-left:auto;background:none;border:0;font:inherit;font-size:14px;font-weight:600;
    color:#0e7a4c;cursor:pointer;padding:6px 2px}
  .msheet__list{display:flex;flex-direction:column;padding:6px 0}
  .msheet__row{display:flex;align-items:center;gap:12px;height:50px;padding:0 12px;border-radius:14px;
    font-size:16px;font-weight:500;color:var(--ink);text-decoration:none;min-height:48px}
  .msheet__row.is-on{background:#f3ede2;font-weight:700}
  .msheet__badge{margin-left:auto;font-style:normal;font-size:12px;font-weight:700;border-radius:999px;
    padding:2px 8px;background:#e9f4ec;color:#0e7a4c}
  .msheet__foot{padding-top:6px}
  /* The workspace switcher, in its phone clothes: the deck's card at the sheet's foot. It is
     a button of this sheet's own that presses the sidebar's real one, so it carries the
     button's own reset as well as the card's look. */
  .msheet__foot .side__biz{display:flex;width:100%;background:#f3ede2;border:0;border-radius:14px;
    padding:12px 14px;font:inherit;color:var(--ink);text-align:left;cursor:pointer}
  .msheet__foot .side__biz:hover{background:#efe7da}

  /* --- the tab bar editor (M4) ---------------------------------------------
     Full screen, with the real bar left showing at the foot of it: the preview is the bar
     itself, repainted as the lists change. */
  .medit{display:block;position:fixed;inset:0;z-index:60;background:#f7f2ea;overflow-y:auto;
    padding:0 0 calc(76px + env(safe-area-inset-bottom))}
  .medit[hidden]{display:none}
  .medit__head{display:flex;align-items:center;height:52px;padding:0 16px}
  .medit__title{flex:1;text-align:center;font-size:17px;font-weight:700}
  .medit__cancel,.medit__done{background:none;border:0;font:inherit;font-size:16px;padding:8px;
    cursor:pointer;color:var(--ink)}
  .medit__done{color:#0e7a4c;font-weight:700}
  .medit__hint{margin:0;padding:8px 19.3px 0;font-size:13.5px;line-height:1.5;color:var(--muted)}
  .medit__label{margin:12px 0 4px;padding:0 21.3px;font-size:11px;font-weight:700;letter-spacing:.06em;
    text-transform:uppercase;color:#8e82a0}
  /* The gap before "More apps" is the deck's own: a wider one after a card than after the
     prose above the first list. */
  .medit__card + .medit__label{margin-top:26px}
  .medit__card{margin:0 15.8px;background:#fff;border-radius:14px;overflow:hidden}
  .medit__row{display:flex;align-items:center;gap:12px;height:56px;padding:0 14px;
    border-bottom:1px solid #f1ebe0}
  .medit__row:last-child{border-bottom:0}
  .medit__row.is-full{opacity:.55}
  .medit__minus,.medit__plus{width:22px;height:22px;flex:none;padding:0;border-radius:50%;
    display:grid;place-items:center;cursor:pointer}
  .medit__minus{background:#e0457a;color:#fff;border:0}
  .medit__plus{background:none;border:1.5px solid #0e7a4c;color:#0e7a4c}
  .medit__minus[disabled],.medit__plus[disabled]{opacity:.4;cursor:default}
  .medit__ico{width:32px;height:32px;border-radius:10px;flex:none;display:grid;place-items:center;color:#fff}
  .medit__svg{display:block}
  .medit__name{font-size:16px;font-weight:500}
  .medit__grip{margin-left:auto;background:none;border:0;padding:6px;color:#b9aecb;cursor:grab;
    touch-action:none;display:grid;place-items:center}
  .medit__full{margin-left:auto;font-size:12px;color:#8e82a0}

  /* --- the header and the tabs leave on a downward scroll -------------------
     The bar stays; these two come back as soon as the page is scrolled up. */
  .is-scrolled .topbar{top:calc(-52px - env(safe-area-inset-top))}
  .is-scrolled .mtabs{top:calc(-52px - env(safe-area-inset-top))}

  /* --- tap targets ---------------------------------------------------------
     The deck draws two controls smaller than the 44px its own behaviour page
     asks for — the 34px page pill and the two 38px header circles — so each gets
     a hit area of its own, laid over the gap around it rather than over its
     neighbour's. The drawing is unchanged; the target is 44px. */
  .mtab::after{content:"";position:absolute;left:0;right:0;top:-5px;bottom:-5px}
  .topbar__search::after,.topbar__bell::after,.topbar__me::after{
    content:"";position:absolute;top:-3px;right:-3px;bottom:-3px;left:-3px;border-radius:50%;
  }

  /* The account menu opens under the header's avatar, which is the button that opens it on
     a phone — the rail's own avatar is not here. */
  .acct{left:auto;right:12px;top:56px;bottom:auto;width:min(300px,calc(100vw - 24px))}

  /* --- the launcher (M3) ---------------------------------------------------
     It slides up over the whole screen: a 30px "Apps" heading and a close button, a 44px
     search field, then the groups in four columns of 60px tiles. No descriptions — the
     deck's phone tiles carry none — and the two footer buttons from the deck's own foot. */
  .lch{background:#fff}
  .lch__scrim{left:0}
  .lch__pop{
    left:0;top:0;width:100%;height:100%;max-width:none;border-radius:0;
    padding:calc(11px + env(safe-area-inset-top)) 18px 0;box-shadow:none;background:#fff;
  }
  /* The drawer comes up from the bottom edge rather than out of nothing. A full-screen
     sheet that only fades has no direction: it is simply there, and the reader cannot
     tell where from. Its motion is the Terminal's own -- the `--rise-in`, `--rise-out` and
     `--veil` tokens in `:root` are one set of values for both sheets -- and the only
     difference is
     the distance: `translateY(100%)` is this panel's own height, which here is the whole
     screen, where the Terminal's sheet is half of it. Its resting box is untouched:
     this is the same full-screen panel that `app-mobile-nav.spec.js` measures, moved. */
  .lch__pop{transform:translateY(100%);transition:transform var(--rise-in)}
  .lch:not(.is-open) .lch__pop{transition:transform var(--rise-out)}
  /* On its way out the panel is not there as far as a finger is concerned. It is off the
     screen and the scrim is at `opacity:0`, but an opacity of zero still takes a tap --
     so a reader who pressed Close and immediately pressed something underneath would be
     pressing the drawer. The open panel keeps every pointer event it had. */
  .lch:not(.is-open){pointer-events:none}
  /* The scrim under it fades in with it, or the app behind would go black in one frame
     while the drawer is still on its way up. */
  .lch__scrim{opacity:0;transition:opacity var(--veil)}
  .lch.is-open .lch__scrim{opacity:1}
  .lch__body{padding:0;max-height:none;flex:1;overflow-y:auto}
  .lch__head{padding:0;display:flex;flex-wrap:wrap;align-items:center;gap:11.3px}
  .lch__title{display:block;order:1;flex:1;margin:0;font-size:30px;line-height:1.2;font-weight:700}
  .lch__close{order:2;width:36px;height:36px;flex:none;border-radius:50%;background:#f3ede2;
    border:0;display:grid;place-items:center;color:var(--ink);cursor:pointer}
  .lch__field{order:3;flex:0 0 100%;height:44px;border-radius:14px;background:#f3ede2;border:0;padding:0 14px}
  .lch__field input{font-size:16px}
  /* The ⌘J keycap is a desktop affordance — a chord no phone has — and the same ruling the
     operator already gave for the top bar's ⌘K. The field keeps its magnifier and its
     placeholder; only the hint goes. */
  .lch__field .kbd{display:none}
  .lch__row,.lch__row--pair{grid-template-columns:minmax(0,1fr)}
  .lch__tiles,.lch__tiles--2{grid-template-columns:repeat(4,minmax(0,1fr))}
  .lch__ico{width:60px;height:60px;border-radius:17px;box-shadow:none}
  .lch__ico svg{width:26px;height:26px}
  .lch__name{margin-top:8px;font-size:12px;font-weight:600}
  .lch__desc{display:none}
  .lch__label{margin:20px 0 4px;font-size:11px}
  .lch__tile{padding:0 2px 14px}
  /* The deck's coral NEW pill. The small ink pin badge the deck draws beside an app the bar
     carries is gone from this launcher, markup and rules both -- app.css carried a .lch__pin
     block until 2026-09-30, and nav.js's launcher section says why it went. */
  .lch__new{font-size:9px;font-weight:700;padding:2px 7px;border-radius:8px;background:#e4572e;
    color:#fff;position:absolute;top:-6px;right:-10px;letter-spacing:.02em}
  /* The desktop's own foot is not here: the deck's phone launcher ends in Settings &
     account and the tab bar editor. */
  .lch__foot{display:none}
  .lch__foot--phone{display:flex;flex-direction:column;align-items:stretch;gap:10px;
    padding:14px 0 calc(16px + env(safe-area-inset-bottom));
    background:#fff;border-top:1px solid var(--line);margin-top:10px}
  .lch__phonebtn{display:grid;place-items:center;height:50px;border-radius:14px;font-size:15px;
    font-weight:600;text-decoration:none;border:1px solid #d8cfc0;background:#fff;color:var(--ink);
    font-family:inherit;cursor:pointer}
  .lch__phonebtn--edit{background:#f3ede2;border-color:#f3ede2;color:#0e7a4c}

  /* The main column keeps room for the bar, and then a little more: the deck's own
     floating button sits 16px above it. */
  .main{min-height:100vh;padding-bottom:calc(80px + env(safe-area-inset-bottom))}

  /* one screen at a time */
  .view{padding:16px 16px 24px}
  .view--flush{padding:0;height:auto;overflow:visible}
  .split,.split--2,.split--inbox,.split--live,.split--phone{grid-template-columns:minmax(0,1fr);height:auto}
  /* the designed markup carries inline column widths (e.g. the tickets rail);
     on a phone those must not survive or the body is squeezed into a column */
  #views .split,#views .split--2,#views .split--inbox,#views .split--live,
  #views .split--phone{grid-template-columns:minmax(0,1fr)!important;height:auto!important}
  #views .pane{padding:14px 16px 0!important}
  .pane{overflow:visible;border:0}
  .pane--side,.pane--plain,.pane--list{border:0;background:none}
  #view-tickets .pane{padding:14px 16px 0!important}
  #view-ticket [data-module="ticket"]{padding:14px 16px 0!important}
  /* body text is never under 12px on a phone */
  .acc__hint,.acc__note,.hd-sub__who,.hd-dim,.mini dt,.mini dd,.sheet__hint,
  .sres__r small,.acc__row .acc__label{font-size:12.5px}

  /* views/teams rail becomes chips above the list */
  .frail{display:none}
  .chiprow{display:flex;overflow-x:auto;gap:8px;padding:0 0 12px;-webkit-overflow-scrolling:touch;scrollbar-width:none}
  .chiprow::-webkit-scrollbar{display:none}
  .chiprow button{flex:none;min-height:44px;padding:0 14px;border-radius:999px;background:var(--sand);
    color:var(--ink);font-weight:600;font-size:13.5px;white-space:nowrap;display:inline-flex;
    align-items:center;gap:7px;border:1px solid transparent}
  .chiprow button.is-on{background:var(--plum);color:var(--oat)}
  .chiprow .hd-count{margin:0;color:inherit;opacity:.72}

  /* tables become cards: title, subtitle, 1–2 chips, time on the right */
  .tablecard{display:none}
  .cardlist{display:flex;flex-direction:column;gap:10px;padding-bottom:16px}
  .card{display:flex;flex-direction:column;gap:7px;width:100%;text-align:left;background:#fff;
    border:1px solid var(--line);border-radius:var(--r-1);padding:13px 14px;min-height:44px;
    font:inherit;color:var(--ink);cursor:pointer}
  .card__top{display:flex;align-items:flex-start;gap:10px}
  .card__title{flex:1;min-width:0;font-weight:700;font-size:15px;line-height:1.3;overflow-wrap:anywhere}
  .card__time{flex:none;font-size:12px;color:var(--muted);white-space:nowrap}
  .card__sub{font-size:13px;color:var(--muted);overflow-wrap:anywhere}
  .card__chips{display:flex;flex-wrap:wrap;gap:6px}
  .card--empty{color:var(--muted);font-size:13.5px;cursor:default;background:var(--paper)}

  /* one filter button instead of a toolbar of them */
  #view-tickets .toolbar{flex-wrap:nowrap;gap:8px;overflow-x:auto;scrollbar-width:none}
  #view-tickets .toolbar::-webkit-scrollbar{display:none}
  #view-tickets .toolbar h2{font-size:19px;white-space:nowrap}
  #view-tickets .toolbar .spacer{margin-left:auto}
  #view-tickets .toolbar .btn--ghost{display:none}
  #view-tickets .toolbar .btn--primary{display:none}
  #view-tickets .toolbar .filterbtn{display:none}
  #view-tickets .toolbar .filterbtn{display:inline-flex}
  #view-tickets .toolbar .input{width:auto;flex:1;min-width:0;height:44px}
  /* anything else that would push the page sideways scrolls or wraps instead */
  .view__acts,.acc__acts,.hd__acts{flex-wrap:wrap;gap:8px}
  .view__acts .btn,.acc__acts .btn,.hd__acts .btn{max-width:100%;min-width:0}
  .toolbar{flex-wrap:nowrap;overflow-x:auto;scrollbar-width:none;padding-bottom:2px}
  .toolbar::-webkit-scrollbar{display:none}
  .toolbar .btn,.toolbar .seg,.toolbar .input{flex:none}
  .seg{max-width:100%;overflow-x:auto;flex-wrap:nowrap;scrollbar-width:none}
  .seg::-webkit-scrollbar{display:none}
  .seg button{flex:none}
  /* the pipeline becomes stage chips, one stage of cards at a time */
  .board{display:none}
  .chiprow--stages,.chiprow--profile{margin:0 0 12px}
  #view-contact .chiprow--profile{margin:0 -16px 12px;padding:0 16px}
  .chiprow button.is-on{background:var(--ink);color:#fff;border-color:var(--ink)}
  .chiprow button.is-quiet{opacity:.55}
  #views [hidden]{display:none!important}

  /* sheets */
  /* Every layer above the page, in one order. The rail is the bar along the foot of the
     screen here at z-index 65, so a sheet — which is a modal over the whole app, its
     buttons included — has to sit above it: at 60 the filter sheet's own "Show 1 ticket"
     lay under the bar and could not be pressed at all, and the ticket's Details sheet had
     the same fault. A popup opened from a sheet goes above the sheet, and a toast above
     both, because it is the answer to something just pressed. */
  .sheetwrap{display:block;position:fixed;inset:0;z-index:75}
  .sheetwrap[hidden]{display:none}
  /* The scrim fades in off the display change and out on the class that closes the sheet, and it
     cannot be the wrapper's own background any more: that would fade the panel with it. The state
     is the wrapper's own `is-leaving` and NOT `body.sheet-open`, which is also the search screen's
     and the fab's -- a search closing under a sheet would start the sheet's exit. */
  .sheetwrap::before{content:"";position:absolute;inset:0;background:rgba(42,30,58,.42);
    opacity:1;transition:opacity var(--veil);pointer-events:none}
  .sheetwrap.is-leaving::before{opacity:0}
  .sheetwrap:not([hidden])::before{animation:sheet-veil var(--veil)}
  .sheet{position:absolute;inset:auto 0 0;background:#fff;border-radius:20px 20px 0 0;max-height:88vh;
    overflow-y:auto;padding:10px 16px calc(18px + env(safe-area-inset-bottom));
    box-shadow:0 -12px 40px rgba(42,30,58,.22);
    transform:none;transition:transform var(--rise-out)}
  .sheetwrap.is-leaving .sheet{transform:translateY(100%)}
  /* The rise is the animation declared on `.msheet` above, for the reason recorded there: this
     panel has more than one opener and a transition would need every one of them to flush. */
  .sheetwrap:not([hidden]) .sheet{animation:sheet-rise var(--rise-in)}
  /* A sheet on its way down takes no finger: it is off the screen, but an opacity of zero still
     receives a press, so a reader pressing Close and then something underneath would be pressing
     the sheet. */
  .sheetwrap.is-leaving{pointer-events:none}
  .sheet__grip{width:40px;height:4px;border-radius:99px;background:var(--line);margin:2px auto 12px}
  .sheet__head{display:flex;align-items:center;gap:10px;margin-bottom:10px}
  .sheet__head h2{margin:0;font-family:var(--font-display);font-weight:800;font-size:20px}
  .sheet__x{margin-left:auto;min-height:44px;padding:0 4px;background:none;border:0;color:var(--muted);
    font-weight:600;font-size:14px}
  .sheet__who{display:flex;align-items:center;gap:11px;padding:8px 0 12px;border-bottom:1px solid var(--line)}
  .sheet__who b{display:block;font-size:15px}
  .sheet__who small{color:var(--muted);font-size:12.5px}
  .sheet__group{margin:16px 0 4px;font-size:11px;letter-spacing:.09em;text-transform:uppercase;
    color:var(--muted-2);font-weight:700}
  .sheet__row{display:flex;align-items:center;gap:12px;width:100%;min-height:52px;padding:10px 2px;
    border:0;border-bottom:1px solid var(--line);background:none;font:inherit;font-size:15px;
    text-align:left;color:var(--ink);text-decoration:none}
  .sheet__row:last-child{border-bottom:0}
  .sheet__row>svg:first-child{width:18px;height:18px;color:var(--muted);flex:none}
  .sheet__row span{flex:1;min-width:0}
  .sheet__hint{display:block;color:var(--muted);font-size:12.5px;font-weight:400}
  .sheet__chev{width:14px;height:14px;color:var(--muted-2);flex:none}
  .sheet__out{margin-top:8px}
  .sheet__row--out{color:var(--bad);justify-content:center}
  .sheet .hd__side{display:flex;flex-direction:column;gap:14px}
  .sheet__acts{display:flex;gap:10px;margin-top:18px}
  .sheet__acts .btn{flex:1}
  .sheet .chips{padding:0 0 4px}
  .taskquick{display:flex;flex-direction:column;gap:10px}

  /* full-screen search — over the phone's rail for the same reason the sheets are */
  .searchscreen{display:block;position:fixed;inset:0;z-index:75;background:#fff;overflow-y:auto;
    padding:12px 16px calc(16px + env(safe-area-inset-bottom))}
  .searchscreen[hidden]{display:none}
  .searchbar{display:flex;align-items:center;gap:10px;border-bottom:1px solid var(--line);padding-bottom:6px}
  .searchbar input{flex:1;min-width:0;border:0;outline:0;background:none;font-size:16px;min-height:44px}
  .sres__g{margin:16px 0 2px;font-size:11px;letter-spacing:.09em;text-transform:uppercase;
    color:var(--muted-2);font-weight:700}
  .sres__r{display:block;width:100%;text-align:left;min-height:48px;padding:11px 0;background:none;
    border:0;border-bottom:1px solid var(--line);color:var(--ink);text-decoration:none;font:inherit}
  .sres__r b{display:block;font-size:14.5px;overflow-wrap:anywhere}
  .sres__r small{color:var(--muted);font-size:12.5px}

  /* The primary action (M1's floating pill): 52px tall, green, and 16px above the tab bar.
     Its label names what it makes — "New deal", "New ticket" — because the deck's phone
     button is a pill with words rather than the desktop's "+ New". */
  .fab{display:flex;position:fixed;right:16px;bottom:calc(80px + env(safe-area-inset-bottom));
    height:52px;width:auto;padding:0 20px;border-radius:26px;background:#0e7a4c;color:#fff;border:0;
    align-items:center;gap:8px;font-family:inherit;font-size:15px;font-weight:700;
    box-shadow:0 10px 24px rgba(31,21,48,.28);z-index:40}
  .fab__label{white-space:nowrap}

  /* ticket screen: sticky reply box, details in a sheet */
  .hdcomposer{position:sticky;bottom:calc(70px + env(safe-area-inset-bottom));z-index:20;
    margin-bottom:12px;box-shadow:0 -8px 22px rgba(42,30,58,.10)}
  .hd__grid{grid-template-columns:minmax(0,1fr)}
  .hd__side{display:none}
  .hd__acts{width:100%;margin-left:0}
  .hd__acts .btn{flex:1 1 auto}
  .hmsg{padding:11px 12px}
  .hdthread{gap:10px}

  /* touch and type */
  .btn{min-height:44px}
  .btn--sm{min-height:42px}
  .input,.select,textarea,input[type="search"],input[type="email"],input[type="date"]{font-size:16px}
  .input,.select{min-height:44px}
  .field{gap:6px}
  h1,h2,h3{overflow-wrap:anywhere}
  .ky-empty{padding:26px 16px}
}

/* quick feedback after an action, with undo */
.toast{position:fixed;left:16px;right:16px;bottom:calc(88px + env(safe-area-inset-bottom));z-index:78;
  display:flex;align-items:center;gap:12px;background:var(--ink);color:var(--oat);border-radius:14px;
  padding:13px 16px;min-height:48px;box-shadow:0 12px 30px rgba(42,30,58,.3);opacity:0;
  transform:translateY(8px);transition:opacity .18s,transform .18s;font-size:14px}
.toast[hidden]{display:none}
.toast.is-on{opacity:1;transform:translateY(0)}
.toast span{flex:1;min-width:0}
.toast button{background:none;border:0;color:var(--sunny);font-weight:700;font-size:14px;min-height:44px;padding:0}
@media (min-width:768px){.toast{left:auto;right:24px;bottom:24px;max-width:340px}}

/* the + must never cover a sheet or the search screen */
.sheet-open .fab{display:none}

/* ---------------------------------------------------------------------------
   A call that is still up, and the one control that ends it.

   Bottom right, on every screen of /app/, and only while a call of this
   business's is up — a call the dialer no longer knows about because the popup or
   the tab was closed while it was running. It sits above the scrim and the sheet
   (z 68) on purpose: a call going on billing is not something a sheet should be
   able to hide. It stays under the toast (70), and the three things that share the
   bottom of the screen move out of its way through `has-livecall`, which the pill
   puts on the body — the toast and the + button otherwise land on top of it. */
.livecall{position:fixed;right:24px;bottom:24px;z-index:68;display:flex;align-items:center;gap:11px;
  max-width:min(360px,calc(100vw - 32px));background:var(--ink);color:var(--oat);border-radius:14px;
  padding:10px 12px 10px 14px;box-shadow:0 14px 34px rgba(42,30,58,.34);font-size:13.4px}
.livecall[hidden]{display:none}
.livecall__pulse{width:9px;height:9px;border-radius:50%;background:var(--coral);flex:0 0 auto;
  box-shadow:0 0 0 0 rgba(232,120,96,.75);animation:livecall-pulse 1.9s ease-out infinite}
@keyframes livecall-pulse{
  0%{box-shadow:0 0 0 0 rgba(232,120,96,.7)}
  70%{box-shadow:0 0 0 11px rgba(232,120,96,0)}
  100%{box-shadow:0 0 0 0 rgba(232,120,96,0)}}
.livecall__text{display:flex;flex-direction:column;min-width:0;line-height:1.25}
.livecall__text b{font-size:13.4px;font-weight:700;white-space:nowrap}
.livecall__text small{color:rgba(250,246,238,.72);font-size:12.2px;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.livecall__end{flex:0 0 auto}
/* The toast is raised over the pill rather than under it, and the + button with it —
   the pill is the only thing here that is about money. */
body.has-livecall .toast{bottom:calc(150px + env(safe-area-inset-bottom))}
@media (min-width:768px){
  body.has-livecall .toast{bottom:96px}
  .livecall{right:24px;bottom:24px}
}
@media (max-width:767px){
  /* Full width above the tab bar, because a thumb finds it without aiming, and the
     + button steps up over it. */
  .livecall{left:16px;right:16px;bottom:calc(76px + env(safe-area-inset-bottom));max-width:none}
  body.has-livecall .fab{bottom:calc(146px + env(safe-area-inset-bottom))}
  body.has-livecall .toast{bottom:calc(156px + env(safe-area-inset-bottom))}
}

/* an uploaded greeting: dashed while nothing is there, so an empty slot reads
   as optional rather than as something that failed to load */
.clip{margin-top:9px;padding:12px 14px;border:1px dashed var(--line-2);border-radius:var(--r-1);background:var(--paper)}
.clip__line{display:flex;flex-wrap:wrap;gap:3px 10px;align-items:baseline;font-size:13.4px}
.clip__line strong{color:var(--ink);overflow-wrap:anywhere}
.clip__meta{color:var(--muted-2);font-size:11.8px}
.clip__audio{display:block;width:100%;max-width:340px;height:34px;margin-top:10px}
.clip__acts{display:flex;gap:8px;flex-wrap:wrap;margin-top:10px}
.clip__hint{margin:8px 0 0;font-size:11.4px;color:var(--muted-2);line-height:1.5}
/* Kept in the layout and merely made invisible, rather than given the hidden
   attribute: Safari on a phone will not open a file picker from a programmatic
   click on an input that is display:none, and this is the one input where that
   click has to work. */
.clip__file{position:absolute;width:1px;height:1px;margin:-1px;padding:0;border:0;opacity:0;pointer-events:none;overflow:hidden}

/* ------------------------------------------------- settings pages, as a list */
/* One navigation in two forms: a list on a desktop rail, a dropdown on a phone.
   Exactly one of them is on screen — the list above 767px, the pick below it.
   A dropdown was chosen for the phone because a row of pills has to be swiped
   sideways there, and a page you cannot see all of is a page you do not use. That
   reasoning is about the phone: the rail is 205px wide and tall enough for all nine
   pages, so on a desktop a dropdown only hides eight of them behind a click. The id
   selector outranks the 767px rule that hides .frail. */
#settingsRail{display:flex}
#settingsRail .frail__list{display:flex;flex-direction:column;gap:2px}
#settingsRail .frail__pick{display:none}
#settingsRail .frail__pick-label{font-size:11px;font-weight:700;letter-spacing:.08em;
  text-transform:uppercase;color:var(--muted-2)}
#settingsRail select{width:100%}
@media (max-width:767px){
  /* Stacked above the page on a phone, so the divider belongs underneath. */
  #settingsRail{border-right:0;border-bottom:1px solid var(--line);padding:14px 16px;gap:10px}
  #settingsRail .frail__list{display:none}
  #settingsRail .frail__pick{display:flex;flex-direction:column;gap:6px}
  /* The rail's group label and the pick's own "Page" label go with the list: stacked above
     the dropdown they read as a second and third title, and the screen's heading already
     says "Settings". Hidden rather than deleted, because the desktop rail's list wants the
     group label back the moment the width returns. The select keeps its accessible name
     through its own aria-label — a `display:none` label leaves the tree with it. */
  #settingsRail .frail__g,#settingsRail .frail__pick-label{display:none}
}

/* -------------------------------------------------------- the centered popup
   The dashboard's Dial, New ticket and New contact buttons open one of these. It
   stays centered at every width rather than becoming a bottom sheet on a phone,
   because a centered popup is what was asked for; the card takes the width it is
   given and scrolls inside itself. z-index 66 puts it over the sidebar and the phone's rail
   (65) and under the control that ends a call left running (68), a sheet (75), the toast
   (78) and the menus (90) — the call control was deliberately above this popup, and raising
   the popup to 76 put a Hang up button over it: measured by the case that ends a call in
   progress from the control rather than from the popup's own button.

   Both `[hidden]` rules are deliberate. This file sets `display` on both elements,
   and the user agent's own `[hidden]{display:none}` loses to any author rule — the
   trap that has caught this stylesheet before, most recently in the workspace. */
.popwrap{position:fixed;inset:0;z-index:66;display:flex;align-items:center;justify-content:center;
  padding:16px;background:rgba(42,30,58,.44)}
.popwrap[hidden]{display:none}
.pop{position:relative;display:flex;flex-direction:column;width:min(560px,100%);
  max-height:min(86vh,760px);background:#fff;border-radius:18px;
  box-shadow:0 24px 60px rgba(42,30,58,.34);overflow:hidden}
.pop__head{display:flex;align-items:center;gap:10px;padding:15px 18px;border-bottom:1px solid var(--line)}
.pop__head h2{margin:0;font-family:var(--font-display);font-weight:800;font-size:18px}
.pop__x{margin-left:auto;min-height:36px;padding:0 6px;background:none;border:0;color:var(--muted);
  font:inherit;font-size:13px;font-weight:700;cursor:pointer}
.pop__x:hover{color:var(--ink)}
.pop__body{padding:18px;overflow-y:auto}
body.pop-open{overflow:hidden}
body.pop-open .fab{display:none}

/* The dialer is drawn as a pane beside the call log, and helpdesk's form as a card in
   the ticket queue. In the popup each one is the whole of what you see, so the frame
   it wears in its own screen comes off. */
.pop__body .pane--dialer{width:100%;background:none;border:0;padding:0;gap:15px}
.pop__body .hdnew{border:0;background:none;padding:0;margin:0}
/* The assistant is offered as a window as well, and it is a conversation rather than a
   form: wider and taller than the 560px column the forms use. It keeps the body's own
   scroller on purpose, because `.asst__composer` is `position:sticky;bottom:0` - that is
   what keeps the box on screen while the conversation scrolls behind it. An
   `overflow:hidden` body with the log as the only scroller (the arrangement `pop--wide`
   needs for its two panes) would clip the composer off a short phone instead. */
.pop--talk{width:min(620px,100%);max-height:min(88vh,820px)}
@media (max-width:767px){
  .popwrap{padding:12px}
  .pop__body{padding:15px}
}

/* ============================================================================
   Projects
   A board of columns that scrolls sideways, three other readings of the same
   cards, and the task screen. Two rules in here are deliberate and worth saying
   out loud:

     - `.pjcols` scrolls horizontally rather than wrapping. A board whose columns
       wrap is not a board: the whole point is that one column sits beside another
       so a card can be moved between them on sight.
     - The calendar is seven columns on a desktop and two on a phone, where the
       days of other months are hidden. Two columns of seven is not a calendar,
       and a month you cannot read is worse than a month you page through.

   Nothing here is a table, so nothing here is turned off below 760px by the rule
   that hides `.tablecard` - which is the trap the Numbers pane fell into.
   ========================================================================== */
.pjfind{display:flex;align-items:center;gap:6px;color:var(--muted)}
.pjfind .input{width:200px;height:34.6px}
.pjstats{display:grid;grid-template-columns:repeat(auto-fit,minmax(148px,1fr));gap:10px;margin-bottom:16px}
.pjstats[hidden]{display:none}
.pjstat{background:#fff;border:1px solid var(--line);border-radius:14px;padding:12px 15px;display:flex;flex-direction:column;gap:2px}
.pjstat[role="button"]{cursor:pointer}
.pjstat[role="button"]:hover{border-color:var(--pickup)}
.pjstat__val{font-family:var(--font-display);font-weight:800;font-size:22px;line-height:1.1}
.pjstat__label{font-size:12px;font-weight:600;color:var(--muted)}
.pjstat__hint{font-size:11.5px;color:var(--muted-2)}
.pjgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(268px,1fr));gap:12px}
.pjtile{background:#fff;border:1px solid var(--line);border-radius:16px;padding:14px 16px;display:flex;flex-direction:column;gap:9px}
.pjtile__top{display:flex;align-items:center;gap:8px}
.pjtile__name{margin:0;font-family:var(--font-display);font-weight:800;font-size:15.5px;flex:1;min-width:0;overflow-wrap:anywhere}
.pjtile__meta{margin:0;font-size:12.5px;color:var(--muted);overflow-wrap:anywhere}
.pjtile__foot{margin:0;display:flex;justify-content:space-between;gap:8px;font-size:12px;color:var(--muted);flex-wrap:wrap}
.pjtile__acts{display:flex;gap:7px}
.pjdot{width:9px;height:9px;border-radius:50%;flex:none}
.pjcode{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11.5px;font-weight:700;color:var(--deep);background:var(--mist);padding:1.5px 6px;border-radius:6px;text-decoration:none;white-space:nowrap}
.pjlate{color:var(--coral);font-weight:700}
.pjblocked{color:#8a6fb8;font-weight:700}
.pjmuted{color:var(--muted-2);font-size:12px}

/* My work and a project's list are the same row in two places. */
.pjgroup{margin-bottom:18px}
.pjgroup__head{display:flex;align-items:baseline;gap:8px;margin-bottom:8px;flex-wrap:wrap}
.pjgroup__head h3{margin:0;font-family:var(--font-display);font-size:14.5px;font-weight:800}
.pjgroup__count{background:var(--sand);border-radius:var(--r-pill);padding:1px 8px;font-size:12px;font-weight:700;color:var(--muted)}
.pjrows{display:flex;flex-direction:column;gap:6px}
.pjrow{display:flex;align-items:center;gap:10px;background:#fff;border:1px solid var(--line);border-radius:13px;padding:10px 13px}
.pjrow__main{flex:1;min-width:0}
.pjrow__title{font-weight:600;font-size:13.6px;color:var(--ink);text-decoration:none;overflow-wrap:anywhere}
.pjrow__title:hover{text-decoration:underline}
.pjrow__meta{margin:2px 0 4px;font-size:12px;color:var(--muted)}
.pjrow__who{flex:none}
.pjcheck{width:22px;height:22px;flex:none;border-radius:7px;border:1.6px solid var(--line-2);background:#fff;color:transparent;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;padding:0}
.pjcheck[aria-pressed="true"]{background:var(--pickup);border-color:var(--pickup);color:#fff}
.pjcheck--lg{width:27px;height:27px;border-radius:9px}
.pjchip{display:inline-block;height:19px;line-height:19px;padding:0 8px;border-radius:6px;font-size:11px;font-weight:700;color:var(--plum);margin:0 4px 4px 0}

/* One project. */
.pjhead__top{display:flex;align-items:center;gap:9px;flex-wrap:wrap}
.pjhead__top h2{margin:0;font-family:var(--font-display);font-weight:800;font-size:21px;overflow-wrap:anywhere}
.pjhead__acts{margin-left:auto;display:flex;gap:7px;flex-wrap:wrap}
.pjhead__meta{margin:8px 0 0;display:flex;flex-wrap:wrap;gap:4px 14px;font-size:12.6px;color:var(--muted)}
.pjhead__meta b{color:var(--ink);font-weight:600}
.pjhead__bar{margin-top:11px;display:flex;align-items:center;gap:12px;font-size:12px;color:var(--muted);flex-wrap:wrap}
.pjhead__bar .bar{flex:1;min-width:120px;max-width:320px}
.pjhead__note{margin:10px 0 14px;font-size:13px;color:var(--muted);max-width:70ch}
.pjcols{display:flex;gap:12px;overflow-x:auto;padding-bottom:8px;align-items:flex-start}
.pjcol{flex:0 0 268px;background:var(--paper);border:1px solid var(--line);border-radius:15px;padding:10px}
.pjcol__head{display:flex;align-items:center;gap:6px;padding:2px 4px 8px}
.pjcol__name{margin:0;font-size:12.4px;font-weight:800;letter-spacing:.02em;text-transform:uppercase;color:var(--muted)}
.pjcol__count{font-size:11.5px;color:var(--muted-2)}
.pjcol__body{display:flex;flex-direction:column;gap:8px;min-height:26px;border-radius:11px}
.pjcol__body.is-over{outline:2px dashed var(--pickup);outline-offset:2px}
.pjcol__empty{margin:0;padding:8px 4px;font-size:12px;color:var(--muted-2)}
.pjcol__add{width:100%;margin-top:8px;background:none;border:0;text-align:left;padding:6px;font:inherit;font-size:12.4px;font-weight:600;color:var(--muted);cursor:pointer;border-radius:9px}
.pjcol__add:hover{background:#fff;color:var(--ink)}
.pjcard{position:relative;background:#fff;border:1px solid var(--line);border-radius:12px;padding:10px 11px 9px;cursor:pointer}
.pjcard:hover{border-color:var(--line-2)}
.pjcard.is-dragging{opacity:.55}
.pjcard__cover{display:block;height:6px;border-radius:6px;margin:-4px 0 8px}
.pjcard__labels{margin-bottom:5px}
.pjcard h4{margin:0;font-size:13.4px;font-weight:700;line-height:1.35;overflow-wrap:anywhere}
.pjcard__meta{margin:5px 0 0;font-size:11.6px;color:var(--muted)}
.pjcard__foot{display:flex;align-items:center;gap:6px;margin-top:8px}
.pjcard__spacer{margin-left:auto}
.pjpill{background:var(--sand);border-radius:var(--r-pill);padding:1px 7px;font-size:11px;font-weight:700;color:var(--muted)}
.pjpri--urgent{color:var(--coral)}
.pjpri--high{color:#b07d18}
.pjcard__moves{display:flex;gap:4px;margin-top:7px;opacity:.6}
.pjcard:hover .pjcard__moves,.pjcard:focus-within .pjcard__moves{opacity:1}
.pjmove{min-width:22px;height:22px;padding:0 5px;border-radius:7px;border:1px solid var(--line);background:#fff;color:var(--muted);font:inherit;font-size:13px;line-height:1;cursor:pointer}
.pjmove:hover{color:var(--ink);border-color:var(--line-2)}

/* The calendar is a month of days, each holding what is due on it. */
.pjcal__bar{display:flex;align-items:center;gap:10px;margin-bottom:10px}
.pjcal__head{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));gap:6px;font-size:11.5px;font-weight:700;color:var(--muted-2);margin-bottom:4px}
.pjcal{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));gap:6px}
.pjcal__day{min-height:92px;background:#fff;border:1px solid var(--line);border-radius:11px;padding:6px}
.pjcal__day.is-out{background:var(--paper)}
.pjcal__day.is-today{border-color:var(--pickup);box-shadow:0 0 0 2px var(--mist)}
.pjcal__num{font-size:11.5px;font-weight:700;color:var(--muted)}
.pjcal__task{display:block;margin-top:4px;font-size:11.4px;background:var(--mist);color:var(--deep);border-radius:7px;padding:3px 6px;text-decoration:none;overflow-wrap:anywhere}
.pjcal__task.is-done{background:var(--sand);color:var(--muted);text-decoration:line-through}
.pjcal__task.is-late{background:var(--blush);color:#8c2f22}
.pjcal__undated{margin-top:10px}

/* ---------------------------------------------------------------- Calendar */
/* The workspace month: one grid of days and, beside it, the day that is picked.
   It borrows the project calendar's look on purpose - two months in one product
   that disagreed about a day's shape would be worse than either choice. */
.cal{display:grid;grid-template-columns:minmax(0,1fr) 300px;gap:16px;align-items:start}
.cal__grid{background:#fff;border:1px solid var(--line);border-radius:14px;padding:10px}
.cal__days{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));gap:6px;font-size:11.5px;font-weight:700;color:var(--muted-2);margin-bottom:4px}
.cal__cells{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));gap:6px}
.cal__day{display:flex;flex-direction:column;gap:4px;min-height:96px;background:#fff;border:1px solid var(--line);border-radius:11px;padding:6px;text-align:left;font:inherit;color:inherit;cursor:pointer}
.cal__day.is-out{background:var(--paper)}
.cal__day.is-today{border-color:var(--pickup);box-shadow:0 0 0 2px var(--mist)}
.cal__day.is-picked{border-color:var(--plum);box-shadow:0 0 0 2px var(--lilac)}
.cal__num{font-size:11.5px;font-weight:700;color:var(--muted)}
.cal__chips{display:flex;flex-direction:column;gap:3px;min-width:0}
.cal__ev{display:flex;gap:4px;align-items:baseline;font-size:11.2px;border-radius:7px;padding:3px 6px;overflow-wrap:anywhere}
.cal__ev.is-ours{background:var(--mist);color:var(--deep)}
.cal__ev.is-copy{background:var(--lilac);color:var(--plum)}
.cal__evt{font-style:normal;font-weight:700;opacity:.75}
.cal__evn{min-width:0;overflow-wrap:anywhere}
.cal__due{font-size:10.8px;color:var(--muted);overflow-wrap:anywhere}
.cal__due.is-done{text-decoration:line-through;color:var(--muted-2)}
.cal__more{font-size:10.8px;color:var(--muted-2)}
.cal__side{background:#fff;border:1px solid var(--line);border-radius:14px;padding:12px}
.cal__sideh{margin:0 0 8px;font-size:13.5px}
.cal__row{display:flex;gap:8px;justify-content:space-between;align-items:flex-start;padding:8px 0;border-top:1px solid var(--line)}
.cal__row:first-child{border-top:0}
.cal__rowmain{display:flex;flex-direction:column;gap:2px;min-width:0}
.cal__rowtitle{font-size:12.8px}
.cal__rowmeta,.cal__rownote{font-size:11.4px;color:var(--muted);overflow-wrap:anywhere}
.cal__rowacts{display:flex;gap:8px;flex:0 0 auto}
.cal__link{background:none;border:0;padding:0;font:inherit;font-size:11.6px;color:var(--deep);cursor:pointer;text-decoration:underline}
.cal__copynote{font-size:11.2px;color:var(--plum)}
.cal__warn{font-size:11.2px;color:#8c2f22;overflow-wrap:anywhere}
.cal__none{margin:0;font-size:12px;color:var(--muted)}
.cal__sub{margin:10px 0 4px;font-size:11.4px;text-transform:uppercase;letter-spacing:.04em;color:var(--muted-2)}
.cal__duerow{display:flex;justify-content:space-between;gap:8px;font-size:12px;padding:5px 0;border-top:1px solid var(--line);text-decoration:none;color:inherit}
.cal__duerow i{font-style:normal;color:var(--muted-2);font-size:11.2px}
.cal__duerow.is-done span{text-decoration:line-through;color:var(--muted-2)}
.cal__monthname{font-size:14px}
.cal__sync{display:flex;gap:10px;align-items:center;flex-wrap:wrap;font-size:11.8px;color:var(--muted);margin:0 0 12px}
/* The Integrations cards for the two calendars. The provider's own name is on
   these and nowhere else in the product. */
.intcard{display:flex;gap:10px;justify-content:space-between;align-items:center;padding:10px 0;border-top:1px solid var(--line)}
.intcard:first-of-type{border-top:0}
.intcard__main{display:flex;flex-direction:column;gap:2px;min-width:0}
.intcard__act{display:flex;gap:8px;align-items:center;flex:0 0 auto;flex-wrap:wrap;justify-content:flex-end}
/* The event form, in the shared popup. */
.calform__row{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px}
.calform__check{flex-direction:row;align-items:center;gap:8px}
.calform__hint{margin:6px 0 0;font-size:11.8px;color:var(--muted)}
.calform__hint.is-err{color:#8c2f22}
.calform__acts{display:flex;gap:10px;margin-top:12px}
/* `hidden` alone does not hide a `.field`, whose own rule sets `display`: the
   same trap the rail and the account menu each needed a rule for. */
.calform .field[hidden]{display:none}
@media (max-width:1099px){.cal{grid-template-columns:minmax(0,1fr)}}
@media (max-width:767px){
  .cal__grid{padding:6px}
  .cal__day{min-height:62px;padding:4px;gap:2px}
  /* A month grid is seven columns at any width, so below the phone breakpoint a
     day carries coloured dots and the words live in the list underneath it -
     which is where a person on a phone is reading anyway. */
  .cal__chips{flex-direction:row;flex-wrap:wrap}
  .cal__ev{padding:0;width:8px;height:8px;border-radius:50%;flex:0 0 auto}
  .cal__evt,.cal__evn,.cal__due{display:none}
  .calform__row{grid-template-columns:1fr}
}

/* Workload: what each person is carrying. */
.pjload{display:flex;flex-direction:column;gap:9px}
.pjload__row{background:#fff;border:1px solid var(--line);border-radius:13px;padding:11px 14px;display:grid;grid-template-columns:200px minmax(80px,1fr);gap:8px 14px;align-items:center}
.pjload__who{display:flex;align-items:center;gap:8px;font-size:13.4px;min-width:0}
.pjload__nums{margin:0;grid-column:1/-1;font-size:12px;color:var(--muted)}

/* One task: what it is on the left, how it is set up on the right. */
.pjd{display:grid;grid-template-columns:minmax(0,1fr) 316px;gap:16px;align-items:start}
.pjd__main{min-width:0;display:flex;flex-direction:column;gap:14px}
.pjd__head{display:flex;align-items:flex-start;gap:11px;flex-wrap:wrap}
.pjd__head h2{margin:0;font-family:var(--font-display);font-weight:800;font-size:20.5px;line-height:1.25;flex:1;min-width:0;overflow-wrap:anywhere}
.pjd__head h2.is-done{text-decoration:line-through;color:var(--muted)}
.pjd__acts{margin-left:auto;display:flex;gap:7px;flex-wrap:wrap}
.pjd__meta{margin:0;font-size:12.4px;color:var(--muted);display:flex;gap:8px;flex-wrap:wrap;align-items:center}
.pjd__notes{margin:0;font-size:13.6px;color:var(--ink);white-space:pre-wrap;line-height:1.55}
.pjd__block{background:#fff;border:1px solid var(--line);border-radius:14px;padding:13px 15px}
.pjd__blockhead{display:flex;align-items:center;gap:9px;margin:0 0 9px;flex-wrap:wrap}
.pjd__blockhead h3{margin:0;font-family:var(--font-display);font-size:14.4px;font-weight:800}
.pjd__blockhead .pjmuted,.pjd__blockhead .btn{margin-left:auto}
.pjd__side{display:flex;flex-direction:column;gap:12px;position:sticky;top:12px}
.pjd__side .field{margin-bottom:9px}
.pjsubs,.pjcomments,.pjtimes,.pjdeps{list-style:none;margin:0 0 9px;padding:0;display:flex;flex-direction:column;gap:5px}
.pjsub{display:flex;align-items:center;gap:8px;font-size:13.2px}
.pjsub>span{flex:1;min-width:0}
.pjsub.is-done>span{color:var(--muted);text-decoration:line-through}
.pjchecklist{border:1px solid var(--line);border-radius:12px;padding:9px 11px;margin-bottom:9px}
.pjchecklist__head{display:flex;align-items:center;gap:8px;font-size:12.8px}
.pjchecklist__head b{overflow-wrap:anywhere}
.pjchecklist ul{list-style:none;margin:6px 0 0;padding:0}
.pjchecklist li{display:flex;align-items:center;gap:8px;font-size:13px;padding:3px 0}
.pjchecklist li span{flex:1;min-width:0}
.pjitem.is-done span{color:var(--muted);text-decoration:line-through}
.pjchecklist label{display:flex;align-items:center;gap:8px;flex:1;min-width:0}
.pjinline{display:flex;gap:7px;flex-wrap:wrap;align-items:center;margin-top:8px}
.pjinline .input{flex:1;min-width:150px;height:33px}
.pjinline--tall{align-items:flex-start}
.pjinline--tall .input{width:100%}
.pjcomment{display:flex;gap:9px;font-size:13.2px}
.pjcomment__who{margin:0;font-size:12px;color:var(--muted)}
.pjcomment__body{margin:2px 0 0;white-space:pre-wrap;overflow-wrap:anywhere}
.pjdep{display:flex;align-items:center;gap:8px;font-size:13px;color:var(--muted)}
.pjdep a{color:var(--deep);text-decoration:none}
.pjdep.is-done a{color:var(--muted);text-decoration:line-through}
.pjtime{font-size:12.8px;color:var(--muted)}
.pjtime b{color:var(--ink)}
.pjfeed{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:9px}
.pjfeed__item p{margin:0;font-size:13px}
.pjfeed__item time{font-size:11.4px;color:var(--muted-2)}
.pjfeed--tight .pjfeed__item p{font-size:12.4px}
.pjlabels{display:flex;flex-wrap:wrap;gap:8px}
.pjlabelpick{display:inline-flex;align-items:center;gap:6px;font-size:12.4px}
.pjnew{background:#fff;border:1px solid var(--line);border-radius:14px;padding:13px 15px;margin-bottom:13px}
.pjnew .field{margin-bottom:9px}
.pjnew__acts{display:flex;gap:8px;justify-content:flex-end;margin-top:10px}

@media (max-width:900px){
  .pjd{grid-template-columns:minmax(0,1fr)}
  .pjd__side{position:static}
  .pjload__row{grid-template-columns:minmax(0,1fr)}
}
@media (max-width:760px){
  .pjstats{grid-template-columns:repeat(2,minmax(0,1fr))}
  .pjgrid{grid-template-columns:minmax(0,1fr)}
  .pjfind{flex:1 1 100%}
  .pjfind .input{width:100%}
  .pjhead__acts{margin-left:0}
  .pjcol{flex:0 0 244px}
  .pjcal__head{display:none}
  .pjcal{grid-template-columns:repeat(2,minmax(0,1fr))}
  .pjcal__day.is-out{display:none}
}

/* ============================================================================
   An incoming call
   The notice that says who is on the line, and the screen it opens: the customer on
   one half and the helpdesk on the other, so a call can be answered knowing what the
   person has open with us rather than after looking it up.

   Three rules here are load-bearing:

     - The two halves are a 1fr/1fr grid, which is what makes them halves of whatever
       the popup is. On a phone they stack, because two columns of a 390px screen is
       two columns of nothing — the same reading of the same information, in the order
       the popup's own scroller can carry.
     - Each half scrolls on its own, so a customer with thirty tickets does not push
       the customer's own details off the screen. That needs the body to stop being a
       scroller (`overflow:hidden` and a flex child), which is why `.pop--wide` reaches
       into `.pop__body` at all.
     - This popup is wider than every other one (`pop--wide`, 1100px against 560px).
       A half of 560px is 280px, and two columns of 280px are two columns of a phone.
   ========================================================================== */
.toast--call{align-items:flex-start;flex-wrap:wrap}
.toast__body{flex:1 1 190px;min-width:0;display:flex;flex-direction:column;gap:2px}
.toast__body b{font-weight:700}
.toast__body small{color:var(--nav);font-size:12.4px;line-height:1.35}
.toast--call button{flex:none;white-space:nowrap}
@media (min-width:768px){.toast--call{max-width:min(430px,100%)}}

.pop--wide{width:min(1100px,100%)}
.pop--wide .pop__body{flex:1;min-height:0;padding:0;overflow:hidden;display:flex}
.caller{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);width:100%;min-height:0}
.caller__pane{padding:17px 19px;overflow-y:auto;min-width:0;display:flex;flex-direction:column;gap:11px}
.caller__pane+.caller__pane{border-left:1px solid var(--line)}
.caller__eyebrow{margin:0;display:flex;gap:8px;align-items:baseline;font-size:11.4px;font-weight:700;
  letter-spacing:.06em;text-transform:uppercase;color:var(--muted-2)}
.caller__eyebrow span{font-weight:600;letter-spacing:0;text-transform:none;color:var(--muted)}
.caller__who{display:flex;align-items:center;gap:11px;min-width:0}
.caller__name{margin:0;font-family:var(--font-display);font-weight:800;font-size:18px;overflow-wrap:anywhere}
.caller__meta{margin:2px 0 0;font-size:12.6px;color:var(--muted);overflow-wrap:anywhere}
.caller__av--unknown{background:var(--sand);color:var(--muted)}
.caller__facts{margin:0;display:flex;flex-direction:column;gap:7px}
.caller__facts>div{display:grid;grid-template-columns:104px minmax(0,1fr);gap:10px;font-size:13.2px}
.caller__facts dt{margin:0;font-size:12.2px;font-weight:600;color:var(--muted-2)}
.caller__facts dd{margin:0;min-width:0;overflow-wrap:anywhere}
.caller__facts a{color:var(--deep)}
.caller__note{margin:0;padding:9px 11px;background:var(--paper);border-radius:11px;font-size:12.8px;color:var(--muted)}
.caller__acts{margin-top:auto;display:flex;gap:8px;flex-wrap:wrap;padding-top:4px}
.caller__tickets{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:7px}
.caller__ticket{display:flex;align-items:center;gap:9px;background:#fff;border:1px solid var(--line);
  border-radius:12px;padding:9px 11px;font-size:12.8px}
.caller__ticket a{flex:1;min-width:0;display:flex;gap:8px;align-items:baseline;color:var(--ink);text-decoration:none}
.caller__ticket a:hover span{text-decoration:underline}
.caller__ticket b{flex:none;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11.6px;color:var(--deep)}
.caller__ticket a span{min-width:0;overflow-wrap:anywhere}
.caller__tag{flex:none;background:var(--sand);border-radius:var(--r-pill);padding:1px 8px;font-size:11px;
  font-weight:700;color:var(--muted);text-transform:capitalize}
.caller__when{flex:none;font-size:11.4px;color:var(--muted-2)}
.caller__empty,.caller__more{margin:0;font-size:12.8px;color:var(--muted-2)}
@media (max-width:760px){
  .pop--wide .pop__body{display:block;overflow-y:auto}
  .caller{grid-template-columns:minmax(0,1fr)}
  .caller__pane{overflow:visible;padding:15px}
  .caller__pane+.caller__pane{border-left:0;border-top:1px solid var(--line)}
  .caller__facts>div{grid-template-columns:92px minmax(0,1fr)}
}

/* Billing → Payments. The payment provider draws its own form inside our page, so
   this only has to give it room and a surface. Deliberately NO display rule: the
   module adds and removes the frame, and a display rule here would outrank the
   hidden attribute if one is ever put on it — which is the trap that has cost this
   stylesheet two bugs already. An empty frame takes no room instead. */
.pay__frame{margin-top:16px}
.pay__frame:empty{display:none}
.pay__frame:not(:empty){min-height:520px}

/* ============================================================================
   Messages — the texts a line carries

   The panes and the bubbles are the design's own (.split, .pane--list, .conv,
   .conv__i, .thread, .msg--in/.msg--out, .composer); what is added here is the
   frame that makes an open conversation fill the second pane, and the phone's
   one-pane-at-a-time form.

   `is-thread` — set on the section by messages.js — is what a phone reads to show
   the open conversation instead of the list. It is a class and not the `hidden`
   attribute on purpose: `.pane--list` sets a background, and an author display rule
   outranks the user agent's `[hidden]`, which is the trap this stylesheet has
   already paid for twice.
   ============================================================================ */
.msgpane{display:flex;flex-direction:column;gap:11.5px}
.msgpane[hidden]{display:none}
.msghead{display:flex;align-items:center;gap:11.5px}
/* What can be done to the open conversation. It is a row of its own because the
   header above it holds a name that can be a full number. */
.msgacts{display:flex;gap:5.5px;flex-wrap:wrap}
.msgacts:empty{display:none}
/* The tray chips sit above the direction chips and share their look, because both
   are filter rows; what tells them apart is that this one counts its conversations
   and the one below it does not. */
.chips--tray{padding-bottom:0}
.chip__n{font-weight:700;opacity:.6}
.chip.is-on .chip__n{opacity:.85}
/* The conversation list's own notice, above the list and outside it: the press that
   archives a conversation closes the pane the other notice lives in. */
#msgListNotice{margin:0 11.5px 11.5px}
#msgListNotice[hidden]{display:none}
/* The way back to the list, which only a phone needs: above the breakpoint both
   panes are on screen and there is nothing to go back to. */
.msgback{display:none}
/* `.field` sets display:block and `.a-msg` sets its own, so neither is hidden by
   the attribute alone. */
.msgto[hidden]{display:none}
#msgBlocked[hidden]{display:none}
#msgNotice[hidden]{display:none}
.msgfoot{margin-top:auto;display:flex;flex-direction:column;gap:11.5px}
.msghint{font-size:12px;margin-top:5.8px}
.pane--thread{padding:17.3px 23.1px;gap:11.5px}
#msgCompose .composer{align-items:flex-end}
/* A text is one line of writing until somebody makes it two, so the box starts at
   a single row and follows what is typed. */
#msgCompose .composer textarea{
  border:0;outline:0;flex:1;font:inherit;font-size:13.9px;background:none;
  resize:none;max-height:132px;line-height:1.5;padding:0;
}
/* The line chooser is a control beside the box, not a field in a form: `.select` at
   width:100% would take the whole row. */
.msgfrom{width:auto;flex:none;max-width:190px}

/* The two-pane form. Only here does the thread get a height to scroll inside: at a
   phone's width the split has `height:auto`, and a flex item with min-height:0 in an
   auto-height container is a flex item of no height at all. */
@media (min-width:761px){
  .msgpane{flex:1;min-height:0}
  #msgThread{flex:1;min-height:0}
}

@media (max-width:760px){
  /* One pane at a time, chosen by whether a conversation is open. */
  #view-messages:not(.is-thread) .pane--thread{display:none}
  #view-messages.is-thread .pane--list{display:none}
  .msgback{display:inline-flex}
  .msgfoot{margin-top:11.5px}
}

/* ============================================================================
   Inbox — the email a workspace forwards into Kylosys

   The panes, the conversation rows and the bubbles are the design's own, exactly as
   they are on Messages (.split, .pane--list, .conv, .conv__i, .thread,
   .msg--in/.msg--out, .composer); what is added here is the frame an open
   conversation fills, the phone's one-pane form, and the two things email has that a
   text does not: a body that is somebody else's HTML, and files.

   `is-thread` — set on the section by inbox.js — is what a phone reads to show the
   open conversation instead of the list, and it is a class rather than the `hidden`
   attribute for the reason this stylesheet has already paid for twice: `.pane--list`
   sets a background, and an author display rule outranks the user agent's `[hidden]`.

   THE BODY IS A FRAME WITH A HEIGHT OF ITS OWN. An `<iframe>` with `height:auto` is
   150 pixels of nothing, so `.mailbody` is given a minimum and scrolls inside itself:
   a message is as long as somebody else decided it is, and the pane cannot grow to
   hold whatever arrives.
   ============================================================================ */
.mailpane{display:flex;flex-direction:column;gap:11.5px}
.mailpane[hidden]{display:none}
.mailhead{display:flex;align-items:center;gap:11.5px}
/* What can be done to the open conversation, in a row of its own: the heading above it
   holds an address that can be long. */
.mailacts{display:flex;gap:5.5px;flex-wrap:wrap}
.mailacts:empty{display:none}
/* The way back to the list, which only a phone needs. */
.mailback{display:none}
#mailListNotice{margin:0 11.5px 11.5px}
#mailListNotice[hidden]{display:none}
#mailNotice[hidden]{display:none}
.mailfoot{margin-top:auto;display:flex;flex-direction:column;gap:11.5px}
#mailCompose .composer{align-items:flex-end}
/* A reply is a letter: the box starts at three rows, can be dragged taller, and Enter
   begins another line rather than sending. */
#mailCompose .composer textarea{
  border:0;outline:0;flex:1;font:inherit;font-size:13.9px;background:none;
  resize:vertical;max-height:220px;line-height:1.5;padding:0;
}
/* THE BODY IS A FRAME WITH A HEIGHT OF ITS OWN, and a FIXED one rather than a minimum.
   An `<iframe>` with `height:auto` is 150 pixels of nothing; and a frame whose box is
   allowed to grow is a frame that grows a moment after the message arrives, which moves
   everything under it — measured on a phone viewport as a Send button that never settles
   enough to be pressed. The message scrolls inside the frame instead. */
.mailbody{width:100%;height:190px;border:1px solid var(--line);border-radius:11.5px;background:#fff}
.mailtext{white-space:pre-wrap;word-break:break-word;font-size:13.9px;line-height:1.55}
.mailatts{display:flex;flex-direction:column;gap:5.5px;margin-top:9.6px}
.mailatt{
  display:inline-flex;gap:8px;align-items:center;align-self:flex-start;font-size:12.5px;
  text-decoration:none;color:var(--deep);border:1px solid var(--line);border-radius:9.6px;
  padding:6.7px 9.6px;background:#fff;
}
.mailatt em{font-style:normal;color:var(--muted-2)}
.mailatt--off{color:var(--muted);background:var(--paper)}
/* Inside a bubble the row of controls sits under the body, on the left. */
.msg .mailacts{margin-top:9.6px;justify-content:flex-start}
/* The third pane's own list of facts about the sender. */
.siderows{display:flex;flex-direction:column;gap:6.7px;margin:11.5px 0}
.siderow{display:flex;justify-content:space-between;gap:11.5px;font-size:12.5px}
.siderow span{color:var(--muted)}
.siderow b{font-weight:700;word-break:break-all;text-align:right}
#mailSide .li__s{margin-bottom:11.5px}

/* Settings -> Email. An address that is copied has to be read letter by letter, so it
   is set in the mono face; and the per-provider forwarding instructions are a
   disclosure each, because a page of four sets of steps is a wall. */
.input--mono{font-family:var(--font-mono);font-size:12.5px}
.disclose{border-top:1px solid var(--line);padding:9.6px 0}
.disclose:first-of-type{border-top:0}
.disclose>summary{cursor:pointer;font-size:12.5px;font-weight:700;color:var(--deep);list-style:none}
.disclose>summary::-webkit-details-marker{display:none}
.disclose>summary::before{content:"+ ";color:var(--muted)}
.disclose[open]>summary::before{content:"\2212 "}
.disclose ol{margin:8px 0 0;padding-left:20px;display:flex;flex-direction:column;gap:5.8px}
.disclose li{font-size:12.5px;color:var(--muted);line-height:1.5}

/* The two-pane form. Only here does the thread get a height to scroll inside: at a
   phone's width the split has `height:auto`, and a flex item with min-height:0 in an
   auto-height container is a flex item of no height at all. */
@media (min-width:761px){
  .mailpane{flex:1;min-height:0}
  #mailThread{flex:1;min-height:0}
}

@media (max-width:760px){
  /* One pane at a time, chosen by whether a conversation is open. */
  #view-inbox:not(.is-thread) .pane--thread{display:none}
  #view-inbox.is-thread .pane--list{display:none}
  /* The third pane is a desktop reading: on a phone the sender's address is already in
     the heading of the open conversation. */
  #view-inbox .pane--side{display:none}
  .mailback{display:inline-flex}
  .mailbody{height:150px}
  /* THE COMPOSER IS PINNED ABOVE THE BAR, NOT LEFT AT THE END OF THE PAGE.
     The phone's tab bar and its Create button are `position:fixed` over the bottom 64 and
     80 pixels, and an overlay is not part of the layout: a Send button scrolled to the
     bottom of the viewport is under them, and it is the bar that answers the press.
     Measured on a 390x844 viewport, the click landed on the Helpdesk tab and on the Create
     button and never on Send. Sticky, the way the ticket reply box already is. */
  .mailfoot{
    position:sticky;bottom:calc(70px + env(safe-area-inset-bottom));z-index:20;
    margin-top:11.5px;padding-top:8px;background:var(--paper);
    border-top:1px solid var(--line)}
  /* The Create button floats over the bottom-right corner, and the Send button is in it:
     the composer keeps that much of the right-hand side clear so a press lands on Send and
     not on + New. Measured — the FAB's own icon was answering the tap. */
  #mailCompose .composer{padding-right:66px}
}

/* ============================================================================
   Toll-free verification, in Settings → Numbers

   A form the carriers read rather than a screen of ours, so it is deliberately
   plain: the panel's own card, and a disclosure for the fields that are optional
   to the phone system and still decide most first submissions.

   `.a-msg--warn` is added here because the message it carries is neither a failure
   nor a note: a rejected submission is a thing somebody has to act on, and the two
   modifiers that already existed say "done" and "broken".
   ============================================================================ */
.a-msg--warn{background:var(--amber);color:var(--warn)}
/* The state sentence is the first thing in the card and sits directly above the two-way
   chooser, or above the form itself. Its top margin is cancelled (the body's own padding
   frames it) and it now keeps 13px underneath, which is the notice's own margin: without it
   the rejection and the sentence telling somebody to correct it ran straight into the boxes
   below. The room is outside the box rather than inside it, so the notice is no taller. */
#verifyCard .card__body>.a-msg:first-child{margin-top:0;margin-bottom:13px}
#verifyCard details>summary{cursor:pointer;font-size:12.5px;font-weight:700;color:var(--muted);list-style:none}
#verifyCard details>summary::-webkit-details-marker{display:none}
#verifyCard details>summary::before{content:"+ "}
#verifyCard details[open]>summary::before{content:"- "}

/* A row in this card wraps instead of running past it, and a long label takes a second line
   rather than a wider button.

   Measured on staging 2026-10-01, after the check step was reported: the review step's own
   action row holds three controls — Back, "Send it to the carriers" and "Open the full form
   with these answers" — and `.btn` has `white-space:nowrap` while `.row` has no wrap, so that
   row was a fixed **557px at every width**. Its column is 514px at 900, 434 at 820, 382 at
   768, 333 at 430, 293 at 390 and 263 at 360, so from 900px down the card clipped it, from
   820 down the PANE scrolled sideways itself (609 of content in a 487px pane), and at 430 and
   below the whole PAGE did: 606px of content in a 430px window is the 176px of sideways
   scroll the owner saw, with the right-hand strip blank and that last button sitting in it.
   The review's own answer rows did the same thing on a narrower phone (423px of label and
   value in a 263px column at 360).

   Wrapping is what the first-login setup's action row already does (`.setup__acts`). The
   height rules are restated rather than inherited because `.btn` is a fixed 34.6px tall and a
   two-line label would spill out of it; `min-height` keeps a one-line button exactly the size
   it was, and the two smaller sizes keep their own heights so a row's little Edit control is
   untouched. Above 900px the row is 237px narrower than the card, so nothing here changes a
   desktop reading — `_deploy/probe-verify-check.mjs` prints the same boxes it did before. */
#verifyCard .row{flex-wrap:wrap}
#verifyCard .row>.btn:not(.btn--xs):not(.btn--sm){
  white-space:normal;height:auto;min-height:34.6px;padding-top:6px;padding-bottom:6px}

/* ============================================================================
   The first-login setup.

   A gate rather than a route: it covers the workspace until
   POST /api/account/onboarding records that the walk was finished, which is why it
   lives outside #views (index.html says why) and why it sits at z-index 95 - above
   the sidebar's 60, the sheets' 60 and the command palette's 90. A person who has
   not yet told the product who they are has nothing to jump to.

   `.setup` sets a display, so the ATTRIBUTE ALONE WOULD NOT HIDE IT: `[hidden]`
   from the user agent loses to any author rule, and `#setup[hidden]` is the rule
   that carries it, the same shape as `.view[hidden]` and `.acc__pane[hidden]`.
   ============================================================================ */
.setup{position:fixed;inset:0;z-index:95;display:flex;justify-content:center;overflow-y:auto;padding:26px;
  background:var(--sand)}
.setup[hidden]{display:none}
.setup__box{width:100%;max-width:672px;height:max-content;margin:auto;background:#fff;border:1px solid var(--line);
  border-radius:var(--r-3);box-shadow:var(--shadow-2);padding:26px 28px 22px}
.setup__head{margin-bottom:14px}
.setup__count{display:block;font-size:11.4px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--muted-2)}
.setup__head h2{font-family:var(--font-display);font-size:23px;font-weight:800;color:var(--ink);margin:5px 0 0}
.setup__dots{display:flex;flex-wrap:wrap;gap:14px;list-style:none;margin:0 0 18px;padding:0}
.setup__dot{display:flex;align-items:center;gap:6px;font-size:11.6px;font-weight:600;color:var(--muted-2)}
.setup__dot span{display:grid;place-items:center;width:19px;height:19px;border-radius:var(--r-pill);
  background:var(--sand);font-family:var(--font-mono);font-size:10.4px}
.setup__dot.is-on{color:var(--deep)}
.setup__dot.is-on span{background:var(--mist);color:var(--deep)}
.setup__lead{font-size:13.4px;color:var(--muted);margin:0 0 16px;line-height:1.55}
.setup__sub{font-size:12.2px;color:var(--muted-2);margin:-8px 0 14px;line-height:1.5}
.setup__h3{font-family:var(--font-display);font-size:14.4px;font-weight:800;color:var(--ink);margin:20px 0 6px}
.setup__grid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:13px 14px}
.setup__body .prods{margin-top:4px}
/* The Inbox name's two written-in-place nodes: the sentence about the name and the names
   nobody has. Both are drawn by the first-login setup and by Settings, email, so both
   need their own rule for `hidden` -- an author `display` beats the user agent's
   `[hidden]{display:none}`. */
.field__err[hidden]{display:none}
.namesug{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin:-2px 0 14px}
.namesug[hidden]{display:none}
.setup__msg{margin-top:14px;padding:10px 13px;border-radius:var(--r-1);background:var(--blush);color:#8a2b1d;
  font-size:12.8px;line-height:1.5}
.setup__msg[hidden]{display:none}
.setup__code{margin:6px 0 4px;padding:16px 18px;border:1px solid var(--line);border-radius:var(--r-2);background:var(--paper)}
.setup__code h3{font-family:var(--font-display);font-size:14.4px;font-weight:800;color:var(--ink);margin:0 0 6px}
.setup__code p.setup__sub{margin:0 0 12px}
.setup__code__value{font-family:var(--font-mono);font-size:26px;font-weight:700;letter-spacing:.12em;color:var(--deep);
  margin:0;user-select:all}
.setup .a-msg{margin-top:16px}
.setup__acts{display:flex;align-items:center;gap:10px;margin-top:20px;flex-wrap:wrap}
.setup__acts .btn--primary{margin-left:auto}
@media (max-width:760px){
  .setup{padding:0}
  .setup__box{border:0;border-radius:0;box-shadow:none;min-height:100%;max-width:none;padding:20px 16px 26px}
  .setup__grid{grid-template-columns:minmax(0,1fr)}
  .setup__head h2{font-size:20px}
  .setup__code__value{font-size:22px}
}
@media (max-width:620px){
  /* The labels would wrap into three lines on a phone; the count in the head already
     says which step this is, so only the step being filled in keeps its name. */
  .setup__dot{display:none}
  .setup__dot.is-on{display:flex}
}
/* ------------------------------------------------- Team & roles: two codes */
/* The short code carries the weight and the long one sits under it at the size a
   UUID deserves. Both wrap rather than widening the table, because a 36-character
   id in a column that also holds a name is what pushes a table off a phone. */
.teamcode{display:block;font-family:var(--font-mono);font-size:14.4px;font-weight:700;letter-spacing:.06em;color:var(--deep)}
.teamcode__long{display:block;font-size:10.4px;color:var(--muted-2);overflow-wrap:anywhere}
.teamjoin{display:flex;gap:9px;align-items:center;margin-top:12px;flex-wrap:wrap}
.teamjoin .input{flex:1 1 190px;min-width:0;font-family:var(--font-mono);letter-spacing:.08em;text-transform:uppercase}
.teamjoin__foot{display:flex;gap:9px;align-items:center;justify-content:space-between;margin-top:12px;
  padding-top:12px;border-top:1px solid var(--line);font-size:12.4px;color:var(--muted);flex-wrap:wrap}

/* ------------------------------------------------------- The assistant */
/* One screen and one column at every width: what was asked, what it answered, and
   under each answer the records it wrote with the way to take each one back.
   It is the only screen in the app with no table in it, so none of the phone's
   table-to-card machinery applies to it; what it does need on a phone is a field
   at 16px, because focusing anything smaller makes the browser zoom the page in
   and never back out, and a composer that stays reachable as the conversation
   grows. */
.asst{display:flex;flex-direction:column;gap:var(--gap);max-width:820px;margin:0 auto;width:100%}
.asst__head{display:flex;align-items:center;gap:12px}
.asst__mark{width:34px;height:34px;flex:0 0 auto;border-radius:var(--r-ico);display:grid;place-items:center;
  color:#fff;background:linear-gradient(160deg,#C77DFF,#7B2CE0)}
.asst__mark svg{width:18px;height:18px}
.asst__who{display:flex;flex-direction:column;line-height:1.3;min-width:0}
.asst__who b{font-family:var(--font-display);font-size:17px;color:var(--ink)}
.asst__sub{font-size:12.4px;color:var(--muted-2);overflow-wrap:anywhere}
.asst__promise{margin:0;padding:10px 13px;border:1px solid var(--line);border-radius:var(--r-1);
  background:var(--paper);font-size:12.6px;line-height:1.5;color:var(--muted)}
.asst__log{display:flex;flex-direction:column;gap:10px;min-height:0}
.asst__log:empty{display:none}
.asst__msg{max-width:min(660px,94%);padding:11px 14px;border-radius:var(--r-2);font-size:14px;line-height:1.5}
.asst__msg p{margin:0;white-space:pre-wrap;overflow-wrap:anywhere}
.asst__msg--user{align-self:flex-end;background:var(--plum);color:#fff;border-bottom-right-radius:var(--r-ico)}
.asst__msg--assistant{align-self:flex-start;background:var(--paper);border:1px solid var(--line);
  color:var(--ink);border-bottom-left-radius:var(--r-ico)}
.asst__msg--bad{align-self:flex-start;background:var(--bad-soft);border:1px solid #f0c6bf;color:var(--bad)}
.asst__think{align-self:flex-start;display:flex;align-items:center;gap:8px;font-size:12.8px;color:var(--muted-2)}
.asst__think svg{animation:asstspin 1.6s linear infinite}
@keyframes asstspin{to{transform:rotate(360deg)}}
/* The same wait, away from the assistant's own screen — the toll-free verification page's
   "write it for me" presses — in the middle of the window, because the words land in a box
   the person is looking at only after the answer arrives. The spark and the turn are the
   assistant's own (asstspin above), so the two read as one product, and it is above every
   layer a settings page can draw. `hidden` needs the rule beside it: this element is a
   flex box, and the user agent's own [hidden]{display:none} loses to a display of mine.
   `pointer-events:none` is deliberate: one of these writes starts by itself when the
   summary step opens, and a dimmed page that swallowed every press would read as a frozen
   app for as long as the model takes. The presses that would spend a second model call are
   refused by the page's own guards, not by where the cursor happens to be. */
.working{position:fixed;inset:0;z-index:85;display:flex;align-items:center;justify-content:center;
  padding:16px;background:rgba(42,30,58,.44);pointer-events:none}
.working[hidden]{display:none}
.working__card{display:flex;flex-direction:column;align-items:center;gap:9px;width:min(330px,100%);
  padding:22px 24px;border-radius:var(--r-2);background:#fff;box-shadow:var(--shadow-2);text-align:center}
.working__card svg{width:26px;height:26px;color:var(--plum);animation:asstspin 1.6s linear infinite}
.working__line{display:block;font-size:14.4px;color:var(--ink)}
.working__what{font-weight:600}
.working__secs{color:var(--muted-2);font-weight:400}
.working__note{font-size:12.4px;line-height:1.5;color:var(--muted)}
.asst__acts{margin-top:10px;padding-top:9px;border-top:1px dashed var(--line);
  display:flex;flex-direction:column;gap:7px}
.asst__act{display:flex;align-items:center;gap:8px;font-size:12.5px;color:var(--muted);min-width:0}
.asst__act svg{flex:0 0 auto;color:var(--ok)}
.asst__acttext{min-width:0;overflow-wrap:anywhere}
.asst__act--undone .asst__acttext{text-decoration:line-through;opacity:.7}
.asst__undone{font-size:12px;color:var(--muted-2);white-space:nowrap}
.asst__undo{flex:0 0 auto}
.asst__start{padding:14px;border:1px dashed var(--line-2);border-radius:var(--r-2);background:var(--paper)}
.asst__starth{margin:0 0 9px;font-size:11.6px;letter-spacing:.06em;text-transform:uppercase;color:var(--muted-2)}
.asst__examples{display:flex;flex-wrap:wrap;gap:8px}
.asst__ex{padding:9px 13px;border:1px solid var(--line);border-radius:var(--r-pill);background:#fff;
  font:inherit;font-size:13px;color:var(--ink);text-align:left;cursor:pointer}
.asst__ex:hover{border-color:var(--line-2);background:var(--oat)}
.asst__abilities{margin:11px 0 0;font-size:12.4px;line-height:1.5;color:var(--muted-2)}
.asst__composer{position:sticky;bottom:0;display:flex;flex-direction:column;gap:6px;
  padding:10px 12px;border-top:1px solid var(--line);background:var(--paper)}
.asst__label{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
.asst__box{display:flex;align-items:flex-end;gap:8px;padding:7px 7px 7px 12px;background:#fff;
  border:1px solid var(--line);border-radius:var(--r-2)}
.asst__box:focus-within{border-color:#7B2CE0;box-shadow:0 0 0 3px rgba(123,44,224,.12)}
.asst__text{flex:1 1 auto;min-width:0;border:0;background:transparent;resize:none;font:inherit;
  font-size:14px;line-height:1.5;color:var(--ink);padding:6px 0;max-height:132px;outline:none}
.asst__text:disabled{color:var(--muted-2)}
.asst__send{flex:0 0 auto;display:inline-flex;align-items:center;gap:7px}
/* `.asst__start` sets its own box and `#asstNew` is a `.btn`, so the attribute
   alone would not hide either of them: the same per-container rule every other
   container here carries. */
.asst__start[hidden],.asst__acts[hidden],.asst__think[hidden],
#asstNew[hidden],#asstNotice[hidden]{display:none}

@media (max-width:767px){
  .asst{gap:11px}
  .asst__msg{max-width:100%}
  .asst__promise{font-size:12.2px}
  .asst__ex{width:100%}
  /* 16px, or focusing the field makes a phone zoom the page in and never back out. */
  .asst__text{font-size:16px}
  .asst__send{width:44px;height:44px;padding:0;justify-content:center}
  .asst__send span{display:none}
  .asst__box{padding:6px 6px 6px 12px}
  .asst__undo{padding:9px 12px}
}

/* --------------------------------------------------------- Branding */
/* Two columns above 900px -- the mark on the left, the palettes on the right, because
   they are two different kinds of thing -- and one column below it. There is no table
   anywhere on this screen, so none of the phone's table-to-card machinery applies to
   it; the phone rules that matter are a 16px field, because focusing anything smaller
   zooms the page in and never back out, and a canvas small enough to leave the
   controls under it on the first screenful. */
.brd{display:flex;flex-direction:column;gap:var(--gap);max-width:1080px;margin:0 auto;width:100%}
.brd__head{display:flex;align-items:center;gap:12px;flex-wrap:wrap}
.brd__mark{width:34px;height:34px;flex:0 0 auto;border-radius:var(--r-ico);display:grid;place-items:center;
  color:#fff;background:linear-gradient(160deg,#F06BA8,#8E2A63)}
.brd__mark svg{width:18px;height:18px}
.brd__who{display:flex;flex-direction:column;line-height:1.3;min-width:0}
.brd__who b{font-family:var(--font-display);font-size:17px;color:var(--ink)}
.brd__sub{font-size:12.4px;color:var(--muted-2);overflow-wrap:anywhere}
.brd__grid{display:grid;grid-template-columns:minmax(0,380px) minmax(0,1fr);gap:var(--gap);align-items:start}
.brd__card{display:flex;flex-direction:column;gap:12px;padding:16px;background:#fff;
  border:1px solid var(--line);border-radius:var(--r-2);min-width:0}
.brd__h{margin:0;font-family:var(--font-display);font-size:15.5px;color:var(--ink)}
.brd__h4{margin:0;font-size:11.6px;letter-spacing:.06em;text-transform:uppercase;color:var(--muted-2)}
.brd__note{margin:0;font-size:12.6px;line-height:1.5;color:var(--muted-2)}
/* The canvas is square and never wider than its column: the drawing is 512px in the
   element's own attributes, so it stays crisp at any size it is shown at. */
.brd__canvas{width:100%;max-width:240px;height:auto;align-self:center;aspect-ratio:1/1;
  border:1px solid var(--line);border-radius:var(--r-2);background:var(--paper)}
.brd__summary{margin:0;font-size:12.4px;color:var(--muted);text-align:center;overflow-wrap:anywhere}
.brd__field{display:flex;flex-direction:column;gap:5px}
.brd__label{font-size:12px;font-weight:600;color:var(--muted)}
.brd__seg{display:flex;flex-wrap:wrap}
.brd__colours{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:8px 10px;align-items:center}
.brd__colour{width:46px;height:30px;padding:0;background:#fff;
  border:1px solid var(--line);border-radius:var(--r-1);cursor:pointer}
.brd__swatches{display:flex;flex-wrap:wrap;gap:7px;align-items:center}
.brd__swatch{width:30px;height:30px;border-radius:50%;cursor:pointer;
  border:1px solid rgba(0,0,0,.14)}
.brd__swatch.is-on{box-shadow:0 0 0 2px var(--ink)}
.brd__none{margin:0;font-size:12.4px;line-height:1.5;color:var(--muted-2)}
.brd__actions{display:flex;align-items:center;gap:9px;flex-wrap:wrap}
.brd__msg{margin:0;font-size:12.6px;line-height:1.5;color:var(--muted)}
.brd__msg.is-good{color:var(--ok)}
.brd__msg.is-bad{color:var(--bad)}
.brd__list{display:flex;flex-direction:column;gap:10px}
.brd__pal{padding:11px;border:1px solid var(--line);border-radius:var(--r-2)}
.brd__pal.is-active{border-color:var(--ink);background:var(--paper)}
.brd__palhead{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.brd__palname{font-size:13.4px;color:var(--ink);overflow-wrap:anywhere}
.brd__chips{display:flex;flex-wrap:wrap;gap:6px;margin:9px 0}
/* A chip copies its own hex: the point of a palette is carrying a colour somewhere
   else, and a colour a person has to retype is one they will get wrong. */
.brd__chip{width:34px;height:26px;border-radius:var(--r-1);cursor:pointer;
  border:1px solid rgba(0,0,0,.12)}
.brd__palactions{display:flex;gap:6px;flex-wrap:wrap}
.brd__editor{display:flex;flex-direction:column;gap:11px;padding:13px;background:var(--paper);
  border:1px dashed var(--line-2);border-radius:var(--r-2)}
.brd__rows{display:flex;flex-direction:column;gap:8px}
.brd__row{display:grid;grid-template-columns:auto minmax(0,1fr) auto;gap:9px;align-items:center}
.brd__hex{font-size:12.4px;color:var(--muted)}
/* `hidden` does nothing on an element whose own rule sets `display`: the editor is a
   flex column and the two messages are paragraphs this stylesheet gives a box, so the
   attribute alone would leave them on screen. Named for the screen rather than for each
   element, because everything in here that can be hidden is inside `.brd`. */
.brd [hidden]{display:none}

@media (max-width:900px){
  .brd__grid{grid-template-columns:minmax(0,1fr)}
}

@media (max-width:767px){
  /* 16px, or focusing a field makes a phone zoom the page in and never back out. */
  #brdText,#palName{font-size:16px}
  .brd__canvas{max-width:190px}
  .brd__palactions .btn{padding:9px 12px}
  .brd__actions .btn{padding:9px 13px}
  .brd__colour{width:52px;height:34px}
}

/* ------------------------------------------------------------------ choosing
   several rows, and clearing them in one press - Calls, Voicemail and Messages.

   The bar itself is the design's own `.bulkbar` (above the pager it was drawn with),
   used here for what its name says. What is added is the checkbox a row grows while
   choosing, the row that is chosen, and the line the question opens with.

   `.bulkhost` is the bar's container in its quiet state, so the Select button sits
   where the bar will be: a bar that appears out of nothing moves the whole list down
   the moment it is used. */
.bulkhost{display:flex;align-items:center;gap:11.5px;flex-wrap:wrap;margin:0 0 11.5px}
/* An author `display` beats the user agent's `[hidden]`, so hiding the bar needs a rule
   of its own. It is hidden where there are no rows to choose from. */
.bulkhost[hidden]{display:none}
.bulkbar{margin:0 0 11.5px}
.bulkbar__all{display:inline-flex;align-items:center;gap:6px;font-size:13.4px;cursor:pointer}
/* `.bulkbar button` says every button in it is oat on plum - right for Cancel, wrong for
   the one that deletes, which keeps the red it has everywhere else. The deck's underline
   on hover is for the text buttons it was drawn with, not for these. */
.bulkbar .btn{opacity:1;text-decoration:none}
.bulkbar .btn--bad{background:var(--coral);color:#fff}
.bulkbar .btn[disabled]{opacity:.45;cursor:default}

.selbox{width:15px;height:15px;flex:none;margin:0;cursor:pointer;accent-color:var(--deep)}
/* The direction glyph and the checkbox share the log's first cell. The glyph is a flex
   box of its own, so a bare sibling would push it onto a second line and every row in
   the log would grow the moment choosing began. */
.cellpick{display:inline-flex;align-items:center;gap:7px}
tr.is-sel td{background:var(--mist)}
/* A chosen conversation or message. A phone turns a table's rows into cards and reads
   the chosen state off the row (mobile.js writes the tag); this is the wide layout's
   own way of saying the same thing. While choosing, the row lines its box up with the
   middle of what it is beside rather than with the first line of it. */
.conv__i.is-pick{cursor:pointer;align-items:center}
.conv__i.is-sel{background:var(--mist)}

/* The line the question opens with. Not a hint: it is the question, so it is the card's
   own reading size, and the red box under it carries what cannot be undone. */
.pop__ask{margin:0 0 13px;font-size:14.4px;line-height:1.5}

/* --- the Terminal ------------------------------------------------------------

   A black-and-green terminal at the foot of the screen, half its height, over a scrim
   that closes it. It is the one overlay in /app/ that is NOT centered like `modal.js`'s
   popup: the popup is a question, and this is a place you type, so it takes the foot of
   the screen and leaves the top half of the page visible behind it.

   **Every number and colour here is the Terminal sheet design spec's** (2026-10-01),
   which is why they are hex literals rather than the app's palette: a terminal is its
   own surface, and the spec fixes #000 for the sheet, #3DF07C for output, #A8FFC8 for
   what the person typed, #1FA550 for system text, #1B5E34 for the handle and the
   hairlines, #FF5F56 for "error:" lines and #0B1F12 behind a key. `--t-font` is the
   spec's stack, and the first family in it is loaded by `app/index.html` beside the DM
   family the rest of the app uses. `_deploy/check-terminal-look.mjs` holds this file and
   `terminal.js` to those numbers, so a later edit cannot quietly undo the skin.

   **Where it sits in the z-index ladder, and why 76.** The ladder is written down at
   `.popwrap` (66), `.sheetwrap` (75) and `.toast` (78): a sheet is over a popup, and a
   toast is over everything that is not a menu. The terminal is a sheet, so it goes above
   the other sheets at 76 -- and stays UNDER the toast, so a toast about something else is
   never hidden by a terminal that happens to be open. The launcher (70), the palette (80),
   the phone's page sheet (80), the working overlay (85) and the menus (90) are all either
   transient or about the whole screen, and all of them are above this.

   **The height is a variable, not `50vh`.** `terminal.js` writes `--term-h` and
   `--term-bottom` from `visualViewport` while the sheet is open, because the operating
   system's keyboard does not change `vh`. The two detents are the spec's: DEFAULT is half
   the web viewport, EXPANDED is the visual viewport less 16pt. The fallbacks here are 50vh
   and 0, which is exactly the desk's measurement.

   **The page behind is locked, and the sheet never chains to it.** `terminal.js` adds
   `term-lock` to `<html>` while the sheet is open -- the same trick `body.pop-open` plays
   for the popup -- and the scrollback itself carries `overscroll-behavior:contain` and
   `touch-action:pan-y`, so a swipe that reaches the end of the transcript stops there
   instead of moving the page underneath. A press outside still closes the sheet: the scrim
   takes it, which is the behaviour that was asked for. */
.termwrap{position:fixed;inset:0;z-index:76;background:rgba(0,0,0,.35);
  opacity:0;transition:opacity var(--veil)}
.termwrap[hidden]{display:none}
.termwrap.is-on{opacity:1}
html.term-lock,html.term-lock body{overflow:hidden}
/* The same rule as the drawer's: a sheet on its way down takes no finger. The scrim is
   at `opacity:0` while the sheet falls, and an opacity of zero still receives a tap. */
.termwrap:not(.is-on){pointer-events:none}
.term{
  --t-bg:#000000;--t-fg:#3DF07C;--t-bright:#A8FFC8;--t-dim:#1FA550;--t-faint:#1B5E34;
  --t-error:#FF5F56;--t-key:#0B1F12;--t-select:#124825;
  --t-font:"JetBrains Mono","SF Mono",ui-monospace,Menlo,"Roboto Mono",monospace;
  position:absolute;left:0;right:0;bottom:var(--term-bottom,0);
  height:var(--term-h,50vh);max-height:var(--term-h,50vh);
  display:flex;flex-direction:column;
  background:var(--t-bg);color:var(--t-fg);
  font:400 .875rem/1.5 var(--t-font);font-variant-ligatures:none;letter-spacing:0;
  border-radius:20px 20px 0 0;overflow:hidden;
  transform:translateY(100%);
  transition:transform var(--rise-in);
}
.termwrap.is-on .term{transform:translateY(0)}
.termwrap:not(.is-on) .term{transition:transform var(--rise-out)}
.term.is-drag{transition:none}
.term::selection,.term ::selection{background:var(--t-select);color:var(--t-bright)}
/* The handle: 40x4, ten points below the top edge, and the drag handle for the header
   rows below it -- which is why both carry `touch-action:none`: without it a phone
   scrolls the page instead of telling this file about the drag. */
.term__grip{width:40px;height:4px;border-radius:2px;background:var(--t-faint);
  margin:10px auto 0;flex:none;touch-action:none;cursor:grab;
  -webkit-user-select:none;user-select:none}
/* 14 (the handle) + 44 = 58, and both words are bottom-aligned inside that 44, so their
   line boxes end together and the 18/24 title's baseline lands on the spec's 52 (58 less
   the 24px line box's own 6pt of descent and leading). The Close button is absolutely
   placed so that its 44x44 tap target does not stretch the row; the scrollback, 16 below
   it, starts at the spec's 74. */
.term__head{position:relative;height:44px;flex:none;padding:0 14px;display:flex;
  align-items:flex-end;touch-action:none}
.term__title{margin:0;font-family:var(--t-font);font-weight:700;font-size:1.125rem;line-height:1.5rem;
  color:var(--t-fg)}
.term__x{position:absolute;right:0;top:0;height:100%;min-width:44px;padding:0 19px 0 12px;
  background:none;border:0;color:var(--t-dim);font-family:var(--t-font);font-size:.875rem;
  line-height:1.3125rem;display:flex;align-items:flex-end;justify-content:flex-end;cursor:pointer}
.term__x:hover,.term__x:focus-visible{color:var(--t-bright)}
/* The scrollback: ONE scroll area holding every line, the live prompt included -- there
   is no field in a box below it, which is what makes the sheet scroll like a terminal.
   It starts where the head above ends plus 17, which is the spec's 74pt. */
.term__body{position:relative;flex:1 1 auto;min-height:0;margin-top:16px;
  display:flex;flex-direction:column}
.term__log{flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain;
  touch-action:pan-y;padding:6px 14px calc(10px + env(safe-area-inset-bottom));
  white-space:pre-wrap;overflow-wrap:anywhere;-webkit-user-select:text;user-select:text;
  scrollbar-width:thin;scrollbar-color:transparent transparent}
/* The chip's glide: the browser animates the assignment below it, and an engine with no
   `scroll-behavior` simply jumps -- which is why this is a class on the scrollback rather
   than an option on a scroll call. */
.term__log.is-gliding{scroll-behavior:smooth}
/* An overlay scrollbar three points wide, in the handle's green, and only while the
   person is actually moving: the class is added on scroll and taken off 600ms later. */
.term__log::-webkit-scrollbar{width:3px;height:3px}
.term__log::-webkit-scrollbar-track{background:transparent}
.term__log::-webkit-scrollbar-thumb{background:transparent;border-radius:2px}
.term__log.is-scrolling{scrollbar-color:var(--t-faint) transparent}
.term__log.is-scrolling::-webkit-scrollbar-thumb{background:var(--t-faint)}
.term__line{position:relative;margin:0;white-space:pre-wrap;overflow-wrap:anywhere;color:var(--t-fg)}
.term__line+.term__line{margin-top:6px}
/* What the reader typed, the prompt it was typed at, and the hint under it. */
.term__ps{color:var(--t-fg);font-weight:700}
.term__typed{color:var(--t-bright)}
.term__hint{color:var(--t-dim)}
.term__hint[hidden]{display:none}
/* A refusal. The spec's rule is that an error line carries the word "error:" as well as
   the colour, because colour alone is not a message. */
.term__line--err{color:var(--t-error)}
/* Something to know that is not an answer: the hours read back, a `^C`, or the honest
   refusal of a command whose product this workspace has switched off. */
.term__line--note{color:var(--t-dim)}
/* A line saying what the assistant DID, which is a different thing from what it said. */
.term__line--did{display:flex;gap:6px;color:var(--t-bright)}
.term__tick{width:12px;height:12px;flex:none;margin-top:4px}
/* The assistant's own words: the same size and colour as any other answer, because a reply
   is text and not a notification. */
.term__line--assistant{color:var(--t-fg)}
/* The cursor is a block, 8.4x17pt at this size, drawn where the caret is: 530ms on and
   530ms off, solid for 600ms after a keystroke (`.is-solid`), and an outline rather than
   a block while the prompt does not have the focus. */
.term__cursor{display:inline-block;width:.6em;height:17px;vertical-align:-3px;
  background:var(--t-fg);animation:blink 1.06s steps(1) infinite}
.term__cursor.is-solid{animation:none;opacity:1}
.term--blur .term__cursor{background:none;box-shadow:inset 0 0 0 1px var(--t-fg);animation:none}
@keyframes blink{50%{opacity:0}}
/* The busy spinner, drawn in the prompt's own place: one character, 80ms a frame. */
.term__spin{display:none;color:var(--t-dim)}
.term__line--live.is-busy .term__spin{display:inline}
.term__line--live.is-busy .term__ps,.term__line--live.is-busy .term__typed,
.term__line--live.is-busy .term__cursor,.term__line--live.is-busy .term__hint{display:none}
/* The field is real and invisible, stacked over the prompt line so that keyboards, IME and
   paste work; what a person reads is the mirror above it. 1rem keeps it at the 16px Safari
   insists on before it will focus a field without zooming the page in. */
.term__field{position:absolute;left:0;top:0;width:100%;height:100%;opacity:0;
  border:0;padding:0;margin:0;background:none;color:transparent;caret-color:transparent;
  font:400 1rem/1.5 var(--t-font)}
/* The key bar: 44pt of bar, 30pt keys, and each key's own tap target reaching the spec's
   44 through a negative margin, so the bar measures 44 and nothing is smaller than a
   finger. */
.term__keys{flex:none;display:flex;gap:8px;padding:7px 14px calc(7px + env(safe-area-inset-bottom));
  border-top:1px solid var(--t-faint)}
.term__keys[hidden]{display:none}
.term__key{flex:1 1 0;height:44px;margin:-7px 0;padding:0;background:none;border:0;
  display:flex;align-items:center;justify-content:center;cursor:pointer;font:inherit}
.term__keyin{display:flex;align-items:center;justify-content:center;height:30px;width:100%;
  border:1px solid var(--t-faint);border-radius:7px;background:var(--t-key);
  color:var(--t-bright);font-size:.8125rem;line-height:1rem}
/* The chip that says the view is behind the newest line: it appears when a person scrolls
   up, fades in over 150ms, and puts them back at the bottom when pressed. */
.term__latest{position:absolute;left:50%;bottom:12px;transform:translateX(-50%);
  min-height:44px;padding:8px 16px;border-radius:99px;border:1px solid var(--t-faint);
  background:var(--t-key);color:var(--t-bright);font-family:var(--t-font);
  font-size:.75rem;line-height:1rem;cursor:pointer;
  opacity:0;transition:opacity 150ms linear}
.term__latest[hidden]{display:none}
.term__latest.is-on{opacity:1}
/* Reduced motion: the sheet fades instead of sliding, the cursor stops blinking and the
   chip does not fade -- the last two of which `terminal.js` also honours for the typing
   effect and the 200ms scroll. */
@media (prefers-reduced-motion: reduce){
  .termwrap{transition:opacity 120ms linear}
  .term{transform:none;transition:opacity 120ms linear}
  .termwrap.is-on .term{transform:none}
  .termwrap:not(.is-on) .term{transition:opacity 120ms linear}
  /* The drawer loses the travel and keeps the direction: it fades where it stands, the
     same trade the Terminal's sheet makes here. `transform:none` also cancels the phone
     block's `translateY(100%)`, which is written earlier in the file and would otherwise
     leave the panel one screen below a reader who asked for less movement. */
  .lch__pop{transform:none;transition:opacity 120ms linear}
  .lch:not(.is-open) .lch__pop{transition:opacity 120ms linear}
  .lch__scrim{transition:opacity 120ms linear}
  .term__cursor{animation:none}
  .term__latest{transition:none}
  /* The two phone sheets and the drawer's own travel, on the same trade the Terminal's sheet
     and the launcher make above: the movement goes. The panels still change state, instantly. */
  /* Selectors at the weight of the declarations they cancel: a rise declared on
     `.sheetwrap:not([hidden]) .sheet` (0,3,0) is NOT overridden by a bare `.sheet` (0,1,0)
     however much later it is written — measured here, the panel still reported `sheet-rise`
     and was caught mid-travel by the same case that reads the fade above. */
  .msheet:not([hidden]) .msheet__panel,.sheetwrap:not([hidden]) .sheet{animation:none}
  .msheet:not([hidden]) .msheet__scrim,.sheetwrap:not([hidden])::before{animation:none}
  .msheet__panel,.sheet,.msheet__scrim,.sheetwrap::before{transition:none}
  .app.is-overlay .side{transition:none}
}
/* Two tiles are buttons now, because the Terminal opens this sheet instead of navigating
   to a screen: a button arrives with the user agent's own background, border and font, so
   the rail's tile and the launcher's reset them here rather than in two places. The
   phone's own bar's slot already carries this reset -- its Apps slot has always been a
   button. Nothing declares `padding` for the launcher's tile here: that rule's own
   `padding:0 4px 11px` is what lines the grid up, and an author rule beats the user
   agent's. */
button.navrail__tile{background:none;border:0;padding:0;font:inherit;cursor:pointer}
button.lch__tile{background:none;border:0;font:inherit;color:inherit;cursor:pointer}

@media (max-width:767px){
  /* Nothing about the sheet changes size on a phone -- the spec is drawn at 430pt -- but
     the key bar is the phone's own keyboard, so the room it takes comes off the bottom of
     the scrollback rather than off the sheet. */
  .term__keys{gap:6px;padding:7px 10px calc(7px + env(safe-area-inset-bottom))}
  .term__keyin{font-size:.75rem}
}

/* ============================================================================
   Invoices & Quotes — the document pane's own rows.
   The list rides on the shared `.tablecard`/`.tbl`/`.toolbar`/`.seg` shapes; what
   is here is only what no other screen draws: one document's head, its lines, the
   money against it, and the editor's line rows.
   ============================================================================ */
.invdoc{display:flex;flex-direction:column;gap:14px}
.invhead{display:flex;align-items:flex-start;gap:12px;flex-wrap:wrap}
.invhead__main{flex:1;min-width:0}
.invhead__kind{display:block;font-size:11.4px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--muted-2)}
.invhead__num{font-family:var(--font-display);font-size:22px;font-weight:800;line-height:1.15;overflow-wrap:anywhere}
.invhead__num--draft{font-size:18.4px;color:var(--muted)}
.invhead__meta{font-size:12.6px;color:var(--muted);margin-top:3px;overflow-wrap:anywhere}
.invacts{display:flex;flex-wrap:wrap;gap:8px}
/* A line and a payment are the same shape: what it was, what it came to. */
.invline{display:flex;align-items:baseline;gap:12px;padding:10px 0;border-bottom:1px solid var(--line)}
.invline:last-child{border-bottom:0;padding-bottom:0}
.invline__what{flex:1;min-width:0;overflow-wrap:anywhere}
.invline__what small{display:block;color:var(--muted-2);font-size:12px;margin-top:2px}
.invline__amt{font-weight:600;white-space:nowrap}
.invsum{margin-left:auto;width:100%;max-width:330px}
.invsum div{display:flex;justify-content:space-between;gap:14px;padding:4px 0;font-size:13.4px}
.invsum div span{font-weight:600}
.invsum div.invsum__total{border-top:1px solid var(--line);margin-top:6px;padding-top:9px;font-weight:700}
.invsum div.invsum__due{font-weight:700}
.invsum div.invsum__due span{font-family:var(--font-display);font-size:19px}
.invtext{font-size:13.4px;color:var(--ink);line-height:1.55;overflow-wrap:anywhere;white-space:pre-wrap}
.invlink code{display:block;font-family:var(--font-mono);font-size:12px;overflow-wrap:anywhere;
  background:var(--paper);border:1px solid var(--line);border-radius:var(--r-1);padding:9px 11px;margin:8px 0}
/* The editor's own line rows: what it is for, how many, at what price, and the way out. */
.invlin{display:grid;grid-template-columns:minmax(0,1fr) 78px 110px 34px;gap:7px;align-items:center;margin-bottom:7px}
/* The list's two decorative cells, which no other table carries. */
.ititle{color:var(--muted);max-width:38ch}
.tbl__dim{color:var(--muted);font-size:12.5px}
/* A cell that opens a document is a button here, and a button arrives with the user
   agent's own box: `.tbl__t` styles the text and nothing else. */
button.tbl__t{background:none;border:0;padding:0;font:inherit;text-align:left;cursor:pointer}
/* `hidden` does nothing on an element whose CSS sets `display`, and `.btn` sets it: the
   Overdue toggle carries `hidden` on the Quotes reading, where overdue is a question only
   an invoice can answer -- measured on staging, it was still on screen and still clickable,
   and the 767px toolbar's own scroll strip does not take it away either. */
#invOverdue[hidden]{display:none}
@media (max-width:767px){
  /* A phone gets the description on its own line and the three controls under it,
     rather than four columns squeezed into 390px. */
  .invlin{grid-template-columns:minmax(0,1fr) minmax(0,1fr) 34px}
  .invlin [name="lineDesc"]{grid-column:1 / -1}
}

/* ============================================================================
   The PDF preview — the sheet a document prints on, and the rules that print it

   A person writing an invoice reads it as the customer will BEFORE anything is
   generated: the document screen's "Preview the PDF" opens the shell's own popup
   holding this sheet, and the press inside it hands that same node to the browser's
   print dialog. One rendering, two uses — a mock-up beside the real thing would be
   the one thing a preview must not be.

   THE SHEET IS ITS OWN DOCUMENT. It carries its own paper, ink and greys rather than
   inheriting the app's theme, because the thing that prints has to read the same on a
   customer's printer whatever the workspace's screen looks like.

   AND ITS BLOCK IS `invsheet`, NOT `sheet`. `sheet` is already the phone's own bottom
   sheet — `.sheet__head`, `.sheet__who`, `.sheet__row`, `.sheet__grip` — so the first
   cut of this restyled both of them and measured a 0×0 sheet at 390px. Every class in
   here is prefixed; nothing borrows a name that already means something in this file.

   THE WINDOW'S HEIGHT IS DEFINITE (`pop--sheet`), because a flex column sized by its
   own content collapses: the stage is `flex:1` with `min-height:0`, so in a
   content-sized card it has no free space to take and shrinks to its padding — which
   is exactly what it did at 390px before this rule existed.
   ========================================================================== */
.pop--sheet{width:min(1100px,100%);height:min(88vh,900px)}
.pop--sheet .pop__body{flex:1;min-height:0;padding:0;overflow:hidden;display:flex}
.pdfprev{flex:1;min-width:0;display:flex;flex-direction:column}
.pdfprev__bar{display:flex;align-items:center;gap:10px;flex-wrap:wrap;padding:13px 18px;
  border-bottom:1px solid var(--line);background:var(--paper)}
.pdfprev__hint{flex:1;min-width:190px;color:var(--muted);font-size:12.6px;line-height:1.45}
.pdfprev__stage{flex:1;min-height:0;overflow:auto;padding:22px;background:#e9ecf2}

.invsheet{width:min(760px,100%);margin:0 auto;background:#fff;color:#111827;
  border:1px solid #d9dde5;border-radius:12px;padding:28px;font-size:14.4px;line-height:1.55;
  box-shadow:0 10px 26px rgba(20,16,32,.14)}
.invsheet__head{display:flex;justify-content:space-between;gap:16px;align-items:flex-start;flex-wrap:wrap}
/* The sender's half: the mark may be a wordmark rather than a square, so it is allowed to be
   as wide as one and shrinks to fit a phone instead of pushing the name off the edge. */
.invsheet__biz{display:flex;gap:14px;align-items:flex-start;min-width:0}
.invsheet__mark{display:block;max-height:64px;max-width:210px;width:auto;object-fit:contain}
.invsheet__who{font-weight:650;font-size:17px}
.invsheet__addr{color:#6b7280;font-size:14px;margin-top:5px}
.invsheet__reach{color:#6b7280;font-size:14px;margin-top:5px;display:flex;gap:8px;flex-wrap:wrap}
.invsheet__reach a{color:inherit}
.invsheet__meta{text-align:right;margin-left:auto}
.invsheet__kind{text-transform:uppercase;letter-spacing:.08em;font-size:12px;color:#6b7280}
.invsheet__num{font-size:20px;font-weight:650;margin-top:2px}
.invsheet__chip{display:inline-block;background:#f3f4f6;border-radius:999px;padding:3px 10px;
  margin-top:6px;font-size:12px;font-weight:600;text-transform:capitalize}
.invsheet__grid{display:grid;grid-template-columns:1fr 1fr;gap:18px;margin:24px 0}
.invsheet__grid h2,.invsheet__note h2{margin:0 0 6px;font-weight:700;font-size:11px;
  text-transform:uppercase;letter-spacing:.08em;color:#6b7280}
.invsheet__muted{color:#6b7280}
.invsheet__title{font-size:15px;margin:0 0 4px}
.invsheet__lines{width:100%;border-collapse:collapse}
.invsheet__lines th,.invsheet__lines td{text-align:left;padding:9px 8px;border-bottom:1px solid #e5e7eb;vertical-align:top}
.invsheet__lines th{font-size:11px;text-transform:uppercase;letter-spacing:.06em;color:#6b7280}
.invsheet__lines .n{text-align:right;white-space:nowrap}
.invsheet__none{color:#6b7280}
/* The sum, right-aligned, in the order a person checks it. */
.invsheet__rows{margin:14px 0 0 auto;max-width:330px}
.invsheet__rows .invsheet__row{display:flex;justify-content:space-between;gap:16px;padding:4px 0}
.invsheet__rows b{font-weight:600}
.invsheet__row--total{border-top:2px solid #111827;margin-top:6px;padding-top:10px;font-size:17px}
.invsheet__row--paid b{color:#047857}
.invsheet__note{margin-top:22px;padding-top:16px;border-top:1px solid #e5e7eb;
  white-space:pre-wrap;overflow-wrap:anywhere}
@media (max-width:620px){
  .invsheet{padding:20px 16px}
  .invsheet__grid{grid-template-columns:1fr}
  .pdfprev__stage{padding:14px}
  /* The hint is the first thing to go when the bar has to fit a phone: it took 134px of a
     366px bar and left the sheet's own stage 28px of it, measured at 390px. The two buttons
     say what they do without it. */
  .pdfprev__hint{display:none}
  .pdfprev__bar{flex-wrap:nowrap;padding:11px 14px}
  /* The two presses keep to the right of the bar, where the hint was holding them. */
  .pdfprev__bar .btn:first-of-type{margin-left:auto}
}

@media print{
  body{background:#fff}
  body.pop-open{overflow:visible}
  /* GATED ON THE POPUP, and that gate is the difference between a preview and a blank page:
     a bare `body > *:not(.popwrap)` would take the app away even when no popup is open, so a
     person pressing Ctrl-P on a screen they were reading would print nothing at all. With a
     popup open — which is what the preview is — the app goes and the popup stays. */
  body.pop-open > *:not(.popwrap){display:none !important}
  body.pop-open .popwrap{position:static;inset:auto;z-index:auto;display:block;padding:0;background:none}
  /* The sheet is a page, not a scrolling window: the card's own height goes too, or a long
     document prints clipped to the one screen the preview was sized to. */
  body.pop-open .pop{width:auto;max-width:none;height:auto;max-height:none;border:0;border-radius:0;
    box-shadow:none;overflow:visible}
  body.pop-open .pop__head{display:none !important}
  body.pop-open .pop__body{padding:0;overflow:visible;display:block;flex:none}
  .pdfprev{display:block}
  .pdfprev__bar{display:none !important}
  .pdfprev__stage{padding:0;background:none;overflow:visible}
  .invsheet{width:auto;max-width:none;margin:0;border:0;border-radius:0;box-shadow:none;padding:0;
    -webkit-print-color-adjust:exact;print-color-adjust:exact}
  /* A line, a sum and a note are things a page break must not cut through. */
  .invsheet__lines tr,.invsheet__rows .invsheet__row,.invsheet__note{break-inside:avoid}
  @page{size:A4;margin:14mm}
}