/* ===========================================================================
   Candid — design system
   ---------------------------------------------------------------------------
   Lifted from the Candid.dc.html design file. That file styles everything
   inline, which is right for a design tool and wrong for an application: the
   same button appears forty times and drifts. Here each decision is made once.

   Palette, type scale, radii and motion are the design's; nothing has been
   reinterpreted.
   =========================================================================== */

:root {
  /* The three brandable slots. applyBrand() in app.js overwrites exactly these
     three on <html> from the customer's main / secondary / accent colours; the
     defaults below are Candid's own. Nothing else in this file may hard-code
     them, or a customer's palette lands everywhere except the one place that
     was written out by hand. */
  --ink:    #10211F;  /* MAIN      — headings, sidebar, primary buttons */
  --coral:  #FF9179;  /* SECONDARY — avatars, record marks, hover */
  --teal:   #00C4B4;  /* ACCENT    — progress, focus rings, selection */

  /* What goes *on top* of those three. A customer can pick a pale main colour
     as easily as a dark one, so the foreground is derived from the surface at
     runtime rather than assumed to be white. Same defaults, same result, until
     someone brands the app. */
  --on-ink:   #FFFFFF;
  --on-teal:  #10211F;
  --on-coral: #10211F;

  /* Body copy. --ink is a *surface* colour — the sidebar, the primary button —
     and reading it as the text colour too is what makes a pale brand colour
     unusable: pick buttercup yellow as your main colour and every paragraph in
     the app turns buttercup on white. applyBrand() darkens the main colour
     until it clears 7:1 against the card, and puts the result here. A brand
     that is already dark comes through untouched. */
  --text: #10211F;

  /* Candid's own ink, for foregrounds on the colours that are *not* brandable
     — the amber badge, the green "sending" pill. Those surfaces never move, so
     their text must not move with the brand either. */
  --ink-fixed: #10211F;

  --page:   #F4F4F4;  /* app background */
  --card:   #FFFFFF;
  --amber:  #FFB300;  /* badges, warnings */
  --good:   #2E9E63;  /* solid "going well" — filled stage pills, score rings */
  --bad:    #C0362C;  /* solid "over" — dead stages, alert badges */
  --muted:  #6B6B6B;

  /* The sending calendar's four steps, as four hues rather than one ink fading
     out. The grey ramp it replaced was honest about the ordering and told you
     nothing else: three follow-ups that differed only in how washed out they
     were, which at 14px is not a difference at all.

     Fixed, not brandable. Stage 1 is the one exception — it stays on --teal
     because "the first email is the accent colour" is the point the whole
     calendar is making, and a customer's accent should carry it. The other
     three are Candid's own, so a brand with a blue accent cannot collapse
     stages 1 and 2 into the same chip.

     Ordered teal → blue → orchid → coral, cool to warm, so the ramp still
     reads as a sequence. Validated: every adjacent pair clears the
     colour-blind separation floor under protanopia, deuteranopia and
     tritanopia, and every foreground below clears 4.5:1 on its own fill. Do not
     nudge one of these by eye — the pairs are what was checked, not the
     swatches.

     The gold that used to close the ramp went with the last follow-up. The
     four that remain are the same four that were checked as a run, so nothing
     here needs re-validating. */
  --stage-1:    var(--teal);
  --stage-2:    #2450C8;
  --stage-3:    #D461C4;
  --stage-4:    #D6402F;
  --on-stage-1: var(--on-teal);
  --on-stage-2: #FFFFFF;
  --on-stage-3: var(--ink-fixed);
  --on-stage-4: #FFFFFF;
  --line:   #D4D4D4;
  --hair:   rgba(0, 0, 0, .08);

  /* Typography. Two slots, same deal: applyBrand() replaces the family names,
     the fallback chain stays. A brand face that fails to load therefore lands
     on Geist rather than on Times New Roman. */
  --font-head: 'Geist';
  --font-body: 'Geist';
  --font-stack: system-ui, -apple-system, 'Segoe UI', sans-serif;

  /* Weight is part of a typeface, not a detail of it: Playfair at 400 and
     Playfair at 700 are different decisions about the same brand. The design
     uses two — regular for copy, semibold for everything emphatic — so those
     are the two the customer sets.

     --weight-strong is the emphatic one, and it is *derived* from the body
     weight rather than chosen. It has to move with it: body copy at 600 beside
     labels pinned at 600 is not a bolder app, it is an app with no emphasis
     left. applyBrand() takes it a step up from the body weight, clamped to
     what the family actually publishes. */
  --weight-head:   600;
  --weight-body:   400;
  --weight-strong: 600;

  /* Candid's own mark is a single-colour SVG and is knocked out to white on the
     ink panels. A customer's logo is not ours to recolour, so applyBrand() sets
     this to `none` the moment one is uploaded. */
  --logo-filter: brightness(0) invert(1);

  /* A multiplier, not a size. The mark appears at four different heights — 28
     on the auth panel, 26 in the sidebar, 24 in the onboarding bar, 22 in the
     phone top bar — and those differences are the design's, not an accident.
     One absolute height would flatten them and blow out the top bar. Scaling
     keeps the relationships, and lets a monogram — which reads far larger than
     a wordmark at the same height — be brought back down to match. Trims
     downward only: 1 is the size every one of those four was drawn at. */
  --logo-scale: 1;

  --r-input: 14px;
  --r-card:  24px;   /* the design's card radius; panels run 28px */
  --r-pill:  99px;

  /* Display scale. 1 everywhere, except the small-laptop rule at the foot of
     this file which takes it to .9.

     The type scale, the control heights and the spacing here are all absolute
     px, deliberately — a button is meant to be the same button on every
     screen — so there is no root font-size the app could be re-based on to
     bring it down. `zoom` on <body> is what does it, and it takes
     everything with it: type, controls, radii, borders, shadows.

     What zoom does *not* scale is vh and vw. A 100vh element inside zoom .9
     lays out at the full 720px and then paints at 648, so the shell would
     stop a tenth of the way up the screen. Nothing below may read 100vh or
     100vw directly for that reason: they read --vh and --vw, which divide the
     zoom back out. A new viewport-unit length has to go through them too. */
  --zoom: 1;
  --vh: calc(100vh / var(--zoom));
  --vw: calc(100vw / var(--zoom));

  --topbar-h: 78px;
  /* One height for every pill control on desktop — buttons, icon buttons, tabs,
     chips, the search field. If they can share a row they have to share a
     height, and the only way to guarantee that is one number they all read. */
  --control-h: 42px;
  --aside-w:  236px;
  --aside-w-collapsed: 78px;
}

* { box-sizing: border-box }

/* Every scroller, not just the page.
 *
 * Setting scrollbar-color anywhere switches Chrome to standard scrollbar
 * styling for the whole document, at which point the ::-webkit-scrollbar rules
 * below are ignored and each scroller falls back to `scrollbar-width: auto` —
 * a full 16px system bar. Only <html> carried the thin declaration, so the page
 * scrollbar was slim and every inner one — the main column, the replies list,
 * the thread, the drawers — was fat. The design puts this on `*`.
 *
 * The ::-webkit- rules are kept below for browsers that do not support the
 * standard properties; where both are understood, these win. */
html, * { scrollbar-width: thin; scrollbar-color: color-mix(in srgb, var(--teal) 45%, transparent) transparent }

body {
  margin: 0;
  font-family: var(--font-body), 'Geist', var(--font-stack);
  font-weight: var(--weight-body);
  color: var(--text);
  background: var(--page);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, p { text-wrap: pretty }

/* Headings take the brand's display face and weight; everything else inherits
   the body face and weight from <body>. Both resolve to Geist at 600/400 until
   a customer picks otherwise.

   Re-scoping --weight-strong here rather than adding a second token is what
   makes the forty-odd rules that style a heading by class — .h2, .h3,
   .auth__form h1, .card__head h2 — follow the heading weight without any of
   them being edited, and without a specificity fight they were never written
   to have. A custom property set on the element is read by whichever rule
   wins. */
h1, h2, h3, h4, .h2, .h3 {
  font-family: var(--font-head), 'Geist', var(--font-stack);
  --weight-strong: var(--weight-head);
  font-weight: var(--weight-head);
}

::selection { background: var(--teal); color: var(--on-teal) }

a { color: var(--text); text-decoration: none }
a:hover { color: var(--coral) }

/* Material Symbols, as the design uses them. */
.ms {
  font-family: 'Material Symbols Outlined';
  font-weight: 400;
  font-style: normal;
  line-height: 1;
  letter-spacing: normal;
  text-transform: none;
  display: inline-block;
  white-space: nowrap;
  direction: ltr;
  user-select: none;
}

.cav { font-family: 'Caveat', cursive; font-weight: var(--weight-strong) }

::-webkit-scrollbar { width: 10px; height: 10px; background: transparent }
::-webkit-scrollbar-track, ::-webkit-scrollbar-corner { background: transparent }
::-webkit-scrollbar-thumb {
  background: color-mix(in srgb, var(--teal) 42%, transparent);
  border-radius: var(--r-pill);
  border: 3px solid transparent;
  background-clip: content-box;
  min-height: 40px;
}
::-webkit-scrollbar-thumb:hover { background: color-mix(in srgb, var(--teal) 62%, transparent); background-clip: content-box }

/* --- motion ------------------------------------------------------------- */
@keyframes cdRise    { from { opacity: 0; transform: translateY(8px) } to { opacity: 1; transform: none } }
@keyframes cdFade    { from { opacity: 0 } to { opacity: 1 } }
@keyframes cdSpin    { to { transform: rotate(360deg) } }
@keyframes cdPulse   { 0%, 100% { opacity: 1 } 50% { opacity: .45 } }
@keyframes blobMorph {
  0%, 100% { border-radius: 58% 42% 47% 53% / 46% 55% 45% 54% }
  33%      { border-radius: 38% 62% 63% 37% / 57% 39% 61% 43% }
  66%      { border-radius: 66% 34% 39% 61% / 41% 66% 34% 59% }
}
@keyframes blobDrift {
  0%, 100% { transform: translate(0, 0) rotate(0deg) scale(1) }
  50%      { transform: translate(-2.5%, 2%) rotate(2.6deg) scale(1.04) }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; animation-iteration-count: 1 !important }
}

/* ===========================================================================
   Auth — split screen: blob art left, form right
   =========================================================================== */

.auth {
  position: fixed;
  inset: 0;
  display: grid;
  grid-template-columns: 1.05fr .95fr;
  background: var(--page);
}

.auth__art {
  position: relative;
  overflow: hidden;
  background: var(--ink);
  color: var(--on-ink);
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding: 48px 52px;
}

.auth__blobs { position: absolute; inset: 0; overflow: hidden; pointer-events: none }
.auth__blobs span {
  position: absolute;
  animation: blobMorph 18s ease-in-out infinite, blobDrift 24s ease-in-out infinite;
}
.auth__blobs span:nth-child(1) { top: -14%; left: 52%; width: 58%; height: 44%; background: var(--teal) }
.auth__blobs span:nth-child(2) { top: 38%; right: -14%; width: 34%; height: 26%; background: var(--amber); animation-delay: -6s }
.auth__blobs span:nth-child(3) { bottom: -12%; left: -14%; width: 48%; height: 34%; background: var(--coral); animation-delay: -12s }
.auth__blobs span:nth-child(4) { bottom: -16%; left: 38%; width: 54%; height: 34%; background: var(--teal); animation-delay: -3s }

.auth__logo { position: relative; z-index: 2; display: flex; align-items: center }
.auth__logo img {
  height: calc(28px * var(--logo-scale)); width: auto; max-width: 100%;
  display: block; filter: var(--logo-filter);
}

.auth__pitch { position: relative; max-width: 460px; margin: auto 0; z-index: 2 }
.auth__pitch h2 { margin: 0; font-size: 44px; font-weight: var(--weight-strong); letter-spacing: -.035em; line-height: 1.08 }
.auth__pitch p  { font-size: 15px; line-height: 1.7; color: color-mix(in srgb, var(--on-ink) 78%, transparent); margin-top: 20px }

.auth__points { display: flex; flex-direction: column; gap: 12px; margin-top: 30px }
.auth__point   { display: flex; align-items: flex-start; gap: 11px; font-size: 13.5px; color: color-mix(in srgb, var(--on-ink) 80%, transparent) }
.auth__point .ms { font-size: 18px; color: var(--teal); margin-top: 1px; flex: 0 0 auto }

.auth__pane { display: flex; align-items: center; justify-content: center; padding: 48px; overflow-y: auto }
.auth__form { width: 100%; max-width: 400px }
.auth__form h1 { margin: 0 0 8px; font-size: 28px; font-weight: var(--weight-strong); letter-spacing: -.03em }
.auth__lede { font-size: 13.5px; color: var(--muted); margin-bottom: 30px }

.auth__rule { display: flex; align-items: center; gap: 12px; margin: 26px 0 }
.auth__rule::before, .auth__rule::after { content: ""; flex: 1; height: 1px; background: var(--line) }
.auth__rule span { font-size: 11.5px; color: var(--muted) }

.auth__alt { font-size: 12.5px; color: var(--muted); margin-top: 28px; line-height: 1.6 }

/* Inline text that acts as a control. A styled span would look identical and
   be unreachable by keyboard, so these stay real buttons. */
.linkbtn {
  font: inherit; font-weight: var(--weight-strong); color: var(--text);
  background: none; border: none; padding: 0;
  cursor: pointer; text-decoration: underline; text-underline-offset: 2px;
}
.linkbtn:hover { color: var(--coral) }
.linkbtn:focus-visible { outline: 2px solid var(--teal); outline-offset: 3px; border-radius: 3px }

/* ===========================================================================
   Form controls
   =========================================================================== */

.field { display: block }
.field + .field { margin-top: 16px }
.field__label { font-size: 12px; color: var(--muted); margin-bottom: 6px; display: flex; align-items: baseline; justify-content: space-between; gap: 12px }
.field__label b { font-size: 12px; font-weight: var(--weight-strong); color: var(--text); cursor: pointer }

.input, .textarea, .select {
  width: 100%;
  border: 1px solid var(--line);
  border-radius: var(--r-input);
  padding: 13px 15px;
  font-family: inherit;
  font-size: 13.5px;
  color: var(--text);
  background: var(--card);
  outline: none;
  transition: border-color .16s ease;
}
.input:focus, .textarea:focus, .select:focus { border-color: var(--teal) }
/* No scrollbar and no grab handle: the field is sized to its text by autosize()
   in app.js, so a scrollbar would only ever appear mid-keystroke and dragging
   the corner would be undone by the next one. min-height still sets the floor. */
.textarea { min-height: 120px; resize: none; overflow-y: hidden; line-height: 1.6 }

/* --- checkbox ------------------------------------------------------------
   A <button role="checkbox"> rather than a div with a click handler: Space,
   Enter, a focus ring and a state a screen reader can announce all come free,
   and none of them are worth re-implementing by hand.

   The tick is drawn here rather than set in Material Symbols. The icon font is
   requested with its weight axis pinned at 400, so at 14px the glyph is a
   hairline that antialiases to grey against the filled box — the tick in the
   sign-in form read as smudged rather than as checked. Two borders on a
   rotated box give a stroke thick enough to survive at that size, and one that
   can be sprung in rather than faded.
   ------------------------------------------------------------------------- */
.check {
  display: inline-flex; align-items: center; gap: 10px;
  margin: 16px 0 0 -7px; padding: 7px;
  border: 0; border-radius: 10px; background: none;
  font: inherit; color: inherit; text-align: left;
  cursor: pointer; -webkit-tap-highlight-color: transparent;
  transition: background .16s ease;
}
.check:hover { background: color-mix(in srgb, var(--ink) 5%, transparent) }
.check:focus-visible { outline: 2px solid var(--teal); outline-offset: 1px }

.check__box {
  width: 20px; height: 20px; flex: 0 0 20px;
  border: 1.5px solid var(--line); border-radius: 6px;
  display: grid; place-items: center;
  background: var(--card); color: var(--on-ink);
  transition: background .16s ease, border-color .16s ease, transform .16s ease;
}
.check:hover .check__box { border-color: var(--ink) }
.check:active .check__box { transform: scale(.92) }
.check[data-on="1"] .check__box { background: var(--ink); border-color: var(--ink) }

/* The tick: a 5×9 box turned 45°, keeping only the two borders that make the
   stroke. Rotating puts the ink's centre 2.3px below the element's, which the
   negative margin takes back out — otherwise the tick sits low in the box. */
.check__box::after {
  content: ""; width: 5px; height: 9px; margin-top: -2px;
  border: 2px solid currentColor; border-top: 0; border-left: 0;
  border-radius: 1px;
  transform: rotate(45deg) scale(.4); opacity: 0;
  transition: transform .2s cubic-bezier(.2, 1.4, .4, 1), opacity .1s ease;
}
.check[data-on="1"] .check__box::after { transform: rotate(45deg) scale(1); opacity: 1 }

.check > span:last-child { font-size: 12.5px; color: var(--muted); transition: color .16s ease }
.check[data-on="1"] > span:last-child { color: var(--text) }

/* --- buttons ------------------------------------------------------------ */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  border: 1px solid transparent;
  border-radius: var(--r-pill);
  /* Height is set, not inferred from padding. A button with an 18px icon in it
     is otherwise two pixels taller than the same button without one, which is
     why these never lined up. --control-h matches .iconbtn and the top bar's
     own controls, so any of them can share a row.

     The transparent border is load-bearing too: it keeps a ghost button exactly
     as tall as a solid one, which the design itself gets wrong by 2px. */
  height: var(--control-h);
  padding: 0 20px;
  line-height: 1;
  font-family: inherit;
  font-size: 13.5px;
  font-weight: var(--weight-strong);
  cursor: pointer;
  background: var(--ink);
  color: var(--on-ink);
  transition: opacity .16s ease, border-color .16s ease, background .16s ease;
}
.btn:hover:not(:disabled) { opacity: .88 }
.btn:disabled { opacity: .45; cursor: not-allowed }
/* Modal footers and the save row on a form page: 44px. */
/* Modal footers and the save row on a form page. Kept as a hook for the wider
   padding; the height is the same as every other control. */
.btn--lg { height: var(--control-h); padding: 0 22px }
.btn--block { width: 100%; height: 46px; padding: 0 22px; font-size: 14px }
.btn--ghost { background: var(--card); color: var(--text); border-color: var(--line) }
.btn--ghost:hover:not(:disabled) { border-color: var(--teal); opacity: 1 }
.btn--teal { background: var(--teal); color: var(--on-teal) }
.btn--sm { height: var(--control-h); padding: 0 16px; font-size: 12.5px }
/* Two labels live in the markup and exactly one is ever displayed, so a
   screen reader reads one. The short one waits for the phone breakpoint. */
.btn__short { display: none }
.btn .ms { font-size: 18px }

.btn__spin {
  width: 15px; height: 15px;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: var(--r-pill);
  animation: cdSpin .7s linear infinite;
}

/* ===========================================================================
   App shell
   =========================================================================== */

.shell { display: flex; height: var(--vh); overflow: hidden; background: var(--page) }

.aside {
  width: var(--aside-w);
  flex: 0 0 var(--aside-w);
  background: var(--ink);
  color: var(--on-ink);
  border-right: 1px solid color-mix(in srgb, var(--on-ink) 8%, transparent);
  display: flex;
  flex-direction: column;
  padding: 0 18px 22px 17px;
  gap: 8px;
  position: sticky; top: 0; height: var(--vh); z-index: 9;
  overflow-x: hidden; overflow-y: auto; scrollbar-width: none;
  transition: width .22s ease, flex-basis .22s ease, padding .22s ease;
}
/* 17 and 16, not 18 and 18. The rail is 78px and its items are 44px, so the
   side padding has to add up to exactly 78 - 44 for an item to fill the content
   box — and it has to pay for the 1px border-right too, which border-box charges
   to the same 78: 17 + 44 + 16 + 1 = 78. Splitting it 17/17 left a 43px content
   box, so an item could not be sized by the box it was in; it needed an explicit
   width, and an explicit width is what stopped it from animating. */
.shell[data-collapsed="1"] .aside {
  width: var(--aside-w-collapsed); flex-basis: var(--aside-w-collapsed);
  padding-left: 17px; padding-right: 16px;
}
/* The class here was .aside__label, which nothing has ever had — the label
   is .navitem__label. So collapsing the sidebar narrowed it and left the text
   in place, clipped by overflow rather than hidden.

   Everything that disappears on collapse is now a flex item growing from a zero
   basis, and collapsing it means taking its flex-grow to 0 over the same .22s as
   the width. That matters. The old rules faded these out and then set
   `position: absolute` on them, which is a layout change with no in-between: the
   labels left the flow on the first frame, while the rail was still 236px wide,
   so every icon snapped to its final place and the sidebar then slid out from
   underneath it. Growing to nothing instead keeps the whole rail moving as one
   piece for the full duration. */
.navitem__label, .navitem__badge, .aside__logo, .userrow__who {
  transition: opacity .14s ease, max-width .22s ease, padding .22s ease;
}
.shell[data-collapsed="1"] .navitem__label,
.shell[data-collapsed="1"] .aside__logo,
.shell[data-collapsed="1"] .userrow__who,
.shell[data-collapsed="1"] .navitem__badge {
  opacity: 0; pointer-events: none;
}
/* grow: 1, shrink: 1, basis: 0 — and left alone in both states. These close
   because the box around them closes, not because anything animates them. The
   grow factors have to sum to exactly 1 for that to work: flex only hands out
   free space in proportion to the factors it is given, so a sum below 1 leaves
   the remainder unclaimed, and a centred row would then re-centre itself into
   the slack. That is the whole reason the icons used to swing right and come
   back — flex-grow was being animated 1 -> 0, and every value in between left a
   little more space unclaimed than the last. */
