/* ==========================================================================
   Utopany - apartment move-in commerce

   THE THEME IS THE MARKETING PAGE'S. This file deliberately mirrors
   apps/landing-page-astro/src/styles/global.css rather than inventing a palette.

   Two apps share one hostname, so a visitor who follows utopany.com/ into the shop
   must not feel they have landed somewhere else. Same lime, same navy, same
   signature, same type scale - copied rather than approximated, because
   "approximately the same lime" is exactly how two products end up looking like
   two products.

   ## The signature
   A chunky 3px outline meeting a tight 4px radius. The two together are what stop
   this reading as another rounded soft-SaaS card grid. `--border-chunky` and
   `--radius-btn` are tokens rather than literals for the same reason the landing
   page makes them tokens.

   ## Hard shadows, never soft ones
   `6px 6px 0` in navy, offset to the bottom-right. A blurred shadow on a 3px border
   looks like a rendering bug; a hard offset shadow looks like a decision.

   ## No dark mode
   The marketing page is light-only, deliberately. An app that follows it must too -
   and a site that flips to grey while its own marketing page does not reads as
   broken rather than considered.

   ## Type scale
   The same fluid `clamp()` steps as `--text-h1`..`--text-h5` in global.css. Page
   titles use the h2/h3 steps rather than h1: the landing page has exactly one h1
   (the hero), and 44px is right for a marketing hero and wrong for a product name.
   The weight, colour and tracking are identical at every step.

   ## No build step
   Served as a static asset, so no page view invokes the Worker. That is why the
   layout system is custom properties and a grid rather than utility classes, and
   why the component classes below are written out rather than composed per element.
   ========================================================================== */

/* The webfont faces are inlined in the page head from `views/font-faces.ts`, which
   `scripts/vendor-be-vietnam-pro.ps1` generates. They are deliberately neither an
   `@import` here nor a sibling <link> to a stylesheet: an `@import` in a
   render-blocking stylesheet is not free, and a second <link> made a file that cannot
   paint on its own render-blocking while also delaying the discovery of the woff2 URLs
   to a second hop. See the module's own comment. */

/* --- Metric-matched fallback ------------------------------------------------
   The face used for the few hundred milliseconds before Be Vietnam Pro arrives.

   ## Why this exists

   Lighthouse reported a layout shift on `h1.hero-title` with all five woff2 files
   listed as the culprits. The cause is *re-wrapping*, not a height change: this
   stylesheet sets `line-height` as a unitless number (1.6 on body, 1.18 on
   headings), so the line box is `1.18 x font-size` no matter which font is
   loaded and cannot change on swap. What does change is how many lines the
   sentence needs, because Be Vietnam Pro is much wider than Arial.

   ## Why size-adjust is derived from width, not x-height

   Matching x-height (the usual advice, and what `next/font` does by default) would
   give 102.21%. That makes the fallback *look* the right size while being ~11% too
   narrow, so it breaks the same paragraph at different points and the block still
   gains or loses a line - the shift survives. Measured over lowercase plus space:

       Be Vietnam Pro 400   average advance 55.204% of em
       Arial                average advance 48.555% of em

   which is 113.69%. At that value the fallback wraps where the webfont wraps, so
   the line count holds and there is nothing to shift. The trade is that the
   fallback's glyphs read ~14% larger during that window. A brief size difference
   is far less visible than a paragraph jumping.

   ## Why ascent/descent are divided by size-adjust

   Because the overrides are percentages of the *adjusted* em. Compensating for the
   scale keeps the resulting line box exactly 126.50% of em - the webfont's own
   asc+desc+gap - so nothing about the box moves. One face per weight because the
   webfont's advance widths differ per weight; the 700 face carries 700 and 800.

   Regenerate with the numbers printed by the metric script if the typeface or its
   versions ever change. */
@font-face {
	font-family: 'Be Vietnam Pro Fallback';
	font-style: normal;
	font-weight: 400;
	src: local('Arial'), local('Helvetica'), local('Liberation Sans');
	size-adjust: 113.69%;
	ascent-override: 87.96%;
	descent-override: 23.31%;
	line-gap-override: 0%;
}

@font-face {
	font-family: 'Be Vietnam Pro Fallback';
	font-style: normal;
	font-weight: 700;
	src: local('Arial'), local('Helvetica'), local('Liberation Sans');
	size-adjust: 118.13%;
	ascent-override: 84.65%;
	descent-override: 22.43%;
	line-gap-override: 0%;
}

@font-face {
	font-family: 'Be Vietnam Pro Fallback';
	font-style: normal;
	font-weight: 800;
	src: local('Arial'), local('Helvetica'), local('Liberation Sans');
	size-adjust: 119.58%;
	ascent-override: 83.63%;
	descent-override: 22.16%;
	line-gap-override: 0%;
}

/* --- Tokens --------------------------------------------------------------
   Copied from global.css's @theme block. Same names, same values, so a change
   to the brand is one edit in two places rather than a design review.
   ------------------------------------------------------------------------- */

:root {
	/* Primary: lime. Text on it must be navy, never white - #fff on #c2d500 is
	   about 1.6:1 and fails WCAG AA outright. */
	--primary: #c2d500;
	--primary-hover: #e2f600;
	--primary-medium: #eaff00;
	--primary-semi-light: #f0ff4d;
	--primary-light: #f9ffb3;
	--primary-semi-dark: #a4b300;
	--primary-dark: #758000;

	/* Secondary: cyan. Navy text on it, not white: #fff on #00b1dd is ~2.3:1. */
	--secondary: #00b1dd;
	--secondary-hover: #00cafc;
	--secondary-semi-light: #4ddbff;
	--secondary-light: #b3f0ff;
	--secondary-semi-dark: #008fb3;
	--secondary-dark: #006680;

	/* Base: navy. Carries headings, footer, full-bleed sections. */
	--navy: #093756;
	--navy-hover: #0b4165;
	--navy-dark: #051e2e;
	--navy-semi-dark: #1168a2;
	--navy-medium: #1894e7;
	--navy-semi-light: #5db4ee;
	--navy-light: #badff8;

	/* Neutrals */
	--ink: #0b1c2a;
	--body: #4c5761;
	--mist: #efedf4;
	--white: #fff;

	/* Accent rotation. A single hue would flatten the difference between two
	   adjacent room tiles, which is the same reason the marketing page rotates
	   them across its feature cards. */
	--accent-orange: #f37424;
	--accent-amber: #f8aa1f;
	--accent-magenta: #e8138b;
	--accent-blue: #146ff8;
	--accent-teal: #3cbcac;
	--accent-red: #d92638;

	/* Darkened counterparts, for anything *read* rather than filled in. These
	   clear 4.5:1 on --mist, the stricter of the two light backgrounds, so one
	   token works on both. Derived by scripts/derive-contrast.mjs in the landing
	   page, not picked by eye. */
	--accent-cyan-dark: #007693;
	--accent-orange-dark: #ab5119;
	--accent-magenta-dark: #ce117b;
	--accent-teal-dark: #26786e;
	--accent-blue-dark: #1265e2;
	--accent-amber-dark: #916312;

	/* Lightened counterpart for step numbers on the navy band. Magenta is the
	   one vivid hue that fails there at 2.89:1; this reaches 4.52:1. */
	--accent-pink: #f16eb8;

	/* The signature. */
	--radius-btn: 0.25em;
	--border-chunky: 3px;
	--spacing-btn-x: 2em;
	--spacing-btn-y: 0.75em;

	/* Section rhythm, from the marketing page's `py-20 sm:py-28`. */
	--pad-section: 5rem;
	--pad-section-lg: 7rem;

	/* Container: `max-w-6xl` with `px-4 sm:px-6 lg:px-8`. */
	--wrap: 72rem;
	--gutter: 1rem;

	/* One height for every interactive control that sits beside another one.
	 *
	 * A quantity input and the button beside it are two halves of one control, and
	 * they were 12px apart because they differ in *two* properties rather than one:
	 * the input is 1rem/line-height 1.6 (25.6px of text) while the button is
	 * 0.875rem/line-height 1 (14px), on identical padding and border.
	 *
	 * Matching them by lowering the input's font size is not an option - iOS Safari
	 * zooms the page on focus below 16px, so a 14px input means a shopper ends up
	 * zoomed in mid-checkout. So both get an explicit height instead, which is also
	 * a better touch target than either was.
	 *
	 * 2.75rem / 44px, the same figure the marketing page uses for its nav toggle.
	 */
	/* The sticky header's rendered height, measured at a 390px viewport (83px). Used to
   keep scroll-into-view clear of it - see the `scroll-margin-top` rule. Only an
   approximation of the real height: the header wraps on narrow screens, so this is
   the common case rather than a guaranteed fit. */
	--header-h: 5.2rem;

	--control-h: 2.75rem;

	/* The large control's height, for the product page's quantity + add-to-cart pair.
	 * 16px text at line-height 1, plus 1rem padding and the 3px border, is 54px. */
	--control-h-lg: 3.375rem;

	/* The hard shadow, used where the marketing page uses
	   `shadow-[6px_6px_0_var(--color-navy)]`. */
	--shadow-hard: 6px 6px 0 var(--navy);
	--shadow-hard-sm: 4px 4px 0 var(--navy);
}

@media (min-width: 40rem) {
	:root {
		--gutter: 1.5rem;
	}
}

@media (min-width: 64rem) {
	:root {
		--gutter: 2rem;
		--pad-section: var(--pad-section-lg);
	}
}

/* --- Reset --------------------------------------------------------------- */

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

* {
	margin: 0;
}

html {
	-webkit-text-size-adjust: 100%;
}

body {
	background: var(--white);
	color: var(--body);
	font-family:
		'Be Vietnam Pro',
		'Be Vietnam Pro Fallback',
		ui-sans-serif,
		system-ui,
		-apple-system,
		'Segoe UI',
		sans-serif;
	font-weight: 400;
	/* Vietnamese renders taller than Latin at the same size, so this is a little
	   above the usual 1.5. Stacked tone marks need the room. */
	line-height: 1.6;
	-webkit-font-smoothing: antialiased;
	text-rendering: optimizeLegibility;
}

img,
svg {
	display: block;
	max-width: 100%;
}

input,
button,
textarea,
select {
	font: inherit;
	color: inherit;
}

/* --- Typography ----------------------------------------------------------
   The same fluid steps as global.css's `--text-h1`..`--text-h5`. A heading is the
   same visual weight on a phone and on a 27" display without six breakpoints.
   ------------------------------------------------------------------------- */

/* --- Typography ----------------------------------------------------------
   The same fluid steps as global.css's `--text-h1`..`--text-h5`. A heading is the
   same visual weight on a phone and on a 27" display.

   ## Weight 800, not the marketing page's 900

   Deliberate divergence, for a font reason rather than a brand one. The marketing
   page uses 900 because Lato has no 800, so headings jump 700 -> 900 - Black, the
   heaviest weight available. On Latin that is fine; on Vietnamese it is the worst
   case, because the stacked marks on â êậ ệ and the horns on ơ ư fill in at Black
   and collide with the lines either side. Be Vietnam Pro ships a true 800, so a
   heading can be emphatic without being solid.

   ## Line-height 1.18, not the marketing page's 1.05

   The other half of the fix, and the bigger one. 1.05 was tuned for Latin and is
   too tight for Vietnamese: measured on this page's hero it gave a 58.7px heading a
   61.6px line box, which is about 1.5px of clearance for a two-tier stack of
   diacritics. 1.18 is the smallest value where the marks on `nhận` and `hoàn` stop
   reading as crowded.

   Both are visible in font-specimen.html, which renders the proposition in six
   families at 1.05 and 1.18 side by side.
   ------------------------------------------------------------------------- */

h1,
h2,
h3,
h4,
h5,
h6 {
	color: var(--navy);
	font-weight: 800;
	line-height: 1.18;
	letter-spacing: -0.02em;
}

.text-h1 {
	font-size: clamp(2.7648rem, 1.88444vw + 2.16178rem, 4.8rem);
}
.text-h2 {
	font-size: clamp(2.304rem, 0.828148vw + 2.03899rem, 3.19rem);
}
.text-h3 {
	font-size: clamp(1.92rem, 0.443889vw + 1.77796rem, 2.3994rem);
}
.text-h4 {
	font-size: clamp(1.6rem, 0.185185vw + 1.54074rem, 1.8rem);
}
.text-h5 {
	font-size: 1.2rem;
}

/* Hero uses the full h1 step; every other page uses h2/h3 for its title. */
.hero-title {
	font-size: clamp(2.7648rem, 1.88444vw + 2.16178rem, 4.8rem);
}

.page-title {
	font-size: clamp(1.92rem, 0.443889vw + 1.77796rem, 2.3994rem);
}

.section-title {
	font-size: clamp(1.6rem, 0.185185vw + 1.54074rem, 1.8rem);
}

p {
	text-wrap: pretty;
}

a {
	color: var(--navy);
	text-underline-offset: 2px;
	text-decoration-thickness: 1px;
}

a:hover {
	text-decoration-thickness: 2px;
}

/* Visible focus ring, and it has to be visible: with a palette this saturated a
   lime button on white is otherwise indistinguishable from its hover state. Cyan,
   because lime on white is the button's own fill and reads as nothing. */
:focus-visible {
	outline: 3px solid var(--secondary);
	outline-offset: 3px;
}

.visually-hidden {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	overflow: hidden;
	clip: rect(0 0 0 0);
	white-space: nowrap;
	border: 0;
}

.skip {
	position: absolute;
	top: 1rem;
	left: 1rem;
	z-index: 100;
	padding: 0.5rem 1rem;
	background: var(--navy);
	color: var(--primary);
	border-radius: var(--radius-btn);
	text-decoration: none;
	font-weight: 700;
	transform: translateY(-200%);
}

.skip:focus {
	transform: translateY(0);
}

/* --- Layout -------------------------------------------------------------- */

.wrap {
	width: 100%;
	max-width: var(--wrap);
	margin-inline: auto;
	padding-inline: var(--gutter);
}

.section {
	padding-block: var(--pad-section);
}

.section-alt {
	background: var(--mist);
	/* The 3px rule is what separates two white sections without a gap. */
	border-top: var(--border-chunky) solid var(--navy);
}

