:root {
	--ab-bg: #f5f3ee;
	--ab-surface: #ffffff;
	--ab-surface-2: #faf8f4;
	--ab-ink: #16181d;
	--ab-ink-strong: #0d0e11;
	--ab-muted: #60697d;
	--ab-line: #dedbd2;
	/* Step 58.1J2 (Reference-Driven Reconstruction): pre-existing bug fix
	   -- .detail-list/.form-panel-list referenced var(--ab-border) in 3
	   places but this token was never defined anywhere, so those
	   dividers silently rendered as no border at all (an invalid
	   var() reference makes the whole declaration compute to its
	   initial value). Defining it as an alias of --ab-line restores the
	   originally-intended dividers with zero visual change to any
	   consumer that already had a working border, and gives dark-canvas
	   scopes below a single divider token to remap. */
	--ab-border: var(--ab-line);
	--ab-accent: #b65725;
	--ab-accent-dark: #91451f;
	--ab-teal: #15756d;
	--ab-blue: #315f93;
	--ab-green: #22764d;
	--ab-red: #b42318;
	--ab-warning: #9a6a12;
	--ab-warning-bg: #fbf1de;
	--ab-charcoal: #16181f;
	/* Step 58.1J2 (Premium Visual Pass): a second, slightly lighter
	   charcoal for layering depth on dark brand/hero surfaces (a card or
	   rule drawn on top of --ab-charcoal needs to read as a step up, not
	   disappear into it). Never used as the dominant background itself. */
	--ab-charcoal-2: #1e222b;
	--ab-hero-line: rgba(247, 244, 239, .08);

	/* Step 58.1J2 (Wave 1): semantic status-family surface tints, all
	   contrast-checked against their own paired foreground color (not
	   against white) at the point of implementation:
	   danger 6.23:1, success 5.30:1, info 5.90:1, neutral 4.87:1,
	   distinction 4.96:1 -- all pass WCAG AA (4.5:1) for normal text.
	   --ab-warning-bg (above, pre-existing) is 4.22:1 -- below AA at
	   normal text size; kept unchanged (not introduced by this pass) and
	   flagged here rather than silently left undocumented. */
	--ab-success-bg: #f3fbf7;
	--ab-danger-bg: #fff7f6;
	--ab-info-bg: #eef3f9;
	--ab-neutral-bg: #f0f1f4;
	--ab-distinction-bg: #e9f5f3;

	/* Step 58.1J2 (Wave 1): the 32 .badge.is-* database-status selectors
	   already resolved, by independent authoring, to exactly 6 distinct
	   color pairs -- named here as the semantic status families the
	   product-owner-approved spec calls for. Values are UNCHANGED
	   (same --ab-green/rgba(35,122,79,.12) etc. as before); this only
	   gives the existing colors names so future status additions have a
	   canonical family to join instead of picking a 33rd independent
	   color. See the mapping comment above .badge.is-planned below for
	   the full 32-status assignment. */
	--ab-family-positive-bg: rgba(35, 122, 79, .12);
	--ab-family-attention-bg: rgba(198, 95, 40, .12);
	--ab-family-negative-bg: rgba(180, 35, 24, .1);
	--ab-family-informational-bg: rgba(49, 95, 147, .12);
	--ab-family-neutral-bg: rgba(102, 112, 133, .12);
	--ab-family-distinction-bg: rgba(23, 126, 117, .12);

	--ab-radius: 8px;
	--ab-radius-lg: 14px;
	--ab-radius-pill: 999px;
	--ab-shadow: 0 12px 28px rgba(22, 24, 29, .08);
	--ab-shadow-lg: 0 24px 56px rgba(22, 24, 29, .14);

	/* Step 58.1I: a single reusable spacing scale -- section/card/group/
	   field rhythm all derive from these, rather than ad hoc pixel
	   values scattered per rule. */
	--ab-space-1: 4px;
	--ab-space-2: 8px;
	--ab-space-3: 14px;
	--ab-space-4: 22px;
	--ab-space-5: 36px;
	--ab-space-6: 56px;

	/* Step 58.1I: deliberate type scale. Marketing display and page H1
	   are distinct (a workspace object title is never as loud as a
	   marketing headline); dense operational text (table/meta/label)
	   stays compact and gets more letter-spacing/weight instead of size
	   to remain legible at small sizes. */
	--ab-text-display: 800 54px/1.05 Inter, ui-sans-serif, system-ui, sans-serif;
	/* Wave 1 addition: fills the confirmed gap between H1(26px) and
	   Display(44px) that ad hoc 30/32px headings were already reaching
	   for (found in the 58.1I mobile hero override) -- major public
	   product headings below the homepage-hero tier, never a page/object
	   H1. */
	--ab-text-product: 700 32px/1.15 Inter, ui-sans-serif, system-ui, sans-serif;
	--ab-text-h1: 800 26px/1.15 Inter, ui-sans-serif, system-ui, sans-serif;
	--ab-text-h2: 700 18px/1.3 Inter, ui-sans-serif, system-ui, sans-serif;
	--ab-text-h3: 700 15px/1.3 Inter, ui-sans-serif, system-ui, sans-serif;
	--ab-text-body: 400 14px/1.55 Inter, ui-sans-serif, system-ui, sans-serif;
	/* Wave 1 addition: dense operational text (table cells, compact
	   fact lists) -- absorbs the audit-confirmed 13px cluster (33
	   occurrences) that previously had no token. */
	--ab-text-data: 600 13px/1.4 Inter, ui-sans-serif, system-ui, sans-serif;
	/* Wave 1 addition: secondary metadata (dates, counts, .metric em
	   context lines) -- absorbs the audit-confirmed 12px cluster (20
	   occurrences). */
	--ab-text-meta: 500 12px/1.4 Inter, ui-sans-serif, system-ui, sans-serif;
	/* --ab-text-label stays 11px, unchanged. Per Product Owner
	   correction: 11px is permitted ONLY for short label-like content
	   (uppercase nav-group labels, badges, micro/compact metadata
	   labels) -- never body copy, help text, or long readable text. The
	   `font` shorthand cannot carry letter-spacing; a companion
	   `letter-spacing: .02em` is documented here for callers that want
	   it (e.g. .nav-title, .badge, .role-code) but is NOT retrofitted
	   onto existing consumers in this wave -- that would be a template-
	   wide rendering change beyond Wave 1's authorized scope. */
	--ab-text-label: 800 11px/1.2 Inter, ui-sans-serif, system-ui, sans-serif;
}

* {
	box-sizing: border-box;
}

/* Guarantee the native `hidden` attribute always wins. Without this, any
   element whose own class sets `display` (e.g. `.badge{display:inline-flex}`,
   `.catalog-step{display:grid}`, `.line-advanced{display:grid}`) silently
   overrides the browser's default `[hidden]{display:none}` UA rule, because
   normal-priority author declarations always beat UA declarations regardless
   of selector specificity. Proven on real mobile Chrome: this previously left
   every JS-toggled `hidden` step/panel/badge permanently visible. A more
   specific selector (e.g. the desktop 3-column catalog view's own
   `!important` override) still correctly wins over this. */
[hidden] {
	display: none !important;
}

html {
	min-height: 100%;
	background: var(--ab-bg);
	color: var(--ab-ink);
	font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}

/* Step 58 UI1: user/content images (e.g. marketplace listing photos) have
   no inherent size limit and were overflowing their containing card/grid
   at their real natural dimensions. Constraining every `img` to its box
   is the standard safe default -- icons/logos/avatars that are already
   sized via explicit width/height attributes or a smaller container are
   unaffected, since max-width:100% only ever caps growth, never forces
   an image larger than its own natural or explicitly-set size. */
img {
	max-width: 100%;
	height: auto;
}

body {
	margin: 0;
	min-height: 100vh;
}

a {
	color: inherit;
	text-decoration: none;
}

.app-shell {
	display: grid;
	grid-template-columns: 248px minmax(0, 1fr);
	min-height: 100vh;
}

/* A real marketing page (currently only the homepage, via $minimalHeader
   in the layout) has no sidebar column at all -- .main takes the full
   width below the slim top nav instead. */
.app-shell--minimal {
	grid-template-columns: minmax(0, 1fr);
}

.marketing-topbar {
	position: sticky;
	top: 0;
	z-index: 30;
	display: flex;
	align-items: center;
	gap: 28px;
	padding: 14px 24px;
	background: #16181f;
	border-bottom: 1px solid rgba(245, 243, 238, .1);
}

.marketing-topbar-brand {
	display: flex;
	align-items: center;
	gap: 10px;
	color: #fff;
	font-weight: 800;
}

/* A real menu, not just the two account actions -- see the layout
   comment above this markup for why. Sits between the brand and the
   account actions; the actions are pushed to the far right via the
   auto margin on this element rather than space-between (which would
   split the row into three even thirds with three flex children). */
.marketing-topbar-links {
	display: flex;
	align-items: center;
	gap: 20px;
	margin-right: auto;
}

.marketing-topbar-links a {
	color: rgba(247, 244, 239, .68);
	font-size: 14px;
	font-weight: 700;
	transition: color .15s ease;
}

.marketing-topbar-links a:hover {
	color: #fff;
}

.marketing-topbar-actions {
	display: flex;
	align-items: center;
	gap: 12px;
}

/* Real, measured bug: this was a bare 42x17px text link right next to
   a 116x36px button -- well under the ~44px touch-target minimum
   (Apple HIG / Material), an easy mis-tap on mobile. Padding + a real
   min-height give it a comparable tap target without changing how it
   reads visually. */
.marketing-topbar-actions a:not(.button) {
	display: inline-flex;
	align-items: center;
	min-height: 44px;
	padding: 0 6px;
	color: rgba(247, 244, 239, .82);
	font-weight: 800;
	font-size: 14px;
	transition: color .15s ease;
}

.marketing-topbar-actions a:not(.button):hover {
	color: #fff;
}

.marketing-topbar-actions .button {
	min-height: 36px;
	padding: 0 16px;
	font-size: 14px;
}

/* No room for brand + 4 links + login + get-started below tablet width
   -- the links drop first (real, but secondary; the footer a scroll
   away covers the same destinations), keeping both account actions,
   which is what a narrow-screen visitor actually came to use. */
@media (max-width: 900px) {
	.marketing-topbar-links {
		display: none;
	}
}

@media (max-width: 500px) {
	.marketing-topbar {
		padding: 12px 16px;
	}

	.marketing-topbar-brand strong {
		display: none;
	}
}

/* ------------------------------------------------------------------
   Step 58.1J2 (Reference-Driven Reconstruction): DARK CANVAS SYSTEM.

   An opt-in per-page `<body class="canvas-dark">` (set by the page
   template itself, e.g. `$bodyClass = 'canvas-dark';` before its
   content renders -- View::render keeps page and layout in the same
   PHP scope, so this needs no framework change). Pages that do not
   opt in are byte-identical to before.

   Rather than re-skinning every component individually, this
   RE-POINTS the shared surface/text/line tokens for everything inside
   `.main` -- every existing .panel/.metric/.bid-row/.task-row/chip/
   badge/table already reads --ab-surface/--ab-ink/--ab-muted/--ab-line/
   --ab-border, so they re-theme automatically with zero per-component
   duplication. Only the handful of components that hardcode a literal
   color instead of a token (.button-secondary, .text-link,
   .collapsible-section summary -- all using #fff or the fixed
   --ab-accent-dark, which is too dark to read on charcoal) get an
   explicit scoped override below.
   ------------------------------------------------------------------ */
body.canvas-dark {
	background: var(--ab-charcoal);
}

body.canvas-dark .main {
	--ab-bg: var(--ab-charcoal);
	--ab-surface: var(--ab-charcoal-2);
	--ab-surface-2: #262b35;
	--ab-ink: #f5f3ee;
	--ab-ink-strong: #ffffff;
	--ab-muted: rgba(245, 243, 238, .62);
	--ab-line: rgba(245, 243, 238, .14);
	--ab-border: rgba(245, 243, 238, .14);
	/* Design audit: --ab-accent-dark (#91451f) is a dark rust made for
	   dark text on a light tint (~15 consumers: Portal's Needs Attention
	   heading, active chip/tab states, etc.) -- with "dark everywhere"
	   now the only theme, every one of those was silently low-contrast.
	   One token remap fixes all 15 at once, same as the other re-points
	   above, rather than patching each consumer. */
	--ab-accent-dark: #f2a65a;
	background: var(--ab-charcoal);
	color: var(--ab-ink);
}

body.canvas-dark .main .button-secondary {
	border-color: rgba(245, 243, 238, .22);
	background: rgba(245, 243, 238, .06);
	color: #fff;
}

body.canvas-dark .main .button-secondary:hover {
	background: rgba(245, 243, 238, .12);
	border-color: rgba(245, 243, 238, .3);
	color: #fff;
}

/* Real, measured accessibility bug (axe-core, WCAG AA): --ab-accent
   (#b65725) was chosen here as "brighter than the too-dark original
   accent-dark", but it still only measures 2.96-3.7:1 against this
   scope's real dark backgrounds (needs 4.5:1) -- it was calibrated as a
   BUTTON background color, not as text-on-dark. --ab-accent-dark is
   already remapped brighter for exactly this scope (line ~310) and
   measures 7-8.7:1 against the same backgrounds -- the correct token
   for this job, not a new one. */
body.canvas-dark .main .text-link,
body.canvas-dark .main .collapsible-section summary {
	color: var(--ab-accent-dark);
}

/* Same root cause, same fix, two more real consumers found by the same
   axe-core pass: .project-identity-mark renders everywhere now that
   canvas-dark is the whole app's default, and .listing-card is always
   dark regardless of page theme (its own self-contained token
   override) -- both used --ab-accent as text-on-dark and measured
   under 4.5:1. */
body.canvas-dark .main .project-identity-mark {
	color: var(--ab-accent-dark);
}

/* Real, measured accessibility bug (axe-core, WCAG AA): .chip's teal
   text (--ab-teal, #15756d) was calibrated for its light-theme
   background (a near-white surface) and measures only 2.57-2.84:1
   against canvas-dark's real chip backgrounds (needs 4.5:1) -- used
   extensively (Portal's Overview stats, Search, profile chips). A
   brighter teal (verified 5.5-6.5:1 against every real dark background
   this renders on) scoped to canvas-dark only, so the light theme's
   own already-passing color is untouched. */
body.canvas-dark .main .chip {
	color: #2fb8ab;
}

/* Real, measured accessibility bug (axe-core, WCAG AA): the same
   root-cause pattern, one family at a time -- --ab-blue/--ab-green/
   --ab-red/--ab-teal were calibrated for their light-theme tinted
   backgrounds and measure 2.4-3.1:1 against canvas-dark's real
   composited family-bg tints (needs 4.5:1). Not fixed by remapping the
   base tokens themselves (they're also used as solid BACKGROUND colors
   elsewhere -- .review-action, tab/pill fills -- which must stay their
   original, darker value); each badge status selector is targeted
   directly instead, verified 6.4-8.3:1 against every real family-bg
   tint. Grouped by family, same grouping as the badge mapping comment
   above. ATTENTION family already uses --ab-accent-dark, already
   bright enough (7-8.7:1) -- untouched. */
body.canvas-dark .main .badge.is-planned,
body.canvas-dark .main .badge.is-draft,
body.canvas-dark .main .badge.is-review,
body.canvas-dark .main .badge.is-planning,
body.canvas-dark .main .badge.is-tendering,
body.canvas-dark .main .badge.is-abc_rfq_closed,
body.canvas-dark .main .badge.is-abc_unpaid,
body.canvas-dark .main .badge.is-abc_wo_scheduled,
body.canvas-dark .main .badge.is-abc_wo_issued {
	color: #7fb0e8;
}

body.canvas-dark .main .badge.is-active,
body.canvas-dark .main .badge.is-ported,
body.canvas-dark .main .badge.is-completed,
body.canvas-dark .main .badge.is-published,
body.canvas-dark .main .badge.is-abc_rfq_open,
body.canvas-dark .main .badge.is-accepted,
body.canvas-dark .main .badge.is-abc_paid,
body.canvas-dark .main .badge.is-abc_wo_completed {
	color: #5fca8f;
}

body.canvas-dark .main .badge.is-blocked,
body.canvas-dark .main .badge.is-suspended,
body.canvas-dark .main .badge.is-disabled,
body.canvas-dark .main .badge.is-abc_overdue,
body.canvas-dark .main .badge.is-hidden,
body.canvas-dark .main .badge.is-cancelled,
body.canvas-dark .main .badge.is-abc_rfq_cancelled,
body.canvas-dark .main .badge.is-rejected,
body.canvas-dark .main .badge.is-abc_wo_cancelled {
	color: #f28b7e;
}

body.canvas-dark .main .badge.is-abc_rfq_awarded,
body.canvas-dark .main .badge.is-featured,
body.canvas-dark .main .badge.is-promoted,
body.canvas-dark .main .badge.is-business-verified,
body.canvas-dark .main .badge.is-verified {
	color: #2fb8ab;
}

body.canvas-dark .main .panel--quiet {
	background: rgba(245, 243, 238, .04);
}

body.canvas-dark .main .panel--actions {
	border-color: rgba(198, 95, 40, .4);
	background: rgba(198, 95, 40, .14);
}

body.canvas-dark .main .panel--attention {
	border-color: rgba(198, 95, 40, .45);
	background: rgba(198, 95, 40, .16);
}

/* AB-D06 Actionable Row (.bid-row/.task-row/.notification-row/
   .conversation-row/.review-row/.review-card) hardcodes background:#fff
   rather than a token (pre-existing, unrelated to this pass) -- scoped
   override so rows read as an inset dark surface instead of a literal
   white card inside the dark canvas. */
/* Step 58.1J2 (Reference-Driven Reconstruction): Project Detail's
   image-substitute identity band -- since no real project photography
   field exists, this uses a deterministic architectural icon plate
   (never a fabricated photo) plus a REAL progress ring computed from
   done_tasks_count/tasks_count. Reuses .entry-circle's precision-
   outline circular language (AB-C06), scaled up. */
.project-identity-band {
	display: flex;
	align-items: center;
	gap: 20px;
}

.project-identity-mark {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	flex-shrink: 0;
	width: 64px;
	height: 64px;
	border-radius: var(--ab-radius-pill);
	border: 1px solid var(--ab-accent);
	background: rgba(198, 95, 40, .12);
	color: var(--ab-accent);
}

.project-identity-mark .icon {
	width: 30px;
	height: 30px;
}

.project-progress-ring {
	position: relative;
	flex-shrink: 0;
	width: 72px;
	height: 72px;
}

.project-progress-ring svg {
	transform: rotate(-90deg);
}

.project-progress-ring-track {
	fill: none;
	stroke: var(--ab-line);
	stroke-width: 6;
}

.project-progress-ring-value {
	fill: none;
	stroke: var(--ab-accent);
	stroke-width: 6;
	stroke-linecap: round;
	transition: stroke-dashoffset .2s ease;
}

.project-progress-ring-label {
	position: absolute;
	inset: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	font: var(--ab-text-h3);
	color: var(--ab-ink-strong);
}

body.canvas-dark .main .bid-row,
body.canvas-dark .main .task-row,
body.canvas-dark .main .notification-row,
body.canvas-dark .main .conversation-row,
body.canvas-dark .main .review-row,
body.canvas-dark .main .review-card {
	background: var(--ab-surface-2);
	border-color: var(--ab-line);
}

/* Step 58.1J2 (Platform-Wide Propagation): every remaining component
   that hardcodes `background: #fff` instead of reading --ab-surface
   (pre-existing across the file, not introduced by this pass) gets one
   dark-scope catch-all here, so the dark canvas can now be turned on
   for ANY authenticated page without hunting down white-on-dark
   invisible-text bugs one page at a time. Form controls are covered by
   a single type-selector rule since every .account-form/.knowledge-
   form/.estimate-form/.filter-bar/.filter-form/.compact-form/
   .catalog-search-row input/select/textarea already shares the exact
   same AB-FM01 Standard Control declaration. */
body.canvas-dark .main input,
body.canvas-dark .main select,
body.canvas-dark .main textarea {
	background: var(--ab-surface-2);
	border-color: var(--ab-line);
	color: var(--ab-ink);
}

body.canvas-dark .main .data-table tbody tr:hover {
	background: rgba(245, 243, 238, .05);
}

body.canvas-dark .main .rating-score,
body.canvas-dark .main .message-bubble,
body.canvas-dark .main .search-result,
body.canvas-dark .main .catalog-row,
body.canvas-dark .main .estimate-section-title,
body.canvas-dark .main .icon-button[aria-expanded="true"],
body.canvas-dark .main .cost-component-row {
	background: var(--ab-surface-2);
	border-color: var(--ab-line);
}

