/* ==========================================================================
   Client detail — page styles

   The tabs, tables, cards and dialogs on this page are all shared components
   from app.css. What lives here is the two things the page adds: the menu a
   row or a note card opens, and how a finished task reads.
   ========================================================================== */

/* ---------- Row / card action menu ----------------------------------------
   The trigger sits inside a `.table-wrap`, which scrolls and therefore clips —
   an absolutely-positioned menu would be cut off by the very box it opens in.
   So the menu is `position: fixed` and placed against the button's own screen
   rectangle when it opens, and closed again on any scroll: a fixed menu left
   behind by a scrolling table is a menu floating beside nothing. */
.row-menu { position: relative; display: inline-flex; }
.row-menu__btn {
  width: 24px; height: 24px; padding: 0; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  border: 0; border-radius: 5px;
  background: transparent; color: var(--color-text-subtle); cursor: pointer;
  transition: background 120ms ease, color 120ms ease;
}
.row-menu__btn:hover,
.row-menu.open .row-menu__btn { background: var(--color-primary-50); color: var(--color-primary-700); }
.row-menu__btn svg { width: 16px; height: 16px; }
.row-menu .dropdown-menu { position: fixed; top: auto; right: auto; min-width: 178px; }
.row-menu.open .dropdown-menu { display: block; }
.row-menu .dropdown-item { width: 100%; background: transparent; border: 0; font: inherit; text-align: left; }
.row-menu .dropdown-item svg { width: 15px; height: 15px; flex: none; }

/* ---------- A finished task -----------------------------------------------
   Struck through and stepped back, driven by the row's own state rather than
   written onto the cell — the checkbox flips one attribute and the row reads
   as done, which is the only way ticking and un-ticking can be symmetrical. */
#cd-task-body tr[data-done="true"] .main {
  text-decoration: line-through;
  color: var(--color-text-subtle);
}

/* ---------- Contacts ------------------------------------------------------
   The phone rides as the email's sub-line and takes the sub's quiet colour,
   which is stronger than the link colour it would otherwise carry. It is
   still a link, though, and a link with no hover is one nobody tries. */
#cd-contact-body .cell-stack a.sub:hover { color: var(--color-primary-700); }

/* ---------- Contact dialog -------------------------------------------------
   Its two paired rows are not two equal halves: a full name needs room a
   nickname does not, and an email address outruns a phone number. Set here
   rather than on `.form-row`, which is an even split everywhere else. */
#modal-contact .form-row > .field:first-child { flex: 1.7; }

/* ---------- Email composer -------------------------------------------------
   From and To are stated, not typed: the sender is the tenant's mail server and
   the recipient came from the row that was clicked. So they read as an envelope
   above the fields rather than as two more controls the user has to check. */
.cd-mailmeta {
  margin-bottom: 14px; padding: 10px 12px;
  background: var(--color-surface-alt);
  border: 1px solid var(--color-border);
  border-radius: 6px;
}
.cd-mailmeta__row { display: flex; gap: 10px; align-items: baseline; }
.cd-mailmeta__row + .cd-mailmeta__row { margin-top: 6px; }
.cd-mailmeta__k {
  flex: none; width: 42px;
  font-family: var(--font-mono); font-size: 12px; font-weight: 500;
  text-transform: uppercase; letter-spacing: 0.08em;
  color: var(--color-text-muted);
}
.cd-mailmeta__v { font-size: 13px; min-width: 0; overflow-wrap: anywhere; }

/* ---------- Edit Client form -----------------------------------------------
   The salesperson's consequence line sits under its own control, not above the
   next one, so it reads as part of that field. `.modal-tab-hint` carries a
   bottom margin for its usual job of introducing a tab. */
.ec-rep-hint { margin: 6px 0 0; }

/* A pinned note keeps the pin visible on the card, not only in its menu. */
.cd-note-pin { color: var(--color-warn); }
.cd-note-pin svg { width: 14px; height: 14px; vertical-align: -2px; }

/* ---------- Company Profile ------------------------------------------------
   Two blocks side by side: who the company is, and who you ring there. The
   terms that used to sit here are gone — Account Summary states all three a
   few inches to the right, and one fact printed twice on a screen is a fact
   that can end up disagreeing with itself. */

/* Name, address, website — stated, not labelled. Each announces what it is by
   its own shape, so a word set above it was a word to be stepped over. Read as
   an address block, so the lines sit close together. */
.cd-ident { display: flex; flex-direction: column; gap: 2px; font-size: 13px; line-height: 1.5; }
/* Below the card's own title (17px h2), not above it. The page title already
   names this client at the largest size on the screen, so the only new thing
   this line carries is the legal suffix — the name on the contract. It leads
   the address block; it does not compete with the heading for a second time. */
.cd-ident__legal {
  font-family: 'Schibsted Grotesk', system-ui, sans-serif;
  letter-spacing: -0.02em;
  font-size: 14px; font-weight: 600; line-height: 1.3;
  margin-bottom: 2px;
}

/* The primary contact. Avatar, then everything else in one column beside it —
   name, role, mail, phone — so the four lines read as one person rather than
   as two halves of a card that happen to be about the same one. */
.cd-primary { display: flex; align-items: flex-start; gap: 12px; }
.cd-primary__face { width: 38px; height: 38px; font-size: 13px; flex: none; }
.cd-primary__body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.cd-primary__name { font-size: 14px; font-weight: 600; }
.cd-primary__role { font-size: 12px; color: var(--color-text-muted); margin-bottom: 3px; }
.cd-primary__line { font-size: 13px; display: flex; align-items: center; gap: 6px; min-width: 0; }
.cd-primary__line svg { width: 14px; height: 14px; flex: none; color: var(--color-text-subtle); }
.cd-primary__when { color: var(--color-text-subtle); font-size: 12px; }

/* What the record holds, then what you actually call them. Set back from the
   full name so the pair reads as one name with a note, not as two names. */
.pref-name { color: var(--color-text-muted); font-weight: 400; }