.page-plain .section {
	padding-block: 0;
}

.section-head {
	display: flex;
	flex-wrap: wrap;
	align-items: end;
	justify-content: space-between;
	gap: var(--s3, 0.75rem);
	margin-bottom: 2rem;
}

.section-lede {
	margin-top: 0.75rem;
	max-width: 42rem;
	color: var(--body);
}

.section-cta {
	margin-top: 2.5rem;
	text-align: center;
}

.section-foot {
	margin-top: 1.5rem;
	font-size: 0.875rem;
}

.section-band {
	margin-top: var(--pad-section);
	padding: 3rem;
	background: var(--navy);
	color: #fff;
	border-radius: var(--radius-btn);
	text-align: center;
}

.section-band .section-title {
	color: #fff;
}

.section-band .section-lede {
	margin: 0.75rem auto 1.5rem;
	color: var(--navy-light);
	max-width: 34rem;
}

/* --- Header --------------------------------------------------------------
   Mirrors the marketing page's `sticky top-0 z-40 border-b-[3px] border-navy
   bg-white/90 backdrop-blur` with an `h-20` bar.
   ------------------------------------------------------------------------- */

.site-header {
	position: sticky;
	top: 0;
	z-index: 40;
	background: rgb(255 255 255 / 0.9);
	border-bottom: var(--border-chunky) solid var(--navy);
}

/* The header is sticky, so any "scroll it into view" - the browser focusing a
   control, an anchor jump, a test driver positioning an element - puts the target
   directly underneath it. Measured at a 390px viewport: the header is 83px tall and
   all five wizard radios sat under it once focused, so a keyboard user tabbing to an
   option could see neither the control nor the selection they had just made, and a
   tap aimed at it landed on the nav instead.
 *
 * `scroll-margin-top` reserves the header's height above the target. Deliberately not
 * gated on `:focus` - this property only affects scroll-into-view, so leaving it on
   always costs nothing and also covers anchor jumps and test drivers.
 */
:is(a, button, input, select, textarea, [tabindex], .choice) {
	scroll-margin-top: calc(var(--header-h) + 0.75rem);
}

@supports (backdrop-filter: blur(8px)) {
	.site-header {
		background: rgb(255 255 255 / 0.9);
		backdrop-filter: blur(8px);
	}
}

.header-inner {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: 1.5rem;
	min-height: 5rem;
	padding-inline: var(--gutter);
}

.brand {
	display: inline-flex;
	align-items: center;
	gap: 0.5rem;
	font-size: 1.125rem;
	font-weight: 800;
	letter-spacing: -0.02em;
	color: var(--navy);
	text-decoration: none;
}

.brand-mark {
	display: grid;
	place-items: center;
	width: 2rem;
	height: 2rem;
	background: var(--primary);
	border-radius: var(--radius-btn);
	font-size: 1rem;
	line-height: 1;
}

.nav-toggle {
	display: grid;
	place-items: center;
	width: 2.75rem;
	height: 2.75rem;
	background: none;
	border: var(--border-chunky) solid var(--navy);
	border-radius: var(--radius-btn);
	cursor: pointer;
}

.nav-toggle-bars,
.nav-toggle-bars::before,
.nav-toggle-bars::after {
	display: block;
	width: 18px;
	height: 2px;
	background: var(--navy);
	content: '';
}

.nav-toggle-bars {
	position: relative;
}

.nav-toggle-bars::before {
	position: absolute;
	top: -6px;
}

.nav-toggle-bars::after {
	position: absolute;
	top: 6px;
}

/* Collapsed on a phone, a row from `sm`. The `hidden` attribute set by the inline
   script is what actually hides it, so CSS alone never shows a nav that JavaScript
   has closed. */
.site-nav {
	display: none;
	flex-basis: 100%;
	flex-direction: column;
	gap: 0.5rem;
	padding-block: 1rem;
}

.site-nav:not([hidden]) {
	display: flex;
}

@media (min-width: 56rem) {
	.nav-toggle {
		display: none;
	}

	.site-nav {
		display: flex;
		flex-basis: auto;
		flex-direction: row;
		align-items: center;
		gap: 1.5rem;
	}
}

.nav-link {
	color: var(--navy);
	text-decoration: none;
	font-weight: 700;
	transition: color 0.15s ease;
	white-space: nowrap;
}

.nav-link:hover {
	color: var(--accent-cyan-dark);
}

.nav-link.is-current {
	color: var(--secondary-dark);
}

/* `min-width: 0` overrides the base button's 14rem, and the tighter padding is the
   marketing page's `min-w-0 px-5 py-2.5 text-sm` header CTA. A 14rem button does not
   fit a header beside four links. */
.nav-cta {
	min-width: 0;
	padding: 0.625rem 1.25rem;
	font-size: 0.875rem;
}

/* Ghost, not lime. The header has exactly one filled button - the wizard CTA, which
   is the one thing worth sending a visitor to. A second filled button beside it says
   "these two matter equally", and the cart is not equal to the wizard. The count
   badge is what makes an empty cart still worth clicking. */
.nav-cart {
	display: inline-flex;
	align-items: center;
	gap: 0.5rem;
	min-width: 0;
	padding: 0.625rem 1.25rem;
	background: transparent;
	border: var(--border-chunky) solid var(--navy);
	border-radius: var(--radius-btn);
	color: var(--navy);
	font-size: 0.875rem;
	font-weight: 700;
	line-height: 1;
	text-decoration: none;
	white-space: nowrap;
}

.nav-cart:hover {
	background: var(--navy);
	color: var(--primary);
}

.cart-count {
	display: grid;
	place-items: center;
	min-width: 1.4rem;
	height: 1.4rem;
	padding-inline: 0.3rem;
	background: var(--primary);
	border: var(--border-chunky) solid var(--navy);
	border-radius: var(--radius-btn);
	font-size: 0.75rem;
	font-weight: 800;
	color: var(--navy-dark);
}

/* --- Buttons -------------------------------------------------------------
   Byte-for-byte the marketing page's `.btn` block. `min-width: 14rem` is the
   part that looks wrong until you see it: it is what makes a column of CTAs
   line up as a column rather than as ragged text.
   ------------------------------------------------------------------------- */

.btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 0.5rem;
	min-width: 14rem;
	padding: var(--spacing-btn-y) var(--spacing-btn-x);
	border: var(--border-chunky) solid transparent;
	border-radius: var(--radius-btn);
	font-weight: 700;
	line-height: 1;
	text-align: center;
	text-decoration: none;
	cursor: pointer;
	transition:
		background-color 0.15s ease,
		color 0.15s ease,
		border-color 0.15s ease;
}

.btn-primary {
	background: var(--primary);
	border-color: var(--primary);
	color: var(--navy-dark);
}

.btn-primary:hover {
	background: var(--primary-hover);
	border-color: var(--primary-hover);
}

.btn-secondary {
	background: var(--secondary);
	border-color: var(--secondary);
	color: var(--navy-dark);
}

.btn-secondary:hover {
	background: var(--secondary-hover);
	border-color: var(--secondary-hover);
}

/* Outlined on light backgrounds. */
.btn-ghost {
	background: transparent;
	border-color: var(--navy);
	color: var(--navy);
}

.btn-ghost:hover {
	background: var(--navy);
	color: var(--primary);
}

/* Outlined on navy. The border is cyan rather than white, because white-on-navy
   is what the button is already saying and a white outline reads as an artefact.

   The dark sections are in this selector list rather than carrying their own copy of
   these four declarations, and rather than relying on each call site remembering to
   swap `btn-ghost` for `btn-ghost-light`. Two call sites did not: `home.ts` put a
   `btn-ghost` in `.closing` (navy on navy-dark, 1.37:1) and `routes/packages.ts` put
   one in `.package-foot-cta` (navy on navy, 1.00:1 - text the same colour as its
   background). Both were invisible, and both were invisible *because* the class was
   written for a light background and the section around it was dark. Listing the
   sections here makes that impossible rather than merely fixed. */
.btn-ghost-light,
.closing .btn-ghost,
.product-cta-band .btn-ghost,
.package-foot-cta .btn-ghost {
	background: transparent;
	border-color: var(--secondary-semi-light);
	color: #fff;
}

.btn-ghost-light:hover,
.closing .btn-ghost:hover,
.product-cta-band .btn-ghost:hover,
.package-foot-cta .btn-ghost:hover {
	background: var(--secondary-semi-light);
	border-color: var(--secondary-semi-light);
	color: var(--navy-dark);
}

/* Explicit height, so this lines up with the input beside it. See `--control-h`. */
.btn-sm {
	min-width: 0;
	height: var(--control-h);
	padding: 0.5rem 1rem;
	font-size: 0.875rem;
}

.btn-lg {
	height: var(--control-h-lg);
	padding: 1rem 2.5rem;
}

.btn-block {
	display: flex;
	width: 100%;
}

/* Already added, or a room fully in the cart. Lime-tinted rather than lime, so a
   row of confirmed states does not shout louder than the primary action. */
.btn-done {
	background: var(--primary-light);
	border-color: var(--primary-dark);
	color: var(--navy-dark);
}

.link-arrow {
	color: var(--navy);
	font-weight: 700;
	white-space: nowrap;
}

.link-arrow:hover {
	color: var(--accent-cyan-dark);
}

/* --- Hero ----------------------------------------------------------------
   The marketing page's hero, on the shop's proposition.
   ------------------------------------------------------------------------- */

.hero {
	padding-block: var(--pad-section) var(--pad-section-lg);
	text-align: center;
}

.hero-inner {
	max-width: 56rem;
	margin-inline: auto;
}

.hero-eyebrow {
	display: inline-block;
	padding: 0.5rem 1rem;
	background: var(--white);
	border: var(--border-chunky) solid var(--navy);
	border-radius: var(--radius-btn);
	font-size: 0.75rem;
	font-weight: 800;
	letter-spacing: 0.05em;
	text-transform: uppercase;
	color: var(--navy);
}

.hero-title {
	margin-block: 2rem;
	color: var(--navy);
}

.hero-lede {
	max-width: 44rem;
	margin-inline: auto;
	font-size: 1.125rem;
}

.hero-actions {
	display: flex;
	flex-direction: column;
	gap: 1rem;
	margin-top: 2.5rem;
}

@media (min-width: 40rem) {
	.hero-actions {
		flex-direction: row;
		justify-content: center;
	}
}

.hero-note {
	margin-top: 1rem;
	font-size: 0.875rem;
}

/* --- Stats ---------------------------------------------------------------
   The shop's own section - the marketing page has no equivalent - built from the
   same parts: a navy band and lime numerals.
   ------------------------------------------------------------------------- */

.stats {
	padding-block: 2.5rem;
	background: var(--navy);
	border-top: var(--border-chunky) solid var(--navy);
	color: #fff;
}

.stats-grid {
	display: grid;
	grid-template-columns: repeat(2, 1fr);
	gap: 1.5rem;
	text-align: center;
}

@media (min-width: 48rem) {
	.stats-grid {
		grid-template-columns: repeat(4, 1fr);
	}
}

.stat-value {
	font-size: clamp(1.5rem, 4vw, 2rem);
	font-weight: 800;
	color: var(--primary);
	line-height: 1.1;
}

.stat-label {
	font-size: 0.875rem;
	color: var(--navy-light);
}

/* --- Steps ---------------------------------------------------------------
   `HowItWorks` on the marketing page, copied: a navy band, a 3px translucent rule
   above each step, and a lime numeral.

   The band is not decoration - it is what the step colours are designed for. An
   earlier version of this file had the same step styles over a white section, where
   `#fff` titles on white measured 1:1 and simply disappeared. Light-on-dark is how
   the marketing page does it, and the only way to have both is the navy band.
   ------------------------------------------------------------------------- */

.steps-band {
	background: var(--navy);
	color: #fff;
}

.steps-band .section-title {
	color: #fff;
}

.steps-band .section-lede {
	color: var(--navy-light);
}

.steps {
	display: grid;
	gap: 2.5rem;
	padding: 0;
	margin: 0;
	list-style: none;
}

@media (min-width: 48rem) {
	.steps {
		grid-template-columns: repeat(2, 1fr);
	}
}

@media (min-width: 64rem) {
	.steps {
		grid-template-columns: repeat(4, 1fr);
	}
}

.step {
	position: relative;
	padding-top: 1.5rem;
	border-top: var(--border-chunky) solid rgb(186 223 248 / 0.3);
	color: #fff;
}

.step-num {
	display: block;
	margin-bottom: 0.75rem;
	font-size: 2.25rem;
	font-weight: 800;
	line-height: 1;
	color: var(--primary);
}

.step-title {
	color: #fff;
	font-size: var(--text-h5, 1.2rem);
	font-weight: 800;
}

.step-body {
	margin-top: 0.5rem;
	font-size: 0.9rem;
	color: rgb(186 223 248 / 0.8);
}

/* --- Cards ---------------------------------------------------------------
   The `Features` card: 3px navy, 4px radius, and a full invert on hover. The
   invert is the part that carries the energy - a card that only changes its
   border colour on hover is a card you can miss.
   ------------------------------------------------------------------------- */

.card,
.product-card,
.room-tile,
.package-tile,
.package-card {
	background: var(--white);
	border: var(--border-chunky) solid var(--navy);
	border-radius: var(--radius-btn);
	transition:
		background-color 0.15s ease,
		color 0.15s ease;
}

.product-card:hover,
.room-tile:hover,
.package-tile:hover {
	background: var(--navy);
}

.product-grid {
	display: grid;
	gap: 1.5rem;
	grid-template-columns: repeat(auto-fill, minmax(min(100%, 16rem), 1fr));
}

.product-card {
	display: flex;
	flex-direction: column;
	overflow: hidden;
}

.product-media {
	position: relative;
	display: grid;
	place-items: center;
	aspect-ratio: 4 / 3;
	background: var(--mist);
	border-bottom: var(--border-chunky) solid var(--navy);
}

.product-glyph {
	font-size: 3.5rem;
	line-height: 1;
}

