/**
 * Oyolloo Elements — plate builder.
 *
 * Figma: file CW8dEwCSdslTKepAdS5uzb, node 472:7916 ("Frame 575", 1920x1008).
 * Full measurements in docs/figma-product-detail.md.
 *
 * The design is a three-stage wizard: a white card on a #f8f8f8 left half, with
 * the product image filling the right half.
 *
 * @package Oyolloo\Elements
 * @since   1.2.0
 */

.oe-plate-builder {
	/* Wizard chrome. #495aff is specific to the stepper and deliberately not a
	   brand token -- it appears nowhere else in the design. */
	--oe-step-accent: #495aff;
	--oe-step-idle:   #cfd6dc;
	--oe-step-text:   #abb7c2;
	--oe-panel:       #f8f8f8;

	/* One height for every single-line control in the form -- the select
	   triggers and the text inputs. 56/48 are the Figma sizes for the headline
	   pickers (472:8160, 472:9807); the step-3 frames draw their own rows at 46
	   and 44, and reading them literally left three heights stacked in the same
	   column. The client asked for one, so the step-3 sizes are dropped rather
	   than the base. Change it here and every control follows. */
	--oe-pb-control-h: 56px;

	/* Physical plate colours for the preview. */
	--oe-plate-yellow: #ffd307;
	--oe-plate-white:  #f3f4f2;
	--oe-plate-blue:   #063298;
	--oe-plate-char:   #111111;
	--oe-plate-font:   'Charles Wright', 'UKNumberPlate', 'Arial Black', 'Helvetica Neue', Impact, sans-serif;
}

.oe-plate-builder {
	width: 100%;
}

.oe-plate-builder .oe-section {
	--oe-section-y: 0px;

	width: 100%;
	padding: 0;
}

/* Two equal halves across the full viewport, as in the 1920 frame. */
.oe-pb {
	display: grid;
	grid-template-columns: 1fr 1fr;
	align-items: stretch;
	width: 100%;
	min-height: 1008px;
}

/*
 * Pinned to the top, not centred. Figma 472:8438 hangs the card 100 from the
 * top of the 1008 section and lets it grow downwards. Centring it meant every
 * answer that changed the card's height — a size preview opening is 168px of
 * it — moved the card half that distance the other way, so choosing an option
 * threw the heading, the stepper and everything the customer was reading
 * upwards by 84px.
 */
.oe-pb__side {
	display: flex;
	align-items: flex-start;
	justify-content: center;
	/*
	 * Fluid, so the panel breathes at every width instead of stepping at two
	 * breakpoints. `clamp` reads floor / preferred / ceiling: the preferred term
	 * is a share of the viewport, and the two ends stop it collapsing on a phone
	 * or running away on a 4K monitor.
	 *
	 * The inline figure is the design's own — 80/1920 = 4.17vw — so at 1920 it
	 * lands exactly on the 80px Figma specifies.
	 *
	 * The block figure is deliberately tighter than Figma's 100px. The form is
	 * long enough that the drawn top padding pushed the first question well down
	 * the fold, so it runs at 3.2vw capped to 70.
	 */
	padding: clamp(30px, 3.2vw, 70px) clamp(20px, 4.17vw, 80px);
	background-color: var(--oe-panel);
	min-width: 0;
}

/* Figma 472:7919: 800 wide, radius 20, padding 40, and a soft drop shadow the
   card is not the same object without. */
.oe-pb__card {
	display: flex;
	flex-direction: column;
	gap: 24px;
	width: 100%;
	max-width: 800px;
	/* Fluid on the same principle as the panel around it — 40/1920 for the
	   preferred term, floored so it never closes up on a phone. */
	padding: clamp(16px, 2.08vw, 40px);
	border-radius: 20px;
	background-color: var(--oe-color-white);
	box-shadow: 0 4px 22px rgba(0, 0, 0, 0.1);
}

.oe-pb__heading {
	margin: 0;
	font-family: var(--oe-font-title);
	/* 48/1920 for the preferred term, floored at the 36px the phone layout
	   used and capped at the design's 48. */
	/*
	 * Smaller floor and a gentler slope than the 2.5vw first tried: the panel is
	 * a column inside the page, not the page, so the heading has roughly half
	 * the width the viewport suggests and 36px broke "Get your customize number
	 * plate" onto two lines well before the layout was tight. 23 rather than 26
	 * for the floor -- the phone frame is narrower still.
	 */
	font-size: clamp(23px, 1.9vw, 48px);
	font-weight: 700;
	/* Unitless, or the leading would stay at 52.8 while the size shrank. */
	line-height: 1.1;
	color: var(--oe-color-black);
	text-wrap: balance;
}

/* -- Stepper (720x66) ---------------------------------------------------- */

.oe-pb__stepper {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	margin: 0;
	padding: 0;
	list-style: none;
	border: 1px solid var(--oe-step-idle);
	border-radius: 16px;
	/* The chevron between two steps is taller than the bar on purpose — Figma
	   472:7927 is 71.5 in a 66 frame, hung at y -3 — and the frame it sits in
	   clips it. Without the clip here the two points break the rounded edge
	   top and bottom. */
	overflow: hidden;
}

.oe-pb__stepper-item {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 16px;
	/* Figma's 24px gutter is what a 1920 screen gets, where the card reaches
	   its full 800. Narrower than that the card shrinks with the viewport, and
	   a fixed gutter turned the bar's own `overflow: hidden` into a guillotine
	   through the middle of the last label. */
	padding: 16px clamp(8px, 1.7vw, 24px);
	position: relative;
	min-width: 0;
}

/*
 * The click target for a step already behind the customer. Absolutely placed
 * over the whole cell, so the number and the label keep their own layout and
 * every rule that colours them through `.oe-pb__stepper-item` still lands.
 *
 * The cell is already `position: relative` for its chevron, so this has a
 * containing block without one being added for it.
 */
.oe-pb__stepper-go {
	position: absolute;
	inset: 0;
	padding: 0;
	border: 0;
	background: none;
	cursor: pointer;
}

/* A reachable step answers the pointer. It had nothing but a cursor to say so,
   which was survivable while only finished steps were live; now that a step
   ahead can be one too, the bar has to look like something you may press. The
   tint is deliberately faint — this is a progress bar being helpful, not a row
   of buttons competing with Save & Next below it. */
.oe-pb__stepper-go {
	transition: background-color 160ms var(--oe-ease, ease);
}

.oe-pb__stepper-go:not([disabled]):hover {
	background-color: rgba(26, 26, 26, 0.04);
	border-radius: 12px;
}

/* Not yet reachable: the current step and everything ahead of it. Still in the
   DOM so the accessibility tree keeps a stable shape, just inert. */
.oe-pb__stepper-go[disabled] {
	cursor: default;
}

/* The visible label sits outside this button, so focus has to be drawn on the
   overlay itself or keyboard users get no ring at all. Inset, so it traces the
   cell rather than spilling over the divider beside it. */
