/* FabQuote storefront styles.
 *
 * Written to sit inside somebody else's theme, so: everything is scoped under
 * .fq-app, colours come from custom properties a theme can override, and no
 * element selector reaches outside the widget. Buttons keep the theme's own
 * .button class where WooCommerce would style them.
 */

/* The widget sizes its own boxes. Most themes set border-box globally and the
   widget quietly rode on that -- which is exactly the kind of borrowing that
   breaks on the one storefront that does not. A field is width:100% inside a
   flex column, so under content-box its padding and border are added on top
   and every input overhangs its card by 18px. Scoped to the widget, so a theme
   outside it is unaffected. */
.fq-app,
.fq-app *,
.fq-app *::before,
.fq-app *::after {
	box-sizing: border-box;
}

.fq-app {
	--fq-ink: #111827;
	--fq-muted: #6b7280;
	--fq-line: #e5e7eb;
	--fq-bg: #ffffff;
	--fq-soft: #f9fafb;
	--fq-accent: #1f6feb;
	--fq-warn: #b45309;
	--fq-warn-bg: #fffbeb;
	--fq-danger: #b91c1c;
	--fq-radius: 10px;

	color: var(--fq-ink);
	max-width: 900px;
	margin: 0 auto;
}

.fq-intro {
	color: var(--fq-muted);
	margin-bottom: 1rem;
}

/* ------------------------------------------------------------- drop zone */

.fq-drop {
	border: 2px dashed var(--fq-line);
	border-radius: var(--fq-radius);
	background: var(--fq-soft);
	padding: 2.25rem 1rem;
	text-align: center;
	cursor: pointer;
	transition: border-color .15s ease, background .15s ease;
}

.fq-drop:hover,
.fq-drop:focus-visible {
	border-color: var(--fq-accent);
	background: var(--fq-bg);
	outline: none;
}

.fq-drop--over {
	border-color: var(--fq-accent);
	background: #eff6ff;
}

.fq-drop svg {
	color: var(--fq-muted);
}

.fq-drop-main {
	font-size: 1.05rem;
	font-weight: 600;
	margin: .5rem 0 .25rem;
}

.fq-drop-sub {
	color: var(--fq-muted);
	font-size: .875rem;
	margin: 0;
}

.fq-link {
	background: none;
	border: 0;
	padding: 0;
	color: var(--fq-accent);
	text-decoration: underline;
	cursor: pointer;
	font: inherit;
}

/* ------------------------------------------------------- the other page */

/* Each page points at the other, once, in a line. Not a control: the reason
   the tabs went is that each page does one thing, and a second button at the
   top would be the tab wearing a different hat. */
.fq-crosslink {
	color: var(--fq-muted);
	font-size: .9rem;
	margin: 1rem 0 0;
}

.fq-crosslink a {
	color: var(--fq-accent);
}

/* ---------------------------------------------------------------- builder */

.fq-build-intro {
	color: var(--fq-muted);
	font-size: .9rem;
	margin: 0 0 .9rem;
}

.fq-build {
	border: 1px solid var(--fq-line);
	border-radius: var(--fq-radius);
	background: var(--fq-soft);
	padding: 1rem;
}

/* Every problem the server found, listed. Amber like the DFM notes rather
   than red: nothing has failed, the part is just not buildable yet. */
.fq-build-problems {
	background: var(--fq-warn-bg);
	border: 1px solid #fde68a;
	border-radius: 8px;
	padding: .6rem .8rem;
	margin-bottom: .85rem;
	font-size: .85rem;
	color: var(--fq-warn);
}

.fq-build-problems ul {
	margin: 0;
	padding-left: 1.1rem;
}

/* One block per hole pattern. Bordered so that with several stacked the eye
   does not lose which field belongs to which pattern -- at 360px they are a
   tall column and that is the only thing keeping them apart. */
.fq-feature {
	border: 1px solid var(--fq-line);
	border-radius: 8px;
	background: var(--fq-bg);
	padding: .6rem .7rem .1rem;
	margin-bottom: .6rem;
}

