/*
 * PBJ CRM — front-end portal styles (Phase 3, re-tokenised in Phase 7F-C).
 * Self-contained, no frameworks, no build step. Every class is prefixed
 * pbjcrm-. Same discipline as pbj-helpdesk 1.0.1: the portal paints its OWN
 * light surface so a dark theme can never leave our text unreadable.
 *
 * Colours, corners and spacing all come from --pbjcrm-* custom properties.
 * The values declared below are the DEFAULTS: the portal script writes the
 * owner's chosen theme onto the same element as inline custom properties, and
 * an inline property always wins over a stylesheet one, so the theme takes
 * over cleanly and a missing/partial theme simply leaves these standing.
 *
 * Where a colour is a tint of another (a pale red behind an error message,
 * say) the flat fallback is declared FIRST and the color-mix() version second.
 * A browser that has never heard of color-mix() drops the second line and
 * keeps the first, so nothing is ever left unpainted.
 */

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

.pbjcrm-portal,
.pbjcrm-login {
	/* Accent — the one colour the owner picks, and its family. */
	--pbjcrm-accent: #2271b1;
	--pbjcrm-accent-strong: #135e96;
	--pbjcrm-accent-soft: #eef3f8;
	--pbjcrm-accent-contrast: #ffffff;
	--pbjcrm-link: #2271b1;

	/* Page furniture. */
	--pbjcrm-bg: #ffffff;
	--pbjcrm-surface: #ffffff;
	--pbjcrm-surface-alt: #f6f7f8;
	--pbjcrm-border: #d8dce0;
	--pbjcrm-border-strong: #b4bcc3;
	--pbjcrm-shadow: rgba(0, 0, 0, 0.18);

	/* Type. */
	--pbjcrm-text: #1d2327;
	--pbjcrm-text-soft: #3c434a;
	--pbjcrm-muted: #646d76;

	/* The strip across the top. */
	--pbjcrm-header-bg: #f6f7f8;
	--pbjcrm-header-text: #1d2327;

	/* Status colours. */
	--pbjcrm-success: #1a7a3c;
	--pbjcrm-warning: #996800;
	--pbjcrm-danger: #b32d2e;

	/* Shape. */
	--pbjcrm-radius: 6px;
	--pbjcrm-radius-sm: 3px;
	--pbjcrm-radius-lg: 12px;

	/* Spacing and size. */
	--pbjcrm-space: 14px;
	--pbjcrm-space-sm: 7px;
	--pbjcrm-space-lg: 28px;
	--pbjcrm-row-height: 42px;
	--pbjcrm-font-size: 15px;

	color: var(--pbjcrm-text);
	font-size: var(--pbjcrm-font-size);
	line-height: 1.5;
	/* Jake, 2026-08-06: use the screen. It was pinned at 1080px, which wasted
	   half a desktop monitor on a screen that is mostly tables. It now grows
	   with the viewport and only stops when lines get too long to scan. */
	width: 100%;
	max-width: min(1600px, 100%);
	margin: 0 auto;
	/* The portal is a self-contained card regardless of the site's theme —
	   helpdesk 1.0.0 shipped without this and dark themes broke it. Light by
	   default; only the CRM's own theme may darken it. */
	background: var(--pbjcrm-bg);
	padding: var(--pbjcrm-space-lg);
	border-radius: var(--pbjcrm-radius);
	/* A hairline so the card still has an edge once it fills the screen.
	   Black on a light page, white on a dark one — see the dark rule below. */
	border: 1px solid rgba(0, 0, 0, 0.16);
}

/* The theme classes land on the portal element itself (the JS adds them to
   `app`), so the dark variant is a compound selector, not a descendant one. */
.pbjcrm-portal.pbjcrm-theme-dark,
.pbjcrm-login.pbjcrm-theme-dark {
	border-color: rgba(255, 255, 255, 0.22);
}

/* The canvas page (5.1). Only ever applies on the plugin's own template, so a
   portal still embedded in a theme page is untouched. The background is set
   SERVER-SIDE from the same theme tokens (see portal-canvas.php) rather than
   waiting for the JS to paint it — otherwise a dark-mode user gets a white
   flash on every load. */
/* Whatever the PAGE BUILDER wrapped our shortcode in.
 *
 * Stripping the theme's header and footer is not enough: the_content() still
 * emits the builder's own markup, and on a builder-made page that is a section
 * wrapper carrying its own white background, padding and rounded corners. On a
 * black canvas it showed up as a white frame around the portal — reported
 * 2026-08-06, and it is why "the theme is gone" was not the same as "the theme
 * is not painting anything".
 *
 * Written WITHOUT naming a theme, because the canvas has to hold on any of
 * them: `:has()` matches only ANCESTORS of the portal, so every wrapper between
 * the body and our card is flattened while the card itself — which does not
 * contain itself — keeps its background. The body is not matched either (the
 * selector is scoped to its descendants), so the page keeps its colour.
 */
body.pbj-crm-canvas *:has( > .pbjcrm-portal ),
body.pbj-crm-canvas *:has( > .pbjcrm-login ),
body.pbj-crm-canvas *:has( .pbjcrm-portal ),
body.pbj-crm-canvas *:has( .pbjcrm-login ) {
	background: transparent !important;
	border: 0 !important;
	box-shadow: none !important;
	padding: 0 !important;
	margin: 0 !important;
	max-width: none !important;
	width: auto !important;
}

/* With the theme's stylesheet dequeued the canvas has no typography of its
   own, so it states one. Deliberately a system stack: it is the one font list
   that exists on every machine, needs no network fetch, and cannot be changed
   out from under us by a theme update. */