.product-badge {
	position: absolute;
	top: 0.5rem;
	left: 0.5rem;
	padding: 0.25rem 0.5rem;
	background: var(--primary);
	border: var(--border-chunky) solid var(--navy);
	border-radius: var(--radius-btn);
	font-size: 0.7rem;
	font-weight: 800;
	letter-spacing: 0.03em;
	text-transform: uppercase;
}

.product-body {
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
	padding: 1.5rem;
	flex: 1;
}

.product-eyebrow {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem;
	font-size: 0.75rem;
	font-weight: 700;
}

.product-eyebrow a {
	color: var(--accent-cyan-dark);
	text-decoration: none;
}

.product-eyebrow a:hover {
	text-decoration: underline;
}

.dot {
	color: var(--navy-light);
}

.product-card:hover .product-eyebrow a {
	color: var(--secondary-semi-light);
}

.product-title {
	font-size: 1rem;
	font-weight: 800;
	line-height: 1.35;
	letter-spacing: -0.02em;
}

.product-title a {
	color: var(--navy);
	text-decoration: none;
}

.product-card:hover .product-title a,
.product-card:hover .product-brand,
.product-card:hover .product-desc {
	color: var(--navy-light);
}

.product-brand,
.product-desc {
	font-size: 0.85rem;
	color: var(--body);
}

.product-desc {
	font-size: 0.875rem;
}

.product-foot {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: 0.5rem;
	margin-top: auto;
	padding-top: 0.5rem;
}

.product-price {
	font-size: 1.1rem;
	font-weight: 800;
	color: var(--navy);
}

.product-card:hover .product-price {
	color: var(--primary);
}

.rating {
	display: inline-flex;
	align-items: center;
	gap: 0.25rem;
	font-size: 0.85rem;
	font-weight: 700;
	color: var(--accent-amber-dark);
	letter-spacing: 0.05em;
}

.rating-count {
	color: var(--body);
	font-weight: 400;
	letter-spacing: 0;
}

.product-card:hover .rating-count {
	color: var(--navy-light);
}

.product-in-cart {
	font-size: 0.8rem;
	font-weight: 800;
	color: var(--accent-teal-dark);
}

/* --- Room and category tiles -------------------------------------------- */

.room-grid {
	display: grid;
	gap: 1rem;
	grid-template-columns: repeat(auto-fill, minmax(min(100%, 15rem), 1fr));
}

.room-tile {
	display: flex;
	flex-direction: column;
	gap: 0.25rem;
	padding: 1.5rem;
	color: var(--body);
	text-decoration: none;
}

.room-tile-link {
	display: flex;
	flex-direction: column;
	gap: 0.25rem;
	height: 100%;
	color: inherit;
	text-decoration: none;
}

.room-glyph {
	font-size: 1.9rem;
	line-height: 1.2;
}

.room-name {
	font-size: 1.05rem;
	font-weight: 800;
	color: var(--navy);
}

.room-blurb {
	font-size: 0.9rem;
}

.room-count {
	margin-top: auto;
	padding-top: 0.5rem;
	font-size: 0.8rem;
	font-weight: 700;
	color: var(--accent-cyan-dark);
}

.room-tile:hover .room-name,
.room-tile:hover .room-count,
.room-tile:hover .room-blurb {
	color: var(--navy-light);
}

.room-tile:hover .room-count {
	color: var(--primary);
}

.category-list {
	display: grid;
	gap: 0.75rem;
	padding: 0;
	margin: 0;
	list-style: none;
	grid-template-columns: repeat(auto-fill, minmax(min(100%, 20rem), 1fr));
}

/* The card fills the <li>, and the <li> is what the grid equalises.
   Without this the grid stretched the <li> to the tallest card in the row while the
   <a> inside stayed at its own content height, so every card was as tall as its blurb
   happened to wrap - 112px for a one-line blurb against 183px for a three-line one.
   Only visible at two or more columns; at a phone width each row holds one card, so
   there is nothing to compare against and the row looks fine. */
.category-list > li {
	display: grid;
}

.category-list a {
	display: grid;
	grid-template-columns: 1fr auto;
	align-items: baseline;
	gap: 0.25rem 1rem;
	padding: 1rem 1.5rem;
	background: var(--white);
	border: var(--border-chunky) solid var(--navy);
	border-radius: var(--radius-btn);
	color: inherit;
	text-decoration: none;
	transition:
		background-color 0.15s ease,
		color 0.15s ease;
}

.category-list a:hover {
	background: var(--navy);
}

.category-name {
	font-weight: 800;
	color: var(--navy);
}

.category-blurb {
	grid-column: 1;
	font-size: 0.875rem;
}

.category-count {
	grid-row: 1 / span 2;
	grid-column: 2;
	font-size: 0.8rem;
	font-weight: 700;
	color: var(--accent-cyan-dark);
	white-space: nowrap;
}

.category-list a:hover .category-name,
.category-list a:hover .category-count {
	color: var(--primary);
}

.category-list a:hover .category-blurb {
	color: var(--navy-light);
}

/* --- Chips --------------------------------------------------------------- */

.chip {
	display: inline-flex;
	align-items: center;
	gap: 0.5rem;
	padding: 0.5rem 1rem;
	background: var(--white);
	border: var(--border-chunky) solid var(--navy);
	border-radius: var(--radius-btn);
	color: var(--navy);
	font-size: 0.9rem;
	font-weight: 700;
	text-decoration: none;
	white-space: nowrap;
}

.chip:hover {
	background: var(--navy);
	color: var(--primary);
}

.chip-sm {
	padding: 0.25rem 0.75rem;
	font-size: 0.85rem;
}

.chip-count {
	padding-inline: 0.5rem;
	background: var(--mist);
	border-radius: var(--radius-btn);
	font-size: 0.75rem;
	color: var(--body);
}

.chip:hover .chip-count {
	background: var(--navy-hover);
	color: var(--navy-light);
}

.room-chip-row {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem;
	padding: 0;
	margin: 0;
	list-style: none;
}

/* --- Budget table --------------------------------------------------------
   Shop-only section, built from the same parts. The bar is the honest part: it
   shows the cheapest band is spent nearly to its ceiling while the top one is
   not, which is a fact about the catalogue rather than something to hide.
   ------------------------------------------------------------------------- */

.budget-table {
	display: grid;
	gap: 0.75rem;
	padding: 0;
	margin: 0;
	list-style: none;
}

.budget-row {
	display: grid;
	grid-template-columns: 1fr;
	gap: 0.25rem;
	padding: 1rem 1.5rem;
	background: var(--white);
	border: var(--border-chunky) solid var(--navy);
	border-radius: var(--radius-btn);
}

@media (min-width: 40rem) {
	.budget-row {
		grid-template-columns: 9rem 1fr 11rem;
		align-items: center;
		gap: 1.5rem;
	}
}

.budget-label {
	font-weight: 800;
	color: var(--navy);
}

.budget-bar {
	display: block;
	height: 8px;
	background: var(--mist);
	border-radius: var(--radius-btn);
	overflow: hidden;
}

.budget-fill {
	display: block;
	height: 100%;
	background: var(--primary-dark);
}

.budget-figure {
	font-weight: 800;
	color: var(--navy);
	white-space: nowrap;
}

.budget-note {
	font-weight: 400;
	font-size: 0.85rem;
}

/* --- Packages ------------------------------------------------------------ */

.package-grid {
	display: grid;
	gap: 1.5rem;
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
}

.package-card {
	display: flex;
	flex-direction: column;
	gap: 1rem;
	padding: 2rem;
}

.package-card-link {
	color: inherit;
	text-decoration: none;
}

.package-card:hover {
	background: var(--navy);
}

.package-glyph {
	font-size: 2.5rem;
	line-height: 1.2;
}

.package-name {
	margin-block: 0.75rem 0.25rem;
	color: var(--navy);
}

.package-card:hover .package-name,
.package-tile:hover .package-name {
	color: var(--primary);
}

.package-tagline {
	font-size: 0.9rem;
}

.package-facts {
	display: grid;
	gap: 0.5rem;
	padding-block: 1rem;
	border-block: var(--border-chunky) solid var(--navy);
}

.package-facts > div {
	display: flex;
	justify-content: space-between;
	gap: 1rem;
	font-size: 0.85rem;
}

.package-facts dt {
	color: var(--body);
}

.package-facts dd {
	font-weight: 800;
	text-align: right;
}

.package-card:hover .package-facts {
	border-color: var(--secondary-semi-light);
}

.package-card:hover .package-facts dt {
	color: var(--navy-light);
}

.package-card:hover .package-facts dd {
	color: #fff;
}

.package-price {
	font-size: 1.05rem;
	font-weight: 800;
	color: var(--navy);
}

.package-price-note {
	font-size: 0.8rem;
	font-weight: 400;
}

.package-tile {
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
	padding: 1.5rem;
	color: inherit;
	text-decoration: none;
}

.package-head {
	display: grid;
	gap: 2.5rem;
	margin-bottom: var(--pad-section);
}

@media (min-width: 56rem) {
	.package-head {
		grid-template-columns: 1fr 20rem;
	}
}

.package-eyebrow {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	font-size: 0.85rem;
	font-weight: 800;
	letter-spacing: 0.05em;
	text-transform: uppercase;
	color: var(--accent-cyan-dark);
}

.audience-list {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem;
	margin-top: 1rem;
	padding: 0;
	list-style: none;
}

.audience-list li {
	padding: 0.25rem 0.75rem;
	background: var(--white);
	border: var(--border-chunky) solid var(--navy);
	border-radius: var(--radius-btn);
	font-size: 0.85rem;
	font-weight: 700;
}

.package-total {
	padding: 1.5rem;
	background: var(--white);
	border: var(--border-chunky) solid var(--navy);
	border-radius: var(--radius-btn);
	box-shadow: var(--shadow-hard);
}

.package-total-label {
	font-size: 0.8rem;
	letter-spacing: 0.05em;
	text-transform: uppercase;
	color: var(--body);
}

.package-total-value {
	font-size: 1.8rem;
	font-weight: 800;
	line-height: 1.2;
	color: var(--navy);
}

.package-total-note {
	font-size: 0.85rem;
}

.package-about,
.package-highlights,
.package-rooms,
.package-list {
	margin-bottom: var(--pad-section);
}

.package-description {
	margin-top: 1rem;
	max-width: 46rem;
	font-size: 1.05rem;
}

.package-highlights ul {
	display: grid;
	gap: 1rem;
	padding: 0;
	margin: 0;
	list-style: none;
}

.package-highlights li {
	display: flex;
	gap: 1rem;
	padding: 1rem 1.5rem;
	background: var(--white);
	border: var(--border-chunky) solid var(--navy);
	border-radius: var(--radius-btn);
}

.highlight-num {
	display: grid;
	place-items: center;
	flex-shrink: 0;
	width: 1.75rem;
	height: 1.75rem;
	background: var(--primary);
	border: var(--border-chunky) solid var(--navy);
	border-radius: var(--radius-btn);
	font-size: 0.85rem;
	font-weight: 800;
}

.package-foot-cta,
.product-cta-band {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: 1.5rem;
	margin-top: var(--pad-section);
	padding: 3rem;
	background: var(--navy);
	border-radius: var(--radius-btn);
	color: #fff;
}

.product-cta-text {
	font-size: 1.1rem;
	font-weight: 800;
	color: #fff;
}

/* --- Closing ------------------------------------------------------------- */

.closing {
	padding-block: var(--pad-section);
	background: var(--navy-dark);
	color: #fff;
	text-align: center;
}

.closing-title {
	color: #fff;
	font-size: clamp(1.92rem, 0.443889vw + 1.77796rem, 2.3994rem);
}

.closing-lede {
	margin-top: 0.5rem;
	color: var(--navy-light);
}

.closing-actions {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: 1rem;
	margin-top: 2.5rem;
}

/* --- Filters and forms ---------------------------------------------------
   `rounded-btn border-[3px] border-navy bg-white`, and the same `min-width:
   14rem` treatment as buttons so a filter row and a button row line up.
   ------------------------------------------------------------------------- */

.filters {
	display: grid;
	gap: 1rem;
	padding: 1.5rem;
	margin-bottom: 2.5rem;
	background: var(--white);
	border: var(--border-chunky) solid var(--navy);
	border-radius: var(--radius-btn);
}

@media (min-width: 48rem) {
	.filters {
		grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
		align-items: end;
	}

	/* The query box takes the whole row above the fixed-width dropdowns.

	   It is the only free-text control on the form, and it is the one a shopper
	   actually types into - a phrase like "air purifier for a small room" has to fit
	   without scrolling in a box the auto-fit columns otherwise hold to ~11rem. The
	   span is the same `1 / -1` the actions row uses.

	   Only the search page carries this class. `/products` shares `.filters` and its
	   `q` input stays in the grid, because there it is one filter among several of
	   equal weight rather than the page's primary control. */
	.field-search {
		grid-column: 1 / -1;
	}

	/* Exactly four equal columns for the four dropdowns left on the search page.

	   `.filters` defaults to `auto-fit` + `minmax(11rem, 1fr)`, which is wrong for a
	   known item count in two ways: `auto-fit` can drop to three columns and leave one
	   orphan on its own row, and the `11rem` minimum is a hard floor - four of them plus
	   gaps and padding need ~50rem, while this media query starts at 48rem, so the row
	   overflowed its container across that whole window instead of fitting.

	   `minmax(0, 1fr)` removes the floor so the four always fit whatever the width, and
	   the cells are free to shrink below their content's intrinsic width - which is what
	   the long `<option>` text ("Tableware & cutlery", "25.000.000 ₫") needs, since a
	   select's intrinsic min-content width would otherwise push the row wide again.

	   Declared here rather than on `.filters` because `/products` has five fields, not
	   four, and hardcoding four columns there would wrap its sort control onto its own row. */
	.filters-search {
		grid-template-columns: repeat(4, minmax(0, 1fr));
	}

	.filters-actions {
		grid-column: 1 / -1;
	}
}

.field {
	display: flex;
	flex-direction: column;
	gap: 0.25rem;
	min-width: 0;
}

.field-label {
	font-size: 0.8rem;
	font-weight: 800;
	color: var(--navy);
}

