/* Inter, served from this host. The variable font covers 200-800 in one
   file per subset, so the whole family is two requests and no third party.
   Subsets and unicode-ranges are Google's, the files are ours. */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 200 800;
  font-display: swap;
  src: url('/assets/fonts/inter-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 200 800;
  font-display: swap;
  src: url('/assets/fonts/inter-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

/* HelpStack — Team Chat / Internal Notes.
   Measurements come from the Figma frames "Team Chat" (104:20) and
   "Internal Notes" (229:326), both 1611x951. Column widths are fixed at the
   design's values and the conversation column takes the slack, so the layout
   is exact at 1611px and still usable either side of it. */

:root {
  --rail: #14171b;
  --panel: #fbfbfc;
  --surface: #ffffff;

  /* Every line that separates one section from the next. They were pitched
     for a white page and all but disappeared on a dark one, so both sets carry
     more contrast now and dark carries the most. */
  --line: #e3e3e4;
  --line-soft: #e6e6e6;
  --line-panel: rgba(164, 164, 164, 0.28);
  --line-input: #ced3da;

  --ink: #333333;
  --ink-strong: #000000;
  --muted: #828282;

  --blue: #2f80ed;
  --blue-wash: rgba(30, 161, 236, 0.1);
  /* Menu hover: the accent at 12%, with the accent itself on the label and
     its icon. */
  --blue-12: rgba(47, 128, 237, 0.12);
  /* Menu icons sit well below the label at rest — present, not competing. */
  --icon-rest: #b4bcc6;

  --bubble-in: #f0f0f0;
  --button-grey: #e0e0e0;
  /* Every hover state a button can have is a token, because in dark mode the
     direction reverses: a grey that goes darker on white has to go lighter
     here, and a literal hex cannot. */
  --button-grey-hover: #d6d6d6;
  --blue-hover: #2a74d6;
  --danger-hover: #9c211a;
  /* Destructive controls are tinted rather than grey: the colour is the
     warning, and a red word on a grey button was doing that work alone. */
  --danger-wash: rgba(179, 38, 30, 0.1);
  --danger-wash-hover: rgba(179, 38, 30, 0.17);

  --note-bg: rgba(242, 153, 74, 0.15);
  /* The note bubble's edge is there to stop the amber fill floating, not to
     be seen. Halved: at 0.04 it read as an outline drawn around the bubble. */
  --note-border: rgba(0, 0, 0, 0.02);
  --note-ink: #492e16;
  --orange: #f2994a;
  --composer-note: #fcf0e4;
  /* Note mode as glass rather than as a solid fill. Thinning #fcf0e4 directly
     would wash it out, so the tint is pitched deeper: at this alpha over the
     white thread it resolves to exactly #fcf0e4 at rest, and still shifts when
     a message passes behind it. It has to be genuinely transparent, because a
     note bubble over white is rgb(253,240,228) — the same colour the panel
     used to be, which left the frost with nothing to show. */
  --composer-note-glass: rgba(250, 228, 206, 0.55);

  --page: #fafafa;
  --glyph-filter: none;
  /* A filled dark button stays a filled dark button. It is not text, so it
     must not follow --ink when --ink turns light. */
  --solid: #333333;
  --solid-hover: #262626;
  --solid-ink: #f2f2f2;
  --badge-muted: #bdbdbd;
  --badge-muted-ink: #ffffff;
  /* The accent is lighter in dark mode, so the count on it goes dark rather
     than white — the same swap --solid-ink makes for a filled button. */
  --badge-ink: #ffffff;
  /* A quiet pill: the count beside a rule, the On/Off beside its name. Light
     in both themes, so the ink is what changes. */
  --chip: #eef0f3;
  --chip-ink: #828282;
  /* The arrow on a select. A background image cannot take currentColor, so it
     carries one copy per theme, like the menu pointer does. */
  --select-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none'%3E%3Cpath d='M3 5L8 10L13 5' stroke='%23828282' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  /* The search pill has its own ink: a navy that reads as typed-in rather than
     as body copy. */
  --search-ink: #183356;
  --composer-glass: rgba(255, 255, 255, 0.72);
  --hairline: rgba(0, 0, 0, 0.08);
  --menu-shadow: 0 6px 11px 0 rgba(0, 0, 0, 0.15), 0 14px 28px rgba(0, 0, 0, 0.1),
    0 0 0 1px rgba(0, 0, 0, 0.05);
  --danger: #b3261e;
  --ok: #219653;

  /* The pointer that ties a menu to the thing that opened it. It is a filled
     shape, not a glyph, so its fill has to be the menu's own surface — hence
     one copy per theme rather than a filter. */
  --menu-tab: url(data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20width=%2218%22%20height=%227%22%20viewBox=%220%200%2018%207%22%3E%3Cpath%20d=%22M11.2443%203.26415L11.4336%203.4779L13.0176%205.17003C13.9841%206.20241%2015.3553%206.7912%2016.7932%206.7912H18.722C19.9801%206.7912%2021%207.78014%2021%209H-1.7457C-1.7457%207.78014%20-0.725802%206.7912%200.532331%206.7912H0.945415C1.86533%206.7912%202.74242%206.41453%203.36071%205.75411L5.49159%203.4779L5.75514%203.1954C7.23311%201.61144%209.80892%201.64375%2011.2443%203.26415Z%22%20fill=%22%23ffffff%22/%3E%3C/svg%3E);
  --menu-tab-shadow: rgba(169, 179, 187, 0.5);

  --font: 'Inter', ui-sans-serif, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;

  /* Radius multiplier. 1 everywhere, 2 where the corners are squircles — see
     the block under the dark palette. */
  --rx: 1;

  color-scheme: light;
}

/* ---------------------------------------------------------- squircles -- */

/* corner-shape draws a superellipse instead of a quarter circle. The curve
   starts turning further from the corner and finishes tighter, so the same
   number reads as less rounding — which is why the radius doubles at the same
   time. Both live behind one @supports: a browser without corner-shape keeps
   the radii exactly as they were rather than getting twice the rounding in the
   wrong shape.
   The keyword, not superellipse(). `squircle` computes to superellipse(2) and
   that is the shape, not a fallback: the function's argument is the log2 of
   the curvature exponent, so round is superellipse(1) — exponent 2, a quarter
   ellipse — and squircle is superellipse(2), exponent 4. superellipse(4) is
   exponent 16, which is a corner with barely a curve left in it.
   Everything is in, including the circles: a 50% squircle is the shape this
   is named after. */
@supports (corner-shape: squircle) {
  :root {
    --rx: 2;
  }

  *,
  *::before,
  *::after {
    corner-shape: squircle;
  }
}

/* ------------------------------------------------------------ dark mode -- */

/* One palette swap, in one block. The system preference is resolved to this
   same attribute before the first paint, so there is no second copy of these
   values living inside a media query — which there was, and the two had already
   drifted apart by three tokens. */
:root[data-theme='dark'] {
  --rail: #0c0e11;
  --panel: #15181c;
  --surface: #1b1f24;
  --page: #101316;
  /* The icon files are flat #333333, drawn for light surfaces. Inverting lands
     them near #cccccc, which is what an icon on this ground wants. */
  --glyph-filter: invert(1) brightness(0.92);
  --solid: #e4e8ed;
  --solid-hover: #ffffff;
  --solid-ink: #15181c;
  --badge-muted: #c6cbd1;
  --badge-muted-ink: #24282e;
  --badge-ink: #15181c;
  --chip: #dfe4ea;
  --chip-ink: #24282e;
  --select-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none'%3E%3Cpath d='M3 5L8 10L13 5' stroke='%238b95a1' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  --search-ink: #dbe2ea;

  --line: #343b44;
  --line-soft: #2e343c;
  --line-panel: rgba(255, 255, 255, 0.14);
  --line-input: #454e59;

  --ink: #e4e8ed;
  --ink-strong: #ffffff;
  --muted: #8b95a1;

  --blue: #4d94f0;
  --blue-wash: rgba(77, 148, 240, 0.16);
  --blue-12: rgba(77, 148, 240, 0.16);
  --icon-rest: #6c7784;

  --bubble-in: #262c33;
  --button-grey: #2b313a;

  --note-bg: rgba(242, 153, 74, 0.16);
  --note-border: rgba(255, 255, 255, 0.03);
  --note-ink: #f2ddc6;
  --composer-note: #2e2418;
  --composer-note-glass: rgba(66, 50, 32, 0.62);

  --composer-glass: rgba(27, 31, 36, 0.72);
  --hairline: rgba(255, 255, 255, 0.12);
  --menu-shadow: 0 6px 14px rgba(0, 0, 0, 0.5), 0 18px 40px rgba(0, 0, 0, 0.4),
    0 0 0 1px rgba(255, 255, 255, 0.09);
  --button-grey-hover: #353d47;
  --blue-hover: #6ba6f3;
  --danger-hover: #f58379;
  --danger-wash: rgba(242, 100, 90, 0.15);
  --danger-wash-hover: rgba(242, 100, 90, 0.22);
  --menu-tab: url(data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20width=%2218%22%20height=%227%22%20viewBox=%220%200%2018%207%22%3E%3Cpath%20d=%22M11.2443%203.26415L11.4336%203.4779L13.0176%205.17003C13.9841%206.20241%2015.3553%206.7912%2016.7932%206.7912H18.722C19.9801%206.7912%2021%207.78014%2021%209H-1.7457C-1.7457%207.78014%20-0.725802%206.7912%200.532331%206.7912H0.945415C1.86533%206.7912%202.74242%206.41453%203.36071%205.75411L5.49159%203.4779L5.75514%203.1954C7.23311%201.61144%209.80892%201.64375%2011.2443%203.26415Z%22%20fill=%22%231b1f24%22/%3E%3C/svg%3E);
  --menu-tab-shadow: rgba(0, 0, 0, 0.45);

  --danger: #f2645a;
  --ok: #4ac97e;

  color-scheme: dark;
}

* { box-sizing: border-box; }

/* Inter's stylistic and character variants, on everywhere. The universal
   selector is deliberate: the `font:` shorthand resets font-feature-settings
   to normal, and several inputs here use it, so inheriting from body alone
   would quietly drop the features on exactly the text you type. */
*,
*::before,
*::after {
  font-feature-settings: 'ss03' on, 'cv03' on, 'cv04' on, 'cv06' on, 'cv09' on, 'cv11' on;
}

p,
dl { margin: 0; }

/* Any display rule on an element outranks the browser's own
   [hidden] { display: none }, which has quietly un-hidden things here more
   than once — the saved replies panel, the send icon, the New button.
   Settle it once for everything. */
[hidden] { display: none !important; }

html, body {
  height: 100%;
  margin: 0;
}

body {
  font-family: var(--font);
  color: var(--ink);
  background: var(--surface);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

button {
  font: inherit;
  color: inherit;
  border: 0;
  background: none;
  padding: 0;
  cursor: pointer;
}

/* ---------------------------------------------------------------- shell -- */

.app {
  position: relative;
  display: flex;
  height: 100vh;
  overflow: hidden;
}

.col-head {
  height: 81px;
  flex: 0 0 81px;
  border-bottom: 1px solid var(--line);
  display: flex;
  align-items: center;
}

/* ----------------------------------------------------------------- rail -- */

.rail {
  width: 81px;
  flex: 0 0 81px;
  background: var(--rail);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 30px;
  padding: 20px 0;
}

/* The workspaces are rendered into a wrapper, and `display: contents` lifts
   them back out so the rail lays them out itself. Spacing is the rail's `gap`
   and not a margin between adjacent items, because an adjacent-sibling
   selector cannot see across this wrapper — it would leave the first
   workspace touching the mark above it. */
.contents { display: contents; }

.rail-item {
  width: 40px;
  height: 40px;
  display: grid;
  place-items: center;
  flex: 0 0 auto;
}

.rail-item img { display: block; }

.rail-logo img { width: 40px; height: 40px; }

.rail-app,
.rail-tile {
  position: relative;
}

.rail-app img { width: 40px; height: 40px; border-radius: 50%; }

.rail-tile img { width: 40px; height: 40px; border-radius: calc(10px * var(--rx)); }

/* Selection without drawing a shape around the mark. These are brand logos —
   some circular in the file, some square, some with their own padding — so a
   ring fits one of them and sits wrong on every other. Weight and a bar at the
   rail's edge say the same thing and do not care what shape the logo is. */
.rail-app,
.rail-tile {
  opacity: 0.5;
  transition: opacity 140ms ease;
}

.rail-app:hover,
.rail-tile:hover { opacity: 0.8; }

.rail-app.is-active,
.rail-tile.is-active { opacity: 1; }

.rail-app.is-active::after,
.rail-tile.is-active::after {
  content: '';
  position: absolute;
  /* The rail is 81 wide and the item 40, centred — 20.5 either side — so this
     lands flush against the left edge of the rail. */
  left: -20.5px;
  top: 50%;
  width: 3px;
  height: 24px;
  transform: translateY(-50%);
  border-radius: 0 3px 3px 0;
  background: #ffffff;
}

.rail-add img { width: 40px; height: 40px; opacity: 0.45; }

.rail-spacer { flex: 1 1 auto; }

.rail-small {
  width: 24px;
  height: 24px;
  display: grid;
  place-items: center;
  opacity: 0.45;
  flex: 0 0 auto;
}

/* The cell stays 24 so the rail's spacing does not move, but the art is drawn
   at 20 and is rendered at 20 — stroke weights are per size. */
.rail-small img { width: 20px; height: 20px; }

.rail-me {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  overflow: hidden;
  flex: 0 0 auto;
}

.rail-me img { width: 100%; height: 100%; object-fit: cover; }

/* -------------------------------------------------------------- sidebar -- */

.sidebar {
  position: relative;
  width: 233px;
  flex: 0 0 234px;
  border-right: 1px solid var(--line);
  background: var(--panel);
  display: flex;
  flex-direction: column;
}

.sidebar-head {
  position: relative;
  padding: 0 25px;
  justify-content: space-between;
}

.ws-toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  height: 100%;
}

.ws-icon {
  display: none;
  width: 24px;
  height: 24px;
}

.ws-title {
  font-size: 15px;
  font-weight: 800;
  letter-spacing: -0.1px;
  color: var(--ink);
}

.sidebar-head .chevron { width: 12px; height: 6px; opacity: 0.85; }

.sidebar-body {
  flex: 1 1 auto;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
}

.nav-section {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 8px 10px;
  border-bottom: 1px solid var(--line);
}

.nav-section:last-child { border-bottom: 0; }

.nav-title {
  height: 36px;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 10px 10px 15px;
  border-radius: calc(10px * var(--rx));
}

.nav-title span {
  flex: 1 1 0;
  min-width: 0;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.77px;
  text-transform: uppercase;
  opacity: 0.5;
}

.nav-title img { width: 16px; height: 16px; }

.nav-item {
  width: 100%;
  height: 42px;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 10px 10px 15px;
  border-radius: calc(10px * var(--rx));
  text-align: left;
}

/* Same treatment as the dropdown rows: the accent at 12% behind the accent
   itself, carried by the label and the icon together. */
.nav-item:hover,
.nav-item.is-active {
  background: var(--blue-12);
  color: var(--blue);
}

/* Weight is what separates the row you are on from the row under the cursor. */
.nav-item.is-active .nav-label { font-weight: 800; }

.nav-label {
  flex: 1 1 0;
  min-width: 0;
  font-size: 14px;
  font-weight: 400;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.nav-icon {
  width: 16px;
  height: 16px;
  flex: 0 0 16px;
  display: grid;
  place-items: center;
  font-size: 14px;
  line-height: 1;
}

.nav-icon img { width: 16px; height: 16px; display: block; }

/* A flat glyph masked out of the row's own colour, which is the whole reason
   these are not <img>: an <img> cannot follow the row into hover. */
.nav-ink {
  width: 16px;
  height: 16px;
  display: block;
  background: currentColor;
  -webkit-mask: var(--i16) center / contain no-repeat;
  mask: var(--i16) center / contain no-repeat;
}

.nav-icon picture {
  display: block;
  width: 100%;
  height: 100%;
}

.nav-icon img.is-round {
  border-radius: 50%;
  object-fit: cover;
}

.nav-initial.is-shown {
  display: grid;
  place-items: center;
}

.nav-initial {
  display: none;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  color: var(--muted);
}

.nav-glyph {
  font-size: 15px;
  font-weight: 500;
  color: var(--ink);
  opacity: 0.85;
}

.badge {
  height: 22px;
  min-width: 22px;
  padding: 4px 7px;
  border-radius: 68px;
  background: var(--blue);
  color: var(--badge-ink);
  font-size: 12px;
  font-weight: 500;
  line-height: 14px;
  text-align: center;
  flex: 0 0 auto;
}

/* The muted badge is a light grey chip in both themes — a count nobody needs
   to act on. White numerals only work on the darker one. */
.badge.is-muted { background: var(--badge-muted); color: var(--badge-muted-ink); }

/* Except on the row you are on, where it is part of the selection. */
.nav-item.is-active .badge.is-muted,
.conv.is-active .badge.is-muted {
  background: var(--blue);
  color: var(--badge-ink);
}

.badge.is-grey { background: var(--button-grey); color: var(--muted); }

.nav-tool { height: auto; padding: 10px 10px 10px 15px; }

/* ---------------------------------------------------- conversation list -- */

.convlist {
  width: 310px;
  flex: 0 0 311px;
  border-right: 1px solid var(--line-soft);
  background: var(--panel);
  display: flex;
  flex-direction: column;
}

.convlist-head { padding: 0 20px; }

.search {
  position: relative;
  width: 100%;
  height: 40px;
}

.search input {
  width: 100%;
  height: 40px;
  padding: 0 40px 0 16px;
  border: 1px solid var(--line-input);
  border-radius: 64px;
  background: var(--surface);
  font-weight: 500;
  font-size: 14px;
  line-height: 1;
  font-family: var(--font);
  color: var(--search-ink);
}

.search input::placeholder { color: var(--search-ink); opacity: 0.45; }

.search input:focus { outline: 2px solid var(--blue-wash); outline-offset: 1px; }

.search .search-icon {
  position: absolute;
  top: 12px;
  right: 15px;
  width: 16px;
  height: 16px;
  opacity: 0.3;
  pointer-events: none;
}

.convlist-body {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: 10px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.conv {
  width: 100%;
  height: 94px;
  padding: 15px;
  border-radius: calc(10px * var(--rx));
  display: flex;
  gap: 15px;
  align-items: flex-start;
  text-align: left;
}

.conv:hover { background: rgba(0, 0, 0, 0.025); }

.conv.is-active { background: var(--blue-wash); }

.avatar {
  position: relative;
  width: 40px;
  height: 40px;
  flex: 0 0 40px;
}

.avatar img {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  object-fit: cover;
  display: block;
}

.avatar .online {
  position: absolute;
  left: 31px;
  top: 31px;
  width: 7px;
  height: 7px;
}

.conv-main {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.conv-top {
  display: flex;
  align-items: center;
  gap: 5px;
}

.conv-unread {
  width: 17px;
  height: 17px;
  border-radius: 68px;
  background: var(--blue);
  color: var(--badge-ink);
  font-size: 12px;
  font-weight: 500;
  line-height: 17px;
  text-align: center;
  flex: 0 0 17px;
}

.conv-name {
  flex: 1 1 0;
  min-width: 0;
  font-size: 14px;
  font-weight: 800;
  color: var(--ink-strong);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.conv-time {
  font-size: 12px;
  color: var(--ink);
  opacity: 0.5;
  white-space: nowrap;
  flex: 0 0 auto;
}

.conv-preview {
  font-size: 13px;
  line-height: 1.6;
  color: var(--ink-strong);
  opacity: 0.6;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* ------------------------------------------------------------ chat pane -- */

.chat {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  background: var(--surface);
}

.chat-head {
  padding: 0 25px;
  justify-content: space-between;
  gap: 20px;
}

.chat-who {
  display: flex;
  align-items: center;
  gap: 15px;
  min-width: 0;
}

.chat-who h2 {
  margin: 0;
  font-size: 15px;
  font-weight: 800;
  color: var(--ink);
  white-space: nowrap;
}

.chat-actions {
  display: flex;
  align-items: center;
  gap: 12px;
  flex: 0 0 auto;
}

.btn {
  min-height: 40px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 11px 14px 12px;
  border-radius: calc(8px * var(--rx));
  font-size: 14px;
  font-weight: 500;
  white-space: nowrap;
}

.btn img { width: 16px; height: 16px; display: block; }

.btn-grey { background: var(--button-grey); color: var(--ink); }

.btn-grey:hover { background: var(--button-grey-hover); }

.btn-icon { width: 44px; padding: 11px 14px 12px; }

.btn-dark { background: var(--solid); color: var(--solid-ink); }

.btn-dark:hover { background: var(--solid-hover); }

/* ------------------------------------------------------------- messages -- */

.thread {
  flex: 1 1 auto;
  overflow-y: auto;
  /* The browser's own scroll anchoring fights the adjustment made when a page
     of older messages is prepended, so the adjustment is made by hand. */
  overflow-anchor: none;
  display: flex;
  flex-direction: column;
  /* The dock floats over the thread rather than sitting under it, so a new
     message can rise up through the glass. The padding keeps resting content
     clear of it; --dock-h is measured, because the composer grows as you type. */
  padding-bottom: calc(var(--dock-h, 130px) + 25px);
}

.thread-inner {
  width: min(625px, 100% - 40px);
  /* auto rather than the container's justify-content: flex-end, which pushes
     overflow out of the top of a scroll container where it cannot be reached. */
  margin: auto auto 0;
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.msg { display: flex; flex-direction: column; gap: 5px; }

.msg-out { padding-left: 124px; }

.msg-in { padding-right: 124px; }

.msg-row { display: flex; gap: 10px; align-items: flex-start; }

.bubble {
  padding: 12px 14px;
  border-radius: calc(10px * var(--rx));
  font-size: 14px;
  line-height: 1.4;
  word-break: break-word;
}

.msg-out .bubble { background: var(--blue); color: #fff; }

.msg-in .bubble {
  background: var(--bubble-in);
  color: var(--ink);
  flex: 1 1 0;
  min-width: 0;
}

.msg.is-note .bubble {
  background: var(--note-bg);
  border: 1px solid var(--note-border);
  color: var(--note-ink);
}

.bubble a { color: var(--blue); text-decoration: underline; }

.msg-out.is-note .bubble a { color: var(--blue); text-decoration: none; }

/* A mention is a person, so it is drawn as one: their face, their name, and a
   pill to hold them together — the same chip a tag uses, because it is the
   same idea, a small thing attached to a bigger one. One rule for the thread
   and the composer, because they are the same object at the same size.

   The three numbers that matter: the face is 18, the padding around it is 3 on
   three sides, and the line-height is pinned to 18 so the box is 24 and the
   inset is 3 all the way round. Left it from the line-height and the box came
   out 25 with 4.5 above the face and 2 beside it — a circle sitting off-centre
   in the round end of its own pill. */
.bubble .mention,
#draft .mention {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin: -3px 1px;
  padding: 3px 10px 3px 3px;
  border-radius: 999px;
  line-height: 18px;
  background: var(--blue-12);
  color: var(--blue);
  font-weight: 500;
  text-decoration: none;
  vertical-align: -4px;
}

.bubble .mention img,
#draft .mention img {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  object-fit: cover;
  /* The thread lets its images be dragged out, because they are content. This
     one is furniture. */
  -webkit-user-drag: none;
}

/* Inside a note the accent would be the one cool thing on an amber card, so
   the chip takes the note's own colour instead. */
.msg.is-note .bubble .mention,
.composer.is-note #draft .mention {
  background: rgba(242, 153, 74, 0.3);
  color: var(--note-ink);
}

.meta {
  font-size: 12px;
  color: var(--muted);
  display: flex;
  align-items: center;
  gap: 5px;
}

.msg-in .meta { padding-left: 50px; }

.meta b { font-weight: 600; }

.meta .dot { font-weight: 200; }

.event {
  font-size: 12px;
  color: var(--muted);
  text-align: center;
}

.event b { font-weight: 600; }

.event.is-loading { color: var(--muted); }

/* ------------------------------------------------------------- composer -- */

.composer {
  flex: 0 0 auto;
  border-top: 1px solid var(--line-soft);
  padding: 20px 25px 21px;
  display: flex;
  flex-direction: column;
  gap: 20px;
  /* Frosted rather than solid: a message on its way up stays visible through
     the panel, so the reply reads as coming from where it was typed. */
  background: var(--composer-glass);
  /* 20px smeared a note's text into nothing; this keeps enough of the shape
     for the passing message to be legible as one. */
  -webkit-backdrop-filter: blur(14px) saturate(165%);
  backdrop-filter: blur(14px) saturate(165%);
}

/* Note mode changes colour and nothing else. It used to carry its own
   padding and gap, so switching modes moved the composer — and with it the
   whole thread — by 12px. */
.composer.is-note {
  background: var(--composer-note-glass);
}

.composer-tools {
  display: flex;
  align-items: center;
  height: 28px;
}

.tool {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  height: 28px;
  padding: 0 10px;
  border-radius: 15px;
}

.tool img { width: 16px; height: 16px; display: block; }

.tool:hover { background: var(--hairline); }

.tool.is-on {
  background: var(--surface);
  cursor: default;
}

.tool.is-on:hover { background: var(--surface); }

.tool-label {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: -0.065px;
  line-height: 1.4;
  color: var(--orange);
}

.composer-row {
  display: flex;
  gap: 20px;
  align-items: flex-start;
}

/* The composer is contenteditable rather than a textarea, because a mention
   is a chip with a face on it and a textarea holds nothing but characters.
   Everything outside this file still thinks in text: draftText() is the
   border, and a chip hands back the handle it stands for. */
.composer-row #draft {
  flex: 1 1 0;
  min-width: 0;
  min-height: 40px;
  max-height: 168px;
  overflow-y: auto;
  border: 0;
  background: none;
  font-weight: 400;
  font-size: 14px;
  line-height: 1.5;
  font-family: var(--font);
  color: var(--ink);
  /* A box that grows by line rather than by pixel, and never sideways. */
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  /* Padding rather than flex centring. A flex container puts the caret at its
     start and the placeholder — a flex item — in its middle, which is two
     different places, so the caret sat a line above the text it was supposed
     to be in front of. Padded, both sit in the same line box: 9.5 + 21 + 9.5
     is the 40 the row is drawn at. */
  display: block;
  padding: 9.5px 0;
}

.composer-row #draft:focus { outline: none; }

.composer-row #draft:empty::before {
  content: attr(data-placeholder);
  color: var(--muted);
  pointer-events: none;
}

/* In the composer it is one thing to select and one thing to delete, rather
   than a face and some letters that happen to sit together. */
#draft .mention { user-select: all; }

.send {
  height: 40px;
  width: 66px;
  border-radius: calc(8px * var(--rx));
  background: var(--blue);
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  /* So the plane leaves under the edge of the button rather than flying off
     across the composer. */
  overflow: hidden;
}

/* A press is the one moment the button itself should move. */
.send:active { transform: scale(0.94); }

.send:hover { background: var(--blue-hover); }

.send img { width: 20px; height: 20px; display: block; }

/* Hovering the button shows the shortcut that does the same thing. Nobody
   discovers ⌘↵ on their own, and it is the same gesture either way — so it
   is taught the same way for a reply and for a note.

   The swap is a launch: the plane leaves up and to the right, turning as it
   goes and accelerating out of frame, and the shortcut climbs into its place
   with an overshoot. Stacked in one grid cell so both can move at once, and
   done with transitions rather than keyframes so letting go of the button
   plays the whole thing backwards from wherever it had got to. */
.send #send-icon,
.send #send-keys {
  grid-area: 1 / 1;
  transition:
    transform 420ms cubic-bezier(0.34, 1.56, 0.64, 1),
    opacity 200ms ease;
}

.send #send-keys {
  opacity: 0;
  transform: translateY(15px) scale(0.55);
  color: #fff;
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0.5px;
}

.send:hover #send-icon,
.send:focus-visible #send-icon {
  opacity: 0;
  transform: translate(26px, -26px) rotate(40deg) scale(0.7);
  /* Leaving is not the reverse of arriving: it starts slow and accelerates
     away, and the fade waits a moment so you see it go. */
  transition:
    transform 360ms cubic-bezier(0.5, 0, 0.9, 0.35),
    opacity 220ms ease 70ms;
}

.send:hover #send-keys,
.send:focus-visible #send-keys {
  opacity: 1;
  transform: none;
}