body.pbj-crm-canvas {
	font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
	-webkit-font-smoothing: antialiased;
	margin: 0;
	min-height: 100vh;
	background: var(--pbjcrm-bg, #f0f0f1);
	padding: 14px;
	box-sizing: border-box;
}

.pbjcrm-portal *,
.pbjcrm-login * {
	box-sizing: border-box;
}

.pbjcrm-portal h2,
.pbjcrm-portal h3,
.pbjcrm-portal h4,
.pbjcrm-portal h5 {
	color: var(--pbjcrm-text);
}

.pbjcrm-card {
	background: var(--pbjcrm-surface);
	border: 1px solid var(--pbjcrm-border);
	border-radius: var(--pbjcrm-radius);
	padding: var(--pbjcrm-space) calc(var(--pbjcrm-space) + 4px);
	margin-bottom: var(--pbjcrm-space);
}

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

.pbjcrm-mono {
	font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
	font-size: 12px;
	color: var(--pbjcrm-muted);
	white-space: nowrap;
}

.pbjcrm-error {
	color: var(--pbjcrm-danger);
	background: #fcf0f1;
	background: color-mix(in srgb, var(--pbjcrm-danger) 8%, var(--pbjcrm-surface));
	border: 1px solid #f0c4c5;
	border-color: color-mix(in srgb, var(--pbjcrm-danger) 30%, var(--pbjcrm-surface));
	border-radius: var(--pbjcrm-radius);
	padding: var(--pbjcrm-space-sm) var(--pbjcrm-space);
}

.pbjcrm-empty {
	color: var(--pbjcrm-muted);
	background: var(--pbjcrm-surface-alt);
	border: 1px dashed var(--pbjcrm-border);
	border-radius: var(--pbjcrm-radius);
	padding: var(--pbjcrm-space) calc(var(--pbjcrm-space) + 2px);
	font-size: 14px;
}

.pbjcrm-loading {
	font-size: 14px;
}

.pbjcrm-noaccess h2 {
	margin-top: 0;
	font-size: 19px;
}

.pbjcrm-adminnote {
	font-size: 13px;
	background: var(--pbjcrm-surface-alt);
	border: 1px solid var(--pbjcrm-border);
	border-radius: var(--pbjcrm-radius);
	padding: var(--pbjcrm-space-sm) var(--pbjcrm-space);
}

.pbjcrm-subhead {
	margin: var(--pbjcrm-space) 0 var(--pbjcrm-space-sm);
	font-size: 14px;
	padding-bottom: 4px;
	border-bottom: 1px solid var(--pbjcrm-border);
}

.pbjcrm-section {
	margin-bottom: var(--pbjcrm-space-lg);
}

.pbjcrm-section > h3 {
	margin: 0 0 4px;
	padding-bottom: 6px;
	border-bottom: 2px solid var(--pbjcrm-border);
}

/* ------------------------------------------- Explainers ("How this works") */

.pbjcrm-explainer {
	background: var(--pbjcrm-surface-alt);
	border: 1px solid var(--pbjcrm-border);
	border-left: 4px solid var(--pbjcrm-accent);
	border-radius: var(--pbjcrm-radius);
	padding: var(--pbjcrm-space-sm) var(--pbjcrm-space);
	margin: 0 0 var(--pbjcrm-space);
	font-size: 14px;
	max-width: 780px;
}

.pbjcrm-explainer > summary {
	cursor: pointer;
	font-weight: 700;
	color: var(--pbjcrm-link);
	list-style-position: outside;
}

.pbjcrm-explainer p {
	margin: var(--pbjcrm-space-sm) 0 0;
	color: var(--pbjcrm-text-soft);
}

.pbjcrm-explainer-foot {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 8px;
	margin-top: var(--pbjcrm-space) !important;
	font-size: 12px;
}

/* --------------------------------------------------------------- Toasts */

/* ⛔ ALSO PORTALLED TO <body> (pbj-crm.js), so it carries the same obligation as
   the dialog above: state a base colour rather than inheriting the page's. The
   two toast rules below then override it with success/danger, and anything they
   do not paint — a nested span, a future second line — has somewhere sane to
   fall back to instead of the surrounding site's ink. */
.pbjcrm-toasts {
	position: fixed;
	right: 16px;
	bottom: 16px;
	z-index: 99995;
	color: var(--pbjcrm-text, #1d2327);
	display: flex;
	flex-direction: column;
	gap: 8px;
	max-width: min(360px, calc(100vw - 32px));
}

.pbjcrm-toast {
	border-radius: var(--pbjcrm-radius);
	padding: 10px 14px;
	font-size: 14px;
	line-height: 1.4;
	box-shadow: 0 4px 16px var(--pbjcrm-shadow);
	background: #edfaef;
	background: color-mix(in srgb, var(--pbjcrm-success) 10%, var(--pbjcrm-surface));
	border: 1px solid #b7e3c0;
	border-color: color-mix(in srgb, var(--pbjcrm-success) 32%, var(--pbjcrm-surface));
	/* ⛔ THE INK IS --pbjcrm-text, NOT --pbjcrm-success, AND THAT IS A MEASURED
	   CHOICE. Green-on-green measured 3.94:1 on Default and 4.32:1 on Soft
	   against this very tint, under the 4.5:1 a toast's sentence owes; the error
	   toast measured 4.36:1 on Dark. Reading the theme's own text colour on the
	   tint measures 9.29:1 at its worst across all eight presets. The tint and
	   the border still carry the meaning — which of the two toasts this is has
	   never depended on the ink. */
	color: #14532d;
	color: var(--pbjcrm-text, #1d2327);
}

.pbjcrm-toast-error {
	background: #fcf0f1;
	background: color-mix(in srgb, var(--pbjcrm-danger) 10%, var(--pbjcrm-surface));
	border-color: #f0c4c5;
	border-color: color-mix(in srgb, var(--pbjcrm-danger) 32%, var(--pbjcrm-surface));
	/* Ink stays --pbjcrm-text; only the fill and the border say which toast this
	   is. See the measurement on .pbjcrm-toast above. */
	color: #7f1d1d;
	color: var(--pbjcrm-text, #1d2327);
}

/* -------------------------------------------------------------- Buttons */

/* A BUTTON LOOKS LIKE A BUTTON (1.1.0).
   Jake: "a standard button and color for the default theme (not just text)".
   The plain button is now a real outlined control with a surface behind it,
   and .pbjcrm-btn-primary is filled with the accent. Colours come from the
   btn-* theme variables so a module can match without copying hex values. */
.pbjcrm-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 6px;
	border: 1px solid var(--pbjcrm-btn-soft-border, var(--pbjcrm-border-strong));
	background: var(--pbjcrm-btn-soft-bg, var(--pbjcrm-surface));
	color: var(--pbjcrm-btn-soft-text, var(--pbjcrm-text));
	border-radius: var(--pbjcrm-radius);
	padding: 8px 16px;
	font-size: 14px;
	font-weight: 600;
	line-height: 1.3;
	cursor: pointer;
	text-decoration: none;
	transition: background 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
}

.pbjcrm-btn:hover {
	background: var(--pbjcrm-btn-soft-hover-bg, var(--pbjcrm-surface-alt));
	border-color: var(--pbjcrm-border-strong);
	color: var(--pbjcrm-btn-soft-text, var(--pbjcrm-text));
}

/* One focus treatment for every control on the page. Derived from the accent,
   so it stays visible on all four presets including Dark. */
.pbjcrm-btn:focus-visible,
.pbjcrm-tab:focus-visible,
.pbjcrm-subnav-item:focus-visible,
.pbjcrm-pill:focus-visible,
.pbjcrm-input:focus-visible,
.pbjcrm-recordfield input:focus-visible,
.pbjcrm-recordfield textarea:focus-visible {
	outline: 2px solid var(--pbjcrm-focus-ring, var(--pbjcrm-accent));
	outline-offset: 2px;
}

.pbjcrm-btn-primary {
	background: var(--pbjcrm-btn-bg, var(--pbjcrm-accent));
	border-color: var(--pbjcrm-btn-border, var(--pbjcrm-accent));
	color: var(--pbjcrm-btn-text, var(--pbjcrm-accent-contrast));
}

.pbjcrm-btn-primary:hover {
	background: var(--pbjcrm-btn-hover-bg, var(--pbjcrm-accent-strong));
	border-color: var(--pbjcrm-btn-hover-bg, var(--pbjcrm-accent-strong));
	color: var(--pbjcrm-btn-hover-text, var(--pbjcrm-btn-text));
}

.pbjcrm-btn-primary:active {
	background: var(--pbjcrm-btn-active-bg, var(--pbjcrm-accent-strong));
	border-color: var(--pbjcrm-btn-active-bg, var(--pbjcrm-accent-strong));
	color: var(--pbjcrm-btn-active-text, var(--pbjcrm-btn-text));
}

.pbjcrm-btn-danger {
	color: var(--pbjcrm-danger);
	border-color: #e5b3b4;
	border-color: color-mix(in srgb, var(--pbjcrm-danger) 40%, var(--pbjcrm-surface));
}

.pbjcrm-btn-ghost {
	border-color: transparent;
	color: var(--pbjcrm-link);
	background: transparent;
}

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

.pbjcrm-btn-sm {
	padding: 5px 11px;
	font-size: 13px;
}

.pbjcrm-btn:disabled {
	background: var(--pbjcrm-action-disabled-bg, var(--pbjcrm-surface-alt));
	color: var(--pbjcrm-action-disabled-text, var(--pbjcrm-text-soft));
	border-color: var(--pbjcrm-action-disabled-border, var(--pbjcrm-edge));
	box-shadow: none;
	opacity: 1;
	cursor: not-allowed;
}

.pbjcrm-linkbtn {
	border: none;
	background: none;
	padding: 0;
	margin: 0;
	color: var(--pbjcrm-link);
	font: inherit;
	cursor: pointer;
	text-align: left;
}

.pbjcrm-linkbtn:hover {
	color: var(--pbjcrm-accent-strong);
	text-decoration: underline;
}

.pbjcrm-backlink {
	margin: 0 0 10px;
}

/* ---------------------------------------------------------------- Forms */

.pbjcrm-portal input[type="text"],
.pbjcrm-portal input[type="email"],
.pbjcrm-portal input[type="url"],
.pbjcrm-portal input[type="tel"],
.pbjcrm-portal input[type="number"],
.pbjcrm-portal input[type="date"],
.pbjcrm-portal input[type="search"],
.pbjcrm-portal input[type="password"],
.pbjcrm-portal select,
.pbjcrm-portal textarea,
.pbjcrm-login input[type="text"],
.pbjcrm-login input[type="password"] {
	width: 100%;
	max-width: 100%;
	padding: 7px 10px;
	border: 1px solid var(--pbjcrm-border);
	border-radius: var(--pbjcrm-radius-sm);
	background: var(--pbjcrm-surface);
	color: var(--pbjcrm-text);
	font-size: 14px;
}

/* Tell the BROWSER the page went dark, so it draws its own dark widgets.
   Nothing in the CRM declared a color-scheme, so the UA still believed it was
   painting on white and drew the date field's
   ::-webkit-calendar-picker-indicator as a black glyph — invisible on the dark
   input beside it. filter: invert() would hide the symptom by damaging the icon
   and breaks wherever a host theme has already inverted something; saying what
   the surface actually IS fixes the picker, the number spinners and the
   drop-down lists at once. THE WHOLE date/time INPUT FAMILY is listed — date, datetime-local,
   time, month — because the 10.6 fix originally named only "date" and the
   dispatch booking form's datetime-local kept its white popup (Jake,
   2026-08-12). pbjcrm-is-dark is the theme's own dark switch
   (PBJ_CRM_Theme::body_classes()) and this is its first consumer — it is only
   emitted on a dark preset, so the light presets are untouched. Compound, not
   descendant, because the theme classes land on the portal element itself. */
.pbjcrm-portal.pbjcrm-is-dark input[type="text"],
.pbjcrm-portal.pbjcrm-is-dark input[type="email"],
.pbjcrm-portal.pbjcrm-is-dark input[type="url"],
.pbjcrm-portal.pbjcrm-is-dark input[type="tel"],
.pbjcrm-portal.pbjcrm-is-dark input[type="number"],
.pbjcrm-portal.pbjcrm-is-dark input[type="date"],
.pbjcrm-portal.pbjcrm-is-dark input[type="datetime-local"],
.pbjcrm-portal.pbjcrm-is-dark input[type="time"],
.pbjcrm-portal.pbjcrm-is-dark input[type="month"],
.pbjcrm-portal.pbjcrm-is-dark input[type="search"],
.pbjcrm-portal.pbjcrm-is-dark input[type="password"],
.pbjcrm-portal.pbjcrm-is-dark select,
.pbjcrm-portal.pbjcrm-is-dark textarea,
.pbjcrm-login.pbjcrm-is-dark input[type="text"],
.pbjcrm-login.pbjcrm-is-dark input[type="password"] {
	color-scheme: dark;
}

.pbjcrm-portal select {
	width: auto;
	max-width: 100%;
}

.pbjcrm-portal textarea {
	min-height: 70px;
	resize: vertical;
}

.pbjcrm-portal input[type="color"] {
	width: 56px;
	height: 34px;
	padding: 2px;
	border: 1px solid var(--pbjcrm-border);
	border-radius: var(--pbjcrm-radius-sm);
	background: var(--pbjcrm-surface);
	cursor: pointer;
}

.pbjcrm-portal label,
.pbjcrm-login label {
	display: block;
	font-weight: 600;
	margin-bottom: 3px;
	font-size: 13px;
}

.pbjcrm-field {
	margin: 0 0 12px;
}

.pbjcrm-field-hint {
	margin: 3px 0 0;
	font-size: 12px;
	font-weight: 400;
}

.pbjcrm-inline-label {
	display: inline-flex !important;
	align-items: center;
	gap: 6px;
	font-weight: 400 !important;
	margin-right: 10px;
}

.pbjcrm-grid-2 {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
	gap: 0 14px;
}

.pbjcrm-inline-form {
	background: var(--pbjcrm-surface-alt);
}

.pbjcrm-inline-form h3,
.pbjcrm-inline-form h4 {
	margin-top: 0;
}

/* ---------------------------------------------------------------- Login */

.pbjcrm-login {
	max-width: 420px;
	margin: 40px auto;
}

/* ------------------------------------------------------------------ Nav */

.pbjcrm-nav-toggle {
	display: none;
	width: 100%;
	min-height: 44px;
	padding: 9px 14px;
	border: 1px solid var(--pbjcrm-border-strong);
	border-radius: var(--pbjcrm-radius);
	background: var(--pbjcrm-header-bg);
	color: var(--pbjcrm-header-text);
	font: 700 14px/1.2 var(--pbjcrm-font);
	text-align: left;
	cursor: pointer;
}

.pbjcrm-nav {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
	background: var(--pbjcrm-header-bg);
	color: var(--pbjcrm-header-text);
	border-bottom: 2px solid var(--pbjcrm-border);
	border-radius: var(--pbjcrm-radius) var(--pbjcrm-radius) 0 0;
	padding: var(--pbjcrm-space-sm);
	margin-bottom: 0;
}

.pbjcrm-nav-item {
	border: 1px solid transparent;
	background: transparent;
	color: var(--pbjcrm-header-text);
	border-radius: 999px;
	padding: 8px 15px;
	font-size: 14px;
	font-weight: 500;
	cursor: pointer;
	transition: background 0.15s ease, border-color 0.15s ease;
}

.pbjcrm-nav-item:hover {
	background: var(--pbjcrm-surface);
	border-color: var(--pbjcrm-border);
}

.pbjcrm-nav-item.pbjcrm-active {
	background: var(--pbjcrm-accent);
	border-color: var(--pbjcrm-accent-strong);
	color: var(--pbjcrm-accent-contrast);
	font-weight: 700;
	box-shadow: 0 1px 4px var(--pbjcrm-shadow);
}

/* REMOVED IN 1.36.0: `.pbjcrm-navmode` and `.pbjcrm-navmode-btn`. The
   Mobile/Desktop PAIR that sat at the end of this bar became ONE button in the
   top header bar beside the logo — `.pbjcrm-hdr-mode` in pbj-crm-header.css,
   where its positive/negative backgrounds live. The JS that emitted these two
   class names is gone with them (modeSwitch() in pbj-crm.js). */

/* The second row: the screens inside whichever group is open. */

.pbjcrm-subnav {
	display: flex;
	flex-wrap: wrap;
	gap: 4px;
	padding: var(--pbjcrm-space-sm) var(--pbjcrm-space-sm) 0;
	border-bottom: 1px solid var(--pbjcrm-border);
	margin-bottom: var(--pbjcrm-space);
}

/* ⛔ `!important` IS DELIBERATE AND MUST NOT BE "TIDIED" (item 4, 2026-08-15).
   `hidden` is the platform's way of saying an element is not here at all, so no
   look may overrule it — this declaration decides whether a control is ON SCREEN,
   which is structural, not cosmetic. It is the same call pbj-crm 1.36.2 made for
   the four Timeline/Add Work display swaps, and for the same reason: raising
   specificity instead would win against today's four tab styles and lose against
   the ninth, and the fix list adds four more styles and six palettes next.

   MEASURED, not theorised — the browser named the winning rule:
     .pbjcrm-tabs-segment .pbjcrm-subnav { display: inline-flex }   (0,2,0, line ~3202)
     .pbjcrm-subnav[hidden]              { display: none }          (0,2,0, line ~587)
   Equal specificity, segment declared ~2600 lines LATER, so order decided it and a
   subnav carrying the hidden attribute still rendered. With no items inside it that
   is an EMPTY 8x8 box wearing the segment style's background, 1px border and 8px
   radius, sitting directly under the nav row — Jake's "stray grey dot", reported on
   Dispatch, Tasks and Tickets, which are exactly the three groups holding one screen
   and therefore the only ones that hide the subnav. It also pushed the screen down
   42-59px, which is the same report's "dead gap".

   ⚠️ INVISIBLE ON THE DEFAULT PRESET: underline, pill and boxed set only border and
   gap; segment is the one style that sets `display`. Studio shipped on
   underline/default and pbj.tech runs terminal-dark/segment, so this reproduces only
   on the customer's theme — the 1.36.2 lesson, one class along.

   ⚠️ NOT to be confused with `.pbjcrm-subnav[hidden] + .pbjcrm-view`'s padding-top
   further down this file. THAT gap is deliberate: it puts back the space the second
   row would have occupied. It is not part of this defect and must stay. */
.pbjcrm-subnav[hidden] {
	display: none !important;
}

.pbjcrm-subnav-item {
	border: 1px solid transparent;
	background: transparent;
	color: var(--pbjcrm-text-soft);
	border-radius: var(--pbjcrm-radius-sm) var(--pbjcrm-radius-sm) 0 0;
	padding: 6px 13px;
	font-size: 13px;
	font-weight: 500;
	cursor: pointer;
	margin-bottom: -1px;
}

.pbjcrm-subnav-item:hover {
	background: var(--pbjcrm-surface-alt);
	color: var(--pbjcrm-text);
}

.pbjcrm-subnav-item.pbjcrm-active {
	background: var(--pbjcrm-surface);
	border-color: var(--pbjcrm-border);
	border-bottom-color: var(--pbjcrm-surface);
	color: var(--pbjcrm-link);
	font-weight: 700;
}

/* ------------------------------------------------------- View head / bar */

.pbjcrm-view-head {
	margin-bottom: 12px;
}

.pbjcrm-view-head h2 {
	margin: 0 0 2px;
	font-size: 19px;
}

.pbjcrm-view-head p {
	margin: 0;
	font-size: 13px;
}

.pbjcrm-view {
	min-width: 0;
}

/* With no second row there is nothing between the tabs and the screen, so
   the gap the second row would have left is put back here instead. */
.pbjcrm-subnav[hidden] + .pbjcrm-view {
	padding-top: var(--pbjcrm-space);
}

.pbjcrm-toolbar {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
	align-items: center;
	margin-bottom: 12px;
}

.pbjcrm-toolbar .pbjcrm-spacer {
	flex: 1;
}

.pbjcrm-toolbar input[type="search"],
.pbjcrm-toolbar input[type="text"] {
	width: 220px;
}

.pbjcrm-toolbar label {
	margin-bottom: 0;
}

/* --------------------------------------------------------------- Tables */

.pbjcrm-table {
	width: 100%;
	border-collapse: collapse;
	background: var(--pbjcrm-surface);
	border: 1px solid var(--pbjcrm-border);
	font-size: 14px;
}

.pbjcrm-table th,
.pbjcrm-table td {
	text-align: left;
	padding: 8px 10px;
	border-bottom: 1px solid var(--pbjcrm-border);
	vertical-align: top;
}

.pbjcrm-table tbody td {
	height: var(--pbjcrm-row-height);
}

.pbjcrm-table th {
	background: var(--pbjcrm-surface-alt);
	font-weight: 600;
	font-size: 13px;
}

.pbjcrm-table tr:last-child td {
	border-bottom: none;
}

.pbjcrm-table tbody tr.pbjcrm-row-click {
	cursor: pointer;
}

.pbjcrm-table tbody tr.pbjcrm-row-click:hover {
	background: var(--pbjcrm-surface-alt);
}

.pbjcrm-table th.pbjcrm-sortable {
	white-space: nowrap;
}

.pbjcrm-sortbtn {
	border: none;
	background: none;
	padding: 0;
	margin: 0;
	font: inherit;
	font-weight: 600;
	color: var(--pbjcrm-text);
	cursor: pointer;
}

.pbjcrm-sortbtn:hover {
	color: var(--pbjcrm-link);
}

.pbjcrm-sortbtn.pbjcrm-active {
	color: var(--pbjcrm-accent-strong);
}

/* Which rows you are looking at, the buttons to walk the pages, and how many
   to show at a time. Wraps rather than scrolls: this row has to survive a
   phone held in one hand. */
.pbjcrm-pager {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 10px;
	margin-top: 12px;
	font-size: 13px;
	color: var(--pbjcrm-muted);
}

.pbjcrm-pager-count {
	font-weight: 600;
	color: var(--pbjcrm-text);
}

.pbjcrm-pager-btns {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 6px;
}

.pbjcrm-pager-page {
	white-space: nowrap;
}

.pbjcrm-perpage {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	margin-left: auto;
}

.pbjcrm-perpage select {
	width: auto;
	min-width: 72px;
}

/* ----------------------------------------------------- Badges and chips */

.pbjcrm-badge {
	display: inline-block;
	border-radius: 999px;
	padding: 1px 9px;
	font-size: 12px;
	font-weight: 600;
	background: var(--pbjcrm-surface-alt);
	border: 1px solid var(--pbjcrm-border);
	color: var(--pbjcrm-text);
	white-space: nowrap;
	margin-right: 4px;
}

.pbjcrm-badge-status {
	background: #eef3f8;
	background: color-mix(in srgb, var(--pbjcrm-accent) 10%, var(--pbjcrm-surface));
	border-color: #c6d6e6;
	border-color: color-mix(in srgb, var(--pbjcrm-accent) 30%, var(--pbjcrm-surface));
	color: #1f4e79;
	color: var(--pbjcrm-accent-strong);
}

.pbjcrm-badge-owner {
	background: #f3f0fa;
	background: var(--pbjcrm-accent-soft);
	border-color: #d4c9ea;
	border-color: color-mix(in srgb, var(--pbjcrm-accent) 24%, var(--pbjcrm-surface));
	color: #4b3b7a;
	color: var(--pbjcrm-text-soft);
}

.pbjcrm-badge-source {
	background: var(--pbjcrm-surface-alt);
	color: var(--pbjcrm-muted);
}

.pbjcrm-badge-won,
.pbjcrm-ms-subscribed {
	background: #edfaef;
	background: color-mix(in srgb, var(--pbjcrm-success) 10%, var(--pbjcrm-surface));
	border-color: #b7e3c0;
	border-color: color-mix(in srgb, var(--pbjcrm-success) 32%, var(--pbjcrm-surface));
	color: var(--pbjcrm-success);
}

.pbjcrm-badge-lost,
.pbjcrm-ms-unsubscribed {
	background: #fcf0f1;
	background: color-mix(in srgb, var(--pbjcrm-danger) 10%, var(--pbjcrm-surface));
	border-color: #f0c4c5;
	border-color: color-mix(in srgb, var(--pbjcrm-danger) 32%, var(--pbjcrm-surface));
	color: var(--pbjcrm-danger);
}

.pbjcrm-ms-bounced {
	background: #fcf3e6;
	background: color-mix(in srgb, var(--pbjcrm-warning) 10%, var(--pbjcrm-surface));
	border-color: #ecd8b2;
	border-color: color-mix(in srgb, var(--pbjcrm-warning) 32%, var(--pbjcrm-surface));
	color: var(--pbjcrm-warning);
}

.pbjcrm-ms-never {
	background: var(--pbjcrm-surface-alt);
	color: var(--pbjcrm-muted);
}

/* Tag chips */

.pbjcrm-chiprow {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
	align-items: center;
	margin-top: 6px;
}

.pbjcrm-chip {
	display: inline-flex;
	align-items: center;
	gap: 5px;
	border: 1px solid var(--pbjcrm-border);
	background: var(--pbjcrm-surface-alt);
	border-radius: 999px;
	padding: 2px 9px;
	font-size: 12px;
	font-weight: 600;
	white-space: nowrap;
	margin-right: 4px;
	color: var(--pbjcrm-text);
}

.pbjcrm-chip-dot {
	display: inline-block;
	width: 8px;
	height: 8px;
	border-radius: 50%;
	background: var(--pbjcrm-muted);
	flex: none;
}

.pbjcrm-chip-x {
	border: none;
	background: none;
	padding: 0 0 0 2px;
	margin: 0;
	color: var(--pbjcrm-muted);
	font-size: 11px;
	line-height: 1;
	cursor: pointer;
}

.pbjcrm-chip-x:hover {
	color: var(--pbjcrm-danger);
}

/* Due / overdue chips */

.pbjcrm-due {
	display: inline-block;
	border-radius: var(--pbjcrm-radius-sm);
	padding: 1px 8px;
	font-size: 11px;
	font-weight: 700;
	white-space: nowrap;
	background: var(--pbjcrm-surface-alt);
	color: var(--pbjcrm-muted);
}

.pbjcrm-due-due {
	background: #eef3f8;
	background: color-mix(in srgb, var(--pbjcrm-accent) 12%, var(--pbjcrm-surface));
	color: #1f4e79;
	color: var(--pbjcrm-accent-strong);
}

.pbjcrm-due-due_soon {
	background: #fcf3e6;
	background: color-mix(in srgb, var(--pbjcrm-warning) 12%, var(--pbjcrm-surface));
	color: var(--pbjcrm-warning);
}

.pbjcrm-due-overdue {
	background: #fcf0f1;
	background: color-mix(in srgb, var(--pbjcrm-danger) 12%, var(--pbjcrm-surface));
	color: var(--pbjcrm-danger);
}

.pbjcrm-due-done {
	background: #edfaef;
	background: color-mix(in srgb, var(--pbjcrm-success) 12%, var(--pbjcrm-surface));
	color: var(--pbjcrm-success);
}

/* ------------------------------------------------- Contact profile head */

.pbjcrm-profile-head {
	display: flex;
	gap: 18px;
	align-items: flex-start;
	flex-wrap: wrap;
}

.pbjcrm-profile-headmain {
	flex: 1;
	min-width: 240px;
}

.pbjcrm-profile-name {
	margin: 0;
	font-size: 22px;
	display: inline-block;
	margin-right: 8px;
}

.pbjcrm-nameedit,
.pbjcrm-titleedit {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
	align-items: center;
	margin-bottom: 6px;
}

.pbjcrm-nameedit input[type="text"],
.pbjcrm-titleedit-input {
	max-width: 320px;
	font-size: 16px;
}

/* The heading a record leads with sits flush with its Edit button rather than
   carrying a paragraph's worth of space under it. inlineTitle() draws an h2 on
   the profile screens and reuses .pbjcrm-profile-name there, so this only has
   to answer for the plainer headings the other record screens ask it for. */
.pbjcrm-titleedit > h2,
.pbjcrm-titleedit > h3 {
	margin: 0;
}

/* The row that carries a record's Edit control and its print icon, at the very
   top of the record. Right-aligned so it never fights the title for the eye,
   and wrapping rather than squeezing on a narrow screen. */
.pbjcrm-recordhead-controls {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
	align-items: center;
	justify-content: flex-end;
	margin-bottom: 6px;
}

.pbjcrm-recordhead-controls:empty {
	display: none;
}

.pbjcrm-profile-badges {
	display: flex;
	flex-wrap: wrap;
	gap: 4px;
	margin-bottom: 8px;
}

.pbjcrm-companyline {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
	align-items: center;
	font-size: 14px;
	margin-bottom: 4px;
}

/* Every business one person belongs to — no "main" one. */
.pbjcrm-companies-line {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 8px;
	font-size: 13px;
	margin: 4px 0 6px;
}

.pbjcrm-companies-line:empty {
	display: none;
}

.pbjcrm-companies-line .pbjcrm-chiprow {
	margin-top: 0;
}

.pbjcrm-companypick .pbjcrm-field {
	max-width: 340px;
}

.pbjcrm-typeahead-input {
	max-width: 340px;
}

.pbjcrm-tagsedit {
	margin-top: 2px;
}

.pbjcrm-typeahead {
	max-width: 340px;
	border-radius: var(--pbjcrm-radius);
	overflow: hidden;
}

.pbjcrm-typeahead:not(:empty) {
	border: 1px solid var(--pbjcrm-border);
	margin-bottom: 8px;
}

.pbjcrm-typeahead-item {
	display: block;
	width: 100%;
	text-align: left;
	border: none;
	border-bottom: 1px solid var(--pbjcrm-border);
	background: var(--pbjcrm-surface);
	padding: 6px 10px;
	font-size: 13px;
	color: var(--pbjcrm-text);
	cursor: pointer;
}

.pbjcrm-typeahead-item:last-child {
	border-bottom: none;
}

.pbjcrm-typeahead-item:hover {
	background: var(--pbjcrm-surface-alt);
}

/* ------------------------------------------ Photo drag-and-drop zone */

.pbjcrm-photo-wrap {
	width: 148px;
	flex: none;
}

.pbjcrm-photozone {
	position: relative;
	width: 148px;
	height: 148px;
	border: 2px dashed var(--pbjcrm-border);
	border-radius: var(--pbjcrm-radius);
	background: var(--pbjcrm-surface-alt);
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 4px;
	cursor: pointer;
	overflow: hidden;
	transition: border-color 0.15s ease, background 0.15s ease, box-shadow 0.15s ease;
}

.pbjcrm-photozone:hover,
.pbjcrm-photozone:focus {
	border-color: var(--pbjcrm-accent);
	outline: none;
}

.pbjcrm-photozone.pbjcrm-dragover {
	border-color: var(--pbjcrm-accent);
	border-style: solid;
	background: #eef3f8;
	background: var(--pbjcrm-accent-soft);
	box-shadow: inset 0 0 0 3px var(--pbjcrm-accent-soft);
}

.pbjcrm-photozone.pbjcrm-uploading {
	opacity: 0.6;
}

.pbjcrm-photo {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}

.pbjcrm-photo-initials {
	font-size: 34px;
	font-weight: 700;
	color: var(--pbjcrm-muted);
	line-height: 1;
}

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

.pbjcrm-photo-caption {
	margin: 6px 0 0;
	font-size: 12px;
	text-align: center;
}

/* ---------------------------------------------------- Profile layout */

.pbjcrm-profile-layout {
	display: grid;
	grid-template-columns: minmax(0, 2fr) minmax(260px, 1fr);
	gap: 16px;
	align-items: start;
}

.pbjcrm-profile-main {
	min-width: 0;
}

.pbjcrm-profile-side {
	display: flex;
	flex-direction: column;
	gap: 12px;
}

/* ------------------------------------- The record: Block 1 and Block 2 (1.36.0)

   Jake's shape, 2026-08-14. Block 1 is the record's identity, its contact
   details, Add Work, note creation and the Timeline view selector, all in ONE
   card in the left column. Block 2 is everything that was the unnamed sidebar,
   now called Attributes, at the top of the right column.

   ⚠️ 2026-08-19 — NOTE CREATION IS A CARD OF ITS OWN INSIDE BLOCK 1, between
   Add Work and the Timeline view selector (Jake's order). It is the one nested
   card that keeps its border and background; see the rule further down.

   ⛔ NOT ONE COLOUR IS DECLARED HERE. Every value is a --pbjcrm-* token the
   theme emits, so all six presets including the two dark ones are right without
   this file knowing any of them. */

.pbjcrm-block1 {
	display: flex;
	flex-direction: column;
	gap: 12px;
}

/* The cards nested INSIDE Block 1 are already inside a card, so they give up
   their own border and background — otherwise the record's head reads as four
   stacked boxes rather than one block. */
.pbjcrm-block1 > .pbjcrm-card {
	border: 0;
	background: none;
	padding: 0;
	box-shadow: none;
}

.pbjcrm-card-head {
	display: flex;
	align-items: baseline;
	gap: 8px;
	flex-wrap: wrap;
}

.pbjcrm-card-head h3 {
	margin: 0;
}

.pbjcrm-card-edit {
	margin-left: auto;
}

/* ⛔ THE ONE RULE THAT HIDES THE BLANKS. contactCard() marks every row with
   nothing in it and this takes them off the page; pressing Edit puts
   .pbjcrm-showblanks on the card, which puts them back. Do NOT set display per
   row from JavaScript — an inline style would win over this and the two would
   fight. */
.pbjcrm-contactcard .pbjcrm-row-empty {
	display: none;
}

.pbjcrm-contactcard.pbjcrm-showblanks .pbjcrm-row-empty {
	display: flex;
}

/* Add Work — buttons on a desktop, one select on a phone. Both are built every
   time; which one shows is decided here and nowhere else, so the two shapes
   cannot drift apart in behaviour. */
.pbjcrm-addwork-heading {
	margin: 0 0 6px;
	font-size: 14px;
}

.pbjcrm-addwork-picker {
	display: none !important;
}

/* ⛔ NOTE CREATION IS A BLOCK OF ITS OWN, BETWEEN ADD WORK AND THE TIMELINE
   (Jake, 2026-08-19). It is the ONE nested card in Block 1 that keeps its own
   edges: `.pbjcrm-block1 > .pbjcrm-card` strips a nested card's border,
   background and padding on purpose — that is what makes the record's head read
   as one block instead of four stacked boxes — and note creation is not part of
   the head, it is a thing you DO, so it gets them back.

   ⛔ THE OVERRIDE IS 0,3,0, NOT 0,2,0, AND THAT IS ON PURPOSE. Matching the
   strip rule's specificity would leave this winning on source order alone, and
   this file has already lost that argument twice (see the two shape swaps).
   `.pbjcrm-card.pbjcrm-note-block` outranks `.pbjcrm-card`, so it cannot be
   undone by anything moving in the file.

   ⚠️ AND THE COMPOSER GIVES UP ITS OWN BOX INSIDE IT. `.pbjcrm-composer` draws
   a bordered panel of its own — right when it stood alone in the head, a box
   inside a box now. The card is the block; the textarea keeps the border every
   `.pbjcrm-portal textarea` has, so nothing is left floating. */
.pbjcrm-block1 > .pbjcrm-card.pbjcrm-note-block {
	border: 1px solid var(--pbjcrm-border);
	background: var(--pbjcrm-surface-alt);
	border-radius: var(--pbjcrm-radius);
	padding: 10px 12px;
}

.pbjcrm-note-block-heading {
	margin: 0 0 6px;
	font-size: 14px;
}

.pbjcrm-note-block .pbjcrm-composer {
	border: 0;
	border-radius: 0;
	background: none;
	padding: 0;
	margin-bottom: 0;
}

.pbjcrm-timelineview {
	display: flex;
	align-items: center;
	gap: 8px;
	flex-wrap: wrap;
	padding-top: 10px;
	border-top: 1px solid var(--pbjcrm-border);
}

/* ⛔ DESKTOP KEEPS THE FULL BUTTONS; THE SELECT IS THE PHONE'S SHAPE ONLY.
   Both are in the DOM every time — see the docblock in paintContact() — and
   these rules are the only thing deciding which is on screen.

   ⛔ `!important` ON EVERY ONE OF THESE FOUR DISPLAY SWAPS, AND IT IS NOT
   LAZINESS — IT IS THE FIX FOR A BUG THAT SHIPPED. The tab-style presets each
   set `display` on `.pbjcrm-tabs`:

       .pbjcrm-tabs-segment .pbjcrm-tabs { display: inline-flex; }

   That is specificity 0,2,0 — exactly the same as `.pbjcrm-block1
   .pbjcrm-timelineview-tabs` — and it is declared ~700 lines LATER in this
   file, so it wins on order and puts the button row back on a phone. The
   result was BOTH shapes on screen at once.

   ⚠️ AND IT ONLY HAPPENED ON SOME SITES, WHICH IS WHY IT GOT THROUGH. Studio
   runs the Underline preset, whose rule sets border and gap but NOT display, so
   the swap worked there and every test passed. pbj.tech runs Segment, which
   does set display. A bug that depends on a THEME SETTING will not show up in
   one environment.

   Raising specificity instead would win this round and lose the next one:
   there are FOUR presets (underline / pill / segment / boxed), any of them may
   set `display`, and the next one added is a coin toss. These four
   declarations decide WHICH SHAPE OF A CONTROL IS ON SCREEN — they are
   structural, not cosmetic, and they have to outrank presentation. */
.pbjcrm-timelineview-select,
.pbjcrm-timelineview-label {
	display: none !important;
}

.pbjcrm-timelineview-tabs {
	display: flex;
	flex-wrap: wrap;
	gap: 4px;
	flex: 1 1 auto;
	margin: 0;
}

/* ⛔ 1.41.0 — THE SECOND ROW: narrowings of "What has happened".
   `flex: 1 0 100%` is load-bearing — the parent is a wrapping flex row, so
   without it these chips would trail the last view button on the same line and
   read as more views. It is a row of its own or it is nothing.

   ⚠️ It lives INSIDE `.pbjcrm-timelineview-tabs`, which the phone hides
   wholesale (the four `!important` swaps above), so the phone never draws it —
   there the narrowings are in the select's own `optgroup`. One control per
   shape, which is the whole point of the pair. */
.pbjcrm-tab-kinds {
	display: flex;
	flex: 1 0 100%;
	flex-wrap: wrap;
	align-items: center;
	gap: 4px;
	margin-top: 6px;
}

.pbjcrm-tab-kinds .pbjcrm-inline-label {
	margin-right: 2px;
}

.pbjcrm-tab-kinds .pbjcrm-chip {
	cursor: pointer;
	margin-right: 0;
}

.pbjcrm-tab-kinds .pbjcrm-chip:hover {
	border-color: var(--pbjcrm-accent);
}

.pbjcrm-tab-kinds .pbjcrm-chip.pbjcrm-active {
	background: var(--pbjcrm-accent);
	border-color: var(--pbjcrm-accent);
	color: var(--pbjcrm-pill-on-text, var(--pbjcrm-accent-contrast));
}

/* Block 2 — Attributes. */

.pbjcrm-attributes {
	padding: 0;
}

.pbjcrm-attributes-summary {
	cursor: pointer;
	padding: 12px 14px;
	list-style-position: inside;
}

.pbjcrm-attributes-summary h3 {
	display: inline;
	margin: 0;
	font-size: 14px;
}

.pbjcrm-attributes[open] .pbjcrm-attributes-summary {
	border-bottom: 1px solid var(--pbjcrm-border);
}

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

.pbjcrm-showclient-btn {
	width: 100%;
	justify-content: center;
}

/* The client preview dialog.

   ⛔ IT STATES ITS OWN `color`, AND THAT ONE MISSING LINE WAS THE WHOLE DEFECT
   (Jake, 2026-08-15: heading, body copy, section label and both ticket rows all
   near-black on near-black).

   crmModal() does `document.body.appendChild( overlay )`, so this dialog is NOT
   a descendant of `.pbjcrm-portal`. That matters twice over. The panel inside it
   is a `.pbjcrm-card`, which paints `background: var(--pbjcrm-surface)` — and
   the surface token resolves perfectly well up here, because the canvas prints
   the whole set on :root. But `color` is only ever stated on `.pbjcrm-portal`
   and on `.pbjcrm-portal h2…h5`, neither of which matches anything inside a
   body-level dialog — so the text fell back to the page's own colour while the
   card behind it followed the theme. On the Dark preset that is #000000 text on
   #1f2229: MEASURED AT 1.32:1, against 4.5:1 for body text.

   ⛔ THE RULE THIS LEAVES BEHIND: a surface that is portalled to <body> must
   STATE both halves of the pair. Inheriting one and theming the other is how
   two colours that were never compared end up on top of each other.

   Declared here rather than on the panel so every descendant — heading,
   paragraph, list row, span — inherits it in one line. No `!important` and no
   raised specificity: nothing anywhere in this file or in a preset block sets
   `color` on `.pbjcrm-overlay` or on `.pbjcrm-card`, so there is nothing to
   beat. Raising specificity here would win today and lose the next time a
   preset wants a say (pbj-crm 1.36.2). */

.pbjcrm-overlay {
	position: fixed;
	inset: 0;
	background: var(--pbjcrm-overlay, rgba(0, 0, 0, .5));
	color: var(--pbjcrm-text, #1d2327);
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 16px;
	z-index: 100000;
}

.pbjcrm-overlay-panel {
	width: min(640px, 100%);
	max-height: 90vh;
	overflow: auto;
	/* Belt to the overlay's colour above. `.pbjcrm-card` supplies these too, but
	   WITHOUT literal fallbacks — so on a site running the portal outside the
	   canvas with the tokens missing entirely, `background: var(--pbjcrm-surface)`
	   computes to transparent and the panel disappears into the scrim. Stating
	   the fallbacks here means the dialog is readable even when the theme is
	   not there at all. */
	background: var(--pbjcrm-surface, #ffffff);
	color: var(--pbjcrm-text, #1d2327);
	/* --pbjcrm-edge, not --pbjcrm-border: a dialog has to say where it ENDS, and
	   the row hairline measures 1.35–1.67:1 against the panel on every preset,
	   against WCAG 1.4.11's 3:1. The edge token measures 3.52:1 at worst. */
	border: 1px solid var(--pbjcrm-edge, #8e9296);
}

.pbjcrm-overlay-panel h3 {
	margin-top: 0;
}

.pbjcrm-clientview-section {
	margin-top: 12px;
}

/* ⛔ text-soft, NOT muted. This is 13px uppercase body text, so it owes 4.5:1 —
   and --pbjcrm-muted measures 4.20:1 on Default and 4.04:1 on Soft against the
   panel behind it. --pbjcrm-text-soft measures 7.33:1 at its worst across all
   eight presets. The muted token is not changed: it is a shipped preset value
   used by every quiet line in the suite, and repainting it here would repaint
   all of them. The SURFACE picks a token that meets its requirement. */
.pbjcrm-clientview-section h4 {
	margin: 0 0 6px;
	font-size: 13px;
	text-transform: uppercase;
	letter-spacing: .06em;
	color: var(--pbjcrm-text-soft, #50575e);
}

.pbjcrm-clientview-rows {
	margin: 0;
	padding: 0;
	list-style: none;
	display: flex;
	flex-direction: column;
	gap: 6px;
}

.pbjcrm-clientview-rows li {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
	align-items: baseline;
	padding: 6px 8px;
	border: 1px solid var(--pbjcrm-border);
	border-radius: var(--pbjcrm-radius);
}

/* Same measurement as the section label above — 4.5:1 is owed and muted does
   not reach it on two of the eight presets. */
.pbjcrm-clientview-meta {
	color: var(--pbjcrm-text-soft, #50575e);
	font-size: 12px;
}

.pbjcrm-panel {
	border: 1px solid var(--pbjcrm-border);
	border-radius: var(--pbjcrm-radius);
	background: var(--pbjcrm-surface);
	padding: 12px 14px;
}

.pbjcrm-panel h3 {
	margin: 0 0 8px;
	font-size: 14px;
}

.pbjcrm-panel .pbjcrm-field:last-of-type {
	margin-bottom: 8px;
}

.pbjcrm-deflist {
	display: grid;
	grid-template-columns: auto minmax(0, 1fr);
	gap: 2px 10px;
	margin: 0 0 10px;
	font-size: 13px;
}

.pbjcrm-deflist dt {
	color: var(--pbjcrm-muted);
	white-space: nowrap;
}

.pbjcrm-deflist dd {
	margin: 0;
	overflow-wrap: anywhere;
}

/* ------------------------------ Companies & people split screen (7F-C) */

.pbjcrm-split {
	display: grid;
	grid-template-columns: minmax(240px, 5fr) minmax(280px, 7fr);
	gap: var(--pbjcrm-space);
	align-items: start;
}

.pbjcrm-split-col {
	min-width: 0;
}

.pbjcrm-split-head {
	margin: 0 0 var(--pbjcrm-space-sm);
	font-size: 15px;
	padding-bottom: 4px;
	border-bottom: 2px solid var(--pbjcrm-border);
}

.pbjcrm-filterbar {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 8px;
	background: var(--pbjcrm-surface-alt);
	border: 1px solid var(--pbjcrm-border);
	border-radius: var(--pbjcrm-radius);
	padding: var(--pbjcrm-space-sm) var(--pbjcrm-space);
	margin-bottom: var(--pbjcrm-space-sm);
	font-size: 14px;
}

.pbjcrm-co-list,
.pbjcrm-person-list {
	display: flex;
	flex-direction: column;
	gap: var(--pbjcrm-space-sm);
}

/* Left column: a business, and a place to drop somebody onto. */

.pbjcrm-co-card {
	border: 1px solid var(--pbjcrm-border);
	border-radius: var(--pbjcrm-radius);
	background: var(--pbjcrm-surface);
	padding: var(--pbjcrm-space-sm) var(--pbjcrm-space);
	min-height: var(--pbjcrm-row-height);
	transition: border-color 0.15s ease, background 0.15s ease, box-shadow 0.15s ease;
}

.pbjcrm-co-name {
	border: none;
	background: none;
	padding: 0;
	margin: 0;
	font: inherit;
	font-weight: 700;
	font-size: 15px;
	color: var(--pbjcrm-text);
	text-align: left;
	cursor: pointer;
	width: 100%;
}

.pbjcrm-co-name:hover {
	color: var(--pbjcrm-link);
}

.pbjcrm-co-meta {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 6px;
	margin-top: 4px;
	font-size: 12px;
}

.pbjcrm-co-card.pbjcrm-selected {
	border-color: var(--pbjcrm-accent);
	background: #eef3f8;
	background: var(--pbjcrm-accent-soft);
	box-shadow: inset 3px 0 0 var(--pbjcrm-accent);
}

/* The one visible promise that a drop will land. */
.pbjcrm-co-card.pbjcrm-droptarget {
	border-color: var(--pbjcrm-accent);
	border-style: dashed;
	background: #eef3f8;
	background: var(--pbjcrm-accent-soft);
	box-shadow: inset 0 0 0 3px var(--pbjcrm-accent);
}

/* Right column: a person you can drag, or move with the buttons. */

.pbjcrm-person-row {
	border: 1px solid var(--pbjcrm-border);
	border-radius: var(--pbjcrm-radius);
	background: var(--pbjcrm-surface);
	padding: var(--pbjcrm-space-sm) var(--pbjcrm-space);
	cursor: grab;
}

.pbjcrm-person-row.pbjcrm-dragging {
	opacity: 0.55;
	border-color: var(--pbjcrm-accent);
	cursor: grabbing;
}

.pbjcrm-person-head {
	display: flex;
	align-items: center;
	gap: 8px;
}

.pbjcrm-drag-grip {
	color: var(--pbjcrm-muted);
	font-size: 15px;
	line-height: 1;
	flex: none;
}

.pbjcrm-person-name {
	font-weight: 700;
	font-size: 15px;
	color: var(--pbjcrm-text);
}

.pbjcrm-person-name:hover {
	color: var(--pbjcrm-link);
}

.pbjcrm-person-contact {
	margin: 2px 0 0;
	font-size: 13px;
}

.pbjcrm-person-actions {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
	margin-top: var(--pbjcrm-space-sm);
}

/* ------------------------------------ Panels for screens a module owns */

.pbjcrm-comingsoon {
	background: var(--pbjcrm-surface-alt);
	max-width: 720px;
}

.pbjcrm-comingsoon p {
	margin: 0 0 var(--pbjcrm-space-sm);
}

.pbjcrm-comingsoon p:first-child {
	font-weight: 700;
	font-size: 15px;
}

/* ----------------------------------------------------------------- Tabs */

.pbjcrm-tabs {
	display: flex;
	flex-wrap: wrap;
	gap: 4px;
	border-bottom: 1px solid var(--pbjcrm-border);
	margin-bottom: 12px;
}

.pbjcrm-tab {
	border: 1px solid transparent;
	border-bottom: none;
	background: transparent;
	color: var(--pbjcrm-text);
	padding: 7px 14px;
	font-size: 14px;
	cursor: pointer;
	border-radius: var(--pbjcrm-radius) var(--pbjcrm-radius) 0 0;
}

.pbjcrm-tab:hover {
	background: var(--pbjcrm-surface-alt);
}

.pbjcrm-tab.pbjcrm-active {
	background: var(--pbjcrm-surface-alt);
	border-color: var(--pbjcrm-border);
	font-weight: 700;
	color: var(--pbjcrm-link);
}

.pbjcrm-tabbody {
	min-width: 0;
}

/* ------------------------------------------------------------- Timeline */

.pbjcrm-composer {
	border: 1px solid var(--pbjcrm-border);
	border-radius: var(--pbjcrm-radius);
	background: var(--pbjcrm-surface-alt);
	padding: 10px 12px;
	margin-bottom: 12px;
}

.pbjcrm-composer-actions {
	margin-top: 8px;
}

.pbjcrm-feed {
	display: flex;
	flex-direction: column;
	gap: 8px;
}

.pbjcrm-feed-row {
	border: 1px solid var(--pbjcrm-border);
	border-left: 3px solid var(--pbjcrm-border);
	border-radius: var(--pbjcrm-radius);
	background: var(--pbjcrm-surface);
	padding: 8px 12px;
}

.pbjcrm-feed-note {
	border-left-color: #ecdf9a;
	border-left-color: color-mix(in srgb, var(--pbjcrm-warning) 45%, var(--pbjcrm-surface));
	background: #fdf9ec;
	background: color-mix(in srgb, var(--pbjcrm-warning) 6%, var(--pbjcrm-surface));
}

.pbjcrm-feed-system,
.pbjcrm-feed-import {
	background: var(--pbjcrm-surface-alt);
	font-size: 13px;
}

.pbjcrm-feed-campaign_send,
.pbjcrm-feed-email_out {
	border-left-color: #c6d6e6;
	border-left-color: color-mix(in srgb, var(--pbjcrm-accent) 35%, var(--pbjcrm-surface));
}

.pbjcrm-feed-deal {
	border-left-color: #b7e3c0;
	border-left-color: color-mix(in srgb, var(--pbjcrm-success) 35%, var(--pbjcrm-surface));
}

.pbjcrm-feed-task {
	border-left-color: #ecd8b2;
	border-left-color: color-mix(in srgb, var(--pbjcrm-warning) 35%, var(--pbjcrm-surface));
}

.pbjcrm-feed-head {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
	align-items: baseline;
	font-size: 12px;
	color: var(--pbjcrm-muted);
	margin-bottom: 4px;
}

.pbjcrm-feed-type {
	font-weight: 700;
	color: var(--pbjcrm-text);
}

.pbjcrm-feed-author {
	font-style: italic;
}

.pbjcrm-feed-time {
	margin-left: auto;
	white-space: nowrap;
}

.pbjcrm-feed-body {
	font-size: 14px;
	white-space: pre-wrap;
	overflow-wrap: anywhere;
}

.pbjcrm-feed-link {
	display: block;
	color: inherit;
	text-decoration: none;
}

.pbjcrm-feed-link:hover,
.pbjcrm-feed-link:focus {
	color: var(--pbjcrm-accent);
	text-decoration: underline;
}

/* ---------------------------------------------------------------- Deals */

.pbjcrm-summary-cards {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
	gap: 12px;
	margin-bottom: 16px;
}

.pbjcrm-summary-card {
	border: 1px solid var(--pbjcrm-border);
	border-radius: var(--pbjcrm-radius);
	background: var(--pbjcrm-surface);
	padding: 12px 14px;
}

.pbjcrm-metric {
	font-size: 22px;
	font-weight: 700;
	display: block;
}

.pbjcrm-metric-label {
	font-size: 12px;
	color: var(--pbjcrm-muted);
}

.pbjcrm-stage-section {
	margin-bottom: 20px;
}

.pbjcrm-stage-head {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: 6px 14px;
	border-bottom: 1px solid var(--pbjcrm-border);
	padding-bottom: 5px;
	margin-bottom: 10px;
}

.pbjcrm-stage-head h3 {
	margin: 0;
	font-size: 15px;
}

.pbjcrm-stage-head .pbjcrm-muted {
	font-size: 13px;
}

.pbjcrm-stage-empty {
	font-size: 13px;
	margin: 0 0 6px;
}

.pbjcrm-deal-list {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
	gap: 10px;
}

.pbjcrm-deal-card {
	border: 1px solid var(--pbjcrm-border);
	border-radius: var(--pbjcrm-radius);
	background: var(--pbjcrm-surface);
	padding: 10px 12px;
}

.pbjcrm-deal-head {
	display: flex;
	gap: 10px;
	align-items: baseline;
	margin-bottom: 6px;
}

.pbjcrm-deal-value {
	margin-left: auto;
	font-weight: 700;
	white-space: nowrap;
}

.pbjcrm-deal-meta,
.pbjcrm-task-meta {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 4px 10px;
	font-size: 12px;
	color: var(--pbjcrm-muted);
	margin-bottom: 8px;
}

.pbjcrm-deal-actions,
.pbjcrm-task-actions {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
	margin-top: 8px;
}

/* Stage stepper */

.pbjcrm-stepper {
	display: flex;
	flex-wrap: wrap;
	gap: 5px;
	margin-bottom: 4px;
}

.pbjcrm-step {
	border: 1px solid var(--pbjcrm-border);
	background: var(--pbjcrm-surface-alt);
	border-radius: 999px;
	padding: 3px 11px;
	font-size: 12px;
	color: var(--pbjcrm-muted);
}

.pbjcrm-step.pbjcrm-step-current {
	background: var(--pbjcrm-accent);
	border-color: var(--pbjcrm-accent);
	color: var(--pbjcrm-accent-contrast);
	font-weight: 700;
}

button.pbjcrm-step.pbjcrm-step-allowed {
	cursor: pointer;
	color: var(--pbjcrm-link);
	background: var(--pbjcrm-surface);
}

button.pbjcrm-step.pbjcrm-step-allowed:hover {
	border-color: var(--pbjcrm-accent);
	background: var(--pbjcrm-surface-alt);
}

/* ---------------------------------------------------------------- Tasks */

.pbjcrm-task-list {
	display: flex;
	flex-direction: column;
	gap: 8px;
}

/* The bulk controls (managers only) and the tick box each card grows beside
   it while they are on screen. The box sits at the top of the card rather
   than the middle so it lines up with the title on a card carrying a
   checklist and three lines of notes. */

.pbjcrm-task-bulk {
	margin-bottom: 8px;
}

.pbjcrm-task-pick {
	display: flex;
	align-items: flex-start;
	gap: 8px;
}

.pbjcrm-task-pick > input[type="checkbox"] {
	margin-top: 14px;
	flex: 0 0 auto;
}

.pbjcrm-task-pick > .pbjcrm-task-select {
	align-self: flex-start;
	min-width: 74px;
}

.pbjcrm-task-select[aria-pressed="true"] {
	font-weight: 700;
}

.pbjcrm-task-pick > .pbjcrm-task-card {
	flex: 1 1 auto;
	min-width: 0;
}

.pbjcrm-task-card {
	border: 1px solid var(--pbjcrm-border);
	border-radius: var(--pbjcrm-radius);
	background: var(--pbjcrm-surface);
	padding: 10px 12px;
}

/* The record-type edge (17.5): every card in this list IS a task, so the
   class itself is the kind and each card wears the task colour — the same
   one the calendar's task entries carry. Falls back to the ordinary border
   when the theme's inline CSS is absent. */
.pbjcrm-task-card {
	border-left: 3px solid var(--pbjcrm-rec-task, var(--pbjcrm-border));
}

.pbjcrm-task-done {
	background: var(--pbjcrm-surface-alt);
}

.pbjcrm-task-head {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 8px;
	margin-bottom: 4px;
}

.pbjcrm-task-title {
	font-weight: 600;
	flex: 1;
	min-width: 140px;
}

.pbjcrm-task-title.pbjcrm-done {
	text-decoration: line-through;
	color: var(--pbjcrm-muted);
	font-weight: 400;
}

.pbjcrm-task-notes {
	margin: 4px 0 0;
	font-size: 13px;
	white-space: pre-wrap;
	overflow-wrap: anywhere;
}

/* A repeating task says so, quietly. */

.pbjcrm-task-repeat {
	border: 1px solid var(--pbjcrm-border);
	border-radius: 999px;
	padding: 1px 8px;
	white-space: nowrap;
}

/* The checklist on a task card. */

.pbjcrm-task-items {
	display: flex;
	flex-direction: column;
	gap: 2px;
	margin: 6px 0 0;
}

.pbjcrm-task-progress {
	margin: 0 0 2px;
	font-size: 12px;
	font-weight: 600;
	color: var(--pbjcrm-muted);
}

.pbjcrm-task-item {
	display: flex;
	align-items: flex-start;
	gap: 7px;
	font-size: 13px;
	line-height: 1.4;
	cursor: pointer;
	overflow-wrap: anywhere;
}

.pbjcrm-task-item input {
	margin-top: 2px;
	flex: 0 0 auto;
}

.pbjcrm-task-item.pbjcrm-done span {
	text-decoration: line-through;
	color: var(--pbjcrm-muted);
}

/* The checklist editor inside the task form. */

.pbjcrm-task-editor {
	display: flex;
	flex-direction: column;
	gap: 5px;
}

.pbjcrm-task-editor-row {
	display: flex;
	align-items: center;
	gap: 6px;
}

/* ⛔ THE BOX MUST HAVE A WIDTH OF ITS OWN — NEVER ONLY A GROWN ONE.
   Reported 2026-08-15 from an iPhone: the checklist line box had collapsed to
   about 20px, so a second line could not be typed, and a blank label is
   deliberately dropped on save — the lines vanished and it read as "adding
   items does not work".

   `flex: 1` is shorthand for `1 1 0%`. A zero basis means the box has NO width
   except what flex-grow hands it, so on any engine that declines to grow a form
   control there is nothing left. MEASURED in the browser: with growth
   suppressed, a 0% basis leaves the box at 22px; an `auto` basis leaves it at
   239px in the same row. 22px is the screenshot.

   `min-width: 0` made it worse by removing the automatic minimum that would
   otherwise have saved it. That value is right for a GRID item whose content
   would push a column off-canvas (1.36.0) and wrong here.

   ⚠️ EVERY OTHER GROWABLE FORM CONTROL IN THIS SUITE ALREADY CARRIES A FLOOR —
   .pbjcrm-predicate input 140px, .pbjcrm-toolbar select 130px, pbj-inv's
   toolbar select 130px. This rule was the only one without one. Keep the floor
   when editing: growth is an enhancement, the floor is the guarantee. */
.pbjcrm-task-editor-row input {
	flex: 1 1 auto;
	min-width: 140px;
}

.pbjcrm-task-editor-add {
	margin: 6px 0 0;
}

/* ------------------------------------------------- Segments / duplicates */

.pbjcrm-checklist {
	display: flex;
	flex-wrap: wrap;
	gap: 2px 4px;
	max-height: 140px;
	overflow: auto;
	border: 1px solid var(--pbjcrm-border);
	border-radius: var(--pbjcrm-radius);
	background: var(--pbjcrm-surface);
	padding: 6px 8px;
	font-size: 13px;
}

.pbjcrm-predicates {
	display: flex;
	flex-direction: column;
	gap: 6px;
	margin-bottom: 8px;
}

.pbjcrm-predicate {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
	align-items: center;
}

.pbjcrm-predicate input[type="text"] {
	flex: 1;
	min-width: 140px;
	width: auto;
}

.pbjcrm-preview {
	background: #eef3f8;
	background: var(--pbjcrm-accent-soft);
	border: 1px solid #c6d6e6;
	border-color: color-mix(in srgb, var(--pbjcrm-accent) 30%, var(--pbjcrm-surface));
	color: #1f4e79;
	color: var(--pbjcrm-text);
	border-radius: var(--pbjcrm-radius);
	padding: 8px 12px;
	font-size: 14px;
}

.pbjcrm-count {
	font-weight: 700;
}

.pbjcrm-mergepane {
	background: var(--pbjcrm-surface-alt);
}

.pbjcrm-mergetable td {
	vertical-align: middle;
	font-size: 13px;
	overflow-wrap: anywhere;
}

.pbjcrm-mergetable .pbjcrm-inline-label {
	align-items: flex-start;
}

/* ------------------------------- One record per firm: the business switch */

/* ⛔ SMALL SINCE 1.36.0. This was a bordered, shaded panel carrying a 22px
   checkbox, a bold 14px label and a sentence of explanation — the single
   largest element on the record, for a switch most people set once. It is now
   one inline line; the sentence became the line's `title`. */
.pbjcrm-bizswitch {
	margin: 4px 0;
	padding: 0;
	border: 0;
	background: none;
}

.pbjcrm-switchline {
	display: inline-flex;
	align-items: center;
	gap: 6px;
}

/* ⚠️ STILL 18px, NOT 14px. It is smaller than it was, and it is deliberately
   not as small as a default checkbox: the people using this are in vans, often
   wearing gloves, and a target they cannot hit is not a saving. */
.pbjcrm-switchline input[type="checkbox"] {
	width: 18px;
	height: 18px;
	margin: 0;
	flex: none;
	accent-color: var(--pbjcrm-accent);
	cursor: pointer;
}

.pbjcrm-switchline label {
	margin: 0;
	font-weight: 500;
	font-size: 13px;
	color: var(--pbjcrm-muted);
	cursor: pointer;
}

.pbjcrm-bizswitch .pbjcrm-field-hint {
	margin: 4px 0 0;
}

.pbjcrm-bizswitch-msg p {
	margin: 6px 0 0;
}

.pbjcrm-badge-business {
	background: #eef3f8;
	background: color-mix(in srgb, var(--pbjcrm-accent) 14%, var(--pbjcrm-surface));
	border-color: #c6d6e6;
	border-color: color-mix(in srgb, var(--pbjcrm-accent) 34%, var(--pbjcrm-surface));
	color: #135e96;
	color: var(--pbjcrm-accent-strong);
}

/* ------------------------- The details, on the front of the record (DB v6) */

.pbjcrm-contactcard {
	margin-bottom: var(--pbjcrm-space);
}

.pbjcrm-contactcard h3 {
	margin: 0 0 var(--pbjcrm-space-sm);
	font-size: 15px;
}

.pbjcrm-inline-rows {
	display: flex;
	flex-direction: column;
}

.pbjcrm-inline-row {
	display: grid;
	grid-template-columns: minmax(120px, 170px) minmax(0, 1fr);
	gap: 4px var(--pbjcrm-space);
	align-items: start;
	padding: 8px 0;
	border-top: 1px solid var(--pbjcrm-border);
}

.pbjcrm-inline-row:first-child {
	border-top: none;
}

.pbjcrm-inline-label {
	color: var(--pbjcrm-muted);
	font-size: 13px;
	font-weight: 600;
	padding-top: 3px;
}

.pbjcrm-inline-value {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 8px;
	min-width: 0;
}

.pbjcrm-inline-value input[type="text"],
.pbjcrm-inline-value input[type="email"],
.pbjcrm-inline-value input[type="url"],
.pbjcrm-inline-value textarea {
	flex: 1 1 200px;
	min-width: 0;
}

.pbjcrm-inline-value .pbjcrm-grid-2,
.pbjcrm-inline-value .pbjcrm-field-hint,
.pbjcrm-inline-btns {
	flex: 1 1 100%;
	margin: 0;
}

.pbjcrm-inline-text {
	overflow-wrap: anywhere;
	white-space: pre-wrap;
}

/* ⛔ TYPE-SCOPED TO `a` ON PURPOSE, AND THAT IS THE TRAP (item 8, 1.56.0).
   An inline value is a LINK when it is an <a> and plain text when it is a
   <span>, so the colour cannot live on the bare class. But a <button> is
   neither, and a button carrying only this class matched nothing and rendered
   in the browser's grey system chrome — which is exactly what happened to the
   phone rows from 1.49.0, when the phone-action registry swapped that element
   from <a> to <button> and nobody moved the colour with it.

   ⛔ SO: A BUTTON THAT WANTS TO LOOK LIKE A LINK CARRIES .pbjcrm-linkbtn
   (line 399) AS WELL. Do not widen this rule to the bare class to "fix" it —
   that would paint every read-only <span> value link-blue and make plain text
   look clickable, which is the opposite mistake. */
a.pbjcrm-inline-text {
	color: var(--pbjcrm-link);
}

/* An empty box says so quietly instead of leaving a hole. */
.pbjcrm-notset {
	color: var(--pbjcrm-muted);
	font-style: italic;
}

/* Where they are on the internet: one round button each, big enough to tap. */
.pbjcrm-sociallist {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
}

.pbjcrm-social {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 34px;
	height: 34px;
	padding: 0 8px;
	border-radius: 999px;
	border: 1px solid var(--pbjcrm-border-strong);
	background: var(--pbjcrm-surface-alt);
	color: var(--pbjcrm-text);
	font-size: 13px;
	font-weight: 700;
	line-height: 1;
	text-decoration: none;
}

.pbjcrm-social:hover,
.pbjcrm-social:focus {
	border-color: var(--pbjcrm-accent);
	background: var(--pbjcrm-accent-soft);
	color: var(--pbjcrm-accent-strong);
	text-decoration: none;
}

.pbjcrm-social-facebook {
	border-color: #1877f2;
	color: #1877f2;
}

.pbjcrm-social-instagram {
	border-color: #c13584;
	color: #c13584;
}

.pbjcrm-social-linkedin {
	border-color: #0a66c2;
	color: #0a66c2;
}

/* X and TikTok are brand-BLACK, and a hardcoded near-black disappears
 * entirely on the Dark preset. Their marks invert to white on a dark
 * background anyway, and --pbjcrm-text is by definition "the readable
 * foreground for whichever theme is on", so follow it. The hex stays as
 * the var() fallback, which is exactly the old value. */
.pbjcrm-social-x {
	border-color: var(--pbjcrm-text, #1d2327);
	color: var(--pbjcrm-text, #1d2327);
}

.pbjcrm-social-youtube {
	border-color: #c4302b;
	color: #c4302b;
}

.pbjcrm-social-tiktok {
	border-color: var(--pbjcrm-text, #010101);
	color: var(--pbjcrm-text, #010101);
}

/* --------------------------------------- People who work at a business */

.pbjcrm-people-panel {
	margin-bottom: var(--pbjcrm-space);
}

.pbjcrm-people-panel h3 {
	margin: 0 0 4px;
	font-size: 15px;
}

.pbjcrm-people-panel > p {
	margin: 0 0 var(--pbjcrm-space-sm);
}

.pbjcrm-people-panel .pbjcrm-person-row {
	cursor: default;
}

/* ------------------ Drop a person here and they become a business (DB v6) */

.pbjcrm-dropzone {
	border: 2px dashed var(--pbjcrm-border-strong);
	border-radius: var(--pbjcrm-radius);
	background: var(--pbjcrm-surface-alt);
	padding: var(--pbjcrm-space-sm) var(--pbjcrm-space);
	margin-bottom: var(--pbjcrm-space-sm);
	text-align: center;
	font-size: 13px;
}

.pbjcrm-dropzone p {
	margin: 4px 0 0;
}

.pbjcrm-dropzone.pbjcrm-droptarget,
.pbjcrm-split-col.pbjcrm-droptarget {
	border-color: var(--pbjcrm-accent);
	background: #eef3f8;
	background: var(--pbjcrm-accent-soft);
}

.pbjcrm-split-col.pbjcrm-droptarget {
	border: 2px dashed var(--pbjcrm-accent);
	border-radius: var(--pbjcrm-radius);
}

/* ----------------------------------------------------------- Responsive */

@media (max-width: 782px) {
	.pbjcrm-portal {
		font-size: 14px;
		padding: var(--pbjcrm-space);
	}

	/* ⛔ ONE COLUMN, AND NOTHING OFF CANVAS (Jake, 2026-08-14, with a screenshot
	   of a record clipped on BOTH edges).

	   `grid-template-columns: 1fr` alone was not enough, and that is the whole
	   lesson: a grid COLUMN is 1fr wide, but a grid ITEM whose content has an
	   intrinsic minimum wider than the column does not shrink — `min-width` on a
	   grid item defaults to `auto`, meaning "at least as wide as my content". A
	   long email address, a wide table or a row of chips therefore pushed the
	   column past the viewport and took the whole record with it. `min-width: 0`
	   on the items is what lets them actually be 1fr. */
	.pbjcrm-profile-layout {
		display: grid;
		grid-template-columns: minmax(0, 1fr);
	}

	.pbjcrm-profile-layout > * {
		min-width: 0;
	}

	/* Add Work becomes the dropdown and the buttons come off — Jake's ask, and
	   the reason both shapes are always built.

	   ⛔ THE SELECTORS CARRY A SECOND CLASS TO WIN ON SPECIFICITY, AND THAT IS
	   NOT TIDINESS. The button row carries `pbjcrm-cal-day-actions` as well, and
	   that class declares `display: flex` at line ~2736 — LATER in this file
	   than this media block. Same specificity, later wins, so a bare
	   `.pbjcrm-addwork-buttons { display: none }` here lost silently and the
	   phone got BOTH shapes at once. Caught by measuring the computed style
	   under the widen-the-breakpoint probe, not by reading the diff.

	   ⛔ THE SCOPE IS .pbjcrm-record-actions, NOT .pbjcrm-block1 (2026-08-19).
	   Add Work is back inside Block 1, so the old selector would match again —
	   but the round trip that moved it out and back is the point: a swap keyed
	   on WHERE THE ROW HANGS silently stops matching the day somebody rearranges
	   the record, and the failure is BOTH shapes on a phone at once, which is
	   invisible on a desktop. `.pbjcrm-record-actions` is the row's OWN wrapper,
	   built by recordActionsRow() and carrying both shapes, so it travels with
	   them wherever they go. Same 0,2,0 specificity as the selector it replaced,
	   so it still out-ranks `.pbjcrm-addwork-picker`'s desktop rule above and
	   still beats `.pbjcrm-cal-day-actions`'s later `display: flex`. */
	/* ⛔ SAME TREATMENT AS THE TIMELINE SWAP, AND DELIBERATELY SO EVEN THOUGH
	   THIS PAIR IS NOT BROKEN TODAY. It survives only because no theme preset
	   happens to set `display` on `.pbjcrm-cal-day-actions` — that is luck, not
	   design, and the timeline pair proves what happens when the luck runs out. */
	.pbjcrm-record-actions .pbjcrm-addwork-buttons {
		display: none !important;
	}

	.pbjcrm-record-actions .pbjcrm-addwork-picker {
		display: block !important;
	}

	.pbjcrm-record-actions .pbjcrm-addwork-select {
		width: 100%;
	}

	/* The phone swaps them over: the ten buttons come off, the select goes on.
	   ⛔ SCOPED TO .pbjcrm-block1 FOR THE SAME REASON Add Work is — `.pbjcrm-tabs`
	   declares its own `display` later in this file, and a bare selector here
	   would lose to it at equal specificity and leave both shapes on screen. */
	/* ⛔ `!important` — see the note beside the desktop half. `.pbjcrm-tabs-segment
	   .pbjcrm-tabs { display: inline-flex }` is the same specificity and later in
	   the file, so without this the phone shows BOTH shapes. */
	.pbjcrm-block1 .pbjcrm-timelineview-tabs {
		display: none !important;
	}

	.pbjcrm-block1 .pbjcrm-timelineview-select,
	.pbjcrm-block1 .pbjcrm-timelineview-label {
		display: block !important;
	}

	.pbjcrm-block1 .pbjcrm-timelineview-select {
		width: 100%;
		max-width: none;
	}

	.pbjcrm-deal-list {
		grid-template-columns: 1fr;
	}

	/* Two columns side by side stop making sense on a phone: the businesses
	   go above the people, and the buttons on each person's card do the work
	   the drag did. */
	.pbjcrm-split {
		grid-template-columns: 1fr;
	}

	.pbjcrm-table th.pbjcrm-col-optional,
	.pbjcrm-table td.pbjcrm-col-optional {
		display: none;
	}

	.pbjcrm-toolbar input[type="search"],
	.pbjcrm-toolbar input[type="text"] {
		width: 100%;
	}

	.pbjcrm-toolbar select {
		flex: 1;
		min-width: 130px;
	}

	.pbjcrm-nav {
		gap: 4px;
	}

	.pbjcrm-nav-item {
		padding: 6px 10px;
		font-size: 13px;
	}

	.pbjcrm-subnav-item {
		padding: 5px 9px;
		font-size: 12px;
	}

	.pbjcrm-card {
		padding: var(--pbjcrm-space);
	}
}

/* A tab bar is useful while it fits and becomes horizontal scrolling once it
   does not. Collapse both levels into one accessible drawer before that point;
   sub-tabs remain a visibly indented second group inside the same menu. */
@media (max-width: 1100px) {
	.pbjcrm-nav-toggle {
		display: block;
	}

	.pbjcrm-portal > .pbjcrm-nav,
	.pbjcrm-portal > .pbjcrm-subnav:not([hidden]) {
		display: none !important;
	}

	.pbjcrm-portal.pbjcrm-nav-open > .pbjcrm-nav {
		display: flex !important;
		flex-direction: column;
		align-items: stretch;
		border-radius: 0;
	}

	.pbjcrm-portal.pbjcrm-nav-open > .pbjcrm-subnav:not([hidden]) {
		display: flex !important;
		flex-direction: column;
		align-items: stretch;
		padding-left: calc(var(--pbjcrm-space) + 14px);
	}

	.pbjcrm-nav-item,
	.pbjcrm-subnav-item {
		justify-content: flex-start !important;
		width: 100%;
		margin: 0;
		text-align: left;
	}

	.pbjcrm-subnav[hidden] + .pbjcrm-view,
	.pbjcrm-subnav + .pbjcrm-view {
		padding-top: var(--pbjcrm-space);
	}
}

@media (max-width: 480px) {
	.pbjcrm-portal {
		padding: var(--pbjcrm-space-sm);
		border-radius: 0;
	}

	.pbjcrm-table th.pbjcrm-col-narrow-hide,
	.pbjcrm-table td.pbjcrm-col-narrow-hide {
		display: none;
	}

	.pbjcrm-profile-head {
		gap: 12px;
	}

	.pbjcrm-photo-wrap,
	.pbjcrm-photozone {
		width: 112px;
	}

	.pbjcrm-photozone {
		height: 112px;
	}

	.pbjcrm-profile-name {
		font-size: 19px;
	}

	.pbjcrm-tab {
		padding: 6px 10px;
		font-size: 13px;
	}

	.pbjcrm-person-actions .pbjcrm-btn {
		flex: 1;
		text-align: center;
	}

	.pbjcrm-predicate select,
	.pbjcrm-predicate input[type="text"] {
		width: 100%;
		flex: none;
	}

	/* One column: the label sits above what it says, and the paging row
	   stacks instead of squeezing. */
	.pbjcrm-inline-row {
		grid-template-columns: minmax(0, 1fr);
		gap: 2px;
	}

	.pbjcrm-inline-label {
		padding-top: 0;
	}

	.pbjcrm-perpage {
		margin-left: 0;
	}

	.pbjcrm-pager-btns .pbjcrm-btn {
		flex: 1;
		text-align: center;
	}

	.pbjcrm-toasts {
		right: 8px;
		left: 8px;
		bottom: 8px;
		max-width: none;
	}

	.pbjcrm-grid-2 {
		grid-template-columns: 1fr;
	}
}

/* 360px sanity: nothing may force a horizontal scrollbar. */
@media (max-width: 360px) {
	.pbjcrm-portal {
		padding: 10px;
	}

	.pbjcrm-view-head h2 {
		font-size: 17px;
	}

	.pbjcrm-table th,
	.pbjcrm-table td {
		padding: 6px 7px;
		font-size: 13px;
	}

	.pbjcrm-btn {
		padding: 7px 12px;
	}

	.pbjcrm-summary-cards {
		grid-template-columns: 1fr;
	}
}

/* ------------------------------------------------------------- Calendar */

/* The colour of each kind of thing. One custom property per category, so a
   site with a different accent still gets a calendar that reads clearly, and
   a module adding its own category only has to add one line here.

   Every category (17.5, widened 2026-08-13) reads the owner's record-type
   colours from the theme (--pbjcrm-rec-*), so the calendar, the task cards,
   the Today queue and any module edge all say the same thing about the same
   kind of record. The hex fallbacks only fire when the theme's inline CSS is
   absent — they are the pre-17.5 calendar colours, unchanged.

   ⛔ --pbjcrm-cal-visits IS NOT OPTIONAL FURNITURE, IT IS A BUG FIX. `visits`
   has been a registered calendar category since pbj-dispatch 1.1.0 and had no
   matching .pbjcrm-cal-cat-visits rule, so every visit fell through the
   `var(--pbjcrm-cal-hue, var(--pbjcrm-cal-people))` fallback below and drew in
   PEOPLE BLUE — a booked job and "somebody added a contact" were the same
   colour on the calendar. The fallback is doing exactly what it was written to
   do, which is why nothing ever errored: a missing category is invisible, not
   broken. Any future category needs BOTH a token here AND a .pbjcrm-cal-cat-*
   rule below, or it inherits the same silent blue. */

.pbjcrm-portal {
	--pbjcrm-cal-people: var(--pbjcrm-rec-contact, #2271b1);
	--pbjcrm-cal-tasks: var(--pbjcrm-rec-task, #8c5e00);
	--pbjcrm-cal-tickets: var(--pbjcrm-rec-ticket, #6d3fa8);
	--pbjcrm-cal-invoices: var(--pbjcrm-rec-invoice, #1a7a3c);
	--pbjcrm-cal-visits: var(--pbjcrm-rec-visit, #0f766e);
	--pbjcrm-cal-deals: var(--pbjcrm-rec-deal, #b3541e);
	--pbjcrm-cal-marketing: var(--pbjcrm-rec-marketing, #0f7b8a);
	--pbjcrm-cal-imports: var(--pbjcrm-rec-import, #5b6770);
	--pbjcrm-cal-system: var(--pbjcrm-rec-system, #7a8188);
}

.pbjcrm-cal-controls {
	margin-bottom: var(--pbjcrm-space);
}

.pbjcrm-cal-row {
	display: flex;
	flex-wrap: wrap;
	align-items: flex-end;
	gap: 8px;
	margin-bottom: 8px;
}

.pbjcrm-cal-row-fields {
	align-items: flex-end;
}

.pbjcrm-cal-field {
	display: flex;
	flex-direction: column;
	gap: 3px;
	font-size: 13px;
	color: var(--pbjcrm-muted);
}

.pbjcrm-cal-kinds {
	align-items: center;
	border-top: 1px solid var(--pbjcrm-border);
	padding-top: 8px;
}

.pbjcrm-cal-kinds-label {
	font-size: 13px;
	color: var(--pbjcrm-muted);
	margin-right: 2px;
}

.pbjcrm-cal-kind {
	display: inline-flex;
	align-items: center;
	gap: 5px;
	font-size: 13px;
	padding: 3px 9px 3px 5px;
	border: 1px solid var(--pbjcrm-border);
	border-radius: 999px;
	cursor: pointer;
	background: var(--pbjcrm-surface);
	border-left-width: 4px;
}

.pbjcrm-cal-kind:hover {
	background: var(--pbjcrm-surface-alt);
}

.pbjcrm-cal-kind input {
	margin: 0;
}

/* The subscription-link card under the calendar (suite row 6.7). */

.pbjcrm-cal-feed {
	margin-top: var(--pbjcrm-space);
}

.pbjcrm-cal-feed-title {
	margin: 0 0 6px;
}

.pbjcrm-cal-feed-row {
	display: flex;
	align-items: center;
	gap: 8px;
	margin: 6px 0;
}

.pbjcrm-cal-feed-url {
	flex: 1 1 auto;
	min-width: 0;
	font-size: 13px;
}

.pbjcrm-cal-feed-actions {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
	margin-top: 6px;
}

.pbjcrm-cal-summary {
	margin: 0 0 8px;
}

.pbjcrm-cal-notice {
	background: var(--pbjcrm-accent-soft);
	border: 1px solid var(--pbjcrm-border);
	border-left: 4px solid var(--pbjcrm-warning);
	border-radius: var(--pbjcrm-radius-sm);
	padding: 8px 12px;
	margin: 0 0 var(--pbjcrm-space);
	font-size: 14px;
}

.pbjcrm-cal-split {
	display: grid;
	grid-template-columns: minmax(0, 2.1fr) minmax(0, 1fr);
	gap: var(--pbjcrm-space);
	align-items: start;
}

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

.pbjcrm-cal-head {
	margin-bottom: 2px;
}

.pbjcrm-cal-dayname {
	font-size: 12px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.03em;
	color: var(--pbjcrm-muted);
	text-align: center;
	padding: 4px 0;
}

.pbjcrm-cal-cell {
	display: flex;
	flex-direction: column;
	align-items: stretch;
	gap: 3px;
	min-height: 92px;
	padding: 5px;
	text-align: left;
	font: inherit;
	color: inherit;
	background: var(--pbjcrm-surface);
	border: 1px solid var(--pbjcrm-border);
	border-radius: var(--pbjcrm-radius-sm);
	cursor: pointer;
	overflow: hidden;
}

.pbjcrm-cal-cell:hover {
	border-color: var(--pbjcrm-border-strong);
}

.pbjcrm-cal-cell:focus-visible {
	outline: 2px solid var(--pbjcrm-accent);
	outline-offset: 1px;
}

/* Days that fall outside the dates asked for still get drawn, so the weeks
   stay whole — but they are dimmed and do nothing when clicked. */
.pbjcrm-cal-outside {
	background: var(--pbjcrm-surface-alt);
	opacity: 0.45;
	cursor: default;
}

.pbjcrm-cal-today {
	border-color: var(--pbjcrm-accent);
	box-shadow: inset 0 0 0 1px var(--pbjcrm-accent);
}

.pbjcrm-cal-picked {
	background: var(--pbjcrm-accent-soft);
	border-color: var(--pbjcrm-accent-strong);
}

.pbjcrm-cal-num {
	font-size: 12px;
	font-weight: 700;
	color: var(--pbjcrm-text-soft);
}

.pbjcrm-cal-today .pbjcrm-cal-num {
	color: var(--pbjcrm-accent-strong);
}

.pbjcrm-cal-chips {
	display: flex;
	flex-direction: column;
	gap: 2px;
	min-width: 0;
}

.pbjcrm-cal-chip {
	display: block;
	font-size: 11px;
	line-height: 1.35;
	padding: 1px 4px;
	border-radius: var(--pbjcrm-radius-sm);
	border-left: 3px solid var(--pbjcrm-cal-people);
	background: var(--pbjcrm-surface-alt);
	color: var(--pbjcrm-text);
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

/* Something that has not happened yet is outlined rather than filled, so a
   glance tells you what is behind you and what is still ahead. */
.pbjcrm-cal-chip.pbjcrm-cal-ahead {
	background: transparent;
	border: 1px dashed var(--pbjcrm-border-strong);
	border-left: 3px solid var(--pbjcrm-cal-people);
}

.pbjcrm-cal-more {
	font-size: 11px;
	color: var(--pbjcrm-muted);
	padding-left: 4px;
}

.pbjcrm-cal-day-panel {
	background: var(--pbjcrm-surface);
	border: 1px solid var(--pbjcrm-border);
	border-radius: var(--pbjcrm-radius);
	padding: var(--pbjcrm-space);
	position: sticky;
	top: 12px;
	max-height: calc(100vh - 40px);
	overflow-y: auto;
}

.pbjcrm-cal-day-title {
	margin: 0 0 10px;
	font-size: 15px;
}

/* The List / Agenda view (1.15.0, phase 6.4). One flat run of days; the month
   grid is untouched and both remain available. Deliberately reuses
   .pbjcrm-cal-day-list and .pbjcrm-cal-item so a row looks the same wherever
   it is drawn. */
.pbjcrm-cal-agenda {
	display: flex;
	flex-direction: column;
	gap: var(--pbjcrm-space);
}

.pbjcrm-cal-agenda-day {
	background: var(--pbjcrm-surface);
	border: 1px solid var(--pbjcrm-border);
	border-radius: var(--pbjcrm-radius);
	padding: var(--pbjcrm-space);
}

.pbjcrm-cal-agenda-today {
	border-color: var(--pbjcrm-accent);
}

.pbjcrm-cal-agenda-head {
	margin: 0 0 10px;
	font-size: 15px;
	font-weight: 600;
	color: var(--pbjcrm-text);
}

/* The heading is a real button on days you can still start something on, so
   it has to shed the browser's button chrome and keep the heading's look. */
button.pbjcrm-cal-agenda-open {
	display: block;
	width: 100%;
	text-align: left;
	background: none;
	border: 0;
	padding: 0;
	cursor: pointer;
	font: inherit;
	font-weight: 600;
	color: var(--pbjcrm-text);
}

button.pbjcrm-cal-agenda-open:hover,
button.pbjcrm-cal-agenda-open:focus-visible {
	color: var(--pbjcrm-accent);
}

.pbjcrm-cal-viewpick {
	display: flex;
	align-items: center;
	gap: 8px;
}

.pbjcrm-cal-viewpick > span {
	color: var(--pbjcrm-muted);
	font-size: 13px;
}

.pbjcrm-cal-viewtabs {
	margin: 0;
}

/* What you can start on this day (1.14.0). Sits between the date and the
   day's list, separated by a rule, so it reads as "here is the day, here is
   what you can add to it, here is what is already on it" rather than as one
   more thing that happened. */
.pbjcrm-cal-day-actions {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
	margin: 0 0 10px;
	padding-bottom: 10px;
	border-bottom: 1px solid var(--pbjcrm-border);
}

.pbjcrm-cal-day-form:not(:empty) {
	margin-bottom: 10px;
}

.pbjcrm-cal-day-list {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: 10px;
}

.pbjcrm-cal-item {
	border-left: 3px solid var(--pbjcrm-cal-people);
	padding: 2px 0 2px 10px;
}

.pbjcrm-cal-item-meta {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 6px;
	margin-bottom: 2px;
}

.pbjcrm-cal-tag {
	font-size: 11px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.03em;
	color: var(--pbjcrm-cal-people);
}

.pbjcrm-cal-time,
.pbjcrm-cal-who {
	font-size: 12px;
	color: var(--pbjcrm-muted);
}

.pbjcrm-cal-item-title {
	display: inline-block;
	font-weight: 600;
	text-align: left;
}

.pbjcrm-cal-item-sub {
	margin: 2px 0 0;
	font-size: 13px;
	color: var(--pbjcrm-text-soft);
}

/* Per-category colour. Applied to the chip's edge, the list item's edge, the
   tick-box pill's edge and the little tag above each entry. */
.pbjcrm-cal-cat-people { --pbjcrm-cal-hue: var(--pbjcrm-cal-people); }
.pbjcrm-cal-cat-tasks { --pbjcrm-cal-hue: var(--pbjcrm-cal-tasks); }
.pbjcrm-cal-cat-tickets { --pbjcrm-cal-hue: var(--pbjcrm-cal-tickets); }
.pbjcrm-cal-cat-invoices { --pbjcrm-cal-hue: var(--pbjcrm-cal-invoices); }
.pbjcrm-cal-cat-visits { --pbjcrm-cal-hue: var(--pbjcrm-cal-visits); }
.pbjcrm-cal-cat-deals { --pbjcrm-cal-hue: var(--pbjcrm-cal-deals); }
.pbjcrm-cal-cat-marketing { --pbjcrm-cal-hue: var(--pbjcrm-cal-marketing); }
.pbjcrm-cal-cat-imports { --pbjcrm-cal-hue: var(--pbjcrm-cal-imports); }
.pbjcrm-cal-cat-system { --pbjcrm-cal-hue: var(--pbjcrm-cal-system); }

.pbjcrm-cal-kind-people { border-left-color: var(--pbjcrm-cal-people); }
.pbjcrm-cal-kind-tasks { border-left-color: var(--pbjcrm-cal-tasks); }
.pbjcrm-cal-kind-tickets { border-left-color: var(--pbjcrm-cal-tickets); }
.pbjcrm-cal-kind-invoices { border-left-color: var(--pbjcrm-cal-invoices); }
.pbjcrm-cal-kind-visits { border-left-color: var(--pbjcrm-cal-visits); }
.pbjcrm-cal-kind-deals { border-left-color: var(--pbjcrm-cal-deals); }
.pbjcrm-cal-kind-marketing { border-left-color: var(--pbjcrm-cal-marketing); }
.pbjcrm-cal-kind-imports { border-left-color: var(--pbjcrm-cal-imports); }
.pbjcrm-cal-kind-system { border-left-color: var(--pbjcrm-cal-system); }

.pbjcrm-cal-chip[class*="pbjcrm-cal-cat-"],
.pbjcrm-cal-item[class*="pbjcrm-cal-cat-"] {
	border-left-color: var(--pbjcrm-cal-hue, var(--pbjcrm-cal-people));
}

.pbjcrm-cal-tag[class*="pbjcrm-cal-cat-"] {
	color: var(--pbjcrm-cal-hue, var(--pbjcrm-cal-people));
}

/* Narrow screens: the day list drops underneath the grid, and the squares
   shrink to the date plus a count rather than trying to fit names in. */
@media (max-width: 900px) {
	.pbjcrm-cal-split {
		grid-template-columns: minmax(0, 1fr);
	}

	.pbjcrm-cal-day-panel {
		position: static;
		max-height: none;
	}

	.pbjcrm-cal-cell {
		min-height: 64px;
	}

	.pbjcrm-cal-chip {
		font-size: 10px;
	}
}

@media (max-width: 600px) {
	.pbjcrm-cal-cell {
		min-height: 52px;
	}

	.pbjcrm-cal-chip {
		/* Too narrow for words — the chips become coloured bars you can still
		   count and colour-match against the tick-boxes above. */
		height: 4px;
		padding: 0;
		border-left: none;
		background: var(--pbjcrm-cal-hue, var(--pbjcrm-cal-people));
		text-indent: -9999px;
	}

	.pbjcrm-cal-chip.pbjcrm-cal-ahead {
		background: transparent;
		border: 1px solid var(--pbjcrm-cal-hue, var(--pbjcrm-cal-people));
	}

	.pbjcrm-cal-more {
		font-size: 10px;
	}
}

/* --------------------------------------------- Motion / contrast respect */

@media (prefers-reduced-motion: reduce) {
	.pbjcrm-portal * {
		transition: none !important;
	}
}

/* ---------------------------------------------------------------------------
   "Anything else" — record-level custom fields (DB v9).

   Visually a list of small stacked blocks rather than a table, because a value
   here is often several lines (an address, a set of access instructions) and a
   table row would squash it. Imported rows are dimmed and locked: the sync owns
   them, and it should LOOK like they are not yours to type in.
   --------------------------------------------------------------------------- */

.pbjcrm-recordfields-list {
	display: flex;
	flex-direction: column;
	gap: var(--pbjcrm-space, 12px);
	margin: 10px 0;
}

.pbjcrm-recordfield {
	display: flex;
	flex-direction: column;
	gap: 4px;
	padding: 8px;
	border: 1px solid var(--pbjcrm-border);
	border-radius: var(--pbjcrm-radius);
	background: var(--pbjcrm-surface-alt);
}

.pbjcrm-recordfield-label {
	font-weight: 600;
}

.pbjcrm-recordfield-value {
	width: 100%;
	resize: vertical;
}

.pbjcrm-recordfield-locked {
	opacity: .75;
	background: var(--pbjcrm-bg);
}

.pbjcrm-recordfields-actions {
	display: flex;
	gap: 6px;
	flex-wrap: wrap;
}

/* ===========================================================================
   TABS AND SUB-TABS AS A THEMING ELEMENT (1.1.0)

   Jake asked for the sub-tabs to be "selectable as a theming element and a
   standard button and color for the default theme (not just text)". So the
   SHAPE is a choice (Settings -> Tab style, four options) and the COLOURS come
   from the tab-* / pill-* theme variables.

   How it works: PBJ_CRM_Theme::body_classes() puts pbjcrm-tabs-<style> on the
   portal shell, and each block below only applies under its own class. The
   base rules are shared, so a style only has to say what is different about it.
   =========================================================================== */

.pbjcrm-tab,
.pbjcrm-subnav-item {
	font-weight: 600;
	transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}

/* ---- Underline (default): a line under the one you are on. ---- */
.pbjcrm-tabs-underline .pbjcrm-tabs,
.pbjcrm-tabs-underline .pbjcrm-subnav {
	border-bottom: 1px solid var(--pbjcrm-tab-border, var(--pbjcrm-border));
	gap: 2px;
}

.pbjcrm-tabs-underline .pbjcrm-tab,
.pbjcrm-tabs-underline .pbjcrm-subnav-item {
	background: transparent;
	border: none;
	border-bottom: 3px solid transparent;
	border-radius: 0;
	color: var(--pbjcrm-tab-text, var(--pbjcrm-text-soft));
	margin-bottom: -1px;
}

.pbjcrm-tabs-underline .pbjcrm-tab:hover,
.pbjcrm-tabs-underline .pbjcrm-subnav-item:hover {
	background: var(--pbjcrm-tab-hover-bg, var(--pbjcrm-surface-alt));
	color: var(--pbjcrm-text);
}

.pbjcrm-tabs-underline .pbjcrm-tab.pbjcrm-active,
.pbjcrm-tabs-underline .pbjcrm-subnav-item.pbjcrm-active {
	background: transparent;
	border-bottom-color: var(--pbjcrm-tab-active-line, var(--pbjcrm-accent));
	color: var(--pbjcrm-text);
	font-weight: 700;
}

/* ---- Rounded buttons: each tab is its own pill. Best on touch. ---- */
.pbjcrm-tabs-pill .pbjcrm-tabs,
.pbjcrm-tabs-pill .pbjcrm-subnav {
	border-bottom: none;
	gap: 6px;
}

.pbjcrm-tabs-pill .pbjcrm-tab,
.pbjcrm-tabs-pill .pbjcrm-subnav-item {
	background: var(--pbjcrm-pill-bg, var(--pbjcrm-surface-alt));
	border: 1px solid var(--pbjcrm-pill-border, var(--pbjcrm-border));
	border-radius: var(--pbjcrm-radius-pill, 999px);
	color: var(--pbjcrm-pill-text, var(--pbjcrm-text-soft));
	padding: 7px 16px;
}

.pbjcrm-tabs-pill .pbjcrm-tab:hover,
.pbjcrm-tabs-pill .pbjcrm-subnav-item:hover {
	border-color: var(--pbjcrm-border-strong);
	color: var(--pbjcrm-text);
}

.pbjcrm-tabs-pill .pbjcrm-tab.pbjcrm-active,
.pbjcrm-tabs-pill .pbjcrm-subnav-item.pbjcrm-active {
	background: var(--pbjcrm-pill-on-bg, var(--pbjcrm-accent));
	border-color: var(--pbjcrm-pill-on-border, var(--pbjcrm-accent));
	color: var(--pbjcrm-pill-on-text, var(--pbjcrm-accent-contrast));
}

/* ---- Joined buttons: one bar, like a row of switches. ---- */
.pbjcrm-tabs-segment .pbjcrm-tabs,
.pbjcrm-tabs-segment .pbjcrm-subnav {
	border: 1px solid var(--pbjcrm-tab-border, var(--pbjcrm-border));
	border-radius: var(--pbjcrm-radius);
	background: var(--pbjcrm-surface-alt);
	padding: 3px;
	gap: 2px;
	display: inline-flex;
	width: auto;
	max-width: 100%;
	overflow-x: auto;
}

.pbjcrm-tabs-segment .pbjcrm-tab,
.pbjcrm-tabs-segment .pbjcrm-subnav-item {
	background: transparent;
	border: none;
	border-radius: calc(var(--pbjcrm-radius) - 2px);
	color: var(--pbjcrm-tab-text, var(--pbjcrm-text-soft));
	white-space: nowrap;
}

.pbjcrm-tabs-segment .pbjcrm-tab:hover,
.pbjcrm-tabs-segment .pbjcrm-subnav-item:hover {
	background: var(--pbjcrm-surface);
	color: var(--pbjcrm-text);
}

.pbjcrm-tabs-segment .pbjcrm-tab.pbjcrm-active,
.pbjcrm-tabs-segment .pbjcrm-subnav-item.pbjcrm-active {
	background: var(--pbjcrm-tab-active-bg, var(--pbjcrm-accent));
	color: var(--pbjcrm-tab-active-text, var(--pbjcrm-accent-contrast));
	box-shadow: var(--pbjcrm-shadow);
}

/* ---- Folder tabs: boxed, joined to the panel below. ---- */
.pbjcrm-tabs-boxed .pbjcrm-tabs,
.pbjcrm-tabs-boxed .pbjcrm-subnav {
	border-bottom: 1px solid var(--pbjcrm-tab-border, var(--pbjcrm-border));
	gap: 3px;
}

.pbjcrm-tabs-boxed .pbjcrm-tab,
.pbjcrm-tabs-boxed .pbjcrm-subnav-item {
	background: var(--pbjcrm-surface-alt);
	border: 1px solid var(--pbjcrm-tab-border, var(--pbjcrm-border));
	border-bottom: none;
	border-radius: var(--pbjcrm-radius) var(--pbjcrm-radius) 0 0;
	color: var(--pbjcrm-tab-text, var(--pbjcrm-text-soft));
	margin-bottom: -1px;
}

.pbjcrm-tabs-boxed .pbjcrm-tab:hover,
.pbjcrm-tabs-boxed .pbjcrm-subnav-item:hover {
	background: var(--pbjcrm-surface);
	color: var(--pbjcrm-text);
}

.pbjcrm-tabs-boxed .pbjcrm-tab.pbjcrm-active,
.pbjcrm-tabs-boxed .pbjcrm-subnav-item.pbjcrm-active {
	background: var(--pbjcrm-surface);
	color: var(--pbjcrm-text);
	font-weight: 700;
	/* Sits ON the panel: the bottom line is painted out. */
	box-shadow: 0 1px 0 0 var(--pbjcrm-surface);
}

/* ===========================================================================
   FILTER PILLS — a row of on/off choices.
   Used by the calendar's categories (previously tick-boxes) and available to
   any module that needs the same thing.
   =========================================================================== */

.pbjcrm-pillrow {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
	align-items: center;
}

.pbjcrm-pill {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	background: var(--pbjcrm-pill-bg, var(--pbjcrm-surface-alt));
	border: 1px solid var(--pbjcrm-pill-border, var(--pbjcrm-border));
	color: var(--pbjcrm-pill-text, var(--pbjcrm-text-soft));
	border-radius: var(--pbjcrm-radius-pill, 999px);
	padding: 5px 13px;
	font-size: 13px;
	font-weight: 600;
	line-height: 1.3;
	cursor: pointer;
	transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}

.pbjcrm-pill:hover {
	border-color: var(--pbjcrm-border-strong);
	color: var(--pbjcrm-text);
}

.pbjcrm-pill[aria-pressed="true"] {
	background: var(--pbjcrm-pill-on-bg, var(--pbjcrm-accent));
	border-color: var(--pbjcrm-pill-on-border, var(--pbjcrm-accent));
	color: var(--pbjcrm-pill-on-text, var(--pbjcrm-accent-contrast));
}

/* A pill carrying its own colour (a calendar category) keeps that colour as a
   dot rather than fighting the on/off state for the whole background. */
.pbjcrm-pill-dot {
	width: 9px;
	height: 9px;
	border-radius: 50%;
	background: currentColor;
	opacity: .85;
	flex: none;
}

@media (max-width: 600px) {
	.pbjcrm-pill {
		padding: 7px 14px; /* Bigger tap target on a phone. */
	}
}

/* The calendar's filter pills carry their category colour on the dot, so the
   colour survives the on/off state instead of fighting it for the background.
   These override the old .pbjcrm-cal-kind-* border-left rules, which were
   written for the label+tick-box markup that 1.1.0 replaced with buttons. */
.pbjcrm-pill.pbjcrm-cal-kind-people   { color: var(--pbjcrm-cal-people); }
.pbjcrm-pill.pbjcrm-cal-kind-tasks    { color: var(--pbjcrm-cal-tasks); }
.pbjcrm-pill.pbjcrm-cal-kind-tickets  { color: var(--pbjcrm-cal-tickets); }
.pbjcrm-pill.pbjcrm-cal-kind-invoices { color: var(--pbjcrm-cal-invoices); }
.pbjcrm-pill.pbjcrm-cal-kind-visits   { color: var(--pbjcrm-cal-visits); }
.pbjcrm-pill.pbjcrm-cal-kind-deals    { color: var(--pbjcrm-cal-deals); }
.pbjcrm-pill.pbjcrm-cal-kind-marketing{ color: var(--pbjcrm-cal-marketing); }
.pbjcrm-pill.pbjcrm-cal-kind-imports  { color: var(--pbjcrm-cal-imports); }
.pbjcrm-pill.pbjcrm-cal-kind-system   { color: var(--pbjcrm-cal-system); }

/* Border-left belonged to the old markup. Neutralise it on the pill so the
   category colour shows only as the dot. */
.pbjcrm-pill[class*="pbjcrm-cal-kind-"] {
	border-left-color: var(--pbjcrm-pill-border, var(--pbjcrm-border));
}

/* When a category pill is ON, the fill takes over and the dot goes light so it
   stays visible against it. */
.pbjcrm-pill[aria-pressed="true"][class*="pbjcrm-cal-kind-"] {
	color: var(--pbjcrm-pill-on-text, var(--pbjcrm-accent-contrast));
	border-left-color: var(--pbjcrm-pill-on-border, var(--pbjcrm-accent));
}

/* ------------------------------------------------------------------ *
 * The card grid (1.6.0). "All people" and "All businesses" draw one
 * card per record — same look as the Companies & people columns, but
 * packed as many across as the screen fits. New classes only: nothing
 * here restyles an existing screen.
 * ------------------------------------------------------------------ */

.pbjcrm-cardgrid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
	gap: var(--pbjcrm-space-sm);
	align-items: stretch;
}

.pbjcrm-listcard {
	position: relative;
	border: 1px solid var(--pbjcrm-border);
	/* THE LEFT EDGE IS THE TYPE BAR (Jake, 2026-08-19: "the left side is
	   color coded per type currently keep that and color the right color
	   border with your plan for urgency"). Left = WHAT it is, right = HOW
	   LATE (urgency lands with the unified card states). */
	border-left: 4px solid var(--pbjcrm-rec-contact, var(--pbjcrm-accent, #2271b1));
	border-radius: var(--pbjcrm-radius);
	background: var(--pbjcrm-surface);
	padding: var(--pbjcrm-space-sm) var(--pbjcrm-space);
	cursor: pointer;
	display: flex;
	flex-direction: column;
	gap: 4px;
	min-width: 0;
	transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.pbjcrm-listcard:hover,
.pbjcrm-listcard:focus-visible {
	border-color: var(--pbjcrm-accent);
	/* Hover brightens the SAME left edge rather than painting a second bar. */
	border-left-color: var(--pbjcrm-accent);
	outline: none;
}

/* A business card's type bar is the business colour. */
.pbjcrm-listcard-business {
	border-left-color: var(--pbjcrm-rec-business, var(--pbjcrm-rec-deal, var(--pbjcrm-accent, #2271b1)));
}

.pbjcrm-listcard-head {
	display: flex;
	align-items: center;
	gap: 6px;
	flex-wrap: wrap;
	/* Leave room for the tick in the corner. */
	padding-right: 26px;
	overflow-wrap: anywhere;
}

.pbjcrm-listcard-line {
	margin: 0;
	overflow-wrap: anywhere;
}

.pbjcrm-listcard-foot {
	margin-top: auto;
	display: flex;
	align-items: center;
	gap: 8px;
	flex-wrap: wrap;
	padding-top: 4px;
	font-size: 12px;
}

.pbjcrm-listcard-tick {
	position: absolute;
	top: 8px;
	right: 8px;
}

.pbjcrm-cardgrid-head {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	margin: 0 0 var(--pbjcrm-space-sm);
	cursor: pointer;
}

/* ===========================================================================
   MOBILE PASS (appended 2026-08-04) — phones and small tablets.

   APPEND-ONLY: nothing above this line is touched, so anything wider than
   782px renders exactly as it did before. These blocks only ADD to the
   responsive work already in the file (the 782 / 480 / 360 blocks near the
   top, the calendar's 900 / 600 blocks) — where a property is already set
   for a narrower width up there, it is deliberately left alone here.

   Two breakpoints, matching the ones the file already uses:
     782px — large phones / small tablets (WordPress' own breakpoint)
     600px — phones held in one hand (the calendar and pills already use it)
   =========================================================================== */

@media (max-width: 782px) {
	/* --- Touch targets. Every control a thumb has to hit is at least 40px
	   tall. Padding is left to the existing rules; only the floor is set. --- */
	.pbjcrm-btn,
	.pbjcrm-nav-item,
	.pbjcrm-subnav-item,
	.pbjcrm-tab,
	.pbjcrm-pill,
	button.pbjcrm-step,
	.pbjcrm-social,
	.pbjcrm-typeahead-item {
		min-height: 40px;
	}

	/* Blockified inside their flex rows anyway — this just centres the label
	   now that the box is taller than the text. */
	.pbjcrm-nav-item,
	.pbjcrm-subnav-item,
	.pbjcrm-tab,
	button.pbjcrm-step {
		display: inline-flex;
		align-items: center;
		justify-content: center;
	}

	/* A row of pills that wraps needs air between the rows, not just the
	   columns, or the two lines read as one. */
	.pbjcrm-nav,
	.pbjcrm-subnav,
	.pbjcrm-tabs,
	.pbjcrm-pillrow,
	.pbjcrm-stepper {
		row-gap: 6px;
	}

	/* The joined-button tab style is the one style that scrolls rather than
	   wraps; give it momentum scrolling and hide the bar it would draw. */
	.pbjcrm-tabs-segment .pbjcrm-tabs,
	.pbjcrm-tabs-segment .pbjcrm-subnav {
		-webkit-overflow-scrolling: touch;
		scrollbar-width: none;
	}

	/* Text boxes big enough to tap into. Colour swatches, tick-boxes and
	   radios keep their own sizes. */
	.pbjcrm-portal input[type="text"],
	.pbjcrm-portal input[type="email"],
	.pbjcrm-portal input[type="url"],
	.pbjcrm-portal input[type="tel"],
	.pbjcrm-portal input[type="number"],
	.pbjcrm-portal input[type="date"],
	.pbjcrm-portal input[type="search"],
	.pbjcrm-portal input[type="password"],
	.pbjcrm-portal select,
	.pbjcrm-login input[type="text"],
	.pbjcrm-login input[type="password"] {
		min-height: 40px;
	}

	/* --- The checklist add row stacks so the box is usable. ---
	   Copied deliberately from pbj-helpdesk's .pbjhd-todo-add, which has done
	   exactly this since its own phone pass: the line box takes a full row of
	   its own and Remove sits under it, so the box never has to compete with a
	   button for width and never depends on flex-grow to have any. The floor on
	   the base rule covers wider screens; this covers the phone. */
	.pbjcrm-task-editor-row {
		flex-wrap: wrap;
	}

	.pbjcrm-task-editor-row input {
		flex: 1 1 100%;
	}

	.pbjcrm-task-editor-row .pbjcrm-btn {
		flex: 0 0 auto;
	}

	/* A row of actions on a card: nothing narrower than a fingertip. */
	.pbjcrm-deal-actions,
	.pbjcrm-task-actions,
	.pbjcrm-person-actions,
	.pbjcrm-recordfields-actions,
	.pbjcrm-explainer-foot {
		gap: 8px;
	}
}

@media (max-width: 600px) {
	/* --- Toolbars and filter bars. The search box and the drop-downs take
	   the full width so nothing is squeezed to three characters; the buttons
	   stay side by side and share what is left of the row. --- */
	.pbjcrm-toolbar,
	.pbjcrm-filterbar {
		align-items: stretch;
	}

	.pbjcrm-toolbar > input,
	.pbjcrm-toolbar > select,
	.pbjcrm-toolbar > label,
	.pbjcrm-filterbar > input,
	.pbjcrm-filterbar > select,
	.pbjcrm-filterbar > label {
		flex: 1 1 100%;
		width: 100%;
	}

	.pbjcrm-toolbar .pbjcrm-btn,
	.pbjcrm-filterbar .pbjcrm-btn {
		flex: 1 1 auto;
	}

	/* The spacer exists to push things apart on a wide row. On a phone there
	   is nothing to push, and left in place it would eat a whole line. */
	.pbjcrm-toolbar .pbjcrm-spacer,
	.pbjcrm-filterbar .pbjcrm-spacer {
		display: none;
	}

	/* --- Tables. The optional columns are already hidden further up; this is
	   the safety net for what is left. The table becomes a block that can
	   scroll sideways, so a long ticket number or a wide date column is
	   reachable instead of being cut off at the edge of the screen. When the
	   content does fit, the table still fills the width as before. --- */
	.pbjcrm-table {
		display: block;
		overflow-x: auto;
		-webkit-overflow-scrolling: touch;
	}

	/* --- Anything that was capped to a comfortable reading width on a desk
	   is simply full width here. --- */
	.pbjcrm-typeahead,
	.pbjcrm-typeahead-input,
	.pbjcrm-companypick .pbjcrm-field,
	.pbjcrm-nameedit input[type="text"],
	.pbjcrm-titleedit-input {
		max-width: none;
	}

	/* --- Cards and panels: a little more room inside, since the screen edge
	   is now close to the text. --- */
	.pbjcrm-panel,
	.pbjcrm-summary-card,
	.pbjcrm-deal-card,
	.pbjcrm-task-card,
	.pbjcrm-co-card,
	.pbjcrm-person-row,
	.pbjcrm-listcard {
		padding: 10px 12px;
	}

	/* Label above value rather than beside it — the same shape the profile's
	   inline rows already take at 480px. */
	.pbjcrm-deflist {
		grid-template-columns: minmax(0, 1fr);
		gap: 0 0;
	}

	.pbjcrm-deflist dd {
		margin-bottom: 6px;
	}

	/* The little x on a tag is a real target now. */
	.pbjcrm-chip-x {
		min-width: 24px;
		min-height: 24px;
		display: inline-flex;
		align-items: center;
		justify-content: center;
		font-size: 13px;
	}

	/* The calendar's date boxes and drop-downs: one per line, full width. The
	   grid itself is handled by the 900px / 600px blocks further up. */
	.pbjcrm-cal-field {
		flex: 1 1 100%;
	}

	.pbjcrm-cal-field select,
	.pbjcrm-cal-field input {
		width: 100%;
	}

	/* Feed and timeline rows lose the hanging timestamp, which otherwise
	   pushes the author off the line. */
	.pbjcrm-feed-time {
		margin-left: 0;
	}
}

/* ---------------------------------------------------------------------------
   "Today" — the unified daily queue (1.13.0, backlog phase 6.2)

   One list carrying rows that came from several different plugins, so the
   styling deliberately does NOT vary by kind beyond the little grey word at the
   front and, since 2026-08-13, the colour of the left edge. A ticket that
   looked WHOLLY different from a task would undo the whole point of merging
   them: the person is meant to read one list top to bottom, not several lists
   that happen to be stacked.

   THE EDGE IS THE ONE EXCEPTION, AND IT COSTS NOTHING because the 4px left
   border was already there — it was simply grey on every row that was not late.
   Colouring it by kind adds no weight, no second column and no new element; it
   just stops the edge saying nothing. It is the same colour, from the same
   token, that the calendar draws the same record with (Jake, 2026-08-13:
   "triage color schemes for visits, tickets, ect.. match calendar").

   ⛔ STATUS STILL WINS *ON ITS OWN CHANNEL* — SUPERSEDED IN PART, 2026-08-15.
   This paragraph used to read: "a row that is overdue is red whatever kind it
   is, because 'this is late' is the more urgent fact and the kind is already
   written on the row in words. Do not reorder these blocks."

   The reasoning was sound and the CONSEQUENCE was not, which is what Jake
   reported: both facts were competing for ONE 4px edge, so status simply erased
   kind — and it erased it *exactly on the day everything is overdue*, which is
   the day you most need to tell a visit from an invoice at a glance. Triage got
   this right because nothing over there ever overrode its edge.

   ⛔ SO THE FIX IS NOT A REORDER, AND MUST NEVER BECOME ONE. Reordering would
   just swap which fact disappears. The two now use DIFFERENT CHANNELS and both
   are on screen at once:

       KIND   -> the 4px left border. Always. Never overridden by anything.
       STATUS -> a 3px stripe inset just inside that border (box-shadow), plus
                 the due date's own colour and weight, which it already had.

   ⚠️ The kind rules and the status rules therefore no longer collide at all —
   they set different properties — so source order between these two blocks has
   stopped mattering. Left in their original order anyway: nothing is gained by
   moving them and the old note told people not to.

   ⚠️ THE ACCESSIBILITY ARGUMENT IS UNCHANGED AND STILL LOAD-BEARING: neither
   colour is the only carrier of its fact. The kind is written on the row in
   words by .pbjcrm-queue-kind, and the day is written in .pbjcrm-queue-due,
   which goes bold and danger-coloured when late. Nobody has to pick a colour
   out of a row to read it.
--------------------------------------------------------------------------- */

.pbjcrm-queue-summary {
	margin: 0 0 0.75em;
	color: var( --pbjcrm-muted );
}

.pbjcrm-queue-list {
	display: flex;
	flex-direction: column;
	gap: 0.4em;
}

.pbjcrm-queue-row {
	display: flex;
	align-items: baseline;
	flex-wrap: wrap;
	gap: 0.6em;
	padding: 0.7em 0.9em;
	border: 1px solid var( --pbjcrm-border );
	border-left-width: 4px;
	border-radius: var( --pbjcrm-radius );
	background: var( --pbjcrm-surface, transparent );
}

.pbjcrm-queue-task-tools {
	grid-column: 2 / -1;
	display: grid;
	gap: 6px;
	margin-top: 6px;
}

.pbjcrm-queue-task-tools .pbjcrm-btn {
	justify-self: start;
}

/* The kind, on the edge. Same token the calendar paints the same record with,
   so a ticket is the same purple in the diary and in Today. An unknown kind —
   a module the CRM has never met — falls through to the plain border and looks
   exactly as it always did, which is why no module has to be taught about
   this. */
.pbjcrm-queue-kind-task { border-left-color: var( --pbjcrm-rec-task, var( --pbjcrm-border ) ); }
.pbjcrm-queue-kind-ticket { border-left-color: var( --pbjcrm-rec-ticket, var( --pbjcrm-border ) ); }
.pbjcrm-queue-kind-invoice { border-left-color: var( --pbjcrm-rec-invoice, var( --pbjcrm-border ) ); }
.pbjcrm-queue-kind-estimate { border-left-color: var( --pbjcrm-rec-invoice, var( --pbjcrm-border ) ); }
.pbjcrm-queue-kind-visit { border-left-color: var( --pbjcrm-rec-visit, var( --pbjcrm-border ) ); }
.pbjcrm-queue-kind-appointment { border-left-color: var( --pbjcrm-rec-appointment, var( --pbjcrm-border ) ); }
.pbjcrm-queue-kind-deal { border-left-color: var( --pbjcrm-rec-deal, var( --pbjcrm-border ) ); }

/* Legacy classes retain only written status below. They paint no inset/edge:
   one item has one type rail, and late/due-soon belongs in its status slot. */
.pbjcrm-queue-late {
	box-shadow: none;
}

.pbjcrm-queue-soon {
	box-shadow: none;
}

.pbjcrm-queue-kind {
	flex: 0 0 auto;
	min-width: 4em;
	font-size: 0.78em;
	text-transform: uppercase;
	letter-spacing: 0.04em;
	color: var( --pbjcrm-muted );
}

/* The title is a link, but it must NOT wear the browser's link colours. Left
   alone, an unvisited row and a visited row came out two different colours in
   the same list — caught on screen, invisible in the diff — and on the dark
   preset one of them was barely readable. A row is a row whether or not you
   happened to open it yesterday. */
.pbjcrm-queue-title,
.pbjcrm-queue-title:visited,
.pbjcrm-queue-title:hover,
.pbjcrm-queue-title:focus {
	flex: 1 1 14em;
	font-weight: 600;
	color: var( --pbjcrm-text );
	text-decoration: none;
}

.pbjcrm-queue-title:hover,
.pbjcrm-queue-title:focus {
	text-decoration: underline;
}

.pbjcrm-queue-meta {
	flex: 0 1 auto;
	color: var( --pbjcrm-muted );
}

.pbjcrm-queue-due {
	flex: 0 0 auto;
	margin-left: auto;
	color: var( --pbjcrm-muted );
	white-space: nowrap;
}

.pbjcrm-queue-late .pbjcrm-queue-due {
	color: var( --pbjcrm-danger );
	font-weight: 600;
}

/* "Open the ticket" on a collapsed pair. This was a bare link at 0.85em and on
   the dark preset it was effectively invisible — dark link colour on a near
   black row. It is the ONLY way into the ticket half of a collapsed pair, so it
   has to be findable: a small soft button, using the button variables that
   already have a guaranteed contrast pairing in every preset. */
.pbjcrm-queue-linked,
.pbjcrm-queue-linked:visited,
.pbjcrm-queue-linked:hover,
.pbjcrm-queue-linked:focus {
	flex: 0 0 auto;
	padding: 0.15em 0.6em;
	/* Fallbacks are not optional here: these four are painted onto the portal
	   root by PBJ_CRM_Theme at runtime, not declared in this stylesheet, so a
	   request that has not painted them yet would otherwise get no colour at
	   all. Every other use of them in this file carries the same fallbacks. */
	border: 1px solid var( --pbjcrm-btn-soft-border, var( --pbjcrm-border ) );
	border-radius: var( --pbjcrm-radius-pill, 999px );
	background: var( --pbjcrm-btn-soft-bg, var( --pbjcrm-surface ) );
	color: var( --pbjcrm-btn-soft-text, var( --pbjcrm-text ) );
	font-size: 0.85em;
	text-decoration: none;
	white-space: nowrap;
}

.pbjcrm-queue-linked:hover,
.pbjcrm-queue-linked:focus {
	background: var( --pbjcrm-btn-soft-hover-bg, var( --pbjcrm-btn-hover-bg ) );
}

/* On a phone the row stacks: the title has to be readable before anything
   else, and the due date belongs under it rather than squeezed off the edge. */
@media ( max-width: 640px ) {
	.pbjcrm-queue-row {
		flex-direction: column;
		align-items: flex-start;
		gap: 0.2em;
	}

	.pbjcrm-queue-due {
		margin-left: 0;
	}
}

/* "Whose day" selector on Today (task #17). The warning line is deliberately
   not a colour-only signal — it says in words that this is not your own list,
   because a manager misreading someone else's workload as their own is the
   failure this screen invites. */
.pbjcrm-queue-subject-label {
	margin-right: 0.5em;
	color: var( --pbjcrm-muted );
}

.pbjcrm-queue-whose {
	margin: 0 0 0.75em;
	padding: 0.5em 0.8em;
	border-left: 4px solid var( --pbjcrm-accent );
	border-radius: var( --pbjcrm-radius );
	background: var( --pbjcrm-surface, transparent );
	font-weight: 600;
}

/* ===========================================================================
   ITEM 11 (2026-08-18) — THE UNIFIED INBOX AND THE SCREENS ROUND IT

   Everything below is drawn by assets/js/pbj-crm.js: the Inbox screen
   (#/inbox), the Today split with its right-hand rail, the Team list under
   Companies & People, the profile's four group buttons and their sub-chips,
   and the "waiting on you" chip a helpdesk row wears.

   ⛔ NOT ONE COLOUR IS DECLARED HERE. Every value is a --pbjcrm-* token the
   theme emits, so all eight presets — the light ones and both dark ones — are
   right without this file knowing what dark means. Where a token is painted at
   runtime rather than declared in this stylesheet it carries a fallback, the
   same rule the rest of the file follows.

   ⚠️ THE MARKUP IS SOMEBODY ELSE'S. These rules key off CLASS NAMES ONLY and
   never off an element name or a child position, because the bundle that emits
   them is free to draw a row as a <button> or an <a>. That is why each block
   resets font, background and border rather than assuming what it is styling.
   =========================================================================== */

/* --------------------------------------------------------- The inbox screen */

.pbjcrm-inbox {
	display: flex;
	flex-direction: column;
	gap: var( --pbjcrm-space );
}

.pbjcrm-inbox-group {
	display: flex;
	flex-direction: column;
	gap: 0.4em;
}

/* The heading carries its own count, so "Email · 4" is one fact in one place.
   Same small-caps treatment as the search overlay's group labels, because they
   are the same kind of thing: a bucket name over a list. */
.pbjcrm-inbox-group-head {
	margin: 0;
	font-size: 0.78em;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.04em;
	color: var( --pbjcrm-muted );
}

/* A row reads like a queue row on purpose: one card, a kind edge, the title
   first and the time last. Somebody who has learnt Today has already learnt
   this screen. */
.pbjcrm-inbox-row,
.pbjcrm-inbox-row:visited {
	display: flex;
	align-items: baseline;
	flex-wrap: wrap;
	gap: 0.6em;
	width: 100%;
	padding: 0.7em 0.9em;
	background: var( --pbjcrm-surface, transparent );
	color: var( --pbjcrm-text );
	border: 1px solid var( --pbjcrm-border );
	border-left-width: 4px;
	border-radius: var( --pbjcrm-radius );
	font: inherit;
	text-align: left;
	text-decoration: none;
	cursor: pointer;
}

.pbjcrm-inbox-row:hover,
.pbjcrm-inbox-row:focus {
	border-color: var( --pbjcrm-border-strong );
	background: var( --pbjcrm-surface-alt );
	text-decoration: none;
}

.pbjcrm-inbox-row:focus-visible {
	outline: 2px solid var( --pbjcrm-focus-ring, var( --pbjcrm-accent ) );
	outline-offset: 1px;
}

/* The kind, on the edge — the SAME tokens the calendar lane, the queue row and
   the phone row use, so a ticket is one colour everywhere in the suite. A kind
   nobody has taught this file falls through to the plain border, which is why
   no module has to be told about this.

   ⚠️ THE FOUR COMMUNICATION KINDS WEAR THE ACCENT, AND THAT IS DELIBERATE, NOT
   A GAP. PBJ_CRM_Theme emits record colours for ticket, task, invoice, visit,
   appointment, contact, deal, marketing, import and system — there is no
   `rec-email` or `rec-text` to ask for, and inventing a var() name nobody
   declares would silently fall through to whatever literal was typed beside it
   on every preset (the mistake written up over .pbjcrm-hdr-mode.is-on in
   pbj-crm-header.css). The accent is a token that exists and is
   contrast-checked; when the theme grows a communication colour, this is the
   one line that changes. */
.pbjcrm-inbox-kind-email,
.pbjcrm-inbox-kind-text,
.pbjcrm-inbox-kind-call,
.pbjcrm-inbox-kind-voicemail { border-left-color: var( --pbjcrm-accent ); }
.pbjcrm-inbox-kind-ticket { border-left-color: var( --pbjcrm-rec-ticket, var( --pbjcrm-border ) ); }
.pbjcrm-inbox-kind-task { border-left-color: var( --pbjcrm-rec-task, var( --pbjcrm-border ) ); }

.pbjcrm-inbox-title {
	flex: 1 1 14em;
	min-width: 0;
	font-weight: 600;
	color: var( --pbjcrm-text );
}

/* Its own line: a subject line and the person it is from are two facts, and
   squeezing them onto one line loses the end of both. */
.pbjcrm-inbox-sub {
	flex: 1 1 100%;
	min-width: 0;
	font-size: 0.9em;
	color: var( --pbjcrm-muted );
}

.pbjcrm-inbox-when {
	flex: 0 0 auto;
	margin-left: auto;
	font-size: 0.85em;
	color: var( --pbjcrm-muted );
	white-space: nowrap;
}

/* How much is behind one row — three unread texts in one thread are ONE row
   with a 3 on it. Accent-filled because it is always a live number: a count of
   one is not drawn at all. */
.pbjcrm-inbox-count {
	flex: 0 0 auto;
	min-width: 1.6em;
	padding: 0.05em 0.5em;
	border-radius: var( --pbjcrm-radius-pill, 999px );
	background: var( --pbjcrm-accent );
	color: var( --pbjcrm-accent-contrast, #ffffff );
	font-size: 0.78em;
	font-weight: 700;
	text-align: center;
	white-space: nowrap;
}

/* ------------------------------------------- Today: the list and the rail */

/* Deliberately the same shape as .pbjcrm-cal-split — the calendar's month grid
   beside its day panel — because it is the same idea: the thing you work in,
   and the thing you glance at. One ratio, two screens. */
.pbjcrm-queue-split {
	display: grid;
	grid-template-columns: minmax( 0, 2.1fr ) minmax( 0, 1fr );
	gap: var( --pbjcrm-space );
	align-items: start;
}

.pbjcrm-side-panel {
	position: sticky;
	top: 12px;
	max-height: calc( 100vh - 40px );
	overflow-y: auto;
	padding: var( --pbjcrm-space );
	background: var( --pbjcrm-surface );
	border: 1px solid var( --pbjcrm-border );
	border-radius: var( --pbjcrm-radius );
}

.pbjcrm-side-panel > h3 {
	margin: 0 0 10px;
	font-size: 15px;
}

/* ⛔ 782px, NOT 900. This is the width the portal itself calls narrow — the
   phone bundle's NARROW and the calendar's own breakpoint — and a rail that
   stacks at a different number from everything else on the page is a layout
   that changes twice as somebody drags a window. */
@media ( max-width: 782px ) {
	.pbjcrm-queue-split {
		grid-template-columns: minmax( 0, 1fr );
	}

	.pbjcrm-side-panel {
		position: static;
		max-height: none;
	}
}

/* ------------------------------------------------------------- The Team list */

/* Agents, under Companies & People. A plain stack rather than a table: the
   three facts on a row (name, email, WP user) are of very different lengths and
   a table gives the longest one the width. */
.pbjcrm-team-list {
	display: flex;
	flex-direction: column;
	gap: 0.4em;
	margin: 0;
	padding: 0;
	list-style: none;
}

.pbjcrm-team-list > * {
	display: flex;
	align-items: baseline;
	flex-wrap: wrap;
	gap: 0.6em;
	padding: 0.7em 0.9em;
	background: var( --pbjcrm-surface, transparent );
	border: 1px solid var( --pbjcrm-border );
	border-left: 4px solid var( --pbjcrm-rec-contact, var( --pbjcrm-border ) );
	border-radius: var( --pbjcrm-radius );
}

/* ------------------------------------------- The profile's four groups (§G3) */

/* The row of group buttons, and the row of sub-chips underneath it. The LAYOUT
   is stated here; the LOOK of a button is not, because these carry the portal's
   own tab or chip classes and the owner's tab-style preset has the final word
   on those (see the four `!important` swaps over .pbjcrm-timelineview-tabs —
   fighting a preset from here is exactly how that bug happened). */
.pbjcrm-profile-groups {
	display: flex;
	flex-wrap: wrap;
	gap: 4px;
	margin: 0 0 10px;
}

/* The fallback, and only the fallback: a bare <button> with none of the
   portal's button classes on it still has to look pressable. Anything already
   wearing .pbjcrm-tab / .pbjcrm-chip / .pbjcrm-pill is left entirely alone. */
.pbjcrm-profile-groups > button:not( .pbjcrm-tab ):not( .pbjcrm-chip ):not( .pbjcrm-pill ) {
	padding: 7px 14px;
	background: var( --pbjcrm-pill-bg, var( --pbjcrm-surface-alt ) );
	color: var( --pbjcrm-pill-text, var( --pbjcrm-text-soft ) );
	border: 1px solid var( --pbjcrm-pill-border, var( --pbjcrm-border ) );
	border-radius: var( --pbjcrm-radius-pill, 999px );
	font: inherit;
	font-size: 14px;
	cursor: pointer;
}

.pbjcrm-profile-groups > button:not( .pbjcrm-tab ):not( .pbjcrm-chip ):not( .pbjcrm-pill ).pbjcrm-active {
	background: var( --pbjcrm-pill-on-bg, var( --pbjcrm-accent ) );
	color: var( --pbjcrm-pill-on-text, var( --pbjcrm-accent-contrast ) );
	border-color: var( --pbjcrm-pill-on-border, var( --pbjcrm-accent ) );
}

/* The second row — what is IN the group somebody just pressed. Smaller and
   indented under the groups, so the two rows never read as one long row of
   equal choices (the mistake .pbjcrm-tab-kinds was written to avoid). */
.pbjcrm-profile-groupchips {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 4px;
	margin: 0 0 12px;
	padding-bottom: 10px;
	border-bottom: 1px solid var( --pbjcrm-border );
}

.pbjcrm-profile-groupchips .pbjcrm-chip {
	cursor: pointer;
	margin-right: 0;
}

.pbjcrm-profile-groupchips .pbjcrm-chip:hover {
	border-color: var( --pbjcrm-accent );
}

.pbjcrm-profile-groupchips .pbjcrm-chip.pbjcrm-active {
	background: var( --pbjcrm-accent );
	border-color: var( --pbjcrm-accent );
	color: var( --pbjcrm-pill-on-text, var( --pbjcrm-accent-contrast ) );
}

/* --------------------------------------------------------- Waiting on you */

/* A ticket whose last word came from the customer.
 *
 * ⚠️ THE DOT IS NOT THE MESSAGE. It is drawn by CSS so no bundle has to emit a
 * glyph, but the chip carries the WORDS as well — colour and a shape are the
 * two things a colour-blind reader and a monochrome printout respectively lose,
 * and this fact is the difference between a job somebody is doing and a job
 * somebody is waiting on.
 *
 * The accent is contrast-checked against the surface by PBJ_CRM_Theme on every
 * preset, which is why this reads on the dark ones without a second rule. */
.pbjcrm-waiting {
	display: inline-flex;
	align-items: center;
	gap: 5px;
	padding: 1px 9px;
	border: 1px solid var( --pbjcrm-pill-border, var( --pbjcrm-border ) );
	border-radius: var( --pbjcrm-radius-pill, 999px );
	background: var( --pbjcrm-accent-soft, var( --pbjcrm-surface-alt ) );
	color: var( --pbjcrm-accent-strong, var( --pbjcrm-accent ) );
	font-size: 12px;
	font-weight: 600;
	white-space: nowrap;
}

.pbjcrm-waiting::before {
	content: "\25CF";
	color: var( --pbjcrm-accent );
	font-size: 10px;
	line-height: 1;
}

/* ===========================================================================
   PHASE 15.1 / 15.2 — the typeface, the style axis, and the sign-in page

   ADDITIVE ONLY. Everything below is appended rather than edited into the
   rules above, so a rule that already worked still works: where two rules have
   the same weight the later one wins, and the portal's inline custom
   properties (written by pbj-crm.js from PBJ_CRM_Theme::css_variables()) still
   beat every one of them.
   =========================================================================== */

/* --------------------------------------------------------- The typeface */

/* --pbjcrm-font (15.1) is a real theme token now, so the portal stops stating
   its own font and asks the theme for one. The value repeated here is the
   fallback for a page that has not been painted yet — deliberately the same
   system stack PBJ_CRM_Theme::FONT_SYSTEM emits, and written UNQUOTED for the
   same reason it is unquoted there: the class filters its output to a safe
   character set with no quotation marks in it, so the two only agree if this
   copy is written the way that one comes out. Multi-word family names are
   legal unquoted in CSS.

   No webfont, here or anywhere. The suite calls nothing on the internet, and a
   font file is a third-party request on every single page load. */
.pbjcrm-portal,
.pbjcrm-login {
	--pbjcrm-font: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica, Arial, sans-serif;

	font-family: var(--pbjcrm-font);
}

/* The canvas page states a font of its own (it has no theme stylesheet to
   inherit one from). It now states the CHOSEN one, with the same stack behind
   it for the moment before the tokens land. */
body.pbj-crm-canvas {
	font-family: var(--pbjcrm-font, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica, Arial, sans-serif);
}

/* --------------------------------------------- 15.2: the sign-in page too */

/*
 * THE SIGN-IN CARD FOLLOWED THE DEFAULTS, NOT THE THEME — and it took reading
 * the cascade to see why, because nothing about it looks wrong.
 *
 * The base rule at the top of this file declares the whole token set on
 * `.pbjcrm-portal, .pbjcrm-login`. On the portal that is harmless: the script
 * writes the owner's values onto the same element as INLINE properties, and
 * inline always wins. The sign-in card has no script — nobody is logged in yet
 * — so its own local declarations shadowed the themed values sitting on :root
 * one level up, and a business that had chosen Dark still got a white login
 * box. The theme was working; the card was simply overriding it with the
 * shipped defaults.
 *
 * `inherit` is the fix that keeps the defaults: it tells the card to take the
 * parent's computed value, which on the canvas is exactly what
 * PBJ_CRM_Theme::inline_css( ':root' ) printed into the head. Scoped to the
 * canvas because that is the page that carries those tokens — a login
 * shortcode dropped in a theme page (the `pbj_crm_use_portal_canvas` escape
 * hatch) has no :root tokens to inherit, and there it keeps the old behaviour
 * rather than inheriting nothing.
 */
body.pbj-crm-canvas .pbjcrm-login {
	--pbjcrm-accent: inherit;
	--pbjcrm-accent-strong: inherit;
	--pbjcrm-accent-soft: inherit;
	--pbjcrm-accent-contrast: inherit;
	--pbjcrm-link: inherit;
	--pbjcrm-bg: inherit;
	--pbjcrm-surface: inherit;
	--pbjcrm-surface-alt: inherit;
	--pbjcrm-border: inherit;
	--pbjcrm-border-strong: inherit;
	--pbjcrm-shadow: inherit;
	--pbjcrm-text: inherit;
	--pbjcrm-text-soft: inherit;
	--pbjcrm-muted: inherit;
	--pbjcrm-header-bg: inherit;
	--pbjcrm-header-text: inherit;
	--pbjcrm-success: inherit;
	--pbjcrm-warning: inherit;
	--pbjcrm-danger: inherit;
	--pbjcrm-radius: inherit;
	--pbjcrm-radius-sm: inherit;
	--pbjcrm-radius-lg: inherit;
	--pbjcrm-space: inherit;
	--pbjcrm-space-sm: inherit;
	--pbjcrm-space-lg: inherit;
	--pbjcrm-row-height: inherit;
	--pbjcrm-font-size: inherit;
	--pbjcrm-font: inherit;
}

/* The hairline round the card, on a dark page. The compound selector further
   up (`.pbjcrm-login.pbjcrm-theme-dark`) only ever matched the PORTAL, whose
   script adds the theme classes to the element itself; the sign-in card is
   plain server-rendered markup, so its theme classes are on the BODY. Both
   spellings now exist, and this one is written against `pbjcrm-is-dark` rather
   than a preset name so it holds for every dark preset there will ever be. */
body.pbjcrm-is-dark .pbjcrm-login {
	border-color: rgba(255, 255, 255, 0.22);
}

/* And the browser's own widgets on a dark sign-in page — the same fix as the
   portal's, hung off the body for the same reason.

   ⚠️ `pbjcrm-is-dark` comes from the preset's `dark` flag
   (PBJ_CRM_Theme::body_classes()), NOT from a list of preset names, which is
   why Terminal (dark) and Notebook (dark) were covered by this the moment they
   were declared and why the next dark preset will be too. Do not "tidy" this
   into `.pbjcrm-theme-dark`. */
body.pbjcrm-is-dark .pbjcrm-login input[type="text"],
body.pbjcrm-is-dark .pbjcrm-login input[type="email"],
body.pbjcrm-is-dark .pbjcrm-login input[type="password"],
body.pbjcrm-is-dark .pbjcrm-portal input[type="text"],
body.pbjcrm-is-dark .pbjcrm-portal input[type="email"],
body.pbjcrm-is-dark .pbjcrm-portal input[type="url"],
body.pbjcrm-is-dark .pbjcrm-portal input[type="tel"],
body.pbjcrm-is-dark .pbjcrm-portal input[type="number"],
body.pbjcrm-is-dark .pbjcrm-portal input[type="date"],
body.pbjcrm-is-dark .pbjcrm-portal input[type="datetime-local"],
body.pbjcrm-is-dark .pbjcrm-portal input[type="time"],
body.pbjcrm-is-dark .pbjcrm-portal input[type="month"],
body.pbjcrm-is-dark .pbjcrm-portal input[type="search"],
body.pbjcrm-is-dark .pbjcrm-portal input[type="password"],
body.pbjcrm-is-dark .pbjcrm-portal select,
body.pbjcrm-is-dark .pbjcrm-portal textarea {
	color-scheme: dark;
}

/* The sign-in card's own buttons, so the door into the CRM is painted by the
   theme like everything behind it. Fallbacks are not optional: these tokens
   are written at runtime, never declared in this stylesheet. */
.pbjcrm-login button[type="submit"],
.pbjcrm-login input[type="submit"] {
	padding: 8px 16px;
	border: 1px solid var( --pbjcrm-btn-border, var( --pbjcrm-accent ) );
	border-radius: var( --pbjcrm-radius-sm );
	background: var( --pbjcrm-btn-bg, var( --pbjcrm-accent ) );
	color: var( --pbjcrm-btn-text, var( --pbjcrm-accent-contrast ) );
	font-family: inherit;
	font-size: 14px;
	font-weight: 600;
	cursor: pointer;
}

.pbjcrm-login button[type="submit"]:hover,
.pbjcrm-login input[type="submit"]:hover {
	background: var( --pbjcrm-btn-hover-bg, var( --pbjcrm-accent-strong ) );
	color: var( --pbjcrm-btn-hover-text, var( --pbjcrm-btn-text ) );
}

.pbjcrm-login button[type="submit"]:active,
.pbjcrm-login input[type="submit"]:active {
	background: var( --pbjcrm-btn-active-bg, var( --pbjcrm-accent-strong ) );
	color: var( --pbjcrm-btn-active-text, var( --pbjcrm-btn-text ) );
}

/* ------------------------------------------------ 15.3: the company logo */

/* No rule here on purpose. The logo's STORAGE lives in PBJ_CRM_Theme
   (header_logo_url() resolves it, '' when there is none, and header_logo()
   also hands back the on-screen width and height so the strip does not jump
   while it loads); its MARKUP and its styling belong to the header that draws
   it, which brings its own stylesheet. One rule here would be a second opinion
   about how tall a logo is, and the two would drift. */

/* ==================================================== 15.1: the style axis

   `pbjcrm-style-terminal` / `-notebook` / `-standard` come from
   PBJ_CRM_Theme::style_of(), which reads the style off the preset slug. That
   is the whole point of the class: these rules never name a preset, so a
   `terminal-amber` added one day inherits all of them for free.

   The class lands on the BODY (portal-canvas.php) and, for the portal, on the
   portal element too (the script). Descendant selectors match either, so both
   spellings are covered without writing every rule twice.
   =========================================================================== */

/* ------------------------------------------------------------- Terminal */

/* A terminal squares things off — but the corner setting stays the OWNER'S.
   Using --pbjcrm-radius-sm (half of whatever they chose) rather than a hard 0
   means "Square corners" is still square, "Very rounded" is still noticeably
   rounder, and nobody's choice is quietly overruled by a style. */
.pbjcrm-style-terminal .pbjcrm-card,
.pbjcrm-style-terminal .pbjcrm-inline-form,
.pbjcrm-style-terminal .pbjcrm-nav,
.pbjcrm-style-terminal .pbjcrm-nav-item {
	border-radius: var( --pbjcrm-radius-sm );
}

/* Figures line up in a column. This is the actual reason somebody picks a
   fixed-width look for a screen full of money and dates, and it costs one
   line: the mono stack supplies the shapes, this makes the browser use the
   tabular ones where a face offers both. */
.pbjcrm-portal.pbjcrm-style-terminal,
.pbjcrm-login.pbjcrm-style-terminal,
body.pbjcrm-style-terminal .pbjcrm-portal,
body.pbjcrm-style-terminal .pbjcrm-login {
	font-variant-numeric: tabular-nums;
	letter-spacing: 0;
}

/* ------------------------------------------------------------- Notebook */

/*
 * Ruled paper, drawn with a gradient. NO IMAGE FILE — nothing to load, nothing
 * to cache-bust, nothing to go missing when somebody clears out uploads, and
 * it costs zero requests, which is the same rule the fonts follow.
 *
 * The lines are stated in flat rgba rather than mixed from a token because
 * they have to be nearly invisible: at these alphas a colour derived from the
 * border token would be either lost or too strong depending on the accent, and
 * a texture that some accents turn into stripes is worse than no texture. The
 * light and dark variants are two rules for that reason and not by accident.
 */
body.pbj-crm-canvas.pbjcrm-style-notebook {
	background-image:
		linear-gradient(to right, transparent 0, transparent 52px, rgba(190, 65, 65, 0.18) 52px, rgba(190, 65, 65, 0.18) 54px, transparent 54px),
		repeating-linear-gradient(to bottom, transparent 0, transparent 31px, rgba(74, 124, 171, 0.16) 31px, rgba(74, 124, 171, 0.16) 32px);
}

body.pbj-crm-canvas.pbjcrm-style-notebook.pbjcrm-is-dark {
	background-image:
		linear-gradient(to right, transparent 0, transparent 52px, rgba(230, 110, 110, 0.13) 52px, rgba(230, 110, 110, 0.13) 54px, transparent 54px),
		repeating-linear-gradient(to bottom, transparent 0, transparent 31px, rgba(145, 181, 214, 0.10) 31px, rgba(145, 181, 214, 0.10) 32px);
}

/* The sign-in card as a sheet of the same paper. Only the card, and only on
   Notebook — the portal's own tables stay clean, because ruled lines behind a
   list of numbers is exactly the point at which a nice idea becomes a
   readability problem. */
.pbjcrm-style-notebook .pbjcrm-login {
	background-image: repeating-linear-gradient(
		to bottom,
		rgba(0, 0, 0, 0) 0,
		rgba(0, 0, 0, 0) 27px,
		rgba(120, 100, 60, 0.12) 27px,
		rgba(120, 100, 60, 0.12) 28px
	);
}

.pbjcrm-style-notebook.pbjcrm-is-dark .pbjcrm-login,
body.pbjcrm-style-notebook.pbjcrm-is-dark .pbjcrm-login {
	background-image: repeating-linear-gradient(
		to bottom,
		rgba(0, 0, 0, 0) 0,
		rgba(0, 0, 0, 0) 27px,
		rgba(255, 240, 205, 0.07) 27px,
		rgba(255, 240, 205, 0.07) 28px
	);
}

/* A book sets its headings a little apart from the text under them. Spacing
   only — no colour, no size, nothing that could make anything harder to read. */
.pbjcrm-style-notebook .pbjcrm-login-title,
.pbjcrm-style-notebook .pbjcrm-card > h2:first-child,
.pbjcrm-style-notebook .pbjcrm-card > h3:first-child {
	margin-top: 0;
	margin-bottom: 0.6em;
	letter-spacing: 0.01em;
}

/* --------------------------------------------------------------- Rustic */

/* Rustic is a working board, not another paper preset. The faint speckle is
   drawn locally and changes from dry-erase white to chalkboard dark with the
   mode. Cards stay clean enough for tables and forms. */
body.pbj-crm-style-rustic .pbjcrm-portal,
.pbjcrm-portal.pbjcrm-style-rustic,
body.pbj-crm-style-rustic .pbjcrm-login,
.pbjcrm-login.pbjcrm-style-rustic {
	background-image:
		radial-gradient(circle at 18% 24%, rgba(22, 42, 37, 0.055) 0 1px, transparent 1.5px),
		radial-gradient(circle at 74% 66%, rgba(22, 42, 37, 0.04) 0 1px, transparent 1.5px);
	background-size: 31px 29px, 43px 41px;
}

body.pbjcrm-style-rustic.pbjcrm-is-dark .pbjcrm-portal,
.pbjcrm-portal.pbjcrm-style-rustic.pbjcrm-is-dark,
body.pbjcrm-style-rustic.pbjcrm-is-dark .pbjcrm-login,
.pbjcrm-login.pbjcrm-style-rustic.pbjcrm-is-dark {
	background-image:
		radial-gradient(circle at 18% 24%, rgba(255, 255, 235, 0.075) 0 1px, transparent 1.6px),
		radial-gradient(circle at 74% 66%, rgba(255, 255, 235, 0.05) 0 1px, transparent 1.6px);
}

.pbjcrm-style-rustic .pbjcrm-title,
.pbjcrm-style-rustic .pbjcrm-card > h2:first-child,
.pbjcrm-style-rustic .pbjcrm-card > h3:first-child {
	letter-spacing: 0.025em;
}

/* ===========================================================================
   1.53.0 — THE COMPACT WAITING MARK, AND THE NOTE BOX ON THE RECORD
   =========================================================================== */

/* THE SAME FACT, SAID SHORTER. .pbjcrm-waiting (above) is the chip: a dot AND
   the words, which is right where there is one of them on a screen — a ticket
   header, a card. A LIST is the other case. Eleven rows each carrying "Waiting
   on a reply" is one sentence printed eleven times, and on a laptop it pushed
   the title of every row along by ten characters.

   ⚠️ THE WORDS ARE NOT LOST, THEY MOVED. Every dot is rendered with `title` and
   `aria-label` set to the full sentence, so a pointer and a screen reader both
   get it. That is the ONE thing that makes dropping the words acceptable here
   and unacceptable in the chip: a lone dot with nothing behind it would be
   colour as the only carrier of meaning, which is the failure the chip's own
   comment exists to forbid.

   The dot itself is still ::before from the rule above — this modifier only
   takes away the chip's box, so there is one place the glyph is decided. */
.pbjcrm-waiting.pbjcrm-waiting-dot {
	gap: 0;
	flex: 0 0 auto;
	padding: 0;
	border: 0;
	border-radius: 0;
	background: none;
	/* No text child is rendered in this shape; zeroing it means a stray space
	   in a future caller cannot re-introduce a stripe of white. */
	font-size: 0;
	line-height: 1;
}

.pbjcrm-waiting.pbjcrm-waiting-dot::before {
	font-size: 11px;
}

/* The rail says the same thing in its one line each. */
.pbjcrm-side-item .pbjcrm-waiting-dot {
	margin-right: 4px;
}

/* THE NOTE BOX IS PART OF THE RECORD NOW (see noteComposer() in pbj-crm.js).
   It sits between Contact Info and Add Work inside Block 1, which is a flex
   column with its own gap — so this only has to stop the composer's old
   bottom margin doubling that gap. */
.pbjcrm-block1 > .pbjcrm-composer {
	margin-bottom: 0;
}

/* ===========================================================================
   THE GUIDED SPLIT — Phase 18.3 (1.55.0)

   openConvertChooser() and openSplitWizard() in pbj-crm.js. Both live inside
   crmModal(), which does `document.body.appendChild( overlay )` — and that one
   fact decides everything below:

   ⛔ EVERY FORM CONTROL IN HERE IS STYLED FROM SCRATCH, AND THAT IS NOT
   DUPLICATION. The base form rule at the top of this file is
   `.pbjcrm-portal input[type="text"], .pbjcrm-portal select, …` — scoped to the
   portal element. A dialog portalled to <body> is not a descendant of it, so a
   text box in here inherits NOTHING: no border, no padding, no themed surface,
   no themed ink. It is the same trap the client-preview dialog fell into with
   `color` in 1.36.2, and that dialog only got away with the rest of it because
   it has no inputs. This one has four. The values below MATCH the portal's own
   form rule on purpose — same padding, same radius token, same border token —
   so the wizard does not look like a different program.

   ⛔ EVERY var() STATES A LITERAL FALLBACK, for the same reason the overlay
   panel does. The canvas prints the token set on :root so they normally
   resolve; the portal-shortcode escape hatch does not, and a control whose
   background computes to `transparent` on a dark page is a control nobody can
   read what they typed into.

   ⛔ LIGHT AND DARK ARE BOTH TOKENS, NEVER A HARD-CODED PAIR. The one thing
   this block says about dark specifically is `color-scheme`, which is what
   tells the BROWSER to draw its own radio dots and drop-down list dark — the
   same fix, and the same reason, as the portal's `.pbjcrm-is-dark` rule for
   date pickers. The theme classes are on the BODY as well as on the portal
   element (PBJ_CRM_Theme::body_classes() via body_class), which is the only
   handle a body-level dialog has.

   ⛔ ONE COLUMN AT PHONE WIDTH, AND IT IS DONE WITH `flex-wrap` FIRST AND A
   MEDIA QUERY SECOND. The three-way control wraps on its own the moment the
   words do not fit; the query at the bottom only takes over the parts that
   wrapping cannot fix — the segments going full-width so they are still a
   thumb's worth of target at 390px, and the footer buttons splitting the row
   evenly instead of squeezing. .pbjcrm-mobile-on is deliberately NOT used: it
   lives on the portal element, so it never matches inside this dialog, and a
   rule written against it here would silently do nothing.
   =========================================================================== */

/* -- The chooser: two ways to convert, each with a sentence saying which. -- */

.pbjcrm-split-choices {
	display: flex;
	flex-direction: column;
	gap: 12px;
	margin: 12px 0 0;
}

.pbjcrm-split-choice {
	border: 1px solid var(--pbjcrm-border, #d8dce0);
	border-radius: var(--pbjcrm-radius, 6px);
	background: var(--pbjcrm-surface-alt, #f6f7f8);
	padding: 12px 14px;
}

/* -- The wizard shell. --

   ⛔ .pbjcrm-split-wizard, NOT .pbjcrm-split. The bare class is the companies-
   and-people two-column grid at line 1569 above; the wizard is a stack inside
   a 640px dialog and would have been laid out in two columns by it. */

.pbjcrm-split-wizard {
	display: block;
}

.pbjcrm-split-steps {
	margin: 0 0 12px;
}

.pbjcrm-split-field {
	margin: 0 0 14px;
}

.pbjcrm-split-label {
	display: block;
	font-weight: 600;
	font-size: 13px;
	margin: 0 0 3px;
	color: var(--pbjcrm-text, #1d2327);
}

/* The portal's form rule cannot reach in here — see the note above. */
.pbjcrm-split-text,
.pbjcrm-split-select {
	width: 100%;
	max-width: 100%;
	padding: 7px 10px;
	border: 1px solid var(--pbjcrm-border, #d8dce0);
	border-radius: var(--pbjcrm-radius-sm, 3px);
	background: var(--pbjcrm-surface, #ffffff);
	color: var(--pbjcrm-text, #1d2327);
	font-size: 14px;
	font-family: inherit;
}

.pbjcrm-split-select {
	width: auto;
	min-width: 160px;
}

.pbjcrm-split-text:focus-visible,
.pbjcrm-split-select:focus-visible,
.pbjcrm-split-segopt input:focus-visible {
	outline: 2px solid var(--pbjcrm-focus-ring, var(--pbjcrm-accent, #2271b1));
	outline-offset: 2px;
}

/* The browser's own widgets — the radio dot and the drop-down list — follow
   the page rather than the default white. Same fix as the portal's date
   pickers; the handle is the body class because the dialog is on <body>. */
body.pbjcrm-is-dark .pbjcrm-split-text,
body.pbjcrm-is-dark .pbjcrm-split-select,
body.pbjcrm-is-dark .pbjcrm-split-segopt input {
	color-scheme: dark;
}

/* The plain sentence under a control. --pbjcrm-text-soft, not --pbjcrm-muted:
   this is the line that explains what the choice DOES, so it is body copy and
   owes 4.5:1 — the same measurement that put the dialog's other small print on
   the soft token rather than the muted one. */
.pbjcrm-split-help {
	margin: 5px 0 0;
	font-size: 12px;
	font-weight: 400;
	line-height: 1.45;
	color: var(--pbjcrm-text-soft, #3c434a);
}

/* -- Step 2 and 3: one row per line of the record. -- */

.pbjcrm-split-rows {
	display: flex;
	flex-direction: column;
	gap: 10px;
	margin: 12px 0 0;
}

.pbjcrm-split-row {
	border: 1px solid var(--pbjcrm-border, #d8dce0);
	border-radius: var(--pbjcrm-radius, 6px);
	background: var(--pbjcrm-surface, #ffffff);
	padding: 10px 12px;
}

.pbjcrm-split-rowhead {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: 4px 10px;
	margin: 0 0 8px;
}

.pbjcrm-split-rowname {
	font-size: 14px;
	color: var(--pbjcrm-text, #1d2327);
}

/* What is actually in the field, so the choice is about something you can see.
   min-width: 0 lets a long email address wrap instead of pushing the row wider
   than the dialog — the same lesson as the record layout's grid items. */
.pbjcrm-split-rowvalue {
	min-width: 0;
	font-size: 12px;
	color: var(--pbjcrm-text-soft, #3c434a);
	overflow-wrap: anywhere;
}

/* -- The three-way control. Real radios; this is only the look. -- */

/* The wizard portals to <body>, OUTSIDE .pbjcrm-portal — so the portal's
   box-sizing reset never reaches it, and a width:100% control with 12px
   padding overflows a 390px phone by its own padding. Measured on the
   1.55.0 proof: wizard scrollWidth 341 vs clientWidth 328. One scoped
   reset closes the class of bug, not just the instance (pbj-behavior 9b). */
.pbjcrm-split-wizard,
.pbjcrm-split-wizard * {
	box-sizing: border-box;
}

.pbjcrm-split-seg {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
}

.pbjcrm-split-segopt {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	margin: 0;
	padding: 6px 12px;
	border: 1px solid var(--pbjcrm-border-strong, #b4bcc3);
	border-radius: var(--pbjcrm-radius, 6px);
	background: var(--pbjcrm-surface, #ffffff);
	color: var(--pbjcrm-text, #1d2327);
	font-size: 13px;
	font-weight: 600;
	line-height: 1.3;
	cursor: pointer;
	/* A control you press with a thumb in a van. */
	min-height: 38px;
}

.pbjcrm-split-segopt:hover {
	background: var(--pbjcrm-surface-alt, #f6f7f8);
}

/* The picked one. The accent pair is the same one .pbjcrm-btn-primary uses, so
   "chosen" looks the same here as it does everywhere else in the portal. */
.pbjcrm-split-segopt-on {
	background: var(--pbjcrm-accent, #2271b1);
	border-color: var(--pbjcrm-accent, #2271b1);
	color: var(--pbjcrm-accent-contrast, #ffffff);
}

.pbjcrm-split-segopt-on:hover {
	background: var(--pbjcrm-accent-strong, #135e96);
}

/* Locked — the job title, which a business cannot have. It still READS, it
   just cannot be argued with, and the sentence under it says why. */
.pbjcrm-split-seg-locked .pbjcrm-split-segopt {
	cursor: not-allowed;
	opacity: 0.75;
}

.pbjcrm-split-seg-locked .pbjcrm-split-segopt-on {
	opacity: 1;
}

.pbjcrm-split-bindrow .pbjcrm-split-select {
	margin: 0;
}

/* -- Step 4: the sentences, and what the server said. -- */

/* ⛔ NOT `display: flex` WITH A GAP, TEMPTING AS IT IS. Flexing a <ul> blocks
   its children, and a blocked <li> loses its marker — the summary would have
   come out as five unbulleted sentences in a row, which is a paragraph, not a
   list of what is about to happen. Margin does the spacing. */
.pbjcrm-split-summary {
	margin: 12px 0 0;
	padding: 0 0 0 20px;
	font-size: 14px;
	line-height: 1.5;
	color: var(--pbjcrm-text, #1d2327);
}

.pbjcrm-split-summary li {
	margin: 0 0 6px;
}

.pbjcrm-split-says {
	margin: 10px 0 0;
}

.pbjcrm-split-donehead {
	margin: 0;
	font-size: 15px;
	font-weight: 700;
	color: var(--pbjcrm-text, #1d2327);
}

/* -- The row of buttons at the bottom of every step. -- */

.pbjcrm-split-foot {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
	margin: 16px 0 0;
	padding-top: 12px;
	border-top: 1px solid var(--pbjcrm-border, #d8dce0);
}

/* -- The way back to the person, in the toast that says it worked. -- */

/* The toast is portalled to <body> too, so the button inside it needs its own
   surface stated rather than the tint it is sitting on. It is a block so the
   sentence keeps its line. */
.pbjcrm-split-toastlink {
	display: inline-flex;
	margin: 8px 0 0;
	background: var(--pbjcrm-surface, #ffffff);
	border-color: var(--pbjcrm-border-strong, #b4bcc3);
	color: var(--pbjcrm-text, #1d2327);
}

/* -- 390px: one column, and nothing off the edge. -- */

@media (max-width: 480px) {
	/* The three answers stack and go full-width. Wrapping alone would leave
	   "Person only" alone on a second line at half the width of the two above
	   it, which reads as a different kind of control. */
	.pbjcrm-split-seg {
		flex-direction: column;
		align-items: stretch;
	}

	.pbjcrm-split-segopt {
		width: 100%;
		justify-content: flex-start;
	}

	.pbjcrm-split-select {
		width: 100%;
		min-width: 0;
	}

	/* Every step's buttons share the row evenly instead of squeezing. */
	.pbjcrm-split-foot .pbjcrm-btn {
		flex: 1 1 auto;
		text-align: center;
	}

	.pbjcrm-split-choice {
		padding: 10px 12px;
	}

	.pbjcrm-split-choice .pbjcrm-btn {
		width: 100%;
	}
}

/* ===========================================================================
   PHASE 19 — THE UNIFIED CARD SYSTEM (1.55.0)

   The JS half is the "PHASE 19" block in assets/js/pbj-crm.js. Read that first:
   it explains the three channels (left edge = type, icon hue = who it concerns,
   icon fill = open or idle) and why an unknown row is
   drawn neutral rather than guessed at.

   ⛔ .pbjcrm-ucard DOES NOT RESTATE BORDER, BACKGROUND OR THE LEFT EDGE, AND
   THAT IS THE DESIGN, NOT AN OMISSION. Three surfaces adopt this skeleton and
   all three already bring their own chrome:

       .pbjcrm-listcard   — the people/business grid card (border, radius,
                            surface, hover, and the 4px left type bar a sibling
                            added this morning at line ~3527)
       .pbjcrm-queue-row  — Today's rows (border-left-width: 4px, per-kind
                            colour from .pbjcrm-queue-kind-*)
       .pbjcrm-inbox-row  — the inbox rows (same arrangement, .pbjcrm-inbox-kind-*)

   Restating `border` here would draw a second hairline over each of them and
   would fight three different hover rules. What .pbjcrm-ucard owns is the
   INSIDE — the icon column, fixed semantic slots, footer/actions, open-item
   row and the right-edge selection tick. Status is written, never another edge.

   ⛔ EVERY COLOUR IS A TOKEN. The state tokens are registered server-side
   (PBJ_CRM_Theme::record_color_map(): state-client, state-internal,
   state-resolved, state-idle, rec-business) and emitted with the rest of the
   set, so an owner who changes them changes the cards AND the legend at once.
   Literal fallbacks are stated everywhere because a portal rendered through
   the shortcode escape hatch has no :root tokens to inherit — and because
   --pbjcrm-state-idle must NEVER fall back to white: idle is a readable
   neutral on light and dark alike, and white would vanish on both a white card
   and a dark preset.
   =========================================================================== */

.pbjcrm-ucard {
	position: relative;
	/* The tick lives in the top-right corner, so the content keeps clear of it.
	   Applied on the CARD rather than on each surface's head, so a surface that
	   never shows a tick still lines up with one that does. */
	padding-right: 30px;
	/* ⛔ THE TEMPLATE OWNS THE LEFT EDGE. See the block below. */
	border-left-width: 4px;
	border-left-style: solid;
}

/* ===========================================================================
   ONE LEFT BAR (Jake, 2026-08-19, with a screenshot of the Dashboard)

   THE DEFECT: seven different rule sets each painted their own left edge, at
   two different widths, from three different token families —

       .pbjcrm-listcard / -business   4px, --pbjcrm-rec-contact / -business
       .pbjcrm-queue-kind-*           4px, --pbjcrm-rec-*
       .pbjcrm-inbox-kind-*           4px, --pbjcrm-rec-* and the accent
       .pbjcrm-feed-row               3px, --pbjcrm-border (flat grey)
       .pbjcrm-task-card              3px, --pbjcrm-rec-task
       .pbjcrm-side-item              4px, --pbjcrm-rec-contact on EVERY row
       .pbjcrm-cal-item[cat]          3px, the --pbjcrm-cal-* family
       .pbjcrm-mrow-kind-* (phone)    3px, --pbjcrm-rec-*

   Side by side that reads as two different bars meaning two different things,
   because that is what it was: a 3px grey stripe next to a 4px purple one is
   not "the same language, two screens".

   THE FIX IS ONE RULE SET, NOT EIGHT PATCHES (rule 9b). .pbjcrm-ucard states
   the WIDTH and STYLE; .pbjcrm-ucard-type-<icon> states the COLOUR; both are
   declared later in this file than every rule above, at plain class
   specificity, so they win wherever the template lands and the old rules stay
   as the fallback for anything the template has not reached. There is never a
   second border — the same edge is simply painted once, by one owner.

   ⚠️ TWO RULES DELIBERATELY STILL OUT-RANK THIS, AND BOTH ARE RIGHT.
   `.pbjcrm-listcard:hover` (0,2,0) brightens the edge on hover, which is the
   behaviour the grid always had. `.pbjcrm-cal-item[class*="cal-cat-"]` (0,2,0)
   keeps the calendar's own hue — and that is not a conflict, because
   --pbjcrm-cal-tasks IS var(--pbjcrm-rec-task); the calendar just reaches the
   same colour by its own name. What it does NOT keep is the odd 3px, because
   that comes from the width above.

   ⛔ THE FOUR COMMUNICATION KINDS AND THE VAULT WEAR THE ACCENT, and that is
   the same documented decision the inbox rules already record: PBJ_CRM_Theme
   emits no rec-email / rec-text / rec-call / rec-voicemail / rec-vault, and
   inventing a var() name nobody declares falls through to whatever literal was
   typed beside it on every preset. When the theme grows a communication
   colour, these are the lines that change.
   =========================================================================== */

.pbjcrm-ucard-type-person { border-left-color: var(--pbjcrm-rec-contact, #2271b1); }
.pbjcrm-ucard-type-business { border-left-color: var(--pbjcrm-rec-business, #1d4ed8); }
.pbjcrm-ucard-type-ticket { border-left-color: var(--pbjcrm-rec-ticket, #6d3fa8); }
.pbjcrm-ucard-type-task { border-left-color: var(--pbjcrm-rec-task, #8c5e00); }
.pbjcrm-ucard-type-visit { border-left-color: var(--pbjcrm-rec-visit, #0f766e); }
.pbjcrm-ucard-type-appointment { border-left-color: var(--pbjcrm-rec-appointment, var(--pbjcrm-rec-visit, #0f766e)); }
/* An estimate and a repeating bill are the invoicing module's records; they
   share its hue rather than each inventing one. */
.pbjcrm-ucard-type-invoice,
.pbjcrm-ucard-type-estimate,
.pbjcrm-ucard-type-recurring { border-left-color: var(--pbjcrm-rec-invoice, #1a7a3c); }
.pbjcrm-ucard-type-deal { border-left-color: var(--pbjcrm-rec-deal, #b3541e); }
.pbjcrm-ucard-type-campaign { border-left-color: var(--pbjcrm-rec-marketing, #0f7b8a); }
.pbjcrm-ucard-type-file { border-left-color: var(--pbjcrm-rec-import, #5b6770); }
/* See the ⛔ above: no token exists for these five yet. */
.pbjcrm-ucard-type-note,
.pbjcrm-ucard-type-email,
.pbjcrm-ucard-type-text,
.pbjcrm-ucard-type-call,
.pbjcrm-ucard-type-voicemail,
.pbjcrm-ucard-type-vault,
.pbjcrm-ucard-type-event { border-left-color: var(--pbjcrm-accent, #2271b1); }

/* The Today rail painted --pbjcrm-rec-contact on every row regardless of what
   the row was, so a ticket and a task were the same blue. The template's type
   edge replaces it outright. */
.pbjcrm-side-item.pbjcrm-ucard {
	border-left-width: 4px;
}

/* -- The skeleton: icon column, then the words. -- */

.pbjcrm-ucard-main {
	display: flex;
	align-items: flex-start;
	gap: 8px;
	min-width: 0;
}

.pbjcrm-ucard-body {
	/* min-width: 0 is what lets a long email address wrap instead of pushing
	   the card wider than its column — the same lesson the record layout's grid
	   items learned at the 782px breakpoint. */
	min-width: 0;
	flex: 1 1 auto;
	display: flex;
	flex-direction: column;
	gap: 2px;
}

.pbjcrm-ucard-title {
	font-weight: 600;
	color: var(--pbjcrm-text, #1d2327);
	overflow-wrap: anywhere;
}

.pbjcrm-ucard-line {
	margin: 0;
	font-size: 13px;
	overflow-wrap: anywhere;
}

.pbjcrm-ucard-foot {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: 8px;
	margin-top: 6px;
	padding-top: 4px;
	font-size: 12px;
}

.pbjcrm-ucard-map .pbjcrm-maplinks {
	margin-left: 4px;
}

/* -- The bottom row of open-item icons (businesses). -- */

.pbjcrm-ucard-items {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
	margin-top: 6px;
	padding-top: 6px;
	border-top: 1px solid var(--pbjcrm-border, #d8dce0);
}

/* -- The select tick, top-right, identical on every card that has one. -- */

.pbjcrm-ucard-tick {
	position: absolute;
	top: 8px;
	right: 8px;
	/* A thumb-sized target even though the box itself is small. */
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 40px;
	min-height: 40px;
	cursor: pointer;
}

/* -- THE ICON. One drawing, four hues, two fills. -- */

.pbjcrm-cardicon {
	display: inline-flex;
	align-items: center;
	gap: 3px;
	flex: none;
	line-height: 1;
	/* currentColor is what the SVG strokes and fills with, so setting `color`
	   here is what makes one drawing serve all four states. */
	color: var(--pbjcrm-state-idle, #8c8f94);
}

.pbjcrm-cardicon svg {
	display: block;
	/* The stroke is the drawing; it must not scale away on a small icon. */
	vector-effect: non-scaling-stroke;
}

.pbjcrm-cardicon-idle { color: var(--pbjcrm-state-idle, #8c8f94); }
.pbjcrm-cardicon-client { color: var(--pbjcrm-state-client, #d63638); }
.pbjcrm-cardicon-internal { color: var(--pbjcrm-state-internal, #dba617); }
.pbjcrm-cardicon-resolved { color: var(--pbjcrm-state-resolved, #00a32a); }

/* FILLED = OPEN, OUTLINE = IDLE — the colourblind cue, and the reason the
   whole set is one drawing rather than nineteen pairs. The marked body shape
   takes a wash of currentColor and the stroke thickens; everything else in the
   drawing stays a stroke, so a filled icon is still recognisably the same
   picture. The wash is deliberately partial: a solid fill swallows the
   detail lines drawn on top of it (the building's windows, the invoice's
   currency mark) and every icon collapses into a blob.

   The flat rule is stated FIRST and the color-mix() version second, so a
   browser that has never heard of color-mix() keeps a working fill rather than
   none — the same belt-and-braces this file uses for every tint. */
.pbjcrm-ucard-open .pbjcrm-iconfill {
	fill: currentColor;
	fill-opacity: 0.22;
}

.pbjcrm-ucard-open svg {
	stroke-width: 2.1;
}

/* The number that rides beside the icon: the employee count on a business, the
   "3/5" on a task, the "2" on an open-item chip. */
.pbjcrm-cardicon-count {
	font-size: 12px;
	font-weight: 700;
	line-height: 1;
	color: var(--pbjcrm-text, #1d2327);
	font-variant-numeric: tabular-nums;
}

/* -- Task controls: a BUTTON to finish, ticks only in the dropdown. -- */

.pbjcrm-ucard-tools {
	margin-top: 8px;
	display: flex;
	flex-direction: column;
	gap: 6px;
}

.pbjcrm-ucard-toolbar {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 6px;
}

.pbjcrm-ucard-checklist[hidden] {
	display: none;
}

/* The two task buttons get named hooks even though the shared button styles do
   the work: "Done" and "Show 3/5 steps" are the two controls a module or a
   later pass is most likely to want to reach, and a class added later cannot be
   relied on by anything already shipped. */
.pbjcrm-ucard-done {
	flex: none;
}

.pbjcrm-ucard-expand {
	font-variant-numeric: tabular-nums;
}

/* Where the in-card question appears. Empty until something is asked, so it
   must contribute no space of its own. */
.pbjcrm-ucard-ask:empty {
	display: none;
}

/* The confirm, drawn INSIDE the card — window.confirm() is not available to
   this system. It has to read as a question the card is asking, not as another
   row of the checklist, so it gets the warning tint and its own edge. */
.pbjcrm-ucard-confirm {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 8px;
	padding: 8px 10px;
	border: 1px solid #e8d08a;
	border-color: color-mix(in srgb, var(--pbjcrm-state-internal, #dba617) 45%, var(--pbjcrm-surface, #ffffff));
	border-radius: var(--pbjcrm-radius, 6px);
	background: #fdf8e8;
	background: color-mix(in srgb, var(--pbjcrm-state-internal, #dba617) 12%, var(--pbjcrm-surface, #ffffff));
}

/* ⛔ THE INK IS --pbjcrm-text, NOT THE WARNING COLOUR. Amber-on-amber is the
   same measurement the toast block records: it does not reach 4.5:1 on every
   preset, and the tint plus the border already carry which kind of message
   this is. */
.pbjcrm-ucard-confirm-q {
	font-size: 13px;
	font-weight: 600;
	color: var(--pbjcrm-text, #1d2327);
}

/* -- THE CARD LEGEND (Setup, and the admin Theming screen when it wires up). */

.pbjcrm-legend {
	/* It is a .pbjcrm-card as well, so it already has the surface and the edge;
	   this only stops the first heading fighting the card's own padding. */
	margin-top: var(--pbjcrm-space-sm);
}

.pbjcrm-legend-head {
	margin: 14px 0 6px;
	font-size: 13px;
	text-transform: uppercase;
	letter-spacing: .06em;
	/* text-soft, not muted: 13px uppercase is body text and owes 4.5:1, which
	   --pbjcrm-muted misses on two of the eight presets. Same call as the
	   client-view dialog's section labels. */
	color: var(--pbjcrm-text-soft, #3c434a);
}

.pbjcrm-legend-head:first-child {
	margin-top: 0;
}

.pbjcrm-legend-rows {
	display: flex;
	flex-direction: column;
	gap: 4px;
}

.pbjcrm-legend-row {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: 10px;
	padding: 5px 8px;
	border: 1px solid var(--pbjcrm-border);
	border-radius: var(--pbjcrm-radius);
}

.pbjcrm-legend-samples {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	flex: none;
}

.pbjcrm-legend-word {
	flex: 1 1 12em;
	min-width: 0;
	font-size: 13px;
}

/* The live value, printed beside the swatch. It is what getComputedStyle()
   actually returned — the legend proving it read the real thing rather than a
   list somebody typed. */
.pbjcrm-legend-value {
	font-size: 11px;
	color: var(--pbjcrm-text-soft, #3c434a);
	white-space: nowrap;
}

.pbjcrm-legend-swatch {
	display: inline-block;
	width: 18px;
	height: 18px;
	border-radius: var(--pbjcrm-radius-sm);
	/* --pbjcrm-edge, not --pbjcrm-border: a swatch has to say where it ENDS
	   even when the colour it carries is nearly the surface behind it. */
	border: 1px solid var(--pbjcrm-edge, #8e9296);
}

/* A token that resolved to nothing, and "no stripe at all". Drawn as an empty
   box rather than left blank, so "not set" is visibly a state. */
.pbjcrm-legend-swatch-none {
	background: repeating-linear-gradient(
		45deg,
		transparent,
		transparent 3px,
		var(--pbjcrm-border, #d8dce0) 3px,
		var(--pbjcrm-border, #d8dce0) 5px
	);
}

.pbjcrm-legend-icons {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
	gap: 6px;
}

.pbjcrm-legend-icon {
	display: flex;
	align-items: center;
	gap: 7px;
	padding: 5px 8px;
	border: 1px solid var(--pbjcrm-border);
	border-radius: var(--pbjcrm-radius);
	font-size: 13px;
}

.pbjcrm-legend-iconword {
	min-width: 0;
	overflow-wrap: anywhere;
}

/* -- Bridging the three surfaces onto the skeleton. --

   These are the ONLY places the old per-surface rules and the new skeleton
   overlap, and both are real defects if left out:

   1. .pbjcrm-listcard is a flex column in a stretch grid, and its footer used
      `margin-top: auto` to sit at the BOTTOM so footers line up across a row of
      unequal cards. The footer is a .pbjcrm-ucard-foot now, so it has to be
      given that push back or a row of cards ends up with its footers at four
      different heights.
   2. .pbjcrm-listcard-head reserved 26px on its right for the tick. The
      skeleton reserves that on the CARD now, so leaving the head's padding on
      indents the title twice — visible as a title that will not line up with
      the sub-lines beneath it. */

.pbjcrm-listcard .pbjcrm-ucard-foot {
	margin-top: auto;
}

.pbjcrm-ucard .pbjcrm-listcard-head {
	padding-right: 0;
}

/* -- The inbox side rail keeps its one-line shape. --

   The compact row drops the SUB-LINE (the JS does that) but keeps the badge,
   the count and the "when" — so the footer is tucked back onto the title's
   line rather than opening a second row in a narrow rail. */
.pbjcrm-inbox-compact .pbjcrm-ucard-main {
	flex-wrap: wrap;
}

.pbjcrm-inbox-compact .pbjcrm-ucard-foot {
	margin-top: 2px;
	padding-top: 0;
}

/* -- 390px: one column, everything wraps, nothing under 38px. -- */

@media (max-width: 480px) {
	/* The card's own gutter for the tick shrinks — 30px of reserved space is a
	   tenth of a 390px screen. */
	.pbjcrm-ucard {
		padding-right: 26px;
	}

	.pbjcrm-ucard-main {
		/* The icon stays on the title's line; only the words wrap. Wrapping the
		   ICON down onto its own row is what makes a phone list look like a
		   different program from the desktop one. */
		flex-wrap: nowrap;
	}

	/* Thumb targets. The buttons a task card offers are the ones somebody
	   presses standing in a van, so they get the height whatever the theme's
	   density preset says. */
	.pbjcrm-ucard-toolbar .pbjcrm-btn,
	.pbjcrm-ucard-confirm .pbjcrm-btn {
		min-height: 38px;
	}

	.pbjcrm-ucard-toolbar .pbjcrm-btn {
		flex: 1 1 auto;
		text-align: center;
	}

	.pbjcrm-ucard-confirm {
		/* The question above, the two answers below — side by side at this width
		   the question is squeezed to two words a line. */
		flex-direction: column;
		align-items: stretch;
	}

	.pbjcrm-ucard-tick {
		min-width: 38px;
		min-height: 38px;
		top: 2px;
		right: 2px;
	}

	.pbjcrm-legend-icons {
		grid-template-columns: minmax(0, 1fr);
	}

	.pbjcrm-legend-value {
		/* Last on the line, and allowed to have the whole width when it lands
		   there — a nowrap hex beside a wrapped sentence forces a scrollbar. */
		flex: 1 1 100%;
	}
}

/* ===========================================================================
   PHASE 20 — THE EMAIL BLOCK BUILDER (compose UI, 1.55.0)

   The JS half is the "PHASE 20" block in assets/js/pbj-crm.js. Two panes live
   inside one campaign editor; the mode dropdown shows one and hides the other
   with the `hidden` attribute, so there is no second render path and half-typed
   work in the pane you are not looking at survives.

   ⛔ THE PREVIEW IFRAME IS THE ONE MARKUP SURFACE IN THE PORTAL, and it is
   sandboxed to nothing (sandbox="" — every permission withheld). It therefore
   cannot inherit a single style from this file, which is correct: what it shows
   must be what a mail client will show, not what our stylesheet would make of
   it. Everything below styles the box AROUND it and never reaches inside.
   =========================================================================== */

/* Both panes are built every time; the mode picker hides one. */
.pbjcrm-writepane[hidden] {
	display: none;
}

/* The sentence under a locked control. --pbjcrm-text-soft, not muted: it is the
   line that explains why a control will not move, so it owes 4.5:1 like any
   other body copy. */
.pbjcrm-locked-note {
	color: var(--pbjcrm-text-soft, #3c434a);
	font-weight: 600;
}

/* -- The block list. -- */

.pbjcrm-blocks {
	margin: 0 0 var(--pbjcrm-space);
}

.pbjcrm-blocks-list {
	display: flex;
	flex-direction: column;
	gap: 8px;
	margin: 0 0 var(--pbjcrm-space-sm);
}

.pbjcrm-block {
	border: 1px solid var(--pbjcrm-border);
	/* The same 4px type edge language the unified cards use, so a block reads
	   as a thing in a list rather than as another form panel. Marketing's own
	   hue, because that is what this is. */
	border-left: 4px solid var(--pbjcrm-rec-marketing, var(--pbjcrm-accent));
	border-radius: var(--pbjcrm-radius);
	background: var(--pbjcrm-surface);
	padding: 10px 12px;
	min-width: 0;
}

/* A divider and a spacer have nothing to fill in, so they are quieter. */
.pbjcrm-block-divider,
.pbjcrm-block-spacer {
	border-left-color: var(--pbjcrm-border-strong);
	background: var(--pbjcrm-surface-alt);
}

.pbjcrm-block-head {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	justify-content: space-between;
	gap: 6px;
	margin: 0 0 8px;
}

.pbjcrm-block-name {
	font-size: 13px;
	text-transform: uppercase;
	letter-spacing: .05em;
	color: var(--pbjcrm-text-soft, #3c434a);
}

.pbjcrm-block-tools {
	display: flex;
	align-items: center;
	gap: 4px;
	flex-wrap: wrap;
}

/* The two arrows are square and the same height as the Remove beside them, so
   the row does not step up and down. */
.pbjcrm-block-tools .pbjcrm-btn {
	min-height: 32px;
}

.pbjcrm-block-body .pbjcrm-field:last-child {
	margin-bottom: 0;
}

/* The portal's own form rule already paints these; the class is here so a
   block's boxes can be found and so width is stated once. */
.pbjcrm-block-input {
	width: 100%;
	max-width: 100%;
}

.pbjcrm-blocks-add {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
}

.pbjcrm-blocks-says:empty {
	display: none;
}

.pbjcrm-blocks-says {
	margin-top: 6px;
}

/* -- The preview, in its dialog. -- */

.pbjcrm-preview-pane {
	min-width: 0;
}

/* ⛔ A WHITE PAPER BEHIND IT, ON EVERY PRESET INCLUDING DARK. An email is a
   white page in somebody's inbox; showing it on the portal's dark surface would
   be a preview of something that does not exist. This is the same reasoning as
   the print block's "paper is white" — one right answer regardless of theme. */
.pbjcrm-preview-frame {
	display: block;
	width: 100%;
	height: min(60vh, 520px);
	border: 1px solid var(--pbjcrm-edge, #8e9296);
	border-radius: var(--pbjcrm-radius-sm);
	background: #ffffff;
}

.pbjcrm-preview-plain {
	margin-top: 10px;
}

.pbjcrm-preview-plain > summary {
	cursor: pointer;
	font-weight: 600;
	color: var(--pbjcrm-link);
}

/* The plain-text twin. `pre` so the line breaks that ARE the formatting survive,
   wrapped so it cannot push the dialog wider than the screen. */
.pbjcrm-preview-text {
	margin: 8px 0 0;
	padding: 8px 10px;
	max-height: 240px;
	overflow: auto;
	border: 1px solid var(--pbjcrm-border);
	border-radius: var(--pbjcrm-radius-sm);
	background: var(--pbjcrm-surface-alt);
	color: var(--pbjcrm-text);
	font-size: 12px;
	line-height: 1.5;
	white-space: pre-wrap;
	overflow-wrap: anywhere;
}

/* -- 390px. -- */

@media (max-width: 480px) {
	/* Six "+ Heading" buttons in a row is six buttons two words wide. Two up
	   fits the words and keeps a thumb's worth of target. */
	.pbjcrm-blocks-add .pbjcrm-btn {
		flex: 1 1 45%;
		min-height: 38px;
		text-align: center;
	}

	/* The tools drop under the block's name rather than squeezing beside it. */
	.pbjcrm-block-head {
		flex-direction: column;
		align-items: flex-start;
	}

	.pbjcrm-block-tools .pbjcrm-btn {
		min-height: 38px;
	}

	.pbjcrm-preview-frame {
		height: 55vh;
	}
}

/* ===========================================================================
   PRINTING — THE ONE PRINT BLOCK FOR THE WHOLE PORTAL (2026-08-13)

   Jake: "All items should have a clear edit button on the top … as well as a
   print icon". The icon is the easy half. This is the half that decides what
   comes out of the printer.

   THE RULE: what prints is the RECORD, not the software around it. Everything
   somebody clicks — the menus, the bars, the buttons, the tabs, the paging —
   is chrome, and chrome on paper is wasted ink and a page of dead links.

   THIS IS THE ONLY @media print BLOCK IN THIS FILE AND THERE MUST NEVER BE A
   SECOND ONE. The module plugins render INSIDE this shell, so they inherit
   these rules for free and only have to answer for their own classes —
   pbj-invoicing has its own short block for the invoice action bar and nothing
   else. A module re-hiding .pbjcrm-nav here would be the same rule written
   twice, which is the one thing this suite does not do.

   ⛔ THIS BLOCK USED TO CARRY NO COLOUR RULES, ON PURPOSE, AND THAT WAS WRONG.
   The reasoning was: "a dark preset prints dark, and the honest fix is the
   browser's own background-graphics setting — repainting the portal white here
   would mean hard-coding colours this file spends 3,000 lines refusing to
   hard-code." It reads well and it produced a BLANK SHEET.

   What was actually observed on 2026-08-13, on paper, which is the only way
   this class of defect is ever caught: on a dark preset a visit and an agent
   invoice printed as very nearly nothing. Chrome's DEFAULT is background
   graphics OFF, so the dark card fills do not print — but the near-white text
   (#eceef1) does, onto white paper. INV-0005, "Line items", "Work done" and
   every total were invisible. The setting the old comment relied on is not a
   fix a customer will find; it is off by default and it is the reason the page
   was empty.

   So: on PAPER only, the portal is black on white. This is not theming and it
   does not touch a single on-screen rule — @media print is the one context
   where there is exactly one right answer regardless of preset, because paper
   is white. pbj-helpdesk reached the same conclusion independently and shipped
   it first; this is that rule brought back to the shell, where it belongs.

   ⛔ IT LIVES HERE AND NOWHERE ELSE. pbj-dispatch and pbj-invoicing render
   INSIDE this shell, so one rule scoped to .pbjcrm-portal covers all three —
   which is the very thing the paragraph above says this suite does not do
   twice. Do not add a colour rule to a module's own print block.
   =========================================================================== */

/* ===========================================================================
   1.56.0 — MULTI-SELECT, THE MERGE TAB AND THE MERGE WIZARD
   ---------------------------------------------------------------------------
   ⛔ EVERY COLOUR HERE IS A TOKEN WITH A FALLBACK, never a bare literal, so a
   theme that redefines the palette moves these with it and a stylesheet loaded
   before the token block still renders something sane.

   ⛔ THE MERGE WIZARD REUSES THE SPLIT WIZARD'S CHROME ON PURPOSE, and there is
   almost nothing below because of it. .pbjcrm-split-wizard, .pbjcrm-split-row,
   .pbjcrm-split-seg, .pbjcrm-split-segopt, .pbjcrm-split-rows,
   .pbjcrm-split-foot, .pbjcrm-split-summary and .pbjcrm-stepper all already
   exist and already answer to the dark theme and to the 480px block. Jake asked
   for "simular as splitting but in reverse"; copying 200 lines of its
   stylesheet under new names is how two screens that are supposed to look
   identical stop looking identical. What follows is only what merge has that
   split has not: two VALUES printed inside each segmented option, and a pair of
   record cards side by side.
   =========================================================================== */

/* The person/business/both box on the two list screens. It is hidden and shown
   from JS (only while somebody is searching), so all this owes it is a shape. */
.pbjcrm-searchscope {
	display: inline-flex;
	align-items: center;
}

/* -- The wizard's segmented options carry a value as well as a word. -- */

.pbjcrm-merge-side {
	display: block;
	font-size: 12px;
	text-transform: uppercase;
	letter-spacing: 0.02em;
	color: var(--pbjcrm-muted, #646d76);
}

.pbjcrm-merge-val {
	display: block;
	/* An email address, an address line or a notes field can be long, and a row
	   that will not wrap pushes the second option off a narrow screen. */
	overflow-wrap: anywhere;
	font-weight: 600;
	color: var(--pbjcrm-text, #1d2327);
}

/* The chosen side, and the locked one, both keep their own words readable —
   .pbjcrm-split-segopt-on already paints the option, so these only stop the
   two lines inheriting a colour that fights it. */
.pbjcrm-split-segopt-on .pbjcrm-merge-side,
.pbjcrm-split-segopt-on .pbjcrm-merge-val {
	color: inherit;
}

/* Step 1: the two records, as one tappable row each. */
.pbjcrm-merge-keeprow {
	display: flex;
	align-items: flex-start;
	gap: 10px;
	cursor: pointer;
}

.pbjcrm-merge-says {
	margin-top: 10px;
}

/* -- The Merge tab's own list. -- */

.pbjcrm-merge-list {
	display: flex;
	flex-direction: column;
	gap: 10px;
	margin-top: 10px;
}

/* A dismissed pair still showing (the "Show dismissed ones" box) reads as
   set aside rather than as live work. Opacity alone would be a colour-only
   signal, so the "Dismissed" badge in the footer carries the same fact in
   words — see mergePairRow(). */
.pbjcrm-merge-pair-off {
	opacity: 0.72;
	border-style: dashed;
}

.pbjcrm-merge-sides {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: 8px;
}

.pbjcrm-merge-side-card {
	flex: 1 1 200px;
	min-width: 0;
}

.pbjcrm-merge-side-name {
	font-weight: 700;
	color: var(--pbjcrm-link, #2271b1);
	overflow-wrap: anywhere;
}

.pbjcrm-merge-side-meta {
	margin: 2px 0 0;
	font-size: 13px;
	overflow-wrap: anywhere;
}

/* "and" between the two records — a joining word, not a heading. */
.pbjcrm-merge-join {
	flex: none;
	font-size: 13px;
	color: var(--pbjcrm-muted, #646d76);
}

.pbjcrm-merge-actions {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
	margin-top: 8px;
}

/* -- The bulk bars. -- */

/* The multi-module bar over the Inbox and Today. It is a .pbjcrm-toolbar
   already; what it adds is that its buttons wrap rather than scroll, because a
   mixed selection can legitimately offer seven of them. */
.pbjcrm-work-bulk {
	flex-wrap: wrap;
	gap: 8px;
	/* It sits above a list somebody is reading, so it stays visible while they
	   scroll to tick the last row. */
	position: sticky;
	top: 0;
	z-index: 2;
	background: var(--pbjcrm-surface, #ffffff);
	border-color: var(--pbjcrm-border-strong, #b4bcc3);
}

/* The finish button on a task's own screen. */
.pbjcrm-task-terminal {
	flex-wrap: wrap;
	gap: 8px;
	margin-bottom: 10px;
}

@media (max-width: 480px) {

	/* On a phone the two records stack, and "and" stops being a column of its
	   own — it reads as a line between them instead. */
	.pbjcrm-merge-side-card {
		flex: 1 1 100%;
	}

	.pbjcrm-merge-join {
		flex: 1 1 100%;
	}

	/* A sticky bar on a short screen would eat the list it is about. */
	.pbjcrm-work-bulk {
		position: static;
	}
}

/* ===========================================================================
   THE NAVIGATION SHELL + THE UNIFIED CONTACTS LIST (handoff 02 §2.2/§2.3/§2.8,
   03 §3.2/§3.3/§3.4, acceptance §1/§3/§5/§7)
   ---------------------------------------------------------------------------
   ⛔ NOT ONE HEX IN THIS SECTION, AND THAT IS THE POINT. Every colour is a
   --nav-* name from assets/css/pbj-crm-nav.css, which is itself an alias layer
   over --pbjcrm-* and therefore over the owner's own theme controls. A literal
   pasted out of the handoff would be right on the Midnight preset and wrong on
   the other sixteen. Where the handoff names a colour that has NO --nav-* name,
   nothing is invented here: the nearest NAMED token is used, the gap is marked
   ⚠️ NO TOKEN at the site, and it is reported upstream. Four such gaps exist
   and all four are marked below.

   ⛔ CHROME GEOMETRY DOES NOT ANSWER TO THE DENSITY SETTING. 48 / 34 / 206 / 60
   come from --nav-header-h / --nav-footer-h / --nav-rail-w /
   --nav-rail-w-collapsed, which resolve to the theme's CHROME_* constants and
   do not move with --pbjcrm-space. Content spacing INSIDE the shell still does,
   exactly as it always has — .pbjcrm-view's own padding below is the seam
   between the two, and it is deliberately the density token.

   ⛔ AND NOTHING HERE MAY WIN OVER THE PHONE. pbj-crm-mobile.css owns the phone
   shape off .pbjcrm-mobile-on and Jake's decision is that the phone stays
   exactly as it is today. That file loads AFTER this one (both the mobile and
   the nav stylesheets are registered with this bundle as a dependency), so at
   equal specificity it already wins — but only on the properties it actually
   states. A shell rule stating `display: flex` where mobile states nothing
   would leak straight through. So the layout rules below are scoped
   `:not(.pbjcrm-mobile-on)`, which is a real check and not an assumption:
   .pbjcrm-portal.pbjcrm-mobile-on is (0,2,0), this is (0,3,0), and the phone
   never sees a rail, a fixed footer or a shell height at all.
   ===========================================================================*/

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

/* ⛔ THE HEADER'S 48px IS ALREADY RESERVED — DO NOT RESERVE IT TWICE. The spec
   (02 §2) puts `padding-top: var(--nav-header-h)` on the content region, but
   pbj-crm-header.css got there first: .pbjcrm-hdr-mount is a block element
   rendered by PHP immediately above #pbj-crm-app carrying `height:
   var(--nav-header-h)`, and its own comment says out loud that if the content
   region ever gains that padding, the mount's height has to come off in the
   same edit. It was read before this was written. So the header offset is the
   MOUNT's and only the footer's is ours — hence the height subtracts the header
   (the mount has already pushed us down by it) and the padding-bottom clears
   the fixed footer.

   ⛔ THE PADDING-BOTTOM IS ON THE SHELL, NOT ON THE VIEW, because acceptance §1
   asks for the rail's bottom-pinned items to be "fully visible — not clipped by
   the footer". Putting it on the view alone would clear the footer for the
   content and let the rail run underneath it. */
.pbjcrm-app.pbjcrm-portal:not(.pbjcrm-mobile-on) {
	display: flex;
	align-items: stretch;

	/* The card is now the application. Everything that made .pbjcrm-portal a
	   centred, padded, bordered box on a page is unwound here — the shell is
	   full-bleed between three fixed regions. */
	width: 100%;
	max-width: none;
	margin: 0;
	padding: 0 0 var(--nav-footer-h);
	border: 0;
	border-radius: 0;
	background: var(--nav-bg-app);
	box-sizing: border-box;

	/* One viewport, minus the header the mount already reserved. dvh second so
	   a browser that has never heard of it keeps the vh line — the same
	   flat-fallback-first discipline the rest of this file uses. */
	height: calc(100vh - var(--nav-header-h));
	height: calc(100dvh - var(--nav-header-h));

	/* ⛔ THIS IS WHAT MAKES .pbjcrm-view THE ONLY SCROLLER. The shell itself
	   never scrolls; it is exactly one viewport tall and clips. Anything that
	   overflows has to be handled by the region it is in. */
	overflow: hidden;
}

/* The bulk bar is 40px, not 34px (02 §2.2 Mode B), so the room reserved for it
   has to grow with it or the last row of the list sits under it — the one thing
   a fixed bar must never do. The footer is a SIBLING of the app, so this is the
   only way to ask from here. */
body:has(.pbjcrm-footer.pbjcrm-footer-bulk) .pbjcrm-app.pbjcrm-portal:not(.pbjcrm-mobile-on) {
	padding-bottom: 40px;
}

/* ⛔ THE ONE SCROLLING REGION ON THE PAGE (02 §2, acceptance §1). `auto` on both
   axes on purpose: a table wider than the column scrolls the view sideways
   rather than being clipped, and a horizontal bar inside the content region is
   still a bar inside the content region.

   The padding is the CONTENT inset and is therefore the density token — it is
   the same --pbjcrm-space-lg the portal card used to carry, moved inwards, so
   the text sits exactly where it sat before the card was unwound. */
.pbjcrm-app.pbjcrm-portal:not(.pbjcrm-mobile-on) > .pbjcrm-view {
	flex: 1 1 auto;
	min-width: 0;
	min-height: 0;
	overflow: auto;
	padding: var(--pbjcrm-space-lg);
	box-sizing: border-box;
}

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

/* ⛔ box-sizing: border-box, AND THE HANDOFF SAYS WHY: "the mockup was clipped
   twice by forgetting this" (02 §2.3). 206px is the OUTER width — the 10px side
   padding and the 1px right border live inside it. */
.pbjcrm-app.pbjcrm-portal:not(.pbjcrm-mobile-on) > .pbjcrm-rail {
	flex: 0 0 var(--nav-rail-w);
	width: var(--nav-rail-w);
	box-sizing: border-box;

	display: flex;
	flex-direction: column;
	align-items: stretch;

	padding: 12px 10px 14px;
	background: var(--nav-bg-rail);
	border-right: 1px solid var(--nav-line);

	font-family: var(--nav-font);

	/* "The rail scrolls internally only if its own content exceeds the
	   viewport" (02 §2). `auto` is exactly that sentence: no bar until there
	   is something to scroll. min-height:0 is what lets a flex child shrink
	   below its content and actually reach that state. */
	min-height: 0;
	overflow-y: auto;
	overflow-x: hidden;
}

.pbjcrm-rail-group + .pbjcrm-rail-group {
	margin-top: 14px;
}

.pbjcrm-rail-label {
	display: block;
	padding-bottom: 4px;
	font-family: var(--nav-font-mono);
	/* One of the two sizes the handoff explicitly takes below 11px. */
	font-size: 9.5px;
	font-weight: 400;
	letter-spacing: .14em;
	text-transform: uppercase;
	color: var(--nav-text-faint);
}

.pbjcrm-rail-item {
	display: flex;
	align-items: center;
	gap: 8px;
	width: 100%;
	box-sizing: border-box;
	margin: 0;
	padding: 7px 9px;
	border: 0;
	border-radius: 7px;
	background: transparent;
	font-family: var(--nav-font);
	font-size: 13px;
	font-weight: 400;
	line-height: 1.3;
	text-align: left;
	color: var(--nav-text-2);
	cursor: pointer;
	/* Anchor for the 44px hit area below. */
	position: relative;
}

.pbjcrm-rail-item + .pbjcrm-rail-item {
	margin-top: 3px;
}

.pbjcrm-rail-item:hover {
	background: var(--nav-bg-panel);
}

.pbjcrm-rail-item.pbjcrm-active {
	background: var(--nav-red);
	color: var(--nav-red-ink);
	font-weight: 500;
}

.pbjcrm-rail-item-label {
	flex: 1 1 auto;
	min-width: 0;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

/* Right-aligned by margin rather than by justify-content, so an item with no
   count looks identical to one with a count (02 §2.3: zero counts are HIDDEN,
   which means the element is simply absent and nothing may depend on it). */
.pbjcrm-rail-count {
	flex: 0 0 auto;
	margin-left: auto;
	font-family: var(--nav-font-mono);
	font-size: 11px;
	color: var(--nav-text-mute);
}

.pbjcrm-rail-item.pbjcrm-active .pbjcrm-rail-count {
	color: var(--nav-red-ink);
}

/* The bottom-pinned utility group (02 §2.3). */
.pbjcrm-rail-pinned {
	margin-top: auto;
	padding-top: 10px;
	border-top: 1px solid var(--nav-line-soft);
}

.pbjcrm-rail-pinned .pbjcrm-rail-item {
	color: var(--nav-text-mute);
}

.pbjcrm-rail-pinned .pbjcrm-rail-item.pbjcrm-active {
	color: var(--nav-red-ink);
}

/* The collapse control at the rail foot. ⚠️ THE HANDOFF GIVES IT NO GEOMETRY OF
   ITS OWN, so it borrows the rail item's — 13px, 7px/9px, 7px radius — rather
   than inventing a second shape for a control that sits in the same column.
   Reported upstream as derived, not specified. */
.pbjcrm-rail-collapse {
	display: flex;
	align-items: center;
	gap: 8px;
	width: 100%;
	box-sizing: border-box;
	margin-top: 10px;
	padding: 7px 9px;
	border: 0;
	border-radius: 7px;
	background: transparent;
	font-family: var(--nav-font);
	font-size: 13px;
	text-align: left;
	color: var(--nav-text-mute);
	cursor: pointer;
	position: relative;
}

.pbjcrm-rail-collapse:hover {
	background: var(--nav-bg-panel);
	color: var(--nav-text-2);
}

/* --------------------------------------------------------- Rail collapsed */

/* 60px, per 02 §2.3 and §2.8. Two doors reach this state and they must look
   identical: the persisted per-account choice (.pbjcrm-rail-collapsed on the
   app) and the automatic collapse below 1100px. The visual rules are therefore
   written once, in a selector list that both doors join.

   ⛔ AND IT IS ICONS-ONLY NOW, AS §2.3 ASKS. This comment used to say the
   opposite — that the contract carried no icon node, so the collapsed rail kept
   a clipped four-character label. That was true when it was written and is not
   true now: pbj-crm.js emits a real .pbjcrm-rail-icon (inline SVG, aria-hidden)
   inside every rail item. The label and the count go, the icon stays, and the
   word is carried for a screen reader by the button's own accessible name
   rather than by anything painted. Corrected where it stands rather than left
   to misdirect the next reader. */
.pbjcrm-app.pbjcrm-rail-collapsed:not(.pbjcrm-mobile-on) > .pbjcrm-rail {
	flex-basis: var(--nav-rail-w-collapsed);
	width: var(--nav-rail-w-collapsed);
	padding-left: 6px;
	padding-right: 6px;
}

.pbjcrm-app.pbjcrm-rail-collapsed:not(.pbjcrm-mobile-on) .pbjcrm-rail-label,
.pbjcrm-app.pbjcrm-rail-collapsed:not(.pbjcrm-mobile-on) .pbjcrm-rail-count,
.pbjcrm-app.pbjcrm-rail-collapsed:not(.pbjcrm-mobile-on) .pbjcrm-rail-item-label {
	display: none;
}

.pbjcrm-app.pbjcrm-rail-collapsed:not(.pbjcrm-mobile-on) .pbjcrm-rail-item,
.pbjcrm-app.pbjcrm-rail-collapsed:not(.pbjcrm-mobile-on) .pbjcrm-rail-collapse {
	justify-content: center;
	padding-left: 4px;
	padding-right: 4px;
}

/* Nothing is left in the item but the icon, so the gap that separated it from
   the words would centre the icon off-centre. */
.pbjcrm-app.pbjcrm-rail-collapsed:not(.pbjcrm-mobile-on) .pbjcrm-rail-item,
.pbjcrm-app.pbjcrm-rail-collapsed:not(.pbjcrm-mobile-on) .pbjcrm-rail-collapse {
	gap: 0;
}

/* -------------------------------------------------------------- The footer */

/* 02 §2.2. A SIBLING of #pbj-crm-app, not a child of it, so it is styled
   unscoped and keeps itself off the phone through the :has() rule at the foot
   of this section.

   z-index 890 sits it UNDER the header (900, pbj-crm-header.css) and under any
   dialog, and over the content. It is deliberately below the phone's own bottom
   bar (.pbjcrm-mbar, z-index 90) in neither direction — that bar and this one
   are never on screen together. */
.pbjcrm-footer {
	position: fixed;
	left: 0;
	right: 0;
	bottom: 0;
	z-index: 890;

	display: flex;
	align-items: center;
	flex-wrap: nowrap;
	gap: 10px;

	height: var(--nav-footer-h);
	padding: 0 14px;
	box-sizing: border-box;

	background: var(--nav-bg-chrome);
	border-top: 1px solid var(--nav-line);

	font-family: var(--nav-font);
	font-size: 12px;
	color: var(--nav-text-2);
}

.pbjcrm-footer.pbjcrm-footer-bulk {
	height: 40px;
	background: var(--nav-bg-bulkbar);
	border-top-color: var(--nav-line-control);
}

/* --- Mode A: back + breadcrumb --- */

/* ⚠️ THE ONE STATED CONTROL SHAPE IN THE FOOTER. 02 §2.2 gives the Back button
   22px, a 1px --nav-line-control edge and a 6px radius and gives the other
   footer controls no geometry at all; rather than invent a second shape per
   control, every footer button below repeats this one. Reported as derived.

   ⚠️ NO TOKEN: the spec fills this button with #15161a, which 01-TOKENS never
   names. --nav-bg-control is the named fill for "a small control sitting ON the
   chrome bar" (it is what the ⌘K chip uses one file over), which is exactly
   what this is, so it is used here instead of pasting the literal. */
.pbjcrm-crumb-back {
	display: inline-flex;
	align-items: center;
	gap: 5px;
	flex: 0 0 auto;
	height: 22px;
	padding: 0 8px;
	border: 1px solid var(--nav-line-control);
	border-radius: 6px;
	background: var(--nav-bg-control);
	font-family: var(--nav-font);
	font-size: 11.5px;
	color: var(--nav-text-2);
	cursor: pointer;
	position: relative;
}

.pbjcrm-crumb-back:hover {
	background: var(--nav-bg-panel);
	color: var(--nav-text);
}

.pbjcrm-crumbs {
	display: flex;
	align-items: center;
	gap: 6px;
	min-width: 0;
	overflow: hidden;
	white-space: nowrap;
	font-family: var(--nav-font-mono);
	font-size: 11px;
	/* Ancestors and the separator both take this; the current crumb overrides
	   it one rule down. */
	color: var(--nav-text-mute);
}

a.pbjcrm-crumb,
a.pbjcrm-crumb:link,
a.pbjcrm-crumb:visited {
	color: var(--nav-text-mute);
	text-decoration: none;
	overflow: hidden;
	text-overflow: ellipsis;
}

a.pbjcrm-crumb:hover,
a.pbjcrm-crumb:focus {
	color: var(--nav-text);
	text-decoration: underline;
}

/* Not a link, and it is the one the reader is standing on. */
.pbjcrm-crumb-current {
	min-width: 0;
	overflow: hidden;
	text-overflow: ellipsis;
	color: var(--nav-text);
}

/* ⚠️ NO TOKEN — REPORTED, NOT INVENTED. 02 §2.2 draws the `/` separator in
   #4a4e55, a value 01-TOKENS never names and this file may not paste. It
   therefore inherits --nav-text-mute from .pbjcrm-crumbs above, which is the
   nearest NAMED colour and is one step lighter than the design asks for. The
   separator will read slightly stronger than the mockup until the name exists.
   The same #4a4e55 appears again on the checked checkbox in 03 §3.1 and is
   marked there too. */
.pbjcrm-crumb-sep {
	flex: 0 0 auto;
	/* colour deliberately inherited — see above */
}

/* --- Mode A: the right-hand pager --- */

.pbjcrm-footer-right {
	display: flex;
	align-items: center;
	gap: 10px;
	flex: 0 0 auto;
	margin-left: auto;
}

/* ⚠️ SPEC-AGAINST-SPEC, REPORTED: 01-TOKENS' type scale says "Never below
   11px"; 02 §2.2 states this readout at mono 10.5px in the same handoff. The
   specific rule is followed and the contradiction is reported rather than
   silently resolved. Same value, same reason, on .pbjcrm-bulk-hint below. */
.pbjcrm-pager-pos {
	font-family: var(--nav-font-mono);
	font-size: 10.5px;
	color: var(--nav-text-mute);
	white-space: nowrap;
}

.pbjcrm-pager-next {
	display: inline-flex;
	align-items: center;
	gap: 5px;
	flex: 0 0 auto;
	height: 22px;
	padding: 0 8px;
	border: 1px solid var(--nav-line-control);
	border-radius: 6px;
	background: var(--nav-bg-control);
	font-family: var(--nav-font);
	font-size: 11.5px;
	color: var(--nav-text-2);
	cursor: pointer;
	position: relative;
}

.pbjcrm-pager-next:hover {
	background: var(--nav-bg-panel);
	color: var(--nav-text);
}

.pbjcrm-pager-next[disabled] {
	opacity: .5;
	cursor: default;
}

/* --- Mode B: the bulk bar (03 §3.4) --- */

.pbjcrm-bulk-count {
	flex: 0 0 auto;
	font-size: 12px;
	font-weight: 600;
	color: var(--nav-text);
	white-space: nowrap;
}

/* ⛔ SIX CONTROLS, ONE LINE, NO WRAPPING — the spec says so twice. nowrap on
   the row AND on this group, because either one alone still lets the other
   break. min-width:0 lets the group shrink instead of pushing `Esc to clear`
   off the bar. */
.pbjcrm-bulk-actions {
	display: flex;
	align-items: center;
	flex-wrap: nowrap;
	gap: 6px;
	min-width: 0;
	flex: 0 1 auto;
}

.pbjcrm-bulk-btn {
	display: inline-flex;
	align-items: center;
	flex: 0 0 auto;
	height: 22px;
	padding: 0 10px;
	border: 1px solid var(--nav-line-control);
	border-radius: 6px;
	background: transparent;
	font-family: var(--nav-font);
	font-size: 12px;
	white-space: nowrap;
	color: var(--nav-text-2);
	cursor: pointer;
	position: relative;
}

.pbjcrm-bulk-btn:hover {
	background: var(--nav-bg-panel);
	color: var(--nav-text);
}

/* Merge is the one filled control in the bar. */
.pbjcrm-bulk-btn.pbjcrm-bulk-merge {
	background: var(--nav-amber);
	border-color: var(--nav-amber);
	color: var(--nav-green-ink);
	font-weight: 600;
}

.pbjcrm-bulk-btn.pbjcrm-bulk-merge:hover {
	background: var(--nav-amber);
	color: var(--nav-green-ink);
}

/* ⚠️ NO TOKEN — REPORTED. 03 §3.4 outlines Delete in #46282a, a third unnamed
   literal. The TEXT colour it names (--nav-red-alert) is a token and is used;
   the border falls back to --nav-line-control, so Delete currently reads as an
   ordinary outlined control with red text rather than one with a red-tinted
   edge. */
.pbjcrm-bulk-btn.pbjcrm-bulk-danger {
	color: var(--nav-red-alert);
}

.pbjcrm-bulk-btn.pbjcrm-bulk-danger:hover {
	background: var(--nav-bg-panel);
	color: var(--nav-red-alert);
}

/* One disabled look for every control in the bar (03 §3.4: "visibly disabled
   (50% opacity)"). Both doors — the real attribute and the class a script may
   set — land on the same rule, so a Merge button that is disabled for a
   mixed-type selection cannot look different from one disabled for any other
   reason. */
.pbjcrm-bulk-btn[disabled],
.pbjcrm-bulk-btn[aria-disabled="true"] {
	opacity: .5;
	cursor: default;
}

.pbjcrm-bulk-btn[disabled]:hover,
.pbjcrm-bulk-btn[aria-disabled="true"]:hover {
	background: transparent;
}

.pbjcrm-bulk-btn.pbjcrm-bulk-merge[disabled]:hover,
.pbjcrm-bulk-btn.pbjcrm-bulk-merge[aria-disabled="true"]:hover {
	background: var(--nav-amber);
}

.pbjcrm-bulk-hint {
	flex: 0 0 auto;
	margin-left: auto;
	font-family: var(--nav-font-mono);
	/* 10.5px — see the note on .pbjcrm-pager-pos. */
	font-size: 10.5px;
	color: var(--nav-text-mute);
	white-space: nowrap;
}

/* ------------------------------------------------- The unified contacts list */

/* 03 §3.2. The fixed columns cannot be squeezed by a long value, and the name
   is the only thing that flexes. The selection tick is visually ordered last,
   at the same right edge used by every unified card below. */
.pbjcrm-clist {
	display: grid;
	grid-template-columns: minmax(0, 1fr);
	gap: var(--pbjcrm-space-sm);
	font-family: var(--nav-font);
}

.pbjcrm-crow {
	display: flex;
	align-items: center;
	gap: 10px;
	padding: 9px 18px;
	border-bottom: 1px solid var(--nav-line-soft);
	font-size: 13.5px;
	color: var(--nav-text);
}

/* ⛔ ROW BACKGROUND IS SELECTION ONLY, NEVER STATE (03 §3.2, 01-TOKENS). Hover
   is not state — it is the pointer — and --nav-bg-row-hover exists for exactly
   it. Selection has to beat hover, and .pbjcrm-crow:hover and
   .pbjcrm-crow.pbjcrm-selected are BOTH (0,2,0), so specificity settles
   nothing and declaration order is doing the work: selected is written second
   on purpose. Do not reorder these two rules. */
.pbjcrm-crow:hover {
	background: var(--nav-bg-row-hover);
}

.pbjcrm-crow.pbjcrm-selected {
	background: var(--nav-bg-row-sel);
}

/* "Rows with no activity in 90+ days render at opacity .62." Opacity, not a
   colour, so it dims the row's own tokens on every preset. */
.pbjcrm-crow.pbjcrm-stale {
	opacity: .62;
}

/* ⛔ CORRECTED ONCE THE SCRIPT EXISTED, AND THE ORIGINAL WAS A SILENT NO-OP.
   The DOM contract this was first written against said `input.pbjcrm-crow-check`
   — the class ON the input. pbj-crm.js ships something else: .pbjcrm-crow-check
   is a WRAPPER, a <label> on a data row and a <span> on the select-all row, with
   a bare unclassed <input type="checkbox"> inside it. Everything the old rule
   said was therefore aimed at the wrapper and three quarters of it did nothing:
   `appearance: none` on a label is inert, `:checked` never matched so the red
   fill never appeared, and `:focus-visible` never matched because a label does
   not take focus. The visual belongs on the input; only the box belongs here.

   ⚠️ AND THE WRAPPER IS WHY THE 44px HIT AREA IS NOW POSSIBLE. It was reported
   as unbuildable because a replaced <input> renders no ::after. A <label> does,
   and a click on the label IS a click on the box it wraps — so .pbjcrm-crow-check
   joins the coarse-pointer list further down and the tick target is a thumb's
   worth of room without the box growing by a pixel. */
.pbjcrm-crow-check {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	order: 10;
	flex: 0 0 14px;
	width: 14px;
	height: 14px;
	margin: 0;
	cursor: pointer;
	position: relative;
}

/* ⚠️ 14px IS THE DESKTOP VISUAL AND THE PHONE OVERRIDES IT ON PURPOSE.
   .pbjcrm-portal.pbjcrm-mobile-on input[type="checkbox"] is (0,3,0) against
   this rule's (0,2,0) and sets 22px for a thumb; that is pbj-crm-mobile.css
   doing its job and this file must not out-specify it. The wrapper above stays
   14px either way, so the phone's bigger box overflows it by 4px a side rather
   than moving the column — which is the lesser of the two, and it is the phone's
   call to make. */
.pbjcrm-crow-check > input[type="checkbox"] {
	flex: 0 0 auto;
	width: 14px;
	height: 14px;
	margin: 0;
	appearance: none;
	-webkit-appearance: none;
	border: 1px solid var(--nav-line-control);
	border-radius: 4px;
	background: transparent;
	cursor: pointer;
}

/* ⚠️ NO TOKEN — REPORTED (second sighting of #4a4e55). 03 §3.1 gives the
   checked box a 1px #4a4e55 edge; with no name for it the unchecked border
   token simply stays, so a ticked box is a red fill inside the same hairline
   it already had. */
.pbjcrm-crow-check > input[type="checkbox"]:checked {
	background: var(--nav-red);
	border-color: var(--nav-line-control);
}

/* The 16px column and the shared shape. The three TYPE variants — business,
   person, internal — are stated further down, in the Phases 4 + 5 section,
   against the kind classes pbj-crm.js emits.

   ⚠️ THIS COMMENT USED TO SAY THE VARIANTS WERE UNREACHABLE and that the
   contract carried no type modifier. That was true when it was written and is
   not true now: the script emits `pbjcrm-crow-glyph-<kind>`. Corrected where it
   stands rather than left to misdirect the next reader — a self-description of
   finished work is exactly the kind of claim that rots. */
.pbjcrm-crow-glyph {
	flex: 0 0 16px;
	width: 16px;
	text-align: center;
	line-height: 1;
	/* The person's size and colour, kept as the fallback for a row whose kind
	   class never arrives — an unstyled glyph is worse than a wrong-sized one. */
	font-size: 11px;
	color: var(--nav-text-2);
}

/* ⛔ THE MARKERS LIVE IN HERE. 03 §3.2 and acceptance §5 both say it: they are
   pushed right inside the NAME cell and are never their own column, so they
   stay next to the name they describe at any table width. min-width:0 is what
   lets the name truncate instead of shoving them off the row. */
.pbjcrm-crow-name {
	flex: 1 1 auto;
	min-width: 0;
	display: flex;
	align-items: center;
	gap: 8px;
}

.pbjcrm-crow-title {
	min-width: 0;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	font-size: 13.5px;
	color: var(--nav-text);
}

/* The urgency rule (01-TOKENS), stated once. ⛔ AND NO ROW BACKGROUND GOES WITH
   IT — "when a title is red, do not also colour its row background". */
.pbjcrm-crow-title.pbjcrm-overdue {
	color: var(--nav-red-alert);
	font-weight: 600;
}

.pbjcrm-markers {
	flex: 0 0 auto;
	margin-left: auto;
	display: flex;
	align-items: center;
	/* ⚠️ The gap BETWEEN chips is not a value the handoff states; 4px is the
	   smallest that keeps two 4px-radius chips visibly separate. Reported. */
	gap: 4px;
}

.pbjcrm-crow-linked {
	flex: 0 0 150px;
	width: 150px;
	min-width: 0;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	font-size: 12.5px;
	color: var(--nav-text-mute);
}

.pbjcrm-crow-linked a,
.pbjcrm-crow-linked a:link,
.pbjcrm-crow-linked a:visited {
	color: var(--nav-link);
	text-decoration: none;
}

.pbjcrm-crow-linked a:hover {
	text-decoration: underline;
}

.pbjcrm-crow-email {
	flex: 0 0 132px;
	width: 132px;
	min-width: 0;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	font-size: 12.5px;
	color: var(--nav-text-mute);
}

.pbjcrm-crow-phone {
	flex: 0 0 104px;
	width: 104px;
	min-width: 0;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	font-size: 12.5px;
	color: var(--nav-text-mute);
}

.pbjcrm-crow-touch {
	flex: 0 0 62px;
	width: 62px;
	min-width: 0;
	overflow: hidden;
	white-space: nowrap;
	font-size: 12px;
	color: var(--nav-text-mute);
}

/* ---------------------------------------------------------- Marker chips */

/* 03 §3.3. Mono 10px — the second of the two sizes the handoff explicitly takes
   below 11px — dark ink on a coloured fill, 4px radius, 1px/5px padding.

   ⚠️ THE INK IS --nav-bg-app AND THAT DESERVES A SENTENCE. §3.3 states the ink
   as #0a0a0b, which 01-TOKENS names exactly once: --nav-bg-app, the page
   background. So the token is used rather than the literal. It follows the
   theme, which on a LIGHT preset means the ink goes pale against a pale chip —
   REPORTED upstream as wanting its own name (a --nav-chip-ink) rather than
   borrowing the background's. */
.pbjcrm-marker {
	display: inline-flex;
	align-items: center;
	flex: 0 0 auto;
	margin: 0;
	padding: 1px 5px;
	border: 0;
	border-radius: 4px;
	font-family: var(--nav-font-mono);
	font-size: 10px;
	line-height: 1.5;
	white-space: nowrap;
	color: var(--nav-bg-app);
	background: var(--nav-grey-chip);
	cursor: pointer;
	position: relative;
}

/* ⛔ THE HIT AREA GROWS, THE CHIP DOES NOT (02 §2.8, acceptance §6). Same
   technique and same numbers as the eight header controls in
   pbj-crm-header.css: a transparent ::after centred on the button, which is
   part of the button — a click on it IS a click on the chip — and paints
   nothing.

   ⚠️ BUT UNDER `pointer: coarse` ONLY, WHICH IS WHERE THIS FILE DIVERGES FROM
   THE HEADER'S, DELIBERATELY AND AFTER MEASURING THE NEIGHBOURS. The header
   applies its hit areas unconditionally and can: its controls sit in a ROW,
   48px tall with 14px gaps, so a 44px box fits vertically with 2px to spare
   and the narrowest horizontal overhang is 11px against a 14px gap — no two
   ever meet, and its own comment says so.

   None of that holds here. A rail is a COLUMN: items are about 30px tall with
   a 3px gap, so a 44px hit area would reach roughly 11px into the item above
   and the item below, and the last one painted would win the overlap. A
   contacts row is about 33px tall, so stacked marker chips would do the same
   to each other. On a mouse that is not an improvement, it is a nav item that
   fires its neighbour near the edge — a real defect traded for a requirement
   that only ever spoke about touch ("**Touch:** every interactive target is at
   least 44×44 CSS px"). So the trade is made exactly where the requirement
   applies and nowhere else: fingers get 44px, pointers get the true edges.

   ⚠️ AND IN THE FOOTER IT CANNOT FULLY FIT, WHICH IS THE SPEC AGAINST ITSELF:
   a 44px target does not go inside a 34px bar (02 §2.2 against §2.8). Centred,
   the area overhangs 5px above the bar and 5px below it; below is off the
   viewport and unclickable, above is a 5px strip of content that answers to
   the footer control. That is the smaller loss and it is reported rather than
   quietly resolved. */
@media (pointer: coarse) {

	.pbjcrm-marker::after,
	.pbjcrm-crow-check::after,
	.pbjcrm-rail-item::after,
	.pbjcrm-rail-collapse::after,
	.pbjcrm-crumb-back::after,
	.pbjcrm-pager-next::after,
	.pbjcrm-bulk-btn::after {
		content: "";
		position: absolute;
		left: 50%;
		top: 50%;
		width: 100%;
		min-width: 44px;
		height: 44px;
		transform: translate(-50%, -50%);
		pointer-events: auto;
	}
}

/* ⚠️ NOT A LITERAL, AND NOT AN INVENTION EITHER. §3.3 spells the hover ring
   `rgba(233,233,236,.45)` — which is --nav-text at 45% — so it is written that
   way, from the token, with color-mix. No flat fallback is declared: a literal
   here would be the exact hard-coded colour this section exists to avoid, and
   a browser without color-mix simply shows no ring on hover while keeping the
   focus ring below, which is the accessible half of the pair. */
.pbjcrm-marker:hover {
	box-shadow: 0 0 0 2px color-mix(in srgb, var(--nav-text) 45%, transparent);
}

/* Marker fills, one per kind (03 §3.3, in the spec's own order: money → work →
   data quality). ⚠️ THE <kind> SLUGS ARE NOT NAMED IN THE HANDOFF — the DOM
   contract says `.pbjcrm-marker-<kind>` and stops there. These nine are derived
   from the legend's own Means column and are REPORTED so the emitting script
   can match them exactly; they are the one place in this section where a name
   had to be chosen rather than read. */
.pbjcrm-marker-money {
	background: var(--nav-red-alert);
}

.pbjcrm-marker-tickets {
	background: var(--nav-red-chip);
}

.pbjcrm-marker-dupe {
	background: var(--nav-amber);
}

.pbjcrm-marker-archive {
	background: var(--nav-violet);
}

.pbjcrm-marker-portal {
	background: var(--nav-green);
}

.pbjcrm-marker-primary {
	background: var(--nav-blue);
}

.pbjcrm-marker-bounced {
	background: var(--nav-red-alert);
}

.pbjcrm-marker-dnc {
	background: var(--nav-grey-chip-2);
}

.pbjcrm-marker-imported {
	background: var(--nav-grey-chip);
}

/* The `+N` overflow chip: "any beyond three collapse into a +2 chip in
   --nav-grey-chip". */
.pbjcrm-marker-more {
	background: var(--nav-grey-chip);
}

/* ------------------------------------------------------------------ Focus */

/* ⛔ EVERY CONTROL THIS SECTION ADDS, NOT THE ONE THAT NEEDED IT
   (pbj-behavior 9b). The same sweep pbj-crm-header.css made across its eight
   bar controls, made here across the rail, the footer and the chips — a focus
   ring that covers most of a screen is worse than none, because the ones it
   misses look unreachable.

   ⚠️ IT USES --nav-focus-ring, WHERE THE HEADER USES --pbjcrm-focus-ring
   DIRECTLY. Both resolve to the same colour (the nav layer aliases one onto the
   other); the --nav-* spelling is used here because that is the layer this work
   was told to build against. Flagged so the two files can be brought into line
   in one edit rather than drifting. */
.pbjcrm-rail-item:focus-visible,
.pbjcrm-rail-collapse:focus-visible,
.pbjcrm-crumb-back:focus-visible,
a.pbjcrm-crumb:focus-visible,
.pbjcrm-pager-next:focus-visible,
.pbjcrm-bulk-btn:focus-visible,
.pbjcrm-crow-check > input[type="checkbox"]:focus-visible,
/* The row itself is a div in the DOM contract and takes no focus today. It is
   in this list anyway: the row IS clickable (03 §3.3 has to say that clicking a
   chip must not ALSO open the row), so the moment it is given a tabindex or
   promoted to a button it needs the ring, and a rule that is already here
   cannot be the one somebody forgets. It costs nothing while it never matches. */
.pbjcrm-crow:focus-visible,
.pbjcrm-marker:focus-visible {
	outline: 2px solid var(--nav-focus-ring);
	outline-offset: 2px;
}

/* The chip keeps its own corner so the ring traces the chip, not a 7px box
   around a 4px shape. */
.pbjcrm-marker:focus-visible {
	border-radius: 4px;
}

/* ------------------------------------------------------------- Responsive */

/* 02 §2.8, first breakpoint. The header stylesheet already collapses Create to
   a `+` at this exact width (its own @media (max-width: 1099.98px)); this is
   the rail's half of the same sentence, written at the same breakpoint so the
   two chrome halves can never disagree by a pixel. */
@media (max-width: 1099.98px) {

	.pbjcrm-app.pbjcrm-portal:not(.pbjcrm-mobile-on) > .pbjcrm-rail {
		flex-basis: var(--nav-rail-w-collapsed);
		width: var(--nav-rail-w-collapsed);
		padding-left: 6px;
		padding-right: 6px;
	}

	.pbjcrm-app.pbjcrm-portal:not(.pbjcrm-mobile-on) .pbjcrm-rail-label,
	.pbjcrm-app.pbjcrm-portal:not(.pbjcrm-mobile-on) .pbjcrm-rail-count,
	.pbjcrm-app.pbjcrm-portal:not(.pbjcrm-mobile-on) .pbjcrm-rail-item-label {
		display: none;
	}

	.pbjcrm-app.pbjcrm-portal:not(.pbjcrm-mobile-on) .pbjcrm-rail-item,
	.pbjcrm-app.pbjcrm-portal:not(.pbjcrm-mobile-on) .pbjcrm-rail-collapse {
		justify-content: center;
		padding-left: 4px;
		padding-right: 4px;
	}

	.pbjcrm-app.pbjcrm-portal:not(.pbjcrm-mobile-on) .pbjcrm-rail-item,
	.pbjcrm-app.pbjcrm-portal:not(.pbjcrm-mobile-on) .pbjcrm-rail-collapse {
		gap: 0;
	}
}

/* 02 §2.8, second breakpoint: the rail becomes an off-canvas drawer. The
   button that opens it belongs to pbj-crm-header.css — its comment says so
   explicitly and hands the drawer, the scrim and the transform to this file —
   and the contract between them is the class `pbjcrm-rail-open` on
   #pbj-crm-app. Same 859.98px the header uses, for the same reason as above. */
@media (max-width: 859.98px) {

	/* Out of the flex flow entirely, so the view fills the shell whether the
	   drawer is open or shut. Full expanded width even here: a 60px drawer
	   would be the worst of both. */
	.pbjcrm-app.pbjcrm-portal:not(.pbjcrm-mobile-on) > .pbjcrm-rail {
		position: fixed;
		top: var(--nav-header-h);
		bottom: var(--nav-footer-h);
		left: 0;
		z-index: 895;
		flex-basis: auto;
		width: var(--nav-rail-w);
		padding-left: 10px;
		padding-right: 10px;
		transform: translateX(-100%);
		transition: transform .16s ease-out;
	}

	/* The collapsed look is undone inside the drawer — there is room for words
	   here, and a drawer of four-letter stubs helps nobody. */
	.pbjcrm-app.pbjcrm-portal:not(.pbjcrm-mobile-on) .pbjcrm-rail-label,
	.pbjcrm-app.pbjcrm-portal:not(.pbjcrm-mobile-on) .pbjcrm-rail-count {
		display: block;
	}

	.pbjcrm-app.pbjcrm-portal:not(.pbjcrm-mobile-on) .pbjcrm-rail-count {
		display: inline;
	}

	.pbjcrm-app.pbjcrm-portal:not(.pbjcrm-mobile-on) .pbjcrm-rail-item,
	.pbjcrm-app.pbjcrm-portal:not(.pbjcrm-mobile-on) .pbjcrm-rail-collapse {
		justify-content: flex-start;
		padding-left: 9px;
		padding-right: 9px;
	}

	.pbjcrm-app.pbjcrm-portal:not(.pbjcrm-mobile-on) .pbjcrm-rail-item-label {
		display: block;
		flex: 1 1 auto;
		text-align: left;
	}

	.pbjcrm-app.pbjcrm-portal:not(.pbjcrm-mobile-on) .pbjcrm-rail-item,
	.pbjcrm-app.pbjcrm-portal:not(.pbjcrm-mobile-on) .pbjcrm-rail-collapse {
		gap: 8px;
	}

	.pbjcrm-app.pbjcrm-rail-open:not(.pbjcrm-mobile-on) > .pbjcrm-rail {
		transform: translateX(0);
	}

	/* The scrim. There is no scrim element in the DOM contract, so the shell
	   draws it as a pseudo-element and nothing new is asked of the markup.

	   ⛔ IT HANGS OFF THE APP, NOT OFF THE RAIL, AND THAT IS NOT A STYLE
	   CHOICE. The rail is translated, and a transform makes an element the
	   containing block for its fixed-position descendants — so a `position:
	   fixed` scrim inside the rail would be positioned against the RAIL and
	   then clipped by the rail's own overflow, which is a 206px column of
	   tint instead of a page of it. The app carries no transform, so its
	   ::before is fixed against the viewport, and an ancestor's `overflow:
	   hidden` does not clip a fixed child that it is not the containing
	   block for. z-index 894 puts it under the drawer (895) and over the
	   content; the header (900) and dialogs stay above. */
	.pbjcrm-app.pbjcrm-rail-open:not(.pbjcrm-mobile-on)::before {
		content: "";
		position: fixed;
		top: var(--nav-header-h);
		right: 0;
		bottom: var(--nav-footer-h);
		left: 0;
		z-index: 894;
		background: color-mix(in srgb, var(--nav-bg-app) 62%, transparent);
	}

	/* "the footer keeps Back + current crumb only, drops the pager" (02 §2.8).
	   The ancestors and their separators go with the pager: what is left is the
	   one crumb saying where you are. */
	.pbjcrm-footer .pbjcrm-footer-right,
	.pbjcrm-footer a.pbjcrm-crumb,
	.pbjcrm-footer .pbjcrm-crumb-sep {
		display: none;
	}

	/* The bulk bar keeps every one of its six controls at every width — 03 §3.4
	   allows no wrapping and no dropping, and §2.8 only ever spoke about the
	   breadcrumb mode. The group scrolls sideways inside the bar instead. */
	.pbjcrm-footer.pbjcrm-footer-bulk .pbjcrm-bulk-actions {
		overflow-x: auto;
	}

	/* Narrow rows lose the two columns that are always reachable one tap
	   further in. The name, its markers and the last-touch age stay: those are
	   what the list is FOR. */
	.pbjcrm-crow-linked,
	.pbjcrm-crow-phone {
		display: none;
	}
}

/* ------------------------------------------------- Keeping off the phone */

/* ⛔ THE PHONE STAYS EXACTLY AS IT IS TODAY (Jake's decision). Today's phone has
   .pbjcrm-mbar across the bottom and no rail; two fixed bottom bars at once
   would be a visible regression, and the rail would be a 206px column on a
   390px screen. The layout rules above already exclude .pbjcrm-mobile-on, and
   these two hide the new chrome outright.

   The footer is a SIBLING of the app, so :has() is the only way to ask from
   here whether the phone shape is on — the same technique this file already
   uses for the canvas wrapper at the top. */
.pbjcrm-app.pbjcrm-mobile-on > .pbjcrm-rail {
	display: none;
}

body:has(.pbjcrm-app.pbjcrm-mobile-on) .pbjcrm-footer {
	display: none;
}

/* Motion is a preference, not a decision this file gets to make. */
@media (prefers-reduced-motion: reduce) {

	.pbjcrm-app.pbjcrm-portal:not(.pbjcrm-mobile-on) > .pbjcrm-rail {
		transition: none;
	}
}

/* ===========================================================================
   PHASES 4 + 5 — WHAT THE SCRIPT NOW EMITS (03 §3.2/§3.4/§3.5/§3.6, 02 §2.5)
   ---------------------------------------------------------------------------
   Same discipline as the section above: --nav-* names only and not one raw hex.

   ⚠️ TWO NAMED EXCEPTIONS, BOTH SANCTIONED AND BOTH THE SAME CHIP. The KEPT
   chip (03 §3.5) and the owner role badge (02 §2.5) are given as the identical
   three literals — #7fd6a3 on #132018 with a #2c4a3a edge — and 01-TOKENS
   defines none of them. They are not three colours, they are one SUCCESS CHIP
   written twice, so both land on the theme's success family (--pbjcrm-*, the
   layer --nav-* is itself an alias over) and both land on the SAME three names.
   That is the whole reason they are allowed to reach past --nav-*: there is no
   --nav- success-surface name to reach for.

   ⚠️ AND MOST OF THIS IS INERT UNTIL THE INLINE STYLES COME OUT. pbj-crm.js
   currently sets the suggestion card, the merge grid, the KEEP ring, the KEPT
   chip and the account badge from `element.style`, which beats any stylesheet
   rule short of !important — and !important here would be a worse answer than
   the duplication. These rules are written to be exactly what the script sets,
   so removing an inline block changes nothing on screen. REPORTED so that
   removal can be a deliberate edit rather than a surprise.
   ===========================================================================*/

/* ------------------------------------------------- The type glyph (§3.2) */

/* The three variants, reachable at last: pbj-crm.js emits
   `pbjcrm-crow-glyph pbjcrm-crow-glyph-<kind>` with kind business | internal |
   person. The 16px column, the centring and the person default are stated on
   .pbjcrm-crow-glyph in the section above; these three say only what differs.

   ⛔ AND THE BUSINESS GLYPH IS THE REASON --nav-blue-business EXISTS. The token
   layer split 01-TOKENS' single --nav-blue in two precisely because the spec
   pointed one name at both the business mark and the primary-contact star; the
   star keeps the plain name (it is on the marker chip in the section above) and
   the firm gets the qualified one. This is the rule that was unreachable while
   the script emitted no kind class. */
.pbjcrm-crow-glyph-business {
	font-size: 13px;
	color: var(--nav-blue-business);
}

.pbjcrm-crow-glyph-person {
	font-size: 11px;
	color: var(--nav-text-2);
}

.pbjcrm-crow-glyph-internal {
	font-size: 12px;
	color: var(--nav-violet);
}

/* --------------------------------------------- The select-all row (§3.2) */

/* ⛔ A HEADER IS NOT A DATA ROW. It carries .pbjcrm-crow for its column
   geometry — which is right, the ticks have to line up — and inherited
   everything else with it: the soft row separator, the hover fill, and the
   13.5px weight of a name. 01-TOKENS is explicit that --nav-line is for "chrome
   borders, TABLE HEADER RULES" while --nav-line-soft is for row separators, so
   the rule under the header is the harder of the two and the row reads as
   chrome rather than as the first contact in the list. */
.pbjcrm-crow.pbjcrm-crow-head {
	border-bottom: 1px solid var(--nav-line);
	font-size: 12px;
	color: var(--nav-text-mute);
	cursor: pointer;
}

/* Nothing about a header answers to the pointer or to selection: it is not a
   record, so it cannot be hovered like one or ticked into a selection. */
.pbjcrm-crow.pbjcrm-crow-head:hover {
	background: transparent;
}

.pbjcrm-crow-head .pbjcrm-crow-name {
	font-size: 12px;
	color: var(--nav-text-mute);
}

/* ------------------------------------------------- The bulk bar (§3.4) */

/* The wrapper the script puts inside the footer in bulk mode. `nowrap` is the
   load-bearing line — §3.4 says six controls on ONE line and says it twice —
   and it is the one property the script does NOT set inline, so it is the one
   that actually takes effect today.

   ⚠️ THE OTHER FOUR ARE DELIBERATE DUPLICATES of what bulkBarNode() sets from
   `element.style`, so the stylesheet stands on its own the moment those lines
   are deleted. They are not fighting the script — they are the same values. */
.pbjcrm-bulk-wrap {
	display: flex;
	align-items: center;
	flex-wrap: nowrap;
	gap: 10px;
	width: 100%;
	min-width: 0;
}

/* ⛔ THE DISABLED LOOK HANGS OFF aria-disabled AND THAT IS NOT A STYLE CHOICE.
   Ten of the bulk controls ship disabled because the server has no verb behind
   them, and the script uses aria-disabled rather than the `disabled` ATTRIBUTE
   on purpose: a disabled button leaves the tab order, which would put the
   tooltip explaining WHY it cannot run out of reach of exactly the people who
   need it, and acceptance §4's gate is a keyboard-only pass. `:disabled` would
   therefore match nothing and the 50%-opacity treatment §3.4 asks for would be
   invisible on every one of the ten.

   The rule itself is already stated in the section above — both spellings, one
   declaration — and this note is here so nobody "tidies" the attribute selector
   out of it later. Grep before touching: .pbjcrm-bulk-btn[aria-disabled="true"]. */

/* ------------------------------------------ Suggested people (§3.6) */

.pbjcrm-suggest {
	display: block;
}

/* ⛔ DASHED, AND THE DASH IS THE WHOLE POINT. §3.6 draws a suggestion as a 1px
   DASHED card because it is the only thing separating a guess from a fact — a
   solid edge would say this person works here, which is the claim the box
   exists to NOT make until somebody presses Link. */
.pbjcrm-suggest-card {
	display: flex;
	align-items: center;
	gap: 10px;
	margin-top: 6px;
	padding: 8px 10px;
	border: 1px dashed var(--nav-line-control);
	border-radius: 8px;
}

/* ⚠️ STRUCTURAL SELECTORS, BECAUSE THE INNER NODES CARRY NO CLASSES. The card
   is avatar + an unclassed <div> holding the name and the reason + two buttons.
   Rather than invent class names the script has never heard of, these reach the
   nodes by shape. It is brittle by construction and it is the lesser evil;
   REPORTED with the names that would fix it (.pbjcrm-suggest-who and
   .pbjcrm-suggest-reason). */
.pbjcrm-suggest-card > div {
	flex: 1 1 auto;
	min-width: 0;
}

.pbjcrm-suggest-card > div > p {
	margin: 0;
	font-size: 11px;
	color: var(--nav-text-mute);
}

/* The rule, the SUGGESTED · N label and its sub-line. Same story: the script
   builds three unclassed <div>/<p> nodes inside .pbjcrm-suggest and styles them
   inline. Reached here by shape so the values live in one place. */
.pbjcrm-suggest > div {
	margin-top: 12px;
	padding-top: 10px;
	border-top: 1px solid var(--nav-line-soft);
}

/* Mono 9.5px in amber — the second of the two places the handoff takes type
   below 11px, and the same shape as a rail group label one section up. Amber
   because §3.6 marks a suggestion as a thing needing a decision, not a fact. */
.pbjcrm-suggest > div > div:first-child {
	font-family: var(--nav-font-mono);
	font-size: 9.5px;
	letter-spacing: .14em;
	text-transform: uppercase;
	color: var(--nav-amber);
}

.pbjcrm-suggest > div > p {
	margin: 2px 0 0;
	font-size: 11px;
	color: var(--nav-text-mute);
}

/* ⚠️ INERT — THE CLASS DOES NOT EXIST YET, AND IT IS NAMED HERE SO IT CAN.
   §3.6: "Former staff render at opacity .65". personLine() emits a plain
   .pbjcrm-person-row with nothing on it saying somebody has left, so this rule
   matches nothing today and will match the moment the script adds the modifier.
   The name it must emit is exactly .pbjcrm-person-former. REPORTED. */
.pbjcrm-person-row.pbjcrm-person-former {
	opacity: .65;
}

/* ---------------------------------------------- The merge review (§3.5) */

/* ⛔ SCOPED TO THE MERGE DIALOG, NOT TO EVERY DIALOG. crmModal() hands every
   overlay in the portal the same .pbjcrm-overlay-panel, and §3.5's 660px, panel
   fill, panel border and 14px radius are the MERGE review's, not the file
   viewer's or the tag picker's. :has() asks the panel whether it is holding a
   merge review — the same technique this file already uses for the canvas
   wrapper — so one screen changes and the other twenty do not. */
.pbjcrm-overlay-panel:has(.pbjcrm-merge-review) {
	max-width: 660px;
	width: 100%;
	background: var(--nav-bg-panel);
	border: 1px solid var(--nav-line-panel);
	border-radius: 14px;
}

.pbjcrm-merge-review {
	font-family: var(--nav-font);
}

.pbjcrm-merge-head h3 {
	margin: 0;
	font-size: 17px;
	font-weight: 600;
	color: var(--nav-text);
}

/* The match reason and the position in the flagged set, on one line. */
.pbjcrm-merge-head p {
	margin: 2px 0 0;
	font-size: 12px;
	color: var(--nav-text-mute);
}

.pbjcrm-merge-grid {
	margin-top: 10px;
}

/* Three columns, exactly as §3.5 states them: 150px of label, then a column
   per record. align-items:start so a cell that gains a KEPT chip does not drag
   the label down with it. */
.pbjcrm-merge-grid-row {
	display: grid;
	grid-template-columns: 150px 1fr 1fr;
	gap: 10px;
	align-items: start;
	padding: 7px 0;
	border-top: 1px solid var(--nav-line-soft);
}

/* The KEEP selector is the first row and has nothing above it to be separated
   from. The script states this inline too; stated here as well so the grid is
   right on its own. */
.pbjcrm-merge-grid-row:first-child {
	border-top: 0;
}

.pbjcrm-merge-grid-label {
	font-size: 12px;
	color: var(--nav-text-mute);
}

/* An email, an address line or a notes field has no spaces to break on and no
   business pushing the column wider than the dialog. */
.pbjcrm-merge-cell {
	min-width: 0;
	overflow-wrap: anywhere;
}

.pbjcrm-merge-keep {
	display: flex;
	align-items: center;
	gap: 6px;
	padding: 7px 9px;
	border-radius: 7px;
	cursor: pointer;
}

/* §3.5: "the chosen one shows a --nav-green ring". Asked of the DOM rather
   than of a class — the radio inside the label already carries the answer, and
   a class would be a second place for the same fact to be recorded and to go
   stale. A ring rather than a fill: the row underneath is a value being
   compared and must not change colour because of what is selected. */
.pbjcrm-merge-keep:has(input:checked) {
	box-shadow: 0 0 0 2px var(--nav-green);
}

/* ⚠️ THE SUCCESS CHIP, EXCEPTION ONE OF TWO. §3.5 prints #7fd6a3 on #132018
   with a 1px #2c4a3a edge and 01-TOKENS names none of the three. This is a
   success chip, so it takes the theme's success family and follows the owner's
   palette on all seventeen presets instead of being right on one.

   ⛔ THE EDGE IS --pbjcrm-success, NOT --pbjcrm-success-contrast, AND THAT IS A
   REPORTED DEPARTURE FROM THE THREE NAMES I WAS HANDED. Read before written:
   class-pbj-crm-theme.php:2440 defines success-contrast as readable_on(success),
   and readable_on() (:3330) returns #1d2327 or #ffffff — an INK for text ON the
   colour, never an edge. On every dark preset that is a near-black hairline
   around a near-black chip: an invisible border where the design draws a
   visible mid-green one. --pbjcrm-success is what the script already sets
   inline, so this also keeps the chip identical when that inline block is
   deleted. Named here rather than changed quietly. */
.pbjcrm-merge-kept {
	margin-left: 6px;
	padding: 1px 5px;
	border: 1px solid var(--pbjcrm-success);
	border-radius: 4px;
	font-family: var(--nav-font-mono);
	font-size: 10px;
	color: var(--pbjcrm-success);
	background: var(--pbjcrm-success-soft);
}

/* Keep / take, per differing field. */
.pbjcrm-merge-pick {
	display: inline-flex;
	align-items: center;
	gap: 4px;
	margin-right: 10px;
	font-size: 12px;
	cursor: pointer;
}

/* ------------------------------------------ Account identity block (§2.5) */

/* ⚠️ THE .pbjcrm-hdr- PREFIX BELONGS TO pbj-crm-header.css BY CONVENTION AND
   THESE TWO ARE HERE ANYWAY. Checked before writing: that file styles
   -account, -account-name, -account-online, -account-menu, -account-label and
   -account-wrap, and neither -role nor -badge — so nothing is being duplicated
   or overridden. They are emitted by pbj-crm.js, not by the header script.
   REPORTED so the two files can be reconciled in one deliberate edit. */
.pbjcrm-hdr-account-role {
	font-size: 11.5px;
	color: var(--nav-text-mute);
}

.pbjcrm-hdr-account-badge {
	margin-left: auto;
	font-family: var(--nav-font-mono);
	font-size: 10px;
	color: var(--nav-text-mute);
}

/* ⚠️ THE SUCCESS CHIP, EXCEPTION TWO OF TWO — AND DELIBERATELY THE SAME THREE
   NAMES AS .pbjcrm-merge-kept ABOVE. §2.5 gives the owner badge as #7fd6a3 on
   #132018 with a #2c4a3a border, which is character for character the KEPT
   chip's trio. Two screens describing one chip, so they resolve to one set of
   tokens; if they ever need to differ, that is a decision somebody makes on
   purpose rather than a drift between two pasted literals.

   ⚠️ INERT — THE MODIFIER DOES NOT EXIST YET. accountMenu() emits a bare
   .pbjcrm-hdr-account-badge carrying the uppercased role as TEXT, with no class
   saying which role it is, so this cannot fire and must not be applied to the
   base class: an employee badge painted owner-green would be worse than a plain
   one. The name to emit is .pbjcrm-hdr-account-badge-owner, following the
   `<base>-<kind>` shape the script already uses for pbjcrm-crow-glyph-business.
   REPORTED. */
.pbjcrm-hdr-account-badge-owner {
	padding: 1px 6px;
	border: 1px solid var(--pbjcrm-success);
	border-radius: 4px;
	color: var(--pbjcrm-success);
	background: var(--pbjcrm-success-soft);
}

/* ===========================================================================
   THE GROUND, THE SCROLL, AND THE COLLAPSED RAIL'S ICONS
   ---------------------------------------------------------------------------
   ⛔ FAULT ONE, AND THE CAUSE IS NOT THE SITE THEME. Jake reported "the
   background and top and bottom bars are not following the theme". Measured on
   Studio before this was written, by fetching /crm/ and reading what the server
   actually sent: <body> carries `pbj-crm-canvas`, and the stylesheet list holds
   pbj-crm.css, -header, -mobile, -nav, three wp-includes sheets and four sibling
   plugins — and NO YOOtheme stylesheet at all. The canvas strips it. There is no
   .uk-container in play and nothing of the theme's is painting anything.

   ⭐ AND THE COLOUR ITSELF IS CORRECT, WHICH IS WORTH WRITING DOWN SO NOBODY
   "FIXES" IT LATER. Studio is on preset florida-dark with the miami palette;
   --pbjcrm-bg resolves to #050617, header-bg #090921, rail-bg #0d0d26. That navy
   IS the theme, faithfully followed. The handoff's #0a0a0b/#111216/#0e0f12 are
   Midnight's values and describe one preset out of seventeen. A shell that
   painted those literals would be the bug.

   ⛔ WHAT IS ACTUALLY WRONG IS OUR OWN 14px. `body.pbj-crm-canvas`, further up
   this file, carries `padding: 14px`. That is right for a portal which is a CARD
   ON A PAGE and wrong for a shell which IS the application, and it causes both
   of Jake's faults at once:

     · it insets the shell by 14px, while the header and footer are fixed to the
       VIEWPORT and do not inset with it — so a strip of body colour runs down
       both edges and along the bottom, beside bars that are full-bleed. That
       strip is "the bars are not following the background";
     · and it makes the document 28px TALLER than the viewport, which is FAULT
       TWO. The shell is one viewport minus the header; 14px above plus 14px
       below is 28px of scrollable document, and scrolling it drags the whole
       composition up under the fixed header. That is the half-cut line of text
       in the screenshot.
     · third symptom, same cause: .pbjcrm-hdr-mount sits 14px lower than the bar
       it reserves for, so the first 14px of app content hides under the header
       before anybody scrolls at all.

   One cause, three symptoms, one fix: on the canvas, with the shell present, the
   page stops being a page with a card on it and becomes the application's own
   ground — no padding, no document scroll, our colour behind everything.

   ⛔ EVERY CONDITION IN THE :has() IS LOAD-BEARING.
     · `.pbjcrm-app.pbjcrm-portal` — only when the new shell is really there. An
       older script that does not emit .pbjcrm-app leaves the page exactly as it
       was rather than losing its scrollbar.
     · `:not(.pbjcrm-mobile-on)` — ⛔ THE PHONE MUST KEEP ITS PAGE SCROLL. On a
       phone the shell rules do not apply at all, the portal is an ordinary
       flowing card, and the DOCUMENT is what scrolls. `overflow: hidden` there
       would freeze the phone solid. This is the guard that stops it.
     · `body.pbj-crm-canvas` — a portal embedded in a builder page still has the
       site's header and footer above and below it, and taking the page's scroll
       away would strand them. The canvas only, which is ours alone.

   ⚠️ NO !important. These win on specificity: body.pbj-crm-canvas is (0,2,0) and
   the :has() argument takes it past the (0,1,0) rule it corrects.
   ===========================================================================*/

html:has( .pbjcrm-app.pbjcrm-portal:not(.pbjcrm-mobile-on) ),
body.pbj-crm-canvas:has( .pbjcrm-app.pbjcrm-portal:not(.pbjcrm-mobile-on) ) {
	height: 100%;

	/* ⛔ AND min-height GOES WITH IT, WHICH IS NOT BELT-AND-BRACES. The rule
	   being corrected also sets `min-height: 100vh`, and vh is the LARGEST
	   viewport — on a tablet whose toolbar retracts, 100vh is taller than the
	   visible page, so the body would stay taller than the viewport and the
	   document would still have somewhere to go. Restating it as 100% ties the
	   body to html, which is tied to the viewport, and the last source of extra
	   height is gone. `overflow: hidden` below would mask it either way; a
	   masked defect is still a defect. */
	min-height: 100%;

	margin: 0;
	padding: 0;

	/* The ground. Nothing behind the shell may be a colour we did not choose. */
	background: var(--nav-bg-app);

	/* ⛔ THE DOCUMENT DOES NOT SCROLL. .pbjcrm-view is the only scroller (02
	   §2), so anything that lets html or body move is by definition a bug: it
	   moves the CONTENT while the bars stay nailed to the viewport. */
	overflow: hidden;

	/* And it does not rubber-band either. A scroll with nowhere left to go
	   bounces the whole page by default, which drags the composition out from
	   under the fixed bars and springs it back — the same fault by another
	   route. */
	overscroll-behavior: none;
}

/* ⛔ THE SCROLL STOPS AT THE EDGE OF THE REGION THAT OWNS IT. `contain` means a
   gesture that reaches the end of the view does not CHAIN to whatever is behind
   it and does not bounce past its own end. The rail gets it for the same reason:
   it scrolls internally when its content is tall, and reaching its bottom must
   not start moving the page.

   ⚠️ `contain` rather than `none`: inside the region the browser keeps its
   normal behaviour, including the pull-to-refresh a tablet user expects. */
.pbjcrm-app.pbjcrm-portal:not(.pbjcrm-mobile-on) > .pbjcrm-view,
.pbjcrm-app.pbjcrm-portal:not(.pbjcrm-mobile-on) > .pbjcrm-rail {
	overscroll-behavior: contain;
}

/* The shell paints its own ground too, so a page that is NOT the canvas — a
   builder embed, or a site that turned the canvas off with
   pbj_crm_use_portal_canvas — still gets our colour everywhere the shell covers.
   What it cannot cover there is whatever the builder wrapped it in, and that is
   the theme's page, not ours to repaint. Stated so the difference is a decision
   rather than a surprise. */

/* ---------------------------------------------------- The rail's icons */

/* The node the collapsed rail is built on. It is an inline SVG, aria-hidden,
   drawn by the script — so it takes its colour from the item's own `color` and
   inherits the active item's ink for free, which is why nothing here names one.
   A fixed box either way: an icon that resizes with its label makes a 60px
   column of slightly different sizes. */
.pbjcrm-rail-icon {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	flex: 0 0 16px;
	width: 16px;
	height: 16px;
	color: inherit;
}

.pbjcrm-rail-icon svg {
	display: block;
	width: 16px;
	height: 16px;
	/* currentColor in the markup does the rest; this is the belt for an SVG that
	   names a stroke and forgets a fill, or the reverse. */
	fill: currentColor;
	stroke: currentColor;
}

/* ------------------------------------------- The pinned group, re-checked */

/* ⚠️ RE-READ AFTER INBOXES MOVED OUT. §2.3 pinned a utility group to the bottom;
   Jake overrode it and INBOXES now sits at the end of the rail's CONTENT,
   leaving Import / export and Field settings in here. The rules still hold —
   margin-top:auto pushes what is left to the bottom, and with a short group that
   is exactly what it is for — but one thing is now worth stating rather than
   relying on: a tall content list above must not squash the two items that are
   left into an unreadable stub. The rule and its 10px stay even at one item,
   because the line is what says "these are not part of the list above". */
.pbjcrm-rail-pinned {
	flex: 0 0 auto;
}

/* --------------------------------------- Theming screen, collapsible groups */

/* Jake: "collapse the theming options." The script owns the disclosure — which
   group is open, the button, the aria-expanded — and this owns the shape. The
   three child names are AGREED with the script's owner, not invented here:
   -head is the button, -body is the panel that takes [hidden], -mark is the
   twisty.

   ⚠️ THE CLOSED STATE IS THE PLATFORM'S OWN. `[hidden]` is how an element says
   it is not here, and this file already treats that as structural rather than
   cosmetic (see the .pbjcrm-subnav[hidden] note further up). No class is
   invented for a state HTML already has a word for. */
.pbjcrm-theme-group {
	border: 1px solid var(--nav-line);
	border-radius: 8px;
	background: var(--nav-bg-card);
	padding: 10px 12px;
}

.pbjcrm-theme-group + .pbjcrm-theme-group {
	margin-top: 8px;
}

/* The whole heading row is the control, so the target is the full width of the
   group rather than the few characters of its label. */
.pbjcrm-theme-group-head {
	display: flex;
	align-items: center;
	gap: 8px;
	width: 100%;
	margin: 0;
	padding: 0;
	border: 0;
	background: transparent;
	font-family: var(--nav-font);
	font-size: 13px;
	font-weight: 600;
	text-align: left;
	color: var(--nav-text);
	cursor: pointer;
}

.pbjcrm-theme-group-head:hover {
	color: var(--nav-text);
}

/* ⛔ THE SAME RING AS EVERY OTHER CONTROL THIS FILE ADDS (pbj-behavior 9b). A
   new interactive element that skips the sweep is one nobody can find with a
   keyboard, and acceptance §6 is a keyboard pass. */
.pbjcrm-theme-group-head:focus-visible {
	outline: 2px solid var(--nav-focus-ring);
	outline-offset: 2px;
}

/* The twisty. Sized as a BOX, never as type — it holds whatever mark the script
   draws, and a font-size here would be a fourth value below 11px that the
   handoff never asked for. It points right when shut and down when open, which
   is the one convention that needs no label. */
.pbjcrm-theme-group-mark {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	flex: 0 0 12px;
	width: 12px;
	height: 12px;
	margin-left: auto;
	color: var(--nav-text-mute);
	transition: transform .12s ease-out;
}

.pbjcrm-theme-group-head[aria-expanded="true"] .pbjcrm-theme-group-mark {
	transform: rotate(90deg);
}

/* Space between the heading and the panel exists only while the panel does. */
.pbjcrm-theme-group-body {
	padding-top: 10px;
}

/* ------------------------------------------- Shared record-screen skeleton */

/* CRM owns this layout and modules supply the record-specific nodes. Nothing
   here defines a palette: every visible value resolves through the same theme
   custom properties that already paint the portal. */
.pbjcrm-recordscreen {
	display: grid;
	gap: var(--pbjcrm-space);
	min-width: 0;
}

.pbjcrm-recordscreen-back {
	display: inline-flex;
	align-items: center;
	gap: var(--pbjcrm-space-sm);
	justify-self: start;
}

.pbjcrm-recordscreen-header {
	display: grid;
	grid-template-columns: minmax(0, 1fr) auto;
	gap: var(--pbjcrm-space);
	align-items: start;
	padding-bottom: var(--pbjcrm-space);
	border-bottom: 1px solid var(--pbjcrm-border);
}

.pbjcrm-recordscreen-identity {
	display: flex;
	align-items: flex-start;
	gap: var(--pbjcrm-space);
	min-width: 0;
}

.pbjcrm-recordscreen-icon {
	display: inline-flex;
	flex: 0 0 auto;
}

.pbjcrm-recordscreen-titlebox {
	min-width: 0;
}

.pbjcrm-recordscreen-eyebrow,
.pbjcrm-recordscreen-meta {
	margin: 0;
	color: var(--pbjcrm-muted);
}

.pbjcrm-recordscreen-eyebrow {
	font-size: .82em;
	font-weight: 700;
	letter-spacing: .04em;
	text-transform: uppercase;
}

.pbjcrm-recordscreen-title {
	margin: 2px 0 0;
	overflow-wrap: anywhere;
}

.pbjcrm-recordscreen-meta {
	display: flex;
	flex-wrap: wrap;
	gap: var(--pbjcrm-space-sm) var(--pbjcrm-space);
	margin-top: var(--pbjcrm-space-sm);
}

.pbjcrm-recordscreen-actions,
.pbjcrm-recordscreen-secondary-actions {
	display: flex;
	flex-wrap: wrap;
	gap: var(--pbjcrm-space-sm);
	align-items: center;
}

.pbjcrm-recordscreen-more,
.pbjcrm-recordscreen-sheet-head,
.pbjcrm-recordscreen-sheet-backdrop {
	display: none;
}

.pbjcrm-recordscreen-grid {
	display: grid;
	grid-template-columns: minmax(0, 2fr) minmax(240px, 1fr);
	gap: var(--pbjcrm-space);
	align-items: start;
}

.pbjcrm-recordscreen-grid.pbjcrm-recordscreen-grid-single {
	grid-template-columns: minmax(0, 1fr);
}

/* Suite-module controls share CRM's existing theme surface and focus rules. */
.pbjcrm-compact-input {
	min-width: 0;
	min-height: 34px;
	box-sizing: border-box;
	padding: var(--pbjcrm-space-sm);
	border: 1px solid var(--pbjcrm-border);
	border-radius: var(--pbjcrm-radius-sm, var(--pbjcrm-radius));
	background: var(--pbjcrm-surface);
	color: var(--pbjcrm-text);
	font: inherit;
}

.pbjcrm-compact-input:focus-visible,
.pbjcrm-identity-chip:focus-visible,
.pbjcrm-recipient-chip-remove:focus-visible {
	outline: 2px solid var(--pbjcrm-focus-ring, var(--pbjcrm-accent));
	outline-offset: 2px;
}

.pbjcrm-identity-chip,
.pbjcrm-recipient-chip {
	display: inline-flex;
	align-items: center;
	gap: var(--pbjcrm-space-sm);
	min-width: 0;
}

a.pbjcrm-identity-chip,
button.pbjcrm-identity-chip {
	border: 0;
	background: transparent;
	color: inherit;
	font: inherit;
	text-decoration: none;
	cursor: pointer;
}

.pbjcrm-identity-chip-label {
	font-weight: 600;
	overflow-wrap: anywhere;
}

.pbjcrm-identity-chip-meta {
	color: var(--pbjcrm-muted);
	overflow-wrap: anywhere;
}

.pbjcrm-recipient-chip {
	padding: 3px var(--pbjcrm-space-sm);
	border: 1px solid var(--pbjcrm-border);
	border-radius: var(--pbjcrm-radius-sm, var(--pbjcrm-radius));
	background: var(--pbjcrm-surface-alt);
}

.pbjcrm-recipient-chip-remove {
	min-width: 28px;
	min-height: 28px;
	padding: 0;
	border: 0;
	background: transparent;
	color: var(--pbjcrm-muted);
	cursor: pointer;
}

/* One communication composer for CRM, Helpdesk, RingCentral and future modules. */
.pbjcrm-composer {
	display: grid;
	gap: 0;
	min-width: 0;
	border: 1px solid var(--pbjcrm-border);
	border-radius: var(--pbjcrm-radius);
	background: var(--pbjcrm-surface);
	overflow: hidden;
}

.pbjcrm-composer-header,
.pbjcrm-composer-section,
.pbjcrm-composer-actions {
	padding: var(--pbjcrm-space);
}

.pbjcrm-composer-header,
.pbjcrm-composer-section {
	border-bottom: 1px solid var(--pbjcrm-border);
}

.pbjcrm-composer-title,
.pbjcrm-composer-section-title,
.pbjcrm-composer-description {
	margin: 0;
}

.pbjcrm-composer-title {
	font-size: 1.25em;
}

.pbjcrm-composer-description,
.pbjcrm-composer-hint {
	color: var(--pbjcrm-muted);
}

.pbjcrm-composer-description {
	margin-top: var(--pbjcrm-space-sm);
}

.pbjcrm-composer-body,
.pbjcrm-composer-section {
	display: grid;
	gap: var(--pbjcrm-space);
	min-width: 0;
}

.pbjcrm-composer-section-title {
	font-size: 1.05em;
}

.pbjcrm-composer-field {
	display: grid;
	gap: 4px;
	min-width: 0;
	margin: 0;
}

.pbjcrm-composer-field > input,
.pbjcrm-composer-field > select,
.pbjcrm-composer-field > textarea,
.pbjcrm-composer-field > .pbjcrm-compact-input {
	box-sizing: border-box;
	width: 100%;
	min-width: 0;
	padding: var(--pbjcrm-space-sm);
	border: 1px solid var(--pbjcrm-border);
	border-radius: var(--pbjcrm-radius-sm, var(--pbjcrm-radius));
	background: var(--pbjcrm-surface);
	color: var(--pbjcrm-text);
	font: inherit;
}

.pbjcrm-composer-field > textarea {
	min-height: 150px;
	resize: vertical;
}

.pbjcrm-composer-label {
	font-weight: 600;
}

.pbjcrm-composer-hint {
	font-size: .9em;
}

.pbjcrm-composer-actions {
	display: flex;
	flex-wrap: wrap;
	justify-content: flex-end;
	gap: var(--pbjcrm-space-sm);
}

.pbjcrm-composer-options {
	display: grid;
	gap: var(--pbjcrm-space-sm);
}

.pbjcrm-composer-chips {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--pbjcrm-space-sm);
}

.pbjcrm-composer-option {
	display: flex;
	align-items: flex-start;
	gap: var(--pbjcrm-space-sm);
	min-height: 34px;
	margin: 0;
}

.pbjcrm-footer-right > .pbjcrm-footer-context,
.pbjcrm-footer-context {
	display: flex;
	align-items: center;
	gap: var(--pbjcrm-space-sm);
	min-width: 0;
}

.pbjcrm-recordscreen-main,
.pbjcrm-recordscreen-rail {
	display: grid;
	gap: var(--pbjcrm-space);
	min-width: 0;
}

.pbjcrm-recordsection {
	min-width: 0;
	margin: 0;
	padding: var(--pbjcrm-space);
	border: 1px solid var(--pbjcrm-border);
	border-radius: var(--pbjcrm-radius);
	background: var(--pbjcrm-surface);
}

.pbjcrm-recordsection > h3,
.pbjcrm-recordsection-head {
	margin: 0;
}

.pbjcrm-recordsection-body {
	display: grid;
	gap: var(--pbjcrm-space-sm);
	margin-top: var(--pbjcrm-space-sm);
	min-width: 0;
}

.pbjcrm-recordsection > summary {
	list-style: none;
	cursor: pointer;
}

.pbjcrm-recordsection > summary::-webkit-details-marker {
	display: none;
}

.pbjcrm-recordsection > summary::after {
	content: "+";
	float: right;
	color: var(--pbjcrm-muted);
}

.pbjcrm-recordsection[open] > summary::after {
	content: "−";
}

.pbjcrm-recordsection > summary:focus-visible {
	outline: 2px solid var(--pbjcrm-focus-ring, var(--pbjcrm-accent));
	outline-offset: 3px;
}

.pbjcrm-recordprops {
	display: grid;
	gap: var(--pbjcrm-space-sm);
	margin: 0;
}

.pbjcrm-recordprop {
	display: grid;
	grid-template-columns: minmax(90px, .8fr) minmax(0, 1.2fr);
	gap: var(--pbjcrm-space-sm);
	padding-bottom: var(--pbjcrm-space-sm);
	border-bottom: 1px solid var(--pbjcrm-border);
}

.pbjcrm-recordprop:last-child {
	padding-bottom: 0;
	border-bottom: 0;
}

.pbjcrm-recordprop dt {
	color: var(--pbjcrm-muted);
}

.pbjcrm-recordprop dd {
	min-width: 0;
	margin: 0;
	overflow-wrap: anywhere;
}

@media (max-width: 782px) {

	.pbjcrm-recordscreen-header,
	.pbjcrm-recordscreen-grid {
		grid-template-columns: minmax(0, 1fr);
	}

	.pbjcrm-recordscreen-actions {
		width: 100%;
	}

	.pbjcrm-recordscreen-primary {
		flex: 1 1 auto;
		min-height: 44px;
	}

	.pbjcrm-recordscreen-primary > .pbjcrm-btn,
	.pbjcrm-recordscreen-primary.pbjcrm-btn {
		width: 100%;
		min-height: 44px;
	}

	.pbjcrm-recordscreen-more {
		display: inline-flex;
		align-items: center;
		justify-content: center;
		min-width: 44px;
		min-height: 44px;
	}

	.pbjcrm-recordscreen-secondary {
		display: none;
	}

	.pbjcrm-recordscreen-secondary.is-open {
		display: block;
		position: fixed;
		z-index: 100002;
		inset: 0;
	}

	.pbjcrm-recordscreen-sheet-backdrop {
		display: block;
		position: absolute;
		inset: 0;
		width: 100%;
		height: 100%;
		border: 0;
		background: var(--pbjcrm-overlay, color-mix(in srgb, var(--pbjcrm-text) 45%, transparent));
	}

	.pbjcrm-recordscreen-sheet {
		display: grid;
		gap: var(--pbjcrm-space);
		position: absolute;
		left: 0;
		right: 0;
		bottom: 0;
		max-height: min(75vh, 620px);
		padding: var(--pbjcrm-space) var(--pbjcrm-space) calc(var(--pbjcrm-space) + env(safe-area-inset-bottom));
		border-top: 1px solid var(--pbjcrm-border);
		border-radius: var(--pbjcrm-radius-lg) var(--pbjcrm-radius-lg) 0 0;
		background: var(--pbjcrm-surface);
		box-shadow: 0 -8px 28px var(--pbjcrm-shadow);
		overflow: auto;
	}

	.pbjcrm-recordscreen-sheet-head {
		display: flex;
		align-items: center;
		justify-content: space-between;
		gap: var(--pbjcrm-space);
	}

	.pbjcrm-recordscreen-secondary-actions {
		display: grid;
	}

	.pbjcrm-recordscreen-secondary-actions > .pbjcrm-btn,
	.pbjcrm-recordscreen-secondary-actions > a,
	.pbjcrm-recordscreen-secondary-actions > button {
		width: 100%;
		min-height: 44px;
	}

	.pbjcrm-recordprop {
		grid-template-columns: minmax(0, 1fr);
	}
}

body.pbjcrm-recordsheet-open {
	overflow: hidden;
}

/* ================================================= CRM desktop redesign ===
   These are composition rules only. Every colour, edge, surface and focus
   treatment resolves through the theme variables the portal already exposes. */

/* The expanded universal-card contract. Legacy cards keep their old inner
   shape; only callers that opt into a structured field receive this anatomy. */
.pbjcrm-ucard-structured {
	min-height: 0;
	align-self: start;
}

/* CRM owns the normalized item card's outer geometry as well as its anatomy.
   Every current/future module row therefore fills the same list column; its
   title length or optional metadata can never choose the card width. */
.pbjcrm-item-card.pbjcrm-ucard-structured {
	width: 100%;
	max-width: 100%;
	box-sizing: border-box;
	align-self: stretch;
	padding: var(--pbjcrm-space-sm) 44px var(--pbjcrm-space-sm) var(--pbjcrm-space-sm);
}

.pbjcrm-item-card.is-clickable {
	cursor: pointer;
}

.pbjcrm-ucard-structured > .pbjcrm-ucard-main {
	width: 100%;
	align-items: center;
}

/* itemCard is deliberately stricter than the low-level legend/card primitive:
   every item uses the same icon, identity, state, metadata and action slots. */
.pbjcrm-item-card > .pbjcrm-ucard-main {
	display: grid;
	grid-template-columns: 24px minmax(0, 1fr);
	align-items: start;
}

.pbjcrm-item-card .pbjcrm-cardicon {
	width: 24px;
	min-height: 24px;
	justify-content: center;
}

.pbjcrm-item-card .pbjcrm-ucard-head {
	display: grid;
	grid-template-columns: minmax(0, 1fr) auto;
	align-items: start;
}

.pbjcrm-item-card .pbjcrm-ucard-identity {
	display: grid;
	grid-template-columns: auto minmax(0, 1fr) auto;
	align-items: center;
}

.pbjcrm-item-card .pbjcrm-ucard-state {
	display: grid;
	grid-template-columns: auto auto;
	align-items: center;
}

.pbjcrm-ucard-head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--pbjcrm-space-sm);
	min-width: 0;
}

.pbjcrm-ucard-identity,
.pbjcrm-ucard-state,
.pbjcrm-ucard-metadata,
.pbjcrm-ucard-actions {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: var(--pbjcrm-space-sm);
	min-width: 0;
}

.pbjcrm-ucard-identity {
	flex: 1 1 auto;
}

.pbjcrm-ucard-type,
.pbjcrm-ucard-reference,
.pbjcrm-ucard-status,
.pbjcrm-ucard-module {
	font-family: var(--nav-font-mono, monospace);
	font-size: .78em;
	letter-spacing: .08em;
	text-transform: uppercase;
}

.pbjcrm-ucard-type,
.pbjcrm-ucard-reference,
.pbjcrm-ucard-date,
.pbjcrm-ucard-module,
.pbjcrm-ucard-meta {
	color: var(--pbjcrm-muted);
}

.pbjcrm-ucard-reference,
.pbjcrm-ucard-status {
	padding: 3px 7px;
	border: 1px solid var(--pbjcrm-border);
	border-radius: var(--pbjcrm-radius-sm, var(--pbjcrm-radius));
}

.pbjcrm-ucard-state {
	flex: 0 0 auto;
	justify-content: flex-end;
	margin-left: auto;
}

.pbjcrm-ucard-date {
	font-size: .85em;
	white-space: nowrap;
}

.pbjcrm-ucard-metadata {
	margin-top: 3px;
	font-size: .9em;
}

.pbjcrm-ucard-meta {
	min-width: 0;
	overflow-wrap: anywhere;
}

.pbjcrm-ucard-content {
	display: grid;
	gap: var(--pbjcrm-space-sm);
	margin-top: var(--pbjcrm-space-sm);
	padding-top: var(--pbjcrm-space-sm);
	border-top: 1px solid var(--pbjcrm-border);
}

.pbjcrm-ucard-content-line,
.pbjcrm-ucard-progress {
	margin: 0;
	color: var(--pbjcrm-text-soft, var(--pbjcrm-text));
}

.pbjcrm-ucard-progress {
	font-family: var(--nav-font-mono, monospace);
	font-size: .82em;
}

.pbjcrm-ucard-actions {
	margin-top: var(--pbjcrm-space-sm);
	padding-top: var(--pbjcrm-space-sm);
	border-top: 1px solid var(--pbjcrm-border);
}

.pbjcrm-ucard-module {
	margin-left: auto;
}

.pbjcrm-ucard-structured.is-selected {
	box-shadow: inset 0 0 0 1px var(--pbjcrm-accent);
}

.pbjcrm-ucard-action-ghost {
	border-color: transparent;
	background: transparent;
}

.pbjcrm-ucard-type-other {
	border-left-color: var(--pbjcrm-border-strong, var(--pbjcrm-border));
}

/* The compact form is still the same card, collapsed to the identity line. */
.pbjcrm-ucard-compact .pbjcrm-ucard-body {
	justify-content: center;
}

.pbjcrm-ucard-compact .pbjcrm-ucard-head {
	align-items: center;
}

.pbjcrm-ucard-compact .pbjcrm-ucard-metadata {
	white-space: nowrap;
	overflow: hidden;
}

.pbjcrm-ucard-compact .pbjcrm-ucard-meta {
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

/* A caller-owned checklist: this adds disclosure anatomy only. The rows,
   checkboxes and actions inside remain the caller's existing live controls. */
.pbjcrm-checklist-section {
	margin-top: var(--pbjcrm-space-sm);
	padding-top: var(--pbjcrm-space-sm);
	border-top: 1px solid var(--pbjcrm-border);
}

.pbjcrm-checklist-summary {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--pbjcrm-space-sm);
	font-weight: 600;
	cursor: pointer;
	list-style: none;
}

.pbjcrm-checklist-summary::-webkit-details-marker {
	display: none;
}

.pbjcrm-checklist-summary::after {
	content: "+";
	color: var(--pbjcrm-muted);
}

.pbjcrm-checklist-section[open] > .pbjcrm-checklist-summary::after {
	content: "−";
}

.pbjcrm-checklist-summary:focus-visible {
	outline: 2px solid var(--pbjcrm-focus-ring, var(--pbjcrm-accent));
	outline-offset: 3px;
}

.pbjcrm-checklist-body {
	display: grid;
	gap: var(--pbjcrm-space-sm);
	margin-top: var(--pbjcrm-space-sm);
}

/* Shared adjustable master/detail anatomy. itemWorkspace stores its stream
   width in the signed-in person's server-side portal preferences. */
.pbjcrm-resizable-split {
	display: grid;
	align-items: start;
	min-width: 0;
}

.pbjcrm-resizable-split-end {
	grid-template-columns: minmax(0, 1fr) 12px auto;
}

.pbjcrm-resizable-split-start {
	grid-template-columns: auto 12px minmax(0, 1fr);
}

.pbjcrm-resizable-pane {
	min-width: 0;
	max-width: 100%;
}

.pbjcrm-item-workspace.pbjcrm-resizable-split-start > .pbjcrm-resizable-pane:last-child,
.pbjcrm-item-workspace.pbjcrm-resizable-split-end > .pbjcrm-resizable-pane:last-child {
	padding-left: var(--pbjcrm-space);
	box-sizing: border-box;
}

.pbjcrm-item-workspace > .pbjcrm-resizable-pane {
	overflow: visible;
	max-height: none;
}

.pbjcrm-splitter {
	position: relative;
	align-self: stretch;
	min-height: 180px;
	cursor: col-resize;
	touch-action: none;
}

.pbjcrm-splitter::before {
	content: "";
	position: absolute;
	top: 0;
	bottom: 0;
	left: 50%;
	width: 2px;
	background: var(--pbjcrm-border);
	transform: translateX(-50%);
}

.pbjcrm-splitter:hover::before,
.pbjcrm-splitter:focus-visible::before,
.pbjcrm-splitter.is-dragging::before {
	background: var(--pbjcrm-accent);
}

.pbjcrm-splitter:focus-visible {
	outline: 2px solid var(--pbjcrm-focus-ring, var(--pbjcrm-accent));
	outline-offset: -2px;
}

/* Inbox: one date-grouped stream and a true selected-row reading rail. */
.pbjcrm-inbox {
	display: grid;
	gap: var(--pbjcrm-space);
}

.pbjcrm-inbox-workhead {
	display: flex;
	align-items: flex-end;
	justify-content: space-between;
	flex-wrap: wrap;
	gap: var(--pbjcrm-space);
}

.pbjcrm-inbox-workhead h2,
.pbjcrm-inbox-workhead p {
	margin: 0;
}

.pbjcrm-inbox-filters {
	justify-content: flex-end;
}

.pbjcrm-inbox-layout {
	align-items: start;
}

.pbjcrm-inbox-stream {
	display: grid;
	gap: var(--pbjcrm-space-sm);
	min-width: 0;
}

.pbjcrm-inbox-dategroup {
	display: grid;
	gap: 4px;
	min-width: 0;
}

.pbjcrm-inbox-dategroup + .pbjcrm-inbox-dategroup {
	margin-top: var(--pbjcrm-space-sm);
}

.pbjcrm-inbox-group-head,
.pbjcrm-daily-group-head {
	margin: 0;
	padding: 3px 1px;
	font-family: var(--nav-font-mono, monospace);
	font-size: .78em;
	font-weight: 700;
	letter-spacing: .1em;
	text-transform: uppercase;
	color: var(--pbjcrm-muted);
}

.pbjcrm-inbox-row.pbjcrm-ucard,
.pbjcrm-inbox-dategroup .pbjcrm-queue-row.pbjcrm-ucard {
	display: block;
	width: 100%;
	min-height: 0;
	box-sizing: border-box;
	padding: 6px 44px 6px 10px;
}

.pbjcrm-inbox-row .pbjcrm-ucard-main,
.pbjcrm-inbox-dategroup .pbjcrm-queue-row .pbjcrm-ucard-main {
	align-items: center;
}

.pbjcrm-inbox-row .pbjcrm-ucard-head,
.pbjcrm-inbox-dategroup .pbjcrm-queue-row .pbjcrm-ucard-head {
	min-height: 20px;
}

.pbjcrm-inbox-reading {
	position: static;
	display: grid;
	gap: var(--pbjcrm-space);
	min-width: 0;
	padding: var(--pbjcrm-space);
	border: 1px solid var(--pbjcrm-border);
	border-radius: var(--pbjcrm-radius);
	background: var(--pbjcrm-surface);
}

.pbjcrm-inbox-reading-head {
	display: grid;
	gap: var(--pbjcrm-space-sm);
}

.pbjcrm-inbox-reading-head h3,
.pbjcrm-inbox-reading-preview {
	margin: 0;
}

.pbjcrm-inbox-reading-kind {
	font-family: var(--nav-font-mono, monospace);
	font-size: .78em;
	font-weight: 700;
	letter-spacing: .1em;
	color: var(--pbjcrm-muted);
}

.pbjcrm-inbox-reading-preview {
	line-height: 1.55;
	color: var(--pbjcrm-text-soft, var(--pbjcrm-text));
	overflow-wrap: anywhere;
}

.pbjcrm-inbox-reading-actions {
	display: flex;
	flex-wrap: wrap;
	gap: var(--pbjcrm-space-sm);
}

.pbjcrm-bulk-context {
	min-width: 0;
	max-width: 240px;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	font-family: var(--nav-font-mono, monospace);
	font-size: 10.5px;
	color: var(--nav-text-mute);
}

.pbjcrm-bulk-select {
	flex: 0 0 auto;
	height: 22px;
	max-width: 180px;
	border: 1px solid var(--nav-line-control);
	border-radius: 6px;
	background: var(--nav-bg-control);
	font-family: var(--nav-font);
	font-size: 12px;
	color: var(--nav-text-2);
}

.pbjcrm-footer .pbjcrm-task-bulk,
.pbjcrm-footer .pbjcrm-work-footer {
	margin: 0;
}

/* Insight remains one assembled workspace. The index is navigation inside the
   route; every installed report panel remains rendered in the detail column. */
.pbjcrm-report-layout {
	align-items: start;
}

.pbjcrm-report-index {
	position: sticky;
	top: 0;
	display: grid;
	gap: var(--pbjcrm-space-sm);
	padding: var(--pbjcrm-space-sm);
	border: 1px solid var(--pbjcrm-border);
	border-radius: var(--pbjcrm-radius);
	background: var(--pbjcrm-surface);
}

.pbjcrm-report-search {
	width: 100%;
	box-sizing: border-box;
}

.pbjcrm-report-index-list,
.pbjcrm-report-workspace {
	display: grid;
	gap: var(--pbjcrm-space-sm);
	min-width: 0;
}

.pbjcrm-report-index-button.pbjcrm-btn {
	justify-content: flex-start;
	width: 100%;
	min-height: 34px;
	padding: 6px 9px;
	border-color: transparent;
	background: transparent;
	text-align: left;
	color: var(--pbjcrm-text);
}

.pbjcrm-report-index-button.pbjcrm-btn:hover,
.pbjcrm-report-index-button.pbjcrm-btn:focus-visible {
	border-color: var(--pbjcrm-border);
	background: var(--pbjcrm-surface-alt);
}

.pbjcrm-report-index-button[aria-current="page"] {
	border-color: var(--pbjcrm-accent);
	background: var(--pbjcrm-accent);
	color: var(--pbjcrm-accent-contrast);
}

.pbjcrm-report-workspace > .pbjcrm-panel {
	min-width: 0;
	margin: 0;
	scroll-margin-top: var(--pbjcrm-space);
}

.pbjcrm-report-workspace > .pbjcrm-panel.is-selected {
	border-color: var(--pbjcrm-accent);
	box-shadow: inset 0 0 0 1px var(--pbjcrm-accent);
}

.pbjcrm-report-workspace .pbjcrm-panel-title {
	margin-top: 0;
}

.pbjcrm-report-figures {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
	gap: var(--pbjcrm-space-sm);
}

.pbjcrm-report-figure {
	min-width: 0;
	margin: 0;
}

.pbjcrm-report-value {
	font-size: 20px;
	font-weight: 600;
}

.pbjcrm-report-workspace .pbjcrm-table {
	width: 100%;
}

.pbjcrm-people-split {
	gap: 0;
}

/* Tasks uses the same adjustable list/reading grammar as Inbox. Every control
   in the reading pane is the task card's existing control, backed by the same
   payload and REST paths; the stream is identity and selection only. */
.pbjcrm-task-layout {
	align-items: start;
}

.pbjcrm-task-stream {
	display: grid;
	gap: 4px;
	min-width: 0;
}

.pbjcrm-task-stream-card.pbjcrm-ucard {
	width: 100%;
	min-height: 0;
	box-sizing: border-box;
	padding: 6px 44px 6px 10px;
}

.pbjcrm-task-stream-open {
	max-width: 100%;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	font-weight: 600;
}

.pbjcrm-task-stream-card.is-picked {
	border-color: var(--pbjcrm-accent);
}

.pbjcrm-task-reading {
	position: static;
	display: grid;
	gap: var(--pbjcrm-space);
	min-width: 0;
	padding: var(--pbjcrm-space);
	border: 1px solid var(--pbjcrm-border);
	border-radius: var(--pbjcrm-radius);
	background: var(--pbjcrm-surface);
}

.pbjcrm-task-reading-head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--pbjcrm-space-sm);
}

.pbjcrm-task-reading > .pbjcrm-task-card {
	margin: 0;
}

/* Daily work: the server's row order survives inside three declared buckets. */
.pbjcrm-daily-list,
.pbjcrm-daily-group {
	display: grid;
	gap: var(--pbjcrm-space-sm);
}

.pbjcrm-daily-group + .pbjcrm-daily-group {
	margin-top: var(--pbjcrm-space-sm);
}

.pbjcrm-daily-group .pbjcrm-queue-row {
	width: 100%;
	max-width: 100%;
	box-sizing: border-box;
	align-self: stretch;
	min-height: 0;
}

.pbjcrm-daily-summary .pbjcrm-recordprop dd {
	text-align: right;
	font-family: var(--nav-font-mono, monospace);
}

/* Contact, business and staff-mirror records now use the shared screen grid. */
.pbjcrm-profile-recordscreen .pbjcrm-recordscreen-header {
	padding-bottom: 0;
	border-bottom: 0;
}

.pbjcrm-profile-recordscreen .pbjcrm-recordscreen-identity,
.pbjcrm-profile-recordscreen .pbjcrm-profile-head {
	width: 100%;
	min-width: 0;
}

.pbjcrm-profile-recordscreen .pbjcrm-recordscreen-grid {
	grid-template-columns: minmax(0, 2.1fr) minmax(280px, 336px);
}

.pbjcrm-profile-recordscreen .pbjcrm-profile-side > .pbjcrm-card,
.pbjcrm-profile-recordscreen .pbjcrm-recordscreen-rail > .pbjcrm-card {
	margin: 0;
}

.pbjcrm-agent-recordscreen .pbjcrm-profile-head {
	border-left: 4px solid var(--pbjcrm-rec-contact, var(--pbjcrm-accent));
}

.pbjcrm-team-list > .pbjcrm-cardgrid {
	display: grid;
	padding: 0;
	border: 0;
	border-radius: 0;
	background: transparent;
}

@media (max-width: 980px) {

	.pbjcrm-resizable-split-start,
	.pbjcrm-resizable-split-end,
	.pbjcrm-profile-recordscreen .pbjcrm-recordscreen-grid {
		grid-template-columns: minmax(0, 1fr);
	}

	.pbjcrm-resizable-pane {
		width: auto !important;
	}

	.pbjcrm-item-workspace.pbjcrm-resizable-split-start > .pbjcrm-resizable-pane:last-child,
	.pbjcrm-item-workspace.pbjcrm-resizable-split-end > .pbjcrm-resizable-pane:last-child {
		padding-left: 0;
		padding-top: var(--pbjcrm-space);
	}

	.pbjcrm-splitter {
		display: none;
	}

	.pbjcrm-inbox-reading {
		position: static;
	}

	.pbjcrm-task-reading {
		position: static;
	}

	.pbjcrm-report-index {
		position: static;
	}
}

@media (max-width: 782px) {
	.pbjcrm-item-card .pbjcrm-ucard-head,
	.pbjcrm-item-card .pbjcrm-ucard-identity,
	.pbjcrm-item-card .pbjcrm-ucard-state {
		grid-template-columns: minmax(0, 1fr);
	}

	.pbjcrm-inbox-workhead {
		align-items: stretch;
	}

	.pbjcrm-inbox-filters {
		justify-content: flex-start;
	}

	.pbjcrm-inbox-reading {
		display: none;
	}

	/* A compact item remains the SAME horizontal identity/state card at a
	   narrow desktop width. The prior blanket column switch made every module
	   row four lines tall and visually different from the phone card. Allow
	   wrapping, but do not change the anatomy or centre its contents. */
	.pbjcrm-item-card .pbjcrm-ucard-main {
		gap: 6px;
		text-align: left;
	}

	.pbjcrm-item-card .pbjcrm-ucard-head {
		align-items: center;
		flex-direction: row;
		flex-wrap: wrap;
	}

	.pbjcrm-item-card .pbjcrm-ucard-identity {
		flex: 1 1 180px;
	}

	.pbjcrm-item-card .pbjcrm-queue-title {
		flex: 1 1 auto;
	}

	.pbjcrm-item-card .pbjcrm-ucard-state {
		align-items: center;
		justify-content: flex-end;
		margin-left: auto;
	}

	.pbjcrm-item-card .pbjcrm-ucard-module {
		margin-left: auto;
	}
}

/* Motion is a preference, not a decision this file gets to make. */
@media (prefers-reduced-motion: reduce) {

	.pbjcrm-theme-group-mark {
		transition: none;
	}
}

@media print {

	/* --- Paper is white. See the note above; this was found by printing. --- */
	.pbjcrm-portal,
	.pbjcrm-portal * {
		background: transparent !important;
		color: #000 !important;
		box-shadow: none !important;
	}

	.pbjcrm-portal *,
	.pbjcrm-portal *::before,
	.pbjcrm-portal *::after {
		border-color: #999 !important;
	}

	/* --- The shell: menus, bars, the phone bar, the replacement header. ---

	   The navigation shell's own two regions join the list they belong to
	   (pbj-behavior 9b: the sibling, not just the one that changed). A rail is
	   navigation and a breadcrumb bar is chrome; on paper both are wasted ink,
	   and a `position: fixed` 34px bar would otherwise overprint the foot of
	   every single sheet. --- */
	.pbjcrm-nav,
	.pbjcrm-subnav,
	.pbjcrm-hdr,
	.pbjcrm-hdr-mount,
	.pbjcrm-mbar,
	.pbjcrm-rail,
	.pbjcrm-footer,
	.pbjcrm-search-overlay,
	.pbjcrm-toasts {
		display: none !important;
	}

	/* The shell is one viewport tall and clips what will not fit, which on
	   paper means one page and a silent loss of everything after it. On paper
	   it is a plain block that grows to whatever the record needs — the same
	   call the .pbjcrm-view rule further down this block already makes about
	   its scrollbar. */
	.pbjcrm-app.pbjcrm-portal {
		display: block !important;
		height: auto !important;
		max-height: none !important;
		overflow: visible !important;
		padding-bottom: 0 !important;
	}

	/* --- The control bars: filters, sorting, paging, bulk pickers. --- */
	.pbjcrm-toolbar,
	.pbjcrm-pager,
	.pbjcrm-backlink,
	.pbjcrm-recordscreen-back,
	.pbjcrm-recordscreen-actions,
	.pbjcrm-recordscreen-secondary,
	.pbjcrm-tabs {
		display: none !important;
	}

	/* --- Anything you press. The tick boxes that pick rows for a bulk action
	   go with them: a tick box on paper is a box nobody can tick. --- */
	.pbjcrm-btn,
	.pbjcrm-chip-x,
	.pbjcrm-recordhead-controls,
	.pbjcrm-explainer,
	.pbjcrm-task-pick input[type="checkbox"],
	/* The unified card's select tick and its task controls go with them, for
	   exactly the reason above: a tick box on paper is a box nobody can tick,
	   and "Done" printed on a sheet is a button that does nothing. The card's
	   ICONS stay — they are part of the record, not something you press. */
	.pbjcrm-ucard-tick,
	.pbjcrm-ucard-tools {
		display: none !important;
	}

	/* Nothing reserves space for a control that is no longer printed. */
	.pbjcrm-ucard {
		padding-right: 0 !important;
	}

	/* --- A form is a thing you fill in, so a half-open one is not part of the
	   record. The editor a screen opened over the record goes with it. --- */
	.pbjcrm-inline-form {
		display: none !important;
	}

	/* --- Two columns on a screen are one column on a page: side panels read
	   as a continuation of the record rather than a margin. --- */
	.pbjcrm-profile-layout,
	.pbjcrm-profile-head,
	.pbjcrm-recordscreen-header,
	.pbjcrm-recordscreen-grid {
		display: block !important;
	}

	/* --- Boxes stop being boxes. The border earns its keep on screen by
	   separating panels; on paper the whitespace already does that. --- */
	.pbjcrm-card,
	.pbjcrm-panel,
	.pbjcrm-recordsection {
		border: 0 !important;
		box-shadow: none !important;
		padding-left: 0 !important;
		padding-right: 0 !important;
	}

	/* --- Nothing scrolls on paper, and a scrolling box prints its first
	   screenful and silently loses the rest. --- */
	.pbjcrm-table,
	.pbjcrm-tabbody,
	.pbjcrm-view {
		overflow: visible !important;
		max-height: none !important;
	}

	/* --- Keep a record's blocks whole where the paper allows it. --- */
	.pbjcrm-card,
	.pbjcrm-panel,
	.pbjcrm-task-card,
	.pbjcrm-deal-card,
	.pbjcrm-recordsection {
		break-inside: avoid;
		page-break-inside: avoid;
	}

	@page {
		margin: 14mm;
	}
}