.field input,
.field select,
.field textarea {
	width: 100%;
	/* Lets a control shrink below its content's intrinsic width. A `<select>`'s
	   min-content width is its longest `<option>` - "Tableware & cutlery" or
	   "25.000.000 ₫" - so without this the control pushes its grid track wider than
	   `1fr` allocated and the filter row overflows its container. `.field` already sets
	   `min-width: 0` for the same reason at the row level; this is the control level. */
	min-width: 0;
	padding: 0.75rem;
	background: var(--white);
	border: var(--border-chunky) solid var(--navy);
	border-radius: var(--radius-btn);
	/* 16px minimum. iOS Safari zooms the page on focus for anything smaller,
	   which on a phone means a shopper ends up zoomed in mid-checkout. */
	font-size: 1rem;
}

.field input:hover,
.field select:hover,
.field textarea:hover {
	background: var(--primary-light);
}

.field textarea {
	resize: vertical;
	min-height: 5rem;
}

.field-hint {
	font-size: 0.8rem;
}

.field-inline {
	flex-direction: row;
	align-items: center;
	gap: 1rem;
}

.field-row {
	display: grid;
	gap: 1rem;
}

@media (min-width: 40rem) {
	.field-row {
		grid-template-columns: 1fr 1fr;
	}
}

.required {
	color: var(--accent-red);
}

.filters-actions {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem;
}

/* --- Search results ------------------------------------------------------ */

/* The count line between the filter form and the grid. Its own class rather than a
   bare <p> so the margin can differ from the grid's without touching every <p>. */
.search-summary {
	margin: 1.5rem 0 1rem;
	font-size: 0.95rem;
	color: var(--body);
}

/* Search-term highlighting on a product card.

   The default <mark> is a bright yellow that fights the lime accent, and it has no
   padding, so it reads as a correction rather than as a match. `--primary-light` is
   the brand's own lime tint, so the highlight reads as part of the palette rather
   than as a browser default pasted over it. `color: inherit` keeps the matched text
   as legible as the rest of the card - setting it would be the one thing that could
   make a highlighted result harder to read than an unhighlighted one. */
mark {
	background: var(--primary-light);
	color: inherit;
	padding: 0 0.1em;
	border-radius: 2px;
}

/* No height of its own. The input is always half of a pair - an input and the button
   that submits it - so its height is set by whichever button it sits beside, below.
   Giving it a fixed height here was wrong for one of its two pairings: it matches a
   `.btn-sm` on the cart line and a `.btn-lg` on the product page.

   `font-size: 1rem` stays, and that is a floor rather than a preference: iOS Safari
   zooms the viewport on focus for anything under 16px, so a 14px input would leave a
   shopper zoomed into the quantity field with no obvious way back. The heights match
   via the tokens above, so nothing here has to be shrunk to make them line up. */
.qty-input {
	width: 5rem;
	padding: 0.5rem 0.75rem;
	background: var(--white);
	border: var(--border-chunky) solid var(--navy);
	border-radius: var(--radius-btn);
	font-size: 1rem;
}

/* Pairing 1: the cart line's quantity input beside its `.btn-sm` "Cập nhật". */
.line-qty .qty-input {
	height: var(--control-h);
}

/* Pairing 2: the product page's quantity input beside its `.btn-lg` "Thêm vào giỏ". */
.product-detail-form .qty-input {
	height: var(--control-h-lg);
}

/* --- Empty states, notices, breadcrumbs ---------------------------------- */

.empty {
	max-width: 32rem;
	margin-inline: auto;
	padding-block: var(--pad-section);
	text-align: center;
}

.empty-glyph {
	font-size: 3rem;
	line-height: 1.2;
}

.empty-title {
	margin-block: 1rem;
}

.empty-body {
	color: var(--body);
}

.empty-action {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: 1rem;
	margin-top: 2rem;
}

.empty-links {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: 0.5rem 0.75rem;
	padding: 0;
	margin: 2rem 0 0;
	list-style: none;
}

.empty-links a {
	display: inline-block;
	padding: 0.25rem 0.75rem;
	background: var(--mist);
	border: var(--border-chunky) solid var(--navy);
	border-radius: var(--radius-btn);
	text-decoration: none;
	font-size: 0.85rem;
	font-weight: 700;
	color: var(--navy);
}

.empty-links a:hover {
	background: var(--primary);
}

/* The 3px border is the marketing page's `border-[3px] border-navy bg-mist`. */
.notice {
	padding: 1rem 1.5rem;
	margin-block: 1.5rem;
	border: var(--border-chunky) solid var(--navy);
	border-radius: var(--radius-btn);
	font-size: 0.9rem;
	text-align: left;
}

.notice-info {
	background: var(--mist);
}

.notice-error {
	background: var(--accent-red);
	border-color: var(--navy);
	color: #fff;
}

.notice-warn {
	background: var(--primary-light);
}

.breadcrumbs ol {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem;
	padding: 0;
	margin: 0 0 2.5rem;
	list-style: none;
	font-size: 0.85rem;
	font-weight: 700;
}

.crumb + .crumb::before {
	content: '/';
	margin-inline-end: 0.5rem;
	color: var(--navy-light);
}

/* --- Pager --------------------------------------------------------------- */

.pager {
	margin-top: var(--pad-section);
	text-align: center;
}

.pager-summary {
	font-size: 0.85rem;
}

.pager-list {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: 0.25rem;
	padding: 0;
	margin-top: 1rem;
	list-style: none;
}

.pager-list a {
	display: grid;
	place-items: center;
	min-width: 2.5rem;
	padding: 0.5rem;
	border: var(--border-chunky) solid var(--navy);
	border-radius: var(--radius-btn);
	color: var(--navy);
	font-weight: 700;
	text-decoration: none;
}

.pager-list a:hover {
	background: var(--primary);
}

.pager-num.is-current {
	background: var(--navy);
	color: var(--primary);
}

/* --- Catalogue and room pages ------------------------------------------- */

.room-head {
	display: flex;
	flex-wrap: wrap;
	align-items: end;
	justify-content: space-between;
	gap: 1.5rem;
	margin-bottom: 3rem;
}

.page-lede {
	margin-top: 0.75rem;
	max-width: 44rem;
	font-size: 1.05rem;
}

.room-head-meta {
	margin-top: 0.75rem;
	font-size: 0.875rem;
	font-weight: 700;
	color: var(--accent-cyan-dark);
}

.room-group + .room-group {
	margin-top: var(--pad-section);
}

.room-filter-row {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0.5rem;
	margin-bottom: 2.5rem;
}

.room-filter-label {
	font-size: 0.875rem;
	font-weight: 700;
}

.product-detail {
	display: grid;
	gap: 2.5rem;
	margin-bottom: var(--pad-section);
}

@media (min-width: 48rem) {
	.product-detail {
		grid-template-columns: 1fr 1fr;
		align-items: start;
	}
}

.product-detail-media {
	display: grid;
	place-items: center;
	aspect-ratio: 1;
	background: var(--mist);
	border: var(--border-chunky) solid var(--navy);
	border-radius: var(--radius-btn);
}

.product-detail-glyph {
	font-size: 7rem;
	line-height: 1;
}

.product-detail-body {
	min-width: 0;
}

.product-detail-title {
	margin-block: 0.5rem;
}

.product-detail-desc {
	margin-block: 1.5rem;
}

.product-detail-price {
	font-size: 1.6rem;
	font-weight: 800;
	color: var(--navy);
}

.product-badge-inline {
	display: inline-block;
	margin-block: 0.75rem;
	padding: 0.25rem 0.75rem;
	background: var(--primary);
	border: var(--border-chunky) solid var(--navy);
	border-radius: var(--radius-btn);
	font-size: 0.8rem;
	font-weight: 800;
}

.product-badge-optional {
	background: var(--white);
	color: var(--body);
}

.product-detail-form {
	display: flex;
	flex-wrap: wrap;
	align-items: end;
	gap: 1rem;
	margin-block: 2rem;
}

.product-detail-note,
.checkout-fineprint {
	font-size: 0.8rem;
}

.product-in-cart-box {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 1rem;
	margin-block: 2rem;
	padding: 1rem 1.5rem;
	background: var(--mist);
	border: var(--border-chunky) solid var(--navy);
	border-radius: var(--radius-btn);
}

.specs {
	margin-block: var(--pad-section);
}

.spec-list {
	display: grid;
	gap: 0.5rem;
	padding: 0;
	margin: 1rem 0 0;
	list-style: none;
}

.spec-list li {
	padding-bottom: 0.5rem;
	border-bottom: 1px solid var(--navy-light);
}

.related {
	margin-block: var(--pad-section);
}

/* --- Wizard -------------------------------------------------------------- */

/* The one column the wizard's chrome and its question share. Declared as a token
   because the progress bar and the panel have to agree on it: they were 72rem and
   40rem respectively, which put the steps' left edge roughly 16rem to the left of
   the question they describe, and the bar read as a separate banner rather than as
   part of the form. One number, both consumers, no drift. */
.wizard {
	--wizard-col: 40rem;
	padding-block: 2.5rem var(--pad-section);
}

.progress {
	/* Sticky under the sticky site header, so "where am I" stays answerable while
	   answering. Step 5 lists up to 24 owned products and scrolls a long way. The
	   offset reuses `--header-h`, the same token `scroll-margin-top` uses, so the bar
	   can never end up tucked under the header if the header changes height. z-index
	   30 is below the header's 40: the bar slides under the header rather than over
	   it. */
	position: sticky;
	top: calc(var(--header-h) + 0.5rem);
	z-index: 30;

	/* Opaque, because content now passes underneath it. White matches the body, so
	   the bar reads as a continuation of the page rather than as a floating panel.
	   `padding-block` gives that background some height to cover the scroll gap. */
	background: var(--white);
	padding-block: 0.5rem;
	margin-block: -0.5rem 1.5rem;

	/* Same column as the panel, so their left edges line up. */
	width: 100%;
	max-width: var(--wizard-col);
	margin-inline: auto;

	/* The five labels do not fit a 360 px phone, so the bar scrolls sideways rather
	   than wrapping. `overscroll-behavior-x: contain` stops a sideways flick at the
	   end of the bar from also scrolling the document behind it. */
	overflow-x: auto;
	overscroll-behavior-x: contain;
	scrollbar-width: thin;
	/* A sticky box still clips its own focus rings, so the padding that keeps the
	   scrollbar off the chips is negative-margin'd away rather than added. */
	scroll-padding-inline: 0.25rem;
}

.progress ol {
	display: flex;
	gap: 0.5rem;
	padding: 0;
	margin: 0;
	list-style: none;
	min-width: max-content;
}

.progress-step {
	display: inline-flex;
	align-items: center;
	gap: 0.5rem;
	padding: 0.5rem 0.75rem;
	background: var(--mist);
	border: var(--border-chunky) solid var(--navy);
	border-radius: var(--radius-btn);
	font-size: 0.85rem;
	font-weight: 700;
	color: var(--navy);
	white-space: nowrap;
}

/* The step's position, in words rather than only in colour.

   The bar used to carry labels alone, so "Bedrooms" and "Budget" were two chips with
   no way to say which was third. `aria-hidden` in the markup: `aria-current="step"`
   already carries position to a screen reader, and announcing "3, Budget" as well is
   the kind of repetition that makes a stepper verbose to listen to.

   Every colour here is stated rather than inherited, because the current step's chip
   is navy and the old `.is-current span` rule reached this element too - lime on the
   white fill measured 1.64:1. Each pairing is named below with its own ratio. */
.progress-num {
	display: grid;
	place-items: center;
	flex-shrink: 0;
	width: 1.5rem;
	height: 1.5rem;
	background: var(--white);
	border: 2px solid var(--navy);
	border-radius: 50%;
	font-size: 0.75rem;
	font-weight: 800;
	line-height: 1;
	color: var(--navy);
}

/* Completed: filled, so "done" is visible without reading the label. Navy-dark on
   primary is 10.40:1. */
.progress-step a .progress-num {
	background: var(--primary);
	border-color: var(--navy);
	color: var(--navy-dark);
}

/* Current: white fill with a lime ring, which is what separates it from the completed
   chips' solid fill. Navy-dark on white is 17.05:1. */
.progress-step.is-current .progress-num {
	background: var(--white);
	border-color: var(--primary);
	color: var(--navy-dark);
}

.progress-step a {
	color: var(--navy);
	text-decoration: none;
}

.progress-step a:hover {
	color: var(--secondary-dark);
}

.progress-step.is-current {
	background: var(--navy);
}

/* Scoped to the label, not to `span`. Reaching every span put the lime on the
   numeral as well, where the fill is white. Primary on navy is 7.57:1. */
.progress-step.is-current .progress-label {
	color: var(--primary);
}

.wizard-panel {
	max-width: var(--wizard-col);
	margin-inline: auto;
	/* Grid children default to `min-width: auto` and refuse to shrink below their
	   content, which on a phone pushes the panel past the viewport and clips the
	   choice labels. */
	min-width: 0;
}

.wizard-step-label {
	font-size: 0.8rem;
	font-weight: 800;
	letter-spacing: 0.05em;
	text-transform: uppercase;
	color: var(--accent-cyan-dark);
}

.wizard-title {
	margin-block: 0.5rem 1rem;
	font-size: clamp(1.92rem, 0.443889vw + 1.77796rem, 2.3994rem);
}

.wizard-lede {
	margin-bottom: 2.5rem;
}

.wizard-form {
	display: flex;
	flex-direction: column;
	gap: 1.5rem;
	align-items: stretch;
}

.wizard-form > .btn {
	align-self: flex-start;
}

.wizard-actions {
	display: flex;
	flex-wrap: wrap;
	gap: 1rem;
}

.wizard-skip {
	margin-top: 1rem;
	font-size: 0.9rem;
	font-weight: 700;
}

.choices {
	display: grid;
	gap: 1rem;
	padding: 0;
	margin: 0;
	border: 0;
	/* `minmax(0, 1fr)` rather than the default `1fr`: without it the track sizes to
	   its content and a long option hint widens the whole column. */
	grid-template-columns: minmax(0, 1fr);
}

@media (min-width: 40rem) {
	.choices-2col {
		grid-template-columns: 1fr 1fr;
	}

	.choices-row {
		grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
	}
}