/* Prose about the company, under the two blocks it describes. No label — a
   paragraph does not need a word above it saying it is a paragraph. */
.cd-desc { font-size: 13px; line-height: 1.55; }
/* The two blocks sit side by side and each takes the room it needs — the
   address decides the left, the contact decides the right. They stack under
   ~700px, where two of them abreast would break an email address in half. */
.cd-head { display: flex; align-items: flex-start; gap: 20px 40px; flex-wrap: wrap; }
.cd-head > * { flex: 1 1 280px; }
/* The mark is the one child that must NOT take a 280px share — it is a fixed chip, and the
   identity block beside it keeps the room. A shorter gap than the card's own 40px, because the
   mark and the name are one thing: the company. */
.cd-head > .cd-ident__mark {
  flex: none;
  width: 44px; height: 44px;
  font-size: 14px;
  margin-right: -24px;
}

/* ---------- Account Summary ------------------------------------------------
   LABEL RIGHT, VALUE LEFT, CLOSING ON THE MIDDLE. Label and value pushed to
   opposite edges of a card turn every row into a journey across white, and the
   wider the card the further apart the two halves of one fact end up. Meeting
   them in the centre keeps each label within a few characters of the thing it
   names while both sides still line up down the card.

   `justify-content: center` on the grid is what does it: the two columns take
   only the width their own content needs, and the pair is centred as a block
   rather than stretched to the edges. */
.acct {
  display: grid;
  grid-template-columns: auto auto;
  justify-content: center;
  align-items: center;
  gap: 11px 10px;
}
.acct__k { text-align: right; color: var(--color-text-muted); font-size: 13px; }
.acct__v { text-align: left; font-size: 13px; min-width: 0; }
.acct__v--rep { display: inline-flex; align-items: center; gap: 6px; }
.acct__v--rep .avatar { width: 22px; height: 22px; font-size: 9px; flex: none; }

/* The lock line belongs to the salesperson row above it, so it sits in the
   value column under the name rather than opening a label of its own. It says
   what the choice costs — assigning a rep is also what shuts the rest of the
   floor out. */
.acct-note {
  grid-column: 2;
  margin-top: -6px;
  font-size: 11px; line-height: 1.45;
  color: var(--color-text-subtle);
}
.acct-note svg { width: 11px; height: 11px; vertical-align: -1px; }
/* ---------- Contacts: boxes, not rows --------------------------------------
   Five columns of table made the reader carry a name across to a phone number
   and back, and the widest cell was an email address — the one value nobody
   scans down a column of. A contact is one person, so it is one box: face,
   name, what they do, and the two ways to reach them, in a block small enough
   to take in at once.

   The `+` is the last box in the grid rather than a button on the toolbar,
   which is how a warehouse is added — what you make appears where the things
   you already made are. It is deliberately narrower than a card: it is an
   action, not a contact, and a full-size dashed card reads as a person who
   failed to load. */
.ct-grid,
.wl-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(268px, 1fr));
  gap: 12px;
  align-items: start;
}
/* A box never sits on its parent's own colour. The card body underneath is
   white, so a white card on it is an outline with nothing inside — the border
   ends up doing all the work of saying "this is a thing". `--color-control` is
   the ground every other box on a white panel takes, and it follows all four
   themes (evening darkens it rather than bleaching the panel). */
