/*
 * Canvas designer styles.
 *
 * Written to DESIGN.md rather than to the 0.4.x homepage: 16px minimum body
 * text, colour from theme.json tokens only, a defined focus-visible ring on
 * every control, 44px minimum touch targets, and no gradients, decorative
 * shadows or glassmorphism. The primary CTA is 18px bold uppercase, which is
 * the only size at which white on orange is permitted.
 */

.ocp-designer {
	--d-gap: 28px;
	color: var(--wp--preset--color--foreground);
	font-size: 1rem;
}

/*
 * The [hidden] attribute is how every optional part of this UI is toggled, but
 * a component rule that sets `display` beats the browser's default [hidden]
 * rule, so those elements would stay on screen. Settle it once, here.
 */
.ocp-designer [hidden] {
	display: none !important;
}

/* ---- heading ------------------------------------------------------------- */

/*
 * Carried for screen readers and search, taking no space. Clipped rather than
 * display:none, which would remove it from the accessibility tree entirely and
 * defeat the point of keeping it.
 */
.ocp-d-sronly {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	overflow: hidden;
	clip: rect(0 0 0 0);
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

/* ---- progress ------------------------------------------------------------ */

.ocp-d-progress {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
	list-style: none;
	margin: 0 0 24px;
	padding: 0;
}

.ocp-d-progress li {
	display: flex;
}

/* The pill itself is a button, so it is reachable by keyboard and announces
   itself as an action rather than as a list item. */
.ocp-d-progress li button {
	display: flex;
	align-items: center;
	gap: 9px;
	min-height: 44px;
	padding: 8px 16px 8px 9px;
	border: 1px solid var(--wp--preset--color--border);
	border-radius: 999px;
	font: inherit;
	font-size: 1rem;
	color: var(--wp--preset--color--muted);
	cursor: pointer;
	background: var(--wp--preset--color--background);
	white-space: nowrap;
}

.ocp-d-progress li button:disabled {
	cursor: not-allowed;
	opacity: 0.5;
}

.ocp-d-progress li .n {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 26px;
	height: 26px;
	border-radius: 50%;
	background: var(--wp--preset--color--surface);
	color: var(--wp--preset--color--foreground);
	font-weight: 600;
	font-size: 1rem;
}

.ocp-d-progress li.is-current button {
	border-color: var(--wp--preset--color--primary);
	color: var(--wp--preset--color--foreground);
}

.ocp-d-progress li.is-current .n {
	background: var(--wp--preset--color--primary);
	color: #fff;
}

.ocp-d-progress li.is-done .n {
	background: var(--wp--preset--color--accent);
	color: #fff;
}

/* ---- campaign offer ------------------------------------------------------ */

.ocp-d-offerbar {
	margin: 0 0 16px;
	padding: 11px 14px;
	border: 1px solid var(--wp--preset--color--accent);
	border-radius: 6px;
	font-size: 1rem;
	line-height: 1.45;
	color: #0F6B43;
}

/* A size the campaign covers shows its own price, with the usual one struck
   through beside it, so the saving is stated rather than implied. */
.ocp-d-size-price.is-offer {
	display: flex;
	align-items: baseline;
	gap: 7px;
}

.ocp-d-size-price.is-offer s {
	font-weight: 400;
	color: var(--wp--preset--color--muted);
}

.ocp-d-size-price.is-offer b {
	color: var(--wp--preset--color--accent);
}

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

.ocp-d-layout {
	display: grid;
	grid-template-columns: minmax(0, 1.15fr) minmax(320px, 0.85fr);
	gap: var(--d-gap);
	align-items: start;
}

/* ---- empty state, before a photo is chosen ------------------------------- */

/*
 * With nothing to preview, the frame was simply reserving space: on a phone it
 * was the largest thing on the opening screen and pushed the one action that
 * screen exists for below the fold. It collapses until there is a photo, and
 * the upload panel takes the space instead.
 */
.ocp-designer.is-empty .ocp-d-stage {
	display: none;
}

.ocp-designer.is-empty .ocp-d-layout {
	grid-template-columns: minmax(0, 1fr);
}

.ocp-designer.is-empty .ocp-d-panel {
	max-width: 620px;
	margin: 0 auto;
}

/*
 * No photo means no price, so the bar has nothing to total. It was reading
 * "Choose a size" beside a blank amount and "incl. VAT" against nothing.
 * Leaves the action bar carrying just the one button.
 */
.ocp-designer.is-empty .ocp-d-sumline {
	display: none;
}

/*
 * With no photo there is nothing to total and nothing to go on to: choosing a
 * photo advances the step by itself, so Next was a dead button and the bar
 * around it an empty strip. The whole bar goes until there is something in it.
 */
.ocp-designer.is-empty .ocp-d-actions {
	display: none;
}

/* ---- stage --------------------------------------------------------------- */

.ocp-d-stage {
	position: sticky;
	top: 20px;
}

/* A plain, slightly warm ground. The canvas casts onto it, so it reads as an
   object standing off a wall rather than a picture printed on the panel. */
.ocp-d-canvas-wrap {
	position: relative;
	background: var(--wp--preset--color--surface);
	border: 1px solid var(--wp--preset--color--border);
	border-radius: 6px;
	padding: 10px;
	display: flex;
	align-items: center;
	justify-content: center;
}

/*
 * The photo only moves on the positioning step. Everywhere else the canvas
 * hands touch back to the page: it is pinned across the top third of a phone
 * screen, so swallowing swipes there would leave a large dead area that
 * refuses to scroll.
 */
.ocp-d-canvas {
	display: block;
	width: 100%;
	touch-action: pan-y;
	cursor: default;
	border-radius: 2px;
}

.ocp-d-canvas.is-positioning {
	touch-action: none;
	cursor: grab;
}

.ocp-d-canvas.is-positioning.is-dragging {
	cursor: grabbing;
}

.ocp-d-canvas:focus-visible {
	outline: 3px solid var(--wp--preset--color--primary);
	outline-offset: 3px;
}

/* ---- panel --------------------------------------------------------------- */

.ocp-d-panel {
	border: 1px solid var(--wp--preset--color--border);
	border-radius: 6px;
	padding: 24px;
	background: var(--wp--preset--color--background);
}

.ocp-d-step h2 {
	margin: 0 0 6px;
	font-size: 1.375rem;
}

.ocp-d-hint {
	margin: 0 0 16px;
	font-size: 1rem;
	line-height: 1.55;
	color: var(--wp--preset--color--muted);
}

.ocp-d-field {
	display: block;
	margin: 18px 0 8px;
	font-size: 1rem;
	font-weight: 600;
}

/* ---- upload -------------------------------------------------------------- */

.ocp-d-drop {
	position: relative;
	border: 2px dashed var(--wp--preset--color--border);
	border-radius: 6px;
	background: var(--wp--preset--color--surface);
	transition: border-color 0.15s, background 0.15s;
}

.ocp-d-drop.is-over {
	border-color: var(--wp--preset--color--primary);
	background: var(--wp--preset--color--background);
}

.ocp-d-drop.is-busy {
	opacity: 0.6;
	pointer-events: none;
}

.ocp-d-drop input[type="file"] {
	position: absolute;
	width: 1px;
	height: 1px;
	opacity: 0;
	overflow: hidden;
}

.ocp-d-droplabel {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 6px;
	padding: 34px 20px;
	text-align: center;
	cursor: pointer;
	font-size: 1rem;
	line-height: 1.5;
	color: var(--wp--preset--color--muted);
	min-height: 44px;
}

.ocp-d-droplabel strong {
	font-size: 1.125rem;
	color: var(--wp--preset--color--primary-dark);
	text-decoration: underline;
	text-underline-offset: 3px;
}

.ocp-d-dropicon svg {
	width: 34px;
	height: 34px;
	color: var(--wp--preset--color--muted);
}

.ocp-d-drop input[type="file"]:focus-visible + .ocp-d-droplabel {
	outline: 3px solid var(--wp--preset--color--primary);
	outline-offset: 2px;
	border-radius: 4px;
}

/* ---- upload guidance ------------------------------------------------------
 * Sits under the dropzone, where the space freed by collapsing the preview
 * was otherwise empty. Framed as help rather than as a warning: the point is
 * to save someone ordering a soft print, not to tell them off before they
 * have done anything.
 */

.ocp-d-tips {
	margin-top: 18px;
	padding-top: 16px;
	border-top: 1px solid var(--wp--preset--color--border);
	font-size: 1rem;
	line-height: 1.55;
	color: var(--wp--preset--color--muted);
}

.ocp-d-tips-lead {
	margin: 0 0 8px;
	font-weight: 600;
	color: var(--wp--preset--color--foreground);
}

.ocp-d-tips ul {
	margin: 0 0 10px;
	padding: 0;
	list-style: none;
}

.ocp-d-tips li {
	position: relative;
	margin: 6px 0;
	padding-left: 18px;
}

.ocp-d-tips li::before {
	content: "";
	position: absolute;
	left: 2px;
	top: 10px;
	width: 5px;
	height: 5px;
	border-radius: 50%;
	background: var(--wp--preset--color--muted);
}

.ocp-d-tips-foot {
	margin: 0;
}

.ocp-d-status {
	margin: 14px 0 0;
	font-size: 1rem;
	color: var(--wp--preset--color--muted);
}

.ocp-d-status.is-error {
	color: var(--wp--preset--color--danger);
	font-weight: 500;
}

.ocp-d-filerow {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: 10px;
	margin-top: 16px;
	padding: 12px 14px;
	border: 1px solid var(--wp--preset--color--border);
	border-radius: 6px;
	font-size: 1rem;
	word-break: break-word;
}

.ocp-d-link {
	background: none;
	border: 0;
	padding: 6px 2px;
	font: inherit;
	color: var(--wp--preset--color--primary-dark);
	text-decoration: underline;
	text-underline-offset: 3px;
	cursor: pointer;
	min-height: 44px;
}

.ocp-d-link:hover {
	color: var(--wp--preset--color--primary);
}

/* ---- sizes --------------------------------------------------------------- */

/*
 * One size a row. The five that sell carry the name they are sold under and
 * what they suit; the rest are a plain list underneath. A grid of equal tiles
 * gave every size the same weight and left no room to say anything about any
 * of them.
 */
.ocp-d-sizes {
	display: flex;
	flex-direction: column;
	gap: 8px;
}

.ocp-d-sizes-head {
	margin: 6px 0 2px;
	font-size: 1rem;
	font-weight: 600;
	color: var(--wp--preset--color--muted);
}

.ocp-d-sizes-head:not(:first-child) {
	margin-top: 18px;
	padding-top: 14px;
	border-top: 1px solid var(--wp--preset--color--border);
}

/*
 * Swatch beside the label rather than above it. Stacked, a tile ran 132px tall
 * and only four of the twelve sizes were on screen at once on a phone; laid
 * out in a row it is 72px and eight are visible, which is the difference
 * between scanning the range and hunting through it.
 */
.ocp-d-size {
	display: flex;
	align-items: center;
	gap: 10px;
	min-height: 60px;
	padding: 11px 12px;
	background: var(--wp--preset--color--background);
	border: 1.5px solid var(--wp--preset--color--border);
	border-radius: 6px;
	cursor: pointer;
	font: inherit;
	color: inherit;
	text-align: left;
}

.ocp-d-size-text {
	display: flex;
	flex-direction: column;
	flex: 1 1 auto;
	min-width: 0;
}

.ocp-d-size-name {
	font-size: 1rem;
	font-weight: 600;
}

.ocp-d-size-blurb {
	margin-top: 2px;
	font-size: 1rem;
	line-height: 1.4;
	color: var(--wp--preset--color--muted);
}

/* The price sits at the end of the row, clear of the description. */
.ocp-d-size .ocp-d-size-price {
	flex: 0 0 auto;
	align-self: flex-start;
	white-space: nowrap;
}

/* A step up is green and a step down red, so which way the total moves reads
   before the number does. A price standing on its own stays ink. */
.ocp-d-size .ocp-d-size-price.is-delta {
	color: var(--wp--preset--color--accent);
}

.ocp-d-size .ocp-d-size-price.is-delta.is-down {
	color: var(--wp--preset--color--danger);
}

.ocp-d-size:hover {
	border-color: var(--wp--preset--color--muted);
}

.ocp-d-size.is-on {
	border-color: var(--wp--preset--color--primary);
	box-shadow: inset 0 0 0 1px var(--wp--preset--color--primary);
}

/* Sized in PHP, proportional to the real canvas. */
.ocp-d-size-shape {
	display: block;
	flex: 0 0 auto;
	border: 1.5px solid var(--wp--preset--color--muted);
	border-radius: 2px;
	background: var(--wp--preset--color--surface);
}

.ocp-d-size.is-on .ocp-d-size-shape {
	border-color: var(--wp--preset--color--primary);
	background: var(--wp--preset--color--background);
}

/* Ink, not green. Colour on this row means the direction the total moves, so
   the one price that is not a step must not borrow the green that says "less
   than the size above". The steps themselves colour further down. */
.ocp-d-size-price {
	font-size: 1rem;
	font-weight: 600;
}

/*
 * Sizes that only some canvas types are sold in carry the restriction on the
 * tile itself. The alternative, a caveat paragraph above the grid, is the kind
 * of text people scroll past, and it grows a sentence every time a type gains
 * a restriction.
 */
.ocp-d-size-tag {
	display: block;
	margin-top: 2px;
	font-size: 1rem;
	line-height: 1.3;
	color: var(--wp--preset--color--primary-dark);
}

.ocp-d-size.is-limited {
	border-style: dashed;
}

/* ---- zoom ---------------------------------------------------------------- */

/*
 * The bar is already the full width of the panel, so what was left to give it
 * was thickness. The browser's own control draws a 4px hairline: on a phone it
 * read as a divider with something resting on it rather than as the one
 * control that resizes the photo. A 12px track and a 28px handle are the tap
 * target, not decoration.
 *
 * Taking the track means drawing the filled part ourselves. Firefox has
 * ::-moz-range-progress for it; WebKit has no equivalent, so the JS writes the
 * position along the bar into --ocp-zoom-p and a gradient stands in. The
 * offsets keep that gradient under the middle of the handle, which is where
 * the fill has to stop or it runs ahead of the thing dragging it.
 */
.ocp-d-panel input[type="range"] {
	--ocp-zoom-p: 0;
	-webkit-appearance: none;
	appearance: none;
	width: 100%;
	min-height: 44px;
	margin: 0;
	background: transparent;
	cursor: pointer;
}

.ocp-d-panel input[type="range"]::-webkit-slider-runnable-track {
	height: 12px;
	border-radius: 999px;
	background: linear-gradient(
		to right,
		var(--wp--preset--color--primary) calc(14px + (100% - 28px) * var(--ocp-zoom-p)),
		var(--wp--preset--color--border) calc(14px + (100% - 28px) * var(--ocp-zoom-p))
	);
}

.ocp-d-panel input[type="range"]::-moz-range-track {
	height: 12px;
	border-radius: 999px;
	background: var(--wp--preset--color--border);
}

.ocp-d-panel input[type="range"]::-moz-range-progress {
	height: 12px;
	border-radius: 999px;
	background: var(--wp--preset--color--primary);
}

.ocp-d-panel input[type="range"]::-webkit-slider-thumb {
	-webkit-appearance: none;
	appearance: none;
	box-sizing: border-box;
	width: 28px;
	height: 28px;
	/* Centres a 28px handle on a 12px track. */
	margin-top: -8px;
	border-radius: 50%;
	border: 2px solid var(--wp--preset--color--primary);
	background: var(--wp--preset--color--background);
	box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
}

.ocp-d-panel input[type="range"]::-moz-range-thumb {
	box-sizing: border-box;
	width: 28px;
	height: 28px;
	border-radius: 50%;
	border: 2px solid var(--wp--preset--color--primary);
	background: var(--wp--preset--color--background);
	box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
}

/* Focus lands on the handle, since the input itself has no box of its own now. */
.ocp-d-panel input[type="range"]:focus {
	outline: none;
}

.ocp-d-panel input[type="range"]:focus-visible::-webkit-slider-thumb {
	outline: 3px solid var(--wp--preset--color--primary);
	outline-offset: 2px;
}

.ocp-d-panel input[type="range"]:focus-visible::-moz-range-thumb {
	outline: 3px solid var(--wp--preset--color--primary);
	outline-offset: 2px;
}

.ocp-d-btnrow {
	display: flex;
	flex-wrap: wrap;
	gap: 10px;
	margin: 12px 0 4px;
}

/* ---- edges --------------------------------------------------------------- */

.ocp-d-wraps {
	display: flex;
	flex-direction: column;
	gap: 10px;
}

.ocp-d-wrap {
	display: block;
	width: 100%;
	min-height: 44px;
	padding: 14px 16px;
	text-align: left;
	background: var(--wp--preset--color--background);
	border: 1.5px solid var(--wp--preset--color--border);
	border-radius: 6px;
	cursor: pointer;
	font: inherit;
	color: inherit;
}

.ocp-d-wrap strong {
	display: block;
	font-size: 1rem;
	margin-bottom: 3px;
}

.ocp-d-wrap span {
	display: block;
	font-size: 1rem;
	line-height: 1.5;
	color: var(--wp--preset--color--muted);
}

.ocp-d-wrap.is-on {
	border-color: var(--wp--preset--color--primary);
	box-shadow: inset 0 0 0 1px var(--wp--preset--color--primary);
}

.ocp-d-colours > div {
	display: flex;
	flex-wrap: wrap;
	gap: 10px;
}

.ocp-d-swatch {
	display: inline-flex;
	align-items: center;
	gap: 9px;
	min-height: 44px;
	padding: 8px 16px 8px 10px;
	background: var(--wp--preset--color--background);
	border: 1.5px solid var(--wp--preset--color--border);
	border-radius: 999px;
	cursor: pointer;
	font: inherit;
	font-size: 1rem;
	color: inherit;
}

.ocp-d-swatch-dot {
	width: 22px;
	height: 22px;
	border-radius: 50%;
	background: var(--sw);
	border: 1px solid var(--wp--preset--color--border);
}

.ocp-d-swatch.is-on {
	border-color: var(--wp--preset--color--primary);
	box-shadow: inset 0 0 0 1px var(--wp--preset--color--primary);
}

/* ---- summary and actions ------------------------------------------------- */

.ocp-d-sumline {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 12px;
	margin-bottom: 16px;
	font-size: 1.0625rem;
}

.ocp-d-sumname {
	min-width: 0;
}

.ocp-d-sumtotal {
	display: flex;
	align-items: baseline;
	gap: 7px;
	white-space: nowrap;
}

.ocp-d-sumline strong {
	font-size: 1.375rem;
	font-weight: 700;
	color: var(--wp--preset--color--accent);
	transition: transform 0.2s ease;
}

.ocp-d-sumline strong.is-changed {
	transform: scale(1.09);
}

@media (prefers-reduced-motion: reduce) {
	.ocp-d-sumline strong {
		transition: none;
	}

	.ocp-d-sumline strong.is-changed {
		transform: none;
	}
}

/* The first price a customer sees must be the VAT-inclusive one, so the label
   rides alongside the amount rather than sitting on its own line. */
.ocp-d-vat {
	font-size: 1rem;
	color: var(--wp--preset--color--muted);
}

.ocp-d-cta {
	display: block;
	width: 100%;
	min-height: 52px;
	padding: 15px 30px;
	background: var(--wp--preset--color--primary);
	color: #fff;
	border: 0;
	border-radius: 4px;
	/* 18px bold is the only size DESIGN.md allows white text on orange. */
	font-size: 1.125rem;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.03em;
	cursor: pointer;
	transition: background 0.15s;
}

.ocp-d-cta:hover:not(:disabled) {
	background: var(--wp--preset--color--primary-dark);
}

.ocp-d-cta:disabled {
	background: var(--wp--preset--color--surface);
	color: var(--wp--preset--color--muted);
	cursor: not-allowed;
}

.ocp-d-secondary {
	min-height: 44px;
	padding: 11px 20px;
	background: var(--wp--preset--color--background);
	color: var(--wp--preset--color--primary-dark);
	border: 1.5px solid var(--wp--preset--color--primary);
	border-radius: 4px;
	font: inherit;
	font-size: 1rem;
	font-weight: 600;
	cursor: pointer;
}

.ocp-d-secondary:hover:not(:disabled) {
	background: var(--wp--preset--color--surface);
}

.ocp-d-secondary:disabled {
	border-color: var(--wp--preset--color--border);
	color: var(--wp--preset--color--muted);
	cursor: not-allowed;
}

.ocp-d-nav {
	display: flex;
	justify-content: space-between;
	gap: 10px;
	margin-top: 14px;
}

/*
 * Next is the primary action on every step except the last, where Add to
 * basket takes over. Only ever one filled orange button on screen, which is
 * what DESIGN.md asks for. Previously Back and Next were both outlined, so the
 * screen had no primary action at all.
 */
.ocp-d-nav .ocp-d-cta-next {
	width: auto;
	flex: 1 1 auto;
}

/* Every interactive control keeps a visible focus ring. */
.ocp-designer button:focus-visible,
.ocp-designer input:focus-visible,
.ocp-designer [tabindex]:focus-visible {
	outline: 3px solid var(--wp--preset--color--primary);
	outline-offset: 2px;
}

/* ---- orientation --------------------------------------------------------- */

.ocp-d-orient {
	display: flex;
	gap: 10px;
}

.ocp-d-orientbtn {
	display: inline-flex;
	align-items: center;
	gap: 9px;
	min-height: 44px;
	padding: 10px 18px;
	background: var(--wp--preset--color--background);
	border: 1.5px solid var(--wp--preset--color--border);
	border-radius: 6px;
	cursor: pointer;
	font: inherit;
	font-size: 1rem;
	color: inherit;
}

.ocp-d-orientbtn.is-on {
	border-color: var(--wp--preset--color--primary);
	box-shadow: inset 0 0 0 1px var(--wp--preset--color--primary);
}

.ocp-d-orientshape {
	display: block;
	border: 1.5px solid var(--wp--preset--color--muted);
	border-radius: 2px;
}

.ocp-d-orientshape.land { width: 26px; height: 19px; }
.ocp-d-orientshape.port { width: 19px; height: 26px; }

/* ---- finish (colour, mono, sepia) ---------------------------------------- */

.ocp-d-effects {
	display: flex;
	flex-direction: column;
	gap: 10px;
}

.ocp-d-effect {
	display: flex;
	align-items: center;
	gap: 14px;
	width: 100%;
	min-height: 44px;
	padding: 12px 14px;
	text-align: left;
	background: var(--wp--preset--color--background);
	border: 1.5px solid var(--wp--preset--color--border);
	border-radius: 6px;
	cursor: pointer;
	font: inherit;
	color: inherit;
}

.ocp-d-effect.is-on {
	border-color: var(--wp--preset--color--primary);
	box-shadow: inset 0 0 0 1px var(--wp--preset--color--primary);
}

.ocp-d-effect-chip {
	flex: 0 0 auto;
	width: 46px;
	height: 36px;
	border-radius: 4px;
	/* A stand-in swatch: warm highlight over a cool ground, so the three
	   treatments read differently at a glance without loading an image. */
	background: linear-gradient(150deg, #F7C97E 0%, #EFA45B 40%, #7FA36B 75%, #5C7F55 100%);
}

.ocp-d-effect strong {
	display: block;
	font-size: 1rem;
	margin-bottom: 2px;
}

.ocp-d-effect span:not(.ocp-d-effect-chip) {
	display: block;
	font-size: 1rem;
	line-height: 1.45;
	color: var(--wp--preset--color--muted);
}

/* ---- frame ladder -------------------------------------------------------- */

.ocp-d-frames {
	display: flex;
	flex-direction: column;
	gap: 10px;
}

.ocp-d-frame {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 12px;
	width: 100%;
	min-height: 60px;
	padding: 13px 15px;
	text-align: left;
	background: var(--wp--preset--color--background);
	border: 1.5px solid var(--wp--preset--color--border);
	border-radius: 6px;
	cursor: pointer;
	font: inherit;
	color: inherit;
}

.ocp-d-frame.is-on {
	border-color: var(--wp--preset--color--primary);
	box-shadow: inset 0 0 0 1px var(--wp--preset--color--primary);
}

.ocp-d-frame-main strong {
	display: block;
	font-size: 1rem;
	margin-bottom: 2px;
}

.ocp-d-frame-help {
	display: block;
	font-size: 1rem;
	line-height: 1.45;
	color: var(--wp--preset--color--muted);
}

.ocp-d-frame-price {
	white-space: nowrap;
	font-size: 1rem;
	font-weight: 600;
}

.ocp-d-frame-price.is-delta {
	color: var(--wp--preset--color--accent);
}

.ocp-d-frame-price.is-delta.is-down {
	color: var(--wp--preset--color--danger);
}

/* Sold, but not in the size they picked. Shown, not hidden, so the option is
   never silently missing, with the reason attached. */
.ocp-d-frame.is-unavailable {
	cursor: not-allowed;
	border-style: dashed;
	background: var(--wp--preset--color--surface);
}

.ocp-d-frame.is-unavailable .ocp-d-frame-main strong,
.ocp-d-frame.is-unavailable .ocp-d-frame-help {
	text-decoration: line-through;
	color: var(--wp--preset--color--muted);
}

.ocp-d-frame-why {
	display: block;
	margin-top: 5px;
	font-size: 1rem;
	font-weight: 600;
	color: var(--wp--preset--color--primary-dark);
}

/* ---- second copy offer --------------------------------------------------- */

.ocp-d-bump {
	display: flex;
	align-items: center;
	gap: 14px;
	padding: 15px;
	border: 1.5px solid var(--wp--preset--color--border);
	border-radius: 6px;
}

.ocp-d-bump-thumbs {
	display: flex;
	flex: 0 0 auto;
}

.ocp-d-bump-thumb {
	display: block;
	width: 46px;
	height: 36px;
	border-radius: 2px;
	background: var(--wp--preset--color--surface) center/cover no-repeat;
	border: 1px solid var(--wp--preset--color--border);
}

.ocp-d-bump-thumb.second {
	margin-left: -28px;
	transform: translateY(6px);
}

.ocp-d-bump-text {
	flex: 1;
	font-size: 1rem;
	line-height: 1.5;
}

.ocp-d-bump-text strong {
	display: block;
	margin-bottom: 1px;
}

.ocp-d-bump-text span {
	color: var(--wp--preset--color--muted);
}

/* The extra shows as a step up, the same shape as the frame ladder's deltas,
   so a customer reads the two the same way. */
.ocp-d-bump-price {
	flex: 0 0 auto;
	align-self: center;
	font-size: 1.0625rem;
	font-weight: 600;
	white-space: nowrap;
	color: var(--wp--preset--color--accent);
}

/* Added already, so the number is what taking it back off saves. */
.ocp-d-bump-price.is-down {
	color: var(--wp--preset--color--danger);
}

.ocp-d-addbtn {
	flex: 0 0 auto;
	min-height: 44px;
	padding: 11px 20px;
	background: var(--wp--preset--color--background);
	color: var(--wp--preset--color--primary-dark);
	border: 1.5px solid var(--wp--preset--color--primary);
	border-radius: 4px;
	font: inherit;
	font-size: 1rem;
	font-weight: 600;
	cursor: pointer;
	white-space: nowrap;
}

.ocp-d-addbtn[aria-pressed="true"] {
	background: var(--wp--preset--color--primary);
	color: #fff;
}

.ocp-d-ticks {
	list-style: none;
	padding: 0;
	margin: 16px 0 0;
	font-size: 1rem;
	color: var(--wp--preset--color--muted);
}

.ocp-d-ticks li {
	position: relative;
	padding-left: 24px;
	margin: 6px 0;
	line-height: 1.5;
}

.ocp-d-ticks li::before {
	content: "";
	position: absolute;
	left: 0;
	top: 6px;
	width: 14px;
	height: 8px;
	border-left: 2px solid var(--wp--preset--color--accent);
	border-bottom: 2px solid var(--wp--preset--color--accent);
	transform: rotate(-45deg);
}

/* ---- confirmation --------------------------------------------------------
 * The last screen before the basket. It covers the designer completely
 * rather than floating over it: this is one job on one screen, and the
 * options behind it are not part of that job.
 */

body.ocp-confirming {
	overflow: hidden;
}

.ocp-confirm {
	position: fixed;
	inset: 0;
	z-index: 40;
	display: flex;
	background: var(--wp--preset--color--background);
}

.ocp-confirm-sheet {
	display: flex;
	flex-direction: column;
	width: 100%;
	min-height: 0;
	background: var(--wp--preset--color--background);
}

.ocp-confirm-head {
	display: flex;
	align-items: center;
	gap: 6px;
	padding: 8px 12px;
	border-bottom: 1px solid var(--wp--preset--color--border);
}

.ocp-confirm-head h2 {
	margin: 0;
	font-size: 1.125rem;
}

.ocp-confirm-back {
	display: inline-flex;
	align-items: center;
	gap: 4px;
	min-height: 44px;
	padding: 8px 10px 8px 4px;
	background: none;
	border: 0;
	font: inherit;
	font-size: 1rem;
	color: var(--wp--preset--color--primary-dark);
	cursor: pointer;
}

.ocp-confirm-back svg {
	width: 20px;
	height: 20px;
}

.ocp-confirm-body {
	flex: 1 1 auto;
	min-height: 0;
	overflow-y: auto;
	/* Hold the scrollbar's width whether it is there or not, so the scene is
	   not measured, drawn, and then squeezed by a bar arriving under it. */
	scrollbar-gutter: stable;
	-webkit-overflow-scrolling: touch;
	padding: 14px 16px 20px;
}

.ocp-confirm-scene {
	border-radius: 6px;
	overflow: hidden;
	background: var(--wp--preset--color--surface);
}

.ocp-confirm-scene canvas {
	display: block;
	width: 100%;
	touch-action: pan-y;
}

/* Only the turned view responds to a drag, so only it takes the gesture. */
.ocp-confirm-scene canvas.is-turnable {
	touch-action: none;
	cursor: grab;
}

/* Two views of the same canvas, so they read as one control with two
   positions rather than as two separate buttons. */
.ocp-confirm-views {
	display: flex;
	gap: 8px;
	margin-top: 12px;
}

.ocp-confirm-views button {
	flex: 1 1 0;
	min-height: 44px;
	padding: 11px 14px;
	background: var(--wp--preset--color--background);
	border: 1.5px solid var(--wp--preset--color--border);
	border-radius: 999px;
	font: inherit;
	font-size: 1rem;
	font-weight: 600;
	color: var(--wp--preset--color--foreground);
	cursor: pointer;
}

.ocp-confirm-views button.is-on {
	border-color: var(--wp--preset--color--primary);
	box-shadow: inset 0 0 0 1px var(--wp--preset--color--primary);
}

.ocp-confirm-caption {
	margin: 10px 0 0;
	font-size: 1rem;
	line-height: 1.5;
	color: var(--wp--preset--color--muted);
}

/* Said once, at the moment of committing, with the way out attached. Kept
   neutral: it is information, not a telling off, and it never blocks the
   order. */
.ocp-confirm-warn {
	margin-top: 16px;
	padding: 14px;
	background: var(--wp--preset--color--surface);
	border: 1.5px solid var(--wp--preset--color--border);
	border-radius: 6px;
}

.ocp-confirm-warn p {
	margin: 0 0 12px;
	font-size: 1rem;
	line-height: 1.55;
}

.ocp-confirm-spec {
	display: grid;
	grid-template-columns: auto 1fr;
	gap: 0;
	margin: 20px 0 0;
	font-size: 1rem;
}

.ocp-confirm-spec dt {
	padding: 10px 16px 10px 0;
	color: var(--wp--preset--color--muted);
	border-top: 1px solid var(--wp--preset--color--border);
}

.ocp-confirm-spec dd {
	margin: 0;
	padding: 10px 0;
	text-align: right;
	font-weight: 600;
	border-top: 1px solid var(--wp--preset--color--border);
	word-break: break-word;
}

.ocp-confirm-body .ocp-d-ticks {
	margin-top: 18px;
}

.ocp-confirm-foot {
	flex: 0 0 auto;
	padding: 12px 16px calc(12px + env(safe-area-inset-bottom, 0px));
	border-top: 1px solid var(--wp--preset--color--border);
	background: var(--wp--preset--color--background);
}

.ocp-confirm-foot .ocp-d-link {
	display: block;
	width: 100%;
	margin-top: 4px;
	text-align: center;
}

/* On a wide screen it is a sheet in the middle of the page, over a dimmed
   designer, because a full screen of white with one button in it looks like
   a page that failed to load. */
@media (min-width: 901px) {
	.ocp-confirm {
		align-items: center;
		justify-content: center;
		padding: 24px;
		background: rgba(31, 33, 34, 0.55);
	}

	.ocp-confirm-sheet {
		width: min(660px, 100%);
		max-height: 100%;
		border-radius: 8px;
		overflow: hidden;
	}

	/* The scene is a room drawn to scale, so its shape decides how much wall
	   is in shot. Left full width on a desktop sheet it becomes a letterbox
	   of a room, with the sofa a distant object in it. */
	.ocp-confirm-scene {
		max-width: 460px;
		margin: 0 auto;
	}
}

/* ---- empty state --------------------------------------------------------- */

.ocp-d-empty {
	max-width: 640px;
	padding: 30px 0;
	font-size: 1rem;
	line-height: 1.6;
}

.ocp-d-empty code {
	background: var(--wp--preset--color--surface);
	padding: 2px 6px;
	border-radius: 3px;
}

/* ---- responsive ---------------------------------------------------------- */

/* ---- desktop placement of the action bar --------------------------------- */

/* On a wide screen the bar sits under the control column rather than spanning
   the full width, which keeps it next to the choices it totals up. */
@media (min-width: 901px) {
	.ocp-d-actions {
		max-width: 520px;
		margin-left: auto;
	}
}

/* ---- phone --------------------------------------------------------------
 * Roughly 90% of customers are on a phone, so this is the primary layout and
 * the desktop rules above are the adaptation.
 *
 * Two structural moves: the preview pins to the top of the screen so tapping
 * an upgrade repaints a canvas the customer is actually looking at, and the
 * running total plus the single primary action pin to the bottom, in the
 * thumb zone. Measured before this change: at the moment of tapping the +£10
 * upgrade, only 31% of the preview was on screen on a 844px phone, 12% on a
 * 667px one.
 */
@media (max-width: 900px) {
	/*
	 * minmax(0, 1fr), not 1fr. A bare 1fr track is minmax(auto, 1fr), so a grid
	 * item whose min-content is wider than the phone pushes the whole page
	 * sideways. Measured at 6px of horizontal scroll on a 390px screen.
	 */
	.ocp-d-layout {
		grid-template-columns: minmax(0, 1fr);
		gap: 16px;
	}

	.ocp-d-stage,
	.ocp-d-panel {
		min-width: 0;
	}

	/* Leave room for the pinned action bar. */
	.ocp-designer {
		padding-bottom: 152px;
	}

	/*
	 * One scrolling row, not a wrap. Six pills wrapped onto two rows and the
	 * sixth stretched across the full width, which read as broken. Only the
	 * current step carries its label, so all six numbers stay reachable.
	 */
	.ocp-d-progress {
		flex-wrap: nowrap;
		overflow-x: auto;
		overscroll-behavior-x: contain;
		scrollbar-width: none;
		position: sticky;
		top: 0;
		z-index: 6;
		gap: 6px;
		margin: 0;
		padding: 8px 0;
		background: var(--wp--preset--color--background);
	}

	.ocp-d-progress::-webkit-scrollbar {
		display: none;
	}

	/*
	 * The row scrolls sideways with no scrollbar, so nothing said more steps
	 * were there: step six sat clipped at the edge looking like a rendering
	 * fault. Its own edges now fade where there is more to reach, set by JS to
	 * whichever sides can still scroll. A mask, not a painted gradient, so it
	 * needs no knowledge of what is behind it.
	 */
	.ocp-d-progress {
		--fade-l: 0px;
		--fade-r: 0px;
		-webkit-mask-image: linear-gradient(to right, transparent 0, #000 var(--fade-l), #000 calc(100% - var(--fade-r)), transparent 100%);
		mask-image: linear-gradient(to right, transparent 0, #000 var(--fade-l), #000 calc(100% - var(--fade-r)), transparent 100%);
	}

	.ocp-d-progress li {
		flex: 0 0 auto;
	}

	.ocp-d-progress li button {
		padding: 8px 12px 8px 8px;
	}

	.ocp-d-progress li .t {
		display: none;
	}

	.ocp-d-progress li.is-current .t {
		display: inline;
	}

	/* Parks below the pinned pill row, whose height JS measures. The hairline
	   marks where the pinned block ends, so options sliding underneath read as
	   scrolling rather than as a card sliced in half. */
	.ocp-d-stage {
		position: sticky;
		top: var(--ocp-pill-h, 60px);
		z-index: 5;
		padding: 8px 0 10px;
		background: var(--wp--preset--color--background);
		border-bottom: 1px solid var(--wp--preset--color--border);
	}

	/*
	 * Jumping to a step must not tuck its heading under the pinned block. The
	 * clearance is measured in JS rather than guessed here, because the pinned
	 * height is the pill row plus a preview whose height depends on the chosen
	 * canvas shape. This is the fallback for anything that scrolls the section
	 * without going through goTo.
	 */
	.ocp-d-step {
		scroll-margin-top: calc(var(--ocp-pill-h, 60px) + 40vh);
	}

	/* Positioning pins a taller preview, so its heading has more to clear. */
	.ocp-d-step[data-step="3"] {
		scroll-margin-top: calc(var(--ocp-pill-h, 60px) + 52vh);
	}

	.ocp-d-canvas-wrap {
		padding: 8px;
		min-height: 0;
	}

	/*
	 * No zoom slider on a phone. Pinching the preview does the same job in the
	 * place the customer is already looking, and the slider was asking for a
	 * second way to say it in the space the options need. Fit to size stays, so
	 * there is still one tap back to the whole photo.
	 */
	.ocp-d-zoomrow {
		display: none;
	}


	.ocp-d-panel {
		padding: 18px 14px;
	}

	/*
	 * The quality note is pinned with the preview, so every pixel of it is a
	 * pixel the options do not get. Compact, and the wording shortened to one
	 * line at this width.
	 */

	.ocp-d-actions {
		position: fixed;
		left: 0;
		right: 0;
		bottom: 0;
		z-index: 20;
		background: var(--wp--preset--color--background);
		border-top: 1px solid var(--wp--preset--color--border);
		padding: 12px 16px calc(12px + env(safe-area-inset-bottom, 0px));
	}

	.ocp-d-summary {
		margin-top: 0;
		padding-top: 0;
		border-top: 0;
	}

	.ocp-d-sumline {
		margin-bottom: 10px;
	}

	/* Back and the primary action share one row, so the bar stays shallow. */
	.ocp-d-actions form {
		display: contents;
	}

	.ocp-d-actions .ocp-d-cta {
		order: 2;
		flex: 1 1 auto;
		width: auto;
	}

	.ocp-d-nav {
		display: contents;
	}

	.ocp-d-actions [data-ocp-back] {
		order: 1;
		flex: 0 0 auto;
	}

	.ocp-d-actions [data-ocp-next] {
		order: 3;
		flex: 1 1 auto;
	}

	.ocp-d-summary {
		display: flex;
		flex-wrap: wrap;
		align-items: center;
		gap: 10px;
	}

	.ocp-d-sumline {
		flex: 1 0 100%;
		margin-bottom: 0;
	}


	.ocp-d-bump {
		flex-wrap: wrap;
	}

	.ocp-d-addbtn {
		width: 100%;
	}
}

/* ---- offer card on a phone ------------------------------------------------
 * The row is thumb, text, price, then the button beneath at full width. The
 * price must not be pushed onto its own line by the wrap that widens the
 * button, or the card grows a mostly empty row.
 */
@media (max-width: 900px) {
	.ocp-d-bump-price {
		align-self: flex-start;
		margin-left: auto;
	}
}

/* ---- page furniture -------------------------------------------------------
 * This stylesheet is only enqueued on the designer page, so these rules reach
 * nowhere else. See oneclickprint_is_designer_page().
 */

/*
 * The promo bar is a reason to start designing, and by this point the customer
 * already is. On a phone it was taking two lines off the top of a screen the
 * whole flow is fighting for.
 */
.ocp-promo {
	display: none;
}
