/*
 * PAJ Engraving configurator styles.
 *
 * SCOPING RULE: every selector in the first section is prefixed .paj-eng or is a
 * .paj-eng-* class. Zero bare element selectors. Flatsome applies uppercase,
 * letter-spacing, gradients and border-radius to `button` and `.button`
 * globally, and Dynamic Content for Elementor applies colours through
 * [data-dce-*] attribute selectors which can outrank a plain class. The reset
 * below neutralises that inside our root and nowhere else.
 *
 * REACTIVITY: the design recomputes twelve inline style objects on every colour
 * change. Here, per-chip values (--pf-*) are written ONCE server-side and never
 * touched, and the live values (--paj-eng-*) are set by JS.
 *
 * 🔴 WHERE the live defaults are declared is load-bearing, and getting it wrong
 * is a silent no-op. A custom property resolves per ELEMENT: a declaration ON
 * an element beats the value it would inherit from an ancestor. While these
 * defaults sat in the `.paj-eng` block below, .paj-eng carried its own
 * declaration of --paj-eng-ink, so every descendant read THAT and never the
 * value JS had written to :root. Selecting Bordeaux left all twelve chip
 * previews white, and the summary plate drew in the default face whichever font
 * was picked. They live on :root now, where nothing shadows the update.
 *
 * JS writes every live value TWICE, to documentElement and to the .paj-eng root
 * (see paintVars). The :root copy is the interface the out-of-root surfaces
 * (sticky bar, the gallery plate slide) read with no shared JS; the .paj-eng
 * copy is what overrides the style attribute the shortcode prints for first
 * paint, which is itself deliberate: per-product, and immune to CSS
 * aggregation.
 *
 * The last section styles nodes OUTSIDE our root. Those classes are still ours;
 * the elements they land on are not.
 */

/* =====================================================================
 * Tokens
 * ================================================================== */

/*
 * LIVE values, and the only block in this file that is not scoped to .paj-eng.
 *
 * On :root by necessity, not by preference: see the reactivity note in the file
 * header. A copy of any of these inside `.paj-eng` shadows the value JS writes
 * to documentElement and breaks colour and font reactivity outright, so do not
 * move them back, and do not add a second declaration for one of them anywhere
 * a .paj-eng ancestor could pick it up.
 *
 * The values here are what renders before JS runs, and what renders if JS never
 * runs at all. The shortcode also prints the product's configured defaults into
 * a style attribute on .paj-eng, which is what makes first paint show the right
 * face and ink rather than these generic ones.
 */
:root {
	--paj-eng-ink: #f2f2f0;
	--paj-eng-ink-hi: #ffffff;
	--paj-eng-fam: 'Caveat', cursive;
	--paj-eng-weight: 600;
	--paj-eng-scale: 1;
	--paj-eng-fit: 1;
	--paj-eng-len: 5;
	--paj-eng-plate-size: 17px;
	--paj-eng-summary-size: 14px;
	--paj-eng-caps: none;
}

.paj-eng {
	--paj-eng-orange: #f05123;
	--paj-eng-navy: #1a2839;
	--paj-eng-cta: #f87d3b;
	--paj-eng-link: #40638c;
	--paj-eng-muted: #64748b;
	--paj-eng-disabled: #94a3b8;
	--paj-eng-border: #cbd5e1;
	--paj-eng-surface: #f0f5fa;
	--paj-eng-amber-bg: #fdebd2;
	--paj-eng-amber-fg: #8a4a12;
	--paj-eng-plate: #17181c;
	--paj-eng-radius: 12px;
	--paj-eng-radius-sm: 8px;

	/* NO live values here. They are on :root above, and a copy in this block is
	   what broke colour and font reactivity. */

	box-sizing: border-box;
	font-family: 'Roboto', -apple-system, BlinkMacSystemFont, 'Segoe UI', Arial, sans-serif;
	color: var(--paj-eng-navy);
	line-height: 1.45;
	-webkit-font-smoothing: antialiased;
}

.paj-eng *,
.paj-eng *::before,
.paj-eng *::after {
	box-sizing: border-box;
}

/*
 * Theme reset, scoped.
 *
 * ⚠️ SPECIFICITY: this selector is (0,1,1), one class plus one element, which
 * outranks a plain .paj-eng-chip at (0,1,0). It has to be that strong to beat
 * Flatsome's and Elementor's own `button` rules. The consequence is that every
 * component rule below which sets a property this reset touches (background,
 * border, border-radius, padding, colour) MUST be written as
 * `.paj-eng .paj-eng-thing` (0,2,0) rather than `.paj-eng-thing`.
 *
 * This is not stylistic. Getting it wrong renders the font chips and colour
 * swatches with no background and no border, meaning white text on white and
 * fifteen invisible circles, which is exactly what happened the first time.
 */
.paj-eng button {
	margin: 0;
	padding: 0;
	background: none;
	border: 0;
	font: inherit;
	color: inherit;
	text-transform: none;
	letter-spacing: normal;
	box-shadow: none;
	border-radius: 0;
	line-height: normal;
	min-height: 0;
	text-shadow: none;
	outline: none;
}

/*
 * NO FOCUS OUTLINE ON ANY BUTTON IN THE CONFIGURATOR.
 *
 * Requested by PAJ on 2026-08-06, after the ring around "Weniger Schriften
 * anzeigen". Both pseudo-classes are listed on purpose: :focus-visible alone
 * would not cover the browsers that ring a button on a plain mouse click, which
 * is the case that prompted the request.
 *
 * 🔴 What this costs, recorded rather than argued: a keyboard user now has no
 * indication of which of the twelve font chips or sixteen swatches is focused.
 * The roving tabindex still makes each row one tab stop and arrow keys still
 * move focus, but nothing on screen says where focus is. This widget's own notes
 * flag BFSG as applying to this shop from June 2025. A middle option exists and
 * was offered: ring on keyboard, none on mouse, via :focus:not(:focus-visible).
 * PAJ chose full removal.
 *
 * NOTE what is deliberately NOT here: box-shadow. The rings on a selected chip
 * or swatch are SELECTION cues driven by aria-checked, and a :focus rule setting
 * box-shadow:none would outrank them at (0,3,1) and erase the selected state on
 * the very element the customer just clicked.
 */