.oe-pb__stepper-go:focus-visible {
	outline: 2px solid var(--oe-step-accent, #495aff);
	outline-offset: -3px;
	border-radius: 12px;
}

/* Figma 472:7927: a 16x72 chevron whose right edge lands on the boundary
   between two cells, overflowing the 66px bar at both ends. */
.oe-pb__stepper-item + .oe-pb__stepper-item::before {
	content: "";
	position: absolute;
	top: 50%;
	inset-inline-start: 0;
	width: 16px;
	height: 72px;
	transform: translate(-100%, -50%);
	background-image: url("../../images/stepper-arrow.svg");
	background-position: center;
	background-repeat: no-repeat;
	background-size: 16px 72px;
}

.oe-pb__stepper-n {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	flex-shrink: 0;
	width: 34px;
	height: 34px;
	border: 2px solid var(--oe-step-idle);
	border-radius: 20px;
	color: var(--oe-step-text);
	font-family: var(--oe-font-body);
	font-size: 16px;
	font-weight: 500;
	line-height: 1;
	transition: border-color 200ms var(--oe-ease, ease), color 200ms var(--oe-ease, ease);
}

.oe-pb__stepper-label {
	font-family: var(--oe-font-body);
	font-size: 14px;
	font-weight: 500;
	line-height: 20px;
	color: var(--oe-step-text);
	text-transform: uppercase;
	white-space: nowrap;
	/* Ellipsis rather than a hard cut, for the widths between a phone and the
	   1920 the bar is drawn at. */
	min-width: 0;
	overflow: hidden;
	text-overflow: ellipsis;
	transition: color 200ms var(--oe-ease, ease);
}

/*
 * The accent belongs to the step being worked on. A finished step is yellow
 * and is styled once, far below -- naming it here as well left two rules
 * fighting over the same circle, decided only by which came later in the file.
 */
.oe-pb__stepper-item.is-active .oe-pb__stepper-n {
	border-color: var(--oe-step-accent);
	color: var(--oe-step-accent);
}

.oe-pb__stepper-item.is-active .oe-pb__stepper-label,
.oe-pb__stepper-item.is-done .oe-pb__stepper-label {
	color: var(--oe-step-accent);
}

/*
 * Answered, and ahead of where the customer is standing — what step 3 becomes
 * when they walk back to step 1 to check something.
 *
 * It is not `is-done`: that is the tick for a step behind them, and a bar with
 * a tick on every cell says the order does not matter. But it must not read as
 * a step they have never reached either, because that one is genuinely shut
 * and this one is a click away. The label takes the live colour and the circle
 * keeps its idle grey — reachable, not finished with.
 */
.oe-pb__stepper-item.is-visited .oe-pb__stepper-label {
	color: var(--oe-step-accent);
}

/* -- Stages and fields --------------------------------------------------- */

.oe-pb__stage[hidden] { display: none; }

/*
 * A stage never scrolls sideways itself — the size rows inside it are their own
 * horizontal scrollers and own that job.
 *
 * Saying so matters on a phone. Those rows clip their content visually, but the
 * stage still *reported* it: on step 2 the panel measured 437px wide with a
 * 1168px scroll width, which walked up through the Elementor wrappers and made
 * the document 1192 wide against a 485 viewport. The page could not actually be
 * scrolled — `body` clips — so nothing looked wrong on desktop, where 1192 fits
 * anyway. A phone reads that width when it picks the initial scale, so it laid
 * the page out at 1192 and zoomed out to fit, which is why step 2 arrived as a
 * thumbnail-sized column with white space beside it. Step 1, whose fields carry
 * no scrollers, measured 494 and rendered correctly.
 *
 * `clip`, not `hidden`: `hidden` would make the stage a scroll container and
 * force `overflow-y` to `auto` with it, hanging a vertical scrollbar on a panel
 * that should just be tall. The `hidden` line is only there for Safari below 16.
 */
.oe-pb__stage {
	overflow-x: hidden;
	overflow-x: clip;
	/*
	 * The stage is given a place in the stacking order of its own, so the
	 * dropdowns inside it can paint over the summary and the buttons below.
	 *
	 * A menu carries `z-index: 20`, which sounds like enough and is not: a
	 * z-index only ranks a box against its siblings inside the nearest
	 * stacking context. The entrance animation leaves `transform:
	 * translateY(0px)` on the stage — an identity transform, invisible, and
	 * still enough to make the stage that context. Every menu was then sealed
	 * inside a box that itself ranked below `.oe-pb__totals`, its own later
	 * sibling: the brand list's rows from the fifth down were drawn over the
	 * summary while the summary took the clicks, so they could be read and not
	 * chosen.
	 *
	 * animateIn() in plate-builder.js now clears that transform, which fixes
	 * the same thing from the other end. This stays because it makes the
	 * outcome deliberate rather than incidental: a stage interrupted
	 * mid-animation never fires its `complete`, and one identity transform
	 * anywhere in this subtree would otherwise bring the whole thing back.
	 */
	position: relative;
	z-index: 1;
}

/* Figma: #f8f8f8 panel, radius 16, padding 20, gap 20. */
/*
 * A two-column grid that all but two fields span.
 *
 * Figma 472:8157 stands Front Plate Size and Rear Plate Size side by side —
 * 330 and 330 across the 680 row on desktop, 151.5 each on a phone — and a
 * flex column can only ever stack them. Everything else claims both tracks, so
 * the rest of the form is unchanged and a hidden field takes no track at all.
 */
.oe-pb__fields {
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	align-items: start;
	gap: 20px;
	padding: 20px;
	border-radius: 16px;
	background-color: var(--oe-panel);
}

.oe-pb__fields > * {
	grid-column: 1 / -1;
}

/*
 * The two size pickers used to share a row as narrow dropdowns. The client's
 * revision made each one a scrolling row of photographs, and such a row in a
 * half-width column is 206px of a 1268px scroll — so they go back to spanning
 * the grid, which is what `.oe-pb__fields > *` above already does for
 * everything else. The rule is deleted rather than inverted; there is nothing
 * left for it to say.
 */

.oe-pb__field {
	display: flex;
	flex-direction: column;
	gap: 16px;
	margin: 0;
	padding: 0;
	border: 0;
}

.oe-pb__field[hidden] { display: none; }

/* A <legend> renders display:table and sits outside its fieldset's flex flow,
   so the 16px gap Figma 472:7939 puts under every label never applied. Margin
   is the only spacing that reaches it. */
.oe-pb__label {
	padding: 0;
	margin-bottom: 16px;
	font-family: var(--oe-font-body);
	font-size: 16px;
	font-weight: 500;
	line-height: 22.4px;
	color: var(--oe-color-ink-strong);
}

.oe-pb__help {
	margin: -8px 0 0;
	font-size: var(--oe-body-sm-size);
	color: var(--oe-color-neutral-500);
}

/* Registration field: 680x84, brand yellow, 2px black at 20%, radius 8. */
/* Figma 472:7941. The box the customer types into is a plain brand-yellow
   plate: no blue band, no inner well, the registration set 40px and centred.
   The band belongs on the preview -- that is what they are buying, not what
   they are filling in. Scoped to the builder so the shared plate field keeps
   its own look everywhere else. */
.oe-pb__field .oe-plate {
	gap: 16px;
	padding: 20px 24px;
	border: 2px solid rgba(0, 0, 0, 0.2);
	border-radius: 8px;
	background-color: var(--oe-color-brand);
}

.oe-pb__field .oe-plate__well {
	min-height: 0;
	padding: 0;
	border-radius: 0;
	background-color: transparent;
	box-shadow: none;
}


/*
 * This selector is (0,2,0) against field.css's plain `.oe-plate__input`, so it
 * also silently beat that file's `@media` steps: the search field shrank to h5
 * on a tablet and h6 on a phone, and the builder's plate stayed at 40px at
 * every width because the winning rule never restated it. It fits — the widest
 * legal plate, MM11 MMM, measures 202px in a 243px well at 390 — but only just,
 * so it ramps down with the well instead of relying on that margin.
 *
 * A clamp rather than a media query on purpose: any breakpoint here would lose
 * to nothing, but would reintroduce the same trap for the next reader.
 */
.oe-pb__field .oe-plate__input {
	padding: 0;
	border: 0 !important;
	border-radius: 0;
	background-color: transparent;
	color: var(--oe-color-black);
	font-family: var(--oe-font-title);
	font-size: clamp(28px, 0.78vw + 25px, 40px);
	font-weight: 800;
	line-height: 1.1;
	text-align: center;
}

/* The plate is the field here — there is no input chrome to light up, so the
   focus ring drew a dark rectangle across the yellow. Ours only ever set
   `border-color`, which the `border: 0` above makes inert, so what showed was
   the browser's own ring: `outline` alone does not clear it, because a theme
   can put the ring on `box-shadow` or re-assert a border. All three go.

   The caret is the focus cue that remains. That is enough for a text box —
   it is visible, it blinks, and it is where typing happens — but it is the
   only one, so nothing else in the builder should follow this pattern. */
.oe-pb__field .oe-plate__input:focus,
.oe-pb__field .oe-plate__input:focus-visible {
	border: 0;
	outline: none;
	box-shadow: none;
}

.oe-pb__field .oe-plate__input::placeholder {
	color: var(--oe-color-black);
	opacity: 0.3;
}

.oe-pb__field .oe-plate__submit { display: none; }

/* -- Choices ------------------------------------------------------------- */

.oe-pb__choices {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
	align-items: center;
}

.oe-pb__choice { flex: 1 1 auto; min-width: 0; }
.oe-pb__choice[hidden] { display: none; }

.oe-pb__choice-label {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 8px;
	height: 100%;
	padding: 16px;
	border: 1px solid var(--oe-color-neutral-300);
	border-radius: 8px;
	background-color: var(--oe-color-white);
	cursor: pointer;
	/* `background-color` belongs here even though the card variants never move
	   it: the dropdown rows below do, on hover, and they are this same element.
	   Left out, every menu row in the builder — the reflective finish, the
	   holder quantity, the seventeen vehicle brands — lit up and went out with
	   no ramp at all, which is the one hover a customer runs down a whole list
	   of. */
	transition:
		border-color 160ms var(--oe-ease, ease),
		box-shadow 160ms var(--oe-ease, ease),
		background-color 160ms var(--oe-ease, ease);
}

.oe-pb__choice-label:hover { border-color: var(--oe-color-ink-strong); }

.oe-pb__choice input:checked + .oe-pb__choice-label {
	border-color: var(--oe-color-black);
	box-shadow: inset 0 0 0 1px var(--oe-color-black);
}

.oe-pb__choice input:focus-visible + .oe-pb__choice-label {
	outline: 2px solid var(--oe-step-accent);
	outline-offset: 2px;
}

.oe-pb__choice-name {
	font-family: var(--oe-font-body);
	font-size: 15px;
	line-height: 1.4;
}

.oe-pb__choice-price {
	font-family: var(--oe-font-body);
	font-size: 15px;
	font-weight: 700;
	white-space: nowrap;
}

/* -- Style picker -------------------------------------------------------- */

/* Figma 472:7964 hangs the "Most popular" flag 12px over the top edge of
   its tile, so the row has to carry that much clear space or the flag sits
   on the legend above it. */
/*
 * Three pills to a row, wrapping to as many rows as the plate type needs.
 *
 * `minmax(0, 1fr)` gave auto-fit no reason to stop, so it made one column per
 * style: fine against Figma 472:7959, which draws the three a 4D plate had at
 * the time, and ruinous against the thirteen the catalogue actually returns —
 * thirteen columns split the 680px row into 50px slivers and every name and
 * price spilled over its neighbour. The floor is what caps the count: the
 * design's pill is 221 wide, and anything above 165 leaves room for three and
 * not four.
 */
.oe-pb__styles {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
	gap: 8px;
	margin-top: 12px;
}

.oe-pb__style { min-width: 0; }

.oe-pb__style[hidden] { display: none; }

/* Figma 472:7962: a pill, not a card. Sixteen of padding, the name and its
   surcharge side by side, and a 1.5px black edge with a soft shadow once it is
   the chosen one. */
.oe-pb__style-label {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 6px;
	height: 100%;
	padding: 16px;
	border: 1.5px solid transparent;
	border-radius: 8px;
	background-color: var(--oe-color-white);
	cursor: pointer;
	text-align: center;
	transition: border-color 160ms var(--oe-ease, ease);
}

/* `~`, not `+`: the tile wearing the "Most popular" flag has that span sitting
   between the input and its label, which breaks adjacency -- so the one style
   the design most wants picked was the only one that never drew its border. */
.oe-pb__style input:checked ~ .oe-pb__style-label {
	border-color: var(--oe-color-black);
	box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
}

.oe-pb__style input:focus-visible ~ .oe-pb__style-label {
	outline: 2px solid var(--oe-step-accent);
	outline-offset: 2px;
}

/* The client's style photographs are product shots, not plate crops. At a
   520:111 plate ratio a 150px tile leaves a 32px strip nothing reads in --
   and being able to see which one looks nicer is the client's whole reason
   for merging the catalogue. */
/* ---------------------------------------------------------------------------
   A note on why the image rules below are scoped to .oe-pb.

   WooCommerce ships `.woocommerce img { height: auto }` and Elementor ships
   `.elementor img { height: auto }`. Both are (0,1,1) and outrank a single
   class, so on a product page every deliberately sized image in here fell
   back to its natural ratio -- a 300x300 size preview rendered 673px tall
   inside a 673px card. The extra ancestor is the whole fix.
   --------------------------------------------------------------------------- */

.oe-pb__style-name { font-size: 14px; font-weight: 600; }

.oe-pb__style-price {
	display: flex;
	align-items: baseline;
	gap: 6px;
	font-size: 14px;
	/* "+£10.65" holds no space, but a break after the plus is still a legal
	   opportunity and a 221px pill takes it — leaving the sign stranded on its
	   own line above the amount. */
	white-space: nowrap;
	flex-shrink: 0;
}

.oe-pb__style-price s { color: var(--oe-color-neutral-500); }

.oe-pb__error {
	margin: 0;
	color: #c1382e;
	font-size: var(--oe-body-sm-size);
}

.oe-pb__error[hidden] { display: none; }

/* -- Totals row (720x86) ------------------------------------------------- */

.oe-pb__totals {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 20px;
}

.oe-pb__total,
.oe-pb__qty {
	display: flex;
	flex-direction: column;
	gap: 8px;
}

.oe-pb__total-label {
	font-family: var(--oe-font-body);
	font-size: 14px;
	font-weight: 400;
	line-height: 22.4px;
	color: rgba(26, 26, 26, 0.6);
}

/* Same ramp as the plate above it, so the two large figures on this panel stay
   in proportion. On a phone the totals row is two columns of about 160px, and
   a four-figure order at the old flat 40px would have run past its cell. */
.oe-pb__total-value {
	font-family: var(--oe-font-title);
	font-size: clamp(28px, 0.78vw + 25px, 40px);
	font-weight: 600;
	line-height: 1.1;
	color: var(--oe-color-black);
	font-variant-numeric: tabular-nums;
}

/* Quantity stepper: 56 tall, white, 1px #d3d3d3, radius 8. */
/* Figma 472:7973: 16 all round, a 24px control at each end and the value
   taking the width between them. */
.oe-pb__qty-control {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 8px;
	height: 56px;
	padding: 16px;
	border: 1px solid var(--oe-color-neutral-300);
	border-radius: 8px;
	background-color: var(--oe-color-white);
	transition: border-color 160ms var(--oe-ease, ease);
}

/* The focus ring belongs to the control, not to the number inside it -- the
   browser's own outline on a bare input read as a stray yellow box. */
.oe-pb__qty-control:focus-within { border-color: var(--oe-color-ink-strong); }

.oe-pb__qty-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	flex: 0 0 auto;
	width: 24px;
	height: 24px;
	padding: 0;
	border: 0;
	background: none;
	cursor: pointer;
}

