/* Dark/Light Glass theme, extended to the webshop storefront (product listing, product
   detail, the "Select Variant" dialog, and /cart) -- until now these rendered with
   zero theme coverage at all (confirmed live and by a repo-wide grep before writing
   this: no rule in portal_theme.css touches .item-card, .product-*, .cart-*,
   .frappe-card, or .modal-content anywhere), so they looked like a stock, unthemed
   ERPNext site sitting inside an otherwise fully "Dark Glass" portal.

   Deliberately NOT a new palette or typeface -- every color here is one of this app's
   own existing tokens (--santek-*, --fg-color, --text-*, --border-color -- see
   portal_theme.css's :root[data-theme] blocks), and every font stays whatever the rest
   of the portal already inherits. A shop that suddenly looked like a different product
   from the rest of the portal would undermine the actual ask ("redesign the webshop...
   inline with our theme"), not fulfill it.

   The one deliberate signature, consistent across every surface below: Santek sells
   lighting, so every product photo and every primary "buy" action gets treated as a
   literal light source -- a soft, blurred, accent-colored glow rendered behind it, via
   the new --santek-glow token (a translucent derivation of --santek-accent, same
   color-mix() technique portal_theme.css already uses once for .btn-login). Everything
   else reuses this app's existing card/radius/shadow/hover-lift conventions verbatim
   rather than inventing new ones, so the "boldness" stays in exactly one place.

   Webshop's own JS (product_ui/grid.js, list.js, views.js -- all in the `webshop` app,
   not this one) builds product cards/toolbar/pagination via template strings with fixed
   class names; there is no server template to override here the way navbar.html was
   overridden earlier in this theme, so this file matches those class names directly,
   same approach already used elsewhere in this app for foreign core markup (.order-items,
   .web-list-item.transaction-list-item, .for-login). */

/* ---------------------------------------------------------------------------
   Signature token: the "light source" glow, derived from the existing accent
   color, not a new hue. Both themes get their own strength -- Light Glass's
   brighter, more saturated backdrop needs a slightly stronger glow to read at
   all against it, mirroring how --santek-card-bg/--fg-color already carry
   different opacities per theme rather than one shared value. */
html[data-theme$="-dark"] {
	--santek-glow: color-mix(in srgb, var(--santek-accent) 40%, transparent);
	--santek-glow-strong: color-mix(in srgb, var(--santek-accent) 65%, transparent);
}

html[data-theme$="-light"] {
	--santek-glow: color-mix(in srgb, var(--santek-accent) 32%, transparent);
	--santek-glow-strong: color-mix(in srgb, var(--santek-accent) 55%, transparent);
}

/* ---------------------------------------------------------------------------
   Listing pages (item_group.html / /all-products / product_search.html): opt
   out of the single big main.container card, same reasoning and same :has()
   mechanism portal_theme.css already uses for Orders/My Returns/Packaging
   Designs -- a shop with a dozen-plus tiles reads as a wall of product, not
   one more document, and each tile gets its own glass card below instead.
   #products-grid-area/#products-list-area/.toolbar are all inserted by
   webshop's own JS after page load, not present in the server-rendered HTML --
   :has() re-evaluates live as the DOM changes, so this still matches once that
   JS runs, the same JS-insertion timing every other :has() rule in this app's
   theme already accounts for. */
html[data-theme$="-dark"] main.container:has(.toolbar),
html[data-theme$="-light"] main.container:has(.toolbar) {
	background: transparent !important;
	border: none !important;
	box-shadow: none !important;
	backdrop-filter: none !important;
	-webkit-backdrop-filter: none !important;
	padding: 0 !important;
}

/* ---------------------------------------------------------------------------
   Toolbar: search box + grid/list view toggle, sitting directly on the open
   gradient background now that the big card is gone above. */
.toolbar {
	gap: 12px;
	margin: 0.5rem 0 1.5rem;
	align-items: center;
}

html[data-theme$="-dark"] #search-box,
html[data-theme$="-light"] #search-box {
	background: var(--santek-input-bg);
	border: 1px solid var(--santek-card-border);
	border-radius: 999px;
	color: var(--text-color);
	padding-right: 2.5rem;
}

html[data-theme$="-dark"] #search-box::placeholder,
html[data-theme$="-light"] #search-box::placeholder {
	color: var(--text-muted);
}

.search-icon {
	color: var(--text-muted);
}

html[data-theme$="-dark"] .toggle-container .btn,
html[data-theme$="-light"] .toggle-container .btn {
	background: var(--santek-input-bg);
	border: 1px solid var(--santek-card-border);
	color: var(--text-muted);
	border-radius: 10px;
}

/* First fix here was a solid var(--santek-accent) fill -- technically correct
   (confirmed live it replaced a flat unthemed rgb(124,124,124) grey square,
   the original bug), but reported back as not fitting the theme: a fully
   opaque color block is foreign to a system where nothing else is opaque.
   Matches DESIGN.md's own Navigation language instead -- "active/hover states
   use the accent border-color treatment on filter pills and category chips" --
   translucent glass background (same token as the inactive state, just one
   step up for visible contrast) with an accent border + accent-tinted icon,
   not a filled block. !important still needed on background: some other, more
   specific core rule was winning that property alone even though it left
   border-color/color to this rule -- unclear which, not worth chasing further
   now that the fill itself is gone. */
html[data-theme$="-dark"] .toggle-container .btn.btn-primary,
html[data-theme$="-light"] .toggle-container .btn.btn-primary {
	background: var(--santek-card-bg) !important;
	border-color: var(--santek-accent);
	color: var(--santek-accent);
}

/* Item Group sub-category chips (e.g. browsing "LED Bulbs" shows "Standard
   Bulbs"/"Smart & RGB Bulbs"/... as pills) -- same pill language as
   .santek-list-filter-pill in timeline.css, reused rather than a new shape. */
.sub-category-container.scroll-categories {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem;
	margin-bottom: 1.5rem;
}

html[data-theme$="-dark"] .category-pill,
html[data-theme$="-light"] .category-pill {
	display: inline-block;
	padding: 0.3rem 0.9rem;
	border-radius: 999px;
	border: 1px solid var(--border-color);
	color: var(--text-muted);
	font-size: 0.85rem;
	background: var(--fg-color);
}

html[data-theme$="-dark"] .category-pill:hover,
html[data-theme$="-light"] .category-pill:hover {
	color: var(--text-color);
	border-color: var(--santek-accent);
}

/* Filter sidebar (Item Groups / attribute checkboxes / discount radios) -- its
   own component, not the site's .web-sidebar nav, so it needs its own card
   treatment rather than inheriting that unrelated selector's rules. */
html[data-theme$="-dark"] #product-filters,
html[data-theme$="-light"] #product-filters {
	background: var(--santek-card-bg);
	backdrop-filter: var(--santek-blur);
	-webkit-backdrop-filter: var(--santek-blur);
	border: 1px solid var(--santek-card-border);
	border-radius: 18px;
	padding: 1.25rem;
	box-shadow:
		inset 0 1px 0 var(--santek-card-highlight),
		0 6px 14px -4px var(--santek-card-shadow);
}

html[data-theme$="-dark"] .filter-label,
html[data-theme$="-light"] .filter-label {
	color: var(--text-color);
	font-weight: 600;
	text-transform: uppercase;
	font-size: 0.75rem;
	letter-spacing: 0.06em;
}

