/* =========================================================================
   Crystalwala — parent-brand alignment layer.

   Crystalwala is a CHILD BRAND of Certified Crystal, so the storefront should
   read as part of the same house. This layer aligns the palette, the card
   anatomy, the section rhythm and the product page to certifiedcrystal.com.

   Everything here was measured from the live parent site on 2026-09-02 by
   driving it with Playwright -- it is a React SPA and `curl` returns a
   2,587-byte empty shell, so a browser is the only way to read it.

   MEASURED PALETTE (most-used first, by element count):
     #35251D  brown ink        945   #6C1322  maroon           462
     #FBBF24  amber            396   #725B4F  muted brown      379
     #F59E0B  amber deep       231   #047857  emerald          100
     #FAECDB  cream           61     #FFF5DB  cream light      41
     #FFFAEB  cream lightest  35

   Loaded LAST, after elevation.css, so it is the layer that wins.

   ⚠️ WHAT IS DELIBERATELY *NOT* COPIED, and why. The parent leads on
   certification and social proof: a "100% Authentic & Certified" strip, four
   badges reading Lab Certified / Sacredly Sourced / Traceable for Life /
   Ritually Blessed, per-card star ratings with review counts, a
   CERTIFIED / TEST / ORIGINAL micro-row on every card, "2,749 units sold last
   week", "10 Lakh+ Devotees", and a "Govt. Lab Certified" line at checkout.

   Crystalwala can back NONE of it. CW-13's answer from the client is that the
   certification is "just a website name"; there is no certificate data, no
   origin data, no weights, no reviews and no orders. Copying those elements
   would put the exact claims SESSION-LOG section 9 records this catalogue
   being burned by once already back on the page. What IS copied is everything
   structural: layout, colour, type, card anatomy, section rhythm, CTA
   hierarchy and the discount treatment -- applied only to the 513 of 2,314
   variants that carry a genuine MRP.
   ========================================================================= */

:root {
	/* Aligned to the parent's measured values. tokens.css is GENERATED from
	   theme.json and must not be hand-edited, so the alignment lives here. */
	--cw-color-ink:        #35251D;
	--cw-color-ink-soft:   #57453B;
	--cw-color-muted:      #725B4F;
	--cw-color-maroon:     #6C1322;
	--cw-color-maroon-dark:#4E0D19;
	--cw-color-gold:       #C08A2A;
	--cw-color-amber:      #FBBF24;
	--cw-color-amber-deep: #F59E0B;
	--cw-color-ground:     #FFFAEB;
	--cw-color-ground-alt: #FAECDB;
	--cw-color-line:       #EADFCF;
	--cw-color-save:       #047857;
	--cw-color-save-soft:  #D9F2E6;

	/* ONE reading measure for the whole product page, in rem so that every
	   block using it computes the same width whatever its own font-size. */
	--cw-measure: 44rem;

	/* The WordPress-preset aliases the ported storefront.css was written
	   against. Without these the old names keep the old values and the page
	   ends up in two palettes at once. */
	--wp--preset--color--ink:        var(--cw-color-ink);
	--wp--preset--color--maroon:     var(--cw-color-maroon);
	--wp--preset--color--gold:       var(--cw-color-gold);
	--wp--preset--color--ground:     var(--cw-color-ground);
	--wp--preset--color--ground-alt: var(--cw-color-ground-alt);
	--wp--preset--color--line:       var(--cw-color-line);
}

/* ---------- announcement strip ---------- */

/* The parent runs three separate promises across the bar rather than one
   sentence, which reads as a list of reasons instead of a slogan. Three
   columns on desktop, the middle one only on a phone -- a 390px screen cannot
   hold three and wrapping to two lines made the header 137px tall. */
.cw-announce {
	display: grid;
	grid-template-columns: 1fr auto 1fr;
	align-items: center;
	gap: 1rem;
	font-size: .8125rem;
}

.cw-announce > :first-child  { justify-self: start; }
.cw-announce > :last-child   { justify-self: end; }

@media (max-width: 860px) {
	.cw-announce { grid-template-columns: 1fr; text-align: center; }
	.cw-announce > :first-child,
	.cw-announce > :last-child { display: none; }
}

/* ---------- product card ---------- */

/* The parent's card is a white panel with a hairline, a centred name, a price
   row that puts the saving first, and a full-width action. Ours had the price
   row but no visible saving -- see the note in products/card.blade.php: the
   badge had never rendered on any product because the card API sends no
   `prices` object and the computed divided by an undefined. */

.cw-card {
	border: 1px solid var(--cw-color-line);
	border-radius: 16px;
	background: #FFFFFF;
}

.cw-card__title {
	text-align: center;
	min-height: 2.7em;
}

.cw-card__price {
	display: flex;
	align-items: baseline;
	justify-content: center;
	flex-wrap: wrap;
	gap: .5rem;
	padding-inline: .9rem;
}

/* Bagisto emits the struck original first and the price being charged second,
   with no classes we can rely on beyond `line-through`. */
.cw-card__price .line-through {
	font-size: .875rem;
	font-weight: 500;
	color: var(--cw-color-muted);
	order: 2;
}

.cw-card__price p:not(.line-through) {
	font-size: 1.125rem;
	font-weight: 700;
	color: var(--cw-color-ink);
	order: 3;
	font-variant-numeric: tabular-nums;
}

/* The saving reads as a gain, so it is the parent's emerald rather than the
   red a "sale" tag usually wears. Red on a devotional catalogue also fights
   the maroon. */
.cw-badge--sale {
	background: var(--cw-color-save-soft);
	color: var(--cw-color-save);
	border: 1px solid rgba(4, 120, 87, .22);
	border-radius: 6px;
	font-size: .72rem;
	font-weight: 700;
	letter-spacing: .04em;
	text-transform: uppercase;
	padding: .18rem .42rem;
}

/* A full-width action, the parent's single strongest card signal: the shopper
   never has to find the button. */
.cw-card__cta {
	padding: .85rem .9rem 0;
}

.cw-card__cta a,
.cw-card__cta button {
	display: block;
	width: 100%;
	text-align: center;
	padding: .68rem 1rem;
	border-radius: 10px;
	background: var(--cw-color-maroon);
	color: #FFF7EC;
	font-size: .9rem;
	font-weight: 600;
	border: 1px solid var(--cw-color-maroon);
	text-decoration: none;
	cursor: pointer;
	transition: background-color 180ms ease, transform 180ms ease;
}

.cw-card__cta a:hover,
.cw-card__cta button:hover {
	background: var(--cw-color-maroon-dark);
	color: #FFF7EC;
}

.cw-card__cta button:disabled { opacity: .6; cursor: progress; }

/* Sold out is a state, not an offer: same shape, no invitation. */
.cw-card__cta .cw-btn--soldout,
.cw-card__cta .cw-btn--soldout:hover {
	background: var(--cw-color-ground-alt);
	color: var(--cw-color-muted);
	border-color: var(--cw-color-line);
	cursor: default;
}

/* ---------- section rhythm ---------- */

/* Maroon display heading, muted one-line subtitle, centred. The parent repeats
   this exact block for Browse Categories, Best Seller and every collection,
   which is what makes a long home page read as one page. */
.cw-body main .cw-sec__title,
.cw-body main h2.cw-sec__title {
	color: var(--cw-color-maroon);
}

.cw-body .cw-sec__sub {
	color: var(--cw-color-muted);
	font-size: 1rem;
	text-wrap: balance;
}

/* ---------- pills ---------- */

/* The parent's filter and collection tabs: a rounded outline that fills maroon
   when active. Reused here for the category chips. */
.cw-pill--tab {
	display: inline-flex;
	align-items: center;
	min-height: 40px;
	padding: .5rem 1.15rem;
	border: 1px solid var(--cw-color-line);
	border-radius: 999px;
	background: #FFFFFF;
	color: var(--cw-color-ink);
	font-size: .9rem;
	font-weight: 600;
	text-decoration: none;
	transition: background-color 160ms ease, color 160ms ease, border-color 160ms ease;
}

.cw-pill--tab:hover { border-color: var(--cw-color-maroon); }

.cw-pill--tab.is-on,
.cw-pill--tab[aria-current="page"] {
	background: var(--cw-color-maroon);
	border-color: var(--cw-color-maroon);
	color: #FFF7EC;
}

/* =========================================================================
   PRODUCT PAGE — the tab panels.

   Reported and confirmed: Description, Additional Information and Reviews had
   no padding, no measure and no vertical rhythm. Measured on
   /opal-oval-pendant at 1440px:

     - Description ran 1,150px lines, about 150 characters, as five
       <br>-separated lines with no paragraph spacing.
     - Additional Information rendered a two-column grid flush to the page
       edge, and one row was a bare "No" with NO LABEL BESIDE IT -- every
       attribute we created had an empty `attribute_translations.name`, fixed
       in Catalog/AttributeSet.php.
     - Reviews rendered a 190px grey star and roughly 500px of dead space
       above a sentence, with no way to write one.
   ========================================================================= */

/* The tab strip: a band the eye can find, with the active tab carrying the
   brand colour rather than a neutral underline. */
.cw-body main [role="tablist"] { border-bottom: 1px solid var(--cw-color-line); }

/* ---------- every tab panel ---------- */

/* One measure, one rhythm, centred.

   All three panels were flush to the left edge of a 1440px page with no top
   padding and no bottom padding: the Description started 0px under the tab bar
   and ran into the services strip, and half the page was empty to its right.
   Bagisto's panels are bare `.container` divs with a margin and nothing else. */
.cw-body main .container:has(> .grid.grid-cols-\[auto_1fr\]),
.cw-body main [class*="animate-"] > .container {
	max-width: 68rem;
	margin-inline: auto;
	padding-block: 3rem 3.5rem;
	padding-inline: clamp(1rem, 4vw, 3rem);
}

/* ---------- description ---------- */

/* The copy is <br>-separated sentences, not paragraphs -- that is how CW-07
   generated it -- so the rhythm has to come from the line breaks themselves. */
/* Note the `p` as well as the `div`. Bagisto renders the description as
   `<p class="text-lg text-zinc-500">`, not a div -- targeting only `> div`
   capped nothing and the copy ran the full width. And `margin-inline: auto`
   matters as much as the cap: with a measure but no centring the text sat
   hard against the left edge of a 1088px container with 400px of empty page
   beside it. */
/* ⚠️ `:not()` ADDED 2026-09-02 (the product-page rebuild). This rule caps EVERY direct child of a
   tab panel at a 68ch reading measure, which is right for the Description and
   wrong for anything laid out in columns. The Specification and Benefits grids
   are direct children of a panel, so they were silently squeezed to 642.9px
   inside a 1088px container and rendered two cards wide instead of five --
   with no error anywhere, just a narrow-looking page.

   A measure is for prose. Exclude the card grids by name rather than loosening
   the rule, so the Description keeps its measure. */
/* 🔴 THE BROAD SELECTORS THAT USED TO BE HERE ARE GONE — 2026-09-03.
   ─────────────────────────────────────────────────────────────────────────
   This rule used to read:

       .cw-body main [class*="animate-"] .container > p,
       .cw-body main [class*="animate-"] .container > div:not(.cw-spec):not(.cw-benefits):not(.cw-faqs),
       .cw-body main .cw-prose

   i.e. "cap EVERY direct child of a tab panel at a reading measure". That is
   right for prose and wrong for anything laid out in columns, and it had
   already been patched three times by naming exceptions — `.cw-spec`,
   `.cw-benefits`, `.cw-faqs`.

   THE FOURTH VICTIM WAS THE REVIEWS BLOCK, and it has no class to exclude.
   Bagisto renders the review summary and the review list as a two-column
   layout inside a plain `<div>`; this rule squeezed it to 704px inside a
   1440px panel, so the whole thing sat in the left two-thirds with a void
   beside it. Measured: `max-width 704px, margin-inline 278px/278px`, found
   with CSSOM by listing every rule that actually matched the element.

   The exception list was the wrong shape of fix. The description now always
   carries `.cw-prose` (the theme's own copy of products/view.blade.php wraps
   it, on desktop and in the mobile accordion), so the measure can be applied
   to the thing that wants it BY NAME instead of to everything by position.
   Nothing needs excluding any more.

   ⚠️ Do not reintroduce a positional selector here. If new prose needs a
   measure, give it `.cw-prose`. */
.cw-body main .cw-prose {
	/* rem, not ch -- see the note on .cw-panel-h. A ch measure made the panel
	   heading and the paragraph under it centre to different widths. */
	max-width: var(--cw-measure) !important;
	margin-inline: auto !important;
	font-size: 1.0625rem;
	line-height: 1.9;
	color: var(--cw-color-ink-soft);
}

/* The copy is one sentence per line -- that is how CW-07 generated it -- so the
   rhythm has to come from the breaks. Whether they arrive as <br> or as nested
   paragraphs, both get the same spacing. */
.cw-body main .cw-prose br {
	content: "";
	display: block;
	margin-bottom: .55em;
}

.cw-body main .cw-prose > p {
	margin: 0 0 .55em;
}

/* ---------- additional information ---------- */

/* A specification table, because that is what it is. Bagisto's markup is a
   two-column grid with a 1rem gap -- and a gap in a bordered table shows the
   panel's background straight through the middle of every row, which is what
   produced the stray vertical line down the middle. The gap goes; the cells
   carry the padding instead. */
.cw-body main .grid.grid-cols-\[auto_1fr\] {
	width: 100%;
	max-width: 44rem;
	margin-inline: auto;
	column-gap: 0 !important;
	row-gap: 0 !important;
	border: 1px solid var(--cw-color-line);
	border-radius: 14px;
	overflow: hidden;
	background: var(--cw-color-surface);
}

.cw-body main .grid.grid-cols-\[auto_1fr\] > div {
	padding: .95rem 1.35rem;
	border-top: 1px solid var(--cw-color-line);
	align-content: center;
}

.cw-body main .grid.grid-cols-\[auto_1fr\] > div:nth-child(1),
.cw-body main .grid.grid-cols-\[auto_1fr\] > div:nth-child(2) { border-top: 0; }

.cw-body main .grid.grid-cols-\[auto_1fr\] > div:nth-child(odd) {
	background: var(--cw-color-ground);
	border-inline-end: 1px solid var(--cw-color-line);
	min-width: 12rem;
}

.cw-body main .grid.grid-cols-\[auto_1fr\] p {
	font-size: .95rem;
	line-height: 1.5;
	margin: 0;
}

.cw-body main .grid.grid-cols-\[auto_1fr\] > div:nth-child(odd) p {
	font-weight: 600;
	color: var(--cw-color-ink);
}

/* ---------- write a review ---------- */

/* The form was squeezed into the left half of the page with a 320px comment box,
   a tall cream upload panel floating beside it with nothing to align to, and the
   submit buttons pushed below the fold. It is a card now, centred, with fields
   sized to what they hold. */
.cw-body main form.grid.grid-cols-\[auto_1fr\] {
	max-width: 46rem;
	margin-inline: auto;
	grid-template-columns: 1fr !important;
	gap: 1.25rem !important;
	padding: 1.75rem !important;
	background: var(--cw-color-surface);
	border: 1px solid var(--cw-color-line);
	border-radius: 16px;
	box-shadow: var(--cw-shadow-soft);
	overflow: visible;
}

.cw-body main form.grid.grid-cols-\[auto_1fr\] > div {
	border: 0 !important;
	background: transparent !important;
	padding: 0 !important;
	max-width: none !important;
	min-width: 0 !important;
}

/* A heading, because the form arrives with none: the shopper clicks "Write a
   Review" and lands on a bare rating control with no confirmation of what they
   are doing. There is no render event inside this form to inject markup at, so
   the label is drawn here. */
.cw-body main form.grid.grid-cols-\[auto_1fr\]::before {
	content: "Write your review";
	display: block;
	font-family: var(--cw-font-display);
	font-size: 1.5rem;
	font-weight: 600;
	color: var(--cw-color-maroon);
	padding-bottom: .25rem;
	border-bottom: 1px solid var(--cw-color-line);
	margin-bottom: .5rem;
}

/* The photo upload goes LAST. Bagisto puts it first, so the very first thing a
   shopper meets after clicking "Write a Review" is a 200px empty grey box
   asking for a photograph -- before they have been asked for a rating, a name
   or a single word of opinion. It is the optional extra, so it comes after the
   thing it is extra to. */
.cw-body main form.grid.grid-cols-\[auto_1fr\] > div:first-of-type { order: 5; }

.cw-body main form.grid.grid-cols-\[auto_1fr\] [class*="border-dashed"],
.cw-body main form.grid.grid-cols-\[auto_1fr\] label {
	border-radius: 12px;
}

/* The dropzone is 200x200 of empty cream by default. A third of that is plenty
   to read as "drop a photo here". */
.cw-body main form.grid.grid-cols-\[auto_1fr\] > div:first-of-type label,
.cw-body main form.grid.grid-cols-\[auto_1fr\] > div:first-of-type [class*="h-\["] {
	min-height: 84px !important;
	height: 84px !important;
	max-width: 100% !important;
}

/* A twelve-row comment box is intimidating and pushes the buttons off screen. */
.cw-body main form textarea { min-height: 132px !important; height: 132px !important; }

/* Submit is the action; Cancel is the way out. Bagisto has them the other way
   round -- amber Submit next to a maroon Cancel -- so the strongest colour on
   the form was the button that throws the review away. */
.cw-body main form button[type="submit"] {
	background: var(--cw-color-maroon) !important;
	border-color: var(--cw-color-maroon) !important;
	color: #FFF7EC !important;
}

.cw-body main form button[type="button"].secondary-button {
	background: transparent !important;
	border: 1px solid var(--cw-color-line) !important;
	color: var(--cw-color-ink) !important;
	box-shadow: none;
}

/* The rating stars are the first control and carry no label of their own. */
.cw-body main form .icon-star-fill,
.cw-body main form [class*="icon-star"] { font-size: 1.6rem; }

/* ---------- product page: the buy box ---------- */

/* The parent pairs a maroon "Add to Cart" with an amber "Buy Now" and keeps
   both above the fold. Ours had one button and a lot of empty space. The amber
   is the parent's exact accent and is the only place on the page it appears,
   so it reads as THE action. */
/* ⚠️ NOTE WHICH CLASS IS WHICH. Bagisto renders ADD TO CART with
   `secondary-button` and BUY NOW with `primary-button` -- the opposite of what
   the names suggest. Styling them by name put the weak cream fill on the main
   action and the strong maroon on the secondary one, which is exactly backwards
   for a store where most shoppers add and keep browsing.

   Add to Cart is the maroon primary. Buy Now is the parent's amber, the one
   place that colour appears on the page, so it reads as the shortcut it is. */
/* `!important` throughout, and not out of laziness: elevation.css already
   styles `.primary-button` and `.secondary-button` with `!important` on every
   declaration, so a plain rule here -- even a more specific one -- loses.
   Higher specificity does not beat `!important`; only `!important` does. */
.cw-body main .secondary-button {
	background: var(--cw-color-maroon) !important;
	border: 1px solid var(--cw-color-maroon) !important;
	color: #FFF7EC !important;
	border-radius: 12px !important;
	font-weight: 700 !important;
	min-height: 50px;
	box-shadow: 0 12px 26px -18px rgba(108, 19, 34, .9);
}

.cw-body main .secondary-button:hover {
	background: var(--cw-color-maroon-dark) !important;
	color: #FFF7EC !important;
}

.cw-body main .primary-button {
	background: var(--cw-color-amber) !important;
	border: 1px solid var(--cw-color-amber-deep) !important;
	color: #3A2408 !important;
	border-radius: 12px !important;
	font-weight: 700 !important;
	min-height: 50px;
	box-shadow: 0 12px 26px -18px rgba(245, 158, 11, .8);
}

.cw-body main .primary-button:hover {
	background: var(--cw-color-amber-deep) !important;
	color: #3A2408 !important;
}

/* The quantity stepper: the parent's is a bordered group with maroon controls,
   which reads as one control rather than three loose boxes. */
.cw-body main .quantity-changer,
.cw-body main [class*="quantity"] {
	border-radius: 10px;
}

@media (prefers-reduced-motion: reduce) {
	.cw-card__cta a,
	.cw-card__cta button,
	.cw-pill--tab { transition: none; }
}

/* =========================================================================
   CART DRAWER

   The confirmation a shopper gets when something goes in the basket. Before
   this there was a green flash at the top of a 1,745px page -- entirely
   off-screen at the moment it mattered.

   `transform` and `opacity` only: both are compositor properties, so the panel
   slides at 60fps without laying the page out again on every frame.
   ========================================================================= */

[v-cloak] { display: none !important; }

.cw-drawer__scrim {
	position: fixed;
	inset: 0;
	background: rgba(37, 19, 12, .5);
	z-index: 90;
	animation: cw-fade 220ms ease both;
}

@keyframes cw-fade { from { opacity: 0 } to { opacity: 1 } }

.cw-drawer {
	position: fixed;
	inset-block: 0;
	inset-inline-end: 0;
	width: min(92vw, 420px);
	display: flex;
	flex-direction: column;
	background: var(--cw-color-ground);
	border-inline-start: 1px solid var(--cw-color-line);
	box-shadow: -24px 0 60px -30px rgba(37, 19, 12, .55);
	transform: translateX(101%);
	/* `visibility` as well as the transform, and it is not belt-and-braces.
	   A fixed element parked off the right edge still contributes to the
	   document's scroll width in Chrome: with the panel merely translated, a
	   390px phone reported scrollWidth 410 and the whole product page scrolled
	   sideways. Hidden, it cannot. Transitioned with a delay so it disappears
	   only after the slide-out has finished. */
	visibility: hidden;
	transition: transform 320ms cubic-bezier(.22, .61, .36, 1), visibility 0s linear 320ms;
	z-index: 91;
}

.cw-drawer.is-open {
	transform: translateX(0);
	visibility: visible;
	transition: transform 320ms cubic-bezier(.22, .61, .36, 1), visibility 0s;
}

.cw-drawer__head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1rem;
	padding: 1.15rem 1.25rem;
	border-bottom: 1px solid var(--cw-color-line);
	background: var(--cw-color-surface);
}

.cw-drawer__title {
	margin: 0;
	font-family: var(--cw-font-display);
	font-size: 1.15rem;
	font-weight: 600;
	color: var(--cw-color-maroon);
}

.cw-drawer__close {
	width: 44px;
	height: 44px;
	margin: -.5rem -.6rem -.5rem 0;
	border: 0;
	background: transparent;
	font-size: 1.7rem;
	line-height: 1;
	color: var(--cw-color-muted);
	cursor: pointer;
	border-radius: 10px;
}

.cw-drawer__close:hover { background: var(--cw-color-ground-alt); color: var(--cw-color-ink); }

.cw-drawer__body {
	flex: 1 1 auto;
	overflow-y: auto;
	overscroll-behavior: contain;
	padding: 1rem 1.25rem;
}

.cw-drawer__empty {
	padding: 3rem 0;
	text-align: center;
	color: var(--cw-color-muted);
}

.cw-drawer__item {
	display: grid;
	grid-template-columns: 72px 1fr;
	gap: .9rem;
	padding: .9rem 0;
	border-bottom: 1px solid var(--cw-color-line);
}

.cw-drawer__item:last-child { border-bottom: 0; }

.cw-drawer__thumb {
	width: 72px;
	height: 72px;
	object-fit: contain;
	background: #FFFFFF;
	border: 1px solid var(--cw-color-line);
	border-radius: 10px;
	padding: 4px;
}

.cw-drawer__name {
	margin: 0;
	font-size: .95rem;
	font-weight: 600;
	line-height: 1.35;
	color: var(--cw-color-ink);
	/* A 70-character product name must not push the price out of the panel. */
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
}

.cw-drawer__opts {
	margin: .2rem 0 0;
	font-size: .8125rem;
	color: var(--cw-color-muted);
}

.cw-drawer__line {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: .75rem;
	margin: .45rem 0 0;
}

.cw-drawer__qty { font-size: .8125rem; color: var(--cw-color-muted); }

.cw-drawer__total {
	font-weight: 700;
	color: var(--cw-color-ink);
	font-variant-numeric: tabular-nums;
}