.send-note { background: var(--solid); }

.send-note:hover { background: var(--solid-hover); }

/* --------------------------------------------------------- details pane -- */

.details {
  width: 310px;
  flex: 0 0 311px;
  border-left: 1px solid var(--line);
  background: var(--panel);
  display: flex;
  flex-direction: column;
}

.details-head { padding: 0 25px; }

.details-head h2 {
  margin: 0;
  font-size: 15px;
  font-weight: 800;
  color: var(--ink);
}

.details-body {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: 20px 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 20px;
}

.detail-group {
  width: 260px;
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.detail-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  height: 17px;
  font-size: 13px;
  line-height: normal;
  color: var(--ink);
}

.detail-row dt { font-weight: 400; white-space: nowrap; }

.detail-row dd {
  margin: 0;
  font-weight: 500;
  letter-spacing: -0.065px;
  text-align: right;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The three panels that report on a rule, a reply or a tag rather than on a
   customer. Their labels carry as much as their values — "Sent 318" is one
   fact, not a value with a caption — so both are a size up and the label is
   no longer the lighter of the two. The measured User Details panel keeps the
   type it was drawn with. */
.detail-group.is-stats { gap: 14px; }

.detail-group.is-stats .detail-row {
  height: auto;
  min-height: 20px;
  font-size: 14px;
}

.detail-group.is-stats dt { font-weight: 500; }

.detail-group.is-stats dd { font-weight: 600; }

.detail-row dd u { text-decoration: underline; }

.detail-row dd.is-link { color: var(--blue); cursor: pointer; }

.details-rule {
  width: min(309px, 100%);
  height: 1px;
  background: #a4a4a4;
  opacity: 0.2;
  flex: 0 0 1px;
}

.prev-head {
  display: flex;
  align-items: center;
  gap: 10px;
}

.prev-head h3 {
  margin: 0;
  font-size: 15px;
  font-weight: 800;
  color: var(--ink);
}

.prev {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.prev-top {
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--ink);
}

.prev-date {
  flex: 1 1 0;
  min-width: 0;
  font-size: 12px;
  font-weight: 700;
}

.prev-ago {
  font-size: 11px;
  font-weight: 400;
  opacity: 0.5;
  white-space: nowrap;
}

.prev-excerpt {
  font-size: 13px;
  line-height: 1.4;
  color: var(--ink-strong);
  opacity: 0.6;
  max-height: 36px;
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}

.load-more {
  font-size: 13px;
  font-weight: 500;
  line-height: 1.4;
  color: var(--blue);
  align-self: flex-start;
}


/* --------------------------------------------------------- autocomplete -- */

/* Sits where the saved replies panel sits, because it is the same gesture
   arrived at from the keyboard instead of the button. */
.ac {
  position: absolute;
  bottom: calc(100% + 10px);
  left: 25px;
  z-index: 25;
  width: 420px;
  max-width: calc(100% - 50px);
  max-height: 280px;
  overflow-y: auto;
  padding: 5px;
  background: var(--surface);
  border-radius: calc(9px * var(--rx));
  box-shadow: var(--menu-shadow);
}

.ac-row {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 7px 10px;
  border-radius: calc(4px * var(--rx));
  text-align: left;
  font-size: 14px;
  transition: 0s;
}

/* The highlight follows the arrow keys, so it is a state rather than a hover —
   but hovering moves it too, and the two must look the same. */
.ac-row:hover,
.ac-row.is-picked {
  background: var(--blue-12);
  color: var(--blue);
}

.ac-key {
  flex: 0 0 auto;
  padding: 1px 6px;
  border-radius: calc(4px * var(--rx));
  background: var(--hairline);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px;
  color: var(--muted);
}

.ac-row.is-picked .ac-key,
.ac-row:hover .ac-key { background: rgba(47, 128, 237, 0.14); color: var(--blue); }

.ac-title { flex: 0 0 auto; font-weight: 500; }

/* The AI rows carry the same mark as the button that opens them. */
.ac-spark { width: 16px; height: 16px; flex: 0 0 16px; display: block; }

/* What the AI is doing, or why it is not. Sits in the tool row because that is
   where the button that asked for it is. */
.tool-note {
  margin-left: 12px;
  font-size: 12px;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The body is context, not the thing being chosen, so it gives up its width
   first and never pushes the shortcut out of view. */
.ac-body {
  flex: 1 1 0;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 13px;
  color: var(--muted);
}

.ac-row.is-picked .ac-body,
.ac-row:hover .ac-body { color: rgba(47, 128, 237, 0.75); }

.ac-face { width: 22px; height: 22px; border-radius: 50%; object-fit: cover; flex: 0 0 22px; }

.ac-row .ac-face + .ac-title { flex: 1 1 auto; }

/* -------------------------------------------------- saved replies popup -- */

.composer-dock {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 5;
}

.qr {
  position: absolute;
  bottom: calc(100% + 10px);
  left: 25px;
  z-index: 20;
  width: 420px;
  max-width: calc(100% - 50px);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: calc(10px * var(--rx));
  box-shadow: 0 16px 40px rgba(16, 18, 22, 0.16), 0 2px 6px rgba(16, 18, 22, 0.06);
}

/* The panel opens upward, so the search sits at the bottom — next to the
   button you just clicked, where your eye already is. */
.qr-head {
  order: 2;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 14px 16px;
  border-top: 1px solid var(--line);
}

.qr-body { order: 1; }

.qr-head h3 {
  margin: 0;
  font-size: 15px;
  font-weight: 800;
  color: var(--ink);
}

/* The panel title doubles as the search field, so it reads as a heading until
   you focus it. */
.qr-search {
  flex: 1 1 0;
  min-width: 0;
  padding: 0;
  border: 0;
  background: none;
  font-weight: 800;
  font-size: 15px;
  line-height: 1.2;
  font-family: var(--font);
  color: var(--ink);
}

.qr-search::placeholder { color: var(--ink); opacity: 1; }

.qr-search:focus { outline: none; }

.qr-search:focus::placeholder { color: var(--muted); }

.qr-add {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 28px;
  padding: 0 10px;
  border-radius: calc(8px * var(--rx));
  background: var(--button-grey);
  font-size: 13px;
  font-weight: 500;
  color: var(--ink);
}

.qr-add img { width: 12px; height: 12px; opacity: 0.7; }

.qr-add:hover { background: var(--button-grey-hover); }

.qr-body {
  max-height: 340px;
  overflow-y: auto;
  padding: 8px;
}

.qr-item {
  width: 100%;
  display: block;
  text-align: left;
  padding: 10px 12px;
  border-radius: calc(8px * var(--rx));
}

.qr-item:hover { background: rgba(0, 0, 0, 0.035); }

.qr-item-top {
  display: flex;
  align-items: center;
  gap: 8px;
}

.qr-shortcut {
  flex: 0 0 auto;
  padding: 2px 6px;
  border-radius: calc(4px * var(--rx));
  background: var(--bubble-in);
  color: var(--muted);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px;
}

.qr-name {
  flex: 1 1 0;
  min-width: 0;
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.qr-actions {
  display: flex;
  gap: 2px;
  flex: 0 0 auto;
  opacity: 0;
}

.qr-item:hover .qr-actions,
.qr-item:focus-within .qr-actions { opacity: 1; }

.qr-act {
  width: 26px;
  height: 26px;
  border-radius: calc(6px * var(--rx));
  display: grid;
  place-items: center;
  color: var(--muted);
}

.qr-act svg { width: 14px; height: 14px; display: block; }

.qr-act:hover { background: rgba(0, 0, 0, 0.06); color: var(--ink); }

.qr-act.is-danger:hover { background: rgba(179, 38, 30, 0.1); color: var(--danger); }

.qr-preview {
  margin-top: 4px;
  font-size: 13px;
  line-height: 1.5;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.qr-empty {
  padding: 28px 12px;
  text-align: center;
  font-size: 13px;
  color: var(--muted);
}

.qr-confirm {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 12px;
  border-radius: calc(8px * var(--rx));
  background: rgba(179, 38, 30, 0.06);
  font-size: 13px;
  color: var(--ink);
}

.qr-confirm p {
  flex: 1 1 0;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* editor */

.qr-form { padding: 4px; }

.qr-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: 12px;
}

.qr-field label {
  font-size: 12px;
  font-weight: 600;
  color: var(--muted);
}

.qr-field input,
.qr-field textarea {
  width: 100%;
  padding: 9px 12px;
  border: 1px solid var(--line-input);
  border-radius: calc(8px * var(--rx));
  background: var(--surface);
  font-weight: 400;
  font-size: 14px;
  line-height: 1.5;
  font-family: var(--font);
  color: var(--ink);
}

.qr-field textarea {
  min-height: 92px;
  resize: vertical;
}

.qr-field input:focus,
.qr-field textarea:focus {
  outline: none;
  border-color: var(--blue);
  box-shadow: 0 0 0 3px var(--blue-wash);
}

.qr-hint {
  font-size: 12px;
  color: var(--muted);
}

.qr-error {
  margin-bottom: 10px;
  font-size: 12px;
  color: var(--danger);
}

.qr-form-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
}

.qr-btn {
  min-height: 40px;
  padding: 0 14px;
  border-radius: calc(8px * var(--rx));
  font-size: 14px;
  font-weight: 500;
}

.qr-btn-ghost { background: var(--button-grey); color: var(--ink); }
.qr-btn-ghost:hover { background: var(--button-grey-hover); }

.qr-btn-primary { background: var(--blue); color: #fff; }
.qr-btn-primary:hover { background: var(--blue-hover); }

.qr-btn-danger { background: var(--danger); color: #fff; }
.qr-btn-danger:hover { background: var(--danger-hover); }

.tool.is-open { background: rgba(0, 0, 0, 0.08); }


/* ------------------------------------------------- narrow: icons only -- */

/* Below this the conversation column gets too tight to read, so the sidebar
   drops its labels and becomes a second icon rail. Every row carries a
   title= so the label is still reachable on hover. */
@media (max-width: 1280px) {
  .sidebar {
    width: 80px;
    flex: 0 0 81px;
  }

  .sidebar-head {
    padding: 0;
    justify-content: center;
  }

  .ws-toggle { justify-content: center; }

  .ws-title { display: none; }

  /* The workspace mark moves off the rail and into this header, and the
     rest of the rail closes up behind it: a display:none flex item is not
     laid out at all, so it leaves no gap behind either. */
  .rail-logo { display: none; }

  .ws-icon {
    display: block;
    width: 40px;
    height: 40px;
  }

  .ws-toggle .chevron { display: none; }

  .nav-section { padding: 8px 6px; }

  .nav-title {
    height: 28px;
    justify-content: center;
    padding: 6px 0;
  }

  .nav-title span { display: none; }

  .nav-title.is-labelonly { display: none; }

  .nav-item {
    position: relative;
    justify-content: center;
    padding: 10px 0;
  }

  .nav-label { display: none; }

  .nav-item .badge {
    position: absolute;
    top: 2px;
    right: 3px;
    height: 16px;
    min-width: 16px;
    padding: 0 4px;
    font-size: 10px;
    line-height: 16px;
  }

  /* With the labels gone the icon is the whole row, so it carries more
     weight here than it does at full width. */
  .nav-item .nav-icon {
    width: 20px;
    height: 20px;
    flex: 0 0 20px;
    font-size: 17px;
  }

  .nav-item .nav-icon img,
  .nav-item .nav-icon picture img {
    width: 20px;
    height: 20px;
  }

  /* Falls back to the 16px art for the few glyphs with no native 20px cut. */
  .nav-item .nav-icon .nav-ink {
    width: 20px;
    height: 20px;
    -webkit-mask-image: var(--i20, var(--i16));
    mask-image: var(--i20, var(--i16));
  }

  /* Rows with no icon fall back to their initial rather than a bare badge. */
  .nav-initial {
    display: grid;
    place-items: center;
    font-size: 12px;
  }
}

/* ------------------------------------------------- workspace dropdown -- */

.ws-menu {
  position: absolute;
  top: calc(100% - 6px);
  left: 12px;
  right: 12px;
  z-index: 30;
  display: grid;
  gap: 4px;
  padding: 6px;
  background: var(--surface);
  border-radius: calc(12px * var(--rx));
  box-shadow: var(--menu-shadow);
}

/* This one is not a list of links, so it is not dressed as one. It switches
   what the whole window is doing, which is a bigger move than the other menus
   make — it gets the mark, the name and a line saying what you are choosing. */
.ws-option {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 12px;
  border-radius: calc(8px * var(--rx));
  text-align: left;
  transition: 0s;
  user-select: none;
  color: var(--ink);
}

.ws-option-mark {
  width: 28px;
  height: 28px;
  flex: 0 0 28px;
  border-radius: calc(6px * var(--rx));
}

.ws-option-text { min-width: 0; }

.ws-option-text b {
  display: block;
  font-size: 14px;
  font-weight: 600;
}

.ws-option-text > span {
  display: block;
  margin-top: 2px;
  font-size: 12px;
  line-height: 1.4;
  color: var(--muted);
}

.ws-option:hover { background: var(--blue-12); }

.ws-option:hover .ws-option-text b { color: var(--blue); }

/* The one you are already in is marked by the accent rather than by weight —
   at this size bolder text is not a strong enough signal. */
.ws-option.is-current {
  background: var(--blue-wash);
  box-shadow: inset 0 0 0 1px var(--blue);
}

.ws-option.is-current .ws-option-text b { color: var(--blue); }


/* Collapsed, the menu needs room of its own — the rail is only 64px wide. */
@media (max-width: 1280px) {
  .ws-menu {
    left: 6px;
    right: auto;
    width: 160px;
  }

  /* No chevron at this width — the tab points at the workspace icon. */
  .ws-menu::after { left: 34px; }
}

/* ------------------------------------------------------------- panes -- */

.pane {
  position: relative;
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
}

.details-head { justify-content: space-between; }

.head-action {
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  margin-right: -4px;
  border-radius: calc(6px * var(--rx));
  opacity: 0.7;
}

.head-action img { width: 16px; height: 16px; display: block; }

.head-action:hover { opacity: 1; background: var(--hairline); }

/* The details column is a reading aid on the chat side and a form on the
   docs side, where the editor benefits from the space back. */
.details.is-collapsed { display: none; }

/* --------------------------------------------------------- articles -- */

.article {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 14px 15px;
  border-radius: calc(10px * var(--rx));
  text-align: left;
}

.article:hover { background: rgba(0, 0, 0, 0.025); }

.article.is-active { background: var(--blue-wash); }

.article-title {
  font-size: 14px;
  font-weight: 800;
  color: var(--ink-strong);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.article-excerpt {
  font-size: 13px;
  line-height: 1.6;
  color: var(--ink-strong);
  opacity: 0.6;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* ----------------------------------------------------- article editor -- */

.docs-head {
  padding: 0 25px;
  justify-content: space-between;
  gap: 20px;
}

.docs-heading {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 5px;
}

.docs-heading h2 {
  margin: 0;
  font-size: 14px;
  font-weight: 800;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.doc-path {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 13px;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
}

/* The separator is the library's drop-down caret turned to point along the
   path, which is how the design composes it. */
.doc-crumb {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 5px;
  height: 10px;
  flex: 0 0 5px;
}

.doc-crumb img {
  width: 10px;
  height: 5px;
  transform: rotate(-90deg);
}

.doc-scroll {
  flex: 1 1 auto;
  overflow-y: auto;
}

.doc-body {
  padding: 26px 25px 48px;
  outline: none;
  color: var(--ink);
  font-size: 14px;
  line-height: 1.7;
  caret-color: var(--blue);
}

.doc-body:focus-visible { outline: none; }

.doc-body h2 {
  margin: 26px 0 8px;
  font-size: 16px;
  font-weight: 800;
  color: var(--ink);
  line-height: 1.4;
}

.doc-body h2:first-child { margin-top: 0; }

.doc-body p { margin: 0 0 14px; }

.doc-body ul {
  margin: 0 0 14px;
  padding-left: 20px;
}

.doc-body li { margin-bottom: 6px; }

.doc-body pre {
  margin: 0 0 16px;
  padding: 12px 14px;
  border-radius: calc(8px * var(--rx));
  background: var(--bubble-in);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 13px;
  line-height: 1.5;
  overflow-x: auto;
}


/* The blocks the toolbar can make, which the seed articles did not contain.
   h2 was the only heading drawn, so h1 and h3 are pitched either side of it
   rather than invented from scratch. */
.doc-body h1 {
  margin: 26px 0 10px;
  font-size: 20px;
  font-weight: 800;
  line-height: 1.3;
}

.doc-body h3 {
  margin: 22px 0 6px;
  font-size: 14px;
  font-weight: 700;
  line-height: 1.4;
}

.doc-body h1:first-child,
.doc-body h3:first-child { margin-top: 0; }

.doc-body ol {
  margin: 0 0 14px;
  padding-left: 20px;
}

.doc-body blockquote {
  margin: 0 0 16px;
  padding: 2px 0 2px 14px;
  border-left: 3px solid var(--blue);
  color: var(--muted);
  font-style: italic;
}

.doc-body a { color: var(--blue); text-decoration: underline; }

.doc-body img {
  display: block;
  max-width: 100%;
  margin: 0 0 16px;
  border-radius: calc(8px * var(--rx));
}

.doc-body code {
  padding: 1px 5px;
  border-radius: calc(4px * var(--rx));
  background: var(--bubble-in);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.92em;
}

/* -------------------------------------------------------- article tools -- */

.doc-tools {
  position: relative;
  display: flex;
  align-items: center;
  gap: 4px;
  flex-wrap: wrap;
  padding: 8px 25px;
  border-bottom: 1px solid var(--line);
}

.doc-tool-group {
  display: flex;
  align-items: center;
  gap: 2px;
}

.doc-tool-rule {
  width: 1px;
  height: 18px;
  margin: 0 6px;
  background: var(--line);
}

.doc-tool {
  min-width: 30px;
  height: 30px;
  min-height: 0;
  padding: 0 6px;
  display: grid;
  place-items: center;
  border-radius: calc(6px * var(--rx));
  color: var(--ink);
  transition: 0s;
}

.doc-tool:hover { background: var(--blue-12); color: var(--blue); }

.doc-tool.is-on { background: var(--blue-12); color: var(--blue); }

.doc-tool img { width: 16px; height: 16px; display: block; }

/* Bold, italic and the quote mark are set in type rather than drawn: the icon
   library has no glyph for any of the three, and a letter B is a letter B. */
.doc-tool.is-type {
  font-size: 13px;
  font-weight: 600;
  line-height: 1;
}

.doc-tool.is-type b { font-weight: 800; }

.doc-tool.is-type i { font-family: Georgia, 'Times New Roman', serif; font-weight: 600; }

.doc-quote {
  font-family: Georgia, 'Times New Roman', serif;
  font-size: 20px;
  line-height: 0;
  padding-bottom: 6px;
}

/* One field, asked for at the moment it is needed. */
.doc-ask {
  position: absolute;
  z-index: 30;
  display: flex;
  gap: 6px;
  width: 320px;
  padding: 8px;
  background: var(--surface);
  border-radius: calc(9px * var(--rx));
  box-shadow: var(--menu-shadow);
  animation: menu-drop 140ms cubic-bezier(0.22, 1, 0.36, 1);
  transform-origin: top left;
}

.doc-ask input {
  flex: 1 1 auto;
  min-width: 0;
  padding: 0 10px;
  border: 1px solid var(--line-input);
  border-radius: calc(6px * var(--rx));
  background: var(--surface);
  font-family: var(--font);
  font-size: 13px;
  color: var(--ink);
}

.doc-ask input:focus { outline: none; border-color: var(--blue); }

.doc-ask .qr-btn { flex: 0 0 auto; }

/* ---------------------------------------------------- article details -- */

.docs-details {
  display: flex;
  flex-direction: column;
  gap: 20px;
  padding: 20px;
  border-bottom: 1px solid var(--line);
}

.doc-field {
  display: flex;
  flex-direction: column;
  gap: 7px;
}

.doc-label {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 16px;
}

.doc-label span {
  flex: 1 1 0;
  min-width: 0;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.77px;
  text-transform: uppercase;
  color: var(--ink);
}

.doc-label img {
  width: 16px;
  height: 16px;
  cursor: pointer;
}

.doc-input {
  display: flex;
  align-items: center;
  gap: 10px;
  height: 42px;
  padding: 0 13px;
  background: var(--surface);
  border: 1px solid var(--button-grey);
  border-radius: calc(8px * var(--rx));
}

.doc-input:focus-within {
  border-color: var(--blue);
  box-shadow: 0 0 0 3px var(--blue-wash);
}

.doc-input input {
  flex: 1 1 0;
  min-width: 0;
  border: 0;
  background: none;
  outline: none;
  font-weight: 400;
  font-size: 14px;
  line-height: 1;
  font-family: var(--font);
  color: var(--ink);
}

/* The caret is one of the shared #333 glyphs; it is meant to read lighter
   than the value beside it, which is opacity's job rather than a second hex. */
.doc-input img {
  width: 16px;
  height: 16px;
  opacity: 0.45;
}

.doc-error {
  margin: -8px 0 0;
  font-size: 12px;
  color: var(--danger);
}

.btn-block {
  height: 40px;
  width: 100%;
  border-radius: calc(8px * var(--rx));
  background: var(--blue);
  color: #f2f2f2;
  font-size: 14px;
  font-weight: 500;
}

.btn-block:hover { background: var(--blue-hover); }

.btn-block.is-saved { background: var(--ok, var(--ok)); }

/* ------------------------------------------ narrow: details overlays -- */

/* Below the same breakpoint the sidebar collapses at, the details column
   stops taking width from the layout. It starts closed and, when opened,
   floats above the content instead of squeezing it — so reading the
   details never reflows the thread or the article you were looking at. */
@media (max-width: 1280px) {
  .details {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    z-index: 40;
    box-shadow: -16px 0 44px rgba(16, 18, 22, 0.14);
  }
}

/* --------------------------------------------------------- user menu -- */

.rail { position: relative; }

.rail-me:hover img { box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.35); }

.rail-me[aria-expanded='true'] img { box-shadow: 0 0 0 2px var(--blue); }

.user-menu {
  position: absolute;
  left: calc(100% + 10px);
  bottom: 14px;
  z-index: 50;
  width: 232px;
  background: var(--surface);
  border-radius: calc(9px * var(--rx));
  box-shadow: var(--menu-shadow);
}

.user-menu-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 12px 14px;
  border-bottom: 1px solid var(--line);
}

.user-menu-head img {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  object-fit: cover;
  flex: 0 0 36px;
}

.user-menu-who {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.user-menu-who b {
  font-size: 14px;
  font-weight: 700;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.user-menu-who span {
  font-size: 12px;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.menu-group { padding: 6px; }

.menu-group + .menu-group { border-top: 1px solid var(--line); }

.user-menu-item {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 7px 12px;
  border-radius: calc(4px * var(--rx));
  text-align: left;
  font-size: 14px;
  font-weight: 500;
  transition: 0s;
  user-select: none;
  color: var(--ink);
}

.user-menu-item:hover {
  background: var(--blue-12);
  color: var(--blue);
}

/* ------------------------------------------- switching composer modes -- */

/* The mode change is worth making obvious: an agent who thinks they are
   writing a private note and is actually replying to the customer has made
   the worst mistake this screen allows. The colour floods out from the
   button that caused it, so the cause and the effect are the same gesture. */

.composer {
  position: relative;
  overflow: hidden;
  transition: background-color 420ms ease;
}

.composer > *:not(.composer-wash) {
  position: relative;
  z-index: 1;
}

.composer-wash {
  position: absolute;
  z-index: 0;
  border-radius: 50%;
  pointer-events: none;
  transform: scale(0);
  opacity: 0;
}

.composer-wash.is-running {
  animation: composer-wash 520ms cubic-bezier(0.22, 1, 0.36, 1) forwards;
}

@keyframes composer-wash {
  from { transform: scale(0); opacity: 1; }
  to { transform: scale(1); opacity: 1; }
}

/* -------------------------------------------------------- sending later -- */

.sending { color: var(--muted); }

/* The countdown must not shuffle the word after it. tnum makes every digit the
   same advance — 0.6484em in this Inter — and the box holds two of them, rounded
   up so the box governs rather than tying with its own contents. 10 and 9 then
   take the same space and Undo never moves.
   Neither ch nor U+2007 works as the unit: both resolve to the *proportional*
   zero advance, 0.6308em, a fifth of a pixel narrower.
   The other features are repeated because font-feature-settings replaces the
   inherited list rather than adding to it. */
.send-count {
  display: inline-block;
  min-width: calc(2 * 0.65em);
  text-align: right;
  font-feature-settings: 'ss03' on, 'cv03' on, 'cv04' on, 'cv06' on, 'cv09' on,
    'cv11' on, 'tnum' on;
}

/* The countdown is the whole reason the row is still here, so it reads as a
   control rather than as a timestamp. */
.undo {
  margin-left: 8px;
  font-size: 12px;
  font-weight: 600;
  color: var(--blue);
  text-decoration: underline;
  text-underline-offset: 2px;
}

.undo:hover { color: #1b64c9; }

.failed { color: var(--danger); }

/* The countdown in the meta line says how long is left. A bar draining across
   the bubble as well said it twice, and put a moving part inside the message
   itself. */

/* ------------------------------------------------ a message arriving -- */

/* A sent message does not simply appear at the end of the list. It comes up
   from the composer it was typed in — through the frosted panel, which blurs
   it on the way — overshoots, and settles. The distance is the dock's own
   height, so it starts exactly where the text was. */
/* The rise is one move, so it is one curve. Chaining eased segments made the
   velocity zero at each reversal but left the acceleration jumping across the
   boundary, and that kink is what read as unsmooth. This is the step response
   of a damped spring instead — sampled into linear() stops, which is what that
   function is for. It starts from a standstill, overshoots once by five per
   cent at the halfway mark, and decays to rest without another reversal. */
@keyframes msg-rise {
  from { transform: translate3d(0, var(--rise, 190px), 0); }
  to { transform: translate3d(0, 0, 0); }
}

/* Re-rendering the list gives the new row its full height at once, so
   everything above it used to jump up and only then did the bubble slide. The
   list starts holding that height as an offset and gives it back on the same
   curve, so the older messages glide up instead — and because the curve
   overshoots, the arrival nudges them a few pixels past their place before
   they settle.
   The two transforms compose: the message's own --rise is what is left of the
   trip once the list has carried its share. */
@keyframes thread-settle {
  from { transform: translate3d(0, var(--shift, 0px), 0); }
  to { transform: translate3d(0, 0, 0); }
}

.thread-inner.is-settling {
  /* The spring is written out rather than held in a custom property: an
     unsupported linear() inside var() is invalid at computed-value time, which
     resets the property instead of falling back to the declaration above. */
  animation: thread-settle 1125ms cubic-bezier(0.22, 1, 0.36, 1) both;
  animation: thread-settle 1125ms linear(0, 0.0098, 0.0367, 0.0769, 0.1275, 0.1854, 0.2483, 0.3142, 0.3811,
    0.4478, 0.5128, 0.5754, 0.6347, 0.6903, 0.7417, 0.7887, 0.8313, 0.8694,
    0.9032, 0.9327, 0.9582, 0.9799, 0.9981, 1.0131, 1.0252, 1.0347, 1.0418,
    1.0469, 1.0503, 1.0521, 1.0527, 1.0522, 1.0508, 1.0488, 1.0463, 1.0434,
    1.0402, 1.037, 1.0336, 1.0303, 1.027, 1.0239, 1.0209, 1.0181, 1.0156,
    1.0132, 1.0111, 1.0092, 1.0075, 1.006, 1.0047, 1.0036, 1.0027, 1.002,
    1.0014, 1.0009, 1.0005, 1.0003, 1.0001, 1, 1) both;
  will-change: transform;
}

/* The fade wants no overshoot, so it is its own animation on its own curve,
   and it is finished while the message is still well behind the glass. */
@keyframes msg-fade {
  from { opacity: 0; }
  to { opacity: 1; }
}

.msg.is-arriving {
  /* The first declaration is the fallback: an unparseable linear() takes the
     whole shorthand with it, and there would be no animation at all. */
  animation: msg-rise 1125ms cubic-bezier(0.22, 1, 0.36, 1) both,
    msg-fade 220ms ease-out both;
  animation: msg-rise 1125ms linear(0, 0.0098, 0.0367, 0.0769, 0.1275, 0.1854, 0.2483, 0.3142, 0.3811,
    0.4478, 0.5128, 0.5754, 0.6347, 0.6903, 0.7417, 0.7887, 0.8313, 0.8694,
    0.9032, 0.9327, 0.9582, 0.9799, 0.9981, 1.0131, 1.0252, 1.0347, 1.0418,
    1.0469, 1.0503, 1.0521, 1.0527, 1.0522, 1.0508, 1.0488, 1.0463, 1.0434,
    1.0402, 1.037, 1.0336, 1.0303, 1.027, 1.0239, 1.0209, 1.0181, 1.0156,
    1.0132, 1.0111, 1.0092, 1.0075, 1.006, 1.0047, 1.0036, 1.0027, 1.002,
    1.0014, 1.0009, 1.0005, 1.0003, 1.0001, 1, 1) both,
    msg-fade 220ms ease-out both;
  will-change: transform, opacity;
}

/* The label unrolls rather than appearing, so the pill grows into place. */
.tool { gap: 0; }

.tool img + .tool-label,
.tool-label {
  max-width: 0;
  margin-left: 0;
  opacity: 0;
  overflow: hidden;
  white-space: nowrap;
  transition: max-width 340ms cubic-bezier(0.22, 1, 0.36, 1),
              margin-left 340ms cubic-bezier(0.22, 1, 0.36, 1),
              opacity 220ms ease 120ms;
}

.tool.is-on .tool-label {
  max-width: 140px;
  margin-left: 10px;
  opacity: 1;
}

.tool.is-on { transition: background-color 300ms ease; }

#note-icon { transform-origin: 50% 50%; }

.composer.is-switching #note-icon {
  animation: note-pop 560ms cubic-bezier(0.34, 1.56, 0.64, 1);
}

@keyframes note-pop {
  0% { transform: rotate(0) scale(1); }
  35% { transform: rotate(-16deg) scale(1.5); }
  65% { transform: rotate(8deg) scale(1.15); }
  100% { transform: rotate(0) scale(1); }
}

.send {
  transition: background-color 380ms ease, width 340ms cubic-bezier(0.22, 1, 0.36, 1);
}

.composer.is-switching .send {
  animation: send-pop 520ms cubic-bezier(0.34, 1.56, 0.64, 1);
}

@keyframes send-pop {
  0% { transform: scale(1); }
  40% { transform: scale(1.12); }
  100% { transform: scale(1); }
}

/* Motion this emphatic is exactly what reduced-motion is asking us to drop;
   the colour still changes, just without the flood. */
@media (prefers-reduced-motion: reduce) {
  .composer-wash.is-running { animation: none; }
  .msg.is-arriving,
  .thread-inner.is-settling { animation: none; }
  .composer.is-switching #note-icon,
  .composer.is-switching .send { animation: none; }

  .send:active { transform: none; }

  .send #send-icon,
  .send #send-keys,
  .send:hover #send-icon,
  .send:focus-visible #send-icon { transition: none; }
  .tool-label { transition: none; }
  .composer { transition: none; }
}

/* The article title is edited in place, so it needs to look editable on
   approach without shouting about it while you are reading. */
.docs-heading h2[contenteditable] {
  padding: 2px 6px;
  margin-left: -6px;
  border-radius: calc(6px * var(--rx));
  outline: none;
  cursor: text;
  transition: background-color 140ms ease, box-shadow 140ms ease;
}

.docs-heading h2[contenteditable]:hover { background: rgba(0, 0, 0, 0.04); }

.docs-heading h2[contenteditable]:focus {
  background: var(--surface);
  box-shadow: 0 0 0 2px var(--blue-wash), inset 0 0 0 1px var(--line-input);
}

.docs-heading h2[contenteditable]:empty::before {
  content: 'Untitled article';
  color: var(--muted);
}

/* ------------------------------------------------ user details menu -- */

.details-head { position: relative; }

.props-menu {
  position: absolute;
  top: calc(100% - 14px);
  right: 16px;
  z-index: 40;
  width: 226px;
  background: var(--surface);
  border-radius: calc(9px * var(--rx));
  box-shadow: var(--menu-shadow);
}

.props-item {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 7px 12px;
  border-radius: calc(4px * var(--rx));
  text-align: left;
  font-size: 14px;
  font-weight: 500;
  transition: 0s;
  user-select: none;
  color: var(--ink);
}

.props-item:hover {
  background: var(--blue-12);
  color: var(--blue);
}


/* ------------------------------------------------------- date and time -- */

/* The styling half of datetime.js. Kept here rather than in the plugin so it
   takes the same tokens as everything else and changes colour with the theme
   without the plugin knowing there is a theme. */

.dt {
  position: relative;
  display: grid;
  gap: 12px;
}

.dt-field {
  display: grid;
  gap: 6px;
}

.dt-label {
  font-size: 12px;
  font-weight: 500;
  color: var(--muted);
}

.dt-trigger {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  min-height: 40px;
  padding: 0 10px;
  border: 1px solid var(--line-input);
  border-radius: calc(6px * var(--rx));
  background: var(--surface);
  font-size: 13px;
  font-weight: 500;
  color: var(--ink);
  text-align: left;
}

.dt-trigger > span { flex: 1 1 auto; }

.dt-trigger img { width: 16px; height: 16px; opacity: 0.75; }

.dt-trigger:hover { border-color: var(--muted); }

.dt-trigger.is-open { border-color: var(--blue); }

.dt-chevron {
  width: 16px;
  height: 16px;
  opacity: 0.5;
  transition: transform 120ms ease;
}

.dt-trigger.is-open .dt-chevron { transform: rotate(180deg); }

/* The calendar and the list of times share one box, which hangs under
   whichever field opened it. */
.dt-pop {
  position: absolute;
  left: 0;
  right: 0;
  z-index: 5;
  max-height: 232px;
  overflow-y: auto;
  padding: 8px;
  background: var(--surface);
  border-radius: calc(9px * var(--rx));
  box-shadow: var(--menu-shadow);
  animation: menu-drop 140ms cubic-bezier(0.22, 1, 0.36, 1);
  transform-origin: top center;
}

.dt-cal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 4px;
  padding: 0 2px 8px;
}

.dt-cal-head b { font-size: 13px; font-weight: 600; }

.dt-page {
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  border-radius: calc(6px * var(--rx));
  color: var(--ink);
}

.dt-page:hover { background: var(--blue-12); }

/* One arrow file, turned: the chevron in the library points down. */
.dt-page .dt-chevron { opacity: 0.8; }

[data-dt-month='-1'] .dt-chevron { transform: rotate(90deg); }

[data-dt-month='1'] .dt-chevron { transform: rotate(-90deg); }

.dt-week,
.dt-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 2px;
}

.dt-week {
  padding-bottom: 4px;
}

.dt-week span {
  text-align: center;
  font-size: 11px;
  font-weight: 600;
  color: var(--muted);
}

.dt-day {
  height: 30px;
  display: grid;
  place-items: center;
  border-radius: calc(6px * var(--rx));
  font-size: 13px;
  font-weight: 500;
  color: var(--ink);
}

.dt-day:hover:not(:disabled) { background: var(--blue-12); color: var(--blue); }

/* Outside the month you are looking at, and before the floor: both are still
   drawn, because a grid with holes in it is harder to read than a grid with
   quiet days in it. */
.dt-day.is-off { color: var(--muted); opacity: 0.55; }

.dt-day:disabled { opacity: 0.3; cursor: default; }

.dt-day.is-today { box-shadow: inset 0 0 0 1px var(--line-input); }

.dt-day.is-on,
.dt-day.is-on:hover {
  background: var(--blue);
  color: var(--badge-ink);
  box-shadow: none;
}

.dt-times {
  display: grid;
  gap: 2px;
}

.dt-time {
  padding: 7px 10px;
  border-radius: calc(6px * var(--rx));
  text-align: left;
  font-size: 13px;
  font-weight: 500;
  color: var(--ink);
}

.dt-time:hover { background: var(--blue-12); color: var(--blue); }

.dt-time.is-on { background: var(--blue); color: var(--badge-ink); }

/* ---------------------------------------------------------- menu motion -- */

/* Every menu arrives the same way: 6px of travel and 140ms, fading in as it
   lands — enough to read as a movement, not enough to wait for. The direction
   is the one the menu is anchored from, so a menu that hangs off a button
   falls out of it and one that opens upward from the composer rises. */
@keyframes menu-drop {
  from {
    opacity: 0;
    transform: translateY(-6px) scale(0.98);
  }
}

@keyframes menu-rise {
  from {
    opacity: 0;
    transform: translateY(6px) scale(0.98);
  }
}

@keyframes menu-slide {
  from {
    opacity: 0;
    transform: translateX(-6px) scale(0.98);
  }
}

@keyframes menu-fade {
  from {
    opacity: 0;
  }
}

.head-menu,
.props-menu,
.ws-menu,
.tag-menu {
  animation: menu-drop 140ms cubic-bezier(0.22, 1, 0.36, 1);
}

.user-menu,
.notif,
.qr,
.ac {
  animation: menu-rise 140ms cubic-bezier(0.22, 1, 0.36, 1);
}

/* This one is level with its row rather than under it, so it comes in from
   the side it opens on. */
.cat-settings {
  animation: menu-slide 140ms cubic-bezier(0.22, 1, 0.36, 1);
}

/* Scaled from the corner it hangs off, so it grows out of its button rather
   than out of the middle of itself. */
.head-menu,
.props-menu,
.tag-menu {
  transform-origin: top right;
}

.ws-menu {
  transform-origin: top left;
}

.user-menu,
.notif,
.qr,
.ac,
.cat-settings {
  transform-origin: bottom left;
}

/* A dialog is not anchored to anything, so its ground fades and the card
   itself drops in — slightly slower, because it is a bigger thing. */
.modal {
  animation: menu-fade 160ms ease-out;
}

.modal-card {
  animation: menu-drop 180ms cubic-bezier(0.22, 1, 0.36, 1);
  transform-origin: center top;
}

@media (prefers-reduced-motion: reduce) {
  .head-menu,
  .props-menu,
  .ws-menu,
  .tag-menu,
  .cat-settings,
  .user-menu,
  .notif,
  .qr,
  .ac,
  .modal,
  .modal-card {
    animation: none;
  }
}

/* ------------------------------------------------------ menu pointers -- */

/* Static.app's pointer: a soft tab rather than a hard triangle, carrying its
   own drop-shadow so it sits on the card's shadow instead of cutting through
   it. One shape, turned to face whatever opened the menu. */
.ws-menu::after,
.props-menu::after,
.user-menu::after,
.qr::after {
  content: "";
  position: absolute;
  width: 18px;
  height: 7px;
  pointer-events: none;
  background: var(--menu-tab) no-repeat center / 18px 7px;
  filter: drop-shadow(0 -1px 1px var(--menu-tab-shadow));
}

/* Opens downward — the tab sits on top, under its trigger. */
.ws-menu::after,
.props-menu::after {
  bottom: 100%;
  margin-left: -9px;
}

.ws-menu::after { left: calc(100% - 19px); }

.props-menu::after { left: calc(100% - 17px); }

/* Opens upward from the composer — flipped, so it hangs beneath. */
.qr::after {
  top: 100%;
  left: 18px;
  margin-left: -9px;
  transform: scaleY(-1);
}

/* Opens beside the rail — turned to point back at the avatar. */
/* Rotating a 18x7 tab about its centre leaves a 7x18 visual box centred on
   the same point, so the offset has to account for the difference — at
   -12.5px the whole tab landed inside the card instead of beside it. */
.user-menu::after {
  right: 100%;
  bottom: 22.5px;
  margin-right: -5.5px;
  transform: rotate(-90deg);
}

/* The saved replies list scrolls, so the arrow must not be clipped by it. */
.qr { overflow: visible; }

.qr-body { border-radius: calc(9px * var(--rx)) calc(9px * var(--rx)) 0 0; }

/* ---------------------------------------------------------- scrollbars -- */

/* A permanent scrollbar is a permanent statement that there is more to see,
   which stops being useful the moment you have read it. These fade in while
   the list is moving, or while the pointer is over it, and the thumb is held
   off the edge by a transparent border rather than pinned to it. */
.sidebar-body,
.convlist-body,
.thread,
.details-body,
.qr-body,
.doc-scroll,
.composer-row #draft {
  scrollbar-width: thin;
  scrollbar-color: transparent transparent;
}

.sidebar-body:hover,
.convlist-body:hover,
.thread:hover,
.details-body:hover,
.qr-body:hover,
.doc-scroll:hover,
.composer-row #draft:hover,
.is-scrolling {
  scrollbar-color: rgba(0, 0, 0, 0.22) transparent;
}

.sidebar-body::-webkit-scrollbar,
.convlist-body::-webkit-scrollbar,
.thread::-webkit-scrollbar,
.details-body::-webkit-scrollbar,
.qr-body::-webkit-scrollbar,
.doc-scroll::-webkit-scrollbar,
.composer-row #draft::-webkit-scrollbar {
  width: 11px;
}

.sidebar-body::-webkit-scrollbar-track,
.convlist-body::-webkit-scrollbar-track,
.thread::-webkit-scrollbar-track,
.details-body::-webkit-scrollbar-track,
.qr-body::-webkit-scrollbar-track,
.doc-scroll::-webkit-scrollbar-track,
.composer-row #draft::-webkit-scrollbar-track { background: transparent; }

.sidebar-body::-webkit-scrollbar-thumb,
.convlist-body::-webkit-scrollbar-thumb,
.thread::-webkit-scrollbar-thumb,
.details-body::-webkit-scrollbar-thumb,
.qr-body::-webkit-scrollbar-thumb,
.doc-scroll::-webkit-scrollbar-thumb,
.composer-row #draft::-webkit-scrollbar-thumb {
  background-color: transparent;
  border: 3px solid transparent;
  border-radius: 999px;
  background-clip: padding-box;
}

.sidebar-body:hover::-webkit-scrollbar-thumb,
.convlist-body:hover::-webkit-scrollbar-thumb,
.thread:hover::-webkit-scrollbar-thumb,
.details-body:hover::-webkit-scrollbar-thumb,
.qr-body:hover::-webkit-scrollbar-thumb,
.doc-scroll:hover::-webkit-scrollbar-thumb,
.composer-row #draft:hover::-webkit-scrollbar-thumb,
.is-scrolling::-webkit-scrollbar-thumb {
  background-color: rgba(0, 0, 0, 0.22);
  background-clip: padding-box;
}

/* ---------------------------------------------------------- menu icons -- */

.menu-icon {
  width: 16px;
  height: 16px;
  flex: 0 0 16px;
  display: block;
  color: var(--icon-rest);
}

/* The row sets its colour on hover and the icon follows it, which is the
   whole reason these are inlined rather than <img>. */
.user-menu-item:hover .menu-icon,
.props-item:hover .menu-icon {
  color: inherit;
}

/* ------------------------------------------------------ add a category -- */

/* The box is the badge's, not the glyph's: this sits at the top of a column of
   counts, and a hover patch that does not match them reads as a different kind
   of thing. The title's right padding comes down to 10 for the same reason —
   that is where the badges end. */
.nav-add {
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  flex: 0 0 22px;
  border-radius: 68px;
  opacity: 0.7;
}

.nav-add img { width: 16px; height: 16px; display: block; }

.nav-add:hover { opacity: 1; background: var(--hairline); }

/* --------------------------------------------------------------- tags -- */

/* Every other row is one line of text at a fixed 17px. This one holds chips
   and a button, wraps, and has to be allowed the height that takes — otherwise
   the second line lands on the rule below it.
   A 24px chip in a 17px row would push everything below it down by seven, so
   the row keeps its slot and the chips overhang it evenly: the rhythm above
   and below stays the one the other rows set. */
.detail-row.is-tags {
  height: auto;
  align-items: flex-start;
  margin: -3.5px 0;
}

/* The label sits against a 24px chip rather than a 17px line of text. */
.detail-row.is-tags dt { padding-top: 7px; }

.detail-row.is-tags dd {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  align-items: center;
  gap: 6px;
  white-space: normal;
  overflow: visible;
}

.tags { display: contents; }

.tag {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 6px 4px 10px;
  border-radius: 999px;
  background: var(--blue-12);
  color: var(--blue);
  font-size: 12px;
  font-weight: 500;
  line-height: 16px;
}

.tag-x {
  width: 16px;
  height: 16px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  color: inherit;
  font-size: 13px;
  line-height: 1;
  opacity: 0.6;
}

.tag-x:hover { opacity: 1; background: rgba(47, 128, 237, 0.18); }

.tag-add {
  font-size: 13px;
  color: var(--blue);
  cursor: pointer;
}

.tag-input {
  width: 100px;
  height: 22px;
  padding: 0 6px;
  border: 1px solid var(--line-input);
  border-radius: calc(4px * var(--rx));
  font-family: var(--font);
  font-size: 12px;
  color: var(--ink);
}

.tag-input:focus { outline: none; border-color: var(--blue); }

/* ------------------------------------------------------------ tag popup -- */

.tag-menu {
  position: fixed;
  z-index: 60;
  width: 232px;
  padding: 6px;
  background: var(--surface);
  border-radius: calc(9px * var(--rx));
  box-shadow: var(--menu-shadow);
}

.tag-menu-head { padding: 2px 2px 6px; }

.tag-menu-search {
  width: 100%;
  padding: 0 10px;
  border: 1px solid var(--line-input);
  border-radius: calc(6px * var(--rx));
  background: var(--surface);
  font-family: var(--font);
  font-size: 13px;
  color: var(--ink);
}

.tag-menu-search:focus { outline: none; border-color: var(--blue); }

/* Long enough to browse, short enough that the popup stays a popup. */
.tag-menu-list {
  max-height: 232px;
  overflow-y: auto;
}

.tag-opt {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 7px 10px;
  border-radius: calc(6px * var(--rx));
  text-align: left;
  font-size: 13px;
  font-weight: 500;
  color: var(--ink);
  transition: 0s;
  user-select: none;
}

.tag-opt:hover { background: var(--blue-12); color: var(--blue); }

/* Already on this conversation: the accent, and the tick that says so. */
.tag-opt.is-on { color: var(--blue); }

.tag-opt-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* How many conversations carry the tag — the thing that tells you whether it
   is one of yours or one someone made once and forgot. */
.tag-opt-count {
  font-size: 12px;
  font-weight: 400;
  color: var(--muted);
}

.tag-opt:hover .tag-opt-count { color: inherit; opacity: 0.75; }

.tag-opt .menu-tick { margin-left: 0; color: inherit; }

.tag-opt-new .tag-opt-name { color: var(--blue); }

.tag-menu-empty {
  padding: 4px 10px 8px;
  font-size: 12px;
  color: var(--muted);
}

.prev.is-hidden { display: none; }

/* Wide, there is nothing to toggle: the details column is one of the three
   columns, the same as the list beside it. The control appears only below the
   breakpoint, where the column becomes a panel that floats over the content —
   and there it is one button with two states, its arrow turned around once the
   panel is open, so the thing that brought it in is the thing that sends it
   back. The panel used to carry a second button of its own for that, which was
   a duplicate wide and redundant narrow, where a click anywhere else already
   dismisses it. */
.details-toggle { display: none; }

@media (max-width: 1280px) {
  .details-toggle { display: inline-flex; }
}

.details-toggle[aria-expanded='true'] img { transform: scaleX(-1); }

/* The way out of the small panel and into the whole set. It sits inside the
   padded body, so it pulls itself back out to the panel's own edges. */
.cat-more {
  display: block;
  margin: 2px -14px -14px;
  padding: 12px 14px;
  border-top: 1px solid var(--line);
  font-size: 13px;
  font-weight: 500;
  color: var(--blue);
}

.cat-more:hover { background: var(--blue-12); }

/* -------------------------------------------------------- notifications -- */

.rail-small { border: 0; background: none; cursor: pointer; position: relative; }

.rail-dot {
  position: absolute;
  top: -1px;
  right: -1px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--blue);
  box-shadow: 0 0 0 2px var(--rail, #14171b);
}

.notif {
  position: absolute;
  left: calc(100% - 8px);
  bottom: 96px;
  z-index: 50;
  width: 320px;
  max-height: 60vh;
  overflow-y: auto;
  padding: 5px;
  background: var(--surface);
  border-radius: calc(9px * var(--rx));
  box-shadow: var(--menu-shadow);
}

.notif-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px 10px;
}

.notif-head h3 { flex: 1 1 auto; font-size: 14px; font-weight: 700; }

.notif-empty { padding: 0 12px 12px; font-size: 13px; color: var(--muted); }

.notif-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  width: 100%;
  padding: 8px 12px;
  border-radius: calc(6px * var(--rx));
  text-align: left;
  transition: 0s;
}

.notif-row:hover { background: var(--blue-12); }

.notif-row img { width: 28px; height: 28px; border-radius: 50%; object-fit: cover; flex: 0 0 28px; }

.notif-text { flex: 1 1 auto; min-width: 0; }

.notif-text b { display: block; font-size: 13px; font-weight: 600; }

.notif-text span {
  display: block;
  font-size: 12px;
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.notif-side {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 4px;
  font-size: 11px;
  color: var(--muted);
}

.pill-mention {
  padding: 1px 6px;
  border-radius: 999px;
  background: var(--blue-12);
  color: var(--blue);
  font-size: 10px;
  font-weight: 600;
}

/* ------------------------------------------------- new workspace dialog -- */

.rail-initial {
  width: 40px;
  height: 40px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.12);
  color: #f0f0f0;
  font-size: 16px;
  font-weight: 600;
}

.plan-picker {
  display: grid;
  gap: 6px;
}

.plan-option {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 10px 12px;
  border-radius: calc(6px * var(--rx));
  text-align: left;
  box-shadow: inset 0 0 0 1px var(--line-input);
  transition: 0s;
}

.plan-option b { font-size: 14px; font-weight: 600; }

.plan-option span { font-size: 12px; color: var(--muted); }

.plan-option:hover { background: var(--blue-12); }

/* The chosen plan is a commitment, so it is marked more firmly than a hover. */
.plan-option.is-picked {
  background: var(--blue-wash);
  box-shadow: inset 0 0 0 2px var(--blue);
}

.plan-option.is-picked b { color: var(--blue); }

/* ------------------------------------------------------------ dark glyphs -- */

/* Inline SVG follows currentColor and needs nothing. These are <img>, so the
   only lever is a filter. */
img[src$='.svg'] { filter: var(--glyph-filter); }

/* Two kinds are already right and must be left alone, and the list is taken
   from the files rather than from intuition: everything in /assets/icons is
   flat #333333 except these ten.
   Seven are drawn light because of where they sit — the rail, which is dark in
   both themes, and the send button, which is blue. Three are coloured on
   purpose. Nothing else is special: designmodo.svg is a brand mark but a
   monochrome one, so it inverts like any other glyph, and leaving it out is
   what made it vanish against the dark sidebar. The PNG brand tiles never
   match this selector at all.
   Matched on the end of the filename, not on a substring anywhere in it:
   `*='chat'` also caught mark-chats-dark.svg, which is how the workspace
   dropdown ended up with a black mark on a black card. */
img[src$='add-circle.svg'],
img[src$='bell.svg'],
img[src$='chat.svg'],
img[src$='logo.svg'],
img[src$='mark-chats-light.svg'],
img[src$='mark-docs-light.svg'],
img[src$='send.svg'],
img[src$='note-active.svg'],
img[src$='online.svg'],
img[src$='pulsetic.svg'] {
  filter: none;
}

/* Masked nav glyphs take the row's colour and were never a problem. */
.nav-ink { filter: none; }

/* The workspace marks come in both polarities as files, so the theme picks the
   artwork rather than inverting one. The src attribute keeps naming the light
   file even when the dark one is being drawn, so the filter has to be waived
   here explicitly or it would invert the swap straight back. */
img.ws-option-mark { filter: none; }

:root[data-theme='dark'] .ws-option[data-ws='chats'] .ws-option-mark {
  content: url(/assets/icons/mark-chats-light.svg);
}

:root[data-theme='dark'] .ws-option[data-ws='docs'] .ws-option-mark {
  content: url(/assets/icons/mark-docs-light.svg);
}

/* ------------------------------------------------------------- appearance -- */

/* Stacked, not side by side: "Appearance" and three options do not fit across
   a 232px menu, and the segmented control was running out of it. */
.theme-row {
  display: grid;
  gap: 8px;
  padding: 7px 12px 9px;
}

.theme-label {
  font-size: 14px;
  font-weight: 500;
}

/* A segmented control rather than three menu rows: it is one setting with
   three answers, and rows would read as three separate things to do. */
.theme-pick {
  display: flex;
  gap: 2px;
  padding: 2px;
  border-radius: calc(6px * var(--rx));
  background: var(--button-grey);
}

.theme-option {
  flex: 1 1 0;
  min-height: 0;
  padding: 5px 8px;
  border-radius: calc(4px * var(--rx));
  font-size: 12px;
  font-weight: 500;
  color: var(--muted);
  transition: 0s;
}

.theme-option:hover { color: var(--ink); }

.theme-option.is-picked {
  background: var(--surface);
  color: var(--ink);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12);
}

/* --------------------------------------------------------------- switch -- */

/* A switch reads as a setting you have already made; a checkbox reads as one
   you are filling in. These are already made.
   Here rather than in account.css because the auto-reply panel renders one
   too, and index.html does not load that stylesheet — which is why the
   Enabled control in that panel was an unstyled button, and so invisible. */
.toggle-row {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  padding: 14px 0;
  border-top: 1px solid var(--line-soft);
}

.toggle-row:first-of-type { border-top: 0; }

.toggle-row > div { flex: 1 1 auto; }

.toggle-row b { display: block; font-size: 14px; font-weight: 600; }

.toggle-row span {
  display: block;
  margin-top: 3px;
  font-size: 13px;
  line-height: 1.5;
  color: var(--muted);
}

.switch {
  flex: 0 0 auto;
  width: 40px;
  height: 24px;
  min-height: 0;
  margin-top: 2px;
  padding: 2px;
  border-radius: 999px;
  background: var(--line-input);
  cursor: pointer;
  transition: background 140ms ease;
}

.switch::after {
  content: '';
  display: block;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--surface);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
  transition: transform 140ms ease;
}