/* Design audit: the four semantic --ab-*-bg tokens (success/danger/
   warning/info) are solid, near-white colors with no dark-canvas remap
   -- unlike --ab-family-*-bg (already translucent rgba, built for both
   themes), .notice.is-success/.is-error/.is-warning/.is-info and the
   account-form error-state input background render as a literal white
   box on the dark canvas (caught live: a green "Logged in." flash
   message on Finance). The paired foreground color (--ab-green etc.) is
   also a dark-on-light tone that loses contrast once the background
   goes translucent-dark, so both are remapped together here. */
body.canvas-dark .main {
	--ab-success-bg: rgba(35, 122, 79, .18);
	--ab-danger-bg: rgba(180, 35, 24, .18);
	--ab-warning-bg: rgba(154, 106, 18, .2);
	--ab-info-bg: rgba(49, 95, 147, .2);
}

body.canvas-dark .main .notice.is-success {
	color: #4ade80;
}

body.canvas-dark .main .notice.is-error {
	color: #f87171;
}

body.canvas-dark .main .notice.is-warning {
	color: #fbbf24;
}

body.canvas-dark .main .notice.is-info {
	color: #60a5fa;
}

.sidebar {
	padding: 22px 18px;
	background: #20232a;
	color: #f7f4ef;
}

/* Step 58.1J2 (PRESENTATION_SWEEP): one canonical nav DOM -- the
   checkbox toggle and its <label> are hidden on desktop and
   .sidebar-content is always visible there; the shared mobile
   breakpoint below turns this into a real, native, keyboard-accessible
   (label-for-input) collapse/expand control with zero JS. Replaces a
   prior <details>/<summary> implementation: closed <details> content is
   hidden by the browser's own internal mechanism, which a plain
   `display:block` on a child cannot override, so the entire sidebar nav
   was invisible in real rendering despite being present in the DOM and
   "verified live" by source-level reasoning alone. This is the single
   owner of the fix; no page ever overrides it. */
.sidebar-summary {
	display: none;
}

/* Visually hidden, not display:none -- stays in the tab order and
   toggles on Space/Enter via its <label for>, matching the keyboard
   behavior the prior <details>/<summary> control had. */
.sidebar-toggle {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	overflow: hidden;
	clip: rect(0, 0, 0, 0);
	white-space: nowrap;
	border: 0;
}

.sidebar-content {
	display: block;
}

.brand {
	display: flex;
	align-items: center;
	gap: 10px;
	margin-bottom: 26px;
}

/* Real, measured accessibility bug (axe-core, WCAG AA): .brand-mark is
   itself a <span> inside .brand, so the muted-subtitle rule below
   (originally meant only for the domain-text span next to the brand
   name) unintentionally matched it too -- diluting its "AB" text to
   rgba(...,.68), which composites to ~2.86:1 against the mark's own
   solid accent-orange background (needs 4.5:1). Scoped out below;
   given its own explicit, verified-passing color here (4.8:1). */
.brand-mark {
	display: grid;
	place-items: center;
	width: 38px;
	height: 38px;
	border: 1px solid rgba(255, 255, 255, .18);
	border-radius: var(--ab-radius);
	background: var(--ab-accent);
	color: #fff;
	font-weight: 800;
	overflow: hidden;
	background: #000;
}

.brand-mark img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.brand strong,
.brand span {
	display: block;
}

.brand strong {
	font-size: 14px;
}

.brand span:not(.brand-mark) {
	margin-top: 2px;
	color: rgba(247, 244, 239, .68);
	font-size: 12px;
}

.nav-group {
	margin-top: 22px;
}

/* Real, measured accessibility bug (axe-core, WCAG AA): .48 opacity
   measured 4.38:1 against the sidebar's real background -- just under
   the 4.5:1 threshold. .6 verified at 6.03:1. */
.nav-title {
	margin: 0 0 8px;
	color: rgba(247, 244, 239, .6);
	font-size: 11px;
	font-weight: 700;
	text-transform: uppercase;
}

.nav-link {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 10px;
	min-height: 38px;
	padding: 8px 10px;
	border-left: 2px solid transparent;
	border-radius: var(--ab-radius);
	color: rgba(247, 244, 239, .78);
	font-size: 14px;
	transition: background-color .12s ease, color .12s ease, border-color .12s ease;
}

@media (prefers-reduced-motion: reduce) {
	.nav-link {
		transition: none;
	}
}

/* Step 58.1J2 (Wave 1): groups an icon + label as one flex item so
   .nav-link's existing space-between still puts exactly two things
   apart (icon+label on the left, .nav-dot on the right) instead of
   spreading three items evenly. Only nav-links with a representative
   icon use this wrapper; plain-text nav-links are unaffected. */
.nav-link-label {
	display: flex;
	align-items: center;
	gap: 10px;
}

.nav-link:hover,
.nav-link.is-active {
	background: rgba(255, 255, 255, .08);
	color: #fff;
}

.nav-link.is-active {
	border-left-color: var(--ab-accent);
	background: rgba(255, 255, 255, .1);
}

.nav-dot {
	width: 8px;
	height: 8px;
	border-radius: 50%;
	background: var(--ab-teal);
}

.sidebar-account {
	margin-top: 24px;
	padding: 12px;
	border: 1px solid rgba(255, 255, 255, .14);
	border-radius: var(--ab-radius);
	background: rgba(255, 255, 255, .06);
}

/* Wave 1: representative avatar usage in the sidebar account block. */
.sidebar-account--with-avatar {
	display: flex;
	align-items: center;
	gap: 10px;
}

.sidebar-account-text {
	min-width: 0;
}

/* .sidebar-account span (below) generically blocks every bare <span>
   descendant for the plain text rows -- override back to the avatar's
   own inline-flex circle so it isn't flattened by that older rule. */
.sidebar-account .avatar {
	display: inline-flex;
	overflow-wrap: normal;
}

.sidebar-account span,
.sidebar-account strong {
	display: block;
	overflow-wrap: anywhere;
}

.sidebar-account span {
	color: rgba(247, 244, 239, .58);
	font-size: 11px;
	font-weight: 800;
	text-transform: uppercase;
}

.sidebar-account strong {
	margin-top: 5px;
	font-size: 13px;
}

.main {
	min-width: 0;
	padding: 28px;
}

.topbar {
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	gap: 20px;
	margin-bottom: 22px;
}

.topbar h1 {
	margin: 0;
	font: var(--ab-text-h1);
	/* Step 58.1H (BL12): a real, unbreakable, space-less title/identifier
	   (proven live) forces this flex child wider than its container at
	   narrow widths -- the same established pattern already used
	   throughout this file for other user-entered content (e.g.
	   .data-table td, .key-value dd). Normal, space-containing titles are
	   unaffected. */
	overflow-wrap: anywhere;
}

.topbar p {
	margin: 6px 0 0;
	max-width: 760px;
	color: var(--ab-muted);
}

/* Communication mobile closure fix (Phase 1.1): the thread page's header
   subtitle renders CommunicationFormatter::preview(), which can contain
   arbitrary user-authored content (including a long unbroken URL) --
   unlike every other page's static/short .topbar subtitle text. Scoped
   to this one Communication-specific class rather than widening the
   shared, platform-wide .topbar/.topbar p rules. */
.topbar-text-block {
	min-width: 0;
}

.communication-thread-preview {
	overflow-wrap: anywhere;
}

/* Step 58.1J2 (Premium Visual Pass): canonical public/admin "product
   header" -- a compact dark brand band, distinct from the marketing
   .landing-hero, giving every domain workspace a real identity moment
   instead of opening straight into H1 -> paragraph -> counters. Additive
   modifier on the existing .topbar (no new component, no markup change
   beyond one class); pages that don't opt in are pixel-identical. When a
   .metric-grid immediately follows, it visually continues the same dark
   band (one seamless brand strip carrying the header AND its key
   metric), then normal light-surface content resumes below. */
.topbar--product {
	margin: -28px -28px 0;
	padding: 30px 28px 22px;
	background:
		linear-gradient(165deg, var(--ab-charcoal-2), var(--ab-charcoal) 65%),
		repeating-linear-gradient(90deg, var(--ab-hero-line) 0, var(--ab-hero-line) 1px, transparent 1px, transparent 80px);
	color: #f7f4ef;
}

.topbar--product h1 {
	color: #fff;
}

.topbar--product p {
	color: rgba(247, 244, 239, .74);
}

.topbar--product .env-pill {
	border-color: rgba(255, 255, 255, .2);
	background: rgba(255, 255, 255, .08);
	color: #fff;
}

.topbar--product + .metric-grid {
	margin: 0 -28px 18px;
	padding: 0 28px 26px;
	background: var(--ab-charcoal);
	border-radius: 0 0 var(--ab-radius-lg) var(--ab-radius-lg);
}

.topbar--product + .metric-grid .metric {
	border-color: rgba(255, 255, 255, .14);
	background: rgba(255, 255, 255, .05);
	box-shadow: none;
}

.topbar--product + .metric-grid .metric span {
	color: rgba(247, 244, 239, .58);
}

.topbar--product + .metric-grid .metric strong {
	color: #fff;
}

.topbar--product + .metric-grid .metric em {
	color: rgba(247, 244, 239, .55);
}

/* A quieter, single-row counter strip -- used where the primary object
   of the page is not the KPIs (Search: the search box is primary, the
   index counts are secondary reference information). */
.metric-grid--compact {
	grid-template-columns: repeat(6, minmax(0, 1fr));
	gap: 10px;
	margin-bottom: 14px;
}

.metric-grid--compact .metric {
	min-height: auto;
	padding: 10px 12px;
}

.metric-grid--compact .metric strong {
	margin-top: 4px;
	font-size: 18px;
}

@media (max-width: 900px) {
	.metric-grid--compact {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}
}

.env-pill {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	min-height: 34px;
	padding: 0 12px;
	border: 1px solid var(--ab-line);
	border-radius: var(--ab-radius);
	background: var(--ab-surface);
	color: var(--ab-muted);
	font-size: 13px;
	white-space: nowrap;
}

.status-dot {
	width: 8px;
	height: 8px;
	border-radius: 50%;
	background: var(--ab-green);
}

.metric-grid,
.module-grid {
	display: grid;
	gap: 14px;
}

.metric-grid {
	grid-template-columns: repeat(4, minmax(0, 1fr));
	margin-bottom: 18px;
}

.metric,
.module,
/* Step 58.1J2 (Wave 1): AB-SF01/AB-SF03 shared shell. .form-panel below
   used to independently redeclare this exact border/radius/background/
   shadow combination -- confirmed byte-identical, now a single shared
   declaration. Values and visual output are unchanged. */
.panel,
.form-panel {
	border: 1px solid var(--ab-line);
	border-radius: var(--ab-radius);
	background: var(--ab-surface);
	box-shadow: var(--ab-shadow);
}

/* Step 58.1I: a reusable "this is reference/passive information" surface
   -- additive only (still a .panel), so every existing .panel consumer
   is unaffected unless it opts in. First consumer: Portal's Overview
   section, deliberately quieter than Attention/Active Work/Waiting. */
.panel--quiet {
	border-color: transparent;
	background: var(--ab-surface-2);
	box-shadow: none;
}

/* Step 58.1I: a recognizable, non-decorative surface for a group of
   "start something new" actions -- Quick Actions on Portal, reusable
   anywhere a distinct actionable panel is warranted. */
.panel--actions {
	border-color: rgba(182, 87, 37, .22);
	background: #fdf7f3;
}

/* Step 58.1J2 (Premium Visual Pass): the ONE dominant tier -- applied
   only when Portal's Needs Attention actually has content (the calm
   "all caught up" empty state stays a plain, unremarkable .panel; a
   populated attention queue needs to visually outrank Active
   Work/Waiting/Overview without inventing a new color, so this reuses
   the existing --ab-family-attention-bg token). */
.panel--attention {
	border-color: rgba(198, 95, 40, .35);
	background: var(--ab-family-attention-bg);
	box-shadow: var(--ab-shadow-lg);
}

.panel--attention .panel-head h2 {
	color: var(--ab-accent-dark);
}

/* Step 58.1I: severity accents for attention-style rows -- additive
   modifiers on top of the unchanged base .bid-row (Tenders/Invoices/RFQ
   bid-rows elsewhere never receive these classes, so they are visually
   untouched). Text severity label (.profile-type) remains the primary,
   non-color-only signal; the border is a secondary reinforcement. */
.bid-row--severity-high {
	border-left: 3px solid var(--ab-red);
}

.bid-row--severity-medium {
	border-left: 3px solid var(--ab-warning);
}

.metric {
	min-height: 104px;
	padding: 16px;
}

.metric span {
	display: block;
	color: var(--ab-muted);
	font-size: 12px;
	font-weight: 700;
	text-transform: uppercase;
}

/* Reference visual-DNA pass: overrides the generic ".metric span"
   block-ification above so the entry-circle keeps its own centered
   icon layout; the label text below it is unaffected. Color is
   deliberately --ab-muted here, not the orange accent -- these are
   decorative navigation icons, and the spec is explicit that orange
   marks active/primary/brand moments, not generic decoration; a
   restrained neutral icon color is the correct call, not an oversight. */
.metric .entry-circle {
	display: inline-flex;
	margin-bottom: 12px;
	text-transform: none;
	color: var(--ab-muted);
}

/* Redesign pass 9: a marketing page reads at a different scale than an
   admin panel. Reusing --ab-text-h3 (15px, built for dense dashboard
   card headers) for section headings was the root cause of the whole
   page reading as "internal tool", not a deliberate choice -- these
   sections get their own scale, generous rhythm and a real content
   width, scoped to .landing-page so nothing else on the platform
   changes. Never touches --ab-text-h2/h3 themselves (used everywhere
   in the authenticated app). */
.landing-page {
	max-width: 1160px;
	margin: 0 auto;
}

.landing-page > header,
.landing-page > section {
	margin-top: 96px;
}

.landing-page > header:first-child {
	margin-top: 0;
}

.landing-page h2 {
	margin: 0 0 var(--ab-space-2);
	font: 800 34px/1.15 Inter, ui-sans-serif, system-ui, sans-serif;
	letter-spacing: -0.01em;
	color: var(--ab-ink-strong);
}

@media (max-width: 700px) {
	.landing-page > header,
	.landing-page > section {
		margin-top: 56px;
	}

	.landing-page h2 {
		font-size: 26px;
	}
}

/* The platform's architecture in one glance -- a single job moving
   through one account, not a grid of independent capabilities.
   Replaces the earlier "four honest capabilities" grid (which
   explained WHAT the platform does but not HOW work actually flows
   through it end to end). Numbered like a real "how it works" section
   (Stripe/Linear pattern): the number carries visual weight so the
   step reads before the label does. */
.landing-workflow-title {
	max-width: 640px;
}

.landing-workflow-steps {
	display: flex;
	flex-wrap: wrap;
	gap: var(--ab-space-5) 34px;
	margin: var(--ab-space-6) 0 0;
	padding: 0;
	list-style: none;
}

.landing-workflow-steps li {
	position: relative;
	flex: 1 1 150px;
	display: flex;
	flex-direction: column;
	gap: 6px;
}

/* Real, measured accessibility bug (axe-core, WCAG AA): --ab-accent
   measured 3.69:1 against this section's charcoal background (needs
   4.5:1) -- this page always renders canvas-dark (landing.php sets
   $bodyClass unconditionally), so --ab-accent-dark's canvas-dark remap
   (7-8.7:1 against every real dark background in the app) always
   applies here and is the correct token for text, not --ab-accent
   (calibrated as a button background color). */
.landing-workflow-step-number {
	font: 800 15px/1 Inter, ui-sans-serif, system-ui, sans-serif;
	color: var(--ab-accent-dark);
}

/* A plain chevron between steps reads as "flow" without a connecting
   rail graphic -- Refactoring UI: use typography, not another shape,
   wherever text alone can carry the meaning. */
/* Real, measured bug: this used var(--ab-line), which resolves to
   rgba(245,243,238,.14) on the canvas-dark scope this section lives
   in -- an almost-invisible sliver, not a readable arrow (confirmed
   via a live screenshot, not assumed). A visible muted white reads as
   a deliberate connector instead. */
.landing-workflow-steps li:not(:last-child)::after {
	content: '\2192';
	position: absolute;
	top: 1px;
	right: -25px;
	color: rgba(245, 243, 238, .38);
	font-size: 18px;
}

/* Don't Make Me Think: a link has to look clickable before anyone
   hovers it, not just react once they do -- underline is the one
   affordance every visitor already reads as "this does something",
   so it's on by default here, not just on :hover. */
.landing-workflow-step-label {
	font: 800 19px/1.25 Inter, ui-sans-serif, system-ui, sans-serif;
	color: #fff;
	text-decoration: underline;
	text-decoration-color: rgba(255, 255, 255, .35);
	text-underline-offset: 3px;
	transition: color .15s ease, text-decoration-color .15s ease;
}

.landing-workflow-step-label:hover {
	color: var(--ab-accent-dark);
	text-decoration-color: currentColor;
}

.landing-workflow-steps em {
	font-style: normal;
	color: var(--ab-muted);
	font-size: 14px;
}

@media (max-width: 900px) {
	.landing-workflow-steps li {
		flex-basis: calc(50% - var(--ab-space-4));
	}

	.landing-workflow-steps li:nth-child(even)::after {
		display: none;
	}
}

@media (max-width: 500px) {
	.landing-workflow-steps li {
		flex-basis: 100%;
	}

	.landing-workflow-steps li::after {
		display: none;
	}
}

/* Proof: "one system" is a claim until you see the same job's data
   carry across three real screens. Frames are deliberately large --
   a small proof image proves nothing an eye can't read. A plain arrow
   between frames, same reasoning as the workflow strip above: the
   connection is the point, not a graphic. */
.landing-proof {
	padding: var(--ab-space-6) var(--ab-space-5);
}

.landing-proof-chain {
	display: flex;
	align-items: center;
	gap: var(--ab-space-4);
}

.landing-proof-frame {
	flex: 1 1 0;
	min-width: 0;
	margin: 0;
	border: 1px solid var(--ab-line);
	border-radius: var(--ab-radius);
	overflow: hidden;
	background: var(--ab-charcoal);
	box-shadow: var(--ab-shadow-lg);
}

.landing-proof-frame img {
	display: block;
	width: 100%;
	height: auto;
}

.landing-proof-frame figcaption {
	padding: 10px var(--ab-space-3);
	font: var(--ab-text-label);
	letter-spacing: 0.06em;
	color: var(--ab-muted);
	border-top: 1px solid var(--ab-line);
}

.landing-proof-arrow {
	flex: 0 0 auto;
	color: var(--ab-muted);
	font-size: 24px;
}

.landing-proof-caption {
	margin: var(--ab-space-5) 0 0;
	color: var(--ab-muted);
	font-size: 17px;
	text-align: center;
}

@media (max-width: 700px) {
	.landing-proof {
		padding: var(--ab-space-5) var(--ab-space-4);
	}

	.landing-proof-chain {
		flex-direction: column;
	}

	.landing-proof-frame {
		/* flex:1 1 0 (the desktop row rule) collapses to near-zero height
		   here: in a column flex container, flex-basis 0 relies on the
		   item's automatic min-height to stay visible, but this element's
		   own overflow:hidden (for the rounded-corner image clip) zeroes
		   that automatic minimum per spec -- a real, measured bug (frame
		   height 2px, image inside it still 61px), not a hypothetical
		   one. flex:0 0 auto sizes to content instead of relying on that
		   minimum at all. */
		flex: 0 0 auto;
		width: 100%;
	}

	.landing-proof-arrow {
		transform: rotate(90deg);
	}
}

.metric strong {
	display: block;
	margin-top: 8px;
	font-size: 28px;
	line-height: 1;
}

.metric em {
	display: block;
	margin-top: 8px;
	color: var(--ab-muted);
	font-size: 12px;
	font-style: normal;
	overflow-wrap: anywhere;
}

.panel {
	margin-bottom: 18px;
	padding: 18px;
}

.panel h2 {
	margin: 0 0 12px;
	font: var(--ab-text-h2);
}

.panel-head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 14px;
	margin-bottom: 12px;
}

.panel-head h2 {
	margin: 0;
}

.module-grid {
	grid-template-columns: repeat(3, minmax(0, 1fr));
}

.module {
	display: flex;
	flex-direction: column;
	gap: 12px;
	min-height: 172px;
	padding: 16px;
}

.module-head {
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	gap: 12px;
}

.module h3 {
	margin: 0;
	font-size: 16px;
}

.module p {
	margin: 0;
	color: var(--ab-muted);
	font-size: 13px;
	line-height: 1.45;
}

.badge {
	display: inline-flex;
	align-items: center;
	min-height: 24px;
	padding: 0 8px;
	border: 0;
	border-radius: var(--ab-radius-pill);
	background: var(--ab-family-distinction-bg);
	color: var(--ab-teal);
	font: inherit;
	font-size: 11px;
	font-weight: 800;
	text-transform: uppercase;
	white-space: nowrap;
}

