/* Three colour systems that must never meet. --brand is the app's own voice —
   actions, selection, the wordmark. The --s-* ramp and --overdue are the lead's
   state, where the colour is the information. The --l-* set is which list a card
   is, and nothing more. A broker must never have to work out which of the three
   he is reading, so no status takes the brand's hue and no list takes a status's.
   Panes are told apart by background alone rather than by borders: the open
   row can then carry the next pane's background and merge into it, which no
   border-based divider allows. */

:root {
  --rail: #e4eaf4;
  --leads: #f2f6fc;
  --detail: #fff;
  /* Blocks filled on the detail pane. Its own colour, not the leads pane's:
     they sit on white and read against it, so the columns can be retoned
     without dragging the detail page's blocks along. */
  --card: #eff4fc;
  --line: #d7e0ee;
  /* --line reads as a grid against the palest pane. */
  --line-soft: #e6edf7;
  --ink: #0e1726;
  /* --muted on --card is body text, and this is the lightest value clearing
     4.5:1 against it. */
  --muted: #5e6a83;

  --brand: #4d7cf3;
  --brand-dk: #3a63d6;
  --brand-grad: linear-gradient(90deg, var(--brand), #2f9ad6);
  --brand-tint: #e4ecfb;
  /* Translucent, so one value tints the rail, a white popover and --card
     alike. */
  --hover: rgba(77, 124, 243, 0.09);

  /* One per list on home. The tile behind each icon is mixed from the same value
     rather than written twice, so the pair cannot drift apart. */
  --l-callback: #b4531f;
  --l-warm: #a9690c;
  --l-calls: #3a63d6;
  --l-list: #5e6a83;
  /* The app's own red, for an action that ends something. Its own value rather
     than --overdue: that red is a lead's state, and the two systems must not
     meet. Lighter than --overdue, which only ever sits on white — this sits on
     --ink. */
  --stop: #e0453f;

  --header: 44px;
  --tab-r: 10px;
  --row-pad-x: 24px;
  /* Row rhythm for the fact grid and anything set beside it. Both read as one
     band of facts, so a row gap that differs between them shows up as the two
     drifting out of line further down. */
  --fact-row: 11px;
  /* Every pane transition runs off these two, so the widths, the collapsing
     card metadata and the fades stay one movement instead of drifting apart. */
  --motion: 0.22s ease-out;
  --fade: 0.12s ease-out;

  /* One ramp along the pipeline: grey while a lead is untouched, warming as it
     heats up, and desaturating once it is settled either way. */
  --s-new: #6b7688;
  --s-attempted-contact: #3f5578;
  --s-interested: #c07a10;
  --s-quoting: #12744c;
  --s-won: #0f5a34;
  --s-lost: #a83b3b;
  --s-needs-convincing: #96603a;
  --s-not-interested: #77747a;
  --overdue: #c02626;
}

* { box-sizing: border-box; }

/* Any `display` rule outranks the browser's own `[hidden] { display: none }`, so
   an element given a display here stays on screen however the attribute is set.
   Everything toggled by `.hidden` in JS depends on this line. */
[hidden] { display: none !important; }

body {
  margin: 0;
  height: 100vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  font-family: system-ui, -apple-system, sans-serif;
  font-size: 14px;
  line-height: 1.5;
  color: var(--ink);
  background: #fff;
}

button { font: inherit; color: inherit; cursor: pointer; }
.muted { color: var(--muted); }
.spacer { flex: 1; }

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

header {
  position: relative;
  flex: 0 0 var(--header);
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 0 16px;
  background: #fff;
  z-index: 2;
}

/* The header's rule. A pseudo-element rather than a border because a border
   colour cannot be a gradient. */
header::after {
  content: '';
  position: absolute;
  inset: auto 0 0;
  height: 2px;
  background: var(--brand-grad);
}

.brand {
  font-weight: 600;
  letter-spacing: 0.02em;
  background: var(--brand-grad);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.link {
  border: 0;
  background: none;
  padding: 4px;
  color: var(--muted);
  text-decoration: underline;
}

/* ---------- the three panes ---------- */

/* Percentages, not fr: a track only animates between values of the same kind,
   and an fr interpolated against a px snaps instead of sliding. */
.panes {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: 100% 0% 0%;
  transition: grid-template-columns var(--motion);
}

.panes[data-view='list']    { grid-template-columns: 20% 80% 0%; }
.panes[data-view='company'] { grid-template-columns: 14% 20% 66%; }

.pane {
  min-width: 0;
  overflow-x: hidden;
  overflow-y: auto;
  transition: background-color var(--motion);
}

.rail   { background: var(--rail); }
.leads  { background: var(--leads); }
.detail { background: var(--detail); }

/* Home is one full-width white page; the rail only takes its own fill once it
   has something to sit beside. The bloom belongs to home alone: every other pane
   carries fades that paint their own background over themselves, and would show
   a seam wherever one crossed it. */
.panes[data-view='home'] .rail {
  background-color: #fff;
  background-image: radial-gradient(52% 40% at 50% 0%, rgba(77, 124, 243, 0.13), transparent 72%);
  background-repeat: no-repeat;
}

/* ---------- lists (rail) ---------- */

/* Home is a centred column of cards; the rail is those same elements with the
   column squeezed out of them. In the rail the pane itself is the only width
   limit, so a list name has the whole rail to run into before it truncates. */
.rail-greeting,
#lists {
  max-width: 720px;
  margin: 0 auto;
  padding: 0 24px 40px;
  transition: max-width var(--motion), padding var(--motion);
}

.panes:not([data-view='home']) .rail-greeting,
.panes:not([data-view='home']) #lists {
  max-width: 100%;
  padding: 0;
}

/* The top of home, hidden once the rail narrows — the same collapse the card
   metadata makes. */
.rail-greeting {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  margin: 38px auto 22px;
  padding-bottom: 0;
  overflow: hidden;
  max-height: 60px;
  transition: max-width var(--motion), padding var(--motion),
    max-height var(--motion), margin var(--motion), opacity var(--fade);
}

/* The one thing on this page that is not data, which is why it is the only place
   the gradient is spent. Clipped on the greeting itself and not the row, or the
   number beside it would come out transparent too. */
#greeting {
  font-size: 25px;
  font-weight: 600;
  letter-spacing: -0.022em;
  background: var(--brand-grad);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* The line this account dials from — a fact about the account, not about the
   work, so it sits at the far edge and stays quiet. */
#my-number {
  flex: none;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.panes:not([data-view='home']) .rail-greeting {
  max-height: 0;
  margin-top: 0;
  margin-bottom: 0;
  opacity: 0;
}

.ghost {
  border: 1px solid var(--line);
  background: none;
  border-radius: 4px;
  padding: 7px 14px;
  white-space: nowrap;
}

.ghost:hover { border-color: var(--ink); }

/* The mirror of the actions above: only there once the rail is a rail, and it
   collapses the same way rather than appearing. Its padding matches a collapsed
   list row so the icon lines up with the list names under it. */
.rail-home {
  display: block;
  border: 0;
  background: none;
  padding: 12px var(--row-pad-x);
  max-height: 44px;
  overflow: hidden;
  color: var(--muted);
  transition: max-height var(--motion), padding var(--motion), opacity var(--fade);
}

.rail-home svg { display: block; width: 16px; height: 16px; }
.rail-home:hover { color: var(--ink); }

.panes[data-view='home'] .rail-home {
  max-height: 0;
  padding-top: 0;
  padding-bottom: 0;
  opacity: 0;
}

.row-button {
  display: block;
  width: 100%;
  text-align: left;
  border: 0;
  border-bottom: 1px solid var(--line);
  background: none;
  transition: padding var(--motion), background-color var(--motion);
}