.switch[aria-checked='true'] { background: var(--blue); }

.switch[aria-checked='true']::after { transform: translateX(16px); }

/* ------------------------------------------------------------- dragging -- */

/* An avatar, an icon or a brand tile is furniture — dragging one out of the
   interface is never what was meant, and the ghost image it leaves mid-drag
   looks like a bug. Content is different: an image in a message or an article
   is a file someone may well want to pull out. */
img {
  -webkit-user-drag: none;
  user-select: none;
}

#thread-inner img,
#doc-body img,
.doc-body img {
  -webkit-user-drag: auto;
  user-select: auto;
}

/* ------------------------------------------------------------- controls -- */

/* Every control and every action button is at least 40px, wherever it appears.
   Left to themselves these drifted — inputs at 34px in one panel and 38px on the
   settings page, dialog buttons at 36px beside header buttons at 40 — and a row
   built from them never lined up with anything beside it.
   Not every <button> is a control: the tag's x, the composer tools, the rail
   icons and the menu rows are all deliberately smaller, which is why this names
   the two button classes rather than the element. */
input[type='text'],
input[type='email'],
input[type='password'],
input[type='search'],
input[type='number'],
input[type='time'],
input[type='date'],
select,
textarea:not(#draft):not(#auto-body):not(#saved-body),
.btn,
.qr-btn,
.btn-block {
  min-height: 40px;
}

/* The number spinner is the last piece of operating-system chrome in a form
   here: two tiny arrows in a font of their own, at a size nothing else uses.
   A number is typed. */
input[type='number'] {
  -moz-appearance: textfield;
  appearance: textfield;
}

input[type='number']::-webkit-outer-spin-button,
input[type='number']::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

/* The two that are deliberately not controls: a tag chip's input sits inline
   in a row of chips, and the composer grows from one line. */
.tag-input { min-height: 22px; }

/* ------------------------------------------------------------- dropdown -- */

/* The styling half of select.js. A <select> draws its list with the operating
   system — its own font, row height and corners — and none of it can be
   touched, so the list is the app's own menu and the select stays underneath
   as the value. It keeps its size so the field does not jump if the script
   never arrives; everything visible is the trigger on top of it. */
.sel {
  position: relative;
  width: 100%;
}

.sel select {
  position: absolute;
  inset: 0;
  width: 100%;
  opacity: 0;
  pointer-events: none;
}

.sel-trigger {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  min-height: 40px;
  padding: 0 12px;
  border: 1px solid var(--line-input);
  border-radius: calc(6px * var(--rx));
  background: var(--surface);
  font-family: var(--font);
  font-size: 13px;
  color: var(--ink);
  text-align: left;
}

.sel-trigger:hover { border-color: var(--muted); }

.sel-trigger[aria-expanded='true'] {
  border-color: var(--blue);
  box-shadow: 0 0 0 3px var(--blue-wash);
}

.sel-value {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.sel-chevron {
  flex: 0 0 16px;
  width: 16px;
  height: 16px;
  opacity: 0.5;
  transition: transform 120ms ease;
}

.sel-trigger[aria-expanded='true'] .sel-chevron { transform: rotate(180deg); }

/* The same rows the properties menu uses, because it is the same gesture:
   a short list, one of which is already true. */
.sel-menu {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  right: 0;
  z-index: 40;
  max-height: 260px;
  overflow-y: auto;
  padding: 6px;
  background: var(--surface);
  border-radius: calc(9px * var(--rx));
  box-shadow: var(--menu-shadow);
  animation: menu-drop 140ms cubic-bezier(0.22, 1, 0.36, 1);
  transform-origin: top center;
}

.sel-option {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-height: 0;
  padding: 7px 10px;
  border-radius: calc(4px * var(--rx));
  text-align: left;
  font-size: 13px;
  font-weight: 500;
  color: var(--ink);
  transition: 0s;
  user-select: none;
}

/* The label, by name: `> span` also matched an emoji mark, which then took
   flexible width and pushed the text it was meant to sit beside. */
.sel-label {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.sel-option:hover,
.sel-option.is-cursor {
  background: var(--blue-12);
  color: var(--blue);
}

/* The one that is already true, which is what you are here to change. */
.sel-option.is-on { color: var(--blue); font-weight: 600; }

.sel-option:disabled { opacity: 0.4; }

/* Where an option has a mark of its own — a team, mostly — it brings it. */
.sel-mark {
  flex: 0 0 16px;
  width: 16px;
  height: 16px;
  display: block;
  border-radius: calc(3px * var(--rx));
  object-fit: cover;
}

.sel-mark-emoji {
  font-size: 13px;
  line-height: 16px;
  text-align: center;
}

/* -------------------------------------------------------- saved replies -- */

/* The shortcut is the thing you actually reach for, so it reads as something
   typed rather than as a label. */
.auto-row .qr-key {
  flex: 0 0 auto;
  padding: 1px 6px;
  border-radius: calc(4px * var(--rx));
  background: var(--hairline);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11px;
  color: var(--muted);
}

#saved-body {
  width: min(625px, 100%);
  min-height: 220px;
  border: 0;
  padding: 0;
  background: none;
  outline: none;
  resize: none;
  font-family: var(--font);
  font-size: 15px;
  line-height: 1.65;
  color: var(--ink);
}

#saved-body::placeholder { color: var(--muted); }

.auto-field input[type='text'] {
  width: 100%;
  padding: 0 10px;
  border: 1px solid var(--line-input);
  border-radius: calc(6px * var(--rx));
  background: var(--surface);
  font-family: var(--font);
  font-size: 13px;
  color: var(--ink);
}

.auto-field input[type='text']:focus {
  outline: none;
  border-color: var(--blue);
  box-shadow: 0 0 0 3px var(--blue-wash);
}

/* Destructive, and it says so before it is pressed rather than after. */
.saved-delete {
  width: 100%;
  background: var(--danger-wash);
  color: var(--danger);
}

.saved-delete:hover { background: var(--danger-wash-hover); }

/* A panel is a few groups, not a column of controls. What belongs together
   sits close — a field, its hint and the button that applies it — and the
   distance between groups is a rule rather than more air. */
.panel-part {
  width: 260px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.panel-part .auto-field {
  width: 100%;
  margin-bottom: 0;
}

.panel-part .qr-btn { width: 100%; }

/* What the button is about to do, in the one place where saying so matters. */
.panel-danger p {
  margin: 0;
  font-size: 12px;
  line-height: 1.45;
  color: var(--muted);
}

/* ------------------------------------------------------ customer actions -- */

.cust-edit {
  display: flex;
  flex-direction: column;
  gap: 4px;
  width: 260px;
}

.cust-edit .auto-field { margin-bottom: 8px; }

.contact-list {
  max-height: 320px;
  overflow-y: auto;
  margin: 0 -5px;
  padding: 0 5px;
}

.contact-row {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 7px 10px;
  border-radius: calc(6px * var(--rx));
  text-align: left;
  transition: 0s;
}

.contact-row:hover { background: var(--blue-12); }

.contact-row.is-current { background: var(--blue-wash); }

.contact-row img { width: 28px; height: 28px; border-radius: 50%; object-fit: cover; flex: 0 0 28px; }

.contact-who { min-width: 0; }

.contact-who b { display: block; font-size: 14px; font-weight: 600; }

.contact-who > span {
  display: block;
  font-size: 12px;
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ----------------------------------------------------------------- tags -- */

/* A tag row is one line: the tag, and how many conversations carry it. It
   borrows .article for hover and selection but not for height — .article is
   built for a title and two lines of excerpt, and a tag has neither, which
   left a chip floating in the middle of a tall empty row with the count
   trailing after it and nothing lining up down the right. */
.tag-row { padding: 7px 10px; }

.tag-row .auto-row-top {
  justify-content: space-between;
  gap: 10px;
}

/* The chip in the details panel carries a × and is padded for it. This one
   does not. */
.tag-row .tag {
  flex: 0 1 auto;
  min-width: 0;
  padding: 3px 10px;
  font-size: 13px;
  white-space: nowrap;
}

/* On the row you are on, the tag goes solid: a light blue chip on a light blue
   wash is the same colour twice. */
.tag-row.is-active .tag {
  background: var(--blue);
  color: var(--badge-ink);
}

.tag-list {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: 10px;
}

/* The rows are the conversation list's rows, because they are the same rows. */
.tag-list .conv { width: 100%; }

/* The row's own padding was there to separate it from the rows above and
   below it on the settings page. Here it is the only one, and the panel's gap
   and rules do that job — its padding just made the group above look like it
   had been pushed away from the header. */
.auto-enabled {
  width: 260px;
  padding: 0;
}

/* --------------------------------------------------------- auto replies -- */

.auto-editor {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: 25px;
}

/* A message, not a document — so a plain field that grows, rather than the
   rich editor the articles get. */
#auto-body {
  width: min(625px, 100%);
  min-height: 220px;
  border: 0;
  padding: 0;
  background: none;
  outline: none;
  resize: none;
  font-family: var(--font);
  font-size: 15px;
  line-height: 1.65;
  color: var(--ink);
}

#auto-body::placeholder { color: var(--muted); }

.auto-row-top {
  display: flex;
  align-items: center;
  gap: 8px;
}

.auto-row-top .article-title { flex: 1 1 auto; }

.auto-state {
  flex: 0 0 auto;
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--chip);
  color: var(--chip-ink);
  font-size: 11px;
  font-weight: 600;
}

.auto-state.is-on { background: rgba(33, 150, 83, 0.12); color: var(--ok); }

.auto-field {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  width: 260px;
  margin-bottom: 18px;
}

.auto-field > span:first-child {
  width: 100%;
  font-size: 13px;
  color: var(--ink);
}

.auto-field select {
  width: 100%;
  /* background-color, not the shorthand: the shorthand resets the chevron.
     The right padding is what keeps a long option off it. */
  padding: 0 34px 0 10px;
  border: 1px solid var(--line-input);
  border-radius: calc(6px * var(--rx));
  background-color: var(--surface);
  font-family: var(--font);
  font-size: 13px;
  color: var(--ink);
}

.auto-field select:focus {
  outline: none;
  border-color: var(--blue);
  box-shadow: 0 0 0 3px var(--blue-wash);
}

.auto-hint-inline {
  width: 100%;
  font-size: 12px;
  line-height: 1.45;
  color: var(--muted);
}

/* The shortcut sits with the message rather than in the panel: it is part of
   writing the reply, not a setting about one. Narrower than the message
   because it holds a word. */
.saved-shortcut {
  width: min(625px, 100%);
  margin-bottom: 22px;
}

/* The same field it was in the panel, dressed by the same rule — only the
   width differs, because the column it sits in now is wider than the panel. */
.saved-shortcut input[type='text'] { width: min(260px, 100%); }

/* Unsaved work is worth showing on the button that resolves it. */
.btn.is-dirty::after {
  content: '';
  display: inline-block;
  width: 6px;
  height: 6px;
  margin-left: 7px;
  border-radius: 50%;
  background: currentColor;
  vertical-align: middle;
}

/* ------------------------------------------------------- header actions -- */

.head-menu {
  position: absolute;
  top: calc(100% - 14px);
  z-index: 40;
  min-width: 230px;
  max-width: 340px;
  padding: 5px;
  background: var(--surface);
  border-radius: calc(9px * var(--rx));
  box-shadow: var(--menu-shadow);
}

.col-head { position: relative; }

.head-menu .menu-row {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 7px 12px;
  border-radius: calc(4px * var(--rx));
  text-align: left;
  font-size: 14px;
  font-weight: 500;
  transition: 0s;
  user-select: none;
}

.head-menu .menu-row:hover { background: var(--blue-12); color: var(--blue); }

.menu-face { width: 20px; height: 20px; border-radius: 50%; object-fit: cover; }

/* Inlined rather than an <img> so it takes the row's colour on hover, the way
   every other icon in a menu here does. */
.menu-tick { margin-left: auto; flex: 0 0 16px; color: var(--blue); }

/* When a preset resolves to is worth showing — "Until next week" and the date
   it lands on are not the same information. */
.menu-aside {
  margin-left: auto;
  padding-left: 16px;
  white-space: nowrap;
  font-size: 12px;
  font-weight: 400;
  color: var(--muted);
}

.menu-row:hover .menu-aside { color: inherit; opacity: 0.75; }

/* A stack with one inset and one gap. It had three insets — 11 at the top, 7
   at the bottom, 12 at the sides, because the menu's 5px padding was being
   added to three different numbers — and the sentence underneath the fields
   sat flush against them while standing 4px off the buttons. The menu's own
   padding is the 5; this makes every side add up to 12. */
.snooze-pick {
  width: 250px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 7px;
}

.snooze-pick input:focus {
  outline: none;
  border-color: var(--blue);
  box-shadow: 0 0 0 3px var(--blue-wash);
}

/* The picker's two fields are a date and a time; this is the sentence they
   add up to, which is the thing you are actually choosing. */
.snooze-reads {
  margin: 0;
  font-size: 13px;
  font-weight: 500;
  color: var(--blue);
}

.menu-rule { height: 1px; margin: 5px -5px; background: var(--line-soft); }

/* A conversation that is assigned or snoozed says so on the control that did
   it — with colour on the label, and on Assign by wearing the face of whoever
   has it. A ring around the button read as a focus state. */
.btn.is-set { color: var(--blue); }

/* A face needs more room than a glyph to read as a face at all. The button
   keeps its 44x40 — the padding gives the room back rather than the button
   growing and shunting the row along. */
.btn-icon img.is-face {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  object-fit: cover;
}

.btn-icon.is-set { padding: 10px 12px; }

.list-empty {
  padding: 20px 25px;
  font-size: 13px;
  color: var(--muted);
}

/* ---------------------------------------------------- new project flow -- */

.modal {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: grid;
  place-items: center;
  padding: 24px;
  background: rgba(20, 23, 27, 0.45);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
}

.modal-card {
  width: 460px;
  max-width: 100%;
  max-height: 100%;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: 20px;
  background: var(--surface);
  border-radius: calc(9px * var(--rx));
  box-shadow: var(--menu-shadow);
}

.modal-head {
  display: flex;
  align-items: center;
  gap: 10px;
}

.modal-head h3 {
  flex: 1 1 auto;
  font-size: 15px;
  font-weight: 600;
}

.modal-step {
  font-size: 12px;
  color: var(--muted);
}

.modal-body {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.modal-lead {
  font-size: 13px;
  line-height: 1.5;
  color: var(--muted);
}

.modal-lead code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px;
  color: var(--ink);
}

/* The snippet is the one thing on this step worth reading closely, so it gets
   to look like what it is. */
.snippet {
  padding: 12px 14px;
  border-radius: calc(6px * var(--rx));
  background: #14171b;
  color: #e6e9ed;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px;
  line-height: 1.6;
  white-space: pre-wrap;
  word-break: break-all;
  user-select: all;
}

.snippet-copy { align-self: flex-start; }

.emoji-row {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}

.emoji-swatch {
  width: 32px;
  height: 32px;
  display: grid;
  place-items: center;
  border-radius: calc(6px * var(--rx));
  font-size: 17px;
  line-height: 1;
  transition: 0s;
}

.emoji-swatch:hover { background: var(--blue-12); }

.emoji-swatch.is-picked { background: var(--blue-wash); box-shadow: inset 0 0 0 1px var(--blue); }

/* ------------------------------------------------- category settings -- */

.nav-row { position: relative; }

.nav-cog {
  position: absolute;
  top: 50%;
  right: 10px;
  transform: translateY(-50%);
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  border-radius: calc(4px * var(--rx));
  opacity: 0;
  background: var(--surface);
}

.nav-cog img { width: 16px; height: 16px; display: block; opacity: 0.7; }

/* The gear takes the badge's place on hover rather than crowding it. */
.nav-row:hover .nav-cog,
.nav-cog:focus-visible { opacity: 1; }

.nav-row:hover .badge { opacity: 0; }

.nav-cog:hover { background: var(--hairline); }

.nav-cog:hover img { opacity: 1; }

.cat-settings {
  position: absolute;
  left: calc(100% + 10px);
  z-index: 60;
  width: 268px;
  overflow: hidden;
  background: var(--surface);
  border-radius: calc(9px * var(--rx));
  box-shadow: var(--menu-shadow);
}

.cat-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 14px;
  border-bottom: 1px solid var(--line);
}

.cat-head h3 {
  margin: 0;
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
}

.cat-preview {
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  flex: 0 0 28px;
  border-radius: calc(6px * var(--rx));
  background: var(--bubble-in);
  font-size: 12px;
  font-weight: 700;
  color: var(--muted);
  overflow: hidden;
}

.cat-preview img { width: 20px; height: 20px; object-fit: contain; }

.cat-body {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 14px;
}

.cat-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.cat-field span {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.77px;
  text-transform: uppercase;
  color: var(--muted);
}

.cat-field input {
  width: 100%;
  height: 36px;
  padding: 0 11px;
  border: 1px solid var(--line-input);
  border-radius: calc(8px * var(--rx));
  background: var(--surface);
  font-family: var(--font);
  font-size: 14px;
  color: var(--ink);
}

.cat-field input:focus {
  outline: none;
  border-color: var(--blue);
  box-shadow: 0 0 0 3px var(--blue-wash);
}

.cat-error {
  margin: -4px 0 0;
  font-size: 12px;
  color: var(--danger);
}

/* Sits under the field it explains, inside the same label. */
.cat-hint {
  margin: 2px 0 0;
  font-size: 12px;
  line-height: 1.4;
  color: var(--muted);
}

.cat-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
}