button.badge {
	cursor: pointer;
}

/* Step 58.1J2 (Wave 1): full 32-status semantic family mapping
   (UNMAPPED_STATUS_VALUE=0). Colors unchanged from before this pass --
   only named. Label TEXT (rendered via each module's own Formatter,
   never this stylesheet) remains the sole disambiguator between
   same-family statuses that mean different things in different
   domains (e.g. "published" vs "open" vs "active" are all POSITIVE).
   POSITIVE:      active, ported, completed, published, abc_rfq_open,
                  accepted, abc_paid, abc_wo_completed
   ATTENTION:     pending, in_progress, abc_wo_in_progress,
                  abc_wo_on_hold
   NEGATIVE:      blocked, suspended, disabled, abc_overdue, hidden,
                  cancelled, abc_rfq_cancelled, rejected,
                  abc_wo_cancelled
   INFORMATIONAL: planned, draft, review, planning, tendering,
                  abc_rfq_closed, abc_unpaid, abc_wo_scheduled,
                  abc_wo_issued
   NEUTRAL:       archived
   DISTINCTION:   abc_rfq_awarded, featured, promoted,
                  business-verified, verified (below; the .badge base
                  rule already defaults to this family)
   Note (not changed here -- a semantic question for the product owner,
   not a Wave 1 decision): abc_unpaid currently sits in INFORMATIONAL
   alongside draft/planning-type "not yet acted on" states. An unpaid
   invoice arguably belongs in ATTENTION instead. Flagging, not
   changing -- this is a pre-existing product decision, not a defect
   introduced or authorized to fix in this pass. */
.badge.is-planned {
	background: var(--ab-family-informational-bg);
	color: var(--ab-blue);
}

.badge.is-blocked {
	background: var(--ab-family-negative-bg);
	color: var(--ab-red);
}

.badge.is-active,
.badge.is-ported {
	background: var(--ab-family-positive-bg);
	color: var(--ab-green);
}

.badge.is-suspended,
.badge.is-disabled {
	background: var(--ab-family-negative-bg);
	color: var(--ab-red);
}

.badge.is-pending {
	background: var(--ab-family-attention-bg);
	color: var(--ab-accent-dark);
}

.badge.is-draft,
.badge.is-review {
	background: var(--ab-family-informational-bg);
	color: var(--ab-blue);
}

.badge.is-in_progress {
	background: var(--ab-family-attention-bg);
	color: var(--ab-accent-dark);
}

.badge.is-planning,
.badge.is-tendering {
	background: var(--ab-family-informational-bg);
	color: var(--ab-blue);
}

.badge.is-completed {
	background: var(--ab-family-positive-bg);
	color: var(--ab-green);
}

.badge.is-published {
	background: var(--ab-family-positive-bg);
	color: var(--ab-green);
}

.badge.is-abc_rfq_open,
.badge.is-accepted {
	background: var(--ab-family-positive-bg);
	color: var(--ab-green);
}

.badge.is-abc_rfq_awarded {
	background: var(--ab-family-distinction-bg);
	color: var(--ab-teal);
}

.badge.is-abc_rfq_closed {
	background: var(--ab-family-informational-bg);
	color: var(--ab-blue);
}

.badge.is-abc_unpaid {
	background: var(--ab-family-informational-bg);
	color: var(--ab-blue);
}

.badge.is-abc_paid {
	background: var(--ab-family-positive-bg);
	color: var(--ab-green);
}

.badge.is-abc_overdue {
	background: var(--ab-family-negative-bg);
	color: var(--ab-red);
}

.badge.is-hidden {
	background: var(--ab-family-negative-bg);
	color: var(--ab-red);
}

.badge.is-archived {
	background: var(--ab-family-neutral-bg);
	color: var(--ab-muted);
}

.badge.is-cancelled,
.badge.is-abc_rfq_cancelled,
.badge.is-rejected {
	background: var(--ab-family-negative-bg);
	color: var(--ab-red);
}

.badge.is-abc_wo_scheduled,
.badge.is-abc_wo_issued {
	background: var(--ab-family-informational-bg);
	color: var(--ab-blue);
}

.badge.is-abc_wo_in_progress,
.badge.is-abc_wo_on_hold {
	background: var(--ab-family-attention-bg);
	color: var(--ab-accent-dark);
}

.badge.is-abc_wo_completed {
	background: var(--ab-family-positive-bg);
	color: var(--ab-green);
}

.badge.is-abc_wo_cancelled {
	background: var(--ab-family-negative-bg);
	color: var(--ab-red);
}

/* Wave 1: these 4 previously had no .badge.is-* rule at all -- they
   rendered via the bare .badge base default, which already happens to
   be the same teal/distinction color. Made explicit rather than
   accidental so the DISTINCTION family is a real, documented decision
   for "notable/paid-visibility" marks, not an unstated fallback.
   Visual output is unchanged. */
.badge.is-featured,
.badge.is-promoted,
.badge.is-business-verified,
.badge.is-verified {
	background: var(--ab-family-distinction-bg);
	color: var(--ab-teal);
}

.module-link {
	margin-top: auto;
	color: var(--ab-accent-dark);
	font-size: 13px;
	font-weight: 800;
}

/* Step 58.1J2: fixed 3-column tracks left visible dead grid space when a
   module has few real records (a single card, two empty equal-width
   columns) -- looked broken, not "quiet." auto-fill + a capped card
   width only creates as many tracks as there are real cards, so the
   layout looks identical and intentional whether a module holds 1 or
   100 real records; this is a resilience fix for the always-real,
   currently-sparse production data, not a fake-content workaround. */
.directory-grid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(280px, 320px));
	justify-content: start;
	gap: 14px;
}

.profile-card {
	display: flex;
	flex-direction: column;
	gap: 12px;
	min-height: 220px;
	padding: 16px;
	border: 1px solid var(--ab-line);
	border-radius: var(--ab-radius);
	background: var(--ab-surface);
}

/* Design audit: cards in the same directory-grid row already stretch to
   equal height (grid's own default align-items:stretch), but their
   trailing "View/Read/Open ..." link sat wherever the description text
   happened to end -- ragged across a row whenever descriptions differ
   in length. Anchoring it to the bottom of the flex column is the same
   fix already applied to .landing-tile's .module-link. */
.profile-card > a:last-child {
	margin-top: auto;
}

.profile-card h3 {
	margin: 4px 0 0;
	font-size: 17px;
	line-height: 1.25;
	/* Step 58.1H (BL12): same shared root cause as .topbar h1 above --
	   an unbreakable title/identifier inside this flex-column card
	   forces card overflow at narrow widths (proven live on the
	   Tenders/Homepage directory-grid manifestation). */
	overflow-wrap: anywhere;
}

.profile-card p {
	margin: 8px 0 0;
	color: var(--ab-muted);
	font-size: 13px;
	line-height: 1.45;
}

.profile-type {
	color: var(--ab-muted);
	font-size: 11px;
	font-weight: 800;
	text-transform: uppercase;
}

.chip-row {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
}

.chip {
	display: inline-flex;
	align-items: center;
	min-height: 28px;
	padding: 0 9px;
	border: 1px solid rgba(23, 126, 117, .18);
	border-radius: var(--ab-radius);
	background: rgba(23, 126, 117, .08);
	color: var(--ab-teal);
	font-size: 12px;
	font-weight: 800;
}

.compact-facts {
	display: grid;
	grid-template-columns: 82px minmax(0, 1fr);
	gap: 6px 10px;
	margin: 0;
	font-size: 12px;
}

.compact-facts dt {
	color: var(--ab-muted);
	font-weight: 800;
}

.compact-facts dd {
	margin: 0;
	overflow-wrap: anywhere;
}

.profile-copy {
	margin: 0;
	max-width: 780px;
	color: var(--ab-ink);
	line-height: 1.65;
}

/* Step 58.1J2 (Reference-Driven Reconstruction): Marketplace's listing
   card becomes a dark, image-led opportunity object -- self-contained
   token override (like .canvas-dark, but scoped to just this card) so
   .profile-card's shared base/partner-card/tender-card/procurement-card
   are completely unaffected. Real listing photography where it exists
   (.listing-card__image img); a deterministic icon plate, never a fake
   photo, where it does not. */
.listing-card {
	--ab-surface: var(--ab-charcoal-2);
	--ab-line: rgba(245, 243, 238, .14);
	--ab-ink: #f5f3ee;
	--ab-ink-strong: #ffffff;
	--ab-muted: rgba(245, 243, 238, .62);
	/* This card is always dark regardless of the page's own theme (a
	   guest viewing a public listing is NOT in canvas-dark), so it needs
	   its own bright --ab-accent-dark override too, same value as the
	   canvas-dark remap -- the root default (#91451f) only measures
	   2.33:1 against this card's own charcoal-2 background. */
	--ab-accent-dark: #f2a65a;
	min-height: 244px;
	padding: 0;
	overflow: hidden;
	background: var(--ab-charcoal-2);
	border-color: var(--ab-line);
	color: var(--ab-ink);
}

.listing-card > *:not(.listing-card__image) {
	padding-left: 16px;
	padding-right: 16px;
}

.listing-card > *:last-child {
	padding-bottom: 16px;
}

.listing-card > *:first-child:not(.listing-card__image) {
	padding-top: 16px;
}

.listing-card h3 {
	color: var(--ab-ink-strong);
}

/* Real, measured accessibility bug (axe-core, WCAG AA): --ab-accent
   measured under 4.5:1 against this card's own dark background (a
   button-background color used as text-on-dark by mistake) --
   --ab-accent-dark, locally overridden brighter on .listing-card
   itself just above, is the correct token here. */
.listing-card .module-link {
	color: var(--ab-accent-dark);
}

.listing-card__image {
	position: relative;
	width: 100%;
	height: 160px;
	margin-top: 0;
	overflow: hidden;
	background: linear-gradient(160deg, var(--ab-charcoal-2), var(--ab-charcoal));
}

.listing-card__image img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.listing-card__image--placeholder,
.listing-card__image.is-broken {
	display: flex;
	align-items: center;
	justify-content: center;
}

.listing-card__image--placeholder span,
.listing-card__image.is-broken span {
	color: rgba(245, 243, 238, .4);
	font: var(--ab-text-label);
	letter-spacing: .08em;
	text-transform: uppercase;
}

.tender-card {
	min-height: 272px;
}

.procurement-card {
	min-height: 252px;
}

.tender-chip-row {
	margin-top: 14px;
}

.tender-split {
	grid-template-columns: minmax(0, 1fr) minmax(360px, 520px);
}

.procurement-split {
	grid-template-columns: minmax(0, 1fr) minmax(340px, 500px);
}

.finance-split {
	grid-template-columns: minmax(0, 1fr) minmax(320px, 420px);
}

.documents-split {
	grid-template-columns: minmax(0, 1fr) minmax(320px, 420px);
}

.tender-stack,
.owner-bid-list,
.quote-stack,
.payment-stack,
.document-version-stack {
	display: grid;
	gap: 12px;
}

.owner-bid-list {
	margin-top: 18px;
	padding-top: 18px;
	border-top: 1px solid var(--ab-line);
}

/* Step 58.1J2 (Wave 1): AB-D06 Actionable Row. bid-row/task-row/
   notification-row/conversation-row/review-row/review-card were 4
   independently-authored blocks (task-row: 14px gap; notification/
   conversation-row: 18px gap, 16px padding; review-row/card: already
   byte-identical to bid-row) sharing the same visual DNA -- consolidated
   onto bid-row's values (a ~2-4px normalization on 3 of the 4 sites,
   not a new design). Domain-specific inner content (h3 sizing, action
   buttons, severity modifiers below) is untouched at each site. */
.bid-row,
.task-row,
.notification-row,
.conversation-row,
.review-row,
.review-card {
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	gap: 16px;
	padding: 14px;
	border: 1px solid var(--ab-line);
	border-radius: var(--ab-radius);
	background: #fff;
}

/* Step 58.1J2 (Wave 4): the actual root cause of the /tenders/my mobile
   overflow -- BL12's overflow-wrap fix covered .topbar h1 and
   .profile-card h3 but never this third long-unbreakable-token
   surface. A real long token (the disclosed UXAUDIT_581G test tender's
   own title) pushed .bid-row's width past the viewport at 360-393px.
   Same established fix, same shared owner -- covers all 6 AB-D06
   Actionable Row consumers (bid-row/task-row/notification-row/
   conversation-row/review-row/review-card) at once, not a per-page
   patch. */
.bid-row h3 {
	margin: 5px 0 0;
	font-size: 16px;
	line-height: 1.25;
	overflow-wrap: anywhere;
}

.bid-row p {
	margin: 8px 0 0;
	color: var(--ab-muted);
	font-size: 13px;
	line-height: 1.45;
}

.bid-actions,
.document-actions,
.link-row {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: flex-end;
	gap: 8px;
}

.project-metric-grid {
	grid-template-columns: repeat(5, minmax(0, 1fr));
}

.project-row {
	align-items: stretch;
}

.project-facts {
	min-width: 240px;
	align-self: center;
}

.project-split {
	grid-template-columns: minmax(0, 1fr) minmax(360px, 560px);
}

.project-split > .form-panel {
	grid-column: 2;
	grid-row: 1 / span 2;
}

.project-actions {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
	margin-top: 18px;
	padding-top: 18px;
	border-top: 1px solid var(--ab-line);
}

.work-order-row {
	align-items: stretch;
}

.work-order-facts {
	min-width: 240px;
	align-self: center;
}

.work-order-create-grid,
.work-order-show-grid {
	grid-template-columns: minmax(0, 1fr) minmax(360px, 560px);
}

.work-order-panel .work-order-card {
	align-items: center;
}

/* Shell now shared via AB-D06 above; only this scope's own child
   selectors remain here. */

.task-row h3 {
	margin: 5px 0 10px;
	font-size: 15px;
	line-height: 1.25;
}

.task-row.is-done h3 {
	color: var(--ab-muted);
	text-decoration: line-through;
}

.review-action {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 14px;
	margin-top: 18px;
	padding: 14px;
	border: 1px solid var(--ab-line);
	border-radius: var(--ab-radius);
	background: #faf9f6;
}

.review-action span {
	display: block;
	color: var(--ab-muted);
	font-size: 11px;
	font-weight: 800;
	text-transform: uppercase;
}

.review-action strong {
	display: block;
	margin-top: 4px;
	font-size: 15px;
}

.review-action p {
	margin: 4px 0 0;
	color: var(--ab-muted);
	font-size: 13px;
	line-height: 1.4;
}

.review-action.is-available {
	border-color: rgba(35, 122, 79, .28);
	background: rgba(35, 122, 79, .07);
}

.review-action.is-complete {
	border-color: rgba(49, 95, 147, .28);
	background: rgba(49, 95, 147, .07);
}

.review-stack,
.rating-selects {
	display: grid;
	gap: 12px;
}

/* Shell now shared via AB-D06 above (was already byte-identical to
   bid-row). Only this scope's own child selectors remain here. */

.review-row h3,
.review-card h3 {
	margin: 6px 0 0;
	font-size: 16px;
	line-height: 1.25;
}

.review-row p,
.review-card p {
	margin: 8px 0 0;
	color: var(--ab-muted);
	font-size: 13px;
	line-height: 1.45;
}

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

.rating-score {
	padding: 12px;
	border: 1px solid var(--ab-line);
	border-radius: var(--ab-radius);
	background: #fff;
}

.rating-score span {
	display: block;
	color: var(--ab-muted);
	font-size: 12px;
	font-weight: 800;
	text-transform: uppercase;
}

.rating-score strong {
	display: block;
	margin-top: 6px;
	font-size: 22px;
}

.search-filter-bar {
	grid-template-columns: minmax(220px, 1.8fr) minmax(150px, 1fr) minmax(160px, 1fr) auto;
}

/* Step 58.1J2 (Premium Visual Pass): Search's own object is the search
   box itself -- it must dominate the page, with the index counters
   (below, .metric-grid--compact) reduced to secondary reference
   information. Scoped to Search only. */
.search-hero-form-panel {
	margin-top: -18px;
	border-color: var(--ab-accent);
	box-shadow: var(--ab-shadow-lg);
}

.search-hero-input input {
	min-height: 52px;
	font-size: 16px;
}

.search-hero-input span {
	font: var(--ab-text-h3);
	color: var(--ab-ink-strong);
	text-transform: none;
}

.search-metric-grid {
	grid-template-columns: repeat(6, minmax(0, 1fr));
}

.knowledge-metric-grid {
	grid-template-columns: repeat(4, minmax(0, 1fr));
}

.knowledge-filter-bar {
	grid-template-columns: minmax(220px, 1.4fr) minmax(180px, 1fr) auto;
}

.knowledge-card {
	min-height: 252px;
}

.knowledge-form,
.stack-form {
	display: grid;
	gap: 16px;
}

.knowledge-form label,
.knowledge-topic-fieldset {
	display: grid;
	gap: 7px;
}

.knowledge-form label span,
.knowledge-topic-fieldset legend {
	color: var(--ab-muted);
	font-size: 12px;
	font-weight: 800;
}

/* Step 58.1J2 (Wave 1): AB-FM01 Standard Control. This exact
   declaration (padding/height) is intentionally identical to
   .estimate-form and .account-form's input/select rules below, and to
   .filter-form's (which already used these values) -- four
   independently-authored, near-duplicate rule blocks consolidated onto
   one locked standard (40px / 8px 10px) rather than the prior 42px /
   9px 11px three of them used. Kept as separate per-scope selectors
   (not merged into one selector list) so each form's own textarea
   min-height exception (e.g. Estimate's 112px) stays local and
   unambiguous. */
.knowledge-form input,
.knowledge-form select,
.knowledge-form textarea {
	width: 100%;
	padding: 8px 10px;
	border: 1px solid var(--ab-line);
	border-radius: var(--ab-radius);
	background: #fff;
	color: var(--ab-ink);
	font: inherit;
}

.knowledge-form input,
.knowledge-form select {
	min-height: 40px;
}

.knowledge-form textarea {
	resize: vertical;
}

.knowledge-topic-fieldset {
	margin: 0;
	padding: 12px;
	border: 1px solid var(--ab-line);
	border-radius: var(--ab-radius);
}

.checkbox-grid {
	display: grid;
	grid-template-columns: repeat(4, minmax(140px, 1fr));
	gap: 8px;
}

.checkbox-grid label {
	display: flex;
	align-items: center;
	gap: 8px;
	min-height: 34px;
	font-weight: 700;
}

.checkbox-grid input {
	width: auto;
	min-height: 0;
}

.knowledge-article .panel-head p {
	margin: 5px 0 0;
	color: var(--ab-muted);
	font-size: 13px;
}

.knowledge-topic-row {
	margin-bottom: 16px;
}

.knowledge-content {
	max-width: 860px;
	border-top: 1px solid var(--ab-line);
	padding-top: 18px;
}

.knowledge-content p {
	margin: 0 0 16px;
	color: var(--ab-ink);
	font-size: 15px;
	line-height: 1.72;
}

.search-results {
	display: grid;
	gap: 12px;
}

.search-result {
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	gap: 18px;
	padding: 16px;
	border: 1px solid var(--ab-line);
	border-radius: var(--ab-radius);
	background: #fff;
}

/* Step 58.1J: the same real, live-proven gap as BL12 (.topbar h1,
   .profile-card h3) -- a flex item's text content defaults to
   min-width:auto, so a long real result title forces this row wider
   than its container at mobile widths. min-width:0 lets it actually
   shrink; overflow-wrap lets an unbreakable token wrap instead of
   forcing overflow. */
.search-result > div {
	min-width: 0;
}

.search-result h3 {
	margin: 5px 0 0;
	font-size: 17px;
	line-height: 1.25;
	overflow-wrap: anywhere;
}

.search-result p {
	margin: 8px 0 0;
	color: var(--ab-muted);
	font-size: 13px;
	line-height: 1.45;
}

.conversation-list,
.notification-list,
.message-list {
	display: grid;
	gap: 12px;
}

/* Shell now shared via AB-D06 above (was: 18px gap, 16px padding --
   normalized to the shared 16/14 values). Only this scope's own child
   selectors remain here. */

/* Communication mobile closure fix (Phase 1.1): a flex child defaults to
   min-width:auto, so a long unbroken token (URL, etc.) in the preview
   text below grew this child -- and the whole row/page -- wider than the
   viewport even though overflow-wrap was already set on the text itself.
   min-width:0 lets it shrink to the row's actual width first. */
.conversation-row > div,
.notification-row > div {
	min-width: 0;
}

.conversation-row h3,
.notification-row h3 {
	margin: 5px 0 0;
	font-size: 17px;
	line-height: 1.25;
}

.conversation-row p,
.notification-row p {
	margin: 8px 0 0;
	color: var(--ab-muted);
	font-size: 13px;
	line-height: 1.45;
	overflow-wrap: anywhere;
}