/* A surface you are meant to click: lifted off the page, with the count it
   exists to show sized like a headline. Never `overflow: hidden` — this is a
   .row-button, and an open one paints its flares outside its own box. */
.list-card {
  position: relative;
  display: flex;
  align-items: center;
  gap: 15px;
  padding: 20px 22px;
  margin-bottom: 10px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: #fff;
  transition: padding var(--motion), margin var(--motion), gap var(--motion),
    border-width var(--motion), border-radius var(--motion), background-color var(--motion),
    box-shadow var(--fade), transform var(--fade);
}

/* Only where a card is a card. In the rail these are rows, and a row that lifted
   under the cursor would fight the tab joining it to the next pane. */
.panes[data-view='home'] .list-card:hover {
  border-color: #aab6cb;
  box-shadow: 0 4px 16px rgba(16, 24, 40, 0.1);
  transform: translateY(-2px);
}

@media (prefers-reduced-motion: reduce) {
  .panes[data-view='home'] .list-card:hover { transform: none; }
}

/* Which list this is, said in colour. The fill is mixed from the same value the
   glyph is drawn in, so a tile can never disagree with its own icon. */
.list-tile {
  display: grid;
  place-items: center;
  /* Both, because this is a flex item in a rail row and a grid item in a tile,
     and only one of the two reads flex-basis. */
  flex: 0 0 34px;
  width: 34px;
  height: 34px;
  border-radius: 9px;
  overflow: hidden;
  color: var(--l-list);
  background: color-mix(in srgb, currentColor 13%, #fff);
  transition: flex-basis var(--motion), height var(--motion), opacity var(--fade);
}

.list-tile svg { width: 17px; height: 17px; }

.list-card[data-id='callback'] .list-tile { color: var(--l-callback); }
.list-card[data-id='warm'] .list-tile { color: var(--l-warm); }
.list-card[data-id='calls'] .list-tile { color: var(--l-calls); }

/* How much of the list has been called, drawn as the card's own bottom edge. The
   clipping is on the track and not the card, for the reason above. */
.list-bar {
  position: absolute;
  inset: auto 0 0;
  height: 4px;
  border-radius: 0 0 9px 9px;
  overflow: hidden;
  background: var(--line-soft);
  transition: opacity var(--fade);
}

.list-bar > span { display: block; height: 100%; background: var(--brand-grad); }

.list-text { display: flex; flex-direction: column; flex: 1; min-width: 0; }

.list-name {
  display: flex;
  min-width: 0;
  font-size: 18px;
  font-weight: 500;
  transition: font-size var(--motion);
}

/* The name needs its own box to be truncated in: a bare text node in a flex row
   is an anonymous item, which never takes an ellipsis. */
.list-name > span {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.list-sub > span { white-space: nowrap; }
.list-sub .overdue { color: var(--overdue); font-weight: 600; }

/* The three built-in lists read as one row of what is waiting; the lists a broker
   loaded read as rows under them. Two shapes rather than one is the whole of what
   tells them apart. */
.autos {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  margin-bottom: 10px;
}

/* In the row a card is a tile: the icon and name on one line, the number under
   them as the thing read first, the breakdown last. `display: contents` is what
   lets the name and the sub be placed on this grid from inside .list-text.
   Scoped to home rather than to .autos, because every rule here would otherwise
   have to be undone again in the rail — `contents` especially, which would put
   the sub-line beside the name instead of under it. */
.panes[data-view='home'] .autos .list-card {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  /* Rows packed to the top rather than spread through the card. The cards are all
     as tall as the tallest, so without this the one carrying an overdue line would
     share its extra height out and sit its number a few pixels off the others. */
  align-content: start;
  gap: 0 11px;
  padding: 17px 18px 15px;
  margin-bottom: 0;
}

.panes[data-view='home'] .autos .list-text { display: contents; }
.panes[data-view='home'] .autos .list-tile { grid-area: 1 / 1; }
.panes[data-view='home'] .autos .list-name { grid-area: 1 / 2; font-size: 14px; }
.panes[data-view='home'] .autos .list-sub { grid-area: 3 / 1 / 4 / -1; }

.panes[data-view='home'] .autos .list-meta {
  grid-area: 2 / 1 / 3 / -1;
  flex-direction: row;
  align-items: baseline;
  gap: 8px;
  max-width: none;
  margin-top: 10px;
}

.panes[data-view='home'] .autos .count { font-size: 38px; }

/* Names the group under it, and carries the space that opens it away from the
   automatic cards — the gap has to live here, since a header between the two
   groups leaves no adjacent pair of cards to hang it on. */
.group-head {
  margin: 26px 0 10px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  transition: margin var(--motion), padding var(--motion);
}

/* Indented to the row padding, so it starts where the list names under it do. */
.panes:not([data-view='home']) .group-head {
  margin: 30px 0 4px;
  padding: 0 var(--row-pad-x);
}

/* Nothing above it to be pushed away from when a broker has no automatic lists. */
#lists > .group-head:first-child { margin-top: 0; }

.list-sub {
  font-size: 13px;
  color: var(--muted);
  overflow: hidden;
  max-height: 24px;
  transition: max-height var(--motion), opacity var(--fade);
}

/* Clipped to nothing in the rail, where it still sets every row's height: a box
   with no width wraps its label at each space, so a two-word one would make its
   row taller than the rest. */
.list-meta {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  flex: 0 0 auto;
  overflow: hidden;
  max-width: 120px;
  white-space: nowrap;
  transition: max-width var(--motion), opacity var(--fade);
}

.count { font-size: 30px; font-weight: 500; line-height: 1.1; }

.count-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

/* The card flattens into a rail row: no surface, no corners, just the name. The
   row of tiles becomes a stack — three grid columns cannot interpolate into one,
   so those three snap while the pane animates around them. */
.panes:not([data-view='home']) .autos {
  display: block;
  margin-bottom: 0;
}

.panes:not([data-view='home']) .list-card {
  display: flex;
  padding: 12px var(--row-pad-x);
  gap: 0;
  margin-bottom: 0;
  border-width: 0 0 1px;
  border-radius: 0;
  background: transparent;
}

/* Each group is closed at the top as well as the bottom: every row draws its own
   lower rule, so without these the first row of a group has an open edge. The
   automatic group opens the rail; the imported one opens under its header. */
.panes:not([data-view='home']) .autos > .list-card:first-child,
.panes:not([data-view='home']) .group-head + .list-card {
  border-top-width: 1px;
}

.panes:not([data-view='home']) .list-name { font-size: 14px; }
.panes:not([data-view='home']) .list-sub { max-height: 0; opacity: 0; }
.panes:not([data-view='home']) .list-meta { max-width: 0; opacity: 0; }
.panes:not([data-view='home']) .list-bar { opacity: 0; }
.panes:not([data-view='home']) .list-card:hover { background: var(--hover); }

/* In the rail the tile sheds its square and is just the glyph — there is no room
   for a filled one — but it keeps its colour, so a row says which list it is at
   both widths. The margin stands in for the row's gap, which is 0 here. */
.panes:not([data-view='home']) .list-tile {
  flex-basis: 15px;
  width: 15px;
  height: 15px;
  margin-right: 8px;
  border-radius: 0;
  background: none;
}

.panes:not([data-view='home']) .list-tile svg { width: 15px; height: 15px; }

/* ---------- companies (leads) ---------- */

.pane-title {
  padding: 26px 28px 12px;
  font-size: 20px;
  font-weight: 500;
  transition: padding var(--motion), font-size var(--motion);
}

.panes[data-view='company'] .pane-title { padding: 14px var(--row-pad-x) 8px; font-size: 15px; }

.lead {
  padding: 14px 28px;
  border-bottom-color: var(--line-soft);
  transition: padding var(--motion), margin var(--motion), background-color var(--motion);
}

.panes[data-view='company'] .lead { padding: 12px var(--row-pad-x); }

/* ---------- the open row ---------- */

/* The open row is a tab joined to the pane it opens: it wears that pane's
   colour and flares outward where they meet, so the two read as one surface. */
.panes[data-view='list'] .list-card.open,
.panes[data-view='company'] .list-card.open { --tab-fill: var(--leads); }
.panes[data-view='company'] .lead.open { --tab-fill: var(--detail); }

/* The inset it gives up on the left it takes back as padding, so the name does
   not move when a row opens. A button sizes to its content, so the width has to
   be spelled out or the tab stops short of the pane and never joins it. */
.panes:not([data-view='home']) .row-button.open {
  --tab-inset: calc(var(--row-pad-x) / 2);
  position: relative;
  width: calc(100% - var(--tab-inset));
  margin-left: var(--tab-inset);
  padding-left: var(--tab-inset);
  background: var(--tab-fill);
  border-radius: var(--tab-r) 0 0 var(--tab-r);
}

/* A divider touching the tab would cut across the join — the rule opening a group
   included, when the row that opens it is the one selected. */
.row-button.open { border-color: transparent; }
.row-button:has(+ .open) { border-bottom-color: transparent; }

/* A flare is the fill painted outside the row with a disc of the pane behind
   punched out of it — a concave corner has no border-radius equivalent. The
   stops straddle --tab-r rather than meeting on it, or the arc renders jagged. */
.row-button.open::before,
.row-button.open::after {
  content: '';
  position: absolute;
  right: 0;
  width: var(--tab-r);
  height: var(--tab-r);
}

.row-button.open::before {
  bottom: 100%;
  background: radial-gradient(
    circle at top left,
    #0000 calc(var(--tab-r) - 0.5px),
    var(--tab-fill) calc(var(--tab-r) + 0.5px)
  );
}

.row-button.open::after {
  top: 100%;
  background: radial-gradient(
    circle at bottom left,
    #0000 calc(var(--tab-r) - 0.5px),
    var(--tab-fill) calc(var(--tab-r) + 0.5px)
  );
}

/* min-width:0 on both the row and the text: without it a flex or grid child
   refuses to shrink below its content, and a long name widens the row instead
   of ellipsing inside it. */
.lead-name {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  font-weight: 500;
}

.lead-label,
.lead-sub {
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.lead-sub { display: block; color: var(--muted); font-size: 13px; }
.lead-sub.overdue { color: var(--overdue); }

/* Filled, unlike every other glyph: a heart in outline reads as a button to
   press rather than as a mark already made. */
.lead-heart {
  flex: 0 0 12px;
  display: flex;
  color: var(--ink);
}

.lead-heart svg { width: 12px; height: 12px; fill: currentColor; stroke: none; }

/* A row in the Calls list: the same lead row with the day added down its right
   side, on the name's line rather than centred between the two. */
.call-row {
  display: grid;
  grid-template-columns: 1fr auto;
  column-gap: 12px;
}

.call-row .lead-name { grid-column: 1; }

/* The day occupies the first row only, so the line under it runs the full width
   of the row rather than staying inside a column the day narrowed. */
.call-row .lead-sub { grid-column: 1 / -1; }

/* The last column, whether or not the summary added one before it. */
.call-when {
  grid-column: -2;
  grid-row: 1;
  color: var(--muted);
  font-size: 13px;
}

/* Only where the pane is the width of the window: a column of its own, wide enough
   to tell one conversation from another and no wider — the row is still the call,
   not the summary. Beside an open company the pane is a fifth of that, and the
   name has to have all of it. */
.call-summary { display: none; }

/* Beside the call rather than out at the edge, with the empty column between it
   and the day: what was said follows who was called, read left to right. */
.panes[data-view='list'] .call-row {
  grid-template-columns: minmax(0, 320px) minmax(0, 300px) 1fr;
  column-gap: 40px;
}

.panes[data-view='list'] .call-when { justify-self: end; }

/* Held to the name's column, so it stops where the summary starts. */
.panes[data-view='list'] .call-row .lead-sub { grid-column: 1; }

.panes[data-view='list'] .call-summary {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  grid-column: 2;
  grid-row: 1 / span 2;
  align-self: start;
  overflow: hidden;
  color: var(--muted);
  font-size: 13px;
  line-height: 1.45;
}

/* The sub-line carries the handset, sized as it is in the record on the company
   page — the same call, drawn the same way, in both panes.

   It wraps rather than truncating, unlike the name above it: a name cut short is
   still recognisable, where what happened on the call is the whole point of the
   row and reads as nothing with its end missing. */
.call-row .lead-sub {
  display: flex;
  align-items: flex-start;
  gap: 7px;
  white-space: normal;
  overflow: visible;
}

/* Held to the first line rather than centred on a block that may be two lines. */
.call-row .lead-sub svg { width: 13px; height: 13px; flex: none; margin-top: 3px; }

.call-row .lead-sub > span { min-width: 0; }

/* Section headings inside Warm leads and a lead list. */
.lead-section {
  display: flex;
  align-items: center;
  gap: 5px;
  padding: 16px var(--row-pad-x) 6px;
  color: var(--muted);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.lead-section svg { flex: 0 0 11px; width: 11px; height: 11px; fill: currentColor; stroke: none; }

/* Archived leads stay reachable but stop competing for attention. */
.archived-toggle {
  display: flex;
  align-items: center;
  gap: 5px;
  width: 100%;
  padding: 14px var(--row-pad-x);
  border: 0;
  border-top: 1px solid var(--line-soft);
  background: none;
  color: var(--muted);
  font-size: 13px;
}

.archived-toggle svg { width: 14px; height: 14px; transition: transform var(--motion); }
.archived-toggle[aria-expanded="true"] svg { transform: rotate(180deg); }
.archived-toggle:hover { color: var(--ink); }
.lead.archived { opacity: 0.45; }

.empty { padding: 26px 28px; color: var(--muted); }

/* Home with nothing in it — the first screen a new broker ever sees. Centred and
   given a mark, because a single grey sentence on a blank page reads as a failure
   rather than as a beginning. */
.empty-home { padding: 56px 0 40px; text-align: center; }

.empty-mark {
  display: grid;
  place-items: center;
  width: 52px;
  height: 52px;
  margin: 0 auto 18px;
  border-radius: 13px;
  color: var(--brand-dk);
  background: var(--brand-tint);
}

.empty-mark svg { width: 24px; height: 24px; }
.empty-home h2 { margin: 0; font-size: 20px; font-weight: 500; }
.empty-home p { margin: 7px auto 0; max-width: 42ch; color: var(--muted); }

/* ---------- company detail ---------- */

/* The head carries the top padding instead of this, so that padding scrolls away
   with it rather than leaving a gap above it once it sticks. */
.detail-body { padding: 0 40px 80px; }

/* Held at the top of the pane: who you are looking at and the button that dials
   them are what the rest of the page is read against, and scrolling down to the
   calls must not take the number away. Pulled out past the body's gutters so the
   band it paints reaches both pane edges. */
.detail-head {
  position: sticky;
  top: 0;
  z-index: 4;
  margin: 0 -40px 28px;
  padding: 28px 40px 14px;
  background: var(--detail);
}

/* The chips are a sibling of this row rather than sitting under the name, so
   they start at the pane's edge in line with the logo instead of indented past
   it. Centred rather than top-aligned: the logo is taller than the name. */
.detail-title {
  display: flex;
  align-items: center;
  gap: 14px;
}

.detail-head h1 { font-size: 24px; font-weight: 500; margin: 0; }

/* Pushed right on its own rather than by space-between, which would put the gap
   between the logo and the name instead. */
.detail-title .call-wrap { margin-left: auto; }

/* No border: a favicon carries its own edge, and a second one drawn around it
   reads as a box inside a box. The corners are still taken off it — overflow is
   what applies them, since the image is what reaches the edge, not this element.
   The initials are the element's own text and the image covers them, which is
   what makes dropping a broken image enough. */
.company-logo {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 44px;
  height: 44px;
  border-radius: 8px;
  overflow: hidden;
  color: var(--muted);
  font-size: 15px;
  font-weight: 500;
}

/* Edge to edge: a favicon carrying its own background looks inset when padded,
   and a favicon drawn as a bare glyph already has its margin in the artwork.
   The image keeps a background so a transparent one hides the initials rather
   than sitting on top of them. */
.company-logo img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  background: var(--detail);
}

/* ---------- lead chips ---------- */

.chips {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-top: 14px;
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 11px;
  border: 1px solid transparent;
  border-radius: 999px;
  background: none;
  font-size: 13px;
  line-height: 1.4;
  white-space: nowrap;
}

.chip svg { flex: 0 0 13px; width: 13px; height: 13px; }

/* Filled because it is a value rather than an action, and the fill is what
   carries which value it is. The toggles beside it fill in --brand, so this row
   is the one place the two colour systems sit side by side. */
.chip.status { color: #fff; font-weight: 500; }
.chip.status:hover { filter: brightness(1.08); }

.caret {
  width: 0;
  height: 0;
  margin-left: 1px;
  border: 3.5px solid transparent;
  border-top-color: currentColor;
  border-bottom: 0;
}

.chip.outline { border-color: var(--line); color: var(--muted); }
.chip.outline:hover { border-color: var(--ink); color: var(--ink); }

/* An outline chip that is switched on fills rather than tints, so on and off
   are told apart by weight instead of by a shade of the same colour. */
.chip.outline.on {
  border-color: var(--brand-dk);
  background: var(--brand-dk);
  color: #fff;
}

.chip.outline.on[data-chip='priority'] svg { fill: currentColor; stroke: none; }
.chip.outline.overdue { border-color: var(--overdue); color: var(--overdue); }

/* Sits outside the flex flow so opening it cannot reflow the chips under it. */
.status-menu {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  z-index: 5;
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 6px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: #fff;
  box-shadow: 0 8px 24px rgba(16, 24, 40, 0.12);
}

.status-option {
  padding: 6px 12px;
  border: 0;
  border-radius: 999px;
  background: none;
  text-align: left;
  font-size: 13px;
  color: var(--ink);
}

.status-option:hover { background: var(--hover); }

/* The one already set is shown filled in its own colour — the same thing the
   chip above it is showing, so the menu says which row it came from. */
.status-option[aria-current] { color: #fff; font-weight: 500; }

.chip.status-new,
.status-option.status-new[aria-current],
.wrapup-status.status-new[aria-pressed="true"] { background: var(--s-new); }
.chip.status-attempted-contact,
.status-option.status-attempted-contact[aria-current],
.wrapup-status.status-attempted-contact[aria-pressed="true"] { background: var(--s-attempted-contact); }
.chip.status-interested,
.status-option.status-interested[aria-current],
.wrapup-status.status-interested[aria-pressed="true"] { background: var(--s-interested); }
.chip.status-quoting,
.status-option.status-quoting[aria-current],
.wrapup-status.status-quoting[aria-pressed="true"] { background: var(--s-quoting); }
.chip.status-won,
.status-option.status-won[aria-current],
.wrapup-status.status-won[aria-pressed="true"] { background: var(--s-won); }
.chip.status-lost,
.status-option.status-lost[aria-current],
.wrapup-status.status-lost[aria-pressed="true"] { background: var(--s-lost); }
.chip.status-needs-convincing,
.status-option.status-needs-convincing[aria-current],
.wrapup-status.status-needs-convincing[aria-pressed="true"] { background: var(--s-needs-convincing); }
.chip.status-not-interested,
.status-option.status-not-interested[aria-current],
.wrapup-status.status-not-interested[aria-pressed="true"] { background: var(--s-not-interested); }

/* Same anchoring as the status menu: outside the flex flow, so opening it cannot
   reflow the chips it hangs from. */
.touchpoint-popover {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  z-index: 5;
  width: 268px;
  padding: 12px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: #fff;
  box-shadow: 0 8px 24px rgba(16, 24, 40, 0.12);
}

.cal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 8px;
  font-weight: 500;
}

.cal-nav {
  width: 26px;
  height: 26px;
  border: 0;
  border-radius: 50%;
  background: none;
  color: var(--muted);
  font-size: 18px;
  line-height: 1;
}

.cal-nav:hover { background: var(--hover); color: var(--ink); }

.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); }

.cal-weekdays {
  margin-bottom: 2px;
  color: var(--muted);
  font-size: 11px;
  text-align: center;
}

.cal-day {
  height: 32px;
  border: 0;
  border-radius: 50%;
  background: none;
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}

.cal-day:hover { background: var(--hover); }
.cal-day.outside { color: #b6c0d2; }

/* Today is outlined and the chosen day is filled, so the two read as different
   kinds of mark rather than as two shades of the same one. */
.cal-day.today { box-shadow: inset 0 0 0 1px var(--line); }
.cal-day.picked { background: var(--brand-dk); color: #fff; }

.cal-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 10px;
}

.cal-link {
  padding: 5px 8px;
  border: 0;
  border-radius: 8px;
  background: none;
  color: var(--muted);
  font-size: 13px;
}

.cal-link:hover { background: var(--hover); color: var(--ink); }

.cal-done {
  padding: 5px 14px;
  border: 0;
  border-radius: 999px;
  background: var(--brand-dk);
  color: #fff;
  font-size: 13px;
}

.call-button {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 0 0 auto;
  border: 0;
  border-radius: 999px;
  padding: 11px 22px;
  background: var(--brand-grad);
  color: #fff;
  font-weight: 500;
}

.call-button svg { flex: 0 0 15px; width: 15px; height: 15px; }
.call-button:disabled { background: #c3ccdb; cursor: not-allowed; }

.call-wrap { position: relative; }

/* Which of the two the button shows is the only thing saying a call is live —
   callControl in company.js says why it is done this way round. */
.call-idle,
.call-live {
  display: flex;
  align-items: center;
  gap: 8px;
}

.call-live { display: none; }
.call-button.calling .call-idle { display: none; }
.call-button.calling .call-live { display: flex; }

/* Hanging up is the call bar's job, so the live button takes no clicks. Pressing
   it were it reachable would be harmless anyway — the dialer refuses a second
   call outright. */
.call-button.calling { pointer-events: none; }

/* Animated rather than transitioned, unlike the rest of this file: a transition
   needs something to have changed, and a call is happening the whole time it
   runs. The four bars share this one cycle and differ only in where they enter
   it. */
@keyframes audio-bar {
  from { transform: scaleY(0.3); }
  to   { transform: scaleY(1); }
}

.call-live rect {
  transform-box: fill-box;
  transform-origin: center;
  animation: audio-bar 0.6s ease-in-out infinite alternate;
}

.call-live rect:nth-child(2) { animation-delay: -0.45s; }
.call-live rect:nth-child(3) { animation-delay: -0.2s; }
.call-live rect:nth-child(4) { animation-delay: -0.6s; }

/* A motion with no end is exactly what this setting is asking about. Held at the
   height the bars pass through, so the icon still reads as levels. */
@media (prefers-reduced-motion: reduce) {
  .call-live rect { animation: none; transform: scaleY(0.65); }
}

/* Anchored to the right edge: the pane is overflow-x: hidden and the button sits
   against it, so a menu opening leftwards would be cut off. */
.call-menu {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 5;
  width: 260px;
  padding: 6px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: #fff;
  box-shadow: 0 8px 24px rgba(16, 24, 40, 0.12);
}

/* Named for the menu it belongs to, not for the call: a bare .call-row is also a
   row of the Calls list, and the two laid out each other by accident. */
.call-menu-row { display: flex; align-items: center; }

.call-option {
  flex: 1;
  min-width: 0;
  display: grid;
  padding: 7px 10px;
  border: 0;
  border-radius: 8px;
  background: none;
  text-align: left;
}

.call-option:hover { background: var(--hover); }
.call-option-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.call-option-sub { font-size: 13px; color: var(--muted); }

.call-info {
  flex: 0 0 auto;
  display: flex;
  padding: 7px;
  border: 0;
  border-radius: 999px;
  background: none;
  color: var(--muted);
}

.call-info svg { width: 15px; height: 15px; }
.call-info:hover { background: var(--hover); color: var(--ink); }

/* ---------- identity ---------- */

.identity { margin-bottom: 26px; }

/* max-content rather than 1fr: equal fractions would put the second column at
   half the pane however short the text is, leaving the two miles apart. Sized to
   their content, the columns sit a fixed gap from each other instead. */
.fact-grid {
  display: grid;
  /* minmax so an unusually long value shortens itself rather than shoving the
     second column back out to where fixed fractions had it. */
  grid-template-columns: repeat(2, minmax(0, max-content));
  justify-content: start;
  max-width: 620px;
  gap: var(--fact-row) 44px;
}

.fact-grid > div,
.fact-grid a {
  display: flex;
  align-items: center;
  min-width: 0;
}

.fact-grid > div { gap: 9px; }
.fact-grid a { gap: 7px; color: inherit; text-decoration: none; }
.fact-grid a:hover span { text-decoration: underline; }

.fact-grid span {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.fact-grid svg {
  flex: 0 0 16px;
  width: 16px;
  height: 16px;
  color: var(--muted);
}

/* The arrow only says "this leaves the app", so it stays quiet until hover. */
.fact-grid a svg:last-child {
  flex-basis: 12px;
  width: 12px;
  height: 12px;
  opacity: 0.45;
}

.fact-grid a:hover svg { opacity: 1; }

/* Collapsed it is a caption to the facts, so one line about as wide as the two
   columns above it. Opened it is the thing being read, and takes a full measure. */
.identity-description {
  --clamp-lines: 1;
  margin-top: 16px;
  max-width: 320px;
}

.identity-description.open { max-width: 620px; }

/* ---------- clamp ---------- */

/* Enough of a block to judge whether the rest is worth opening, keeping whatever
   follows at a constant place on the pane. Two knobs a user can set on itself:
   --clamp-lines for how much shows, and --clamp-bg for whatever the block sits
   on, since the fade below has to end in that colour.
   Both defaults live in the var() fallbacks rather than on .clamp — declared
   there, they would beat any override written earlier in this file.
   Paired with wireClamp() in company.js, which reveals the button only when there
   is a line being held back. */
.clamp { position: relative; }

.clamp p {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: var(--clamp-lines, 2);
  overflow: hidden;
  margin: 0;
  color: var(--muted);
  line-height: 1.6;
}

.clamp.open p { -webkit-line-clamp: none; }

/* Sits on the end of the clamped line rather than under it. It covers the last
   words, so it carries the block's own background leftward as a fade: the text
   runs out instead of disappearing behind the word. Inherited font size and the
   paragraph's line-height are what put it on that line and not between lines. */
.clamp button {
  position: absolute;
  right: 0;
  bottom: 0;
  padding: 0 0 0 48px;
  border: 0;
  background: linear-gradient(to right, transparent, var(--clamp-bg, var(--detail)) 48px);
  color: var(--muted);
  font-size: inherit;
  line-height: 1.6;
  text-decoration: underline;
}

/* Expanded there is nothing to fade, and the last line may be full width. */
.clamp.open button {
  position: static;
  padding: 0;
  margin-top: 5px;
  background: none;
}

.clamp button:hover { color: var(--ink); }

/* ---------- identity links ---------- */

/* ---------- benefits ---------- */

/* A value the filing does not carry, naming the field it stands in for. The shape
   of this section is what a broker scans mid-call, so a row that vanished would
   move everything under it — and "Commission unknown" answers the question the
   empty slot would otherwise leave open. Italic and pale so it never reads as
   data at a glance. */
.none {
  color: #a6b0c2;
  font-style: italic;
}

/* The label column is fixed so Carriers, Covers and Brokers start on one line —
   the three rows are read as a stack, not each on its own. */
/* The parties stand as a third column beside the numbers rather than under them,
   so the filing reads as one band of facts and the contracts button is the only
   thing below it. Wraps underneath when the pane is too narrow to hold both. */
.benefit-top {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 12px 44px;
}

.benefit-top .fact-grid { flex: 0 0 auto; }

/* The label column takes the width of the longest label rather than a fixed one,
   so each list starts right after the word naming it instead of across a gap. */
.benefit-parties {
  flex: 1 1 240px;
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  gap: var(--fact-row) 10px;
  min-width: 0;
  margin: 0;
}

.benefit-parties dt { color: var(--muted); }
.benefit-parties dd { margin: 0; min-width: 0; overflow-wrap: anywhere; }

/* The section has ONE open state, driven by .benefits.open: the tail of every
   list, the contracts and the signer all appear together. A row that opened on
   its own would make a broker click three times to see one filing. */
.benefits:not(.open) .extra,
.benefits:not(.open) .contracts,
.benefits:not(.open) .benefit-signer,
.benefits.open .benefit-more { display: none; }

.benefit-more {
  border: 0;
  padding: 0;
  background: none;
  font: inherit;
  color: #a6b0c2;
  cursor: pointer;
  transition: color var(--fade);
}

.benefit-more:hover { color: var(--ink); }

/* The control for that one open state. A chevron rather than a caret glyph, so
   it can rotate to say which way the section is about to move. */
.benefit-toggle {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  margin-top: 16px;
  border: 0;
  padding: 0;
  background: none;
  font: inherit;
  font-weight: 600;
  color: inherit;
  cursor: pointer;
  transition: color var(--fade);
}

.benefit-toggle:hover { color: var(--muted); }

.benefit-toggle::after {
  content: '';
  width: 7px;
  height: 7px;
  border: solid currentColor;
  border-width: 0 1.5px 1.5px 0;
  transform: translateY(-2px) rotate(45deg);
  transition: transform var(--fade);
}

.benefits.open .benefit-toggle::after { transform: translateY(1px) rotate(-135deg); }

/* Same grid as signals and people: the detail pane has one rhythm for blocks
   that repeat, and a contract is one of those. */
.contracts {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
  margin-top: 10px;
  align-items: start;
}

.contract {
  padding: 15px 17px;
  border-radius: 9px;
  background: var(--card);
}

.contract-carrier { font-weight: 600; }

.contract-facts {
  display: flex;
  flex-direction: column;
  margin-top: 6px;
  color: var(--muted);
}

/* Who holds the contract, and what they take off it. Separated from the facts
   above by a rule: it is the answer the expanded view exists to give. */
.contract-agent {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  margin-top: 8px;
  padding-top: 8px;
  border-top: 1px solid var(--line-soft);
}

.contract-agent + .contract-agent { margin-top: 0; }
.contract-agent span:first-child { min-width: 0; overflow-wrap: anywhere; }
.contract-agent span:last-child { flex: 0 0 auto; color: var(--muted); }

.benefit-signer { margin-top: 10px; color: var(--muted); }

/* ---------- advertised benefits on website ---------- */

/* Muted like the description, because the chips under it are what gets clicked. */
.site-benefits p {
  margin: 0;
  max-width: 620px;
  color: var(--muted);
  line-height: 1.6;
}

/* ---------- signals ---------- */

.signals {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
  align-items: start;
}

.signal {
  padding: 15px 17px;
  border-radius: 9px;
  background: var(--card);
}

/* No clamp: headlines run to 170 characters and the whole point of one is that
   it can be read without opening anything. */
.signal-title { font-weight: 600; }

/* The research keeps its own line breaks — quotes sit one per line — so they are
   honoured rather than collapsed into a paragraph. */
.signal-detail { --clamp-bg: var(--card); margin-top: 6px; }
.signal-detail p { white-space: pre-line; }


/* ---------- source chips ---------- */

/* One row that scrolls rather than wraps, so a block with seven sources is the
   same height as one with two. The mask only shows itself once a chip actually
   reaches the right edge — while they all fit it is fading empty space, which is
   what makes the fade mean "there is more" without asking JS whether there is. */
.source-chips {
  display: flex;
  gap: 6px;
  margin-top: 12px;
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
  mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
}

.source-chips::-webkit-scrollbar { display: none; }

/* Chips rather than the bordered buttons the identity links use: there are up to
   seven of them under one block, and they are a footnote to it, not an action. */
.source-chips a {
  display: inline-flex;
  flex: 0 0 auto;
  align-items: baseline;
  gap: 6px;
  white-space: nowrap;
  padding: 3px 10px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: #fff;
  color: var(--ink);
  font-size: 12px;
  text-decoration: none;
  transition: border-color var(--fade);
}

.source-chips a:hover { border-color: #b6c0d2; }

/* Lighter than the domain: which source it is gets read first, when it was
   written only once the domain has been chosen. */
.source-chips span { color: #8e99ad; }

/* ---------- folds ---------- */

/* Paler than the dividers elsewhere: these outline a block a broker skims past,
   where a divider is separating two things he is reading. */
.fold {
  /* Named because the record breaks out of it and hands it back per row. */
  --fold-pad: 16px;
  border: 1px solid #eaf0f8;
  border-radius: 12px;
  margin-top: 12px;
}

.fold-head {
  display: flex;
  align-items: center;
  gap: 14px;
  width: 100%;
  padding: 14px 16px;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}

.fold h2 {
  font-size: 15px;
  font-weight: 600;
  margin: 0;
}

.fold-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 34px;
  height: 34px;
  border-radius: 9px;
  background: var(--brand-tint);
  color: var(--brand-dk);
}

.fold-icon svg { width: 17px; height: 17px; }

.fold-head > svg {
  width: 16px;
  height: 16px;
  margin-left: auto;
  color: var(--muted);
  transition: transform var(--motion);
}

.fold.open .fold-head > svg { transform: rotate(180deg); }

/* A section holding nothing yet. Recessed rather than disabled: it is still the
   way in, because what fills it is written inside it. */
.fold.muted { border-color: #f0f4fa; }
.fold.muted .fold-head { color: var(--muted); }
.fold.muted .fold-icon { background: var(--card); color: #9ca7ba; }

/* A row squeezed to nothing rather than a height animated to a number nobody can
   know in CSS. Three elements, because the row cannot shrink below what the
   element in it contributes, and padding counts towards that however the box is
   sized — so the padding has to sit one level deeper than the row can reach. */
.fold-body {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--motion);
}

.fold.open .fold-body { grid-template-rows: 1fr; }

/* Clipping is also what lets the row shrink at all: a grid item's automatic
   minimum size is its content, unless the item clips. */
.fold-clip { overflow: hidden; }

/* The padding is the whole of the gap: a section ending in a bare paragraph
   would otherwise sit deeper than one ending in a card. */
.fold-inner { padding: 0 var(--fold-pad) 18px; }
.fold-inner > :first-child { margin-top: 0; }
.fold-inner > :last-child { margin-bottom: 0; }

/* ---------- calls & notes ---------- */

/* A height that does not grow with the lead: the section reads the same on one
   called once and one worked for a year, and the rest of the page stays where
   the broker left it. Started at the bottom, by wireHistory. */
/* Out to the section's edges so a highlighted call is a band across the whole of
   it, with the inset handed to each row instead. Widening the rows themselves
   would not work: overflow-y makes the record clip sideways as well. */
.history-box {
  position: relative;
  margin: 0 calc(var(--fold-pad) * -1);
}

.history > * { padding: 0 var(--fold-pad); }

/* The gap lives here rather than on the entries, so the space between two of them
   is one number instead of two paddings meeting. */
.history {
  display: flex;
  flex-direction: column;
  gap: 18px;
  max-height: 220px;
  overflow-y: auto;
  /* No overscroll-behavior here on purpose: reaching either end of the record has
     to carry on down the page, so the box never traps the wheel. */

  /* No bar down the side of the record. The fade over the top edge is what says
     it scrolls, and it says it without taking a column of the block. */
  scrollbar-width: none;
}

.history::-webkit-scrollbar { display: none; }

/* Laid over the top edge rather than sitting above it, so the entries scrolling
   past dissolve into the pane instead of stopping at a line — which is the whole
   of the invitation to scroll up. wireHistory shows it only when there is
   something up there to reach. */
.history-box::before {
  content: '';
  position: absolute;
  inset: 0 0 auto;
  height: 40px;
  background: linear-gradient(var(--detail) 15%, transparent);
  opacity: 0;
  transition: opacity var(--fade);
  pointer-events: none;
}

.history-box.scrolled::before { opacity: 1; }

/* Space between entries, never a rule: a line under every attempt would make the
   quiet majority of the record as loud as the conversations in it. */
.entry {
  display: flex;
  align-items: flex-start;
  gap: 12px;
}

.entry > .block,
.entry > .entry-text { flex: 1; }

.entry-when {
  color: var(--muted);
  font-size: 13px;
  white-space: nowrap;
}

/* A quiet line has no head to ride, so the date is nudged onto the line itself. */
.entry.quiet .entry-when { padding-top: 2px; }

/* An attempt, or a lead state the broker set: the whole row recedes, so what is
   left standing is what somebody actually said. */
.entry.quiet .entry-text { color: var(--muted); }

.entry-text {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  min-width: 0;
  overflow-wrap: anywhere;
}

.entry-text svg { width: 14px; height: 14px; flex: none; }

/* What there is to come back to: a conversation, or something the broker wrote. */
.block {
  padding: 10px 13px;
  border-radius: 10px;
  background: var(--card);
}

/* Who and how long, above the substance rather than beside it — read once to see
   whose conversation this was, then not again. */
.block-head {
  display: flex;
  align-items: center;
  gap: 7px;
  margin: 0;
  color: var(--muted);
  font-size: 13px;
}

.block-head svg { width: 14px; height: 14px; flex: none; }

/* The far edge of the head line, so the date is the last thing on it. */
.block-head .entry-when { margin-left: auto; }
.block-head + .block-text { margin-top: 5px; }

.block-text {
  margin: 0;
  min-width: 0;
  overflow-wrap: anywhere;
  outline: 0;
}

.play {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  margin-top: 9px;
  padding: 5px 11px 5px 9px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--detail);
  font: inherit;
  font-size: 13px;
  color: inherit;
  cursor: pointer;
}

.play:hover { border-color: #aab6cb; }
.play svg { width: 12px; height: 12px; }

.entry-actions {
  display: flex;
  gap: 2px;
  align-self: start;
}

.entry-actions button {
  display: flex;
  padding: 4px;
  border: 0;
  border-radius: 6px;
  background: none;
  color: var(--muted);
  cursor: pointer;
}

.entry-actions button svg { width: 15px; height: 15px; }
.entry-actions button:hover { background: var(--hover); color: inherit; }

/* Only on the row being pointed at: two icons beside every note is more furniture
   than record. */
.entry-actions { visibility: hidden; }
.entry:hover .entry-actions,
.entry:focus-within .entry-actions { visibility: visible; }

/* Replaces the Play button in the block, so it stands off the head the same way. */
.block audio { height: 34px; max-width: 100%; margin-top: 9px; }

/* Grows with what is typed where the browser can, so a long note is written in
   full rather than through a one-line window. */
.note-input {
  display: block;
  width: 100%;
  margin-top: 12px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 9px;
  background: none;
  font: inherit;
  color: inherit;
  resize: none;
  field-sizing: content;
}

.note-input:focus { outline: 0; border-color: var(--brand); }

/* ---------- people ---------- */

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

/* A filled bar rather than a bordered card: on a white pane the fill is what
   makes each person read as a separate, reachable thing. */
.person {
  padding: 13px 16px;
  border-radius: 9px;
  background: var(--card);
  transition: background-color var(--fade);
}

.person:hover { background: var(--hover); }

.person-name { font-weight: 600; }

.person-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  row-gap: 3px;
  margin-top: 2px;
  color: var(--muted);
}

/* The separator hangs off the item before it rather than standing alone, so a
   row that wraps never opens with a stray dot. */
.person-item:not(:last-child)::after {
  content: '·';
  margin: 0 9px;
  color: #b6c0d2;
}

/* Buttons dial, links leave — but they are one row of choices, so they are
   styled as one thing rather than as a button beside a hyperlink. */
.person-action {
  border: 0;
  padding: 0;
  background: none;
  font: inherit;
  color: var(--muted);
  text-decoration: none;
  cursor: pointer;
  transition: color var(--fade);
}

/* Hovering anywhere on the bar underlines every action at once: that is what
   says the row is interactive. Which one you are about to hit is then said by
   the single action going dark under the cursor. */
.person:hover .person-action { text-decoration: underline; }
.person-action:hover { color: var(--ink); }

audio { height: 32px; vertical-align: middle; }

/* ---------- call bar ---------- */

/* Above everything, the transcript panel included: whatever else is open, a call
   in progress is the thing that must stay reachable. */

/* The one filled surface that is not --brand. It holds --stop, and red on the
   brand gradient fights; on --ink the hang-up button is the only saturated thing
   in the bar, which is what it has to be. It also keeps starting a call and
   being in one visually distinct — the Call button is the gradient, this is not. */
#callbar {
  position: fixed;
  z-index: 30;
  left: 50%;
  bottom: 24px;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 12px 18px;
  border-radius: 999px;
  background: var(--ink);
  color: #fff;
  box-shadow: 0 6px 24px rgba(16, 24, 40, 0.32);
}

/* White alphas rather than greys: a neutral grey on --ink now reads warm. */
#call-state { color: rgba(255, 255, 255, 0.66); }

#keypad { display: grid; grid-template-columns: repeat(6, auto); gap: 4px; }

#keypad button {
  border: 1px solid rgba(255, 255, 255, 0.28);
  background: none;
  color: #fff;
  border-radius: 4px;
  width: 30px;
  padding: 3px 0;
}

.danger {
  display: flex;
  align-items: center;
  gap: 7px;
  flex: 0 0 auto;
  border: 0;
  border-radius: 999px;
  padding: 7px 14px 7px 12px;
  background: var(--stop);
  color: #fff;
  font-weight: 500;
}

.danger svg { flex: 0 0 15px; width: 15px; height: 15px; }

/* ---------- sign in ---------- */

body.centered {
  display: grid;
  place-items: center;
  overflow: auto;
  background: var(--leads);
}

.card {
  display: grid;
  gap: 10px;
  width: 300px;
  padding: 28px;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 8px;
}

.card h1 { font-size: 20px; font-weight: 500; margin: 0 0 6px; }

.card input {
  font: inherit;
  padding: 9px 11px;
  border: 1px solid var(--line);
  border-radius: 4px;
}

.card button {
  border: 0;
  border-radius: 4px;
  padding: 10px;
  background: var(--brand-grad);
  color: #fff;
  font-weight: 500;
}

.error { color: var(--ink); font-size: 13px; }

/* ---------- transcript panel ---------- */

/* Never hidden, only inert: a panel added to the page and given a transform in
   the same frame arrives without animating, and the class alone avoids the
   requestAnimationFrame dance that would otherwise be needed to slide it in. */
#transcript { position: fixed; inset: 0; z-index: 40; }
#transcript[inert] { pointer-events: none; }

.transcript-shade {
  position: absolute;
  inset: 0;
  background: rgba(14, 23, 38, 0.38);
  opacity: 0;
  transition: opacity var(--motion);
}

/* Half the company pane, which is 66% of the window. The bounds keep it readable
   on a laptop and stop it running to a column of two words on a wide display. */
.transcript-panel {
  position: absolute;
  top: 0;
  right: 0;
  height: 100%;
  width: 33vw;
  min-width: 380px;
  max-width: 560px;
  display: flex;
  flex-direction: column;
  background: var(--detail);
  border-left: 1px solid var(--line);
  transform: translateX(100%);
  transition: transform var(--motion);
}

#transcript.open .transcript-shade { opacity: 1; }
#transcript.open .transcript-panel { transform: none; }

/* The head stays put while the words scroll under it: which call this is and the
   control to replay it are what a broker reaches for from anywhere in a long
   transcript. */
.transcript-head {
  flex: none;
  padding: 20px 24px 16px;
  border-bottom: 1px solid var(--line-soft);
}

.transcript-head .entry-when { display: block; margin-bottom: 4px; }
.transcript-head .block-head { margin: 0; }
.transcript-head audio { width: 100%; height: 34px; margin-top: 12px; }

.transcript-close {
  position: absolute;
  top: 14px;
  right: 16px;
  border: 0;
  background: none;
  padding: 4px;
  line-height: 1;
  font-size: 20px;
  color: var(--muted);
  cursor: pointer;
}

.transcript-close:hover { color: var(--ink); }

.transcript-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 16px 24px 32px;
}

/* The summary is the whole call in a few lines, so it reads before the transcript
   rather than as one more block inside it. */
.transcript-summary { margin: 0 0 18px; }

/* Every line is a seek control, so it fills the width and is styled as text
   rather than as a row of buttons — the click is a convenience on top of
   reading, not the reason the panel is open. */
.transcript-line {
  display: block;
  width: 100%;
  border: 0;
  background: none;
  padding: 6px 8px;
  margin-left: -8px;
  border-radius: 5px;
  font: inherit;
  text-align: left;
  color: inherit;
  cursor: pointer;
}

.transcript-line:hover { background: var(--hover); }

/* Who said it, above the words: the two speakers alternate every few lines, and a
   name in the margin would set the whole transcript in a narrow column. */
.transcript-who {
  display: block;
  font-size: 12px;
  font-weight: 600;
  color: var(--muted);
}

/* The broker's own turns recede — a broker rereads a call for what the other side
   said, and already knows what he said himself. */
.transcript-line.mine .transcript-who { color: #93a0b8; }
.transcript-line.mine { color: var(--muted); }

/* Play and Show transcript sit side by side under a call. */
.play + .play { margin-left: 7px; }

/* ---------- the call that was clicked ---------- */

/* The Calls list holds a row per call, so opening the company answers only half
   the question. These two say which one: the flash to find it, the weight to keep
   it found. All three parts are --muted normally, so this is a real jump. */
.entry.found .entry-when,
.entry.found .block-head,
.entry.found .entry-text {
  color: var(--ink);
  font-weight: 600;
}

/* Carried on a box-shadow spread rather than padding, so nothing reflows mid
   animation — the record is a scrolling box and a row changing height under a
   smooth scroll lands it somewhere else. */
/* Background alone: anything touching the box would shift a row's height under a
   scroll still running. */
@keyframes entry-found {
  0%   { background: transparent; }
  30%  { background: var(--brand-tint); }
  100% { background: transparent; }
}

.entry.pulse { animation: entry-found 0.9s ease-out; }

/* The weight alone still says which call it is, so nothing is lost by holding
   still — the same trade as the call icon's bars. */
@media (prefers-reduced-motion: reduce) {
  .entry.pulse { animation: none; }
}

/* ---------- the post-call wrap-up ---------- */

/* Inert rather than hidden, for the same reason the transcript is — see
   #transcript. Below it, not above: the transcript panel is opened from inside
   this popup and has to land in front of the thing that opened it. */
#wrapup { position: fixed; inset: 0; z-index: 30; }
#wrapup[inert] { pointer-events: none; }

/* Darker than the transcript's shade, which lies over a page still being read.
   This one is a stop: nothing behind it is worth looking at for four seconds. */
.wrapup-shade {
  position: absolute;
  inset: 0;
  background: rgba(14, 23, 38, 0.5);
  opacity: 0;
  transition: opacity var(--motion);
}

.wrapup-panel {
  position: absolute;
  top: 50%;
  left: 50%;
  width: min(560px, calc(100vw - 32px));
  max-height: calc(100vh - 48px);
  display: flex;
  flex-direction: column;
  border-radius: 16px;
  background: var(--detail);
  box-shadow: 0 24px 64px rgba(16, 24, 40, 0.28);
  /* The 6px is the whole entrance: a modal that flies in costs more of the four
     seconds than it is worth. */
  transform: translate(-50%, calc(-50% + 6px));
  opacity: 0;
  transition: opacity var(--motion), transform var(--motion);
}

#wrapup.open .wrapup-shade { opacity: 1; }
#wrapup.open .wrapup-panel { opacity: 1; transform: translate(-50%, -50%); }

.wrapup-head {
  flex: none;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 18px 24px 14px;
  border-bottom: 1px solid var(--line-soft);
}

.wrapup-head strong { font-size: 16px; }

/* What the call is still turning into, in the corner it left free. Silent until
   there is something to say, and a link once there is. */
.wrapup-work {
  display: flex;
  align-items: center;
  gap: 6px;
  border: 0;
  background: none;
  padding: 0;
  color: var(--muted);
  font-size: 13px;
  white-space: nowrap;
}

.wrapup-work:disabled { cursor: default; }
.wrapup-work.live { color: var(--brand-dk); }
.wrapup-work.live:hover { text-decoration: underline; }

.spinner {
  width: 11px;
  height: 11px;
  border: 1.5px solid var(--line);
  border-top-color: var(--muted);
  border-radius: 50%;
  animation: spin 0.7s linear infinite;
}

@keyframes spin { to { transform: rotate(360deg); } }

.wrapup-body {
  flex: 1;
  overflow-y: auto;
  padding: 16px 24px 20px;
}

.wrapup-label {
  display: block;
  margin-bottom: 8px;
  color: var(--muted);
  font-size: 13px;
}

/* The one thing the popup will not let a broker skip, so it is the one thing that
   turns red when they try. One pulse per attempt, not a loop: a control that keeps
   flashing is ignored by the second call of the day. */
.wrapup-ask.nag {
  color: var(--stop);
  animation: wrapup-nag 0.5s ease-out;
}

@keyframes wrapup-nag {
  0%, 100% { transform: translateX(0); }
  25%      { transform: translateX(-4px); }
  75%      { transform: translateX(4px); }
}

/* The weight and the colour still say it, so nothing is lost by holding still. */
@media (prefers-reduced-motion: reduce) {
  .wrapup-ask.nag { animation: none; }
}

/* What a call nobody answered has to say, in place of the buttons an answered one
   offers. */
.wrapup-said { margin: 0 0 18px; }
.wrapup-said strong { font-weight: 500; }

.wrapup-statuses {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 18px;
}

.wrapup-status {
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 8px 16px;
  background: none;
  color: var(--muted);
  font-size: 14px;
}

.wrapup-status:hover { border-color: var(--ink); color: var(--ink); }

/* The colour comes from the status ramp, alongside the chip and the menu row that
   show the same value elsewhere on the page. */
.wrapup-status[aria-pressed='true'] {
  border-color: transparent;
  color: #fff;
  font-weight: 500;
}

.wrapup-more { display: flex; align-items: center; gap: 6px; }

/* Anchored out of the flex flow, so opening it cannot reflow the row it hangs
   from — the same anchoring the status menu on the company page uses. */
.wrapup-more-menu {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 5;
  min-width: 150px;
  padding: 6px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: #fff;
  box-shadow: 0 8px 24px rgba(16, 24, 40, 0.12);
}

.wrapup-note {
  display: block;
  width: 100%;
  margin-bottom: 18px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--detail);
  color: var(--ink);
  font: inherit;
  resize: vertical;
}

.wrapup-note:focus { outline: none; border-color: var(--brand); }

.wrapup-when {
  position: relative;
  display: flex;
  align-items: center;
  gap: 8px;
}

.wrapup-when .wrapup-label { margin: 0; }

.wrapup-day {
  display: flex;
  align-items: center;
  gap: 6px;
  border: 0;
  background: none;
  padding: 4px 0;
  color: var(--ink);
}

.wrapup-day:hover { color: var(--brand-dk); }

/* Opens upward out of the footer, which is the one part of the popup that cannot
   scroll away — see drawWrapup for why it is not hung from the button itself. */
.wrapup-cal {
  top: auto;
  bottom: calc(100% + 10px);
  left: 24px;
  z-index: 6;
}

.wrapup-run {
  position: relative;
  flex: none;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 14px 24px 18px;
  border-top: 1px solid var(--line-soft);
}

/* The countdown and the way forward are one control: the fill is the wait, and
   pressing it is skipping the wait. A separate "go" button beside a bar would be
   two things saying one. */
.wrapup-tube {
  position: relative;
  flex: 1;
  overflow: hidden;
  border: 0;
  border-radius: 999px;
  padding: 11px 18px;
  background: var(--brand-tint);
  color: var(--brand-dk);
  text-align: center;
  font-weight: 500;
}

.wrapup-tube span { position: relative; z-index: 1; }
.wrapup-tube strong { font-weight: 600; }

/* Paused rather than removed when the broker asks for time, so the fill freezes
   where it stands; held runs it to full, which is the tube saying it is waiting on
   a press rather than on the clock. */
.wrapup-fill {
  position: absolute;
  inset: 0;
  transform-origin: left;
  transform: scaleX(0);
  background: rgba(77, 124, 243, 0.28);
  animation: wrapup-count var(--wrapup-seconds) linear forwards;
  animation-play-state: paused;
}

.wrapup-panel.counting .wrapup-fill { animation-play-state: running; }
.wrapup-panel.held .wrapup-fill { animation: none; transform: scaleX(1); }

@keyframes wrapup-count {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

.wrapup-go {
  border: 0;
  border-radius: 999px;
  padding: 10px 22px;
  background: var(--brand-dk);
  color: #fff;
  font-weight: 500;
}

.wrapup-go:hover { filter: brightness(1.08); }

/* The lead the wrap-up moved on to, and the only thing on that page worth doing.
   A pseudo-element rather than a box-shadow, and for the same reason the header's
   rule is one: a shadow takes a single colour, and this has to be the button's own
   gradient. It sits behind the button's background and grows out from under it.
   `inset` rather than a scale, so a wide pill widens by as much as it heightens. */
.call-button { position: relative; }

.call-button.queued::after {
  content: '';
  position: absolute;
  z-index: -1;
  border-radius: inherit;
  background: var(--brand-grad);
  animation: call-queued 1.6s ease-out infinite;
}

@keyframes call-queued {
  from { inset: 0;     opacity: 0.6; }
  to   { inset: -18px; opacity: 0; }
}

/* Decorative, unlike the wrap-up's countdown — the button is already the one
   filled control on the page — so it holds still when motion is unwanted. */
@media (prefers-reduced-motion: reduce) {
  .call-button.queued::after { animation: none; }
}