.ct-card,
.wl-card {
  display: flex; flex-direction: column; gap: 5px;
  padding: 12px 13px;
  border: 1px solid var(--color-border); border-radius: 7px;
  background: var(--color-control);
}
.ct-card__top,
.wl-card__top { display: flex; align-items: flex-start; gap: 10px; }
.ct-card__face { width: 36px; height: 36px; font-size: 12px; flex: none; }
.ct-card__id,
.wl-card__id { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.ct-card__name,
.wl-card__name { font-size: 13px; font-weight: 600; }
.ct-card__role,
.wl-card__spec { font-size: 11px; color: var(--color-text-muted); }

/* One line each, and a long address is cut rather than allowed to widen every
   column in the grid — the full value is on the card's own dialog. */
.ct-card__line,
.wl-card__line {
  display: flex; align-items: center; gap: 6px;
  font-size: 12px; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ct-card__line svg,
.wl-card__line svg { width: 13px; height: 13px; flex: none; color: var(--color-text-subtle); }
.ct-card__foot { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 3px; }

/* An inactive contact is still on the record and still reachable, so the card
   is the same card — the badge says Inactive and the name steps back, and that
   is the whole signal. A paler ground was tried and removed: it put one card
   back on the panel's own colour, which is the thing every box here avoids,
   and it said a second time what the badge already said once. */
.ct-card[data-inactive] .ct-card__name { color: var(--color-text-muted); }

.ct-add-tile {
  width: 92px; min-height: 92px; align-self: stretch; justify-self: start;
  display: flex; align-items: center; justify-content: center;
  border: 1.5px dashed var(--color-border-strong); border-radius: 7px;
  background: transparent; color: var(--color-text-subtle);
  cursor: pointer; font: inherit;
  transition: border-color 120ms, color 120ms;
}
.ct-add-tile:hover { border-color: var(--color-primary); color: var(--color-primary); }
.ct-add-tile svg { width: 34px; height: 34px; stroke-width: 2.4; }

/* ---------- Wish list: the same box as a contact ----------------------------
   A wished-for product is one thing the way a contact is one person, so it
   takes the contact's box — the rules above are shared, not copied, and any
   change to one moves both.

   What differs is the picture. A contact's face is a 36px square because a
   face is recognised at any size; a stone is only recognised by its veining,
   and the 61px strip the table used showed a smear of colour. Here it is the
   first thing listed and the reason the box beats the row, so it gets the
   width to actually read as that material. */
.wl-card .thumb { width: 78px; height: 46px; }

/* The name links to the product, and takes the link colour without the
   underline — the whole card is already a target-shaped thing. */
a.wl-card__name { color: var(--color-primary-700); text-decoration: none; }
a.wl-card__name:hover { color: var(--color-primary); }

/* ---------- Activity tab -------------------------------------------------
   The Activity workspace's own rules, carried onto this tab so the two read as
   one control: `activity-page.component.css` in the portal app is where they
   are maintained, and anything changed there belongs here too. The surface-wide
   half — `.facet`, `.facet-opt`, `.facet-count`, `.activity-chips`, `.act-th`,
   `.act-arrow`, `.activity-empty` — is already in `css/app.css` and is not
   repeated. What is left is the custom-range row and the change lines, which
   until now lived only in the Angular component and in `activity.html`'s own
   page-local <style>. */
.activity-custom {
  display: flex; align-items: flex-end; gap: 10px;
  margin: 2px 0 10px; padding: 10px 12px;
  border: 1px solid var(--color-border); border-radius: var(--radius-md);
  background: var(--color-surface-2, var(--color-surface));
}

/* Before → after, one line per changed field. Only the first shows until "+N
   more" is pressed, so a multi-field edit cannot blow the row height out; the
   cell's own title carries the full set either way. */
.act-change { display: flex; flex-direction: column; gap: 1px; }
.act-change__line { display: block; }
.act-change__field { color: var(--color-text-subtle); margin-right: 4px; }
.act-sep { color: var(--color-text-subtle); }
.act-more {
  align-self: flex-start; margin-top: 1px; padding: 0; border: 0; background: none;
  color: var(--color-primary-700); font: inherit; cursor: pointer; text-decoration: underline;
}

/* The workspace's own column weights, for the four it shows by default. Keyed by
   the column's name, never its position. */
#tbl-flex-2 :is(th, td).col-when { width: 9%; }
#tbl-flex-2 :is(th, td).col-client { width: 20%; }
#tbl-flex-2 :is(th, td).col-action { width: 24%; }
/* By Who is a name and nothing else — no avatar, no second line — so it takes
   the width the avatar used to need away from Action, which carries prose.
   Being last, it right-aligns on app.css's own rule; nothing to add here. */
#tbl-flex-2 :is(th, td).col-who { width: 12%; }

/* ---- Quotes tab --------------------------------------------------------
   Column weights for what this client raised. Source is a tag column of three
   known words, so it takes what the longest one needs and leaves the rest to
   the quote, which carries a number and a project name on two lines. */
.cd-est-table :is(th, td).col-est    { width: 32%; }
.cd-est-table :is(th, td).col-source { width: 15%; }
.cd-est-table :is(th, td).col-valid  { width: 14%; }
.cd-est-table :is(th, td).col-status { width: 22%; }
.cd-est-table :is(th, td).col-total  { width: 17%; }
/* Lucide's 24px default would set the row height from inside a tag. */
.cd-est-table .col-source .badge svg { width: 12px; height: 12px; }

/* ---- Products Viewed tab --------------------------------------------------
   When, then what — and the minutes riding beside the thumbnail rather than at
   the row's far end, so a long look is read next to the material that held it.
   Four fixed-order tracks: a table would have stretched the last one across the
   card and put the number a page away from the picture. */
/* Two columns, because one row of this needs half a card and the other half was
   dead space. Filled down the first column and on into the second, which is what
   the running number says out loud — without it, two columns of dates read as two
   separate lists. */
.pv-list { column-count: 2; column-gap: 28px; }
.pv-row {
  break-inside: avoid;
  display: grid;
  grid-template-columns: 28px 104px auto minmax(0, 1fr);
  align-items: center; gap: 10px;
  padding: 9px 12px;
  position: relative;
  color: inherit; text-decoration: none;
  transition: background 120ms ease;
}
/* The place in the sequence. It has to carry the eye from the foot of one column
   to the head of the next, so it is read, not merely present — and the hash says
   it counts views rather than minutes, the other number on the row. */
.pv-row__n {
  font-family: 'Hanken Grotesk', sans-serif;
  font-size: 13px; font-weight: 700;
  color: var(--color-text-muted);
  text-align: right;
}
.pv-row__n::before { content: '#'; margin-right: 1px; color: var(--color-text-subtle); }
.pv-row:hover { background: #FAFAFE; }
.pv-row__when .main { font-size: 13px; }
/* How long they stayed on it is what this tab is for, so it leads and the clock
   falls in under it — a visit is found by its date, but a material is judged by
   the minutes. On its own ground so the pair reads as one figure, not two. */
.pv-row__spent {
  display: flex; flex-direction: column; align-items: center; gap: 1px;
  padding: 4px 8px;
  border-radius: 5px;
  background: var(--color-control);
}
.pv-row__m {
  font-family: 'Hanken Grotesk', sans-serif;
  font-size: 15px; font-weight: 700; line-height: 1.1;
  color: var(--color-text);
}
.pv-row__at { font-size: 11px; color: var(--color-text-muted); white-space: nowrap; }
/* The same short mark the tables draw between rows, inset to where the row's
   own text begins. */
.pv-row + .pv-row::before {
  content: ""; position: absolute; top: -1px; height: 1px; left: 12px; right: 12px;
  background: linear-gradient(to right,
    var(--color-border-strong) 0%,
    var(--color-border-strong) 35%,
    var(--color-border) 65%,
    transparent 100%);
}

/* ---- Price list, picked in place ------------------------------------------
   The value is the control: it reads as text until it is hovered, because a
   button drawn as a button here would be the loudest thing in a badge and in a
   row of plain account facts. The caret is what says it opens. */
/* The house .dropdown carries no positioning of its own, and the menu inside it
   is absolute — so the anchor has to be set here, opening from the left edge of
   the word it belongs to rather than the right. */
.pl-pick { position: relative; display: inline-flex; }
.pl-pick > .pl-menu { left: 0; right: auto; }
.pl-trigger {
  display: inline-flex; align-items: baseline; gap: 3px;
  padding: 0; border: 0; background: none;
  font: inherit; color: var(--color-primary-700); cursor: pointer;
}
.pl-trigger:hover { color: var(--color-primary); }
.pl-trigger svg { width: 13px; height: 13px; align-self: center; }
/* Inside the header badge the trigger takes the badge's own ink, not the link
   blue — a coloured word in a grey chip reads as a mistake. */
.badge .pl-trigger { color: inherit; }
.badge .pl-trigger svg { width: 11px; height: 11px; }
.pl-menu { min-width: 246px; }
/* Each row is the list and what it takes off base, because the discount is how
   these four are told apart. The check marks the one in force. */
.pl-opt { width: 100%; background: transparent; border: 0; font-family: inherit; font-size: 13px; text-align: left; }
.pl-opt__off { margin-left: auto; font-size: 11px; color: var(--color-text-muted); }
.pl-opt .pl-check { width: 15px; height: 15px; flex-shrink: 0; visibility: hidden; color: var(--color-primary-700); }
.pl-opt.active { color: var(--color-primary-700); font-weight: 500; }
.pl-opt.active .pl-check { visibility: visible; }
.pl-menu .dropdown-item svg { width: 15px; height: 15px; flex: none; }


/* =============================================================================
   THE CRM DASHBOARD, ON THE CLIENT RECORD

   The client half of the CRM module, brought onto the record it describes: the
   facts line, the chips that say what is live, and the eight cards that each
   hold a count and the top of a list. Copied from css/pages/crm.css rather
   than shared, because that file also carries the CRM page's own tables and
   two of their ids collide with this page's.
   ========================================================================== */
/* -----------------------------------------------------------------------------
   Today — seven cards, and nothing that scrolls

   A card is a question answered by a number, then the top of the list that
   number counts. The list itself is never on the dashboard: clicking a card
   slides it in from the right. That is the whole rule here, and it is what
   keeps the panel to one screen no matter how long any of the lists get.

   Three columns, not four. Four fitted the cards on one screen and then spent
   the saving on ellipses: at a quarter of the page every preview row cut its
   client off mid-name — "2 slabs Calacatta · Elite Ki…" — and a row you cannot
   finish reading is not a preview, it is a hint that you have to click. A third
   of the page carries the longest line any of these cards holds, so the third
   row of cards is the price and it is the right way round.
   -------------------------------------------------------------------------- */
.dgrid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; }
/* Six cards fill two rows of three exactly, so the seventh takes the last row
   whole rather than leaving a third of it empty — and two progress bars and an
   outcome line are the one thing here that reads better the wider it gets. */
.dcard--wide { grid-column: 1 / -1; }

.dcard {
  display: flex; flex-direction: column; gap: 7px;
  padding: 12px 14px 10px;
  background: var(--color-surface);
  border: 1px solid var(--color-border); border-radius: 7px;
  font: inherit; text-align: left; text-decoration: none; color: inherit;
  cursor: pointer;
  transition: border-color 120ms, box-shadow 120ms;
}
.dcard:hover { border-color: var(--color-primary); box-shadow: var(--shadow-sm); }
.dcard:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; }

.dcard__h { display: flex; align-items: center; gap: 8px; }
.dcard__h > svg { flex: none; width: 16px; height: 16px; color: var(--color-primary); }
.dcard__t { font-size: 13px; font-weight: 600; }
.dcard__go { flex: none; width: 15px; height: 15px; margin-left: auto; color: var(--color-text-subtle); }
.dcard:hover .dcard__go { color: var(--color-primary); }

/* Value first, then what it counts, tight — a number pushed to the far side of
   the card stops being read with its own label. */
.dcard__n { display: flex; align-items: baseline; gap: 7px; font-size: 13px; color: var(--color-text-muted); }
.dcard__v { font-size: 22px; font-weight: 700; line-height: 1; color: var(--color-text); }

/* The breakdown under the number is a sentence, not a row of chips. Boxing each
   fragment put three bordered pills in a card that already carries an icon, a
   figure and three preview rows, and once one of them was red and one amber the
   card was mostly decoration. They read as one muted line separated by dots,
   and the emphasis is carried by weight and by a single colour: red, and only
   for the thing that is actually late. */
.dcard__sub { display: flex; align-items: baseline; flex-wrap: wrap; gap: 2px 7px; font-size: 11px; color: var(--color-text-subtle); }
.dcard__flag { display: inline-flex; align-items: baseline; gap: 7px; }
.dcard__flag + .dcard__flag::before { content: "\00B7"; color: var(--color-text-subtle); opacity: 0.5; }
.dcard__flag--hot  { color: var(--color-danger); font-weight: 600; }
.dcard__flag--warn { color: var(--color-text-muted); font-weight: 600; }

/* Three rows at most, one line each. The preview is there to make the number
   concrete, not to be worked from. */
.dcard__list { display: flex; flex-direction: column; margin-top: 2px; }
.dcard__r { position: relative; display: flex; align-items: center; gap: 8px; min-width: 0; padding: 5px 0; font-size: 12px; }
.dcard__r + .dcard__r::before {
  content: ""; position: absolute; left: 0; top: 0;
  width: 50%; height: 1px; background: var(--color-border-strong);
}
.dcard__r .avatar { flex: none; width: 20px; height: 20px; font-size: 9px; }
.dcard__rn { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dcard__w { flex: none; width: 54px; font-size: 11px; font-weight: 600; color: var(--color-text-muted); }
/* Every card annotates its rows the same way — plain text at the right edge.
   To Call used filled badges here and was the only one that did, which is why
   it came out a different colour in every row: amber for a promise, red for a
   delay, green for a delivery. The reason is worth reading; three pills of
   three hues to say it is not. Red survives, for late and nothing else. */
.dcard__c { flex: none; font-size: 11px; font-weight: 600; color: var(--color-text-muted); }
.dcard__c--late { color: var(--color-danger); }
.dcard__r .badge { flex: none; }
.dcard__out { margin-top: 7px; font-size: 11px; line-height: 1.5; color: var(--color-text-subtle); }

/* Pinned to the bottom so six cards of different content still line up. */
.dcard__more { margin-top: auto; padding-top: 4px; font-size: 12px; font-weight: 600; color: var(--color-primary-700); }

/* -----------------------------------------------------------------------------
   The panels the cards open

   Same rows the dashboard previews, at full length, over the top of it. The
   dashboard keeps its scroll position and its shape while you read one.
   -------------------------------------------------------------------------- */
.so-list .slideover-head h2 { margin: 0; font-size: 17px; }
.so-list .slideover-foot { align-items: center; }
.so-list .slideover-foot .muted { font-size: 12px; }
.so-list .slideover-foot .spacer { flex: 1; }


.cd-link__who {
  padding: 1px 6px; border-radius: 5px;
  background: var(--color-surface-alt); border: 1px solid var(--color-border);
  font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--color-text-subtle);
}


/* -----------------------------------------------------------------------------
   Who this is

   Three rows above the cards and none of them a card: identity, then the
   numbers, then what is live. Together they are the five-second read the page
   was built for, and none of them costs a click.
   -------------------------------------------------------------------------- */
.cd-hero {
  display: grid;
  grid-template-columns: 52px minmax(0, 1fr) auto;
  gap: 3px 14px; align-items: center;
  margin-bottom: 10px;
}
.cd-hero__avatar { width: 52px; height: 52px; font-size: 18px; grid-row: span 2; }
.cd-hero__name { margin: 0; font-size: 21px; line-height: 1.15; }
.cd-hero__meta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 3px 0 0; }
.cd-hero__metatext { font-size: 12px; color: var(--color-text-muted); }
.cd-hero__do { grid-row: span 2; display: flex; gap: 8px; flex-wrap: wrap; }