.oe-pb .oe-pb__qty-icon { display: block; width: 24px; height: 24px; }

.oe-pb__qty-btn:hover { background-color: var(--oe-color-neutral-100); }

/* Was 4ch, which clipped the numeral once it was set at 18px. It takes the
   space between the two controls instead, as the design has it. */
.oe-pb .oe-pb__qty-input {
	flex: 1 1 auto;
	min-width: 0;
	width: auto;
	height: 24px;
	padding: 0;
	border: 0;
	outline: 0;
	background: none;
	color: var(--oe-color-neutral-900, #0a0a0a);
	font-family: var(--oe-font-title);
	font-size: 18px;
	line-height: 1.1;
	text-align: center;
	font-weight: 500;
	font-variant-numeric: tabular-nums;
	-moz-appearance: textfield;
}

.oe-pb__qty-input::-webkit-outer-spin-button,
.oe-pb__qty-input::-webkit-inner-spin-button {
	appearance: none;
	margin: 0;
}

.oe-pb__qty-input:focus { outline: none; }

/* -- Actions ------------------------------------------------------------- */

/* Figma 472:8186 splits the row evenly — Back 353, Next 351, 16 between. */
.oe-pb__actions {
	display: flex;
	align-items: center;
	gap: 16px;
}

.oe-pb__actions > .oe-pb__back,
.oe-pb__actions > .oe-pb__next,
.oe-pb__actions > .oe-pb__submit {
	flex: 1 1 0;
	min-width: 0;
}

.oe-pb__next,
.oe-pb__submit {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 6px;
	flex: 1 1 auto;
	height: 56px;
	padding: 16px 24px;
	border-radius: 8px;
	background-color: var(--oe-color-black);
	/* Both restated for base.css's hover/focus reset. Without the surface,
	   clicking Add to cart left the button transparent — white on the panel —
	   for as long as it kept focus: the "goes white after you click it" report.
	   Without the foreground, hovering swapped the white label for the panel's
	   own near-black, which is what made a disabled Save & Next look like it
	   was still reacting to the pointer. */
	--oe-btn-surface: var(--oe-color-black);
	--oe-btn-fg: var(--oe-color-white);
	color: var(--oe-color-white);
	font-family: var(--oe-font-body);
	font-size: 16px;
	font-weight: 700;
	line-height: 24px;
	letter-spacing: -0.16px;
	text-transform: uppercase;
	cursor: pointer;
	transition:
		background-color 200ms var(--oe-ease, ease),
		color 200ms var(--oe-ease, ease),
		box-shadow 200ms var(--oe-ease, ease);
}

/* Both actions land on the brand yellow with black text: the black "Save &
   next" turns yellow, and "Add to cart" — already yellow — keeps its colour
   and answers with a lift instead, so the hover still reads.

   Guarded on `:not([disabled])`, or a button held shut still lights up under
   the cursor: the colour swap and the lift both say "press me" while the click
   does nothing, which is a worse lie than no hover at all. */
.oe-pb__next:not([disabled]):hover,
.oe-pb__submit:not([disabled]):hover {
	background-color: var(--oe-color-brand);
	color: var(--oe-color-ink-strong);
	box-shadow: 0 6px 18px rgba(0, 0, 0, 0.12);
}

.oe-pb__next[hidden],
.oe-pb__submit[hidden],
.oe-pb__back[hidden] { display: none; }

/* Figma 472:8186 gives Back and Save & Next an equal half of the row each,
   and sets the arrow beside the word rather than above it. */
.oe-pb__back {
	display: inline-flex;
	flex: 1 1 0;
	align-items: center;
	justify-content: center;
	gap: 6px;
	height: 56px;
	padding: 16px 24px;
	background-color: var(--oe-color-neutral-100);
	--oe-btn-surface: var(--oe-color-neutral-100);
	border: 1px solid var(--oe-color-neutral-300);
	border-radius: 8px;
	background-color: var(--oe-color-white);
	font-family: var(--oe-font-body);
	font-size: 16px;
	font-weight: 700;
	cursor: pointer;
	letter-spacing: -0.16px;
	text-transform: uppercase;
}

.oe-pb__back:hover { border-color: var(--oe-color-black); }

.oe-pb__cart { width: 24px; height: 24px; flex-shrink: 0; }

.oe-pb.is-pricing .oe-pb__total-value { opacity: 0.45; }

/* -- Preview half -------------------------------------------------------- */

.oe-pb__preview {
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 60px;
	background-color: var(--oe-color-ink-strong);
}

.oe-pb__preview-inner {
	display: flex;
	flex-direction: column;
	gap: 16px;
	width: 100%;
	max-width: 640px;
}

.oe-pb__preview-label { margin: 0; color: rgba(255, 255, 255, 0.5); }

/* -- Responsive ---------------------------------------------------------- */

@media (max-width: 1200px) {
	.oe-pb { grid-template-columns: 1fr; min-height: 0; }

	/* The side padding and the heading size are fluid now, so the steps they
	   used to take at this width are already covered by their clamps. */

	.oe-pb__preview { order: -1; padding: 32px 24px; }
}

@media (max-width: 767px) {
	/* Figma 472:9751 / 9834 / 9988. The white card loses its chrome — on a
	   phone the page itself is the card — and the stepper keeps its labels at
	   10px rather than collapsing to bare numbers, which is what tells someone
	   how far through the wizard they are. */
	/* The clamp covers three sides; only the deeper bottom is specific to the
	   phone, where the actions row needs clearance above the fold. */
	.oe-pb__side { padding-block-end: 48px; }

	.oe-pb__card {
		max-width: none;
		padding: 0;
		border-radius: 0;
		background-color: transparent;
		gap: 16px;
	}

	/* Size and leading come from the clamp; only the heavier weight is a phone
	   decision (Figma 472:11227). */
	.oe-pb__heading { font-weight: 700; }

	.oe-pb__stepper { border-radius: 16px; }

	/* 9px block padding keeps the pill at the 44 the design draws, once the
	   1px border on each edge is counted. */
	.oe-pb__stepper-item {
		gap: 4px;
		padding: 9px 6px;
		justify-content: center;
	}

	.oe-pb__stepper-n {
		width: 24px;
		height: 24px;
		border-width: 2px;
		font-size: 11px;
	}

	.oe-pb__stepper-label {
		display: inline;
		font-size: 10px;
		line-height: 12.1px;
		white-space: normal;
	}

	/* The preview moves above the fields and lines up with the 24px gutter.
	   It is a grid sibling of the form, not a child, so the rounded dark
	   panel is the inner element rather than this wrapper. */
	.oe-pb__preview {
		order: -1;
		padding: 0 24px;
		background-color: transparent;
	}

	.oe-pb__preview-inner {
		display: flex;
		align-items: center;
		justify-content: center;
		width: 100%;
		min-height: 220px;
		padding: 16px;
		border-radius: 16px;
		background-color: var(--oe-color-ink-strong);
	}

	/* Phone: the panel is already inset by the step body, so its own 16px
	   padding only narrowed every control by another 32px. */
	.oe-pb__fields { padding: 0; gap: 16px; background-color: transparent; }

	.oe-pb__totals { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 20px; }

	.oe-pb__qty-control { width: 100%; }

	.oe-pb__actions { flex-wrap: wrap; gap: 16px; }
	.oe-pb__back { flex: 0 0 auto; }
	.oe-pb__next,
	.oe-pb__submit { flex-basis: 100%; }
}

/* -- The plate render ----------------------------------------------------
   Physical UK oblong, 520x111mm. Sized in container query units so the
   characters stay proportional at any rendered width.
   ------------------------------------------------------------------------ */

/* The plate itself is drawn on the canvas now -- body, band, characters and
   all -- so the figure keeps only the shadow that sits it on the photograph
   behind it. */
.oe-plate-render {
	position: relative;
	display: block;
	margin: 0;
	filter: drop-shadow(0 10px 24px rgba(0, 0, 0, 0.45));
}

.oe-pb .oe-plate-render__canvas {
	display: block;
	width: 100%;
	height: auto;
}

.oe-plate-render[hidden] { display: none; }

/* It used to sit inside the plate, which worked while the plate was a box of
   elements. The canvas draws edge to edge now, so the caption belongs under it
   rather than printed across the registration. */
.oe-plate-render__caption {
	margin-top: 8px;
	color: rgba(255, 255, 255, 0.55);
	font-family: var(--oe-font-body);
	font-size: 12px;
	font-weight: 500;
	text-transform: uppercase;
	letter-spacing: 0.08em;
}

/* ==========================================================================
   Image lightbox (Figma 472:11050)
   Option and style thumbnails are small on a phone; tapping one enlarges it.
   Hidden until JavaScript opens it, so nothing is lost without JS.
   ========================================================================== */

.oe-zoom[hidden] { display: none; }

.oe-zoom {
	position: fixed;
	inset: 0;
	z-index: 1000;
	display: flex;
	align-items: center;
	justify-content: center;
	/* The deeper top and sides are the close button's room. It hangs off the
	   panel's top-right corner now, past both edges, and the panel is centred
	   in whatever this padding leaves — so reserving the space here is what
	   stops the button being pushed off a short or narrow window. It costs the
	   panel a little centring, which nobody will see, against a close button
	   that cannot be reached, which everyone would. */
	padding: 70px 56px 24px;
}

/* Too narrow to hang anything beside the panel: it takes the full width here,
   so the button comes back over the top edge where there is still room. */
@media (max-width: 480px) {
	.oe-zoom { padding: 70px 24px 24px; }
}

.oe-zoom__scrim {
	position: absolute;
	inset: 0;
	background-color: rgba(0, 0, 0, 0.4);
	opacity: 0;
	transition: opacity 200ms var(--oe-ease, ease);
}

.oe-zoom.is-open .oe-zoom__scrim { opacity: 1; }

.oe-zoom__panel {
	position: relative;
	display: flex;
	flex-direction: column;
	gap: 10px;
	max-width: min(344px, 100%);
	max-height: 100%;
	padding: 5px;
	border-radius: 10px;
	background-color: var(--oe-color-white);
	opacity: 0;
	transform: scale(0.96);
	transition: opacity 200ms var(--oe-ease, ease), transform 200ms var(--oe-ease, ease);
}

.oe-zoom.is-open .oe-zoom__panel { opacity: 1; transform: none; }

/*
 * The close button hangs off the panel's top-right corner, clear of both edges.
 *
 * It used to sit over the corner of the photograph. That is fine on a drawing
 * and poor on a real one: the picture is the whole reason the panel opened, and
 * a white disc parked on it covers the very corner someone enlarged the image
 * to look at. Out on the scrim it covers nothing, and a white disc on a dark
 * ground is easier to find than a white disc on a photograph.
 *
 * Past both edges rather than square above the top one, so it reads as the
 * panel's corner rather than as a thing balanced on its lid. `.oe-zoom` keeps
 * padding on all sides for it; the phone step there is where the panel grows
 * too wide to have anything beside it.
 */
.oe-zoom__close {
	position: absolute;
	/* A 36px button at -30px overlaps the panel by 6px on each axis, so it sits
	   on the corner rather than floating away from it. The panel's own padding
	   is 5px, so those 6px land on the frame and not on the photograph.
	   Anything that merely touches the corner still reads as a gap: the button
	   is round, and a circle's edge pulls back about 7px from the square corner
	   it is boxed in. */
	inset-block-start: -30px;
	inset-inline-end: -30px;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	/* The box stays 36px so there is still something to hit; only the disc is
	   gone. The cross itself is 20px, so the target is comfortably larger than
	   the mark, which is what it should be. */
	width: 36px;
	min-width: 0;
	height: 36px;
	padding: 0;
	border-radius: 0;
	background-color: transparent;
	--oe-btn-surface: transparent;
	color: var(--oe-color-white);
	/* Declared twice, which is the bargain base.css sets out: its hover/focus
	   reset is two classes deep and outranks this rule, so it repaints the
	   cross from `--oe-btn-fg`. Left undeclared that falls back to `inherit`,
	   and what this button inherits is the panel's dark body text — so the
	   white cross turned black under the pointer, and stayed black after a
	   click, because a clicked button keeps `:focus`. */
	--oe-btn-fg: var(--oe-color-white);
	/* The disc used to guarantee the cross was legible. Without it the mark
	   lies on the scrim, and the scrim is only 40% black over whatever the page
	   was showing — a pale photograph underneath would swallow a white cross.
	   The shadow does the disc's old job without drawing a shape of its own. */
	box-shadow: none;
	filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.6));
	/* The hover moves opacity now that there is no fill to move, and the
	   inherited button transition does not cover it. */
	transition: opacity 160ms var(--oe-ease, ease);
}