.navitem__label, .userrow__who, .aside__logo { flex: 1 1 0; min-width: 0; overflow: hidden }
/* A badge cannot grow from zero — it is sized by its own digits — so it closes
   on max-width instead. Same .22s, same result. */
.shell[data-collapsed="1"] .navitem__badge { max-width: 0; padding-left: 0; padding-right: 0 }
/* Only the gap. No width, no height, no margin, no padding and — above all — no
   justify-content: the item is already 44px tall, it fills whatever the content
   box is, and its 11px of side padding is the same number in both states. The
   icon is the first thing in a flex-start row, so 17px of aside padding plus 11px
   of item padding puts its left edge at 28px and holds it there for the whole
   .22s. Nothing downstream of it in the row can push it. */
.shell[data-collapsed="1"] .navitem { gap: 0 }

/* Red, and separate from the amber badge: a badge counts work, an alert says
   something is broken. Collapsed it becomes a dot, because a number nobody can
   read is just noise. */
.navitem__alert {
  background: #C0362C; color: #FFF; font-size: 11px; font-weight: var(--weight-strong);
  padding: 3px 9px; border-radius: var(--r-pill);
}
/* left/top, not a margin nudge. A margin is applied to wherever the flow puts
   the dot, and the flow underneath it is moving for .22s — so the dot drifted
   with the label it happened to be sitting behind. The icon it belongs to does
   not move, so neither should it: 46px and 7px is its top-right corner. */
.shell[data-collapsed="1"] .navitem__alert {
  position: absolute; left: 29px; top: 7px; margin: 0; padding: 0;
  width: 9px; height: 9px; flex: 0 0 9px; font-size: 0;
}

/* Account row and its menu */
.userrow {
  display: flex; align-items: center; gap: 12px; width: 100%;
  padding: 14px 12px 14px 14px; border-top: 1px solid color-mix(in srgb, var(--on-ink) 12%, transparent);
  cursor: pointer; background: none; border-left: none; border-right: none; border-bottom: none;
  font-family: inherit; text-align: left; color: var(--on-ink);
  transition: padding .22s ease, gap .22s ease;
}
/* Block, not inline. These are spans, and without a display the name and the
   email laid out on one line and ran into each other — and because ellipsis
   does nothing to an inline box, the overflow was clipped mid-word rather than
   truncated. The design stacks them. */
.userrow__who { display: block }
.userrow__name, .userrow__email { display: block }
.userrow__name { font-size: 13px; font-weight: var(--weight-strong); color: var(--on-ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis }
.userrow__email { font-size: 11px; color: color-mix(in srgb, var(--on-ink) 55%, transparent); white-space: nowrap; overflow: hidden; text-overflow: ellipsis }
.userrow .ms {
  font-size: 18px; line-height: 1; flex: 0 0 20px; width: 20px; height: 20px;
  display: grid; place-items: center; color: color-mix(in srgb, var(--on-ink) 55%, transparent);
  transition: transform .2s ease, flex-basis .22s ease, width .22s ease, opacity .14s ease;
}
.userrow[aria-expanded="true"] .ms { transform: rotate(180deg) }
/* The gap has to go to 0 alongside the two things it was separating, or the
   avatar ends up 32px to the right of the icon rail it is meant to line up
   with — the one place in the sidebar where the alignment is by eye, not by a
   shared 44px box. */
/* 6px, because the avatar in this row is 32px and 6 + 32 + 6 is the 44px rail.
   (32px is set inline by userMenu() — the sidebar row uses a smaller avatar than
   the 38px .avatar default, which is why 3px, the figure that would centre a
   38px one, left it sitting 3px to the left of the icons above it.)

   Padding is what centres it, not justify-content, and that matters twice over.
   It makes the 8px journey monotonic rather than letting the avatar be
   re-centred into slack on the way. And at 6px the content box is exactly the
   width of the avatar, so .userrow__who — flex: 1 1 0, and therefore always
   entitled to whatever is left over — is left nothing to hold on to and closes
   to zero. At 3px it kept 6px of width and pinned the avatar against the
   padding, which is the other half of why it never reached the centre. */
.shell[data-collapsed="1"] .userrow { gap: 0; padding: 14px 6px }
.shell[data-collapsed="1"] .userrow .ms { flex-basis: 0; width: 0; opacity: 0; pointer-events: none }

.aside__brand {
  display: flex; align-items: center; gap: 10px;
  height: var(--topbar-h); flex: 0 0 var(--topbar-h); margin-bottom: 12px; padding: 0 0 0 14px;
  transition: padding .22s ease, gap .22s ease;
}
/* The chevron is never moved by anything. The logo beside it is the flexible
   item, so it absorbs the entire width change, and the toggle — a fixed 44px box
   that cannot shrink — just stays pinned to the right-hand edge of a content box
   that is closing. It arrives, on its own, exactly filling the 44px rail.

   What it used to do: switch the row to `justify-content: center` at the same
   instant the logo left the flow, which teleported the chevron from the right of
   a 236px sidebar to the middle of one — and only then let the width animate,
   sliding it back out to the right. Left, then right, inside 220ms. */
/* The gap closes with the logo. A flex gap is charged between two items even
   when one of them has been grown down to nothing, so 10px of it survived the
   collapse and shunted the chevron 10px off the rail centre — the one thing this
   whole arrangement exists to line up. */
.shell[data-collapsed="1"] .aside__brand { padding: 0; gap: 0 }

/* min-width on the flex parent, max-width on the image: a wordmark at 200%
   should shrink to the sidebar rather than push the collapse toggle off it. */
/* object-fit, not a bare max-width. The height here is fixed, so clamping the
   width alone squashed the wordmark as the row closed — for the 140ms before the
   fade finished you could watch it distort. `contain` scales it down instead and
   keeps it left-aligned, so it reads as the mark shrinking away. */
.aside__logo img {
  height: calc(26px * var(--logo-scale)); width: auto; max-width: 100%;
  object-fit: contain; object-position: left center;
  display: block; filter: var(--logo-filter);
}

.topbar__title { display: flex; align-items: center; min-width: 0 }
.topbar__logo { display: none }
/* The same 44px box as a nav item, in both states — so the chevron is the same
   size and sits on the same centre line whichever way it points, and the hit
   area does not change size under the cursor mid-transition. */
.aside__toggle {
  flex: 0 0 44px; width: 44px; height: 44px;
  display: grid; place-items: center;
  color: color-mix(in srgb, var(--on-ink) 55%, transparent);
  cursor: pointer; font-size: 22px; transition: color .16s ease;
}
.aside__toggle:hover { color: var(--on-ink) }

.aside nav { display: flex; flex-direction: column; gap: 4px }

.navitem {
  display: flex;
  align-items: center;
  gap: 12px;
  /* 44px, set rather than inferred from padding. A 22px icon with 12px above and
     below makes a 46px row, but the collapsed rail's items are 44px — so every
     icon sat 2px lower than its own collapsed self, and because the error
     accumulated down a column with 4px gaps, the last one was 9px out. The icons
     climbed the rail as it closed. One height fixes all five at once: the icon
     centres are identical in both states, so nothing moves vertically at all. */
  height: 44px;
  /* 11px, not 14px, and the same 11px when collapsed: 11 + 22 + 11 is exactly
     the 44px rail, so one number both centres the icon on the closed rail and
     pins it in place on the open one. */
  padding: 0 11px;
  border-radius: 14px;
  font-size: 14px;
  font-weight: 500;
  color: color-mix(in srgb, var(--on-ink) 70%, transparent);
  cursor: pointer;
  transition: background .18s ease, color .18s ease, gap .22s ease, padding .22s ease;
}
.navitem:hover { background: color-mix(in srgb, var(--on-ink) 7%, transparent); color: var(--on-ink) }
.navitem[aria-current="page"] { background: var(--teal); color: var(--on-teal); font-weight: var(--weight-strong) }
.navitem .ms { font-size: 22px; flex: 0 0 auto }
.navitem__label { white-space: nowrap }
.navitem__badge {
  background: var(--amber); color: var(--ink-fixed);
  font-size: 11px; font-weight: var(--weight-strong);
  padding: 3px 9px; border-radius: var(--r-pill);
  flex: 0 0 auto; max-width: 60px; overflow: hidden; white-space: nowrap;
}

.aside__foot { margin-top: auto; display: flex; flex-direction: column; gap: 4px }

.col { display: flex; flex-direction: column; flex: 1; min-width: 0; height: var(--vh) }

.topbar {
  display: flex;
  align-items: center;
  gap: 20px;
  height: var(--topbar-h);
  flex: 0 0 var(--topbar-h);
  padding: 0 32px;
  border-bottom: 1px solid var(--hair);
  background: var(--card);
  position: sticky;
  top: 0;
  z-index: 8;
}
.topbar h1 {
  margin: 0; font-size: 20px; font-weight: var(--weight-strong); letter-spacing: -.02em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  margin-right: auto;
}

.search {
  display: flex; align-items: center; gap: 8px;
  background: var(--page); border: 1px solid var(--line);
  border-radius: var(--r-pill); height: var(--control-h);
  padding: 0 18px; flex: 0 1 340px; min-width: 180px;
  transition: border-color .16s ease;
}
.search:focus-within { border-color: var(--teal) }
.search .ms { font-size: 18px; color: var(--muted) }
.search input {
  flex: 1; min-width: 0; border: none; outline: none; background: transparent;
  font-family: inherit; font-size: 13px; color: var(--text);
}

.avatar {
  width: 38px; height: 38px; flex: 0 0 38px;
  border-radius: var(--r-pill);
  background: var(--coral); color: var(--on-coral);
  display: grid; place-items: center;
  font-size: 13px; font-weight: var(--weight-strong);
  cursor: pointer;
}

/* The way into Business & sending. It is not on the nav, so it carries the
   accent here to stay findable from every screen. */
.bizbtn {
  display: flex; align-items: center; gap: 9px; flex: 0 0 auto;
  height: var(--control-h); box-sizing: border-box; padding: 0 18px;
  border-radius: var(--r-pill); cursor: pointer;
  background: var(--teal); border: 1px solid var(--teal); color: var(--on-teal);
  font-family: inherit; font-size: 13.5px; font-weight: var(--weight-strong); white-space: nowrap;
  transition: background .16s ease, border-color .16s ease, color .16s ease;
}
.bizbtn:hover { background: var(--ink); border-color: var(--ink); color: var(--on-ink) }
.bizbtn .ms { font-size: 19px; color: inherit }

/* The account menu only appears in the header once the sidebar has gone. */
.mavatar { display: none; position: relative; flex: 0 0 auto }
.mavatar .avatar { width: 40px; height: 40px; flex: 0 0 40px; font-size: 13.5px }

.main {
  flex: 1; min-width: 0; min-height: 0;
  overflow-y: auto; overflow-x: hidden;
  scrollbar-gutter: stable;
  padding: 32px;
}

/* mobile tab bar — hidden until the sidebar goes away */
.tabbar { display: none }

/* ===========================================================================
   Content primitives
   =========================================================================== */

.stack { display: flex; flex-direction: column; gap: 24px }
.row   { display: flex; align-items: center; gap: 12px }
.row--wrap { flex-wrap: wrap }
.spacer { margin-left: auto }

/* The design's card is a solid #D4D4D4 hairline at 24px with 26/28 padding.
   This had drifted to an 8%-black border, which on white is several shades
   lighter — cards read as floating tints rather than as bordered surfaces, and
   sat inconsistently beside .panel and .table, which were already correct. */
.card {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  padding: 26px 28px;
}
.card--flush { padding: 0; overflow: hidden }
.card__head {
  display: flex; align-items: center; gap: 12px;
  padding: 20px 24px; border-bottom: 1px solid var(--hair);
  /* The count beside the heading drops to its own line on a phone rather than
     forcing both into a narrow column each and wrapping them side by side. */
  flex-wrap: wrap; row-gap: 2px;
}
.card__head h2 { margin: 0; font-size: 16px; font-weight: var(--weight-strong); letter-spacing: -.02em }
/* The same, for the heads that use an h3 — every one of them does. Without it
   the UA's own 1.17em margin survives inside a flex row, where margins do not
   collapse, and every card head in the voice trainer stood 103px tall for 25px
   of text. It read as a heading floating above an empty band. */
.card__head h3 { margin: 0; font-size: 16px; font-weight: var(--weight-strong); letter-spacing: -.02em }

/* A head inside a padded card must not pad again. At 26/28 on the card plus
   20/24 on the head, the heading sat 24px to the right of the paragraph under
   it and the rule it draws stopped short of both card edges — the two cards in
   the voice trainer, side by side, disagreed about where their left edge was.
   Flush cards keep the padding: there is none on the card for them to sit in. */
.card:not(.card--flush) > .card__head { padding: 0 0 16px; margin: 0 0 18px }

.h2 { margin: 0; font-size: 22px; font-weight: var(--weight-strong); letter-spacing: -.03em }
.lede { font-size: 13.5px; color: var(--muted); line-height: 1.6; margin: 6px 0 0 }
.muted { color: var(--muted) }

.grid { display: grid; gap: 18px }
.grid--4 { grid-template-columns: repeat(4, minmax(0, 1fr)) }
.grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)) }
.grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)) }

/* ===========================================================================
   Dashboard
   ---------------------------------------------------------------------------
   A 12-column grid, as the design lays it out, with panels spanning 12, 7, 6 or
   5. Panels here are 28px radius on a solid #D4D4D4 border rather than the
   hairline used elsewhere — the dashboard is the one screen made of large
   distinct blocks rather than a list of cards.
   =========================================================================== */

/* minmax(0, 1fr), not 1fr.
   A bare `1fr` track is minmax(auto, 1fr), and that `auto` floor is the item's
   min-content — so one wide child drags the whole grid out with it. The
   "Businesses added" strip is a flex row of 262px cards; its min-content is
   about 2200px, which stretched the entire dashboard to 2245px inside a 386px
   phone and pushed the hero, its stats and every panel off the side of the
   screen. A zero floor lets the track take the width it is given and leaves the
   strip to scroll, which is what it is built to do. */
.dash { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); gap: 32px }
/* A period button refetches only the performance figures, so only those fade
   while it happens. The numbers you were reading stay legible and stay put
   rather than being replaced by a spinner and then landing somewhere else. */
.dash__perfdata { transition: opacity .18s ease }
.dash__perfdata[aria-busy="true"] { opacity: .45 }
.sp12 { grid-column: span 12 }
.sp7  { grid-column: span 7 }
.sp6  { grid-column: span 6 }
.sp5  { grid-column: span 5 }

.panel {
  background: var(--card); border: 1px solid var(--line);
  border-radius: 28px; padding: 28px 30px;
}
.panel > h3 { margin: 0 0 22px; font-size: 17px; font-weight: var(--weight-strong) }
.panel__head { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-bottom: 20px }
.panel__head h3 { margin: 0; font-size: 17px; font-weight: var(--weight-strong) }
.panel__link { background: none; border: none; font-family: inherit; font-size: 13px; font-weight: var(--weight-strong); color: var(--text); cursor: pointer }
.panel__link:hover { color: var(--coral) }

/* A labelled bar. Used for stages, priorities, pain points, steps, outcomes —
   the design varies only the track height and the label width. */
.barrow { display: flex; align-items: center; gap: 16px }
.barrow__name { flex: 0 0 130px; min-width: 130px; font-size: 13.5px; overflow-wrap: anywhere }
.barrow__name--flex { flex: 1 1 auto; min-width: 0; font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis }
.barrow__track { flex: 1 1 40px; min-width: 40px; height: 12px; border-radius: var(--r-pill); background: var(--page); overflow: hidden }
.barrow__track--sm { height: 10px }
.barrow__track > div { height: 100%; border-radius: var(--r-pill); background: var(--teal); transition: width .6s cubic-bezier(.4, 0, .2, 1) }
.barrow__value { flex: 0 0 auto; text-align: right; font-size: 13.5px; font-weight: var(--weight-strong); white-space: nowrap }
.barrow__meta { flex: 0 0 auto; text-align: right; font-size: 12.5px; color: var(--muted); white-space: nowrap }

/* Priority rows put the label above a full-width bar rather than beside it. */
.prio { display: flex; flex-direction: column; gap: 8px }
.prio__head { display: flex; align-items: center; justify-content: space-between }
.prio__head b { font-size: 13.5px; font-weight: var(--weight-strong) }
.prio__head span { font-size: 13px; color: var(--muted) }
.prio__track { height: 8px; border-radius: var(--r-pill); background: var(--page); overflow: hidden }
.prio__track > div { height: 100%; border-radius: var(--r-pill); background: var(--teal); transition: width .6s cubic-bezier(.4, 0, .2, 1) }

/* Horizontally scrolling strips of recently added records. */
.strip { display: flex; gap: 16px; overflow-x: auto; padding-bottom: 6px }
.strip__card {
  flex: 0 0 262px; border: 1px solid var(--line); border-radius: 16px;
  padding: 18px 20px; background: var(--card); text-align: left;
  font-family: inherit; color: var(--text); transition: border-color .16s ease;
}
.strip__card[data-clickable] { cursor: pointer }
.strip__card[data-clickable]:hover { border-color: var(--teal) }
.strip__top { display: flex; align-items: center; justify-content: space-between; margin-bottom: 14px }
.strip__tag { font-size: 11.5px; font-weight: var(--weight-strong); padding: 5px 12px; border-radius: var(--r-pill); background: var(--teal); color: var(--on-teal) }
.strip__tag--coral { background: var(--coral) }
.strip__when { font-size: 11.5px; color: var(--muted) }
.strip__name { font-size: 14.5px; font-weight: var(--weight-strong); margin-bottom: 4px }
.strip__meta { font-size: 12.5px; color: var(--muted) }

/* Performance */
.perf__head { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin-top: 8px }
.perf__head h2 { margin: 0; font-size: 22px; font-weight: var(--weight-strong); letter-spacing: -.03em }
.perf__head > span { font-size: 13px; color: var(--muted) }
.perf__periods { margin-left: auto; display: flex; align-items: center; gap: 8px; flex-wrap: wrap }
/* Also the tab strip on Settings, and the type pickers in the add forms — a
   chip is a button, so it stands the same height as one. */
.chip {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  height: var(--control-h); padding: 0 16px;
  font-size: 12.5px; font-weight: var(--weight-strong); border-radius: var(--r-pill);
  border: 1px solid var(--line); background: var(--card); color: var(--muted);
  cursor: pointer; font-family: inherit; white-space: nowrap;
}
.chip[aria-pressed="true"] { background: var(--ink); border-color: var(--ink); color: var(--on-ink) }
/* Shown only when the Custom period is picked. */
.perfdate {
  border: 1px solid var(--line); border-radius: 12px; padding: 9px 12px;
  font-family: inherit; font-size: 12.5px; color: var(--text);
  background: var(--card); outline: none;
}
.perfdate:focus { border-color: var(--teal) }

/* The dashboard's four headline numbers. This lived as an inline style on the
   element, which meant the phone could not touch it: an inline
   repeat(4, 1fr) kept four columns at 375px, squeezing each tile to about
   60px, and 1fr has no floor — so a long value like £153,000 pushed its own
   column wider and ran the whole row off the side of the screen. Both the
   track sizing and the gap belong here, where a media query can reach them. */
.kpis { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 32px }
.kpi { background: var(--card); border: 1px solid var(--line); border-radius: 24px; padding: 24px 26px }
.kpi__label { font-size: 11.5px; font-weight: var(--weight-strong); color: var(--muted); margin-bottom: 12px }
/* Every realistic figure fits the tile at both sizes — a seven-figure pipeline
   still clears it on a 375px phone. This is the backstop for the one that does
   not: a number that outgrows its tile is trimmed inside the card rather than
   allowed to spill across the rounded border and out of the layout. */