/* The contact line. One row, because a rep needs the number, not a directory —
   the label each channel came with rides along as its tooltip. */
.cd-hero__reach {
  grid-column: 2; margin: 0;
  display: flex; align-items: center; flex-wrap: wrap; gap: 4px 16px;
  font-size: 13px; color: var(--color-text-muted);
}
.cd-reach { display: inline-flex; align-items: center; gap: 6px; }
.cd-reach > svg { width: 14px; height: 14px; flex: none; color: var(--color-primary); }
.cd-reach--who { color: var(--color-text); }
.cd-reach--who strong { font-weight: 600; }

/* The numbers, birthday first. A date of birth is a fact a rep should not have
   to open anything to see, so it leads the row and turns red on the day. */
.cd-facts {
  display: flex; align-items: center; flex-wrap: wrap;
  padding: 10px 15px; margin-bottom: 8px;
  background: var(--color-surface);
  border: 1px solid var(--color-border); border-radius: 7px;
}
.cd-facts .cd-stat,
.cd-fact { padding: 0 16px; border-right: 1px solid var(--color-border-strong); }
.cd-facts > :first-child { padding-left: 0; }
.cd-facts > :last-child { border-right: 0; padding-right: 0; }

.cd-stat { display: inline-flex; align-items: baseline; gap: 6px; }
.cd-stat__v { font-size: 17px; font-weight: 700; color: var(--color-primary-700); }
.cd-stat__l { font-size: 13px; color: var(--color-text-muted); }