/* Reported live as unreadable on dark theme. First attempt here targeted
   .filter-options label's own color (var(--text-muted), only brightening on
   :hover) -- that rule was real (light theme's #3d4552 muted value has plenty
   of contrast on a light card; dark theme's #a9b4c6 was noticeably weaker
   against the dark navy card) but turned out not to be what was actually
   controlling the visible pixels: confirmed live via the real DOM
   (<label><input>...<span class="label-area">B22</span></label>) that the
   filter text lives in a nested .label-area span, and core webshop's own
   bundled CSS sets `color: var(--gray-800)` directly on that span --
   `--gray-800` is a fixed, non-theme-aware Bootstrap-style gray this app
   never redefines per theme, so it renders the identical dark value in both
   themes: fine by coincidence on light theme's light card, unreadable on
   dark theme's dark card. An element's own directly-set color always wins
   over whatever its ancestor's color is, regardless of specificity, so the
   .filter-options label fix above was harmless but never actually reached
   the text -- kept anyway since it's still correct for any other content
   this element might render without a .label-area wrapper. Filter option
   text is primary, actionable content (a shopper has to read the label to
   decide whether to click it), not secondary caption text, so it gets full
   --text-color always, not just on hover.

   !important on the .label-area rule is load-bearing: core's own rule is
   actually `.label-area, #page-index .filters-section .checkbox .label-area
   {...}` -- a comma list where the *second* alternative includes #page-index
   (confirmed live this page's body carries that id), giving it (1,3,0)
   specificity that beats this rule's (0,3,2) on the leading ID column alone,
   regardless of how many classes get added here. */
html[data-theme] .filter-options label {
	color: var(--text-color);
}

html[data-theme] .filter-options label .label-area {
	color: var(--text-color) !important;
}

/* Search-box autocomplete dropdown (webshop's own #search-box results panel) --
   confirmed live it had zero theme coverage at all: an opaque white
   .dropdown-menu, a flat grey Bootstrap shadow, 6px radius (off this system's
   ladder), and every child (category chip, result rows, "Recent" pills) using
   raw Bootstrap greys. A solid white popover sitting directly under an
   otherwise-glass search field read exactly like the unthemed .modal-content
   this file's own dialog section fixed earlier. Panel gets the Control radius
   (10px, DESIGN.md's own "small dropdown menus" category, same one the
   language picker uses) rather than Container/Card scale -- it's a small
   transient popover, not a page-level surface. */
html[data-theme$="-dark"] .dropdown-menu.w-100,
html[data-theme$="-light"] .dropdown-menu.w-100 {
	background: var(--santek-card-bg) !important;
	backdrop-filter: var(--santek-blur);
	-webkit-backdrop-filter: var(--santek-blur);
	border: 1px solid var(--santek-card-border) !important;
	border-radius: 10px !important;
	box-shadow:
		inset 0 1px 0 var(--santek-card-highlight),
		0 6px 14px -4px var(--santek-card-shadow) !important;
}

html[data-theme$="-dark"] .dropdown-menu.w-100 .dropdown-item,
html[data-theme$="-light"] .dropdown-menu.w-100 .dropdown-item {
	border-radius: 8px;
}

html[data-theme$="-dark"] .dropdown-menu.w-100 .dropdown-item:hover,
html[data-theme$="-light"] .dropdown-menu.w-100 .dropdown-item:hover {
	background: var(--santek-input-bg);
}

html[data-theme$="-dark"] .dropdown-menu.w-100 .dropdown-item a,
html[data-theme$="-light"] .dropdown-menu.w-100 .dropdown-item a {
	color: var(--santek-accent) !important;
}

html[data-theme$="-dark"] .brand-line,
html[data-theme$="-light"] .brand-line {
	color: var(--text-muted) !important;
}

html[data-theme$="-dark"] .recent-searches b,
html[data-theme$="-light"] .recent-searches b {
	color: var(--text-color);
	font-weight: 600;
	text-transform: uppercase;
	font-size: 0.75rem;
	letter-spacing: 0.06em;
}

/* .category-chip (search dropdown) and .recent-search (past queries) are the
   same pill-tag language as .category-pill above -- reused verbatim rather
   than a third near-identical rule. */
html[data-theme$="-dark"] .category-chip,
html[data-theme$="-light"] .category-chip,
html[data-theme$="-dark"] .recent-search,
html[data-theme$="-light"] .recent-search {
	display: inline-block;
	border-radius: 999px !important;
	border: 1px solid var(--border-color) !important;
	color: var(--text-muted) !important;
	background: var(--fg-color) !important;
}

html[data-theme$="-dark"] .category-chip:hover,
html[data-theme$="-light"] .category-chip:hover,
html[data-theme$="-dark"] .recent-search:hover,
html[data-theme$="-light"] .recent-search:hover {
	color: var(--text-color) !important;
	border-color: var(--santek-accent) !important;
}

/* ---------------------------------------------------------------------------
   Product image "light box" -- the signature glow, shared by every place a
   product photo renders (grid tile, list row, detail hero, cart line item,
   variant dialog preview). A ::before pseudo-element keeps this to pure CSS,
   no new markup needed anywhere it's used: a blurred radial glow sized larger
   than the frame, centered behind it, strongest directly on the image, fading
   to nothing at the tile's own edge so it reads as light spilling from the
   product, not a decorative background shape. */
.card-img-container,
.list-image,
.item-slideshow-image,
.cart-item-image,
.santek-variant-preview-col {
	position: relative;
}

.card-img-container::before,
.list-image::before,
.cart-item-image::before,
.santek-variant-preview-col::before {
	content: "";
	position: absolute;
	inset: 0;
	/* "circle" with no sizing keyword defaults to farthest-corner -- confirmed
	   live via getBoundingClientRect that this calibrates 100% against the
	   diagonal corner distance, not the near edge, so on a ~300x210 tile the
	   actual visible edges (the only part not already covered by the opaque
	   image on top) sat past this gradient's own fade-to-transparent point --
	   invisible regardless of opacity/blur tuning. farthest-side calibrates
	   100% to the nearer of each axis's own edge instead; pushing the
	   transparent stop out past 100% keeps real color present at and beyond
	   that edge, exactly the ring the image doesn't cover. */
	background: radial-gradient(circle farthest-side, var(--santek-glow) 0%, transparent 170%);
	filter: blur(18px);
	z-index: 0;
	opacity: 1;
	transition: opacity 0.25s ease;
	pointer-events: none;
}

.item-card:hover .card-img-container::before,
.list-row:hover .list-image::before {
	opacity: 1;
}

html[data-theme$="-dark"] .card-img-container img,
html[data-theme$="-dark"] .list-image img,
html[data-theme$="-dark"] .santek-variant-preview-col img,
html[data-theme$="-light"] .card-img-container img,
html[data-theme$="-light"] .list-image img,
html[data-theme$="-light"] .santek-variant-preview-col img {
	position: relative;
	z-index: 1;
	border-radius: 12px;
}

html[data-theme$="-dark"] .card-img-top.no-image,
html[data-theme$="-dark"] .card-img-top.no-image-list,
html[data-theme$="-light"] .card-img-top.no-image,
html[data-theme$="-light"] .card-img-top.no-image-list {
	position: relative;
	z-index: 1;
	background: var(--fg-color);
	color: var(--text-muted);
	border-radius: 12px;
	display: flex;
	align-items: center;
	justify-content: center;
	font-weight: 600;
}

/* ---------------------------------------------------------------------------
   Grid tiles (webshop.ProductGrid). Same 18px-radius glass card and hover-lift
   already used for .web-list-item.transaction-list-item/.order-items rows --
   deliberately not a new card shape, just the established one applied here. */
html[data-theme$="-dark"] .item-card .card,
html[data-theme$="-light"] .item-card .card {
	background: var(--santek-card-bg);
	backdrop-filter: var(--santek-blur);
	-webkit-backdrop-filter: var(--santek-blur);
	border: 1px solid var(--santek-card-border);
	border-radius: 18px;
	box-shadow:
		inset 0 1px 0 var(--santek-card-highlight),
		0 6px 14px -4px var(--santek-card-shadow);
	transition: transform 0.15s ease;
	height: 100%;
}

html[data-theme$="-dark"] .item-card .card:hover,
html[data-theme$="-light"] .item-card .card:hover {
	transform: translateY(-4px);
}

/* overflow:hidden here (not on .card above) is the glow's own clip boundary --
   confirmed live that leaving both containers unclipped let the glow bleed
   down into the title text below it, reading as a stray artifact rather than
   ambient light. Clipping at the image container's own edge (not the image's)
   still leaves the glow visible in the padding around the photo -- the actual
   effect this was going for -- without ever touching card-body content. */