.fq-feature-head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	margin-bottom: .4rem;
}

.fq-feature-n {
	color: var(--fq-muted);
	font-size: .75rem;
	text-transform: uppercase;
	letter-spacing: .04em;
}

.fq-feature-drop,
.fq-feature-add {
	appearance: none;
	background: none;
	border: 0;
	font: inherit;
	color: var(--fq-accent);
	cursor: pointer;
	padding: .35rem .2rem;
	min-height: 32px;
}

.fq-feature-drop {
	color: var(--fq-muted);
	font-size: .8rem;
}

.fq-feature-drop:hover {
	color: var(--fq-ink);
	text-decoration: underline;
}

.fq-feature-add {
	font-size: .9rem;
	font-weight: 600;
	min-height: 44px;
	margin-bottom: .5rem;
}

.fq-feature-add[disabled] {
	color: var(--fq-muted);
	cursor: not-allowed;
}

/* The live preview. Fixed height rather than an aspect ratio: the shape
   changes as the form is filled in, and a panel that resizes with it would
   walk the button up and down the page under somebody's finger. */
.fq-build-preview {
	height: 240px;
	margin-bottom: .85rem;
	padding: .5rem;
	border: 1px solid var(--fq-line);
	border-radius: 8px;
	background: var(--fq-bg);
	display: flex;
	align-items: center;
	justify-content: center;
}

/* Not the current spec: the form has something wrong with it and this is the
   last shape that built. Faded so it cannot be mistaken for what is being
   typed now. */
.fq-build-preview--stale {
	opacity: .3;
}

.fq-build-preview svg {
	max-width: 100%;
	max-height: 100%;
	height: auto;
}

/* 1.6 units in a 480 box is a hairline once the drawing is scaled to fit */
.fq-build-preview svg path,
.fq-build-preview svg line,
.fq-build-preview svg circle {
	vector-effect: non-scaling-stroke;
}

.fq-build-foot {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: .5rem .9rem;
}

.fq-build-summary {
	color: var(--fq-muted);
	font-size: .85rem;
}

/* Palette only, and no .button class, so a theme cannot repaint it. The
   disabled state carries real information here -- it is how the shopper knows
   the part is not buildable yet -- and a theme brand colour applied to every
   .button would flatten it away. */
.fq-build-go {
	appearance: none;
	border: 1px solid transparent;
	border-radius: 8px;
	background: var(--fq-accent);
	color: #fff;
	font: inherit;
	font-size: .95rem;
	font-weight: 600;
	padding: .6rem 1.1rem;
	min-height: 44px;
	cursor: pointer;
}

.fq-build-go:hover:not([disabled]) {
	filter: brightness(1.08);
}

.fq-build-go:focus-visible {
	outline: 2px solid var(--fq-ink);
	outline-offset: 2px;
}

.fq-build-go[disabled] {
	background: var(--fq-line);
	color: var(--fq-muted);
	cursor: not-allowed;
}

/* ----------------------------------------------------------------- alerts */

.fq-alert {
	margin: 1rem 0;
	padding: .75rem 1rem;
	border-radius: var(--fq-radius);
	border: 1px solid var(--fq-line);
	font-size: .925rem;
}

.fq-alert--error {
	border-color: #fecaca;
	background: #fef2f2;
	color: var(--fq-danger);
}

.fq-alert--notice {
	border-color: #bfdbfe;
	background: #eff6ff;
}

/* ------------------------------------------------------------------ cards */

.fq-parts {
	display: grid;
	/* minmax(0,1fr), not the default auto track: an auto track sizes to the
	   widest card's max-content, and one long part — a 1500 x 150 angle — then
	   drags every card wider than the page. */
	grid-template-columns: minmax(0, 1fr);
	gap: 1rem;
	margin-top: 1.25rem;
}

.fq-card {
	display: block;
	border: 1px solid var(--fq-line);
	border-radius: var(--fq-radius);
	background: var(--fq-bg);
	padding: 1rem;
}