.choice {
	display: block;
	min-width: 0;
	cursor: pointer;
}

.choice input {
	/* Visually hidden but still focusable and still the real control. The focus ring
	   is drawn on the label instead, so keyboard focus stays visible. */
	position: absolute;
	opacity: 0;
	pointer-events: none;
}

.choice-body {
	display: flex;
	flex-direction: column;
	gap: 0.25rem;
	height: 100%;
	padding: 1.5rem;
	background: var(--white);
	border: var(--border-chunky) solid var(--navy);
	border-radius: var(--radius-btn);
	transition:
		background-color 0.15s ease,
		border-color 0.15s ease;
}

.choice:hover .choice-body {
	background: var(--mist);
}

.choice input:checked + .choice-body {
	background: var(--primary-light);
	border-color: var(--navy);
}

.choice input:focus-visible + .choice-body {
	outline: 3px solid var(--secondary);
	outline-offset: 3px;
}

.choice-label {
	font-weight: 800;
	color: var(--navy);
}

.choice-hint {
	font-size: 0.875rem;
}

.choice-traits {
	display: flex;
	flex-wrap: wrap;
	gap: 0.25rem;
	margin-top: 0.25rem;
}

.trait {
	padding: 0.25rem 0.5rem;
	background: var(--mist);
	border-radius: var(--radius-btn);
	font-size: 0.75rem;
	font-weight: 700;
	color: var(--body);
}

.own-intro {
	color: var(--body);
}

.own-group {
	margin-bottom: 0.75rem;
	background: var(--white);
	border: var(--border-chunky) solid var(--navy);
	border-radius: var(--radius-btn);
}

.own-summary {
	display: flex;
	align-items: center;
	gap: 1rem;
	padding: 1rem 1.5rem;
	cursor: pointer;
	font-weight: 800;
	color: var(--navy);
	list-style: none;
}

.own-summary::-webkit-details-marker {
	display: none;
}

.own-summary::before {
	content: '+';
	font-weight: 800;
}

.own-group[open] .own-summary::before {
	content: '\2212';
}

.own-glyph {
	font-size: 1.3rem;
}

.own-name {
	flex: 1;
}

.own-count {
	font-size: 0.8rem;
	font-weight: 700;
	color: var(--body);
}

.own-items {
	display: grid;
	gap: 0.5rem;
	padding: 0 1.5rem 1.5rem;
}

@media (min-width: 48rem) {
	.own-items {
		grid-template-columns: 1fr 1fr;
	}
}

.own-item {
	display: flex;
	align-items: start;
	gap: 1rem;
	padding: 0.5rem;
	border: var(--border-chunky) solid transparent;
	border-radius: var(--radius-btn);
	cursor: pointer;
}

.own-item:hover {
	background: var(--mist);
	border-color: var(--navy);
}

.own-item input {
	width: 1.15rem;
	height: 1.15rem;
	margin-top: 0.2rem;
	flex-shrink: 0;
	accent-color: var(--navy);
}

.own-item-body {
	display: flex;
	flex-direction: column;
	gap: 2px;
	flex: 1;
	min-width: 0;
}

.own-item-name {
	font-size: 0.9rem;
	font-weight: 700;
	line-height: 1.35;
}

.own-item-meta {
	font-size: 0.75rem;
	color: var(--body);
}

.own-item-price {
	font-size: 0.8rem;
	font-weight: 800;
	color: var(--accent-cyan-dark);
}

.own-footer {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: 1rem;
	padding-top: 1rem;
	border-top: var(--border-chunky) solid var(--navy);
}

.own-selected {
	font-size: 0.9rem;
}

/* --- Plan ---------------------------------------------------------------- */

.plan-hero {
	padding-block: 2.5rem var(--pad-section);
	background: var(--navy);
	color: #fff;
}

.plan-hero-inner {
	display: grid;
	gap: 2.5rem;
}

@media (min-width: 56rem) {
	.plan-hero-inner {
		grid-template-columns: 1fr auto;
		align-items: center;
	}
}

.plan-eyebrow {
	font-size: 0.8rem;
	font-weight: 800;
	letter-spacing: 0.05em;
	text-transform: uppercase;
	color: var(--primary);
}

.plan-title {
	margin-block: 0.75rem 1rem;
	color: #fff;
	font-size: clamp(1.92rem, 0.443889vw + 1.77796rem, 2.3994rem);
}

.plan-summary {
	color: var(--navy-light);
}

.plan-total-block {
	padding: 1.5rem;
	background: var(--navy-dark);
	border: var(--border-chunky) solid var(--secondary-semi-light);
	border-radius: var(--radius-btn);
}

.plan-total-label {
	font-size: 0.8rem;
	letter-spacing: 0.05em;
	text-transform: uppercase;
	color: var(--navy-light);
}

.plan-total {
	font-size: clamp(1.6rem, 3vw, 2.2rem);
	font-weight: 800;
	line-height: 1.2;
	color: var(--primary);
}

.plan-total-note {
	font-size: 0.85rem;
	color: var(--navy-light);
}

.meter {
	margin-top: 1rem;
}

.meter-track {
	height: 6px;
	background: rgb(255 255 255 / 0.15);
	border-radius: var(--radius-btn);
	overflow: hidden;
}

.meter-fill {
	height: 100%;
	background: var(--primary);
}

.meter-label {
	margin-top: 0.25rem;
	font-size: 0.75rem;
	color: var(--navy-light);
}

.plan {
	padding-block: var(--pad-section) var(--pad-section);
}

.plan-answers,
.plan-notes {
	padding: 1.5rem;
	margin-bottom: var(--pad-section);
	background: var(--white);
	border: var(--border-chunky) solid var(--navy);
	border-radius: var(--radius-btn);
}

.plan-notes {
	background: var(--mist);
}

.answer-list {
	margin-top: 1rem;
}

.answer-row {
	display: grid;
	grid-template-columns: 8rem 1fr auto;
	align-items: baseline;
	gap: 1rem;
	padding-block: 0.5rem;
	border-bottom: 1px solid var(--navy-light);
}

.answer-row dt {
	font-size: 0.85rem;
	color: var(--body);
}

.answer-row dd {
	font-weight: 800;
	color: var(--navy);
}

.answer-edit {
	font-size: 0.8rem;
	font-weight: 700;
}

.plan-answers-actions {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem;
	margin-top: 1rem;
}

.plan-incomplete {
	margin-top: 1rem;
	font-size: 0.875rem;
}

.note-list {
	display: grid;
	gap: 0.75rem;
	padding: 0;
	margin: 1rem 0 0;
	list-style: none;
}

.note {
	position: relative;
	padding-left: 1.5rem;
	font-size: 0.9rem;
}

.note::before {
	position: absolute;
	left: 0;
	content: '\203a';
	font-weight: 800;
	color: var(--accent-cyan-dark);
}

.plan-room {
	margin-bottom: 1.5rem;
	padding: 1.5rem;
	background: var(--white);
	border: var(--border-chunky) solid var(--navy);
	border-radius: var(--radius-btn);
}

.plan-room-head {
	display: flex;
	flex-wrap: wrap;
	align-items: start;
	justify-content: space-between;
	gap: 1rem;
	padding-bottom: 1rem;
	margin-bottom: 1rem;
	border-bottom: var(--border-chunky) solid var(--navy);
}

.plan-room-title {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	font-size: 1.15rem;
	font-weight: 800;
}

.plan-room-sub {
	font-size: 0.875rem;
}

.plan-room-figures {
	display: flex;
	flex-direction: column;
	align-items: end;
	gap: 0.5rem;
}

.plan-room-subtotal {
	font-weight: 800;
	white-space: nowrap;
	color: var(--navy);
}

.plan-items,
.line-list,
.mini-lines,
.omitted-list {
	display: grid;
	gap: 1rem;
	padding: 0;
	margin: 0;
	list-style: none;
}

.plan-item {
	display: flex;
	align-items: start;
	gap: 1rem;
	padding-bottom: 1rem;
	border-bottom: 1px solid var(--navy-light);
}

.plan-item:last-child {
	border-bottom: 0;
	padding-bottom: 0;
}

.plan-item-glyph {
	font-size: 1.6rem;
	line-height: 1.3;
}

.plan-item-body {
	flex: 1;
	min-width: 0;
}

.plan-item-name {
	font-weight: 800;
	line-height: 1.35;
}

.plan-item-name a {
	color: var(--navy);
	text-decoration: none;
}

.plan-item-name a:hover {
	color: var(--secondary-dark);
}

.plan-item-meta {
	font-size: 0.8rem;
	color: var(--body);
}

/* The reason line is the product. Italic is the marketing page's own treatment for
   supporting copy on a card, and it is what makes this read as a recommendation
   rather than a list. */
.plan-item-reason {
	margin-top: 0.5rem;
	font-size: 0.85rem;
	font-style: italic;
	color: var(--body);
}

.plan-item-foot {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 1rem;
	margin-top: 0.5rem;
}

.plan-item-price {
	font-weight: 800;
	color: var(--navy);
}

.plan-item-action {
	flex-shrink: 0;
}

.plan-item-added {
	font-size: 0.8rem;
	font-weight: 800;
	color: var(--accent-teal-dark);
}

.plan-room-empty {
	font-size: 0.9rem;
}

.plan-omitted {
	margin-top: 1rem;
	padding-top: 1rem;
	border-top: var(--border-chunky) dashed var(--navy);
	font-size: 0.875rem;
}

.plan-omitted summary {
	cursor: pointer;
	font-weight: 700;
	color: var(--navy);
}

.omitted-list {
	gap: 0.25rem;
	margin-top: 0.75rem;
}

.omitted-item {
	display: flex;
	justify-content: space-between;
	gap: 1rem;
}

.omitted-price {
	white-space: nowrap;
}

.omitted-note {
	margin-top: 0.5rem;
}

.plan-cta {
	margin-top: var(--pad-section);
	padding: 3rem;
	background: var(--white);
	border: var(--border-chunky) solid var(--navy);
	border-radius: var(--radius-btn);
	box-shadow: var(--shadow-hard);
	text-align: center;
}

.plan-cta-form {
	display: flex;
	justify-content: center;
}

.plan-cta-note {
	margin-top: 0.75rem;
	font-size: 0.85rem;
}

/* --- Cart and checkout --------------------------------------------------- */

.cart-layout,
.checkout-layout {
	display: grid;
	gap: 2.5rem;
}

@media (min-width: 56rem) {
	.cart-layout,
	.checkout-layout {
		grid-template-columns: 1fr 20rem;
		align-items: start;
	}
}

.line {
	display: flex;
	align-items: start;
	gap: 1rem;
	padding: 1.5rem;
	background: var(--white);
	border: var(--border-chunky) solid var(--navy);
	border-radius: var(--radius-btn);
}

.line-glyph {
	font-size: 2rem;
	line-height: 1.2;
}

.line-body {
	flex: 1;
	min-width: 0;
}

.line-name {
	font-weight: 800;
	line-height: 1.35;
}

.line-name a {
	color: var(--navy);
	text-decoration: none;
}

.line-meta,
.line-unit {
	font-size: 0.8rem;
}

.line-actions {
	display: flex;
	flex-direction: column;
	align-items: end;
	gap: 0.5rem;
	flex-shrink: 0;
}

.line-qty {
	display: flex;
	align-items: center;
	gap: 0.5rem;
}

.line-total {
	font-weight: 800;
	white-space: nowrap;
	color: var(--navy);
}

.cart-clear {
	margin-top: 1rem;
}

.cart-summary,
.checkout-summary {
	padding: 1.5rem;
	background: var(--white);
	border: var(--border-chunky) solid var(--navy);
	border-radius: var(--radius-btn);
}

.summary-title {
	margin-bottom: 1rem;
	font-size: 1.1rem;
}

.summary-list {
	display: grid;
	gap: 0.5rem;
	margin-bottom: 1rem;
}

.summary-row {
	display: flex;
	justify-content: space-between;
	gap: 1rem;
	font-size: 0.9rem;
}

.summary-total {
	padding-top: 0.5rem;
	border-top: var(--border-chunky) solid var(--navy);
	font-size: 1.05rem;
	font-weight: 800;
	color: var(--navy);
}

.summary-shipping-note,
.summary-note {
	margin-top: 0.5rem;
	font-size: 0.8rem;
}

.cart-summary .btn {
	margin-top: 1rem;
}

.mini-lines {
	gap: 0.5rem;
	margin-bottom: 1.5rem;
}

.mini-line {
	display: grid;
	grid-template-columns: auto 1fr auto;
	align-items: baseline;
	gap: 0.5rem;
	font-size: 0.85rem;
}

.mini-name {
	min-width: 0;
}

.mini-qty {
	margin-inline-start: 0.25rem;
}

.mini-price {
	white-space: nowrap;
	font-weight: 800;
}

.checkout-form {
	display: grid;
	gap: 1.5rem;
	margin-top: 2rem;
}

fieldset.field {
	border: 0;
	padding: 0;
	margin: 0;
}

.radio-row {
	display: flex;
	align-items: center;
	gap: 1rem;
	padding: 1rem 1.25rem;
	margin-top: 0.5rem;
	border: var(--border-chunky) solid var(--navy);
	border-radius: var(--radius-btn);
	cursor: pointer;
	transition:
		background-color 0.15s ease,
		border-color 0.15s ease;
}

.radio-row:has(input:checked) {
	background: var(--primary-light);
}

/* The control is sized rather than left at the browser default.
   A ~13px radio beside a 3px chunky border and a two-line label reads as broken
   rather than as compact, and `flex-shrink` stops a long label squeezing it. */
.radio-row input {
	width: 1.35rem;
	height: 1.35rem;
	flex-shrink: 0;
	accent-color: var(--navy);
	cursor: pointer;
}

.radio-body {
	display: flex;
	flex-direction: column;
}

.radio-label {
	font-weight: 800;
	color: var(--navy);
}

/* --- Account and Confirmation ------------------------------------------- */

.account-card,
.confirm-card {
	max-width: 42rem;
	margin-inline: auto;
	padding: 2.5rem 1.5rem;
	background: var(--white);
	border: var(--border-chunky) solid var(--navy);
	border-radius: var(--radius-btn);
	box-shadow: var(--shadow-hard);
}