.cd-fact {
  display: inline-flex; align-items: baseline; gap: 6px;
  border-top: 0; border-bottom: 0; border-left: 0;
  background: transparent; font: inherit; cursor: pointer;
}
.cd-fact > svg { width: 15px; height: 15px; align-self: center; color: var(--color-primary); }
.cd-fact:hover .cd-stat__v, .cd-fact:hover .cd-stat__l { color: var(--color-primary); }
.cd-fact--bday.is-hot > svg { color: var(--color-danger); }
.cd-fact--bday.is-hot .cd-stat__v { color: var(--color-danger); }
.cd-fact--bday.is-hot .cd-stat__l { color: var(--color-danger); font-weight: 600; }
.cd-fact--gap > svg,
.cd-fact--gap .cd-stat__l { color: var(--color-text-subtle); }
.cd-fact--gap:hover .cd-stat__l { color: var(--color-primary); }

/* -----------------------------------------------------------------------------
   What is live right now

   One fact per chip and no advice in any of them. A birthday is a date, a
   ceiling is a number, and "never polished" is something the client said — all
   three are checkable. What to open with is not, so the page does not say.

   Every chip opens the card that owns the data behind it, which is why there is
   no body text: the explanation is one click away in the place it belongs.
   -------------------------------------------------------------------------- */