.item-card .card-img-container {
	margin: -1px -1px 0;
	padding: 1.25rem 1.25rem 0;
	overflow: hidden;
}

.item-card .card-body {
	padding: 1rem 1.25rem 1.25rem;
}

html[data-theme$="-dark"] .item-card .product-title,
html[data-theme$="-light"] .item-card .product-title {
	color: var(--text-color);
	font-weight: 600;
	font-size: 1rem;
	line-height: 1.35;
}

html[data-theme$="-dark"] .item-card .product-category,
html[data-theme$="-light"] .item-card .product-category {
	color: var(--text-muted);
	font-size: 0.75rem;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	margin-top: 0.25rem;
}

html[data-theme$="-dark"] .item-card .product-price,
html[data-theme$="-light"] .item-card .product-price {
	color: var(--santek-accent);
	font-weight: 700;
	font-size: 1.05rem;
	margin-top: 0.6rem;
}

.item-card .striked-price {
	color: var(--text-muted);
	font-weight: 400;
	margin-left: 0.4rem;
}

/* ---------------------------------------------------------------------------
   List rows (webshop.ProductList) -- same per-row glass card as every other
   list surface in this app (Orders/Returns/Packaging Designs' .web-list-item),
   reused verbatim here rather than a shop-specific variant. */
html[data-theme$="-dark"] .list-row,
html[data-theme$="-light"] .list-row {
	background: var(--santek-card-bg);
	backdrop-filter: var(--santek-blur);
	-webkit-backdrop-filter: var(--santek-blur);
	border: 1px solid var(--santek-card-border);
	border-radius: 18px;
	padding: 1.1rem 1.25rem;
	/* No margin override -- confirmed live this was zeroing the row's own
	   `mb-4` Bootstrap utility class (ProductList's real markup carries
	   `class="row list-row w-100 mb-4"`), bonding every row flush against the
	   next with zero gap. Every other per-row glass card in this app
	   (.web-list-item.transaction-list-item/.mb-3 in portal_theme.css) already
	   gets its spacing the same way -- leave the row's own utility class alone
	   rather than fight it -- this rule just never followed that precedent. */
	box-shadow:
		inset 0 1px 0 var(--santek-card-highlight),
		0 6px 14px -4px var(--santek-card-shadow);
	transition: transform 0.15s ease;
	align-items: center;
}

html[data-theme$="-dark"] .list-row:hover,
html[data-theme$="-light"] .list-row:hover {
	transform: translateY(-2px);
}

.list-row .list-image {
	padding: 0.4rem;
	border: none !important;
}

html[data-theme$="-dark"] .list-row a,
html[data-theme$="-light"] .list-row a {
	color: var(--text-color) !important;
}

html[data-theme$="-dark"] .list-row .product-code,
html[data-theme$="-light"] .list-row .product-code {
	color: var(--text-muted);
	font-size: 0.8rem;
	margin-top: 0.3rem;
}

html[data-theme$="-dark"] .list-row .product-price,
html[data-theme$="-light"] .list-row .product-price {
	color: var(--santek-accent);
	font-weight: 700;
	margin-top: 0.5rem;
}

/* ---------------------------------------------------------------------------
   Stock badges -- reuse the exact status-dot vocabulary timeline.css already
   defines for Return Intake stages (green/muted text, no new color scheme). */