.confirm-card {
	padding: 3rem 1.5rem;
	text-align: center;
}

.account-header {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 1.25rem;
	padding-bottom: 1.5rem;
	border-bottom: var(--border-chunky) solid var(--navy);
}

.account-header.text-center {
	flex-direction: column;
	text-align: center;
	justify-content: center;
}

.account-avatar {
	display: grid;
	place-items: center;
	width: 3.5rem;
	height: 3.5rem;
	background: var(--primary-light);
	border: var(--border-chunky) solid var(--navy);
	border-radius: 999px;
	font-size: 1.75rem;
	flex-shrink: 0;
}

.account-avatar-lg {
	width: 4.5rem;
	height: 4.5rem;
	font-size: 2.25rem;
	background: var(--primary);
}

.account-profile-info {
	display: flex;
	flex-direction: column;
	gap: 0.25rem;
}

.account-title {
	font-size: clamp(1.5rem, 0.4vw + 1.4rem, 1.8rem);
	font-weight: 800;
	color: var(--navy);
	margin: 0;
}

.account-email {
	font-size: 0.95rem;
	color: var(--body);
	margin: 0;
}

.account-details {
	padding-block: 1.5rem;
}

.account-info-list {
	display: grid;
	gap: 1rem;
	margin: 0;
}

.account-info-item {
	display: flex;
	flex-wrap: wrap;
	justify-content: space-between;
	align-items: center;
	gap: 0.5rem;
	padding: 0.875rem 1.25rem;
	background: var(--mist);
	border: var(--border-chunky) solid var(--navy);
	border-radius: var(--radius-btn);
}

.account-info-item dt {
	font-size: 0.875rem;
	font-weight: 700;
	color: var(--body);
}

.account-info-item dd {
	font-size: 0.95rem;
	font-weight: 800;
	color: var(--navy);
	margin: 0;
	display: flex;
	align-items: center;
	gap: 0.5rem;
}

.badge {
	display: inline-flex;
	align-items: center;
	padding: 0.15rem 0.5rem;
	border: 2px solid var(--navy);
	border-radius: var(--radius-btn);
	font-size: 0.75rem;
	font-weight: 800;
	text-transform: uppercase;
}

.badge-google {
	background: #ea4335;
	color: #fff;
	border-color: var(--navy);
}

/* The one badge that is not a provider: "Mặc định" on a saved address. Lime with navy
   text, because --primary is the app's own accent and white on it fails AA. */
.badge-accent {
	background: var(--primary);
	color: var(--navy);
	border-color: var(--navy);
}

.account-actions {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 1rem;
	padding-top: 1rem;
	border-top: var(--border-chunky) solid var(--navy);
}

.account-actions .btn {
	flex: 1 1 12rem;
}

.account-actions form {
	flex: 1 1 12rem;
	display: flex;
}

.account-actions form .btn {
	width: 100%;
}

.btn-danger {
	border-color: var(--navy);
	color: var(--accent-red);
}

.btn-danger:hover {
	background: var(--accent-red);
	color: #fff;
	border-color: var(--navy);
}

.btn-icon {
	font-size: 1.1rem;
	line-height: 1;
}

.account-login-card {
	max-width: 32rem;
}

@media (min-width: 48rem) {
	.confirm-card {
		padding: 3.5rem 3rem;
	}
}

/* --- Orders Card List --------------------------------------------------- */

.account-orders-container {
	max-width: 48rem;
}

.account-orders-head {
	margin-bottom: 2rem;
}

.account-email-sub {
	color: var(--body);
	font-size: 0.95rem;
	margin-top: 0.25rem;
}

.breadcrumbs-nav {
	margin-bottom: 1rem;
	font-weight: 700;
}

.order-card-list {
	display: grid;
	gap: 1.25rem;
	padding: 0;
	margin: 0;
	list-style: none;
}

.order-card {
	background: var(--white);
	border: var(--border-chunky) solid var(--navy);
	border-radius: var(--radius-btn);
	box-shadow: var(--shadow-hard);
	padding: 1.25rem 1.5rem;
	display: flex;
	flex-direction: column;
	gap: 1rem;
	transition: transform 0.15s ease;
}

.order-card-header {
	display: flex;
	flex-wrap: wrap;
	justify-content: space-between;
	align-items: center;
	gap: 0.5rem;
	padding-bottom: 0.875rem;
	border-bottom: 2px solid var(--mist);
}

.order-ref-badge {
	font-weight: 800;
	font-size: 1rem;
	color: var(--navy);
	background: var(--mist);
	padding: 0.25rem 0.625rem;
	border: 2px solid var(--navy);
	border-radius: var(--radius-btn);
}

.order-card-date {
	font-size: 0.875rem;
	color: var(--body);
	font-weight: 600;
}

.order-card-body {
	display: flex;
	flex-wrap: wrap;
	justify-content: space-between;
	align-items: center;
	gap: 1rem;
}

.order-card-metric {
	display: flex;
	flex-direction: column;
	gap: 0.2rem;
}

.order-metric-label {
	font-size: 0.75rem;
	text-transform: uppercase;
	letter-spacing: 0.04em;
	color: var(--body);
	font-weight: 700;
}

.order-metric-value {
	font-size: 1rem;
	font-weight: 700;
	color: var(--navy);
}

.order-total-price {
	font-size: 1.2rem;
	font-weight: 800;
	color: var(--navy);
}

.order-card-foot {
	display: flex;
	justify-content: flex-end;
	padding-top: 0.5rem;
}

/* --- Order Detail View -------------------------------------------------- */

.order-detail-container {
	max-width: 48rem;
}

.order-detail-top-nav {
	display: flex;
	justify-content: space-between;
	align-items: center;
	margin-bottom: 1.5rem;
}

.breadcrumbs-link {
	font-weight: 700;
	color: var(--navy);
	text-decoration: none;
}

.breadcrumbs-link:hover {
	text-decoration: underline;
}

.order-detail-header {
	margin-bottom: 2rem;
}

.order-detail-title-group {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: 1rem;
}

.order-ref-badge-lg {
	font-weight: 800;
	font-size: 1.25rem;
	color: var(--navy);
	background: var(--primary-light);
	padding: 0.35rem 0.875rem;
	border: var(--border-chunky) solid var(--navy);
	border-radius: var(--radius-btn);
	box-shadow: var(--shadow-hard-sm);
}

.order-detail-date {
	font-size: 0.95rem;
	font-weight: 600;
	color: var(--body);
}

.order-detail-grid {
	display: grid;
	gap: 1.5rem;
}

.order-detail-card {
	background: var(--white);
	border: var(--border-chunky) solid var(--navy);
	border-radius: var(--radius-btn);
	box-shadow: var(--shadow-hard);
	padding: 1.5rem;
}

.order-card-title {
	font-size: 1.1rem;
	font-weight: 800;
	color: var(--navy);
	margin-bottom: 1rem;
	padding-bottom: 0.5rem;
	border-bottom: 2px solid var(--mist);
}

.order-info-dl {
	display: grid;
	gap: 0.75rem;
	margin: 0;
}

.order-info-row {
	display: grid;
	grid-template-columns: 8rem 1fr;
	gap: 1rem;
	font-size: 0.95rem;
}

.order-info-row dt {
	color: var(--body);
	font-weight: 600;
}

.order-info-row dd {
	color: var(--navy);
	font-weight: 800;
	margin: 0;
}

.order-note-text {
	font-size: 0.95rem;
	color: var(--body);
	margin: 0;
	font-style: italic;
}

.order-item-list {
	list-style: none;
	padding: 0;
	margin: 0 0 1.5rem;
	display: grid;
	gap: 1rem;
}

.order-item-row {
	display: flex;
	justify-content: space-between;
	align-items: center;
	gap: 1rem;
	padding-bottom: 0.875rem;
	border-bottom: 1px solid var(--mist);
}

.order-item-row:last-child {
	border-bottom: 0;
	padding-bottom: 0;
}

.order-item-left {
	display: flex;
	align-items: center;
	gap: 1rem;
}

.order-item-glyph {
	font-size: 2rem;
	line-height: 1;
	background: var(--mist);
	padding: 0.5rem;
	border-radius: var(--radius-btn);
	border: 2px solid var(--navy);
}

.order-item-details {
	display: flex;
	flex-direction: column;
	gap: 0.2rem;
}

.order-item-name {
	font-weight: 800;
	color: var(--navy);
	font-size: 1rem;
}

.order-item-qty-price {
	font-size: 0.85rem;
	color: var(--body);
	font-weight: 600;
}

.order-item-total {
	font-weight: 800;
	color: var(--navy);
	font-size: 1rem;
	white-space: nowrap;
}

.order-detail-summary {
	margin-top: 1rem;
	padding-top: 1rem;
	border-top: var(--border-chunky) solid var(--navy);
}

.order-summary-dl {
	display: grid;
	gap: 0.5rem;
	margin: 0;
}

.order-summary-row {
	display: flex;
	justify-content: space-between;
	font-size: 0.95rem;
}

.order-summary-row dt {
	color: var(--body);
}

.order-summary-row dd {
	font-weight: 800;
	color: var(--navy);
	margin: 0;
}

.order-summary-total {
	font-size: 1.15rem;
	padding-top: 0.5rem;
	border-top: 2px solid var(--mist);
}

.order-summary-total dt,
.order-summary-total dd {
	color: var(--navy);
	font-weight: 800;
}

/* --- Address book --------------------------------------------------------- */

/*
 * The saved-address list, and the compact controls on each row.
 *
 * Every row carries up to four controls (set default, edit, delete), so the buttons are
 * deliberately smaller than the page's normal `.btn`. The compact sizing lives in
 * `.address-row-actions` rather than in a `.btn-sm` modifier so it cannot leak onto a
 * button elsewhere that happens to be inside a row.
 */
.address-book {
	max-width: 46rem;
}

.address-book-head {
	display: flex;
	flex-wrap: wrap;
	justify-content: space-between;
	align-items: center;
	gap: 0.75rem;
	margin-bottom: 1.5rem;
}

.address-book-head .section-title {
	margin: 0;
}

.address-list {
	list-style: none;
	margin: 1.5rem 0 0;
	padding: 0;
	display: grid;
	gap: 1rem;
}

.address-row {
	padding: 1.25rem;
	background: var(--white);
	border: var(--border-chunky) solid var(--navy);
	border-radius: var(--radius-btn);
	box-shadow: var(--shadow-hard);
}

.address-row-head {
	display: flex;
	flex-wrap: wrap;
	justify-content: space-between;
	align-items: baseline;
	gap: 0.5rem;
}

.address-row-name {
	font-size: 1.05rem;
	font-weight: 800;
	color: var(--navy);
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0.5rem;
}

.address-row-phone {
	font-size: 0.95rem;
	color: var(--body);
}

.address-row-text {
	margin: 0.75rem 0 1.25rem;
}

.address-row-actions {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0.5rem;
}

/* A form is a block element, so three inline forms in a flex row would each push a line. */
.address-row-actions form {
	display: inline-flex;
}

.address-row-actions .btn {
	padding: 0.5rem 0.875rem;
	min-height: 2.25rem;
	font-size: 0.85rem;
}

.address-row-actions .btn-danger {
	color: var(--accent-red);
}

/*
 * The checkout picker.
 *
 * Sits above the form it fills rather than inside it, so the `<select>` is a control in
 * its own right and the form's own fields keep their labels and order.
 */
.address-picker {
	margin-bottom: 1.5rem;
	padding: 1rem 1.25rem;
	background: var(--mist);
	border: var(--border-chunky) solid var(--navy);
	border-radius: var(--radius-btn);
}

.address-picker .field-label {
	display: block;
	margin-bottom: 0.5rem;
	font-weight: 700;
	color: var(--navy);
}

.address-picker select {
	width: 100%;
	padding: 0.5rem 0.75rem;
	background: var(--white);
	border: var(--border-chunky) solid var(--navy);
	border-radius: var(--radius-btn);
	font: inherit;
	font-size: 0.95rem;
	color: var(--navy);
}

.confirm-mark {
	display: grid;
	place-items: center;
	width: 3.5rem;
	height: 3.5rem;
	margin-inline: auto;
	margin-bottom: 1.5rem;
	background: var(--primary);
	border: var(--border-chunky) solid var(--navy);
	border-radius: 999px;
	font-size: 1.5rem;
	font-weight: 800;
	color: var(--navy-dark);
}

.confirm-title {
	margin-bottom: 0.5rem;
}

.confirm-ref {
	color: var(--body);
}

.confirm-section {
	margin-top: 2.5rem;
	padding-top: 2rem;
	border-top: var(--border-chunky) solid var(--navy);
	text-align: left;
}

.confirm-list {
	display: grid;
	gap: 0.5rem;
	margin-top: 1rem;
}

.confirm-list > div {
	display: grid;
	grid-template-columns: 8rem 1fr;
	gap: 1rem;
	font-size: 0.9rem;
}

.confirm-list dt {
	color: var(--body);
}

.confirm-actions {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: 1rem;
	margin-top: var(--pad-section);
}

/* --- Footer --------------------------------------------------------------
   `border-t-[3px] border-navy bg-navy-dark text-navy-light`, as on the marketing
   page.
   ------------------------------------------------------------------------- */

.site-footer {
	margin-top: var(--pad-section);
	padding-block: 3rem 2.5rem;
	background: var(--navy-dark);
	border-top: var(--border-chunky) solid var(--navy);
	color: var(--navy-light);
	font-size: 0.875rem;
}

.footer-grid {
	display: grid;
	gap: 2.5rem;
}

@media (min-width: 48rem) {
	.footer-grid {
		grid-template-columns: 1.5fr 1fr 1fr;
	}
}

.footer-brand {
	font-size: 1.125rem;
	font-weight: 800;
	color: #fff;
}

.footer-tagline {
	max-width: 22rem;
	margin-top: 0.5rem;
	color: rgb(186 223 248 / 0.8);
}

.footer-heading {
	margin-bottom: 0.5rem;
	font-size: 0.8rem;
	font-weight: 800;
	letter-spacing: 0.05em;
	text-transform: uppercase;
	color: rgb(186 223 248 / 0.6);
}