/* Nothing to hang beside the panel at this width — see `.oe-zoom` above. The
   button keeps its distance from the top edge and tucks back to the panel's
   right edge, which is the furthest right it can go and stay on screen. */
@media (max-width: 480px) {
	.oe-zoom__close { inset-inline-end: 0; }
}

/* With no fill to darken, the hover is the mark itself easing back. */
.oe-zoom__close:hover { background-color: transparent; opacity: 0.7; }

.oe-zoom__close svg { width: 20px; height: 20px; }

.oe-zoom .oe-zoom__img {
	display: block;
	width: 100%;
	max-height: 70vh;
	object-fit: contain;
	border-radius: 6px;
	background-color: #f3f3f3;
}

/* The trigger is a real button so it can be reached and fired from the
   keyboard; it carries no chrome of its own. */
.oe-pb__zoom {
	display: block;
	width: 100%;
	padding: 0;
	border: 0;
	background: none;
	cursor: zoom-in;
}

/*
 * On a chooser the photograph has two jobs, and which one a click gets depends
 * on whether the choice is already taken.
 *
 * Not taken: the button stands aside. A click passes through it to the label
 * and selects, which is the whole point of a tile and what the enlarge button
 * used to swallow. The cursor says `pointer` with the rest of the tile, because
 * that is what the click will do.
 *
 * Taken: selecting it again would do nothing, so the click is free. The button
 * takes it and opens the photograph full size — which is exactly when a
 * customer wants a proper look, having just picked the thing.
 *
 * `:has()` is doing real work here: the checked state lives on the input, and
 * this is the only way the wrapper around the image can be told about it
 * without a line of script.
 */
.oe-pb__choices--cards .oe-pb__zoom,
.oe-pb__choices--tiles-h .oe-pb__zoom {
	pointer-events: none;
	cursor: pointer;
}

.oe-pb__choices--cards .oe-pb__choice:has(input:checked) .oe-pb__zoom,
.oe-pb__choices--tiles-h .oe-pb__choice:has(input:checked) .oe-pb__zoom {
	pointer-events: auto;
	cursor: zoom-in;
}