.paj-eng button:focus,
.paj-eng button:focus-visible {
	outline: none;
}

.paj-eng input[type='text'] {
	margin: 0;
	box-shadow: none;
	background: #fff;
	min-height: 0;
}

.paj-eng p {
	margin: 0;
}

/* Kept for the name INPUT and the spelling checkbox, which are not buttons and
   are not covered by the removal above. The spelling checkbox in particular is
   a gate the CTA sends the customer to, so it has to be findable when it
   receives focus programmatically. */
.paj-eng input:focus-visible {
	outline: 2px solid var(--paj-eng-link);
	outline-offset: 2px;
}

/*
 * The [hidden] attribute only carries `display:none` from the UA stylesheet, so
 * ANY component rule setting display (flex, grid, block) silently defeats it.
 * JS toggles `hidden` on the summary, price, spell row, notes, hint and caps
 * note, so one blanket rule is worth more than six per-component ones, and it
 * covers whatever gets added next.
 */
.paj-eng [hidden] {
	display: none !important;
}

.paj-eng-sr {
	position: absolute !important;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip: rect(0, 0, 0, 0);
	white-space: nowrap;
	border: 0;
}

/* =====================================================================
 * Root layout
 * ================================================================== */

.paj-eng {
	display: block;
	width: 100%;
	container-type: inline-size;
}

.paj-eng-notices:empty {
	display: none;
}

/* =====================================================================
 * Variant cards
 * ================================================================== */

.paj-eng-cards {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 12px;
}

.paj-eng .paj-eng-card {
	position: relative;
	display: block;
	width: 100%;
	text-align: center;
	background: #fff;
	cursor: pointer;
	border-radius: var(--paj-eng-radius) !important;
	transition: border-color 200ms, box-shadow 200ms;

	/* Unselected carries a 1px border and +1px padding on every side. That extra
	   pixel is not a typo: it compensates for the thinner border so the card
	   does not shift by 1px when selection moves. Straight from the design. */
	border: 1px solid var(--paj-eng-border);
	padding: 17px 13px 15px;
}

.paj-eng .paj-eng-card[aria-checked='true'] {
	border: 2px solid var(--paj-eng-orange);
	padding: 16px 12px 14px;
	box-shadow: 0 4px 12px rgba(240, 81, 35, 0.12);
}

.paj-eng-card__badge {
	position: absolute;
	top: -11px;
	left: 50%;
	transform: translateX(-50%);
	background: var(--paj-eng-orange);
	color: #fff;
	border-radius: 999px;
	font-size: 11px;
	font-weight: 700;
	padding: 3px 11px;
	letter-spacing: 0.5px;
	white-space: nowrap;
}

.paj-eng-card__tick {
	position: absolute;
	top: 10px;
	right: 10px;
	width: 20px;
	height: 20px;
	border-radius: 50%;
	background: var(--paj-eng-orange);
	color: #fff;
	font-size: 13px;
	line-height: 1;
	display: none;
	align-items: center;
	justify-content: center;
}

/* The tick is the non-colour selection cue, so it must not be the only one:
   the 1px→2px border change carries it too, for anyone who cannot distinguish
   the orange. */
.paj-eng-card[aria-checked='true'] .paj-eng-card__tick {
	display: flex;
}

.paj-eng-card__plate {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 96px;
	margin: 0 auto 12px;
	position: relative;

	/* The named card's housing render is 293×256; pinning the plate to that
	   ratio keeps the two card titles level now that the standard card shows
	   the flatter 290×196 hardware render, centered in the same box (0.8.1). */
	aspect-ratio: 293 / 256;

	/* cqw in __engrave resolves against this box, the .paj-eng-plate pattern:
	   1cqw = 0.96px here, 0.76px at the narrow block's 76px, so the on-device
	   text scales with the plate instead of overflowing a smaller window. */
	container-type: inline-size;
}

.paj-eng-card__housing {
	width: 100%;
	height: auto;
	display: block;
	pointer-events: none;
}

/* The two renders frame the device differently: in hardware-led.webp it spans
   81.7% of the frame width (bbox 237/290), in housing.png 95.9% (281/293).
   In the same 96px plate that drew the standard device at 78.5px against the
   named card's 92.1px — 92.08/78.46 ≈ 1.174. transform, not width: the plate
   box and title baseline stay put, the named card's calibrated window is
   untouched, and the 76px narrow plate inherits the ratio for free. Only
   transparent margin overflows the plate; the visible device stays inside. */
.paj-eng .paj-eng-card[data-paj-eng-card='standard'] .paj-eng-card__housing {
	transform: scale(1.17);
}

/* The engraving window on the housing render: 21%/33% offset, 52%×26%.
   Same percentages as the German prototype, over the same 293×256 image.
   Only the named card overlays text since 0.8.1 — the standard card's render
   ships with the wordmark already printed, so its clipping-prone CSS twin of
   the logo is gone rather than patched.

   Typography is the hardened tier + !important: the same hostile page CSS
   that flattened the font chips rendered this span in the wrong face. */