html[data-theme$="-dark"] .in-stock.in-green,
html[data-theme$="-light"] .in-stock.in-green {
	color: var(--green-500, #2e7d32);
	font-weight: 600;
}

html[data-theme$="-dark"] .out-of-stock,
html[data-theme$="-light"] .out-of-stock {
	color: var(--red-500, #c62828);
	font-weight: 600;
}

/* Wishlist heart -- muted by default, accent when active, matching how every
   other icon-affordance in this theme (theme toggle, lang picker chevron)
   already uses --text-muted/--santek-accent rather than a hardcoded color. */
html[data-theme$="-dark"] .like-action,
html[data-theme$="-dark"] .like-action-list,
html[data-theme$="-dark"] .like-action-item-fp,
html[data-theme$="-light"] .like-action,
html[data-theme$="-light"] .like-action-list,
html[data-theme$="-light"] .like-action-item-fp {
	background: var(--fg-color);
	border-radius: 999px;
}

/* Same fill-not-color bug as .remove-cart-item-logo above, fixed alongside
   since it's the identical one-line cause -- not independently reproduced
   live on this page (wishlist is hidden unless Webshop Settings.
   enable_wishlist), but the underlying mechanism is already proven. */
html[data-theme$="-dark"] .wish-icon,
html[data-theme$="-light"] .wish-icon {
	color: var(--text-muted);
	fill: currentColor !important;
}

html[data-theme$="-dark"] .wish-icon.wished,
html[data-theme$="-light"] .wish-icon.wished {
	color: var(--santek-accent);
}

/* ---------------------------------------------------------------------------
   Pagination + empty state. */
html[data-theme$="-dark"] .product-paging-area .btn,
html[data-theme$="-light"] .product-paging-area .btn {
	background: var(--santek-input-bg);
	border: 1px solid var(--santek-card-border);
	color: var(--text-color);
	border-radius: 999px;
	padding: 0.4rem 1.2rem;
}

html[data-theme$="-dark"] .product-paging-area .btn:disabled,
html[data-theme$="-light"] .product-paging-area .btn:disabled {
	color: var(--text-muted);
	opacity: 0.5;
}

/* Deliberately no card here -- reported live as "a big card again" on an empty
   cart, same call already made for /me's own settings list: a sparse content area
   (one illustration, one line of text, one link) reads better as plain content on
   the page's own gradient background than boxed inside a second glass panel,
   especially now that main.container's own outer card is stripped for this page
   too (see portal_theme.css's main.container:has(.cart-empty) rule).

   !important is load-bearing, not decorative: .cart-empty also carries the plain
   .frappe-card class, and core's own website.bundle.css styles that generically
   (border-radius: var(--border-radius-md); padding: var(--padding-sm); background-
   color: var(--card-bg); border: 1px solid var(--border-color)) -- confirmed live
   that simply not re-declaring our own override left that core card fully visible
   underneath, it doesn't disappear on its own just because our override is gone.
   Scoped to .cart-empty specifically, not a blanket .frappe-card rule -- other real
   cards elsewhere in webshop (cart items table, product cards) are supposed to look
   like cards and are untouched. Layout properties (min-height/flex-centering) stay
   webshop's own native .cart-empty.frappe-card rule (webshop-web.bundle.css).

   Real second round of this same bug, found live after the first fix still showed
   a visible box: this app's own generic ".frappe-card" rule below (line ~732) also
   sets backdrop-filter: var(--santek-blur) on every element carrying that class,
   .cart-empty included, and it has no !important -- but that doesn't matter when
   nothing MORE specific ever mentions the property at all. This rule originally
   left backdrop-filter out entirely (only background/border/shadow/radius/padding),
   so the generic rule's blur kept applying untouched -- a real blur behind a fully
   transparent, borderless box still reads as a visible card-shaped region against
   the page's own detailed gradient. An overridden rule silently omitting a property
   doesn't reset it to nothing; the cascade just falls through to whatever else
   still sets it. Confirmed via getComputedStyle(el).backdropFilter directly, not
   guessed -- background/border/shadow all correctly showed "none"/transparent while
   backdrop-filter alone still reported the full blur() value. */
html[data-theme] .cart-empty.frappe-card {
	background: transparent !important;
	border: none !important;
	box-shadow: none !important;
	border-radius: 0 !important;
	padding: 0 !important;
	backdrop-filter: none !important;
	-webkit-backdrop-filter: none !important;
	text-align: center;
}

.cart-empty-state img {
	max-width: 160px;
	opacity: 0.7;
}

html[data-theme$="-dark"] .cart-empty-message,
html[data-theme$="-light"] .cart-empty-message {
	color: var(--text-muted);
	font-size: 1.05rem;
}

/* ---------------------------------------------------------------------------
   Product detail page (templates/generators/item/item.html). main.container's
   existing single-card treatment already wraps this page correctly (it has no
   .toolbar, so the opt-out above doesn't touch it) -- but .product-container.
   item-main, webshop's own inner wrapper, carries its own hardcoded opaque
   white background + box-shadow (confirmed live via getComputedStyle -- a
   real "card" look core webshop gives this element on a plain page). Left
   alone it painted a solid white panel with near-white text on top of it,
   directly inside the dark glass card behind it -- worse than the page's
   original unstyled state, not just incomplete. Neutralized the same way
   portal_theme.css already neutralizes .website-list's own opaque background
   for the exact same reason (one real card per page, not two nested ones).
   item.html reuses this same .product-container wrapper class a second time,
   without .item-main, around the specifications/reviews section further down
   the page -- confirmed live it carries the identical opaque white background,
   so this targets the bare class rather than the compound one to catch both. */
html[data-theme$="-dark"] .product-container,
html[data-theme$="-light"] .product-container {
	background: transparent !important;
	border: none !important;
	box-shadow: none !important;
}

html[data-theme$="-dark"] .product-title,
html[data-theme$="-light"] .product-title {
	color: var(--text-color);
	font-weight: 700;
	font-size: 1.6rem;
	line-height: 1.3;
}

html[data-theme$="-dark"] .product-code,
html[data-theme$="-light"] .product-code {
	color: var(--text-muted);
	font-size: 0.85rem;
	margin-top: 0.4rem;
}

html[data-theme$="-dark"] .product-description,
html[data-theme$="-light"] .product-description {
	color: var(--text-color);
	margin-top: 1.5rem;
	line-height: 1.6;
}

html[data-theme$="-dark"] .product-details .product-price,
html[data-theme$="-light"] .product-details .product-price {
	color: var(--santek-accent);
	font-weight: 700;
	font-size: 1.4rem;
}

html[data-theme$="-dark"] .item-cart .formatted-price,
html[data-theme$="-light"] .item-cart .formatted-price {
	color: var(--text-muted);
	font-weight: 400;
}

html[data-theme$="-dark"] .item-slideshow-image,
html[data-theme$="-light"] .item-slideshow-image {
	border-radius: 10px;
	border: 2px solid transparent;
	opacity: 0.6;
	cursor: pointer;
}

html[data-theme$="-dark"] .item-slideshow-image.active,
html[data-theme$="-light"] .item-slideshow-image.active {
	border-color: var(--santek-accent);
	opacity: 1;
}

html[data-theme$="-dark"] .offers-heading,
html[data-theme$="-light"] .offers-heading {
	color: var(--text-color);
}

html[data-theme$="-dark"] .offer-container p,
html[data-theme$="-light"] .offer-container p {
	color: var(--text-muted);
}

html[data-theme$="-dark"] .recommendation-header,
html[data-theme$="-light"] .recommendation-header {
	color: var(--text-muted);
	text-transform: uppercase;
	font-size: 0.75rem;
	letter-spacing: 0.06em;
}

/* ---------------------------------------------------------------------------
   "Select Variant" dialog (frappe.ui.Dialog, item_configure.js /
   webshop_variant_selector.js) -- the first frappe.ui.Dialog to ever appear in
   this app's guest-facing portal (desk's own dialogs are a different, already-
   themed surface entirely; this one had zero coverage). Uses the same
   large-card treatment as main.container (28px radius, the big drop shadow)
   since a modal is this page's own temporary focal surface, same visual
   weight class as a full page card. */
html[data-theme$="-dark"] .modal-content,
html[data-theme$="-light"] .modal-content {
	background: var(--santek-card-bg);
	backdrop-filter: var(--santek-blur);
	-webkit-backdrop-filter: var(--santek-blur);
	border: 1px solid var(--santek-card-border);
	border-radius: 28px !important;
	box-shadow:
		inset 0 1px 0 var(--santek-card-highlight),
		0 30px 80px var(--santek-card-shadow);
}

html[data-theme$="-dark"] .modal-header,
html[data-theme$="-light"] .modal-header {
	border-bottom: 1px solid var(--border-color);
}

html[data-theme$="-dark"] .modal-title,
html[data-theme$="-dark"] .modal-body label,
html[data-theme$="-dark"] .modal-body .control-label,
html[data-theme$="-light"] .modal-title,
html[data-theme$="-light"] .modal-body label,
html[data-theme$="-light"] .modal-body .control-label {
	color: var(--text-color);
}

html[data-theme$="-dark"] .modal-footer,
html[data-theme$="-light"] .modal-footer {
	border-top: 1px solid var(--border-color);
}

/* The variant picker's <select> elements (webshop core's own custom-dropdown
   markup, .input-with-feedback.form-control -- confirmed live via computed
   style) ship with border-width/border-style reset to 0/none, presumably so
   webshop's own chevron icon reads as the only affordance. portal_theme.css's
   general input rule only overrides border-color, which lands on a border
   that's already zero-width -- invisible regardless of color. Restate the
   full border here so the Input Field spec's 1px solid edge actually renders. */
html[data-theme$="-dark"] .modal select,
html[data-theme$="-light"] .modal select {
	border: 1px solid var(--border-color) !important;
}

html[data-theme$="-dark"] .modal .close,
html[data-theme$="-light"] .modal .close {
	color: var(--text-muted);
	opacity: 1;
	text-shadow: none;
}

html[data-theme$="-dark"] .modal .help-box a,
html[data-theme$="-dark"] .santek-variant-change,
html[data-theme$="-light"] .modal .help-box a,
html[data-theme$="-light"] .santek-variant-change {
	color: var(--santek-accent) !important;
}

html[data-theme$="-dark"] .santek-variant-lock-note,
html[data-theme$="-light"] .santek-variant-lock-note {
	color: var(--text-muted) !important;
}

/* Variant preview column (webshop_variant_selector.js's mockup/3D viewer,
   rendered only once a template has published Packaging Design mockups --
   not exercised by this app's own test catalog, styled from the class names
   directly rather than guessed at). Gets the same light-box glow as every
   other product image. */
.santek-variant-preview-col .card-img-top.no-image-item {
	position: relative;
	z-index: 1;
}

html[data-theme$="-dark"] .santek-variant-preview-col .card-img-top.no-image-item,
html[data-theme$="-light"] .santek-variant-preview-col .card-img-top.no-image-item {
	background: var(--fg-color);
	color: var(--text-muted);
	border-radius: 12px;
}

html[data-theme$="-dark"] .santek-mockup-viewer,
html[data-theme$="-light"] .santek-mockup-viewer {
	border-radius: 12px;
	overflow: hidden;
	background: var(--fg-color);
}

/* ---------------------------------------------------------------------------
   /cart. Every plain white .frappe-card box on this page (items table,
   payment summary, addresses, terms) gets the same glass-card treatment as
   everywhere else, rather than a cart-specific card shape. */
html[data-theme$="-dark"] .frappe-card,
html[data-theme$="-light"] .frappe-card {
	background: var(--santek-card-bg) !important;
	backdrop-filter: var(--santek-blur);
	-webkit-backdrop-filter: var(--santek-blur);
	border: 1px solid var(--santek-card-border) !important;
	border-radius: 20px !important;
	box-shadow:
		inset 0 1px 0 var(--santek-card-highlight),
		0 20px 50px -10px var(--santek-card-shadow);
}

html[data-theme$="-dark"] .cart-items-header,
html[data-theme$="-light"] .cart-items-header {
	color: var(--text-color);
	font-weight: 600;
	font-size: 1.1rem;
}

html[data-theme$="-dark"] .cart-table th,
html[data-theme$="-light"] .cart-table th {
	color: var(--text-muted);
	font-size: 0.75rem;
	text-transform: uppercase;
	letter-spacing: 0.05em;
	border-bottom: 1px solid var(--border-color) !important;
}

html[data-theme$="-dark"] .cart-table td,
html[data-theme$="-light"] .cart-table td {
	border-top: 1px solid var(--border-color) !important;
}

.cart-item-image {
	padding: 0.3rem;
}

html[data-theme$="-dark"] .no-image-cart-item,
html[data-theme$="-light"] .no-image-cart-item {
	position: relative;
	z-index: 1;
	background: var(--fg-color);
	color: var(--text-muted);
	border-radius: 10px;
	display: flex;
	align-items: center;
	justify-content: center;
	width: 100%;
	height: 100%;
	font-weight: 600;
}

html[data-theme$="-dark"] .item-title,
html[data-theme$="-light"] .item-title {
	color: var(--text-color);
	font-weight: 600;
}

html[data-theme$="-dark"] .item-subtitle,
html[data-theme$="-light"] .item-subtitle {
	color: var(--text-muted);
	font-size: 0.8rem;
}

/* Built by cart_item_labels.js from get_variant_attribute_summaries' structured
   [{attribute, value}] pairs -- requested live, 2026-08-23, after an in-browser
   preview: each attribute on its own line, label/value column-aligned across
   the row rather than run together as one comma-joined sentence. Theme-agnostic
   layout only (color/font-size already come from the .item-subtitle rule above,
   this class's own parent element) -- max-content columns size to the longest
   label/value actually present per item, not a fixed width. */
.cart-item-attribute-grid {
	display: grid;
	grid-template-columns: max-content max-content;
	column-gap: 8px;
	row-gap: 2px;
}

html[data-theme$="-dark"] .item-rate,
html[data-theme$="-light"] .item-rate {
	color: var(--text-muted);
	font-size: 0.8rem;
}

html[data-theme$="-dark"] .notes textarea,
html[data-theme$="-light"] .notes textarea {
	background: var(--santek-input-bg);
	border: 1px solid var(--border-color);
	color: var(--text-color);
	border-radius: 8px;
	scrollbar-color: var(--border-color) transparent;
}

/* Reported live: the native browser resize grip (bottom-right corner) and
   scrollbar stay OS-default chrome regardless of the rules above -- neither
   is a real CSS property this app's tokens ever reach, they need their own
   pseudo-elements. ::-webkit-resizer/-scrollbar-* are real, stylable in every
   Chromium/WebKit browser (unlike a native <select>'s own open dropdown,
   already documented elsewhere as genuinely out of CSS's reach) -- this isn't
   the same "OS chrome we can't touch" case, just one this app hadn't styled
   yet. Firefox has no equivalent for the resizer specifically; its own
   scrollbar-color/-width properties are added alongside for the same visual
   intent there. */
html[data-theme$="-dark"] .notes textarea::-webkit-resizer,
html[data-theme$="-light"] .notes textarea::-webkit-resizer {
	background-color: var(--santek-input-bg);
}

html[data-theme$="-dark"] .notes textarea::-webkit-scrollbar,
html[data-theme$="-light"] .notes textarea::-webkit-scrollbar {
	width: 8px;
}

html[data-theme$="-dark"] .notes textarea::-webkit-scrollbar-track,
html[data-theme$="-light"] .notes textarea::-webkit-scrollbar-track {
	background: transparent;
}

html[data-theme$="-dark"] .notes textarea::-webkit-scrollbar-thumb,
html[data-theme$="-light"] .notes textarea::-webkit-scrollbar-thumb {
	background-color: var(--border-color);
	border-radius: 999px;
}

/* Quantity stepper as a pill, same shape as .santek-list-filter-pill's own
   border-radius: 999px language rather than Bootstrap's plain input-group.
   !important throughout -- confirmed live that webshop's own compiled SCSS
   (webshop_cart.scss's .number-spinner .cart-btn { background: var(--gray-100) })
   wins this cascade otherwise: an opaque near-white rgb(243,243,243) endcap
   sitting inside an otherwise translucent glass pill, a real Ink-vs-Interactive
   violation, plus a flat 0-radius .cart-qty with no border at all (the same
   "core zeroes border-width, portal only overrides border-color" gap already
   fixed on the variant dialog's <select> elements above).

   Reported live: the "+" end's rounded cap looked much bigger/rounder than
   the "-" end's -- confirmed via getBoundingClientRect this was never a
   glyph or button-box asymmetry (both .cart-btn elements measured pixel-
   identical, twice, across two separate live reports). Root cause is core
   webshop's own webshop_cart.scss: `.number-spinner{width:75%;
   min-width:105px}` sizes this pill to a percentage of its ANCESTOR,
   completely unrelated to its own content's actual width. At this layout's
   real proportions 75% renders far wider than the stepper's own buttons +
   input need (~132px of real content in a 150px+ box) -- and since flex
   items default to flex-start, 100% of that leftover space piles up on the
   right (nothing sits after the "+" button to balance it), making the right
   cap look like a mostly-empty rounded blob next to the left cap's tight,
   flush fit. Fixed by sizing the pill to its own content instead of an
   unrelated ancestor's width. */
html[data-theme$="-dark"] .number-spinner,
html[data-theme$="-light"] .number-spinner {
	border: 1px solid var(--border-color) !important;
	border-radius: 999px !important;
	overflow: hidden;
	background: var(--santek-input-bg) !important;
	width: fit-content !important;
}

html[data-theme$="-dark"] .number-spinner .cart-btn,
html[data-theme$="-light"] .number-spinner .cart-btn {
	background: transparent !important;
	color: var(--text-color) !important;
	border: none !important;
	/* Touch target, 2026-08-22 audit -- up from core's 24x28, same reasoning as
	   .remove-cart-item's own sizing comment above. */
	width: 32px !important;
	height: 32px !important;
}

html[data-theme$="-dark"] .number-spinner .cart-qty,
html[data-theme$="-light"] .number-spinner .cart-qty {
	background: transparent !important;
	color: var(--text-color) !important;
	border: none !important;
	height: 32px !important;
}

/* .remove-cart-item itself (not just its icon below) was never actually themed --
   confirmed live it still carries core's opaque rgb(243,243,243) background/border
   (webshop_cart.scss's `background-color: var(--gray-100)`), same untouched-core-
   default class of bug already fixed on .cart-btn/.cart-qty above, just missed in
   that pass since this selector isn't an input/select/form-control the general
   rule in portal_theme.css ever reaches.
   Sizing bumped 28x28 -> 32px alongside the stepper below, both from the
   2026-08-22 audit's touch-target finding (measured live via
   getBoundingClientRect: .cart-btn 24x28, .remove-cart-item 28x28, both under the
   WCAG 2.5.8 AA 24x24 floor at least in one dimension, well under the 44x44 AAA
   target) -- kept modest rather than pushed all the way to 44 to avoid visually
   overweighting the compact stepper pill this session already fixed and
   live-verified once. */
html[data-theme$="-dark"] .remove-cart-item,
html[data-theme$="-light"] .remove-cart-item {
	background: var(--santek-input-bg) !important;
	border: 1px solid var(--border-color) !important;
	width: 32px !important;
	height: 32px !important;
	/* Reported live, 2026-08-24, right after the pointer:coarse 44px touch-
	   target bump: the X icon looked "distorted" -- confirmed via
	   getBoundingClientRect this was never actually centered by flex at all.
	   Core's own markup gives this element `.d-flex` (display:flex,
	   justify-content:center from webshop's own CSS) but no align-items, so
	   the cross-axis default ("normal", stretch-like with no effect on a
	   fixed-height SVG) left the icon pinned ~5px from the top regardless of
	   the button's own height -- invisible slop at 32px (5px top vs 6.8px
	   bottom, close enough to look centered), glaring at 44px (5px top vs
	   18.8px bottom, visibly floating near the top of a much taller box).
	   align-items: center fixes both sizes at once, not just the touch one. */
	align-items: center;
	/* Reported live: snapped directly against the stepper pill with no gap --
	   confirmed via getBoundingClientRect, 0px between .number-spinner's right
	   edge and this element's left edge. Both sit in the same plain .d-flex
	   row with no gap of their own (core markup, not something this app
	   controls), so margin on this element is the simplest fix without
	   touching .d-flex generically. */
	margin-left: 12px;
}

/* Second half of the same centering fix -- align-items: center above
   correctly centers the icon's <span> wrapper within the button (confirmed
   live, 0px difference top/bottom), but the <span> is a plain block element
   around an inline-block <svg> with vertical-align: middle, which aligns to
   text baseline/x-height, not true geometric center -- confirmed live the
   SVG still sat flush against the span's own bottom edge (0px gap below,
   4.36px above) regardless of the outer fix. Making the span itself a
   centering flex container closes that second, independent gap. */
html[data-theme$="-dark"] .remove-cart-item span,
html[data-theme$="-light"] .remove-cart-item span {
	display: flex;
	align-items: center;
	justify-content: center;
}

/* Reported live as invisible on dark theme. `color` alone does nothing here --
   the sprite's own <path> carries no fill attribute at all, so it renders at
   SVG's implicit default (black) rather than inheriting `color` via
   currentColor -- confirmed live via getComputedStyle: color correctly read
   --text-muted, but fill still reported a hardcoded rgb(82,82,82) from
   somewhere else entirely, ignoring color. Same bug class already fixed once
   this session for .navbar-toggler svg and the home-dashboard card icons.
   fill: currentColor plus its own color declaration (not just color alone)
   is what actually reaches the rendered pixels.

   A second round was needed even with !important: core's own
   webshop_cart.scss ships `#page-cart .cart-container .cart-table
   .remove-cart-item-logo { fill: var(--gray-700) !important; }` -- also
   !important, and its (1,3,0) specificity (one ID, three classes) beat this
   rule's plain (0,3,1) even though both carry !important, since among
   competing !important declarations normal specificity still decides the
   winner. #page-cart is real and stable (core's own cart page wrapper,
   confirmed live) -- adding it plus .cart-table, one more real ancestor
   class from that same chain, brings this rule to (1,3,1): ties on classes,
   wins on the extra html type selector core's own version doesn't have. Not
   redefining --gray-700 itself instead -- that's a generic Bootstrap-style
   gray this app doesn't otherwise theme, likely reused in places well
   outside this one icon, too broad a change for a single-element fix. */
html[data-theme$="-dark"] #page-cart .cart-table .remove-cart-item-logo,
html[data-theme$="-light"] #page-cart .cart-table .remove-cart-item-logo {
	color: var(--text-muted);
	fill: currentColor !important;
}

html[data-theme$="-dark"] .remove-cart-item:hover .remove-cart-item-logo,
html[data-theme$="-light"] .remove-cart-item:hover .remove-cart-item-logo {
	color: var(--red-500, #c62828);
}

/* Reported live: on narrow viewports there is no way to discard a cart item at
   all -- confirmed via getComputedStyle, core's own webshop-web.bundle.css
   ships `#page-cart .cart-container .cart-table .column-sm-view { display:
   none !important; }` under `@media (max-width: 992px)`. That class is meant
   to hide the *Subtotal* column on narrow screens, which has a real mobile
   replacement two rows down (.sm-item-subtotal) -- but .remove-cart-item also
   carries .column-sm-view (presumably to keep it out of the way of the
   subtotal column on desktop's row layout) and has no such replacement, so it
   silently disappears with nothing to take over its job. Scoped to
   `.remove-cart-item.column-sm-view` specifically, not a blanket override of
   .column-sm-view, so the Subtotal column (which should stay hidden here) is
   untouched. Matches core's own breakpoint rather than picking a new one. */
@media (max-width: 992px) {
	html[data-theme$="-dark"] #page-cart .cart-container .cart-table .remove-cart-item.column-sm-view,
	html[data-theme$="-light"] #page-cart .cart-container .cart-table .remove-cart-item.column-sm-view {
		display: flex !important;
	}
}

html[data-theme$="-dark"] .bill-label,
html[data-theme$="-dark"] .bill-content,
html[data-theme$="-light"] .bill-label,
html[data-theme$="-light"] .bill-content {
	color: var(--text-muted);
	border: none !important;
}

html[data-theme$="-dark"] .grand-total .bill-content,
html[data-theme$="-light"] .grand-total .bill-content {
	color: var(--text-color);
	font-weight: 700;
	font-size: 1.15rem;
}

html[data-theme$="-dark"] .txtcoupon,
html[data-theme$="-light"] .txtcoupon {
	background: var(--santek-input-bg);
	border: 1px solid var(--border-color);
	color: var(--text-color);
	border-radius: 999px;
}

html[data-theme$="-dark"] .address-header h6,
html[data-theme$="-light"] .address-header h6 {
	color: var(--text-color);
}

/* Reported live as barely visible on dark theme -- never themed at all before
   this, core webshop's own near-black rgb(23,23,23) default (confirmed via
   getComputedStyle) against a dark navy card. Accent color, not plain
   --text-color, since this is a real action link (opens the new-address
   form) rather than a heading/label -- matches how every other clickable
   action in this file (Clear All, past-quotes-style links) already reads. */
html[data-theme$="-dark"] .btn-new-address,
html[data-theme$="-light"] .btn-new-address {
	color: var(--santek-accent) !important;
}

html[data-theme$="-dark"] .address-card,
html[data-theme$="-light"] .address-card {
	background: var(--fg-color);
	border: 1px solid var(--border-color);
	border-radius: 12px;
	color: var(--text-color);
}

html[data-theme$="-dark"] .address-card.active,
html[data-theme$="-light"] .address-card.active {
	border-color: var(--santek-accent);
}

html[data-theme$="-dark"] .t-and-c-container h5,
html[data-theme$="-light"] .t-and-c-container h5 {
	color: var(--text-color);
}

html[data-theme$="-dark"] .t-and-c-terms,
html[data-theme$="-light"] .t-and-c-terms {
	color: var(--text-muted);
}

/* ---------------------------------------------------------------------------
   Signature "buy" action -- the accent glow, used consistently for every
   product-purchase verb across the whole shop (Explore, Add to Cart/Quote, Go
   to Cart/Quote, Select Variant, Place Order, Request for Quote). Deliberately
   the only saturated, glowing surface in this whole file -- the product photo
   glow above is the other half of the same "light" idea, everything else stays
   quiet so these two don't have to compete with a third accent treatment. */
html[data-theme$="-dark"] .btn-explore-variants,
html[data-theme$="-dark"] .btn-add-to-cart,
html[data-theme$="-dark"] .btn-add-to-cart-list,
html[data-theme$="-dark"] .btn-configure,
html[data-theme$="-dark"] .btn-place-order,
html[data-theme$="-dark"] .btn-request-for-quotation,
html[data-theme$="-light"] .btn-explore-variants,
html[data-theme$="-light"] .btn-add-to-cart,
html[data-theme$="-light"] .btn-add-to-cart-list,
html[data-theme$="-light"] .btn-configure,
html[data-theme$="-light"] .btn-place-order,
html[data-theme$="-light"] .btn-request-for-quotation {
	background: linear-gradient(135deg, var(--santek-accent), color-mix(in srgb, var(--santek-accent) 65%, var(--santek-teal)));
	border: none;
	color: #fff;
	border-radius: 999px;
	font-weight: 600;
	box-shadow:
		inset 0 1px 0 rgba(255, 255, 255, 0.3),
		0 10px 24px -8px var(--santek-glow-strong);
	transition:
		transform 0.15s ease,
		box-shadow 0.15s ease;
}

html[data-theme$="-dark"] .btn-explore-variants:hover,
html[data-theme$="-dark"] .btn-add-to-cart:hover,
html[data-theme$="-dark"] .btn-add-to-cart-list:hover,
html[data-theme$="-dark"] .btn-configure:hover,
html[data-theme$="-dark"] .btn-place-order:hover,
html[data-theme$="-dark"] .btn-request-for-quotation:hover,
html[data-theme$="-light"] .btn-explore-variants:hover,
html[data-theme$="-light"] .btn-add-to-cart:hover,
html[data-theme$="-light"] .btn-add-to-cart-list:hover,
html[data-theme$="-light"] .btn-configure:hover,
html[data-theme$="-light"] .btn-place-order:hover,
html[data-theme$="-light"] .btn-request-for-quotation:hover {
	color: #fff;
	transform: translateY(-2px);
	box-shadow:
		inset 0 1px 0 rgba(255, 255, 255, 0.35),
		0 14px 30px -8px var(--santek-glow-strong);
}

/* Secondary cart actions (View in Cart/Quote, Continue Shopping, Past
   Orders/Quotes) -- the input-bg treatment already established for secondary
   buttons elsewhere (page-header-actions-block .btn-secondary), not the glow. */
html[data-theme$="-dark"] .btn-primary-light,
html[data-theme$="-dark"] .btn-view-in-cart,
html[data-theme$="-light"] .btn-primary-light,
html[data-theme$="-light"] .btn-view-in-cart {
	background: var(--santek-input-bg);
	border: 1px solid var(--santek-card-border);
	color: var(--text-color);
	border-radius: 999px;
}

html[data-theme$="-dark"] .btn-primary-light:hover,
html[data-theme$="-dark"] .btn-view-in-cart:hover,
html[data-theme$="-light"] .btn-primary-light:hover,
html[data-theme$="-light"] .btn-view-in-cart:hover {
	background: var(--fg-color);
	color: var(--text-color);
}

/* Reduced-motion pass, 2026-08-22 audit -- same reasoning as portal_theme.css's
   own reduced-motion block: neutralizes only the `transform` half of each
   hover-lift rule above (grid tiles, list rows, and the buy-button family),
   never the `transition-duration` broadly. Deliberately leaves the buy-button
   hover's color/box-shadow glow strengthening untouched -- that's real state
   feedback (a still-interactive, now-hovered control), not motion, and removing
   it would make hover feel broken rather than calmer. */
@media (prefers-reduced-motion: reduce) {
	html[data-theme$="-dark"] .item-card .card:hover,
	html[data-theme$="-light"] .item-card .card:hover,
	html[data-theme$="-dark"] .list-row:hover,
	html[data-theme$="-light"] .list-row:hover,
	html[data-theme$="-dark"] .btn-add-to-cart:hover,
	html[data-theme$="-dark"] .btn-add-to-cart-list:hover,
	html[data-theme$="-dark"] .btn-configure:hover,
	html[data-theme$="-dark"] .btn-place-order:hover,
	html[data-theme$="-dark"] .btn-request-for-quotation:hover,
	html[data-theme$="-light"] .btn-add-to-cart:hover,
	html[data-theme$="-light"] .btn-add-to-cart-list:hover,
	html[data-theme$="-light"] .btn-configure:hover,
	html[data-theme$="-light"] .btn-place-order:hover,
	html[data-theme$="-light"] .btn-request-for-quotation:hover {
		transform: none !important;
	}
}

/* ---------------------------------------------------------------------------
   Mobile adaptation pass, 2026-08-22 /impeccable adapt -- three real gaps
   found using a genuinely narrow (390px) iframe viewport, since this
   environment's own browser-resize tool doesn't actually change the page's
   viewport (confirmed twice). All three are core webshop's own markup, never
   given a narrow-width treatment at all -- not new coverage this app removed. */

/* 1. Filters sidebar toggle (webshop_mobile_filters.js adds the button this
   styles). #product-filters ships `collapse d-md-block` with zero trigger
   anywhere in core webshop -- confirmed via a repo-wide grep -- so the whole
   filter sidebar was simply unreachable below the md breakpoint. The toggle
   button itself uses the same secondary/ghost button language as
   .btn-primary-light elsewhere in this file; deliberately NOT rendered at
   md+ (d-md-none), where the sidebar is already always visible. */
html[data-theme$="-dark"] .santek-mobile-filters-toggle,
html[data-theme$="-light"] .santek-mobile-filters-toggle {
	background: var(--santek-input-bg);
	border: 1px solid var(--santek-card-border);
	color: var(--text-color);
	border-radius: 999px;
	padding: 8px 20px;
	margin-bottom: 1rem;
	width: 100%;
}

/* Off-canvas drawer, matching the site's own mobile nav drawer
   (portal_theme.css's #navbarSupportedContent block) -- requested directly,
   2026-08-24, after the original inline expand-in-place (push the product grid
   down) read as visually inconsistent with the hamburger menu's slide-in
   takeover on the same page. Same shape throughout: position:fixed panel below
   the navbar, transform:translateX slide, visibility timed to the transform's
   own transition so the closed panel is unreachable/unreadable rather than
   just invisible, a dimming ::after scrim via :has(), and body scroll locked
   while open. display:block!important is still required here for the same
   reason the original toggle needed it -- #product-filters ships core's own
   `collapse d-md-block` classes, and Bootstrap's `.collapse:not(.show)` rule
   sets display:none with no !important of its own to out-specificity; since
   this toggle never adds Bootstrap's own `.show` class (santek-filters-open is
   a plain custom class, not wired to Bootstrap's collapse JS -- see
   webshop_mobile_filters.js's own header comment on why), that selector would
   otherwise always match and keep the panel display:none regardless of
   transform/visibility. mobile_nav_scroll_lock.js watches this element's own
   class list the same way it watches the nav drawer's. */
@media (max-width: 767px) {
	html[data-theme$="-dark"] #product-filters,
	html[data-theme$="-light"] #product-filters {
		display: block !important;
		position: fixed;
		top: 44px;
		left: 0;
		height: calc(100vh - 44px);
		height: calc(100dvh - 44px);
		width: 100vw;
		width: 100dvw;
		max-width: 100%;
		margin: 0;
		padding: 20px;
		overflow-y: auto;
		background: var(--santek-mobile-menu-bg);
		transform: translateX(-100%);
		visibility: hidden;
		transition:
			transform 0.3s ease,
			visibility 0s linear 0.3s;
		z-index: 1045;
		isolation: isolate;
		will-change: transform;
	}

	html[data-theme$="-dark"] #product-filters.santek-filters-open,
	html[data-theme$="-light"] #product-filters.santek-filters-open {
		transform: translateX(0);
		visibility: visible;
		transition: transform 0.3s ease;
	}

	body:has(#product-filters.santek-filters-open)::after {
		content: "";
		position: fixed;
		inset: 0;
		background: rgba(0, 0, 0, 0.45);
		z-index: 1040;
	}

	body:has(#product-filters.santek-filters-open) {
		overflow: hidden;
	}

	/* Sticky close bar, first child of #product-filters (webshop_mobile_filters.js).
	   Negative margins equal to the panel's own 20px padding above pull it flush
	   to the drawer's real edges, and `top: -20px` (not 0) compensates for that
	   same padding so the sticky element actually reaches the scroll container's
	   top the instant it's scrolled to, rather than stopping 20px short of it --
	   the standard "full-bleed sticky header inside a padded scroll area" shape.
	   Pinned to the drawer's own scroll position, not the page's -- see this
	   button's own JS-side header comment for why the external toggle above
	   #product-filters can't be relied on as the close control once the drawer
	   is fixed/full-screen. */
	html[data-theme$="-dark"] .santek-mobile-filters-close,
	html[data-theme$="-light"] .santek-mobile-filters-close {
		display: flex;
		align-items: center;
		justify-content: center;
		position: sticky;
		top: -20px;
		margin: -20px -20px 16px;
		padding: 12px 20px;
		width: calc(100% + 40px);
		background: var(--santek-mobile-menu-bg);
		border: none;
		border-bottom: 1px solid var(--santek-card-border);
		color: var(--text-color);
		font-weight: 600;
		z-index: 2;
	}
}