.fq-card--in-cart {
	border-color: var(--fq-accent);
}

.fq-card--failed {
	border-color: #fecaca;
	background: #fef2f2;
}

/* ---------------------------------------------------------- part preview */

/* Full width of the card, height from the part's own proportions, clamped at
   both ends.

   --fq-pv-ar is the blank's width/height, set on the element when the card is
   built — which is before the drawing is fetched, so the panel is already the
   right size when the part lands in it and nothing below moves.

   The 400px ceiling is what stops a 566mm-tall part from pushing the material
   and quantity controls off the screen, and a 3 metre part from producing an
   absurd panel; 170px is the floor that keeps a 1500 x 150 angle from being a
   letterbox. Between them the panel simply fits the part. */
.fq-preview {
	box-sizing: border-box;   /* explicit: the theme's box model is not ours */
	/* width first, then the ratio gives the height. Without the explicit 100%
	   the ratio is free to solve the other way and set the width instead. */
	width: 100%;
	aspect-ratio: var(--fq-pv-ar, 21 / 10);
	max-height: 400px;
	min-height: 170px;
	margin: 0 0 .85rem;
	padding: 14px;
	border: 1px solid var(--fq-line);
	border-radius: 8px;
	background: var(--fq-soft);
	display: flex;
	align-items: center;
	justify-content: center;
	overflow: hidden;
}

.fq-preview svg,
.fq-preview img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: contain;      /* the <img> fallback; the inline SVG fits itself */
}

/* Strokes in screen pixels, not user units. The viewBox is cropped to each
   part, so its scale changes from part to part — an absolute width that reads
   on a wide plate is a hairline on a tall sliver. */
.fq-preview svg .fq-pv-outline,
.fq-preview svg .fq-pv-hole,
.fq-preview svg .fq-pv-bend {
	vector-effect: non-scaling-stroke;
}

.fq-preview svg .fq-pv-outline { stroke-width: 1.8px; }
.fq-preview svg .fq-pv-hole    { stroke-width: 1.3px; }

.fq-preview svg .fq-pv-bend {
	stroke-width: 1.6px;
	stroke-dasharray: 8 5;
}