.message-list {
	/* Same min-width:auto grid-item issue as above, for message bubbles. */
	min-width: 0;
}

.message-bubble {
	max-width: 780px;
	min-width: 0;
	padding: 14px;
	border: 1px solid var(--ab-line);
	border-radius: var(--ab-radius);
	background: #fff;
}

.message-bubble.is-own {
	margin-left: auto;
	border-color: rgba(23, 126, 117, .28);
	background: rgba(23, 126, 117, .07);
}

.message-bubble p {
	margin: 8px 0 0;
	line-height: 1.55;
	overflow-wrap: anywhere;
}

.message-meta,
.notification-actions {
	display: flex;
	align-items: center;
	gap: 10px;
}

.message-meta span {
	color: var(--ab-muted);
	font-size: 12px;
}

.notification-actions {
	flex-wrap: wrap;
	justify-content: flex-end;
}

.filter-bar {
	display: grid;
	grid-template-columns: minmax(220px, 1.4fr) repeat(auto-fit, minmax(118px, 1fr));
	gap: 12px;
	align-items: end;
}

.procurement-filter-bar {
	grid-template-columns: minmax(220px, 1.4fr) minmax(180px, 1fr) auto;
}

.finance-filter-bar {
	grid-template-columns: minmax(180px, 260px) auto;
	margin-top: 12px;
}

.finance-tabs {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
}

.documents-filter-bar {
	grid-template-columns: minmax(220px, 1.4fr) minmax(180px, 1fr) auto;
	margin-top: 12px;
}

.document-upload-grid {
	grid-template-columns: minmax(220px, 1.4fr) minmax(180px, 1fr);
}

.estimate-form {
	display: grid;
	gap: 18px;
}

.estimate-form-grid {
	display: grid;
	grid-template-columns: repeat(3, minmax(180px, 1fr));
	gap: 12px;
}

.estimate-form-grid label,
.estimate-form > label,
.estimate-line-grid label,
.estimate-adjustment-grid label {
	display: grid;
	gap: 7px;
	font-weight: 700;
}

.estimate-form-grid label span,
.estimate-form > label span,
.estimate-line-grid label span,
.estimate-adjustment-grid label span {
	color: var(--ab-muted);
	font-size: 12px;
}

/* AB-FM01 Standard Control -- see .knowledge-form's identical comment. */
.estimate-form input,
.estimate-form select,
.estimate-form textarea {
	width: 100%;
	padding: 8px 10px;
	border: 1px solid var(--ab-line);
	border-radius: var(--ab-radius);
	background: #fff;
	color: var(--ab-ink);
	font: inherit;
}

.estimate-form input,
.estimate-form select {
	min-height: 40px;
}

/* AB-FM02 Textarea, Long-form variant (locked LONG_FORM_TEXTAREA_MIN_
   HEIGHT=112px) -- Estimate's scope/note fields are long-form content;
   kept as this domain's own documented exception, not forced to the
   96px standard. */
.estimate-form textarea {
	min-height: 112px;
	resize: vertical;
}

.estimate-form input:focus,
.estimate-form select:focus,
.estimate-form textarea:focus {
	border-color: var(--ab-teal);
	outline: 3px solid rgba(23, 126, 117, .14);
}

.estimate-lines {
	display: grid;
	gap: 12px;
	padding-top: 16px;
	border-top: 1px solid var(--ab-line);
}

.estimate-lines h2 {
	margin: 0;
	font-size: 17px;
}

.estimate-line-grid {
	display: grid;
	grid-template-columns: minmax(220px, 1.7fr) minmax(120px, .8fr) minmax(110px, .7fr) minmax(110px, .7fr) minmax(180px, 1.2fr);
	gap: 10px;
	align-items: end;
}

.estimate-adjustment-grid {
	display: grid;
	grid-template-columns: minmax(140px, .9fr) minmax(180px, 1.4fr) minmax(140px, .9fr) minmax(120px, .8fr);
	gap: 10px;
	align-items: end;
}

.estimate-line-row {
	display: grid;
	gap: 10px;
	padding: 14px;
	border: 1px solid var(--ab-line);
	border-radius: var(--ab-radius);
	background: var(--ab-surface);
}

.estimate-line-summary {
	display: flex;
	align-items: flex-start;
	gap: 10px;
	cursor: pointer;
}

.estimate-line-summary-main {
	flex: 1;
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: 6px 10px;
	min-width: 0;
}

.estimate-line-summary-main strong {
	flex-basis: 100%;
	font-size: 15px;
}

.estimate-line-summary-meta {
	color: var(--ab-muted);
	font-size: 13px;
}

.estimate-line-summary-amount {
	font-weight: 800;
	font-size: 13px;
}

.estimate-line-summary-actions {
	flex-shrink: 0;
	display: flex;
	gap: 10px;
	white-space: nowrap;
}

/* Phase 2.5 internal Estimate View: mobile-safe read-only line blocks,
   distinct from .estimate-line-summary (the editor's tap-to-edit row,
   which stays untouched per the "do not redesign the editor" rule). */
.estimate-view-line {
	padding: 10px 0;
	border-bottom: 1px solid var(--ab-border);
}

.estimate-view-line:last-child {
	border-bottom: none;
}

.estimate-view-line-head {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: 6px 8px;
}

.estimate-view-line-name {
	font-weight: 700;
	font-size: 15px;
}

.estimate-view-line-meta {
	display: flex;
	justify-content: space-between;
	align-items: baseline;
	gap: 10px;
	margin-top: 4px;
}

.estimate-view-line-description {
	margin: 6px 0 0;
	color: var(--ab-muted);
	font-size: 13px;
}

.estimate-total-banner {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: 12px;
	padding: 14px 16px;
	margin-bottom: 16px;
	background: var(--ab-surface);
	border: 1px solid var(--ab-border);
	border-radius: 12px;
}

.estimate-total-banner strong {
	display: block;
	font-size: 26px;
}

.detail-list {
	display: grid;
	gap: 8px;
}

.detail-list > div {
	display: flex;
	justify-content: space-between;
	gap: 12px;
	padding: 6px 0;
	border-bottom: 1px solid var(--ab-border);
	font-size: 14px;
}

.detail-list > div:last-child {
	border-bottom: none;
}

.detail-list dt {
	color: var(--ab-muted);
}

.detail-list dd {
	margin: 0;
	text-align: right;
	font-weight: 600;
}

.estimate-line-editor {
	display: grid;
	gap: 10px;
	margin-top: 10px;
	padding-top: 10px;
	border-top: 1px dashed var(--ab-line);
}

/* Catalog picker v2: a contained dialog using compact progressive
   disclosure (Category -> Service -> Work Item) instead of rendering all
   70 categories or a category's 60-100 work items at once. Mobile shows
   one step at a time with a breadcrumb; a wide viewport shows Category/
   Service/Work Item as three simultaneous columns. */
.catalog-trigger-row {
	display: flex;
	align-items: center;
	gap: 12px;
	flex-wrap: wrap;
}

.catalog-dialog {
	width: min(720px, 94vw);
	max-height: 88vh;
	padding: 0;
	border: 1px solid var(--ab-line);
	border-radius: var(--ab-radius);
	background: var(--ab-surface);
	color: var(--ab-ink);
}

.catalog-dialog::backdrop {
	background: rgba(22, 24, 29, .45);
}

.catalog-dialog-inner {
	display: grid;
	grid-template-rows: auto auto 1fr;
	max-height: 88vh;
}

.catalog-dialog-head {
	display: flex;
	align-items: center;
	gap: 12px;
	padding: 16px 16px 0;
}

.catalog-dialog-head h3 {
	flex: 1;
	margin: 0;
	font-size: 17px;
}

.catalog-search-row {
	padding: 12px 16px;
	border-bottom: 1px solid var(--ab-line);
}

.catalog-search-row input {
	min-height: 44px;
	padding: 9px 11px;
	border: 1px solid var(--ab-line);
	border-radius: var(--ab-radius);
	background: #fff;
	font: inherit;
	width: 100%;
}

.catalog-breadcrumb {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 6px;
	padding: 0 16px 12px;
	color: var(--ab-muted);
	font-size: 12px;
	font-weight: 700;
	text-transform: uppercase;
}

.catalog-breadcrumb button {
	border: 0;
	background: none;
	padding: 0;
	color: var(--ab-teal);
	font: inherit;
	font-weight: 800;
	cursor: pointer;
}

.catalog-body {
	overflow-y: auto;
	padding: 0 16px 16px;
}

.catalog-recent {
	padding: 12px 16px 0;
}

.catalog-recent-title {
	margin: 0 0 8px;
	color: var(--ab-muted);
	font-size: 12px;
	font-weight: 800;
	text-transform: uppercase;
}

.catalog-domain-group {
	border-bottom: 1px solid var(--ab-line);
}

.catalog-domain-group summary {
	display: flex;
	align-items: center;
	justify-content: space-between;
	min-height: 44px;
	padding: 10px 4px;
	font-weight: 800;
	cursor: pointer;
	list-style: none;
}

.catalog-domain-group summary::-webkit-details-marker {
	display: none;
}

.catalog-domain-group[open] summary {
	color: var(--ab-teal);
}

.catalog-row-list {
	display: grid;
	gap: 6px;
	padding: 2px 0 12px;
}

.catalog-row {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 10px;
	min-height: 44px;
	padding: 10px 12px;
	border: 1px solid var(--ab-line);
	border-radius: var(--ab-radius);
	background: #fff;
	color: var(--ab-ink);
	font: inherit;
	font-size: 14px;
	font-weight: 600;
	text-align: left;
	cursor: pointer;
}

.catalog-row:hover {
	border-color: var(--ab-teal);
}

.catalog-row:focus-visible {
	outline: 3px solid rgba(23, 126, 117, .45);
	outline-offset: 2px;
}

.catalog-row-unit {
	flex-shrink: 0;
	color: var(--ab-muted);
	font-size: 11px;
	font-weight: 800;
	text-transform: uppercase;
}

.catalog-row-arrow {
	flex-shrink: 0;
	color: var(--ab-muted);
}

.catalog-service-chips {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
	padding: 2px 0 14px;
}

.catalog-service-chip {
	min-height: 40px;
	padding: 0 14px;
	border: 1px solid rgba(23, 126, 117, .18);
	border-radius: var(--ab-radius-pill);
	background: rgba(23, 126, 117, .08);
	color: var(--ab-teal);
	font: inherit;
	font-size: 13px;
	font-weight: 800;
	cursor: pointer;
}

.catalog-service-chip:hover,
.catalog-service-chip[aria-current="true"] {
	background: var(--ab-teal);
	color: #fff;
}

.catalog-step {
	display: grid;
	gap: 4px;
}

.catalog-step-title {
	margin: 0;
	padding-top: 8px;
	color: var(--ab-muted);
	font-size: 12px;
	font-weight: 800;
	text-transform: uppercase;
}

.catalog-confirm-meta {
	margin: 0 0 12px;
	color: var(--ab-muted);
	font-size: 13px;
	line-height: 1.5;
}

@media (min-width: 900px) {
	.catalog-dialog {
		width: min(1040px, 94vw);
	}

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

	.catalog-columns .catalog-step[hidden] {
		display: grid !important;
	}

	.catalog-columns .catalog-step {
		max-height: 46vh;
		overflow-y: auto;
		padding-right: 4px;
		border-right: 1px solid var(--ab-line);
	}

	.catalog-columns .catalog-step:last-child {
		border-right: 0;
	}
}

.custom-item-details {
	border: 1px dashed var(--ab-line);
	border-radius: var(--ab-radius);
	padding: 12px 14px;
}

.custom-item-details summary {
	cursor: pointer;
	font-weight: 700;
	color: var(--ab-accent-dark);
}

.custom-item-details[open] summary {
	margin-bottom: 12px;
}

.estimates-filter-bar {
	grid-template-columns: minmax(160px, .8fr) minmax(180px, 1fr) minmax(220px, 1.2fr) auto;
}

.filter-bar label {
	display: grid;
	gap: 7px;
	font-weight: 700;
}

.filter-bar label span {
	font-size: 13px;
}

.filter-bar input,
.filter-bar select {
	width: 100%;
	min-height: 40px;
	padding: 8px 10px;
	border: 1px solid var(--ab-line);
	border-radius: var(--ab-radius);
	background: #fff;
	color: var(--ab-ink);
	font: inherit;
}

.filter-form {
	display: grid;
	grid-template-columns: minmax(180px, 1fr) minmax(180px, 1fr) auto;
	gap: 12px;
	align-items: end;
}

.filter-form label {
	display: grid;
	gap: 7px;
	font-weight: 700;
}

.filter-form label span {
	font-size: 13px;
}

/* AB-FM01 Standard Control -- this scope's values are the ones the
   other three (knowledge/estimate/account-form) now match. */
.filter-form input,
.filter-form select {
	width: 100%;
	min-height: 40px;
	padding: 8px 10px;
	border: 1px solid var(--ab-line);
	border-radius: var(--ab-radius);
	background: #fff;
	color: var(--ab-ink);
	font: inherit;
}

.filter-actions {
	display: flex;
	align-items: center;
	gap: 10px;
	min-height: 40px;
}

.split-panels {
	display: grid;
	grid-template-columns: minmax(0, 1fr) minmax(320px, 460px);
	gap: 18px;
	align-items: start;
}

.favorite-form {
	margin-top: 16px;
}

.key-value {
	display: grid;
	grid-template-columns: 180px minmax(0, 1fr);
	gap: 10px 18px;
	margin: 0;
}

.key-value dt {
	color: var(--ab-muted);
	font-weight: 700;
}

.key-value dd {
	margin: 0;
	overflow-wrap: anywhere;
}

.empty {
	padding: 18px;
	border: 1px dashed var(--ab-line);
	border-radius: var(--ab-radius);
	color: var(--ab-muted);
}

/* Step 58.1J: a richer empty state -- differentiates WHY nothing is
   shown (true empty dataset / no filter results / auth required) from
   the plain one-line .empty, and always gives the visitor a real next
   action instead of a dead end. One shared owner for every public
   directory (Marketplace/Tenders/Partners and future consumers), never
   a per-page variant. */
.empty-state {
	padding: var(--ab-space-4);
	border: 1px dashed var(--ab-line);
	border-radius: var(--ab-radius);
	background: var(--ab-surface-2);
}

.empty-state h3 {
	margin: 0 0 6px;
	font: var(--ab-text-h3);
}

.empty-state p {
	margin: 0 0 var(--ab-space-2);
	color: var(--ab-muted);
	max-width: 520px;
}

.empty-state .empty-state-actions {
	display: flex;
	flex-wrap: wrap;
	gap: 10px;
	margin-top: var(--ab-space-2);
}

.empty p {
	margin: 0 0 10px;
}

.empty p:last-child {
	margin-bottom: 0;
}

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

.role-card {
	display: flex;
	flex-direction: column;
	gap: 10px;
	min-height: 160px;
	padding: 16px;
	border: 1px solid var(--ab-line);
	border-radius: var(--ab-radius);
	background: var(--ab-surface);
	/* Self-contained text color: this card previously only ever sat on a
	   light-background section and relied on inheriting the page's
	   default ink color. Now that role-grid also renders inside the dark
	   .landing-cta-band, it must reset its own color rather than inherit
	   the band's near-white text -- otherwise "Client / Owner" etc. render
	   as pale cream-on-white, unreadable (real bug, caught in a live
	   screenshot review, not theoretical). */
	color: var(--ab-ink);
	box-shadow: var(--ab-shadow);
}

.role-card:hover {
	border-color: rgba(198, 95, 40, .48);
}

.role-code {
	color: var(--ab-muted);
	font-size: 11px;
	font-weight: 800;
	text-transform: uppercase;
}

.role-card strong {
	font-size: 18px;
	line-height: 1.25;
}

.role-action {
	margin-top: auto;
	color: var(--ab-accent-dark);
	font-size: 13px;
	font-weight: 800;
}

/* Shell (border/radius/background/shadow) now shared with .panel above.
   Only this scope's own distinct values remain here. */
.form-panel {
	max-width: 760px;
	padding: 18px;
}

/* Step 58.1J2 (Section 16): canonical auth-entry shell. Additive only --
   .form-panel above is completely unchanged, so any other page still
   using a bare .form-panel (Contact Seller, Bid, Quote, etc.) is
   unaffected. Used only by Login/Register/Forgot/Reset via
   partials/_auth-aside.php. */
.auth-shell {
	display: grid;
	grid-template-columns: minmax(0, 1fr) minmax(280px, 360px);
	gap: 20px;
	align-items: start;
}

.auth-shell .form-panel {
	max-width: none;
}

.auth-aside {
	position: relative;
	padding: 32px 26px;
	border-radius: var(--ab-radius-lg);
	/* Real, CC0-licensed editorial construction photography (see
	   public/assets/img/CREDITS.txt) -- decorative atmosphere only. */
	background:
		linear-gradient(190deg, rgba(20, 23, 28, .88), rgba(22, 24, 30, .96) 70%),
		repeating-linear-gradient(90deg, var(--ab-hero-line) 0, var(--ab-hero-line) 1px, transparent 1px, transparent 64px),
		url('/assets/img/auth-crane.jpg');
	background-size: auto, auto, cover;
	background-position: center, center, center 35%;
	color: #f7f4ef;
	overflow: hidden;
}

.auth-aside > * {
	position: relative;
}

.auth-aside-logo {
	display: block;
	width: 140px;
	height: 140px;
	border-radius: var(--ab-radius-lg);
}

.auth-aside h2 {
	margin: 16px 0 10px;
	font: var(--ab-text-h2);
}

.auth-aside p {
	margin: 0 0 16px;
	color: rgba(247, 244, 239, .78);
	font-size: 14px;
	line-height: 1.55;
}

.auth-aside-list {
	margin: 0;
	padding: 0;
	list-style: none;
	display: flex;
	flex-direction: column;
	gap: 10px;
}

.auth-aside-list li {
	padding-left: 18px;
	position: relative;
	color: rgba(247, 244, 239, .88);
	font-size: 13px;
}

.auth-aside-list li::before {
	content: '';
	position: absolute;
	left: 0;
	top: 7px;
	width: 8px;
	height: 8px;
	border-radius: 50%;
	background: var(--ab-teal);
}

@media (max-width: 900px) {
	.auth-shell {
		grid-template-columns: minmax(0, 1fr);
	}

	.auth-aside {
		order: 2;
	}
}

/* Step 58.1I's original charcoal-brand-surface rationale still holds (a
   visitor and a signed-in user share one brand surface color). Redesign
   pass 9: no longer bleeds edge-to-edge of .main via negative margins
   -- .landing-page centers everything in a 1160px reading column, so
   the hero gets rounded corners on all four sides. Redesign pass 11:
   the product screenshot (originally a split layout, copy left/product
   right) is gone entirely -- a static dashboard image gave a tired
   visitor nothing to act on -- centered on the message and the one
   action instead. */
/* Redesign pass 18: the whole page read as one undifferentiated flat
   black canvas top to bottom (a real critique, not a hypothetical one)
   -- a soft radial warm glow gives the hero real depth without
   introducing a second loud color or a literal gradient banner. Same
   treatment closes the page on .landing-final-cta below, bookending
   it; the sections between stay plain/raised in their own established
   rhythm (.panel already lifts to --ab-charcoal-2 under canvas-dark). */
.landing-hero {
	padding: var(--ab-space-6) var(--ab-space-5);
	background:
		radial-gradient(ellipse 900px 420px at 50% -15%, rgba(198, 95, 40, .16), transparent 65%),
		var(--ab-charcoal);
	color: #f7f4ef;
	border-radius: var(--ab-radius-lg);
	text-align: center;
}

.landing-hero-copy {
	max-width: 640px;
	margin: 0 auto;
}

.landing-hero h1 {
	font: var(--ab-text-display);
	font-size: 46px;
	/* Craft detail: large display type reads tighter and more considered
	   with slightly negative tracking -- default letter-spacing at 54px
	   leaves visibly loose gaps between characters (Refactoring UI). */
	letter-spacing: -0.01em;
	color: #fff;
}

.landing-kicker {
	font: var(--ab-text-label);
	letter-spacing: 0.12em;
	color: var(--ab-accent-dark);
}

.landing-lead {
	margin-top: var(--ab-space-2);
	font-size: 19px;
	line-height: 1.5;
	color: rgba(247, 244, 239, .82);
	/* Typographic craft: without this, the line breaks wherever the
	   browser's greedy wrap happens to fall. text-wrap:balance
	   distributes lines evenly; unsupported browsers just keep the
	   previous (acceptable) greedy wrap -- pure progressive enhancement. */
	text-wrap: balance;
}

/* "Why trust" gets one honest line in the hero itself, not a boxed
   badge or a bank of decorative KPI cards -- the real product
   screenshot to its right is already doing the main credibility work. */