/* 2. Cart line items don't stack on narrow screens -- confirmed live the
   image+title flex row (webshop's own cart_items.html, a bare `.d-flex` with
   no responsive variant) squeezes the title/attribute-summary column to a
   sliver once the viewport gets phone-narrow, wrapping one or two words per
   line. Scoped to `td:first-child > .d-flex` specifically, not `.cart-items
   .d-flex` broadly -- the quantity column (a sibling <td>) uses the exact
   same bare `.d-flex` class for its stepper controls, which must stay a row
   regardless of width. */
@media (max-width: 576px) {
	.cart-items td:first-child > .d-flex {
		flex-direction: column;
	}

	.cart-items .cart-item-image {
		margin-right: 0 !important;
		margin-bottom: 12px;
		max-width: 140px;
	}

	/* Reported live on a real phone, 2026-08-24: cart rows spilled past their
	   own card's right edge -- the quantity stepper's number, the subtotal, and
	   the Rate line all rendered outside the visible frame. Root cause,
	   confirmed via getBoundingClientRect: .cart-table has no width constraint
	   of its own (`table-layout: auto`, the browser default), so with
	   table-layout:auto a <table> sizes to its content's natural,
	   un-shrunk width rather than respecting its parent -- it grows past the
	   card instead of compressing. The flex-direction:column rule two rules up
	   only reflows content INSIDE the item cell; it can't stop the table
	   itself from overflowing, since .number-spinner's own core-set
	   min-width: 105px (confirmed earlier this session, see the pill-width
	   fix's own comment above) plus the remove button and subtotal text in the
	   second cell already needs more room than a real phone's card has left
	   once the first cell takes its own share.

	   Fixed by dropping the two-cell side-by-side row entirely below this same
	   breakpoint the image/title stacking above already uses -- each <td>
	   becomes its own full-width block, stacked vertically, so the stepper/
	   remove/subtotal cell gets the *entire* card width to lay out in instead
	   of splitting it with the item-info cell. `border-top` (the existing
	   per-row divider, .cart-table td above) is suppressed on every cell after
	   the first within one row -- each `<tr>` still gets exactly one divider
	   line, at its own top edge, rather than a second one appearing between
	   the now-stacked cells and reading as two separate rows. thead is hidden
	   outright -- "ITEM"/"QUANTITY" column labels are meaningless once nothing
	   is actually in columns. */
	#page-cart .cart-table thead {
		display: none;
	}

	#page-cart .cart-table,
	#page-cart .cart-table tbody,
	#page-cart .cart-table tr {
		display: block;
		width: 100%;
	}

	#page-cart .cart-table tr {
		margin-bottom: 1.25rem;
	}

	#page-cart .cart-table tr:last-child {
		margin-bottom: 0;
	}

	#page-cart .cart-table td {
		display: block;
		width: 100% !important;
	}

	html[data-theme$="-dark"] #page-cart .cart-table td + td,
	html[data-theme$="-light"] #page-cart .cart-table td + td {
		border-top: none !important;
		margin-top: 12px;
	}

	/* Second real overflow source found in the same live check: the attribute
	   grid's own `max-content max-content` columns (see .cart-item-attribute-grid
	   above) size to a long value's natural, un-wrapped width regardless of
	   available space -- confirmed live via getBoundingClientRect, the value
	   column's own right edge landed ~37px past the viewport on a long
	   Compliance/Container-Type value even after the row-stacking fix above
	   gave the item cell the full card width. minmax(0, 1fr) keeps the label
	   column sized to its own content (labels are short and consistent, e.g.
	   "Wattage:") while letting the value column shrink to whatever's actually
	   left and wrap instead of forcing the grid wider than its container. */
	.cart-item-attribute-grid {
		grid-template-columns: max-content minmax(0, 1fr);
	}

	.cart-item-attribute-grid > div:nth-child(even) {
		overflow-wrap: break-word;
	}
}

