/**
 * Collage builder — chrome UI around the Konva stage (V1-inspired workspace).
 */

/* Fullscreen body reset (when using the builder page template). */
body.wccb-fullscreen {
	margin: 0;
	padding: 0;
	overflow: hidden;
	background: var(--color-bg, #F1F4F6);
}
body.wccb-fullscreen * { box-sizing: border-box; }

/* Remove every Storefront chrome the theme might emit. */
body.wccb-fullscreen .site-header,
body.wccb-fullscreen .site-footer,
body.wccb-fullscreen .storefront-handheld-footer-bar,
body.wccb-fullscreen #wpadminbar { display: none !important; }

body.wccb-fullscreen #collage-builder-root {
	width: 100vw;
	height: 100vh;
	/* Mobile Builder P1 (B3): dvh tracks the iOS URL bar so the bottombar
	 * CTA is never pushed below the fold; the vh line above is the
	 * fallback for engines without dynamic viewport units. */
	height: 100dvh;
	min-height: 0;
}

.wccb-app {
	display: grid;
	grid-template-columns: 1fr;
	grid-template-rows: auto 1fr auto; /* Mobile Builder P1: bars size themselves (min-heights below) */
	grid-template-areas:
		"top"
		"stage"
		"bottom";
	height: 100%;
	width: 100%;
	font-family: var(--font-body, 'DM Sans', system-ui, sans-serif);
	color: var(--color-ink, #1A1F22);
	background: var(--color-bg, #F1F4F6);
}

/* ── Top toolbar (V1-style: tool groups with separators) ── */

/* Phase O 2026-05-10: topbar consistency pass.
 * - Legacy --color-* tokens swapped to V2 (--surface, --border, --ink,
 *   --ink-muted, --accent, --accent-soft).
 * - Logo font fallback chain: 'DM Serif Display' (V1 legacy) → 'Fraunces'
 *   to match site canonical via var(--font-display). */
.wccb-topbar {
	grid-area: top;
	display: flex;
	align-items: center;
	gap: 8px;
	padding: 0 12px;
	background: var(--surface);
	border-bottom: 1px solid var(--border);
	min-height: 48px;
}

.wccb-topbar__brand {
	/* Match the site wordmark exactly (.wh-topnav__wordmark): italic Fraunces,
	 * terminal period, tight tracking. Slightly smaller than the 26px header so
	 * it fits the tool-dense 48px builder bar. */
	font-family: var(--font-display, 'Fraunces', 'Tiempos Headline', 'Cormorant Garamond', Georgia, serif);
	font-style: italic;
	font-weight: 400;
	font-size: 24px;
	line-height: 1;
	color: var(--ink);
	letter-spacing: -0.045em;
	margin-right: 4px;
	/* Brand is a link to the homepage — strip the default anchor chrome. */
	display: inline-flex;
	align-items: center;
	text-decoration: none;
	cursor: pointer;
	transition: opacity 120ms;
}
.wccb-topbar__brand:hover { opacity: .7; }
.wccb-topbar__brand:focus-visible {
	outline: 2px solid var(--accent);
	outline-offset: 3px;
	border-radius: 2px;
}
/* Full wordmark on desktop, compact "w." mark on mobile (set in the @media block). */
.wccb-topbar__brand-mark { display: none; }

/* Persistent save-status pill next to the brand. */
.wccb-save-pill {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	font-size: 11px;
	padding: 3px 10px;
	border-radius: 999px;
	background: #F1F4F6;
	color: #5A6063;
	transition: background 150ms, color 150ms;
	white-space: nowrap;
}
.wccb-save-pill--ok    { background: #E8F3EC; color: #1E6B45; }
.wccb-save-pill--busy  { background: #FAF1DF; color: #8A6418; }
.wccb-save-pill--fail  { background: #FBECEA; color: #A8362D; }
.wccb-save-pill::before {
	content: ''; width: 6px; height: 6px; border-radius: 50%; background: currentColor;
}
/* The busy state renders a spinner instead of the static dot. */
.wccb-save-pill--busy::before { display: none; }

/* Top-of-canvas Probo-down warning.
 * Phase P 2026-05-10: hardcoded hex (#FAF1DF/#C08A2E/#8A6418) → V2 warn
 * tokens (--warn-soft / --warn / --warn-soft-ink). Sharp corners per §09.
 * Phase R 2026-05-10: z-index 16 → 100 to overlay canvas chrome
 * (.wccb-zoom at z-index 50). */
.wccb-probo-warning {
	position: absolute;
	top: 8px; left: 50%;
	transform: translateX(-50%);
	background: var(--warn-soft);
	border: 1px solid var(--warn);
	color: var(--warn-soft-ink);
	padding: 6px 14px;
	border-radius: 0;
	font-size: 12px;
	z-index: 100;
	pointer-events: none;
	max-width: 90%;
}

/* Photo-load error retry overlay (per frame). */
.wccb-photo-error {
	background: rgba(139,31,24,.88);
	color: #fff;
	padding: 4px 10px;
	border-radius: 4px;
	font-size: 11px;
	text-align: center;
	cursor: pointer;
	position: absolute;
	top: 50%; left: 50%;
	transform: translate(-50%, -50%);
	pointer-events: auto;
}

/* Corner warning indicator — Option A "minimal dot, full chip".
 *
 * The frame-bar warn chip carries the label + popover with full detail.
 * The corner indicator is the wall-scan complement: a small Lucide-icon
 * dot that says "this frame has an issue" without competing with the
 * chip. Tapping the dot selects the frame so the bar's warn chip appears
 * and the customer can drill in via its popover.
 *
 * Tokens:
 *   --err    → blocking error (red)
 *   --warn   → non-blocking warning (amber, e.g. low DPI)
 *   --on-ink → glyph color (white on saturated bg)
 *   --bg     → 2px ring around the dot for contrast against frame photos
 */
.wccb-warning {
	/* Phase Z.16 2026-05-13: badge now sits fully INSIDE the frame
	 * (top:6 right:6, was top:-10 right:8 with half-overlap on the
	 * top edge). Half-overlap relied on overflow:visible on the
	 * parent, but at low zoom the centered upload button bled into
	 * the frame-bar + corner area. Containing the badge inside the
	 * frame keeps the Phase V clip safety AND keeps the badge visible.
	 * Bumped z-index so the badge stays on top of the upload button
	 * if a small frame's button reaches the corner. */
	position: absolute;
	top: 6px;
	right: 6px;
	width: 22px;
	height: 22px;
	border-radius: 50%;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	background: var(--err);
	color: var(--on-ink);
	cursor: pointer;
	pointer-events: auto;
	box-shadow: 0 0 0 2px var(--bg), 0 2px 6px rgba(14, 14, 12, .25);
	z-index: 30;
	border: 0;
	padding: 0;
	transition: transform 120ms var(--ease, cubic-bezier(.2, .7, .2, 1));
}
.wccb-warning--amber { background: var(--warn); }
.wccb-warning:hover,
.wccb-warning:focus-visible {
	transform: scale(1.12);
}
.wccb-warning svg {
	width: 14px;
	height: 14px;
	stroke: currentColor;
	fill: none;
	stroke-width: 2.2;
	stroke-linecap: round;
	stroke-linejoin: round;
}
/* Extended invisible hit target so it's easy to tap on phones. */
.wccb-warning::before {
	content: "";
	position: absolute;
	inset: -8px;
}

/* Hover tooltip — V2 tokens. Hover-only (CSS-driven, no JS aria-expanded
   handling, since clicking the badge selects the frame instead). */
.wccb-warning__bubble {
	position: absolute;
	top: calc(100% + 8px);
	right: -4px;
	min-width: 180px;
	max-width: 260px;
	padding: 8px 12px;
	background: var(--ink);
	color: var(--on-ink);
	font: 400 12px/1.4 var(--font-body);
	text-align: left;
	white-space: normal;
	box-shadow: var(--shadow-2);
	pointer-events: none;
	opacity: 0;
	transform: translateY(-4px);
	transition: opacity 140ms var(--ease, cubic-bezier(.2, .7, .2, 1)), transform 140ms var(--ease, cubic-bezier(.2, .7, .2, 1));
	z-index: 6;
}
.wccb-warning__bubble::before {
	content: "";
	position: absolute;
	top: -5px;
	right: 14px;
	border: 5px solid transparent;
	border-top: 0;
	border-bottom-color: var(--ink);
}
.wccb-warning:hover .wccb-warning__bubble,
.wccb-warning:focus-visible .wccb-warning__bubble {
	opacity: 1;
	transform: translateY(0);
}

@media (max-width: 700px) {
	.wccb-warning { width: 24px; height: 24px; }
	.wccb-warning svg { width: 15px; height: 15px; }
	.wccb-warning__bubble { font-size: 13px; max-width: min(260px, calc(100vw - 48px)); }
}

.wccb-topbar__back {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 32px;
	height: 32px;
	border-radius: var(--radius-pill);
	color: var(--ink-muted);
	text-decoration: none;
	transition: background 120ms, color 120ms;
}
.wccb-topbar__back:hover {
	background: var(--accent-soft);
	color: var(--ink);
}

.wccb-tool-group {
	display: flex;
	align-items: center;
	gap: 2px;
}

.wccb-tool-sep {
	width: 1px;
	height: 22px;
	background: var(--border);
	margin: 0 4px;
}

.wccb-tool-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 6px;
	min-width: 32px;
	height: 32px;
	padding: 0 10px;
	border: 0;
	background: transparent;
	color: var(--ink-muted);
	border-radius: var(--radius-pill);
	cursor: pointer;
	transition: background 120ms, color 120ms;
	font: inherit;
	font-size: 12px;
}

/* Force SVG strokes to follow the button's text colour regardless of theme rules. */
.wccb-tool-btn svg,
.wccb-topbar__back svg {
	width: 16px;
	height: 16px;
	fill: none !important;
	stroke: currentColor !important;
	stroke-width: 2;
	stroke-linecap: round;
	stroke-linejoin: round;
	flex-shrink: 0;
}
.wccb-tool-btn:hover:not(:disabled) {
	background: var(--accent-soft);
	color: var(--ink);
}
.wccb-tool-btn span { white-space: nowrap; }
@media (max-width: 1023px) {
	/* Icon-only collapse on tablet + phone — title/aria-label keep accessibility.
	 * Phase Z.11 2026-05-13: was 1200px, now 1023px so labels return on
	 * standard laptop widths (1024-1366). */
	.wccb-tool-btn span { display: none; }
}
.wccb-tool-btn:disabled { opacity: .35; cursor: not-allowed; }
.wccb-tool-btn--on {
	background: var(--accent-soft);
	color: var(--accent);
}
/* Pure icon-only variant — no label at any viewport. Used by the
 * top-right Deel / Sla op / Nieuw ontwerp buttons; the title and
 * aria-label keep them discoverable for hover + screen readers. */
.wccb-tool-btn--icon {
	width: 32px;
	padding: 0;
	justify-content: center;
}
/* Mobile overflow ("...") trigger. Hidden on desktop/tablet (>699px) where the
 * full toolbar shows every action; revealed only at <=699px, where the topbar
 * trims those actions (see the @max-699 block). The three dots: r:1 circles with
 * the inherited 2px tool-btn stroke read as filled dots. */
.wccb-more-btn { display: none; }
.wccb-more-btn svg circle { fill: currentColor !important; }

/* Phase Z.6 2026-05-12: .wccb-tool-toggle (Phase G slider toggles for
 * Snap + Linealen) removed — those controls are now native checkboxes
 * inside the Weergave popover. See .wccb-view-check below. */

.wccb-tool-select {
	height: 28px;
	padding: 0 8px;
	border: 1px solid var(--color-border, #E4E8EA);
	border-radius: var(--radius-sm, 4px);
	background: var(--color-surface, #FFF);
	font: inherit;
	font-size: 12px;
	color: var(--color-ink, #1A1F22);
}

/* Phase L 2026-05-10: topbar Raster pill — popover-trigger button that
 * mirrors the §26 Materiaal-switcher spec verbatim (--surface bg ·
 * --border-strong border · pill · 8×14 padding · 13px Inter 500).
 * Replaces the native <select> approach so the builder's grid-size
 * picker shares the same popover register as the prop-bar chips. */
/* Phase O Option A 2026-05-10: shrunk from §26 Materiaal-switcher spec
 * (8×14 padding) to a builder-toolbar variant (6×12) so the pill aligns
 * to the surrounding 32px tool-btn register. Effective height drops
 * from ~32 to ~28-30, sitting visually flush with .wccb-tool-btn (32).
 * Documented as a builder-only divergence from §26 — recommend adding
 * a "compact" variant to §26 if reused. The frame-bar prop chips
 * (Materiaal/Formaat/Opties/Rand) keep the canonical 8×14 register. */
/* Phase Z.7 2026-05-12: .wccb-tool-pill (Raster popover trigger) and
 * .wccb-view-trigger (standalone Weergave button) both removed — view
 * controls live inside the gear-icon Muur instellingen popover. The
 * chip + check classes below are still in use there. */

/* ── Raster chips + view checkboxes — live inside the wall popover ── */
/* Phase Z.10 2026-05-13: fixed 4-column grid (was flex-wrap). Locks
 * Uit · 1 cm · 2 cm · 5 cm evenly across the row, no wrapping. */
.wccb-view-chips {
	display: grid;
	grid-template-columns: repeat(4, 1fr);
	gap: 4px;
}
.wccb-view-chip {
	height: 32px;
	padding: 0 10px;
	border: 1px solid var(--border);
	background: var(--surface);
	color: var(--ink);
	font: 500 12px/1 var(--font-body);
	border-radius: var(--radius-pill);
	cursor: pointer;
	transition: background 120ms, color 120ms, border-color 120ms;
}
.wccb-view-chip:hover {
	background: var(--accent-soft);
	border-color: var(--accent-soft);
}
.wccb-view-chip.is-on {
	background: var(--ink);
	color: var(--on-ink, #FFF);
	border-color: var(--ink);
}
.wccb-popover__sep {
	height: 1px;
	background: var(--border);
	margin: 12px -4px;
}
.wccb-view-check {
	display: flex;
	align-items: center;
	gap: 10px;
	padding: 6px 2px;
	cursor: pointer;
	font: 500 13px/1.2 var(--font-body);
	color: var(--ink);
	user-select: none;
}
.wccb-view-check:hover { color: var(--accent); }
.wccb-view-check input[type="checkbox"] {
	width: 16px;
	height: 16px;
	accent-color: var(--accent);
	margin: 0;
	cursor: pointer;
	flex: 0 0 auto;
}

/* ── Phase Z.8 2026-05-12 — Redesigned wall popover ─────────────────
 * Sticky header (icon + title + close) → scrollable body containing
 * three sections (Afmetingen · Achtergrond · Weergave) separated by
 * hairline dividers. Toggle switches for the boolean view options.
 * Section headers carry a 13px Lucide icon left of the mono label.
 * Width bumped to 340px (from 320) to give the W×H row and swatch
 * grid more breathing room. */

.wccb-popover--redesigned {
	min-width: 320px;
	max-width: 340px;
	padding: 0;
	overflow: hidden;
}
.wccb-popover--redesigned.wccb-popover--sheet { max-width: none; }

.wccb-pop__head {
	display: flex;
	align-items: center;
	gap: 8px;
	padding: 12px 12px 10px;
	border-bottom: 1px solid var(--border);
	background: var(--surface);
	position: sticky;
	top: 0;
	z-index: 2;
}
/* Phase Z.9 2026-05-12: header icon removed; title sits flush-left. */
.wccb-pop__head-title {
	flex: 1 1 auto;
	font: 500 11px/1 var(--font-mono);
	text-transform: uppercase;
	letter-spacing: .14em;
	color: var(--ink);
}
.wccb-pop__head-close {
	flex: 0 0 auto;
	width: 24px;
	height: 24px;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	background: transparent;
	border: 0;
	color: var(--ink-muted);
	cursor: pointer;
	border-radius: 4px;
	transition: background 120ms, color 120ms;
}
.wccb-pop__head-close:hover {
	background: var(--bg-alt, #F1F4F6);
	color: var(--ink);
}

.wccb-pop__body {
	overflow-y: auto;
	overscroll-behavior: contain;
	padding: 12px;
	flex: 1 1 auto;
}

.wccb-pop__section { margin: 0; }
.wccb-pop__section + .wccb-pop__divider { margin: 12px -12px; }
/* Phase Z.9 2026-05-12: section icons removed; titles are plain mono text. */
.wccb-pop__section-title {
	font: 500 11px/1 var(--font-mono);
	text-transform: uppercase;
	letter-spacing: .14em;
	color: var(--ink-muted);
	margin: 0 0 10px;
}

.wccb-pop__divider {
	height: 1px;
	background: var(--border);
}

.wccb-pop__field-label {
	font: 500 11px/1 var(--font-body);
	color: var(--ink-muted);
	margin: 0 0 6px;
}

.wccb-pop__clear { width: 100%; margin-top: 8px; }

/* Toggle switch — replaces the plain checkbox for boolean view options.
 * Native checkbox stays the source of truth (so `change` handler works
 * untouched); the track + thumb are visual overlay siblings styled via
 * `:checked` on the input. Track + thumb are tokenised. */
.wccb-pop__toggle {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 12px;
	padding: 10px 0;
	font: 500 13px/1.2 var(--font-body);
	color: var(--ink);
	cursor: pointer;
	user-select: none;
}
.wccb-pop__toggle:hover { color: var(--accent); }
.wccb-pop__toggle + .wccb-pop__toggle { border-top: 1px solid var(--border); }
.wccb-pop__toggle:first-of-type { margin-top: 10px; border-top: 1px solid var(--border); }
.wccb-switch {
	position: relative;
	display: inline-block;
	width: 32px;
	height: 18px;
	flex: 0 0 auto;
}
.wccb-switch input {
	position: absolute;
	inset: 0;
	opacity: 0;
	width: 100%;
	height: 100%;
	margin: 0;
	cursor: pointer;
}
.wccb-switch__track {
	display: block;
	width: 100%;
	height: 100%;
	background: var(--border-strong);
	border-radius: 999px;
	transition: background 140ms var(--ease, cubic-bezier(.2, .7, .2, 1));
}
.wccb-switch__thumb {
	position: absolute;
	top: 2px;
	left: 2px;
	width: 14px;
	height: 14px;
	background: #fff;
	border-radius: 50%;
	box-shadow: 0 1px 2px rgba(14, 14, 12, .25);
	transition: transform 140ms var(--ease, cubic-bezier(.2, .7, .2, 1));
}
.wccb-switch input:checked ~ .wccb-switch__track {
	background: var(--accent);
}
.wccb-switch input:checked ~ .wccb-switch__track .wccb-switch__thumb {
	transform: translateX(14px);
}
.wccb-switch input:focus-visible ~ .wccb-switch__track {
	outline: 2px solid var(--accent);
	outline-offset: 2px;
}

/* Phase Z.7 2026-05-12: responsive topbar tiers (Weergave-trigger
 * references stripped after the trigger moved into the wall popover).
 *
 * Strategy: as width drops, prefer collapsing labels → icon-only over
 * hiding controls outright. Only hide truly redundant chrome (save-pill
 * "· Opgeslagen" status text, the brand wordmark on phone).
 *
 * ≥1280 (desktop):  Default. Full topbar — labels + icons everywhere.
 * 960-1279 (laptop): Object-action labels (Centreer / Dupliceer) → icon-only.
 * 700-959  (tablet): Save-pill hides.
 * <700     (phone):  Brand wordmark → icon-only. Save-later icon hides.
 *                    Wall popover renders as bottom sheet (handled
 *                    in JS via .wccb-popover--sheet class).
 */
/* Phase Z.13 2026-05-13: reverted to 1279px. The 1023 tier (Z.11) fit
 * the left-side labels but overflowed once Opslaan / Nieuw /
 * Instellingen got labels too (Z.12) — the topbar ran off-screen
 * between 1024 and 1279. Below 1280 there isn't enough horizontal
 * runway for every label; collapse to icon-only in the topbar in that
 * band. The universal rule above (max-width:1023) still covers
 * bottombar + other regions. */
@media (max-width: 1279px) {
	.wccb-topbar .wccb-tool-btn:not(.wccb-tool-btn--icon) span {
		display: none;
	}
	.wccb-topbar .wccb-tool-btn:not(.wccb-tool-btn--icon) {
		min-width: 32px;
		width: 32px;
		padding: 0;
	}
}
@media (max-width: 959px) {
	.wccb-save-pill { display: none; }
}
@media (max-width: 699px) {
	/* Keep brand presence on mobile but as the compact "w." mark (the full
	 * wordmark is too wide for the tool-dense bar). */
	.wccb-topbar__brand-full { display: none; }
	.wccb-topbar__brand-mark { display: inline; }
	.wccb-topbar .wccb-tool-btn[data-act="save-later"] { display: none; }
	/* Wall popover renders as a bottom sheet on phone — handled in
	 * JS via .wccb-popover--sheet, no CSS needed here. */

	/* Mobile topbar fit (2026-06-12, updated 2026-06-15): the right-side actions
	 * were pushed off-screen by the flex:1 spacer + too many tools. Drop the
	 * spacer + trim secondary / desktop-only tools so the essential set fits one
	 * row: back · undo/redo · zoom · Muur · ⋯. Fullscreen is removed outright (the
	 * Fullscreen API isn't supported on mobile browsers anyway). overflow-x is a
	 * safety net. The trimmed actions (Nieuw + Centreer / Dupliceer / Passend /
	 * Deel / Opslaan) are reachable via the "..." overflow menu (.wccb-more-btn). */
	.wccb-topbar { gap: 4px; overflow-x: auto; }
	.wccb-topbar__spacer { display: none; }
	.wccb-topbar [data-act="fullscreen"],
	.wccb-topbar [data-act="zoom-fit"],
	.wccb-topbar [data-act="share"],
	.wccb-topbar [data-act="center"],
	.wccb-topbar [data-act="duplicate-collage"],
	.wccb-topbar [data-act="new-design"],
	.wccb-topbar .wccb-zoom__sep,
	.wccb-topbar .wccb-tool-sep { display: none; }
	/* Reveal the overflow trigger only here (it's display:none >699px). */
	.wccb-more-btn { display: inline-flex; }
	/* Right-align the remaining action group (Muur instellingen + ⋯): the spacer
	 * is display:none here, so push them with margin-left:auto on the first of the
	 * pair. Degrades safely to packed-left + scroll if a very narrow phone overflows. */
	.wccb-topbar [data-act="bb-wall"] { margin-left: auto; }
}

.wccb-topbar__spacer { flex: 1; }

/* multi-select: frame-bar group morph (2026-05-30): the topbar-docked
   .wccb-group-bar was removed — the group affordance now lives inside the
   floating frame-bar (.wccb-frame-bar__count + .wccb-frame-bar__del, styled
   alongside the frame-bar chrome below). */

/* ── Sidebar (accordion panels) ── */

.wccb-panel {
	background: var(--color-surface, #FFF);
	border-right: 1px solid var(--color-border, #E4E8EA);
	overflow-y: auto;
	padding: 0;
}
.wccb-panel--left { grid-area: left; }

.wccb-accordion {
	border-bottom: 1px solid var(--color-border, #E4E8EA);
}
.wccb-accordion__head {
	width: 100%;
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding: 12px 16px;
	background: transparent;
	border: 0;
	font: inherit;
	font-size: 13px;
	font-weight: 600;
	color: var(--color-ink, #1A1F22);
	cursor: pointer;
	text-transform: uppercase;
	letter-spacing: .04em;
}
.wccb-accordion__head:hover { background: var(--color-accent-soft, #F1F4F6); }
.wccb-accordion__chev {
	transition: transform 150ms;
	width: 12px; height: 12px;
}
.wccb-accordion[data-open="true"] .wccb-accordion__chev { transform: rotate(180deg); }
.wccb-accordion__body {
	padding: 0 16px 14px;
	display: none;
}
.wccb-accordion[data-open="true"] .wccb-accordion__body { display: block; }

/* Wall chips */
.wccb-chips {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
	margin-top: 8px;
}
.wccb-chip {
	padding: 6px 10px;
	border: 1px solid var(--color-border, #E4E8EA);
	border-radius: var(--radius-sm, 4px);
	background: var(--color-surface, #FFF);
	font: inherit;
	font-size: 12px;
	color: var(--color-ink, #1A1F22);
	cursor: pointer;
	transition: all 120ms;
}
.wccb-chip:hover,
.wccb-chip--on {
	background: var(--color-accent, #235C68);
	color: #fff;
	border-color: var(--color-accent, #235C68);
}

/* Wall dims row */
.wccb-wall-row {
	display: grid;
	grid-template-columns: 1fr auto 1fr auto;
	gap: 6px;
	align-items: center;
	margin-bottom: 4px;
}
.wccb-wall-row input {
	width: 100%;
	height: 32px;
	padding: 0 8px;
	border: 1px solid var(--color-border, #E4E8EA);
	border-radius: var(--radius-sm, 4px);
	background: var(--color-surface, #FFF);
	font: inherit;
	font-size: 13px;
}
.wccb-wall-row__unit {
	color: var(--color-ink-muted, #5A6063);
	font-size: 12px;
}

/* Frame-size preset grid */
.wccb-sizes-label {
	font-size: 11px;
	text-transform: uppercase;
	letter-spacing: .06em;
	color: var(--color-ink-muted, #5A6063);
	margin: 10px 0 6px;
}
.wccb-sizes {
	display: grid;
	grid-template-columns: repeat(4, 1fr);
	gap: 6px;
}
.wccb-size-btn {
	padding: 8px 4px;
	border: 1px solid var(--color-border, #E4E8EA);
	border-radius: var(--radius-sm, 4px);
	background: var(--color-surface, #FFF);
	font: inherit;
	font-size: 11px;
	color: var(--color-ink, #1A1F22);
	cursor: pointer;
	text-align: center;
}
.wccb-size-btn:hover {
	background: var(--color-accent-soft, #F1F4F6);
	border-color: var(--color-accent, #235C68);
}
.wccb-size-btn--pop {
	background: var(--color-accent-soft, #F1F4F6);
	border-color: var(--color-accent, #235C68);
	color: var(--color-accent, #235C68);
	font-weight: 600;
}

/* Selected frame properties panel (inline under Frames accordion) */
.wccb-frame-props {
	border-top: 1px dashed var(--color-border, #E4E8EA);
	padding-top: 8px;
	margin-top: 10px;
}

/* ── Bottom bar ── */

.wccb-bottombar {
	grid-area: bottom;
	display: flex;
	align-items: center;
	gap: 12px;
	padding: 0 16px;
	background: var(--color-surface, #FFF);
	border-top: 1px solid var(--color-border, #E4E8EA);
	min-height: 60px;
}
.wccb-bottombar__info {
	display: flex;
	align-items: center;
	gap: 8px;
	color: var(--color-ink-muted, #5A6063);
	font-size: 13px;
}
.wccb-bottombar__sep {
	color: var(--color-border, #E4E8EA);
}
/* margin-left:auto on __price (first of the right cluster) pushes
   price + vat + button together to the right edge. DOM order:
   __price → __vat → button, so "incl. BTW" reads after the number. */
.wccb-bottombar__price {
	margin-left: auto;
	font-family: var(--font-display, serif);
	font-size: 22px;
	font-weight: 600;
	color: var(--color-ink, #1A1F22);
}
.wccb-bottombar__vat {
	font-size: 11px;
	color: var(--color-ink-muted, #5A6063);
}
/* "Inclusief ophangset[ & installatieposter] ⓘ" — lives in the LEFT info
   row alongside piece-count + material + save-status (not under the price).
   Inline so it sits on the same baseline as its siblings; tooltip anchors
   below-left so it doesn't overflow off-screen near the left edge. */
.wccb-bottombar__included {
	position: relative;
	display: inline-flex;
	align-items: center;
	gap: 6px;
}
.wccb-bottombar__included[hidden] {
	display: none;
}
.wccb-bottombar__included-label {
	font-family: var(--font-body, sans-serif);
}
.wccb-bottombar__included-info {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 18px;
	height: 18px;
	padding: 0;
	margin: 0;
	background: transparent;
	border: 0;
	border-radius: 50%;
	color: var(--color-ink-muted, #5A6063);
	cursor: pointer;
	transition: color 120ms ease;
}
.wccb-bottombar__included-info:hover,
.wccb-bottombar__included-info[aria-expanded="true"] {
	color: var(--color-ink, #1A1F22);
}
.wccb-bottombar__included-info:focus-visible {
	outline: 2px solid var(--color-ink, #1A1F22);
	outline-offset: 2px;
}
.wccb-bundle-tooltip {
	position: absolute;
	bottom: calc(100% + 8px);
	left: 0;
	min-width: 280px;
	max-width: 340px;
	padding: 12px 14px;
	background: var(--color-ink, #1A1F22);
	color: var(--color-on-ink, #FFFFFF);
	font-size: 12px;
	line-height: 1.45;
	border-radius: var(--radius-1, 6px);
	box-shadow: var(--shadow-2, 0 6px 24px rgba(0, 0, 0, 0.15));
	z-index: 50;
}
.wccb-bundle-tooltip[hidden] {
	display: none;
}
.wccb-bundle-tooltip__title {
	font-family: var(--font-mono, monospace);
	font-size: 10px;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--color-on-ink, #FFFFFF);
	opacity: 0.7;
	margin-bottom: 8px;
}
.wccb-bundle-tooltip__list {
	list-style: none;
	margin: 0;
	padding: 0;
}
.wccb-bundle-tooltip__list li {
	padding-left: 12px;
	margin-bottom: 4px;
	position: relative;
}
.wccb-bundle-tooltip__list li::before {
	content: "•";
	position: absolute;
	left: 0;
	color: var(--color-on-ink, #FFFFFF);
	opacity: 0.5;
}
.wccb-bundle-tooltip__list li:last-child {
	margin-bottom: 0;
}

/* Stage-wrap — positioning context for the zoom + canvas-tools sibling
   pills. .wccb-stage itself can't host them because Konva wipes its
   innerHTML on mount. */
.wccb-stage-wrap {
	position: relative;
	grid-area: stage;
	display: flex;
	min-height: 0;
	min-width: 0;
}
.wccb-stage-wrap > .wccb-stage {
	flex: 1 1 auto;
	grid-area: auto; /* override the original .wccb-stage grid-area now that the wrap owns the slot */
	min-width: 0;
	min-height: 0;
}

/* ── Canvas overlay controls — bottom-left zoom pill + bottom-right
   raster + fullscreen group, matching Wandhaus design system spec
   (Builder.jsx lines 280-308). ── */

/* Phase J 2026-05-10: dropped z-index 9999 → 50. Canvas chrome (zoom +
 * canvas-tools) sits above Konva/overlays but BELOW interactive
 * popovers (.wccb-popover at 9997, .wccb-frame-bar at 9996,
 * .wccb-reposition-bar at 9998) so size-popover & friends overlay
 * the chrome cleanly when active.
 * Phase R 2026-05-10 (Option A): tightened — 4 → 3 px padding, hex
 * (#FFFFFF/#F1F4F6) → V2 tokens, double box-shadow → --shadow-1.
 * Combined with the 36 → 32 px buttons below, cluster total drops
 * from ~44 to ~38 px — sits visually flush with topbar tool-btns. */
/* Phase Z.1 2026-05-12: zoom controls live in the topbar now.
 * Phase Z.1a 2026-05-12: dropped the pill chrome (background, border,
 * shadow, padding) so the cluster visually blends with the rest of
 * the topbar icon buttons (.wccb-tool-btn). Internal vertical
 * dividers (.wccb-zoom__sep) stay — they group zoom-±/level vs
 * passend vs fullscreen logically. */
.wccb-zoom {
	display: inline-flex;
	align-items: center;
	gap: 2px;
	pointer-events: auto;
}
/* Phase N 2026-05-10: shrunk from 40 → 36 px.
 * Phase R 2026-05-10 (Option A): further shrunk 36 → 32 px (matches
 * topbar tool-btn register exactly). Font 16 → 14. Hex #1A1F22 →
 * var(--ink). Cluster total height now ~38 px. */
.wccb-zoom__btn {
	width: 32px;
	height: 32px;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 6px;
	border: 0;
	background: transparent;
	color: var(--ink);
	cursor: pointer;
	font-family: var(--font-body);
	font-size: 14px;
	font-weight: 600;
	line-height: 1;
	border-radius: var(--radius-pill);
	transition: background .15s ease;
}
.wccb-zoom__btn svg {
	flex-shrink: 0;
}
.wccb-zoom__btn:hover { background: var(--accent-soft, #E6EEEF); }
/* Phase Y 2026-05-12: dropped .wccb-zoom__btn--text — zoom-pill is
   now icon-only, no inline button labels (Figma/Miro convention). */
.wccb-zoom__sep {
	width: 1px;
	align-self: stretch;
	background: var(--border, #F1F4F6);
	margin-inline: 4px;
}
.wccb-zoom__level {
	min-width: 56px;
	height: 32px;
	padding: 0 10px;
	border: 0;
	background: transparent;
	font-family: var(--font-body);
	font-size: 13px;
	font-weight: 600;
	color: var(--ink);
	cursor: pointer;
	border-radius: var(--radius-pill);
	transition: background .15s ease;
}
.wccb-zoom__level:hover { background: var(--accent-soft, #E6EEEF); }

/* Phase M 2026-05-10: .wccb-canvas-tools (the bottom-right canvas-chrome
 * island) was deleted entirely. Its only remaining occupant — the
 * Volledig scherm pill — was folded into .wccb-zoom on the top-left as
 * a `.wccb-zoom__btn` after a separator. F-key fullscreen shortcut still
 * hits [data-act="fullscreen"]. Phase K (Raster pill → topbar) and
 * Phase L (popover trigger) had already evacuated the other tenants. */

/* Phase Z.1 2026-05-12: dropped the mobile pin for .wccb-zoom — now
 * it lives in the topbar and flows naturally. The canvas FAB takes
 * the bottom-left anchor instead (see .wccb-canvas-fab above). */

/* Bottom-sheet variant — applied to popovers under 700px so they take
 * full width and dock to the viewport bottom instead of floating
 * around the anchor. Inline styles set left/right/bottom/top/maxHeight
 * at open time; this class adds the visual chrome (rounded top, drag
 * indicator, fade-in animation). */
.wccb-popover.wccb-popover--sheet {
	min-width: 0 !important;
	max-width: none !important;
	width: 100%;
	border-radius: 16px 16px 0 0;
	padding-top: 18px;
	box-shadow: 0 -8px 32px rgba(0,0,0,.18);
}
.wccb-popover.wccb-popover--sheet::before {
	content: '';
	position: absolute;
	top: 8px; left: 50%;
	transform: translateX(-50%);
	width: 36px; height: 4px;
	border-radius: 2px;
	background: var(--color-border, #E4E8EA);
}

/* Coord readout while dragging */
.wccb-coords {
	position: absolute;
	top: 12px;
	left: 50%;
	transform: translateX(-50%);
	background: rgba(26,26,24,.85);
	color: #fff;
	padding: 4px 10px;
	border-radius: var(--radius-sm, 4px);
	font-size: 11px;
	font-variant-numeric: tabular-nums;
	pointer-events: none;
	z-index: 20;
	display: none;
}

.wccb-panel h3 {
	font-family: var(--font-display, 'DM Serif Display', Georgia, serif);
	font-size: .95rem;
	margin: 0 0 var(--space-2, 8px);
	letter-spacing: .02em;
	text-transform: uppercase;
	color: var(--color-ink-muted, #5A6063);
}

.wccb-field {
	display: flex;
	flex-direction: column;
	gap: 4px;
	margin-bottom: var(--space-3, 12px);
}

.wccb-field label {
	font-size: .75rem;
	color: var(--color-ink-muted, #5A6063);
}

.wccb-field input,
.wccb-field select {
	padding: 8px 10px;
	border: 1px solid var(--color-border, #E4E8EA);
	border-radius: var(--radius-sm, 4px);
	background: var(--color-surface, #FFF);
	font: inherit;
	color: inherit;
}

.wccb-stage {
	grid-area: stage;
	position: relative;
	background: var(--color-bg, #F1F4F6);
	overflow: hidden;
	isolation: isolate; /* fresh stacking context so z-index values inside are self-contained */
}

.wccb-stage__hint {
	position: absolute;
	top: 50%;
	left: 50%;
	transform: translate(-50%, -50%);
	color: var(--color-ink-muted, #5A6063);
	font-size: .875rem;
	text-align: center;
	pointer-events: none;
	max-width: 320px;
}

.wccb-stage__toolbar {
	position: absolute;
	bottom: 12px;
	left: 50%;
	transform: translateX(-50%);
	display: flex;
	gap: 6px;
	padding: 6px;
	background: var(--color-surface, #FFF);
	border: 1px solid var(--color-border, #E4E8EA);
	border-radius: var(--radius-md, 8px);
	box-shadow: var(--shadow-card, 0 1px 3px rgba(0,0,0,.06));
}

/* V2 token migration (2026-05-04): mirrors canonical `.btn-primary` /
   `.btn-accent` / `.btn-ghost` from tokens.css. Pure tokens, no hex
   fallbacks (design-system style). Danger has no design-system home —
   `--err` bg + brightness filter on hover keeps the destructive cue
   without inventing an off-system "darker err" color. */
/* Phase E 2026-05-10: aligned border-radius to §02 Knoppen pill (999px).
 * Phase F 2026-05-10: aligned full sizing/padding/typography to canonical
 *   .btn register (tokens.css §Buttons). Was inline+text-flow with
 *   vertical+horizontal padding (~33 px tall). Now flex+height-based:
 *   44 px tall, 0×20 horizontal padding, font 500 14/1, reserved
 *   `1px transparent` border so ghost variant doesn't introduce a 2 px
 *   size jump. JS-bound `.wccb-btn` selectors unchanged. */
.wccb-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 8px;
	height: 44px;
	padding: 0 20px;
	appearance: none;
	border: 1px solid transparent;
	background: var(--ink);
	color: var(--bg);
	font: 500 14px/1 var(--font-body);
	letter-spacing: .01em;
	text-decoration: none;
	white-space: nowrap;
	border-radius: var(--radius-pill);
	cursor: pointer;
	transition: all .18s var(--ease);
}
.wccb-btn:hover { background: var(--accent, #235C68); color: var(--on-ink, #FFFFFF); }

.wccb-btn--primary { background: var(--accent, #235C68); color: var(--on-ink, #FFFFFF); }
.wccb-btn--primary:hover { background: var(--accent-ink, #1A4651); color: var(--on-ink, #FFFFFF); }

.wccb-btn--ghost {
	background: transparent;
	color: var(--ink);
	border: 1px solid var(--border-strong);
}
.wccb-btn--ghost:hover {
	background: var(--ink);
	color: var(--bg);
	border-color: var(--ink);
}

/* Branded confirm-modal "destructive" variant — used by confirmModal() with
   { danger: true } for actions like "Start a new design (loses current work)".
   The design system has no err-ink darker pair; hover uses `filter:
   brightness(.88)` so we don't introduce a new off-system color. */
.wccb-btn--danger {
	background: var(--err);
	color: var(--on-ink);
}
.wccb-btn--danger:hover { filter: brightness(.88); color: var(--on-ink); }

.wccb-btn--small { height: 34px; padding: 0 14px; font-size: 13px; }

.wccb-summary {
	display: flex;
	flex-direction: column;
	gap: 6px;
	margin-top: var(--space-3, 12px);
	padding-top: var(--space-3, 12px);
	border-top: 1px solid var(--color-border, #E4E8EA);
}
.wccb-summary__row {
	display: flex;
	justify-content: space-between;
	font-size: .875rem;
}
.wccb-summary__total {
	font-weight: 600;
	font-size: 1rem;
	padding-top: 6px;
	border-top: 1px solid var(--color-border, #E4E8EA);
}

.wccb-frame-list {
	display: flex;
	flex-direction: column;
	gap: 8px;
	margin-bottom: var(--space-3, 12px);
}

.wccb-frame-list__item {
	display: flex;
	justify-content: space-between;
	align-items: center;
	padding: 8px 10px;
	background: var(--color-accent-soft, #F1F4F6);
	border-radius: var(--radius-sm, 4px);
	font-size: .85rem;
	cursor: pointer;
}
.wccb-frame-list__item.is-selected {
	outline: 2px solid var(--color-accent, #235C68);
}

.wccb-status {
	font-size: .75rem;
	color: var(--color-ink-muted, #5A6063);
	min-height: 1em;
}

.wccb-loading {
	padding: 24px;
	text-align: center;
	color: var(--color-ink-muted, #5A6063);
}

/* ---- Frame overlays (in-frame actions, upload prompts) ---- */

.wccb-overlays {
	position: absolute;
	inset: 0;
	pointer-events: none; /* individual overlays re-enable it */
	z-index: 10; /* Konva canvas paints at 0 — we must sit on top of it */
}

/* Konva drops a .konvajs-content div into the stage container; keep it below overlays. */
.wccb-stage .konvajs-content { z-index: 1; }

.wccb-overlay {
	position: absolute;
	pointer-events: none; /* pass clicks through to Konva by default */
	box-sizing: border-box;
	display: flex;
	align-items: center;
	justify-content: center;
	font-family: var(--font-body, system-ui);
	transition: outline-color 120ms;
}

/* Empty-frame overlay — fully transparent. The frame's own outline
   (drawn on the Konva canvas) is the visible boundary; the round
   icon-only upload button is the only visible UI from the DOM
   overlay. The previous dashed border + warm-paper tint felt like
   an extra "frame" sitting on top of the actual frame, which the
   customer didn't ask for. */
.wccb-overlay--empty {
	border: 0;
	background: transparent;
	color: var(--color-ink-muted, #5A6063);
	font-size: 13px;
	text-align: center;
}

/* Reposition mode: full-frame outline + grab cursor so the affordance is unmistakable. */
.wccb-overlay--repositioning {
	outline: 2px solid var(--color-accent, #235C68);
	outline-offset: -2px;
}

/* ───────────── Shared loader primitive ─────────────
 * One spinner glyph used everywhere (save pill, frame uploads, future modals).
 * Size variants via modifier classes so consumers stay declarative.
 */
.wccb-loader {
	display: inline-block;
	border-radius: 50%;
	border: 2px solid currentColor;
	border-top-color: transparent;
	border-right-color: transparent;
	animation: wccb-loader-spin 700ms linear infinite;
	vertical-align: middle;
}
.wccb-loader--sm { width: 12px; height: 12px; border-width: 2px; }
.wccb-loader--md { width: 20px; height: 20px; border-width: 2.5px; }
.wccb-loader--lg { width: 32px; height: 32px; border-width: 3px; }

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

/* Save pill, busy state — spinner sits inline with the label. */
.wccb-save-pill--busy .wccb-loader {
	margin-right: 6px;
	color: currentColor;
}

/* Skeleton placeholder — shimmering block used in the bottom bar while the
 * price is being calculated. Same visual language as the loader (warm neutrals
 * + subtle animation) so the builder has one coherent "working" look. */
.wccb-skeleton {
	display: inline-block;
	background: linear-gradient(
		90deg,
		rgba(26, 26, 24, .06) 0%,
		rgba(26, 26, 24, .14) 50%,
		rgba(26, 26, 24, .06) 100%
	);
	background-size: 200% 100%;
	border-radius: 6px;
	animation: wccb-skeleton-shimmer 1200ms ease-in-out infinite;
	vertical-align: middle;
}
.wccb-skeleton--price { width: 88px; height: 26px; }

@keyframes wccb-skeleton-shimmer {
	0%   { background-position: 100% 0; }
	100% { background-position: -100% 0; }
}

/* Mid-refresh state: existing price stays visible but dimmed + a tiny
 * pulsing dot appears next to it, so price recalculations after a resize
 * or material change don't strobe a skeleton over the layout. Only fires
 * when at least one frame already has a computed price; the very first
 * recalc still shows the full skeleton (no stale value to dim). */
[data-el="bottom-total"].is-refreshing {
	position: relative;
	opacity: .55;
	transition: opacity 200ms;
}
[data-el="bottom-total"].is-refreshing::after {
	content: "";
	display: inline-block;
	width: 6px;
	height: 6px;
	margin-left: 6px;
	border-radius: 50%;
	background: var(--color-ink-muted, #5A6063);
	vertical-align: middle;
	animation: wccb-pulse-dot 900ms ease-in-out infinite;
}
@keyframes wccb-pulse-dot {
	0%, 100% { opacity: .25; transform: scale(0.85); }
	50%      { opacity: 1;    transform: scale(1.15); }
}

/* Inline spinner used inside primary buttons during async work
 * (add-to-cart). White-on-terracotta context so the ring uses translucent
 * white over current background. */
.wccb-btn__spinner {
	display: inline-block;
	width: 14px;
	height: 14px;
	margin-right: 8px;
	border: 2px solid rgba(255, 255, 255, .35);
	border-top-color: #fff;
	border-radius: 50%;
	vertical-align: -2px;
	animation: wccb-btn-spin 700ms linear infinite;
}
.wccb-btn.is-busy { cursor: progress; opacity: .85; }
@keyframes wccb-btn-spin {
	to { transform: rotate(360deg); }
}

/* Frame upload in progress: fixed to <body>, positioned at the frame's viewport
 * coords (same pattern as the floating chip bar). Very high z-index so no
 * ancestor overflow / stacking / paint order can hide it. */
.wccb-frame-loader {
	position: fixed;
	left: 0;
	top: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	background: rgba(26, 26, 24, .55);
	pointer-events: auto;
	z-index: 9997;
	box-sizing: border-box;
	animation: wccb-frame-loader-in 140ms ease-out;
}
@keyframes wccb-frame-loader-in {
	from { opacity: 0; }
	to   { opacity: 1; }
}
.wccb-frame-loader__card {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 10px;
	padding: 14px 18px;
	background: var(--color-surface, #FFF);
	border-radius: var(--radius-md, 8px);
	box-shadow: 0 6px 20px rgba(0, 0, 0, .22);
	color: var(--color-accent, #235C68);
	font-size: 12px;
	font-weight: 600;
	text-align: center;
	max-width: calc(100% - 24px);
}
.wccb-frame-loader__label {
	color: var(--color-ink, #1A1F22);
	line-height: 1.3;
}

/* ── Empty-frame "dropzone" upload surface ──────────────────────────
   Mirrors the design-system Dropzone component (Wandhaus Design System
   §27 "Builder · in-frame upload"): dashed border + bg-alt fill +
   centered icon + drag-drop CTA. Three size tiers driven by the
   `wccb-overlay--compact` / `wccb-overlay--roomy` classes set in JS:
     • roomy (≥180×120) → full dropzone with icon + heading + sub-label
     • default          → icon + short heading
     • compact (<60)    → no UI, frame bar handles upload

   Tokens:
     --border-strong → idle dashed border
     --bg-alt        → idle surface tint
     --ink           → hover border + heading color
     --accent        → drag-over border + "selecteer" link
     --accent-soft   → drag-over surface tint */

.wccb-overlay--empty {
	/* No border + no fill — the dashed border overlapped neighbouring frame
	   outlines on dense layouts, and the hover bg-alt wash made the empty
	   frame read as "selected" before the customer had clicked it. The
	   icon-button cluster below is now the only visible affordance; the
	   drag-over state is expressed on the cluster, not the whole frame. */
	background: transparent;
	box-sizing: border-box;
	/* Clicks + drags pass through to the Konva frame underneath. Inner
	   button has its own pointer-events: auto for click-to-upload. Global
	   `body.wccb-dragging-file` flips this to auto during an OS file drag
	   so the full frame area becomes a drop target. */
	pointer-events: none;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: var(--s-2, 8px);
	padding: var(--s-3, 12px);
	/* Phase V 2026-05-11: clip overflow so the upload-button + meta
	 * stack doesn't escape the frame box at low zoom levels (the
	 * `--with-meta` tier class set in JS gates the meta caption, but
	 * `overflow: hidden` is the belt-and-braces safeguard for sub-tier
	 * sizes where the button alone is still bigger than the visible
	 * frame). Fixes the regression where button + format-text bled
	 * outside the frame container on zoom-out. */
	overflow: hidden;
}
/* Phase Z.15 2026-05-13: reverted — the previous `:has()` rule relaxed
 * the Phase V clip but let the centered upload button bleed into the
 * frame-bar / corner-warning area at low zoom. Solution moved to the
 * warning element itself (`.wccb-warning` repositioned fully INSIDE
 * the frame, no overflow escape needed). */
body.wccb-dragging-file .wccb-overlay--empty {
	pointer-events: auto;
}
/* Phase T 2026-05-10: redesigned in-frame upload as a round icon-only
 * button (44 × 44 default · 56 × 56 on roomy frames · hidden on compact).
 * Replaces the previous icon + heading + sub-text vertical cluster which
 * dominated the frame and made the two-click flow (Phase S) feel heavy.
 * The round affordance is editorial-light, sits centered, and matches
 * the canvas-add "+" FAB pattern at .wccb-canvas-add. Drag-and-drop
 * still works on the parent overlay; tooltip + aria-label preserve
 * the upload affordance for SR / hover users. */
.wccb-overlay--empty.is-dragover .wccb-overlay__upload {
	border-color: var(--accent);
	background: var(--accent-soft);
	color: var(--accent);
}

.wccb-overlay__upload {
	pointer-events: auto;
	cursor: pointer;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 44px;
	height: 44px;
	padding: 0;
	border: 1px solid var(--border-strong);
	border-radius: 50%;
	background: var(--surface);
	color: var(--ink);
	box-shadow: var(--shadow-1);
	appearance: none;
	-webkit-appearance: none;
	box-sizing: border-box;
	transition: border-color .15s var(--ease), background .15s var(--ease), color .15s var(--ease), transform .15s var(--ease);
}
.wccb-overlay__upload:hover {
	border-color: var(--ink);
	background: var(--surface-2);
	transform: scale(1.04);
}
.wccb-overlay__upload:focus-visible {
	outline: 2px solid var(--accent);
	outline-offset: 3px;
}
.wccb-overlay__upload svg {
	width: 20px;
	height: 20px;
	stroke-width: 1.5;
	color: inherit;
	flex-shrink: 0;
}

/* Roomy frames (≥180 × 120) — slightly bigger button for visual weight
 * + visible meta caption below button (format list + max size). */
.wccb-overlay--roomy .wccb-overlay__upload {
	width: 56px;
	height: 56px;
}
.wccb-overlay--roomy .wccb-overlay__upload svg {
	width: 22px;
	height: 22px;
}

/* Phase V 2026-05-11: meta caption — two-line sentence-case (was
 * mono-caps single line in Phase U). Hidden by default; visible only
 * when JS sets `--with-meta` (frame box ≥ 120 × 100 post-zoom). Below
 * that threshold the button alone has to fit; below the `--compact`
 * threshold (<60) the whole UI is hidden. Two spans for the two lines
 * — formats on top, max-size below — keeps each line short enough
 * that even on tighter frames neither needs to wrap. */
.wccb-overlay__upload-meta {
	display: none;
	margin: 10px 0 0;
	padding: 0 8px;
	font-family: var(--font-body);
	font-size: 11px;
	font-weight: 400;
	line-height: 1.4;
	color: var(--ink-muted);
	text-align: center;
	max-width: 100%;
}
/* Phase Z.18d 2026-05-13: meta caption only renders for EMPTY frames
 * with enough room. Previously `.wccb-overlay--with-meta` was gated on
 * frame size alone (JS at builder.bundle.js:813), so a big enough
 * filled frame kept the "JPG, PNG, HEIC max 5MB" caption visible on
 * top of the uploaded image. Adding `.wccb-overlay--empty` to the
 * selector enforces the right state — the JS-side empty toggle is the
 * single source of truth for "has the frame got a photo yet". */
.wccb-overlay--empty.wccb-overlay--with-meta .wccb-overlay__upload-meta {
	display: block;
}
.wccb-overlay__upload-meta-formats,
.wccb-overlay__upload-meta-size {
	display: block;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}
.wccb-overlay__upload-meta-size {
	color: var(--ink-faint);
}

/* Compact frames (<60 px any axis) — hide entirely; frame-bar "Upload"
 * chip handles the action when the frame is selected. */
.wccb-overlay--compact.wccb-overlay--empty {
	border: 0 !important;
	background: transparent !important;
}
.wccb-overlay--compact .wccb-overlay__upload {
	display: none !important;
	pointer-events: none !important;
}
.wccb-overlay--compact {
	pointer-events: none !important;
}

/* Action buttons + dimensions pill are drawn directly on the Konva stage —
 * see addFrame() / redrawFrame() in builder.bundle.js. No HTML counterpart. */

/* (DPI badge styles removed v0.10.8 — surfaced via the frame bar instead.) */

/* ---- Toast ---- */

.wccb-toast-host {
	position: fixed;
	bottom: 24px;
	left: 50%;
	transform: translateX(-50%);
	/* Top-most layer: toasts carry feedback + errors ("Upload eerst een foto",
	 * "Foto-lade is vol", "Maat begrensd…") that can fire WHILE a sheet/popover
	 * is open, so they must never hide behind one (sheet/modal 9999, drag-ghost
	 * 10000, coach 10001). See Builder z-index Map (2026-06-14). */
	z-index: 10002;
	display: flex;
	flex-direction: column;
	gap: 8px;
	pointer-events: none;
}
.wccb-toast {
	background: var(--color-ink, #1A1F22);
	color: #fff;
	padding: 10px 16px;
	border-radius: var(--radius-md, 8px);
	box-shadow: var(--shadow-elevated, 0 8px 32px rgba(0,0,0,.2));
	font-size: 13px;
	animation: wccb-toast-in 180ms ease-out;
	pointer-events: auto;
	max-width: 440px;
}
.wccb-toast--success { background: #2E7D55; }
.wccb-toast--error   { background: #C0453B; }
@keyframes wccb-toast-in {
	from { opacity: 0; transform: translateY(8px); }
	to   { opacity: 1; transform: translateY(0);   }
}

/* ---- Modal ---- */
.wccb-modal-overlay {
	position: fixed;
	inset: 0;
	background: rgba(26,26,24,.5);
	display: flex;
	align-items: center;
	justify-content: center;
	z-index: 9999;
	padding: 16px;
}
.wccb-modal {
	background: var(--color-surface, #FFF);
	border-radius: var(--radius-md, 8px);
	padding: 24px;
	width: 100%;
	max-width: 420px;
	/* Mobile Builder P1 (B8): cap + scroll so title/CTA stay reachable on
	 * short or landscape screens and with the keyboard open. */
	max-height: calc(100vh - 32px);
	max-height: calc(100dvh - 32px);
	overflow-y: auto;
	box-shadow: var(--shadow-elevated, 0 8px 32px rgba(0,0,0,.2));
}
.wccb-modal h3 {
	font-family: var(--font-display, serif);
	margin-top: 0;
}

/* ── Add Frame modal — §32 design preview ─────────────────────────────
 * Push-step pattern: Material → Format → Confirm. Lifts §09 + §06 + §03
 * + §07 + §17 + §02 tokens verbatim. Lives on document.body so it can
 * z-index above the canvas without compositing weirdness. */
.wccb-add-frame {
	width: 100%;
	max-width: 480px;
	padding: 24px;
}
.wccb-add-frame__head {
	display: flex;
	justify-content: space-between;
	align-items: center;
	gap: 12px;
	margin-bottom: 12px;
}
.wccb-add-frame__step-label {
	font-family: var(--font-mono, monospace);
	font-size: 10px;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--color-ink-muted, #5A6063);
	flex: 1;
	text-align: center;
}
.wccb-add-frame__back {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	padding: 4px 10px;
	background: transparent;
	border: 0;
	cursor: pointer;
	font: 400 12px/1 var(--font-body, sans-serif);
	color: var(--color-ink, #1A1F22);
	border-radius: var(--radius-sm, 4px);
}
.wccb-add-frame__back:hover { background: var(--color-surface-2, #F1F4F6); }
.wccb-add-frame__back[hidden] { display: none; }
.wccb-add-frame__close {
	width: 28px;
	height: 28px;
	border: 0;
	background: transparent;
	cursor: pointer;
	color: var(--color-ink-muted, #5A6063);
	display: inline-flex;
	align-items: center;
	justify-content: center;
	border-radius: 50%;
}
.wccb-add-frame__close:hover { color: var(--color-ink, #1A1F22); }
.wccb-add-frame__progress {
	display: grid;
	grid-template-columns: repeat(2, 1fr);
	gap: 6px;
	margin-bottom: 16px;
}
.wccb-add-frame__progress > span { height: 2px; background: var(--color-border-strong, #CED6DA); display: block; }
.wccb-add-frame__progress > span.is-done { background: var(--color-ink, #1A1F22); }
.wccb-add-frame__title {
	font-family: var(--font-display, serif);
	font-size: 22px;
	letter-spacing: -0.01em;
	margin: 0 0 16px;
	color: var(--color-ink, #1A1F22);
}
.wccb-add-frame__step { display: none; }
.wccb-add-frame__step.is-active { display: block; }
.wccb-add-frame__footer {
	display: flex;
	justify-content: space-between;
	align-items: center;
	gap: 8px;
	margin-top: 20px;
}
.wccb-add-frame__footer-note {
	font-family: var(--font-mono, monospace);
	font-size: 10px;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--color-ink-muted, #5A6063);
}

/* Step 1 — material list */
.wccb-add-frame__list {
	display: flex;
	flex-direction: column;
	border: 1px solid var(--color-border, #E4E8EA);
	max-height: 420px;
	overflow-y: auto;
}
.wccb-add-frame__row {
	display: grid;
	grid-template-columns: 56px 1fr auto 16px;
	gap: 16px;
	align-items: center;
	padding: 14px 16px;
	border-bottom: 1px solid var(--color-border, #E4E8EA);
	cursor: pointer;
	background: transparent;
	border-left: 0;
	border-right: 0;
	border-top: 0;
	text-align: left;
	font: inherit;
	color: inherit;
	transition: background-color 120ms ease;
}
.wccb-add-frame__row:last-child { border-bottom: 0; }
.wccb-add-frame__row:hover,
.wccb-add-frame__row:focus-visible {
	background: var(--color-surface-2, #F1F4F6);
	outline: none;
}
.wccb-add-frame__row.is-last-picked { box-shadow: inset 0 0 0 1px var(--color-ink, #1A1F22); }
.wccb-add-frame__thumb {
	width: 56px;
	height: 56px;
	background: var(--color-surface-2, #F1F4F6);
	border: 1px solid var(--color-border, #E4E8EA);
	display: grid;
	place-items: center;
	color: var(--color-ink-muted, #5A6063);
	overflow: hidden;
}
.wccb-add-frame__thumb img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}
.wccb-add-frame__row-label {
	font-family: var(--font-body, sans-serif);
	font-size: 14px;
	color: var(--color-ink, #1A1F22);
}
.wccb-add-frame__row-vanaf {
	font-family: var(--font-mono, monospace);
	font-size: 12px;
	color: var(--color-ink-muted, #5A6063);
}

/* Step 2 — format step */
.wccb-add-frame__tabs {
	display: flex;
	gap: 8px;
	margin-bottom: 16px;
	flex-wrap: wrap;
}
.wccb-add-frame__tab {
	padding: 6px 14px;
	border: 1px solid var(--color-border, #E4E8EA);
	background: var(--color-surface, #FFF);
	font: 400 12px/1.2 var(--font-body, sans-serif);
	color: var(--color-ink, #1A1F22);
	cursor: pointer;
}
.wccb-add-frame__tab.is-active {
	background: var(--color-ink, #1A1F22);
	color: var(--color-on-ink, #FFF);
	border-color: var(--color-ink, #1A1F22);
}
/* Preset tiles — canonical §03/wh-sp-preset register (wandhaus-single-panel.css:719).
   font-display 16 px dim · font-mono 11 px price · border-color shifts on hover →
   ink-muted, on selected → ink + surface-2 background. Same visual treatment
   the SP configurator ships so picker UX is consistent across the two surfaces. */
.wccb-add-frame__preset-grid {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: 8px;
}
@media (max-width: 639px) {
	.wccb-add-frame__preset-grid { grid-template-columns: repeat(2, 1fr); }
}
.wccb-add-frame__preset {
	display: flex;
	flex-direction: column;
	gap: 4px;
	padding: 14px 16px;
	border: 1px solid var(--color-border, #E4E8EA);
	background: var(--color-surface, #FFF);
	text-align: left;
	cursor: pointer;
	font: inherit;
	transition: border-color 150ms ease, background-color 150ms ease;
}
.wccb-add-frame__preset:hover { border-color: var(--color-ink-muted, #5A6063); }
.wccb-add-frame__preset:focus-visible {
	outline: 2px solid var(--color-ink, #1A1F22);
	outline-offset: 2px;
}
.wccb-add-frame__preset.is-selected {
	border-color: var(--color-ink, #1A1F22);
	background: var(--color-surface-2, #F1F4F6);
}
.wccb-add-frame__preset-dim {
	font-family: var(--font-display, serif);
	font-size: 16px;
	letter-spacing: -0.01em;
	color: var(--color-ink, #1A1F22);
}
.wccb-add-frame__preset-price {
	font-family: var(--font-mono, monospace);
	font-size: 11px;
	font-weight: 400;
	letter-spacing: 0.04em;
	color: var(--color-ink-muted, #5A6063);
}
.wccb-add-frame__preset.is-selected .wccb-add-frame__preset-price {
	color: var(--color-ink, #1A1F22);
}
.wccb-add-frame__free-row {
	display: flex;
	align-items: center;
	gap: 12px;
	margin-bottom: 12px;
}
.wccb-add-frame__free-label {
	font: 400 10px/1 var(--font-mono, monospace);
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--color-ink-muted, #5A6063);
	min-width: 64px;
}
.wccb-add-frame__free-input {
	width: 96px;
	padding: 8px 12px;
	border: 1px solid var(--color-border-strong, #CED6DA);
	background: var(--color-surface, #FFF);
	font: 400 13px/1.3 var(--font-body, sans-serif);
	outline: 0;
}
.wccb-add-frame__free-input:focus {
	border-color: var(--color-ink, #1A1F22);
}
.wccb-add-frame__free-range {
	font: 400 11px/1.4 var(--font-mono, monospace);
	color: var(--color-ink-muted, #5A6063);
	margin-bottom: 12px;
}
.wccb-add-frame__free-price {
	padding: 10px 12px;
	background: var(--color-bg-alt, #F1F4F6);
	font: 400 13px/1.2 var(--font-mono, monospace);
	color: var(--color-ink, #1A1F22);
}

/* Empty state */
.wccb-add-frame__empty {
	padding: 32px 16px;
	text-align: center;
	color: var(--color-ink-muted, #5A6063);
	font: 400 13px/1.5 var(--font-body, sans-serif);
}
.wccb-add-frame__empty-icon {
	display: inline-flex;
	width: 48px;
	height: 48px;
	margin-bottom: 12px;
	color: var(--color-border-strong, #CED6DA);
}

/* Skeleton placeholders while prices load */
.wccb-add-frame__sk {
	display: inline-block;
	background: var(--color-surface-2, #F1F4F6);
	border-radius: 2px;
	animation: wccb-skeleton-shimmer 1200ms ease-in-out infinite;
}
.wccb-add-frame__sk--vanaf  { width: 72px; height: 12px; }
.wccb-add-frame__sk--price  { width: 48px; height: 11px; }

/* ── Phase Z.18 2026-05-13 — Wall Dimensions Splash ─────────────────
 * One-shot blocking modal fired on first builder load. Re-uses the
 * existing `.wccb-modal-overlay` + `.wccb-modal` primitives + adds the
 * splash-specific layout (title, lede, SVG preview, dim inputs, CTA,
 * skip-link). Tokens-only — no raw hex.
 * Spec: `Vault/02 Plugin (wc-collage-builder)/Wall Dimensions Splash Scope.md`.
 */
.wccb-splash .wccb-modal {
	max-width: 480px;
	padding: 28px 28px 24px;
	display: flex;
	flex-direction: column;
	gap: 16px;
}
.wccb-splash__title {
	font-family: var(--font-display, serif);
	font-size: 24px;
	line-height: 1.15;
	letter-spacing: -0.01em;
	font-weight: 500;
	margin: 0;
	color: var(--ink, #1A1F22);
}
.wccb-splash__lede {
	font: 400 14px/1.55 var(--font-body, system-ui);
	color: var(--ink-2, #2B3034);
	margin: 0;
}
.wccb-splash__preview {
	background: var(--bg-alt, #F1F4F6);
	height: 140px;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 14px;
	overflow: hidden;
}
.wccb-splash__preview svg {
	width: 100%;
	height: 100%;
}
.wccb-splash__preview-rect {
	fill: var(--surface, #FFF);
	stroke: var(--ink, #1A1F22);
	stroke-width: 1;
	vector-effect: non-scaling-stroke;
}
.wccb-splash__row {
	margin: 0;
}
.wccb-splash__cta {
	width: 100%;
	justify-content: center;
	gap: 8px;
	height: 48px;
	font-size: 15px;
}
.wccb-splash__cta:disabled {
	opacity: .4;
	cursor: not-allowed;
}
.wccb-splash__skip {
	background: transparent;
	border: 0;
	padding: 8px 0 0;
	color: var(--ink-muted, #5A6063);
	font: 400 13px/1.4 var(--font-body, system-ui);
	cursor: pointer;
	text-align: center;
	text-decoration: underline;
	text-underline-offset: 3px;
}
.wccb-splash__skip:hover {
	color: var(--ink, #1A1F22);
}
@media (max-width: 480px) {
	.wccb-splash .wccb-modal {
		padding: 22px 18px;
		gap: 12px;
	}
	.wccb-splash__title { font-size: 20px; }
	.wccb-splash__preview { height: 110px; }
}

/* ── Phase Z.20 2026-05-13 — Add-to-Cart Approval Modal ─────────────
 * Blocking dialog that surfaces every per-frame warning / error before
 * the customer adds to cart. Customer must tick the approval checkbox
 * (when warnings are warn-level only); error-level rows hold the
 * primary CTA disabled until resolved.
 *
 * Spec: `Vault/02 Plugin (wc-collage-builder)/Add-to-Cart Warning Approval Scope.md`.
 */
.wccb-modal--approval {
	max-width: 560px;
	padding: 28px 28px 24px;
	display: flex;
	flex-direction: column;
	gap: 16px;
}
.wccb-approval__title {
	font-family: var(--font-display, serif);
	font-size: 22px;
	line-height: 1.2;
	letter-spacing: -0.01em;
	font-weight: 500;
	margin: 0;
	color: var(--ink);
}
.wccb-approval__lede {
	font: 400 14px/1.55 var(--font-body);
	color: var(--ink-2);
	margin: 0;
}
.wccb-warning-list {
	list-style: none;
	margin: 0;
	padding: 0;
	border: 1px solid var(--border);
	max-height: 320px;
	overflow-y: auto;
	overscroll-behavior: contain;
}
.wccb-warning-list__row {
	display: grid;
	grid-template-columns: auto 1fr;
	gap: 12px;
	padding: 14px 16px;
	border-top: 1px solid var(--border);
	background: var(--surface);
}
.wccb-warning-list__row:first-child { border-top: 0; }
.wccb-warning-list__row--warn  { background: var(--warn-soft, #FAF1DF); }
.wccb-warning-list__row--error { background: var(--err-soft,  #FBECEA); }
.wccb-warning-list__icon {
	flex-shrink: 0;
	color: var(--warn, #C08A2E);
	margin-top: 2px;
}
.wccb-warning-list__row--error .wccb-warning-list__icon { color: var(--err, #C0453B); }
.wccb-warning-list__body { min-width: 0; }
.wccb-warning-list__title {
	margin: 0 0 4px;
	display: flex;
	align-items: baseline;
	gap: 8px;
	flex-wrap: wrap;
	font-size: 13px;
	color: var(--ink);
}
.wccb-warning-list__title strong { font-weight: 600; }
.wccb-warning-list__code {
	font-size: 10px;
	letter-spacing: .14em;
	text-transform: uppercase;
	color: var(--ink-muted);
}
.wccb-warning-list__msg {
	margin: 0 0 4px;
	font: 400 13px/1.5 var(--font-body);
	color: var(--ink-2);
}
.wccb-warning-list__detail {
	margin: 0 0 6px;
	font: 400 12px/1.4 var(--font-body);
	color: var(--ink-muted);
}
.wccb-warning-list__jump {
	background: transparent;
	border: 0;
	padding: 4px 0 0;
	font: 500 12px/1 var(--font-body);
	color: var(--ink);
	cursor: pointer;
	text-decoration: underline;
	text-underline-offset: 3px;
}
.wccb-warning-list__jump:hover { color: var(--accent); }
.wccb-approval__error-note {
	margin: 0;
	font: 500 13px/1.4 var(--font-body);
	color: var(--err, #C0453B);
}
.wccb-approval__check {
	display: flex;
	align-items: flex-start;
	gap: 10px;
	padding: 12px 14px;
	background: var(--bg-alt);
	border: 1px solid var(--border);
	cursor: pointer;
	font: 400 13px/1.5 var(--font-body);
	color: var(--ink);
}
.wccb-approval__check input[type="checkbox"] {
	flex: 0 0 auto;
	width: 18px;
	height: 18px;
	margin: 0;
	accent-color: var(--accent);
	cursor: pointer;
}
.wccb-approval__check input[type="checkbox"][disabled] { cursor: not-allowed; opacity: .35; }
.wccb-approval__check input[type="checkbox"][disabled] + span { color: var(--ink-muted); cursor: not-allowed; }
.wccb-approval__actions {
	display: flex;
	justify-content: space-between;
	gap: 12px;
	flex-wrap: wrap;
}
.wccb-approval__actions .wccb-btn {
	flex: 1 1 auto;
	min-width: 140px;
	justify-content: center;
}
.wccb-approval__actions .wccb-btn--primary:disabled { opacity: .4; cursor: not-allowed; }
@media (max-width: 480px) {
	.wccb-modal--approval { padding: 22px 18px; gap: 12px; }
	.wccb-approval__title { font-size: 18px; }
	.wccb-warning-list { max-height: 260px; }
	.wccb-approval__actions { flex-direction: column-reverse; }
	.wccb-approval__actions .wccb-btn { width: 100%; }
}

/* ─────────────────────────────────────────────────────────────────
 * Floating frame bar — single surface for editing the selected frame.
 * Desktop: absolutely positioned below (or above) the selected frame
 * via positionFrameBar(). Mobile: pinned bottom-sheet.
 * ───────────────────────────────────────────────────────────────── */

.wccb-frame-bar {
	position: fixed;
	left: 0;
	top: 0;
	display: none;
	align-items: stretch;
	/* No wrap on desktop. The JS-driven two-stage collapse (compact → sheet)
	 * handles overflow: when chip content can't fit in the stage width, the
	 * bar's labels collapse first, then the bar snaps to a bottom sheet.
	 * Wrapping here would (a) look messy when the bar pins to a frame at
	 * the viewport edge, and (b) confuse offsetWidth measurement so the
	 * collapse logic never triggers. */
	flex-wrap: nowrap;
	gap: 6px;
	padding: 6px;
	background: var(--color-surface, #FFF);
	border: 1px solid var(--color-border, #E4E8EA);
	border-radius: 24px; /* still pill-ish on single row, looks fine when wrapped */
	box-shadow: 0 6px 24px rgba(0, 0, 0, .12), 0 1px 2px rgba(0, 0, 0, .06);
	z-index: 9996; /* above every page layer except popovers/toasts */
	max-width: calc(100vw - 24px);
	animation: wccb-frame-bar-in 140ms ease-out;
}
.wccb-frame-bar.is-visible { display: inline-flex !important; }

/* Responsive collapse — JS measures after render and applies progressively:
 *   --compact → drop labels on property chips, icon-only on action chips
 *   --sheet   → last resort, pin to bottom of the viewport (desktop equivalent
 *               of the mobile bottom-sheet, for narrow windows)
 */
.wccb-frame-bar--compact .wccb-chipbtn__label { display: none; }
.wccb-frame-bar--compact .wccb-chipbtn { padding: 8px 10px; gap: 4px; }

/* Mobile redesign (2026-06-12): the sheet was a floating card that wrapped
 * each group onto its own full-width row (3 stacked rows) and hovered 76px
 * above the cart bar with an awkward gap. Re-do it as a clean full-width
 * DOCKED toolbar that sits flush above the cart bar: one horizontal row of
 * chips, scrollable if they overflow, with an upward shadow so it reads as a
 * pinned secondary bar rather than a floating card. */
.wccb-frame-bar--sheet {
	left: 0 !important;
	right: 0 !important;
	top: auto !important;
	bottom: calc(env(safe-area-inset-bottom, 0) + 68px) !important;
	max-width: none;
	flex-wrap: nowrap;
	overflow-x: auto;
	overflow-y: hidden;
	-webkit-overflow-scrolling: touch;
	justify-content: flex-start;
	gap: 8px;
	padding: 10px 12px;
	border-radius: var(--radius-lg, 18px) var(--radius-lg, 18px) 0 0;
	border-left: 0;
	border-right: 0;
	border-bottom: 0;
	box-shadow: 0 -4px 18px rgba(26, 31, 34, .10);
}

@keyframes wccb-frame-bar-in {
	from { opacity: 0; transform: translateY(4px); }
	to   { opacity: 1; transform: translateY(0);   }
}

/* multi-select: frame-bar group morph (2026-05-30) — crossfade when the bar
   swaps between single-frame props and the group affordance. The content is
   re-written by JS on a ~130ms timeout; .is-swapping fades + nudges it out
   first, removing it fades the new content back in. */
.wccb-frame-bar {
	transition: opacity 150ms ease, transform 150ms ease;
}
.wccb-frame-bar.is-swapping {
	opacity: 0;
	transform: translateY(3px) scale(.985);
}
@media (prefers-reduced-motion: reduce) {
	.wccb-frame-bar { transition: none; animation: none; }
	.wccb-frame-bar.is-swapping { opacity: 1; transform: none; }
}

/* multi-select: frame-bar group morph (2026-05-30) — group affordance shown
   inside the frame-bar when ≥2 frames are selected. Count label + a danger
   delete button (Lucide trash), matching the frame-bar chrome. */
.wccb-frame-bar__count {
	display: inline-flex;
	align-items: center;
	padding: 0 4px 0 8px;
	font-size: 12px;
	font-weight: 600;
	color: var(--ink);
	white-space: nowrap;
}
.wccb-frame-bar__del {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	height: 36px;
	padding: 0 14px;
	border: 0;
	background: var(--err);
	color: var(--on-ink);
	border-radius: var(--radius-pill);
	cursor: pointer;
	font: inherit;
	font-size: 12px;
	font-weight: 600;
	white-space: nowrap;
	transition: filter 120ms;
}
.wccb-frame-bar__del:hover { filter: brightness(.9); }
.wccb-frame-bar__del svg {
	width: 16px;
	height: 16px;
	fill: none !important;
	stroke: currentColor !important;
	stroke-width: 1.5;
	stroke-linecap: round;
	stroke-linejoin: round;
	flex-shrink: 0;
}

.wccb-frame-bar__sep {
	width: 1px;
	background: var(--color-border, #E4E8EA);
	align-self: stretch;
	margin: 4px 6px;
}

/* Chip buttons — shared styling for the reposition bar (Reset/zoom/Klaar) and the
 * multi-select group bar. (The per-frame property chips were retired in C3 — those
 * controls live in the slide-out editor now.) flex-shrink:0 lets the JS read the
 * bar's natural width for the --compact/--sheet group-bar collapse; --sheet re-allows
 * shrink so the bottom-pinned layout shares space evenly. */
.wccb-chipbtn {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	padding: 8px 12px;
	border: 0;
	border-radius: 999px;
	background: transparent;
	color: var(--color-ink, #1A1F22);
	font: inherit;
	font-size: 12px;
	cursor: pointer;
	transition: background 120ms, color 120ms, box-shadow 120ms;
	min-height: 36px;
	white-space: nowrap;
	flex-shrink: 0;
}
.wccb-frame-bar--sheet .wccb-chipbtn { flex-shrink: 1; }
.wccb-chipbtn:hover,
.wccb-chipbtn[aria-expanded="true"] {
	background: var(--color-accent-soft, #F1F4F6);
}
.wccb-chipbtn:disabled {
	opacity: .45;
	cursor: not-allowed;
}
.wccb-chipbtn__ico {
	display: inline-flex;
	width: 16px;
	height: 16px;
	color: var(--color-ink-muted, #5A6063);
}
.wccb-chipbtn__ico svg {
	width: 100%;
	height: 100%;
}
.wccb-chipbtn__label {
	color: var(--color-ink-muted, #5A6063);
}

/* Action chips (reposition bar): icon-forward, label hidden by default. */
.wccb-chipbtn--action .wccb-chipbtn__ico { color: var(--color-ink, #1A1F22); }
.wccb-chipbtn--action:hover .wccb-chipbtn__ico { color: var(--color-accent, #235C68); }
.wccb-chipbtn--action .wccb-chipbtn__label { display: none; }
.wccb-chipbtn--action { padding: 8px 10px; }

/* Primary chip used by the reposition "Klaar" button. */
.wccb-chipbtn--primary {
	background: var(--color-accent, #235C68);
	color: #fff;
	padding: 8px 14px;
}
.wccb-chipbtn--primary:hover { background: var(--color-accent-dark, #1A4651); color: #fff; }
.wccb-chipbtn--primary,
.wccb-chipbtn--primary:hover,
.wccb-chipbtn--primary .wccb-chipbtn__label { color: #fff; }
.wccb-chipbtn--primary .wccb-chipbtn__ico { color: #fff; }

/* ───────────── Popover (property pickers) ───────────── */

/* Phase B 2026-05-10: aligned to design-system §26 popover body, which
 * mirrors §09 popover register exactly — surface bg · 1px border ·
 * var(--shadow-2) · sharp corners · 8px outer padding (rows handle
 * their own padding). Legacy --color-* aliases swapped for canonical
 * V2 tokens. JS-bound `.wccb-popover` selector unchanged. */
.wccb-popover {
	position: absolute;
	background: var(--surface);
	border: 1px solid var(--border);
	border-radius: 0;
	box-shadow: var(--shadow-2);
	padding: 8px;
	min-width: 220px;
	max-width: 320px;
	/* Cap popover height to ~70% of viewport and let the body scroll inside.
	 * Long preset lists (Canvas 18mm has 35 sizes across substrate × shape)
	 * would otherwise overflow the screen. The title + substrate tabs stay
	 * pinned at the top via sticky positioning so the customer always knows
	 * which axis they're filtering. */
	max-height: min(70vh, 560px);
	display: flex;
	flex-direction: column;
	overflow: hidden;
	z-index: 9997;
	animation: wccb-frame-bar-in 140ms ease-out;
}

/* Mobile overflow ("...") dropdown. Reuses the .wccb-popover surface (border,
 * shadow, z-index) but stays a compact anchored list — NOT a bottom sheet. The
 * <=767 popover blanket would force it bottom-anchored full-width; the variant
 * overrides that in the @max-767 block, and JS sets left/top via inline
 * !important. Menu items reuse the topbar actions' data-act so they fire the
 * existing handlers. */
.wccb-popover--menu {
	min-width: 200px;
	max-width: 260px;
	padding: 6px;
	gap: 2px;
}
.wccb-popover__menu-item {
	display: flex;
	align-items: center;
	gap: var(--s-2, 8px);
	width: 100%;
	min-height: 44px;
	padding: 0 12px;
	border: 0;
	background: transparent;
	color: var(--ink);
	border-radius: var(--radius-sm, 8px);
	font: inherit;
	font-size: 14px;
	text-align: left;
	cursor: pointer;
	transition: background 120ms;
}
.wccb-popover__menu-item:hover,
.wccb-popover__menu-item:focus-visible {
	background: var(--accent-soft);
	outline: none;
}
.wccb-popover__menu-item svg {
	width: 16px;
	height: 16px;
	flex-shrink: 0;
	fill: none;
	stroke: currentColor;
	stroke-width: 1.5;
	stroke-linecap: round;
	stroke-linejoin: round;
	color: var(--ink-muted);
}
.wccb-popover__menu-item span { white-space: nowrap; }
.wccb-popover__menu-sep {
	height: 1px;
	margin: 4px 8px;
	background: var(--border);
}
/* Mobile sheet backdrop + per-frame close button — hidden on desktop (the
 * click-away handler + hover suffice there). Shown only ≤767px (Bug Backlog
 * #5/#6, 2026-06-13). */
.wccb-popover-backdrop { display: none; }
.wccb-popover__sheet-close {
	display: none;
	position: absolute;
	top: 9px;
	right: 10px;
	width: 30px;
	height: 30px;
	align-items: center;
	justify-content: center;
	padding: 0;
	border: 0;
	border-radius: var(--radius-pill, 999px);
	background: var(--bg-alt, #F1F4F6);
	color: var(--ink, #1A1F22);
	cursor: pointer;
	z-index: 3;
}

/* ════════════════════════════════════════════════════════════════
   wccb-sheet — shared mobile bottom sheet (frame settings; tray to
   follow). One sheet language for the builder. Mobile-only in practice
   (only opened behind isMobile()); the backdrop only paints ≤767px.
   See [[Mobile Frame-Bar as Bottom Sheet Scope (2026-06-13)]].
   ════════════════════════════════════════════════════════════════ */
.wccb-sheet {
	position: fixed;
	left: 0; right: 0; bottom: 0;
	z-index: 9999;
	display: flex;
	flex-direction: column;
	max-height: 82dvh;
	background: var(--surface, #FFFFFF);
	border-radius: var(--radius-lg, 18px) var(--radius-lg, 18px) 0 0;
	box-shadow: 0 -10px 40px rgba(26, 31, 34, .20);
	padding-bottom: env(safe-area-inset-bottom, 0);
	transition: transform .26s var(--ease, cubic-bezier(.2, .7, .2, 1));
	font-family: var(--font-body);
	will-change: transform;
}
.wccb-sheet.is-hidden { transform: translateY( calc(100% + 24px) ); pointer-events: none; }
@media (prefers-reduced-motion: reduce) { .wccb-sheet { transition: none; } }

/* C3 (2026-06-14): the per-frame EDITOR is a docked right-rail on desktop (≥768px)
 * and a bottom sheet on mobile (≤767px, the base rules above) — one buildFrameSheet
 * body, two presentations, exactly like the Foto-lade tray. Opening it closes the
 * tray (one panel at a time, see openFrameSheet). */
@media (min-width: 768px) {
	.wccb-frame-sheet {
		top: 64px;
		right: 12px;
		left: auto;
		bottom: 92px;
		width: 340px;
		max-height: none;
		border-radius: var(--radius-md, 12px);
		box-shadow: var(--shadow-2, 0 8px 32px rgba(26, 31, 34, .14));
		padding-bottom: 0;
	}
	.wccb-frame-sheet.is-hidden { transform: translateX( calc(100% + 20px) ); }
	/* No swipe-to-dismiss on desktop — the header X + selecting elsewhere close it. */
	.wccb-frame-sheet .wccb-sheet__handle { display: none; }
}
.wccb-sheet__handle {
	width: 40px; height: 4px;
	border-radius: var(--radius-pill, 999px);
	background: var(--border-strong, #CED6DA);
	margin: 10px auto 4px;
	flex: 0 0 auto;
	cursor: grab;
	touch-action: none;
}
.wccb-sheet__head {
	display: flex; align-items: center; justify-content: space-between;
	gap: 12px;
	padding: 4px 16px 12px;
	border-bottom: 1px solid var(--border, #E4E8EA);
	touch-action: none;
}
.wccb-sheet__titles {
	display: flex;
	flex-direction: column;
	gap: 1px;
	min-width: 0;
}
.wccb-sheet__title {
	font-family: var(--font-display, serif);
	font-size: 17px; font-weight: 600;
	color: var(--ink, #1A1F22);
	line-height: 1.2;
}
/* Second line under the title (e.g. the frame's material) — subtitle style. */
.wccb-sheet__subtitle {
	font-size: 12px;
	font-weight: 500;
	color: var(--ink-muted, #5B6770);
	line-height: 1.2;
}
.wccb-sheet__subtitle[hidden] { display: none; }
.wccb-sheet__close {
	width: 34px; height: 34px;
	padding: 0;
	display: inline-flex; align-items: center; justify-content: center;
	border: 0; border-radius: var(--radius-pill, 999px);
	background: var(--bg-alt, #F1F4F6);
	color: var(--ink, #1A1F22);
	cursor: pointer; flex: 0 0 auto;
}
/* flex:none so the icon can't shrink to width:0 inside the flex button
   (same trap as the hero chevron). */
.wccb-sheet__close svg { flex: none; width: 16px; height: 16px; }
.wccb-sheet-act svg { flex: none; }
.wccb-sheet__body { overflow-y: auto; -webkit-overflow-scrolling: touch; padding: 4px 16px 16px; }
.wccb-sheet-backdrop { display: none; }
.wccb-sheet-section { padding: 14px 0; border-bottom: 1px solid var(--border, #E4E8EA); }
.wccb-sheet-section:last-child { border-bottom: 0; }
/* openPopover content mounted inline in a section — strip the floating chrome
   (higher specificity than the ≤767px .wccb-popover !important rule). */
.wccb-sheet .wccb-popover--inline {
	position: static !important;
	left: auto !important; right: auto !important; top: auto !important; bottom: auto !important;
	width: 100%; min-width: 0; max-width: none; max-height: none;
	border: 0; box-shadow: none; padding: 0; margin: 0;
	background: transparent; overflow: visible; display: block;
	animation: none;
}
.wccb-sheet .wccb-popover--inline::before { display: none; }
.wccb-sheet .wccb-popover--inline .wccb-popover__sheet-close { display: none; }
.wccb-sheet-actions { display: flex; gap: 8px; }
.wccb-sheet-act {
	flex: 1 1 0;
	display: inline-flex; flex-direction: column; align-items: center; gap: 5px;
	padding: 12px 6px;
	min-height: var(--tap, 44px);
	border: 1px solid var(--border, #E4E8EA);
	border-radius: var(--radius-md, 12px);
	background: var(--surface, #FFFFFF);
	color: var(--ink, #1A1F22);
	font: 600 11px/1.1 var(--font-body);
	cursor: pointer;
}
.wccb-sheet-act svg { width: 18px; height: 18px; }
.wccb-sheet-act--danger {
	color: var(--err, #B42318);
	border-color: color-mix( in srgb, var(--err, #B42318) 40%, var(--border, #E4E8EA) );
}

/* Segmented control (Foto | Instellingen) inside the frame slide-out
   ([[Mobile Frame Interaction Model — Rebuild Scope (2026-06-14)]] §4.5). */
.wccb-sheet-tabs {
	display: flex;
	gap: 4px;
	padding: 4px;
	margin: 4px 0 12px;
	background: var(--bg-alt, #F1F4F6);
	border-radius: var(--radius-md, 12px);
}
.wccb-sheet-tab {
	flex: 1 1 0;
	min-height: var(--tap, 44px);
	border: 0;
	border-radius: var(--radius-sm, 8px);
	background: transparent;
	color: var(--ink-muted, #5A6063);
	font: 600 13px/1 var(--font-body);
	cursor: pointer;
	transition: background .15s, color .15s;
}
.wccb-sheet-tab.is-active {
	background: var(--surface, #FFFFFF);
	color: var(--ink, #1A1F22);
	box-shadow: var(--shadow-1, 0 1px 2px rgba(0,0,0,.08));
}
.wccb-sheet-panel[hidden] { display: none; }
.wccb-sheet-foot { border-bottom: 0; padding-bottom: 4px; }

/* Foto tab block */
.wccb-foto-block { display: flex; gap: 14px; align-items: center; }
.wccb-foto-preview {
	flex: 0 0 auto;
	width: 88px; height: 110px;          /* 4:5 panel proportion */
	border-radius: var(--radius-sm, 8px);
	background: var(--bg-alt, #F1F4F6) center/cover no-repeat;
	border: 1px solid var(--border, #E4E8EA);
}
.wccb-foto-preview.is-empty {
	display: flex; align-items: center; justify-content: center;
	color: var(--border-strong, #CED6DA);
}
.wccb-foto-preview.is-empty svg { width: 28px; height: 28px; }
.wccb-foto-actions { flex: 1 1 auto; display: flex; flex-direction: column; gap: 8px; }
.wccb-foto-actions .wccb-btn { width: 100%; min-height: var(--tap, 44px); display: inline-flex; align-items: center; justify-content: center; gap: 8px; }
.wccb-foto-actions .wccb-btn svg { flex: none; width: 16px; height: 16px; }
.wccb-foto-lowdpi {
	display: flex; align-items: flex-start; gap: 6px;
	margin: 12px 0 0;
	font-size: 12px; line-height: 1.4;
	color: var(--warn-ink, #8A5A00);
}
.wccb-foto-lowdpi svg { flex: none; margin-top: 1px; }

/* All-in-One photo LIBRARY zone — lives inside the editor sidebar below the
 * per-frame editor (buildLibraryZone). Reuses the .wccb-tray__drop/__grid/__empty
 * + .wccb-tray-thumb styles for the upload zone, grid and thumbnails. */
.wccb-lib__head {
	display: flex;
	align-items: center;
	gap: 8px;
	margin: 0 0 10px;
}
/* Section label — matches the slide-out's other section titles (Materiaal /
 * Formaat): uppercase mono, muted. So the library reads as one of the sidebar's
 * own sections, not a bolted-on tray. */
.wccb-lib__title {
	font: 500 11px/1 var(--font-mono, monospace);
	text-transform: uppercase;
	letter-spacing: .14em;
	color: var(--ink-muted, #5A6063);
}
.wccb-lib__count {
	font: 500 11px/1 var(--font-body);
	color: var(--ink-muted, #5A6063);
}
/* "Vul lege frames" — a ghost button matching the slide-out's --ghost actions. */
.wccb-lib__fill {
	margin-left: auto;
	display: inline-flex;
	align-items: center;
	gap: 6px;
	padding: 7px 12px;
	border: 1px solid var(--border, #E4E8EA);
	border-radius: var(--radius-sm, 6px);
	background: var(--surface, #FFFFFF);
	color: var(--ink, #1A1F22);
	font: 500 12px/1 var(--font-body);
	cursor: pointer;
	transition: background .15s, border-color .15s;
}
.wccb-lib__fill svg { flex: none; color: var(--ink-muted, #5A6063); }
.wccb-lib__fill:hover:not(:disabled) { background: var(--bg-alt, #F1F4F6); border-color: var(--accent, #235C68); }
.wccb-lib__fill:hover:not(:disabled) svg { color: var(--accent, #235C68); }
.wccb-lib__fill:disabled { opacity: .5; cursor: default; }
.wccb-lib__drop-cta { color: var(--accent, #235C68); border-bottom: 1px solid var(--accent, #235C68); }
.wccb-lib__grid { margin-top: 12px; }
/* Highlight the library photo that's on the frame you're currently editing. */
.wccb-tray-thumb.is-current { border-color: var(--accent, #235C68); box-shadow: 0 0 0 2px var(--accent-soft, #F1F4F6); }

/* First-run coachmark (D5) — one-time mobile teaching overlay. */
.wccb-coach {
	position: fixed; inset: 0; z-index: 10001;
	display: flex; align-items: center; justify-content: center;
	padding: 24px;
	background: rgba(26, 31, 34, .45);
}
.wccb-coach__card {
	max-width: 340px; width: 100%;
	background: var(--surface, #FFFFFF);
	border-radius: var(--radius-lg, 18px);
	box-shadow: var(--shadow-3, 0 16px 48px rgba(0,0,0,.25));
	padding: 22px 20px;
	font-family: var(--font-body);
}
.wccb-coach__title {
	font-family: var(--font-display, serif);
	font-size: 18px; font-weight: 600; margin: 0 0 12px;
	color: var(--ink, #1A1F22);
}
.wccb-coach__list { margin: 0 0 18px; padding-left: 18px; }
.wccb-coach__list li { margin-bottom: 8px; font-size: 14px; line-height: 1.45; color: var(--ink-muted, #5A6063); }
.wccb-coach .wccb-btn { width: 100%; min-height: var(--tap, 44px); }

.wccb-popover__title,
.wccb-popover .wccb-axis-tabs {
	flex-shrink: 0;
	background: var(--surface);
}
.wccb-popover__scroll {
	overflow-y: auto;
	overscroll-behavior: contain;
	margin: 0 -8px -8px;
	padding: 0 8px 8px;
}
.wccb-popover__section .wccb-popover__section-label {
	position: sticky;
	top: 0;
	background: var(--surface);
	padding: 4px 0;
	z-index: 1;
}
.wccb-popover__title {
	font-family: var(--font-mono);
	font-size: 11px;
	text-transform: uppercase;
	letter-spacing: .14em;
	color: var(--ink-muted);
	margin-bottom: 8px;
	font-weight: 500;
}
.wccb-popover__row {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 8px;
	margin-bottom: 8px;
}
.wccb-popover__row label {
	display: flex;
	flex-direction: column;
	gap: 4px;
	font-size: 11px;
	color: var(--color-ink-muted, #5A6063);
	font-weight: 600;
}
.wccb-popover__row input {
	height: 36px;
	padding: 0 10px;
	border: 1px solid var(--color-border, #E4E8EA);
	border-radius: var(--radius-sm, 4px);
	font: inherit;
	font-size: 13px;
	background: var(--color-surface, #FFF);
}
.wccb-popover__row input[type="color"] {
	padding: 2px;
	height: 36px;
}
.wccb-popover__presets {
	display: grid;
	grid-template-columns: repeat(5, 1fr);
	gap: 4px;
}
.wccb-popover__preset {
	padding: 8px 2px;
	border: 1px solid var(--color-border, #E4E8EA);
	background: var(--color-surface, #FFF);
	border-radius: var(--radius-sm, 4px);
	font: inherit;
	font-size: 11px;
	cursor: pointer;
}
.wccb-popover__preset:hover {
	background: var(--color-accent-soft, #F1F4F6);
	border-color: var(--color-accent, #235C68);
}
.wccb-popover__preset.is-on {
	background: var(--color-accent, #235C68);
	border-color: var(--color-accent, #235C68);
	color: #fff;
	font-weight: 600;
}
.wccb-popover__preset.is-disabled,
.wccb-popover__preset:disabled,
.wccb-orient-tile.is-disabled,
.wccb-orient-tile:disabled {
	opacity: .4;
	cursor: not-allowed;
	pointer-events: auto; /* keep the title tooltip on hover */
	text-decoration: line-through;
}
.wccb-popover__preset.is-disabled:hover,
.wccb-popover__preset:disabled:hover,
.wccb-orient-tile.is-disabled:hover,
.wccb-orient-tile:disabled:hover {
	background: transparent;
	border-color: var(--color-border, #E4E8EA);
}

/* Orientation toggle — two big tiles inside the customer-options popover when
 * the group is detected as portrait/landscape. Each tile draws an SVG glyph at
 * the actual aspect ratio of that orientation given the frame's current dims,
 * so the customer sees the proportion swap before committing. */
.wccb-orient-toggle {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 12px;
	margin-top: 4px;
}
.wccb-orient-tile {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 8px;
	padding: 16px 12px;
	border: 1px solid var(--color-border, #E4E8EA);
	border-radius: var(--radius-md, 8px);
	background: var(--color-surface, #FFF);
	cursor: pointer;
	transition: transform 120ms, border-color 120ms, box-shadow 120ms, background 120ms;
	font: inherit;
	font-size: 12px;
	font-weight: 600;
	color: var(--color-ink-muted, #5A6063);
	min-height: 110px;
	text-align: center;
}
.wccb-orient-tile:hover {
	transform: translateY(-2px);
	border-color: var(--color-accent, #235C68);
}
.wccb-orient-tile:focus-visible {
	outline: 2px solid var(--color-accent, #235C68);
	outline-offset: 2px;
}
.wccb-orient-tile.is-on {
	border-color: var(--color-accent, #235C68);
	box-shadow: 0 0 0 2px var(--color-accent, #235C68) inset, 0 4px 14px rgba(35, 92, 104, .12);
	color: var(--color-ink, #1A1F22);
	background: var(--color-accent-soft, #F1F4F6);
}
.wccb-orient-tile__glyph {
	width: 64px;
	height: 64px;
	display: flex;
	align-items: center;
	justify-content: center;
	color: currentColor;
}
.wccb-orient-tile__glyph svg {
	max-width: 100%;
	max-height: 100%;
	fill: currentColor;
	opacity: .85;
}
.wccb-orient-tile.is-on .wccb-orient-tile__glyph svg { opacity: 1; }
.wccb-orient-tile__label {
	line-height: 1;
	white-space: nowrap;
}
@media (max-width: 380px) {
	.wccb-orient-toggle { grid-template-columns: 1fr; }
}

/* Suggested preset (e.g. nearest match after a material switch). Subtle ring
 * + a small tag inside the button so it doesn't compete with the is-on selection. */
.wccb-popover__preset--suggested {
	border-color: var(--color-accent, #235C68);
	box-shadow: 0 0 0 2px rgba(35, 92, 104, .18);
	font-weight: 600;
}
.wccb-popover__preset-tag {
	display: inline-block;
	margin-left: 6px;
	padding: 1px 6px;
	font-size: 9px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: .04em;
	border-radius: 999px;
	background: var(--color-accent, #235C68);
	color: #fff;
	vertical-align: middle;
}

/* Live out-of-range warning under the W/H inputs. */
.wccb-popover__rangewarn {
	margin-top: 6px;
	font-size: 11px;
	color: #A8362D;
	background: #FBECEA;
	border: 1px solid #F0CFC9;
	padding: 6px 8px;
	border-radius: 4px;
}

/* Single-column popover row — used for the custom-mm input in the border popover. */
.wccb-popover__row--single { grid-template-columns: 1fr; }

/* Footer notes in popovers (hints, caveats). */
.wccb-popover__note {
	margin: 8px 0 0;
	font-size: 11px;
	color: var(--color-ink-muted, #5A6063);
	line-height: 1.4;
}
.wccb-popover__note--muted { opacity: .75; }

/* Swatch grid for the border-colour picker. 8 print-safe colours; each shows a
 * coloured dot + label, selected gets an accent ring. Disabled state (border
 * width == 0) greys everything and blocks pointer events. */
.wccb-swatches {
	display: grid;
	grid-template-columns: repeat(4, 1fr);
	gap: 6px;
	margin-top: 4px;
}
.wccb-swatches.is-disabled { opacity: .45; pointer-events: none; }

.wccb-swatch {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 4px;
	padding: 8px 4px;
	border: 1px solid var(--color-border, #E4E8EA);
	background: var(--color-surface, #FFF);
	border-radius: var(--radius-sm, 4px);
	cursor: pointer;
	font: inherit;
	font-size: 10px;
	color: var(--color-ink, #1A1F22);
	transition: border-color 120ms, box-shadow 120ms;
}
.wccb-swatch:hover { border-color: var(--color-accent, #235C68); }
.wccb-swatch--on {
	border-color: var(--color-accent, #235C68);
	box-shadow: 0 0 0 2px var(--color-accent, #235C68) inset, 0 0 0 3px rgba(35, 92, 104,.15);
}
.wccb-swatch:disabled { cursor: not-allowed; }
.wccb-swatch__dot {
	display: block;
	width: 24px;
	height: 24px;
	border-radius: 50%;
	border: 1px solid rgba(26,26,24,.15);
	box-shadow: inset 0 0 0 1px rgba(255,255,255,.4);
}
.wccb-swatch__label {
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
	max-width: 100%;
	line-height: 1.1;
}
.wccb-popover__list {
	display: flex;
	flex-direction: column;
	gap: 2px;
}
.wccb-popover__item {
	display: flex;
	justify-content: space-between;
	align-items: center;
	padding: 10px 12px;
	border: 0;
	background: transparent;
	border-radius: var(--radius-sm, 4px);
	font: inherit;
	font-size: 13px;
	color: var(--color-ink, #1A1F22);
	cursor: pointer;
	text-align: left;
	min-height: 40px;
}
.wccb-popover__item:hover { background: var(--color-accent-soft, #F1F4F6); }
.wccb-popover__item.is-on {
	background: var(--color-accent-soft, #F1F4F6);
	color: var(--color-accent, #235C68);
	font-weight: 600;
}
.wccb-popover__item-check {
	width: 16px;
	height: 16px;
	color: var(--color-accent, #235C68);
}

/* Material picker — list rows with a 40×40 thumbnail. The thumb is rendered
 * as a CSS background so missing images are silent (instead of broken-img
 * icons). The placeholder SVG bundled with the plugin is the fallback when
 * no Probo image and no admin upload is available. */
.wccb-popover__item--with-thumb {
	display: grid;
	grid-template-columns: 40px 1fr auto auto;
	align-items: center;
	gap: 12px;
	padding: 8px 10px;
}
/* C5: "vanaf €X" on the frame-editor material rows (matches the Add Frame modal). */
.wccb-popover__item-vanaf {
	font-size: 12px;
	color: var(--ink-muted, #5A6063);
	white-space: nowrap;
	justify-self: end;
}
.wccb-popover__item-vanaf:empty { display: none; }
.wccb-popover__item-thumb {
	width: 40px;
	height: 40px;
	border-radius: 0;
	background: var(--bg-alt) center/cover no-repeat;
	border: 1px solid var(--border);
	flex-shrink: 0;
}
.wccb-popover__item-check svg { width: 100%; height: 100%; }

/* ───────────── Reposition mode chrome ───────────── */

.wccb-stage__dim {
	position: absolute;
	inset: 0;
	background: rgba(26, 26, 24, .48);
	opacity: 0;
	pointer-events: none; /* decorative only — click-outside handled via Konva stage */
	transition: opacity 160ms;
	z-index: 8; /* below overlays (10) but above Konva (1) */
}
.wccb-stage__dim.is-on { opacity: 1; }

.wccb-reposition-bar {
	position: fixed;
	left: 0;
	top: 0;
	display: none;
	align-items: stretch;
	gap: 4px;
	padding: 6px;
	background: var(--color-surface, #FFF);
	border: 1px solid var(--color-border, #E4E8EA);
	border-radius: 999px;
	box-shadow: 0 8px 32px rgba(0, 0, 0, .2), 0 1px 2px rgba(0, 0, 0, .08);
	z-index: 9998; /* above dim + overlays + frame-bar */
	animation: wccb-frame-bar-in 140ms ease-out;
}
.wccb-reposition-bar.is-visible { display: inline-flex !important; }

/* During reposition, make sure the frame being repositioned sits above the dim. */
.wccb-overlays--repositioning { pointer-events: none !important; }
.wccb-overlays--repositioning * { pointer-events: none !important; }

/* Keep touch gestures (pinch / one-finger pan) from browser-zooming the page
 * when the user is trying to reposition a photo. */
.wccb-stage { touch-action: none; }

/* ───────────── Mobile (<768px): bottom sheet ───────────── */

@media (max-width: 767px) {
	/* Redesign 2026-06-12: was a floating rounded card 8px off each edge that
	 * wrapped its chips onto 2-3 rows and hovered with a gap above the cart
	 * bar. Now a full-width DOCKED toolbar flush above the bottombar — one
	 * horizontal row, scrolls sideways if the chips overflow, sheet-style top
	 * corners + an upward shadow so it reads as pinned chrome, not a card. */
	.wccb-frame-bar {
		position: fixed;
		left: 0;
		right: 0;
		/* JS (positionFrameBar) sets the exact bottom = measured bottombar height
		 * so the bar always sits flush on the cart bar regardless of safe-area /
		 * font scaling / 2-row wrap. This calc() is only the pre-JS fallback. */
		bottom: calc(env(safe-area-inset-bottom, 0) + 124px);
		top: auto !important;
		max-width: none;
		border-radius: var(--radius-lg, 18px) var(--radius-lg, 18px) 0 0;
		border-left: 0;
		border-right: 0;
		border-bottom: 0;
		padding: 10px 12px;
		/* WRAP, not horizontal scroll. The old nowrap+overflow-x:auto row pushed
		 * "Meer" + every secondary chip off-screen to the right (the row measured
		 * 433–797px in a 390px viewport), so "Meer" looked dead — its revealed
		 * chips were simply scrolled out of view. Wrapping keeps every chip on
		 * screen; the sheet grows UPWARD from its bottom anchor when "Meer" adds
		 * a row. Capped so a huge expand still leaves the canvas usable. */
		flex-wrap: wrap;
		justify-content: center;
		overflow-x: hidden;
		overflow-y: auto;
		max-height: 42dvh;
		gap: 8px;
		box-shadow: 0 -4px 18px rgba(26, 31, 34, .10);
	}
	.wccb-frame-bar__sep { display: none; }
	/* Canva model (Mobile Builder P2, §36): the desktop groups dissolve
	 * (display:contents) so chips form one orderable flex flow. Collapsed
	 * sheet = Foto · Materiaal · Formaat · Meer; .is-expanded reveals the
	 * secondary chips. The DPI warning chip always stays visible. */

	.wccb-chipbtn {
		flex: 0 0 auto; /* content width — the row scrolls instead of wrapping */
		min-height: var(--tap, 44px); /* iOS HIG tap target */
		padding: 10px 12px;
		justify-content: center;
		font-size: 11px;
		white-space: nowrap;
	}
	/* One FAB only — hidden while the sheet owns the thumb zone (§36). */
	body.wccb-has-selection .wccb-canvas-fab {
		opacity: 0;
		pointer-events: none;
	}
	/* §36: labels are part of the touch affordance — out-rank the desktop
	 * icon-only rules for action chips ((0,2,0)). The danger chip keeps its
	 * arm-to-confirm label behaviour. */

	.wccb-reposition-bar {
		position: fixed;
		left: 8px;
		right: 8px;
		bottom: calc(env(safe-area-inset-bottom, 0) + 12px);
		top: auto !important;
		max-width: none;
		border-radius: var(--radius-md, 12px);
		justify-content: center;
		padding: 8px;
	}
	.wccb-reposition-bar .wccb-chipbtn {
		flex: 1 1 0;
		min-height: 48px;
		font-size: 12px;
	}

	.wccb-popover {
		position: fixed !important;
		left: 8px !important;
		right: 8px !important;
		top: auto !important;
		bottom: calc(env(safe-area-inset-bottom, 0) + 12px);
		max-width: none;
		width: auto;
		/* Mobile Builder P1 (§36): every pinned popover gets the designed
		 * sheet chrome (was wall-settings only) + scrolls when tall. */
		border-radius: 16px;
		padding-top: 18px;
		max-height: min(70dvh, 480px);
		overflow-y: auto;
		box-shadow: 0 -8px 32px rgba(0, 0, 0, .18);
		z-index: 9999; /* above the sheet backdrop (9998) — Bug Backlog #5/#6 */
	}
	.wccb-popover::before {
		content: '';
		position: absolute;
		top: 8px; left: 50%;
		transform: translateX(-50%);
		width: 36px; height: 4px;
		border-radius: 2px;
		background: var(--color-border, #E4E8EA);
	}
	/* The overflow ("...") menu stays an anchored dropdown, NOT the bottom sheet
	 * the blanket above imposes. JS sets left/top/right/bottom via inline
	 * !important; here we just strip the sheet chrome (full width, grip handle,
	 * sheet radius/top-padding, upward shadow). Higher specificity than the
	 * blanket, so no !important needed for these. */
	.wccb-popover.wccb-popover--menu {
		width: auto;
		min-width: 200px;
		max-width: 260px;
		border-radius: var(--radius-md, 12px);
		padding: 6px;
		padding-top: 6px;
		max-height: min(70dvh, 420px);
		box-shadow: var(--shadow-2);
	}
	.wccb-popover.wccb-popover--menu::before { display: none; }
	/* Bottom-sheet popovers (Muur instellingen) go EDGE-TO-EDGE — flush left /
	 * right / bottom — not the 8px-inset card the blanket above imposes. The
	 * conflict otherwise: --sheet forces width:100% while the blanket forces
	 * left:8/right:8, so a full-width sheet was shoved 8px right and overflowed
	 * the right edge (off-centre + off-screen). Higher specificity + !important
	 * beats the blanket's !important left/right. */
	.wccb-popover.wccb-popover--sheet {
		left: 0 !important;
		right: 0 !important;
		bottom: 0 !important;
		width: 100%;
		max-width: none;
		border-radius: 16px 16px 0 0;
	}
	.wccb-popover__presets { grid-template-columns: repeat(3, 1fr); }

	/* Mobile sheet backdrop (Bug Backlog #5/#6, 2026-06-13). Dim layer ABOVE
	 * the canvas + frame-bar (9996) + bottombar, BELOW the popover (9999), so a
	 * tap beside a bottom-sheet popover reliably closes it on touch (Konva can't
	 * swallow a tap on a real DOM element). Mirrors the Foto-lade tray backdrop. */
	.wccb-popover-backdrop.is-on {
		display: block;
		position: fixed;
		inset: 0;
		z-index: 9998;
		background: rgba(26, 31, 34, .28);
	}
	/* Corner close on the per-frame popover (the wall popover has its own header
	 * X). The grip handle sits at top-center, so this clears it at top-right. */
	.wccb-popover__sheet-close { display: inline-flex; }

	/* "Wat is inbegrepen?" tooltip (Bug Backlog #7). Its info-row ancestor is
	 * overflow:hidden on mobile (to truncate the status line), which CLIPPED the
	 * upward-opening absolute tooltip — it opened but was invisible. position:
	 * fixed escapes the clip (no transformed ancestor) and anchors it as a
	 * full-width card above the stacked bottombar. */
	.wccb-bundle-tooltip {
		position: fixed;
		left: 12px;
		right: 12px;
		bottom: calc(env(safe-area-inset-bottom, 0) + 132px);
		min-width: 0;
		max-width: none;
		z-index: 9999;
		/* The info row sets white-space:nowrap (to truncate the status line);
		 * the tooltip inherits it and its bullets ran off-screen. Reset. */
		white-space: normal;
	}

	/* Bigger chips for desktop topbar collapse on phone. */
	.wccb-topbar { overflow-x: auto; padding-top: env(safe-area-inset-top, 0); }
	.wccb-tool-btn span { display: none; }

	/* Bottombar — Mobile Builder P1 (§36): info line on top, price + CTA on
	 * one row below, safe-area padded. The app grid's bottom row is `auto`
	 * so the bar may grow past 60px. */
	.wccb-bottombar {
		flex-wrap: wrap;
		gap: 4px 8px;
		padding: 10px 12px calc(10px + env(safe-area-inset-bottom, 0));
	}
	.wccb-bottombar__info {
		flex: 1 1 100%;
		font-size: 11px;
		white-space: nowrap;
		overflow: hidden;
	}
	.wccb-bottombar__price { margin-left: 0; font-size: 20px; }
	.wccb-bottombar .wccb-btn--primary[data-act="checkout"] {
		margin-left: auto;
		min-height: 48px;
	}
}

/* ============================================================ */
/* Option-C redesign: canvas-edge "+" affordance, FAB, slim      */
/* sidebar header, single popover, new dimension popover         */
/* variants.                                                     */
/* ============================================================ */

/* Slim sidebar header + hint */
.wccb-panel--slim .wccb-panel__header {
	padding: 14px 16px 8px;
	border-bottom: 1px solid #F1F4F6;
	margin-bottom: 8px;
}
.wccb-panel__eyebrow {
	font-family: var(--font-mono);
	font-size: 10px;
	letter-spacing: .16em;
	text-transform: uppercase;
	color: var(--ink-muted);
}
.wccb-accordion__hint {
	font-size: 11px;
	color: var(--ink-muted);
	background: #FAFBFC;
	border: 1px dashed #E4E8EA;
	border-radius: 3px;
	padding: 8px 10px;
	margin: 0 0 12px;
	line-height: 1.5;
}
.wccb-accordion__hint strong { color: #235C68; }

/* Canvas-edge "+" affordance removed (Mobile Builder P1) — JS never rendered
 * .wccb-canvas-add; the FAB (.wccb-canvas-fab) is the add affordance. */
/* Hide the hover "+" once frames exist — the FAB + per-frame controls take over. */
.wccb-stage[data-frame-count]:not([data-frame-count="0"]) .wccb-canvas-add {
	display: none;
}

/* Bottom-right FAB */
.wccb-fab {
	position: absolute;
	right: 20px;
	bottom: 20px;
	z-index: 40;
	display: inline-flex;
	align-items: center;
	gap: 8px;
	padding: 12px 16px;
	padding-right: 18px;
	background: #1A1F22;
	color: #FAFBFC;
	border: 0;
	border-radius: 999px;
	font: 500 13px/1 'Inter Tight', 'Helvetica Neue', Helvetica, Arial, sans-serif;
	letter-spacing: .02em;
	cursor: pointer;
	box-shadow: 0 8px 24px rgba( 21, 20, 15, .18 ), 0 2px 6px rgba( 21, 20, 15, .1 );
	transition: background .18s ease;
}
.wccb-fab:hover { background: #235C68; }
.wccb-fab svg { width: 16px; height: 16px; }
.wccb-fab__label { white-space: nowrap; }

/* FAB popover — wider variant holding all three size groups */
.wccb-popover--fab {
	width: 320px;
	max-height: 460px;
	overflow-y: auto;
}
.wccb-popover--default-mat {
	width: 280px;
}

/* Preset-mode popover: bigger tiles, 2-col grid, matches Wandhaus editorial weight */
.wccb-popover__presets--preset {
	grid-template-columns: 1fr 1fr;
	gap: 6px;
}
/* Phase Z.20 2026-05-30: the load-bearing [hidden] reset the bundle was missing.
 * .wccb-popover__presets sets display:grid (above), which overrides the UA
 * [hidden]{display:none} on origin + source order, so an inactive orientation
 * panel carrying the hidden attribute stayed visually rendered (stacked) - the
 * Formaat orientation tabs flipped panel.hidden but nothing moved on screen and
 * 30x20 (landscape) sat below the fold. Mirrors the four existing per-component
 * [hidden] patches. Attr selector (0,2,0) beats the bare class; !important is
 * belt-and-suspenders vs a future source reorder. */
.wccb-popover__presets[hidden] { display: none !important; }
.wccb-popover__presets--preset .wccb-popover__preset {
	padding: 10px 12px;
	text-align: left;
	font-family: var(--font-mono);
	font-size: 12px;
}
.wccb-popover__presets--preset .wccb-popover__preset.is-on {
	background: #1A1F22;
	color: #FAFBFC;
	border-color: #1A1F22;
}

/* Soft-warning bar above the bottom bar — surfaces frame-overlap state
 * after a wall resize or destructive material change. Hidden until at
 * least one frame carries a `warning='overlap'` flag. */
/* Phase P 2026-05-10: V2 warn-token migration. Hardcoded hex
 * (#FAF1DF/#C08A2E/#8A6418/#fff) replaced with --warn-soft / --warn /
 * --warn-soft-ink / --on-ink / --surface. Pill chips keep 999px radius
 * (chip register §02); container stays sharp per §09. */
.wccb-warning-bar[hidden] { display: none; }
.wccb-warning-bar {
	display: flex;
	align-items: center;
	gap: 10px;
	padding: 8px 16px;
	background: var(--warn-soft);
	border-top: 1px solid var(--warn);
	color: var(--warn-soft-ink);
	font-size: 12px;
	flex-wrap: wrap;
}
.wccb-warning-bar__icon {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 18px;
	height: 18px;
	border-radius: 50%;
	background: var(--warn);
	color: var(--on-ink);
	font-weight: 700;
	flex-shrink: 0;
}
.wccb-warning-bar__text { flex: 0 1 auto; }
.wccb-warning-bar__chips { display: flex; gap: 6px; flex-wrap: wrap; margin-left: auto; }
.wccb-warning-bar__chip {
	background: var(--surface);
	border: 1px solid var(--warn);
	color: var(--warn-soft-ink);
	padding: 3px 10px;
	border-radius: var(--radius-pill);
	cursor: pointer;
	font: 500 11px/1 var(--font-body);
	transition: background .15s var(--ease);
}
.wccb-warning-bar__chip:hover { background: var(--warn-soft); }

/* Orientation sections inside the Formaat popover — Vierkant / Staand /
 * Liggend headers above their respective preset tiles. The header is
 * intentionally quiet (small caps, muted colour) so the tiles dominate. */
.wccb-popover__section + .wccb-popover__section { margin-top: 12px; }
.wccb-popover__section-label {
	font-family: var(--font-mono);
	font-size: 10px;
	letter-spacing: .14em;
	text-transform: uppercase;
	color: var(--ink-muted);
	margin: 0 0 6px;
}

/* Orientation tab strip — sits between the title and preset grid in the
 * Formaat popover. Phase Z.19 2026-05-13: now actually rendered (was
 * dead CSS); tokenised (was raw hex). Substrate axis lives on the
 * separate Variant chip, so these tabs are orientation-only:
 * Vierkant / Staand / Liggend with preset-count badges. */
.wccb-axis-tabs {
	display: flex;
	gap: 4px;
	margin: 8px 0 10px;
	padding: 3px;
	background: var(--bg-alt);
	border: 1px solid var(--border);
	border-radius: 4px;
}
.wccb-axis-tabs__tab {
	flex: 1 1 0;
	padding: 7px 10px;
	background: transparent;
	border: 0;
	border-radius: 3px;
	font-family: var(--font-mono);
	font-size: 11px;
	letter-spacing: .04em;
	color: var(--ink-muted);
	cursor: pointer;
	transition: background-color .12s ease, color .12s ease;
}
.wccb-axis-tabs__tab:hover {
	color: var(--ink);
}
.wccb-axis-tabs__tab.is-active {
	background: var(--ink);
	color: var(--on-ink, #fff);
}
.wccb-axis-tabs__count {
	opacity: .55;
	font-size: 10px;
	margin-left: 2px;
}

/* Single-mode readout */
.wccb-popover__single {
	padding: 14px 16px;
	background: #FAFBFC;
	border: 1px solid #E4E8EA;
	border-radius: 3px;
	display: flex;
	align-items: baseline;
	gap: 10px;
	flex-wrap: wrap;
}
.wccb-popover__single strong {
	font-family: 'Fraunces', 'Tiempos Headline', Georgia, serif;
	font-size: 20px;
	letter-spacing: -0.01em;
	color: #1A1F22;
}
.wccb-popover__single span {
	font-family: var(--font-mono);
	font-size: 10px;
	color: var(--ink-muted);
	letter-spacing: .12em;
	text-transform: uppercase;
}

/* Stage hint nudges customer toward the new "+" affordance when canvas is empty */
.wccb-stage[data-frame-count="0"] .wccb-stage__hint::after {
	content: " ⬇";
}

/* ─────────────────────────────────────────────────────────────────
 * Bottombar primary actions — "+ Frame" and "Wand" replace the deleted
 * left sidebar without adding floating FAB chrome on top of the canvas.
 * Sit on the left edge of the bottom bar, popovers open UPWARD.
 * ───────────────────────────────────────────────────────────────── */
/* Phase Z.2/Z.3 2026-05-12: removed .wccb-bottombar__actions and its
 * children (.wccb-bottombar__add, .wccb-bottombar__wall). The Frame
 * button is now a canvas FAB (.wccb-canvas-fab below) and Wand
 * instellen moved to the topbar as .wccb-tool-btn--icon. */

/* Phase Z.2 2026-05-12: "+ Frame toevoegen" promoted to a primary
 * canvas FAB anchored bottom-left of .wccb-stage-wrap (where the
 * zoom pill used to live). Bigger than the old bottombar pill so it
 * visually owns the "add the next frame" action. Same orange accent
 * + shadow-2 lift as primary buttons. */
.wccb-canvas-fab {
	position: absolute;
	left: 16px;
	bottom: 16px;
	z-index: 50;
	display: inline-flex;
	align-items: center;
	gap: 8px;
	height: 44px;
	padding: 0 18px 0 14px;
	border: 0;
	border-radius: var(--radius-pill);
	background: var(--accent);
	color: #FFF;
	font-family: var(--font-body);
	font-size: 14px;
	font-weight: 600;
	letter-spacing: .01em;
	cursor: pointer;
	box-shadow: var(--shadow-2);
	transition: background .15s ease, transform .15s ease, box-shadow .15s ease;
}
.wccb-canvas-fab:hover {
	background: var(--accent-dark, #1A4651);
	color: var(--on-ink, #FFFFFF); /* re-assert white: Storefront's element-level `button:hover` darkens text otherwise */
	transform: translateY(-1px);
	box-shadow: 0 6px 18px rgba(35, 92, 104, .35);
}
.wccb-canvas-fab:active {
	transform: translateY(0);
	box-shadow: var(--shadow-1);
}
.wccb-canvas-fab[aria-expanded="true"] {
	box-shadow: 0 0 0 3px rgba(35, 92, 104, .25), var(--shadow-2);
}
.wccb-canvas-fab svg {
	flex: 0 0 auto;
}

/* Material picker grid inside the "+" Frame FAB popover */
.wccb-fab-mats {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 6px;
	margin-bottom: 6px;
}
.wccb-fab-mat {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 8px;
	padding: 10px 12px;
	background: #fff;
	border: 1px solid var(--color-border, #E4E8EA);
	border-radius: 10px;
	font: 500 12px/1.2 'Inter Tight', system-ui, sans-serif;
	color: #1A1F22;
	cursor: pointer;
	transition: border-color .15s ease, background .15s ease;
	text-align: left;
}
.wccb-fab-mat:hover { border-color: #235C68; }
.wccb-fab-mat.is-on {
	border-color: #235C68;
	background: #F1F4F6;
}
.wccb-fab-mat__name { flex: 1; }
.wccb-fab-mat__check {
	display: inline-flex;
	color: #235C68;
}
.wccb-fab-mat__check svg { width: 14px; height: 14px; }

/* File-upload styled label inside the Wand popover */
.wccb-fab-upload {
	display: flex;
	align-items: center;
	gap: 10px;
	padding: 10px 12px;
	border: 1px dashed var(--color-border, #CED6DA);
	border-radius: 10px;
	font: 500 12px/1.2 'Inter Tight', system-ui, sans-serif;
	color: #1A1F22;
	cursor: pointer;
	background: #fff;
	transition: border-color .15s ease, background .15s ease;
}
.wccb-fab-upload:hover {
	border-color: #235C68;
	background: #F1F4F6;
}
.wccb-fab-upload svg { width: 16px; height: 16px; }

.wccb-popover--wall {
	width: 280px;
}

/* Wall-color swatch grid in the Wand popover */
.wccb-wall-swatches {
	display: grid;
	grid-template-columns: repeat(6, 1fr);
	gap: 6px;
	margin-bottom: 4px;
}
.wccb-wall-swatch {
	position: relative;
	aspect-ratio: 1 / 1;
	border-radius: 6px;
	border: 1px solid rgba( 21, 20, 15, .12 );
	cursor: pointer;
	padding: 0;
	transition: transform .15s ease, box-shadow .15s ease, border-color .15s ease;
}
.wccb-wall-swatch:hover {
	transform: translateY( -1px );
	box-shadow: 0 4px 10px rgba( 21, 20, 15, .12 );
}
.wccb-wall-swatch.is-on {
	border-color: #235C68;
	box-shadow: 0 0 0 2px #235C68, 0 4px 10px rgba(35, 92, 104, .25 );
}
/* Custom-color tile — inherits base size from .wccb-wall-swatch so it
   visually matches the curated tiles. White surface + small muted "+"
   centered. When active (a custom hex is set), the tile fills with the
   chosen color and the "+" hides. The native <input type="color"> is
   absolutely positioned inside the button, fully transparent, so the
   entire tile is the click target. */
.wccb-wall-swatch--custom {
	background: #ffffff;
	color: var(--ink-muted);
	display: inline-flex;
	align-items: center;
	justify-content: center;
	overflow: hidden;
}
.wccb-wall-swatch--custom svg {
	width: 14px;
	height: 14px;
	pointer-events: none;
}
.wccb-wall-swatch--custom.is-on svg { display: none; }
.wccb-wall-swatch--custom input[type="color"] {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	border: 0;
	margin: 0;
	padding: 0;
	background: transparent;
	cursor: pointer;
	opacity: 0;
}

/* Per-frame customer-options popover (Probo customer_options) */
.wccb-popover__group {
	margin-bottom: 12px;
}
.wccb-popover__group:last-of-type {
	margin-bottom: 4px;
}
.wccb-popover__group-label {
	font-size: 11px;
	text-transform: uppercase;
	letter-spacing: .06em;
	color: var(--color-ink-muted, #5A6063);
	font-weight: 600;
	margin-bottom: 6px;
}

/* Resume-design banner — fades in at the top of the builder when the
 * customer returns to a saved design. Auto-dismisses after 8s; close
 * button lets them remove it sooner. */
/* Phase P 2026-05-10: banner consistency pass.
 * - Legacy --color-* tokens swapped to V2 (--surface, --border, --ink,
 *   --ink-muted, --bg-alt, --warn, --warn-soft, --warn-soft-ink).
 * - Resume banner: sharp corners (was 10 px) per §09 popover register.
 * - Eyebrow letter-spacing .08em → .14em (V2 mono-caps tracking).
 * - Close button: added explicit `padding: 0; box-sizing: border-box;
 *   appearance: none; display: inline-flex; align-items + justify-content
 *   center;` so the × renders in a tight 28×28 circle without the
 *   browser's user-agent button padding stretching it. */
.wccb-resume-banner {
	position: absolute;
	top: 12px;
	left: 50%;
	transform: translateX(-50%);
	/* Phase R 2026-05-10: z-index 30 → 100 to overlay canvas chrome
	 * (.wccb-zoom at 50). Banners are user-attention messages, must sit
	 * above passive chrome. */
	z-index: 100;
	display: flex;
	align-items: center;
	gap: 12px;
	max-width: calc(100% - 24px);
	padding: 8px 14px 8px 8px;
	background: var(--surface);
	border: 1px solid var(--border);
	border-radius: 0;
	box-shadow: var(--shadow-2);
	animation: wccb-resume-banner-in 280ms ease-out both;
}
.wccb-resume-banner__thumb {
	width: 56px;
	height: 56px;
	object-fit: contain;
	background: var(--bg-alt);
	flex: 0 0 auto;
}
.wccb-resume-banner__body {
	min-width: 0;
}
.wccb-resume-banner__eyebrow {
	font-family: var(--font-mono);
	font-size: 10px;
	text-transform: uppercase;
	letter-spacing: .14em;
	color: var(--ink-muted);
	font-weight: 500;
}
.wccb-resume-banner__title {
	font-family: var(--font-body);
	font-size: 14px;
	font-weight: 500;
	color: var(--ink);
	line-height: 1.3;
}
.wccb-resume-banner__close {
	flex: 0 0 auto;
	width: 28px;
	height: 28px;
	padding: 0;
	border: 0;
	background: transparent;
	appearance: none;
	-webkit-appearance: none;
	box-sizing: border-box;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	font: 500 20px/1 var(--font-body);
	color: var(--ink-muted);
	cursor: pointer;
	border-radius: 50%;
	transition: background .15s var(--ease), color .15s var(--ease);
}
.wccb-resume-banner__close:hover {
	background: var(--bg-alt);
	color: var(--ink);
}
@keyframes wccb-resume-banner-in {
	from { opacity: 0; transform: translate(-50%, -8px); }
	to   { opacity: 1; transform: translate(-50%, 0); }
}


/* In-button spinner variant that derives its colour from the button text
 * (currentColor) so it shows on ghost buttons too — the base .wccb-btn__spinner
 * is white-on-terracotta. Used by the conflict-modal busy state (spinner inside
 * the clicked button, matching the add-to-cart button). */
.wccb-btn__spinner--ink {
	border-color: currentColor;
	border-top-color: transparent;
}
.wccb-btn[data-conflict-act].is-faded { opacity: .4; }
.wccb-btn[data-conflict-act].is-busy { display: inline-flex; align-items: center; justify-content: center; gap: 8px; }

/* ─────────────────────────────────────────────────────────────────
 * Touch density — Mobile Builder P1+2 (design preview §36).
 * Capability-gated on (pointer:coarse), NOT width: a small desktop
 * window keeps mouse density; a large tablet gets touch density.
 * --tap token lives in the theme's tokens.css (44px fallback here).
 * ───────────────────────────────────────────────────────────────── */
.wccb-app button,
.wccb-frame-bar button,
.wccb-reposition-bar button,
.wccb-popover button,
.wccb-modal-overlay button { touch-action: manipulation; }

@media (pointer: coarse) {
	.wccb-tool-btn {
		min-width: var(--tap, 44px);
		height: var(--tap, 44px);
	}
	.wccb-topbar__back {
		width: var(--tap, 44px);
		height: var(--tap, 44px);
	}
	.wccb-popover__preset { min-height: var(--tap, 44px); }
	/* ⓘ info dot: 32px real size (44 would blow up the inline info row);
	 * negative margin keeps the row height unchanged. */
	.wccb-bottombar__included-info {
		width: 32px;
		height: 32px;
		margin: -7px 0;
	}
	/* ≥16px text on touch inputs — kills the iOS focus-zoom (B7). */
	.wccb-wall-row input,
	.wccb-add-frame__free-input,
	.wccb-popover input,
	.wccb-modal-overlay input {
		font-size: 16px;
		min-height: var(--tap, 44px);
	}
}

/* "Meer" overflow chip is a phone-sheet affordance only (§36) — the desktop
 * frame bar shows every chip inline. */

/* ════════════════════════════════════════════════════════════════
   Photo library — upload drop-zone + thumbnail grid + drag/tap-to-place.
   Since the All-in-One sidebar (2026-06-14) these live INSIDE the editor
   sidebar (.wccb-lib, buildLibraryZone); the standalone .wccb-tray panel is
   retired. These class rules are shared by .wccb-lib. Token-based.
   ════════════════════════════════════════════════════════════════ */
.wccb-tray__drop { display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 16px 12px; border: 2px dashed var(--border-strong, #CED6DA); border-radius: var(--radius-sm, 6px); background: var(--bg-alt, #F1F4F6); color: var(--ink-muted, #5A6063); text-align: center; cursor: pointer; font-size: 12px; transition: border-color .15s, color .15s; }
.wccb-tray__drop:hover, .wccb-tray__drop.is-dragover { border-color: var(--accent, #235C68); color: var(--ink, #1A1F22); }
.wccb-tray__grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; }
.wccb-tray__empty { font-size: 12px; color: var(--ink-muted, #5A6063); text-align: center; padding: 8px 4px; }

.wccb-tray-thumb { position: relative; aspect-ratio: 1 / 1; border-radius: var(--radius-sm, 6px); overflow: hidden; cursor: grab; background: var(--bg-alt, #F1F4F6) center/cover no-repeat; border: 2px solid transparent; transition: border-color .15s, box-shadow .15s; }
.wccb-tray-thumb:hover { box-shadow: var(--shadow-2); }
.wccb-tray-thumb.is-selected { border-color: var(--accent, #235C68); box-shadow: 0 0 0 3px rgba(35, 92, 104, .22); cursor: pointer; }
.wccb-tray-thumb.is-uploading { cursor: default; }
.wccb-tray-thumb__spin { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; background: var(--bg-alt, #F1F4F6); color: var(--ink-muted, #5A6063); }
.wccb-tray-thumb__spin svg { animation: wccb-loader-spin 700ms linear infinite; transform-origin: 50% 50%; }
@media (prefers-reduced-motion: reduce) { .wccb-tray-thumb__spin svg { animation: none; } }
.wccb-tray-thumb__badge { position: absolute; left: 5px; bottom: 5px; display: inline-flex; align-items: center; padding: 1px 6px; border-radius: var(--radius-pill, 999px); background: rgba(26, 31, 34, .74); color: #FFF; font-size: 10px; font-weight: 600; line-height: 1.5; }
.wccb-tray-thumb__remove { position: absolute; top: 5px; right: 5px; width: 20px; height: 20px; border: 0; border-radius: var(--radius-pill, 999px); background: rgba(26, 31, 34, .6); color: #FFF; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; opacity: .9; transition: opacity .12s, background .12s; z-index: 2; }
.wccb-tray-thumb:hover .wccb-tray-thumb__remove, .wccb-tray-thumb.is-selected .wccb-tray-thumb__remove { opacity: 1; background: rgba(26, 31, 34, .82); }
/* A global builder `svg` rule collapses the × to width:0 — pin its box so the glyph always renders. */
.wccb-tray-thumb__remove svg { width: 11px; height: 11px; flex: none; display: block; }

/* pointer-drag preview (the photo follows the cursor/finger) + drop-hover highlight */
.wccb-tray-thumb.is-dragging { opacity: .4; }
.wccb-tray-drag-preview { position: fixed; z-index: 10000; width: 72px; height: 72px; margin: -36px 0 0 -36px; border-radius: var(--radius-sm, 6px); background: var(--bg-alt, #F1F4F6) center/cover no-repeat; box-shadow: var(--shadow-3); border: 2px solid var(--surface, #FFFFFF); pointer-events: none; transform: rotate(-3deg); }
.wccb-overlay--drop-hover { outline: 3px solid var(--accent, #235C68); outline-offset: -3px; background: color-mix(in srgb, var(--accent, #235C68) 14%, transparent); }
body.wccb-dragging-tray { cursor: grabbing; user-select: none; }

/* toggle button shown when the tray is hidden */
.wccb-tray-toggle { position: fixed; z-index: 41; top: 64px; right: 12px; display: inline-flex; align-items: center; gap: 7px; padding: 8px 13px; border: 1px solid var(--border, #E4E8EA); border-radius: var(--radius-pill, 999px); background: var(--surface, #FFFFFF); color: var(--ink, #1A1F22); font: 600 12px/1 var(--font-body); cursor: pointer; box-shadow: var(--shadow-1); transition: background .15s; }
.wccb-tray-toggle:hover { background: var(--bg-alt, #F1F4F6); }
.wccb-tray-toggle.is-hidden { display: none; }
.wccb-tray-toggle__n { display: inline-flex; align-items: center; justify-content: center; min-width: 16px; height: 16px; padding: 0 4px; border-radius: var(--radius-pill, 999px); background: var(--accent, #235C68); color: #FFF; font-size: 10px; }
/* Desktop: dock the "Foto's" toggle next to the "+ Frame toevoegen" FAB (bottom-left),
 * same height (44px) so they read as a matched pair. (Mobile keeps the fixed
 * top-right position via the base rule + the ≤767 z-index bump below.) */
@media (min-width: 768px) {
	.wccb-tray-toggle {
		position: absolute;
		left: 202px; /* FAB at left:16 is 176px wide ("+ Frame toevoegen") + 10px gap */
		bottom: 16px;
		top: auto;
		right: auto;
		z-index: 50; /* same layer as the FAB */
		height: 44px;
		padding: 0 16px;
		font-size: 14px;
		box-shadow: var(--shadow-2);
	}
}

/* a frame eligible to receive a tapped/dragged photo */
.wccb-overlay--place-target { outline: 2px dashed var(--accent, #235C68); outline-offset: -2px; }

@media (max-width: 767px) {
	/* The library grid goes 4-up on phones (the sidebar is full-width there). */
	.wccb-tray__grid { grid-template-columns: repeat(4, 1fr); }
	/* Toggle stays top-right (base rule) on mobile too — the bottom belongs to the
	   frame bar + bottombar; a bottom toggle collided with them. */
	.wccb-tray-toggle { z-index: 9990; }
	/* The resume banner ("Je gaat verder met dit ontwerp") is a top-of-canvas
	 * notification; on mobile the Foto's toggle is bumped to 9990, so the banner's
	 * base z-index 100 left it hidden underneath. Sit it just above the toggle but
	 * below the tray sheet (9998) so opening the tray still covers it. */
	.wccb-resume-banner { z-index: 9991; }
	.wccb-sheet-backdrop.is-on { display: block; position: fixed; inset: 0; z-index: 9998; background: rgba(26, 31, 34, .32); }
	/* In tray mode (sheet open OR a photo armed) hide the per-frame bar — you're
	   browsing/placing photos, not editing one frame. Otherwise the high-z (9996)
	   frame bar covers the sheet + pill (the reported "in the way" bug). */
	body.wccb-tray-mode .wccb-frame-bar,
	body.wccb-tray-mode .wccb-frame-bar.is-visible { display: none !important; }
}

/* mobile placement pill — shown after a photo is armed + the sheet auto-closes */
.wccb-tray-place-pill { position: fixed; z-index: 9999; left: 50%; transform: translateX(-50%); bottom: calc(env(safe-area-inset-bottom, 0px) + 140px); display: inline-flex; align-items: center; gap: 10px; padding: 9px 10px 9px 16px; border-radius: var(--radius-pill, 999px); background: var(--accent-ink, #1A4651); color: #FFF; font: 600 13px/1 var(--font-body); box-shadow: var(--shadow-2); }
.wccb-tray-place-pill[hidden] { display: none; }
.wccb-tray-place-pill button { width: 24px; height: 24px; border: 0; border-radius: var(--radius-pill, 999px); background: rgba(255, 255, 255, .18); color: #FFF; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; }