.paj-eng .paj-eng-card__engrave {
	position: absolute;
	left: 21%;
	top: 33%;
	width: 52%;
	height: 26%;
	display: flex;
	align-items: center;
	justify-content: center;
	white-space: nowrap;
	overflow: hidden;
	text-shadow: 0 1px 0 rgba(0, 0, 0, 0.55), 0 -1px 0 rgba(255, 255, 255, 0.1);
	font-family: var(--paj-eng-fam) !important;
	font-weight: var(--paj-eng-weight) !important;
	color: var(--paj-eng-ink);
	text-transform: var(--paj-eng-caps, none) !important;
	line-height: 1 !important;

	/* fitPlate() from engraving.js transcribed to cqw, the same formula as
	   .paj-eng-plate__mark further down: boxW = the 52% window = 52cqw. At
	   the 96px plate that is 49.92px vs the JS constant's hardcoded 50 —
	   parity within 0.2% — and at the 76px narrow plate the text now scales
	   down instead of keeping its 96px-sized pixel value. */
	font-size: calc(min(52cqw / (var(--paj-eng-len, 5) * 0.52), 52cqw * 0.3) * var(--paj-eng-scale, 1)) !important;
}

.paj-eng-card__title {
	display: block;
	font-weight: 700;
	font-size: 16px;
}

.paj-eng .paj-eng-card__sub {
	display: block;
	font-family: 'Roboto', -apple-system, BlinkMacSystemFont, 'Segoe UI', Arial, sans-serif !important;
	font-size: 13px !important;
	font-weight: 400 !important;
	line-height: 1.45 !important;
	color: var(--paj-eng-muted);
	margin-top: 2px;
}

/* wc_price() emits .woocommerce-Price-amount > bdi > …currencySymbol inside
   the named card's sub, and Woo/theme/kit rules style those classes globally
   — on the draft review page the amount rendered in theme typography. The
   design: muted label, bold navy amount, one typeface, one size (0.8.1). */
.paj-eng .paj-eng-card__sub .woocommerce-Price-amount,
.paj-eng .paj-eng-card__sub .woocommerce-Price-amount bdi,
.paj-eng .paj-eng-card__sub .woocommerce-Price-currencySymbol {
	font-family: inherit !important;
	font-size: inherit !important;
	font-weight: 700 !important;
	color: var(--paj-eng-navy) !important;
}

/* =====================================================================
 * Expanding panel
 * ================================================================== */

.paj-eng-panel {
	max-height: 0;
	opacity: 0;
	overflow: hidden;
	transition: max-height 300ms ease-out, opacity 300ms ease-out;
}

.paj-eng-panel.is-open {
	/* Ceiling, not a height. 1200px → 1240px when the 2026-08-10 spacing pass
	   added ~20px to the fully-revealed panel; overflow:hidden below would clip
	   a too-small value silently. */
	max-height: 1240px;
	opacity: 1;
}

/* overflow:hidden is required for the max-height transition, and it clips the
   4px selection ring on the outermost chips. The design does not account for
   this. 6px of inner padding gives the ring room inside the clip. */
.paj-eng-panel__inner {
	padding: 6px;
	margin-top: 16px;
	display: flex;
	flex-direction: column;
	gap: 16px;
}

/* =====================================================================
 * Name field
 * ================================================================== */

.paj-eng .paj-eng-label {
	font-size: 14px;
	font-weight: 500;
	display: block;
	margin-bottom: 6px;
}

.paj-eng .paj-eng-inputwrap {
	position: relative;
}

/*
 * !important on every visually-defining property, not just the reset-touched
 * ones: Elementor's kit styles fields as
 * `.elementor-kit-N input:not([type=button]):not([type=submit])` at (0,3,1),
 * which no class chain of ours can outrank. That kit rule is what flattened
 * this field to a thin square box on the live PDP (Johnny, 2026-08-10) while
 * every !important-ed component around it kept its radius.
 */
.paj-eng .paj-eng-input {
	width: 100%;
	height: 64px !important;
	border: 1px solid var(--paj-eng-border) !important;
	border-radius: var(--paj-eng-radius) !important;
	padding: 0 64px 0 20px !important;
	font-size: 24px !important;
	font-family: inherit;
	color: var(--paj-eng-navy) !important;
	background: #fff !important;
	transition: border-color 150ms, background-color 300ms;
}

/* border-color must stay !important below: the base border above is
   !important, and an important shorthand beats a plain longhand even from a
   more specific rule. */
.paj-eng .paj-eng-input:hover {
	border-color: #94a3b8 !important;
}

/* Flashed for 800ms when the client removes a character the server would also
   have refused. The plugin's rule is reject loudly, never strip silently, and the
   visual flash plus the aria-live announcement together are the "loudly". */
.paj-eng .paj-eng-input.is-rejected {
	border-color: var(--paj-eng-orange) !important;
	background: #fde7df !important;
}

.paj-eng .paj-eng-counter {
	position: absolute;
	right: 16px;
	bottom: 10px;
	font-size: 13px;
	font-weight: 500;
	transition: color 150ms;

	/* The design uses #94A3B8, which is 2.8:1 on white and fails WCAG AA for
	   text. BFSG applies to this shop from June 2025, so: #64748B, 4.6:1. */
	color: var(--paj-eng-muted);
}

.paj-eng .paj-eng-counter.is-warn {
	color: var(--paj-eng-orange);
}

/* =====================================================================
 * Row labels ("Schriftart: Adobe Handwriting")
 * ================================================================== */

.paj-eng-rowlabel {
	font-size: 14px;
	font-weight: 500;
	margin: 0 0 8px !important;
}

.paj-eng-rowlabel__value {
	font-weight: 400;
	color: var(--paj-eng-muted);
}

/* "Farbe:" starts a new group and needs air above it — the reveal link's 8px
   bottom padding alone left the two rows visually welded (design review,
   2026-08-10). :not(:first-child) keeps "Schriftart:", the style block's first
   row, governed by the panel's own 16px gap instead of stacking onto it.
   !important because the base rule's margin shorthand above is !important. */
.paj-eng-style .paj-eng-rowlabel:not(:first-child) {
	margin-top: 12px !important;
}

/* =====================================================================
 * Font chips
 * ================================================================== */