/* ---------------------------------------------------------------------------
   /cart adaptation pass, 2026-08-24 /impeccable adapt -- two more real gaps,
   found by measuring real getBoundingClientRect/getComputedStyle values at
   live window widths this environment's resize tool actually reached this
   session (roughly 500-1450px reliably; a true <500px iframe test was tried
   again, same as the 2026-08-22 pass's own note, and blocked the same way --
   dev.portal refuses to be framed cross-origin, X-Frame-Options/CSP). Below
   576px is already covered by item #2 above; these two are new.

   4. Real, reproduced overflow: core webshop's `#page-cart` markup puts
      Items/Payment-Addresses in Bootstrap's own `.col-md-8`/`.col-md-4`
      (flex: 0 0 66.6667%/33.3333%), which starts the two-column split at
      768px -- a generic Bootstrap default, never tuned to this page's own
      content. Confirmed live at a real 836px window: Items shrinks to a
      504.5px card, but the quantity column's own fixed-width content
      (105px stepper + 12px gap + 32px remove button, all already themed
      earlier this session) needs 149px minimum and won't compress --
      forcing `.cart-table` to render 532px wide, wider than its own
      504.5px card, so .remove-cart-item's right edge (561px) spilled 40px
      past the card's own right edge (521px). This is a content-driven
      threshold problem, not a device one: the fix is to not start the
      side-by-side split until there is actually enough room for it, which
      conveniently is the same real breakpoint core webshop's own
      `column-sm-view` already uses (992px, hides the Subtotal column) --
      unifying two previously uncoordinated breakpoints into one. Scoped to
      #page-cart specifically since Bootstrap's plain .col-md-8/.col-md-4
      are reused site-wide for layouts that don't have this problem. */