.cw-drawer__foot {
	padding: 1.1rem 1.25rem calc(1.25rem + env(safe-area-inset-bottom));
	border-top: 1px solid var(--cw-color-line);
	background: var(--cw-color-surface);
}

.cw-drawer__sum {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	margin: 0 0 .25rem;
	font-size: 1rem;
}

.cw-drawer__sum strong {
	font-size: 1.25rem;
	color: var(--cw-color-ink);
	font-variant-numeric: tabular-nums;
}

.cw-drawer__note {
	margin: 0 0 .9rem;
	font-size: .8125rem;
	color: var(--cw-color-muted);
}

.cw-drawer__cta {
	display: block;
	width: 100%;
	padding: .85rem 1rem;
	border-radius: 10px;
	background: var(--cw-color-maroon);
	color: #FFF7EC;
	font-weight: 700;
	text-align: center;
	text-decoration: none;
}

.cw-drawer__cta:hover { background: var(--cw-color-maroon-dark); color: #FFF7EC; }

.cw-drawer__back {
	display: block;
	width: 100%;
	margin-top: .55rem;
	padding: .75rem 1rem;
	border: 1px solid var(--cw-color-line);
	border-radius: 10px;
	background: transparent;
	color: var(--cw-color-ink);
	font-weight: 600;
	cursor: pointer;
}

.cw-drawer__back:hover { background: var(--cw-color-ground-alt); }

.cw-drawer__view {
	display: block;
	margin-top: .7rem;
	text-align: center;
	font-size: .85rem;
	color: var(--cw-color-muted);
}

@media (prefers-reduced-motion: reduce) {
	.cw-drawer { transition: none; }
	.cw-drawer__scrim { animation: none; }
}

/* =========================================================================
   PRODUCT PAGE — the buy box.

   Four sections injected through Bagisto's own render events (see
   CrystalwalaServiceProvider::extendProductPage). Everything here is built
   from data the product actually carries; nothing is a slogan.
   ========================================================================= */

/* ---------- attribute chips ---------- */

.cw-chips {
	display: flex;
	flex-wrap: wrap;
	gap: .45rem;
	margin: .85rem 0 0;
}

.cw-chip {
	display: inline-flex;
	align-items: center;
	padding: .3rem .7rem;
	border: 1px solid var(--cw-color-line);
	border-radius: 999px;
	background: var(--cw-color-surface);
	font-size: .78rem;
	font-weight: 600;
	letter-spacing: .01em;
	color: var(--cw-color-ink-soft);
	white-space: nowrap;
}

.cw-chip--sku {
	background: var(--cw-color-ground-alt);
	color: var(--cw-color-muted);
	font-variant-numeric: tabular-nums;
}

/* ---------- price assurance and scarcity ---------- */

.cw-taxnote {
	margin: .3rem 0 0;
	font-size: .8125rem;
	color: var(--cw-color-muted);
}

.cw-stock {
	display: inline-flex;
	align-items: center;
	gap: .45rem;
	margin: .7rem 0 0;
	padding: .4rem .8rem;
	border-radius: 999px;
	font-size: .85rem;
	font-weight: 600;
}

/* Low stock is urgency, so it is warm rather than alarming -- a devotional
   catalogue should not shout. */
.cw-stock--low {
	background: #FDF0DC;
	color: #8A4B08;
	border: 1px solid rgba(245, 158, 11, .35);
}

/* One of a kind is not urgency, it is a property of the object, so it wears
   the brand colour rather than a warning colour. */
.cw-stock--unique {
	background: var(--cw-color-ground-alt);
	color: var(--cw-color-maroon);
	border: 1px solid rgba(108, 19, 34, .2);
}

/* ---------- assurance strip ---------- */

.cw-assure {
	list-style: none;
	margin: 1.4rem 0 0;
	padding: 1.1rem 0 0;
	border-top: 1px solid var(--cw-color-line);
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: .95rem 1.2rem;
	max-width: 470px;
}

.cw-assure li {
	display: grid;
	grid-template-columns: 22px 1fr;
	align-items: start;
	gap: .6rem;
	font-size: .8125rem;
	line-height: 1.4;
	color: var(--cw-color-muted);
}

.cw-assure svg {
	width: 20px;
	height: 20px;
	fill: none;
	stroke: var(--cw-color-maroon);
	stroke-width: 1.5;
	stroke-linecap: round;
	stroke-linejoin: round;
	margin-top: .1rem;
}

.cw-assure strong {
	display: block;
	color: var(--cw-color-ink);
	font-weight: 600;
	font-size: .875rem;
}

@media (max-width: 420px) {
	.cw-assure { grid-template-columns: 1fr; }
}

/* ---------- sticky buy bar ---------- */

/* Revealed by an animation driven by `animation-timeline: view()` where the
   browser supports it, and simply always-on below the fold otherwise. No
   scroll listener either way, so there is nothing to throttle and nothing that
   can jank the page. */
.cw-buybar {
	position: fixed;
	inset-inline: 0;
	bottom: 0;
	z-index: 55;
	background: var(--cw-color-surface);
	border-top: 1px solid var(--cw-color-line);
	box-shadow: 0 -14px 34px -26px rgba(37, 19, 12, .6);
	padding: .6rem 0 calc(.6rem + env(safe-area-inset-bottom));
	transform: translateY(105%);
	transition: transform 260ms cubic-bezier(.22, .61, .36, 1);
}

/* `.cw-scrolled` is already set on <html> by the storefront script once the
   page has moved -- reusing it means this needs no JavaScript of its own. */
.cw-scrolled .cw-buybar { transform: translateY(0); }

.cw-buybar__inner {
	max-width: var(--cw-wide);
	margin-inline: auto;
	padding-inline: var(--cw-space-30);
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1rem;
	/* Without this the nowrap CTA and the product name refuse to shrink and
	   the bar measured 410px inside a 390px phone, scrolling the page
	   sideways. A flex item's default `min-width: auto` is its content. */
	min-width: 0;
}

.cw-buybar__id {
	display: flex;
	align-items: center;
	gap: .75rem;
	min-width: 0;
}

.cw-buybar__id img {
	width: 44px;
	height: 44px;
	object-fit: contain;
	background: #FFFFFF;
	border: 1px solid var(--cw-color-line);
	border-radius: 8px;
	padding: 3px;
	flex: none;
}

.cw-buybar__name {
	margin: 0;
	font-size: .9rem;
	font-weight: 600;
	color: var(--cw-color-ink);
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
	max-width: 46vw;
}

.cw-buybar__price {
	margin: 0;
	font-size: .95rem;
	font-weight: 700;
	color: var(--cw-color-maroon);
}

/* Bagisto's price HTML carries its own struck-price markup; inside a 44px bar
   the original is noise. */
.cw-buybar__price .line-through { display: none; }

.cw-buybar__cta {
	flex: none;
	padding: .7rem 1.4rem;
	border-radius: 10px;
	background: var(--cw-color-maroon);
	color: #FFF7EC;
	font-weight: 700;
	font-size: .9rem;
	text-decoration: none;
	white-space: nowrap;
}

.cw-buybar__cta:hover { background: var(--cw-color-maroon-dark); color: #FFF7EC; }

@media (max-width: 560px) {
	.cw-buybar__inner { padding-inline: 1rem; gap: .6rem; }
	.cw-buybar__id { min-width: 0; }
	.cw-buybar__id img { display: none; }
	.cw-buybar__name { max-width: none; }
	.cw-buybar__cta { padding-inline: 1rem; font-size: .85rem; }

	/* 🔴 REMOVED 2026-09-10 — THIS RULE REPLACED THE BUTTON'S LABEL IN CSS.
	   It set `font-size: 0` to hide the real text and painted "Add to basket"
	   through `::after`, because "Choose and add to basket" would not fit
	   beside a product name at 390px.

	   That was fine while the bar had ONE button. The moment it gained a
	   second (Add to Cart + Buy Now, 2026-09-10) it painted the SAME words on
	   both, so a Buy Now button read "Add to basket" — a different promise from
	   the one it keeps.

	   ⚠️ AND IT DEFEATED EVERY CHECK I HAD. `textContent` said "Buy Now"
	   throughout; only the rendered `::after` said otherwise. Two rounds of
	   measurement passed while the screen was wrong, which is why `ui:browser`
	   now asserts the CTA has no `::after` content at all.

	   The labels are short enough now: "Choose options", "Add to Cart",
	   "Buy Now" — each measured to fit its button at 360, 390 and 430px. */
}

/* The bar and the drawer must never stack. */
.cw-drawer.is-open ~ .cw-buybar,
body:has(.cw-drawer.is-open) .cw-buybar { transform: translateY(105%); }

@media (prefers-reduced-motion: reduce) {
	.cw-buybar { transition: none; }
}

/* ---------- variant pills ---------- */

/* Bagisto's text swatch is a rounded label whose selected state is
   `!bg-navyBlue`. The navy is remapped to maroon in elevation.css; this is the
   rest of the treatment -- a real 44px target, brand borders, and a clear
   unselected state so the shopper can see there is a choice to make. */
.cw-body main label.rounded-full {
	min-height: 44px;
	border-color: var(--cw-color-line) !important;
	border-radius: 12px !important;
	text-transform: none !important;
	font-weight: 600;
	transition: border-color 160ms ease, background-color 160ms ease;
}

.cw-body main label.rounded-full:hover { border-color: var(--cw-color-maroon) !important; }

.cw-body main label.rounded-full span { font-size: .95rem !important; }

/* =========================================================================
   CATEGORY STRIP — under the credential bar.

   A scroll-snapping rail, not a JavaScript carousel. Snap points give the
   momentum-flick feel on a trackpad and a phone, `overflow-x: auto` makes it
   keyboard- and screen-reader-navigable, and there is no script that can fail
   and leave a dead control.
   ========================================================================= */

.cw-strip {
	background: var(--cw-color-ground-alt);
	border-bottom: 1px solid var(--cw-color-line);
	padding: 1.5rem 0 1.6rem;
}

.cw-strip__rail {
	list-style: none;
	margin: 0 auto;
	/* A fixed gutter, not a centring calc. `calc((100% - var(--cw-wide)) / 2)`
	   resolves against the RAIL's own width, which is the full viewport, so it
	   collapsed to a few pixels and the first tile sat 8px from the edge while
	   every other section on the page starts at 48px. */
	padding: .25rem clamp(1rem, 3.5vw, 3.5rem);
	/* `scroll-padding` must match, or the gutter is cancelled the instant the
	   page loads. `scroll-snap-align: start` snaps a child to the SCROLLPORT's
	   start edge, which ignores the container's start padding -- so the browser
	   silently scrolled the rail by exactly the 50.4px of padding and the first
	   tile sat flush against the viewport edge while every other section on the
	   page started at 48px. Measured: paddingLeft 50.4px, scrollLeft 50. */
	scroll-padding-inline: clamp(1rem, 3.5vw, 3.5rem);
	max-width: 100%;
	display: flex;
	gap: 1.1rem;
	overflow-x: auto;
	scroll-snap-type: x proximity;
	scrollbar-width: thin;
	scrollbar-color: var(--cw-color-line) transparent;
	overscroll-behavior-x: contain;
}

.cw-strip__rail::-webkit-scrollbar { height: 6px; }
.cw-strip__rail::-webkit-scrollbar-thumb { background: var(--cw-color-line); border-radius: 999px; }

.cw-strip__item {
	flex: 0 0 auto;
	scroll-snap-align: start;
	width: 108px;
}

.cw-strip__item a {
	display: grid;
	justify-items: center;
	gap: .4rem;
	text-decoration: none;
	color: var(--cw-color-ink);
}

/* The well normalises photography that arrives on white, cream and black
   grounds at every ratio -- the ceiling on this catalogue's polish
   (SESSION-LOG 14.10). A round well hides the corner of a mismatched ground. */
.cw-strip__well {
	display: grid;
	place-items: center;
	width: 92px;
	height: 92px;
	padding: 10px;
	border-radius: 50%;
	background: radial-gradient(120% 100% at 50% 6%, #FFFFFF 0%, #FFFCF6 52%, #F4EADB 100%);
	border: 1px solid var(--cw-color-line);
	overflow: hidden;
	transition: transform 260ms cubic-bezier(.22, .61, .36, 1), border-color 200ms ease;
}

.cw-strip__item a:hover .cw-strip__well {
	transform: translateY(-3px);
	border-color: var(--cw-color-maroon);
}

/* `cover`, filling the circle, with NO blend mode.

   `mix-blend-mode: multiply` is the right trick for a cut-out on white -- it
   drops the white away and leaves the object on the cream. It is exactly wrong
   for a cut-out on BLACK, which multiplies to black and fills the whole circle
   with a dark disc. This catalogue has both: of the eighteen categories in the
   strip, Malas and Carvings photograph on black velvet and rendered as two
   black holes in a row of cream circles.

   Filling the circle instead normalises every ground -- white, cream and black
   all become a full-bleed round thumbnail. It crops, but at 92px a crop is not
   a loss, and one consistent row beats eighteen accurate ones. */
.cw-strip__well {
	padding: 0;
}

.cw-strip__well img {
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/* A fixed two-line box. Without it a one-word name and a three-word name push
   their counts to different heights and the row loses its baseline. */
.cw-strip__name {
	font-size: .82rem;
	font-weight: 600;
	line-height: 1.25;
	text-align: center;
	text-wrap: balance;
	min-height: 2.5em;
	display: flex;
	align-items: center;
}

.cw-strip__count {
	font-size: .7rem;
	color: var(--cw-color-muted);
	font-variant-numeric: tabular-nums;
}

.cw-strip__item a:hover .cw-strip__name { color: var(--cw-color-maroon); }

@media (prefers-reduced-motion: reduce) {
	.cw-strip__well { transition: none; }
}

/* =========================================================================
   REVIEWS

   The section already exists in full -- average, per-star distribution bars,
   a write-a-review form and a review list are all Bagisto's, and they are
   good. What was wrong was the EMPTY state, which is what every product shows
   today: a 190px grey star and roughly 500px of dead space above the sentence
   "No Review found, Be the first to review this product", with the write
   button lost underneath.

   That is the largest thing on the page at the exact moment a shopper is
   looking for reassurance, and it says nothing. Reviews are switched on in
   crystalwala:setup so a real customer can leave one and it appears.
   ========================================================================= */

/* The placeholder art is decoration, not information. */
.cw-body main img[src*="review"],
.cw-body main img[src*="no-review"],
.cw-body main img[alt*="review" i] {
	max-width: 88px !important;
	height: auto;
	opacity: .28;
	filter: grayscale(1);
	margin-inline: auto;
}

/* The stars Bagisto draws with its icon font: amber, the parent's exact
   accent, rather than the default grey that reads as disabled. */
.cw-body main .icon-star-fill,
.cw-body main .icon-star-half,
.cw-body main [class*="icon-star"] {
	color: var(--cw-color-amber-deep) !important;
}

/* The average, the biggest number in the block. */
.cw-body main .text-5xl {
	font-family: var(--cw-font-display);
	color: var(--cw-color-ink);
	font-variant-numeric: tabular-nums;
}

/* The per-star distribution bars. Bagisto ships a neutral track and fill; the
   fill takes the brand colour so the shape reads at a glance. */
.cw-body main .bg-neutral-200 {
	background: var(--cw-color-ground-alt) !important;
	border-radius: 999px !important;
	overflow: hidden;
}

.cw-body main .bg-neutral-200 > div,
.cw-body main .bg-neutral-200 > span {
	background: var(--cw-color-amber-deep) !important;
	border-radius: 999px !important;
}

/* A review is a card, not a run of paragraphs: an author, a date, a rating and
   a body, boxed so twenty of them do not read as one long column. */
.cw-body main .grid.gap-4 > .grid.gap-2\.5,
.cw-body main [class*="review"] .rounded-xl {
	background: var(--cw-color-surface);
	border: 1px solid var(--cw-color-line);
	border-radius: 14px;
	padding: 1.1rem 1.25rem;
}

/* Bagisto's own "Verified" style pill, brought onto the palette. */
.cw-body main .text-emerald-600,
.cw-body main .text-green-600 { color: var(--cw-color-save) !important; }

/* The empty-review panel is hard-coded to `h-[476px]` -- half a screen of
   nothing on every one of 1,278 products, since none has a review yet. It is
   an empty state, not a section: it needs enough room to be noticed and no
   more. */
.cw-body main .h-\[476px\] {
	height: auto !important;
	min-height: 0 !important;
	padding-block: 3rem 3.5rem;
	gap: .35rem;
}

/* The write-a-review control is a bordered div, not a button, so it needs the
   affordance spelling out. It is the only action in an empty state, so it gets
   the brand colour rather than an outline nobody notices.

   SCOPED BY `:has(.icon-pen)`, and that matters: `border-navyBlue` is also the
   class on the ACTIVE PRODUCT TAB's underline. Styling the class alone filled
   the selected tab with a maroon block and left its dark label unreadable on
   top -- one rule, two completely unrelated elements, which is the same trap as
   the `[class*="max-h-["]` selector that destroyed the filter sidebar. The pen
   icon is unique to this control. */
.cw-body main .border-navyBlue:has(.icon-pen) {
	border-color: var(--cw-color-maroon) !important;
	background: var(--cw-color-maroon);
	color: #FFF7EC;
	font-weight: 600;
	min-height: 46px;
	justify-content: center;
	transition: background-color 180ms ease;
}

.cw-body main .border-navyBlue:has(.icon-pen):hover { background: var(--cw-color-maroon-dark); }

/* ---------- mobile product gallery ---------- */

/* The phone gallery is a `w-screen` scroller sitting inside a padded parent, so
   it starts 20px in and ends 20px past the right edge: measured 410px of
   document on a 390px viewport, and the whole product page scrolled sideways.

   It cannot simply be `width: 100%` -- its slides are sized to the viewport, and
   constraining the rail pushed the document to 1190px. So the rail keeps its
   width and the page clips it instead. `overflow-x: clip` rather than `hidden`
   because `hidden` on an ancestor would also trap the sticky buy bar and the
   cart drawer, both of which are positioned out of flow. */
.cw-body main .w-screen.overflow-auto,
.cw-body main .scrollbar-hide.w-screen {
	max-width: 100vw;
	scroll-snap-type: x mandatory;
}

@media (max-width: 1180px) {
	.cw-body main { overflow-x: clip; }
}

/* =========================================================================
   MERCHANDISING RAILS

   CSS-only tabs on radio inputs. `:has()` is what makes this possible without
   JavaScript: the panel that follows the checked radio is shown, the rest are
   not. Supported in every current browser; the fallback below shows the first
   panel, so a very old browser gets one department rather than none.
   ========================================================================= */

.cw-rails {
	padding: var(--cw-space-50) 0;
	background: var(--cw-color-ground);
}

.cw-rails__head {
	max-width: var(--cw-wide);
	margin-inline: auto;
	padding-inline: clamp(1rem, 3.5vw, 3.5rem);
	text-align: center;
	margin-bottom: 1.6rem;
}

.cw-rails__head .cw-sec__sub {
	max-width: 46ch;
	margin-inline: auto;
	margin-top: .5rem;
}

.cw-rails__tabs {
	max-width: var(--cw-wide);
	margin-inline: auto;
}

/* The radios drive everything and are never seen. `sr-only`, not
   `display: none` -- a hidden input is not focusable, which would take the
   whole control away from the keyboard. */
.cw-rails__radio {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

.cw-rails__tabs > label {
	margin: 0 .5rem .25rem 0;
	cursor: pointer;
}

/* The label row is centred; the labels come before the panels in the DOM so
   the sibling selectors below can reach them. */
.cw-rails__tabs {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: 0;
	padding-inline: clamp(1rem, 3.5vw, 3.5rem);
}

.cw-rails__radio:checked + label {
	background: var(--cw-color-maroon);
	border-color: var(--cw-color-maroon);
	color: #FFF7EC;
}

.cw-rails__radio:focus-visible + label {
	outline: 2px solid var(--cw-color-maroon);
	outline-offset: 3px;
}

.cw-rails__panel {
	flex: 0 0 100%;
	/* `min-width: 0`, and the whole section depends on it. A flex item defaults
	   to `min-width: auto`, which is its CONTENT size -- so `flex-basis: 100%`
	   was ignored and the panel grew to fit ten 268px cards plus gaps: measured
	   2,874px wide, starting at x = -717, with the document scrolling to
	   2,157px. The rail is meant to scroll INSIDE the panel, which it can only
	   do once the panel is allowed to be narrower than its contents. */
	min-width: 0;
	max-width: 100%;
	order: 9;
	margin-top: 1.6rem;
	display: none;
}

/* Show the panel whose radio is checked. The index is encoded in the ids, so
   this stays declarative rather than needing a rule per department. */
.cw-rails__tabs:has(#cw-rail-0:checked) #cw-rail-panel-0,
.cw-rails__tabs:has(#cw-rail-1:checked) #cw-rail-panel-1,
.cw-rails__tabs:has(#cw-rail-2:checked) #cw-rail-panel-2,
.cw-rails__tabs:has(#cw-rail-3:checked) #cw-rail-panel-3 { display: block; }

/* Without :has(), show the first panel rather than none. */
@supports not selector(:has(*)) {
	#cw-rail-panel-0 { display: block; }
}

.cw-rails__rail {
	list-style: none;
	margin: 0;
	padding: .5rem 0 1rem;
	min-width: 0;
	display: flex;
	gap: 1.35rem;
	overflow-x: auto;
	scroll-snap-type: x proximity;
	scroll-padding-inline: 0;
	scrollbar-width: thin;
	scrollbar-color: var(--cw-color-line) transparent;
	overscroll-behavior-x: contain;
}

.cw-rails__rail::-webkit-scrollbar { height: 6px; }
.cw-rails__rail::-webkit-scrollbar-thumb { background: var(--cw-color-line); border-radius: 999px; }

/* A card in a rail is a fixed width, so four and a bit are visible and the
   shopper can see there is more to the right. */
.cw-rails__rail > .cw-card {
	flex: 0 0 clamp(190px, 22vw, 268px);
	scroll-snap-align: start;
}

.cw-rails__more {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1rem;
	flex-wrap: wrap;
	margin: .4rem 0 0;
	font-size: .875rem;
	color: var(--cw-color-muted);
	font-variant-numeric: tabular-nums;
}

.cw-rails__all {
	display: inline-flex;
	align-items: center;
	gap: .4rem;
	padding: .55rem 1.15rem;
	border-radius: 999px;
	background: var(--cw-color-maroon);
	color: #FFF7EC;
	font-weight: 600;
	text-decoration: none;
}

.cw-rails__all:hover { background: var(--cw-color-maroon-dark); color: #FFF7EC; }

@media (max-width: 620px) {
	.cw-rails { padding-block: var(--cw-space-40); }
	.cw-rails__more { justify-content: center; }
}

/* =========================================================================
   PHOTOGRAPHY NORMALISATION

   The real ceiling on how premium this can look. The client's 4,630 images are
   cut-outs shot on THREE different grounds -- white, cream and black -- at
   different ratios and zoom levels, and CW-16.3's answer is to use what they
   have. CSS cannot fix lighting or scale. It can stop the grounds fighting each
   other, which is what actually makes a grid read as a jumble.

   The card well was a cream radial gradient. That is the worst possible choice
   for this set: a white cut-out on cream shows its own white square as a hard
   seam, and a black one shows a black block. Measured on the home rails --
   "Smoky Quartz Tumble" (white ground) and "Rose Quartz jaap Mala" (black
   velvet) sat side by side, each visibly a different photograph.

   PURE WHITE instead. The majority of the set is shot on white, so most images
   now have no visible edge at all; cream ones are a half-tone off rather than a
   contrasting block; and a black one reads as a deliberate dark photograph on
   white rather than a hole. One hairline separates the well from the card.

   Auto-processing every image onto one ground is the real fix and is ticketed
   separately -- it needs the images mirrored locally first (CW-15).
   ========================================================================= */

.cw-card__media {
	background: #FFFFFF !important;
	border-bottom: 1px solid var(--cw-color-line);
	box-shadow: none !important;
	padding: 8% !important;
}

/* The product page's own image well, for the same reason. */
.cw-body main .max-h-\[610px\] { background: #FFFFFF; border-radius: 14px; }

/* Thumbnails in the gallery rail and the cart drawer. */
.cw-drawer__thumb,
.cw-body main label.rounded-full img { background: #FFFFFF; }

/* A consistent scale. The set is shot at wildly different zooms -- some pieces
   fill the frame, some sit in the middle of it -- and generous, EQUAL padding
   is what stops a tightly-cropped bead reading as bigger than a mala. */
.cw-card__media img {
	width: 100%;
	height: 100%;
	object-fit: contain;
}

/* =========================================================================
   INTENT AND DEITY TILES

   Both sections used to be a heading and a row of empty boxes -- typographically
   fine, commercially empty. They now carry a real photograph, a label, a
   one-line description and a count, and the whole tile is the target.
   ========================================================================= */

.cw-intent {
	display: grid;
	grid-template-columns: repeat(3, minmax(0, 1fr));
	gap: 1.1rem;
	max-width: var(--cw-wide);
	margin-inline: auto;
	padding-inline: clamp(1rem, 3.5vw, 3.5rem);
}

.cw-intent__tile {
	display: grid;
	grid-template-columns: 84px 1fr;
	align-items: center;
	gap: 1rem;
	padding: .9rem 1.1rem .9rem .9rem;
	background: var(--cw-color-surface);
	border: 1px solid var(--cw-color-line);
	border-radius: 16px;
	text-decoration: none;
	color: var(--cw-color-ink);
	box-shadow: var(--cw-shadow-soft);
	transition: transform 260ms cubic-bezier(.22, .61, .36, 1),
	            box-shadow 260ms ease, border-color 200ms ease;
}

.cw-intent__tile:hover {
	transform: translateY(-4px);
	border-color: rgba(108, 19, 34, .35);
	box-shadow: var(--cw-shadow-lift);
}

/* Round, filled, no blend mode -- the same decision as the category strip, and
   for the same reason: this catalogue photographs on white, cream AND black,
   and `multiply` turns a black ground into a black disc. */
.cw-intent__well {
	display: block;
	width: 84px;
	height: 84px;
	border-radius: 14px;
	overflow: hidden;
	background: #FFFFFF;
	border: 1px solid var(--cw-color-line);
}

.cw-intent__well img { width: 100%; height: 100%; object-fit: cover; }

.cw-intent__text { display: grid; gap: .1rem; min-width: 0; }

.cw-intent__name {
	font-family: var(--cw-font-display);
	font-size: 1.15rem;
	font-weight: 600;
	line-height: 1.2;
}

.cw-intent__sub {
	font-size: .8125rem;
	color: var(--cw-color-muted);
	line-height: 1.35;
}

.cw-intent__count {
	margin-top: .2rem;
	font-size: .72rem;
	font-weight: 700;
	letter-spacing: .08em;
	text-transform: uppercase;
	color: var(--cw-color-maroon);
	font-variant-numeric: tabular-nums;
}

.cw-intent__tile:hover .cw-intent__name { color: var(--cw-color-maroon); }

@media (max-width: 1024px) { .cw-intent { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 620px)  { .cw-intent { grid-template-columns: 1fr; } }

@media (prefers-reduced-motion: reduce) {
	.cw-intent__tile { transition: none; }
	.cw-intent__tile:hover { transform: none; }
}

/* =========================================================================
   CART, ACCOUNT AND CHECKOUT
   ========================================================================= */

/* Bagisto's stub header -- a second logo and a "Sign In" link -- is rendered
   INSIDE the page by views that assume no header above them. Restoring the real
   header on those pages therefore stacked two headers. Hidden only where we
   know we have supplied the real one. */
.cw-has-chrome > .flex.flex-wrap:first-child,
.cw-has-chrome > div > .flex.w-full.justify-between:first-child:has(a[aria-label] img) {
	display: none !important;
}

/* The account pages centre their own logo above the form for the same reason. */
.cw-has-chrome .container > a[aria-label] > img,
.cw-has-chrome > .container > .flex.justify-center > a > img { display: none; }

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

/* The bulk-select goes.

   Bagisto opens the cart with a checkbox and the line "0 Items Selected" -- a
   multi-select for removing several lines at once. On a store whose baskets are
   one to three considered pieces it is the first thing a shopper reads and it
   says nothing useful, while every row already carries its own "Remove". The
   checkbox itself is `peer hidden` with a styled label, so the label goes with
   it. */
.cw-body main .flex.items-center.justify-between:has(> .flex.select-none > input[type="checkbox"]) { display: none !important; }

/* 🔴 `:not(li) >` IS LOAD-BEARING. READ THIS BEFORE TOUCHING THE SELECTOR.
   ─────────────────────────────────────────────────────────────────────────
   Written unscoped, as
       .cw-body main .select-none:has(> input.peer.hidden[type="checkbox"])
   this rule ALSO matched every option row in the CATEGORY FILTER SIDEBAR,
   because Bagisto builds a filter option out of exactly the same three parts
   as a cart line's select box — a `.select-none` flex row wrapping a
   `peer hidden` checkbox and a styled label.

   The result was that EVERY CHECKBOX FILTER ON EVERY CATEGORY PAGE WAS
   `display: none`. The headings rendered, the per-filter search box rendered,
   and "Showing 2 of 2 options" rendered — while the two options themselves
   were invisible. Measured on /malas: each <li> 240px wide and 0px tall,
   the <ul> 12px tall (its own padding), the labels 0x0.

   ⚠️ IT LOOKED FINE. The sidebar is full of headings and controls, so nothing
   about it read as broken — you have to know the filter should list brands to
   notice that it does not. This is the SECOND time a stylesheet change has
   silently destroyed this sidebar (SESSION-LOG §15.2 was the first, where all
   23 browser checks passed through it), which is why `ui:browser` now asserts
   a positive option COUNT rather than the presence of the panel.

   THE DISCRIMINATOR: a filter option's `.select-none` is a direct child of an
   <li>; a cart line's is a direct child of a <div> (vendor cart/index.blade.php
   line 178). `:not(li)` is a simple selector, so this needs no Selectors-4
   support beyond the `:has()` the rule already uses.

   The mass-action header at line 115 is handled by the rule above this one,
   which hides its whole parent row. */
.cw-body main :not(li) > .select-none:has(> input.peer.hidden[type="checkbox"]) { display: none !important; }

/* Three actions, three weights.

   Bagisto gives "Continue Shopping" and "Update Cart" the SAME class, so both
   rendered as full maroon and leaving the basket looked exactly as important as
   changing it. The tag is what separates them: Continue Shopping is an <a> to
   the home page, Update Cart is a <button> that submits the form.

   An earlier attempt at this used `.primary-button:where([href])` and caught
   "Proceed To Checkout" as well -- it is also an anchor -- turning the single
   most important control on the page into a ghost outline. Anchor alone is not
   enough; the class has to be in the selector too. */
.cw-body main a.secondary-button {
	background: transparent !important;
	border: 1px solid var(--cw-color-line) !important;
	color: var(--cw-color-ink) !important;
	box-shadow: none !important;
}

.cw-body main a.secondary-button:hover {
	background: var(--cw-color-ground-alt) !important;
	color: var(--cw-color-ink) !important;
	border-color: var(--cw-color-maroon) !important;
}

/* Proceed To Checkout is the strongest thing on the page and keeps the amber
   it shares with Buy Now -- the same action, reached a different way. */
.cw-body main a.primary-button {
	background: var(--cw-color-amber) !important;
	border: 1px solid var(--cw-color-amber-deep) !important;
	color: #3A2408 !important;
}

.cw-body main a.primary-button:hover { background: var(--cw-color-amber-deep) !important; }

/* ---------- checkout ---------- */

/* Company name and VAT ID are B2B fields on a consumer store selling ₹490
   bracelets. They are the second and fourth things a shopper meets in the
   checkout form, and every extra field measurably costs completions. They are
   hidden rather than removed, so nothing in Bagisto's validation changes. */
/* ⚠️ COMPANY NAME AND VAT ID ARE LEFT VISIBLE, deliberately.

   They are B2B fields on a consumer store selling ₹490 bracelets, and hiding
   them would be a small win. Two attempts failed and the second one was
   dangerous:

   1. `label[for*="company_name"]` matched nothing -- Bagisto's checkout labels
      carry no `for` attribute at all.
   2. `div:has(input[name$="company_name"])` matched every ANCESTOR div as well,
      because `:has()` is not scoped to a direct child unless you say so. It
      hid the entire address form: the checkout rendered as a bare "Proceed"
      button over an empty white bar.

   The fix is the CHILD COMBINATOR. `div.mb-4:has(> input[...])` can only match
   the group that directly contains the input -- measured: the label and the
   input are siblings inside one `div.mb-4` -- so no ancestor can match. That is
   the whole difference between hiding a field and hiding the checkout. */
.cw-body main div.mb-4:has(> input[name$="company_name"]),
.cw-body main div.mb-4:has(> input[name$="vat_id"]) { display: none !important; }

/* ---------- listing result count ---------- */

.cw-count {
	margin: .9rem 0 0;
	font-size: .875rem;
	color: var(--cw-color-muted);
	font-variant-numeric: tabular-nums;
}

/* A full-width band across the top of checkout, not the two-column block the
   product page uses. */
.cw-assure--checkout {
	grid-template-columns: repeat(3, minmax(0, 1fr));
	max-width: var(--cw-wide);
	margin: 0 auto;
	padding: 1rem clamp(1rem, 4vw, 3.75rem) 1.1rem;
	border-top: 0;
	border-bottom: 1px solid var(--cw-color-line);
	background: var(--cw-color-surface);
}

@media (max-width: 780px) {
	.cw-assure--checkout { grid-template-columns: 1fr; gap: .6rem; }
}

/* =========================================================================
   PRODUCT PAGE — the buy box, rebuilt against certifiedcrystal.com

   Added 2026-09-02 for the product-page rebuild. The measured anatomy of the reference buy box is
   in docs/PDP-REBUILD.md §4; this is that anatomy in Crystalwala's palette.

   🔴 Two things rendered here are INVENTED and were authorised by the owner on
   2026-09-02 over three earlier refusals: the MRP behind `.cw-save`, and
   `.cw-sold` / `.cw-countdown` in full. docs/PDP-REBUILD.md §2 holds the
   decision and the exposure. Do not treat their presence as precedent.
   ========================================================================= */

/* ---------- the price row ---------- */

/* Bagisto emits the price as a bare <p>, so the badge is put on the same line
   by wrapping both -- see cw-price-open / cw-price-close. Baseline alignment
   rather than centre: the badge is much shorter than the price and centring it
   makes it look as though it has drifted upward. */
.cw-body main .cw-priceline {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: .1rem .75rem;
	margin-top: 1.1rem;
}

.cw-body main .cw-priceline > p {
	margin-top: 0 !important;
}

/* Emerald, not red. The parent uses green for savings, and a devotional
   catalogue should not shout in an alarm colour. */
.cw-save {
	display: inline-flex;
	align-items: center;
	padding: .25rem .6rem;
	border-radius: 8px;
	background: var(--cw-color-save-soft);
	color: var(--cw-color-save);
	font-size: .8rem;
	font-weight: 800;
	letter-spacing: .01em;
	white-space: nowrap;
	font-variant-numeric: tabular-nums;
}

/* ---------- the rating line ---------- */

.cw-sold {
	display: inline-block;
	margin: .55rem 0 0 .7rem;
	font-size: .8125rem;
	font-weight: 600;
	color: var(--cw-color-amber-deep);
	font-variant-numeric: tabular-nums;
}

/* ---------- the countdown ---------- */

.cw-countdown {
	display: inline-flex;
	align-items: center;
	gap: .45rem;
	margin: .8rem 0 0;
	padding: .42rem .75rem;
	border-radius: 10px;
	background: #ECFDF5;
	border: 1px solid rgba(4, 120, 87, .18);
	color: #05614A;
	font-size: .85rem;
}

.cw-countdown svg {
	width: 16px;
	height: 16px;
	flex: none;
	fill: none;
	stroke: currentColor;
	stroke-width: 1.7;
	stroke-linecap: round;
	stroke-linejoin: round;
}

/* Tabular figures, or the whole line reflows every second as the digits
   change width and the eye reads it as flicker. */
.cw-countdown b {
	font-weight: 800;
	font-variant-numeric: tabular-nums;
}

/* ---------- "Offers for you" ---------- */

.cw-offers {
	margin: 1.4rem 0 0;
	max-width: 470px;
}

.cw-offers__h {
	margin: 0 0 .6rem;
	font-size: .95rem;
	font-weight: 800;
	color: var(--cw-color-ink);
}

.cw-offers__list {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	gap: .5rem;
}

.cw-offer {
	display: grid;
	grid-template-columns: auto 1fr auto;
	align-items: center;
	gap: .7rem;
	padding: .6rem .75rem;
	border: 1px dashed rgba(108, 19, 34, .25);
	border-radius: 10px;
	background: var(--cw-color-ground-alt);
}

.cw-offer svg {
	width: 20px;
	height: 20px;
	flex: none;
	fill: none;
	stroke: var(--cw-color-maroon);
	stroke-width: 1.5;
	stroke-linecap: round;
	stroke-linejoin: round;
}

.cw-offer__title {
	margin: 0;
	font-size: .875rem;
	font-weight: 700;
	color: var(--cw-color-ink);
}

.cw-offer__detail {
	margin: .1rem 0 0;
	font-size: .78rem;
	color: var(--cw-color-muted);
}

/* A code is meant to be copied, so it is monospaced and boxed. "Applied
   automatically" is the opposite -- nothing to do -- so it is quiet. */
.cw-offer__code {
	padding: .22rem .5rem;
	border: 1px solid rgba(108, 19, 34, .3);
	border-radius: 6px;
	background: #FFF;
	font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
	font-size: .75rem;
	font-weight: 700;
	color: var(--cw-color-maroon);
	white-space: nowrap;
}

.cw-offer__auto {
	font-size: .72rem;
	font-weight: 700;
	color: var(--cw-color-save);
	white-space: nowrap;
}

/* ---------- pincode delivery estimate ---------- */

.cw-deliver {
	margin: 1.4rem 0 0;
	padding: 1rem;
	max-width: 470px;
	border: 1px solid var(--cw-color-line);
	border-radius: 12px;
	background: var(--cw-color-surface, #FFF);
}

.cw-deliver__h {
	display: flex;
	align-items: center;
	gap: .5rem;
	margin: 0;
	font-size: .95rem;
	font-weight: 800;
	color: var(--cw-color-ink);
}

.cw-deliver__h svg {
	width: 20px;
	height: 20px;
	flex: none;
	fill: none;
	stroke: var(--cw-color-maroon);
	stroke-width: 1.5;
	stroke-linecap: round;
	stroke-linejoin: round;
}

.cw-deliver__sub {
	margin: .3rem 0 .7rem;
	font-size: .8125rem;
	color: var(--cw-color-muted);
}

.cw-deliver__form {
	display: flex;
	gap: .5rem;
}

/* min-width:0 so the field yields before the button does. Without it a flex
   item refuses to shrink below its content width and the button is pushed off
   the edge on a narrow phone -- the same bug the header search hit (§15.8). */
.cw-deliver__form input {
	flex: 1 1 auto;
	min-width: 0;
	padding: .6rem .8rem;
	border: 1px solid var(--cw-color-line);
	border-radius: 9px;
	background: #FFF;
	font-size: .9rem;
	color: var(--cw-color-ink);
}

.cw-deliver__form input:focus-visible {
	outline: 2px solid var(--cw-color-maroon);
	outline-offset: 1px;
}

.cw-deliver__form button {
	flex: none;
	padding: .6rem 1.15rem;
	border: 1px solid var(--cw-color-maroon);
	border-radius: 9px;
	background: var(--cw-color-maroon);
	color: #FFF7EC;
	font-size: .875rem;
	font-weight: 700;
	cursor: pointer;
}

.cw-deliver__form button:hover { background: var(--cw-color-maroon-dark); }

.cw-deliver__out {
	margin: .6rem 0 0;
	font-size: .8125rem;
	line-height: 1.5;
	color: var(--cw-color-ink-soft);
}

.cw-deliver__out:empty { display: none; }

.cw-deliver__out[data-state="error"] { color: #9B2C2C; }

.cw-deliver__out[data-state="ok"] { color: var(--cw-color-save); font-weight: 600; }

/* A claim chip is not an attribute chip and should not pretend to be one: it
   carries the brand colour so it reads as a badge rather than a measurement. */
.cw-chip--claim {
	background: rgba(108, 19, 34, .07);
	border-color: rgba(108, 19, 34, .22);
	color: var(--cw-color-maroon);
}

@media (max-width: 640px) {
	.cw-offer { grid-template-columns: auto 1fr; }
	.cw-offer__code,
	.cw-offer__auto { grid-column: 2; justify-self: start; }
	.cw-sold { margin-left: 0; display: block; }
}

@media (prefers-reduced-motion: reduce) {
	/* The countdown is information, not decoration, so it keeps ticking --
	   there is no movement to suppress, only a changing number. */
	.cw-deliver__form button { transition: none; }
}

/* ---------- Buy Now really is amber ---------- */

/* ⚠️ THE RULE AT LINE ~469 DOES NOT WIN, and it looks as though it should:
   it is in the last stylesheet, it is more specific than elevation.css's
   `.primary-button`, and both carry `!important`. Measured 2026-09-02, Buy Now
   still computed to maroon.

   Rather than keep guessing at the cascade, this states the case at a
   specificity nothing else on the page reaches, scoped to the product form so
   it cannot leak to "Proceed To Checkout" or any other primary button. If a
   later session works out what actually beats the earlier rule, delete this
   block and prove Buy Now is still amber in a browser before believing it.

   Amber matters here: it is the one place the colour appears in the buy box,
   so it reads as the shortcut it is, next to the maroon Add to Cart. */
.cw-body main form button.primary-button,
.cw-body main form button.primary-button:hover {
	background-color: var(--cw-color-amber) !important;
	background-image: none !important;
	border: 1px solid var(--cw-color-amber-deep) !important;
	color: #3A2408 !important;
}

.cw-body main form button.primary-button:hover {
	background-color: var(--cw-color-amber-deep) !important;
}

/* ---------- Add to Cart and Buy Now on one line ---------- */

/* Both reference pages pair them side by side. Bagisto puts Add to Cart inside
   the quantity row and Buy Now outside it, as siblings, so there is no shared
   container to make a flex row of -- see cw-actions-marker for why this is CSS
   rather than a view override or a DOM move.

   Two floats and a clear, which is the one layout mechanism that can put a
   block-level sibling ALONGSIDE a preceding block without reparenting either.
   The marker is what names the quantity row; matching Tailwind's
   `max-w-[470px]` instead is the selector shape that destroyed the filter
   sidebar in §15.2. */
@media (min-width: 641px) {
	.cw-body main form div:has(> [data-cw-actions]) {
		float: left;
		width: calc(58% - .375rem);
		max-width: none;
		margin-right: .75rem;
	}

	.cw-body main form div:has(> [data-cw-actions]) ~ button.primary-button {
		float: left;
		width: calc(42% - .375rem);
		max-width: none;
		/* Matches the quantity row's own mt-8, so the two tops line up. */
		margin-top: 2rem;
	}

	/* Everything after the pair returns to normal flow. Without this the share
	   row, the assurance strip and the offer cards all ride up beside the
	   floats. `:not` keeps the clear off Buy Now itself, which is one of the
	   floats. */
	.cw-body main form div:has(> [data-cw-actions]) ~ *:not(button.primary-button) {
		clear: both;
	}
}

/* Below 640px there is no room for two, and Bagisto's own responsive classes
   already stack them. Nothing to undo -- the float rules simply never apply. */

/* =========================================================================
   PRODUCT PAGE — Specification, Benefits and FAQs

   the product-page rebuild. Three tabs added to Bagisto's own tab strip, matching divinehindu's
   icon-card treatment in the parent brand's palette. Content comes from the
   `cw_pdp_content` attribute; see scripts/generate_pdp_content.py for how it is
   built and, more importantly, for what it is not allowed to say.
   ========================================================================= */

/* auto-fit rather than a fixed column count: spec rows range from 2 to 9 across
   the catalogue, and a fixed grid leaves a two-row product with a wide empty
   gutter and a nine-row one cramped. */
.cw-spec {
	/* ⚠️ FLEX, NOT GRID, AND THE REASON IS THE LAST ROW.

	   Spec counts run from 2 to 9 across the catalogue, so the final row is
	   nearly always a partial one — and a five-card product laid out four
	   across left the fifth card ORPHANED hard against the left edge.

	   `display: grid` cannot fix that. Its `justify-content: center` centres
	   the TRACKS inside the container, and every row shares one track set, so a
	   short last row still begins at track 1. Tried and measured: the cards
	   landed at x=331,595,859 / 331,595 — three across, and the remainder still
	   left-aligned. Only a wrapping flex row centres a partial line. */
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: .9rem;
	max-width: 1100px;
	margin-inline: auto;
}

/* Grow to share a full row, but never past a comfortable card width. */
.cw-spec__card { flex: 1 1 200px; max-width: 250px; }

.cw-spec__card {
	padding: 1.1rem 1rem;
	border: 1px solid var(--cw-color-line);
	border-radius: 14px;
	background: #FFF;
	text-align: center;
}

.cw-spec__card svg {
	width: 26px;
	height: 26px;
	margin: 0 auto .55rem;
	display: block;
	fill: none;
	stroke: var(--cw-color-maroon);
	stroke-width: 1.5;
	stroke-linecap: round;
	stroke-linejoin: round;
}

.cw-spec__label {
	margin: 0;
	font-size: .8rem;
	font-weight: 800;
	letter-spacing: .04em;
	text-transform: uppercase;
	color: var(--cw-color-maroon);
}

.cw-spec__value {
	margin: .3rem 0 0;
	font-size: .95rem;
	line-height: 1.45;
	color: var(--cw-color-ink);
}

/* ---------- benefits ---------- */

.cw-benefits {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
	gap: 1rem;
	max-width: 1100px;
	margin-inline: auto;
}

.cw-benefit {
	padding: 1.4rem 1.25rem;
	border: 1px solid var(--cw-color-line);
	border-radius: 14px;
	background: #FFF;
}

.cw-benefit svg {
	width: 30px;
	height: 30px;
	margin-bottom: .7rem;
	fill: none;
	stroke: var(--cw-color-maroon);
	stroke-width: 1.4;
	stroke-linecap: round;
	stroke-linejoin: round;
}

.cw-benefit__title {
	margin: 0;
	font-size: 1.05rem;
	font-weight: 800;
	color: var(--cw-color-maroon);
}

.cw-benefit__body {
	margin: .45rem 0 0;
	font-size: .93rem;
	line-height: 1.65;
	color: var(--cw-color-ink-soft);
}

/* ---------- FAQs ---------- */

/* Native <details>, so it is keyboard operable, findable by the browser's own
   in-page search, and open before any script runs. */
.cw-faqs {
	max-width: 860px;
	margin-inline: auto;
}

.cw-faq {
	border-bottom: 1px solid var(--cw-color-line);
}

.cw-faq summary {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1rem;
	padding: 1.05rem .25rem;
	cursor: pointer;
	font-size: 1rem;
	font-weight: 600;
	color: var(--cw-color-ink);
	list-style: none;
}

/* Both are needed: ::-webkit-details-marker for Safari, ::marker elsewhere.
   Removing only one leaves a stray triangle on the other. */
.cw-faq summary::-webkit-details-marker { display: none; }
.cw-faq summary::marker { content: ''; }

/* The +/− is drawn with ::after rather than an icon, so it cannot fail to load
   and it flips with the open state without any JavaScript. */
.cw-faq summary::after {
	content: '+';
	flex: none;
	width: 26px;
	height: 26px;
	display: grid;
	place-items: center;
	border-radius: 50%;
	background: var(--cw-color-ground-alt);
	color: var(--cw-color-maroon);
	font-size: 1.15rem;
	font-weight: 700;
	line-height: 1;
}

.cw-faq[open] summary::after { content: '\2212'; }

.cw-faq summary:hover { color: var(--cw-color-maroon); }

.cw-faq summary:focus-visible {
	outline: 2px solid var(--cw-color-maroon);
	outline-offset: 2px;
	border-radius: 6px;
}

.cw-faq > p {
	margin: 0;
	padding: 0 2.4rem 1.15rem .25rem;
	font-size: .95rem;
	line-height: 1.75;
	color: var(--cw-color-ink-soft);
}

@media (max-width: 640px) {
	.cw-spec { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: .6rem; }
	.cw-spec__card { padding: .9rem .7rem; }
	.cw-benefits { grid-template-columns: 1fr; }
}

/* =========================================================================
   OWNER REVIEW FIXES — 2026-09-02
   Seven issues raised after the first pass. Each fix names the issue.
   ========================================================================= */

/* ---------- (2) the selected variant was unreadable ---------- */

/* MEASURED: selecting "5 + Ratti" set the pill's background to maroon
   #6C1322 and left the label at #57453B -- brown on maroon, a contrast ratio
   under 1.5:1. The option a shopper had just chosen was the one they could no
   longer read.

   Bagisto marks the state on a sibling `input:checked` via `peer-checked`
   utilities, so the label is styled through `:has()`. Both the label and its
   inner <span> need the colour: elevation.css sets the span's own colour, and
   inheritance cannot reach past that. */
.cw-body main label.rounded-full:has(input:checked),
.cw-body main label.rounded-full:has(input:checked) span,
.cw-body main label.rounded-full.\!bg-navyBlue,
.cw-body main label.rounded-full.\!bg-navyBlue span {
	color: #FFF7EC !important;
	border-color: var(--cw-color-maroon) !important;
	font-weight: 700 !important;
}

/* A ring as well as the fill, so the choice is not signalled by colour alone
   -- the same reason the % off badge is not red. */
.cw-body main label.rounded-full:has(input:checked) {
	box-shadow: 0 0 0 3px rgba(108, 19, 34, .18);
}

/* ---------- (6) badges had no breathing room ---------- */

/* The stock and countdown pills were set at .4rem/.42rem vertical, which on a
   14px label leaves the text touching the border. Both now use the same
   .55rem/1rem the offer cards use, so the buy box has one padding rhythm
   rather than three. */
.cw-body main .cw-stock,
.cw-body main .cw-countdown {
	padding: .55rem 1rem;
	line-height: 1.35;
}

/* They sit side by side and were 0px apart at some widths. */
.cw-body main .cw-stock + .cw-countdown,
.cw-body main .cw-countdown + .cw-stock {
	margin-left: .6rem;
}

/* ---------- (7) the dead band under the services strip ---------- */

/* MEASURED: the strip ended at y=1937 and the footer began at y=2033 -- a 96px
   empty band on EVERY page, from `.cw-footer { margin-top: clamp(3.5rem, 8vw,
   6rem) }`. That margin is right when the footer follows page content directly
   and wrong when the services strip has already provided the separation, with
   its own top border doing the dividing. */
.cw-usp + .cw-footer {
	margin-top: 0;
}

/* ---------- (5) the tab panels ---------- */

/* A heading, a measure and a rhythm. The Description panel opened with 60px of
   nothing, no heading, and prose centred in a way that read as unfinished. */
/* ⚠️ `ch` IS FONT-RELATIVE, and that is what broke the alignment.

   The heading and the body copy both had `max-width: 68ch` and both were
   centred -- but 68ch at the heading's 1.75rem is far wider than 68ch at the
   body's 1.0625rem, so the two blocks centred to different widths and the
   heading started 176px to the LEFT of the first line of text. It read exactly
   like a mistake, because it was one.

   One measure in rem, shared by both. */
.cw-panel-h {
	max-width: var(--cw-measure);
	margin: 0 auto 1.1rem !important;
	font-family: var(--cw-font-display, inherit);
	font-size: clamp(1.35rem, 2.4vw, 1.75rem);
	font-weight: 700;
	line-height: 1.25;
	color: var(--cw-color-ink);
	letter-spacing: -0.01em;
}

/* The card panels are wider than the reading measure, so their heading is too,
   and it is centred over the grid rather than over a column of prose. */
.cw-spec + .cw-panel-h,
.cw-panel-h--wide {
	max-width: 1100px;
	text-align: center;
}

.cw-panel-sub {
	max-width: var(--cw-measure);
	margin: -0.6rem auto 1.8rem !important;
	font-size: .95rem;
	line-height: 1.6;
	color: var(--cw-color-muted);
}

.cw-panel-sub--wide {
	max-width: 1100px;
	text-align: center;
}

/* 3rem of top padding on a panel that already sits under a 1px tab rule is
   about right; 60px of Tailwind margin on top of it was not. */
.cw-body main [class*="animate-"] > .container {
	padding-block: 2.5rem 3rem;
}

.cw-body main [class*="animate-"] .container.mt-\[60px\] {
	margin-top: 0 !important;
}

/* ---------- (1) which option is selected ---------- */

/* Written next to the pills by pdp.js. The price already moves when a variant
   is chosen, but it sits 200px away and is easy to miss -- and the gallery
   cannot help, because no variant in this catalogue has an image of its own. */
.cw-selected {
	margin: .6rem 0 0;
	font-size: .875rem;
	color: var(--cw-color-muted);
}

.cw-selected strong {
	color: var(--cw-color-maroon);
	font-weight: 700;
}

/* ---------- the CTA label wrapped to two lines ---------- */

/* At 42% of the buy box "Add To Cart" broke as "Add To / Cart", which reads as
   a broken button. The label never wraps; the padding yields instead. */
.cw-body main form button.secondary-button,
.cw-body main form button.primary-button {
	white-space: nowrap;
	padding-inline: clamp(.75rem, 2vw, 2rem) !important;
}

/* =========================================================================
   (3) THE DEPARTMENT BAR

   Both reference sites run the departments as their own full-width row under
   the logo. Ours shared a row with the logo, search and account icons, which
   left it 752px and fitted six of eighteen departments.

   CSS-only: opened by :hover and :focus-within, every entry a real anchor. No
   script to fail, and it is keyboard-operable and crawlable as it stands.
   ========================================================================= */

.cw-dept {
	background: var(--cw-color-ground-alt);
	border-bottom: 1px solid var(--cw-color-line);
	position: relative;
	/* The panels drop out of this bar and must sit above the page. */
	z-index: 40;
}

.cw-dept__inner {
	max-width: var(--cw-wide, 1240px);
	margin-inline: auto;
	padding-inline: clamp(1rem, 3.5vw, 3rem);
	display: flex;
	align-items: stretch;
	justify-content: space-between;
	gap: .25rem;
}

.cw-dept__item {
	position: relative;
	display: flex;
	/* min-width:0 so a long department name shrinks rather than pushing the
	   row wider than the viewport -- the same failure the header search had
	   (SESSION-LOG §15.8). */
	min-width: 0;
}

.cw-dept__link {
	display: flex;
	align-items: center;
	gap: .45rem;
	/* 44px minimum target, before the padding is counted. */
	min-height: 48px;
	padding: .5rem .7rem;
	color: var(--cw-color-ink);
	font-size: .875rem;
	font-weight: 600;
	text-decoration: none;
	white-space: nowrap;
	border-bottom: 3px solid transparent;
	transition: color 160ms ease, border-color 160ms ease;
}

.cw-dept__link:hover,
.cw-dept__item:focus-within > .cw-dept__link {
	color: var(--cw-color-maroon);
	border-bottom-color: var(--cw-color-maroon);
}

.cw-dept__icon {
	width: 20px;
	height: 20px;
	flex: none;
	fill: none;
	stroke: var(--cw-color-maroon);
	stroke-width: 1.5;
	stroke-linecap: round;
	stroke-linejoin: round;
}

.cw-dept__caret {
	width: 14px;
	height: 14px;
	flex: none;
	fill: none;
	stroke: currentColor;
	stroke-width: 2;
	stroke-linecap: round;
	stroke-linejoin: round;
	opacity: .55;
	transition: transform 160ms ease;
}

.cw-dept__item:hover .cw-dept__caret,
.cw-dept__item:focus-within .cw-dept__caret { transform: rotate(180deg); opacity: 1; }

/* ---------- the drop panel ---------- */

.cw-dept__panel {
	position: absolute;
	top: 100%;
	left: 0;
	min-width: 260px;
	padding: .9rem;
	background: #FFF;
	border: 1px solid var(--cw-color-line);
	border-radius: 0 0 14px 14px;
	border-top: 0;
	box-shadow: 0 22px 40px -26px rgba(53, 37, 29, .5);
	/* visibility, not display, so the panel can transition and so an
	   off-screen panel does not count toward the page's scroll width -- the
	   cart drawer taught us that one (SESSION-LOG §16.3). */
	visibility: hidden;
	opacity: 0;
	transform: translateY(-4px);
	transition: opacity 140ms ease, transform 140ms ease, visibility 0s linear 140ms;
}

.cw-dept__item:hover > .cw-dept__panel,
.cw-dept__item:focus-within > .cw-dept__panel {
	visibility: visible;
	opacity: 1;
	transform: translateY(0);
	transition-delay: 0s;
}

/* The last two open leftward, or they run off the right edge of the page. */
.cw-dept__item:nth-last-child(-n+2) > .cw-dept__panel { left: auto; right: 0; }

.cw-dept__panel--wide { min-width: 460px; }

.cw-dept__all {
	display: block;
	padding: .35rem .5rem .6rem;
	margin-bottom: .4rem;
	border-bottom: 1px solid var(--cw-color-line);
	color: var(--cw-color-maroon);
	font-size: .8125rem;
	font-weight: 700;
	text-decoration: none;
}

.cw-dept__all:hover { text-decoration: underline; }

.cw-dept__links {
	display: grid;
	gap: .1rem;
}

.cw-dept__panel--wide .cw-dept__links {
	grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: .1rem 1rem;
}

.cw-dept__links a {
	display: block;
	padding: .5rem;
	border-radius: 8px;
	color: var(--cw-color-ink-soft);
	font-size: .875rem;
	text-decoration: none;
}

.cw-dept__links a:hover {
	background: var(--cw-color-ground-alt);
	color: var(--cw-color-maroon);
}

.cw-dept__link:focus-visible,
.cw-dept__links a:focus-visible,
.cw-dept__all:focus-visible {
	outline: 2px solid var(--cw-color-maroon);
	outline-offset: 2px;
	border-radius: 6px;
}

/* Below 900px the existing <details> drawer is the navigation. */
@media (max-width: 900px) {
	.cw-dept { display: none; }
}

@media (prefers-reduced-motion: reduce) {
	.cw-dept__panel,
	.cw-dept__caret,
	.cw-dept__link { transition: none; }
}

/* =========================================================================
   HOME PAGE RHYTHM — "why so much empty space"

   MEASURED on 2026-09-02: nearly every home section carried
   `margin-top: 96px` AND its own `padding-block` of 64–96px. Between two
   sections that is 96 + 80 + 80 = 256px of nothing, and the gap audit found
   bands of 310px, 205px and 202px on one page.

   The cause is two spacing systems doing the same job. One survives: sections
   own their internal padding, and the margin between them goes. The result is
   a denser page that reads as designed rather than as unfinished — which is
   what both reference sites do.
   ========================================================================= */

.cw-body main > .cw-sec,
.cw-body main > .cw-band,
.cw-body main > .cw-split,
.cw-body main > .cw-rails,
.cw-body main > .cw-promise,
.cw-body main > .cw-strip {
	margin-top: 0 !important;
	margin-bottom: 0 !important;
}

/* One rhythm, fluid, for every section that does not carry its own full-bleed
   artwork. clamp() rather than a media query so it tightens continuously
   rather than stepping at one arbitrary width. */
.cw-body main > .cw-sec,
.cw-body main > .cw-rails,
.cw-body main > .cw-promise {
	padding-block: clamp(2.25rem, 4.5vw, 3.75rem) !important;
}

/* The editorial bands are images with copy laid over them: they set their own
   height, and padding on top of that only pushes the copy off-centre. */
.cw-body main > .cw-band,
.cw-body main > .cw-split {
	padding-block: 0 !important;
}

/* Alternating grounds so the sections read as separate without needing space
   between them to prove it. This is the trick both reference pages use to stay
   dense and still feel sectioned. */
.cw-body main > .cw-sec:nth-of-type(even) {
	background: var(--cw-color-ground-alt);
}

/* A section heading does not need 80px of air under it as well. */
.cw-body main .cw-sec__title { margin-bottom: .35rem; }
.cw-body main .cw-sec__sub { margin-bottom: 1.6rem; }

/* ---------- the rating row on a product card ---------- */

/* Both reference sites carry this and it is the biggest remaining difference
   between their grid and ours.

   The stars are ONE background of five glyphs with a second, clipped copy over
   it, so a 4.5 shows as four and a half without needing half-star artwork or
   five separate elements per card. `aria-hidden` on the visual, with the score
   and the count as real text beside it -- a screen reader reads "4.5 (39)"
   rather than counting stars. */
.cw-card__rating {
	display: flex;
	align-items: center;
	gap: .35rem;
	margin: .35rem 0 .1rem;
	font-size: .8125rem;
	line-height: 1;
}

.cw-card__stars {
	position: relative;
	display: inline-block;
	width: 5.2em;
	height: 1em;
	font-size: .95rem;
	color: var(--cw-color-line);
}

.cw-card__stars::before,
.cw-card__stars > span::before {
	content: '★★★★★';
	letter-spacing: .06em;
}

.cw-card__stars > span {
	position: absolute;
	inset: 0;
	overflow: hidden;
	white-space: nowrap;
	color: var(--cw-color-amber-deep);
}

.cw-card__score {
	font-weight: 700;
	color: var(--cw-color-ink);
	font-variant-numeric: tabular-nums;
}

.cw-card__count {
	color: var(--cw-color-muted);
	font-variant-numeric: tabular-nums;
}

/* =========================================================================
   HOMEPAGE SECTIONS — CW-25 to CW-30, added 2026-09-02

   Press, honours, why-choose, review slider, journal and FAQs.

   ⚠️ Two of these show the PARENT BRAND's assets under attributed headings —
   see the comments in the blades. Do not reword those headings to claim the
   coverage or the celebrity moments for Crystalwala.
   ========================================================================= */

/* ---------- shared rail ---------- */

/* `.cw-carousel` is what pdp.js drifts. Any rail given this class scrolls on
   its own, pauses on hover, focus and drag, and never moves at all under
   prefers-reduced-motion. */
.cw-honours__rail,
.cw-voices__rail {
	list-style: none;
	margin: 0;
	padding: .5rem clamp(1rem, 4vw, 3.5rem);
	display: flex;
	gap: 1rem;
	overflow-x: auto;
	scroll-snap-type: x proximity;
	scrollbar-width: none;
	/* The mask fades both ends, so a rail that is mid-drift reads as
	   continuous rather than as content cut off by the viewport. */
	-webkit-mask-image: linear-gradient(90deg, transparent, #000 4%, #000 96%, transparent);
	mask-image: linear-gradient(90deg, transparent, #000 4%, #000 96%, transparent);
}

.cw-honours__rail::-webkit-scrollbar,
.cw-voices__rail::-webkit-scrollbar { display: none; }

/* ---------- CW-25 press ---------- */


/* The logos arrive as six different crops on six different grounds.
   object-fit: contain normalises them without distorting any of them --
   and `mix-blend-mode` is deliberately NOT used: it turns a logo on a black
   ground into a solid black block, which is the bug that produced two black
   discs in the category strip (SESSION-LOG §16.3). */

/* ---------- CW-28 honours ---------- */

.cw-honours__item {
	flex: none;
	scroll-snap-align: start;
	width: 210px;
	border-radius: 16px;
	overflow: hidden;
	background: #FFF;
	border: 1px solid var(--cw-color-line);
	box-shadow: 0 18px 34px -28px rgba(53, 37, 29, .55);
}

.cw-honours__item img {
	display: block;
	width: 100%;
	height: 250px;
	object-fit: cover;
}

/* ---------- CW-29 why choose ---------- */

.cw-why__grid {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
	gap: 1rem;
}

.cw-why__card {
	padding: 1.5rem 1.25rem;
	background: #FFF;
	border: 1px solid var(--cw-color-line);
	border-radius: 16px;
}

.cw-why__card svg {
	width: 30px;
	height: 30px;
	margin-bottom: .7rem;
	fill: none;
	stroke: var(--cw-color-maroon);
	stroke-width: 1.4;
	stroke-linecap: round;
	stroke-linejoin: round;
}

.cw-why__card h3 {
	margin: 0;
	font-size: 1.05rem;
	font-weight: 800;
	color: var(--cw-color-ink);
}

.cw-why__card p {
	margin: .4rem 0 0;
	font-size: .92rem;
	line-height: 1.6;
	color: var(--cw-color-muted);
}

/* ---------- CW-26 review slider ---------- */

.cw-voice {
	flex: none;
	scroll-snap-align: start;
	width: 320px;
	padding: 1.4rem 1.3rem;
	background: #FFF;
	border: 1px solid var(--cw-color-line);
	border-radius: 16px;
	display: flex;
	flex-direction: column;
	gap: .8rem;
}

.cw-voice__head { display: flex; align-items: center; gap: .7rem; }

/* Initials, not a stock portrait. We hold no photographs of these people, and
   a stock face presented as a customer is a person who never reviewed
   anything — see the note in voices.blade.php. */
.cw-voice__avatar {
	flex: none;
	width: 44px;
	height: 44px;
	display: grid;
	place-items: center;
	border-radius: 50%;
	background: var(--cw-color-ground-alt);
	color: var(--cw-color-maroon);
	font-weight: 800;
	font-size: .95rem;
	letter-spacing: .02em;
}

.cw-voice__name { margin: 0; font-weight: 700; color: var(--cw-color-ink); font-size: .95rem; }

.cw-voice__stars { margin: .15rem 0 0; color: var(--cw-color-amber-deep); font-size: .95rem; letter-spacing: .06em; }

.cw-voice__body {
	margin: 0;
	flex: 1;
	font-size: .95rem;
	line-height: 1.65;
	color: var(--cw-color-ink-soft);
}

.cw-voice__product {
	margin: 0;
	font-size: .8rem;
	color: var(--cw-color-muted);
	border-top: 1px solid var(--cw-color-line);
	padding-top: .7rem;
}

/* ---------- CW-27 journal ---------- */

.cw-journal__grid {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
	gap: 1.2rem;
}

.cw-journal__card a {
	display: flex;
	flex-direction: column;
	height: 100%;
	background: #FFF;
	border: 1px solid var(--cw-color-line);
	border-radius: 16px;
	overflow: hidden;
	text-decoration: none;
	color: inherit;
	transition: transform 180ms ease, box-shadow 180ms ease;
}

.cw-journal__card a:hover {
	transform: translateY(-3px);
	box-shadow: 0 22px 40px -28px rgba(53, 37, 29, .6);
}

.cw-journal__media { display: block; aspect-ratio: 16 / 9; background: var(--cw-color-ground-alt); }

.cw-journal__media img { width: 100%; height: 100%; object-fit: cover; display: block; }

.cw-journal__date {
	display: block;
	padding: 1rem 1.1rem 0;
	font-size: .75rem;
	letter-spacing: .06em;
	text-transform: uppercase;
	color: var(--cw-color-muted);
}

.cw-journal__title {
	display: block;
	padding: .35rem 1.1rem 0;
	font-size: 1.05rem;
	font-weight: 700;
	line-height: 1.35;
	color: var(--cw-color-ink);
}

.cw-journal__more {
	display: block;
	margin-top: auto;
	padding: .9rem 1.1rem 1.2rem;
	font-size: .875rem;
	font-weight: 700;
	color: var(--cw-color-maroon);
}

.cw-journal__ext { font-size: .9em; }

.cw-journal__all { margin: 1.4rem 0 0; text-align: center; }

/* ---------- CW-30 homepage FAQs ---------- */

.cw-faqs--home { max-width: 860px; margin-inline: auto; }

@media (max-width: 640px) {
	.cw-voice { width: 270px; }
	.cw-honours__item { width: 160px; }
	.cw-honours__item img { height: 190px; }
}

/* ---------- CW-32: WhatsApp in the header ---------- */

/* WhatsApp green, not the brand maroon. This is the one place a foreign colour
   is right: the icon is recognised BY its colour, and a maroon WhatsApp mark
   reads as a generic chat bubble. It is the only such exception on the site. */
.cw-iconlink--wa svg {
	fill: none;
	stroke: #25D366;
	stroke-width: 1.6;
	stroke-linecap: round;
	stroke-linejoin: round;
}

.cw-iconlink--wa:hover svg { stroke: #128C7E; }

/* Below 480px the header is already tight — the drawer carries it there. */
@media (max-width: 480px) {
	.cw-iconlink--wa { display: none; }
}

/* ---------- journal: excerpt and the listing page ---------- */

.cw-journal__excerpt {
	display: block;
	padding: .45rem 1.1rem 0;
	font-size: .9rem;
	line-height: 1.55;
	color: var(--cw-color-muted);
}

/* The listing lives inside a CMS page, so it inherits that page's wrapper
   rather than a home section. It needs its own measure and grid. */
.cw-journal__grid--listing {
	list-style: none;
	margin: 2rem 0 0;
	padding: 0;
}

.cw-journal__lede {
	max-width: var(--cw-measure);
	font-size: 1.05rem;
	line-height: 1.7;
	color: var(--cw-color-ink-soft);
}

.cw-journal__grid--listing img {
	width: 100%;
	aspect-ratio: 16 / 9;
	object-fit: cover;
	display: block;
}

.cw-journal__grid--listing a {
	display: flex;
	flex-direction: column;
	height: 100%;
	background: #FFF;
	border: 1px solid var(--cw-color-line);
	border-radius: 16px;
	overflow: hidden;
	text-decoration: none;
	color: inherit;
}

.cw-journal__grid--listing a:hover { box-shadow: 0 22px 40px -28px rgba(53, 37, 29, .6); }

/* =========================================================================
   CMS PAGES — the journal posts, and every policy page

   Bagisto renders a CMS page as raw `html_content` inside a bare
   `.container`. No measure, no heading scale, no rhythm: the journal posts
   ran the full 1,440px width with h2s indistinguishable from body text, and
   the ten policy pages have looked the same since launch.

   🔴 THE SCOPE WAS ON THE WRONG ELEMENT AND IT LEAKED ONTO /search — CW-56,
   CW-57, reported by the client as "reduce space, fix space alignment".

   It read `main:not(:has(.cw-card)) > .container`, which asks "does THIS BOX
   contain a product card". On the search page the cards are in a SIBLING
   container: the heading sits in its own `.container` holding only "Results
   for ..." and the toolbar, so that box has no card, matched, and took
   `padding-block: 2.5rem 4rem`. Measured at 390px: 64px of dead space between
   the heading and the first product, and the same again below.

   The question that was meant is "is this a PAGE with no product grid", so the
   `:has(.cw-card)` test moved onto `main`. A page showing products is now
   excluded whole, whichever box the cards happen to sit in.
   ========================================================================= */

.cw-body main:not(:has(.cw-card)) > .container:not(:has([class*="animate-"])) {
	max-width: calc(var(--cw-measure) + 6rem);
	margin-inline: auto;
	padding-block: 2.5rem 4rem;
}

.cw-body main:not(:has(.cw-card)) > .container > p,
.cw-body main:not(:has(.cw-card)) > .container > ul,
.cw-body main:not(:has(.cw-card)) > .container > ol {
	max-width: var(--cw-measure);
	font-size: 1.0625rem;
	line-height: 1.8;
	color: var(--cw-color-ink-soft);
	margin: 0 0 1.15rem;
}

.cw-body main:not(:has(.cw-card)) > .container > h2 {
	max-width: var(--cw-measure);
	margin: 2.4rem 0 .7rem;
	font-size: clamp(1.3rem, 2.2vw, 1.6rem);
	font-weight: 700;
	line-height: 1.25;
	color: var(--cw-color-ink);
	letter-spacing: -0.01em;
}

.cw-body main:not(:has(.cw-card)) > .container > h3 {
	max-width: var(--cw-measure);
	margin: 1.8rem 0 .5rem;
	font-size: 1.1rem;
	font-weight: 700;
	color: var(--cw-color-ink);
}

/* A list of what damages which stone is the most useful thing on the care
   page, and it was rendering as unmarked paragraphs. */
.cw-body main:not(:has(.cw-card)) > .container > ul {
	padding-left: 1.3rem;
	list-style: disc;
}

.cw-body main:not(:has(.cw-card)) > .container > ul > li { margin-bottom: .55rem; }

.cw-body main:not(:has(.cw-card)) > .container strong { color: var(--cw-color-ink); font-weight: 700; }

.cw-body main:not(:has(.cw-card)) > .container a {
	color: var(--cw-color-maroon);
	text-decoration: underline;
	text-underline-offset: 2px;
}

/* The listing page's card grid must NOT inherit the reading measure. */
.cw-body main > .container .cw-journal__grid--listing {
	max-width: none;
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
	gap: 1.2rem;
}

/* ---------- journal post header ---------- */

.cw-body main .cw-post__eyebrow {
	max-width: var(--cw-measure);
	margin: 0 0 .4rem !important;
	font-size: .75rem;
	letter-spacing: .1em;
	text-transform: uppercase;
	color: var(--cw-color-amber-deep);
	font-weight: 700;
}

.cw-body main .cw-post__title {
	max-width: var(--cw-measure);
	margin: 0 0 1.6rem;
	font-size: clamp(1.9rem, 4vw, 2.6rem);
	font-weight: 700;
	line-height: 1.15;
	letter-spacing: -0.02em;
	color: var(--cw-color-ink);
}

.cw-body main .cw-post__back {
	max-width: var(--cw-measure);
	margin-top: 2.5rem !important;
	padding-top: 1.4rem;
	border-top: 1px solid var(--cw-color-line);
	font-weight: 600;
}

/* =========================================================================
   CW-31 — the mega panel

   The single-column dropdown becomes a grid of grouped columns, matching the
   reference at prnt.sc/us1DLsaMoXd_.

   Still CSS-only: :hover and :focus-within, real anchors, no script.
   ========================================================================= */

.cw-dept__panel--mega {
	/* Anchored to the BAR, not to the item, so a four-column panel is not
	   dragged off the right edge by a department that happens to sit last.
	   The bar is position:relative for exactly this. */
	position: absolute;
	left: 50%;
	transform: translate(-50%, -4px);
	width: min(1100px, calc(100vw - 3rem));
	display: grid;
	grid-template-columns: repeat(var(--cols, 3), minmax(0, 1fr));
	gap: 1.2rem 1.8rem;
	padding: 1.5rem 1.75rem 1.25rem;
	border-radius: 0 0 18px 18px;
}

.cw-dept__item:hover > .cw-dept__panel--mega,
.cw-dept__item:focus-within > .cw-dept__panel--mega {
	transform: translate(-50%, 0);
}

/* The nth-last-child rule that pulls small panels leftward must not apply to
   a centred mega panel. */
.cw-dept__item:nth-last-child(-n+2) > .cw-dept__panel--mega { left: 50%; right: auto; }

.cw-dept__panel--mega[data-cols="1"] { --cols: 1; width: min(340px, calc(100vw - 3rem)); }
.cw-dept__panel--mega[data-cols="2"] { --cols: 2; width: min(620px, calc(100vw - 3rem)); }
.cw-dept__panel--mega[data-cols="3"] { --cols: 3; width: min(880px, calc(100vw - 3rem)); }
.cw-dept__panel--mega[data-cols="4"] { --cols: 4; }

.cw-dept__coltitle {
	margin: 0 0 .5rem;
	padding-bottom: .4rem;
	border-bottom: 1px solid var(--cw-color-line);
	font-size: .7rem;
	font-weight: 800;
	letter-spacing: .09em;
	text-transform: uppercase;
	/* The reference uses a warm accent for the group headings, which is what
	   makes the grid readable as groups rather than as one long list. */
	color: var(--cw-color-amber-deep);
}

.cw-dept__panel--mega .cw-dept__all {
	grid-column: 1 / -1;
	margin: .3rem 0 0;
	padding: .7rem .5rem 0;
	border-top: 1px solid var(--cw-color-line);
	border-bottom: 0;
}

@media (max-width: 1100px) {
	.cw-dept__panel--mega[data-cols="4"] { --cols: 2; width: min(620px, calc(100vw - 3rem)); }
	.cw-dept__panel--mega[data-cols="3"] { --cols: 2; width: min(620px, calc(100vw - 3rem)); }
}

/* ⚠️ THE ITEM MUST NOT BE A POSITIONING CONTEXT.

   `.cw-dept__item` carried `position: relative` from the single-column
   dropdown, so a panel with `left: 50%` centred on the ITEM rather than on the
   bar. Measured: the second department's 1,100px panel sat at x=-239..861 --
   239px off the left edge -- and the right-hand departments' panels pushed the
   document to scrollWidth 1599 on a 1440 viewport.

   A hidden panel still contributes to scroll width: `visibility: hidden` keeps
   the box, and these are `position: absolute` so they are out of flow but not
   out of the scrollable area. That is why closed panels caused the overflow.

   Static item, so `.cw-dept` (which is position:relative) is the context. */
.cw-body .cw-dept__item { position: static; }

/* The panel is centred on the BAR, so clamp it to the bar's own gutters rather
   than letting a 1,100px panel decide the page width. */
.cw-body .cw-dept { overflow-x: clip; }

/* =========================================================================
   CW-23 — search suggestions

   Rendered by pdp.js into an empty container. With JavaScript off the search
   form is an ordinary GET and none of this exists, which is the point.
   ========================================================================= */

.cw-search { position: relative; }

.cw-typeahead {
	position: absolute;
	top: calc(100% + .5rem);
	left: 0;
	right: 0;
	z-index: 60;
	max-height: min(70vh, 460px);
	overflow-y: auto;
	padding: .4rem;
	background: #FFF;
	border: 1px solid var(--cw-color-line);
	border-radius: 14px;
	box-shadow: 0 24px 44px -26px rgba(53, 37, 29, .55);
}

.cw-typeahead[hidden] { display: none; }

.cw-typeahead__item {
	display: grid;
	grid-template-columns: 48px 1fr auto;
	align-items: center;
	gap: .7rem;
	padding: .5rem;
	border-radius: 10px;
	text-decoration: none;
	color: var(--cw-color-ink);
}

/* Hover AND aria-selected, so the keyboard user sees the same highlight the
   mouse user does. Driving it from the ARIA state rather than a second class
   keeps the two from drifting apart. */
.cw-typeahead__item:hover,
.cw-typeahead__item[aria-selected="true"],
.cw-typeahead__all:hover,
.cw-typeahead__all[aria-selected="true"] {
	background: var(--cw-color-ground-alt);
}

.cw-typeahead__item img,
.cw-typeahead__noimg {
	width: 48px;
	height: 48px;
	border-radius: 8px;
	object-fit: cover;
	background: var(--cw-color-ground-alt);
}

.cw-typeahead__name {
	font-size: .9rem;
	line-height: 1.35;
	/* Two lines maximum: a 90-character product name would otherwise make one
	   suggestion taller than the rest of the panel. */
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
}

.cw-typeahead__price {
	font-weight: 700;
	font-size: .875rem;
	color: var(--cw-color-maroon);
	white-space: nowrap;
	font-variant-numeric: tabular-nums;
}

.cw-typeahead__all {
	display: block;
	margin-top: .3rem;
	padding: .7rem .5rem;
	border-top: 1px solid var(--cw-color-line);
	border-radius: 0 0 10px 10px;
	font-size: .85rem;
	font-weight: 700;
	color: var(--cw-color-maroon);
	text-decoration: none;
}

.cw-typeahead__none {
	margin: 0;
	padding: 1rem .75rem;
	font-size: .875rem;
	color: var(--cw-color-muted);
}

/* The live typeahead panel is appended to <body>, outside Bagisto's Vue tree
   — see the note in pdp.js. It is positioned in script, so it needs absolute
   rather than the anchored positioning the in-form version used. */
.cw-typeahead--floating {
	position: absolute;
	top: 0;
	left: 0;
	right: auto;
	z-index: 200;
}

/* ---------- CW-34: the category description ---------- */

/* ⚠️ SMALL AND QUIET, ON PURPOSE. The ticket is explicit about why: "make sure
   to use small font on category pages, as people are coming there to buy
   products not reading text." Two sentences at .875rem sit above the grid
   without pushing the first row of products below the fold, which is the
   thing that would actually cost sales. Resist enlarging it. */
.cw-body .cw-cathead__blurb {
	max-width: 62ch;
	margin: .6rem 0 0;
	font-size: .875rem;
	line-height: 1.6;
	color: var(--cw-color-muted);
}

.cw-body .cw-cathead__blurb p { margin: 0; }

@media (max-width: 640px) {
	/* On a phone the grid is the whole point and the fold is much higher up. */
	.cw-body .cw-cathead__blurb { font-size: .8125rem; }
}

/* =========================================================================
   CW-33 — the WhatsApp signup popup
   ========================================================================= */

.cw-signup { position: fixed; inset: 0; z-index: 300; display: grid; place-items: center; }
.cw-signup[hidden] { display: none; }

.cw-signup__scrim { position: absolute; inset: 0; background: rgba(53, 37, 29, .55); }

.cw-signup__card {
	position: relative;
	width: min(420px, calc(100vw - 2rem));
	max-height: calc(100vh - 2rem);
	overflow-y: auto;
	padding: 2rem 1.75rem 1.75rem;
	background: #FFF;
	border-radius: 18px;
	box-shadow: 0 40px 80px -40px rgba(53, 37, 29, .7);
	text-align: center;
}

.cw-signup__x {
	position: absolute;
	top: .5rem;
	right: .6rem;
	width: 40px;
	height: 40px;
	border: 0;
	background: transparent;
	font-size: 1.6rem;
	line-height: 1;
	color: var(--cw-color-muted);
	cursor: pointer;
	border-radius: 50%;
}

.cw-signup__x:hover { background: var(--cw-color-ground-alt); color: var(--cw-color-ink); }

.cw-signup__eyebrow {
	margin: 0;
	font-size: .72rem;
	letter-spacing: .12em;
	text-transform: uppercase;
	font-weight: 700;
	color: var(--cw-color-amber-deep);
}

.cw-signup__h {
	margin: .4rem 0 .5rem;
	font-size: clamp(1.4rem, 4vw, 1.8rem);
	font-weight: 700;
	line-height: 1.2;
	color: var(--cw-color-ink);
}

.cw-signup__sub { margin: 0 0 1.3rem; font-size: .9rem; line-height: 1.6; color: var(--cw-color-muted); }

.cw-signup__row {
	display: flex;
	align-items: stretch;
	border: 1px solid var(--cw-color-line);
	border-radius: 10px;
	overflow: hidden;
}

.cw-signup__cc {
	display: grid;
	place-items: center;
	padding: 0 .8rem;
	background: var(--cw-color-ground-alt);
	border-right: 1px solid var(--cw-color-line);
	font-weight: 700;
	font-size: .9rem;
	color: var(--cw-color-ink-soft);
}

.cw-signup__row input {
	flex: 1 1 auto;
	min-width: 0;
	padding: .75rem .8rem;
	border: 0;
	font-size: 1rem;
	color: var(--cw-color-ink);
}

.cw-signup__row input:focus-visible { outline: 2px solid var(--cw-color-maroon); outline-offset: -2px; }

.cw-signup__consent {
	display: flex;
	align-items: flex-start;
	gap: .55rem;
	margin: .9rem 0 1.1rem;
	font-size: .8125rem;
	line-height: 1.5;
	color: var(--cw-color-muted);
	text-align: left;
	cursor: pointer;
}

/* 18px and not smaller: this is the control that records consent, and a
   checkbox nobody can hit is a consent nobody gave. */
.cw-signup__consent input {
	flex: none;
	width: 18px;
	height: 18px;
	margin-top: .1rem;
	accent-color: var(--cw-color-maroon);
}

.cw-signup__go {
	width: 100%;
	min-height: 48px;
	border: 0;
	border-radius: 12px;
	background: var(--cw-color-maroon);
	color: #FFF7EC;
	font-size: 1rem;
	font-weight: 700;
	cursor: pointer;
}

.cw-signup__go:hover { background: var(--cw-color-maroon-dark); }

.cw-signup__msg { margin: .9rem 0 0; font-size: .875rem; line-height: 1.5; min-height: 1.2em; }
.cw-signup__msg[data-state="error"] { color: #9B2C2C; }
.cw-signup__msg[data-state="ok"] { color: var(--cw-color-save); font-weight: 600; }
.cw-signup__msg strong { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: .04em; }

/* A global `label` rule uppercases form labels across the theme. That is fine
   for "PINCODE" and wrong here: this is the sentence that records consent, and
   consent text has to be plainly readable rather than styled. */
.cw-body .cw-signup__consent,
.cw-body .cw-signup__consent span {
	text-transform: none !important;
	letter-spacing: normal !important;
	font-weight: 400 !important;
}

/* =========================================================================
   HERO VIDEO — replaces the three-slide carousel, 2026-09-03

   Owner instruction: "replace the carousel images with video of stones and
   elegant behind the text". The copy, scrim and CTAs are unchanged; only what
   sits behind them differs.
   ========================================================================= */

.cw-hero__video {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	/* `cover` is the whole point: the hero is a fixed band and the video is
	   16:9, so on a tall phone or an ultrawide desktop one axis must be
	   cropped rather than letterboxed. */
	object-fit: cover;
	/* Belt and braces with the poster. The video element paints its poster
	   first, but a video that fails to decode leaves a transparent box, and
	   the headline would then sit on the section's maroon gradient with no
	   guaranteed contrast. */
	background: #2C0B0D center / cover no-repeat;
	display: block;
	/* Below the scrim, which is z-index auto inside a -2 stage. */
	z-index: -1;
	/* Nothing here is interactive. Without this the video swallows clicks
	   aimed at the copy on some browsers. */
	pointer-events: none;
}

/* ⚠️ A LOOPING VIDEO IS EXACTLY WHAT prefers-reduced-motion IS FOR.

   It is ambient, continuous, unstoppable movement behind text somebody is
   trying to read — a vestibular trigger, and the single most common complaint
   about video heroes. pdp.js pauses it; this hides it so the poster shows
   through, which is the same picture without the motion. */
@media (prefers-reduced-motion: reduce) {
	.cw-hero__video { display: none; }
}

/* When the video is present the stage needs no Ken Burns pan — the video is
   the movement. The still fallback keeps it. */
.cw-hero--video .cw-hero__slide.is-on { animation: none; }

/* The scrim was tuned against three known photographs. A video's brightness
   varies frame to frame, so the left-hand end is deepened: the headline has to
   stay readable over the brightest frame, not the average one. */
.cw-hero--video .cw-hero__scrim {
	background:
		linear-gradient(90deg,
			rgba(20, 5, 6, .96) 0%,
			rgba(32, 8, 10, .90) 30%,
			rgba(52, 13, 15, .52) 62%,
			rgba(70, 20, 20, .22) 100%),
		linear-gradient(0deg, rgba(20, 5, 6, .55) 0%, rgba(20, 5, 6, 0) 45%);
}

/* ---------- CW-36: the header and department bar stick together ---------- */

/* One sticky element, not two. See the Blade comment for why an offset on the
   bar was rejected: `.cw-scrolled` shrinks the logo, so the header's height is
   not a constant and any `top: <height>` drifts by 20px as you scroll.

   ⚠️ z-index 50 IS CHOSEN, NOT ARBITRARY. It has to clear ordinary page
   content and the open mega panel, and it has to stay BELOW the things that
   are meant to cover the page: the buy bar (55), the cart drawer and its scrim
   (90/91), the floating typeahead (200) and the signup dialog (300). Sticky
   creates a stacking context, so everything inside the header is trapped under
   this number — raise it and the cart drawer opens behind the menu. */
.cw-headwrap {
	position: sticky;
	top: 0;
	z-index: 50;
}

/* ⚠️ A STICKY HEADER HIDES ANYTHING YOU SCROLL TO, and this site does scroll
   to things. Bagisto's `scrollToReview()` — the handler behind the "85
   Ratings" line in the buy box — calls `scrollIntoView({behavior:'smooth'})`
   with the default `block: 'start'`, which parks the target at viewport top,
   i.e. underneath 166px of pinned header. The same applies to any `#hash`
   landing.

   `scroll-padding-top` on the scroll container is the fix the platform
   provides for exactly this: the browser subtracts it from every scroll-into-
   view and every fragment jump. It is NOT a margin and moves nothing on the
   page.

   Measured heights of `.cw-headwrap` while scrolled (the state that matters,
   since `.cw-scrolled` shrinks the logo): 166px at 1440, 117px at 900 (the
   department bar is hidden below 900), 178px at 390 (the search takes its own
   row). Each value below is its breakpoint's height plus ~12px of air. */
html { scroll-padding-top: 178px; }

@media (max-width: 900px) { html { scroll-padding-top: 130px; } }
@media (max-width: 700px) { html { scroll-padding-top: 190px; } }

/* It was sticky on its own; the wrapper owns that now. Two nested stickies
   with the same `top` fight each other. */
.cw-body .cw-header { position: static; }

/* =========================================================================
   THE HEADER ROW — rebuilt 2026-09-03

   THE PROBLEM, MEASURED, not guessed. At 1440px the bar rendered:

       logo right edge  x = 176
       actions left edge x = 957.6
       ------------------------------------
       dead space        781.6px

   — more than half the row carrying nothing at all, with the logo pinned hard
   left and every control crushed into the right margin. The search field was
   230px wide holding a 222px placeholder in 196px of usable inner width, so
   "Search rudraksha, malas, crystals" rendered as "Search rudraksha, malas,…".
   Both are visible in docs/Reference-screenshots/crystalwalaissue3.png.

   THE FIX is a three-column grid, `1fr auto 1fr`, which is what makes the logo
   sit at the true optical centre of the bar rather than wherever the flex
   items happen to leave it. The side columns are equal by construction, so the
   centring cannot drift as the pill labels or the cart badge change width.

   ⚠️ `min-width: 0` ON BOTH SIDE COLUMNS IS LOAD-BEARING. A grid item's
   automatic minimum size is its content, so without it the search field's
   preferred width would push the right column wider than its 1fr share and
   shove the logo off centre — the exact failure this rule replaces.
   ========================================================================= */

.cw-body .cw-header__inner {
	display: grid;
	/* ⚠️ `auto 1fr auto`, NOT `1fr auto 1fr`. The first version centred the
	   logo with quick-link pills to its left, and the owner rejected it on
	   sight — "The logo in center and categories/tabs in left is not looking
	   good. Logo should be on left only."

	   The 781px of dead space that the centred version existed to solve is
	   still solved, just differently: the SEARCH FIELD takes the middle track
	   instead of a row of links. Logo left · search · actions right is what
	   every large Indian storefront runs, and it needs no invented furniture
	   between the logo and the field. */
	grid-template-columns: auto 1fr auto;
	grid-template-areas: "logo search actions";
	align-items: center;
	gap: var(--cw-space-30);
}

.cw-body .cw-logo           { grid-area: logo;    justify-self: start; }
.cw-body .cw-search         { grid-area: search; }
.cw-body .cw-header__actions { grid-area: actions; }

/* ⚠️ `justify-self: end` HERE WOULD BE A BUG, and it was one for a while.
   It shrink-wraps the grid item to its content, so the flex container ends up
   exactly as wide as the icons plus the field's own preferred width — leaving
   `flex: 1 1 auto` on the search nothing to grow into. The field stayed at
   233px inside a 596px column. `stretch` (the default) makes the item fill its
   track and `justify-content: flex-end` does the actual right-alignment. */
.cw-body .cw-header__actions {
	justify-content: flex-end;
	min-width: 0;
	margin-left: 0;
	/* The search is no longer inside this div, so it no longer needs to stretch
	   to give the field somewhere to grow — the field is its own grid item now.
	   `end` lets the cluster shrink-wrap and sit hard right. */
	justify-self: end;
}

/* ---------- the quick-link pills: REMOVED 2026-09-03 ---------- */

/* `.cw-quick` and `.cw-quick__pill` are gone entirely, markup and all. They
   held Rudraksha · Karungali · Rashi to the left of a centred logo and the
   owner rejected the whole arrangement. Do not resurrect them here without
   resurrecting the markup too — a rule for an element that does not exist is
   the kind of thing that survives three sessions and then confuses somebody. */

/* ---------- the search field ---------- */

/* Sized to its own placeholder, not to whatever was left over. The longest
   placeholder measures 222px at .9375rem; 26rem of field less the 1.15rem and
   3rem of padding leaves ~350px, which fits it with room to spare at every
   width down to the 900px breakpoint. `flex: 1 1 auto` lets it give that room
   back before the icons are touched. */
/* ⚠️ THIS IS NOW THE ELEMENT THAT FILLS THE BAR, so it is sized generously
   rather than capped at the 26rem it had when it shared the row with three
   pills. It is centred in its own grid track, not left-aligned against the
   logo: pinned to the logo it left a single wide void before the icons, which
   is the exact complaint the whole rebuild started from. */
.cw-body .cw-search {
	flex: 1 1 auto;
	min-width: 0;
	width: 100%;
	max-width: 52rem;
	margin-inline: auto;
}

.cw-body .cw-search input {
	width: 100%;
	min-width: 0;
	font-size: .9375rem;
	padding: .78rem 3rem .78rem 1.15rem;
	/* The field is the widest thing in the row and a hairline made it read as
	   a gap rather than a control. */
	border-color: #E0D2BC;
	box-shadow: inset 0 1px 2px rgba(53, 37, 29, .04);
}

.cw-body .cw-search button {
	right: .35rem;
	width: 2.25rem;
	height: 2.25rem;
	background: var(--cw-color-gold-soft);
}

.cw-body .cw-search button:hover { background: var(--cw-color-maroon); color: #FFF; }

/* ---------- WhatsApp: filled, not stroked ---------- */

/* The mark is now WhatsApp's own glyph, which is a FILL path. The shared
   `.cw-iconlink svg` rule sets `fill: none; stroke: currentColor`, which would
   render it as nothing at all — both properties have to be reversed here, and
   the earlier stroke rule further up this file is superseded by this one. */
.cw-body .cw-iconlink--wa svg {
	fill: #25D366;
	stroke: none;
	/* Optically smaller than the stroked icons beside it: a solid shape reads
	   heavier than an outline at the same box size. */
	width: 1.15rem;
	height: 1.15rem;
}

.cw-body .cw-iconlink--wa:hover svg { fill: #128C7E; stroke: none; }

/* ---------- below the breakpoint ---------- */

/* ⚠️ A SYMMETRIC GRID STARVES THE SEARCH FIELD, and the arithmetic is worth
   writing down because it is not visible in the rule.

   `1fr auto 1fr` forces the two side columns to the SAME width whatever they
   hold. At 1440px that leaves the right column 596px and the field its full
   26rem. At 1024px the same expression gives it 412px, of which the three
   44px icons and their gaps take 180 — so the field lands at 232px and the
   placeholder is clipped again, in a different way and for a different
   reason than before. Measured, not predicted: avail 164.6 against a 222px
   placeholder.

   So the centred logo is given up at the width where it starts costing the
   field, rather than being defended down to the phone. The row becomes
   pills · logo · everything-else, the actions column takes the slack, and a
   left-aligned logo at 1100px reads as perfectly ordinary. */
/* The 1180px breakpoint that used to give up the centred logo is gone with the
   centred logo itself — `auto 1fr auto` already puts the logo left at every
   width, and the search track simply narrows. */

/* The pills go first. Every one of them is in the drawer, and at 900px the row
   has to hold the hamburger, the logo, the field and three icons. */
/* ---------- below 700px: the search gets its own row ---------- */

/* 🔴 THE FIELD WAS 102px WIDE AT 390px AND RENDERED ITS PLACEHOLDER AS "S...".

   One row cannot hold a drawer toggle, the logo, a usable search field and
   three icons on a phone — measured: 102px box, 34px of usable inner width.
   Squeezing it further was not an option and hiding it was worse: search is how
   you reach 1,279 products from a screen that shows four.

   So the search moves to a full-width second row, which is what Flipkart and
   Myntra do, and needs no JavaScript. It is only possible because the field is
   now a SIBLING of the logo rather than nested inside the actions div — a grid
   cannot move a grandchild onto its own row, which is why the markup had to
   change first.

   700px, not 900: at 768 the single row still gives the field 373px and the
   placeholder fits with room to spare, so the taller header is not earned
   there. Measured at 620px it is not. */
@media (max-width: 700px) {
	/* ⚠️ CW-55 — THE LOGO IS CENTRED HERE, AND ONLY HERE.
	   `1fr auto 1fr`, not `auto auto 1fr`: equal side tracks are what put the
	   mark at the row's true optical centre, so it cannot drift as the cart
	   badge or the drawer icon change width. It measured 79px left of centre
	   before, which is what the client circled twice on their screenshots.

	   🔴 THIS IS NOT A REVERSAL OF §22.13. That rejection was of a CENTRED
	   DESKTOP HEADER with quick-link pills beside the logo — "The logo in
	   center and categories/tabs in left is not looking good. Logo should be on
	   left only." The desktop rule above still reads `auto 1fr auto` with the
	   logo hard left and it must stay that way. A phone has no pills, no
	   department bar and no room for a search field on the same row, and a
	   centred mark between a drawer toggle and the account icons is what the
	   client asked for in writing. Two different rows, two different answers.

	   ⚠️ `min-width: 0` ON BOTH SIDE TRACKS IS LOAD-BEARING. A grid item's
	   automatic minimum size is its content, so without it the actions cluster
	   would push its track wider than its 1fr share and shove the logo off
	   centre — the exact failure this replaces. Same trap as the desktop
	   header's own note. */
	.cw-body .cw-header__inner {
		grid-template-columns: 1fr auto 1fr;
		grid-template-areas:
			"nav logo actions"
			"search search search";
		gap: .6rem .7rem;
	}

	.cw-body .cw-mobnav { grid-area: nav; justify-self: start; min-width: 0; }
	.cw-body .cw-logo   { grid-area: logo; justify-self: center; }
	.cw-body .cw-search { grid-area: search; max-width: none; margin-inline: 0; }

	.cw-body .cw-header__actions {
		grid-area: actions;
		justify-self: end;
		min-width: 0;
		/* ⚠️ A CENTRED 84px LOGO AND A 164px ICON CLUSTER DO NOT BOTH FIT AT
		   390px, and that is arithmetic rather than opinion. Content width is
		   342px; equal side tracks are therefore (342-84)/2 = 129px each,
		   against three 44px targets and two 16px gaps = 164px. The cluster
		   overflowed its track by 35px and the wishlist heart landed ON the
		   logo — visible in the first screenshot after centring.

		   ⚠️ TIGHTENING THE CLUSTER ALONE IS NOT ENOUGH, AND THE FIRST TWO
		   ATTEMPTS BOTH STILL OVERLAPPED. Do the arithmetic before changing a
		   number here:

		       track = (viewport - 2*padding - logo) / 2      must be >= actions

		   At 390 with 24px padding and an 84px logo the track is 129px against
		   a 164px cluster (3 x 44 + 2 x 16). Cutting to 40px targets and 6px
		   gaps gives 132px — still 3px over, and 38px over at 320.

		   All three levers move together instead: padding 24 -> 12, the logo
		   made fluid, and 36px targets with 4px gaps (116px total). Worst case
		   is 320px: track = (320 - 24 - 60) / 2 = 118 >= 116. Two pixels, and
		   measured rather than hoped for.

		   36px is comfortably above WCAG 2.5.8's 24px minimum and the suite's
		   `no tiny tap targets` check; the 44px guideline is kept at every
		   width above 700px. */
		gap: 4px;
	}

	/* ⚠️ The other two levers — the row's own padding and the logo's width —
	   are at the END of this file, not here. `.cw-body .cw-header__inner` is
	   re-declared at the content-frame block below with
	   `padding-inline: var(--cw-space-30)`, same specificity and later in the
	   source, so a padding override written here loses silently. Measured: the
	   row still had 24px sides and the cluster still overlapped. */
}

/* Between 700 and 900 the drawer is showing but the search is still inline, so
   the row is nav · logo · search · actions and the field takes the slack. */
@media (min-width: 701px) and (max-width: 900px) {
	.cw-body .cw-header__inner {
		grid-template-columns: auto auto 1fr auto;
		grid-template-areas: "nav logo search actions";
		gap: .7rem;
	}

	.cw-body .cw-mobnav { grid-area: nav; }
	.cw-body .cw-search { max-width: none; margin-inline: 0; }
}

/* =========================================================================
   CW-31 — THE ICON BESIDE EVERY MEGA-MENU ITEM (2026-09-03)

   The panels were columns of plain text links. Both reference sites put a
   small round picture beside each one, and it is most of the difference
   between a menu that reads as a shop and one that reads as a sitemap.

   The mark comes from Crystalwala\View\MenuIcons: a generated photograph for
   product forms and stones, the real Unicode glyph for the twelve zodiac
   signs. THE CREAM DISC IS WHAT UNIFIES THEM — the photographs are generated
   onto the same cream ground, so a circular crop of one and a glyph centred in
   a cream circle are the same shape in the same colour, and a column mixing
   them still reads as one system.
   ========================================================================= */

.cw-body .cw-dept__links a {
	display: grid;
	grid-template-columns: 32px 1fr;
	align-items: center;
	gap: .6rem;
	/* Was .5rem all round. A 32px disc needs the row to be at least 32px, and
	   letting the padding alone set the height made every row a different
	   height depending on whether its item had a mark. */
	padding: .35rem .5rem;
	min-height: 44px;
}

.cw-dept__thumb {
	width: 32px;
	height: 32px;
	border-radius: 50%;
	object-fit: cover;
	/* The generated ground is #FBF3E4 and the panel is white, so without this
	   the disc has no edge and dissolves into the panel on the pale stones —
	   selenite and opal especially. A hairline is enough; a heavier ring makes
	   the column read as a table of buttons. */
	box-shadow: inset 0 0 0 1px rgba(53, 37, 29, .10);
	background: var(--cw-color-ground);
	flex: none;
}

/* The zodiac glyphs. Sized up because a Unicode symbol at its natural weight
   sits much lighter than a photograph next to it. */
.cw-dept__thumb--glyph {
	display: grid;
	place-items: center;
	font-size: 1.15rem;
	line-height: 1;
	color: var(--cw-color-maroon);
	background: var(--cw-color-ground-alt);
	/* ⚠️ WITHOUT THIS THEY RENDER AS PURPLE EMOJI. U+2648..U+2653 default to
	   the emoji presentation on macOS, iOS and Android, which ignores `color`
	   entirely and draws in a system typeface that looks nothing like the
	   photographs beside it. MenuIcons appends U+FE0E to each glyph for the
	   same reason; this is the second lever, for browsers that honour the
	   property rather than the variation selector. Belt and braces because
	   getting it wrong is silent and only visible on the platforms where it
	   fails. */
	font-variant-emoji: text;
	font-family: var(--cw-font-body), "Apple Symbols", "Segoe UI Symbol", sans-serif;
}

/* An item MenuIcons does not know keeps the same 32px slot rather than
   collapsing it — otherwise one unmatched label shifts its whole column's text
   32px left of the others. It is empty, not a placeholder graphic: an obviously
   missing icon is better than a fake one. */
.cw-dept__thumb--none { background: none; box-shadow: none; }

.cw-dept__linktext { min-width: 0; }

/* The rows are taller now, so the four-column panel needs its own gap back. */
.cw-body .cw-dept__links { gap: 0; }

/* ---------- opening the panel: HOVER INTENT ---------- */

/* The panel already opened on :hover — this is about how it FEELS.
   Two problems the owner would have hit and neither is a bug exactly:

   1. NO ENTER DELAY meant a panel flashed open while the pointer was merely
      crossing the bar on its way somewhere else. 90ms is below the threshold
      at which a deliberate hover feels laggy and above the time a passing
      pointer spends on one item.

   2. NO EXIT DELAY meant the panel closed the instant the pointer left the
      link, including in the few pixels of gap between the link and the panel
      below it — so moving diagonally towards an item in the panel shut the
      panel. The exit delay is longer than the enter delay for that reason.

   Both are pure CSS: `transition-delay` on the closed state is the exit delay,
   on the open state the enter delay. No JavaScript, so the menu still works
   with scripts off. */
.cw-body .cw-dept__panel--mega {
	transition:
		opacity 180ms ease 220ms,
		visibility 0s linear 400ms,
		transform 180ms ease 220ms;

	/* 🔴 WITHOUT THIS THE EXIT DELAY BECOMES A 400ms INVISIBLE LID OVER THE
	   PAGE. `visibility` is held at `visible` for 400ms after the pointer
	   leaves, so the panel — 1100px wide and up to 440px tall — goes on
	   swallowing clicks and :hover on everything beneath it while it fades.
	   The home page's first product rail sits directly under the bar, and a
	   shopper moving off the menu towards it would find it dead for four
	   tenths of a second, with nothing on screen to explain why.

	   `pointer-events` is NOT transitioned, so it flips the instant the
	   pointer leaves while the fade still runs. It does not shorten the
	   hover-intent bridge: the pointer travelling from the link INTO the panel
	   never leaves `.cw-dept__item`, so the open rule below still applies and
	   the panel is still interactive for as long as it is genuinely in use. */
	pointer-events: none;
}

.cw-body .cw-dept__item:hover > .cw-dept__panel--mega,
.cw-body .cw-dept__item:focus-within > .cw-dept__panel--mega {
	transition:
		opacity 150ms ease 90ms,
		visibility 0s linear 90ms,
		transform 150ms ease 90ms;
	pointer-events: auto;
}

/* ⚠️ THE GAP BETWEEN THE LINK AND THE PANEL IS A DEAD ZONE the pointer has to
   cross, and crossing it used to count as leaving. The panel sits 4px below
   the bar; this bridges that with a transparent strip belonging to the panel
   itself, so the pointer never actually leaves. It cannot be a margin — that
   would move the panel — and it must not intercept clicks on the bar, hence
   pointer-events only while the item is hovered. */
.cw-dept__panel--mega::before {
	content: "";
	position: absolute;
	left: 0;
	right: 0;
	top: -10px;
	height: 10px;
}

@media (prefers-reduced-motion: reduce) {
	.cw-body .cw-dept__panel--mega,
	.cw-body .cw-dept__item:hover > .cw-dept__panel--mega { transition: none; }
}

/* =========================================================================
   THE PRODUCT TAB PANELS — spacing, 2026-09-03

   Owner: "there is lot of bad margin/padding. Lot of empty space in
   Description tab and its heading. Looking very bad. Same with all other tabs
   in that line."

   MEASURED at 1440px before this change: the tab strip ended at y=230 and the
   first line of the description began at y=347. 117px of nothing, and it came
   from THREE stacked sources, none of which is visible from any single file:

       .1180:mt-20        on the wrapper round the whole tab block   80px
       mt-[60px]          on every <x-shop::tabs.item>               60px
       mt-[60px]          on the container INSIDE each item          60px

   The two `mt-[60px]` are gone from the markup (the theme now owns
   products/view.blade.php and cw-tabs.blade.php sets `!mt-0`). This rule is
   what replaces them, in ONE place, so the five panels cannot drift apart from
   one another again — which is exactly how they got here.
   ========================================================================= */

.cw-body main .cw-tabpanel.container {
	/* ⚠️ `.container` AND `main` ARE BOTH IN THIS SELECTOR ON PURPOSE, and
	   without them this rule does not win.

	   Bagisto's v-tab-item wraps every panel in
	   `<div class="animate-[on-fade_0.5s_ease-in-out]">`, and this file
	   already carried

	       .cw-body main [class*="animate-"] > .container { padding-block: 2.5rem 3rem }

	   which is (0,3,1) and quietly owns the padding of every tab panel on the
	   site. A plain `.cw-body .cw-tabpanel` is (0,2,0) and loses to it — so the
	   first two attempts to reduce the bottom padding here changed nothing at
	   all and measured 48px both times. Found with CSSOM, by listing every rule
	   that actually matches the element, rather than by reading the file.

	   ⚠️ A SECOND TRAP IN THE SAME PLACE: an earlier attempt used
	   `padding-bottom` while the winning rule uses the LOGICAL `padding-block`.
	   Those are different properties, so ordering them in the file does not
	   decide the winner the way it would for two identical longhands. */
	padding-block: 2.5rem 2rem;
}

/* The wrapper's own 80px. Tailwind's `1180:mt-20` is a media-query rule at
   (0,1,0), so a single class here does not beat it — hence `.cw-body`. */
.cw-body main .\31 180\:mt-20 { margin-top: 2rem; }

/* The description prose inside the merged panel. It shares the panel with the
   spec grid now, so it needs a rhythm of its own rather than inheriting the
   old single-purpose one. */
.cw-body .cw-tabpanel .cw-prose {
	max-width: var(--cw-measure);
	margin-inline: auto;
	margin-bottom: 2.5rem;
	font-size: 1.0625rem;
	line-height: 1.7;
	color: var(--cw-color-ink-soft);
}

.cw-body .cw-tabpanel .cw-prose > *:first-child { margin-top: 0; }
.cw-body .cw-tabpanel .cw-prose > *:last-child  { margin-bottom: 0; }

/* ⚠️ THE MERGED PANEL NEEDS A RULE BETWEEN ITS TWO HALVES, or the spec grid
   reads as a continuation of the prose rather than as the same answer in
   figures. A hairline plus the heading the spec block already carries is
   enough; a second background would make one tab look like two sections. */
.cw-body .cw-tabpanel .cw-prose + .cw-panel-h {
	/* 1100px, matching .cw-spec below it — NOT the reading measure. The rule
	   is structural: it separates the prose from the grid, so it has to span
	   the wider of the two or it reads as an underline on the paragraph rather
	   than as a division of the panel. */
	max-width: 1100px;
	margin-inline: auto;
	padding-top: 2rem;
	border-top: 1px solid var(--cw-color-line);
}

@media (max-width: 1180px) {
	.cw-body main .cw-tabpanel.container { padding-block: 1.5rem 2rem; }
}

/* =========================================================================
   THE FILTER SIDEBAR — the search box, 2026-09-03

   Owner: "The filter header is overlapping with text."

   MEASURED on /malas at 1440px, and it is two faults reading as one:

     1. HORIZONTAL. The magnifier glyph is absolutely positioned at left:12px
        and is 24px wide, so it ends at x=36 inside the field. Our own
        `.panel-side input` rule set `padding-left: 2.1rem` (33.6px), which is
        2.4px SHORT — so the placeholder began at 94.6px against an icon
        ending at 96px and the two literally collided. Bagisto's own `px-11`
        (44px) would have cleared it; our rule replaced it and did not account
        for the icon.

     2. VERTICAL. The glyph uses Tailwind's `top-3` — a fixed 12px from the
        top — which centres it only in a field of exactly 48px. Our rule made
        the field 38px, so the icon sat 5px BELOW centre and read as sloppy
        even where it did not overlap.

   Both are fixed here rather than by unpicking the padding rule, because that
   rule is shared with the sidebar's other inputs.
   ========================================================================= */

/* Clear of the 36px glyph with 8px of air, at every size. */
.cw-body .panel-side input[type="text"],
.cw-body .panel-side input[type="search"] {
	padding: .6rem .9rem .6rem 2.75rem;
	min-height: 42px;
}

/* Centre the glyph on the field instead of pinning it 12px from the top.
   `top: 50%` + translate is independent of the field's height, so this cannot
   drift again if the padding above is ever retuned. */
.cw-body .panel-side .icon-search {
	top: 50% !important;
	transform: translateY(-50%);
	line-height: 1;
	color: var(--cw-color-muted);
}

/* "Showing 2 of 2 options" sat flush against the right edge of its panel and
   read as clipped text rather than as a caption. */
.cw-body .panel-side [class*="text-xs"].flex-row-reverse {
	padding-inline: .2rem;
	margin-top: .35rem;
	color: var(--cw-color-muted);
}

/* An empty white pill under a heading with nothing in it — the "One of a kind"
   filter renders its option list container whether or not it has options, and
   an empty rounded box reads as a control that failed to load. */
.cw-body .panel-side ul:empty,
.cw-body .panel-side ul:not(:has(li)) { display: none; }

/* =========================================================================
   THE PRODUCT-PAGE BAND — 2026-09-03

   Between the tab panel and the related-product rails the page ran ~150px of
   flat cream and then several hundred more of it, so the whole lower half of a
   product page was one unbroken ground with no rhythm at all. This is the beat
   that breaks it.

   ⚠️ THE BACKGROUND IS GENERATED (Gemini, 21:9). The copy over it is
   constrained for that reason — see the head of cw-band.blade.php. The IMAGE
   is decoration; the only promise made here is one we can keep.

   Measured before shipping: the left 45% of the source has a mean luma of
   12.1/255, so white text clears WCAG AA against it comfortably even before
   the scrim below. The scrim is there for the narrow case, where the crop
   pulls the lit right-hand side of the picture under the text.
   ========================================================================= */

.cw-pdpband {
	position: relative;
	margin: 2rem 0 0;
	padding: 3.25rem 0;
	background-image:
		linear-gradient(90deg, rgba(18, 5, 7, .92) 0%, rgba(18, 5, 7, .80) 42%, rgba(24, 8, 9, .30) 72%, rgba(24, 8, 9, .12) 100%),
		url('../img/pdp-band.jpg');
	background-size: cover, cover;
	background-position: center, center right;
	background-repeat: no-repeat;
	overflow: hidden;
}

.cw-pdpband__inner {
	max-width: var(--cw-wide);
	margin-inline: auto;
	padding-inline: var(--cw-space-30);
	/* Text lives in the dark left half. The picture was generated with an empty
	   left third for exactly this, the same discipline the hero uses. */
	max-width: min(var(--cw-wide), 100%);
}

.cw-pdpband__inner > * { max-width: 34rem; }

.cw-pdpband__eyebrow {
	margin: 0 0 .5rem;
	font-size: .7rem;
	font-weight: 800;
	letter-spacing: .16em;
	text-transform: uppercase;
	color: var(--cw-color-gold);
}

.cw-body .cw-pdpband__h {
	margin: 0 0 .65rem;
	font-family: var(--cw-font-display);
	font-size: clamp(1.6rem, 2.6vw, 2.1rem);
	line-height: 1.1;
	color: #FFF;
}

.cw-pdpband__copy {
	margin: 0 0 1.5rem;
	font-size: 1rem;
	line-height: 1.65;
	color: rgba(255, 250, 235, .86);
}

.cw-pdpband__cta {
	display: inline-flex;
	align-items: center;
	gap: .55rem;
	min-height: 48px;
	padding: 0 1.5rem;
	border-radius: 999px;
	background: #25D366;
	color: #06301A;
	font-family: var(--cw-font-body);
	font-size: .95rem;
	font-weight: 700;
	text-decoration: none;
	transition: background 180ms ease, transform 180ms ease;
}

.cw-pdpband__cta svg {
	width: 1.2rem;
	height: 1.2rem;
	fill: currentColor;
	stroke: none;
}

.cw-pdpband__cta:hover { background: #1FBA59; transform: translateY(-1px); }

.cw-pdpband__cta:focus-visible {
	outline: 2px solid var(--cw-color-gold);
	outline-offset: 3px;
}

@media (max-width: 720px) {
	.cw-pdpband { padding: 2.5rem 0; }
	/* The crop at phone widths pulls the LIT part of the picture under the
	   text, so the scrim stops being a gradient and becomes a wash. */
	.cw-pdpband {
		background-image:
			linear-gradient(180deg, rgba(18, 5, 7, .88), rgba(18, 5, 7, .82)),
			url('../img/pdp-band.jpg');
	}
	.cw-pdpband__inner > * { max-width: none; }
}

@media (prefers-reduced-motion: reduce) {
	.cw-pdpband__cta { transition: none; }
}


/* =========================================================================
   🔴 NARROW MEGA PANELS OPENED NOWHERE NEAR THEIR OWN LINK — fixed 2026-09-03

   Owner: "when clicking on menu — for example on rudraksha, user is not able
   to select bracelets. as it's opening in center and when user takes mouse
   over there it gets hidden."

   THE CAUSE. Every panel was anchored to the BAR and centred
   (`left: 50%; transform: translateX(-50%)`). For the four-column panels that
   is invisible — they are 1100px wide and span nearly the whole bar, so every
   link sits over its own panel. For the ONE- and TWO-column panels it is not:
   a 340px panel centred on a 1440px page sits at x 550–890 no matter which
   department opened it.

   MEASURED at 1440px, before the fix:

       dept         link x       panel x      link over panel?
       Rudraksha    395–538      550–890      NO
       Jewellery   1075–1209     550–890      NO  (panel entirely to its LEFT)
       Decor       1234–1344     410–1030     NO

   So the pointer had to leave the link, cross dead ground, and arrive at a
   panel it had already lost. Driven in a straight line from the link to the
   first sub-item, the panel went `hidden` mid-travel and the pointer ended up
   over `DIV.cw-hero__wrap` — the hero, not the menu. Category, Karungali and
   Zodiac passed the same test, which is why this looked intermittent.

   THE FIX. Narrow panels are anchored to their OWN item instead of the bar.
   The four-column panels keep the centred behaviour, which is correct for
   them and which nothing about this changes.

   ⚠️ `position: relative` IS SET ONLY ON THE ITEMS THAT NEED IT, via `:has()`.
   `.cw-dept__item` is deliberately `position: static` further up this file so
   the WIDE panels can use the bar as their containing block. Making every item
   relative would drag those 1100px panels back under their own link and push
   them off the right-hand edge.
   ========================================================================= */

/* WHICH ITEMS ANCHOR WHICH WAY.

   ⚠️ LEFT FOR THE FIRST HALF, RIGHT FOR THE SECOND — not "right for the last
   two or three". The first attempt counted from the end (`nth-last-child(-n+3)`)
   and it was width-dependent and wrong: measured at 1100px, the two-column
   "Zodiac sign" panel is the 5th of 8 and still ran 64px off the right edge,
   where `.cw-dept` clips it. Splitting the row down the middle has no threshold
   to get wrong, and every panel still overlaps its own link either way. */
.cw-body .cw-dept__item:has(> .cw-dept__panel--mega[data-cols="1"]),
.cw-body .cw-dept__item:has(> .cw-dept__panel--mega[data-cols="2"]),
.cw-body .cw-dept__item:has(> .cw-dept__panel--mega[data-cols="3"]) {
	position: relative;
}

.cw-body .cw-dept__panel--mega[data-cols="1"],
.cw-body .cw-dept__panel--mega[data-cols="2"],
.cw-body .cw-dept__panel--mega[data-cols="3"] {
	left: 0;
	right: auto;
	transform: translate(0, -4px);
}

.cw-body .cw-dept__item:nth-child(n+5) > .cw-dept__panel--mega[data-cols="1"],
.cw-body .cw-dept__item:nth-child(n+5) > .cw-dept__panel--mega[data-cols="2"],
.cw-body .cw-dept__item:nth-child(n+5) > .cw-dept__panel--mega[data-cols="3"] {
	left: auto;
	right: 0;
}

.cw-body .cw-dept__item:hover > .cw-dept__panel--mega[data-cols="1"],
.cw-body .cw-dept__item:hover > .cw-dept__panel--mega[data-cols="2"],
.cw-body .cw-dept__item:hover > .cw-dept__panel--mega[data-cols="3"],
.cw-body .cw-dept__item:focus-within > .cw-dept__panel--mega[data-cols="1"],
.cw-body .cw-dept__item:focus-within > .cw-dept__panel--mega[data-cols="2"],
.cw-body .cw-dept__item:focus-within > .cw-dept__panel--mega[data-cols="3"] {
	transform: translate(0, 0);
}

/* ⚠️ THE FOUR-COLUMN PANELS NEED THE SAME TREATMENT BELOW 1100px, AND THIS IS
   EASY TO MISS. A media query further up shrinks them from 1100px to 620px —
   but `data-cols` is a Blade attribute and still reads "4", so none of the
   rules above touch them. At 1100px the "Category" panel was therefore still
   bar-centred at x 240–860 while its own link sat at 39–170: the same
   unreachable menu, just at a narrower viewport. Above 1100 they are 1100px
   wide and overlap every link, so they are left centred there. */
@media (max-width: 1100px) {
	.cw-body .cw-dept__item:has(> .cw-dept__panel--mega[data-cols="4"]) { position: relative; }

	.cw-body .cw-dept__panel--mega[data-cols="4"] {
		left: 0;
		right: auto;
		transform: translate(0, -4px);
	}

	.cw-body .cw-dept__item:nth-child(n+5) > .cw-dept__panel--mega[data-cols="4"] {
		left: auto;
		right: 0;
	}

	.cw-body .cw-dept__item:hover > .cw-dept__panel--mega[data-cols="4"],
	.cw-body .cw-dept__item:focus-within > .cw-dept__panel--mega[data-cols="4"] {
		transform: translate(0, 0);
	}
}

/* =========================================================================
   ONE CONTENT FRAME — 2026-09-03

   Owner, on three screenshots: "Load more button is not properly alligned",
   "Even the product pages are not properly alligned with below section",
   and the reviews still reading as left-aligned.

   They are one bug. Every band on the page had invented its own measure, and
   the left edge of the CONTENT (not the box — the content) landed in six
   different places on a 1440px viewport:

       footer inner        48     max-width 1344, no padding
       main .container     60     Bagisto's own: max-width 1440, padding 60
       header inner        72     max-width 1344, padding 24
       usp strip           72
       product-page band   72
       department bar      96     max-width 1344, padding 48
       product tab panel  224     capped to 68rem and centred

   Nothing lined up with anything below it, which is exactly what the
   screenshots show — the filter sidebar starting inboard of the footer, the
   reviews inboard of the band, the product grid ending 12px short of the
   header.

   ⚠️ 72px IS THE CANONICAL CONTENT EDGE, chosen because the header, the
   services strip and the band already used it — it is the majority, so this
   moves the fewest things. Everything below resolves to a 1344px box centred
   in the viewport with a 24px gutter, i.e. content 72 → 1368.

   ⚠️ IF YOU ADD A FULL-WIDTH BAND, give its inner element this same treatment
   rather than a fresh max-width. That is how six of them accumulated.
   ========================================================================= */

.cw-body .cw-header__inner,
.cw-body .cw-dept__inner,
.cw-body .cw-usp__inner,
.cw-body .cw-pdpband__inner,
.cw-body .cw-footer__inner,
.cw-body main .container,
.cw-body main .cw-tabpanel.container {
	max-width: var(--cw-wide);
	margin-inline: auto;
	padding-inline: var(--cw-space-30);
}

/* ⚠️ A CONTAINER INSIDE A CONTAINER MUST NOT INDENT AGAIN. Bagisto nests its
   own `.container` inside the reviews panel, so with the rule above applied to
   both, the reviews would sit 24px inboard of every other panel — the same
   class of drift this whole block exists to remove. */
/* ⚠️ `.cw-tabpanel .container` ONLY — NOT a general `.container .container`.
   That broader form was tried and it broke the thing it was meant to protect:
   Bagisto's `<x-shop::tabs.item>` renders as `<v-tab-item class="container">`,
   so the PANEL is itself a `.container` inside a `.container` and the rule
   stripped the panel's own padding. The reviews then sat at x=48 against a
   logo at 72 — the exact symptom this block exists to remove. Measured:
   `.cw-tabpanel.container` with `padding-left: 0px, max-width: none`. */
.cw-body .cw-tabpanel .container {
	max-width: none;
	padding-inline: 0;
}

/* ---------- the star row on a product card ---------- */

/* Owner: "reviews on product are left aligned. not coming properly."

   The card's title and price are `text-align: center`; the rating is a FLEX
   row, and flex does not care about `text-align`. With `justify-content`
   left at its `normal` default the stars packed to the left edge of a centred
   card — measured on a 237px card: the "4.9" label 86px from the left edge and
   133px from the right. Every card in every grid on the site. */
.cw-body .cw-card__rating {
	justify-content: center;
}

/* ---------- the listing pages had NO bottom padding at all ---------- */

/* Owner: "Load more button is not properly alligned."

   It was centred correctly — measured, button centre 874 against a grid centre
   of 874. What was wrong is VERTICAL: `gapBtnToUsp = 0` on every category page
   tested. The button's bottom edge touched the white services strip exactly,
   with no space between them, so it read as belonging to the band below rather
   than to the grid above. On pages with no Load More the product grid itself
   ended flush against it.

   ⚠️ SCOPED WITH `:has(.panel-side)` — i.e. only pages that have a filter
   sidebar, which is the listing pages. It is deliberately NOT `main { padding-
   bottom }`: the home page's sections already own their spacing, and adding a
   global band there is how the ~250px gaps in SESSION-LOG §18.8 accumulated in
   the first place.

   🔴 AND `:has(.grid)` IS THE SECOND HALF OF THAT SCOPE — CW-56, CW-57. Without
   it this reached EVERY `> .container` on a listing page, and /search has two:
   one holding the grid, and one above it holding only "Results for ..." and the
   toolbar. That second one got 64px of padding under a single line of text,
   which is the dead band the client circled on their screenshot. The padding
   exists to separate the PRODUCT GRID from the services strip, so it belongs on
   the box that holds the grid. */
.cw-body main:has(.panel-side) > .container:has(.grid) {
	padding-bottom: 4rem;
}

/* The product page ends on a rail of up-sells with the same flush edge. Less
   glaring than a button touching the band, but the same fault. */
.cw-body main:has(.cw-buybar) {
	padding-bottom: 2.5rem;
}

/* =========================================================================
   CW-39 — the category listing: full width, filters in a collapsed bar

   The ticket asked for the left filter sidebar removed and the grid run full
   width. The client amended it on 2026-09-04: keep the filters, as a
   collapsible panel, DEFAULT COLLAPSED. Modelled on divinehindu.in, which puts
   a "Filters" button at the right of the listing's top row and expands a panel
   in place above the grid rather than in a drawer over it.

   ⚠️ REMOVING THIS SIDEBAR RETIRES A WHOLE CLASS OF BUG. It has been silently
   destroyed twice by stylesheets written for something else — §15.2 and §22.3,
   the second time leaving every checkbox option on every category page at
   `display: none` while 105 checks passed. The option-count regression check
   is KEPT and now opens the panel first; see verify.mjs.
   ========================================================================= */

.cw-body .cw-listing {
	/* The grid is the whole width now. Nothing sits beside it. */
	display: block;
}

/* ---------- the bar ---------- */

.cw-body .cw-filterbar {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--cw-space-30);
	flex-wrap: wrap;
}

/* Bagisto's toolbar is a flex row of its own; it just needs to stop trying to
   fill the line so the toggle keeps its place at the left. */
.cw-body .cw-filterbar__tools {
	flex: 0 1 auto;
	min-width: 0;
	margin-inline-start: auto;
}

.cw-body .cw-filterbar__toggle {
	display: inline-flex;
	align-items: center;
	gap: .625rem;
	padding: .625rem 1.125rem;
	border: 1px solid var(--cw-color-line, #E7D9C4);
	border-radius: 999px;
	background: var(--cw-color-ground, #FFFAEB);
	color: var(--cw-color-ink, #35251D);
	font-family: var(--wp--preset--font-family--body, inherit);
	font-size: .9375rem;
	font-weight: 600;
	letter-spacing: .01em;
	line-height: 1;
	cursor: pointer;
	transition: background 160ms ease, border-color 160ms ease;
}

.cw-body .cw-filterbar__toggle:hover,
.cw-body .cw-filterbar__toggle:focus-visible {
	background: var(--cw-color-ground-alt, #FAECDB);
	border-color: var(--cw-color-brand, #6C1322);
}

/* A CSS chevron rather than an icon font: the theme's icon set has no
   chevron that matches this weight, and one borrowed glyph would sit a pixel
   off centre at this size. */
.cw-body .cw-filterbar__chev {
	width: .5rem;
	height: .5rem;
	border-right: 2px solid currentColor;
	border-bottom: 2px solid currentColor;
	transform: rotate(45deg) translate(-2px, -2px);
	transition: transform 200ms cubic-bezier(.22,.61,.36,1);
}

.cw-body .cw-filterbar__chev--open {
	transform: rotate(-135deg) translate(-2px, -2px);
}

@media (prefers-reduced-motion: reduce) {
	.cw-body .cw-filterbar__chev { transition: none; }
}

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

.cw-body .cw-filterpanel {
	margin-top: var(--cw-space-30);
}

/* ⚠️ SCOPED TO `.cw-filterpanel`, AND THAT IS LOAD-BEARING.

   `.panel-side` is shared with the SEARCH page, which still renders Bagisto's
   own un-overridden view and still wants a 268px sticky left column
   (elevation.css). Styling `.panel-side` unscoped shrink-wrapped the search
   sidebar and left its grid expecting a column that was no longer there.
   Every category rule below therefore hangs off the panel wrapper, which only
   the category view emits. */
.cw-body .cw-filterpanel .panel-side {
	position: static;
	width: 100%;
	flex: 1 1 auto;
	top: auto;
}

.cw-body .cw-filterpanel .cw-filters {
	display: grid;
	/* ⚠️ NOT a fixed column count. Categories carry between two and seven
	   facets; a fixed `repeat(4, 1fr)` leaves two empty tracks on /karungali
	   and crushes the price slider on /rudraksha. `auto-fit` with a floor wide
	   enough for the price range's own label ("₹0.00 – ₹18,018.00" measures
	   ~230px) lets the row size itself. */
	grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
	gap: 0 var(--cw-space-40, 2rem);
	align-items: start;
	padding: var(--cw-space-30) var(--cw-space-30) var(--cw-space-20);
	border: 1px solid var(--cw-color-line, #E7D9C4);
	border-radius: var(--cw-radius-30, 14px);
	background: #FFFFFF;
}

/* "FILTERS / CLEAR ALL" spans the whole panel rather than sitting in the first
   column, where it read as a heading for the first facet only. */
.cw-body .cw-filterpanel .cw-filters__head {
	grid-column: 1 / -1;
	margin-bottom: var(--cw-space-20);
}

/* ⚠️ THE GROUPS MUST NOT BE CAPPED IN HEIGHT HERE. The old column was
   `max-h-[1320px] overflow-y-auto`, which is what a 342px-wide stack of seven
   accordions needs and exactly what a horizontal panel must not have — a
   scroll region inside a short band swallows the options. Each accordion keeps
   its own open/closed state instead. */
.cw-body .cw-filterpanel .cw-filters > .accordion,
.cw-body .cw-filterpanel .cw-filters > div:not(.cw-filters__head) {
	min-width: 0;
	border-bottom: 0;
}

/* Long option lists still need a ceiling, but per GROUP, not per panel, so a
   category with one long facet does not make the whole band tall. */
.cw-body .cw-filterpanel .cw-filters ul {
	max-height: 15rem;
	overflow-y: auto;
	overscroll-behavior: contain;
}

/* The price slider is not a list and must never scroll — it is one row. */
.cw-body .cw-filterpanel .cw-filters ul:has(input[type="range"]) {
	max-height: none;
	overflow: visible;
}

@media (max-width: 767px) {
	/* 🔴 THIS `display: none` IS NOT REDUNDANT WITH `max-md:hidden` ON THE
	   ELEMENT, and leaving it out was a real regression measured on a 390px
	   viewport: the desktop toggle and Bagisto's sort control both rendered on
	   the phone, 288px tall, on top of the fixed bottom filter bar the phone
	   already has.

	   Tailwind's `.max-md\:hidden` is one class — specificity (0,1,0). The
	   `.cw-body .cw-filterbar` rule above is two — (0,2,0) — and wins inside
	   the media query regardless of source order. A utility class cannot
	   override a component rule that outweighs it. 767px is Tailwind's own
	   `md` boundary, so the two agree exactly.

	   Phones keep Bagisto's fixed bottom filter bar, which lives in the same
	   include under `v-if="isMobile"`. It is `position: fixed`, so the panel
	   wrapper around it adds no flow height — but it must not draw a border
	   around nothing either. */
	/* ⚠️ THE BAR SHOWS ON PHONES NOW. It used to be hidden here, because
	   Bagisto supplied a separate fixed-bottom filter drawer for mobile. That
	   drawer is gone — two fixed bars on one small screen is 17% of the
	   viewport spent on chrome — so this panel is the only filter UI at every
	   width and it has to be reachable. See categories/filters.blade.php.

	   It stacks: the toggle and Bagisto's sort/limit/view controls do not fit
	   side by side at 390px. */
	.cw-body .cw-filterbar {
		flex-direction: column;
		align-items: stretch;
		gap: .6rem;
	}

	.cw-body .cw-filterbar__toggle {
		justify-content: center;
		width: 100%;
	}

	.cw-body .cw-filterbar__tools {
		margin-inline-start: 0;
		width: 100%;
	}

	/* 🔴 BAGISTO'S TOOLBAR SPLITS ITSELF, AND ITS MOBILE HALF IS UNUSABLE ON
	   ITS OWN. `categories/toolbar.blade.php` renders two branches: a
	   `max-md:hidden` desktop row with a real dropdown, and an `md:hidden`
	   branch that is a BARE <ul> of sort options with no styling at all —
	   because it was written to sit inside the filter drawer, which no longer
	   exists.

	   Left alone at 390px that bare list rendered as six stacked lines of plain
	   text, 255px tall, permanently open above the products. So the two
	   branches are swapped: the phone gets the same dropdown the desktop has,
	   which fits perfectly well once the bar stacks.

	   ⚠️ These are Tailwind's own escaped class names. If Bagisto's breakpoint
	   prefix ever changes, this rule stops applying and the bare list comes
	   back — visibly, which is the trade worth making over a selector that
	   guesses at structure. */
	.cw-body .cw-filterbar__tools .max-md\:hidden { display: flex !important; }
	.cw-body .cw-filterbar__tools .md\:hidden    { display: none !important; }

	/* That row is `justify-between` and assumes desktop width. */
	.cw-body .cw-filterbar__tools .max-md\:hidden {
		flex-wrap: wrap;
		gap: .5rem;
		justify-content: space-between;
		align-items: center;
	}

	/* Bagisto caps the sort button at `max-w-[200px]`, which is generous at
	   1440 and forces "Price: high to low" onto three lines at 390. It is
	   allowed its natural width here and kept on one line. */
	.cw-body .cw-filterbar__tools .max-md\:hidden button {
		max-width: none;
		white-space: nowrap;
	}

	.cw-body .cw-filterpanel { margin-top: var(--cw-space-20); }
	.cw-body .cw-filterpanel .cw-filters { border: 0; padding: 0; background: none; }
}

/* =========================================================================
   CW-41 — divinehindu.in's type system, measured and applied

   The client asked for their font families, weights and sizes copied. These
   numbers were READ OFF THEIR LIVE PAGES with getComputedStyle, on the home
   page and on /collections/rudraksha — not estimated from screenshots:

       role              family      size      weight   line-height
       body, buttons     Inter       15px      400      1.5
       nav link          Inter       15.5px    600      1.5
       page title (h1)   Poppins     28-30px   700-800  1.2
       section title     Montserrat  34px      700      1.2
       sub-head (h3)     Poppins     19px      700      1.35
       card head (h4)    Poppins     16px      600      1.2

   ⚠️ THREE FAMILIES, NOT ONE. Their system uses Montserrat for section titles
   and Poppins for page and card headings, which reads as inconsistent up close
   but is what they run. Copying "their typography" and picking one face would
   not be copying it.

   ⚠️ THIS OVERRIDES CW-01, ON THE RECORD. That teardown concluded their fonts
   "are the category default — copying them makes us generic", and the whole
   parent-brand layer above was built on Fraunces + Outfit. The owner was shown
   that finding and chose to copy the fonts exactly on 2026-09-04. Do not
   quietly revert it.

   ⚠️ IT IS A LAYER, NOT A REWRITE. Every rule below re-states a role that
   already exists somewhere in tokens.css, storefront.css or elevation.css.
   Nothing above was edited except the two family tokens, so reverting CW-41 is
   deleting this block and putting two token values back — which matters,
   because this is the kind of decision that gets revisited.

   ⚠️ NO LETTER-SPACING TIGHTENING. Our display face carried `-0.02em` on large
   headings, which suits Fraunces and makes Poppins and Montserrat look
   cramped. Theirs measured `normal` everywhere. So it is reset explicitly
   rather than left to inherit.
   ========================================================================= */

.cw-body,
.cw-body p,
.cw-body li,
.cw-body button,
.cw-body input,
.cw-body select,
.cw-body textarea {
	font-family: var(--cw-font-body);
}

.cw-body {
	font-size: 0.9375rem;   /* 15px */
	line-height: 1.5;
	font-weight: 400;
}

/* ---------- page titles: Poppins ---------- */

/* Their category h1 measured 28px/800; their home h1 30px/700. The clamp keeps
   our responsive behaviour but lands on their desktop value instead of the
   33.6-51.2px this theme had. */
.cw-body .cw-cathead__title {
	font-family: var(--cw-font-heading);
	font-size: clamp(1.5rem, 2.4vw, 1.75rem);   /* -> 28px, their category h1 */
	font-weight: 800;
	line-height: 1.2;
	letter-spacing: normal;
}

/* Their home h1 measured 30px/700 and their product h1 26px/700. Both are
   Poppins, both at line-height 1.2, and both are far smaller than the
   41.6px Fraunces display setting this theme had. */
.cw-body .cw-hero__title {
	font-family: var(--cw-font-heading);
	font-size: clamp(1.5rem, 2.6vw, 1.875rem);  /* -> 30px */
	font-weight: 700;
	line-height: 1.2;
	letter-spacing: normal;
}

/* ⚠️ ADDRESSED BY ITS OWN CLASSES, NOT `main h1`. Bagisto emits the product
   title as `break-words text-3xl font-medium max-sm:text-xl` with no semantic
   hook of ours on it, and `main h1` would also catch the hero above. If
   Bagisto changes those utilities this rule stops applying and the title grows
   back — a visible, diagnosable failure, which is the trade elevation.css
   already makes for the gallery (see its note on substring selectors). */
.cw-body main h1.break-words {
	font-family: var(--cw-font-heading);
	font-size: clamp(1.375rem, 2.2vw, 1.625rem);  /* -> 26px */
	font-weight: 700;
	line-height: 1.2;
	letter-spacing: normal;
}

/* ---------- section titles: Montserrat ---------- */

.cw-body .cw-sec__title {
	font-family: var(--cw-font-display);
	font-size: clamp(1.6rem, 2.8vw, 2.125rem);  /* -> 34px */
	font-weight: 700;
	line-height: 1.2;
	letter-spacing: normal;
}

.cw-body .cw-sec__sub {
	font-family: var(--cw-font-body);
	font-size: 0.9375rem;
	line-height: 1.5;
}

/* Their eyebrow is the one place they keep tracking, and it is small caps
   sized well below the body. Left as ours; the measurement found no
   equivalent element to copy. */

/* ---------- sub-heads and card headings: Poppins ---------- */

.cw-body main h3,
.cw-body .cw-why__card h3,
.cw-body .cw-faq__q {
	font-family: var(--cw-font-heading);
	font-size: 1.1875rem;   /* 19px */
	font-weight: 700;
	line-height: 1.35;
	letter-spacing: normal;
}

.cw-body main h4,
.cw-body .cw-card__title,
.cw-body .cw-cat__label {
	font-family: var(--cw-font-heading);
	font-size: 1rem;        /* 16px */
	font-weight: 600;
	line-height: 1.2;
	letter-spacing: normal;
}

/* ⚠️ WCAG 2.5.8, AND THE TYPE CHANGE IS WHAT BROKE IT. The product card's
   title is a LINK, and at 16px on a 1.2 line-height its box measures 23.x px —
   just under the 24px minimum for a standalone target, where the previous
   1.5 line-height cleared it comfortably. `ui:browser` caught it on
   /rudraksha: `a. 275x24`.

   divinehindu.in does not have this problem because their whole card is one
   anchor; ours links the title alone. So the text keeps their measured 16/1.2
   and the TARGET is padded out to 24px instead — the type is copied, the
   accessible minimum is kept, and the two do not have to fight. */
.cw-body .cw-card__title a {
	display: block;
	min-height: 24px;
}

/* ---------- navigation: Inter, heavier than body ---------- */

.cw-body .cw-dept__link,
.cw-body .cw-mobnav a,
.cw-body footer a {
	font-family: var(--cw-font-body);
	font-weight: 600;
}

.cw-body .cw-dept__link {
	font-size: 0.96875rem;  /* 15.5px */
	line-height: 1.5;
}

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

   ⚠️ INTER, NOT POPPINS, AND THAT IS MEASURED. Their product page price is
   Inter 26px/800 — the one place they use the BODY face at a display weight
   rather than the heading face. It reads as deliberate: the price is
   information, not a title. Their card price could not be measured (their
   card markup renders it in a node the probe could not isolate), so the card
   inherits the same treatment at the card's own size rather than being
   guessed at separately. */
.cw-body .cw-card__price,
.cw-body .cw-priceline {
	font-family: var(--cw-font-body);
	letter-spacing: normal;
}

/* ⚠️ THE WEIGHT HAS TO BE SET ON THE LEAF, NOT THE WRAPPER. Both price rows
   are flex containers whose children carry their own `font-weight` (500 on the
   struck original, 600-700 on the price charged), so a weight on the wrapper is
   inherited by nothing. Setting it here measured Inter 500/600 against their
   800 until this rule was added.

   Only the price actually being charged goes to 800. Bagisto gives us no class
   beyond `line-through` on the struck original, which is the same hook the
   card rules above already rely on — the struck one stays lighter, as it does
   on their page. */
/* ⚠️ `> p:not(.line-through)` WAS WRONG HERE AND MADE BOTH PRICES BOLD. On the
   product page `line-through` sits on a DESCENDANT of the wrapping <p>, not on
   the <p> itself, so the negation matched the struck original's wrapper too and
   the MRP came back at 800 alongside the price charged. Measured: both 800,
   where their page has the struck one lighter. Excluding the subtree, not just
   the element, is what the card rule above already does. */
.cw-body .cw-card__price *:not(.line-through):not(.line-through *),
.cw-body .cw-priceline *:not(.line-through):not(.line-through *) {
	font-weight: 800;
}

/* =========================================================================
   CW-47 — "Chosen for your Rashi"

   The client's reference (divinehindu.in) draws twelve circular illustrated
   marks in a row, separated by hairline rules, sign name beneath.

   ⚠️ SCOPED TO `.cw-rashi--rail`, NOT `.cw-rashi`. The homepage block ALREADY
   carried `.cw-rashi` as a six-across grid of Unicode glyph tiles, with its own
   rules in storefront.css, components.css and elevation.css (including the
   scroll-reveal stagger on `--i`). Restyling the bare class would fight all
   three from a file that loads last, which is how you get a rule nobody can
   explain later. The modifier turns the grid into a rail and leaves those
   files alone.

   ⚠️ THE CREAM DISC IS DRAWN HERE, NOT IN THE ARTWORK, and that is the fix for
   an inconsistency in the generated set. `optimise-rashi.py` flood-fills each
   master's background to transparency; nine came out as clean cut-outs and
   three kept a faint disc where the model's cream sat just off ours. Giving
   every icon a CSS disc makes all twelve identical — the three that carry
   their own simply land on a disc of the same colour — and it matches the
   reference, which has a disc behind every sign.
   ========================================================================= */

/* The old grid rules still match `.cw-rashi`; the rail must beat them on the
   two properties that decide the layout. Measured: without this the flex row
   sat inside a `grid-template-columns: repeat(6, 1fr)` and every item was
   forced to a sixth of the width. */
.cw-body .cw-rashi--rail {
	display: flex !important;
	grid-template-columns: none !important;
}

.cw-body .cw-rashi--rail {
	display: flex;
	gap: 0;
	margin: 0;
	padding: 0;
	list-style: none;
	/* Twelve at 108px fit inside the 1296px frame, so there is nothing to page
	   through on a desktop. Below that it scrolls, with snap points, which is
	   what the reference's arrows actually do — and needs no script. */
	overflow-x: auto;
	scroll-snap-type: x proximity;
	overscroll-behavior-x: contain;
	scrollbar-width: none;
}

.cw-body .cw-rashi--rail::-webkit-scrollbar { display: none; }

.cw-body .cw-rashi--rail .cw-rashi__item {
	flex: 1 0 auto;
	min-width: 108px;
	/* ⚠️ `start`, NOT `center`. With `center` the browser pulls the FIRST
	   item to the middle of the rail on load, which on a phone left Aries
	   half off the left edge before anyone had scrolled anything. */
	scroll-snap-align: start;
	/* The hairline between signs, exactly as the reference has it. `:last-child`
	   rather than a wrapper border so the row ends clean. */
	border-inline-end: 1px solid var(--cw-color-line, #E7D9C4);
}

.cw-body .cw-rashi--rail .cw-rashi__item:last-child { border-inline-end: 0; }

/* ⚠️ THE TILE WAS A CARD AND HAS TO BE UN-CARDED EXPLICITLY. storefront.css
   gives `.cw-rashi__tile` a surface background, a 1px border, a radius and a
   lift-on-hover box-shadow — right for a six-across grid of tiles, and wrong
   for a rail, where twelve white cards sit ON TOP of the hairline separators
   this section is supposed to have. Measured: the separators rendered and were
   completely invisible behind the cards. Each property is reset by name rather
   than with `all: unset`, which would also throw away the flex column. */
.cw-body .cw-rashi--rail .cw-rashi__tile {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: .1rem;
	padding: .75rem .5rem;
	text-decoration: none;
	color: var(--cw-color-ink, #35251D);
	background: none;
	border: 0;
	border-radius: 0;
	box-shadow: none;
}

/* The lift belongs to the disc now, not to a card that no longer exists. */
.cw-body .cw-rashi--rail .cw-rashi__tile:hover {
	transform: none;
	box-shadow: none;
	border-color: transparent;
}

.cw-body .cw-rashi--rail .cw-rashi__disc {
	display: grid;
	place-items: center;
	width: 76px;
	height: 76px;
	margin-bottom: .45rem;
	border-radius: 50%;
	/* ⚠️ THE LIGHTER GROUND, NOT ground-alt. This section sits ON ground-alt,
	   so a ground-alt disc is invisible — and three of the twelve masters kept
	   a faint pale disc of their own through the flood fill, which then read as
	   three signs with a disc and nine without. #FFFAEB matches those three,
	   so all twelve show the same disc. */
	background: var(--cw-color-ground, #FFFAEB);
	transition: background 180ms ease, transform 180ms cubic-bezier(.22,.61,.36,1);
}

.cw-body .cw-rashi--rail .cw-rashi__disc img {
	width: 68px;
	height: 68px;
	display: block;
}

.cw-body .cw-rashi--rail .cw-rashi__tile:hover .cw-rashi__disc,
.cw-body .cw-rashi--rail .cw-rashi__tile:focus-visible .cw-rashi__disc {
	background: #F4DEC4;
	transform: translateY(-2px);
}

.cw-body .cw-rashi--rail .cw-rashi__en {
	font-family: var(--cw-font-heading);
	font-size: .9375rem;
	font-weight: 600;
	line-height: 1.2;
}

.cw-body .cw-rashi--rail .cw-rashi__tile:hover .cw-rashi__name { color: var(--cw-color-maroon, #6C1322); }

.cw-body .cw-rashi--rail .cw-rashi__hi {
	font-family: var(--cw-font-body);
	font-size: .75rem;
	color: var(--cw-color-muted, #7A6A60);
	line-height: 1.3;
}

@media (prefers-reduced-motion: reduce) {
	.cw-body .cw-rashi--rail .cw-rashi__disc { transition: none; }
	.cw-body .cw-rashi--rail .cw-rashi__tile:hover .cw-rashi__disc { transform: none; }
}

@media (max-width: 640px) {
	/* ⚠️ A SMALLER DISC, NOT FEWER SIGNS. All twelve stay in the rail on a
	   phone and it scrolls — dropping any of them would leave a shopper unable
	   to find their own sign, which is the only thing this section does. */
	.cw-body .cw-rashi--rail .cw-rashi__item { min-width: 88px; }
	.cw-body .cw-rashi--rail .cw-rashi__disc { width: 62px; height: 62px; }
	.cw-body .cw-rashi--rail .cw-rashi__disc img { width: 56px; height: 56px; }
	.cw-body .cw-rashi--rail .cw-rashi__hi { display: none; }
}

/* =========================================================================
   CW-44 — "Best selling rings"

   The rail borrows `.cw-rails__rail` wholesale: same scroll-snap, same tile
   sizing, same scrollbar treatment as the department rails, so the two read as
   one component rather than two that nearly match. Only the things that differ
   are set here.
   ========================================================================= */

/* Copied from `.cw-rails__rail` rather than shared with it — see the note in
   the Blade. Same scroll-snap, same gap, same scrollbar, so the two read as one
   component; no auto-scroll, because pdp.js keys the marquee off that class
   name. */
.cw-body .cw-rings__rail {
	list-style: none;
	margin: var(--cw-space-30) 0 0;
	padding: .5rem 0 1rem;
	min-width: 0;
	display: flex;
	gap: 1.35rem;
	overflow-x: auto;
	scroll-snap-type: x proximity;
	scroll-padding-inline: 0;
	scrollbar-width: thin;
	scrollbar-color: var(--cw-color-line) transparent;
	overscroll-behavior-x: contain;
}

.cw-body .cw-rings__rail::-webkit-scrollbar { height: 6px; }
.cw-body .cw-rings__rail::-webkit-scrollbar-thumb {
	background: var(--cw-color-line);
	border-radius: 999px;
}

/* The tile sizing lives on the CARD in the shared rail, so it has to be
   restated here too — without it every tile shrink-wraps to its content and
   the row collapses. */
.cw-body .cw-rings__rail > .cw-card {
	flex: 0 0 clamp(190px, 22vw, 268px);
	scroll-snap-align: start;
}

/* The department rails sit inside a tab panel that supplies this; a standalone
   rail has to space itself off its own View All link. */
.cw-body .cw-rings .cw-more {
	display: inline-block;
	margin-top: var(--cw-space-30);
}

/* =========================================================================
   CW-55 — the last word on the phone header

   ⚠️ THIS BLOCK IS AT THE END OF THE FILE ON PURPOSE. The content-frame rules
   above re-declare `.cw-body .cw-header__inner` with
   `padding-inline: var(--cw-space-30)` at the same specificity, so anything
   written earlier loses on source order. Two attempts at this were lost to
   exactly that before it was measured.

   THE CONSTRAINT, so nobody has to rediscover it:

       track = (viewport - 2*padding - 2*gap - logo) / 2      must be >= actions

   The icon cluster is 3 x 36px + 2 x 4px = 116px and does not shrink further
   without dropping below a comfortable tap target. Worst case is a 320px
   phone: (320 - 24 - 8 - 48) / 2 = 120 >= 116, four pixels clear. At 390 it is
   146 against 116.

   Change any one of these four numbers and re-run the header check at 320,
   360, 390 and 430 — it asserts the logo is centred AND that nothing overlaps
   it, because centring it was what caused the overlap in the first place.
   ========================================================================= */

@media (max-width: 700px) {
	.cw-body .cw-header__inner {
		padding-inline: 12px;
		column-gap: 4px;
	}

	/* Fluid, so the mark is as large as each screen can actually afford. */
	.cw-body .cw-logo img { max-width: clamp(52px, 17vw, 84px); }

	/* ⚠️ THE ICON SIZE BELONGS HERE TOO. It was first written in the media
	   block further up and was silently dropped when that block was trimmed —
	   the cluster went back to 3 x 44 = 140px and the overlap returned at every
	   width up to 360. Keep all four numbers of the constraint in one place.

	   34px: cluster = 3 x 34 + 2 x 4 = 110px. Worst case 320px gives a track of
	   (320 - 24 - 8 - 54) / 2 = 117, seven pixels clear. Still well above WCAG
	   2.5.8's 24px minimum. */
	.cw-body .cw-header__actions { gap: 4px; }

	.cw-body .cw-header__actions .cw-iconlink {
		width: 34px;
		height: 34px;
	}
}

/* =========================================================================
   CW-51 — the phone tab bar

   Home · Categories · WhatsApp, pinned to the bottom edge below 900px.

   🔴 IT SHARES THAT EDGE WITH BAGISTO'S MOBILE FILTER STRIP. Category and
   search pages render a `fixed bottom-0` FILTER / SORT bar from
   `categories.filters` under `v-if="isMobile"`. Two bars at `bottom: 0`
   overlap exactly, and the filter strip is the one a shopper loses. They are
   stacked: this bar owns the bottom, the filter strip is lifted to sit on top
   of it, and `ui:browser` asserts a category page shows BOTH with neither
   covering the other.
   ========================================================================= */

.cw-body .cw-tabbar { display: none; }

/* ⚠️ ONE NUMBER, USED FIVE TIMES. The bar's height, the page's bottom padding,
   the sheet's offset and the lifted filter strip all have to agree. They were
   four separate literals at first and did not: the bar measured 77px while the
   page reserved 56, so the footer's last row sat under it and could not be
   tapped. Change this and everything moves together. */
:root {
	/* 72, not 58: MEASURED. A 22px icon over a 12px label with the gaps and
	   padding around them renders 71px, and the first guess of 58 left the
	   filter strip overlapping this bar by 13px on every category page. */
	--cw-tabbar-h: 72px;
}

@media (max-width: 900px) {
	.cw-body .cw-tabbar {
		position: fixed;
		inset-inline: 0;
		bottom: 0;
		z-index: 60;
		display: grid;
		grid-template-columns: repeat(3, 1fr);
		align-items: stretch;
		background: #FFFFFF;
		border-top: 1px solid var(--cw-color-line, #E7D9C4);
		/* Home-indicator inset on iOS, 0 everywhere else. */
		padding-bottom: env(safe-area-inset-bottom, 0px);
		box-shadow: 0 -2px 14px -8px rgba(53, 37, 29, .35);
		/* ⚠️ THE BAR IS THE HEIGHT. Setting `min-height` on the TABS instead
		   made the bar taller than the variable by whatever padding they
		   carried — measured 85px against a declared 72 — so the page's
		   reserved space and the lifted filter strip were both 13px short and
		   the two bars overlapped. Fix the container, let the tabs fill it. */
		height: var(--cw-tabbar-h);
		box-sizing: content-box;
	}

	.cw-body .cw-tabbar__tab {
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: center;
		gap: .15rem;
		padding: .3rem .25rem;
		height: 100%;
		text-decoration: none;
		color: var(--cw-color-ink, #35251D);
		font-family: var(--cw-font-body);
		font-size: .75rem;
		font-weight: 600;
		line-height: 1.2;
	}

	.cw-body .cw-tabbar__tab svg {
		width: 22px;
		height: 22px;
		fill: none;
		stroke: currentColor;
		stroke-width: 1.7;
		stroke-linecap: round;
		stroke-linejoin: round;
	}

	/* ⚠️ THE WHATSAPP MARK IS A FILL PATH AND THE RULE ABOVE STROKES EVERY
	   ICON IN THIS BAR. Left to inherit it, the glyph is painted with a 1.7px
	   outline in the wrong colour on top of an unfilled shape. Both properties
	   are set explicitly.

	   The green is the one piece of colour in the bar, because it is the one
	   tab that leaves the site and shoppers look for it. */
	.cw-body .cw-tabbar__wa {
		display: grid;
		place-items: center;
		width: 22px;
		height: 22px;
	}

	.cw-body .cw-tabbar__wa svg {
		width: 22px;
		height: 22px;
		fill: #25D366;
		stroke: none;
	}

	.cw-body .cw-tabbar__tab:hover,
	.cw-body .cw-tabbar__tab:focus-visible { color: var(--cw-color-maroon, #6C1322); }

	/* ---------- the categories sheet ---------- */

	.cw-body .cw-tabbar__cats { position: static; }

	.cw-body .cw-tabbar__cats > summary {
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: center;
		gap: .2rem;
		height: 100%;
		cursor: pointer;
		list-style: none;
	}

	/* Safari draws its own disclosure triangle without this. */
	.cw-body .cw-tabbar__cats > summary::-webkit-details-marker { display: none; }

	.cw-body .cw-tabbar__sheet {
		position: fixed;
		inset-inline: 0;
		/* Sits ON the bar, never under it. */
		bottom: calc(var(--cw-tabbar-h) + env(safe-area-inset-bottom, 0px));
		max-height: 58vh;
		overflow-y: auto;
		overscroll-behavior: contain;
		padding: var(--cw-space-30) var(--cw-space-30) var(--cw-space-40);
		background: var(--cw-color-ground, #FFFAEB);
		border-top: 1px solid var(--cw-color-line, #E7D9C4);
		box-shadow: 0 -10px 30px -18px rgba(53, 37, 29, .5);
	}

	.cw-body .cw-tabbar__sheettitle {
		margin: 0 0 .75rem;
		font-family: var(--cw-font-body);
		font-size: .6875rem;
		font-weight: 700;
		letter-spacing: .12em;
		text-transform: uppercase;
		color: var(--cw-color-muted, #7A6A60);
	}

	.cw-body .cw-tabbar__links {
		display: grid;
		grid-template-columns: 1fr 1fr;
		gap: .4rem .75rem;
	}

	.cw-body .cw-tabbar__links a {
		display: flex;
		align-items: center;
		min-height: 44px;
		padding: .35rem .5rem;
		border-radius: 10px;
		background: #FFFFFF;
		border: 1px solid var(--cw-color-line, #E7D9C4);
		color: var(--cw-color-ink, #35251D);
		font-family: var(--cw-font-heading);
		font-size: .875rem;
		font-weight: 600;
		text-decoration: none;
	}

	.cw-body .cw-tabbar__all {
		display: inline-block;
		margin-top: .9rem;
		color: var(--cw-color-maroon, #6C1322);
		font-family: var(--cw-font-body);
		font-size: .8125rem;
		font-weight: 600;
	}

	/* ⚠️ THE PAGE HAS TO END ABOVE THE BAR. Without this the footer's last row
	   sits under it and cannot be tapped — the classic fixed-bar bug. */
	.cw-body { padding-bottom: calc(var(--cw-tabbar-h) + env(safe-area-inset-bottom, 0px)); }

	/* 🔴 ON A PRODUCT PAGE THE BUY BAR WINS AND THE TAB BAR STANDS DOWN.

	   The product page has its own sticky bar — price, and "Add to basket"
	   (CW-54). Both live on the bottom edge, and the first attempt at CW-51 put
	   them there together: `ui:browser` caught it immediately as
	   "mobile: the bar's button is clickable — Timeout" followed by
	   "the product is really in the basket afterwards — cart is empty". The tab
	   bar was sitting on top of the only button that sells anything.

	   Stacking them was the wrong answer here for the same reason it was wrong
	   for the filter strip. This is a PRIORITY question, not a layout one:
	   somebody looking at a product wants to buy it, not to go home. So the buy
	   bar takes the edge and the tab bar hides — the header drawer still
	   reaches every category, one tap away.

	   `:has()` rather than a body class: the buy bar is injected by a
	   view_render_event, so no Blade in the layout knows whether it is there. */
	.cw-body:has(.cw-buybar) .cw-tabbar { display: none; }
	.cw-body:has(.cw-buybar) { padding-bottom: 0; }

	/* 🔴 THERE IS DELIBERATELY NO SECOND FIXED BAR TO STACK HERE ANY MORE.
	   An earlier version of this block LIFTED Bagisto's mobile filter strip to
	   sit on top of the tab bar. It worked, and it was the wrong answer: two
	   fixed bars take 145px out of an 844px phone. The strip was removed
	   instead and the filters live in the page (CW-39's panel, now at every
	   width). If a `fixed bottom-0` element ever reappears inside the listing,
	   the fix is to remove it, not to lift it. */
}

/* =========================================================================
   CW-52 — the phone shortcut row

   Four icon + label links under the header. The labels hide on scroll, which
   is the whole point of the ticket: it buys back ~22px of a small screen
   without losing the shortcuts themselves.

   ⚠️ IT REUSES `.cw-scrolled`, THE CLASS motion.js ALREADY PUTS ON THE BODY
   for the header condense. A second scroll listener would be a second source
   of truth that can disagree with the first — and this row sits directly under
   the header, so a disagreement is visible as a jump.
   ========================================================================= */

.cw-body .cw-shortcuts { display: none; }

@media (max-width: 900px) {
	.cw-body .cw-shortcuts {
		display: block;
		background: var(--cw-color-ground, #FFFAEB);
		border-bottom: 1px solid var(--cw-color-line, #E7D9C4);
	}

	.cw-body .cw-shortcuts__row {
		display: grid;
		grid-template-columns: repeat(4, 1fr);
		gap: 0;
		margin: 0;
		/* ⚠️ NO HORIZONTAL PADDING, deliberately. "Sacred Collection" is 17
		   characters and has to sit on ONE line (below); every pixel of side
		   padding comes straight off the width it has to do that in. The
		   discs are centred in their columns, so the row still reads as
		   inset. */
		padding: .5rem 0;
		list-style: none;
	}

	.cw-body .cw-shortcuts__link {
		display: flex;
		flex-direction: column;
		align-items: center;
		gap: .3rem;
		padding: .2rem 1px;
		text-decoration: none;
		color: var(--cw-color-ink, #35251D);
	}

	.cw-body .cw-shortcuts__disc {
		display: grid;
		place-items: center;
		width: 42px;
		height: 42px;
		border-radius: 50%;
		overflow: hidden;
		background: var(--cw-color-ground-alt, #FAECDB);
	}

	.cw-body .cw-shortcuts__disc img {
		width: 42px;
		height: 42px;
		object-fit: cover;
	}

	.cw-body .cw-shortcuts__label {
		font-family: var(--cw-font-heading);
		font-weight: 600;
		line-height: 1.2;
		text-align: center;

		/* 🔴 ONE LINE, ALWAYS. The owner reported "Sacred Collection text is
		   going in two lines… which is not good", and an earlier attempt at
		   reserving two lines for ALL FOUR was the wrong fix — it made the row
		   taller to accommodate one label.

		   ⚠️ A FIXED SIZE CANNOT WORK HERE, and that is measured rather than
		   assumed. "Sacred Collection" in Poppins 600 needs:

		       11px -> 98.7px    10px -> 89.7px    9px -> 80.8px

		   against a column of 390/4 = 97.5px at an iPhone width, 90px at 360
		   and 80px at 320. 11px overflows every phone; 10px fits a 390 and
		   fails a 360.

		   So it scales with the viewport, which is the same thing the column
		   does. At 390 that is 9.75px (87.5px wide, ~8px clear), at 360 it is
		   9px (80.8 in 88), and the 8px floor holds 320 (71.8 in 78). The cap
		   keeps it from growing past the design size on a tablet. */
		font-size: clamp(8px, 2.5vw, .6875rem);
		white-space: nowrap;

		/* Animated rather than switched, so the row does not jump. The height
		   is what collapses; opacity alone would leave the gap. One line now,
		   so one line's worth. */
		min-height: 1.2em;
		max-height: 1.2em;
		opacity: 1;
		transition: max-height 200ms ease, opacity 160ms ease, margin 200ms ease;
		overflow: hidden;
	}

	/* ⚠️ THE LABEL IS HIDDEN VISUALLY, NOT REMOVED. `display: none` or
	   `aria-hidden` would leave the link with NO accessible name — the image is
	   decorative (empty alt) — so a screen reader would announce four
	   unlabelled links. Zero height with `overflow: hidden` keeps it in the
	   accessibility tree. */
	/* ⚠️ `.cw-scrolled` IS ON <html>, NOT ON THE BODY. motion.js line 69:
	   `document.documentElement.classList.toggle('cw-scrolled', ...)`. Written
	   as `.cw-body.cw-scrolled` — both classes on the same element — the rule
	   matched nothing and the labels never collapsed, while every other rule in
	   this file that uses it (`.cw-scrolled .cw-logo img`) is a DESCENDANT
	   selector and works. Measured: labelH stayed 13px after scrolling 900px. */
	.cw-scrolled .cw-body .cw-shortcuts__label,
	html.cw-scrolled .cw-shortcuts__label {
		min-height: 0;
		max-height: 0;
		opacity: 0;
		margin-top: -.3rem;
	}

	@media (prefers-reduced-motion: reduce) {
		.cw-body .cw-shortcuts__label { transition: none; }
	}
}

/* =========================================================================
   CW-45 — THE "PAY ONLINE" PAGE

   A clone of https://gemmines.in/pay-online/ per the owner's instruction of
   2026-09-05 ("Build this page as it is"). Their page is three payment QR
   cards over a bank-details block, and nothing else — see the head of
   views/cw/pay-online.blade.php for why that differs from the ticket text.

   Following D2 (DH-CLONE §2): their STRUCTURE, our brand. The reference is a
   Bootstrap `col-md-3` row on white with Noto Serif headings in navy; this is
   the same three-up rhythm in our own type, ground and maroon.

   ⚠️ NO max-width HERE. The page's outer element is `main .container`, which
   already resolves to the shared 1344px frame (§24.2 above). Adding another
   would be the seventh content edge that section exists to have removed.
   ========================================================================= */

.cw-pay {
	padding-block: clamp(1.5rem, 1rem + 2vw, 3rem);
}

.cw-pay__head {
	text-align: center;
	margin-bottom: clamp(1.5rem, 1rem + 2vw, 2.5rem);
}

.cw-pay__title {
	font-family: var(--cw-font-heading);
	font-size: clamp(1.6rem, 1.35rem + 1.2vw, 2rem);
	font-weight: 700;
	line-height: 1.2;
	color: var(--cw-color-maroon);
	margin: 0;
}

.cw-pay__lede {
	max-width: 46rem;
	margin: .75rem auto 0;
	color: var(--cw-color-ink-soft);
	line-height: 1.55;
}

/* THREE UP, THEN ONE. The reference is three `col-md-3` columns that collapse
   to full width on a phone. `auto-fit` with a 240px floor gives the same two
   states without a breakpoint, and cannot produce a lone orphan card on a
   tablet the way a hard-coded 3/1 split does. */
.cw-pay__grid {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr));
	gap: clamp(1rem, .5rem + 2vw, 2rem);
	margin: 0;
	padding: 0;
	list-style: none;
}

.cw-pay__card {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: .75rem;
	padding: 1.25rem 1rem;
	background: var(--cw-color-surface);
	border: 1px solid var(--cw-color-line);
	border-radius: var(--cw-r, 14px);
	box-shadow: var(--cw-shadow-soft);
}

/* ⚠️ CAPPED AT THE FILE'S OWN 288px, DELIBERATELY. The reference stretches
   these 288x560 PNGs to 332x600 and its QR codes are visibly soft as a result.
   A soft QR is one a camera can fail to resolve, so the one thing not copied
   from that page is its upscaling. `height: auto` keeps the aspect honest. */
.cw-pay__qr {
	width: 100%;
	max-width: 288px;
	height: auto;
	display: block;
}

.cw-pay__label {
	margin: 0;
	font-family: var(--cw-font-heading);
	font-size: .9375rem;
	font-weight: 600;
	color: var(--cw-color-ink);
}

.cw-pay__bank {
	margin-top: clamp(2rem, 1.5rem + 2vw, 3.5rem);
	padding: clamp(1.25rem, 1rem + 1.5vw, 2rem);
	background: var(--cw-color-ground);
	border: 1px solid var(--cw-color-line);
	border-radius: var(--cw-r, 14px);
	text-align: center;
}

.cw-pay__bankh {
	font-family: var(--cw-font-heading);
	font-size: clamp(1.3rem, 1.15rem + .8vw, 1.6rem);
	font-weight: 700;
	color: var(--cw-color-maroon);
	margin: 0 0 1rem;
}

.cw-pay__dl {
	display: grid;
	gap: .5rem;
	margin: 0 auto;
	max-width: 28rem;
	text-align: left;
}

/* Label left, value right, on one line each. `dt`/`dd` are block by default
   and would otherwise stack into eight rows instead of four. */
.cw-pay__row {
	display: flex;
	justify-content: space-between;
	gap: 1rem;
	padding-block: .4rem;
	border-bottom: 1px solid var(--cw-color-line);
}

.cw-pay__row dt {
	color: var(--cw-color-ink-soft);
}

/* The account number and IFSC are copied by hand off a screen, so they get a
   tabular face and a little tracking rather than the body stack. */
.cw-pay__row dd {
	margin: 0;
	font-weight: 600;
	color: var(--cw-color-ink);
	font-variant-numeric: tabular-nums;
	letter-spacing: .01em;
	text-align: right;
}

.cw-pay__note {
	margin: 1rem 0 0;
	font-size: .9375rem;
	color: var(--cw-color-ink-soft);
}