.landing-hero-trust {
	margin-top: var(--ab-space-3);
	color: rgba(247, 244, 239, .68);
	font-size: 14px;
}

.landing-hero-trust strong {
	color: #fff;
}

.landing-cta-row {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: 12px;
	margin-top: var(--ab-space-2);
}

/* A visual tier for the two revenue-relevant discovery sections
   (Marketplace, Tenders) -- a quiet tint + accent top border, not a
   louder card, so they read as more central without competing with the
   hero or looking decorative. */
.landing-section--primary {
	background: var(--ab-surface-2);
	border-top: 3px solid var(--ab-accent);
}

/* Redesign pass 8: 530 real, named partner companies is still the
   platform's strongest proof point, but a full 12-logo wall was too
   much page for one idea (the earlier redesign's own wall is now the
   compact version below) -- one true number, the categories it spans,
   six representative logos, a link to the full directory. */
.landing-partners-compact {
	padding: var(--ab-space-6) var(--ab-space-5);
}

.landing-partners-compact .panel-head {
	margin-bottom: 8px;
	align-items: flex-end;
}

.landing-partners-compact .text-link {
	font-size: 15px;
	font-weight: 800;
}

/* Real, measured bug: at mobile widths the 34px "530 Verified Partners
   Across Canada" heading and "Explore the network" fight for space in
   the same row (.panel-head's shared flex-row layout), wrapping the
   heading itself to 3 cramped lines. Stacking here doesn't touch
   .panel-head itself (used across the whole app; this is scoped to
   just this landing section). */
@media (max-width: 500px) {
	.landing-partners-compact .panel-head {
		flex-direction: column;
		align-items: flex-start;
		gap: 4px;
	}
}

.landing-partners-categories {
	margin: 0 0 var(--ab-space-4);
	color: var(--ab-muted);
	font-size: 16px;
}

.landing-partner-logo-grid {
	display: grid;
	grid-template-columns: repeat(6, minmax(0, 1fr));
	gap: var(--ab-space-3);
}

.landing-partner-logo {
	display: flex;
	align-items: center;
	justify-content: center;
	height: 84px;
	padding: var(--ab-space-3);
	border: 1px solid var(--ab-line);
	border-radius: var(--ab-radius);
	background: #fff;
	transition: transform .15s ease, box-shadow .15s ease;
}

.landing-partner-logo:hover {
	transform: translateY(-2px);
	box-shadow: 0 8px 20px rgba(0, 0, 0, .28);
}

.landing-partner-logo img {
	max-width: 100%;
	max-height: 100%;
	object-fit: contain;
}

@media (max-width: 900px) {
	.landing-partner-logo-grid {
		grid-template-columns: repeat(3, minmax(0, 1fr));
	}
}

/* Final conversion: the page's second and last CTA, deliberately
   plain -- no card, no band, just the close of the argument the whole
   page just made. Given real weight (large heading, large button) so
   it reads as a deliberate closing beat, not a footnote. */
/* Bookends the hero -- same warm radial glow on its own dark card,
   closing the page with the same visual weight it opened with, rather
   than trailing off as a thin-bordered plain section. */
.landing-final-cta {
	padding: var(--ab-space-6) var(--ab-space-5);
	background:
		radial-gradient(ellipse 900px 420px at 50% 115%, rgba(198, 95, 40, .16), transparent 65%),
		var(--ab-charcoal);
	border-radius: var(--ab-radius-lg);
	text-align: center;
}

.landing-final-cta h2 {
	margin: 0 auto 10px;
}

.landing-final-cta p {
	margin: 0 auto var(--ab-space-4);
	max-width: 440px;
	color: var(--ab-muted);
	font-size: 17px;
}

.landing-final-cta .button {
	display: inline-flex;
	padding: 14px 32px;
	font-size: 16px;
}

.landing-final-cta-secondary {
	margin: var(--ab-space-4) 0 0;
	font-size: 14px;
}

.landing-final-cta-secondary a {
	color: var(--ab-muted);
}

.landing-final-cta-secondary a:hover {
	color: var(--ab-accent-dark);
}

/* Redesign pass 10: a tired visitor picks faster from four short,
   side-by-side options than from four cards asking to be read first
   (Hick's Law) -- one line each, name + a few words, no separate
   paragraph. */
.landing-role-pills {
	display: flex;
	flex-wrap: wrap;
	gap: var(--ab-space-3);
	margin-top: var(--ab-space-4);
}

.landing-role-pill {
	flex: 1 1 200px;
	display: flex;
	flex-direction: column;
	gap: 2px;
	padding: var(--ab-space-3) var(--ab-space-4);
	border: 1px solid var(--ab-line);
	border-radius: var(--ab-radius);
	color: var(--ab-ink);
	transition: transform .15s ease, border-color .15s ease, box-shadow .15s ease;
}


.landing-role-pill strong {
	font-size: 16px;
	color: var(--ab-ink-strong);
}

.landing-role-pill span {
	color: var(--ab-muted);
	font-size: 13px;
}

.landing-role-pill:hover {
	border-color: var(--ab-accent);
	transform: translateY(-2px);
	box-shadow: 0 8px 20px rgba(0, 0, 0, .28);
}

.landing-role-pill:hover strong {
	color: var(--ab-accent-dark);
}

/* Redesign pass 13: the mid-page sticky CTA strip is gone -- the new
   .marketing-topbar (shared layout, $minimalHeader) is itself sticky
   and holds the Login/Get Started pair from first paint onward, so a
   second, separate sticky element further down the page was just a
   duplicate mechanism solving an already-solved problem. Redesign pass
   15: the hero's own copy of that same pair (immediately below the
   topbar, zero scroll between them) was removed for the same reason --
   a real reported complaint ("too many Get Started buttons"), not a
   hypothetical one. */

.partner-logo {
	width: 64px;
	height: 64px;
	object-fit: contain;
	border-radius: var(--ab-radius);
	border: 1px solid var(--ab-line);
	background: #fff;
}

.partner-logo-thumb {
	width: 100%;
	height: 88px;
	object-fit: contain;
	border-radius: var(--ab-radius);
	border: 1px solid var(--ab-line);
	background: #fff;
	margin-bottom: 10px;
}

.image-grid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
	gap: 12px;
	margin-bottom: 14px;
}

.image-tile {
	position: relative;
	border: 1px solid var(--ab-line);
	border-radius: var(--ab-radius);
	overflow: hidden;
	background: var(--ab-surface);
}

.image-tile img {
	display: block;
	width: 100%;
	height: 140px;
	object-fit: cover;
}

/* Real, confirmed bug (live screenshot, not assumed): the marketplace
   listing detail page's main/hero photo (.listing-gallery__main) reuses
   .image-tile for its border/radius, which meant it also inherited
   .image-tile img's 140px thumbnail height -- full page width but only
   140px tall, cropping most real listing photos into a thin band (a
   clock face showing only a sliver through the middle). Compound
   selector for guaranteed precedence over the thumbnail rule
   regardless of source order, same pattern as the earlier
   .entry-circle.landing-capability-icon fix. */
.image-tile.listing-gallery__main img {
	height: 360px;
}

.listing-gallery__placeholder {
	padding: 32px;
	border: 1px dashed var(--ab-line);
	border-radius: var(--ab-radius);
	color: var(--ab-muted);
	text-align: center;
	margin-bottom: 14px;
}

.image-placeholder {
	display: flex;
	align-items: center;
	justify-content: center;
	height: 100%;
	color: var(--ab-muted);
	font-size: 12px;
	text-align: center;
	background: var(--ab-surface-2);
}

@media (max-width: 700px) {
	.image-tile.listing-gallery__main img {
		height: 220px;
	}
}

.image-tile form {
	position: absolute;
	top: 6px;
	right: 6px;
}

.account-form {
	display: grid;
	gap: 14px;
}

.stacked-form {
	padding-top: 16px;
	border-top: 1px solid var(--ab-line);
}

.stacked-form:first-of-type {
	padding-top: 0;
	border-top: 0;
}

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

.account-form label {
	display: grid;
	gap: 7px;
	color: var(--ab-ink);
	font-weight: 700;
}

.account-form label span {
	font-size: 13px;
}

/* AB-FM01 Standard Control -- see .knowledge-form's identical comment. */
.account-form input,
.account-form select,
.account-form textarea {
	width: 100%;
	padding: 8px 10px;
	border: 1px solid var(--ab-line);
	border-radius: var(--ab-radius);
	background: #fff;
	color: var(--ab-ink);
	font: inherit;
}

.account-form input,
.account-form select {
	min-height: 40px;
}

/* Account's own bio/about textarea is genuine long-form content -- kept
   at the locked LONG_FORM_TEXTAREA_MIN_HEIGHT (112px), not forced to
   96px. */
.account-form textarea {
	min-height: 112px;
	resize: vertical;
}

.account-form input:focus,
.account-form select:focus,
.account-form textarea:focus {
	border-color: var(--ab-teal);
	outline: 3px solid rgba(23, 126, 117, .14);
}

.account-form .checkbox-label {
	display: flex;
	align-items: center;
	gap: 10px;
	font-weight: 700;
}

.account-form .checkbox-label input {
	width: auto;
	min-height: 0;
}

.form-actions {
	display: flex;
	align-items: center;
	gap: 14px;
	margin-top: 4px;
}

/* ------------------------------------------------------------------
   ACTION HIERARCHY (Step 58.1B-3): the canonical semantic mapping for
   every action-role decision going forward -- do not invent a new
   variant before checking this list maps to nothing existing.
     PRIMARY     -> .button
     SECONDARY   -> .button-secondary  (add .button-compact when paired
                    alongside a PRIMARY action, so it never competes)
     TERTIARY    -> .text-link
     DESTRUCTIVE -> .button-danger     (never combined with .button's
                    filled-accent weight; must not out-rank PRIMARY)
   .button-compact is an orthogonal size modifier, not a 5th role.
   ------------------------------------------------------------------ */
/* Step 58.1J2 (Wave 1): AB-A01 Primary Button, height/padding raised to
   the locked ArtBuild standard (44px / 0 20px, up from 40px / 0 14px) --
   a deliberate product-standard interaction-target size, not a
   conformance claim about any specific accessibility criterion. */
.button {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-height: 44px;
	padding: 0 20px;
	border: 1px solid var(--ab-accent);
	border-radius: var(--ab-radius);
	background: var(--ab-accent);
	color: #fff;
	font: inherit;
	font-size: 14px;
	font-weight: 800;
	cursor: pointer;
	transition: background-color .15s ease, border-color .15s ease, transform .05s ease;
}

.button:active {
	transform: translateY(1px);
}

@media (prefers-reduced-motion: reduce) {
	.button {
		transition: none;
	}

	.button:active {
		transform: none;
	}
}

.button-compact {
	min-height: 34px;
	padding: 0 10px;
	font-size: 12px;
}

/* Wave 1 infrastructure only -- not wired to any template yet. For a
   real async submit (matches the Estimate command bar's existing
   data-save-state precedent, AB-FB06): mark the button
   aria-busy="true" while its own request is in flight. No spinner
   graphic, no decorative animation -- text-only, per the "only where
   actual waiting exists, no decorative loaders" rule. */
.button[aria-busy="true"] {
	opacity: .7;
	cursor: wait;
}

.button:hover {
	/* Hardcoded, not var(--ab-accent-dark): this is the one consumer of
	   that token using it as a darker HOVER BACKGROUND rather than
	   accent-colored text -- the canvas-dark remap below brightens the
	   token for its 13 text-color consumers, which would wrongly make a
	   pressed button lighter instead of darker if this read the same
	   var(). #91451f is the token's original, unchanged value. */
	background: #91451f;
	border-color: #91451f;
}

.button:disabled,
.account-form input:disabled,
.account-form select:disabled,
.account-form textarea:disabled {
	cursor: not-allowed;
	opacity: .55;
}

.button-secondary {
	border-color: var(--ab-line);
	background: #fff;
	color: var(--ab-ink);
}

.button-secondary:hover {
	background: #f7f8fa;
	border-color: var(--ab-line);
	color: var(--ab-ink);
}

.button-danger {
	border-color: var(--ab-red);
	background: var(--ab-red);
	color: #fff;
}

.button-danger:hover {
	border-color: #8f1b13;
	background: #8f1b13;
	color: #fff;
}

.text-link {
	color: var(--ab-accent-dark);
	font-size: 14px;
	font-weight: 800;
}

.notice {
	margin-bottom: 16px;
	padding: 12px 14px;
	border: 1px solid var(--ab-line);
	border-radius: var(--ab-radius);
	background: var(--ab-surface);
	color: var(--ab-ink);
	box-shadow: var(--ab-shadow);
}

.notice.is-error {
	border-color: rgba(180, 35, 24, .28);
	background: var(--ab-danger-bg);
	color: var(--ab-red);
}

.notice.is-success {
	border-color: rgba(35, 122, 79, .28);
	background: var(--ab-success-bg);
	color: var(--ab-green);
}

/* Step 58.1J2 (Wave 1): confirmed missing -- templates already request
   is-warning/is-info dynamically (`is-<?php echo $notice['type']?>`)
   with no matching rule, rendering with zero color treatment. Real bug
   fix, not a new feature. */
.notice.is-warning {
	border-color: rgba(154, 106, 18, .3);
	background: var(--ab-warning-bg);
	color: var(--ab-warning);
}

.notice.is-info {
	border-color: rgba(49, 95, 147, .28);
	background: var(--ab-info-bg);
	color: var(--ab-blue);
}

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

.inline-form {
	margin: 0;
}

.table-wrap {
	width: 100%;
	overflow-x: auto;
}

.data-table {
	width: 100%;
	min-width: 760px;
	border-collapse: collapse;
	font-size: 13px;
}

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

.data-table th {
	color: var(--ab-muted);
	font-size: 11px;
	font-weight: 800;
	text-transform: uppercase;
	white-space: nowrap;
}

.data-table td {
	overflow-wrap: anywhere;
}

/* Reproduced live defect: Admin > Users' name+email cell has no min-
   width, so at real desktop widths (248px sidebar + a wide Actions
   column reserving 280px) the remaining space per data column is
   narrower than a single word -- overflow-wrap:anywhere then breaks
   "Kateryna Veselova" letter-by-letter instead of at the space that
   already exists. A dedicated min-width restores normal word-wrapping;
   the table's own .table-wrap horizontal scroll (unchanged) still
   covers genuinely narrow viewports. */
.admin-user-cell {
	min-width: 180px;
	overflow-wrap: normal;
}

.data-table tbody tr:hover {
	background: #faf9f6;
}

.table-subtext {
	display: block;
	margin-top: 3px;
	color: var(--ab-muted);
	font-size: 12px;
}

.admin-actions {
	display: grid;
	gap: 8px;
	min-width: 280px;
}

.compact-form {
	display: flex;
	align-items: center;
	gap: 8px;
	margin: 0;
}

.compact-form input,
.compact-form select {
	min-height: 34px;
	padding: 6px 8px;
	border: 1px solid var(--ab-line);
	border-radius: var(--ab-radius);
	background: #fff;
	color: var(--ab-ink);
	font: inherit;
	font-size: 12px;
}

/* Focus visibility (keyboard navigation) — applied globally rather than
   per-component, since every interactive element needs the same treatment
   and none had one before this pass. */
a:focus-visible,
button:focus-visible,
.button:focus-visible,
.nav-link:focus-visible,
.text-link:focus-visible,
.module-link:focus-visible,
.chip:focus-visible,
.role-card:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible {
	outline: 3px solid rgba(23, 126, 117, .45);
	outline-offset: 2px;
}

.site-footer {
	margin-top: 32px;
	padding-top: 24px;
	border-top: 1px solid var(--ab-line);
}

.site-footer__grid {
	display: grid;
	grid-template-columns: 2fr repeat(3, 1fr);
	gap: 24px;
}

.site-footer__col strong {
	display: block;
	margin-bottom: 8px;
	font-size: 15px;
}

.site-footer__col p {
	margin: 0 0 6px;
	color: var(--ab-muted);
	font-size: 13px;
	line-height: 1.5;
}

.site-footer__title {
	margin: 0 0 10px;
	color: var(--ab-muted);
	font-size: 11px;
	font-weight: 800;
	text-transform: uppercase;
}

.site-footer__col a {
	display: block;
	margin-bottom: 8px;
	color: var(--ab-ink);
	font-size: 13px;
	font-weight: 700;
}

.site-footer__col a:hover {
	color: var(--ab-accent-dark);
}

.site-footer__bar {
	margin-top: 24px;
	padding-top: 16px;
	border-top: 1px solid var(--ab-line);
	color: var(--ab-muted);
	font-size: 12px;
}

.sponsored-banner {
	max-width: 100%;
	border-radius: var(--ab-radius);
}

.panel-spaced {
	margin-top: 16px;
}

.stack-tight {
	display: flex;
	flex-direction: column;
	align-items: stretch;
	gap: 8px;
}

.image-tile-sm {
	width: 160px;
}

@media (max-width: 1080px) {
	.app-shell {
		grid-template-columns: 1fr;
	}

	.sidebar {
		position: static;
		padding: 0;
	}

	/* Step 58.1J2: below the shared breakpoint, the sidebar is a real,
	   collapsed-by-default disclosure driven by a hidden checkbox and its
	   <label> -- so <main> starts immediately after this one compact
	   bar, not after the full nav, with zero JS and normal (non-UA-magic)
	   CSS visibility rules. The exact same nav DOM/authorization data
	   from desktop; only its visibility toggle changes. */
	.sidebar-summary {
		display: flex;
		align-items: center;
		gap: 10px;
		list-style: none;
		cursor: pointer;
		padding: 14px 18px;
		color: #fff;
	}

	.sidebar-toggle:focus-visible ~ .sidebar-summary {
		outline: 2px solid var(--ab-accent);
		outline-offset: -2px;
	}

	.sidebar-menu-hint {
		display: block;
		margin-top: 2px;
		color: rgba(247, 244, 239, .68);
		font-size: 11px;
		font-weight: 700;
		text-transform: uppercase;
	}

	.sidebar-toggle:not(:checked) ~ .sidebar-content {
		display: none;
	}

	.sidebar-toggle:checked ~ .sidebar-content {
		display: block;
		padding: 4px 18px 22px;
	}

	.metric-grid,
	.module-grid,
	.checkbox-grid,
	.filter-bar,
	.filter-form,
	.estimate-form-grid,
	.estimate-line-grid,
	.estimate-adjustment-grid,
	.role-grid,
	.site-footer__grid {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}

	.filter-actions {
		grid-column: 1 / -1;
	}

	.split-panels {
		grid-template-columns: 1fr;
	}

	.project-split > .form-panel {
		grid-column: auto;
		grid-row: auto;
	}
}

@media (max-width: 680px) {
	.main {
		padding: 20px 14px;
	}

	/* Keep the hero/CTA full-bleed edges matching .main's own mobile
	   padding exactly -- otherwise a fixed desktop offset either leaves a
	   gap or, worse, extends past the viewport edge (page overflow). */
	.landing-hero {
		margin: -20px -14px var(--ab-space-5);
		padding: var(--ab-space-5) var(--ab-space-3);
	}

	.landing-hero h1 {
		font-size: 30px;
	}

	.landing-cta-band {
		margin: var(--ab-space-5) -14px -20px;
	}

	/* Same full-bleed correction as .landing-hero above, for the
	   .topbar--product product-header primitive and its merged
	   metric-grid continuation. */
	.topbar--product {
		margin: -20px -14px 0;
		padding: 24px 14px 18px;
	}

	.topbar--product + .metric-grid {
		margin: 0 -14px 18px;
		padding: 0 14px 20px;
	}

	.topbar {
		display: block;
	}

	.env-pill {
		margin-top: 12px;
		white-space: normal;
	}

	.metric-grid,
	.module-grid,
	.directory-grid,
	.checkbox-grid,
	.filter-bar,
	.filter-form,
	.role-grid,
	.form-grid,
	.estimate-form-grid,
	.estimate-line-grid,
	.estimate-adjustment-grid,
	.key-value,
	.site-footer__grid {
		grid-template-columns: 1fr;
	}

	.review-action,
	.review-row,
	.review-card,
	.work-order-row,
	.work-order-card,
	.document-version-row {
		display: grid;
	}

	.form-actions {
		align-items: stretch;
		flex-direction: column;
	}
}

/* ------------------------------------------------------------------
   PHASE 2: PROFESSIONAL ESTIMATOR WORKSPACE
   Sections, the four-tab Add Item chooser, line Advanced disclosure,
   cost components, and adjustments-as-business-actions. Reuses the
   existing token set and .button/.badge/.chip/.catalog-* conventions
   rather than introducing a parallel style vocabulary.
   ------------------------------------------------------------------ */

.field-optional {
	font-style: normal;
	font-weight: 600;
	color: var(--ab-muted);
}