.site-footer ul {
	display: grid;
	gap: 0.5rem;
	padding: 0;
	margin: 0;
	list-style: none;
}

.site-footer a {
	color: var(--navy-light);
	text-decoration: none;
}

.site-footer a:hover {
	color: var(--primary);
	text-decoration: underline;
}

.footer-legal {
	margin-top: 2.5rem;
	padding-top: 1.5rem;
	border-top: 1px solid rgb(186 223 248 / 0.2);
	font-size: 0.8rem;
	color: rgb(186 223 248 / 0.6);
}

/* --- Motion and print ---------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
	html {
		scroll-behavior: auto;
	}

	*,
	*::before,
	*::after {
		animation-duration: 0.01ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: 0.01ms !important;
		scroll-behavior: auto !important;
	}
}

/* ==========================================================================
   Operator admin

   `/admin` only. Not part of the shop's design language so much as a plainer
   reading of it: the same tokens, the same widths, the same button shape, but
   none of the marketing page's furniture. The admin's chrome lives in
   `views/admin.ts` rather than in `views/layout.ts`, so nothing below can reach
   a shopper page - the reverse leak, a selector here matching a shop class,
   is the thing worth avoiding by keeping every name prefixed.
   ========================================================================== */

.admin-body {
	background: var(--mist);
	color: var(--ink);
	min-height: 100%;
}

.admin-header {
	background: var(--navy);
	color: var(--white);
	border-bottom: var(--border-chunky) solid var(--navy-dark);
}

.admin-header-inner {
	max-width: var(--wrap);
	margin: 0 auto;
	padding: 1rem var(--gutter);
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 1rem 1.5rem;
}

.admin-brand {
	color: var(--white);
	font-weight: 700;
	text-decoration: none;
	white-space: nowrap;
}

.admin-nav {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem;
	/* Takes the slack between the brand and the identity block, so a long address
	 * does not push the nav onto a second line on a narrow screen. */
	flex: 1 1 auto;
}

.admin-nav-link {
	color: var(--navy-light);
	text-decoration: none;
	padding: 0.4rem 0.75rem;
	border-radius: var(--radius-btn);
	/* Underlined rather than colour-only: the current page has to be identifiable
	 * without relying on a hue, and this is the same treatment the shop's nav uses. */
	border-bottom: 2px solid transparent;
}

.admin-nav-link:hover {
	color: var(--white);
}

.admin-nav-link.is-current {
	color: var(--white);
	border-bottom-color: var(--accent-lime, var(--accent-cyan-dark));
}

.admin-identity {
	display: flex;
	align-items: center;
	gap: 0.75rem;
}