@media (min-width: 768px) and (max-width: 991.98px) {
	#page-cart .cart-container .col-md-8,
	#page-cart .cart-container .col-md-4 {
		flex: 0 0 100%;
		max-width: 100%;
	}
}

/* 5. Touch targets sized for a mouse, not a finger. The quantity stepper
   and remove buttons are 32x32 (bumped from core's 24x28/28x28 by the
   2026-08-22 audit) -- deliberately kept under the 44x44 AAA target at the
   time to avoid overweighting the stepper pill on a mouse-driven desktop
   view. adapt.md's own guidance is to size by input method, not screen
   width (`@media (pointer: coarse)`), which this app hadn't used anywhere
   yet -- confirmed via a repo-wide grep before adding it. Only touches
   coarse-pointer (touch) sessions; a desktop mouse/trackpad session is
   completely unaffected regardless of window width.

   Reported live right after shipping: the +/- buttons' shape looked broken
   on a real touch device. Root cause -- `.number-spinner` (the pill wrapper)
   has `overflow: hidden` and its own height is only ever implicitly set by
   its 32px children; bumping just the buttons to 44px let the pill's own
   shorter box clip them, squashing the circle into a flattened shape rather
   than actually growing the touch target. Fixed by growing the pill's own
   height to match, plus `.cart-qty` (the number input between the two
   buttons, separately fixed at height:32px !important above) -- otherwise
   the input would sit visibly shorter than its own now-44px neighbors
   inside the same pill. */
@media (pointer: coarse) {
	html[data-theme$="-dark"] #page-cart .number-spinner,
	html[data-theme$="-light"] #page-cart .number-spinner {
		height: 44px !important;
	}

	html[data-theme$="-dark"] #page-cart .number-spinner .cart-btn,
	html[data-theme$="-light"] #page-cart .number-spinner .cart-btn,
	html[data-theme$="-dark"] #page-cart .remove-cart-item,
	html[data-theme$="-light"] #page-cart .remove-cart-item {
		width: 44px !important;
		height: 44px !important;
	}

	html[data-theme$="-dark"] #page-cart .number-spinner .cart-qty,
	html[data-theme$="-light"] #page-cart .number-spinner .cart-qty {
		height: 44px !important;
	}
}

/* 3. "Select Variant" dialog's matched-item alert row (webshop's own
   item_configure.js: `.alert.d-flex.justify-content-between`, the green
   item-code/price pill + "Clear Values" link) never wraps -- confirmed live
   at phone width the item-code text and the link crowd/overlap instead of
   the link dropping to its own line. flex-wrap is safe at any width (no
   visual change once both halves already fit on one line). */
.modal .alert.d-flex {
	flex-wrap: wrap;
	gap: 0.5rem;
}