/* Step 58.1J2 (Wave 1): form-field infrastructure -- required indicator,
   help text, inline validation. Applied representatively this wave
   (Register, the platform's clearest real required/optional mix), not
   retrofitted onto all ~9 form scopes yet. Visual required markers must
   only ever be added where the paired control actually carries
   `required`; never decorative. */
.field-required {
	color: var(--ab-red);
	font-weight: 800;
	margin-left: 2px;
}

.field-help {
	display: block;
	margin-top: 4px;
	font: var(--ab-text-meta);
	color: var(--ab-muted);
}

.field-error {
	display: block;
	margin-top: 4px;
	font: var(--ab-text-meta);
	font-weight: 700;
	color: var(--ab-red);
}

/* Applies alongside AB-FM01's per-scope input rules (not a replacement
   for them) -- the error border sits on top of whichever scope's base
   border/radius/background is already declared. */
.account-form input.has-error,
.account-form select.has-error,
.account-form textarea.has-error {
	border-color: var(--ab-red);
	background: var(--ab-danger-bg);
}

.account-form input[readonly],
.account-form select[readonly] {
	background: var(--ab-surface-2);
	color: var(--ab-muted);
}

/* AB-FM05 File Upload -- reuses the existing dashed "recessed/
   placeholder" surface idiom (.empty's border style) rather than
   inventing a new visual language for what was previously a fully
   unstyled native control. */
.file-field {
	display: block;
	padding: var(--ab-space-3);
	border: 1px dashed var(--ab-line);
	border-radius: var(--ab-radius);
	background: var(--ab-surface-2);
	color: var(--ab-muted);
	font: var(--ab-text-body);
}

button.text-link {
	border: 0;
	padding: 0;
	background: none;
	font: inherit;
	cursor: pointer;
}

.collapsible-section {
	margin-top: 16px;
	padding: 14px 16px;
	border: 1px dashed var(--ab-line);
	border-radius: var(--ab-radius);
}

.collapsible-section summary {
	cursor: pointer;
	font-weight: 800;
	color: var(--ab-accent-dark);
	list-style: none;
}

.collapsible-section summary::-webkit-details-marker {
	display: none;
}

.collapsible-section[open] summary {
	margin-bottom: 14px;
}

.collapsible-section .estimate-form-grid,
.collapsible-section > label {
	margin-top: 12px;
}

.collapsible-section > label:first-of-type {
	margin-top: 0;
}

/* LIVE FINANCIAL SUMMARY: read-only customer-facing roll-up. */
.estimate-summary-panel {
	margin-top: 16px;
	padding-top: 16px;
	border-top: 1px solid var(--ab-line);
}

.summary-row {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 12px;
	padding: 6px 0;
	font-weight: 800;
}

.summary-row-secondary {
	font-weight: 600;
	color: var(--ab-muted);
	font-size: 14px;
}

.summary-total-row {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 12px;
	margin-top: 8px;
	padding-top: 10px;
	border-top: 2px solid var(--ab-ink);
	font-size: 18px;
	font-weight: 800;
}

/* MOBILE FINANCIAL COMMAND SURFACE: stays reachable while scrolling.
   Fixed only below the desktop breakpoint -- desktop keeps the Summary
   panel and the existing end-of-document Save action, per the explicit
   instruction that desktop does not need the same sticky presentation. */
.sticky-command-bar {
	display: flex;
	align-items: center;
	gap: 12px;
	margin-bottom: 16px;
	padding: 10px 14px;
	border: 1px solid var(--ab-line);
	border-radius: var(--ab-radius);
	background: var(--ab-surface);
}

.sticky-command-info {
	flex: 1;
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: 4px 10px;
	min-width: 0;
}

.sticky-command-label {
	color: var(--ab-muted);
	font-size: 11px;
	font-weight: 800;
	text-transform: uppercase;
}

.sticky-command-info strong {
	font-size: 17px;
}

.sticky-save-state {
	flex-basis: 100%;
	color: var(--ab-muted);
	font-size: 12px;
	font-weight: 700;
}

.sticky-save-state[data-save-state="saved"] {
	color: var(--ab-green);
}

.sticky-save-state[data-save-state="saving"] {
	color: var(--ab-teal);
}

.sticky-save-state[data-save-state="unsaved"] {
	color: var(--ab-accent-dark);
}

.sticky-save-state[data-save-state="failed"] {
	color: var(--ab-red);
	font-weight: 800;
}

@media (max-width: 680px) {
	.sticky-command-bar {
		position: fixed;
		left: 0;
		right: 0;
		bottom: 0;
		z-index: 15;
		margin: 0;
		border-radius: 0;
		border-width: 1px 0 0;
		box-shadow: 0 -6px 16px rgba(22, 24, 29, .08);
		padding-bottom: calc(10px + env(safe-area-inset-bottom));
	}

	/* Keeps the last real content (the end-of-document Save action) from
	   being hidden behind the fixed bar. */
	.estimate-form {
		padding-bottom: 76px;
	}
}

/* Collapsible document-identity header -- one canonical set of inputs;
   the collapsed <summary> is a read-only, JS-synced preview, never a
   second input surface. */
.estimate-header-details {
	margin-bottom: 16px;
	padding: 14px 16px;
	border: 1px solid var(--ab-line);
	border-radius: var(--ab-radius);
	background: var(--ab-surface);
}

.estimate-header-details summary {
	display: flex;
	align-items: center;
	gap: 12px;
	cursor: pointer;
	list-style: none;
}

.estimate-header-details summary::-webkit-details-marker {
	display: none;
}

.estimate-header-details[open] summary {
	margin-bottom: 14px;
	padding-bottom: 14px;
	border-bottom: 1px dashed var(--ab-line);
}

.estimate-header-summary {
	flex: 1;
	display: grid;
	gap: 3px;
	min-width: 0;
}

.estimate-header-summary strong {
	font-size: 16px;
}

.estimate-header-summary span {
	color: var(--ab-muted);
	font-size: 13px;
}

.estimate-header-summary span[hidden] {
	display: none;
}

.estimate-header-edit-hint {
	flex-shrink: 0;
	color: var(--ab-teal);
	font-size: 13px;
	font-weight: 800;
}

.estimate-header-details[open] .estimate-header-edit-hint {
	display: none;
}

/* Sections workspace */
.estimate-sections {
	display: grid;
	gap: 16px;
}

.estimate-section {
	display: grid;
	gap: 10px;
	padding: 14px;
	border: 1px solid var(--ab-line);
	border-radius: var(--ab-radius);
	background: rgba(22, 24, 29, .015);
}

.estimate-section-head {
	display: flex;
	align-items: center;
	gap: 8px;
}

.estimate-section-number {
	flex-shrink: 0;
	min-width: 22px;
	color: var(--ab-teal);
	font-weight: 800;
	font-variant-numeric: tabular-nums;
}

.estimate-line-number {
	flex-shrink: 0;
	min-width: 28px;
	color: var(--ab-muted);
	font-weight: 800;
	font-variant-numeric: tabular-nums;
}

.empty-compact {
	padding: 10px 12px;
	margin: 0;
	font-size: 13px;
}

.metric-summary-compact {
	display: none;
	margin: 0 0 16px;
	padding: 10px 14px;
	border: 1px solid var(--ab-line);
	border-radius: var(--ab-radius);
	background: var(--ab-surface);
	color: var(--ab-muted);
	font-size: 13px;
	line-height: 1.6;
}

@media (max-width: 680px) {
	.metric-summary-compact {
		display: block;
	}

	.metric-grid-full {
		display: none;
	}
}

.estimate-section-title {
	flex: 1;
	min-height: 38px;
	padding: 7px 10px;
	border: 1px solid var(--ab-line);
	border-radius: var(--ab-radius);
	background: #fff;
	font: inherit;
	font-weight: 800;
}

/* Overflow menu: one shared, accessible pattern for section actions
   (Move Up/Down/Remove) and line actions (Advanced/Duplicate/Remove) --
   keeps destructive/secondary controls off the default, always-visible
   row so the collapsed worksheet stays compact. */
/* Step 58.1J2 (Wave 1): AB-Icon-01 delivery. One canonical <svg><use>
   reference into /assets/icons/sprite.svg -- no raw inline SVG paths
   scattered through templates. Sizing is set by the consumer (nav-link,
   icon-button, etc.) via font-size-independent width/height, since
   `currentColor` makes the glyph inherit whatever text color already
   applies. */
.icon {
	display: inline-block;
	flex-shrink: 0;
	width: 18px;
	height: 18px;
	vertical-align: -4px;
}

.icon-button .icon {
	width: 18px;
	height: 18px;
	vertical-align: 0;
}

.nav-link .icon {
	width: 18px;
	height: 18px;
	vertical-align: -4px;
}

/* Step 58.1J2 (Wave 1): reusable visually-hidden-but-accessible utility
   -- same technique already used for .sidebar-toggle, generalized into
   one shared class instead of a one-off. */
.sr-only {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	overflow: hidden;
	clip: rect(0, 0, 0, 0);
	white-space: nowrap;
	border: 0;
}

/* AB-Avatar-01. Initials-fallback is the default; a real photo (never
   fabricated -- only rendered when the caller passes an actual
   already-uploaded URL) replaces it via .avatar img. */
.avatar {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	flex-shrink: 0;
	border-radius: 50%;
	background: var(--ab-charcoal);
	color: #f7f4ef;
	font-weight: 800;
	overflow: hidden;
}

.avatar img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.avatar--xs {
	width: 24px;
	height: 24px;
	font-size: 10px;
}

.avatar--sm {
	width: 32px;
	height: 32px;
	font-size: 12px;
}

.avatar--md {
	width: 48px;
	height: 48px;
	font-size: 16px;
}

.avatar--lg {
	width: 96px;
	height: 96px;
	font-size: 28px;
}

/* Step 58.1J2 (Reference visual-DNA pass): AB-C06 Entry Circle. Thin
   precision-outline circular badge for major product entry points --
   translates the reference image's circular-geometry language (used
   there for the home dashboard's Projects/Find Work/Materials/Tools
   entries) without copying its literal composition. Icon only, never a
   fabricated photo -- content-free decoration is exactly what this
   audit's imagery rules forbid. Public/marketing use only; operational
   lists/tables/forms stay rectilinear per the same reference's own
   "Find Work" screen, which is NOT circular. */
.entry-circle {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	flex-shrink: 0;
	width: 56px;
	height: 56px;
	border-radius: var(--ab-radius-pill);
	border: 1px solid var(--ab-line);
	background: var(--ab-surface-2);
	color: var(--ab-muted);
}

.entry-circle .icon {
	width: 26px;
	height: 26px;
}

/* AB-D04b Circular KPI -- infrastructure only, not wired to any
   template this wave. A real, existing, currently-unrendered data
   source (ProjectFormatter::progress(), computed from real
   tasks_count/done_tasks_count) is the proven candidate for this
   component once Project detail's information architecture is
   addressed -- explicitly 58.1K scope, not this visual-migration wave.
   Built now only so the primitive exists and is documented; wiring it
   into a page would be new authenticated content, not a like-for-like
   visual restyle. Pure CSS conic-gradient ring -- no SVG/JS required,
   degrades to a plain circle if conic-gradient is unsupported (all
   evergreen browsers support it). */
.progress-ring {
	--progress-ring-value: 0;
	display: inline-grid;
	place-items: center;
	width: 96px;
	height: 96px;
	border-radius: var(--ab-radius-pill);
	background: conic-gradient(var(--ab-accent) calc(var(--progress-ring-value) * 1%), var(--ab-line) 0);
}

.progress-ring-inner {
	display: grid;
	place-items: center;
	width: 78px;
	height: 78px;
	border-radius: var(--ab-radius-pill);
	background: var(--ab-surface);
	font: var(--ab-text-h2);
	color: var(--ab-ink-strong);
}

.icon-button {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 32px;
	height: 32px;
	border: 1px solid transparent;
	border-radius: var(--ab-radius);
	background: none;
	color: var(--ab-muted);
	font-size: 18px;
	line-height: 1;
	cursor: pointer;
}

.icon-button:hover,
.icon-button[aria-expanded="true"] {
	border-color: var(--ab-line);
	background: #fff;
	color: var(--ab-ink);
}

.menu {
	position: relative;
	flex-shrink: 0;
}

.dropdown-menu {
	position: absolute;
	right: 0;
	top: calc(100% + 4px);
	z-index: 20;
	display: grid;
	min-width: 180px;
	padding: 6px;
	border: 1px solid var(--ab-line);
	border-radius: var(--ab-radius);
	background: var(--ab-surface);
	box-shadow: var(--ab-shadow);
}

.dropdown-menu button {
	border: 0;
	padding: 9px 10px;
	border-radius: calc(var(--ab-radius) - 2px);
	background: none;
	color: var(--ab-ink);
	font: inherit;
	font-size: 13px;
	font-weight: 700;
	text-align: left;
	cursor: pointer;
}

.dropdown-menu button:hover {
	background: rgba(23, 126, 117, .08);
}

.dropdown-menu-danger {
	color: var(--ab-red) !important;
}

.dropdown-menu-danger:hover {
	background: rgba(180, 35, 24, .08) !important;
}

.badge-subtle {
	background: none;
	color: var(--ab-muted);
	padding: 0 2px;
	font-weight: 700;
}

.estimate-section-lines {
	display: grid;
	gap: 10px;
}

.estimate-section-lines:empty {
	display: none;
}

/* Add-Item chooser tabs (Recent / Price Book / Catalog / Custom) */
.chooser-tabs {
	display: flex;
	flex-wrap: wrap;
	gap: 4px;
	padding: 12px 16px 0;
	border-bottom: 1px solid var(--ab-line);
}

.chooser-tab {
	min-height: 38px;
	padding: 0 14px;
	border: 1px solid transparent;
	border-bottom: none;
	border-radius: var(--ab-radius) var(--ab-radius) 0 0;
	background: none;
	color: var(--ab-muted);
	font: inherit;
	font-size: 13px;
	font-weight: 800;
	cursor: pointer;
}

.chooser-tab:hover {
	color: var(--ab-ink);
}

.chooser-tab.is-active {
	border-color: var(--ab-line);
	background: var(--ab-surface);
	color: var(--ab-teal);
}

.chooser-panel {
	padding: 14px 16px;
	overflow-y: auto;
	max-height: 60vh;
}

.catalog-search-label {
	display: block;
	margin-bottom: 10px;
}

.catalog-search-label input {
	width: 100%;
	min-height: 42px;
	padding: 9px 11px;
	border: 1px solid var(--ab-line);
	border-radius: var(--ab-radius);
	font: inherit;
}

/* Line Advanced disclosure + cost mode */
.line-advanced {
	display: grid;
	gap: 14px;
	margin-top: 10px;
	padding-top: 12px;
	border-top: 1px dashed var(--ab-line);
}

.line-advanced-section {
	display: grid;
	gap: 10px;
}

.line-advanced-title {
	margin: 0;
	color: var(--ab-muted);
	font-size: 12px;
	font-weight: 800;
	text-transform: uppercase;
}

.cost-mode-toggle {
	display: flex;
	flex-wrap: wrap;
	gap: 16px;
}

.cost-mode-toggle label {
	display: flex;
	align-items: center;
	gap: 6px;
	font-weight: 700;
}

.line-allowance-toggle {
	display: flex;
	align-items: center;
	gap: 6px;
	font-weight: 700;
	font-size: 13px;
	color: var(--ab-muted);
}

/* Cost components */
.cost-components-list {
	display: grid;
	gap: 10px;
}

.cost-component-row {
	display: flex;
	flex-wrap: wrap;
	align-items: end;
	gap: 12px;
	padding: 10px;
	border: 1px solid var(--ab-line);
	border-radius: var(--ab-radius);
	background: #fff;
}

.cost-component-row label {
	display: grid;
	gap: 5px;
	font-size: 12px;
	font-weight: 700;
}

.cost-component-row input {
	min-height: 36px;
	padding: 6px 8px;
	border: 1px solid var(--ab-line);
	border-radius: var(--ab-radius);
	font: inherit;
	width: 100px;
}

.component-type-label {
	min-width: 84px;
	color: var(--ab-teal);
	font-weight: 800;
	text-transform: uppercase;
	font-size: 12px;
}

.component-add-row {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
}

/* Adjustments as business actions */
.adjustment-actions {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
}

.adjustment-row {
	padding: 12px;
	border: 1px solid var(--ab-line);
	border-radius: var(--ab-radius);
	background: var(--ab-surface);
}

.adjustment-summary {
	display: flex;
	align-items: center;
	gap: 10px;
}