.oe-pb__zoom:focus-visible {
	outline: 2px solid var(--oe-step-accent, #495aff);
	outline-offset: 2px;
	border-radius: 8px;
}

.oe-pb__choice-img {
	display: block;
	width: 100%;
	aspect-ratio: 16 / 10;
	object-fit: cover;
	border-radius: 8px;
	background-color: #f3f3f3;
}

@media (prefers-reduced-motion: reduce) {
	.oe-zoom__scrim,
	.oe-zoom__panel { transition: none; }
	.oe-zoom__panel { transform: none; }
}

/* -- Size dropdown (Figma 472:8357 / 472:8382) ---------------------------- *
 * Both nodes carry the same eight rows and differ only in width, so one
 * component serves front and rear. Rows are 44px with 12px inline padding;
 * the selected row is filled rather than outlined, which is why this cannot
 * reuse the bordered card treatment the other groups use.
 */

.oe-pb__select {
	position: relative;
	width: 100%;
}

.oe-pb__select-trigger {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 8px;
	width: 100%;
	min-height: var(--oe-pb-control-h);
	padding: 0 12px;
	border: 1px solid var(--oe-color-neutral-300);
	border-radius: 8px;
	background-color: var(--oe-color-white);
	font-family: var(--oe-font-body);
	font-size: 16px;
	line-height: 1.5;
	text-align: left;
	color: var(--oe-color-neutral-700);
	cursor: pointer;
}

.oe-pb__select-trigger:hover { border-color: var(--oe-color-ink-strong); }

.oe-pb__select-trigger:focus-visible {
	outline: 2px solid var(--oe-step-accent);
	outline-offset: 2px;
}

/*
 * The text and multi-line inputs are the same control as a select trigger with
 * a different job, so they take its height, border and radius instead of the
 * generic `.oe-input` defaults, which are sized from the global spacing scale
 * and landed several pixels short. A textarea keeps its `rows`; the minimum
 * only stops it falling under a single-line control.
 */
.oe-pb .oe-input {
	min-height: var(--oe-pb-control-h);
	padding: 12px;
	border: 1px solid var(--oe-color-neutral-300);
	border-radius: 8px;
	font-size: 16px;
	line-height: 1.5;
}

.oe-pb .oe-pb__textarea {
	resize: vertical;
}

.oe-pb__select-value {
	overflow: hidden;
	white-space: nowrap;
	text-overflow: ellipsis;
	/* Takes the slack so the trigger reads [glyph] [label] … [caret]. Without
	   it the trigger's own `justify-content: space-between` spreads three
	   children evenly and strands the marque away from the name it belongs to. */
	flex: 1 1 auto;
	min-width: 0;
}

/* The glyph cloned onto the trigger from the chosen option — see syncSelect().
   Sizing comes from `.oe-pb__choice-icon img`, which the clone keeps. */
.oe-pb__select-icon {
	display: inline-flex;
	flex: 0 0 auto;
}

.oe-pb__select-caret {
	flex: 0 0 auto;
	width: 10px;
	height: 10px;
	border-right: 2px solid currentColor;
	border-bottom: 2px solid currentColor;
	/* Nudged up by a quarter of its own height so the rotated glyph reads as
	   centred: rotating a square about its centre leaves the visual mass low. */
	transform: translateY(-25%) rotate(45deg);
	transition: transform 160ms var(--oe-ease, ease);
}

.oe-pb__select-trigger[aria-expanded="true"] .oe-pb__select-caret {
	transform: translateY(25%) rotate(-135deg);
}

/* The menu is the same .oe-pb__choices element, re-laid-out. */
.oe-pb__choices--menu {
	position: absolute;
	z-index: 20;
	top: calc(100% + 4px);
	left: 0;
	/* Kept in the layout and hidden with `visibility`, so opening and closing
	   can be animated. It is absolutely positioned, so it costs the page
	   nothing to leave it there, and `visibility: hidden` keeps it out of the
	   accessibility tree and off the tab order exactly as `display: none` did. */
	display: flex;
	visibility: hidden;
	opacity: 0;
	transform: translateY(-6px);
	transition:
		opacity 160ms var(--oe-ease, ease),
		transform 160ms var(--oe-ease, ease),
		visibility 160ms;
	flex-direction: column;
	flex-wrap: nowrap;
	gap: 0;
	width: 100%;
	/*
	 * The character colours run to seventeen entries -- at the 44px a menu row
	 * stands, 750px of list. The menu is absolutely positioned, so nothing below
	 * it moves out of the way: it simply covered the final total and the submit
	 * button, and its own last rows fell off the card. Seven rows, then it
	 * scrolls. The `vh` term is for a short window, where a flat 320 would still
	 * reach past the fold.
	 */
	max-height: min(320px, 45vh);
	/* Vertical scroll, but the radius still has to clip the rows at the corners
	   -- any non-visible overflow establishes that, on both axes. */
	overflow-x: hidden;
	overflow-y: auto;
	/* The page behind should not start scrolling once the list hits its end. */
	overscroll-behavior: contain;
	border-radius: 8px;
	background-color: var(--oe-color-white);
	box-shadow: 0 0 24px rgba(0, 0, 0, 0.2);
}

.oe-pb__select.is-open .oe-pb__choices--menu {
	visibility: visible;
	opacity: 1;
	transform: none;
}

.oe-pb__choices--menu .oe-pb__choice { flex: 0 0 auto; width: 100%; }

.oe-pb__choices--menu .oe-pb__choice-label {
	justify-content: flex-start;
	gap: 8px;
	min-height: 44px;
	padding: 0 12px;
	border: 0;
	border-radius: 0;
	font-size: 16px;
	color: var(--oe-color-neutral-700);
}

.oe-pb__choices--menu .oe-pb__choice-label:hover {
	background-color: var(--oe-color-neutral-100);
}

.oe-pb__choices--menu .oe-pb__choice input:checked + .oe-pb__choice-label {
	border: 0;
	box-shadow: none;
	background-color: var(--oe-color-neutral-100);
	color: var(--oe-color-black);
}

.oe-pb__choices--menu .oe-pb__choice input:focus-visible + .oe-pb__choice-label {
	outline: 2px solid var(--oe-step-accent);
	outline-offset: -2px;
}

.oe-pb__choices--menu .oe-pb__choice-name { font-size: 16px; }
.oe-pb__choices--menu .oe-pb__choice-price { margin-left: auto; font-weight: 400; }

@media (prefers-reduced-motion: reduce) {
	.oe-pb__select-caret { transition: none; }
}

/* ---------------------------------------------------------------------------
   Plate-type cards — Figma 472:7945.

   Three photo tiles between the registration and the style list. Same
   .oe-pb__choices element as everywhere else, re-laid-out, exactly as the
   dropdown variant above does.
   --------------------------------------------------------------------------- */

/*
 * Three across, wrapping to as many rows as the group needs. Figma 472:7945
 * drew three cards on one row and flex was enough; the client's revision put
 * six there, and `flex: 1 1 0` answered by dividing the same row six ways —
 * 95px cards with the label broken over two lines. A fixed column count keeps
 * each card the width its photograph was cropped for, however many there are.
 */
.oe-pb__choices--cards {
	display: grid;
	grid-template-columns: repeat(3, minmax(0, 1fr));
	gap: 8px;
}

.oe-pb__choices--cards .oe-pb__choice-label {
	position: relative;
	flex-direction: column;
	gap: 6px;
	padding: 0 0 6px;
	overflow: hidden;
	border: 1.5px solid transparent;
	border-radius: 8px;
	background: #fff;
	text-align: center;
}

/* A ratio rather than a fixed height, so the photo keeps its shape as the card
   narrows instead of cropping harder at every width. */
.oe-pb__choices--cards .oe-pb__choice-img,
.oe-pb__choices--cards .oe-pb__zoom {
	display: block;
	width: 100%;
	aspect-ratio: 3 / 2;
	padding: 0;
	border: 0;
	border-radius: 0;
	background: var(--oe-color-neutral-100, #f5f5f5);
	object-fit: cover;
}

.oe-pb__choices--cards .oe-pb__zoom .oe-pb__choice-img { aspect-ratio: 3 / 2; }

.oe-pb__choices--cards .oe-pb__choice-name {
	font-size: 16px;
	font-weight: 700;
	line-height: 24px;
	letter-spacing: -0.16px;
}

.oe-pb__choices--cards .oe-pb__choice input:checked + .oe-pb__choice-label {
	border-color: var(--oe-color-ink-strong);
	box-shadow: 0 4px 24px rgba(0, 0, 0, 0.08);
}

/* Figma 472:7958: a 34px brand disc with a white rim, tucked into the top-left
   of the photo. The disc and rim are here; the tick inside it is the exported
   icon every variant shares. */
.oe-pb__choices--cards .oe-pb__choice-check {
	position: absolute;
	top: 8px;
	left: 9px;
	width: 34px;
	height: 34px;
	border: 1.5px solid #fff;
}

/* ---------------------------------------------------------------------------
   "Most popular" flag — Figma 472:7964.

   Straddles the top edge of the tile it belongs to, so the tile has to
   establish the containing block.
   --------------------------------------------------------------------------- */

.oe-pb__style { position: relative; }

.oe-pb__popular {
	position: absolute;
	top: -12px;
	left: 50%;
	z-index: 2;
	transform: translateX(-50%);
	padding: 6px 12px;
	border-radius: 503px;
	background: var(--oe-color-accent-green, #009444);
	color: #fff;
	font-size: 12px;
	line-height: 1;
	text-transform: uppercase;
	white-space: nowrap;
}

@media (max-width: 767px) {
	/* Figma 472:9566 fits three tiles across at 393px. The photo used to be
	   pinned to 66px here; the 3/2 ratio it now carries lands within a pixel of
	   that at this width, so the override is gone rather than fighting it. */

	.oe-pb__choices--cards .oe-pb__choice-name { font-size: 14px; }

	.oe-pb__choices--cards .oe-pb__choice-check {
		top: 6px;
		left: 6px;
		width: 24px;
		height: 24px;
	}

	.oe-pb__choices--cards .oe-pb__check-icon { width: 16px; height: 16px; }
}

/* ---------------------------------------------------------------------------
   Size preview — Figma 472:8163.

   A 160px photo of the chosen size, with the bare measurement on a brand badge
   over its top-left corner. Stays hidden until a size that has artwork is
   picked, so it costs nothing while the photographs are outstanding.
   --------------------------------------------------------------------------- */

.oe-pb__size-preview { position: relative; margin-top: 8px; }

.oe-pb__size-preview[hidden] { display: none; }

/* Figma 472:8163 draws this under the plate sizes in step 2 and nowhere else.
   The reflective picker carries sample photographs too, and once it opened on
   a chosen finish they turned into a panel the design has no room for. */
[data-oe-stage="3"] .oe-pb__size-preview { display: none; }

.oe-pb .oe-pb__size-img {
	display: block;
	width: 100%;
	height: 160px;
	border-radius: 8px;
	object-fit: cover;
}

.oe-pb__size-badge {
	position: absolute;
	top: 8px;
	left: 10px;
	padding: 6px 12px;
	border-radius: 503px;
	background: var(--oe-color-brand);
	color: var(--oe-color-ink-strong);
	font-size: 12px;
	font-weight: 500;
	line-height: 1;
	text-transform: uppercase;
}

/* ---------------------------------------------------------------------------
   The tick — Figma 472:8990 / 472:9018 / 472:7958.

   One 24px brand disc holding the exported check icon, shared by the card,
   tile and swatch variants. Hidden until its radio is chosen, so no variant
   has to draw its own.
   --------------------------------------------------------------------------- */

.oe-pb__choice-check { display: none; }

.oe-pb__choice input:checked + .oe-pb__choice-label .oe-pb__choice-check {
	display: flex;
	flex: 0 0 auto;
	align-items: center;
	justify-content: center;
	width: 24px;
	height: 24px;
	border-radius: 50%;
	background: var(--oe-color-brand);
}

.oe-pb .oe-pb__check-icon { display: block; width: 24px; height: 24px; }

/* ---------------------------------------------------------------------------
   Legal / show tiles — Figma 472:8978.

   Two tiles side by side: an icon in a round well, a title with a caption
   under it, and the tick on the right.
   --------------------------------------------------------------------------- */

.oe-pb__choices--tiles { gap: 8px; }

.oe-pb__choices--tiles .oe-pb__choice { flex: 1 1 0; }

.oe-pb__choices--tiles .oe-pb__choice-label {
	gap: 16px;
	align-items: center;
	height: 100%;
	padding: 12px;
	border: 1.5px solid transparent;
	border-radius: 8px;
	background: #fff;
	text-align: left;
}

.oe-pb__choices--tiles .oe-pb__choice input:checked + .oe-pb__choice-label {
	border-color: var(--oe-color-ink-strong);
	/* Figma's "Drop Shadow" style, 472:8464: 24px of blur, not 12. */
	box-shadow: 0 4px 24px rgba(0, 0, 0, 0.08);
}

.oe-pb__choices--tiles .oe-pb__choice-icon {
	display: flex;
	flex: 0 0 auto;
	padding: 12px;
	border-radius: 50px;
	background: #f8f8f8;
	transition: background-color 200ms var(--oe-ease, ease);
}

.oe-pb__choices--tiles .oe-pb__choice input:checked + .oe-pb__choice-label .oe-pb__choice-icon {
	background: var(--oe-color-brand);
}

.oe-pb .oe-pb__choice-icon img { display: block; width: 24px; height: 24px; }

.oe-pb__choice-text {
	display: flex;
	flex-direction: column;
	gap: 4px;
	min-width: 0;
}

.oe-pb__choices--tiles .oe-pb__choice-text { flex: 1 1 auto; }

.oe-pb__choices--tiles .oe-pb__choice-name {
	font-size: 18px;
	font-weight: 500;
	line-height: 24px;
	letter-spacing: -0.18px;
}

.oe-pb__choice-sub {
	color: rgba(26, 26, 26, 0.6);
	font-size: 14px;
	font-weight: 400;
	line-height: 1.6;
}

/* ---------------------------------------------------------------------------
   Fitting-kit swatches — Figma 472:9008.

   Only the chosen swatch carries its label and price; the rest collapse to the
   44px thumbnail. A swatch with no artwork keeps its label either way, so the
   row stays usable while the client's photographs are outstanding.
   --------------------------------------------------------------------------- */

.oe-pb__choices--swatches { gap: 8px; flex-wrap: wrap; }

.oe-pb__choices--swatches .oe-pb__choice { flex: 0 0 auto; }

/* `1 1 0`, not `1 1 auto`: a wrapping flex line breaks on the item's *base*
   size, and `auto` makes that the label plus its price. The open swatch was
   therefore claiming a full row of its own and pushing the remaining
   thumbnails onto a second one. From a zero basis it takes whatever the
   thumbnails leave, so Figma 472:9008's single row holds however long the
   chosen kit's name happens to be. */
.oe-pb__choices--swatches .oe-pb__choice:has(input:checked) {
	flex: 1 1 0;
	min-width: 0;
}

/* A swatch with no artwork has only its label to show, so it is sized by that
   label rather than shrunk to share the row. */
.oe-pb__choices--swatches .oe-pb__choice:not(:has(.oe-pb__choice-img)) {
	flex: 1 1 auto;
	min-width: 0;
}

/* Figma 472:9014 reads left to right: thumbnail, then the name and its price
   together, then the tick pushed to the far edge. Inherited centring had the
   name floating in the middle with the price stranded on the right. */
.oe-pb__choices--swatches .oe-pb__choice-label {
	gap: 12px;
	align-items: center;
	justify-content: flex-start;
	height: 100%;
	padding: 8px;
	border: 1px solid var(--oe-color-neutral-300);
	border-radius: 8px;
	background: #fff;
	text-align: left;
}

.oe-pb__choices--swatches .oe-pb__choice-text {
	flex: 0 1 auto;
	text-align: left;
}

.oe-pb__choices--swatches .oe-pb__choice-check { margin-inline-start: auto; }

.oe-pb__choices--swatches .oe-pb__choice input:checked + .oe-pb__choice-label {
	border: 1.5px solid var(--oe-color-ink-strong);
	padding: 8px 16px 8px 8px;
	/* Figma 472:8494 lifts the chosen kit off the panel the same way the legal
	   tile above it is lifted. */
	box-shadow: 0 4px 24px rgba(0, 0, 0, 0.08);
}

.oe-pb__choices--swatches .oe-pb__zoom,
.oe-pb__choices--swatches .oe-pb__choice-img {
	flex: 0 0 auto;
	width: 44px;
	height: 44px;
	padding: 0;
	border: 0;
	border-radius: 4px;
	object-fit: cover;
}

/* A swatch that has artwork shows its label only once chosen -- but not before
   anything at all is chosen. On arrival the row would otherwise be four
   nameless thumbnails, which is the design's populated state read backwards:
   Figma 472:9008 always has one selected. So the row opens legible and folds
   down to the design as soon as the customer picks. */
.oe-pb__choices--swatches:has(input:checked) .oe-pb__choice:has(.oe-pb__choice-img) .oe-pb__choice-text,
.oe-pb__choices--swatches:has(input:checked) .oe-pb__choice:has(.oe-pb__choice-img) .oe-pb__choice-price { display: none; }

.oe-pb__choices--swatches:not(:has(input:checked)) .oe-pb__choice { flex: 1 1 auto; min-width: 0; }

.oe-pb__choices--swatches .oe-pb__choice input:checked + .oe-pb__choice-label .oe-pb__choice-text { display: flex; flex: 1 1 auto; }
.oe-pb__choices--swatches .oe-pb__choice input:checked + .oe-pb__choice-label .oe-pb__choice-price { display: inline; }

/* ---------------------------------------------------------------------------
   Compliance notice — Figma 472:8975.
   --------------------------------------------------------------------------- */

.oe-pb__notice {
	display: flex;
	align-items: center;
	justify-content: center;
	margin: 0 0 4px;
	padding: 4px 8px;
	border: 1px solid rgba(225, 175, 0, 0.5);
	border-radius: 8px;
	background: rgba(255, 199, 0, 0.1);
	color: #5e4900;
	font-size: 14px;
	font-weight: 500;
	line-height: 1.6;
	text-align: center;
}

/* ---------------------------------------------------------------------------
   Checkout reassurance — Figma 472:9061 / 472:9062 / 472:9067.
   --------------------------------------------------------------------------- */

/* The final action is brand yellow with black text, unlike the black
   "Save & next" that carries the customer through steps 1 and 2. */
/* Add to cart overrides the shared black with the brand yellow, so it has to
   override both reset tokens too — otherwise a hover while it is held shut
   restores the *shared* black surface and loses the dark label with it. */
.oe-pb__submit {
	background-color: var(--oe-color-brand);
	--oe-btn-surface: var(--oe-color-brand);
	--oe-btn-fg: var(--oe-color-ink-strong);
	color: var(--oe-color-ink-strong);
}

/* The hover colours are set once, with the other action button, further up. */

.oe-pb__paypal {
	display: none;
	flex-wrap: wrap;
	align-items: center;
	justify-content: center;
	gap: 8px;
	margin: 24px 0 0;
	color: rgba(26, 26, 26, 0.6);
	font-size: 14px;
	line-height: 1.6;
}

/* Keyed off the form, not the stage: both of these are printed once, after
   the stages, so a `.oe-pb__stage ...` selector could never match them. */
.oe-pb.is-last .oe-pb__paypal {
	display: flex;
}

.oe-pb .oe-pb__paypal-logo {
	display: block;
	width: 60px;
	height: 16px;
	object-fit: contain;
}

.oe-pb__paypal-more { color: inherit; text-decoration: underline; }

.oe-pb__badges {
	display: none;
	flex-wrap: wrap;
	align-items: center;
	justify-content: center;
	gap: 10px;
	margin: 24px 0 0;
	padding: 24px 0 0;
	border-top: 1px solid var(--oe-color-neutral-300);
	list-style: none;
}

.oe-pb.is-last .oe-pb__badges {
	display: flex;
}

.oe-pb .oe-pb__badge img { display: block; width: 54px; height: 54px; object-fit: contain; }

@media (max-width: 767px) {
	/* The single-column stack for this width is set at the end of the file,
	   with the rest of the phone layout. A flex-basis here is a leftover from
	   when this row was a flex wrap and does nothing to a grid item. */

	.oe-pb__choices--tiles .oe-pb__choice { flex: 1 1 100%; }
	.oe-pb__choices--tiles .oe-pb__choice-name { font-size: 16px; }
	.oe-pb__badge img { width: 44px; height: 44px; }
}

/* ---------------------------------------------------------------------------
   Back — Figma 472:8188 (in the actions row) and 472:11227 (above the heading
   on a phone, where the actions row has scrolled away).
   --------------------------------------------------------------------------- */

.oe-pb .oe-pb__back-icon { display: block; width: 24px; height: 24px; }

.oe-pb__back--top { display: none; }

/* ---------------------------------------------------------------------------
   The last step — Figma 472:8435 (desktop) and 472:9855 (phone).

   Figma draws Add to cart across the whole action row with no Back anywhere on
   desktop, and it was built that way. The client has since asked for a Back on
   step 3 as well, so it is here again — please do not "restore the design" and
   remove it a third time.

   It takes an equal half of the row, the same as it does in steps 1 and 2, so
   the actions row does not change shape at the last step. It hugged its label
   here for a while to leave Add to cart the wider button; the client asked for
   the two to match the other steps instead.

   No rule of its own is needed for that — the shared
   `.oe-pb__actions > .oe-pb__back` sizing already says `flex: 1 1 0`, and this
   block only has to stop overriding it. The phone still uses the Back above the
   heading, which Figma 472:9857 does draw.
   --------------------------------------------------------------------------- */

/* Figma 472:8488 draws the reflective picker shorter than the size pickers in
   step 2, and sets its value in the title face. Scoped to this step so the
   step-2 controls, drawn from their own frames, keep their 56. */
[data-oe-stage="3"] .oe-pb__field:not(:has(.oe-pb__row)) .oe-pb__select-trigger {
	padding: 13px 16px;
	font-family: var(--oe-font-title);
	font-size: 18px;
	line-height: 1.1;
}

/* Figma 472:8458 sets the notice 8 under its caption, and 472:8457 leaves 16
   between that pair and the tiles. The caption's own 16 is the gap every other
   group uses, so only the one carrying a notice moves. */
.oe-pb__field:has(.oe-pb__notice) .oe-pb__label { margin-bottom: 8px; }

.oe-pb__notice { margin-bottom: 16px; }

/* Figma 472:8510 rules off the fitting kit before the secondary rows below it.
   Hung on the kit group rather than on the first row beneath it: those rows
   come and go with the answers above them, and a hidden field is still a
   sibling, so an adjacency rule would draw the line on something invisible. */
[data-oe-stage="3"] .oe-pb__field[data-oe-step="fitting_kit"] {
	padding-bottom: 20px;
	border-bottom: 1px solid var(--oe-color-neutral-300);
}

@media (max-width: 767px) {
	.oe-pb__back--top {
		/* `justify-self`, not just `align-self`: the card this used to sit in was
		   a flex column, but it goes `display: contents` on a phone, so this is a
		   grid item of .oe-pb now — and there `align-self` only governs the block
		   axis, leaving the control stretched across all 345px. Figma 472:9774
		   hugs it at 70. */
		align-self: flex-start;
		justify-self: start;
		gap: 6px;
		height: auto;
		padding: 0;
		border: 0;
		background: none;
	}

	.oe-pb__back--top:not([hidden]) { display: inline-flex; }

	/* Only one of the two is ever wanted, and on a phone it is the top one. */
	.oe-pb__actions .oe-pb__back { display: none; }
}

/* Figma 472:12300 sets a 24px mark beside each brand name in the menu. It only
   appears once the client attaches artwork to the option. */
.oe-pb__choices--menu .oe-pb__choice-img,
.oe-pb__choices--menu .oe-pb__zoom {
	flex: 0 0 auto;
	width: 24px;
	height: 24px;
	padding: 0;
	border: 0;
	border-radius: 4px;
	object-fit: contain;
}

/* ---------------------------------------------------------------------------
   Secondary option row — Figma 472:9029 / 472:9036 / 472:9043.

   The pattern the design gives every step-3 chooser that is not one of the
   headline pickers: a 14px label, the control taking the width, and what it
   costs in a fixed 74px column on the right. The 44px the design draws it at
   is gone -- see --oe-pb-control-h; only the label and the price column are
   still particular to this row.
   --------------------------------------------------------------------------- */

.oe-pb__row {
	display: flex;
	align-items: center;
	gap: 8px;
	width: 100%;
}

.oe-pb__row .oe-pb__select { flex: 1 1 0; min-width: 0; }

.oe-pb__row .oe-pb__select-trigger {
	padding: 10px 14px;
}

.oe-pb__row .oe-pb__select-value {
	font-family: var(--oe-font-body);
	font-size: 16px;
	line-height: 24px;
}

/*
 * A group whose every choice is free -- the brand badges and the character
 * colours, both held at zero until the client prices them -- renders no
 * `.oe-pb__choice-price` at all, so the 74px column beside it reserved width
 * for a figure that never arrives and stood those two selects 82px short of
 * the ones above them. Keyed on the group rather than on what is currently
 * chosen: collapsing it per selection would jump the control's width the
 * moment a priced option was picked.
 */
.oe-pb__row:not(:has(.oe-pb__choice-price)) .oe-pb__row-price {
	display: none;
}

.oe-pb__row-price {
	flex: 0 0 74px;
	width: 74px;
	color: var(--oe-color-ink-strong);
	font-size: 16px;
	font-weight: 500;
	line-height: 1.4;
	text-align: right;
	font-variant-numeric: tabular-nums;
}

/* Figma 472:9027 sets a secondary row's label a step smaller and closer to
   its control than the headline groups. */
.oe-pb__field:has(.oe-pb__row) .oe-pb__label {
	margin-bottom: 8px;
	font-size: 14px;
	line-height: 1.6;
}

/* Figma 472:9940 keeps the price beside its control on a phone too: at the
   narrowest width supported the 74px column still leaves the select over
   200px, which is well past a comfortable tap target. */

/* ---------------------------------------------------------------------------
   The preview panel — Figma 472:7918, reconciled with the client's brief.

   The design fills this half with one photograph of two plates. The client
   asked for the live plate back: his old builder had one, he took it off, and
   he calls rebuilding it "ideally what we need to improve on" — and the feature
   he wants most, dropping the plate onto a photo of your car, cannot exist
   without it. So the photograph is not used at all: the whole scene is drawn
   on a canvas, ground and plates together, and the reg on it is the live one.

   Nothing here loads an image. The colour below is only what shows in the
   moment before the canvas has painted.
   --------------------------------------------------------------------------- */

.oe-pb__preview {
	padding: 40px;
	background-color: var(--oe-color-ink-strong);
}

@media (max-width: 1200px) {
	.oe-pb__preview { padding: 32px 24px; }
}

/*
 * Figma 472:8137 replaces a finished step's numeral with a tick.
 *
 * The disc was brand yellow, which made it the only brand-coloured thing in a
 * bar that is otherwise one blue-and-grey system — active ring, active label
 * and done label are all `--oe-step-accent`. The client asked for the disc to
 * join them, so it is the accent filled in, with a white mark on it.
 *
 * The literal fallbacks are deliberate. `--oe-step-accent` is declared at the
 * top of this sheet, so it always resolves here — but the pattern is kept
 * because `--oe-color-*` is not in any stylesheet at all (Tokens.php prints
 * that `:root` block inline on wp_head), and anywhere it does not reach an
 * unguarded `var()` is invalid: a background falls back to transparent and a
 * border to currentColor, so the step would lose its fill rather than just its
 * colour, which is far harder to recognise.
 */
.oe-pb__stepper-item.is-done .oe-pb__stepper-n {
	border-color: var(--oe-step-accent, #495aff);
	background-color: var(--oe-step-accent, #495aff);
	color: transparent;
}

/*
 * The tick is sized from the circle, not in pixels. The circle is 34 on a
 * desktop, 24 under 768 and 16 on the phone frame, and a fixed 24px mask box
 * meant the tick kept its full size while the disc shrank under it: the stroke
 * covers 13 of the artwork's 24 units, so on the 16px step it spanned all 13
 * of the 13.3px inside the border and ran out over the curve.
 *
 * 80% of the padding box reproduces the 24px box the desktop had (34 less two
 * 2px borders, times 0.8) and follows every smaller circle down -- a 7px mark
 * on the phone, the 13px one unchanged on a desktop.
 */
.oe-pb__stepper-item.is-done .oe-pb__stepper-n::after {
	content: "";
	position: absolute;
	inset: 0;
	/* White on the blue disc. It was black, which only read because the disc
	   was brand yellow. */
	background-color: var(--oe-color-white, #ffffff);
	-webkit-mask: url("../../images/icon-check.svg") center / 80% 80% no-repeat;
	mask: url("../../images/icon-check.svg") center / 80% 80% no-repeat;
}

.oe-pb__stepper-n { position: relative; }

/* A last guard for the default pill row. Artwork landing there has no variant
   rule of its own, so WooCommerce's `img { height: auto }` wins and a 300x300
   photograph renders at the pill's full width.

   Deliberately matched on rows with no --modifier: written as a plain
   `.oe-pb .oe-pb__choice-img` it would tie with the card, swatch and menu rules
   on specificity, and being later in the file would quietly override all three. */
.oe-pb .oe-pb__choices:not([class*="oe-pb__choices--"]) .oe-pb__choice-img {
	height: 88px;
	object-fit: cover;
	border-radius: 4px;
}

.oe-pb .oe-pb__qty-input:focus,
.oe-pb .oe-pb__qty-input:focus-visible {
	outline: 0;
	box-shadow: none;
}

/* The chosen plate type's own photograph, filling the half outright.
   `cover`, and no aspect ratio of its own: holding Figma's 960x1008 left the
   picture narrower than the panel on most viewports, so the panel's near-black
   ground showed down both sides as a border. The option pictures are square-ish
   and the plate sits mid-frame in all six, so cropping to fill takes the empty
   margins rather than the plate. */
.oe-pb .oe-pb__preview-img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/* A fixed height, and stuck to the top of the viewport.

   As a plain grid sibling it stretched to whatever the option column happened
   to be, so the scene grew and shrank -- and the canvas redrew -- every time a
   group appeared or a list filtered. Figma 472:7916 gives this half 1008px;
   pinning it there makes the preview stable and keeps it in sight while the
   customer works down the form. */
.oe-pb__preview {
	position: sticky;
	top: 0;
	align-self: start;
	height: 100vh;
	max-height: 1008px;
	padding: 0;
}

.oe-pb__preview-inner {
	position: absolute;
	inset: 0;
	gap: 0;
	max-width: none;
}

/* Between the phone and the two-column layout, the scene is a band as well.
   Same reason as the phone rule below, and it has to sit here for the same
   reason: `height: 100vh` above is one class, so only source order can beat it.

   Sticky and a viewport tall is what makes the preview work as a column — it
   holds still beside the form. Stacked, that same pair turns it into a
   full-screen photograph with the builder underneath: at 1092 x 806 the scene
   measured 806px, exactly one screen, and the first question sat 1150px down
   the page. On a 820 x 1180 tablet it was 1008px and the first question 1341px
   down. Nobody scrolls past a whole screen of decoration to find the tool they
   came for.

   `relative`, not `static`: `.oe-pb__preview-inner` is placed absolutely
   against this box, and releasing it would stretch the photograph across the
   whole builder instead of the band. */
@media (min-width: 768px) and (max-width: 1200px) {
	.oe-pb__preview {
		position: relative;
		top: auto;
		/* Proportionate to the phone's 220px without being tied to it: enough
		   to read as the scene, never enough to own the screen. */
		height: clamp(240px, 30vh, 340px);
		max-height: none;
	}
}

/* Figma 472:9558 gives the scene 220px above the card on a phone, and nothing
   is sticky at that width -- it sits above the form and stays put.

   This has to come after the rule above, not with the other phone rules
   earlier in the file: both are one class, so source order decides, and up
   there the desktop 100vh quietly won and the scene ate the whole screen. */
@media (max-width: 767px) {
	.oe-pb__preview {
		/* Relative, not static: the inner is absolutely positioned, so a static
		   parent lets it escape to the nearest positioned ancestor and the
		   picture grows to the whole card instead of this 220px. */
		position: relative;
		height: 220px;
		max-height: none;
	}
}

/* ==========================================================================
   Phone: one column, in the design's order (Figma 472:9540)
   heading, stepper, preview, the white field card, totals, button.

   The preview is a grid sibling of the form column while the heading and the
   stepper are two levels inside it, so no amount of `order` can slot it
   between them. The two wrappers stand aside instead: on a phone the card
   already has no chrome of its own, so nothing is lost by collapsing it, and
   the gutter and panel colour move up to the grid.

   This block is last in the file on purpose. Two earlier ones also target
   these classes, and a media query carries no extra weight -- source order is
   what decides between them.
   ========================================================================== */
@media (max-width: 767px) {
	.oe-pb {
		row-gap: 16px;
		padding: 30px 24px 48px;
		background-color: var(--oe-panel);
	}

	.oe-pb__side,
	.oe-pb__card {
		display: contents;
	}

	.oe-pb__back--top { order: 0; }
	.oe-pb__heading   { order: 1; }
	.oe-pb__stepper   { order: 2; }
	.oe-pb__preview   { order: 3; }
	.oe-pb__stage     { order: 4; }
	.oe-pb__totals    { order: 5; }
	.oe-pb__actions   { order: 6; }
	.oe-pb__paypal    { order: 8; }
	.oe-pb__badges    { order: 9; }

	/* 345 x 220 -- Figma 472:9558 -- and the canvas fills it, so the scene
	   keeps the design's ratio here just as 960 x 1008 does on desktop. */
	.oe-pb__preview {
		position: relative;
		height: 220px;
		max-height: none;
		padding: 0;
		background-color: transparent;
	}

	.oe-pb__preview-inner {
		position: absolute;
		inset: 0;
		padding: 0;
		border-radius: 16px;
		overflow: hidden;
		background-color: var(--oe-color-ink-strong);
	}

	/* Figma 472:9584 stacks the styles full width; a three-up grid of
	   thumbnails is the desktop arrangement and runs five rows deep here. */
	.oe-pb__styles {
		display: grid;
		grid-template-columns: 1fr;
		gap: 8px;
	}

	/*
	 * 36 tall, from a 16px number with 10px of air either side — Figma
	 * 472:9543/9545 on the frame this step is drawn in. The earlier rule in
	 * this file sizes it at 44 from the step-2 and step-3 mobile frames, which
	 * draw the same component two sizes; this follows the one the step being
	 * matched actually uses.
	 */
	.oe-pb__stepper-item {
		/* No side padding: Figma 472:9544 gives step one 115px and its content
		   fills all 115 (16 number + 4 + 95 label). Six pixels either side left
		   the label 83 to live in, so it wrapped to two lines and pushed the row
		   from 36 to 46. */
		padding: 10px 0;
	}

	.oe-pb__stepper-n {
		width: 16px;
		height: 16px;
		border-width: 1.33px;
		border-radius: 13.33px;
		font-size: 8px;
	}

	/*
	 * No pill on a phone. Figma 472:9543 keeps the 16px radius on the frame but
	 * gives it neither stroke nor fill, so the steps sit straight on the page —
	 * a bordered bar squeezed into 345px is what made the labels wrap and the
	 * row grow from 36 to 46.
	 */
	.oe-pb__stepper {
		display: flex;
		align-items: center;
		gap: 29px;
		border: 0;
		background-color: transparent;
		/*
		 * The three steps come to about 333px, so they do not fit a 350px
		 * phone once the card's gutters are taken off — and Figma's own frame
		 * is 8px over its 375 too, because the row is set to hug. As a grid
		 * item that refuses to shrink it dragged the whole card past the
		 * viewport and put a horizontal scrollbar on the page. It scrolls on
		 * its own axis instead, which costs nothing at 375 and up, where the
		 * row fits and there is nothing to scroll.
		 */
		min-width: 0;
		overflow-x: auto;
		overflow-y: hidden;
		scrollbar-width: none;
	}

	.oe-pb__stepper::-webkit-scrollbar { display: none; }

	.oe-pb__stepper-item {
		justify-content: flex-start;
		gap: 4px;
	}

	.oe-pb__stepper-label {
		font-size: 10px;
		line-height: 1.2;
		white-space: nowrap;
	}

	/*
	 * 472:9548 — a 5x9 chevron centred in the 29px gap, not the 16x72 bar
	 * divider. Offset in pixels, not a percentage: a percentage translate is
	 * read against the mark's own 5px width, so the -1200% that centred nothing
	 * put it 60px away, behind the previous step's label.
	 */
	.oe-pb__stepper-item + .oe-pb__stepper-item::before {
		width: 5px;
		height: 9px;
		inset-inline-start: -17px;
		transform: translateY(-50%);
		background-image: url("../../images/stepper-arrow-sm.svg");
		background-size: 5px 9px;
	}
}

/*
 * Desktop pills read left, not centred.
 *
 * Figma 472:7962 centres its row, but it also carries the surcharge inside the
 * one label string ("Double 4D (6mm) +£25") so there is nothing to align. Ours
 * keeps the name and the price as separate spans — the price has to stay
 * legible when a name wraps — and centring two ragged blocks reads as a
 * mistake. The phone frame the client signed off is still centred.
 */
@media (min-width: 768px) {
	.oe-pb__style-label {
		justify-content: flex-start;
		text-align: start;
	}

	.oe-pb__style-name {
		min-width: 0;
	}
}

@media (max-width: 767px) {
	.oe-pb__fields {
		gap: 10px;
	}

	.oe-plate-builder {
		--oe-pb-control-h: 48px;
	}

	/* The bar scrolls on a phone, so a label has room to say its whole name.
	   The ellipsis is for the widths in between, where it cannot. */
	.oe-pb__stepper-label {
		overflow: visible;
		text-overflow: clip;
	}

	/*
	 * Figma 472:9916 stands the fitting kits up as a list, one per line, each
	 * one naming itself — the row of thumbnails is the desktop arrangement and
	 * would wrap to two ragged lines here.
	 */
	.oe-pb__choices--swatches {
		flex-direction: column;
		flex-wrap: nowrap;
	}

	.oe-pb__choices--swatches .oe-pb__choice { flex: 0 0 auto; width: 100%; }

	/* Every kit names itself here, chosen or not. Written to match the desktop
	   rule that folds the unchosen ones down to a thumbnail, because a shorter
	   selector would lose to it however far down the file it sat. */
	.oe-pb__choices--swatches:has(input:checked) .oe-pb__choice:has(.oe-pb__choice-img) .oe-pb__choice-text,
	.oe-pb__choices--swatches:has(input:checked) .oe-pb__choice:has(.oe-pb__choice-img) .oe-pb__choice-price {
		display: block;
	}

	/* Figma 472:9979 fits all eight across the card, so they take a share of it
	   rather than a fixed size. */
	.oe-pb__badges { flex-wrap: nowrap; }

	.oe-pb__badge { flex: 1 1 0; min-width: 0; }

	.oe-pb .oe-pb__badge img { width: 100%; height: auto; }
}

/* ===========================================================================
   Motion

   Answering a question here changes what the next question is, so parts of the
   form appear and disappear constantly. Toggling `hidden` does that in a single
   frame, which reads as the card snapping rather than responding. These fade
   the change in over about a fifth of a second.

   `transition-behavior: allow-discrete` plus `@starting-style` is what lets an
   element that is `display: none` animate at all: the browser holds the
   display change until the rest of the transition has run on the way out, and
   `@starting-style` gives it a value to start from on the way in.
   =========================================================================== */

@media (prefers-reduced-motion: no-preference) {
	.oe-pb__field,
	.oe-pb__size-preview {
		transition:
			opacity 220ms var(--oe-ease, ease),
			transform 220ms var(--oe-ease, ease),
			display 220ms allow-discrete;
	}

	.oe-pb__field[hidden],
	.oe-pb__size-preview[hidden] {
		display: none;
		opacity: 0;
		transform: translateY(-6px);
	}

	@starting-style {
		.oe-pb__field:not([hidden]),
		.oe-pb__size-preview:not([hidden]) {
			opacity: 0;
			transform: translateY(-6px);
		}
	}

	/* The running total is refreshed from the server, so the figure arrives a
	   moment after the choice. Fading between the two stops it flickering. */
	.oe-pb__total-value {
		transition: opacity 200ms var(--oe-ease, ease);
	}
}

/* The basket is filled in the background, so this is where a refusal lands. */
.oe-pb__submit-error {
	margin: 12px 0 0;
	color: var(--oe-color-danger, #c62828);
	font-size: 14px;
	line-height: 1.6;
	text-align: center;
}

.oe-pb__submit-error[hidden] { display: none; }

/* While the request is out. The button keeps its size so nothing reflows. */
.oe-pb.is-adding .oe-pb__submit {
	opacity: 0.65;
	cursor: progress;
}

/* ---------------------------------------------------------------------------
   Horizontal photo tiles — the step 2 size pickers and the step 3 fitting kit.

   The client's revision replaced the size dropdowns with a row of photographs
   that scrolls sideways under a visible scrollbar. Native overflow rather than
   the .oe-slider used elsewhere: the design draws a real scrollbar track, not
   a dot pager, and a radiogroup that scrolls itself keeps arrow-key navigation
   without a script.
   --------------------------------------------------------------------------- */

.oe-pb__choices--tiles-h {
	display: flex;
	flex-wrap: nowrap;
	gap: 12px;
	/* The base rule centres its choices, which is right for a row of pills and
	   wrong for a row of tiles: the fitting-kit names run to two lines where the
	   size names run to one, and centred tiles of two heights would hang their
	   photographs at two heights too. Stretching makes every tile as tall as the
	   tallest, and the `flex-grow` on the text below then holds the prices on one
	   baseline. The sizes look no different -- their labels are all one line --
	   so the row that was already signed off pays nothing for this. */
	align-items: stretch;
	overflow-x: auto;
	/* Room for the scrollbar the design shows, so it sits under the tiles
	   rather than over their lower edge. */
	padding-block-end: 10px;
	scroll-snap-type: x proximity;
}

.oe-pb__choices--tiles-h .oe-pb__choice {
	flex: 0 0 auto;
	/* The design's tile. Fixed rather than fractional so the next one is cut by
	   the container and the row reads as scrollable at any width. */
	width: 148px;
	scroll-snap-align: start;
}

.oe-pb__choices--tiles-h .oe-pb__choice-label {
	position: relative;
	flex-direction: column;
	align-items: stretch;
	gap: 0;
	height: 100%;
	padding: 0 0 8px;
	overflow: hidden;
	border: 1.5px solid var(--oe-color-tile-border, #e0e0e0);
	border-radius: 8px;
	background: #fff;
	text-align: center;
}

.oe-pb__choices--tiles-h .oe-pb__choice-img {
	display: block;
	width: 100%;
	height: 84px;
	border-radius: 0;
	background: var(--oe-color-neutral-100, #f5f5f5);
	object-fit: cover;
}

/* Until the client supplies the size photographs every tile is label-only, and
   a tile with no image should not collapse to a bare word — it keeps the frame
   so the row still reads as a set of pictures waiting to be filled. */
.oe-pb__choices--tiles-h .oe-pb__choice-label:not(:has(.oe-pb__choice-img))::before {
	content: "";
	display: block;
	height: 84px;
	background: var(--oe-color-neutral-100, #f5f5f5);
}

.oe-pb__choices--tiles-h .oe-pb__choice-text {
	padding: 8px 8px 0;
	flex-grow: 1;
}

.oe-pb__choices--tiles-h .oe-pb__choice-name {
	font-size: 12px;
	font-weight: 600;
	line-height: 16px;
}

/*
 * The tile is a fixed 148/132px, so the price is the one line that can outgrow
 * it: a sale figure carries its struck original beside it ("29.99 35.00") and
 * the base rule keeps the pair on one line with `white-space: nowrap`. Rather
 * than let it run under the tile edge, the size tracks the viewport and only
 * reaches the design's 12px once there is width for it -- 9px is the floor,
 * still legible against the 12px name above.
 */
.oe-pb__choices--tiles-h .oe-pb__choice-price {
	display: block;
	padding-inline: 8px;
	font-size: clamp(9px, 1.4vw, 12px);
	line-height: 16px;
}

.oe-pb__choices--tiles-h .oe-pb__choice input:checked + .oe-pb__choice-label {
	border-color: var(--oe-color-ink-strong);
	box-shadow: 0 4px 24px rgba(0, 0, 0, 0.08);
}

.oe-pb__choices--tiles-h .oe-pb__choice-check {
	position: absolute;
	top: 6px;
	left: 6px;
	width: 24px;
	height: 24px;
	border: 1.5px solid #fff;
}

.oe-pb__choices--tiles-h .oe-pb__check-icon {
	width: 16px;
	height: 16px;
}

/* ---------------------------------------------------------------------------
   Step 1 plate types — capped to two rows.

   Six cards is two rows of three; a seventh would silently make the step taller
   than the panel. Capping the height and letting it scroll keeps the step a
   fixed size however many types the client adds.
   --------------------------------------------------------------------------- */

.oe-pb__choices--cards {
	max-height: 420px;
	overflow-y: auto;
	padding-inline-end: 4px;
}

@media (max-width: 767px) {
	.oe-pb__choices--cards {
		max-height: 300px;
	}

	.oe-pb__choices--tiles-h .oe-pb__choice {
		width: 132px;
	}

	.oe-pb__choices--tiles-h .oe-pb__choice-img,
	.oe-pb__choices--tiles-h .oe-pb__choice-label:not(:has(.oe-pb__choice-img))::before {
		height: 72px;
	}
}

/*
 * A scrolling row only scrolls if something stops it growing. A fieldset
 * defaults to `min-width: min-content`, and a flex item to `min-width: auto`,
 * so without both of these the tile row simply widened its parent to fit all
 * nine tiles and reported scrollWidth === clientWidth — the row was never
 * overflowing, the panel was.
 */
.oe-pb__field {
	min-width: 0;
}

.oe-pb__choices--tiles-h {
	max-width: 100%;
	min-width: 0;
}

/*
 * Final total, with the undiscounted price struck through beside it. Baseline
 * alignment rather than centre: the two figures are different sizes, and a
 * struck price reads as an annotation on the real one, not as a second number.
 */
.oe-pb__total-row {
	display: flex;
	align-items: baseline;
	flex-wrap: wrap;
	gap: 10px;
}

/* Half the total's ramp, so the struck-through was-price keeps its relation to
   the live one as both come down. */
.oe-pb__total-was {
	font-family: var(--oe-font-title);
	font-size: clamp(16px, 0.35vw + 13.3px, 20px);
	font-weight: 500;
	line-height: 1;
	color: rgba(26, 26, 26, 0.45);
}

/* `hidden` is a UA default that any display rule would beat; restated so the
   empty was-price cannot flash in before the first price comes back. */
.oe-pb__total-was[hidden] {
	display: none;
}

/* Save & Next is held shut until the registration is one the DVLA would
   accept, so it has to look held shut rather than merely ignore the click.

   Add to cart is held the same way twice — while the configuration is still
   incomplete, and again for the moment the basket request is in flight — and
   was carrying no disabled treatment at all, so it stayed fully lit while
   refusing to work. */
.oe-pb__next[disabled],
.oe-pb__submit[disabled] {
	opacity: 0.45;
	cursor: not-allowed;
}