.cd-sig { display: flex; align-items: center; flex-wrap: wrap; gap: 7px; margin-bottom: 12px; }
.cd-sig__c {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 5px 11px; border-radius: 7px;
  background: var(--color-control); border: 1px solid var(--color-control-border);
  font: inherit; font-size: 12px; font-weight: 600; color: var(--color-text);
  cursor: pointer;
}
.cd-sig__c > svg { width: 14px; height: 14px; flex: none; }
.cd-sig__c:hover { border-color: var(--color-primary); }
.cd-sig__c--hot     { background: var(--color-danger-50);  border-color: #FECACA; color: #B91C1C; }
.cd-sig__c--warm    { background: var(--color-warn-50);    border-color: #FDE68A; color: #B45309; }
.cd-sig__c--info    { background: var(--color-primary-50); border-color: #D6E2FA; color: var(--color-primary-700); }
/* A caution is a constraint, not an alarm — it reads as a rule, in grey. */
.cd-sig__c--careful { background: var(--color-surface-alt); border-color: var(--color-border-strong); color: var(--color-text-muted); }
.cd-sig__c--hot:hover, .cd-sig__c--warm:hover,
.cd-sig__c--info:hover, .cd-sig__c--careful:hover { border-color: currentColor; }

/* -----------------------------------------------------------------------------
   Likes and doesn't like — one column inside its panel, not two side by side
   -------------------------------------------------------------------------- */
.cd-taste__h {
  display: flex; align-items: center; gap: 7px; flex-wrap: wrap;
  margin-bottom: 9px;
  font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em;
}
.cd-taste__h svg { width: 14px; height: 14px; }
.cd-taste__h--like { color: var(--color-success); }
.cd-taste__h--dislike { color: var(--color-danger); }
.crm-hardex {
  font-size: 10px; font-weight: 600; letter-spacing: 0.04em; text-transform: none;
  color: #B91C1C; background: var(--color-danger-50); border-radius: 5px; padding: 1px 5px;
}

/* -----------------------------------------------------------------------------
   Bits the panels share
   -------------------------------------------------------------------------- */
.cd-sub-h {
  display: flex; align-items: center; gap: 7px;
  margin: 0 0 8px; font-size: 13px; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.05em; color: var(--color-text-muted);
}
.cd-sub-h svg { width: 14px; height: 14px; color: var(--color-primary); }
.crm-note { margin: 0; font-size: 13px; line-height: 1.6; color: var(--color-text-muted); }
.crm-foot-ico { width: 13px; height: 13px; vertical-align: -2px; }

/* -----------------------------------------------------------------------------
   Who you are talking to
   -------------------------------------------------------------------------- */
.cd-people { display: flex; flex-direction: column; }
.cd-person {
  position: relative; display: flex; gap: 12px; padding: 12px 0;
}
.cd-person:first-child { padding-top: 0; }
.cd-person:not(:last-child)::after {
  content: ""; position: absolute; left: 0; bottom: 0;
  width: 50%; height: 1px; background: var(--color-border-strong);
}
.cd-person .avatar { width: 38px; height: 38px; flex: none; }
.cd-person__t { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.cd-person__top { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
.cd-person__name { font-size: 14px; font-weight: 700; }
.cd-person__role { font-size: 12px; color: var(--color-text-muted); }
.cd-person__dob {
  display: inline-flex; align-items: center; gap: 6px; align-self: flex-start;
  padding: 2px 8px; border-radius: 7px;
  background: var(--color-warn-50); color: #92400E; font-size: 12px;
}
.cd-person__dob { font-size: 13px; }
.cd-person__dob svg { width: 14px; height: 14px; }
.cd-person__dob--gap { background: var(--color-surface-alt); color: var(--color-text-subtle); }
.cd-person__reach { font-size: 13px; color: var(--color-text-muted); }
.cd-person__note { font-size: 14px; line-height: 1.6; }

.cd-personal { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.cd-personal li {
  position: relative; padding: 9px 0 9px 20px;
  font-size: 14px; line-height: 1.6;
}
.cd-personal li::before {
  content: ""; position: absolute; left: 6px; top: 15px;
  width: 5px; height: 5px; border-radius: 50%; background: var(--color-primary);
}
.cd-personal li:not(:last-child)::after {
  content: ""; position: absolute; left: 0; bottom: 0;
  width: 50%; height: 1px; background: var(--color-border-strong);
}

.cd-pref {
  display: flex; flex-direction: column; gap: 5px;
  padding: 11px 13px; border-radius: 7px;
  background: var(--color-control); border: 1px solid var(--color-control-border);
  border-left: 3px solid var(--color-success);
}
.cd-pref--dislike { border-left-color: var(--color-danger); }
.cd-pref__top { display: flex; align-items: center; gap: 10px; }
.cd-pref__what { font-size: 14px; font-weight: 700; }
.cd-pref__why { font-size: 13px; line-height: 1.55; color: var(--color-text-muted); }
/* Provenance is a footnote, not a heading — it was shouting in caps. */
.cd-pref__src { font-size: 11px; color: var(--color-text-subtle); }

/* Strength, 1 to 5 — the unset marks stay hollow. */
.crm-str { display: inline-flex; gap: 3px; flex: none; }
.crm-str i { width: 8px; height: 8px; border-radius: 50%; background: var(--color-success); display: inline-block; }
.crm-str i.off { background: transparent; box-shadow: inset 0 0 0 1px var(--color-border-strong); }
.cd-pref--dislike .crm-str i { background: var(--color-danger); }
.cd-pref--dislike .crm-str i.off { background: transparent; }

/* -----------------------------------------------------------------------------
   What they keep looking at
   -------------------------------------------------------------------------- */
.cd-looks { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.cd-look {
  position: relative; display: grid;
  grid-template-columns: 28px minmax(0, 1fr); gap: 4px 10px; align-items: start;
  padding: 10px 0;
}
.cd-look:first-child { padding-top: 0; }
.cd-look:not(:last-child)::after {
  content: ""; position: absolute; left: 0; bottom: 0;
  width: 50%; height: 1px; background: var(--color-border-strong);
}
.cd-look__ico {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; border-radius: 7px;
  background: var(--color-primary-50); color: var(--color-primary-700);
}
.cd-look__ico svg { width: 14px; height: 14px; }
.cd-look__t { font-size: 14px; line-height: 1.55; }
/* Auto-placement would drop the meta line into the 28px icon column on row 2. */
.cd-look__meta { grid-column: 2; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.cd-look__tag {
  flex: none; padding: 2px 7px; border-radius: 5px;
  background: var(--color-surface-alt); border: 1px solid var(--color-border);
  font-size: 11px; color: var(--color-text-muted); white-space: nowrap;
}
.cd-look__when { flex: none; font-size: 11px; color: var(--color-text-subtle); white-space: nowrap; }

/* -----------------------------------------------------------------------------
   Last conversations
   -------------------------------------------------------------------------- */
.cd-talks { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.cd-talk { position: relative; display: flex; flex-direction: column; gap: 4px; padding: 11px 0; }
.cd-talk:first-child { padding-top: 0; }
.cd-talk:not(:last-child)::after {
  content: ""; position: absolute; left: 0; bottom: 0;
  width: 50%; height: 1px; background: var(--color-border-strong);
}
.cd-talk__head { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
.cd-talk__when { font-size: 13px; font-weight: 700; }
.cd-talk__kind {
  padding: 1px 7px; border-radius: 5px;
  background: var(--color-surface-alt); border: 1px solid var(--color-border);
  font-size: 11px; color: var(--color-text-muted);
}
.cd-talk__who { font-size: 11px; color: var(--color-text-subtle); }
.cd-talk__text { font-size: 14px; line-height: 1.6; }
.cd-talk__follow {
  display: inline-flex; align-items: center; gap: 6px; align-self: flex-start;
  padding: 3px 8px; border-radius: 7px;
  background: var(--color-warn-50); color: #92400E; font-size: 11px; font-weight: 600;
}
.cd-talk__follow svg { width: 13px; height: 13px; }

.cd-dot { width: 7px; height: 7px; border-radius: 50%; display: inline-block; }
.cd-dot--good { background: var(--color-success); }
.cd-dot--neutral { background: var(--color-text-subtle); }
.cd-dot--bad { background: var(--color-danger); }
.cd-dot--none { background: transparent; box-shadow: inset 0 0 0 1px var(--color-border-strong); }

/* -----------------------------------------------------------------------------
   What they have paid
   -------------------------------------------------------------------------- */
.crm-layer--gated {
  display: inline-flex; align-items: center; gap: 6px; flex: none; align-self: flex-start;
  padding: 4px 9px; border-radius: 7px;
  background: var(--color-warn-50); color: #B45309;
  font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em;
}
.crm-layer--gated svg { width: 13px; height: 13px; }
.crm-masked { color: var(--color-text-subtle); }
.crm-above { display: inline-flex; align-items: center; gap: 4px; font-size: 11px; font-weight: 600; color: var(--color-text-subtle); }
.crm-above svg { width: 12px; height: 12px; }
.crm-row--masked td { background: var(--color-surface-alt); }

.cd-tbl-prices .col-date { width: 13%; }
.cd-tbl-prices .col-mat  { width: 36%; }
.cd-tbl-prices .col-qty  { width: 12%; }
.cd-tbl-prices .col-list { width: 11%; }
.cd-tbl-prices .col-paid { width: 11%; }
.cd-tbl-prices .col-disc { width: 17%; }

/* -----------------------------------------------------------------------------
   Dates that matter
   -------------------------------------------------------------------------- */
.cd-dates { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.cd-date {
  position: relative; display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  padding: 10px 0;
}
.cd-date:first-child { padding-top: 0; }
.cd-date:not(:last-child)::after {
  content: ""; position: absolute; left: 0; bottom: 0;
  width: 50%; height: 1px; background: var(--color-border-strong);
}
.cd-date__l { font-size: 14px; font-weight: 600; }
.cd-date__w { font-size: 13px; color: var(--color-text-muted); }
.cd-date__d {
  margin-left: auto; flex: none;
  font-size: 11px; font-weight: 600; color: var(--color-text-subtle); white-space: nowrap;
}
.cd-date--hot .cd-date__d {
  padding: 2px 8px; border-radius: 7px;
  background: var(--color-danger-50); color: #B91C1C;
}
.cd-date--gap .cd-date__l { color: var(--color-text-subtle); font-weight: 500; }
.cd-date__add {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 0; border: 0; background: transparent; cursor: pointer;
  font: inherit; font-size: 11px; color: var(--color-primary-700);
}
.cd-date__add:hover { color: var(--color-primary); font-weight: 600; }
.cd-date__add svg { width: 12px; height: 12px; }

/* -----------------------------------------------------------------------------
   What they buy
   -------------------------------------------------------------------------- */
.cd-bars { display: flex; flex-direction: column; gap: 7px; }
.cd-bar { display: grid; grid-template-columns: minmax(0, 96px) 1fr 38px; gap: 10px; align-items: center; }
.cd-bar__k { font-size: 13px; color: var(--color-text); }
.cd-bar__t { height: 7px; border-radius: 5px; background: var(--color-surface-alt); overflow: hidden; }
.cd-bar__t > span { display: block; height: 100%; background: var(--color-primary); border-radius: 5px; }
.cd-bar__p { font-size: 11px; color: var(--color-text-muted); text-align: right; }

.cd-stats { display: flex; flex-direction: column; gap: 8px; }
.cd-stats .cd-stat { align-items: baseline; }
.cd-stats .cd-stat__l { line-height: 1.45; }

.cd-tentative {
  display: flex; align-items: flex-start; gap: 8px;
  margin: 0 0 12px; padding: 10px 12px; border-radius: 7px;
  background: var(--color-warn-50); border: 1px solid #FDE68A;
  font-size: 13px; line-height: 1.55; color: #92400E;
}
.cd-tentative svg { width: 15px; height: 15px; flex: none; margin-top: 1px; }

/* The honest empty state: no pattern is claimed, the orders are simply shown. */
.cd-thin {
  display: flex; align-items: flex-start; gap: 9px;
  margin-bottom: 12px; padding: 12px 13px; border-radius: 7px;
  background: var(--color-surface-alt); border: 1px dashed var(--color-border-strong);
  font-size: 13px; line-height: 1.6; color: var(--color-text-muted);
}
.cd-thin svg { width: 16px; height: 16px; flex: none; margin-top: 1px; }

.cd-raw { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.cd-raw li {
  position: relative; display: grid; grid-template-columns: 1fr auto; gap: 2px 10px;
  padding: 9px 0; font-size: 12px;
}
.cd-raw li:not(:last-child)::after {
  content: ""; position: absolute; left: 0; bottom: 0;
  width: 50%; height: 1px; background: var(--color-border-strong);
}
.cd-raw__d { font-weight: 600; }
.cd-raw__v { text-align: right; font-weight: 600; }
.cd-raw__m { grid-column: 1 / -1; color: var(--color-text-muted); }
.cd-raw__q { grid-column: 1 / -1; color: var(--color-text-subtle); font-size: 11px; }

/* -----------------------------------------------------------------------------
   What we have for them
   -------------------------------------------------------------------------- */
.cd-offers { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.cd-offer {
  display: grid; grid-template-columns: 46px minmax(0, 1fr); gap: 12px; align-items: start;
  padding: 11px 12px; border-radius: 7px;
  background: var(--color-surface); border: 1px solid var(--color-border);
}
.cd-offer--out { background: var(--color-surface-alt); border-style: dashed; }
.cd-offer__score { display: flex; flex-direction: column; align-items: center; gap: 5px; }
.cd-offer__score svg { width: 17px; height: 17px; color: var(--color-danger); }
.cd-offer__num { font-size: 18px; font-weight: 700; color: var(--color-primary-700); }
.cd-offer__meter { display: block; width: 100%; height: 5px; border-radius: 5px; background: var(--color-surface-alt); overflow: hidden; }
.cd-offer__meter > span { display: block; height: 100%; background: var(--color-primary); border-radius: 5px; }
.cd-offer__t { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.cd-offer__name { font-size: 14px; font-weight: 700; }
.cd-offer__why { font-size: 13px; line-height: 1.55; color: var(--color-text-muted); }
.cd-offer__avail { display: inline-flex; align-items: center; gap: 6px; font-size: 11px; color: var(--color-text-subtle); }

/* -----------------------------------------------------------------------------
   Bits borrowed by the cards
   -------------------------------------------------------------------------- */
/* The add control lives in the card header when the card is a list rather than
   a page with its own toolbar. */
.add-row-btn {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: 24px; height: 24px;
  border: 0; border-radius: 5px; cursor: pointer;
  background: var(--color-primary); color: #fff;
}
.add-row-btn:hover { background: var(--color-primary-600); }
.add-row-btn svg { width: 15px; height: 15px; stroke-width: 2.6; }

/* =============================================================================
   Responsive
   ========================================================================== */
@media (max-width: 1200px) {
  /* Two, for the same reason the base row is three: a third of this width is
     back under the length of a client's name. */
  .dgrid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 900px) {
  /* The actions drop under the identity rather than squeezing the name. */
  .cd-hero { grid-template-columns: 52px minmax(0, 1fr); }
  .cd-hero__do { grid-column: 2; grid-row: auto; margin-top: 6px; }
  /* The board stops being one row here, so a full-height column stops being a
     column and becomes a screen of its own. Each one keeps a ceiling instead
     and scrolls under it — five of these stacked is a page, not a board. */
  .wm-col { height: auto; max-height: 340px; }
}

@media (max-width: 768px) {
  .cs { max-width: none; }
  .ct-find .cs { flex-basis: 100%; max-width: none; }
  .dgrid { grid-template-columns: minmax(0, 1fr); }
  .dcard--wide { grid-column: auto; }
  .cd-hero { grid-template-columns: minmax(0, 1fr); }
  .cd-hero__avatar { display: none; }
  .cd-hero__reach,
  .cd-hero__do { grid-column: 1; }
  .cd-hero__do .btn { flex: 1; justify-content: center; }
  .cd-hero__name { font-size: 19px; }
  .page .card-header { flex-wrap: wrap; gap: 10px; }
  .cd-bar { grid-template-columns: minmax(0, 88px) 1fr 34px; }
  /* The countdown keeps its own line and the action goes beside it, so a
     three-line reason never squeezes the row it belongs to. */
  .ct-o { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 6px 12px; }
  .ct-o__clock { grid-column: 1; grid-row: 1; width: auto; }
  .ct-o > .btn { grid-column: 2; grid-row: 1; }
  .ct-o__t { grid-column: 1 / -1; grid-row: 2; }
  /* Touch targets — nothing tappable drops under 44px on a phone. */
  .page .btn,
  .page .fchip,
  .cs__row,
  .ct-s__c,
  .cd-sig__c,
  .ct-recent__chip { min-height: 44px; }
  .add-row-btn { width: 44px; height: 44px; }
  .cs__input { padding: 15px 0; }
}

@media (max-width: 480px) {
  /* The fact row stacks; the dividers between the numbers go with it. */
  .cd-facts { flex-direction: column; align-items: flex-start; gap: 9px; }
  .cd-facts .cd-stat,
  .cd-fact { padding: 0; border-right: 0; }
  .cd-stat__v { font-size: 16px; }
  .cd-person .avatar { display: none; }
  .ct-c__row .avatar { display: none; }
  .cd-offer { grid-template-columns: 38px minmax(0, 1fr); gap: 10px; }
}

/* =============================================================================
   Slide-overs

   Every dialog in this module is a panel. A dialog is a box that has to justify
   its height before it has said anything; a panel has the room, so a form can
   breathe and a list can be the length it is.
   ========================================================================== */
/* One width for the module, forms and lists alike — opening a list and then a
   form should not move the edge of the panel. */
.so-list .slideover { width: min(640px, 100%); }
/* The walkthrough is the one panel sized by its content rather than its form. */
.so-video .slideover { width: min(880px, 100%); }
.so-video .video-embed { margin: 0; }
/* A note under a form row is the row's own footnote, not the next row — it takes
   less than the 14px between rows, and more than nothing. */
.slideover-body :is(.field, .field-grid, .form-row, .fchips) + .crm-note { margin-top: 12px; }
/* Remember This swaps a whole group of fields in under the chips. The group is a
   plain div, so it is not a `.field` and the stacked rhythm above misses it. */
.slideover-body :is(.field, .field-grid, .form-row) + [data-rem-group] { margin-top: 14px; }

/* ===========================================================================
   THE CRM FRONT TAB
   Two lists and the terms. Both tables put their state column last and right,
   which is where the eye goes when it is scanning for what has moved. */
#tbl-crm-owed   { min-width: 420px; }
#tbl-crm-owed thead th.col-owed-due   { width: 16%; }
#tbl-crm-owed thead th.col-owed-state { width: 26%; }
#tbl-crm-owed .col-owed-state { text-align: right; white-space: nowrap; }

#tbl-crm-flight { min-width: 460px; }
#tbl-crm-flight thead th.col-fl-valid { width: 14%; }
#tbl-crm-flight thead th.col-fl-total { width: 20%; }
#tbl-crm-flight thead th.col-fl-state { width: 28%; }
#tbl-crm-flight .col-fl-state { text-align: right; white-space: nowrap; }
#tbl-crm-flight tbody tr { cursor: pointer; }