.adjustment-summary-label {
	flex: 1;
	min-width: 0;
	font-weight: 700;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.adjustment-summary-value {
	flex-shrink: 0;
	color: var(--ab-muted);
	font-weight: 700;
	font-size: 13px;
}

.adjustment-summary-amount {
	flex-shrink: 0;
	font-weight: 800;
	font-size: 13px;
	min-width: 90px;
	text-align: right;
}

.adjustment-editor {
	display: grid;
	grid-template-columns: minmax(140px, 1.2fr) minmax(120px, .8fr) minmax(110px, .7fr) minmax(140px, .9fr) auto;
	gap: 10px;
	align-items: end;
	margin-top: 12px;
	padding-top: 12px;
	border-top: 1px dashed var(--ab-line);
}

@media (max-width: 1080px) {
	.adjustment-editor {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}
}

@media (max-width: 680px) {
	.adjustment-editor {
		grid-template-columns: 1fr;
	}
}

@media (max-width: 1080px) {
	.cost-component-row {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}
}

@media (max-width: 680px) {
	.cost-component-row {
		flex-direction: column;
		align-items: stretch;
	}

	.cost-component-row input {
		width: 100%;
	}

	.chooser-tabs {
		padding: 10px 12px 0;
	}

	.chooser-panel {
		padding: 12px;
	}
}

/* External public tender banner: the source badge can be long (e.g. the
   full NBON name) -- let the header wrap instead of overflowing on phones. */
.tender-external .panel-head {
	flex-wrap: wrap;
	gap: 8px;
}

.tender-external .panel-head .badge {
	white-space: normal;
	max-width: 100%;
}

/* Marketplace browse: real at-a-glance facts and browse shortcuts (counts
   of actual active listings) instead of public moderation/contact totals. */
.market-pulse {
	display: grid;
	gap: 10px;
	margin-bottom: 16px;
}

.market-pulse__line {
	margin: 0;
	color: var(--ab-muted);
}

.market-pulse__line strong {
	color: var(--ab-ink-strong, var(--ab-ink));
}

.market-shortcuts {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
}

.market-shortcut {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	padding: 6px 12px;
	border: 1px solid var(--ab-line);
	border-radius: 999px;
	color: var(--ab-ink);
	font-size: 14px;
	text-decoration: none;
	background: var(--ab-surface);
}

.market-shortcut span {
	color: var(--ab-muted);
	font-size: 12px;
}

.market-shortcut:hover,
.market-shortcut.is-active {
	border-color: var(--ab-accent);
}

.market-shortcut--quiet {
	background: transparent;
}

.filter-bar--primary .filter-more {
	grid-column: 1 / -1;
}

.filter-more summary {
	cursor: pointer;
	color: var(--ab-accent-dark);
	font-weight: 600;
	margin-bottom: 4px;
}

.filter-more[open] summary {
	margin-bottom: 12px;
}

/* Cards and the detail hero keep the image's own 16:10 proportion, so a
   listing image is never cropped (previously the hero cut off the top of
   the image and card images lost their top label). */
.listing-card__image {
	height: auto;
	aspect-ratio: 16 / 10;
}

.image-tile.listing-gallery__main {
	background: var(--ab-charcoal, #15171c);
}

.image-tile.listing-gallery__main img {
	height: auto;
	max-height: 480px;
	aspect-ratio: 16 / 10;
	object-fit: contain;
	margin: 0 auto;
}

@media (max-width: 700px) {
	.image-tile.listing-gallery__main img {
		height: auto;
	}
}

.listing-card__actions {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 8px 12px;
	margin-top: auto;
}

.listing-card {
	display: flex;
	flex-direction: column;
}

.chip--quiet {
	opacity: .75;
}

.listing-contact__actions {
	display: flex;
	flex-wrap: wrap;
	gap: 10px;
	margin-bottom: 14px;
}

.listing-contact__actions .button {
	max-width: 100%;
	overflow-wrap: anywhere;
}

.listing-provenance {
	color: var(--ab-muted);
	font-size: 13px;
	line-height: 1.5;
}

.listing-provenance .badge {
	margin-right: 6px;
}

@media (max-width: 700px) {
	.market-shortcuts {
		flex-wrap: nowrap;
		overflow-x: auto;
		padding-bottom: 4px;
		-webkit-overflow-scrolling: touch;
		scrollbar-width: none;
	}

	.market-shortcut {
		flex: 0 0 auto;
	}
}

.listing-photo-credit {
	margin: -6px 0 14px;
	color: var(--ab-muted);
	font-size: 13px;
}

/* ------------------------------------------------------------------
   Homepage (pages/landing.php): one search and six actions first, then
   the commercial sections, then learning content. Scoped to .home so
   nothing else on the platform changes. Always rendered inside
   body.canvas-dark .main, so it uses that scope's remapped tokens.
   ------------------------------------------------------------------ */
.home {
	--home-gold: #f2a65a;
	max-width: 1180px;
	margin: 0 auto;
}

.home-hero {
	display: flex;
	flex-direction: column;
	align-items: center;
	padding: 36px 0 12px;
	text-align: center;
}

.home-hero__logo {
	width: 132px;
	height: 132px;
	border-radius: 28px;
	box-shadow: 0 0 0 1px rgba(242, 166, 90, .25), 0 24px 60px rgba(242, 166, 90, .14);
}

.home-hero h1 {
	margin: 26px 0 0;
	max-width: 980px;
	font: 800 clamp(32px, 5vw, 54px)/1.06 Inter, ui-sans-serif, system-ui, sans-serif;
	letter-spacing: -0.02em;
	color: var(--ab-ink-strong);
}

.home-hero__lead {
	margin: 16px 0 0;
	max-width: 620px;
	color: var(--ab-muted);
	font-size: 18px;
	line-height: 1.5;
}

.home-search {
	display: flex;
	align-items: center;
	gap: 10px;
	width: 100%;
	max-width: 720px;
	margin-top: 30px;
	padding: 6px 6px 6px 18px;
	border: 1px solid rgba(242, 166, 90, .35);
	border-radius: 999px;
	background: var(--ab-surface);
	box-shadow: 0 18px 44px rgba(0, 0, 0, .35);
}

.home-search:focus-within {
	border-color: var(--home-gold);
}

.home-search .icon {
	color: var(--ab-muted);
}

body.canvas-dark .main .home-search input {
	flex: 1;
	padding: 0;
	box-shadow: none;
	min-width: 0;
	min-height: 48px;
	border: 0;
	background: transparent;
	color: var(--ab-ink-strong);
	font: inherit;
	font-size: 16px;
	outline: none;
}

.home-search input::placeholder {
	color: var(--ab-muted);
}

.home-search .button {
	border-radius: 999px;
	padding: 0 26px;
}

.home-actions {
	display: grid;
	grid-template-columns: repeat(6, minmax(0, 1fr));
	gap: 12px;
	width: 100%;
	margin-top: 28px;
}

.home-action {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 6px;
	padding: 18px 10px 16px;
	border: 1px solid var(--ab-line);
	border-radius: var(--ab-radius-lg);
	background: var(--ab-surface);
	color: var(--ab-ink-strong);
	transition: border-color .15s ease, transform .15s ease, background-color .15s ease;
}

.home-action:hover {
	border-color: rgba(242, 166, 90, .55);
	background: var(--ab-surface-2);
	transform: translateY(-2px);
}

.home-action strong {
	font-size: 14.5px;
}

.home-action > span:last-child {
	color: var(--ab-muted);
	font-size: 12px;
}

.home-action__icon {
	display: inline-grid;
	place-items: center;
	width: 42px;
	height: 42px;
	border-radius: 12px;
	background: rgba(242, 166, 90, .12);
	color: var(--home-gold);
}

.home-action__icon .icon {
	width: 20px;
	height: 20px;
	vertical-align: 0;
}

.home-hero__stats {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: 8px 22px;
	margin: 22px 0 0;
	color: var(--ab-muted);
	font-size: 14px;
}

.home-hero__stats strong {
	color: var(--ab-ink-strong);
}

.home-section {
	margin-top: 72px;
}

.home-section__head {
	display: flex;
	align-items: flex-end;
	justify-content: space-between;
	gap: 16px;
	margin-bottom: 20px;
}

.home-section__head h2 {
	margin: 6px 0 0;
	font: 800 clamp(22px, 2.6vw, 30px)/1.15 Inter, ui-sans-serif, system-ui, sans-serif;
	letter-spacing: -0.01em;
	color: var(--ab-ink-strong);
}

.home-section__links {
	display: flex;
	align-items: center;
	gap: 16px;
	flex: none;
}

.home-kicker {
	color: var(--home-gold);
	font-size: 12px;
	font-weight: 800;
	letter-spacing: .12em;
	text-transform: uppercase;
}

.home-grid {
	display: grid;
	gap: 16px;
}

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

.home-card {
	display: flex;
	flex-direction: column;
	overflow: hidden;
	border: 1px solid var(--ab-line);
	border-radius: var(--ab-radius-lg);
	background: var(--ab-surface);
	color: var(--ab-ink);
}

.home-card__media {
	position: relative;
	display: block;
	overflow: hidden;
	aspect-ratio: 16 / 10;
	background:
		radial-gradient(circle at 30% 30%, rgba(242, 166, 90, .12), transparent 60%),
		var(--ab-surface-2);
}

.home-card__media img {
	position: absolute;
	inset: 0;
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.home-card__body {
	display: flex;
	flex: 1;
	flex-direction: column;
	gap: 8px;
	padding: 16px 18px 18px;
}

.home-card h3 {
	margin: 0;
	font-size: 16px;
	line-height: 1.3;
	color: var(--ab-ink-strong);
}

.home-card h3 a {
	color: inherit;
}

.home-card h3 a:hover {
	color: var(--home-gold);
}

.home-card p {
	margin: 0;
	color: var(--ab-muted);
	font-size: 13.5px;
	line-height: 1.5;
}

.home-card__meta {
	color: var(--ab-muted);
	font-size: 12px;
	font-weight: 700;
	letter-spacing: .02em;
}

.home-card__facts {
	display: flex;
	flex-wrap: wrap;
	gap: 4px 14px;
	color: var(--ab-muted);
	font-size: 13px;
}

.home-card__price {
	color: var(--ab-ink-strong);
	font-weight: 800;
}

.home-card__actions {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 10px;
	margin-top: auto;
	padding-top: 6px;
}

.home-card__by {
	overflow: hidden;
	color: var(--ab-muted);
	font-size: 12.5px;
	text-overflow: ellipsis;
	white-space: nowrap;
	min-width: 0;
	flex: 1;
}

.home-card__place {
	font-size: 13px;
}

.home-facts {
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: 10px 16px;
	margin: 4px 0 0;
}

.home-facts dt {
	color: var(--ab-muted);
	font-size: 11.5px;
	font-weight: 700;
	letter-spacing: .06em;
	text-transform: uppercase;
}

.home-facts dd {
	margin: 2px 0 0;
	color: var(--ab-ink-strong);
	font-size: 14px;
	font-weight: 700;
}

.home-company {
	display: flex;
	align-items: center;
	gap: 14px;
}

.home-company__logo {
	display: grid;
	flex: none;
	place-items: center;
	width: 56px;
	height: 56px;
	overflow: hidden;
	border-radius: 12px;
	background: #fff;
}

.home-company__logo img {
	width: 100%;
	height: 100%;
	object-fit: contain;
	padding: 6px;
}

.home-tool {
	gap: 10px;
	padding: 20px;
	transition: border-color .15s ease, transform .15s ease;
}

.home-tool:hover {
	border-color: rgba(242, 166, 90, .55);
	transform: translateY(-2px);
}

.home-tool__cta {
	margin-top: auto;
	color: var(--home-gold);
	font-size: 13.5px;
	font-weight: 800;
}

.home-trend {
	padding: 18px 20px;
}

.home-trend h3 {
	margin-bottom: 10px;
}

.home-trend__list {
	margin: 0;
	padding: 0;
	list-style: none;
	counter-reset: trend;
}

.home-trend__list li {
	display: flex;
	align-items: baseline;
	gap: 10px;
	padding: 8px 0;
	border-top: 1px solid var(--ab-line);
	font-size: 14px;
	counter-increment: trend;
}

.home-trend__list li:first-child {
	border-top: 0;
}

.home-trend__list li::before {
	content: counter(trend);
	width: 16px;
	color: var(--home-gold);
	font-weight: 800;
	font-size: 12px;
}

.home-trend__list--plain li::before {
	content: none;
}

.home-trend__list a {
	flex: 1;
	min-width: 0;
	overflow: hidden;
	color: var(--ab-ink-strong);
	text-overflow: ellipsis;
	white-space: nowrap;
}

.home-trend__list a:hover {
	color: var(--home-gold);
}

.home-trend__list span {
	flex: none;
	color: var(--ab-muted);
	font-size: 12.5px;
}

.home-empty {
	margin: 0;
	padding: 22px;
	border: 1px dashed var(--ab-line);
	border-radius: var(--ab-radius-lg);
	color: var(--ab-muted);
}

.home-join {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 10px 18px;
	margin-top: 20px;
	padding: 18px 22px;
	border-radius: var(--ab-radius-lg);
	background: linear-gradient(120deg, rgba(242, 166, 90, .14), rgba(242, 166, 90, .03));
	border: 1px solid rgba(242, 166, 90, .25);
}

.home-join p {
	margin: 0;
	color: var(--ab-muted);
	font-weight: 700;
}

.home-join a:not(.button) {
	color: var(--ab-ink-strong);
	font-weight: 700;
}

.home-join a:not(.button):hover {
	color: var(--home-gold);
}

.home-join .button {
	margin-left: auto;
}

@media (max-width: 1024px) {
	.home-actions { grid-template-columns: repeat(3, minmax(0, 1fr)); }
	.home-grid--3,
	.home-grid--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 640px) {
	.home-hero { padding-top: 12px; }
	.home-hero__logo { width: 96px; height: 96px; border-radius: 22px; }
	.home-hero__lead { font-size: 16px; }
	.home-search { padding-left: 14px; }
	.home-search .button { padding: 0 16px; }
	.home-actions { grid-template-columns: repeat(2, minmax(0, 1fr)); }
	.home-grid--2,
	.home-grid--3,
	.home-grid--4 { grid-template-columns: minmax(0, 1fr); }
	.home-section { margin-top: 52px; }
	.home-section__head { flex-direction: column; align-items: flex-start; }
	.home-join .button { margin-left: 0; }
}

/* ------------------------------------------------------------------
   Site-wide homepage style. Every page now runs on the dark canvas and
   (for guests) under the same top navigation as the homepage; this
   layer brings the shared building blocks -- page header, panels,
   cards, chips, filters, forms, tables, sidebar -- to the homepage's
   look: large heavy headings, gold accent, 14px rounded surfaces with
   a gold hover edge. Only shared classes are restyled here; no page
   markup depends on this block existing.
   ------------------------------------------------------------------ */
:root {
	--ab-gold: #f2a65a;
	--ab-gold-soft: rgba(242, 166, 90, .12);
	--ab-gold-line: rgba(242, 166, 90, .45);
}

body.canvas-dark {
	background: #16181f;
}

body.canvas-dark .main {
	--ab-radius: 10px;
	--ab-radius-lg: 14px;
	--ab-surface: #1e222b;
	--ab-surface-2: #262b35;
	--ab-shadow: none;
	--ab-shadow-lg: 0 18px 44px rgba(0, 0, 0, .35);
	padding: 32px 40px 0;
}

/* Guest pages: content sits in the homepage's centred column. */
.app-shell--minimal .main {
	width: 100%;
	max-width: 1260px;
	margin: 0 auto;
	padding: 36px 40px 0;
}

/* --- Top navigation (guests) ------------------------------------- */
.marketing-topbar {
	gap: 22px;
	background: rgba(22, 24, 31, .92);
	backdrop-filter: blur(12px);
	-webkit-backdrop-filter: blur(12px);
}

.marketing-topbar-links {
	gap: 4px;
}

.marketing-topbar-links a {
	padding: 8px 10px;
	border-radius: 8px;
	font-size: 13.5px;
}

.marketing-topbar-links a:hover {
	background: rgba(245, 243, 238, .06);
}

.marketing-topbar-links a.is-active {
	color: var(--ab-gold);
}

.marketing-topbar-search {
	color: rgba(247, 244, 239, .82);
}

.marketing-topbar-search:hover {
	color: var(--ab-gold);
}

.marketing-topbar-search .icon {
	width: 18px;
	height: 18px;
	vertical-align: 0;
}

.marketing-menu {
	display: none;
	position: relative;
}

.marketing-menu summary {
	display: flex;
	flex-direction: column;
	justify-content: center;
	gap: 5px;
	width: 44px;
	height: 44px;
	padding: 0 11px;
	list-style: none;
	cursor: pointer;
}

.marketing-menu summary::-webkit-details-marker {
	display: none;
}

.marketing-menu summary span {
	height: 2px;
	border-radius: 2px;
	background: #f5f3ee;
}

.marketing-menu__panel {
	position: absolute;
	right: 0;
	top: calc(100% + 10px);
	display: flex;
	flex-direction: column;
	min-width: 220px;
	padding: 8px;
	border: 1px solid rgba(245, 243, 238, .12);
	border-radius: 14px;
	background: #1e222b;
	box-shadow: 0 18px 44px rgba(0, 0, 0, .45);
}

.marketing-menu__panel a {
	padding: 11px 12px;
	border-radius: 8px;
	color: rgba(247, 244, 239, .86);
	font-weight: 700;
	font-size: 14.5px;
}

.marketing-menu__panel a:hover,
.marketing-menu__panel a.is-active {
	background: rgba(245, 243, 238, .06);
	color: var(--ab-gold);
}

.marketing-dropdown {
	position: relative;
}

.marketing-dropdown summary {
	display: flex;
	align-items: center;
	gap: 6px;
	padding: 8px 10px;
	border-radius: 8px;
	color: rgba(247, 244, 239, .68);
	font-size: 13.5px;
	font-weight: 700;
	list-style: none;
	cursor: pointer;
	transition: color .15s ease;
}

.marketing-dropdown summary::-webkit-details-marker {
	display: none;
}

.marketing-dropdown summary::after {
	content: "";
	width: 6px;
	height: 6px;
	margin-top: -3px;
	border-right: 2px solid currentColor;
	border-bottom: 2px solid currentColor;
	transform: rotate(45deg);
}

.marketing-dropdown summary:hover,
.marketing-dropdown[open] summary {
	background: rgba(245, 243, 238, .06);
	color: #fff;
}

.marketing-dropdown summary.is-active {
	color: var(--ab-gold);
}

.marketing-dropdown__panel {
	position: absolute;
	left: 0;
	top: calc(100% + 8px);
	display: flex;
	flex-direction: column;
	min-width: 180px;
	padding: 6px;
	border: 1px solid rgba(245, 243, 238, .12);
	border-radius: 12px;
	background: #1e222b;
	box-shadow: 0 18px 44px rgba(0, 0, 0, .45);
}

.marketing-topbar-links .marketing-dropdown__panel a {
	padding: 10px 12px;
}

.marketing-menu__title {
	margin: 10px 12px 2px;
	color: var(--ab-gold);
	font-size: 10.5px;
	font-weight: 800;
	letter-spacing: .12em;
	text-transform: uppercase;
}

.marketing-menu__login {
	margin-top: 6px;
	border-top: 1px solid rgba(245, 243, 238, .1);
}

@media (max-width: 900px) {
	.marketing-menu {
		display: block;
	}

	.marketing-topbar-actions {
		margin-left: auto;
	}

	.marketing-topbar-search {
		display: none !important;
	}
}

@media (max-width: 500px) {
	.marketing-topbar-actions a:not(.button) {
		display: none;
	}
}

/* --- Sidebar (signed-in workspace) ------------------------------- */
.sidebar {
	background: #111318;
	border-right: 1px solid rgba(245, 243, 238, .08);
}

.nav-title {
	color: var(--ab-gold);
	font-size: 10.5px;
	font-weight: 800;
	letter-spacing: .12em;
}

.nav-dot {
	display: none;
}

/* Collapsed sidebar groups ("More", "Admin"). */
.nav-more {
	margin-top: 22px;
}

.nav-more__summary {
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding: 8px 10px;
	border-radius: var(--ab-radius);
	color: rgba(247, 244, 239, .6);
	font-size: 10.5px;
	font-weight: 800;
	letter-spacing: .12em;
	text-transform: uppercase;
	list-style: none;
	cursor: pointer;
}

.nav-more__summary::-webkit-details-marker {
	display: none;
}

.nav-more__summary::after {
	content: "";
	width: 6px;
	height: 6px;
	border-right: 2px solid currentColor;
	border-bottom: 2px solid currentColor;
	transform: rotate(-45deg);
	transition: transform .15s ease;
}

.nav-more[open] > .nav-more__summary::after {
	transform: rotate(45deg);
}

.nav-more__summary:hover {
	background: rgba(245, 243, 238, .06);
	color: #fff;
}

.nav-more .nav-group {
	margin-top: 4px;
}

/* The first admin group's own "Admin" title repeats the summary. */
.nav-more > .nav-more__summary + .nav-group > .nav-title {
	display: none;
}

.nav-link {
	border-left: 0;
}

.nav-link:hover {
	background: rgba(245, 243, 238, .06);
}

.nav-link.is-active {
	background: var(--ab-gold-soft);
	color: var(--ab-gold);
}

.nav-link.is-active .icon {
	color: var(--ab-gold);
}

/* --- Page header ------------------------------------------------- */
body.canvas-dark .main .topbar {
	align-items: flex-end;
	margin-bottom: 26px;
}

body.canvas-dark .main .topbar h1 {
	font: 800 clamp(28px, 3.4vw, 42px)/1.1 Inter, ui-sans-serif, system-ui, sans-serif;
	letter-spacing: -0.02em;
	color: #fff;
}

body.canvas-dark .main .topbar p {
	margin-top: 10px;
	color: var(--ab-muted);
	font-size: 16px;
	line-height: 1.5;
}

/* The product header used to be its own dark band; on the all-dark
   canvas it is simply the page heading, like the homepage. */
body.canvas-dark .main .topbar--product {
	margin: 0 0 26px;
	padding: 0;
	background: none;
}

body.canvas-dark .main .topbar--product + .metric-grid {
	margin: 0 0 22px;
	padding: 0;
	background: none;
}

body.canvas-dark .main .env-pill,
body.canvas-dark .main .topbar--product .env-pill {
	min-height: 32px;
	border-color: var(--ab-gold-line);
	border-radius: 999px;
	background: var(--ab-gold-soft);
	color: var(--ab-gold);
	font-weight: 800;
}

body.canvas-dark .main .env-pill .status-dot {
	background: var(--ab-gold);
}

/* One line of public facts under a page header (homepage hero stats). */
.page-stats {
	display: flex;
	flex-wrap: wrap;
	gap: 8px 22px;
	margin: -8px 0 24px;
	color: var(--ab-muted);
	font-size: 14px;
}

.page-stats strong {
	color: var(--ab-ink-strong);
}

/* --- Metrics ----------------------------------------------------- */
body.canvas-dark .main .metric,
body.canvas-dark .main .topbar--product + .metric-grid .metric {
	min-height: 0;
	padding: 18px 20px;
	border: 1px solid var(--ab-line);
	border-radius: var(--ab-radius-lg);
	background: var(--ab-surface);
}

body.canvas-dark .main .metric span,
body.canvas-dark .main .topbar--product + .metric-grid .metric span {
	color: var(--ab-gold);
	font-size: 11px;
	font-weight: 800;
	letter-spacing: .1em;
}

body.canvas-dark .main .metric strong,
body.canvas-dark .main .topbar--product + .metric-grid .metric strong {
	margin-top: 6px;
	color: #fff;
	font-size: 26px;
	font-weight: 800;
	letter-spacing: -0.01em;
}

body.canvas-dark .main .metric em {
	color: var(--ab-muted);
}

/* --- Panels ------------------------------------------------------ */
body.canvas-dark .main .panel,
body.canvas-dark .main .form-panel {
	border-color: var(--ab-line);
	border-radius: var(--ab-radius-lg);
	background: var(--ab-surface);
	box-shadow: none;
}

body.canvas-dark .main .panel--quiet {
	border-color: transparent;
	background: rgba(245, 243, 238, .03);
}

body.canvas-dark .main .panel--actions,
body.canvas-dark .main .panel--attention {
	border-color: var(--ab-gold-line);
	background: linear-gradient(120deg, rgba(242, 166, 90, .12), rgba(242, 166, 90, .03));
}

body.canvas-dark .main .panel h2,
body.canvas-dark .main .form-panel h2,
body.canvas-dark .main .panel-head h2 {
	font-size: 20px;
	font-weight: 800;
	letter-spacing: -0.01em;
	color: #fff;
}

body.canvas-dark .main .panel-head {
	margin-bottom: 16px;
}

/* --- Cards (directory listings) ---------------------------------- */
body.canvas-dark .main .directory-grid {
	grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
	gap: 16px;
}

body.canvas-dark .main .profile-card {
	border: 1px solid var(--ab-line);
	border-radius: var(--ab-radius-lg);
	background: var(--ab-surface);
	box-shadow: none;
	transition: border-color .15s ease, transform .15s ease;
}

body.canvas-dark .main .profile-card:hover {
	border-color: var(--ab-gold-line);
	transform: translateY(-2px);
}

body.canvas-dark .main .profile-card h3 {
	font-size: 16px;
	line-height: 1.3;
	color: #fff;
}

body.canvas-dark .main .profile-card p {
	color: var(--ab-muted);
	line-height: 1.5;
}

body.canvas-dark .main .profile-type {
	color: var(--ab-gold);
	font-size: 11px;
	font-weight: 800;
	letter-spacing: .1em;
	text-transform: uppercase;
}

body.canvas-dark .main .compact-facts dt {
	color: var(--ab-muted);
}

body.canvas-dark .main .compact-facts dd {
	color: var(--ab-ink-strong);
}

body.canvas-dark .main .module-link,
body.canvas-dark .main .text-link {
	color: var(--ab-gold);
	font-weight: 800;
}

body.canvas-dark .main .module-link::after {
	content: " \2192";
}

body.canvas-dark .main .module-link:hover,
body.canvas-dark .main .text-link:hover {
	color: #ffc185;
}

/* --- Chips and badges -------------------------------------------- */
body.canvas-dark .main .chip {
	border: 1px solid var(--ab-line);
	border-radius: 999px;
	background: rgba(245, 243, 238, .05);
	color: rgba(245, 243, 238, .86);
	font-weight: 700;
}

body.canvas-dark .main .badge {
	border-radius: 999px;
}

/* --- Buttons ----------------------------------------------------- */
body.canvas-dark .main .button,
.marketing-topbar .button {
	border-radius: var(--ab-radius, 10px);
}

body.canvas-dark .main .button-secondary {
	border-color: rgba(245, 243, 238, .2);
	background: rgba(245, 243, 238, .04);
}

/* --- Filters and forms ------------------------------------------- */
body.canvas-dark .main .filter-bar label span,
body.canvas-dark .main .account-form label span,
body.canvas-dark .main .form-grid label span {
	color: var(--ab-muted);
	font-size: 12px;
	font-weight: 700;
	letter-spacing: .02em;
}

body.canvas-dark .main input,
body.canvas-dark .main select,
body.canvas-dark .main textarea {
	border-radius: var(--ab-radius);
	background: #14171d;
	border-color: rgba(245, 243, 238, .14);
	color: #fff;
}

body.canvas-dark .main input::placeholder,
body.canvas-dark .main textarea::placeholder {
	color: rgba(245, 243, 238, .4);
}

body.canvas-dark .main input:focus,
body.canvas-dark .main select:focus,
body.canvas-dark .main textarea:focus {
	border-color: var(--ab-gold);
	outline: none;
	box-shadow: 0 0 0 3px rgba(242, 166, 90, .18);
}

body.canvas-dark .main input[type="checkbox"],
body.canvas-dark .main input[type="radio"] {
	accent-color: var(--ab-gold);
}

/* --- Tables ------------------------------------------------------ */
body.canvas-dark .main .table-wrap {
	border-radius: var(--ab-radius-lg);
}

body.canvas-dark .main .data-table th {
	color: var(--ab-gold);
	font-size: 11px;
	font-weight: 800;
	letter-spacing: .1em;
	text-transform: uppercase;
}

body.canvas-dark .main .data-table td {
	border-color: var(--ab-line);
}

/* --- Footer ------------------------------------------------------ */
body.canvas-dark .main .site-footer {
	margin-top: 80px;
}

@media (max-width: 760px) {
	body.canvas-dark .main,
	.app-shell--minimal .main {
		padding: 22px 16px 0;
	}

	body.canvas-dark .main .topbar {
		align-items: flex-start;
	}
}

/* ------------------------------------------------------------------
   Tools (pages/tools/index.php): one workspace -- a grouped tool list
   on the left, the calculator and its result on the right. Becomes a
   horizontal strip of tools above the calculator on narrow screens.
   ------------------------------------------------------------------ */
.tools-layout {
	display: grid;
	grid-template-columns: 260px minmax(0, 1fr);
	gap: 24px;
	align-items: start;
}

.tools-picker {
	position: sticky;
	top: 88px;
	display: flex;
	flex-direction: column;
	gap: 2px;
	padding: 12px;
	border: 1px solid var(--ab-line);
	border-radius: var(--ab-radius-lg);
	background: var(--ab-surface);
}

.tools-picker__group {
	margin: 12px 10px 4px;
	color: var(--ab-gold);
	font-size: 10.5px;
	font-weight: 800;
	letter-spacing: .12em;
	text-transform: uppercase;
}

.tools-picker__group:first-child {
	margin-top: 4px;
}

.tools-picker__item {
	display: flex;
	align-items: center;
	gap: 10px;
	padding: 9px 10px;
	border-radius: 10px;
	color: rgba(245, 243, 238, .82);
	font-size: 14px;
	font-weight: 700;
	line-height: 1.25;
	transition: background-color .15s ease, color .15s ease;
}

.tools-picker__item:hover {
	background: rgba(245, 243, 238, .06);
	color: #fff;
}

.tools-picker__item.is-active {
	background: var(--ab-gold-soft);
	color: var(--ab-gold);
}

.tools-picker__icon {
	display: inline-grid;
	flex: none;
	place-items: center;
	width: 30px;
	height: 30px;
	border-radius: 8px;
	background: rgba(245, 243, 238, .06);
}

.tools-picker__item.is-active .tools-picker__icon {
	background: rgba(242, 166, 90, .18);
}

.tools-picker__icon .icon {
	width: 16px;
	height: 16px;
	vertical-align: 0;
}

.tools-picker__empty {
	margin: 8px;
	color: var(--ab-muted);
}

.tools-workspace {
	padding: 28px;
	border: 1px solid var(--ab-line);
	border-radius: var(--ab-radius-lg);
	background: var(--ab-surface);
	scroll-margin-top: 88px;
}

.tools-workspace__head {
	display: flex;
	gap: 18px;
	align-items: flex-start;
	padding-bottom: 22px;
	margin-bottom: 22px;
	border-bottom: 1px solid var(--ab-line);
}

.tools-workspace__icon {
	display: inline-grid;
	flex: none;
	place-items: center;
	width: 56px;
	height: 56px;
	border-radius: 16px;
	background: var(--ab-gold-soft);
	color: var(--ab-gold);
}

.tools-workspace__icon .icon {
	width: 26px;
	height: 26px;
	vertical-align: 0;
}

body.canvas-dark .main .tools-workspace__head h2 {
	margin: 4px 0 0;
	font-size: clamp(22px, 2.4vw, 28px);
	line-height: 1.15;
}

.tools-workspace__head p {
	margin: 8px 0 0;
	max-width: 640px;
	color: var(--ab-muted);
	font-size: 15px;
	line-height: 1.5;
}

.tools-form {
	display: grid;
	gap: 16px;
}

.tools-fields {
	display: grid;
	grid-template-columns: repeat(3, minmax(0, 1fr));
	gap: 14px 16px;
}

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

.tools-field span {
	color: var(--ab-muted);
	font-size: 12.5px;
	font-weight: 700;
}

body.canvas-dark .main .tools-field input,
body.canvas-dark .main .tools-field textarea {
	width: 100%;
	min-height: 46px;
	padding: 10px 12px;
	border: 1px solid rgba(245, 243, 238, .14);
	font-size: 16px;
	font-weight: 700;
}

body.canvas-dark .main .tools-field textarea {
	font-weight: 500;
}

.tools-checklist {
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: 10px;
	margin: 0;
	padding: 0;
	border: 0;
}

.tools-checklist legend {
	margin-bottom: 10px;
	color: var(--ab-muted);
	font-size: 12.5px;
	font-weight: 700;
}

.tools-check {
	display: flex;
	align-items: flex-start;
	gap: 12px;
	padding: 14px;
	border: 1px solid var(--ab-line);
	border-radius: 12px;
	background: rgba(245, 243, 238, .03);
	color: rgba(245, 243, 238, .86);
	font-size: 14px;
	line-height: 1.4;
	cursor: pointer;
	transition: border-color .15s ease, background-color .15s ease;
}

.tools-check:hover {
	border-color: rgba(245, 243, 238, .24);
}

.tools-check:has(input:checked) {
	border-color: var(--ab-gold-line);
	background: var(--ab-gold-soft);
	color: #fff;
}

body.canvas-dark .main .tools-check input {
	flex: none;
	width: 18px;
	height: 18px;
	min-height: 0;
	margin: 1px 0 0;
}

.tools-form__actions {
	display: flex;
	align-items: center;
	gap: 16px;
	padding-top: 4px;
}

.tools-form__actions span {
	color: var(--ab-muted);
	font-size: 13px;
}

.tools-calc {
	min-width: 180px;
	min-height: 50px;
	font-size: 16px;
}

.tools-result {
	--tone: #4ade80;
	--tone-bg: rgba(74, 222, 128, .08);
	--tone-line: rgba(74, 222, 128, .35);
	margin-top: 24px;
	padding: 22px;
	border: 1px solid var(--tone-line);
	border-radius: var(--ab-radius-lg);
	background: var(--tone-bg);
}

.tools-result.is-warn {
	--tone: #fbbf24;
	--tone-bg: rgba(251, 191, 36, .08);
	--tone-line: rgba(251, 191, 36, .35);
}

.tools-result.is-bad {
	--tone: #f87171;
	--tone-bg: rgba(248, 113, 113, .08);
	--tone-line: rgba(248, 113, 113, .35);
}

.tools-result--empty {
	border-style: dashed;
	border-color: var(--ab-line);
	background: transparent;
	text-align: center;
}

.tools-result--empty p {
	margin: 0;
	color: var(--ab-muted);
}

.tools-result__headline {
	margin: 0;
	color: var(--tone);
	font: 800 clamp(20px, 2.4vw, 26px)/1.2 Inter, ui-sans-serif, system-ui, sans-serif;
	letter-spacing: -0.01em;
}

.tools-result__metrics {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
	gap: 10px;
	margin: 18px 0 0;
}

.tools-metric {
	padding: 12px 14px;
	border: 1px solid var(--ab-line);
	border-radius: 12px;
	background: rgba(17, 19, 24, .55);
}

.tools-metric dt {
	color: var(--ab-muted);
	font-size: 11px;
	font-weight: 800;
	letter-spacing: .08em;
	text-transform: uppercase;
}

.tools-metric dd {
	margin: 4px 0 0;
	color: #fff;
	font-size: 18px;
	font-weight: 800;
}

.tools-metric.is-total {
	border-color: var(--tone-line);
}

.tools-metric.is-total dd {
	color: var(--tone);
}

.tools-result__summary {
	margin: 16px 0 0;
	color: rgba(245, 243, 238, .82);
	line-height: 1.5;
}

.tools-result__actions {
	margin-top: 16px;
}

.tools-snapshots {
	margin-top: 24px;
}

.tools-snapshots__list {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
	gap: 12px;
}

.tools-snapshot {
	display: grid;
	gap: 4px;
	padding: 14px;
	border: 1px solid var(--ab-line);
	border-radius: 12px;
}

.tools-snapshot strong {
	color: #fff;
}

.tools-snapshot span {
	color: var(--ab-muted);
	font-size: 12.5px;
}

.tools-snapshot p {
	margin: 4px 0 0;
	color: rgba(245, 243, 238, .82);
	font-size: 13.5px;
}

@media (max-width: 1100px) {
	.tools-fields {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}
}

@media (max-width: 860px) {
	.tools-layout {
		grid-template-columns: minmax(0, 1fr);
	}

	.tools-picker {
		position: static;
		flex-direction: row;
		gap: 8px;
		padding: 0 0 6px;
		overflow-x: auto;
		border: 0;
		background: none;
		scrollbar-width: none;
	}

	.tools-picker__group {
		display: none;
	}

	.tools-picker__item {
		flex: none;
		border: 1px solid var(--ab-line);
		border-radius: 999px;
		padding: 6px 14px 6px 6px;
		background: var(--ab-surface);
		white-space: nowrap;
	}

	.tools-picker__item.is-active {
		border-color: var(--ab-gold-line);
	}

	.tools-workspace {
		padding: 20px 16px;
	}

	.tools-checklist {
		grid-template-columns: minmax(0, 1fr);
	}
}

@media (max-width: 520px) {
	.tools-fields {
		gap: 12px;
	}

	.tools-workspace__head {
		flex-direction: column;
		gap: 12px;
	}

	.tools-form__actions {
		flex-direction: column;
		align-items: stretch;
	}
}

/* Tool snapshots (pages/tools/snapshots.php): each saved result is a
   card with its share controls, like an estimate's Issue & Share. */
.tools-snapshot-list {
	display: grid;
	gap: 14px;
}

.tools-snapshot-card {
	display: grid;
	grid-template-columns: minmax(0, 1.3fr) minmax(280px, 1fr);
	gap: 22px;
	padding: 20px 22px;
	border: 1px solid var(--ab-line);
	border-radius: var(--ab-radius-lg);
	background: var(--ab-surface);
	scroll-margin-top: 24px;
}

.tools-snapshot-card:target {
	border-color: var(--ab-gold-line);
	box-shadow: 0 0 0 3px rgba(242, 166, 90, .14);
}

.tools-snapshot-card__headline {
	margin: 8px 0 0;
	color: #4ade80;
	font-size: 19px;
	font-weight: 800;
	line-height: 1.3;
}

.tools-snapshot-card__headline.is-warn {
	color: #fbbf24;
}

.tools-snapshot-card__headline.is-bad {
	color: #f87171;
}

.tools-snapshot-card__summary {
	margin: 6px 0 0;
	color: var(--ab-muted);
	font-size: 14px;
	line-height: 1.5;
}

.tools-snapshot-card__share {
	display: grid;
	align-content: start;
	gap: 10px;
}

.tools-share-link {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 8px;
}

body.canvas-dark .main .tools-share-link input {
	flex: 1;
	min-width: 0;
	min-height: 38px;
	padding: 6px 10px;
	color: var(--ab-muted);
	font-size: 12.5px;
}

.tools-share-link [data-copy-link-confirm] {
	color: #4ade80;
	font-size: 12.5px;
	font-weight: 700;
}

body.canvas-dark .main .tools-share-email {
	margin: 0;
}

.tools-result__actions {
	display: flex;
	flex-wrap: wrap;
	gap: 10px;
}

@media (max-width: 860px) {
	.tools-snapshot-card {
		grid-template-columns: minmax(0, 1fr);
		gap: 14px;
	}
}

/* ------------------------------------------------------------------
   Partners directory (pages/partners/index.php): search + specialty /
   location filters, compact cards, "Load More Partners". The numbered
   pager (real ?page=N links) stays for desktop; on phones the one
   Load More button replaces it.
   ------------------------------------------------------------------ */
.partner-search {
	display: grid;
	grid-template-columns: minmax(220px, 2fr) repeat(3, minmax(140px, 1fr)) auto;
	gap: 12px;
	align-items: end;
	margin-bottom: 22px;
	padding: 16px;
	border: 1px solid var(--ab-line);
	border-radius: var(--ab-radius-lg);
	background: var(--ab-surface);
}

.partner-search__query {
	position: relative;
	display: block;
}

.partner-search__query .icon {
	position: absolute;
	left: 14px;
	top: 50%;
	transform: translateY(-50%);
	color: var(--ab-muted);
	vertical-align: 0;
}

body.canvas-dark .main .partner-search__query input {
	width: 100%;
	min-height: 46px;
	padding: 10px 14px 10px 42px;
	font-size: 15px;
}

.partner-search__field {
	display: grid;
	gap: 6px;
}

.partner-search__field span {
	color: var(--ab-muted);
	font-size: 12px;
	font-weight: 700;
}

body.canvas-dark .main .partner-search__field select,
body.canvas-dark .main .partner-search__field input {
	width: 100%;
	min-height: 46px;
}

.partner-search__more {
	grid-column: 1 / -1;
	order: 10;
}

.partner-search__more summary {
	display: inline-flex;
	color: var(--ab-gold);
	font-size: 13px;
	font-weight: 800;
	cursor: pointer;
}

.partner-search__more-fields {
	display: grid;
	grid-template-columns: repeat(2, minmax(140px, 220px));
	gap: 12px;
	margin-top: 12px;
}

.partner-search__actions {
	display: flex;
	align-items: center;
	gap: 12px;
}

.partner-search__actions .button {
	min-height: 46px;
	padding: 0 24px;
}

.partner-directory__head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 12px;
	margin-bottom: 14px;
}

.partner-directory__count {
	margin: 0;
	color: var(--ab-muted);
	font-size: 14px;
}

.partner-directory__count strong {
	color: var(--ab-ink-strong);
}

.partner-grid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
	gap: 14px;
}

.partner-card {
	position: relative;
	display: flex;
	flex-direction: column;
	gap: 10px;
	padding: 16px;
	border: 1px solid var(--ab-line);
	border-radius: var(--ab-radius-lg);
	background: var(--ab-surface);
	transition: border-color .15s ease, transform .15s ease;
}

.partner-card:hover,
.partner-card:focus-within {
	border-color: var(--ab-gold-line);
	transform: translateY(-2px);
}

.partner-card__top {
	display: flex;
	align-items: center;
	gap: 12px;
}

.partner-card__logo {
	position: relative;
	display: grid;
	flex: none;
	place-items: center;
	width: 52px;
	height: 52px;
	overflow: hidden;
	border-radius: 12px;
	background: #fff;
	color: #16181f;
	font-size: 20px;
	font-weight: 800;
}

.partner-card__logo img {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	padding: 5px;
	object-fit: contain;
	background: #fff;
}

.partner-card__title {
	min-width: 0;
}

body.canvas-dark .main .partner-card h3 {
	margin: 0;
	font-size: 15.5px;
	line-height: 1.3;
}

.partner-card h3 a {
	color: #fff;
}

/* The whole card opens the profile; Request Quote sits above it. */
.partner-card h3 a::after {
	content: "";
	position: absolute;
	inset: 0;
	border-radius: inherit;
}

.partner-card__specialty {
	display: block;
	margin-top: 3px;
	color: var(--ab-gold);
	font-size: 11px;
	font-weight: 800;
	letter-spacing: .08em;
	text-transform: uppercase;
}

.partner-card__services {
	margin: 0;
	overflow: hidden;
	color: rgba(245, 243, 238, .86);
	font-size: 14px;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.partner-card__meta {
	display: flex;
	flex-wrap: wrap;
	gap: 2px 12px;
	margin: 0;
	color: var(--ab-muted);
	font-size: 12.5px;
}

.partner-card__meta span {
	max-width: 100%;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.partner-card__quote {
	position: relative;
	z-index: 1;
	align-self: flex-start;
	margin-top: auto;
	color: var(--ab-gold);
	font-size: 13px;
	font-weight: 800;
}

.partner-card__quote::after {
	content: " \2192";
}

.partner-more {
	display: grid;
	justify-items: center;
	gap: 16px;
	margin-top: 26px;
}

.partner-more__button {
	min-width: 260px;
	min-height: 50px;
	font-size: 15px;
}

.partner-more__button[aria-busy="true"] {
	opacity: .7;
	pointer-events: none;
}

.partner-pager {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: center;
	gap: 6px;
	font-size: 13.5px;
}

.partner-pager a,
.partner-pager__current {
	display: inline-grid;
	place-items: center;
	min-width: 36px;
	min-height: 36px;
	padding: 0 10px;
	border: 1px solid var(--ab-line);
	border-radius: 8px;
	color: rgba(245, 243, 238, .82);
	font-weight: 700;
}

.partner-pager a:hover {
	border-color: var(--ab-gold-line);
	color: #fff;
}

.partner-pager__current {
	border-color: var(--ab-gold-line);
	background: var(--ab-gold-soft);
	color: var(--ab-gold);
}

.partner-pager__gap {
	color: var(--ab-muted);
}

@media (max-width: 1100px) {
	.partner-search {
		grid-template-columns: repeat(3, minmax(0, 1fr));
	}

	.partner-search__query {
		grid-column: 1 / -1;
	}
}

@media (max-width: 760px) {
	.partner-search {
		grid-template-columns: repeat(2, minmax(0, 1fr));
		gap: 10px;
		padding: 12px;
	}

	.partner-search__field:last-of-type {
		grid-column: 1 / -1;
	}

	.partner-search__actions {
		grid-column: 1 / -1;
	}

	.partner-search__actions .button {
		flex: 1;
	}

	.partner-search__more-fields {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}

	.partner-grid {
		grid-template-columns: minmax(0, 1fr);
		gap: 10px;
	}

	.partner-card {
		padding: 14px;
		gap: 8px;
	}

	/* Phones: no page numbers -- one Load More button. */
	.partner-pager {
		display: none;
	}

	.partner-more__button {
		width: 100%;
	}
}