.admin-identity-name {
	font-size: 0.875rem;
	color: var(--navy-light);
	max-width: 16rem;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.admin-main {
	max-width: var(--wrap);
	margin: 0 auto;
	padding: 2rem var(--gutter) 4rem;
}

.admin-title {
	font-size: 1.75rem;
	font-weight: 700;
	margin: 0 0 0.5rem;
}

.admin-lede {
	color: var(--body);
	margin: 0 0 1rem;
}

.admin-panel {
	background: var(--white);
	border: var(--border-chunky) solid var(--navy);
	border-radius: var(--radius-btn);
	padding: 1.5rem;
}

.admin-login {
	max-width: 26rem;
	background: var(--white);
	border: var(--border-chunky) solid var(--navy);
	border-radius: var(--radius-btn);
	padding: 1.5rem;
}

.admin-form {
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
	margin: 1rem 0;
}

.admin-label {
	font-weight: 600;
	font-size: 0.875rem;
}

/* `--control-h` rather than a padding recipe, so this input is the same height as
 * every control beside it - the reason that token exists, per the note in `:root`. */
.admin-input {
	height: var(--control-h);
	padding: 0 0.75rem;
	border: 2px solid var(--navy);
	border-radius: var(--radius-btn);
	font-size: 1rem;
	/* Never below 16px: iOS Safari zooms the page on focus below that, and an
	 * operator on a phone would be left zoomed in on the login form. */
	font-family: inherit;
	width: 100%;
}

/* ---------------------------------------------------------------------------
   The AI provider picker

   Radio buttons rather than a select, for three reasons specific to this control:
   there are only two providers, so a select hides a choice the operator is
   meant to weigh; each option carries a price and a caveat that a select's
   closed list cannot show; and changing provider has to change the model list,
   which a select cannot do without a second control that depends on it.

   The list is CSS-only where it can be, so it works with JavaScript disabled.
   --------------------------------------------------------------------------- */

.admin-fieldset {
	border: 2px solid var(--navy);
	border-radius: var(--radius-btn);
	padding: 0.75rem;
	margin: 0;
}

.admin-legend {
	font-weight: 600;
	font-size: 0.875rem;
	padding: 0 0.35rem;
}

.admin-choice {
	display: flex;
	align-items: flex-start;
	gap: 0.6rem;
	padding: 0.6rem;
	border-radius: var(--radius-btn);
	cursor: pointer;
}

.admin-choice:hover {
	background-color: rgba(0, 0, 0, 0.04);
}

.admin-choice input {
	margin-top: 0.2rem;
	/* Never below 16px: iOS Safari zooms on focus below that, and an operator
	 * checking this from a phone would be left zoomed in. */
	width: 1.1rem;
	height: 1.1rem;
	flex-shrink: 0;
	accent-color: var(--navy);
}

.admin-choice-body {
	display: flex;
	flex-direction: column;
	gap: 0.15rem;
	min-width: 0;
}

.admin-choice-label {
	font-weight: 600;
	font-size: 0.95rem;
}

.admin-choice-meta {
	font-size: 0.8rem;
	color: #555;
}

/* The price sits apart from the description so the eye can find it without
 * reading the sentence around it. */
.admin-choice-price {
	font-variant-numeric: tabular-nums;
}

.admin-choice-caveat {
	font-size: 0.8rem;
	color: #8a5a00;
}

/* A hand-typed model id. Hidden until its radio is chosen, which is done with
 * `:has()` rather than script, so the field is never hidden by a script that
 * failed to load.
 *
 * The scope is `.admin-model-group` - the fieldset wrapping the whole list - and
 * the trigger is the `.admin-source-custom` radio inside it, not the textbox's
 * own field. The two rules were previously written as
 * `.admin-model-custom:has(input[name='chat_model_source']:checked)`, which
 * asks the textbox to contain a radio that is its *sibling*: the selector can
 * never match, the box stayed `display: none` forever, and choosing "Nhập tay"
 * did nothing visible. */
.admin-model-custom {
	display: none;
}

.admin-model-group:has(.admin-source-custom:checked) .admin-model-custom {
	display: block;
}

/* Choosing a provider filters the model list, which is the whole reason both
 * providers' lists are in the document. Hidden rather than removed, so the form
 * posts a valid selection for the provider that is showing; the server reads
 * only that provider's group, because a hidden input still submits.
 *
 * Without `:has()` support both lists show at once, which is ugly but harmless:
 * every input is scoped to its provider, so a wrong group still cannot be saved. */
.admin-ai-form:has(input[name='provider'][value='cloudflare']:checked) .admin-model-group[data-provider='gemini'] {
	display: none;
}

.admin-ai-form:has(input[name='provider'][value='gemini']:checked) .admin-model-group[data-provider='cloudflare'] {
	display: none;
}

/* Which source a model id came from: our list, or the operator's own. Kept as
 * radios rather than a checkbox because both may be chosen and neither is a
 * default. */
.admin-source {
	display: flex;
	flex-wrap: wrap;
	gap: 1rem;
	align-items: center;
	font-size: 0.875rem;
}

.admin-source label {
	display: inline-flex;
	align-items: center;
	gap: 0.35rem;
	cursor: pointer;
}

/* The result of the "test before saving" button. */
.admin-probe {
	margin-top: 0.75rem;
	padding: 0.75rem;
	border-radius: var(--radius-btn);
	font-size: 0.875rem;
}

.admin-probe-ok {
	background-color: #ecfdf5;
	border: 1px solid #10b981;
	color: #065f46;
}

.admin-probe-error {
	background-color: #fef2f2;
	border: 1px solid #ef4444;
	color: #991b1b;
}

/* Says the check could not run. Distinct from both states above on purpose: an
 * amber line is "we could not tell", and reading it as either "all good" or
 * "broken" would be a guess. The palette matches the `admin-notice` neutral used
 * elsewhere in the admin. */
.admin-probe-neutral {
	background-color: #f8fafc;
	border: 1px solid #94a3b8;
	color: #334155;
}

/* The rows of the live configuration, so an operator can see what a change
 * would replace without opening a log. */
.admin-current {
	font-size: 0.875rem;
	margin: 0.5rem 0 1rem;
}

.admin-current dt {
	font-weight: 600;
	color: #555;
}

.admin-current dd {
	margin: 0 0 0.35rem;
	font-variant-numeric: tabular-nums;
}

/* The robots.txt field. Its own class rather than `.admin-input` reused, because that rule
   sets a fixed `height` and no vertical padding — correct for one line, wrong for a file the
   operator needs to read all of. `min-height` plus `resize` lets a short file stay short and a
   long one be opened up, which a fixed height would forbid. */
.admin-textarea {
	min-height: 18rem;
	padding: 0.75rem;
	border: 2px solid var(--navy);
	border-radius: var(--radius-btn);
	font-family: inherit;
	font-size: 1rem;
	line-height: 1.5;
	width: 100%;
	resize: vertical;
}

.admin-button {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	/* The shared control height, again, for the reason above. */
	min-height: var(--control-h);
	padding: 0 var(--spacing-btn-x);
	background: var(--navy);
	color: var(--white);
	border: 2px solid var(--navy);
	border-radius: var(--radius-btn);
	font-weight: 600;
	font-size: 0.9375rem;
	cursor: pointer;
	text-decoration: none;
	justify-self: start;
}

.admin-button:hover {
	background: var(--navy-hover);
}

.admin-button-quiet {
	background: transparent;
	color: var(--navy-light);
	border-color: transparent;
	padding: 0 0.5rem;
}

.admin-button-quiet:hover {
	background: var(--navy-semi-dark);
	color: var(--white);
}

.admin-notice {
	padding: 0.75rem 1rem;
	border-radius: var(--radius-btn);
	border-left: 4px solid;
	margin: 0 0 1.25rem;
}

.admin-notice-error {
	background: #fdecec;
	border-left-color: var(--accent-red);
	color: #7a1018;
}

.admin-notice-ok {
	background: #e8f6ef;
	border-left-color: var(--accent-teal-dark);
	color: #17453e;
}

.admin-panel-head {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 1rem;
	flex-wrap: wrap;
}

.admin-back {
	margin: 0 0 0.75rem;
	font-size: 0.875rem;
}

.admin-back a,
.admin-sub a {
	color: var(--navy-semi-dark);
}

.admin-sub {
	display: block;
	color: var(--body);
	font-size: 0.875rem;
	font-weight: 400;
}

/* A captured model call: the request or response as sent and received.
 *
 * A real payload is around 8 KB of JSON, and a single unbreakable line of it would
 * otherwise stretch the admin's whole layout, so it scrolls horizontally instead. The
 * cap on height keeps one turn's payload from pushing the events table off the page - it
 * is read, not skimmed, and scrolling a bounded box beats scrolling the document.
 *
 * Monospace and a light background because this is machine output. It is still escaped
 * text, never markup: the payload contains whatever the shopper typed. */
.admin-payload {
	margin: 0 0 1rem;
	padding: 0.75rem;
	max-height: 28rem;
	overflow: auto;
	border: 1px solid var(--mist);
	border-radius: 6px;
	background: var(--mist);
	font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
	font-size: 0.8125rem;
	line-height: 1.5;
	white-space: pre;
	tab-size: 2;
}

.admin-payload code {
	font: inherit;
	white-space: inherit;
}

/* The collapsed header of a captured call. Clickable, keyboard-reachable, and a real
 * disclosure rather than a div with a click handler. */
.admin-payload + .admin-legend,
details > summary {
	cursor: pointer;
}

details > summary {
	padding: 0.35rem 0;
	font-size: 0.875rem;
	color: var(--navy-semi-dark);
}

details[open] > summary {
	margin-bottom: 0.5rem;
	font-weight: 600;
}
* so the current tab is identifiable without colour - and so it survives a high-contrast * mode that drops the fills. */ .admin-tabs {
	display: flex;
	flex-wrap: wrap;
	gap: 0.25rem;
	margin: 0 0 1.25rem;
	border-bottom: 2px solid var(--mist);
}

.admin-tab {
	display: inline-flex;
	align-items: center;
	gap: 0.4rem;
	padding: 0.5rem 0.75rem;
	color: var(--body);
	text-decoration: none;
	border-bottom: 3px solid transparent;
	margin-bottom: -2px;
}

.admin-tab:hover {
	color: var(--navy);
}

.admin-tab.is-current {
	color: var(--navy);
	font-weight: 600;
	border-bottom-color: var(--navy);
}

.admin-tab-count {
	background: var(--mist);
	color: var(--body);
	border-radius: 999px;
	padding: 0.05rem 0.5rem;
	font-size: 0.75rem;
	font-variant-numeric: tabular-nums;
}

.admin-tab.is-current .admin-tab-count {
	background: var(--navy);
	color: var(--white);
}

.admin-table {
	width: 100%;
	border-collapse: collapse;
	font-size: 0.9375rem;
}

.admin-table th,
.admin-table td {
	text-align: left;
	padding: 0.7rem 0.5rem;
	border-bottom: 1px solid var(--mist);
	vertical-align: top;
}

.admin-table th {
	font-size: 0.75rem;
	text-transform: uppercase;
	letter-spacing: 0.04em;
	color: var(--body);
	font-weight: 600;
}

.admin-table td a {
	color: var(--navy-semi-dark);
	text-decoration: none;
}

.admin-table td a:hover {
	text-decoration: underline;
}

/* `tabular-nums` on the money column, so a column of prices lines up on the digit and
 * not on the proportional glyph widths. */
.admin-table td:nth-child(4) {
	font-variant-numeric: tabular-nums;
	white-space: nowrap;
}

/* The index health strip on the RAG DevTools page.
 *
 * A row of numbers rather than a definition list, because the whole point is
 * comparing counts against each other at a glance - "44 products, 0 vectors, 44
 * in FTS" is one visual read. The colour sits on the value, not the tile: the same
 * field is healthy at one number and a fault at another, so tinting the whole tile
 * would make a healthy index look like a warning.
 */
.admin-stats {
	display: flex;
	flex-wrap: wrap;
	gap: 0.75rem;
	margin: 1rem 0;
}

.admin-stat {
	min-width: 8.5rem;
	padding: 0.75rem 0.9rem;
	border: 1px solid var(--mist);
	border-radius: var(--radius-sm, 6px);
	background: var(--paper, #fff);
}

.admin-stat-value {
	display: block;
	font-size: 1.5rem;
	font-weight: 700;
	line-height: 1.2;
	font-variant-numeric: tabular-nums;
}

.admin-stat-label {
	display: block;
	margin-top: 0.2rem;
	font-size: 0.8125rem;
	color: var(--body);
}

/* The named faults under the strip. Each item says what is wrong *and* what to do
 * about it, because a bare count leaves the operator to work out which button is
 * the right one. */
.admin-list {
	margin: 0.75rem 0 1.25rem;
	padding-left: 1.25rem;
	display: grid;
	gap: 0.5rem;
	font-size: 0.9375rem;
}

.admin-list code {
	font-size: 0.875em;
}

/* The orders table's fourth column is money, and `nowrap` there keeps a column of
 * prices aligned on the digit. The DevTools tables put free text in that position
 * instead - "có · 768 chiều" - so `admin-table-dense` opts them back out. */
.admin-table-dense {
	font-size: 0.875rem;
}

.admin-table-dense th,
.admin-table-dense td {
	padding: 0.5rem 0.4rem;
}

.admin-table-dense td:nth-child(4) {
	white-space: normal;
}

.admin-status {
	display: inline-block;
	padding: 0.15rem 0.6rem;
	border-radius: 999px;
	font-size: 0.8125rem;
	font-weight: 600;
	background: var(--mist);
	color: var(--body);
	white-space: nowrap;
}

/* Tinted rather than filled: the label is the information, and it has to stay readable.
 * The borders carry the same distinction at the same contrast, so the states are not
 * distinguished by hue alone. */
.admin-status-pending {
	background: #fdf3e3;
	color: #7a4a06;
	box-shadow: inset 0 0 0 1px #e8c68a;
}

.admin-status-paid {
	background: #e8f0fd;
	color: #16457e;
	box-shadow: inset 0 0 0 1px #a8c6ee;
}

.admin-status-shipped {
	background: #e6f6f3;
	color: #17564c;
	box-shadow: inset 0 0 0 1px #93d5cb;
}

.admin-status-done {
	background: #e9f6ec;
	color: #1d5c2c;
	box-shadow: inset 0 0 0 1px #a5d9b0;
}

.admin-status-cancelled {
	background: #fdecec;
	color: #7a1018;
	box-shadow: inset 0 0 0 1px #efb0b4;
}

.admin-actions {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem;
	margin: 1.25rem 0;
}

.admin-actions form {
	margin: 0;
}

@media print {
	.site-header,
	.site-footer,
	.btn,
	.pager,
	.plan-answers-actions,
	.plan-cta,
	.product-cta-band {
		display: none !important;
	}

	body {
		background: #fff;
		color: #000;
		font-size: 12pt;
	}

	a[href^='http']::after {
		content: ' (' attr(href) ')';
		font-size: 9pt;
		color: #555;
	}

	.agent-fab,
	.agent-panel {
		display: none !important;
	}
}

/* Shopping Agent Widget */
.agent-fab {
	position: fixed;
	bottom: 1.5rem;
	right: 1.5rem;
	z-index: 50;
	background-color: #17453e;
	color: #ffffff;
	border: 2px solid #17453e;
	border-radius: 9999px;
	padding: 0.75rem 1.25rem;
	font-weight: 700;
	box-shadow:
		0 10px 15px -3px rgba(0, 0, 0, 0.1),
		0 4px 6px -2px rgba(0, 0, 0, 0.05);
	cursor: pointer;
	transition:
		background-color 0.2s,
		transform 0.2s;
}

.agent-fab:hover {
	background-color: #0f302b;
	transform: translateY(-2px);
}

.agent-panel {
	position: fixed;
	bottom: 5rem;
	right: 1.5rem;
	z-index: 50;
	width: 24rem;
	max-width: calc(100vw - 3rem);
	height: 32rem;
	max-height: calc(100vh - 7rem);
	background-color: #ffffff;
	border: 2px solid #17453e;
	border-radius: 0.75rem;
	box-shadow:
		0 20px 25px -5px rgba(0, 0, 0, 0.1),
		0 10px 10px -5px rgba(0, 0, 0, 0.04);
	display: flex;
	flex-direction: column;
	overflow: hidden;
}

.agent-panel[hidden] {
	display: none !important;
}

.agent-header {
	background-color: #17453e;
	color: #ffffff;
	padding: 0.75rem 1rem;
	display: flex;
	justify-content: space-between;
	align-items: center;
}

.agent-header-title {
	display: flex;
	align-items: center;
	gap: 0.5rem;
}

.agent-close-btn {
	background: none;
	border: none;
	color: #ffffff;
	font-size: 1.25rem;
	cursor: pointer;
	padding: 0.25rem;
	line-height: 1;
}

.agent-body {
	flex: 1;
	padding: 1rem;
	overflow-y: auto;
	background-color: #f9fafb;
}

.agent-messages {
	list-style: none;
	padding: 0;
	margin: 0;
	display: flex;
	flex-direction: column;
	gap: 0.75rem;
}

.agent-msg {
	display: flex;
	flex-direction: column;
	max-width: 85%;
}

.agent-msg-user {
	align-self: flex-end;
}

.agent-msg-user .agent-msg-content {
	background-color: #17453e;
	color: #ffffff;
	border-radius: 1rem 1rem 0 1rem;
	padding: 0.6rem 0.9rem;
	font-size: 0.9rem;
}

.agent-msg-assistant {
	align-self: flex-start;
}

.agent-msg-assistant .agent-msg-content {
	background-color: #e5e7eb;
	color: #111827;
	border-radius: 1rem 1rem 1rem 0;
	padding: 0.6rem 0.9rem;
	font-size: 0.9rem;
	white-space: pre-wrap;
}

.agent-proposals {
	margin-top: 0.5rem;
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
}

.agent-proposal-chip {
	background-color: #ffffff;
	border: 1px solid #d1d5db;
	border-radius: 0.5rem;
	padding: 0.5rem;
	display: flex;
	justify-content: space-between;
	align-items: center;
	font-size: 0.85rem;
}

.agent-citations {
	font-size: 0.75rem;
	color: #6b7280;
	margin-top: 0.25rem;
	display: flex;
	flex-wrap: wrap;
	gap: 0.35rem;
	align-items: baseline;
}

/* A citation the model wrote inline, turned into a link to the product page. */
.agent-cite {
	font-size: 0.75rem;
	color: #2563eb;
	background-color: #eff6ff;
	border-radius: 0.25rem;
	padding: 0 0.2rem;
	text-decoration: none;
}

.agent-cite:hover {
	text-decoration: underline;
}

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

   A search answered in prose alone left the shopper with nothing to click. The
   card is what makes the add-to-cart button possible, so the price and the
   button sit on one line: a shopper deciding between two beds should not have
   to scroll to act on either.
   --------------------------------------------------------------------------- */

.agent-product-list {
	list-style: none;
	margin: 0.75rem 0 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
}

.agent-product {
	display: flex;
	align-items: center;
	gap: 0.75rem;
	background-color: #ffffff;
	border: 1px solid #e5e7eb;
	border-radius: 0.75rem;
	padding: 0.6rem 0.75rem;
}

.agent-product-glyph {
	font-size: 1.5rem;
	line-height: 1;
	flex-shrink: 0;
}

.agent-product-info {
	display: flex;
	flex-direction: column;
	gap: 0.15rem;
	min-width: 0;
	flex: 1;
}

.agent-product-name {
	font-size: 0.9rem;
	font-weight: 600;
	color: #111827;
	text-decoration: none;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.agent-product-name:hover {
	text-decoration: underline;
}

.agent-product-meta {
	font-size: 0.8rem;
	color: #6b7280;
}

.agent-add {
	flex-shrink: 0;
	white-space: nowrap;
}

.agent-footer {
	padding: 0.75rem;
	background-color: #ffffff;
	border-top: 1px solid #e5e7eb;
}

.agent-input-row {
	display: flex;
	gap: 0.5rem;
}

.agent-input {
	flex: 1;
	border: 1px solid #d1d5db;
	border-radius: 0.375rem;
	padding: 0.5rem;
	font-size: 0.875rem;
	resize: none;
}

.agent-send-btn {
	background-color: #17453e;
	color: #ffffff;
	border: none;
	border-radius: 0.375rem;
	padding: 0 1rem;
	font-weight: 600;
	cursor: pointer;
}

.agent-disclaimer {
	font-size: 0.7rem;
	color: #9ca3af;
	margin: 0.375rem 0 0 0;
	text-align: center;
}

/*
 * The panel's conversation history.
 *
 * The header is a three-button row now rather than a title and a close, so the
 * controls sit in their own element: without one, `justify-content: space-between`
 * puts the new button against the title and the close button at the far edge.
 */
.agent-header-actions {
	display: flex;
	align-items: center;
	gap: 0.25rem;
}

/* Same shape as the close button, one size down - these are not the primary action. */
.agent-header-btn {
	background: none;
	border: none;
	color: #ffffff;
	font-size: 1rem;
	cursor: pointer;
	padding: 0.25rem 0.375rem;
	line-height: 1;
	border-radius: 0.25rem;
}

.agent-header-btn:hover {
	background-color: rgba(255, 255, 255, 0.15);
}

.agent-header-btn[hidden] {
	display: none !important;
}

.agent-drawer {
	background-color: #ffffff;
	border-bottom: 1px solid #e5e7eb;
	padding: 0.75rem 1rem;
	max-height: 15rem;
	overflow-y: auto;
}

.agent-drawer[hidden] {
	display: none !important;
}

.agent-drawer-head {
	display: flex;
	justify-content: space-between;
	align-items: center;
	gap: 0.5rem;
	margin-bottom: 0.5rem;
	font-size: 0.85rem;
}

.agent-drawer-close {
	background: none;
	border: none;
	color: #6b7280;
	font-size: 0.9rem;
	cursor: pointer;
	padding: 0.125rem 0.25rem;
	line-height: 1;
}

.agent-drawer-list {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: 0.375rem;
}

/* A whole row is the control: a shopper is picking one conversation out of a list,
   and a link-sized hit area for that is a miss. */
.agent-drawer-item {
	width: 100%;
	display: flex;
	justify-content: space-between;
	align-items: baseline;
	gap: 0.75rem;
	text-align: left;
	background-color: #f9fafb;
	border: 1px solid #e5e7eb;
	border-radius: 0.375rem;
	padding: 0.5rem 0.625rem;
	cursor: pointer;
	font: inherit;
}

.agent-drawer-item:hover {
	background-color: #e5e7eb;
}

.agent-drawer-item-title {
	font-size: 0.85rem;
	color: #111827;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.agent-drawer-item-date {
	font-size: 0.7rem;
	color: #6b7280;
	flex-shrink: 0;
}

.agent-drawer-empty {
	font-size: 0.8rem;
	color: #6b7280;
	padding: 0.25rem 0;
}

.agent-drawer-foot {
	font-size: 0.7rem;
	color: #9ca3af;
	margin: 0.625rem 0 0 0;
}

/*
 * `/account/assistant` - the same rows as the address book, under their own names.
 *
 * The address book's `.address-row` rules are not reused: they carry layout that only
 * makes sense for a name, a phone number and an address, and a conversation has a
 * title, a date and three actions. The visual tokens are the same, which is what keeps
 * the two pages looking like one shop.
 */
.agent-section {
	margin-top: 2rem;
}

.agent-section-title {
	font-size: 1.15rem;
	font-weight: 800;
	color: var(--navy);
	margin: 0 0 0.5rem;
}

.agent-section-foot {
	margin-top: 1rem;
}

.chat-list,
.memory-list {
	list-style: none;
	margin: 1rem 0 0;
	padding: 0;
	display: grid;
	gap: 1rem;
}

.chat-row,
.memory-row {
	padding: 1.25rem;
	background: var(--white);
	border: var(--border-chunky) solid var(--navy);
	border-radius: var(--radius-btn);
	box-shadow: var(--shadow-hard);
}

.chat-row-head,
.memory-row-head {
	display: flex;
	flex-wrap: wrap;
	justify-content: space-between;
	align-items: baseline;
	gap: 0.5rem;
}

.chat-row-title,
.memory-row-key {
	font-size: 1.05rem;
	font-weight: 800;
	color: var(--navy);
}

.chat-row-date {
	font-size: 0.9rem;
	color: var(--body);
}

.chat-row-actions,
.memory-row-actions {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0.5rem;
	margin-top: 0.75rem;
}

/* A form is a block element, so two inline forms in a flex row would each push a line. */
.chat-row-actions form,
.memory-row-actions form {
	display: inline-flex;
}

.chat-row-actions .btn,
.memory-row-actions .btn {
	padding: 0.5rem 0.875rem;
	min-height: 2.25rem;
	font-size: 0.85rem;
}

.chat-row-actions .btn-danger,
.memory-row-actions .btn-danger {
	color: var(--accent-red);
}

.chat-row-rename {
	flex: 1;
	min-width: 16rem;
	gap: 0.5rem;
}

.chat-row-rename input {
	flex: 1;
	min-width: 0;
	padding: 0.4rem 0.6rem;
	border: 1px solid var(--mist);
	border-radius: 0.375rem;
	font: inherit;
	font-size: 0.85rem;
}

.memory-row-value {
	margin: 0.5rem 0 0;
}

/* The shopper's own words, from the turn the preference was learned in. */
.memory-row-quote {
	font-size: 0.85rem;
	color: var(--body);
	font-style: italic;
	flex: 1;
	min-width: 12rem;
}