.kpi__value {
  font-size: 34px; font-weight: var(--weight-strong); letter-spacing: -.03em;
  min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.kpi__delta { font-size: 12.5px; font-weight: 500; color: #2E9E63; margin-top: 6px }
.kpi__delta--bad { color: #C0362C }

/* Dashboard hero. The one dark panel in the app, and the only thing on the
   dashboard that carries a moving element — a slow drifting blob behind the
   figure, exactly as the design has it. */
.hero {
  position: relative; overflow: hidden;
  background: var(--ink); color: var(--on-ink);
  border-radius: 28px; padding: 38px 40px;
}
.hero__blob {
  position: absolute; top: -150px; right: -70px; width: 480px; height: 400px;
  background: var(--teal); opacity: .14; pointer-events: none;
  border-radius: 58% 42% 47% 53% / 46% 55% 45% 54%;
  animation: blobDrift 24s ease-in-out infinite;
}
.hero__inner {
  position: relative;
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 48px; flex-wrap: wrap;
}
/* Both columns need a zero floor. A flex item's automatic minimum is its
   content, and the left column contains a 520px progress track — so the column
   refused to go under 520px and its `max-width: 100%` resolved against its own
   inflated width, which is no constraint at all. On a phone the value, the
   caption and the bar all ran off the side of the panel. */
.hero__inner > * { min-width: 0 }
.hero__label { font-size: 12px; font-weight: var(--weight-strong); color: color-mix(in srgb, var(--on-ink) 76%, transparent); margin-bottom: 14px }
.hero__value { font-size: 64px; font-weight: var(--weight-strong); letter-spacing: -.04em; line-height: 1 }
.hero__track {
  height: 8px; border-radius: var(--r-pill); background: color-mix(in srgb, var(--on-ink) 28%, transparent);
  overflow: hidden; margin: 26px 0 12px; width: 520px; max-width: 100%;
}
.hero__track > div { height: 100%; background: var(--teal); transition: width .6s cubic-bezier(.4, 0, .2, 1) }
.hero__caption { font-size: 14px; color: color-mix(in srgb, var(--on-ink) 82%, transparent) }
.hero__stats { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; margin-left: auto }
.hero__stat { background: color-mix(in srgb, var(--on-ink) 16%, transparent); border-radius: 14px; padding: 11px 14px; min-width: 118px }
.hero__stat b { display: block; font-size: 10px; font-weight: var(--weight-strong); color: color-mix(in srgb, var(--on-ink) 80%, transparent); margin-bottom: 4px }
.hero__stat span { font-size: 21px; font-weight: var(--weight-strong); letter-spacing: -.03em }

/* "This week" — four large figures, as in the design. */
.week { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 20px }
.week > div { border-radius: 18px; padding: 22px 24px; background: var(--page); color: var(--text) }
.week__value { font-size: 32px; font-weight: var(--weight-strong); letter-spacing: -.03em }
.week__label { font-size: 12.5px; color: var(--muted); margin-top: 6px }
/* One tile carries colour so the eye lands on the number that matters. */
.week--on { background: #2E9E63 !important; color: #FFF !important }
.week--on .week__label { color: rgba(255, 255, 255, .82) }

.stat { background: var(--card); border: 1px solid var(--hair); border-radius: var(--r-card); padding: 22px }
.stat__label { font-size: 12px; color: var(--muted) }
.stat__value { font-size: 30px; font-weight: var(--weight-strong); letter-spacing: -.03em; margin-top: 8px; line-height: 1 }
.stat__foot  { font-size: 12px; color: var(--muted); margin-top: 10px }

.pill {
  display: inline-flex; align-items: center; gap: 6px;
  border-radius: var(--r-pill); padding: 5px 12px;
  font-size: 11.5px; font-weight: var(--weight-strong);
  background: var(--page); color: var(--muted);
}
.pill--teal  { background: color-mix(in srgb, var(--teal) 16%, transparent); color: #04635C }
.pill--amber { background: rgba(255, 179, 0, .18); color: #6B4A00 }
.pill--coral { background: rgba(255, 145, 121, .2); color: #8A2F1A }
.pill--ink   { background: var(--ink); color: var(--on-ink) }
/* The design's two solid tones: a stage that is going well is filled green,
   one that is over is filled red. Both are solid rather than tinted, because
   they are the only two states in the table worth reading from across a room. */
.pill--good  { background: var(--good); color: #FFF }
.pill--bad   { background: var(--bad);  color: #FFF }
/* Green with ink on it rather than white. Two variants because the design uses
   both: white on the stage column, ink on the reply tags. */
.pill--goodink { background: var(--good);  color: var(--text) }
.pill--warn    { background: var(--amber); color: var(--ink-fixed) }
/* The stage column runs a size up from the generic pill — it is the column the
   table is scanned by. */
.pill--stage { font-size: 12.5px; padding: 6px 13px }
/* The connection chips run a size down from the generic pill. */
.pill--sm    { font-size: 11px; padding: 4px 10px }

.tablewrap { overflow-x: auto }
table.tbl { width: 100%; border-collapse: collapse; font-size: 13.5px }
table.tbl th {
  text-align: left; font-size: 11.5px; font-weight: var(--weight-strong); color: var(--muted);
  padding: 14px 24px; border-bottom: 1px solid var(--hair); white-space: nowrap;
}
table.tbl td { padding: 15px 24px; border-bottom: 1px solid var(--hair); vertical-align: middle }
table.tbl tbody tr:last-child td { border-bottom: none }
table.tbl tbody tr { cursor: pointer; transition: background .14s ease }
table.tbl tbody tr:hover { background: var(--page) }

.empty { text-align: center; padding: 64px 24px; color: var(--muted) }
.empty .ms { font-size: 34px; color: #C4C4C4; display: block; margin-bottom: 14px }
.empty h3 { margin: 0 0 6px; font-size: 16px; font-weight: var(--weight-strong); color: var(--text) }
.empty p { margin: 0 auto; font-size: 13.5px; max-width: 420px; line-height: 1.6 }

.banner {
  display: flex; align-items: flex-start; gap: 14px;
  border-radius: var(--r-card); padding: 18px 20px;
  font-size: 13.5px; line-height: 1.6;
}
.banner--amber { background: rgba(255, 179, 0, .14); color: #4A3600 }
.banner--teal  { background: color-mix(in srgb, var(--teal) 14%, transparent); color: #04524C }
.banner--coral { background: rgba(255, 145, 121, .16); color: #7A2A17 }
.banner .ms { font-size: 20px; flex: 0 0 auto; margin-top: 1px }

.toast {
  position: fixed; left: 50%; bottom: 28px; transform: translateX(-50%);
  background: var(--ink); color: var(--on-ink);
  padding: 13px 22px; border-radius: var(--r-pill);
  font-size: 13.5px; font-weight: 500;
  z-index: 90; animation: cdToastRise .25s ease both;
  max-width: calc(var(--vw) - 32px);
}
/* The same trap the modal hits below, and this one was live: cdRise ends on
   `transform: none`, and `both` holds that for the four seconds the toast is
   up — so the translate that centres it was thrown away the moment the
   animation finished, and every toast sat with its left edge on the middle of
   the screen, running off the right. Each frame carries the centring itself.
   The declaration above still matters: reduced motion drops the animation,
   and then it is the only thing centring this. */
@keyframes cdToastRise {
  from { opacity: 0; transform: translate(-50%, 8px) }
  to   { opacity: 1; transform: translate(-50%, 0) }
}
.toast--bad { background: #8A2F1A }

/* ===========================================================================
   Skeletons
   ---------------------------------------------------------------------------
   Nothing in Candid waits behind a spinner. A wheel says "something is
   happening" and nothing else: the screen stays empty, the layout arrives all
   at once, and everything you were about to read jumps into place under your
   cursor. A skeleton says what is coming and how much of it — the toolbar, the
   six columns, the eight rows — so the page you end up looking at is the page
   you were already looking at, with the grey filled in.

   Every skeleton in this app is built from the *real* containers: .table,
   .corow, .dash, .panel, .rep__grid, .members. That is deliberate and it is
   what makes them work on a phone as well as a desktop without a single
   breakpoint of their own — the companies skeleton scrolls sideways at 360px
   because .corow does, and the replies skeleton drops its thread pane because
   .rep[data-view="list"] does. Only the text inside is replaced.
   =========================================================================== */

/* The sweep runs right to left across a block that is twice its own width, so
   the highlight crosses once per cycle rather than snapping back. */
@keyframes cdShimmer { from { background-position: 200% 0 } to { background-position: -200% 0 } }

.skel {
  --skel-fill: linear-gradient(90deg, #E8E8E8 24%, #F4F4F4 50%, #E8E8E8 76%);
  background: var(--skel-fill) 200% 0 / 200% 100%;
  border-radius: var(--r-pill);
  animation: cdShimmer 1.5s linear infinite;
  /* A skeleton stands in for a line of text, so a flex parent must not stretch
     it past the height it was given. */
  flex: 0 0 auto;
}

/* On the sidebar, the tab bar, the hero and the billing hero the ground is
   --ink, and a grey block on it reads as a rendering fault. Same shimmer,
   knocked out of the surface colour rather than laid on top of it. */
.skel--ink { --skel-fill: linear-gradient(90deg, rgba(255, 255, 255, .11) 24%, rgba(255, 255, 255, .2) 50%, rgba(255, 255, 255, .11) 76%) }

/* The progress tracks fill themselves through `> div`, which is a more
   specific selector than .skel — so a skeleton inside one came out as a solid
   teal bar, which is not a placeholder, it is a reading. Named here so it
   shimmers like every other block. */
.hero__track > .skel,
.barrow__track > .skel,
.prio__track > .skel { background: var(--skel-fill) 200% 0 / 200% 100% }

/* Marks, avatars and score rings are the three non-pill shapes. */
.skel--mark { width: 34px; height: 34px; flex: 0 0 34px; border-radius: 10px }
.skel--round { border-radius: var(--r-pill) }
.skel--box { border-radius: 14px }

/* The root of any skeleton tree. Nothing inside one is real, so nothing inside
   one should react to a cursor — .corow and .seqcard both light up on hover,
   and a placeholder that responds to the mouse invites a click that goes
   nowhere. Text selection is off for the same reason: there is no text. */
[data-skeleton] { pointer-events: none; user-select: none; -webkit-user-select: none }

/* The other half of the wait, and the more common one once a screen has been
   opened: the page is already there and readable, and only the data behind it
   is being checked. Nothing is torn down, so the fade is the entire answer to
   the click — enough to say the click landed, not so much that the page stops
   being legible while it waits. The rows on screen are real ones, so they stay
   clickable throughout. */
.screen { transition: opacity .18s ease }
.screen[aria-busy="true"] { opacity: .55 }

/* Job progress — an import running, a file uploading. Not a skeleton: there is
   no shape to preview, only a length of time nobody knows. An indeterminate
   track says that honestly, and the status line under it says the rest. */
.skeltrack {
  height: 6px; border-radius: var(--r-pill); overflow: hidden;
  background: var(--page); margin: 0 auto 14px; max-width: 320px;
}
.skeltrack > div {
  width: 40%; height: 100%; border-radius: var(--r-pill);
  background: var(--teal);
  animation: cdTrack 1.25s cubic-bezier(.4, 0, .2, 1) infinite;
}
@keyframes cdTrack {
  from { transform: translateX(-100%) }
  to   { transform: translateX(250%) }
}

/* provider tiles on the connect screen */
.provider {
  display: flex; align-items: center; gap: 14px;
  border: 1px solid var(--line); border-radius: var(--r-input);
  padding: 16px 18px; background: var(--card);
  cursor: pointer; text-align: left; width: 100%;
  font-family: inherit; color: var(--text);
  transition: border-color .16s ease;
}
.provider:hover:not(:disabled) { border-color: var(--teal) }
.provider:disabled { opacity: .5; cursor: not-allowed }
.provider__mark {
  width: 40px; height: 40px; flex: 0 0 40px;
  border-radius: var(--r-pill); display: grid; place-items: center;
  background: var(--page);
}
.provider__mark .ms { font-size: 21px }
/* Brand marks come in as inline SVG and should not inherit the mark chip's
   grey plate sizing. */
.provider__mark svg { width: 28px; height: 28px; display: block }
.provider b { display: block; font-size: 14px; font-weight: var(--weight-strong) }
.provider small { display: block; font-size: 12px; color: var(--muted); margin-top: 2px }

/* ===========================================================================
   Profile and settings pages
   ---------------------------------------------------------------------------
   Both run the full width of the main column and let their own grids decide
   how the content lays out at that width, rather than a fixed cap deciding it
   for them. .fgrid is auto-fit, so the fields fan out across a wide monitor
   and stack on a phone without a breakpoint being involved.
   =========================================================================== */

.formpage { display: flex; flex-direction: column; gap: 24px; width: 100% }
.settingspage { display: flex; flex-direction: column; gap: 26px }
.fpanel { background: var(--card); border: 1px solid var(--line); border-radius: 24px; padding: 26px 28px }
.fpanel > h3 { margin: 0 0 6px; font-size: 17px; font-weight: var(--weight-strong) }
.fpanel__lede { font-size: 12.5px; color: var(--muted); margin-bottom: 16px }
.fpanel__head { display: flex; align-items: center; gap: 16px; margin-bottom: 24px }
.fpanel__head h3 { margin: 0; font-size: 17px; font-weight: var(--weight-strong) }
.fpanel__head .muted { font-size: 12.5px; margin-top: 3px }

/* Auto-fit so two fields sit side by side on a desktop and stack on a phone
   without a breakpoint deciding it. */
.fgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 16px 20px }
.fgrid .field + .field { margin-top: 0 }
.flabel { font-size: 12px; color: var(--muted); margin-bottom: 7px }
.finput--muted { background: var(--page); color: var(--muted) }

/* ===========================================================================
   Team
   ---------------------------------------------------------------------------
   A card per person rather than a row, because what matters about a colleague
   here is whether their connections are healthy — a broken mailbox stops their
   outreach entirely, and that is a state worth a panel rather than a cell.
   =========================================================================== */

.members { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 20px }
.member {
  background: var(--card); border: 1px solid var(--line); border-radius: 24px;
  padding: 24px 26px; cursor: pointer; text-align: left;
  font-family: inherit; color: var(--text); transition: border-color .16s ease;
}
.member:hover { border-color: var(--ink) }

/* The action inside a blocked notice.
   A plain white pill rather than a solid red one: the panel behind it is
   already carrying the alarm, and a second red on top of the first was louder
   than the problem it described. Only the spacing is set here — everything
   else comes from .btn so it matches every other button in the app. */
.member__blocked .btn { margin-top: 12px }
.member__top { display: flex; align-items: center; gap: 14px; margin-bottom: 18px }
.member__name { font-size: 15.5px; font-weight: var(--weight-strong) }
.member__role { font-size: 12.5px; color: var(--muted) }

/* Something is wrong and this person cannot send until it is fixed. */
.member__blocked {
  display: flex; align-items: flex-start; gap: 11px;
  background: #FCEBE8; border: 1px solid #E9A9A2; border-radius: 14px;
  padding: 13px 15px; margin-bottom: 16px;
}
.member__blocked .ms { font-size: 18px; color: #C0362C }
.member__blocked b { display: block; font-size: 12.5px; font-weight: var(--weight-strong); color: #8E1D15 }
.member__blocked span { display: block; font-size: 12px; line-height: 1.5; color: #8E1D15; margin-top: 3px }

.member__link {
  display: flex; align-items: center; gap: 12px;
  background: var(--page); border-radius: 12px; padding: 11px 14px;
}
.member__link .ms { font-size: 18px; color: var(--teal) }
.member__link span:nth-child(2) {
  flex: 1; font-size: 12.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.member__stats {
  display: flex; align-items: center; gap: 18px; margin-top: 18px;
  padding-top: 16px; border-top: 1px solid var(--hair);
}
.member__stats b { display: block; font-size: 18px; font-weight: var(--weight-strong) }
.member__stats span { display: block; font-size: 11px; color: var(--muted) }

.member--add {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 10px; min-height: 220px; border: 1px dashed var(--teal); border-radius: 24px;
  background: var(--page); cursor: pointer; color: var(--text);
  font-family: inherit; font-size: 14px; font-weight: var(--weight-strong);
}
.member--add:hover { border-color: var(--ink) }
.member--add .ms { font-size: 26px }

.invrow { display: flex; align-items: center; gap: 16px; padding: 14px 0; border-top: 1px solid var(--hair) }
.invrow__mark {
  width: 34px; height: 34px; flex: 0 0 34px; border-radius: var(--r-pill);
  background: var(--page); color: var(--muted);
  display: grid; place-items: center; font-size: 12px; font-weight: var(--weight-strong);
}

.logday { font-size: 11.5px; font-weight: var(--weight-strong); color: var(--muted); padding: 18px 0 6px }
.logrow { display: flex; align-items: center; gap: 14px; padding: 13px 0; border-top: 1px solid var(--hair) }
.logrow__icon {
  width: 32px; height: 32px; flex: 0 0 32px; border-radius: var(--r-pill);
  display: grid; place-items: center; background: var(--page); color: var(--muted);
}
.logrow__icon--bad { background: #FCEBE8; color: #C0362C }
.logrow__text { flex: 1; min-width: 0; font-size: 13.5px; line-height: 1.5 }
.logrow__time { flex: 0 0 auto; font-size: 12px; color: var(--muted); white-space: nowrap }

/* ===========================================================================
   Companies
   ---------------------------------------------------------------------------
   One bordered table with its controls inside it, rather than a card with a
   toolbar floating above. The pain score is a ring rather than a number in a
   pill: it is the figure the whole pipeline is ordered by, and a ring reads as
   a proportion at a glance where a number has to be compared.
   =========================================================================== */

.table { background: var(--card); border: 1px solid var(--line); border-radius: 24px; overflow: hidden }
.table__toolbar {
  display: flex; align-items: center; gap: 10px;
  padding: 20px 26px; border-bottom: 1px solid var(--line); flex-wrap: wrap;
}
.table__tools { margin-left: auto; display: flex; align-items: center; gap: 10px }
.table__filters {
  display: flex; align-items: center; gap: 10px;
  padding: 16px 26px; border-bottom: 1px solid var(--line); flex-wrap: wrap;
}
.table__filters .select { width: auto; min-width: 150px }
/* Was an inline style, which a phone stylesheet cannot override. */
.table__order { margin-left: auto }

/* Holds the head and the rows together so they scroll as one. It does
   nothing until a phone gives the grid a width the screen cannot hold. */
.table__scroll { min-width: 0 }

.corow {
  display: grid; grid-template-columns: 2fr .8fr .6fr 1.2fr .9fr 1fr;
  gap: 14px; align-items: center; padding: 17px 26px;
  border-bottom: 1px solid var(--hair); width: 100%;
  background: none; border-left: none; border-right: none; border-top: none;
  font-family: inherit; text-align: left; color: var(--text); cursor: pointer;
}
.corow:hover { background: var(--page) }
.corow--head {
  background: var(--page); cursor: default; padding: 14px 26px; border-bottom: none;
  font-size: 11.5px; font-weight: var(--weight-strong); color: var(--muted);
}
.corow--head:hover { background: var(--page) }
.corow__mark {
  width: 34px; height: 34px; flex: 0 0 34px; border-radius: 10px;
  background: var(--coral); color: var(--on-coral);
  display: grid; place-items: center; font-size: 12px; font-weight: var(--weight-strong);
}
.corow__name { font-size: 14px; font-weight: var(--weight-strong); white-space: nowrap; overflow: hidden; text-overflow: ellipsis }
.corow__site { font-size: 12px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis }
.corow__right { text-align: right; font-size: 13.5px; font-weight: var(--weight-strong) }

/* Pain score, as a ring. The conic gradient is set inline because the angle is
   the datum. */
.wheel { width: 38px; height: 38px; flex: 0 0 38px; border-radius: var(--r-pill); display: grid; place-items: center }
.wheel__inner {
  width: 28px; height: 28px; border-radius: var(--r-pill); background: var(--card);
  display: grid; place-items: center; font-size: 11.5px; font-weight: var(--weight-strong); color: var(--text);
}
.wheel__of { font-size: 12px; color: var(--muted) }

/* The loading rows this table used to define for itself are now the app-wide
   skeleton system — see "Skeletons", above. They are built out of .corow and
   .corow--head, so they inherit this table's columns and its phone scroller
   rather than approximating them. */

.noresults {
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  padding: 52px 26px; text-align: center;
}
.noresults .ms { font-size: 28px; color: #C4C4C4 }
.noresults b { font-size: 14px; font-weight: var(--weight-strong) }

/* ===========================================================================
   Outreach
   ---------------------------------------------------------------------------
   Three numbered stages down the page — pending approval, approved and
   waiting, sent — so the screen reads as a pipeline rather than a list with a
   status column. The numbering shifts when auto-approve is on, because the
   first stage stops existing.

   Each card carries the whole four-email schedule as a rail, so approving is a
   decision about a sequence rather than about one subject line.
   =========================================================================== */

.stage { display: flex; flex-direction: column; gap: 36px }
.stage__head { display: flex; align-items: center; gap: 12px; margin-bottom: 18px; flex-wrap: wrap }
.stage__head h3 { margin: 0; font-size: 19px; font-weight: var(--weight-strong); letter-spacing: -.02em }
.stage__count {
  font-size: 12px; font-weight: var(--weight-strong); padding: 5px 13px;
  border-radius: var(--r-pill); white-space: nowrap;
  background: var(--amber); color: var(--ink-fixed);
}
.stage__count--go { background: #2E9E63; color: #FFF }
/* The two controls share a wrapper so a phone can move them as a pair. Here
   it takes no part in the layout, leaving the Add button and the switch as
   direct children of the header row, exactly as they were. */
.stage__actions { display: contents }
/* Pushes the Add button and the auto-approve switch together to the right. */
.stage__add { margin-left: auto }
.stage__grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(400px, 1fr)); gap: 20px }

.seqcard {
  border-radius: 24px; padding: 24px 26px; display: flex; flex-direction: column;
  cursor: pointer; text-align: left; font-family: inherit; color: var(--text);
  transition: border-color .16s ease;
  background: #FFF8E6; border: 1px solid #F0DFAE;
}
.seqcard:hover { border-color: var(--ink) }
.seqcard--go { background: #E9F5EE; border-color: #C4E0CF }
.seqcard__top { display: flex; align-items: center; gap: 12px; margin-bottom: 14px }
.seqcard__mark {
  width: 40px; height: 40px; flex: 0 0 40px; border-radius: 11px;
  display: grid; place-items: center; font-size: 13px; font-weight: var(--weight-strong);
  background: var(--coral); color: var(--on-coral);
}
.seqcard--go .seqcard__mark { background: #2E9E63; color: #FFF }
.seqcard__co { font-size: 15px; font-weight: var(--weight-strong) }
.seqcard__person { font-size: 12.5px; color: var(--muted) }
.seqcard__due { margin-left: auto; font-size: 12px; font-weight: var(--weight-strong); color: var(--muted); white-space: nowrap }
.seqcard--go .seqcard__due { color: #1E7A4A }
/* Why this sequence is not moving. Amber because it warns rather than breaks —
   the draft is fine, it is waiting on a person. Left rule instead of a filled
   box: it sits between the company and the subject, and a second filled panel
   inside the card would compete with the card. */
.seqcard__held {
  margin-bottom: 14px; padding: 9px 0 9px 11px;
  border-left: 3px solid var(--amber);
  font-size: 12.5px; line-height: 1.45; color: #6B4A00;
}
.seqcard__held div + div { margin-top: 6px }

.seqcard__label { font-size: 11px; font-weight: var(--weight-strong); letter-spacing: .02em; color: #9A8B5C; margin-bottom: 5px }
.seqcard--go .seqcard__label { color: #6F9C84 }
.seqcard__subject { font-size: 15px; font-weight: var(--weight-strong); line-height: 1.35 }
.seqcard__sender { display: flex; align-items: center; gap: 8px; margin-top: 12px }
.seqcard__sender span:last-child {
  font-size: 12px; color: var(--muted); min-width: 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.seqcard__foot {
  display: flex; align-items: center; gap: 10px; margin-top: 20px;
  padding-top: 18px; border-top: 1px solid rgba(0, 0, 0, .08);
  font-size: 12.5px; font-weight: var(--weight-strong); color: var(--muted);
}
.seqcard--go .seqcard__foot { border-top-color: rgba(16, 33, 31, .1) }
.seqcard__go { display: flex; align-items: center; gap: 7px; font-size: 13px; font-weight: var(--weight-strong); color: #1E7A4A }

/* The five-step schedule, drawn as a rail so the shape of the sequence is
   visible without opening it.

   The dot has to land on the label's centre line, and the gutter cannot ask
   the text how tall it is — so the line box and the gap to the next step are
   named here and both columns are measured off them. Splitting the row height
   between a fixed top segment and an elastic bottom one is what does it: with
   two elastic halves the dot centres in the whole row instead, which sat it
   below a label pinned to the top of that row. */
.rail {
  --rail-line: 20px; --rail-gap: 18px; --rail-dot: 10px;
  display: flex; align-items: stretch; gap: 12px;
}
.rail__gutter { display: flex; flex-direction: column; align-items: center; width: 12px; flex: 0 0 12px }
.rail__line { width: 2px; background: #E6D9AE }
.rail__line:first-child { flex: 0 0 calc((var(--rail-line) - var(--rail-dot)) / 2) }
.rail__line:last-child { flex: 1 1 auto }
.seqcard--go .rail__line { background: #C4E0CF }
/* The stubs above the first dot and below the last one are spacers, not rail:
   they keep both ends of the line stopping dead on a dot. Written after the
   tone rules and with the same reach, or a toned card repaints them. */
.rail__line--blank, .seqcard--go .rail__line--blank { background: transparent }
.rail__dot {
  width: var(--rail-dot); height: var(--rail-dot); flex: 0 0 var(--rail-dot);
  border-radius: var(--r-pill); box-sizing: border-box; background: #E6D9AE;
}
.seqcard--go .rail__dot { background: #2E9E63 }
.rail__dot--first { background: var(--ink) }
.rail__body {
  flex: 1; min-width: 0; display: flex; align-items: baseline; gap: 10px;
  line-height: var(--rail-line); padding-bottom: var(--rail-gap);
}
.rail__label { font-size: 13px; line-height: var(--rail-line); color: var(--text) }
.rail__label--muted { color: var(--muted) }
.rail__when {
  margin-left: auto; font-size: 11.5px; line-height: var(--rail-line);
  color: var(--muted); white-space: nowrap;
}

/* Sent, as a table of sequences with a pip per step. */
.senttable { background: var(--card); border: 1px solid var(--line); border-radius: 24px; overflow: hidden }
.sentrow {
  display: grid; grid-template-columns: minmax(0, 1.4fr) minmax(0, 3.6fr) minmax(0, 1fr);
  gap: 26px; align-items: center; padding: 15px 26px;
  border-top: 1px solid var(--hair); cursor: pointer; width: 100%;
  background: none; border-left: none; border-right: none; border-bottom: none;
  font-family: inherit; text-align: left; color: var(--text);
}
.sentrow:hover { background: var(--page) }
.sentrow--head {
  background: var(--page); border-top: none; padding: 14px 26px; cursor: default;
  font-size: 11.5px; font-weight: var(--weight-strong); color: var(--muted);
}
.sentrow--head:hover { background: var(--page) }
.pips { display: grid; grid-template-columns: repeat(5, 1fr); align-items: start; min-width: 0 }
.pip { position: relative; display: flex; flex-direction: column; align-items: center; gap: 5px; min-width: 0 }
.pip__label { font-size: 10.5px; font-weight: var(--weight-strong); letter-spacing: .01em; color: var(--muted); white-space: nowrap }
.pip__dot {
  width: 22px; height: 22px; border-radius: var(--r-pill); display: grid; place-items: center;
  background: var(--page); color: var(--muted); font-size: 13px; z-index: 1;
}
.pip__dot--done { background: var(--ink); color: var(--on-ink) }
.pip__dot--reply { background: #2E9E63; color: #FFF }
.pip__dot--bad { background: #C0362C; color: #FFF }
.pip__rail { position: absolute; top: 10px; left: 50%; width: 100%; height: 2px; background: var(--hair) }
.pip__date { font-size: 10.5px; color: var(--muted); white-space: nowrap }

/* ---------------------------------------------------------------------------
   Stage three: Sent, or the sending calendar
   ---------------------------------------------------------------------------
   A segmented control rather than two tabs, because these are two views of one
   stage rather than two places to be. It sits at the right of the stage header
   where the Add button sits on the stages above, so the header keeps one shape
   all the way down the page.
   --------------------------------------------------------------------------- */

/* The two halves are two filters over the same stage, which is what the four
   periods on Replies are — so they are drawn the same way: free-standing .chip
   pills in a row, each with its own border, the selected one filled with ink.
   They shared one enclosing track before, which read as a segmented control:
   a different idea, and the only one of its kind in the app. */
.stagetoggle { margin-left: auto; display: inline-flex; align-items: center; gap: 10px }
.stagetoggle .ms { font-size: 17px }
.stagetoggle .chip:hover { color: var(--text) }
.stagetoggle .chip[aria-pressed="true"]:hover { color: var(--on-ink) }

.cal { display: block }
.cal__key { display: flex; align-items: center; gap: 16px; flex-wrap: wrap }
.cal__keyitem { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; color: var(--muted) }
.cal__keyitem .cal__chip:last-of-type { margin-right: 2px }
.cal__swatch { width: 11px; height: 11px; border-radius: 3px; display: inline-block }
.cal__swatch--free { background: var(--line) }

/* One card with hairlines through it, matching the sent table it shares the
   stage with: same surface, same border, same 24px radius, same grey header
   strip. It was thirty separately outlined tiles floating on the page colour,
   which read as a different component belonging to a different screen.

   Five columns, Monday to Friday. Weekends are not hidden to save space — they
   are genuinely not sending days, and a row of permanently empty cells would
   suggest otherwise. */
.cal__card { background: var(--card); border: 1px solid var(--line); border-radius: 24px; overflow: hidden }
/* The key sits in the same grey strip the sent table puts its column names in,
   so the two stages read as one component seen two ways. */
.cal__head { background: var(--page); padding: 13px 26px }
.cal__grid {
  display: grid; grid-template-columns: repeat(5, minmax(0, 1fr));
  border-top: 1px solid var(--hair);
}
.cal__dow {
  background: var(--page); padding: 14px 16px;
  font-size: 11.5px; font-weight: var(--weight-strong); color: var(--muted);
  letter-spacing: .02em; border-left: 1px solid var(--hair);
}
.cal__day {
  display: flex; flex-direction: column; justify-content: space-between; gap: 10px;
  min-height: 104px; padding: 13px 16px; text-align: left; cursor: pointer;
  background: var(--card); border: none;
  border-top: 1px solid var(--hair); border-left: 1px solid var(--hair);
  font-family: inherit; color: var(--text);
  transition: background .16s ease;
}
/* Five day-name cells come first, so every fifth child from the first is the
   Monday column — header included, which is exactly what wants no left edge. */
.cal__grid > :nth-child(5n + 1) { border-left: none }
.cal__day:hover:not(:disabled) { background: var(--page) }
.cal__day:disabled { cursor: default }
.cal__day--past { opacity: .5 }
.cal__day--today { box-shadow: inset 0 0 0 2px var(--ink) }
.cal__day--picked { box-shadow: inset 0 0 0 2px var(--teal) }
.cal__day--picked:hover { background: var(--card) }

.cal__date { display: flex; align-items: baseline; gap: 5px }
.cal__date b { font-size: 15px; font-weight: var(--weight-strong) }
.cal__date span { font-size: 11px; color: var(--muted) }

/* One bar, three segments, sized by count. The shape of the day is readable
   without reading the numbers, which is the whole point of putting six weeks on
   one screen. */
.cal__bar { display: flex; gap: 2px; height: 6px; margin-bottom: 8px }
.cal__seg { display: block; border-radius: 99px; min-width: 3px }
/* The stage ramp on the bar, the same four --stage-N the chips and tags wear.
   One source, so a stage cannot be one colour in the key and another in the bar
   underneath it. A day then reads left to right as "one new conversation, then
   the tail of the ones started before it" — and reads it in hue, which survives
   being 6px tall better than four shades of the same grey did. */
.cal__seg--1 { background: var(--stage-1) }
.cal__seg--2 { background: var(--stage-2) }
.cal__seg--3 { background: var(--stage-3) }
.cal__seg--4 { background: var(--stage-4) }
.cal__seg--free { background: var(--line) }

/* One line, four possible entries, only the non-zero ones drawn. The day detail
   keeps this form; the cell uses the column below. */
.cal__stages { display: flex; align-items: center; gap: 7px; flex-wrap: wrap }
.cal__stage { display: inline-flex; align-items: center; gap: 3px }
.cal__stage b { font-size: 11.5px; font-weight: var(--weight-strong) }

/* The cell's column: chip, name, count, one stage per line.
   Three explicit tracks rather than a flex row, so the counts land on one right
   edge and the names start on one left edge in every cell of the grid — the
   whole reason for stacking them. The middle track is the one that gives when a
   column is narrow, and it ellipsises rather than wrapping, because a name that
   wraps takes its row's count with it and the alignment is gone. */
.cal__rows { display: flex; flex-direction: column; gap: 3px }
.cal__stagerow {
  display: grid; grid-template-columns: 14px minmax(0, 1fr) auto;
  align-items: center; gap: 8px; font-size: 11.5px;
}
/* The name is the flexible track and the only one allowed to be cut. The metric
   beside it is the row's whole payload — a step's name is recoverable from the
   chip and the key, "4 emails · 3 people" is not recoverable from anything. */
.cal__stagename {
  color: var(--muted); min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cal__stagemetric { color: var(--muted); white-space: nowrap }
.cal__stagemetric b {
  color: var(--text); font-weight: var(--weight-strong);
  font-variant-numeric: tabular-nums;
}
.cal__chip {
  display: inline-grid; place-items: center; width: 14px; height: 14px;
  border-radius: 4px; font-size: 9.5px; font-weight: var(--weight-strong);
  font-style: normal; line-height: 1; flex: 0 0 14px;
}
.cal__free { font-size: 11px; color: var(--muted); margin-top: 7px }
/* One span per part, so the line wraps between them and never through one. */
.cal__free span { white-space: nowrap }
.cal__closed { font-size: 11.5px; color: var(--muted) }

.cal__detail {
  margin-top: 12px; background: var(--card); border: 1px solid var(--line);
  border-radius: 24px; padding: 18px 26px;
}
.cal__detailhead { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; flex-wrap: wrap }
.cal__detailhead b { font-size: 15px; font-weight: var(--weight-strong) }
.cal__detailhead .closer { margin-left: auto }
.cal__list { display: flex; flex-direction: column }
.cal__item {
  display: grid; grid-template-columns: 104px minmax(0, 1.6fr) minmax(0, 1fr) auto;
  gap: 14px; align-items: center; padding: 9px 0;
  border-top: 1px solid var(--hair); font-size: 13px;
}
.cal__item:first-child { border-top: none }
.cal__tag {
  font-size: 11px; font-weight: var(--weight-strong); padding: 4px 10px;
  border-radius: var(--r-pill); background: var(--page); color: var(--muted);
  text-align: center; white-space: nowrap;
}
.cal__from { font-size: 12px; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap }

/* The stage ramp again, for the two things that carry a digit or a word. Taken
   straight, with the foreground each fill was checked against — nothing mixed
   or faded, because a 9.5px bold digit on a washed-out square is a smudge,
   which is what the old ink ramp made of stages 3 and 4.
   Declared after .cal__chip and .cal__tag, not with the bar: both set their own
   background, and at equal specificity the later rule wins. */
.cal__chip--1, .cal__tag--1 { background: var(--stage-1); color: var(--on-stage-1) }
.cal__chip--2, .cal__tag--2 { background: var(--stage-2); color: var(--on-stage-2) }
.cal__chip--3, .cal__tag--3 { background: var(--stage-3); color: var(--on-stage-3) }
.cal__chip--4, .cal__tag--4 { background: var(--stage-4); color: var(--on-stage-4) }
.cal__who { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap }
.cal__status { font-size: 12px; white-space: nowrap }

/* On a phone the week grid stops being a grid: five columns of 60px tell you
   nothing. One day per row, with the same bar, reads as a list of what is
   coming — which is what a calendar is for on a small screen anyway. */
@media (max-width: 720px) {
  .cal__grid { grid-template-columns: minmax(0, 1fr) }
  .cal__dow { display: none }
  .cal__day {
    min-height: 0; flex-direction: row; align-items: center; gap: 14px;
    border-left: none;
  }
  /* Once the day names are hidden the first day cell sits on the card's own top
     edge and must not draw a second line under it. Addressed by position, not
     by :first-child: the five day-name cells are still in the DOM, just not
     displayed, so the grid's first child is one of those. */
  .cal__grid > .cal__day:nth-child(6) { border-top: none }
  .cal__day > div:last-child { flex: 1 1 auto; min-width: 0 }
  .cal__date { flex: 0 0 54px; gap: 6px }
  .cal__free { margin-top: 2px }
  .cal__item { grid-template-columns: minmax(0, 1fr); gap: 3px }
  .cal__tag { justify-self: start }
}

/* The auto-approve control, which is a switch plus a state word. */
/* No margin-left:auto here. The Add button beside it already has one, and two
   auto margins in a flex row split the free space between them — which left the
   Add button stranded in the middle of the header instead of sitting next to
   this switch, as the design has it. */
.aa {
  display: flex; align-items: center; gap: 10px;
  border: 1px solid var(--line); background: var(--card);
  padding: 9px 14px; border-radius: var(--r-pill); cursor: pointer; font-family: inherit;
}
.aa:hover { border-color: var(--ink) }
.aa b { font-size: 13px; font-weight: var(--weight-strong); white-space: nowrap }
.aa__state { font-size: 12px; font-weight: var(--weight-strong); color: var(--muted) }
.aa__state--on { color: #1E7A4A }

/* ===========================================================================
   Replies
   ---------------------------------------------------------------------------
   Two panes that own the viewport rather than scrolling with the page: a list
   of conversations on the left, the selected conversation on the right. On a
   phone they become one pane and the thread pushes in over the list, which is
   how every mail client on a phone behaves and therefore what people expect.
   =========================================================================== */

.rep { display: flex; flex-direction: column; height: calc(var(--vh) - 142px); min-height: 0; overflow: hidden }
/* Same idea as the dashboard: a period chip refetches the list, so the list
   fades and the chips stay solid and clickable while it does. */
.rep[aria-busy="true"] .rep__grid { opacity: .45 }
.rep__filters {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; margin-bottom: 20px; flex-wrap: wrap; flex: 0 0 auto;
}
/* Separates the four periods from the toggle that cuts across all of them. */
.rep__sep { width: 1px; height: 24px; background: var(--line); margin: 0 4px }
.rep__grid {
  display: grid; grid-template-columns: minmax(0, .8fr) minmax(0, 1.6fr);
  gap: 24px; flex: 1 1 auto; min-height: 0;
  transition: opacity .18s ease;
}

.rep__list { height: 100%; min-height: 0 }
.rep__scroll {
  padding: 14px 8px 14px 0; height: 100%; box-sizing: border-box;
  overflow-y: auto; overflow-x: hidden; scrollbar-gutter: stable;
  /* Fades the list at both ends so a part-scrolled conversation does not look
     like the last one. */
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 20px, #000 calc(100% - 20px), transparent 100%);
  mask-image: linear-gradient(to bottom, transparent 0, #000 20px, #000 calc(100% - 20px), transparent 100%);
}

.rep__item {
  display: flex; flex-direction: column; gap: 9px; width: 100%; text-align: left;
  padding: 14px 16px; border-radius: 16px; margin-bottom: 8px; cursor: pointer;
  background: var(--card); border: 1px solid var(--line); font-family: inherit; color: var(--text);
  transition: background .16s ease, border-color .16s ease;
}
.rep__item:hover { border-color: #B9B9B9 }
/* Selection is the ink border. It has to be a border rather than a fill
   because the fill is already carrying a different signal — see below. */
.rep__item[aria-selected="true"] { border-color: var(--ink) }

/* A conversation waiting on a human turns the whole card pink, text included.
   That is the one state on this screen worth seeing without reading, so it
   takes the fill and leaves selection the border. Selecting a pink card
   deepens it rather than replacing the signal. */
.rep__item[data-awaiting="1"] { background: #FCEBE8; border-color: #E9A9A2 }
.rep__item[data-awaiting="1"]:hover { border-color: #D98A80 }
.rep__item[data-awaiting="1"][aria-selected="true"] { background: #F7D9D5; border-color: var(--ink) }
.rep__item[data-awaiting="1"] .rep__name { color: #8E1D15 }
.rep__item[data-awaiting="1"] .rep__preview,
.rep__item[data-awaiting="1"] .rep__time { color: #A8382E }
.rep__top { display: flex; align-items: center; justify-content: space-between; gap: 10px }
.rep__name { font-size: 14px; font-weight: var(--weight-strong); white-space: nowrap; overflow: hidden; text-overflow: ellipsis }
.rep__time { font-size: 11.5px; color: var(--muted); white-space: nowrap; flex: 0 0 auto }
.rep__preview {
  font-size: 12.5px; color: var(--muted); line-height: 1.5;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.rep__badge {
  font-size: 11px; font-weight: var(--weight-strong); padding: 4px 10px;
  border-radius: var(--r-pill); background: #C0362C; color: #FFF;
}

.rep__thread {
  background: var(--card); border: 1px solid var(--line); border-radius: 24px;
  height: 100%; box-sizing: border-box; overflow: hidden; min-height: 0; padding: 6px;
}
.rep__threadscroll {
  padding: 26px 18px 26px 28px; height: 100%; box-sizing: border-box;
  overflow-y: auto; overflow-x: hidden; scrollbar-gutter: stable;
}
/* Only ever shown on a phone, where the thread has covered the list. */
.rep__back { display: none; align-items: center; gap: 10px; margin: -8px 0 16px; cursor: pointer;
  background: none; border: none; font-family: inherit; width: 100%; text-align: left; color: var(--text) }

.bubble { border: 1px solid var(--line); border-radius: 18px; padding: 20px 22px }
.bubble--you   { background: var(--page); margin-inline-start: min(56px, 8%) }
.bubble--them  { background: var(--card); margin-inline-end: min(56px, 8%) }
.bubble__meta  { display: flex; align-items: center; gap: 6px; font-size: 11.5px; font-weight: var(--weight-strong); color: var(--muted); margin-bottom: 8px }
.bubble__text  { font-size: 14.5px; line-height: 1.7; white-space: pre-line }

.suggest { background: var(--page); border: 1px solid var(--teal); border-radius: 20px; padding: 20px 22px }
.suggest__label { display: flex; align-items: center; gap: 8px; font-size: 12px; font-weight: var(--weight-strong); margin-bottom: 10px }
.suggest__text { font-size: 14.5px; line-height: 1.7; white-space: pre-line }
.suggest .textarea { background: var(--card); font-size: 14.5px; line-height: 1.7 }
.suggest__editing { display: flex; align-items: center; gap: 14px; margin-top: 10px; flex-wrap: wrap }
.suggest__editing b { display: flex; align-items: center; gap: 5px; font-size: 11.5px; font-weight: var(--weight-strong); color: #B07C00 }
.suggest__revert {
  display: flex; align-items: center; gap: 5px; background: none; border: none;
  font-family: inherit; font-size: 11.5px; font-weight: var(--weight-strong); color: var(--muted); cursor: pointer;
}
.suggest__revert:hover { color: var(--text) }

.meetbox { background: var(--card); border: 1px solid var(--line); border-radius: 16px; padding: 16px 18px; margin-top: 16px }
.meetbox__head { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; flex-wrap: wrap }
.meetbox__head b { font-size: 13px; font-weight: var(--weight-strong) }
.meetbox__head span { margin-left: auto; font-size: 11.5px; color: var(--muted) }
.slots { display: flex; flex-wrap: wrap; gap: 9px }
.slot {
  font-size: 12.5px; font-weight: var(--weight-strong); padding: 8px 14px; border-radius: var(--r-pill);
  border: 1px solid var(--line); background: var(--card); color: var(--text);
  cursor: pointer; font-family: inherit;
}
.slot[aria-pressed="true"] { background: var(--ink); border-color: var(--ink); color: var(--on-ink) }

/* ===========================================================================
   Controls the inner screens share
   =========================================================================== */

.h3 { margin: 0; font-size: 17px; font-weight: var(--weight-strong); letter-spacing: -.02em }

/* Pill switch. Used for auto-approve and every notification preference. */
.switch {
  width: 42px; height: 24px; flex: 0 0 42px;
  border-radius: var(--r-pill); background: var(--line);
  position: relative; cursor: pointer; display: inline-block;
  transition: background-color .18s ease;
}
.switch[aria-checked="true"] { background: var(--teal) }
.switch__knob {
  position: absolute; top: 3px; left: 3px; width: 18px; height: 18px;
  border-radius: var(--r-pill); background: #FFF;
  box-shadow: 0 1px 3px rgba(0, 0, 0, .2);
  transition: transform .18s cubic-bezier(.32, .72, 0, 1);
}
.switch[aria-checked="true"] .switch__knob { transform: translateX(18px) }

/* Horizontal bar, for every breakdown on the dashboard. */
.meter { height: 8px; border-radius: var(--r-pill); background: var(--page); overflow: hidden }
.meter__fill {
  height: 100%; border-radius: var(--r-pill); background: var(--teal);
  transition: width .5s cubic-bezier(.4, 0, .2, 1);
}

.slider { flex: 1; accent-color: var(--ink); height: 24px }

/* Sidebar account menu */
.usermenu__wrap { position: relative }
.usermenu {
  position: absolute; bottom: calc(100% + 10px); left: 0; right: 0;
  background: var(--card); border: 1px solid var(--line);
  border-radius: 18px; padding: 8px; z-index: 15;
  box-shadow: 0 18px 44px rgba(16, 33, 31, .22);
  animation: cdRise .16s ease both;
}
.usermenu__who { padding: 10px 12px 12px }
.usermenu__who b { display: block; font-size: 13px; font-weight: var(--weight-strong); color: var(--text) }
.usermenu__who span { display: block; font-size: 11.5px; color: var(--muted) }
.usermenu__item {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 10px 12px; border-radius: 12px; cursor: pointer;
  background: none; border: none; font-family: inherit;
  font-size: 13px; font-weight: 500; color: var(--text); text-align: left;
}
.usermenu__item:hover { background: var(--page) }
.usermenu__item .ms { font-size: 18px; color: inherit }
.usermenu__item--danger {
  color: #C0362C; margin-top: 4px;
  border-top: 1px solid var(--hair); border-radius: 0 0 12px 12px;
}
.shell[data-collapsed="1"] .usermenu { left: 0; width: 200px; right: auto }
/* Hung off the header avatar rather than the sidebar row, so it opens downward
   and to the left of its trigger instead of upward and full-width. */
.usermenu--drop {
  top: calc(100% + 12px); bottom: auto; left: auto; right: 0; width: 238px;
}

/* Header search results */
.search { position: relative }
.results {
  position: absolute; top: calc(100% + 10px); left: 0; right: 0; z-index: 25;
  background: var(--card); border: 1px solid var(--line); border-radius: 18px;
  padding: 6px; max-height: calc(var(--vh) * .6); overflow-y: auto;
  box-shadow: 0 20px 50px rgba(16, 33, 31, .16);
  animation: cdRise .16s ease both;
}
.results__head {
  font-size: 11px; font-weight: var(--weight-strong); color: var(--muted);
  padding: 10px 12px 6px; text-transform: uppercase; letter-spacing: .04em;
}
.results__row {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 12px; border-radius: 12px; cursor: pointer;
}
.results__row:hover { background: var(--page) }
.results__row b { font-size: 13px; display: block }
.results__empty { padding: 16px 12px; font-size: 13px; color: var(--muted) }

/* ===========================================================================
   Onboarding — the setup wizard
   ---------------------------------------------------------------------------
   A full-screen shell of its own rather than a route inside the app, because
   until it finishes there is no pipeline, no ICP and no voice for the app to
   show. It is also the only surface that repaints itself in the customer's own
   colours: the reveal step transitions this shell from Candid's ink to their
   brand, which is why the palette is held in custom properties and everything
   that uses one carries a background-color transition.
   =========================================================================== */

.ob {
  position: fixed; inset: 0; z-index: 60;
  display: flex; flex-direction: column;
  --ob-main: var(--ink);
  --ob-accent: var(--teal);
  background: var(--ob-main); color: var(--on-ink);
  font-family: var(--ob-body, var(--font-body)), 'Geist', var(--font-stack);
  transition: background-color .8s cubic-bezier(.4, 0, .2, 1);
}

.ob__bar {
  position: absolute; top: 0; left: 0; right: 0; z-index: 2;
  display: flex; align-items: center; justify-content: space-between; gap: 24px;
  padding: 24px 32px; pointer-events: none;
}
.ob__bar img {
  height: calc(24px * var(--logo-scale)); width: auto; max-width: calc(var(--vw) * .46);
  display: block; filter: var(--logo-filter);
}
.ob__bar button {
  pointer-events: auto; background: none; border: none; cursor: pointer;
  font-family: inherit; font-size: 13px; font-weight: var(--weight-strong); color: color-mix(in srgb, var(--on-ink) 60%, transparent);
}
.ob__bar button:hover { color: var(--on-ink) }

.ob__scroll {
  position: relative; z-index: 1; flex: 1; min-height: 0;
  overflow-y: auto; overflow-x: hidden;
  display: flex; flex-direction: column; align-items: center;
  padding: 80px 32px 48px;
}
.ob__group {
  margin: auto 0; width: 100%; max-width: 660px;
  display: flex; flex-direction: column; align-items: stretch; gap: 18px;
}
/* The question card is a different height on every question, and the group is
   centred as a whole — so a short question used to pull the checklist above it
   down the screen and a long one pushed it back up. On a desktop there is room
   to hold the group to one height instead: the checklist sits at the top of
   that fixed box and stays where it is, and the card grows and shrinks into
   the space below it. The 100% keeps the box inside the viewport on a short
   window, where it fills the space and the checklist sits at the top of it. */
@media (min-width: 901px) {
  .ob__group--prog { min-height: min(640px, 100%) }
  /* Otherwise the card would take the spare height as flex growth and every
     question would be one tall box with the answers stranded at the top. */
  .ob__group--prog > .ob__card { flex: 0 0 auto }
}

.ob__card {
  flex: 1 1 0; min-width: 0; width: auto; box-sizing: border-box;
  background: var(--card); color: var(--text);
  border-radius: 28px; padding: 38px 40px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, .22);
  transition: background-color .8s cubic-bezier(.4, 0, .2, 1);
}
.ob__card h1 {
  margin: 0 0 10px; font-size: 29px; font-weight: var(--weight-strong);
  letter-spacing: -.03em; line-height: 1.15;
  font-family: var(--ob-head, var(--font-head)), 'Geist', var(--font-stack);
}
.ob__lede { font-size: 14px; line-height: 1.65; color: var(--muted); margin-bottom: 26px; max-width: 520px }
.ob__label { font-size: 12px; color: var(--muted); margin-bottom: 6px }
.ob__label span { color: #9A9A9A; font-weight: 400 }
/* Slightly tighter than the app's fields: the design uses a 12px radius here,
   not the 14px used everywhere else. */
.ob .input, .ob .select, .ob .textarea { border-radius: 12px; padding: 12px 14px }

.ob__foot { display: flex; align-items: center; gap: 16px; margin-top: 26px; flex-wrap: wrap }
.ob__status { font-size: 12.5px; color: var(--muted) }
.ob__cta {
  margin-left: auto; display: flex; align-items: center; gap: 8px;
  border: none; background: var(--ob-main); color: var(--on-ink);
  font-family: inherit; font-size: 14px; font-weight: var(--weight-strong);
  padding: 13px 24px; border-radius: var(--r-pill); cursor: pointer;
  transition: background-color .8s cubic-bezier(.4, 0, .2, 1), opacity .16s ease;
}
.ob__cta:disabled { opacity: .45; cursor: not-allowed }
.ob__back {
  display: flex; align-items: center; gap: 5px; background: none; border: none;
  font-family: inherit; font-size: 12.5px; font-weight: var(--weight-strong); color: var(--text); cursor: pointer;
}
.ob__back .ms { font-size: 16px }
.ob__skip {
  background: none; border: none; font-family: inherit;
  font-size: 12.5px; font-weight: var(--weight-strong); color: var(--muted); cursor: pointer;
}
.ob__skip:hover { color: var(--text) }

/* mailbox / calendar pickers */
.ob__providers { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px }
.ob__provider {
  display: flex; align-items: center; gap: 13px; width: 100%; text-align: left;
  border: 1px solid #E4E4E4; border-radius: 16px; padding: 16px 17px;
  background: var(--card); cursor: pointer; font-family: inherit; color: var(--text);
  transition: border-color .18s ease;
}
.ob__provider:hover:not(:disabled) { border-color: var(--ink) }
.ob__provider:disabled { opacity: .5; cursor: not-allowed }
.ob__provider b { display: block; font-size: 14px; font-weight: var(--weight-strong) }
.ob__provider small { display: block; font-size: 12px; color: var(--muted); margin-top: 2px }

/* logo and font drop targets */
.ob__drop {
  width: 100%; border: 1.5px dashed #C9C9C9; border-radius: 14px; padding: 22px;
  display: flex; align-items: center; justify-content: center; text-align: center;
  cursor: pointer; background: var(--card); font-family: inherit; color: var(--text);
}
.ob__drop:hover, .ob__drop[data-over="1"] { border-color: var(--teal) }
.ob__drop--filled { padding: 14px 16px }
.ob__chip {
  width: 62px; height: 62px; flex: 0 0 62px; border-radius: 12px;
  border: 1px solid #E4E4E4; display: grid; place-items: center; background-color: #FFF;
  /* Chequerboard, so a transparent logo reads as transparent rather than white. */
  background-image:
    linear-gradient(45deg, #EFEFEF 25%, transparent 25%, transparent 75%, #EFEFEF 75%),
    linear-gradient(45deg, #EFEFEF 25%, transparent 25%, transparent 75%, #EFEFEF 75%);
  background-size: 12px 12px; background-position: 0 0, 6px 6px;
}
.ob__chip img { max-width: 44px; max-height: 44px; width: auto; height: auto; display: block }

/* logo scale — only shown once there is a logo of the customer's own to size */
.ob__scale { display: flex; align-items: center; gap: 14px; margin-top: 12px }
.ob__scale[hidden] { display: none }
.ob__scale .slider { flex: 1; min-width: 0 }
.ob__scaleval {
  flex: 0 0 auto; min-width: 52px; text-align: right;
  font-size: 12.5px; font-weight: var(--weight-strong); color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.ob__scalereset {
  flex: 0 0 auto; border: none; background: none; padding: 0; cursor: pointer;
  font-family: inherit; font-size: 12.5px; font-weight: var(--weight-strong);
  color: var(--muted); text-decoration: underline;
}
.ob__scalereset:hover { color: var(--text) }
.ob__scalereset:disabled { opacity: 0; pointer-events: none }

.ob__swatch { width: 38px; height: 38px; flex: 0 0 38px; border-radius: 12px; border: 1px solid #E4E4E4 }

/* The swatch *is* the colour picker.
 *
 * A hex field alone asks the customer to already know their brand code, which
 * half of them do not — and a swatch that only reports the colour is a control
 * that looks pressable and is not. Native input[type=color] gives the OS picker
 * with an eyedropper for free; all it needs is the browser's own chrome taken
 * off so it reads as the same 38px tile the design drew. */
input.ob__swatch {
  padding: 0; cursor: pointer; background: none; -webkit-appearance: none; appearance: none;
}
input.ob__swatch::-webkit-color-swatch-wrapper { padding: 0 }
input.ob__swatch::-webkit-color-swatch { border: none; border-radius: 11px }
input.ob__swatch::-moz-color-swatch { border: none; border-radius: 11px }

.ob__hex {
  width: 116px; flex: 0 0 116px; text-transform: uppercase;
  font-variant-numeric: tabular-nums;
}

/* --- the type picker ------------------------------------------------------
   A combobox rather than a <select>, because the list is every Google font.
   1,820 native options cannot be searched, cannot be filtered by category, and
   render every name in the interface face — which loses the one thing a font
   list is for. */
.fontpick__pair { display: flex; align-items: stretch; gap: 10px }
.fontpick { position: relative; flex: 1 1 auto; min-width: 0 }

.fontpick__weight {
  flex: 0 0 152px; height: 46px; padding: 0 34px 0 13px;
  border-radius: var(--r-input); border: 1px solid var(--line);
  background: var(--card); color: var(--text);
  font-family: inherit; font-size: 13.5px; cursor: pointer;
  -webkit-appearance: none; appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%),
                    linear-gradient(135deg, var(--muted) 50%, transparent 50%);
  background-position: calc(100% - 18px) 21px, calc(100% - 13px) 21px;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}
.fontpick__weight:focus { outline: none; border-color: var(--teal) }
.fontpick__weight:disabled { opacity: .5; cursor: not-allowed; background-image: none }

@media (max-width: 560px) {
  .fontpick__pair { flex-wrap: wrap }
  .fontpick__weight { flex: 1 1 100% }
}

.fontpick__btn {
  display: flex; align-items: center; gap: 10px; width: 100%;
  height: 46px; padding: 0 13px 0 15px; border-radius: var(--r-input);
  border: 1px solid var(--line); background: var(--card); color: var(--text);
  font-family: inherit; font-size: 14px; cursor: pointer; text-align: left;
  transition: border-color .16s ease;
}
.fontpick__btn:hover { border-color: var(--teal) }
.fontpick__btn[aria-expanded="true"] { border-color: var(--ink) }
.fontpick__btn .ms { margin-left: auto; font-size: 20px; color: var(--muted); flex: 0 0 auto }
.fontpick__name {
  flex: 1; min-width: 0; font-size: 16px; font-weight: 500;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.fontpick__pop {
  position: absolute; z-index: 40; top: calc(100% + 6px); left: 0; right: 0;
  display: flex; flex-direction: column;
  border: 1px solid var(--line); border-radius: 16px; background: var(--card);
  box-shadow: 0 18px 44px rgba(0, 0, 0, .16);
  overflow: hidden;
  animation: cdRise .16s ease both;
}
.fontpick__pop[hidden] { display: none }

.fontpick__search { display: flex; align-items: center; gap: 9px; padding: 12px 14px 10px }
.fontpick__search .ms { font-size: 19px; color: var(--muted); flex: 0 0 auto }
.fontpick__input {
  flex: 1; min-width: 0; border: none; outline: none; background: none;
  font-family: inherit; font-size: 14px; color: var(--text);
}
.fontpick__input::-webkit-search-cancel-button { filter: grayscale(1) opacity(.5) }

.fontpick__cats {
  display: flex; gap: 6px; padding: 0 14px 11px; overflow-x: auto;
  border-bottom: 1px solid var(--hair); scrollbar-width: none;
}
.fontpick__cats::-webkit-scrollbar { display: none }
.fontpick__cat {
  flex: 0 0 auto; border: 1px solid var(--line); background: var(--card);
  border-radius: var(--r-pill); padding: 5px 11px; cursor: pointer;
  font-family: inherit; font-size: 11.5px; font-weight: var(--weight-strong); color: var(--muted);
  transition: background .14s ease, color .14s ease, border-color .14s ease;
}
.fontpick__cat[aria-pressed="true"] {
  background: var(--ink); border-color: var(--ink); color: var(--on-ink);
}

/* Fixed height rather than max-height: a list that changes height as you type
   walks the panel up and down the page under the pointer. */
.fontpick__list { height: 268px; overflow-y: auto; padding: 6px }

.fontpick__group {
  padding: 10px 10px 5px; font-size: 11px; font-weight: var(--weight-strong);
  letter-spacing: .04em; text-transform: uppercase; color: var(--muted);
}

.fontpick__row {
  display: flex; align-items: baseline; gap: 12px; width: 100%;
  padding: 8px 10px; border: none; border-radius: 10px; background: none;
  font-family: inherit; text-align: left; cursor: pointer; color: var(--text);
}
.fontpick__row:hover, .fontpick__row--active { background: var(--page) }
.fontpick__row[aria-selected="true"] { background: var(--ink); color: var(--on-ink) }
.fontpick__rowname {
  flex: 1; min-width: 0; font-size: 17px; line-height: 1.35;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.fontpick__rowcat { flex: 0 0 auto; font-size: 10.5px; font-weight: var(--weight-strong); color: var(--muted) }
.fontpick__row[aria-selected="true"] .fontpick__rowcat { color: inherit; opacity: .7 }

/* Both the "still loading" marker and the sentinel that loads it. */
.fontpick__more, .fontpick__none {
  padding: 14px 10px; font-size: 12.5px; color: var(--muted); text-align: center;
}

.fontpick__upload {
  display: flex; align-items: center; gap: 9px; width: 100%;
  padding: 13px 16px; border: none; border-top: 1px solid var(--hair);
  background: var(--card); font-family: inherit; font-size: 13px;
  font-weight: var(--weight-strong); color: var(--text); cursor: pointer; text-align: left;
}
.fontpick__upload:hover { background: var(--page) }
.fontpick__upload .ms { font-size: 19px; color: var(--muted) }

/* Set inline to the face being previewed, so the sample is the answer to
   "what does this look like" rather than a description of it. */
.ob__sample { font-size: 21px; line-height: 1.25; margin-top: 10px; color: var(--text) }
.ob__sample small { display: block; font-size: 12.5px; color: var(--muted); margin-top: 5px }

/* --- the live preview -----------------------------------------------------
   Three colours, a logo and two faces are six decisions whose only real test
   is what they look like together. This is that test, at a size that fits
   beside the controls: the sidebar in the main colour with the logo on it, a
   record mark in the secondary, a progress bar and a button in the accent. */
.ob__preview {
  border: 1px solid var(--line); border-radius: 18px; overflow: hidden;
  display: grid; grid-template-columns: 96px minmax(0, 1fr); min-height: 188px;
  background: var(--card);
}
.ob__pvside {
  padding: 14px 12px; display: flex; flex-direction: column; gap: 14px;
  transition: background-color .4s ease;
}
.ob__pvlogo { min-height: 20px; display: flex; align-items: center }
.ob__pvlogo img { max-width: 100%; width: auto; display: block }
.ob__pvnav { font-size: 11px; font-weight: var(--weight-strong); padding: 6px 8px; border-radius: var(--r-pill) }
.ob__pvbody { padding: 16px 18px; display: flex; flex-direction: column; gap: 12px; background: var(--page) }
.ob__pvhead { font-size: 17px; font-weight: var(--weight-strong); letter-spacing: -.02em; margin: 0 }
.ob__pvcopy { font-size: 12.5px; line-height: 1.55; margin: 0 }
.ob__pvrow { display: flex; align-items: center; gap: 10px; margin-top: auto }
.ob__pvmark {
  width: 30px; height: 30px; flex: 0 0 30px; border-radius: 9px;
  display: grid; place-items: center; font-size: 11px; font-weight: var(--weight-strong);
}
.ob__pvtrack { flex: 1; height: 7px; border-radius: var(--r-pill); background: #E2E2E2; overflow: hidden }
.ob__pvtrack > div { width: 64%; height: 100% ; border-radius: var(--r-pill) }
.ob__pvbtn {
  border: none; border-radius: var(--r-pill); height: 30px; padding: 0 15px;
  font-family: inherit; font-size: 12px; font-weight: var(--weight-strong);
}

/* The reveal, and the signup password meter, both used to live here.
   ---------------------------------------------------------------------------
   The reveal was the onboarding step that spent seven seconds animating a
   customer's colours onto the interface. It went when the platform console took
   over collecting branding: the business is created already branded, so
   applyBrand() runs at sign-in before any screen is drawn and there is nothing
   left to reveal. The meter went earlier, with the signup screen, when Candid
   became sold rather than self-served.

   Their rules are deleted rather than left in place. Dead CSS naming a screen
   that no longer exists is worse than no CSS: it reads as a feature to whoever
   finds it next, and it is the sort of thing somebody restores a broken version
   of because the styles were still there. */

/* ===========================================================================
   Overlays — the drawers and the import dialog
   ---------------------------------------------------------------------------
   Both sit above the shell rather than replacing it, because both are things
   you do *to* a row you are looking at. Losing the list behind them would mean
   losing your place in it.
   =========================================================================== */

.scrim {
  position: fixed; inset: 0; z-index: 20;
  background: rgba(16, 33, 31, .18);
  animation: cdFade .22s ease both;
}
.scrim--dark { background: rgba(16, 33, 31, .28); z-index: 30 }

.drawer {
  position: fixed; top: 0; right: 0; bottom: 0; z-index: 21;
  width: min(680px, calc(var(--vw) * .92));
  background: var(--page); border-left: 1px solid var(--line);
  display: flex; flex-direction: column;
  animation: cdSlideIn .32s cubic-bezier(.22, .8, .28, 1) both;
  will-change: transform;
}
/* The design's drawer slides the full width of itself, from off-screen. Mine
   nudged 24px and faded, which reads as a flicker rather than a panel arriving
   — the movement is what tells you where it came from and how to dismiss it. */
@keyframes cdSlideIn { from { transform: translateX(100%) } to { transform: translateX(0) } }
@keyframes cdFadeIn  { from { opacity: 0 } to { opacity: 1 } }
@keyframes cdSweep {
  0%   { transform: translateX(-60%) skewX(-12deg); opacity: 0 }
  25%  { opacity: .85 }
  100% { transform: translateX(160%) skewX(-12deg); opacity: 0 }
}
@keyframes cdFloatA { 0%, 100% { transform: translate3d(0,0,0) rotate(-1.5deg) } 50% { transform: translate3d(14px,-22px,0) rotate(1deg) } }
@keyframes cdFloatB { 0%, 100% { transform: translate3d(0,0,0) rotate(2deg) }    50% { transform: translate3d(-18px,16px,0) rotate(-1.5deg) } }
@keyframes cdFloatC { 0%, 100% { transform: translate3d(0,0,0) rotate(1deg) }    50% { transform: translate3d(10px,20px,0) rotate(-2deg) } }

.drawer__head {
  display: flex; align-items: center; gap: 16px;
  height: var(--topbar-h); flex: 0 0 var(--topbar-h); padding: 0 28px;
  background: var(--card); border-bottom: 1px solid var(--line);
}
.drawer__head h3 { margin: 0; font-size: 17px; font-weight: var(--weight-strong); letter-spacing: -.02em }
.drawer__head .muted { font-size: 12.5px; margin-top: 2px }

.drawer__tabs {
  display: flex; align-items: center; gap: 8px; flex: 0 0 auto;
  padding: 14px 28px; background: var(--card); border-bottom: 1px solid var(--line);
}
.tab {
  display: inline-flex; align-items: center; justify-content: center;
  height: var(--control-h); padding: 0 18px;
  font-size: 13px; font-weight: var(--weight-strong);
  border-radius: var(--r-pill); cursor: pointer; white-space: nowrap;
  color: var(--text); background: var(--card); border: 1px solid var(--line);
  transition: background .16s ease, color .16s ease, border-color .16s ease;
}
.tab:hover { border-color: var(--ink) }
.tab[aria-selected="true"] { background: var(--ink); border-color: var(--ink); color: var(--on-ink) }

.drawer__body {
  flex: 1; overflow-y: auto; overflow-x: hidden; scrollbar-gutter: stable;
  padding: 24px 20px 40px 28px;
  display: flex; flex-direction: column; gap: 20px;
}

.modal {
  position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 31;
  width: min(680px, calc(var(--vw) * .92)); max-height: calc(var(--vh) * .88); overflow: auto; scrollbar-gutter: stable;
  background: var(--card); border: 1px solid var(--line); border-radius: 26px;
  padding: 30px 32px; box-shadow: 0 24px 60px rgba(16, 33, 31, .18);
  animation: cdPop .24s ease both;
}
/* Needs its own keyframes rather than cdRise: an animation with `both` holds
   its final transform after it finishes, so cdRise's `transform: none` would
   overwrite the translate that centres this and drop the dialog half a viewport
   down and to the right. Every frame has to carry the centring itself. */
@keyframes cdPop {
  from { opacity: 0; transform: translate(-50%, -50%) scale(.985) }
  to   { opacity: 1; transform: translate(-50%, -50%) }
}
.modal__head { display: flex; align-items: center; gap: 12px; margin-bottom: 14px }
.modal__head h3 { margin: 0; font-size: 18px; font-weight: var(--weight-strong); letter-spacing: -.02em; flex: 1; min-width: 0 }
/* A warning modal leads with its icon rather than its words. */
.modal__mark {
  width: 38px; height: 38px; flex: 0 0 38px; border-radius: 11px;
  background: #FFF8E6; color: #B07C00; display: grid; place-items: center;
}
.modal__mark .ms { font-size: 21px }
.modal__lede { font-size: 13.5px; line-height: 1.7; color: var(--text); margin: 0 0 14px; text-wrap: pretty }
.aapoints { display: flex; flex-direction: column; gap: 9px; margin-bottom: 20px }
.aapoint { display: flex; gap: 9px; font-size: 13px; color: var(--muted) }
.aapoint .ms { font-size: 17px; color: #B07C00; flex: 0 0 auto }

.closer { font-size: 22px; color: var(--muted); cursor: pointer; flex: 0 0 auto }
.closer:hover { color: var(--text) }

/* The recessed panels the design uses inside cards for a single fact. */
.tile { background: var(--page); border-radius: 16px; padding: 16px 18px }
.tile__label { font-size: 12px; color: var(--muted); margin-bottom: 6px }
.tile__value { font-size: 15px; font-weight: var(--weight-strong); line-height: 1.35 }

.iconbtn {
  display: grid; place-items: center;
  width: var(--control-h); height: var(--control-h); flex: 0 0 var(--control-h);
  border-radius: var(--r-pill); border: 1px solid var(--line);
  background: var(--card); color: var(--muted); cursor: pointer; padding: 0;
  transition: border-color .16s ease, color .16s ease;
}
.iconbtn:hover:not(:disabled) { border-color: var(--ink); color: var(--text) }
.iconbtn:disabled { opacity: .45; cursor: not-allowed }
.iconbtn--bad:hover:not(:disabled) { border-color: #C0362C; color: #C0362C }
.iconbtn .ms { font-size: 19px }

/* The Filters button stands alone at the left of the table toolbar, with the
   tools group pushed away to the right, so it has no neighbour to be told
   apart from and the ring around it was doing no work. Bare icon, but still
   --control-h square, so it takes the same bite out of the row and the
   toolbar stands at one height. */
.filterbtn {
  display: grid; place-items: center;
  width: var(--control-h); height: var(--control-h); flex: 0 0 var(--control-h);
  border: none; background: none; padding: 0; border-radius: var(--r-pill);
  color: var(--muted); cursor: pointer; transition: color .16s ease;
}
.filterbtn:hover { color: var(--text) }
.filterbtn .ms { font-size: 19px }
/* Filters are applied. The border used to carry this; the icon carries it now. */
.filterbtn--on { color: var(--ink) }

/* Only ever shown on a phone, where the search field has collapsed. */
.searchbtn {
  display: none; place-items: center; width: 40px; height: 40px; flex: 0 0 40px;
  border-radius: var(--r-pill); border: 1px solid var(--line);
  background: var(--card); color: var(--text); cursor: pointer; padding: 0;
}

/* Approving is the one irreversible button in the app, so it is the one place
   that gets a colour of its own rather than the house ink. */
.btn--go { background: #2E9E63; color: #FFF }
.btn--go:hover:not(:disabled) { background: #26824F; opacity: 1 }

/* One email within a four-step sequence. The card carries the state of the
   whole sequence, not of itself: all four are yellow while the sequence waits
   on the user and all four turn green the moment it is approved. Same two
   tones as the pipeline cards on the outreach page, so a draft looks the same
   wherever you meet it. */
.step { background: var(--card); border: 1px solid var(--line); border-radius: 18px; padding: 18px 20px }
.step--pending { background: #FFF8E6; border-color: #F0DFAE }
.step--go { background: #E9F5EE; border-color: #C4E0CF }
/* Last, so an edited card keeps its amber border over either tone. */
.step--edited { border-color: var(--amber) }
.step__num {
  width: 28px; height: 28px; flex: 0 0 28px; border-radius: var(--r-pill);
  display: grid; place-items: center; background: var(--page);
  font-size: 12px; font-weight: var(--weight-strong);
}
.step--pending .step__num { background: #F6EACB; color: #6B4A00 }
.step--go .step__num { background: #2E9E63; color: #FFF }
.step .textarea { min-height: 190px; font-size: 13.5px }

.dropzone {
  display: flex; align-items: center; gap: 14px;
  background: var(--page); border: 1px dashed var(--teal); border-radius: 18px;
  padding: 20px 22px; width: 100%; text-align: left;
  font-family: inherit; color: var(--text); cursor: pointer;
}
.dropzone:hover, .dropzone[data-over="1"] { border-color: var(--ink) }
.dropzone .ms { font-size: 24px; flex: 0 0 auto }

/* Column mapping: your header, what it becomes, what is actually in it. */
.maprow {
  display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 14px; align-items: center;
  padding: 11px 4px; border-top: 1px solid var(--hair);
}
.maprow > * { min-width: 0 }
.maprow__head { border: none; padding: 0 4px 10px; font-size: 11.5px; font-weight: var(--weight-strong); color: var(--muted) }
.maprow__name { font-size: 13px; font-weight: var(--weight-strong); white-space: nowrap; overflow: hidden; text-overflow: ellipsis }
.maprow__sample { font-size: 12.5px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis }
.maprow .select { padding: 9px 12px; font-size: 12.5px; border-radius: 11px }

/* ===========================================================================
   Mobile — the sidebar becomes a bottom tab bar, as in the design
   =========================================================================== */

/* ===========================================================================
   Business & sending
   =========================================================================== */

.settabs { display: flex; align-items: center; gap: 10px; flex-wrap: wrap }

/* One labelled row of the ideal customer profile, and the same shape reused
   for a pain point. Grey card on white, as the design has it. */
.icprow { background: var(--page); border-radius: 16px; padding: 16px 18px }
.icprow__label { font-size: 14px; font-weight: var(--weight-strong); margin-bottom: 6px }
.icprow__value { font-size: 13px; line-height: 1.6; color: var(--muted); text-wrap: pretty }
.icprow .input, .icprow .textarea { width: 100%; box-sizing: border-box }
.icpsub { font-size: 12.5px; color: var(--muted); margin: -12px 0 18px }

.pprow__top { display: flex; align-items: baseline; gap: 12px; margin-bottom: 6px }
.pprow__label { font-size: 14px; font-weight: var(--weight-strong) }
.pprow__wlabel { margin-left: auto; font-size: 11.5px; font-weight: var(--weight-strong); color: var(--muted); white-space: nowrap }
.pprow__name { flex: 1; min-width: 0 }
.pprow__weight { flex: 0 0 118px }
.pprow__x { font-size: 18px; color: #9A9A9A; cursor: pointer }
.pprow__x:hover { color: #C0362C }

/* Key business stats. Both fields edit in place — the card *is* the form. */
.statgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 12px }
.statcard { position: relative; background: var(--page); border-radius: 16px; padding: 18px 20px }
.statcard__x {
  position: absolute; top: 12px; right: 12px; display: grid; place-items: center;
  width: 26px; height: 26px; border-radius: var(--r-pill);
  font-size: 16px; color: var(--muted); cursor: pointer;
}
.statcard__x:hover { background: var(--card); color: #C0362C }
.statcard__stat {
  width: 100%; border: none; background: transparent; font-family: inherit;
  font-size: 26px; font-weight: var(--weight-strong); letter-spacing: -.02em; line-height: 1.1;
  color: var(--text); padding: 0 30px 0 0; margin-bottom: 8px; outline: none;
}
.statcard__text {
  width: 100%; border: none; background: transparent; resize: none; font-family: inherit;
  font-size: 13px; line-height: 1.6; color: var(--muted); padding: 0; outline: none;
}

/* Font upload row on the Branding tab. */
.ob__font {
  display: flex; align-items: center; gap: 12px; width: 100%;
  border: 1.5px dashed #C9C9C9; border-radius: 12px; padding: 12px 14px;
  cursor: pointer; background: var(--card); font-family: inherit; text-align: left;
}
.ob__font:hover { border-color: var(--teal) }
.ob__font .ms { font-size: 20px; color: var(--muted) }
.ob__font .ob__font-ok { color: #12A57A }

/* A card whose content is a table, so the head keeps its padding but the rows
   run edge to edge. Shared by the do not send list and Invoices. */
.tablecard {
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--r-card); overflow: hidden;
}
.tblhead { display: flex; align-items: center; gap: 12px; padding: 22px 26px; flex-wrap: wrap }
.tblhead h3 { margin: 0; font-size: 17px; font-weight: var(--weight-strong) }

.strow {
  display: grid; grid-template-columns: 2fr 1.1fr 1.4fr .8fr; gap: 14px;
  align-items: center; padding: 15px 26px; border-bottom: 1px solid var(--hair);
  font-size: 13px;
}
.strow--head {
  padding: 12px 26px; background: var(--page);
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
  font-size: 11.5px; font-weight: var(--weight-strong); color: var(--muted);
}
.strow__who {
  min-width: 0; font-size: 13.5px; font-weight: var(--weight-strong);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* Billing */
.billhero {
  position: relative; overflow: hidden;
  background: var(--ink); color: var(--on-ink);
  border-radius: var(--r-card); padding: 30px 32px;
}
.billhero__inner {
  position: relative; display: flex; align-items: flex-start;
  justify-content: space-between; gap: 32px; flex-wrap: wrap;
}
.billhero__plan { font-size: 30px; font-weight: var(--weight-strong); letter-spacing: -.03em }
.billhero__sub { font-size: 13.5px; color: color-mix(in srgb, var(--on-ink) 82%, transparent); margin-top: 8px }
.billhero__acts { display: flex; gap: 10px; flex-wrap: wrap; margin-left: auto }

/* Two buttons that only ever sit on the dark hero. */
.btn--accent { background: var(--teal); color: var(--on-teal) }
/* The colour is repeated here on purpose. This button is an anchor now (the
   plan enquiry is a mailto), and the global `a:hover` recolour outranks the
   plain .btn--accent rule — without restating it the label turns coral on
   hover while the pill stays teal. */
.btn--accent:hover:not(:disabled) { background: var(--teal); color: var(--on-teal); opacity: .88 }

/* Log out, on Your profile. */
.btn--danger { color: #C0362C }
.btn--danger:hover:not(:disabled) { border-color: #C0362C }

/* Your profile: daily send cap and notification rows. */
.capline { font-size: 13.5px; font-weight: var(--weight-strong); margin-bottom: 10px }
.capout { flex: 0 0 78px; text-align: right; font-size: 14.5px; font-weight: var(--weight-strong) }
.capnote { margin-top: 12px; font-size: 11.5px; line-height: 1.5; color: var(--muted) }

.notif {
  display: flex; align-items: center; gap: 16px;
  background: var(--page); border-radius: 16px; padding: 16px 18px; cursor: pointer;
}
.notif__label { font-size: 13.5px; font-weight: var(--weight-strong) }
.notif__hint { font-size: 12.5px; color: var(--muted); margin-top: 3px }

/* ===========================================================================
   Company drawer
   =========================================================================== */

.drawer__kicker {
  font-size: 11.5px; font-weight: var(--weight-strong); letter-spacing: .01em; color: var(--muted);
}

/* Overview — Candid fit */
.fitgrid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; margin: 16px 0 }
.fittile { background: var(--page); border-radius: 16px; padding: 18px 20px }
.fittile__label { font-size: 12.5px; color: var(--muted); margin-bottom: 12px }
.fittile__band { font-size: 13.5px; font-weight: var(--weight-strong); color: var(--text) }
.fittile__of { font-size: 12px; color: var(--muted); margin-top: 2px }
.fittile__primary { font-size: 16px; font-weight: var(--weight-strong); line-height: 1.3 }

.wheel--lg { width: 58px; height: 58px; flex: 0 0 58px }
.wheel--lg .wheel__inner { width: 44px; height: 44px; font-size: 16px; background: var(--page) }

.fitvalue {
  display: flex; align-items: center; gap: 16px;
  background: var(--page); border: 1px solid var(--teal);
  border-radius: 16px; padding: 18px 20px;
}
.fitvalue__num { font-size: 24px; font-weight: var(--weight-strong); letter-spacing: -.03em }
.fitvalue__num span { font-size: 13px; font-weight: 500; color: var(--muted) }
.fitvalue__note { font-size: 12px; color: var(--muted) }
.fitvalue__conf { font-size: 12px; font-weight: var(--weight-strong); color: var(--text); text-align: right }

/* Overview — pain point breakdown */
.lever { background: var(--page); border: 1px solid transparent; border-radius: 16px; padding: 16px 18px }
.lever--primary { border-color: var(--teal) }
.lever__top { display: flex; align-items: center; gap: 12px; margin-bottom: 8px; flex-wrap: wrap }
.lever__rank {
  width: 24px; height: 24px; flex: 0 0 24px; border-radius: 8px;
  background: var(--card); border: 1px solid var(--line);
  display: grid; place-items: center; font-size: 11.5px; font-weight: var(--weight-strong);
}
.lever--primary .lever__rank { background: var(--ink); border-color: var(--ink); color: var(--on-ink) }
.lever__name { font-size: 14.5px; font-weight: var(--weight-strong) }
.lever__flag {
  background: var(--ink); color: var(--on-ink); font-size: 10.5px; font-weight: var(--weight-strong);
  letter-spacing: .01em; padding: 4px 9px; border-radius: var(--r-pill);
}
.lever__end { margin-left: auto; display: flex; align-items: center; gap: 10px }
.lever__pips { display: flex; gap: 4px }
.lever__pip { width: 16px; height: 8px; border-radius: var(--r-pill); background: var(--line) }
.lever__pip--on { background: var(--teal) }
.lever--primary .lever__pip--on { background: var(--ink) }
.lever__score { font-size: 12.5px; font-weight: var(--weight-strong); color: var(--muted); white-space: nowrap }
.lever__note { font-size: 13px; line-height: 1.6; color: var(--muted) }

/* Research */
.statusbox { background: var(--page); border-radius: 16px; padding: 18px 20px; margin-bottom: 16px }
.research__text { font-size: 14px; line-height: 1.7; color: var(--text) }
.factbox { background: var(--page); border-radius: 16px; padding: 16px 18px }
.factbox__label { font-size: 12px; font-weight: var(--weight-strong); color: var(--muted); text-transform: capitalize; margin-bottom: 6px }
.factbox__text { font-size: 13.5px; line-height: 1.6; color: var(--text); word-break: break-word }
.factsrc {
  display: flex; align-items: center; gap: 8px; min-width: 0;
  font-size: 12.5px; color: var(--muted); text-decoration: none;
}
.factsrc:hover { color: var(--text) }
.factsrc span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap }
.factsrc .ms { font-size: 15px; flex: 0 0 auto }

/* Contacts — ranked by who to approach first.
   Rank is carried by size, not by a badge. The numeral, the avatar, the name
   and the padding all step down together, so the order reads before any of the
   words do; a numbered chip says the same thing at one size and then says it
   again on every row. The design steps four deep and then holds, because the
   fifth contact is not meaningfully further down the list than the fourth. */
.ranked {
  display: grid; grid-template-columns: 38px minmax(0, 1fr);
  align-items: center; gap: 6px;
  --rank-num: 17px; --rank-av: 32px; --rank-name: 13px; --rank-pad: 13px;
}
.ranked--depth-0 { --rank-num: 26px; --rank-av: 46px; --rank-name: 16px;   --rank-pad: 20px }
.ranked--depth-1 { --rank-num: 22px; --rank-av: 40px; --rank-name: 14.5px; --rank-pad: 17px }
.ranked--depth-2 { --rank-num: 19px; --rank-av: 36px; --rank-name: 13.5px; --rank-pad: 15px }

.ranked__rank {
  text-align: center; color: #C4C4C4;
  font-size: var(--rank-num); font-weight: var(--weight-strong);
  font-variant-numeric: tabular-nums; letter-spacing: -.02em;
}
/* Only the contact Candid is actually on gets a numeral in full ink. */
.ranked__rank--now { color: var(--text) }

.ranked__card {
  display: grid; grid-template-columns: var(--rank-av) minmax(0, 1fr) auto;
  align-items: center; gap: 14px; width: 100%; text-align: left;
  background: var(--card); border: 1px solid var(--line); border-radius: 18px;
  padding: var(--rank-pad) calc(var(--rank-pad) + 2px);
  cursor: pointer; font-family: inherit; color: var(--text);
  transition: background .2s ease, border-color .2s ease;
}
.ranked__card:hover { border-color: var(--ink) }
.ranked__card .avatar {
  width: var(--rank-av); height: var(--rank-av); flex: 0 0 var(--rank-av);
  font-size: calc(var(--rank-av) / 3.2);
  background: var(--page); color: var(--text);
}
.ranked__name { font-size: var(--rank-name); font-weight: var(--weight-strong) }
.ranked__role { font-size: 12.5px; color: var(--muted); margin-top: 3px }
.ranked__why  { font-size: 12px; line-height: 1.5; color: var(--muted); margin-top: 6px; text-wrap: pretty }
/* Says what state the row is in, in the smallest voice that still carries. */
.ranked__tag {
  flex: 0 0 auto; font-size: 10px; font-weight: var(--weight-strong);
  letter-spacing: .04em; text-transform: uppercase; white-space: nowrap;
  color: color-mix(in srgb, var(--teal) 62%, var(--ink));
}

/* The one Candid is approaching now. */
.ranked--now .ranked__card {
  background: color-mix(in srgb, var(--teal) 10%, var(--card));
  border-color: var(--teal);
}
.ranked--now .ranked__card .avatar { background: var(--coral); color: var(--on-coral) }

/* Contacts Candid has moved past. They keep their place in the order — the
   reason the second name is the one being approached is the first name being
   unavailable, and hiding them would hide the reason. */
.ranked--out .ranked__card { background: var(--page); border-color: #E4E4E4; opacity: .72 }
.ranked--out .ranked__card .avatar { background: #E4E4E4; color: #9A9A9A }
.ranked--out .ranked__name { color: #9A9A9A }
.ranked--out .ranked__role,
.ranked--out .ranked__why,
.ranked--out .ranked__tag { color: #B0B0B0 }

/* ===========================================================================
   Add a lead / Add a company
   =========================================================================== */

.hairline { height: 1px; background: #EDEDED }

/* Two mutually exclusive ways to name the company. A segmented control rather
   than chips, because picking one hides the other's fields. */
.segment { display: flex; gap: 8px }
.segment__btn {
  flex: 1; display: flex; align-items: center; justify-content: center;
  height: var(--control-h); padding: 0 14px; font-family: inherit;
  font-size: 12.5px; font-weight: var(--weight-strong);
  border-radius: 12px; cursor: pointer;
  border: 1px solid var(--line); background: var(--card); color: var(--text);
  transition: background .2s ease, color .2s ease, border-color .2s ease;
}
.segment__btn[aria-pressed="true"] { background: var(--ink); border-color: var(--ink); color: var(--on-ink) }

.picklist {
  margin-top: 8px; max-height: 172px;
  overflow-y: auto; overflow-x: hidden; scrollbar-gutter: stable;
  display: flex; flex-direction: column; gap: 6px;
}
.pickrow {
  display: flex; align-items: center; gap: 11px; width: 100%; text-align: left;
  padding: 9px 11px; border-radius: 12px; cursor: pointer; font-family: inherit;
  border: 1px solid #EDEDED; background: var(--card);
}
.pickrow[aria-pressed="true"] { border-color: var(--ink); background: var(--page) }
.pickrow__mark {
  width: 30px; height: 30px; flex: 0 0 30px; border-radius: 9px;
  background: var(--coral); color: var(--on-coral);
  display: grid; place-items: center; font-size: 11px; font-weight: var(--weight-strong);
}
.pickrow__name { font-size: 13.5px; font-weight: var(--weight-strong); white-space: nowrap; overflow: hidden; text-overflow: ellipsis }
.pickrow__site { font-size: 12px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis }
/* Present at all times so picking one does not shift the row. */
.pickrow__tick { font-size: 18px; color: transparent }
.pickrow[aria-pressed="true"] .pickrow__tick { color: var(--text) }

/* ===========================================================================
   Onboarding — the scripted questions
   =========================================================================== */

/* Progress for the whole set, above the card rather than inside it. */
.setupprog { display: flex; flex-direction: column; gap: 14px; width: 100% }
.setupprog__head { display: flex; align-items: center; gap: 12px; width: 100%; padding: 0 2px }
.setupprog__head b { font-size: 12px; font-weight: var(--weight-strong); color: color-mix(in srgb, var(--on-ink) 90%, transparent); white-space: nowrap }
.setupprog__head span { font-size: 12px; font-weight: var(--weight-strong); color: color-mix(in srgb, var(--on-ink) 50%, transparent); white-space: nowrap }
.setupprog__rail {
  flex: 1; height: 3px; border-radius: var(--r-pill);
  background: color-mix(in srgb, var(--on-ink) 14%, transparent); overflow: hidden;
}
.setupprog__rail > div {
  height: 100%; border-radius: var(--r-pill); background: var(--ob-accent);
  transition: width .5s cubic-bezier(.4, 0, .2, 1);
}

/* The section cards slide so the one in play sits at the left edge; the fade
   on the right says there is more without drawing a scrollbar. */
.setupprog__strip {
  width: 100%; overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, #000 0, #000 78%, transparent 100%);
  mask-image: linear-gradient(90deg, #000 0, #000 78%, transparent 100%);
}
.setupprog__track { display: flex; gap: 12px; transition: transform .55s cubic-bezier(.4, 0, .2, 1) }
/* Only the first time the checklist appears. After that the cards move between
   themselves and re-running this would slide the whole strip in on every
   answer. */
.setupprog--enter .setupprog__strip { animation: cdSlideIn .4s cubic-bezier(.22, .8, .28, 1) both }

.setupsec {
  flex: 0 0 196px; width: 196px; box-sizing: border-box;
  border-radius: 14px; padding: 12px 14px;
  display: flex; flex-direction: column; gap: 9px;
  border: 1px solid color-mix(in srgb, var(--on-ink) 12%, transparent);
  background: color-mix(in srgb, var(--on-ink) 4%, transparent); opacity: .32;
  transition: opacity .45s ease, background .45s ease, border-color .45s ease;
}
.setupsec[data-state="now"] {
  opacity: 1; border-color: color-mix(in srgb, var(--on-ink) 34%, transparent); background: color-mix(in srgb, var(--on-ink) 13%, transparent);
}
.setupsec[data-state="done"] { opacity: .55 }
.setupsec b { font-size: 12.5px; font-weight: var(--weight-strong); color: var(--on-ink); line-height: 1.3 }
.setupsec .ms { font-size: 16px; color: color-mix(in srgb, var(--on-ink) 40%, transparent) }
.setupsec[data-state="now"] .ms { color: color-mix(in srgb, var(--on-ink) 95%, transparent) }
.setupsec[data-state="done"] .ms { color: color-mix(in srgb, var(--on-ink) 70%, transparent) }
.setupsec__count { margin-left: auto; font-size: 11px; font-weight: var(--weight-strong); color: color-mix(in srgb, var(--on-ink) 45%, transparent) }
.setupsec[data-state="done"] .setupsec__count { color: color-mix(in srgb, var(--on-ink) 85%, transparent) }
.setupsec__track { height: 3px; border-radius: var(--r-pill); overflow: hidden; background: color-mix(in srgb, var(--on-ink) 13%, transparent) }
.setupsec__track > div {
  height: 100%; border-radius: var(--r-pill); background: var(--ob-accent);
  transition: width .5s cubic-bezier(.4, 0, .2, 1);
}

/* The question itself is the heading — the card has no title above it. */
.setupq {
  font-size: 21px; font-weight: var(--weight-strong); letter-spacing: -.02em; line-height: 1.35;
  margin-bottom: 8px; font-family: var(--ob-head, var(--font-head)), 'Geist', var(--font-stack);
}
.setuphint { font-size: 12.5px; color: var(--muted); margin-bottom: 12px }

.setupchip {
  font-family: inherit; font-size: 13px; font-weight: var(--weight-strong);
  padding: 10px 16px; border-radius: var(--r-pill); cursor: pointer;
  border: 1px solid var(--line); background: var(--card); color: var(--text);
  transition: border-color .16s ease, background .16s ease, color .16s ease;
}
.setupchip:hover { border-color: var(--ink) }
.setupchip[aria-pressed="true"] { border-color: var(--ink); background: var(--ink); color: var(--on-ink) }
.setupchip--sm { font-size: 12.5px; padding: 9px 15px }

.setuptext { display: flex; gap: 10px; margin-top: 12px }
.setuptext .input { flex: 1; min-width: 0 }
.setupsend {
  border: none; background: var(--ob-main, var(--ink)); color: var(--on-ink);
  font-family: inherit; font-size: 13.5px; font-weight: var(--weight-strong);
  padding: 0 22px; border-radius: 14px; cursor: pointer; flex: 0 0 auto;
}

.setupfoot {
  display: flex; align-items: center; gap: 16px;
  margin-top: 24px; padding-top: 18px; border-top: 1px solid #EDEDED;
}
.setupback {
  display: flex; align-items: center; gap: 5px; background: none; border: none;
  font-family: inherit; font-size: 12.5px; font-weight: var(--weight-strong); color: var(--text); cursor: pointer;
}
.setupback .ms { font-size: 16px }
.setupback:disabled { color: #C4C4C4; cursor: default }
.setupconfirm {
  margin-left: auto; border: none; font-family: inherit;
  font-size: 13px; font-weight: var(--weight-strong); padding: 11px 22px;
  border-radius: var(--r-pill); background: var(--ink); color: var(--on-ink); cursor: pointer;
}
.setupconfirm:disabled { background: #E4E4E4; color: #9A9A9A; cursor: not-allowed }

/* ===========================================================================
   Onboarding — the interviewer thinking out loud
   ===========================================================================
   Runs on the dark shell with no card: this is a stream the customer watches,
   not a single question to answer. */

.build {
  width: 100%; max-width: 620px; margin: 0 auto;
  padding: calc(var(--vh) * .06) 0 calc(var(--vh) * .2); display: flex; flex-direction: column; gap: 26px;
  animation: cdFadeIn .5s ease both;
}
.build__title {
  margin: 0; font-size: 32px; line-height: 1.15; letter-spacing: -.03em;
  font-weight: var(--weight-strong); color: var(--on-ink); text-wrap: pretty;
}
.build__sub { margin-top: 10px; font-size: 14px; color: color-mix(in srgb, var(--on-ink) 62%, transparent) }
.build__lines { display: flex; flex-direction: column; gap: 14px }

/* A thought. Dims once the next one lands, so the live line is obvious. */
.build__think {
  display: flex; align-items: flex-start; gap: 12px; padding: 2px;
  opacity: .55; transition: opacity .5s ease;
  animation: cdSlideIn .45s cubic-bezier(.22, .8, .28, 1) both;
}
.build__think[data-live="true"] { opacity: 1 }
.build__think .ms { font-size: 17px; margin-top: 2px; color: color-mix(in srgb, var(--on-ink) 65%, transparent) }
.build__think[data-live="true"] .ms { color: var(--ob-accent); animation: cdSpin 1.1s linear infinite }
.build__thinktext { font-size: 14.5px; font-weight: var(--weight-strong); color: var(--on-ink) }
.build__thinkdetail { font-size: 12.5px; color: color-mix(in srgb, var(--on-ink) 55%, transparent); margin-top: 3px; text-wrap: pretty }

/* A question put back to the customer. A card, because it wants an answer. */
.build__ask {
  display: flex; flex-direction: column; gap: 12px;
  padding: 18px 20px; border-radius: 18px;
  border: 1px solid var(--line); background: var(--card);
  animation: cdSlideIn .45s cubic-bezier(.22, .8, .28, 1) both;
  transition: opacity .5s ease, padding .5s ease;
}
.build__ask[data-answered="true"] { padding: 14px 16px; border-color: #E4E4E4; opacity: .72 }
.build__asktext { font-size: 15px; font-weight: var(--weight-strong); color: var(--text); line-height: 1.4; text-wrap: pretty }
.build__askwhy { font-size: 12.5px; color: var(--muted); margin-top: 6px; text-wrap: pretty }
.build__answer {
  font-size: 13.5px; line-height: 1.6; color: var(--text);
  background: var(--page); border-radius: 12px; padding: 11px 14px;
}

.build__cta {
  align-self: flex-start; border: none; background: #FFF; color: var(--text);
  font-family: inherit; font-size: 14px; font-weight: var(--weight-strong);
  padding: 14px 26px; border-radius: 16px; cursor: pointer;
}

/* ===========================================================================
   Contact drawer
   =========================================================================== */

/* The company and contact drawers lead with a rounded square, not a circle —
   a circle is a person, and these two are a record. */
.drawer__mark {
  width: 40px; height: 40px; flex: 0 0 40px; border-radius: 11px;
  background: var(--coral); color: var(--on-coral);
  display: grid; place-items: center; font-size: 13px; font-weight: var(--weight-strong);
}

/* Approve and delete sit right-aligned above the emails. */
.drawer__actions { display: flex; align-items: center; justify-content: flex-end; gap: 8px }

.tile__big { font-size: 15px; font-weight: var(--weight-strong); line-height: 1.35 }

/* Where this person ranks against the others at their company. Bars rather
   than a number, because the useful fact is the position, not the integer. */
.rankbars { display: flex; align-items: flex-end; gap: 6px; height: 34px }
.rankbar { width: 12px; border-radius: 4px; background: #DCDCDC }
.rankbar--best { background: var(--teal) }
.rankbar--here { background: var(--ink) }
.rankbars__num { margin-left: 8px; font-size: 15px; font-weight: var(--weight-strong); color: var(--muted) }
.rankbars__num[data-best="true"] { color: var(--text) }

/* Why this pain point fits this person. */
.leverfit {
  display: flex; gap: 10px; background: var(--page);
  border-radius: 16px; padding: 14px 16px; margin-top: 14px;
}
.leverfit .ms { font-size: 17px; color: var(--muted); flex: 0 0 auto }
.leverfit span { font-size: 12.5px; line-height: 1.6; color: var(--muted) }

/* One email of the sequence. */
.step__label { font-size: 11.5px; font-weight: var(--weight-strong); color: var(--muted); margin-bottom: 6px }
.step__edited {
  display: flex; align-items: center; gap: 5px;
  font-size: 11.5px; font-weight: var(--weight-strong); color: #B07C00;
}
.step__edited .ms { font-size: 15px }
.step__orig {
  margin-top: 10px; background: var(--page);
  border: 1px dashed #C4C4C4; border-radius: 14px; padding: 14px 16px;
}

/* The conversation, once there is one. */
.thread { display: flex; flex-direction: column; gap: 18px }
.threadempty {
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  padding: 44px 20px; text-align: center;
}
.threadempty .ms { font-size: 30px; color: #C4C4C4 }
.threadempty b { font-size: 14px; font-weight: var(--weight-strong) }
.threadempty p {
  margin: 0; font-size: 12.5px; color: var(--muted);
  max-width: 320px; text-wrap: pretty;
}

/* ===========================================================================
   Team member drawer
   =========================================================================== */

/* A card whose whole subject is broken. The border carries it so the state is
   readable before any of the words are. */
.card--bad { border-color: #E9A9A2 }

.mbhead { display: flex; align-items: center; gap: 13px; margin-bottom: 18px }
.mbchip {
  width: 40px; height: 40px; flex: 0 0 40px; border-radius: 11px;
  background: var(--page); display: grid; place-items: center;
}
.mbchip .ms { font-size: 20px; color: var(--muted) }

.mbproblem {
  display: flex; gap: 11px; background: #FCEBE8; border: 1px solid #E9A9A2;
  border-radius: 14px; padding: 14px 16px; margin-bottom: 16px;
  font-size: 12.5px; line-height: 1.55; color: #8E1D15;
}
.mbproblem .ms { font-size: 18px; color: #C0362C; flex: 0 0 auto }

.mbbar { height: 8px; border-radius: var(--r-pill); background: var(--page); overflow: hidden; margin-bottom: 16px }
.mbbar > div { height: 100%; border-radius: var(--r-pill); background: var(--teal) }
.mbbar > div[data-bad="true"] { background: #C0362C }

.mbstats {
  display: flex; align-items: center; gap: 20px;
  padding-top: 16px; border-top: 1px solid var(--hair);
}
.mbstats b { display: block; font-size: 16px; font-weight: var(--weight-strong) }
.mbstats span { display: block; font-size: 11px; color: var(--muted) }

/* A line Candid wrote in their voice, quoted back. */
.voicesample {
  font-size: 13px; line-height: 1.65; color: var(--muted);
  background: var(--page); border-radius: 14px; padding: 14px 16px;
}

.linkrow {
  display: flex; align-items: center; gap: 12px;
  background: var(--page); border-radius: 12px; padding: 11px 14px;
}
.linkrow .ms { font-size: 18px; color: var(--teal); flex: 0 0 auto }
.linkrow > span:not(.ms):not(.pill) {
  flex: 1; min-width: 0; font-size: 12.5px; color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* The one destructive button in the drawer. */
.btn--bad { background: #C0362C; color: #FFF }
.btn--bad:hover:not(:disabled) { background: #A32C24; opacity: 1 }

/* ===========================================================================
   Voice training
   ---------------------------------------------------------------------------
   The screen where somebody's own sent mail is on the glass, and the design
   problem is entirely about what red means.

   Red here is not an error. It is "this will not be sent", and it has to read
   that way at a glance across a body of text somebody is skim-reading — so a
   struck-out span gets the strike, a wash, and a muted colour all three. One
   of them alone reads as emphasis; together they read as removed.

   The unmarked-but-suggested state does not exist. A suggestion arrives struck
   out, because the safe default is that a phone number does not leave the
   browser, and clicking it back in is the deliberate act.
   =========================================================================== */

/* minmax(0, 1fr), not the implicit `auto`: an auto column is at least as wide
   as its min-content, and the min-content of a mail row is the row's own
   nowrap subject plus a sender address that does not break. On a phone that
   made the whole trainer wider than the card holding it, and every row was
   clipped at the card's edge with no ellipsis to say so. */
.vt { display: grid; gap: 18px; margin-top: 18px; grid-template-columns: minmax(0, 1fr) }
.vt .stack { min-width: 0 }

.vt__wait { display: grid; gap: 14px; padding: 8px 2px }
.vt__wait .muted { font-size: 13px }

.vt__hint {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 12px 26px 4px; font-size: 12.5px; color: var(--muted);
}
.vt__hint .ms { font-size: 18px; color: var(--teal); flex: 0 0 auto }

/* --- one email ---------------------------------------------------------- */

.vt__mail { border-top: 1px solid var(--line) }
.vt__mail[data-picked="1"] { background: color-mix(in srgb, var(--teal) 5%, transparent) }

.vt__mailtop {
  display: flex; align-items: center; gap: 14px;
  padding: 14px 26px;
}
/* The tick inside a row carries the row's own spacing, not the form margin the
   standalone checkbox is written with. */
.vt__pick { margin: 0; flex: 0 0 auto }
.vt__pick > span:last-child {
  /* The label is for screen readers and for the hover target; the row's
     subject is what a sighted reader is aiming at. */
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

.vt__subject {
  flex: 1; min-width: 0;
  font-size: 13.5px; font-weight: 600;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* The subject and the sender are block children, so the ellipsis above never
   applied to either — it governs the box's own inline content. Without this a
   subject too long for the row was cut mid-word with nothing to mark it. */
.vt__subject > div { overflow: hidden; text-overflow: ellipsis }
.vt__meta { font-size: 11.5px; font-weight: 400; color: var(--muted) }

.vt__expand {
  flex: 0 0 auto; display: grid; place-items: center;
  width: 32px; height: 32px; border: 0; border-radius: 9px;
  background: none; color: var(--muted); cursor: pointer;
  transition: background .16s ease, color .16s ease;
}
.vt__expand:hover { background: var(--page); color: var(--ink) }

.vt__body {
  padding: 4px 26px 20px 26px;
  font-size: 13px; line-height: 1.7; color: var(--text);
  white-space: pre-wrap; word-break: break-word;
  /* The whole interaction is dragging across text, so the browser's own
     "select the paragraph" behaviours are the ones we want, untouched. */
  user-select: text;
}

.vt__mark {
  border-radius: 4px; padding: 0 2px;
  background: none; color: inherit;
  cursor: pointer;
  transition: background .14s ease, color .14s ease;
}
.vt__mark:hover { background: color-mix(in srgb, var(--coral) 14%, transparent) }
.vt__mark[data-on="1"] {
  background: color-mix(in srgb, var(--coral) 16%, transparent);
  color: var(--coral);
  text-decoration: line-through;
  text-decoration-thickness: 1.5px;
}

/* --- their own writing --------------------------------------------------- */

/* The three promises in the amber banner. Tight enough to read as one block
   rather than as a list of unrelated items. */
.vt__points { margin: 8px 0 0; padding-left: 19px; display: grid; gap: 6px }
.vt__points li { padding-left: 2px }

.vt__docs { display: grid; gap: 8px; margin-bottom: 12px }
.vt__doc {
  display: flex; align-items: center; gap: 12px;
  background: var(--page); border-radius: 12px; padding: 10px 12px;
  font-size: 12.5px;
}
.vt__doc .ms { font-size: 18px; color: var(--teal); flex: 0 0 auto }
.vt__doc > div { flex: 1; min-width: 0 }
.vt__doc > div > div:first-child {
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* --- the footer, and the calibration drafts ------------------------------ */

.vt__foot {
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
  padding-top: 4px;
}
.vt__count { flex: 1; min-width: 0; font-size: 12.5px; color: var(--muted) }

.vt__sample .textarea { margin-bottom: 4px }

@media (max-width: 900px) {
  /* iOS zooms the page in when a focused field is under 16px. Every input in
     the app is 13.5px, so without this every tap on a form jumps the viewport
     and does not jump back. */
  .input, .textarea, .select, input, textarea, select { font-size: 16px }

  /* Touch targets. 44px is the smallest reliably hittable control; the icon
     buttons are already square and sized, so they are left alone. */
  .btn, .provider, .ob__provider, .ob__cta { min-height: 44px }
  /* The icon carries the verb on a phone; the label only has to say what. */
  .btn__full { display: none }
  .btn__short { display: inline }
  .input, .select { min-height: 44px }

  /* The app is a full-bleed surface on a phone; native scrollbars over it read
     as a rendering artefact rather than a control. */
  * { scrollbar-width: none; -ms-overflow-style: none }
  ::-webkit-scrollbar { width: 0; height: 0; display: none }

  html, body { overscroll-behavior-y: none }
  body { touch-action: manipulation; -webkit-text-size-adjust: 100% }
  * { -webkit-tap-highlight-color: transparent }

  .auth { grid-template-columns: 1fr; display: flex; flex-direction: column; overflow-y: auto }
  /* The art panel collapses to a branded strip rather than disappearing — it is
     the only thing on the screen that says whose product this is. */
  .auth__art {
    flex: 0 0 auto; min-height: 74px; max-height: 74px;
    padding: 16px 20px; justify-content: center; overflow: hidden;
  }
  .auth__pitch { display: none }
  .auth__logo { position: relative; top: auto; left: auto }
  /* No blobs on a phone. At 74px tall the strip crops them to a few coloured
     bands behind the wordmark, which reads as a rendering fault rather than as
     the artwork it is on a full-height desktop panel. Plain ink, logo on top. */
  .auth__blobs { display: none }
  .auth__pane { flex: 0 0 auto; padding: 26px 18px calc(28px + env(safe-area-inset-bottom)); align-items: flex-start }

  .aside { display: none }
  .shell, .col { height: 100dvh }

  /* The status bar sits over the top of a standalone web app, so the header has
     to be pushed clear of it or the title renders under the clock. */
  /* The sidebar is what carries the brand colour on a desktop, and it is gone
     here — so the header takes it on. That also closes a seam: applyBrand()
     already paints the browser's own chrome with the main colour, so a white
     bar left a band of card between the address bar and the app. Ink to ink,
     through to the ink tab bar at the far end of the screen. */
  .topbar {
    height: calc(58px + env(safe-area-inset-top));
    flex-basis: calc(58px + env(safe-area-inset-top));
    padding: env(safe-area-inset-top) 14px 0; gap: 10px;
    background: var(--ink);
    color: var(--on-ink);
    border-bottom-color: color-mix(in srgb, var(--on-ink) 8%, transparent);
  }
  /* The wordmark takes the title's place. The page name stays in the document
     for assistive tech — it is clipped, not removed — so the heading still
     announces the screen. */
  .topbar h1 { font-size: 17px }
  /* Same knockout the sidebar gives it: the mark is solid black artwork, which
     is why it needs inverting to sit on ink. --logo-filter is none once a
     customer uploads their own, so their artwork is never recoloured. */
  .topbar__logo {
    display: block; height: calc(22px * var(--logo-scale)); width: auto;
    max-width: 44vw; flex: 0 0 auto; filter: var(--logo-filter);
  }
  .topbar__name {
    position: absolute; width: 1px; height: 1px;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap;
  }
  .search { flex: 1 1 auto; min-width: 0 }
  .mavatar { display: block }
  /* Room for the title, the search icon and the avatar means the button keeps
     its icon and loses its words. */
  .bizbtn { width: 40px; height: 40px; padding: 0; flex: 0 0 40px; justify-content: center }
  .bizbtn__label { display: none }
  /* The accent button's hover is ink, which is now the bar behind it — so on a
     touch screen, where a tap can leave the hover stuck, it would vanish into
     the header. Phones do not need a hover state; take it off. */
  .bizbtn:hover { background: var(--teal); border-color: var(--teal); color: var(--on-teal) }

  .main { padding: 16px 14px calc(96px + env(safe-area-inset-bottom)) }

  .grid--4, .grid--3, .grid--2 { grid-template-columns: minmax(0, 1fr) }

  /* Replies becomes one pane. The list fills it; selecting a conversation
     swaps the thread in over the top, and Back swaps it back. */
  .rep { height: auto; overflow: visible }
  .rep__grid { grid-template-columns: minmax(0, 1fr); gap: 0 }
  .rep__scroll {
    height: auto; padding: 0; overflow: visible;
    -webkit-mask-image: none; mask-image: none;
  }
  .rep__list, .rep__thread { height: auto }
  .rep__thread { background: transparent; border: none; border-radius: 0; padding: 0 }
  .rep__threadscroll { height: auto; padding: 0; overflow: visible }
  .rep[data-view="thread"] .rep__list, .rep[data-view="thread"] .rep__filters { display: none }
  .rep[data-view="list"] .rep__thread { display: none }

  .rep__back {
    display: flex; position: sticky; top: 0; z-index: 4;
    background: var(--page); padding: 12px 0; margin: 0 0 10px;
    border-bottom: 1px solid var(--hair);
  }
  .rep__item { padding: 15px 16px; border-radius: 18px; background: var(--card); border-color: var(--hair) }

  /* ---- page headers on a phone ------------------------------------------
     All three of these headers are a title, a count and one or two controls
     laid out in a row that wraps. On a phone the wrap left them ragged: a
     right-aligned button orphaned on its own line, a switch under it aligned
     left, a divider stranded at the start of a wrapped row. Full-width
     controls stacked under the heading read as deliberate rows instead. */

  /* Outreach — the Add button and the auto-approve switch come out of the
     header row and sit above the heading, as one right-aligned row. They are
     the page's two actions rather than that one stage's, so they read better
     at the top of the screen than buried under whichever stage happens to be
     first. order:-1 lifts them; the full-width basis gives them their own
     line so the title keeps the one below.

     Neither control shrinks or wraps, and the Add button falls back to its
     short label, which is what buys the pair a shared row at 360px with
     room to spare. Below about 340px they stack — still
     right-aligned and still their own width, because a pill with its label
     broken across two lines inside a fixed-height button is worse than a
     second row. */
  .stage__head { gap: 10px 12px }
  .stage__actions {
    display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end;
    gap: 10px; order: -1; flex: 0 0 100%; min-width: 0;
    /* The band is its own thing, not part of the heading, so it sits at least
       as far from the title as the title sits from its cards. The header's
       10px row gap alone read as though the two pills belonged to the
       heading underneath them. */
    margin-bottom: 8px;
  }
  .stage__add { margin-left: 0; flex: 0 0 auto; padding: 0 14px; font-size: 12.5px; white-space: nowrap }
  .stage__add .ms { font-size: 16px }
  /* .aa is a button but not a .btn, so it missed the 44px touch floor above
     and sat two pixels shorter than the Add button beside it. Matching the
     floor gives it the target size it should have had and squares off the
     pair now that they share a row. */
  .aa { flex: 0 0 auto; min-height: 44px; padding: 8px 14px; gap: 10px }
  .aa b { font-size: 12.5px }

  /* Replies — the four periods fill one row; the toggle that cuts across them
     takes the next. The divider between the two only means anything while they
     share a line, so it goes. */
  .rep__sep { display: none }
  .chip--awaiting { width: 100%; justify-content: center }

  /* ---- the companies table on a phone ------------------------------------
     Six columns of real data will not fit 347px, and squeezing them to fr
     units is what turned the company name into "Stonega…". So the columns
     keep widths that fit their content and the table scrolls sideways
     instead, with the head and the rows inside one scroller so they cannot
     drift apart.

     A frozen company column was tried and taken out again: a grid item's
     containing block is its own grid area, so it unpins the moment the
     scroll passes that column's width. Freezing one properly needs a real
     <table>, which is a bigger change than this screen justifies. */
  .table__scroll {
    overflow-x: auto; overscroll-behavior-x: contain;
    /* Scrollbars are hidden on a phone (see the top of this block), so the
       edge has to say "there is more" by itself. The first layer is an opaque
       patch attached to the content, so it slides over the shadow exactly
       when the last column arrives; the second is the shadow, pinned to the
       viewport's edge. */
    background:
      linear-gradient(var(--card), var(--card)) right center / 18px 100% no-repeat local,
      linear-gradient(to right, transparent, rgba(16, 33, 31, .11)) right center / 18px 100% no-repeat scroll;
  }
  .corow {
    /* The value column takes any slack rather than leaving it stranded at the
       right edge — between about 690px and the breakpoint the fixed columns
       come to less than the card, and the gap landed after the last one. */
    grid-template-columns: 180px 76px 56px 108px 96px minmax(80px, 1fr);
    gap: 12px; padding: 14px 16px;
    /* The row has to be as wide as its columns, not as wide as the window.
       It inherits width:100% from the rule above, which inside a scroller
       resolves against the visible 360px rather than the 672px of content —
       so the painted box stopped a third of the way along: the head's grey
       band and every row's hairline ended in mid-table, and the row's own
       right padding sat inside the visible area instead of after the last
       column, leaving the values jammed against the edge with nothing to
       scroll into. min-width keeps it filling a tablet, where the fixed
       columns come to less than the screen. */
    width: max-content; min-width: 100%;
  }
  .corow--head { padding: 12px 16px }

  /* A stage name longer than its column would otherwise push the pill into
     the next one. */
  .corow .pill { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap }

  /* ---- the companies toolbar and filters ---------------------------------
     All five controls share one row, which takes some doing: at the desktop
     sizes they came to 382px against about 300px of usable width. The gutter
     drops from 26px to 12px, the gaps from 10px to 6px, and the two buttons
     to a 10px inset around a 15px icon. The three utility buttons come down
     from 42px to 38px — the one real concession, and it is spent on import,
     export and filters rather than on the two buttons that add something.

     That lands at 318px against 325px of room on a 375px screen — the card's
     own 1px borders are part of that sum, and forgetting them was worth two
     pixels and a wrapped row. The row holds from about 368px up. Narrower
     than that it splits in two on purpose; see the block below. */
  .table__toolbar { padding: 14px 10px; gap: 5px }
  .table__tools { display: contents }
  .table__toolbar .iconbtn, .table__toolbar .filterbtn { width: 38px; height: 38px; flex: 0 0 38px }
  .table__toolbar .btn { gap: 5px; padding: 0 10px; font-size: 12px }
  .table__toolbar .btn .ms { font-size: 15px }
  /* Pushes the two buttons to the far end, away from the utilities. */
  .table__toolbar .btn--ghost { margin-left: auto }
  .table__filters { padding: 14px 16px; gap: 10px }
  /* Each filter takes a full row. A 150px minimum on a wrapping flex row was
     what left "Any stage" and "Any score" side by side and the order control
     stranded on a line of its own, half a screen from either. */
  .table__filters .select { flex: 1 1 100%; width: 100%; min-width: 0 }
  .table__order { margin-left: 0; width: 100%; gap: 10px }
  .table__order .select { flex: 1 1 auto; width: auto }

  /* Team — a 220px dashed box is most of a phone screen for one button, and it
     pushed the people underneath it off the bottom. And auto-fill at a 320px
     minimum overflows any viewport narrower than that, so the track loses its
     floor here too. */
  .members { grid-template-columns: minmax(0, 1fr) }
  .member--add { min-height: 112px; flex-direction: row; gap: 12px }
  .member--add .ms { font-size: 22px }

  /* Your profile and the settings tabs. The panels give up the desktop padding
     so the fields get the width back — at 375px, 28px a side was a tenth of
     the screen spent on nothing. The action row stops right-aligning its last
     button once it wraps, for the same reason the page headers above do: a
     lone ghost button hanging off the right edge reads as a mistake. Each
     control takes a row of its own instead. */
  .fpanel { padding: 20px 18px; border-radius: 22px }
  .fpanel__head { gap: 12px; margin-bottom: 18px }
  .formacts > .spacer { display: none }
  .formacts > .btn { flex: 1 1 100%; justify-content: center }

  /* A phone conversation reads as chat: yours on the right, in ink. */
  .bubble { max-width: 88%; padding: 15px 17px; border-radius: 20px; font-size: 14.5px }
  .bubble--you {
    align-self: flex-end; margin-inline-start: 0;
    background: var(--ink); border-color: var(--ink); color: var(--on-ink);
  }
  .bubble--you .bubble__meta { color: color-mix(in srgb, var(--on-ink) 66%, transparent) }
  .bubble--them { align-self: flex-start; margin-inline-end: 0 }

  /* One column, and the panels lose the generous desktop padding. */
  .dash { grid-template-columns: minmax(0, 1fr); gap: 16px }
  .sp12, .sp7, .sp6, .sp5 { grid-column: span 1 }
  .panel { padding: 20px 18px; border-radius: 22px }
  .barrow__name { flex-basis: 96px; min-width: 96px; font-size: 13px }
  .perf__periods { margin-left: 0; width: 100%; overflow-x: auto; flex-wrap: nowrap }
  .perf__periods .chip { flex: 0 0 auto }
  /* Two by two, not a single column. Each tile still clears 165px at 375px —
     more than the longest value needs — and four stacked cards would push the
     panels underneath them off the first screen entirely. */
  .kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px }
  .kpi { padding: 16px 14px; border-radius: 18px }
  .kpi__value { font-size: 26px }
  .kpi__label { margin-bottom: 8px }
  /* "+99 vs previous period" has to wrap in a 165px tile, so it is set tight
     enough that two lines still read as one note rather than a paragraph. */
  .kpi__delta { font-size: 11.5px; line-height: 1.35 }

  .hero { padding: 24px 20px; border-radius: 22px }
  .hero__value { font-size: 38px }
  .hero__inner { gap: 22px }
  .hero__stats { margin-left: 0; width: 100%; gap: 10px }
  .week { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px }
  .week__value { font-size: 26px }

  .tabbar {
    display: flex;
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 12;
    align-items: stretch; gap: 2px;
    padding: 5px 6px calc(6px + env(safe-area-inset-bottom));
    /* Was Candid's ink written out in full, which meant a branded customer got
       a header in their colour and a tab bar in ours. Same colour, brandable. */
    background: color-mix(in srgb, var(--ink) 92%, transparent);
    backdrop-filter: saturate(160%) blur(14px);
    -webkit-backdrop-filter: saturate(160%) blur(14px);
    border-top: 1px solid color-mix(in srgb, var(--on-ink) 10%, transparent);
  }
  .tabbar__item {
    flex: 1; display: flex; flex-direction: column; align-items: center; gap: 3px;
    padding: 8px 4px; border-radius: 12px;
    color: color-mix(in srgb, var(--on-ink) 60%, transparent);
    font-size: 10.5px; font-weight: 500;
    cursor: pointer;
    transition: transform .16s cubic-bezier(.32, .72, 0, 1), background .2s ease, color .2s ease;
  }
  .tabbar__item .ms { font-size: 22px }
  .tabbar__item[aria-current="page"] { color: var(--on-ink) }
  .tabbar__item:active { transform: scale(.9) }

  /* ---- Sent, on a phone ---------------------------------------------------
     Three desktop columns across 375px gave the company 90px to wrap a name
     down five lines, overlapped the five step labels into each other, and cut
     the sender's address to "robert...". Each row becomes a stacked card
     instead: who it went to, the sequence across the full width, then who it
     was sent from. The column header goes with the columns — a stack has no
     columns to name — so the sender row grows its own label. */
  .senttable { border-radius: 22px }
  .sentrow--head { display: none }
  .sentrow--head + .sentrow { border-top: none }
  .sentrow {
    grid-template-columns: minmax(0, 1fr);
    gap: 14px; padding: 16px;
  }
  .sentrow__from { padding-top: 12px; border-top: 1px solid var(--hair) }
  .sentrow__from::before {
    content: "Sent from"; flex: 0 0 auto;
    font-size: 11px; font-weight: var(--weight-strong); color: var(--muted);
  }

  /* Five steps still fit across the width once the labels are allowed to wrap.
     They are held to a fixed two-line box so that a one-line "Initial" and a
     wrapped "Follow-up 1" still hang their dots on the same line — otherwise
     the rail between them runs diagonally. */
  .pip { gap: 4px }
  .pip__label {
    white-space: normal; text-align: center;
    font-size: 9.5px; line-height: 1.25; min-height: 24px;
    /* The padding is the gutter. Without it a 320px screen leaves the five
       labels exactly column-wide and touching each other. */
    padding: 0 2px;
  }
  .pip__dot { width: 20px; height: 20px; font-size: 12px }
  .pip__rail { top: 9px }
  .pip__date { font-size: 9.5px }

  table.tbl th { display: none }
  table.tbl td { display: block; padding: 4px 16px; border: none }
  table.tbl tbody tr { display: block; padding: 14px 0; border-bottom: 1px solid var(--hair) }

  /* A phone gives it the full width rather than sizing it to its longest
     line, which is what left a two-line message looking wedged to one side.
     Centred by its own insets now, so there is no translate to preserve and
     the plain rise is the right animation. A 99px pill radius on a box that
     wraps to two lines reads as a bubble, so it softens to a corner. */
  .toast {
    left: 14px; right: 14px; transform: none; max-width: none;
    text-align: center; padding: 14px 18px; border-radius: 18px;
    bottom: calc(84px + env(safe-area-inset-bottom));
    animation-name: cdRise;
  }

  /* The drawer on a phone. It stops short of the left edge so a sliver of the
     page it came from stays showing. That sliver is doing the explaining: it
     says this is a thing lying on top of something rather than a new screen,
     and it leaves an obvious place to reach for. Tapping it closes, because
     it is scrim; dragging the panel off the side closes too.

     The X in the corner goes with it. A sheet you can throw off the side does
     not need a second way out, and the top-right corner is the furthest part
     of a phone screen from the thumb holding it. */
  .drawer {
    left: 28px; right: 0; width: auto;
    /* Square corners, as the design has every full-height surface. The edge is
       drawn by the border and the shadow; rounding it as well made the panel
       read as a card floating on the page rather than a side that has been
       pulled across it. */
    border-left: 1px solid var(--line);
    box-shadow: -20px 0 50px rgba(16, 33, 31, .22);
  }
  /* The browser keeps the vertical axis and the drag takes the horizontal one.
     Declaring it means the first sideways frame is ours rather than arriving
     after the browser has waited to see which way the finger was going.

     It goes on the two panes rather than on .drawer itself. A browser works
     out what a touch is allowed to do by intersecting the rules along the
     whole ancestor chain, so pan-y on the drawer and pan-x on the tab strip
     inside it left the strip with the intersection of the two — nothing — and
     it could not be scrolled by finger at all. Siblings do not intersect. */
  .drawer__head, .drawer__body { touch-action: pan-y }
  /* The grip. It sits in a gutter the head, the tabs and the body all widen
     to leave for it, so it never lands on top of anything. */
  .drawer::before {
    content: ""; position: absolute; z-index: 2; pointer-events: none;
    left: 6px; top: 50%; margin-top: -26px; width: 4px; height: 52px;
    border-radius: var(--r-pill);
    background: color-mix(in srgb, var(--text) 26%, transparent);
  }
  /* Mid-drag the panel is being moved, not read. */
  .drawer--dragging { user-select: none; -webkit-user-select: none }
  .drawer__head .closer { display: none }

  .drawer__head { height: 62px; flex-basis: 62px; padding: 0 16px 0 20px }
  .drawer__tabs {
    padding: 12px 16px 12px 20px; flex-wrap: nowrap;
    overflow-x: auto; scrollbar-width: none; touch-action: pan-x;
  }
  .drawer__tabs::-webkit-scrollbar { width: 0; height: 0 }
  .drawer__tabs > * { flex: 0 0 auto }
  .drawer__body { padding: 16px 14px calc(40px + env(safe-area-inset-bottom)) 20px }

  /* A dialog stays a dialog on a phone: same centred window, same corners, same
     entrance as the desktop. It used to anchor to the bottom and rise as a
     sheet with a grab handle, which made the same dialog look like two
     different components depending on the width of the screen.

     Only the measurements change — margin off the edges, and a height bounded
     by dvh so the mobile browser's own chrome cannot push the footer buttons
     out of reach. */
  .modal {
    width: min(680px, calc(100vw - 24px));
    max-height: min(88vh, calc(100dvh - 32px));
    padding: 24px 20px;
  }
  .modal .row .btn { flex: 1 1 auto; justify-content: center }

  /* Search collapses to an icon and expands over the bar when tapped, because
     a permanent field leaves no room for the title on a 375px screen. */
  /* On ink, the button's card fill and grey border read as a hole punched in
     the bar. It takes the sidebar's treatment instead — a wash of the
     foreground colour, which follows a customer's palette either way. */
  .searchbtn {
    display: grid;
    background: color-mix(in srgb, var(--on-ink) 12%, transparent);
    border-color: color-mix(in srgb, var(--on-ink) 18%, transparent);
    color: var(--on-ink);
  }
  .topbar .search { display: none }
  .topbar .search[data-open="1"] {
    display: flex; position: absolute; left: 12px; right: 12px;
    top: calc(env(safe-area-inset-top) + 8px); z-index: 22;
  }
  .topbar .search[data-open="1"] { right: 62px }
  .topbar .search[data-open="1"] ~ .mavatar,
  .topbar .search[data-open="1"] ~ .bizbtn { display: none }
  .searchbtn { position: relative; z-index: 26 }

  /* Three columns of mapping do not fit; stack them and label the sample. */
  .maprow { grid-template-columns: minmax(0, 1fr); gap: 8px; padding: 14px 4px }
  .maprow__head { display: none }

  /* Onboarding. The bar becomes fixed and translucent so the card can scroll
     under it, and it inherits the shell's colour so it stays branded through
     the reveal rather than reverting to ink. No shadow under it: the bar and
     the page behind it are the same colour, so the only thing the shadow
     drew was a seam across the top of the screen. */
  .ob__bar {
    position: fixed; background: inherit;
    padding: calc(12px + env(safe-area-inset-top)) 18px 14px;
    backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
  }
  .ob__bar img { height: 21px }
  .ob__scroll { padding: 70px 14px calc(40px + env(safe-area-inset-bottom)) }
  .ob__card { padding: 22px 18px; border-radius: 22px }
  .ob__card h1 { font-size: 24px }
  .ob__providers { grid-template-columns: minmax(0, 1fr) }
  /* Five progress panels will not fit across a phone; they scroll instead of
     shrinking to unreadable. */
  .ob__cta { margin-left: 0; width: 100%; justify-content: center }
  .ob__foot { gap: 12px }
  .ob__hex { width: 100px; flex-basis: 100px }
}

/* ===========================================================================
   Narrow phones
   ---------------------------------------------------------------------------
   The companies toolbar holds five controls, and below about 368px they stop
   fitting a single row however tightly they are packed. Rather than let the
   last button wrap on its own, the row splits where it means something: the
   three utilities on one line, the two buttons that add something on the next,
   right-aligned. The break is a zero-height pseudo-element ordered between the
   two groups, which keeps the markup grouped as the wide screen needs it.
   =========================================================================== */

@media (max-width: 367px) {
  .table__toolbar::after { content: ""; flex: 0 0 100%; height: 0; order: 1 }
  .table__toolbar .btn { order: 2 }
}

/* ===========================================================================
   Small laptops
   ---------------------------------------------------------------------------
   A 13-inch screen gets the whole app at 90%.

   Nothing can ask the browser how large the display physically is, so this
   runs on the closest honest proxy: a viewport wide enough to be getting the
   desktop layout, and short enough that it cannot be a desktop monitor.

   Height is the test rather than width because a 13-inch panel is short at
   every resolution it ships at, whether the horizontal number is 1280, 1470 or
   1512 — width alone would also catch a half-width window on a 27-inch screen,
   which is not a small display and should not be shrunk. Once the browser
   chrome is out, a 13-inch laptop leaves roughly 660-900px of viewport height;
   a 1080p monitor leaves ~990 windowed and ~1030 full-screen, so it stays at
   100% — and so does the laptop itself the moment it is docked to one.

   The two numbers are the whole rule. Move them if the cut-off lands wrong.
   =========================================================================== */

@media (min-width: 1000px) and (max-height: 940px) {
  :root { --zoom: .9 }
  body { zoom: var(--zoom) }
}