.fq-preview--loading {
	background: linear-gradient(90deg, #f3f4f6 25%, #e9ebef 37%, #f3f4f6 63%);
	background-size: 400% 100%;
	animation: fq-shimmer 1.2s ease-in-out infinite;
}

/* Nothing to draw: drop to the floor height. Holding 400px of empty panel open
   for a sentence reads as broken rather than as reserved space. */
.fq-preview--none {
	aspect-ratio: auto;
	color: var(--fq-muted);
	font-size: .85rem;
}

@keyframes fq-shimmer {
	0%   { background-position: 100% 50%; }
	100% { background-position: 0 50%; }
}

@media (prefers-reduced-motion: reduce) {
	.fq-preview--loading { animation: none; }
}

.fq-card-body {
	min-width: 0;
}

.fq-card-title {
	margin: 0 0 .25rem;
	font-size: 1.05rem;
	line-height: 1.3;
	word-break: break-word;
}

.fq-card-facts,
.fq-card-status {
	color: var(--fq-muted);
	font-size: .875rem;
	margin: 0 0 .75rem;
}

/* ---------------------------------------------------------------------------
   The size confirmation.

   Laid out so the SIZE is what the eye lands on and the tick is the small
   thing beside it. That is the point of the block: it is the only check on
   this page with no size ceiling, so it has to survive being seen a hundred
   times, and a control that reads as one more box to clear will not. The
   number is 1.25rem in full ink; the sentence around it and the note under it
   are muted and small.

   Soft ground and a left rule rather than the warning yellow -- nothing is
   wrong yet, we are asking a question. Yellow here would cry wolf on every
   upload and take the warning colour down with it.
   --------------------------------------------------------------------------- */
.fq-sizecheck {
	border-left: 3px solid var(--fq-accent);
	background: var(--fq-soft);
	border-radius: var(--fq-radius);
	padding: .6rem .75rem;
	margin: 0 0 .75rem;
}

.fq-sizecheck-head {
	font-size: .75rem;
	font-weight: 600;
	letter-spacing: .04em;
	text-transform: uppercase;
	color: var(--fq-muted);
	margin-bottom: .35rem;
}

.fq-sizecheck-row {
	display: flex;
	align-items: baseline;
	gap: .5rem;
	cursor: pointer;
	color: var(--fq-muted);
	font-size: .875rem;
	line-height: 1.35;
}

/* the checkbox keeps its own size at the top of a wrapping line */
.fq-sizecheck-row input[type="checkbox"] {
	flex: 0 0 auto;
	width: 1.05rem;
	height: 1.05rem;
	margin: 0;
	align-self: flex-start;
	position: relative;
	top: .18rem;
	accent-color: var(--fq-accent);
}

.fq-sizecheck-value {
	font-size: 1.25rem;
	font-weight: 700;
	color: var(--fq-ink);
	white-space: nowrap;
}

.fq-sizecheck-why {
	color: var(--fq-muted);
	font-size: .8125rem;
	margin: .4rem 0 0;
	padding-left: 1.55rem;      /* clears the checkbox, aligns under the text */
}

@media (max-width: 380px) {
	.fq-sizecheck-value {
		font-size: 1.125rem;
		white-space: normal;
	}
}

.fq-card-error {
	color: var(--fq-danger);
	font-size: .9rem;
	margin: .25rem 0 .5rem;
}

/* DFM notes: worth reading, not alarming — amber, never red. */
.fq-notes {
	background: var(--fq-warn-bg);
	border: 1px solid #fde68a;
	border-radius: 8px;
	padding: .6rem .8rem;
	margin-bottom: .85rem;
	font-size: .85rem;
	color: var(--fq-warn);
}

.fq-notes ul {
	margin: .35rem 0 0;
	padding-left: 1.1rem;
}

/* Bend directions: a question about making the part right, asked once, on a
   flat drawing that never said. Same amber family as the DFM notes -- worth
   answering, not alarming -- and the whole block quietens once saved. */
.fq-dirs {
	background: var(--fq-warn-bg);
	border: 1px solid #fde68a;
	border-radius: 8px;
	padding: .6rem .8rem;
	margin-bottom: .85rem;
	font-size: .85rem;
}

.fq-dirs--done { opacity: .7; }

.fq-dirs-why { margin: .25rem 0 .5rem; color: var(--fq-warn); }

.fq-dirs-row {
	display: flex;
	align-items: center;
	gap: .4rem;
	padding: .15rem 0;
}

.fq-dirs-label { flex: 1; font-variant-numeric: tabular-nums; }

.fq-dir,
.fq-dir-all {
	font: inherit;
	font-size: .8rem;
	padding: .2rem .7rem;
	border: 1px solid #d1d5db;
	border-radius: 999px;
	background: #fff;
	cursor: pointer;
}

.fq-dir--on {
	background: #2563eb;
	border-color: #2563eb;
	color: #fff;
	font-weight: 600;
}

.fq-dirs-quick { margin: .4rem 0; display: flex; gap: .4rem; }

.fq-dirs-foot { display: flex; align-items: center; gap: .6rem; margin-top: .4rem; }

.fq-dirs-save {
	font: inherit;
	font-size: .85rem;
	padding: .35rem .9rem;
	border-radius: 6px;
	border: 1px solid #2563eb;
	background: #2563eb;
	color: #fff;
	cursor: pointer;
}

.fq-dirs-save:disabled { opacity: .5; cursor: default; }

.fq-dirs-msg { color: var(--fq-warn); }

/* the row under the mouse lights its line in the preview */
.fq-preview svg .fq-pv-bend--hi { stroke: #dc2626; stroke-width: 2.6px; }

/* ---------------------------------------------------------------- options */

.fq-options {
	display: flex;
	flex-wrap: wrap;
	gap: .75rem;
	margin-bottom: .85rem;
}

.fq-field {
	display: flex;
	flex-direction: column;
	gap: .2rem;
	font-size: .8rem;
	min-width: 9rem;
}

.fq-field--qty {
	min-width: 6rem;
	max-width: 7rem;
}

.fq-field-label {
	color: var(--fq-muted);
	text-transform: uppercase;
	letter-spacing: .04em;
	font-size: .7rem;
}

.fq-field select,
.fq-field input {
	padding: .45rem .5rem;
	border: 1px solid var(--fq-line);
	border-radius: 7px;
	background: var(--fq-bg);
	font: inherit;
	font-size: .9rem;
	color: inherit;
	width: 100%;
}

/* Finishing, hardware, lead time. Kept visually quieter than material and
   quantity: most orders take the defaults, and the pickers should not shout at
   the people who do. */
.fq-extras {
	display: flex;
	flex-direction: column;
	gap: .5rem;
	margin-bottom: .85rem;
	padding: .6rem .7rem;
	border: 1px solid var(--fq-line);
	border-radius: 8px;
	background: var(--fq-soft);
}

.fq-extra {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: .5rem .9rem;
}

.fq-check,
.fq-hw {
	display: inline-flex;
	align-items: center;
	gap: .35rem;
	font-size: .875rem;
	cursor: pointer;
}

.fq-hw input {
	width: 3.6rem;
	padding: .2rem .35rem;
	border: 1px solid var(--fq-line);
	border-radius: 6px;
	background: var(--fq-bg);
	font: inherit;
	font-size: .85rem;
	color: inherit;
}

.fq-hint {
	color: var(--fq-muted);
	font-size: .75rem;
}

/* ----------------------------------------------------------------- price */

.fq-price-row {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: .35rem 1rem;
	margin-bottom: .6rem;
	min-height: 1.6rem;
}

.fq-price strong {
	font-size: 1.35rem;
	letter-spacing: -.01em;
}

.fq-price-each,
.fq-price-total,
.fq-lead {
	color: var(--fq-muted);
	font-size: .875rem;
}

/* Quantity breaks read as an offer, not a table. */
.fq-breaks {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: .4rem;
	margin-bottom: .85rem;
}

.fq-breaks-label {
	color: var(--fq-muted);
	font-size: .8rem;
	margin-right: .15rem;
}

.fq-break {
	display: inline-flex;
	flex-direction: column;
	align-items: center;
	gap: .1rem;
	border: 1px solid var(--fq-line);
	border-radius: 8px;
	background: var(--fq-bg);
	padding: .3rem .6rem;
	cursor: pointer;
	font: inherit;
	line-height: 1.15;
	/* Buttons do not inherit text colour: without this, anything uncoloured
	   inside the chip renders in the THEME's button colour — black qty labels
	   on a dark theme (Aug 26). Every text element in the widget must get its
	   colour from the widget's own palette, never the theme's defaults. */
	color: var(--fq-ink);
}

.fq-break:hover {
	border-color: var(--fq-accent);
}

/* Ink on top, muted below: the same big/small treatment as the price line
   ("R1 726.46" in ink, "each" in muted) — the qty is the chip's emphasis. */
.fq-break-qty {
	font-weight: 600;
	font-size: .85rem;
	color: var(--fq-ink);
}

.fq-break-price {
	color: var(--fq-muted);
	font-size: .75rem;
}

/* Hand-quote enquiry: the form that replaces the dead-end message when the
   server refuses to price a part. Every text element takes its colour from
   the widget's palette — never the theme's defaults (the Aug-26 rule). */
.fq-enquiry {
	display: flex;
	flex-direction: column;
	gap: .45rem;
	width: 100%;
	max-width: 34rem;
	margin-top: .35rem;
	padding: .7rem .8rem;
	border: 1px solid var(--fq-line);
	border-radius: 8px;
	background: var(--fq-soft);
	color: var(--fq-ink);
}

.fq-enquiry-fields {
	display: flex;
	flex-wrap: wrap;
	gap: .45rem;
}

.fq-enquiry input,
.fq-enquiry textarea {
	flex: 1 1 9rem;
	padding: .45rem .5rem;
	border: 1px solid var(--fq-line);
	border-radius: 7px;
	background: var(--fq-bg);
	font: inherit;
	font-size: .9rem;
	color: inherit;
}

.fq-enquiry textarea {
	width: 100%;
	resize: vertical;
}

/* the honeypot leaves the page without leaving the DOM */
.fq-enquiry .fq-hp {
	position: absolute;
	left: -9999px;
	width: 1px;
	height: 1px;
	opacity: 0;
}

.fq-enquiry-send {
	align-self: flex-start;
}

.fq-enquiry-ok {
	color: var(--fq-ink);
	font-size: .925rem;
	margin: 0;
}

.fq-enquiry-status {
	color: var(--fq-danger);
	font-size: .85rem;
	margin: 0;
	min-height: 1em;
}

.fq-add--done {
	background: #047857 !important;
	border-color: #047857 !important;
	color: #fff !important;
}

.fq-app .fq-add[disabled] {
	opacity: .55;
	cursor: not-allowed;
}

/* ---------------------------------------------------------------- footer */

.fq-footer {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: 1rem;
	margin-top: 1.25rem;
	padding-top: 1rem;
	border-top: 1px solid var(--fq-line);
}

.fq-total-label {
	color: var(--fq-muted);
	margin-right: .4rem;
}

/* ------------------------------------------------------------------- cart */

.fq-cart-thumb {
	width: 80px;
	height: 80px;
	object-fit: contain;
	background: var(--fq-soft, #f9fafb);
	border-radius: 6px;
}

/* --------------------------------------------------------------- narrow */

@media (max-width: 600px) {
	.fq-card {
		padding: .85rem;
	}

	/* Still the full width of the card, but a phone screen is mostly height
	   and the controls have to stay reachable under it. */
	.fq-preview {
		max-height: 260px;
		min-height: 130px;
		padding: 10px;
	}

	.fq-field {
		min-width: 7.5rem;
	}

	.fq-price strong {
		font-size: 1.2rem;
	}

	.fq-build {
		padding: .8rem;
	}
}

/* A 360 px phone is the narrowest screen this has to work on, and the builder
   is the densest thing in the widget. Two dimension fields per row still fit;
   below that they go single file rather than squeeze. The tabs are allowed to
   shrink their padding but never their tap height. */
@media (max-width: 400px) {
	.fq-options {
		gap: .5rem;
	}

	.fq-field {
		/* two across at 360 px, one across if a theme adds its own padding */
		min-width: 0;
		flex: 1 1 8.5rem;
	}

	.fq-build-foot {
		gap: .5rem;
	}

	.fq-build-go {
		width: 100%;
	}

	/* Shorter on a phone: the form above it is tall already, and a 240px
	   panel pushes the button off the bottom of the screen. */
	.fq-build-preview {
		height: 170px;
	}
}

/* Themes that set a dark background: keep contrast rather than fight it. */
@media (prefers-color-scheme: dark) {
	.fq-app {
		--fq-ink: #e5e7eb;
		--fq-muted: #9ca3af;
		--fq-line: #374151;
		--fq-bg: #111827;
		--fq-soft: #1f2937;
		--fq-warn-bg: #2a2213;
		--fq-accent: #60a5fa;
	}

	.fq-drop--over {
		background: #172554;
	}

	.fq-build-problems {
		border-color: #78560c;
	}

	/* The part itself keeps its own light plate and dark outline — it reads as
	   sheet metal on a dark bench. Only the waiting state needs restating. */
	.fq-preview--loading {
		background: linear-gradient(90deg, #1f2937 25%, #2b3542 37%, #1f2937 63%);
		background-size: 400% 100%;
	}

	.fq-alert--error {
		background: #2a1414;
		border-color: #7f1d1d;
		color: #fca5a5;
	}

	.fq-alert--notice {
		background: #16233c;
		border-color: #1e40af;
	}
}