.paj-eng-fontrow {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
	align-items: center;
}

/* display:contents keeps the radiogroup in the accessibility tree while
   removing its box, so the chips wrap as part of the parent row. */
.paj-eng-fonts {
	display: contents;
}

.paj-eng .paj-eng-chip {
	position: relative;
	width: 80px;
	height: 40px;
	border-radius: var(--paj-eng-radius-sm) !important;
	background: var(--paj-eng-plate);
	cursor: pointer;
	display: flex;
	align-items: center;
	justify-content: center;
	overflow: hidden;
	flex: none;
	transition: box-shadow 200ms, border-color 200ms;
	border: 1px solid var(--paj-eng-border);
}

/* Driving the visual off aria-checked rather than a class makes "looks selected
   but is not announced as selected" structurally impossible. */
.paj-eng .paj-eng-chip[aria-checked='true'] {
	border-color: transparent;
	box-shadow: 0 0 0 2px #fff, 0 0 0 4px var(--paj-eng-orange);
}

/*
 * Hardened like the name input (0.7.1 D2): on the draft review page a
 * site-wide rule flattened all twelve chips to one face at one size while
 * the inline --pf-* attributes and the data-caps transform kept working, so
 * whatever matched the span outranked a bare (0,1,0) class. Same cure as
 * the input: our tier plus !important on every typographic property. No
 * container/media block re-declares any of these, so the container-query
 * caveat further down does not apply here.
 */
.paj-eng .paj-eng-chip__preview {
	font-family: var(--pf-fam) !important;
	font-weight: var(--pf-weight) !important;

	/* == the design's 16 * f.scale * min(1, 7/nameLen) */
	font-size: calc(16px * var(--pf-scale, 1) * var(--paj-eng-fit, 1)) !important;
	color: var(--paj-eng-ink);
	text-shadow: 0 -1px 1px rgba(0, 0, 0, 0.6);
	white-space: nowrap;
	line-height: 1 !important;
	max-width: 100%;
	overflow: hidden;
}

/* Each chip previews its OWN face, so the caps rule keys off that chip's
   server-printed data-caps, NOT off --paj-eng-caps, which tracks the SELECTED
   face. Using the variable here would uppercase all twelve chips the moment a
   caps-only face was picked. */
.paj-eng .paj-eng-chip[data-caps='1'] .paj-eng-chip__preview {
	text-transform: uppercase !important;
}

/* =====================================================================
 * Colour swatches
 * ================================================================== */

.paj-eng-colourrow {
	display: flex;
	flex-wrap: wrap;
	gap: 10px;
	align-items: center;
}

.paj-eng-colours {
	display: contents;
}

.paj-eng .paj-eng-swatch {
	position: relative;
	width: 36px;
	height: 36px;
	border-radius: 50% !important;
	background: var(--pf-ink);
	cursor: pointer;
	display: flex;
	align-items: center;
	justify-content: center;
	flex: none;
	transition: box-shadow 200ms;

	/* The border is not decoration: the white swatch is invisible on a white
	   card without it. */
	border: 1px solid var(--paj-eng-border);
}

/* Navy ring here, orange on the font chips, deliberately different in the
   design, so the two rows never read as one selection. */
.paj-eng .paj-eng-swatch[aria-checked='true'] {
	box-shadow: 0 0 0 2px #fff, 0 0 0 4px var(--paj-eng-navy);
}

.paj-eng-swatch__tick {
	font-size: 15px;
	font-weight: 700;
	line-height: 1;
	color: var(--pf-tick);
	opacity: 0;
	transition: opacity 150ms;
}

/* Always in the DOM, opacity-toggled, so the design's conditional render costs
   zero JS, and a checkmark (not a border alone) always conveys selection. */
.paj-eng-swatch[aria-checked='true'] .paj-eng-swatch__tick {
	opacity: 1;
}

/* =====================================================================
 * Reveals
 * ================================================================== */

/* Default state shows everything. The collapsing class is what hides the
   overflow, so no-JS renders the complete set rather than a truncated one. */
.paj-eng-fonts.is-collapsed [data-extra='1'],
.paj-eng-colours.is-collapsed [data-extra='1'] {
	display: none;
}

.paj-eng .paj-eng-reveal {
	align-self: flex-start;
	background: none;
	border: none;
	padding: 8px 0;

	/* 4px + the 8px top padding = 12px of visual air under the chips. Was
	   -4px, which left ~4px: on the live render the link sat pressed against
	   the chip row (design review, 2026-08-10). */
	margin-top: 4px;
	color: var(--paj-eng-link);
	font-size: 14px;
	font-weight: 500;
	text-decoration: underline;
	cursor: pointer;
}

.paj-eng .paj-eng-reveal:hover {
	text-decoration: none;
}

.paj-eng .paj-eng-reveal-dot {
	width: 36px;
	height: 36px;
	border-radius: 50% !important;
	background: #fff;
	border: 1px dashed var(--paj-eng-disabled);
	color: var(--paj-eng-link);
	font-size: 18px;
	line-height: 1;
	cursor: pointer;
	display: flex;
	align-items: center;
	justify-content: center;
	flex: none;
	transition: background-color 200ms, border-color 200ms;
}

.paj-eng .paj-eng-reveal-dot:hover {
	background: var(--paj-eng-surface);
	border-color: var(--paj-eng-muted);
}

.paj-eng-capsnote {
	font-size: 13px;
	color: var(--paj-eng-muted);
	margin: -4px 0 0 !important;
}

/* =====================================================================
 * Summary
 * ================================================================== */

.paj-eng-summary {
	display: flex;
	align-items: center;
	gap: 10px;
	font-size: 14px;
	padding: 10px 12px;
	background: var(--paj-eng-surface);
	border-radius: var(--paj-eng-radius-sm);
	margin-top: 12px;
}

.paj-eng-summary__chip {
	width: 52px;
	height: 34px;
	background: var(--paj-eng-plate);
	border-radius: 5px;
	display: flex;
	align-items: center;
	justify-content: center;
	flex: none;
	overflow: hidden;
}

.paj-eng-summary__text-inner {
	font-family: var(--paj-eng-fam);
	font-weight: var(--paj-eng-weight);
	font-size: var(--paj-eng-summary-size);
	color: var(--paj-eng-ink);
	text-transform: var(--paj-eng-caps, none);
	text-shadow: 0 -1px 1px rgba(0, 0, 0, 0.6);
	white-space: nowrap;
	line-height: 1;
}

.paj-eng-summary__label {
	flex: 1 1 auto;
	min-width: 0;
	color: var(--paj-eng-navy);
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.paj-eng .paj-eng-link {
	background: none;
	border: 0;
	padding: 0;
	color: var(--paj-eng-link);
	font-size: 14px;
	font-weight: 500;
	text-decoration: underline;
	cursor: pointer;
	flex: none;
}

/* =====================================================================
 * Price
 * ================================================================== */

.paj-eng-price {
	margin-top: 12px;
}

/* Hardened tier + !important on the type: same page, same hostile cascade as
   the card sub — this block also wraps wc_price() output (0.8.1). */
.paj-eng .paj-eng-price__breakdown {
	font-size: 13px !important;
	color: var(--paj-eng-muted);
	margin: 0 0 2px !important;
}

.paj-eng .paj-eng-price__total {
	font-size: 15px !important;
	margin: 0 !important;
}

.paj-eng .paj-eng-price__label {
	font-weight: 500 !important;
	margin-right: 6px;
}

.paj-eng .paj-eng-price__amount {
	font-weight: 700 !important;
	font-size: 20px !important;
}

/* Germanized's suffix travels with the composed total, because PAngV requires the VAT
   statement wherever a price is advertised. */
.paj-eng .paj-eng-price__suffix {
	font-size: 12px !important;
	color: var(--paj-eng-muted);
	margin-left: 6px;
}

.paj-eng .paj-eng-price__suffix small {
	font-size: inherit !important;
}

/* Inherit-pinning for the wc_price() markup inside this block: the block's
   own spans state the design; the Woo spans must not restate it. `inherit`
   rather than a weight — the muted 13px breakdown line wraps wc_price() too
   and must not go bold. */
.paj-eng .paj-eng-price .woocommerce-Price-amount,
.paj-eng .paj-eng-price .woocommerce-Price-amount bdi,
.paj-eng .paj-eng-price .woocommerce-Price-currencySymbol {
	font-family: inherit !important;
	font-size: inherit !important;
	font-weight: inherit !important;
	color: inherit !important;
}

/* =====================================================================
 * Spelling confirmation
 * ================================================================== */

.paj-eng-spell {
	display: flex;
	gap: 10px;
	align-items: flex-start;
	font-size: 13px;
	color: var(--paj-eng-muted);
	cursor: pointer;
	margin-top: 12px;
	padding: 8px;
	border-radius: var(--paj-eng-radius-sm);
	transition: background-color 300ms;
}

/* An 18px checkbox's own focus ring is easy to miss, and this is a gate the
   customer has to find when the CTA sends them here. */
.paj-eng-spell:focus-within {
	outline: 2px solid var(--paj-eng-link);
	outline-offset: 2px;
}

.paj-eng-spell.is-flash {
	background: #fde7df;
}

.paj-eng-spell__box {
	width: 18px;
	height: 18px;
	margin: 1px 0 0;
	accent-color: var(--paj-eng-orange);
	flex: none;
}

.paj-eng-spell strong {
	color: var(--paj-eng-navy);
}

/* =====================================================================
 * Hint and notes
 * ================================================================== */

/* aria-hidden in the markup: the same message is announced through
   #paj-eng-live on a guarded click, and a screen reader must not hear it twice. */
.paj-eng-hint {
	margin: 8px 0 0 !important;
	font-size: 13px;
	color: var(--paj-eng-orange);
	font-weight: 500;
}

/* .paj-eng-notes removed 2026-08-06 with the two "Gravur:" lines it styled. See
   the note in class-shortcode.php where the markup was. */

/* Phase-1 style warning banners, kept for the flag-on-locally state. */
.paj-eng-warn {
	background: #fcf9e8;
	border-left: 4px solid #dba617;
	padding: 8px 12px;
	font-size: 13px;
	margin: 0 0 12px !important;
}

.paj-eng-warn--block {
	background: #fcf0f1;
	border-left-color: #b32d2e;
}

/* =====================================================================
 * Animations
 * ================================================================== */

/* Prefixed pajEng*, not the design's pf*, because keyframe names are global and
   two-letter prefixes on a page running Elementor + Flatsome are collision bait. */
@keyframes pajEngShimmer {
	0% {
		transform: translateX(-120%);
	}
	100% {
		transform: translateX(140%);
	}
}

@keyframes pajEngFadeUp {
	from {
		opacity: 0;
		transform: translateY(10px);
	}
	to {
		opacity: 1;
		transform: translateY(0);
	}
}

.paj-eng-fadeup {
	animation: pajEngFadeUp 300ms ease-out;
}

.paj-eng-card__glare,
.paj-eng-chip__glare {
	position: absolute;
	top: 0;
	bottom: 0;
	left: 0;
	width: 45%;
	pointer-events: none;
	background: linear-gradient(105deg, transparent, rgba(255, 255, 255, 0.3), transparent);
	opacity: 0;
}

/* One class on the container replays the sweep across every plate at once. The
   per-element alternative costs twelve synchronous layout flushes per change.
   opacity:1 is load-bearing: the base rule parks the glare at opacity 0 and the
   keyframes animate transform only, so without it the sweep plays invisibly —
   which is exactly what shipped until 0.4.0. */
.paj-eng.is-shimmer .paj-eng-card__glare,
.paj-eng.is-shimmer .paj-eng-chip__glare {
	opacity: 1;
	animation: pajEngShimmer 400ms ease-out both;
}

@media (prefers-reduced-motion: reduce) {
	.paj-eng *,
	.paj-eng *::before,
	.paj-eng *::after {
		animation: none !important;
		transition: none !important;
	}
	.paj-eng-panel {
		transition: none !important;
	}
}

/* =====================================================================
 * Narrow viewports
 * ================================================================== */

@container (max-width: 420px) {
	/* The base .paj-eng .paj-eng-input declarations are !important (Elementor
	   kit, see the name-field block), and an important declaration outranks a
	   plain one regardless of the media/container context — so these narrow
	   overrides must be !important too, or the 64px base silently wins. */
	.paj-eng .paj-eng-input {
		height: 56px !important;
		font-size: 20px !important;
		padding-right: 56px !important;
	}
	.paj-eng .paj-eng-card {
		padding: 13px 9px 11px;
	}
	.paj-eng .paj-eng-card[aria-checked='true'] {
		padding: 12px 8px 10px;
	}
	/* 0.8.1: the engrave text is cqw-sized against the plate, so 76px scales
	   it automatically — no font override needed here. */
	.paj-eng-card__plate {
		width: 76px;
	}
}

/* Fallback for browsers without container queries. The gallery snippet's own
   breakpoint is 600px, so matching it keeps the two in step. */
@supports not (container-type: inline-size) {
	/* The card engrave sizes itself in cqw, which never resolves here. The
	   two-declaration fallback idiom does NOT work for it: cqw sits inside
	   calc() with var(), which parses fine and only fails at computed-value
	   time, collapsing font-size to inherit instead of falling back. So the
	   JS-measured pixel size (fitPlate, boxW=50) stays the fallback path —
	   at every width, hence outside the @media. !important because the base
	   declaration is. */
	.paj-eng .paj-eng-card__engrave {
		font-size: var(--paj-eng-plate-size) !important;
	}

	@media (max-width: 600px) {
		/* !important for the same reason as the container-query block above. */
		.paj-eng .paj-eng-input {
			height: 56px !important;
			font-size: 20px !important;
			padding-right: 56px !important;
		}
		.paj-eng-card__plate {
			width: 76px;
		}
	}
}

/* =====================================================================
 * Nodes OUTSIDE our root
 *
 * These classes are ours; the elements are Elementor's, Flatsome's or
 * WooCommerce's. Kept unscoped by necessity and deliberately minimal: every
 * rule here is one we could be asked to justify.
 * ================================================================== */

/* The guarded add-to-cart button. aria-disabled, never the disabled attribute:
   a disabled button is skipped by screen readers and offers no reason.
   !important on every declaration: this class sits on a THEME button, where
   Flatsome/Elementor kit rules outrank a bare (0,1,0) selector (0.8.1's
   hostile-CSS lesson; the .paj-eng ancestor tier is unavailable out here).
   Deliberately NO pointer-events:none — the click must reach the guard so it
   can route the customer to the missing field. And no [aria-disabled]
   selector: Flatsome deletes that attribute at will (D13); the class is the
   one bit gate and visuals share. */
.paj-eng-guarded {
	opacity: 0.55 !important;
	cursor: not-allowed !important;
	filter: grayscale(0.35) !important;
}

/* Used to hide a foreign price node while our composed total sits beside it —
   in the sticky bars and, since 0.7.0, in the main Elementor price widget. We
   never rewrite their text, so restoring is removing one class. */
.paj-eng-hidden {
	display: none !important;
}

.paj-eng-sticky-total {
	font-weight: 700;
}

/* ---------------------------------------------------------------------
 * The live engraving on the gallery plate slide
 *
 * Built by the plateSlide integration in engraving.js, which wraps the last
 * slide's <img> rather than positioning against .paj-main-slide. Read that
 * function's docblock before changing anything here: several of these
 * declarations are load-bearing against the gallery's own CSS and against its
 * drag and lightbox handlers.
 * ------------------------------------------------------------------ */

.paj-eng-plate {
	position: relative;
	display: block;

	/*
	 * The wrapper's box must EQUAL the image's box, or the percentage window
	 * geometry below lands in the wrong place. aspect-ratio matches housing.png's
	 * intrinsic 293x256 and the img rule underneath fills it exactly, so there is
	 * no letterboxing to compensate for.
	 */
	aspect-ratio: 293 / 256;

	/*
	 * 78%, not 100%. The slide is a 1:1 box up to ~600px wide while the render is
	 * only 293px, so filling the slide upscales it past 2x and the housing goes
	 * soft. Capping it reads as a deliberately centred product render instead.
	 */
	width: min(100%, 78%);

	/* cqw units in .paj-eng-plate__mark resolve against this. */
	container-type: inline-size;

	/*
	 * 🔴 Both required. .paj-main-slider carries mousedown/touchstart drag
	 * handlers and every .paj-main-slide has a click listener calling
	 * openLightbox(i). Without these a mouse drag selects the engraved text
	 * instead of swiping the gallery, and the mobile lightbox tap mis-fires.
	 * .paj-main-slide img already sets pointer-events:none for the same reason.
	 */
	pointer-events: none;
	user-select: none;
	-webkit-user-select: none;
}

/*
 * !important is unavoidable here, not laziness. The gallery snippet styles its
 * images as `#pajGallery_<id> .paj-main-slide img`, which carries an ID and
 * therefore outranks any class selector we can write, and it sets
 * height:100% + object-fit:contain. Those two are exactly what would letterbox
 * the render inside its element and invalidate the window percentages.
 */
.paj-eng-plate img {
	width: 100% !important;
	height: 100% !important;
	object-fit: fill !important;
	display: block;
}

/*
 * The engraving window, same 21%/33% offset and 52%x26% size as
 * .paj-eng-card__engrave, over the same image. Kept identical on purpose: one
 * calibration for both surfaces means the card and the product image cannot
 * disagree about where the mark sits.
 */
.paj-eng-plate__mark {
	position: absolute;
	left: 21%;
	top: 33%;
	width: 52%;
	height: 26%;
	display: flex;
	align-items: center;
	justify-content: center;
	white-space: nowrap;
	overflow: hidden;
	line-height: 1;

	font-family: var(--paj-eng-fam);
	font-weight: var(--paj-eng-weight);
	color: var(--paj-eng-ink);
	text-transform: var(--paj-eng-caps, none);
	text-shadow: 0 1px 0 rgba(0, 0, 0, 0.55), 0 -1px 0 rgba(255, 255, 255, 0.1);

	/*
	 * fitPlate() from engraving.js, transcribed into CSS:
	 *   min( boxW / (len * 0.52), boxW * 0.3 ) * scale
	 * with boxW expressed as 52cqw, i.e. this box's own width. Doing it this way
	 * rather than publishing a fourth px variable is what removes the need to
	 * measure the slide and to re-measure it on every resize, orientation change
	 * and thumbnail-rail reflow.
	 */
	font-size: calc(
		min(52cqw / (var(--paj-eng-len, 5) * 0.52), 52cqw * 0.3) * var(--paj-eng-scale, 1)
	);
}

/*
 * content, not textContent. paintVars already publishes --paj-eng-name as a
 * JSON-quoted string, which is precisely the form CSS content requires, so the
 * name reaches a surface outside our root with no JS text handling at all.
 */
.paj-eng-plate[data-paj-eng-plate-state='named'] .paj-eng-plate__mark::before {
	content: var(--paj-eng-name, '');
}

/* Standard variant: the wordmark, matching the one printed on the standard
   card's hardware render (the CSS overlay it once mirrored was retired in
   0.8.1). Hardcoded rather than read from strings-de.json because CSS cannot
   read config, and because it is a wordmark rather than a translatable
   phrase. */
.paj-eng-plate[data-paj-eng-plate-state='standard'] .paj-eng-plate__mark {
	color: #c9ccd2;
	font-family: 'Roboto', -apple-system, BlinkMacSystemFont, 'Segoe UI', Arial, sans-serif;
	font-weight: 700;
	font-size: 5cqw;
	letter-spacing: 0.1em;
	text-transform: none;
}

.paj-eng-plate[data-paj-eng-plate-state='standard'] .paj-eng-plate__mark::before {
	content: 'PAJ GPS';
}

/* No container-query support: fall back to a fraction of the viewport, which is
   wrong in detail but keeps a readable mark rather than an unstyled one. */
@supports not (container-type: inline-size) {
	.paj-eng-plate {
		container-type: normal;
	}
	.paj-eng-plate__mark {
		font-size: calc(min(4vw, 28px) * var(--paj-eng-scale, 1));
	}
	.paj-eng-plate[data-paj-eng-plate-state='standard'] .paj-eng-plate__mark {
		font-size: min(1.4vw, 11px);
	}
}

/* ---------------------------------------------------------------------
 * The live engraving on the gallery PHOTO slide (the hero dog photo)
 *
 * Built by the photoSlide integration in engraving.js — the primary preview
 * surface; the plate block above is its fallback. Same load-bearing rules as
 * .paj-eng-plate: read the plateSlide docblock before changing anything.
 *
 * PAINT ONLY. The plaque's position, size and rotation arrive as inline styles
 * from products.json gallery_photo.engrave, so recalibrating against a new
 * photo is a JSON edit, never an edit here.
 *
 * One fact the plate comments predate: the snippet's own late CSS block (the
 * [data-peek] rules, outside its media query) gives EVERY .paj-main-slide
 * position:relative and its img position:absolute + object-fit:contain at
 * every viewport. Wrapping the img re-anchors that absolute positioning to
 * this wrapper — exactly what we want — but it is also why the wrapper must
 * fill the slide's square box itself rather than trusting the img to size it.
 * ------------------------------------------------------------------ */

.paj-eng-photo {
	position: relative;
	display: block;

	/*
	 * The photo is 1:1 and the slide box is 1:1, so unlike the plate's 78% cap
	 * the wrapper fills the slide edge to edge: the render is 1024px, never
	 * upscaled past ~2x. aspect-ratio (not height:100%) keeps the wrapper's box
	 * EQUAL to the image's box even if the slide box ever stops being square —
	 * the percentage window survives; the slide's overflow:hidden does the rest.
	 */
	width: 100%;
	aspect-ratio: 1 / 1;

	/* cqw units below resolve against this. */
	container-type: inline-size;

	/* Clip the plaque and pill exactly like the snippet clips its img. */
	border-radius: 16px;
	overflow: hidden;

	/* 🔴 Both required — same drag/lightbox hazards as .paj-eng-plate. */
	pointer-events: none;
	user-select: none;
	-webkit-user-select: none;
}

/*
 * Same specificity fight as the plate: the snippet styles its images as
 * `#pajGallery_<id> … img`, which outranks any class selector we can write.
 * cover, not fill: 1:1 into 1:1 they are identical, but if the wrapper is ever
 * not square, cover keeps the photo undistorted while the window percentages
 * stay anchored to the wrapper.
 */
.paj-eng-photo img {
	width: 100% !important;
	height: 100% !important;
	object-fit: cover !important;
	display: block;
}

/*
 * The synthetic plaque, transcribed from the design (PET Finder Hero.dc.html).
 * Deliberately OPAQUE: the photo has 'Bailey' rendered into the tracker
 * pixels, and this gradient is what keeps it from bleeding through the
 * customer's name. Geometry (left/top/width/height/transform) is inline.
 */
.paj-eng-photo__mark {
	position: absolute;
	display: flex;
	align-items: center;
	justify-content: center;
	white-space: nowrap;
	overflow: hidden;
	line-height: 1;

	background: radial-gradient(ellipse at 50% 40%, #26272c 0%, #1a1b1f 55%, #111216 100%);
	border-radius: 1.2cqw;
	box-shadow: inset 0 0.3cqw 0.8cqw rgba(0, 0, 0, 0.7),
		inset 0 -0.2cqw 0.5cqw rgba(255, 255, 255, 0.05);
}

/* Named variant: the design's text treatment on the plaque. */
.paj-eng-photo[data-paj-eng-photo-state='named'] .paj-eng-photo__mark {
	font-family: var(--paj-eng-fam);
	font-weight: var(--paj-eng-weight);
	color: var(--paj-eng-ink);
	text-transform: var(--paj-eng-caps, none);
	letter-spacing: 0.02em;

	/*
	 * Two declarations on purpose. Browsers without color-mix() drop the second
	 * at parse time and keep the first — the design minus its ink highlight,
	 * rather than no shadow at all.
	 */
	text-shadow: 0 -1px 1px rgba(0, 0, 0, 0.65);
	text-shadow: 0 -1px 1px rgba(0, 0, 0, 0.65),
		0 1px 1px color-mix(in srgb, var(--paj-eng-ink-hi) 25%, transparent);

	/*
	 * The design's formula verbatim: 3.4cqw × font scale × min(1, 6.5/len).
	 * With the current 5-character cap the fit term is always 1; it stays so a
	 * raised cap shrinks long names instead of overflowing the plaque.
	 */
	font-size: calc(3.4cqw * var(--paj-eng-scale, 1) * min(1, 6.5 / var(--paj-eng-len, 5)));
}

.paj-eng-photo[data-paj-eng-photo-state='named'] .paj-eng-photo__mark::before {
	content: var(--paj-eng-name, '');
}

/* Standard variant: the wordmark, same convention as the plate and the
   standard card. Hardcoded for the same reasons documented on the plate. */
.paj-eng-photo[data-paj-eng-photo-state='standard'] .paj-eng-photo__mark {
	color: #c9ccd2;
	font-family: 'Roboto', -apple-system, BlinkMacSystemFont, 'Segoe UI', Arial, sans-serif;
	font-weight: 700;
	font-size: 2.1cqw;
	letter-spacing: 0.12em;
	text-transform: none;
	text-shadow: 0 -1px 1px rgba(0, 0, 0, 0.65);
}

.paj-eng-photo[data-paj-eng-photo-state='standard'] .paj-eng-photo__mark::before {
	content: 'PAJ GPS';
}

/*
 * The sweep. Unlike the card glare this rests OFFSCREEN (translateX(-120%),
 * clipped by the plaque's overflow:hidden), so with the animation removed —
 * reduced motion, old browser — it is simply never seen; no opacity gate to
 * forget. engraving.js remounts the span on font/colour change, which restarts
 * the animation without any reflow juggling. Keyframes: pajEngShimmer above.
 */
.paj-eng-photo__shimmer {
	position: absolute;
	top: 0;
	bottom: 0;
	left: 0;
	width: 45%;
	background: linear-gradient(105deg, transparent, rgba(255, 255, 255, 0.3), transparent);
	transform: translateX(-120%);
	animation: pajEngShimmer 400ms ease-out both;
}

/* 'Live-Vorschau'. JS gates the attribute on the REAL typed name — the
   'Buddy' placeholder must never be advertised as a live preview. */
.paj-eng-photo__pill {
	display: none;
	position: absolute;
	top: 12px;
	left: 12px;
	background: rgba(255, 255, 255, 0.92);
	border-radius: 999px;
	padding: 6px 14px;
	font-family: 'Roboto', -apple-system, BlinkMacSystemFont, 'Segoe UI', Arial, sans-serif;
	font-size: 13px;
	font-weight: 500;
	color: #1a2839;
}

.paj-eng-photo[data-paj-eng-pill='1'] .paj-eng-photo__pill {
	display: block;
}

/* No container-query support: viewport approximations, degraded but legible,
   same trade as the plate fallback above. */
@supports not (container-type: inline-size) {
	.paj-eng-photo {
		container-type: normal;
	}
	.paj-eng-photo__mark {
		border-radius: 7px;
		box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.7),
			inset 0 -1px 3px rgba(255, 255, 255, 0.05);
	}
	.paj-eng-photo[data-paj-eng-photo-state='named'] .paj-eng-photo__mark {
		font-size: calc(min(3.4vw, 21px) * var(--paj-eng-scale, 1) * min(1, 6.5 / var(--paj-eng-len, 5)));
	}
	.paj-eng-photo[data-paj-eng-photo-state='standard'] .paj-eng-photo__mark {
		font-size: min(2.1vw, 13px);
	}
}

/* The root-scoped reduced-motion block above (.paj-eng *) cannot reach this
   wrapper — it lives in the gallery, outside the shortcode root. */
@media (prefers-reduced-motion: reduce) {
	.paj-eng-photo__shimmer {
		animation: none !important;
	}
}

/* .paj-eng-price-inject removed 2026-08-10 with the injected breakdown line:
   the main price now swaps to the composed total instead (see the price
   integration in engraving.js). .paj-eng-price-total needs no rule — it sits
   inside the widget's p.price and inherits its styling.

   .paj-eng-chip-swapped removed 2026-08-06 with the deliveryChip integration
   that applied it. See the note where that integration was, in engraving.js.
   .paj-eng-chip-warn below stays: returnsChip still uses it, and is still
   gated on legal sign-off rather than removed. */

.elementor-icon-list-item.paj-eng-chip-warn {
	background: #fdebd2 !important;
}

.elementor-icon-list-item.paj-eng-chip-warn .elementor-icon-list-text {
	color: #8a4a12 !important;
}
