/* assets/css/00-tokens.css */
/* ==========================================================================
   Sulitista design tokens. Three theme states (see "Sulitista Brand Guide"):
   bare :root = light (the un-stamped default), a prefers-color-scheme block
   guarded so an explicit light choice wins, and a [data-theme] block so the
   toggle wins both directions. Every colour is defined in :root first.
   WCAG 2.1 AA verified.
   ========================================================================== */

:root {
	color-scheme: light;

	/* -- palette: light -- */
	--sul-ground:        #FFFAF7;
	--sul-surface:       #FFFFFF;
	--sul-surface-2:     #FFF3EE;
	--sul-ink:           #2A1F26;
	--sul-muted:         #7A6B72;
	--sul-border:        #F0DFD9;
	--sul-field-border:  #D3B8AF;

	--sul-guava:         #EB5C7A;   /* decoration / large display only */
	--sul-guava-strong:  #CE3358;   /* actionable text, links */
	--sul-marigold:      #F2A93C;
	--sul-marigold-ink:  #7A4B10;
	--sul-calamansi:     #3F9D6E;
	--sul-calamansi-ink: #1F5E3F;
	/* Solid fill for white-text green badges (the carousel discount badge) -
	   4.52:1 against #fff. Deliberately NOT redefined in the dark states: an
	   opaque mid-green with white text reads the same in both themes. */
	--sul-calamansi-fill: #33855C;
	--sul-sili:          #C6413F;

	/* Edit-mode pencil chip (storefront inline editing, 17-inline-edit.css):
	   a dark chip with a white pencil inside a white ring, so it reads as tool
	   chrome rather than store content on a photo, the cream page or the dark
	   theme alike. Deliberately NOT redefined in the dark states: the ring is
	   what separates it from the page. To restyle every edit pencil at once,
	   change these four. */
	--sul-edit-chip:       #2A1F26;
	--sul-edit-chip-hover: #46363F;
	--sul-edit-chip-fg:    #FFFFFF;
	--sul-edit-chip-ring:  #FFFFFF;

	--sul-btn-bg:        #CE3358;
	--sul-btn-fg:        #FFFFFF;
	--sul-btn-bg-hover:  #B0264A;

	/* Where the header's vertical gradient ends, at its bottom edge (06-header-footer.css):
	   the page colour in light; in dark a step above the page, so the header still reads as
	   a bar. The top of the gradient is the header tint (--sul-header-bg, #F4E8E4 in light). */
	--sul-header-end:    var(--sul-ground);
	/* The About page's closing band (2026-10-16, Joefer: solid guava strong): the button fill and its label from
	   the Brand Guide (white on #CE3358 is 4.95:1; in the dark theme #2A1016 on #F27389 is 6.4:1). */
	--sul-band-bg:       #CE3358;
	--sul-band-fg:       #FFFFFF;

	--sul-shadow:        0 4px 22px -8px rgba(42, 31, 38, .22);
	--sul-shadow-sm:     0 1px 3px rgba(42, 31, 38, .10);

	/* transparent - not a color match at all, after two rounds of chasing
	   the "right" token to alias (first --sul-surface, then --sul-ground)
	   for a canvas used in three different contexts (the detail page's
	   plain cream background, two different cards' white/dark surface
	   background) that don't all share one correct answer anyway.
	   Transparent sidesteps the whole problem: whatever's actually behind
	   this element shows through correctly, in any context, in either
	   theme, permanently, with nothing here to ever re-break if a
	   surrounding background changes again. Product photos (phone-shot,
	   wildly inconsistent aspect ratios) sit on this via object-fit:
	   contain rather than being cropped to fill their box - the "canvas"
	   this token names is now purely the aspect-ratio box object-fit needs
	   to contain within, not a visible surface. Briefly set to a literal
	   #FFFFFF (2026-09-14) purely so the canvas was visible for a live
	   visual-inspection round after the carousel/layout work landed -
	   reverted back here once that review wrapped, per direct request. */
	--sul-image-bg: transparent;

	/* -- type -- */
	--sul-font-display: "Bricolage Grotesque", "Trebuchet MS", Verdana, sans-serif;
	--sul-font-body:    "Hanken Grotesk", system-ui, -apple-system, "Segoe UI", sans-serif;

	--sul-fs-display: clamp(2.2rem, 6vw, 3.6rem);
	--sul-fs-h1:      clamp(1.7rem, 4vw, 2rem);
	--sul-fs-h2:      1.35rem;
	--sul-fs-h3:      1.0625rem;
	--sul-fs-body:    1.0625rem;
	--sul-fs-sm:      0.9rem;
	--sul-fs-label:   0.72rem;

	/* -- shape & space -- */
	--sul-radius-sm:   10px;
	--sul-radius:      16px;
	--sul-radius-pill: 999px;

	--sul-space-1: 0.25rem;
	--sul-space-2: 0.5rem;
	--sul-space-3: 0.75rem;
	--sul-space-4: 1rem;
	--sul-space-6: 1.5rem;
	--sul-space-8: 2rem;
	--sul-space-12: 3rem;

	--sul-container: 1080px;
}

@media (prefers-color-scheme: dark) {
	:root:not([data-theme="light"]) {
		color-scheme: dark;

		--sul-ground:        #1E151A;
		--sul-surface:       #2A1F25;
		--sul-surface-2:     #34262E;
		--sul-ink:           #F6EAE4;
		--sul-muted:         #B99FAA;
		--sul-border:        #3E2E37;
		--sul-field-border:  #5C4852;

		--sul-guava:         #F27389;
		--sul-guava-strong:  #F79AAC;
		--sul-marigold:      #F3B65C;
		--sul-marigold-ink:  #F3B65C;
		--sul-calamansi:     #58B98C;
		--sul-calamansi-ink: #86D3AC;
		--sul-sili:          #E67B72;

		--sul-btn-bg:        #F27389;
		--sul-btn-fg:        #2A1016;
		--sul-btn-bg-hover:  #F79AAC;
		--sul-header-end:    #2D2128;
		--sul-band-bg:       #F27389;
		--sul-band-fg:       #2A1016;

		--sul-shadow:        0 6px 26px -8px rgba(0, 0, 0, .55);
		--sul-shadow-sm:     0 1px 3px rgba(0, 0, 0, .4);
	}
}

:root[data-theme="dark"] {
	color-scheme: dark;

	--sul-ground:        #1E151A;
	--sul-surface:       #2A1F25;
	--sul-surface-2:     #34262E;
	--sul-ink:           #F6EAE4;
	--sul-muted:         #B99FAA;
	--sul-border:        #3E2E37;
	--sul-field-border:  #5C4852;

	--sul-guava:         #F27389;
	--sul-guava-strong:  #F79AAC;
	--sul-marigold:      #F3B65C;
	--sul-marigold-ink:  #F3B65C;
	--sul-calamansi:     #58B98C;
	--sul-calamansi-ink: #86D3AC;
	--sul-sili:          #E67B72;

	--sul-btn-bg:        #F27389;
	--sul-btn-fg:        #2A1016;
	--sul-btn-bg-hover:  #F79AAC;
	--sul-header-end:    #2D2128;
	--sul-band-bg:       #F27389;
	--sul-band-fg:       #2A1016;

	--sul-shadow:        0 6px 26px -8px rgba(0, 0, 0, .55);
	--sul-shadow-sm:     0 1px 3px rgba(0, 0, 0, .4);
}

/* assets/css/01-base.css */
/* Reset-ish base. */

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

html {
	-webkit-text-size-adjust: 100%;
	/* Keeps an in-page anchor jump (e.g. the listing footnote's link to
	   /community-guidelines/#buyer-safety) from landing a heading behind
	   the sticky .sul-header (06-header-footer.css: 64px min-height + 1px
	   border ≈ 65px) — 80px clears it with a little breathing room.
	   Update this if that header's height ever changes. Since the two-row
	   header (2026-10-14) the sticky part is row 2 only, still 64px on a wide
	   screen; on a phone only its search line stays pinned (about 60px). */
	scroll-padding-top: 80px;
}

body {
	margin: 0;
	background: var(--sul-ground);
	color: var(--sul-ink);
	font-family: var(--sul-font-body);
	font-size: var(--sul-fs-body);
	line-height: 1.6;
	-webkit-font-smoothing: antialiased;
	text-rendering: optimizeLegibility;
	/* Sticky-footer layout (2026-09-25, direct request - the footer sat
	   wherever a short page's content happened to end, leaving genuine
	   empty space below it on any page shorter than the viewport, worst
	   on a very tall/high-res screen). .sul-header (position: sticky) and
	   .sul-footer size to their own content as ordinary flex items;
	   .sul-main (03-layout.css) gets flex: 1 0 auto to absorb whatever
	   space is left, pushing the footer to the true bottom of the
	   viewport on a short page while staying a normal flow layout (not
	   position: fixed) on a page tall enough to scroll. */
	display: flex;
	flex-direction: column;
	min-height: 100vh;
}

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

a { color: var(--sul-guava-strong); text-underline-offset: 0.15em; }
a:hover { text-decoration-thickness: 2px; }

:focus-visible {
	outline: 2px solid var(--sul-ink);
	outline-offset: 2px;
	border-radius: 2px;
}

::selection { background: var(--sul-guava); color: #fff; }

hr { border: 0; border-top: 1px solid var(--sul-border); margin: var(--sul-space-8) 0; }

.sul-skip-link {
	position: absolute;
	left: -9999px;
	top: 0;
	background: var(--sul-surface);
	color: var(--sul-ink);
	padding: var(--sul-space-3) var(--sul-space-4);
	z-index: 1000;
	border-radius: 0 0 var(--sul-radius-sm) 0;
}
.sul-skip-link:focus { left: 0; }

@media (prefers-reduced-motion: reduce) {
	*, *::before, *::after {
		animation-duration: 0.01ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: 0.01ms !important;
		scroll-behavior: auto !important;
	}
}

/* The page never drags sideways (2026-10-16, Joefer: "I can drag the page slightly left and right" on a phone). The
   cause on the About page was its animated cursor, which glides past the right edge of a narrow screen; anything
   that pokes past an edge is now clipped instead of making the page wider. `clip`, not `hidden`: it makes no scroll
   container, so the sticky header keeps working. Browsers without `clip` simply keep the old behaviour. This is a
   safety net: a real overflow is still a bug to fix at its source (find it by measuring, not by this rule). */
@supports (overflow: clip) {
	html, body { overflow-x: clip; }
}

/* assets/css/02-typography.css */
/* Type. @font-face rules for the self-hosted woff2 live in fonts-local.css,
   which functions.php enqueues only once bin/fetch-fonts.sh has produced the
   files. Until then the Google Fonts stylesheet supplies the same families. */

h1, h2, h3, h4 {
	font-family: var(--sul-font-display);
	font-weight: 700;
	line-height: 1.12;
	letter-spacing: -0.02em;
	text-wrap: balance;
	margin: 0 0 var(--sul-space-4);
}

h1, .sul-page-title { font-size: var(--sul-fs-h1); }
h2 { font-size: var(--sul-fs-h2); }
h3 { font-size: var(--sul-fs-h3); font-weight: 600; }

p { margin: 0 0 var(--sul-space-4); }

.sul-eyebrow {
	font-family: var(--sul-font-body);
	font-weight: 600;
	font-size: var(--sul-fs-label);
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--sul-guava-strong);
	margin: 0 0 var(--sul-space-3);
}
/* single-sulitista_listing.php's store attribution line ("Sold by X") -
   moved below the page title and off .sul-eyebrow entirely (direct
   feedback: the bold guava-pink eyebrow treatment, sized for a breadcrumb
   sitting above the title, overpowered a line whose job is just quiet
   attribution after the product itself has already led). Ink-colored
   link, not guava - this isn't a call to action, and the seller's own
   store name (echoed verbatim) shouldn't be re-cased or re-colored the
   way OUR section labels are. */
/* Reverted back to this plain line (2026-09-23) - a fuller Shopee-style
   strip was tried here first, right under the title, then asked to move
   entirely: a real raised seller CARD after the description instead
   (.sul-listing__seller-card below), not a replacement for this line -
   both exist now, different jobs. */
.sul-listing__sold-by {
	font-size: var(--sul-fs-sm);
	color: var(--sul-muted);
	margin: 0 0 var(--sul-space-4);
	/* .sul-listing-layout__identity is a flex column with its own
	   space-6 gap between every child (08-storefront.css) - on top of
	   the title's own space-4 bottom margin, that put ~40px of empty
	   space between the title and this one small, quiet line, reading
	   as orphaned/disconnected rather than attached to the title it's
	   actually about (direct feedback: "so lonely, too far from the
	   other elements"). Pulls it up so the visible gap reads closer to
	   space-2 instead, without touching the (correct, untouched) gap
	   the flex column still gives everything else. */
	margin-top: calc(var(--sul-space-2) - var(--sul-space-4) - var(--sul-space-6));
}
.sul-listing__sold-by a { color: var(--sul-ink); font-weight: 600; text-decoration: none; }
.sul-listing__sold-by a:hover { text-decoration: underline; }

.sul-lede { font-size: 1.15rem; color: var(--sul-muted); }
.sul-muted { color: var(--sul-muted); }

/* Short-form UI copy (a screen's intro sentence, an inline explainer) that
   sits inside a WIDE container — the dashboard uses the base .sul-container
   (1080px, --sul-container), not --md's 760px .sul-prose gets - left
   uncapped, one or two sentences stretch edge to edge and read badly. This
   is NOT the --sul-measure cap removed from .sul-prose above (2026-09-14,
   long-form article/policy body copy, already living in a 760px container,
   compared unfavorably narrow against real reference pages) - a completely
   different content shape in a completely different container, not the
   same decision reopened. Replaces the inline style="max-width:NNch" every
   dashboard screen kept re-inventing its own number for (52/62/70ch, each
   guessed independently) with one consistent value. */
.sul-muted--measure { max-width: 48ch; }

.sul-wordmark {
	font-family: var(--sul-font-display);
	font-weight: 700;
	font-size: 1.55rem;
	letter-spacing: -0.03em;
	color: var(--sul-ink);
	text-decoration: none;
	white-space: nowrap;
}
.sul-wordmark--sm { font-size: 1.2rem; }
.sul-wordmark__dot { color: var(--sul-guava); }

/* No max-width of its own (2026-09-14, dropped --sul-measure/62ch
   entirely) - prose now fills whatever container it's placed in
   (typically sul-container--md, 760px), matching Rentl Digest's own
   .rentl-prose (assets/css/04-components.css there), which never had a
   separate readability cap layered on top of its container either.
   62ch read as noticeably narrower than comparable pages elsewhere
   (Carousell's help articles run ~700-760px, no separate cap) once
   actually compared side by side, not just assumed correct as a
   standard typography default. */
.sul-prose > * + * { margin-top: var(--sul-space-4); }
.sul-prose h2, .sul-prose h3 { margin-top: var(--sul-space-8); }
.sul-prose a { color: var(--sul-guava-strong); }
/* A bare <blockquote> has nothing but the browser's own default indent -
   no border, no color change - so a Quote block in a listing description
   (sul_listing_description_allowed_html(), sanitize.php) rendered live
   read as plain indented text, easy to mistake for a continuation of
   whatever list sat above it (reported directly against a live
   screenshot). Same left-border/muted-text treatment the description
   editor's own preview already gives it (.sul-richtext__body blockquote,
   05-forms.css) - this is that same block rendered on the actual public
   page, so the two should look alike. */
.sul-prose blockquote {
	padding-left: var(--sul-space-3);
	border-left: 3px solid var(--sul-border);
	color: var(--sul-muted);
}

.sul-price, .sul-tabular { font-variant-numeric: tabular-nums; }

/* assets/css/03-layout.css */
/* Layout primitives. */

.sul-container {
	width: 100%;
	max-width: var(--sul-container);
	margin-inline: auto;
	padding-inline: clamp(1rem, 4vw, 2rem);
}
.sul-container--md { max-width: 760px; }
.sul-container--sm { max-width: 560px; }
/* Sign-up (2026-10-10): wide enough that the headline and the intro do not wrap
   for no reason and short fields can sit two to a row on a desktop screen
   (.sul-form__row, 05-forms.css). Sign-in has two fields and stays on --sm. */
.sul-container--auth { max-width: 720px; }
/* Listing detail (single-sulitista_listing.php) - narrow like --md while
   its content is a single stacked column (mobile), but widens back to
   the base container once .sul-listing-layout switches to two columns
   at the same breakpoint (08-storefront.css) - --md's 760px is too
   narrow to give a photo column and a text column both real room. */
.sul-container--listing { max-width: 760px; }
@media (min-width: 700px) {
	.sul-container--listing { max-width: var(--sul-container); }
}

.sul-section { padding-block: clamp(2rem, 6vw, 4rem); }
/* A page that opens with a breadcrumb trail (a product, a blog post, a policy page, Discover, a
   store's blog list) puts the trail 16px under the site header, not a full section's padding. */
.sul-section:has(.sul-breadcrumbs) { padding-top: var(--sul-space-4); }

/* flex: 1 0 auto (2026-09-25) - replaces the old min-height: 50vh, which
   only ever guaranteed HALF the viewport, not a true "fill whatever's
   left" - see body's own comment (01-base.css) for the full sticky-footer
   setup this is one piece of. */
.sul-main { flex: 1 0 auto; }

.sul-stack { display: flex; flex-direction: column; gap: var(--sul-space-6); }
.sul-stack--sm { gap: var(--sul-space-2); }

.sul-cluster { display: flex; flex-wrap: wrap; gap: var(--sul-space-3); align-items: center; }

.sul-grid {
	display: grid;
	gap: var(--sul-space-6);
	grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
}

/* assets/css/04-components.css */
/* Components. Border / fill / radius / shadow spent by role, not stamped
   on every block. */

/* -- button -- */
.sul-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: var(--sul-space-2);
	font: inherit;
	font-weight: 600;
	font-size: var(--sul-fs-sm);
	line-height: 1;
	padding: 0.7rem 1.15rem;
	border: none;
	border-radius: var(--sul-radius-sm);
	background: var(--sul-btn-bg);
	color: var(--sul-btn-fg);
	text-decoration: none;
	cursor: pointer;
	box-shadow: var(--sul-shadow-sm);
	transition: background 0.15s ease, transform 0.15s ease;
}
.sul-btn:hover { background: var(--sul-btn-bg-hover); transform: translateY(-1px); }
.sul-btn:focus-visible { outline: 2px solid var(--sul-ink); outline-offset: 2px; }
.sul-btn[disabled] { opacity: 0.55; cursor: not-allowed; transform: none; }
.sul-btn--full { width: 100%; }
.sul-btn--ghost {
	background: transparent;
	color: var(--sul-ink);
	border: 1px solid var(--sul-border);
	box-shadow: none;
}
.sul-btn--ghost:hover { background: var(--sul-surface-2); }
/* Tighter padding for a button sitting inline among plain text links (the
   header's account row, 2026-09-30) - the base size is sized for standalone
   CTA placement (hero, cards) and reads oversized crammed next to a
   same-line "Sign in" text link. */
.sul-btn--sm { padding: 0.45rem 0.9rem; }

/* -- card -- */
.sul-card {
	background: var(--sul-surface);
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius);
	padding: clamp(1.25rem, 3vw, 2rem);
	box-shadow: var(--sul-shadow-sm);
}

/* -- pill / badge -- */
.sul-pill {
	display: inline-flex;
	align-items: center;
	gap: var(--sul-space-1);
	font-size: var(--sul-fs-label);
	font-weight: 600;
	padding: 0.2rem 0.55rem;
	border-radius: var(--sul-radius-pill);
	border: 1px solid var(--sul-border);
	color: var(--sul-muted);
	white-space: nowrap;
}
/* Clickable pill (2026-10-01) - a Condition/Status/Authenticity pill on a
   card that filters the results (sul_card_pill_el(), meta-listing.php).
   Keeps its own tier color; hover/focus add a guava ring. No "active"
   look (direct call, 2026-10-01): with a filter applied every pill of that
   kind reads the same, so highlighting them all is noise and looks like a
   stuck hover - the dropdown already shows the active filter. ::after is an invisible
   hit-area extension - the pill is small and sits on a photo, which is a
   link itself, so a near-miss should land on the pill, not the photo. */
a.sul-pill--link { text-decoration: none; cursor: pointer; }
/* Label-only link inside a pill that also holds an info button (the listing
   detail page's Condition/Authenticity pills) - looks like the plain label
   until hovered. */
.sul-pill__link { color: inherit; text-decoration: none; }
.sul-pill__link:hover,
.sul-pill__link:focus-visible { text-decoration: underline; }
a.sul-pill--link::after { content: ""; position: absolute; inset: -5px; }
a.sul-pill--link:hover,
a.sul-pill--link:focus-visible {
	box-shadow: 0 0 0 2px color-mix(in srgb, var(--sul-guava-strong) 40%, transparent);
	outline: none;
}
.sul-pill--available { color: var(--sul-calamansi-ink); border-color: color-mix(in srgb, var(--sul-calamansi) 40%, var(--sul-border)); }
.sul-pill--reserved  { color: var(--sul-marigold-ink);  border-color: color-mix(in srgb, var(--sul-marigold) 45%, var(--sul-border)); }
.sul-pill--sold      { color: var(--sul-sili);          border-color: color-mix(in srgb, var(--sul-sili) 40%, var(--sul-border)); }
/* .sul-pill--discount removed (2026-09-30) - the discount badge is no
   longer a pill at all, see .sul-listing__discount (08-storefront.css)
   for its replacement. Direct feedback on TWO points, not just one:
   first, guava/pink read as a warm "negative"-toned color for something
   that's actually good news for the buyer - a saving - so it moved to
   calamansi green, this theme's existing positive/success color (already
   used by .sul-pill--available above). Second, separately: "i'm
   borderline hating on pills now" - plain colored text, no border/radius/
   padding, one fewer pill on a page that already has two (condition,
   authenticity) right below it. */
/* Listing condition/authenticity (single-sulitista_listing.php,
   listing-card.php, search-result-card.php) - plain ink by default
   (.sul-pill--fact alone), same base every "fact" pill on the site
   shares. .is-positive/.is-caution/.is-warning below reintroduce color,
   reversing the earlier "ink text, the icon carries the distinction"
   call - direct request for a broader color pass across the cards, then
   mocked up in both light and dark mode before building. Reuses the
   exact three-color semantic system .sul-pill--available/--reserved/
   --sold already established above (calamansi/marigold/sili), not a new
   palette, and the tier for every condition/authenticity VALUE is
   decided once in PHP (sul_listing_condition_tier()/
   sul_listing_authenticity_tier(), meta-listing.php) so it can never
   drift between the three templates that render these pills. Unbranded
   deliberately has no tier class - not a judgment either way, tinting
   it would misrepresent a non-claim as positive or negative. */
.sul-pill--fact { color: var(--sul-ink); position: relative; }
.sul-pill--fact svg { width: 13px; height: 13px; color: var(--sul-muted); flex-shrink: 0; }
.sul-pill--fact.is-positive { color: var(--sul-calamansi-ink); border-color: color-mix(in srgb, var(--sul-calamansi) 40%, var(--sul-border)); }
.sul-pill--fact.is-caution  { color: var(--sul-marigold-ink);  border-color: color-mix(in srgb, var(--sul-marigold) 45%, var(--sul-border)); }
.sul-pill--fact.is-warning  { color: var(--sul-sili);          border-color: color-mix(in srgb, var(--sul-sili) 40%, var(--sul-border)); }

/* Info tooltip riding inside a .sul-pill--fact (sul_fact_pill_tip(),
   meta-listing.php; toggled by assets/js/listing-fact-tip.js) - the
   pill's own label stays short at a glance, the tooltip is where it
   actually gets spelled out (2026-09-24, "the icons don't really speak
   for themselves"). position: relative above is what anchors this. */
.sul-pill__tip-btn {
	display: inline-flex;
	align-items: center;
	padding: 0;
	margin: 0 0 0 2px;
	background: none;
	border: none;
	color: inherit;
	cursor: pointer;
}
.sul-pill__tip-icon svg { width: 13px; height: 13px; display: block; opacity: 0.6; }
.sul-pill__tip {
	position: absolute;
	top: calc(100% + 8px);
	left: 0;
	z-index: 5;
	width: max-content;
	max-width: 220px;
	background: var(--sul-surface);
	color: var(--sul-ink);
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius-sm);
	box-shadow: var(--sul-shadow-sm);
	padding: 0.55rem 0.7rem;
	font-size: var(--sul-fs-sm);
	font-weight: 400;
	line-height: 1.5;
	white-space: normal;
}
.sul-pill__tip::before {
	content: "";
	position: absolute;
	top: -5px;
	left: 14px;
	width: 9px;
	height: 9px;
	background: var(--sul-surface);
	border-left: 1px solid var(--sul-border);
	border-top: 1px solid var(--sul-border);
	transform: rotate(45deg);
}
/* Hover the WHOLE pill opens the tooltip too (2026-09-24, direct
   feedback: "clicking on the i is so hard since it's so small") - the
   click toggle (assets/js/listing-fact-tip.js) stays as the actual
   trigger for touch/keyboard, this is additive for a mouse. Scoped to
   (hover: hover) - a touchscreen's first tap on the pill can register as
   a lingering ":hover" in some browsers (no real pointer to "leave" with
   after), which would make the tooltip stick open there for no reason;
   restricting this to devices that have a real hover concept avoids that
   entirely rather than fighting it after the fact. !important is
   required here (confirmed live, not just theoretical) - 07-utilities.css's
   own global `[hidden] { display: none !important; }` sits later in the
   bundle and beats plain specificity regardless of how targeted this
   selector is; that global rule is deliberately forceful (guards every
   OTHER hidden element on the site against being un-hidden by an
   accidental higher-specificity rule elsewhere) so it stays as-is, and
   this narrow, deliberate exception matches it on its own terms. */
@media (hover: hover) {
	/* .sul-pill--affiliate added 2026-09-25 - the Affiliate listing pill
	   carries the same tip now (single-sulitista_listing.php). */
	.sul-pill--fact:hover .sul-pill__tip[hidden],
	.sul-pill--affiliate:hover .sul-pill__tip[hidden] { display: block !important; }
}

/* "Affiliate listing" disclosure (single-sulitista_listing.php, Sulitista
   Affiliate Links handoff) - deliberately NOT .sul-pill--fact's plain ink
   treatment. This is a disclosure a buyer needs to actually notice, not a
   neutral fact like condition/authenticity - same marigold "notable/
   flagged" tint the Sponsored badge already uses (13-search.css), a
   filled tint rather than just a bordered outline so it reads with real
   weight at a glance. */
.sul-pill--affiliate {
	color: var(--sul-marigold-ink);
	background: color-mix(in srgb, var(--sul-marigold) 22%, var(--sul-surface));
	border-color: color-mix(in srgb, var(--sul-marigold) 45%, var(--sul-border));
	/* Anchors its own info tip the same way .sul-pill--fact does below
	   (2026-09-25) - it now carries one too. */
	position: relative;
}
.sul-pill--affiliate svg { width: 13px; height: 13px; flex-shrink: 0; }

/* Verified pill (2026-10-09, option 3 of the badge mockups): solid marigold with
   a fixed dark ink and the seal mark. The marigold is light in every theme state,
   so the ink is a fixed dark brown (--sul-ink flips light in dark mode and would
   vanish on it), and the same pill works on a cream page and on a cover photo. */
.sul-badge-verified {
	display: inline-flex;
	align-items: center;
	gap: 0.3rem;
	font-size: var(--sul-fs-label);
	font-weight: 700;
	line-height: 1.3;
	color: #3a2306;
	background: var(--sul-marigold);
	border: 0;
	padding: 0.2rem 0.65rem 0.2rem 0.4rem;
	border-radius: var(--sul-radius-pill);
}
.sul-badge-verified::before {
	content: "";
	flex: none;
	width: 15px;
	height: 15px;
	background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Crect x='3.5' y='3.5' width='17' height='17' rx='4.5' fill='%233a2306'/%3E%3Crect x='3.5' y='3.5' width='17' height='17' rx='4.5' fill='%233a2306' transform='rotate(45 12 12)'/%3E%3Cpath d='M8 12.3l2.7 2.7L16.2 9.4' stroke='%23F2A93C' fill='none' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / contain no-repeat;
}

/* -- Verified Store icon (includes/verified-icon.php, assets/js/verified-tip.js) --
   A marigold seal with a tick that sits on the store name's line and names
   itself in a small tip below it. Click/tap toggles (touchscreens have no
   hover), a real mouse also gets hover. The seal is the same marigold in every
   theme state, so the tick is a fixed dark brown: --sul-ink flips light in
   dark mode and would vanish on it. */
/* Sits inline inside the store name's heading, so it follows the last word
   even when a long name wraps. */
.sul-verified { position: relative; display: inline-flex; align-items: center; vertical-align: middle; margin-left: 0.1em; }
.sul-verified__btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 28px;
	height: 28px;
	padding: 0;
	background: none;
	border: none;
	border-radius: 50%;
	cursor: pointer;
}
.sul-verified__btn:focus-visible { outline: 2px solid var(--sul-guava-strong); outline-offset: 1px; }
/* The plain seal used inside a link (store card, product card seller line). */
.sul-verified-mark { flex: none; vertical-align: middle; }
.sul-verified__disc { fill: var(--sul-marigold); }
.sul-verified__tick { stroke: #3a2306; }
.sul-verified__tip {
	position: absolute;
	top: calc(100% + 4px);
	right: -4px;
	z-index: 5;
	width: max-content;
	background: var(--sul-surface);
	color: var(--sul-ink);
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius-sm);
	box-shadow: var(--sul-shadow-sm);
	padding: 0.35rem 0.65rem;
	font-size: var(--sul-fs-sm);
	font-weight: 400;
	line-height: 1.4;
	white-space: nowrap;
	text-shadow: none;
}
.sul-verified__tip::before {
	content: "";
	position: absolute;
	top: -5px;
	right: 14px;
	width: 9px;
	height: 9px;
	background: var(--sul-surface);
	border-left: 1px solid var(--sul-border);
	border-top: 1px solid var(--sul-border);
	transform: rotate(45deg);
}
/* !important for the same reason as the fact-pill tip above: 07-utilities.css's
   global [hidden] rule is !important and sits later in the bundle. */
@media (hover: hover) {
	.sul-verified:hover .sul-verified__tip[hidden] { display: block !important; }
}

/* -- age gate (single-sulitista_listing.php) -- */
.sul-age-gate__panel {
	max-width: 420px;
	margin: var(--sul-space-8) auto;
	padding: var(--sul-space-6);
	background: var(--sul-surface);
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius);
	text-align: center;
}
.sul-age-gate__title { font-weight: 700; margin: 0 0 var(--sul-space-1); }
.sul-age-gate__panel .sul-cluster { justify-content: center; margin-top: var(--sul-space-4); }

/* -- checkbox field (dashboard listing editor) -- */
.sul-checkbox {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	font-weight: 400;
	cursor: pointer;
}
/* Wraps several .sul-checkbox chips onto one flowing row (listing editor's
   Tags field, listing-tags.php) - each chip stays its own flex item, this
   just lets them wrap rather than stack one per line like Age-restricted's
   single checkbox does. */
.sul-tag-chips { display: flex; flex-wrap: wrap; gap: var(--sul-space-2) var(--sul-space-4); }

/* -- pagination (core) -- */
.pagination, .nav-links {
	display: flex;
	gap: var(--sul-space-2);
	flex-wrap: wrap;
	margin-top: var(--sul-space-8);
}
.page-numbers {
	display: inline-flex;
	min-width: 2.25rem;
	height: 2.25rem;
	align-items: center;
	justify-content: center;
	padding: 0 0.5rem;
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius-sm);
	text-decoration: none;
	color: var(--sul-ink);
}
.page-numbers.current { background: var(--sul-ink); color: var(--sul-ground); border-color: var(--sul-ink); }

/* -- post summary (interim) -- */
.sul-post-summary { padding-bottom: var(--sul-space-6); border-bottom: 1px solid var(--sul-border); }
.sul-post-summary:last-child { border-bottom: 0; }
.sul-post-summary h2 { margin-bottom: var(--sul-space-2); }
.sul-post__figure { border-radius: var(--sul-radius); overflow: hidden; margin: 0 0 var(--sul-space-6); }

/* -- inline status note (appeal feedback, buyer caution, etc.) -- */
.sul-note {
	padding: var(--sul-space-3) var(--sul-space-4);
	border-radius: var(--sul-radius-sm);
	border: 1px solid var(--sul-border);
	background: var(--sul-surface-2);
	font-size: var(--sul-fs-sm);
	margin-bottom: var(--sul-space-4);
}
.sul-note.is-ok { border-color: var(--sul-calamansi); color: var(--sul-calamansi-ink); }
.sul-note.is-error { border-color: var(--sul-sili); color: var(--sul-sili); }

/* -- "Report" control (storefront + listing) -- */
.sul-report { margin-top: var(--sul-space-8); padding-top: var(--sul-space-6); border-top: 1px solid var(--sul-border); }
.sul-report__toggle {
	background: none;
	border: none;
	padding: 0;
	font: inherit;
	font-size: var(--sul-fs-sm);
	font-weight: 600;
	color: var(--sul-muted);
	text-decoration: underline;
	text-underline-offset: 2px;
	cursor: pointer;
}
.sul-report__toggle:hover,
.sul-report__toggle[aria-expanded="true"] { color: var(--sul-guava-strong); }
.sul-report__panel { margin-top: var(--sul-space-4); max-width: 480px; }

/* -- tooltip (includes/tip.php, assets/js/tip.js) -- an "i" button or a dotted word
   with a popover next to it. No display on .sul-tip__text, so its [hidden] works. */
.sul-tip { position: relative; display: inline; }
.sul-tip__btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	position: relative;
	width: 1.35rem;
	height: 1.35rem;
	margin-left: 0.3rem;
	padding: 0;
	font: inherit;
	font-size: 0.72rem;
	font-weight: 600;
	line-height: 1;
	vertical-align: 0.05em;
	color: var(--sul-guava-strong);
	background: var(--sul-surface);
	border: 1px solid var(--sul-guava-strong);
	border-radius: 50%;
	cursor: help;
}
.sul-tip__word {
	font: inherit;
	color: inherit;
	background: none;
	border: 0;
	border-bottom: 1.5px dotted var(--sul-guava-strong);
	padding: 0;
	cursor: help;
}
/* A bigger touch target than the 22px circle (the finger, not the drawing). */
.sul-tip__btn::after { content: ""; position: absolute; inset: -0.5rem; }
.sul-tip__btn:focus-visible,
.sul-tip__word:focus-visible { outline: 2px solid var(--sul-ink); outline-offset: 2px; }
.sul-tip__text {
	position: absolute;
	left: 0;
	top: calc(100% + 0.4rem);
	z-index: 30;
	width: min(20rem, calc(100vw - 2rem));
	padding: 0.65rem 0.8rem;
	font-size: var(--sul-fs-sm);
	font-weight: 400;
	line-height: 1.5;
	text-align: left;
	color: var(--sul-ink);
	background: var(--sul-surface);
	border: 1px solid var(--sul-field-border);
	border-radius: var(--sul-radius-sm);
	box-shadow: var(--sul-shadow);
}
.sul-tip__text a { color: var(--sul-guava-strong); }

/* -- breadcrumbs (includes/breadcrumbs.php) --
   Sulitista / Store / Product above a page. One line at every width: nothing
   wraps. Only the page itself (the last crumb) shortens with an ellipsis; the
   crumbs before it keep their full name, so the store is always readable.
   Direct feedback: a store name cut to "Cebu T..." while the product title
   still had room. A middle crumb is only cut past 60% of the line, so a very
   long store name cannot push the page name down to nothing. Muted links, the
   page itself in ink.
   Spacing (2026-10-12, every page that has one): 16px from the site header to the trail
   (.sul-section:has(.sul-breadcrumbs), 03-layout.css) and 32px from the trail to the page's
   own content, below. */
.sul-breadcrumbs { margin-bottom: var(--sul-space-8); font-size: var(--sul-fs-sm); }
.sul-breadcrumbs__list {
	display: flex;
	flex-wrap: nowrap;
	align-items: center;
	min-width: 0;
	margin: 0;
	padding: 0;
	list-style: none;
}
.sul-breadcrumbs__item {
	min-width: 0;
	overflow: hidden;
	white-space: nowrap;
	text-overflow: ellipsis;
	color: var(--sul-ink);
}
.sul-breadcrumbs__item:not(:last-child):not(.sul-breadcrumbs__item--soft) { flex-shrink: 0; max-width: 60%; }
/* The one crumb in the middle that may give way (the category) so the store name stays
   whole; it shortens before the page name does. */
.sul-breadcrumbs__item--soft { flex-shrink: 1; }
.sul-breadcrumbs__item + .sul-breadcrumbs__item::before {
	content: "/";
	margin: 0 var(--sul-space-2);
	color: var(--sul-muted);
}
.sul-breadcrumbs__item a { color: var(--sul-muted); text-decoration: none; }
.sul-breadcrumbs__item a:hover { color: var(--sul-ink); text-decoration: underline; }

/* assets/css/05-forms.css */
/* Forms. */

.sul-form { display: flex; flex-direction: column; gap: var(--sul-space-4); }

/* The actual mechanism behind the dashboard Save buttons' width, found via
   DevTools this time, not guessed: .sul-form has no align-items override, so
   its default (stretch) was already stretching a submit button - a DIRECT
   child of .sul-form here, not wrapped in a .sul-form__field - to full
   width regardless of the sul-btn--full class, which was riding along as a
   total no-op. Removing that class alone (the previous, ineffective fix)
   never touched the real cause. align-self: center opts these two specific
   buttons out of the parent's stretch (and centers them, on request); .sul-
   btn's own default sizing (inline-flex, sized to its label) takes over
   from there. Scoped by the two dashboard forms' own ids, not a general
   .sul-btn rule - the register/contact/message-seller buttons elsewhere
   still want the stretch behavior and are unaffected. */
#sul-listing-form > button[type="submit"],
#sul-store-form > button[type="submit"] {
	align-self: center;
}

.sul-form__field { display: flex; flex-direction: column; gap: var(--sul-space-2); }

/* Two short fields side by side on a desktop screen (password + confirm, province
   + city, surname + given names) from 720px up. One column below that, where it already reads well.
   min-width: 0 on the cells so a long value or a themed select cannot push the row
   wider than the card. */
.sul-form__row { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--sul-space-4); }
.sul-form__row > .sul-form__field { min-width: 0; }
@media (min-width: 720px) {
	.sul-form__row { grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: start; }
}

.sul-form__field label {
	font-weight: 600;
	font-size: var(--sul-fs-sm);
}

.sul-form input[type="text"],
.sul-form input[type="email"],
.sul-form input[type="password"],
.sul-form input[type="search"],
.sul-form input[type="url"],
.sul-form input[type="tel"],
.sul-form input[type="number"],
.sul-form input[type="date"],
.sul-form input[type="time"],
.sul-form input[type="datetime-local"],
.sul-form input[type="file"],
.sul-form textarea,
.sul-form select {
	font: inherit;
	font-size: var(--sul-fs-body);
	width: 100%;
	padding: 0.65rem 0.8rem;
	background: var(--sul-surface);
	color: var(--sul-ink);
	border: 1px solid var(--sul-field-border);
	border-radius: var(--sul-radius-sm);
}
/* Edge/IE inject their own reveal-password eye + clear-x inside
   input[type=password] — it floats on top of our own Show/Hide button and
   flickers in and out on focus. Suppress the native one; ours replaces it. */
.sul-form input[type="password"]::-ms-reveal,
.sul-form input[type="password"]::-ms-clear { display: none; }

/* padding-right alone (first attempt) verified via DevTools as correctly
   applying, yet the arrow still read as glued to the edge - a native
   <select>'s own arrow glyph is UA-drawn chrome anchored near the box
   edge, not something that reliably respects author padding-right the
   way real text does. Same fix as .sul-header__search select
   (06-header-footer.css) and .sul-discover-search select
   (12-directory.css) - strip the native arrow, draw our own SVG chevron
   at an exact position, the same pattern .sul-dash-listing__status
   (10-dashboard.css) already used. */
.sul-form select {
	appearance: none;
	-webkit-appearance: none;
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%237A6B72' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
	background-repeat: no-repeat;
	background-position: right 0.8rem center;
	padding-right: 1.75rem;
}
.sul-form input:focus-visible,
.sul-form textarea:focus-visible,
.sul-form select:focus-visible {
	outline: 2px solid var(--sul-guava-strong);
	outline-offset: 1px;
	border-color: var(--sul-guava-strong);
}
.sul-form textarea { resize: vertical; min-height: 6rem; }

/* Listing description's rich-text editor (dashboard.js's richTextEditorHtml()) -
   a toolbar of document.execCommand() buttons over a contenteditable body,
   built specifically so the formatting sul_listing_description_allowed_
   html() (sanitize.php) actually permits is reachable by clicking a
   button, not by knowing HTML and typing tags by hand into a plain
   textarea - the gap direct feedback called out once the field itself
   already supported bold/italic/etc. */
/* overflow: clip, not hidden: hidden makes this box the toolbar's scroll container, which would stop the
   toolbar sticking to the screen (clip rounds the corners the same and is not a scroll container). */
.sul-richtext { border: 1px solid var(--sul-field-border); border-radius: var(--sul-radius-sm); overflow: hidden; overflow: clip; }
/* The toolbar follows you down a long post: it sticks just under the site header while the editor is on
   screen, so Photo, Video, headings and links are always in reach. inline-edit.js sets the offset to the
   header's real height. */
.sul-richtext__toolbar {
	position: sticky;
	top: var(--sul-richtext-sticky, 0px);
	z-index: 5;
	display: flex;
	flex-wrap: wrap;
	gap: var(--sul-space-1);
	/* Room on the right for the code toggle, which stays in the top right corner whatever the rows do. */
	padding: var(--sul-space-2) calc(var(--sul-space-2) + 2.5rem) var(--sul-space-2) var(--sul-space-2);
	background: var(--sul-surface-2);
	border-bottom: 1px solid var(--sul-field-border);
	border-radius: var(--sul-radius-sm) var(--sul-radius-sm) 0 0;
}
.sul-richtext__btn {
	min-width: 2rem;
	padding: 0.3rem 0.5rem;
	font: inherit;
	font-size: var(--sul-fs-sm);
	line-height: 1;
	background: var(--sul-surface);
	color: var(--sul-ink);
	border: 1px solid var(--sul-field-border);
	border-radius: var(--sul-radius-sm);
	cursor: pointer;
}
/* The formatting buttons tighten on a phone so they fit in two rows beside the code toggle (after the
   base rule above, which it overrides). */
@media (max-width: 640px) {
	.sul-richtext__toolbar { gap: 4px; }
	.sul-richtext__btn { min-width: 1.75rem; padding: 0.35rem 0.3rem; }
}
.sul-richtext__btn:hover { background: var(--sul-border); }
.sul-richtext__btn:disabled { opacity: .5; cursor: default; }
/* The HTML view switch is a different kind of control from the formatting buttons, so it looks like one:
   monospace, a dashed outline, quiet until it is on (then filled). It is pinned to the top right corner of
   the toolbar, whatever the other buttons do; they wrap in the room beside it. */
.sul-richtext__code-toggle {
	position: absolute;
	top: var(--sul-space-2);
	right: var(--sul-space-2);
	min-width: 2.5rem;
	padding: 0.3rem 0.4rem;
	font-family: ui-monospace, 'SF Mono', Consolas, monospace;
	font-size: var(--sul-fs-sm);
	line-height: 1;
	background: transparent;
	color: var(--sul-muted);
	border: 1px dashed var(--sul-field-border);
	border-radius: var(--sul-radius-sm);
	cursor: pointer;
}
.sul-richtext__code-toggle:hover { color: var(--sul-guava-strong); border-color: var(--sul-guava-strong); }
.sul-richtext__code-toggle[aria-pressed="true"] { color: #fff; background: var(--sul-guava-strong); border: 1px solid var(--sul-guava-strong); }
.sul-richtext__source {
	display: block;
	width: 100%;
	min-height: 6rem;
	padding: 0.65rem 0.8rem;
	background: var(--sul-surface);
	color: var(--sul-ink);
	font-family: ui-monospace, 'SF Mono', Consolas, monospace;
	font-size: var(--sul-fs-sm);
	line-height: 1.5;
	border: none;
	resize: vertical;
}
.sul-richtext__source:focus-visible { outline: 2px solid var(--sul-guava-strong); outline-offset: -2px; }
.sul-richtext__body {
	min-height: 6rem;
	padding: 0.65rem 0.8rem;
	background: var(--sul-surface);
	color: var(--sul-ink);
	font-size: var(--sul-fs-body);
	overflow-y: auto;
}
.sul-richtext__body:focus-visible {
	outline: 2px solid var(--sul-guava-strong);
	outline-offset: -2px;
}
/* A blank contenteditable still has a real (empty) element inside it,
   nothing for a placeholder attribute to hook into the way <textarea
   placeholder> does - :empty targets exactly that case. */
.sul-richtext__body:empty::before {
	content: 'Condition notes, what\2019s included, anything a buyer should know\2026';
	color: var(--sul-muted);
}
.sul-richtext__body p { margin: 0 0 var(--sul-space-2); }
.sul-richtext__body p:last-child { margin-bottom: 0; }
.sul-richtext__body blockquote {
	margin: var(--sul-space-2) 0;
	padding-left: var(--sul-space-3);
	border-left: 3px solid var(--sul-border);
	color: var(--sul-muted);
}
/* A blog post's photos and videos inside the editor (assets/js/blog-dashboard.js). A video is a
   labelled box (the label comes from data-label, so it is not counted as text). */
.sul-richtext__body img[data-photo] {
	display: block;
	max-width: min( 100%, 320px );
	height: auto;
	margin: var(--sul-space-2) 0;
	border-radius: var(--sul-radius-sm);
}
.sul-richtext__body p[data-video] {
	margin: var(--sul-space-2) 0;
	padding: var(--sul-space-3) var(--sul-space-4);
	background: var(--sul-surface-2);
	border: 1px dashed var(--sul-border);
	border-radius: var(--sul-radius-sm);
	color: var(--sul-muted);
	font-size: var(--sul-fs-sm);
}
/* A photo is a block with its caption under it; pressing it opens the caption dialog (blog posts only). */
.sul-richtext__body figure.sul-richtext__photo { margin: var(--sul-space-2) 0; cursor: pointer; border-radius: var(--sul-radius-sm); }
.sul-richtext__body figure.sul-richtext__photo:hover,
.sul-richtext__body figure.sul-richtext__photo:focus-visible { outline: 2px solid var(--sul-guava-strong); outline-offset: 2px; }
.sul-richtext__body figure.sul-richtext__photo img[data-photo] { margin: 0; width: 100%; max-width: none; }
/* An "Edit" chip in the top right corner of the photo says it can be pressed. It is generated from data-edit-label
   (set by rich-text.js), so it never becomes part of the saved text, and it has the same dark look as the other
   edit pencils (17-inline-edit.css). */
.sul-richtext__body figure.sul-richtext__photo { position: relative; }
.sul-richtext__body figure.sul-richtext__photo::after {
	content: attr( data-edit-label );
	position: absolute;
	top: var(--sul-space-2);
	right: var(--sul-space-2);
	padding: 0.2rem 0.65rem;
	font-size: var(--sul-fs-sm);
	font-weight: 600;
	line-height: 1.4;
	color: var(--sul-edit-chip-fg, #fff);
	background: var(--sul-edit-chip, #2a2127);
	border: 2px solid var(--sul-edit-chip-ring, #fff);
	border-radius: 999px;
	box-shadow: 0 1px 6px rgba( 0, 0, 0, 0.3 );
	pointer-events: none;
}
.sul-richtext__body figure.sul-richtext__photo:hover::after { background: var(--sul-edit-chip-hover, #000); }
/* Drawn at the size and alignment chosen in the dialog, as the post shows it (see 18-blog.css). */
.sul-richtext__body figure.sul-richtext__photo.is-small,
.sul-richtext__body figure.sul-richtext__photo.is-medium { margin-left: auto; margin-right: auto; }
.sul-richtext__body figure.sul-richtext__photo.is-small { width: 40%; }
.sul-richtext__body figure.sul-richtext__photo.is-medium { width: 60%; }
.sul-richtext__body figure.sul-richtext__photo.is-left { float: left; margin: var(--sul-space-1) var(--sul-space-3) var(--sul-space-2) 0; }
.sul-richtext__body figure.sul-richtext__photo.is-right { float: right; margin: var(--sul-space-1) 0 var(--sul-space-2) var(--sul-space-3); }
@media (max-width: 640px) {
	.sul-richtext__body figure.sul-richtext__photo.is-small,
	.sul-richtext__body figure.sul-richtext__photo.is-medium { width: 100%; }
	.sul-richtext__body figure.sul-richtext__photo.is-left,
	.sul-richtext__body figure.sul-richtext__photo.is-right { float: none; margin: var(--sul-space-2) 0; }
}
.sul-richtext__body figure.sul-richtext__photo figcaption { margin-top: var(--sul-space-1); font-size: var(--sul-fs-sm); color: var(--sul-muted); }
.sul-richtext__body figure.sul-richtext__photo figcaption:empty::before { content: attr( data-placeholder ); font-style: italic; }
.sul-richtext__body p[data-video]::before { content: '\25B6\00a0\00a0' attr( data-label ); }
/* A YouTube video also shows its still picture (the picture is set inline by the server or the editor). */
.sul-richtext__body p[data-video].has-thumb {
	display: flex;
	align-items: flex-end;
	min-height: 150px;
	background-size: cover;
	background-position: center;
	border-style: solid;
	color: #fff;
	text-shadow: 0 1px 3px rgba( 0, 0, 0, 0.85 );
	box-shadow: inset 0 -64px 40px -24px rgba( 0, 0, 0, 0.65 );
}

/* Live plain-text count against SUL_LISTING_DESCRIPTION_MAX_CHARS
   (meta-listing.php) - a visible warning only, not a hard stop (see
   dashboard.js's bindRichTextEditor()/updateCount() for why blocking
   keystrokes mid-typing in a contenteditable isn't safe). */
.sul-richtext__count {
	margin: 0;
	padding: var(--sul-space-1) var(--sul-space-3);
	font-size: var(--sul-fs-label);
	color: var(--sul-muted);
	text-align: right;
	border-top: 1px solid var(--sul-field-border);
}
.sul-richtext__count.is-over { color: var(--sul-sili); font-weight: 600; }

/* File input — style the browser's picker button to match the field */
.sul-form input[type="file"] { padding: 0.45rem 0.55rem; cursor: pointer; }
.sul-form input[type="file"]::file-selector-button {
	font: inherit;
	font-size: var(--sul-fs-sm);
	font-weight: 600;
	margin-right: 0.7rem;
	padding: 0.4rem 0.8rem;
	background: var(--sul-surface-2);
	color: var(--sul-ink);
	border: 1px solid var(--sul-field-border);
	border-radius: var(--sul-radius-sm);
	cursor: pointer;
}
.sul-form input[type="file"]::file-selector-button:hover { background: var(--sul-border); }

/* honeypot — visually and semantically hidden */
.sul-hp {
	position: absolute !important;
	left: -9999px !important;
	width: 1px;
	height: 1px;
	overflow: hidden;
}

.sul-form__response {
	font-size: var(--sul-fs-sm);
	margin: 0;
}
.sul-form__response.is-ok { color: var(--sul-calamansi-ink); }
.sul-form__response.is-error { color: var(--sul-sili); }

/* core search form */
.search-form { display: flex; gap: var(--sul-space-2); max-width: 420px; }
.search-form input[type="search"] {
	flex: 1;
	font: inherit;
	padding: 0.6rem 0.8rem;
	border: 1px solid var(--sul-field-border);
	border-radius: var(--sul-radius-sm);
	background: var(--sul-surface);
	color: var(--sul-ink);
}

/* Themed dropdown (2026-10-01) - listbox-select.js upgrades every
   <select data-sul-listbox> into this trigger + panel; the native select
   stays in the DOM, visually hidden, as the form value. Native selects'
   open lists are browser chrome and can't be rounded/recoloured, so this
   mirrors the Location picker's panel (12-directory.css) instead. Each
   surface (header search, Discover toolbar/Sort/filters) restyles
   .sul-lb__trigger for its own context in its own stylesheet. No `display`
   on .sul-lb__panel: its [hidden] attribute toggles visibility, and an
   author display here would override the UA's [hidden] rule. */
.sul-lb { position: relative; display: flex; align-items: stretch; flex: 0 0 auto; min-width: 0; }
.sul-lb > select.sul-lb__native {
	position: absolute;
	inset: 0 auto auto 0;
	width: 1px;
	height: 1px;
	min-width: 0;
	margin: 0;
	padding: 0;
	border: 0;
	opacity: 0;
	pointer-events: none;
}
.sul-lb__trigger {
	display: flex;
	align-items: center;
	gap: 0.4rem;
	max-width: 100%;
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius-sm);
	background-color: var(--sul-surface);
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%237A6B72' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
	background-repeat: no-repeat;
	background-position: right 0.6rem center;
	color: var(--sul-ink);
	font: inherit;
	font-size: var(--sul-fs-sm);
	padding: 0.4rem 1.75rem 0.4rem 0.6rem;
	text-align: left;
	white-space: nowrap;
	cursor: pointer;
	appearance: none;
	-webkit-appearance: none;
}
.sul-lb__value { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.sul-lb__trigger:focus-visible {
	outline: none;
	box-shadow: inset 0 0 0 2px var(--sul-guava-strong);
}
/* position/top/left/min-width are set inline by listbox-select.js at open
   time (the panel is moved to <body> to escape clipping ancestors). */
.sul-lb__panel {
	position: fixed;
	max-width: calc(100vw - 16px);
	max-height: 320px;
	overflow-y: auto;
	background: var(--sul-surface);
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius-sm);
	box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
	z-index: 1000;
}
.sul-lb__option {
	padding: 0.5rem 0.75rem;
	font-size: var(--sul-fs-sm);
	color: var(--sul-ink);
	white-space: nowrap;
	cursor: pointer;
}
.sul-lb__option:hover,
.sul-lb__option:focus {
	background: color-mix(in srgb, var(--sul-ink) 8%, var(--sul-surface));
	outline: none;
}
.sul-lb__option[aria-selected="true"] {
	background: color-mix(in srgb, var(--sul-ink) 12%, var(--sul-surface));
}
.sul-lb__trigger:disabled { opacity: 0.6; cursor: not-allowed; }
.sul-lb__option.is-disabled { opacity: 0.45; cursor: not-allowed; }
.sul-lb__option.is-disabled:hover,
.sul-lb__option.is-disabled:focus { background: transparent; }

/* Inside a .sul-form (dashboard editors, register) the trigger stands in for
   .sul-form select above: full width, same padding/font/border, and the
   same guava focus treatment the other fields get. */
.sul-form .sul-lb { width: 100%; }
.sul-form .sul-lb__trigger {
	width: 100%;
	font-size: var(--sul-fs-body);
	padding: 0.65rem 1.75rem 0.65rem 0.8rem;
	border-color: var(--sul-field-border);
	background-position: right 0.8rem center;
}
.sul-form .sul-lb__trigger:focus-visible {
	outline: 2px solid var(--sul-guava-strong);
	outline-offset: 1px;
	border-color: var(--sul-guava-strong);
	box-shadow: none;
}

/* assets/css/06-header-footer.css */
/* Header + footer. */

/* Tint + brand gradient stripe (2026-09-18) — replaces a dark-ink bookend
   (#239) after direct feedback that wasn't the right direction, and before
   it a plain --sul-ground/--sul-surface-2 that was barely distinguishable
   from the page body at all. Header and footer both move to a small step
   past the footer's original --sul-surface-2 tint (color-mix with
   --sul-ink), giving them a warmer, deeper tone clearly apart from
   --sul-ground without leaving the cream palette or needing a fixed
   literal color the way the ink bookend did — built entirely from tokens
   that already redefine themselves per theme, so this SAME rule holds
   correctly in both light and dark mode with no separate override block
   (unlike the ink bookend, which broke dark mode outright and needed one).
   A 3px gradient (guava -> marigold -> calamansi) marks the header's
   bottom edge and the footer's top edge (colors mirrored, so it reads as
   one continuous idea framing the body between them) in place of a plain
   1px border — ties the chrome to the site's full accent palette instead
   of just guava, and (being built from --sul-guava/--sul-marigold/
   --sul-calamansi directly) stays vivid in dark mode automatically too. */
.sul-header {
	/* Exposed as a custom property (2026-09-25) so anything sitting ON the
	   header - the search select's own tint below - can compute its
	   contrast relative to what it's ACTUALLY sitting on, not a generic
	   surface token that may not track it. The select's tint (8% ink into
	   plain --sul-surface) went nearly invisible in dark mode specifically
	   - --sul-surface is DARKER than --sul-surface-2 in dark mode (the
	   opposite ordering from light mode, a real token-flip gotcha this
	   codebase has hit before), so that formula's result landed at or
	   below the header's own background instead of a visible step above it. */
	--sul-header-bg: color-mix(in srgb, var(--sul-ink) 5%, var(--sul-surface-2));
	/* Row 2's share of one vertical gradient across the whole header (2026-10-14, option A
	   of a second mockup round; replaces the left-to-right guava-to-marigold sweep on row 1,
	   which did not land: "not the rainbow"). One colour family: the header tint at the top
	   (#F4E8E4 in light) fading to the page colour at the bottom edge, so the header melts
	   into the page. Row 1 (.sul-topbar) holds the top 41% (its 45px of the 109px total) and
	   row 2 the rest, so the two read as one sweep; when row 2 sticks alone it still ends on
	   the page colour. The 3px stripe along its bottom edge is unchanged. */
	background: linear-gradient(180deg, color-mix(in srgb, var(--sul-header-end) 41%, var(--sul-header-bg)), var(--sul-header-end));
	position: sticky;
	top: 0;
	z-index: 100;
}
.sul-header::after {
	content: "";
	position: absolute;
	left: 0;
	right: 0;
	bottom: 0;
	height: 3px;
	background: linear-gradient(90deg, var(--sul-guava) 0%, var(--sul-marigold) 50%, var(--sul-calamansi) 100%);
}
.sul-header__inner {
	display: flex;
	align-items: center;
	gap: var(--sul-space-4);
	min-height: 64px;
}
.sul-header__logo img { max-height: 40px; width: auto; }

/* The dropdown (2026-10-14, two-row header): row 1 (.sul-topbar, below) shows
   the utility and account links on a wide screen, so this folded copy exists
   only on a phone, where row 1 is hidden - see the mobile block at the end of
   this header section. */
.sul-header__menu { display: none; }

/* Hidden on desktop - shown only past the mobile breakpoint below, where
   it's the only way to reach .sul-header__menu. */
.sul-header__hamburger {
	display: none;
	align-items: center;
	justify-content: center;
	width: 38px;
	height: 38px;
	flex-shrink: 0;
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius-sm);
	background: var(--sul-surface);
	color: var(--sul-ink);
	cursor: pointer;
}
.sul-header__hamburger svg { width: 20px; height: 20px; pointer-events: none; }
.sul-header__hamburger:focus-visible { outline: 2px solid var(--sul-guava-strong); outline-offset: 2px; }

/* Sitewide product search (2026-09-13 reframe) - reachable from every page,
   not just the homepage hero. Stays outside .sul-header__menu on purpose
   (2026-09-19, direct request) - nav and account can live behind the
   hamburger, but search needs to stay reachable without an extra tap, on
   every page, mobile included. min-width:0 lets it shrink below the
   input's own clamp() minimum if the row is ever genuinely tight (a real
   flex-shrink floor, not just relying on content to define one) - on
   desktop this is the base, permanently-inline layout; the mobile rule
   further down replaces it with a tap-to-expand overlay instead (see the
   toggle/close rules just below and header.php's own comment). */
.sul-header__search {
	display: flex;
	align-items: stretch;
	gap: 0;
	min-width: 0;
	/* Occupies whatever space is actually available between the logo and
	   the nav/account group - direct feedback, 2026-09-25, once
	   search-select-width.js started resizing the select: the WHOLE widget
	   visibly grew/shrank as the category changed, since neither piece was
	   absorbing the other's width change, so the header's own layout
	   jittered. The input below is the piece that actually absorbs it
	   (flex:1 1 auto, no fixed width of its own) - the select changes, the
	   input compensates, this outer width never does. */
	flex: 1 1 auto;
}
/* Category select fused to the input's left edge (2026-09-25, Amazon-style
   direct request) — same fused-piece treatment as .sul-header__search
   button already had on the right, just mirrored. Fixed width, not
   flex-grow, so a long category name never eats into the input's own room
   (that's the input's job at every width, mobile included below). */
.sul-header__search select {
	flex: 0 0 auto;
	/* width:auto turned out NOT to size to the currently selected option
	   the way it was assumed to (2026-09-25, live feedback: a short pick
	   like "Others" still rendered as wide as the LONGEST option in the
	   whole list) - that's what a closed <select> actually measures itself
	   against in practice, not the current selection. search-select-
	   width.js sets an explicit width per-selection instead; this is just
	   its pre-JS floor so the box never reads as a tiny nub before that
	   script runs (or if it's ever blocked). */
	min-width: 70px;
	/* Padding-right alone (first attempt) verified via DevTools as
	   correctly computing to 28px, yet the arrow still read as glued to
	   the edge - because a native <select>'s own arrow glyph is UA-drawn
	   chrome anchored near the box edge, NOT something that reliably
	   respects author padding-right the way real text content does;
	   padding only pushed the TEXT further left, widening a gap nobody
	   was complaining about. The only reliable fix (2026-09-25) is the
	   same one .sul-dash-listing__status already uses (10-dashboard.css)
	   - strip the native arrow entirely and draw our own SVG chevron at
	   an exact position. Read dynamically by search-select-width.js's own
	   chrome calculation (getComputedStyle(...).paddingRight), so the
	   JS-driven width still accounts for it correctly with no JS change
	   needed. */
	appearance: none;
	-webkit-appearance: none;
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%237A6B72' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
	background-repeat: no-repeat;
	background-position: right 0.6rem center;
	padding: 0.4rem 1.75rem 0.4rem 0.5rem;
	border: 1px solid var(--sul-border);
	border-right: none;
	border-radius: var(--sul-radius-sm) 0 0 var(--sul-radius-sm);
	/* Tint mixed relative to the header's OWN background (--sul-header-bg,
	   .sul-header's own rule above), not a generic surface token - mixing
	   against plain --sul-surface went nearly invisible in dark mode
	   (that comment's own explanation). Mixing --sul-ink itself into
	   whatever it's sitting on moves the result AWAY from that background
	   in the right direction automatically in both themes, since --sul-ink
	   flips dark/light between them - darkens against the header's light-
	   mode bg, lightens against its dark-mode one. 12% (first pass) read
	   as indistinguishable from the header itself; 30% (second pass)
	   overshot the other way - "now darker, I hate it" - direct feedback,
	   2026-09-25. 20% as the middle ground: still a real, visible step
	   from the header's own pale background, not a heavy dark tile.
	   background-color, NOT the background shorthand - the shorthand
	   would silently reset the background-image/repeat/position set
	   above it in this same rule back to none. */
	background-color: color-mix(in srgb, var(--sul-ink) 20%, var(--sul-header-bg));
	color: var(--sul-ink);
	font: inherit;
	font-size: var(--sul-fs-sm);
}
/* Themed dropdown variant (2026-10-01) - listbox-select.js replaces the
   native category <select> with .sul-lb, so the fused-left-edge look above
   moves onto the trigger button (same tint, border, radius; the native
   select underneath is hidden). */
.sul-header__search .sul-lb__trigger {
	padding: 0.4rem 1.75rem 0.4rem 0.5rem;
	border-right: none;
	border-radius: var(--sul-radius-sm) 0 0 var(--sul-radius-sm);
	background-color: color-mix(in srgb, var(--sul-ink) 20%, var(--sul-header-bg));
}
/* The OPEN option list's own background/hover-highlight is drawn by the
   browser/OS, not fully controllable via CSS - this basic background/color
   is a best-effort a few browsers do respect for the list's resting state;
   the hover highlight color itself is native chrome regardless. */
.sul-header__search option {
	background: var(--sul-surface);
	color: var(--sul-ink);
}
/* No custom focus-visible color for the select anymore (2026-09-25) -
   three attempts (outline, outline w/ negative offset + border-radius:0,
   box-shadow:inset) all still rendered wrong on an OPEN select
   specifically ("the border does not respect the corner rounding
   again") - a native <select>'s open/focus chrome on at least one
   tested browser draws its own ring as part of the control itself,
   which author CSS doesn't fully control. Direct call: not worth a
   fourth attempt. outline:none with no replacement (not just deleting
   the rule) - deleting it outright would fall back to 01-base.css's
   global ink-colored ring, reintroducing the very first version of this
   same overshoot problem; this suppresses that too and leaves whatever
   the browser draws natively for an open select, undisturbed. */
.sul-header__search select:focus-visible {
	outline: none;
}
/* input/button never had their own focus-visible override, unlike select
   above - still fell back to 01-base.css's global ink-colored ring
   (positive offset, its own border-radius), rendering as a black
   overshooting box on the input specifically (live screenshot,
   2026-09-25 - "remove this active effect on header search"). Same
   box-shadow:inset treatment already proven on .sul-discover-search's
   own input/button (12-directory.css) - not a native popup control like
   select, so this one actually works cleanly here. */
.sul-header__search input[type="search"]:focus-visible,
.sul-header__search button:focus-visible {
	outline: none;
	box-shadow: inset 0 0 0 2px var(--sul-guava-strong);
}
.sul-header__search input[type="search"]::placeholder { text-overflow: ellipsis; }
.sul-header__search input[type="search"] {
	/* flex:1 1 auto (not its own fixed/clamped width) - this is the piece
	   that absorbs the select's width changes, see .sul-header__search's
	   own comment above. */
	flex: 1 1 auto;
	min-width: 0;
	padding: 0.4rem 0.7rem;
	border: 1px solid var(--sul-border);
	border-right: none;
	border-radius: 0;
	background: var(--sul-surface);
	color: var(--sul-ink);
	font: inherit;
	font-size: var(--sul-fs-sm);
}
/* Icon-only (2026-09-30, direct request - swap the "Search" text for a
   magnifying-glass icon, same SVG single-sulitista_store.php's own
   in-input search icon already uses) - padding tightened from the old
   text button's own (wide enough for the word "Search") to something
   closer to square around a 16px icon, and flex-centered so the icon
   doesn't inherit a text button's baseline alignment.
   [type="submit"] specifically (not just `button`) - once the mobile
   overlay's own × close button joined this form as a second <button>,
   the plain element selector matched it too and out-specified its own
   dedicated .sul-header__search-close rule (a bare class loses to
   class+element on specificity) — every property this rule sets
   silently overrode the close button's "no background, borderless"
   look (2026-09-30 live screenshot: the × rendered as a second pink
   submit-style button instead of a bare icon). Scoping to the type
   attribute leaves the close button alone entirely instead of winning
   a specificity fight against it. */
.sul-header__search button[type="submit"] {
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 0.4rem 0.7rem;
	border: 1px solid var(--sul-border);
	border-radius: 0 var(--sul-radius-sm) var(--sul-radius-sm) 0;
	background: var(--sul-btn-bg);
	color: var(--sul-btn-fg);
	cursor: pointer;
}
.sul-header__search button[type="submit"]:hover { background: var(--sul-btn-bg-hover); }
/* One shared height for every piece of the search (2026-09-30: the icon buttons
   read taller than the select and input beside them when each was sized by its
   own padding). 40px since the two-row header (2026-10-14): row 2 is 64px tall
   and the search is its centre, so it reads as the main control. border-box
   (01-base.css) keeps padding from inflating it. */
.sul-header__search select,
.sul-header__search .sul-lb__trigger,
.sul-header__search input[type="search"],
.sul-header__search button[type="submit"] {
	height: 40px;
}
/* A store page's own bar (2026-10-15): it searches that store only, so it has no category pick and the
   input takes the left corners the select used to round. The spacer is for a store with no products
   (nothing to search): it keeps the Saved heart on the right. In the owner's edit mode the bar is hidden
   (data-sul-ie-hide), and the logo takes the free space so the heart stays on the right. */
.sul-header__search--store input[type="search"] { border-radius: var(--sul-radius-sm) 0 0 var(--sul-radius-sm); }
.sul-header__search--none { height: 0; }
body.sul-ie-on .sul-header .sul-wordmark,
body.sul-ie-on .sul-header .sul-header__logo { margin-right: auto; }
.sul-menu {
	list-style: none;
	display: flex;
	gap: var(--sul-space-4);
	margin: 0;
	padding: 0;
}
.sul-menu a {
	color: var(--sul-ink);
	text-decoration: none;
	font-weight: 600;
	font-size: var(--sul-fs-sm);
}
.sul-menu a:hover { color: var(--sul-guava-strong); }

/* Row 1 (2026-10-14, the two-row header; direct request, modelled on Shopee):
   the account and the settings. NOT sticky and not inside .sul-header: a sticky
   child cannot leave its parent, so row 1 is its own element and scrolls away,
   while row 2 (.sul-header) keeps the old sticky header's height, so every
   offset that clears the header stays right. The same --sul-header-bg token as
   row 2, set again here because that variable is declared on .sul-header, and
   a hairline between the two. Wide screens only; a phone gets the dropdown. */
.sul-topbar {
	--sul-header-bg: color-mix(in srgb, var(--sul-ink) 5%, var(--sul-surface-2));
	/* Row 1's share of the header's vertical gradient: from the header tint down to 41% of the
	   way to the page colour, where row 2 (.sul-header) picks it up. Built from tokens, so light,
	   guarded dark and [data-theme] all resolve. See the note on .sul-header. A first version
	   (a guava-to-marigold sweep on row 1 only, PR #732) was dropped the same day. */
	background: linear-gradient(180deg, var(--sul-header-bg), color-mix(in srgb, var(--sul-header-end) 41%, var(--sul-header-bg)));
	border-bottom: 1px solid var(--sul-border);
	font-size: var(--sul-fs-sm);
}
.sul-topbar__inner {
	display: flex;
	align-items: center;
	gap: var(--sul-space-4);
	min-height: 44px;
}
.sul-topbar__nav .sul-menu { gap: var(--sul-space-5, var(--sul-space-4)); }
.sul-topbar__account {
	display: flex;
	align-items: center;
	gap: var(--sul-space-4);
	margin-left: auto;
}

/* Links in the account group, in row 1 and in the phone dropdown alike. */
.sul-topbar__account a,
.sul-header__account a {
	color: var(--sul-ink);
	text-decoration: none;
	font-weight: 600;
}
.sul-topbar__account a:hover,
.sul-header__account a:hover { color: var(--sul-guava-strong); }
/* "Start your store" is a real .sul-btn, but the link rules above (0,1,1)
   outrank .sul-btn's own color (0,1,0), so the button got ink text on its
   guava fill (and guava-strong text on hover, nearly invisible). Restore the
   button's own foreground token in all three theme states. */
.sul-topbar__account a.sul-btn,
.sul-topbar__account a.sul-btn:hover,
.sul-header__account a.sul-btn,
.sul-header__account a.sul-btn:hover {
	color: var(--sul-btn-fg);
}
/* Quieter than the rest of the account group (2026-09-30) - "Log out" is a
   rare, low-stakes action that doesn't need the same visual weight as
   Dashboard/Sign in/Start your store beside it. */
.sul-header__account a.sul-header__account-logout {
	color: var(--sul-muted);
	font-weight: 500;
}
.sul-header__account a.sul-header__account-logout:hover { color: var(--sul-ink); }
/* In row 1 muted text is only about 4.2:1 on the header tint (under 4.5): the normal text
   colour at the lighter weight keeps it quieter than Dashboard without failing contrast. */
.sul-topbar__account a.sul-header__account-logout {
	color: var(--sul-ink);
	font-weight: 500;
}
.sul-topbar__account a.sul-header__account-logout:hover { color: var(--sul-guava-strong); }

/* The heart's own list, at the right end of row 2 for a signed-in store owner. */
.sul-header__saved {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	flex: none;
	width: 40px;
	height: 40px;
	border-radius: var(--sul-radius-sm);
	color: var(--sul-ink);
}
.sul-header__saved svg { width: 22px; height: 22px; }
.sul-header__saved:hover { color: var(--sul-guava-strong); }
.sul-header__saved:focus-visible { outline: 2px solid var(--sul-guava-strong); outline-offset: 2px; }

/* Switch-style theme toggle (2026-09-19), matching Rentl-v2's own
   rentl-theme-toggle: a pill track with two dimmed static icons (sun/moon)
   and a thumb that slides to whichever side matches the CURRENT theme —
   not the old single-glyph button, which just showed one icon for what
   clicking would switch TO.

   Track = var(--sul-ink), thumb = var(--sul-surface): both tokens flip
   under dark mode, but since they're used here as a CONTRASTING PAIR (not
   for what color identity they carry) the toggle stays readable either
   way — light mode gets a dark track + white thumb, dark mode gets a
   light track + dark thumb, both still clearly a track-and-thumb control.
   This is different from the header/footer background mistake earlier
   (#239): that reused --sul-ink AS a background expecting it to stay one
   fixed color, which broke when it flipped; here the flip is fine because
   both sides of the pair move together and contrast is all that matters. */
.sul-theme-toggle {
	position: relative;
	flex: none;
	display: flex;
	align-items: center;
	width: 56px;
	height: 30px;
	padding: 3px;
	border: none;
	border-radius: var(--sul-radius-pill);
	background: var(--sul-ink);
	cursor: pointer;
}
.sul-theme-toggle:focus-visible { outline: 2px solid var(--sul-guava-strong); outline-offset: 2px; }
/* Two copies (2026-10-14): row 1 on a wide screen, row 2 on a phone. */
.sul-theme-toggle--row { display: none; }
.sul-theme-toggle__icon {
	position: absolute;
	top: 50%;
	width: 24px;
	height: 24px;
	transform: translateY(-50%);
	display: flex;
	align-items: center;
	justify-content: center;
	color: var(--sul-surface);
	opacity: .5;
}
.sul-theme-toggle__icon svg { width: 14px; height: 14px; }
.sul-theme-toggle__icon--sun { left: 3px; }
.sul-theme-toggle__icon--moon { right: 3px; }
.sul-theme-toggle__thumb {
	position: relative;
	z-index: 1;
	width: 24px;
	height: 24px;
	border-radius: var(--sul-radius-pill);
	background: var(--sul-surface);
	box-shadow: var(--sul-shadow-sm);
	display: flex;
	align-items: center;
	justify-content: center;
	transition: transform .2s ease;
}
:root[data-theme="dark"] .sul-theme-toggle__thumb { transform: translateX(26px); }
.sul-theme-toggle__thumb-icon {
	position: absolute;
	width: 14px;
	height: 14px;
	color: var(--sul-marigold-ink);
}
.sul-theme-toggle__thumb-icon--moon { display: none; color: var(--sul-ink); }
:root[data-theme="dark"] .sul-theme-toggle__thumb-icon--sun { display: none; }
:root[data-theme="dark"] .sul-theme-toggle__thumb-icon--moon { display: flex; }

/* footer — same tint + gradient-stripe treatment as the header, mirrored;
   see the comment above .sul-header. */
.sul-footer {
	margin-top: var(--sul-space-12);
	background: color-mix(in srgb, var(--sul-ink) 5%, var(--sul-surface-2));
	position: relative;
}
.sul-footer::before {
	content: "";
	position: absolute;
	left: 0;
	right: 0;
	top: 0;
	height: 3px;
	background: linear-gradient(90deg, var(--sul-calamansi) 0%, var(--sul-marigold) 50%, var(--sul-guava) 100%);
}
.sul-footer__inner {
	padding-block: var(--sul-space-8);
	display: flex;
	flex-direction: column;
	gap: var(--sul-space-8);
}
/* Four-column grid (2026-10-01, modelled on Rentl-v2's footer): brand
   column wider, three headed link groups. Phones get two columns - brand
   and the last group (Rules & support, the longest list) span full width,
   Explore and For sellers sit side by side. */
.sul-footer__cols {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: var(--sul-space-8) var(--sul-space-6);
}
.sul-footer__brand { grid-column: 1 / -1; display: flex; flex-direction: column; gap: var(--sul-space-3); }
.sul-footer__col:last-child { grid-column: 1 / -1; }
@media (min-width: 768px) {
	.sul-footer__cols { grid-template-columns: 1.6fr 1fr 1fr 1fr; align-items: start; }
	.sul-footer__brand,
	.sul-footer__col:last-child { grid-column: auto; }
}
/* No width cap: the line fills its column, and when it needs two lines they are balanced, so no single
   word is left alone on the last one. */
.sul-footer__tagline { margin: 0; font-size: var(--sul-fs-sm); line-height: 1.5; color: var(--sul-muted); text-wrap: balance; }
.sul-footer__col { display: flex; flex-direction: column; gap: var(--sul-space-3); }
.sul-footer__heading {
	margin: 0;
	font-size: var(--sul-fs-sm);
	font-weight: 700;
	color: var(--sul-ink);
	text-transform: uppercase;
	letter-spacing: 0.06em;
}
.sul-footer__col ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--sul-space-2); }
.sul-footer__col a { color: var(--sul-muted); text-decoration: none; font-size: var(--sul-fs-sm); }
.sul-footer__col a:hover { color: var(--sul-guava-strong); }
.sul-footer__bottom {
	display: flex;
	flex-wrap: wrap;
	justify-content: space-between;
	align-items: center;
	gap: var(--sul-space-3) var(--sul-space-6);
	padding-top: var(--sul-space-6);
	/* A mix of --sul-ink rather than --sul-border (2026-10-01, direct
	   request; 2px was tried and was too thick, back to 1px): --sul-border is tuned for hairlines on the page
	   background and vanished against the footer's own tint in dark mode.
	   --sul-ink flips per theme (dark on light, light on dark), so the same
	   mix is visible in both. */
	border-top: 1px solid color-mix(in srgb, var(--sul-ink) 22%, transparent);
}
.sul-footer__legal { display: flex; flex-wrap: wrap; gap: var(--sul-space-1) var(--sul-space-6); }
.sul-footer__top {
	display: inline-flex;
	align-items: center;
	gap: var(--sul-space-2);
	color: var(--sul-muted);
	font-size: var(--sul-fs-sm);
	text-decoration: none;
	white-space: nowrap;
	margin-left: auto; /* stays on the right even if it wraps to its own line on a phone */
}
.sul-footer__top:hover,
.sul-footer__top:focus-visible { color: var(--sul-guava-strong); }
.sul-footer__bottom p { margin: 0; max-width: none; font-size: var(--sul-fs-label, 0.8rem); color: var(--sul-muted); }

/* A phone (2026-10-14, the two-row header). Row 1 (.sul-topbar) is hidden and
   its links fold under the hamburger, as they did before the two-row header
   (2026-09-19, mirroring Rentl-v2's .rentl-header__hamburger / [data-nav-open]).
   Row 2 becomes two lines: the logo line (logo, the heart, the dark-mode
   switch, the hamburger) and the search on its own full-width line under it, always
   visible - the tap-to-expand magnifier (2026-09-30) is gone; Joefer asked to
   try this version. The logo line scrolls away and only the search line stays
   pinned: a negative sticky top by exactly the logo line's height. A grid with
   an explicit first row gives that height as a number, so the offset is not a
   guess. */
@media (max-width: 760px) {
	.sul-topbar { display: none; }
	/* No row 1 here, so row 2 carries the whole sweep, top tint to page colour. */
	.sul-header { background: linear-gradient(180deg, var(--sul-header-bg), var(--sul-header-end)); }
	.sul-theme-toggle--row { display: flex; }

	.sul-header { --sul-header-line1: 56px; top: calc(-1 * var(--sul-header-line1)); }
	.sul-header__inner {
		display: grid;
		grid-template-columns: 1fr auto auto auto;
		grid-template-rows: var(--sul-header-line1) auto;
		align-items: center;
		gap: 0;
		min-height: 0;
		padding-bottom: var(--sul-space-3);
	}
	.sul-header__inner > .sul-wordmark,
	.sul-header__inner > .sul-header__logo { grid-column: 1; grid-row: 1; justify-self: start; }
	.sul-header__saved { grid-column: 2; grid-row: 1; margin-left: var(--sul-space-2); }
	.sul-theme-toggle--row { grid-column: 3; grid-row: 1; margin-left: var(--sul-space-2); }
	.sul-header__hamburger { display: flex; grid-column: 4; grid-row: 1; margin-left: var(--sul-space-2); }
	/* The top margin is what keeps the search off the screen's top edge once the
	   logo line has scrolled away and only this line is pinned. */
	.sul-header__search { grid-column: 1 / -1; grid-row: 2; margin-top: var(--sul-space-2); }
	.sul-header__search--none { display: none; }

	/* The dropdown is a rounded card that floats under the header, inset from the screen edges like Rentl's
	   (2026-10-14, Joefer: option B of a mockup round, "some kind of rounded bounding box"). Rows are 48px tall
	   (a comfortable tap), each with a small guava icon, and Start your store is the one button, last. */
	.sul-header__menu {
		position: absolute;
		top: calc(100% + var(--sul-space-2));
		left: var(--sul-space-4);
		right: var(--sul-space-4);
		flex-direction: column;
		align-items: stretch;
		gap: 0;
		padding: var(--sul-space-1, 4px) 0 var(--sul-space-4);
		background: color-mix(in srgb, var(--sul-ink) 5%, var(--sul-surface-2));
		border: 1px solid var(--sul-border);
		border-radius: 14px;
		box-shadow: var(--sul-shadow);
	}
	.sul-header[data-nav-open="true"] .sul-header__menu { display: flex; }
	.sul-header__nav .sul-menu {
		flex-direction: column;
		align-items: stretch;
		gap: 0;
	}
	.sul-header__nav .sul-menu a,
	.sul-header__account a:not(.sul-btn) {
		display: flex;
		align-items: center;
		gap: var(--sul-space-3);
		min-height: 48px;
		padding: 0 var(--sul-space-4);
		border-bottom: 1px solid var(--sul-border);
		font-size: var(--sul-fs-base, 1rem);
		font-weight: 600;
		color: var(--sul-ink);
	}
	.sul-header__account a.sul-header__account-logout { color: var(--sul-ink); font-weight: 600; }
	.sul-menu-row__icon { flex: none; color: var(--sul-guava-strong); }
	.sul-header__account {
		display: flex;
		flex-direction: column;
		align-items: stretch;
		font-size: var(--sul-fs-base, 1rem);
	}
	.sul-header__account a.sul-btn {
		margin: var(--sul-space-4) var(--sul-space-4) 0;
		justify-content: center;
		text-align: center;
	}
}

/* assets/css/07-utilities.css */
/* Utilities — keep this short; prefer components. */

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

.sul-text-center { text-align: center; }
.sul-mt-6 { margin-top: var(--sul-space-6); }
.sul-mt-8 { margin-top: var(--sul-space-8); }

[hidden] { display: none !important; }

/* assets/css/08-storefront.css */
/* Storefront + listing detail. */

/* -- storefront layout: Compact (includes/storefront-theme.php, the default) --
   Lean identity row (logo + name/facts + CTAs/socials inline), then the three
   "How to order / Payment & shipping / Meetup areas" sections as separate
   cards, each tinted to match its icon accent — a quick, color-coded scan
   rather than one undifferentiated block of text. */
.sul-storefront-compact__id {
	display: flex;
	align-items: center;
	gap: var(--sul-space-4);
	flex-wrap: wrap;
	padding-bottom: var(--sul-space-4);
	border-bottom: 1px solid var(--sul-border);
	margin-bottom: var(--sul-space-2);
}
/* flex-shrink:0 on the wrapper, not just a size on the img - without it,
   the flexbox narrows this div under space pressure (a phone-width cover
   trying to fit logo + name/facts + two CTA buttons in one row), and the
   global img{max-width:100%} reset (01-base.css) then caps the img's
   width to match while its explicit height:64px stays put, squishing it
   into an oval - a real shipped bug, not just a risk. */
.sul-storefront-compact__logo { flex-shrink: 0; }
.sul-storefront-compact__logo img {
	width: 80px;
	height: 80px;
	border-radius: var(--sul-radius);
	object-fit: cover;
	border: 1px solid var(--sul-border);
}
.sul-storefront-compact__meta { flex: 1 1 260px; min-width: 0; }
.sul-storefront-compact__name { margin: 0 0 var(--sul-space-1); font-size: var(--sul-fs-h2); }
/* URL on its own line, in both layouts (below-cover and plain) — direct
   request to keep it isolated rather than run together with location/
   member-since on one line. */
/* Handle and facts line (Member since) are the same size, a little under the
   body-small token so the two lines sit comfortably beside the 80px logo. */
.sul-storefront-compact__url { font-size: 0.85rem; margin: 0 0 2px; }
.sul-storefront-compact__facts { font-size: 0.85rem; margin: 0; }
/* Bolder headline treatment (2026-09-17) — the plain-muted-text version
   read as quiet, near-invisible filler between the identity block and the
   contact clusters. Borrowing the display font ties it to the store's own
   "voice" instead of reading as another line of body copy. */
.sul-storefront-compact__tagline {
	font-family: var(--sul-font-display);
	font-weight: 700;
	font-size: 1.15rem;
	letter-spacing: -0.01em;
	color: var(--sul-ink);
	/* Top margin added (2026-09-18, direct feedback) — the cover photo
	   above only carries its own margin-bottom (space-2, 8px), so the
	   tagline sat almost flush against it. Matches the bottom margin
	   instead of leaving it at 0. */
	margin: var(--sul-space-6) 0 var(--sul-space-6);
}
/* The shipping answer beside the Payment & shipping heading
   (includes/storefront-facts.php): quiet text, a step smaller and lighter than
   the heading, in the ink colour so it stays legible on the section's tinted
   header. It may wrap on a narrow phone. */
.sul-section-ship { flex: 0 1 auto; min-width: 0; display: inline-flex; align-items: center; gap: 0.4em; font-size: var(--sul-fs-sm); font-weight: 400; color: var(--sul-ink); }
.sul-section-ship__ico { display: inline-flex; flex-shrink: 0; color: var(--sul-marigold-ink); }
/* On a phone the heading and the answer do not fit side by side, so the answer
   drops to its own line under the heading (aligned with it, past the icon) and
   loses its separator. Compact keeps the chevron on the heading's line. */
@media (max-width: 560px) {
	.sul-storefront-sidebar__section-head { flex-wrap: wrap; }
	.sul-storefront-sidebar__section-head .sul-section-ship { flex: 1 0 100%; padding-left: calc(22px + var(--sul-space-2)); margin-top: calc(-1 * var(--sul-space-2)); }
	.sul-section-ship__sep { display: none; }
}
.sul-storefront-compact__ctas .sul-contact-btn { flex: 0 1 auto; }
/* Shared spacing for the seller-contact label+icon-row groups
   ("Message us", "You'll also find us on") below the identity block -
   matches the
   tagline's own bottom margin so every one of these blocks reads at the
   same rhythm. Without this, a block with nothing after it but another
   bare, unmargined block (Also find -> Share, both below the cover) sat
   flush against it - a real shipped bug, not just a risk (.sul-share-row
   only ever had its own margin-bottom, never margin *above* it). */
.sul-storefront-compact__cluster { margin-bottom: var(--sul-space-6); }
/* Share now uses the same label + .sul-social-row icon-row pattern as
   the other two seller-contact groups above (all three read as
   one consistent rhythm instead of two matching rows and one styled
   differently) - overrides .sul-share-row's own inline flex layout,
   which stays as-is for its other use on the listing detail page.
   Compound selector, not just .sul-storefront-compact__share alone -
   that had equal specificity to .sul-share-row and lost the cascade to
   whichever rule happened to be declared later in the file, so the
   override silently did nothing (a real shipped bug, not just a risk).
   Color: tried a muted ink, then a soft guava tint intended to read as
   "related but quieter" than the socials row's full guava (sharing
   being a buyer-facing utility action, not the seller's own identity)
   - reverted (2026-09-17, direct feedback: "revert back to the loud
   pink") to park the quieter-share-icon idea for a later revisit.
   No color override left here at all now - .sul-share-icon's own base
   color (below) is the loud guava again. */
.sul-share-row.sul-storefront-compact__share { display: block; margin-bottom: var(--sul-space-6); }
/* -- cover photo (optional, free for every tier — see storefront-head-compact.php) --
   Absent by default: no placeholder, the plain __id row above is the whole
   header. Only rendered at all once a seller actually uploads one. 8:3
   (~2.67:1, matching Facebook's own ~2.63:1 cover-photo ratio — a
   narrower strip than a generic 16:9 banner), via aspect-ratio rather
   than a fixed height so it scales with the container at any width
   instead of a breakpoint-specific pixel value. */
.sul-storefront-compact__cover {
	position: relative;
	aspect-ratio: 8 / 3;
	background-size: cover;
	background-position: center;
	background-color: var(--sul-surface-2); /* shows briefly while the image itself loads */
	border-radius: var(--sul-radius);
	overflow: hidden;
	margin-bottom: var(--sul-space-2);
	/* Pulls the cover up into .sul-section's top padding (03-layout.css) —
	   the cover is the first thing on the page and shouldn't float in a
	   gap sized for a generic page's breathing room. Partial here since
	   desktop's cover is a contained card, not full-bleed, and keeps a
	   little room above it; the mobile media query below cancels it
	   completely instead, matching .sul-section's own clamp() value. */
	margin-top: -1.5rem;
}
.sul-storefront-compact__cover-scrim {
	position: absolute;
	inset: 0;
	background: linear-gradient(to top, rgba(20, 14, 17, .8), rgba(20, 14, 17, 0) 65%);
}
/* Picked over two other reviewed options (a separate tagline+CTA row
   below the cover; a lonely CTA floating right with nothing else near
   it) - the CTA sits directly on the photo, next to the identity block,
   so it's never disconnected from what it's a button "for". A solid
   button carries its own background/shadow, so it doesn't need the
   scrim's contrast protection the way plain overlaid text does. */
.sul-storefront-compact__cover-id {
	position: absolute;
	left: var(--sul-space-6);
	right: var(--sul-space-6);
	bottom: var(--sul-space-4);
	display: flex;
	align-items: flex-end;
	justify-content: space-between;
	gap: var(--sul-space-4);
}
.sul-storefront-compact__cover-identity {
	display: flex;
	align-items: flex-end;
	gap: var(--sul-space-4);
	min-width: 0;
}
/* The CTA row is a sibling of .cover, not nested inside it (markup in
   storefront-head-compact.php) - .cover has overflow:hidden and a fixed
   8:3 aspect-ratio, so anything requiring more room than the photo itself
   would just get clipped. .cover-wrap only exists to give this row a
   position:absolute anchor that matches .cover's own box (it contributes
   no in-flow height of its own once the row below is pulled out of flow),
   without being inside the clipped element. Mobile drops the absolute
   positioning entirely (760px query below) so the row renders in normal
   flow underneath the photo instead of cramming onto it - see the
   flex-shrink comment on .sul-storefront-compact__logo above for the bug
   this replaces. */
.sul-storefront-compact__cover-wrap { position: relative; }
.sul-storefront-compact__ctas--oncover {
	position: absolute;
	right: var(--sul-space-6);
	bottom: var(--sul-space-4);
}
.sul-storefront-compact__ctas--oncover .sul-contact-btn { box-shadow: 0 2px 10px rgba(0, 0, 0, .35); }
/* Mobile-only duplicate of the on-cover CTA row, placed after the tagline in
   the markup - hidden here, shown in the 760px query below. It can't be the
   same DOM node as the on-cover row above (that one has to stay a descendant
   of .cover-wrap for its absolute positioning to work), and it sits under a
   different parent than the tagline it needs to follow, so CSS order
   (siblings-only) can't move it. */
.sul-storefront-compact__ctas.sul-storefront-compact__ctas--mobile { display: none; }
.sul-storefront-compact__logo--oncover img { border: 3px solid rgba(255, 255, 255, .85); }
.sul-storefront-compact__cover-id .sul-storefront-compact__name,
.sul-storefront-compact__cover-id .sul-storefront-compact__url,
.sul-storefront-compact__cover-id .sul-storefront-compact__facts { color: #fff; }
.sul-storefront-compact__cover-id .sul-storefront-compact__url,
.sul-storefront-compact__cover-id .sul-storefront-compact__facts { opacity: .88; }

/* Full-bleed on mobile — a contained, rounded-corner banner reads as
   small/boxed-in on a narrow phone screen where there's no "reading
   width" to preserve the way the container serves on desktop. Breaks out
   of .sul-container's padding via the viewport-relative margin trick
   rather than needing to know its exact padding value. Desktop keeps the
   contained treatment (deliberate call, not revisited here — the
   full-bleed Direction A banner is what didn't land earlier). */
@media (max-width: 760px) {
	.sul-storefront-compact__cover {
		width: 100vw;
		margin-left: calc(50% - 50vw);
		margin-right: calc(50% - 50vw);
		margin-top: calc(-1 * clamp(2rem, 6vw, 4rem));
		border-radius: 0;
	}
	/* CTAs drop out of the on-photo overlay into normal flow below it -
	   see the .sul-storefront-compact__cover-wrap comment above. The facts
	   line stays on the photo: location and "Joined Oct 2026", short enough
	   to wrap cleanly in the name column (Verified is an icon on the name
	   line), so there is no separate mobile copy. */
	/* Compound selectors below, not the bare modifier class alone - each
	   has to beat a same-specificity base rule (.sul-contact-row's own
	   display:flex, declared later in the file) that would otherwise win
	   the cascade on file order alone, same trap .sul-share-row hit
	   earlier this round of work. */
	.sul-storefront-compact__ctas.sul-storefront-compact__ctas--oncover { display: none; }
	.sul-storefront-compact__ctas.sul-storefront-compact__ctas--mobile {
		display: flex;
		margin: 0 0 var(--sul-space-6);
	}
	/* No longer flex: 1 1 0 (its original value, equal-width stretched
	   buttons) - direct feedback, once Call/SMS made this a 4-button row
	   instead of the original 2: the non-uniform width this row gets for
	   free from flex: 0 1 auto (the same sizing the listing page's own
	   contact-button rows use - each button only as wide as its own
	   label needs) reads better than forcing every button to the same
	   width, "loved" the effect there and wanted it kept everywhere.
	   Nothing to override here now - .sul-storefront-compact__ctas
	   .sul-contact-btn's own flex: 0 1 auto (line 54) already applies. */
}

/* The ctas/socials row that used to sit inside __id now sits below the
   cover instead — same content, no border/padding-top since the cover
   image itself already provides the visual separation the border gave. */
.sul-storefront-compact__id--below-cover {
	border-bottom: none;
	padding-bottom: 0;
	margin-bottom: var(--sul-space-2);
}

/* Find us on / Share (2026-10-13, option B of a mockup round): one strip, the footer of the
   cover card (a dashed divider under the photo), or a small card of its own when the store
   has no cover. */
.sul-sf-strip {
	display: flex;
	justify-content: space-between;
	align-items: center;
	flex-wrap: wrap;
	gap: var(--sul-space-3) var(--sul-space-6);
	margin: var(--sul-space-4) 0 var(--sul-space-2);
	padding: var(--sul-space-3) var(--sul-space-6);
	background: var(--sul-surface);
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius);
}
.sul-sf-strip--cover { margin-top: 0; border-top: 1px dashed var(--sul-border); border-radius: 0 0 var(--sul-radius) var(--sul-radius); }
/* The cover above it gives up its bottom margin and its bottom corners. */
.sul-storefront-compact__cover-wrap:has(+ .sul-sf-strip--cover) .sul-storefront-compact__cover { margin-bottom: 0; border-radius: var(--sul-radius) var(--sul-radius) 0 0; }
.sul-sf-strip__group { display: flex; align-items: center; flex-wrap: wrap; gap: var(--sul-space-2) var(--sul-space-3); }
.sul-sf-strip__group--share { margin-left: auto; }
/* The blog link (2026-10-13, option B of a mockup round): a quiet icon and word, between the two
   groups; it shows only when the store has public posts. */
.sul-sf-strip__mid { flex: 1; min-width: 0; display: flex; justify-content: center; }
.sul-sf-strip__blog { display: inline-flex; align-items: center; gap: 5px; font-size: var(--sul-fs-sm); font-weight: 600; color: var(--sul-guava-strong); text-decoration: none; }
.sul-sf-strip__blog svg { width: 16px; height: 16px; flex-shrink: 0; }
@media (hover: hover) {
	.sul-sf-strip__blog:hover { text-decoration: underline; }
}
.sul-sf-strip__label { font-size: var(--sul-fs-sm); font-weight: 500; color: var(--sul-muted); }
.sul-sf-strip .sul-social-row { gap: var(--sul-space-3); }
@media (max-width: 760px) {
	.sul-sf-strip { column-gap: var(--sul-space-3); }
	/* Under the full-bleed cover the strip runs edge to edge too. */
	.sul-sf-strip--cover {
		margin-left: calc(50% - 50vw);
		margin-right: calc(50% - 50vw);
		padding-left: var(--sul-space-4);
		padding-right: var(--sul-space-4);
		border-left: none;
		border-right: none;
		border-radius: 0;
	}
	.sul-storefront-compact__cover-wrap:has(+ .sul-sf-strip--cover) .sul-storefront-compact__cover { border-radius: 0; }
}

/* The storefront's sections as folder tabs (2026-10-13, option A from a mockup round;
   direct request: "a file organizer look" that keeps each section's colour). The tabs carry
   the colour and the panel body is white; the open tab sits on its panel's coloured edge
   and the other tabs sit a little lower and paler. This replaced the accordion. The accordion's own reason for not being
   tabs (a buyer reading "How to order" has no cue that "Meetup areas" exists) is met by
   the strip showing every tab's name at once. In edit mode the strip is hidden and every
   panel shows, stacked with its own heading, so each can be edited in place. */
.sul-storefront-compact__sections {
	display: block;
	margin-top: var(--sul-space-8);
	margin-bottom: var(--sul-space-8);
}
.sul-sf-tabs {
	--sul-tab-hue: var(--sul-ink);
	--sul-tab-strength: 8%;
}
.sul-sf-tabs__nav {
	display: flex;
	align-items: flex-end;
	gap: 4px;
	/* The open tab overlaps the panel's top border by one pixel; that pixel sits in this
	   padding so the scroll safety net below does not clip it. */
	padding-bottom: 1px;
	margin-bottom: -1px;
	overflow-x: auto;
	scrollbar-width: none;
}
.sul-sf-tabs__nav::-webkit-scrollbar { display: none; }
.sul-sf-tabs__tab,
.sul-sf-tabs__panel {
	--sul-tab-body: color-mix(in srgb, var(--sul-tab-hue) var(--sul-tab-strength), var(--sul-surface));
	--sul-tab-line: color-mix(in srgb, var(--sul-tab-hue) 45%, var(--sul-border));
}
.sul-sf-tabs__tab--who,     .sul-sf-tabs__panel--who     { --sul-tab-hue: var(--sul-guava);     --sul-tab-strength: 20%; }
.sul-sf-tabs__tab--order,   .sul-sf-tabs__panel--order   { --sul-tab-hue: var(--sul-ink);       --sul-tab-strength: 8%; }
.sul-sf-tabs__tab--payment, .sul-sf-tabs__panel--payment { --sul-tab-hue: var(--sul-marigold);  --sul-tab-strength: 26%; }
.sul-sf-tabs__tab--meetup,  .sul-sf-tabs__panel--meetup  { --sul-tab-hue: var(--sul-calamansi); --sul-tab-strength: 24%; }
.sul-sf-tabs__tab {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	flex-shrink: 0;
	padding: 0.55rem 1rem 0.45rem;
	font: inherit;
	font-size: var(--sul-fs-sm);
	font-weight: 600;
	color: var(--sul-muted);
	white-space: nowrap;
	cursor: pointer;
	/* Paler and lower while closed. */
	background: color-mix(in srgb, var(--sul-tab-hue) calc(var(--sul-tab-strength) * 0.55), var(--sul-surface));
	border: 1px solid color-mix(in srgb, var(--sul-tab-hue) 28%, var(--sul-border));
	border-bottom: none;
	border-radius: 10px 10px 0 0;
}
.sul-sf-tabs__tab svg { width: 17px; height: 17px; flex-shrink: 0; }
.sul-sf-tabs__tab.is-active {
	position: relative;
	z-index: 1;
	margin-bottom: -1px;
	padding-top: 0.75rem;
	color: var(--sul-ink);
	background: var(--sul-tab-body);
	border-color: var(--sul-tab-line);
}
/* The tab icons keep one colour, open or closed (2026-10-13, direct request). */
.sul-sf-tabs__tab > svg { color: var(--sul-ink); }
.sul-sf-tabs__tab:focus-visible { outline: 2px solid var(--sul-ink); outline-offset: 2px; }
.sul-sf-tabs__label--short { display: none; }
.sul-sf-tabs__panel {
	display: none;
	padding: var(--sul-space-6);
	/* White body (2026-10-13, direct request): only the tabs carry the colour. The thin
	   coloured border still ties each panel to its tab. */
	background: var(--sul-surface);
	border: 1px solid var(--sul-tab-line);
	border-radius: 0 var(--sul-radius) var(--sul-radius) var(--sul-radius);
}
.sul-sf-tabs__panel.is-active { display: block; }
.sul-sf-tabs__panel > :first-child { margin-top: 0; }
.sul-sf-tabs__panel > :last-child { margin-bottom: 0; }
.sul-sf-tabs__panel ul { margin: 0; padding-left: 1.2em; }
/* The tab is the heading while reading; the heading row only shows in edit mode, except
   the shipping answer, which stays at the top of the Payment panel. */
/* While reading, the head row has no box of its own (display: contents): its heading is hidden
   and the shipping badge positions itself against the panel. Edit mode shows it as a row. */
.sul-sf-tabs__head { display: contents; }
.sul-sf-tabs__head-id { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.sul-sf-tabs__head-icon { display: inline-flex; width: 34px; height: 34px; border-radius: var(--sul-radius-pill); background: var(--sul-tab-body); align-items: center; justify-content: center; color: var(--sul-ink); }
.sul-sf-tabs__head-icon svg { width: 18px; height: 18px; }
.sul-sf-tabs__head h2 { font-size: var(--sul-fs-h3); margin: 0; }
.sul-sf-tabs__head .sul-section-ship__sep { display: none; }
/* "Ships nationwide" as a perk (2026-10-13, option C of a mockup round): a green badge on the
   panel's top edge, and a small one on the tab. "Meetup or local only" is a plain note. */
.sul-sf-tabs__panel { position: relative; }
.sul-sf-tabs__panel .sul-section-ship {
	position: absolute;
	top: 0;
	right: var(--sul-space-6);
	transform: translateY(-50%);
	z-index: 1;
	padding: 4px 11px 4px 9px;
	border-radius: var(--sul-radius-pill);
	font-size: var(--sul-fs-label);
	font-weight: 600;
	letter-spacing: 0.02em;
	white-space: nowrap;
}
.sul-sf-tabs__panel .sul-section-ship--yes { background: var(--sul-calamansi-fill); border: 1px solid var(--sul-calamansi-fill); color: #fff; }
.sul-sf-tabs__panel .sul-section-ship--no { background: var(--sul-surface); border: 1px solid var(--sul-field-border); color: var(--sul-muted); }
.sul-sf-tabs__panel .sul-section-ship__ico { color: inherit; }
/* A green dot on the Payment tab when the store ships nationwide (2026-10-13, direct request: the
   same dot on desktop and phone, after a van icon and a text pill both looked wrong). It shows
   only while the tab is closed: once it is open, the badge or band in the panel says it. */
.sul-sf-tabs__dot {
	display: block;
	position: absolute;
	top: 5px;
	right: 5px;
	width: 9px;
	height: 9px;
	border-radius: 50%;
	background: var(--sul-calamansi-fill);
}
.sul-sf-tabs__tab { position: relative; }
.sul-sf-tabs__tab.is-active .sul-sf-tabs__dot { display: none; }
@media (max-width: 599px) {
	/* The answer is a full-width band flush across the top of the panel (option C of a mockup round), so it
	   never shares the text's edge. */
	/* On a phone the dot sits on the tab's corner. */
	.sul-sf-tabs__dot { top: -2px; right: -2px; width: 10px; height: 10px; border: 1.5px solid var(--sul-surface); }
	.sul-sf-tabs__panel .sul-section-ship {
		position: static;
		transform: none;
		display: flex;
		margin: calc(-1 * var(--sul-space-4)) calc(-1 * var(--sul-space-4)) var(--sul-space-4);
		padding: var(--sul-space-2) var(--sul-space-4);
		border: none;
		border-radius: 0 calc(var(--sul-radius) - 1px) 0 0;
		font-size: var(--sul-fs-sm);
	}
	.sul-sf-tabs__panel .sul-section-ship--no { background: color-mix(in srgb, var(--sul-ink) 7%, var(--sul-surface)); }
}
@media (max-width: 599px) {
	.sul-sf-tabs__nav { gap: 3px; }
	.sul-sf-tabs__tab { padding: 0.5rem 0.5rem 0.4rem; gap: 0; }
	.sul-sf-tabs__tab.is-active { padding-top: 0.7rem; }
	.sul-sf-tabs__tab svg { display: none; }
	.sul-sf-tabs__label--long { display: none; }
	.sul-sf-tabs__label--short { display: inline; }
	.sul-sf-tabs__panel { padding: var(--sul-space-4); }
}

/* -- storefront layout: Sidebar (includes/storefront-theme.php) --
   Identity + CTA + socials as a left column card; the three content
   sections as ONE continuous card on the right, separated by icon headers
   and dividers instead of separate cards. */
.sul-storefront-sidebar {
	display: grid;
	grid-template-columns: 300px 1fr;
	gap: var(--sul-space-8);
	align-items: start;
	margin-bottom: var(--sul-space-8);
}
.sul-storefront-sidebar__id { display: flex; flex-direction: column; align-items: center; text-align: center; }
.sul-storefront-sidebar__logo img {
	width: 80px;
	height: 80px;
	border-radius: var(--sul-radius);
	object-fit: cover;
	border: 1px solid var(--sul-border);
	margin-bottom: var(--sul-space-4);
}
.sul-storefront-sidebar__name { font-size: var(--sul-fs-h2); }

.sul-storefront-sidebar__url { font-size: var(--sul-fs-sm); margin: var(--sul-space-1) 0 0; }
.sul-storefront-sidebar__tagline { margin: var(--sul-space-3) 0; }
.sul-storefront-sidebar__facts { justify-content: center; gap: var(--sul-space-2); margin-bottom: var(--sul-space-4); }
.sul-storefront-sidebar__ctas { flex-direction: column; width: 100%; margin-bottom: var(--sul-space-2); }
.sul-storefront-sidebar__share { justify-content: center; }
.sul-storefront-sidebar__divider { width: 100%; border-top: 1px solid var(--sul-border); margin: var(--sul-space-4) 0; }

/* General-sibling (~), not adjacent (+): an owner's empty section is a
   <template> element sitting between two real sections
   (includes/inline-edit.php), which would break "+" adjacency and drop the
   divider. Identical result for visitors, whose only siblings are sections. */
.sul-storefront-sidebar__section ~ .sul-storefront-sidebar__section {
	border-top: 1px solid var(--sul-border);
	margin-top: var(--sul-space-6);
	padding-top: var(--sul-space-6);
}
.sul-storefront-sidebar__section h2 { font-size: var(--sul-fs-h3); margin: 0; }
.sul-storefront-sidebar__section ul { margin: 0; padding-left: 1.2em; }
.sul-storefront-sidebar__section-head {
	display: flex;
	align-items: center;
	gap: var(--sul-space-2);
	margin-bottom: var(--sul-space-4);
}
.sul-storefront-sidebar__section-head svg { width: 22px; height: 22px; flex-shrink: 0; }
.sul-storefront-sidebar__section-head--order   svg { color: var(--sul-guava-strong); }
.sul-storefront-sidebar__section-head--payment svg { color: var(--sul-marigold-ink); }
.sul-storefront-sidebar__section-head--meetup  svg { color: var(--sul-calamansi-ink); }

@media (max-width: 760px) {
	.sul-storefront-sidebar { grid-template-columns: 1fr; }
}

/* -- storefront listings section: framed card (2026-09-18) --
   Direct feedback that font-weight + a filled search box (the prior pass
   here) still read as too quiet next to the three boldly color-blocked
   accordion sections above (How to order / Payment & shipping / Meetup
   areas) — those are solid tinted blocks, this was a plain label on the
   page's own background no matter how bold its text got. Wrapping the
   whole section — heading, search, grid, pagination — in one tinted card
   gives it the same block-level weight those sections have, in the
   store's own guava accent rather than a utility tint, so it reads as
   the page's main content, not one more strip of text. */
/* Like a Facebook Page (2026-10-12), two parts. The cover touches the site header, square at the top
   and rounded at the bottom: when the cover is the very first thing on the page the section drops its
   top padding and the cover its pull-up and top corners (a note above it keeps the old gap; the
   owner's bar is fixed at the bottom of the screen, so it is not above anything: the owner sees
   exactly the top a buyer sees). Then the Products band: everything from Products down (the blog strip, the
   filter sheet, Report) sits on a background one step off the page's own, edge to edge, with the
   profile above it on the plain page. surface-2 is that step in the light, the dark and the
   data-theme states alike. */
.sul-storefront-top { padding-bottom: var(--sul-space-6); }
.sul-section:has(> .sul-storefront-compact:first-child > .sul-storefront-compact__cover-wrap:first-child),
.sul-section:has(> .sul-ie-strip:first-child + .sul-ie-bar + .sul-storefront-compact > .sul-storefront-compact__cover-wrap:first-child) { padding-top: 0; }
.sul-section:has(> .sul-storefront-compact:first-child > .sul-storefront-compact__cover-wrap:first-child) .sul-storefront-compact__cover,
.sul-section:has(> .sul-ie-strip:first-child + .sul-ie-bar + .sul-storefront-compact > .sul-storefront-compact__cover-wrap:first-child) .sul-storefront-compact__cover {
	margin-top: 0;
	border-top-left-radius: 0;
	border-top-right-radius: 0;
}
.sul-storefront__band {
	/* Neutral, one step off the page (2026-10-13, option C of a mockup round). A guava-tinted band
	   was tried first (option B) and swapped back; the box that used to sit inside is gone either
	   way. It is close to the footer's colour, which was noted and left as it is. */
	background: var(--sul-surface-2);
	border-top: 1px solid var(--sul-border);
	padding-block: var(--sul-space-8) clamp(2rem, 6vw, 4rem);
	/* /@handle#products lands on the band's top edge, flush under the stuck header (2026-10-15). The
	   page already keeps 80px clear at the top (`html { scroll-padding-top }`, 01-base.css) and the
	   margin ADDS to it, so it is the header's height minus 80: row 2 is 64px (-16px), and on a phone
	   only the pinned search line stays, 60px (-20px; 06-header-footer.css). store-search.js works out
	   the same spot for the header search. */
	scroll-margin-top: -16px;
}
@media (max-width: 760px) {
	.sul-storefront__band { scroll-margin-top: -20px; }
}
/* The band runs down to the footer (2026-10-13): the footer's own top margin (06-header-footer.css) would
   leave a strip of plain page colour between the two on a storefront. */
body.single-sulitista_store .sul-footer { margin-top: 0; }
.sul-storefront__listings { scroll-margin-top: 90px; } /* clears the sticky header when a page link lands here (#sul-store-products) */
/* Only a layout wrapper now (the phone layout below makes it a flex row): the tinted, shadowed,
   padded box it used to be is gone. */
.sul-storefront__listings-frame { display: block; }
/* -- storefront listings search (debounced, server-side — cpt-listing.php's
   sul_ajax_store_search(), assets/js/store-search.js). Icon-input shape and
   guava-strong focus ring copied 1:1 from the dashboard's own
   .sul-dash__search-row (10-dashboard.css) so this reads as the same control
   language rather than a new one, even though the two are unrelated
   (client-side dashboard filter vs. this server-side public search). -- */
.sul-storefront__listings-head {
	display: flex;
	flex-direction: column;
	gap: var(--sul-space-3);
	margin-bottom: var(--sul-space-4);
}
.sul-storefront__listings-title { display: flex; align-items: center; gap: var(--sul-space-2); }
.sul-storefront__listings-title h2 {
	font-family: var(--sul-font-display);
	font-weight: 700;
	font-size: var(--sul-fs-h2);
	margin: 0;
	color: var(--sul-ink);
}
.sul-storefront__listings-icon {
	width: 34px;
	height: 34px;
	border-radius: var(--sul-radius-pill);
	background: var(--sul-guava-strong);
	color: #fff;
	display: flex;
	align-items: center;
	justify-content: center;
	flex-shrink: 0;
}
.sul-storefront__listings-icon svg { width: 17px; height: 17px; }
.sul-storefront__listings-count {
	background: var(--sul-surface);
	border-radius: var(--sul-radius-pill);
	padding: 2px var(--sul-space-2);
	font-size: var(--sul-fs-sm);
	font-weight: 600;
	color: var(--sul-guava-strong);
}
@media (min-width: 560px) {
	.sul-storefront__listings-head { flex-direction: row; align-items: center; justify-content: space-between; }
}
.sul-store-search { position: relative; width: 100%; max-width: 320px; }
.sul-store-search__icon {
	position: absolute;
	left: 0.7rem;
	top: 50%;
	transform: translateY(-50%);
	color: var(--sul-guava-strong);
	pointer-events: none;
	display: flex;
}
.sul-store-search__icon.is-loading { animation: sul-store-search-spin 0.8s linear infinite; }
@keyframes sul-store-search-spin { to { transform: translateY(-50%) rotate(360deg); } }
.sul-store-search__input {
	font: inherit;
	font-size: var(--sul-fs-sm);
	width: 100%;
	box-sizing: border-box;
	/* The same shape as the Condition / Status / Sort dropdowns beside it (2026-10-15, Joefer: "the pill styling needs
	   to go"): a rounded rectangle (--sul-radius-sm), the dropdowns' own border colour and height (38px), and no
	   shadow, so the row reads as one set of controls. */
	height: 38px;
	padding: 0 0.8rem 0 2.15rem;
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius-sm);
	/* Plain --sul-surface (white), not --sul-surface-2 — now that the whole
	   section sits inside .sul-storefront__listings-frame's guava tint, a
	   white field is what stands out against that background; surface-2
	   was picked for the old plain-page background and would blend into
	   the tint instead. */
	background: var(--sul-surface);
	box-shadow: none;
	color: var(--sul-ink);
}
.sul-store-search__input:focus {
	border-color: var(--sul-guava-strong);
	outline: none;
	box-shadow: 0 0 0 3px color-mix(in srgb, var(--sul-guava-strong) 12%, transparent);
}
/* Filter row (2026-10-01, direct request: add Sort by and decide the
   layout). Row one is the title and the search box (the original header);
   row two, .sul-store-bar, holds Condition/Status/Authenticity on the left
   and Sort by on the right - one line with all three filters plus Sort
   would have squeezed the search box to a sliver. The dropdowns are the
   same ones Discover has, in Discover's own styling (.sul-discover-filter /
   .sul-discover-sort in 12-directory.css). Three widths, since the labelled
   filters need about 620px and Sort another ~200px: >=1080px labelled;
   900-1079px the filters' visible labels dropped (still in the DOM for
   screen readers - "Any Condition" says what each one is); <=899px the
   three fold into Discover's Filter button + bottom sheet
   (.sul-discover-mobile-filter__* classes, store-search.js drives it) on
   the left of the same row, Sort stays on the right with its label hidden,
   like Discover on a phone. The Filter button's `display` is set here with
   two classes so it out-specifies that base rule regardless of bundle
   order. */
.sul-storefront__listings-title { flex: 0 0 auto; }
.sul-store-bar {
	scroll-margin-top: 90px; /* clears the sticky header when a card pill scrolls here */
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--sul-space-4);
	margin-bottom: var(--sul-space-4);
}
.sul-store-filters { display: flex; align-items: center; gap: var(--sul-space-4); flex: 0 1 auto; min-width: 0; }
/* order + margin-top:0 reset Discover's own <=600px layout rules for these
   same classes (12-directory.css gives .sul-discover-sort order:5 and the
   Filter trigger order:6 plus a top margin, for ITS three-row mobile
   layout) - here they must stay Filter-left / Sort-right on one line. */
.sul-store-bar .sul-store-sort { margin: 0 0 0 auto; order: 2; flex: 0 0 auto; }
.sul-store-sort .sul-store-sort__icon { display: none; }
.sul-store-bar .sul-store-filter-trigger { display: none; flex: 0 0 auto; order: 1; margin-top: 0; }
.sul-store-filters { order: 0; }
/* The filters' visible labels are gone at every width (2026-10-11, direct pick of option A
   after Category became a fourth dropdown and the row ran out of room, clipping "Sort by"):
   each dropdown already says what it is ("All categories", "Any Condition"). The labels stay
   in the page for screen readers. This replaces the earlier "labelled at 1080px and up". */
.sul-store-filters .sul-discover-filter__label {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	overflow: hidden;
	clip: rect(0 0 0 0);
	white-space: nowrap;
	border: 0;
}
@media (max-width: 899px) {
	/* One compact controls row (2026-10-01, direct pick of mockup C): title on
	   its own line, then search, Filter and Sort together - Filter and Sort
	   as 38px icon buttons. The header and the bar wrappers stop generating
	   boxes (display:contents) so the title, search and both buttons become
	   siblings in the frame's own wrapping flex row; the results block takes
	   a full line of its own. */
	.sul-storefront__listings-frame {
		display: flex;
		flex-wrap: wrap;
		align-items: center;
		gap: var(--sul-space-3) var(--sul-space-2);
	}
	.sul-storefront__listings-head,
	.sul-store-bar { display: contents; }
	.sul-storefront__listings-title { flex: 1 1 100%; }
	.sul-storefront__listings-frame > .sul-store-search,
	.sul-storefront__listings-head .sul-store-search { flex: 1 1 0; min-width: 0; width: auto; max-width: none; order: 1; }
	#sul-store-listings-results { flex: 1 1 100%; order: 4; min-width: 0; }
	/* Same 38px as the two icon buttons beside it. */
	.sul-store-search__input { height: 38px; padding-block: 0; }
	.sul-store-filters { display: none; }

	/* Filter: icon only, count badge on its corner; the word stays for
	   screen readers. */
	.sul-store-bar .sul-store-filter-trigger {
		display: inline-flex;
		position: relative;
		justify-content: center;
		width: 38px;
		height: 38px;
		padding: 0;
		order: 2;
	}
	.sul-store-filter-trigger__text {
		position: absolute;
		width: 1px;
		height: 1px;
		margin: -1px;
		padding: 0;
		overflow: hidden;
		clip: rect(0 0 0 0);
		white-space: nowrap;
		border: 0;
	}
	.sul-store-filter-trigger .sul-discover-mobile-filter__count { position: absolute; top: -6px; right: -6px; }

	/* Sort: the same 38px icon button. The real themed dropdown is laid
	   over it, invisible, so a tap opens the list; the label (icon + the
	   words for screen readers) sits underneath and ignores pointer events. */
	.sul-store-bar .sul-store-sort {
		position: relative;
		display: flex;
		align-items: center;
		justify-content: center;
		width: 38px;
		height: 38px;
		margin: 0;
		order: 3;
		border: 1px solid var(--sul-border);
		border-radius: var(--sul-radius-sm);
		background: var(--sul-surface);
	}
	.sul-store-sort .sul-discover-sort__label {
		position: absolute;
		inset: 0;
		width: auto;
		height: auto;
		margin: 0;
		clip: auto;
		overflow: visible;
		display: flex;
		align-items: center;
		justify-content: center;
		pointer-events: none;
		color: var(--sul-ink);
	}
	.sul-store-sort .sul-store-sort__icon { display: block; }
	.sul-store-sort .sul-store-sort__text {
		position: absolute;
		width: 1px;
		height: 1px;
		margin: -1px;
		padding: 0;
		overflow: hidden;
		clip: rect(0 0 0 0);
		white-space: nowrap;
		border: 0;
	}
	.sul-store-sort .sul-lb { position: absolute; inset: 0; opacity: 0; }
	.sul-store-sort .sul-lb__trigger { width: 100%; height: 100%; }
}
@media (min-width: 900px) {
	.sul-store-filter-mobile { display: none; }
}
/* Zero-match state: Clear search and filters, and (with a keyword) Search all
   of Sulitista, as two equal buttons. The ghost button gets a solid surface so
   it reads against the frame's guava tint. */
.sul-store-search__actions {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: var(--sul-space-3);
	margin-top: var(--sul-space-3);
}
.sul-store-search__actions .sul-btn--ghost { background: var(--sul-surface); }
.sul-store-search__empty {
	display: flex;
	flex-direction: column;
	align-items: center;
	text-align: center;
	gap: var(--sul-space-2);
	padding: var(--sul-space-12) var(--sul-space-4);
	color: var(--sul-muted);
}
.sul-store-search__empty svg { width: 40px; height: 40px; color: var(--sul-border); }
.sul-store-search__empty strong { color: var(--sul-ink); font-family: var(--sul-font-display); font-size: 1.1rem; }

/* -- listing card -- */
.sul-listing-card {
	display: flex;
	flex-direction: column;
	background: var(--sul-surface);
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius);
	overflow: hidden;
	box-shadow: var(--sul-shadow-sm);
	/* .sul-grid's own display:grid stretches every card in a row to the
	   tallest one's height already (default align-items: stretch, no
	   override needed) - but nothing inside the card used that extra
	   height, so a listing missing its condition pill (a real gap: not
	   every listing has one, e.g. Affiliate never shows one) left a
	   ragged bottom edge instead of an even row. transition/:hover below
	   - direct request, "no hover feedback... cheap to add". */
	transition: box-shadow 0.15s ease, transform 0.15s ease;
}
.sul-listing-card:hover { box-shadow: var(--sul-shadow); transform: translateY(-2px); }
/* Positioning context for the status/condition pills, which sit beside
   the photo link now rather than inside it (clickable pills, 2026-10-01). */
.sul-listing-card__photo { position: relative; }
.sul-listing-card__media {
	position: relative;
	display: flex;
	align-items: center;
	justify-content: center;
	/* 1:1, not 4:3 - the marketplace convention (Shopee, Lazada, Carousell,
	   Amazon, Etsy), not a blog/editorial one: a square tile treats every
	   product shape equally regardless of grid column count. */
	aspect-ratio: 1 / 1;
	/* Deliberately NOT the detail gallery's transparent --sul-image-bg
	   (15-carousel.css) - direct request to treat the grid card differently
	   from the product gallery. Same tint .sul-listing-card__noimg's "No
	   photo yet" state already uses, so a letterboxed real photo and the
	   empty state read as the same placeholder canvas, not two different
	   backgrounds. Only the top corners round here (the card's own
	   border-radius + overflow:hidden clip the media block's top edge,
	   which sits flush against the card boundary; its bottom edge is
	   interior to the card, above the body, so it stays square - direct
	   fix for a bad example where the bottom corners were rounding too). */
	background: var(--sul-surface-2);
	/* A light/white product photo (Canon battery set, white studio shot)
	   read as melding straight into the card's own white body below it -
	   reported directly against a screenshot. border-bottom + a soft
	   downward shadow give the tile a real edge regardless of what color
	   the photo itself happens to be, picked over a plain-line-only
	   option shown in the same mockup round. */
	border-bottom: 1px solid var(--sul-border);
	box-shadow: 0 2px 4px -2px color-mix(in srgb, var(--sul-ink) 18%, transparent);
}
/* No border-radius on the <img> itself (reversed from the previous fix) -
   direct request: unlike the detail gallery (15-carousel.css), a grid
   card's photo shouldn't read as its own rounded tile floating inside the
   square; it should fill the tile edge-to-edge like the placeholder states
   already do. Rounding lives on the tile (.sul-listing-card, overflow:
   hidden) alone. */
.sul-listing-card__media img {
	max-width: 100%;
	max-height: 100%;
	width: auto;
	height: auto;
	object-fit: contain;
}
/* No background of its own - .sul-listing-card__media's own flat
   var(--sul-surface-2) already shows through underneath (PR #298), so a
   separate gradient here just drifted a shade off from the exact same
   color behind a real (letterboxed) photo - reported directly, "the
   background fill color is not the same as the one on 'No photo yet'".
   One flat backdrop, not two nominally-the-same-but-visually-different
   ones. */
.sul-listing-card__noimg {
	display: flex; align-items: center; justify-content: center;
	width: 100%; height: 100%;
}
/* A plain gradient tile read as neutral, not "incomplete" - reported
   directly against a screenshot ("so it's just a blank photo?"). Explicit
   "No photo yet" text turns it into an honest deterrent instead of
   something that could pass for a deliberate design choice (field-muting/
   photo-optional round, 2026-09-22 - every listing can skip a photo now,
   so this state needs to visibly read as unfinished). */
.sul-listing-card__noimg::after {
	content: "No photo yet";
	font-size: var(--sul-fs-label);
	font-weight: 600;
	color: var(--sul-muted);
}
/* Condition, top-right (2026-09-30) - mirrors the status pill's own
   top-left spot, opposite corner so both can coexist without
   overlapping. Solid background needed here for the same reason the
   status pill already has one - sits directly on a photo, a
   transparent/bordered .sul-pill--fact wouldn't stay legible against an
   arbitrary photo color. */
.sul-listing-card__condition {
	position: absolute;
	/* Top-left (2026-10-13), where the status pill used to be: Sold and Reserved are
	   a mark over the whole photo now (includes/status-mark.php), and the top-right
	   corner is the like heart's. Above the mark so it stays readable. */
	left: var(--sul-space-2);
	top: var(--sul-space-2);
	z-index: 2;
	background: var(--sul-surface);
}
/* The Sold / Reserved mark (includes/status-mark.php): a 45% colour fill over the
   whole photo with the word across it in pure white, on a diagonal (2026-10-13). A
   word cut out of the fill was tried first and could not be read. It never takes a tap. */
.sul-status-mark { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 1; pointer-events: none; }
.sul-status-mark__fill { opacity: 0.45; }
.sul-status-mark__word { fill: #fff; }
.sul-status-mark--sold .sul-status-mark__fill { fill: var(--sul-sili); }
.sul-status-mark--reserved .sul-status-mark__fill { fill: var(--sul-marigold); }
.sul-status-mark text { font-family: var(--sul-font-display), sans-serif; font-weight: 700; letter-spacing: 0.08em; }
/* flex: 1 - absorbs the extra height .sul-grid's row-stretch gives a
   shorter card (one with no condition pill, no price, etc.), so the
   card's own bottom edge/border lines up evenly across a row instead of
   leaving blank space inside a taller neighbor's border. */
.sul-listing-card__body { padding: var(--sul-space-3) var(--sul-space-4) var(--sul-space-4); display: flex; flex-direction: column; gap: var(--sul-space-2); flex: 1; }
/* Clamped to 2 lines, truncated with an ellipsis past that - a title's
   length is the one genuinely unpredictable piece of card content, and
   letting it run free pushed price/pills/card-bottom out of alignment
   by however many extra lines one particular listing's title happened
   to wrap to. -webkit-line-clamp is non-standard but has supported
   every real browser for years (Safari/Chrome/Firefox/Edge) - the
   practical way to clamp text to a line count in plain CSS, no JS
   truncation needed.

   line-clamp only ever CAPS at 2 lines - it never RESERVES 2 lines of
   height for a title that only needs 1, so a single-line title's box
   just collapsed to its own 1-line height, which was the actual
   misalignment (reported directly against a live screenshot: two
   single-line titles sitting shorter than a neighboring 2-line one, so
   everything below still didn't line up). explicit line-height +
   min-height set to exactly 2 lines' worth fixes that - the box always
   reserves 2 lines of space regardless of how much of it the actual
   text fills. */
.sul-listing-card__title {
	font-size: 1rem;
	margin: 0;
	font-weight: 600;
	line-height: 1.3;
	min-height: calc(1rem * 1.3 * 2);
	/* Overrides the inherited text-wrap: balance from the global h1-h4
	   rule (02-typography.css) - found via live DevTools inspection, not
	   guessed. balance deliberately trades away a maximal first line to
	   even out both lines' lengths, which suits an actual page heading
	   but not a small, space-constrained card title, where fitting as
	   much as possible on line 1 (plain greedy wrapping) reads better
	   than typographic evenness. "normal" is NOT a valid text-wrap value
	   (confirmed live: DevTools flagged the first attempt as invalid, so
	   it silently never overrode the inherited balance at all) - "wrap"
	   is the actual keyword that resets both text-wrap-mode and
	   text-wrap-style back to their plain/greedy defaults. */
	text-wrap: wrap;
	display: -webkit-box;
	-webkit-line-clamp: 2;
	line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
}
.sul-listing-card__title a { color: var(--sul-ink); text-decoration: none; }
.sul-listing-card__title a:hover { color: var(--sul-guava-strong); }
/* Price + Condition/Authenticity share one row instead of stacking as
   separate lines - space-saving direct request. space-between pins price
   left / pills right when both fit; flex-wrap lets the pills drop to their
   own line on a narrow card rather than forcing an overflow or a squeeze. */
.sul-listing-card__meta {
	display: flex;
	align-items: center;
	justify-content: space-between;
	flex-wrap: wrap;
	gap: var(--sul-space-2);
}
/* Display font, matching the search-result card's identical fix
   (13-search.css) and every other price on the site (.sul-listing__price)
   - a plain-body-font 700 weight didn't stand out from the title above it. */
.sul-listing-card__price { font-family: var(--sul-font-display); font-weight: 700; font-size: 1.2rem; margin: 0; color: var(--sul-guava-strong); }
/* "Was" price (2026-09-30, discount pricing) - same compact treatment as
   the search-result card's own .sul-search-card__price-original
   (13-search.css) - no room here for the single listing page's pill, the
   strikethrough alone carries the discount signal. */
.sul-listing-card__price-original { font-family: var(--sul-font-body); font-weight: 400; color: var(--sul-muted); font-size: var(--sul-fs-sm); }
/* Compact carousel card's discount signal, on the photo now (2026-09-30
   follow-up) - the inline "X% off" text (PR #442, .sul-listing-card__discount,
   now removed) still wrapped at some price lengths, since it shared a
   row with a peso amount that has no upper bound on length. Moved to a
   photo badge instead - same top-right spot .sul-listing-card__condition
   uses on the regular card (free here, Condition never renders in
   compact mode) - a corner badge has no text it's competing with for
   room. Solid background + calamansi color, same shape/positioning as
   .sul-listing-card__status/__condition's own photo-badge treatment. */
.sul-listing-card__discount-badge {
	position: absolute;
	/* Top-left with the Condition pill (2026-10-13): top-right is the heart's. */
	left: var(--sul-space-2);
	top: var(--sul-space-2);
	z-index: 2;
	/* Solid green with white text (2026-10-01, direct request - first white
	   with green text was "so quiet", then dark-green fill was "too loud";
	   picked option D3 from a mockup round). --sul-calamansi-fill is a
	   mid green that gives white text 4.52:1 (AA); same in both themes. */
	background: var(--sul-calamansi-fill);
	border-color: var(--sul-calamansi-fill);
	color: #fff;
	font-weight: 700;
}

/* -- listing detail -- */
.sul-listing__price { font-family: var(--sul-font-display); font-weight: 700; font-size: 1.6rem; }
/* Compare price (2026-09-30, discount pricing) - <del> already carries the
   strikethrough semantically/visually; muted + smaller than the real price
   so it reads as superseded context, not a second price competing with it. */
.sul-listing__price-original { color: var(--sul-muted); font-size: var(--sul-fs-sm); }
/* Discount percentage (2026-09-30) - plain colored text, not a pill (two
   rounds of direct feedback, mockups compared live: guava/pink read as a
   warm "negative"-toned color for what's actually good news for the
   buyer, moved to calamansi - this theme's existing positive/success
   color, same one .sul-pill--available already uses; separately, "I'm
   borderline hating on pills now" - dropped the border/radius/padding
   entirely rather than just recoloring it, one fewer pill on a page that
   already has two right below it). */
.sul-listing__discount { color: var(--sul-calamansi-ink); font-weight: 600; font-size: var(--sul-fs-sm); }
/* The gallery itself — the interactive carousel + lightbox, and the
   uniform-canvas "infinite" look (transparent --sul-image-bg,
   object-fit: contain, no border/box-shadow) it's built on — now lives in
   15-carousel.css (single-sulitista_listing.php), including the
   featured-image-only fallback for a listing with no gallery items
   (.sul-listing__fallback-media). */

/* Gallery/identity split (2026-09-14, revised same day) - mirrors
   Rentl-v2's gallery/content two-column layout, without the kit/related/
   tabs/sticky-sidebar complexity Sulitista's simpler listing page has no
   equivalent of. Desktop (700px+): photos + description share the LEFT
   column (description is the one piece of content here with genuinely
   unpredictable length, so it's what actually fills the variable gap a
   landscape photo's letterboxing leaves inside the carousel's square
   canvas - 15-carousel.css), eyebrow/title/price/pills/CTA share the
   RIGHT column. Mobile: still one stacked column, but a DIFFERENT
   sequence than the desktop grouping implies - photo, then title/price/
   pills/CTA, description LAST - via the display:contents + order scheme
   below, not two copies of the markup. Deliberately NOT sticky
   (Rentl-v2's own Rent card is) - direct decision: the extra overlap/
   z-index complexity that took several iterations to get right there
   isn't worth it for what's a much shorter identity column here. */
.sul-listing-layout {
	display: flex;
	flex-direction: column;
	gap: var(--sul-space-6);
}
/* Mobile: display:contents flattens both wrappers away, so their children
   become direct flex items of .sul-listing-layout itself - which is what
   lets the order values below put description LAST on mobile (after price/
   pills/CTA) while still visually grouping it with the photos on desktop,
   without two copies of the markup. Same technique Rentl-v2 uses for its
   own gallery/content split (.rentl-gear-layout__content/__sidebar). */
.sul-listing-layout__gallery,
.sul-listing-layout__identity {
	display: contents;
}
@media (min-width: 700px) {
	.sul-listing-layout {
		flex-direction: row;
		align-items: flex-start;
		gap: var(--sul-space-8);
	}
	.sul-listing-layout__gallery,
	.sul-listing-layout__identity {
		display: flex;
		flex-direction: column;
		gap: var(--sul-space-6);
		flex: 1 1 0;
		min-width: 0;
	}
}

/* Order scheme shared by both breakpoints - not just inside an @media block
   - because the same numbers have to sort correctly two different ways at
   once: flattened into ONE sequence on mobile (1-7 in a row), and split
   across TWO independent flex columns on desktop (1+2 in the gallery
   column, 3-7 in the identity column - order only has to be consistent
   *within* each column there, which it already is). One set of values
   satisfies both without a duplicate mobile-only override. Description
   used to be #8 here, gallery-column-only content that ran last on both
   breakpoints - moved out of .sul-listing-layout entirely (2026-09-23,
   "stretch the Description to occupy the whole width") to its own
   full-width section below both columns, so there's no #8 left to give
   an order value to. */
#sul-carousel,
.sul-listing__fallback-media { order: 1; }
.sul-listing-layout__identity > .sul-page-title { order: 4; }
/* Same order as the title, not its own number in the 1-7 scheme above -
   it now sits directly after the title in the markup, and ties resolve
   by document order, so this doesn't need everything from 5 onward
   renumbered for one line that's meant to read as part of the title
   block anyway. */
.sul-listing-layout__identity > .sul-listing__sold-by { order: 4; }
/* .sul-listing__price-block (2026-09-24) wraps price-row + quantity as
   ONE flex item now - see that wrapper's own comment
   (single-sulitista_listing.php) for why. */
.sul-listing__price-block { order: 5; }
.sul-listing__condition-row { order: 6; }
/* Share (2026-10-12): straight under the Condition/Authenticity pills. Same
   order as the pills, and it follows them in the markup, so it comes next. */
.sul-listing-layout__identity > .sul-share-row { order: 6; }
/* Reserved / Sold banner (2026-10-01) - first thing in the contact block.
   Same two palettes as the status pills (.sul-pill--reserved / --sold in
   04-components.css) so the colour means the same thing everywhere. */
.sul-listing__status-banner {
	display: flex;
	gap: var(--sul-space-3);
	align-items: flex-start;
	padding: var(--sul-space-3) var(--sul-space-4);
	margin-bottom: var(--sul-space-4);
	border: 1px solid;
	border-radius: var(--sul-radius-sm);
	font-size: var(--sul-fs-sm);
	line-height: 1.45;
}
.sul-listing__status-banner svg { width: 18px; height: 18px; flex: 0 0 auto; margin-top: 1px; }
.sul-listing__status-banner--reserved {
	color: var(--sul-marigold-ink);
	background: color-mix(in srgb, var(--sul-marigold) 14%, var(--sul-surface));
	border-color: color-mix(in srgb, var(--sul-marigold) 45%, var(--sul-border));
}
.sul-listing__status-banner--sold {
	color: var(--sul-sili);
	background: color-mix(in srgb, var(--sul-sili) 9%, var(--sul-surface));
	border-color: color-mix(in srgb, var(--sul-sili) 40%, var(--sul-border));
}
.sul-listing__status-banner-link { color: inherit; font-weight: 600; text-decoration: underline; }

.sul-listing-layout__identity > .sul-contact-cluster { order: 7; }

/* Full-width, below .sul-listing-layout entirely now (2026-09-23) - see
   that block's own comment above for why. Same space-8 rhythm
   .sul-report (04-components.css) and .sul-contact-cluster already use
   for "the next major section starts here". Renamed from
   .sul-listing__description to .sul-listing-tabs (2026-09-24) - now a
   full tabbed section, not just the description alone; see that block's
   own comment (single-sulitista_listing.php) for why. */
.sul-listing-tabs { margin-top: var(--sul-space-8); }
.sul-listing__related { margin-top: var(--sul-space-8); }

/* Quantity ("N available") — plain text now, not a pill (2026-09-24,
   direct feedback: "'1 available' looks so lost here... I feel like
   [pills are] overused" - a count isn't a category the way Status/
   Condition/Authenticity are). Own line directly under the price row,
   tight spacing (margin-top) - no order value needed, it's a normal
   block-flow child of .sul-listing__price-block now, not a separate flex
   item of the identity column. */
.sul-listing__quantity { margin: 2px 0 0; font-size: var(--sul-fs-sm); }

/* Tab bar — horizontally scrollable as a safety net even with the
   shortened labels (Description/How to order/Payment/Meetup, chosen
   specifically so all four fit one row on a phone without this ever
   needing to kick in for the common case) rather than wrapping to a
   second row, which would read as broken given how it's meant to look
   like one continuous strip. No visible scrollbar - thumb-swipeable on
   touch, and there's nothing here that isn't already reachable by
   tapping a tab directly. */
.sul-listing-tabs__nav {
	display: flex;
	gap: var(--sul-space-2);
	overflow-x: auto;
	scrollbar-width: none;
	/* No baseline border across the whole row (2026-09-24, direct
	   feedback: "that long line does not look good" - it stretched a
	   full-width gray line well past the last tab into empty space). Only
	   the active tab's own colored underline (.is-active below) shows now
	   - nothing to stretch awkwardly since it's only ever as wide as one
	   tab label. */
}
.sul-listing-tabs__nav::-webkit-scrollbar { display: none; }
.sul-listing-tabs__btn {
	display: inline-flex;
	align-items: center;
	gap: 5px;
	flex-shrink: 0;
	background: none;
	border: none;
	border-bottom: 2px solid transparent;
	padding: 0.6rem 0.2rem;
	font-family: inherit;
	font-size: var(--sul-fs-sm);
	font-weight: 600;
	color: var(--sul-muted);
	cursor: pointer;
	white-space: nowrap;
}
/* 2026-09-24 - reuses sul_storefront_section_icon() (includes/storefront-theme.php)
   1:1 with each tab's own key; "we've got enough space now" once the
   panel-spacing/divider fixes landed. Sized/colored to match the label
   text it sits beside rather than the storefront accordion's own icon
   treatment (which tints per-section) - one plain color here since these
   four tabs don't have (and don't need) that section's own color coding. */
.sul-listing-tabs__btn svg { width: 15px; height: 15px; flex-shrink: 0; }
.sul-listing-tabs__btn + .sul-listing-tabs__btn { margin-left: var(--sul-space-3); }
.sul-listing-tabs__btn.is-active { color: var(--sul-guava-strong); border-bottom-color: var(--sul-guava-strong); }
/* var(--sul-space-6), not the --sul-space-5 this originally shipped with
   (2026-09-24 fix) - that token was never actually defined (the scale
   only has 1/2/3/4/6/8/12), so the padding was silently computing to
   NOTHING, not just "a bit small" - direct feedback: "that text is
   sitting way too close to the tab headers" was a real bug, not a
   values-tuning ask. */
.sul-listing-tabs__panel { padding-top: var(--sul-space-6); }
/* From 700px the row has room the phone layout did not: body-size labels,
   the storefront's full headings (Payment & shipping, Meetup areas), a
   bigger icon and looser spacing. Phones keep the short labels. */
.sul-tab-long { display: none; }
@media (min-width: 700px) {
	.sul-tab-short { display: none; }
	.sul-tab-long { display: inline; }
	.sul-listing-tabs__btn { font-size: var(--sul-fs-body); padding: 0.75rem 0.25rem; gap: 7px; }
	.sul-listing-tabs__btn svg { width: 18px; height: 18px; }
	.sul-listing-tabs__btn + .sul-listing-tabs__btn { margin-left: var(--sul-space-6); }
}
/* Carousel (2026-09-24, was a static 4-card .sul-grid) - content is
   lazy-loaded client-side (assets/js/related-carousel.js), so this only
   ever styles whatever .sul-listing-card markup that script appends into
   #sul-related-track; nothing here assumes a fixed count.
   Nav buttons overlay the track (position: relative on the wrapper)
   rather than sitting beside it as flex siblings - direct feedback/
   Shopee-style request: the old side-by-side layout ate real card width
   for two 32px columns even while the arrows sat there small and mostly
   unused. Overlaying lets the track use the full available width, and
   the arrows only grow prominent on hover/focus - see .sul-related-
   carousel__nav below. */
.sul-related-carousel { position: relative; }
.sul-related-carousel__track {
	display: flex;
	gap: var(--sul-space-3);
	overflow-x: auto;
	scroll-snap-type: x mandatory;
	scroll-behavior: smooth;
	padding-bottom: var(--sul-space-1);
	/* Scrollbar hidden - direct feedback, the native bar under the track
	   read as a stray extra scrollbar rather than part of the carousel's
	   own chrome. Prev/Next + native touch/trackpad scroll (both still
	   fully functional) don't need it visible to be discoverable. */
	scrollbar-width: none;
	-ms-overflow-style: none;
}
.sul-related-carousel__track::-webkit-scrollbar { display: none; }
/* min-width, not clamp()'s upper bound - a capped max left leftover
   space on a wide viewport once 6 cards at their max width didn't
   exactly fill the track, which is exactly what let a 6th/7th card
   visibly peek in past the "full" row (direct report against a
   screenshot). calc()'s own value is always the exact width that fills
   the track edge to edge for however wide it actually is - no leftover
   sliver, ever - and min-width is the only thing that still protects a
   narrow (phone) viewport from squeezing six illegibly-narrow cards;
   below that floor the track simply becomes a real peek-and-scroll
   strip instead, which is the intentional/different case. 6 at a time,
   not 5 (was 3, then 5) - direct feedback, offered as a way to make the
   exact-fit math work out cleaner at typical widths. */
.sul-related-carousel__track > .sul-listing-card {
	flex: 0 0 calc((100% - 5 * var(--sul-space-3)) / 6);
	min-width: 140px;
	scroll-snap-align: start;
}
/* Small/quiet at rest, grows + gains a real shadow on hover or keyboard
   focus of the whole carousel (Shopee's own rail-arrow behavior) - a
   resting-small arrow reads as "available but not shouting for
   attention," never overlapping a card's own content the way a
   constantly-full-size floating button would. Starts fully invisible
   (not just small) - same "nothing to page through yet" gate as
   .sul-carousel__thumb-nav (15-carousel.css); there's nothing to scroll
   until the track's first lazy-loaded batch actually lands and
   overflows, which is what related-carousel.js's updateNav() reveals
   (into the small resting state here, not full size). */
.sul-related-carousel__nav {
	position: absolute;
	top: 50%;
	/* left/right: 0 + translateX(-50%)/(50%) - the button's CENTER sits
	   exactly on the track's own edge (Shopee's own placement, confirmed
	   against a mockup before building), half floating outside the row
	   over the page background and half over the first/last card - not
	   inset a few px in from the edge. */
	width: 32px; height: 32px; padding: 0;
	border-radius: 50%; border: 1px solid var(--sul-field-border);
	background: var(--sul-surface); color: var(--sul-ink);
	display: flex; align-items: center; justify-content: center;
	cursor: pointer;
	z-index: 1;
	visibility: hidden;
	opacity: 0.8;
	box-shadow: var(--sul-shadow-sm);
	transition: transform 0.15s ease, opacity 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.sul-related-carousel__nav--prev { left: 0; transform: translate(-50%, -50%) scale(0.72); }
.sul-related-carousel__nav--next { right: 0; transform: translate(50%, -50%) scale(0.72); }
/* scale(1.25), not scale(1) - direct request to make the hover/focus
   enlarge more pronounced than the base 32px box, not just "back to
   its own full size". */
.sul-related-carousel:hover .sul-related-carousel__nav--prev,
.sul-related-carousel:focus-within .sul-related-carousel__nav--prev {
	transform: translate(-50%, -50%) scale(1.25);
	opacity: 1;
}
.sul-related-carousel:hover .sul-related-carousel__nav--next,
.sul-related-carousel:focus-within .sul-related-carousel__nav--next {
	transform: translate(50%, -50%) scale(1.25);
	opacity: 1;
}
.sul-related-carousel__nav svg { width: 18px; height: 18px; }
.sul-related-carousel__nav:hover:not(:disabled) { border-color: var(--sul-guava-strong); color: var(--sul-guava-strong); }
/* Fully gone, not just dimmed, the moment there's nowhere left to go
   that direction (e.g. Prev at the very start) - direct request: a
   merely-faded arrow still implied "maybe clickable" in both its small
   resting state and its hover-enlarged one; disabled means neither. */
.sul-related-carousel__nav:disabled { display: none; }

/* Seller card (2026-09-23), after the description - Shopee-inspired,
   direct request for a thin single row with real breathing room rather
   than the earlier vertical block that sat under the title. A thin
   --sul-field-border outline, not a shadow - "let's replace the shadow
   effect with a thin darker colored border" - --sul-field-border is
   this theme's own already-established "more visible than the plain
   hairline" border tone (form fields use it), reused here rather than
   introducing a new border color. Corner radius: 4px - landed on after
   mocking up five options side by side (0/4/8/10/16px) rather than
   shipping a guess again. space-between spreads identity left / stats
   right; the stats themselves get a generous gap so they read as spaced
   apart, not packed. */
.sul-listing__seller-card {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: var(--sul-space-4) var(--sul-space-8);
	margin-top: var(--sul-space-8);
	padding: var(--sul-space-4) var(--sul-space-6);
	background: var(--sul-surface);
	border-radius: 4px;
	border: 1px solid var(--sul-field-border);
	/* The whole strip is one link to the store: the store-name link stretches
	   over it with ::after (below), so the avatar, stats and empty space all
	   click through. A stretched link, not an <a> around the strip, because the
	   avatar and name are already links and links cannot nest. */
	position: relative;
}
@media (hover: hover) {
	/* The strip's own hover (tint + underlined name) is off while the pointer is
	   on something that does its own thing, a social icon or Visit store, so
	   that element's hover is the only one showing. */
	.sul-listing__seller-card:hover:not(:has(.sul-social-icon:hover, .sul-listing__seller-card__visit:hover)) { background: var(--sul-surface-2); }
}
.sul-listing__seller-card__identity { display: flex; align-items: center; gap: var(--sul-space-3); min-width: 0; }
.sul-listing__seller-card__avatar {
	flex-shrink: 0;
	display: block;
	width: 48px;
	height: 48px;
	/* A rounded square, not a circle (2026-10-12, direct request). */
	border-radius: 10px;
	overflow: hidden;
}
.sul-listing__seller-card__avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
.sul-listing__seller-card__text { min-width: 0; }
.sul-listing__seller-card__name { margin: 0; font-size: var(--sul-fs-body); font-weight: 600; }
.sul-listing__seller-card__name a { color: var(--sul-ink); text-decoration: none; }
.sul-listing__seller-card__name a:hover,
.sul-listing__seller-card:hover:not(:has(.sul-social-icon:hover, .sul-listing__seller-card__visit:hover)) .sul-listing__seller-card__name a { text-decoration: underline; }
.sul-listing__seller-card__name a::after {
	content: "";
	position: absolute;
	inset: 0;
	border-radius: 4px;
}
/* The focus ring goes around the whole strip, not just the name. */
.sul-listing__seller-card__name a:focus-visible { outline: none; }
.sul-listing__seller-card__name a:focus-visible::after {
	outline: 2px solid var(--sul-ink);
	outline-offset: 2px;
}
.sul-listing__seller-card__name .sul-badge-verified { margin-left: var(--sul-space-1); }
/* 2026-10-12 redesign (mocked up first, option A): one row. Identity on the
   left with its facts as plain text under the name; the social icons and a
   Visit store button on the right. The facts have no labels, only an icon. */
.sul-listing__seller-card__stats { display: flex; flex-wrap: wrap; gap: var(--sul-space-1) var(--sul-space-4); margin-top: var(--sul-space-1); }
.sul-listing__seller-card__actions { display: flex; align-items: center; gap: var(--sul-space-4); margin-left: auto; }
.sul-listing__seller-card__visit {
	border: 1px solid var(--sul-guava-strong);
	color: var(--sul-guava-strong);
	border-radius: 999px;
	padding: 0.4rem 1rem;
	font-size: var(--sul-fs-sm);
	font-weight: 600;
	white-space: nowrap;
	text-decoration: none;
	/* Above the stretched store link so it has a hover of its own. */
	position: relative;
	z-index: 1;
	transition: background-color 0.15s ease;
}
@media (hover: hover) {
	.sul-listing__seller-card__visit:hover { background: color-mix(in srgb, var(--sul-guava-strong) 12%, transparent); text-decoration: none; }
}
.sul-listing__seller-card__stat { display: inline-flex; align-items: center; gap: 5px; font-size: var(--sul-fs-sm); color: var(--sul-muted); }
.sul-listing__seller-card__stat-icon { width: 14px; height: 14px; flex-shrink: 0; }
/* The social icons rise above the stretched store link so each is its own tap. */
.sul-listing__seller-card__socials { position: relative; z-index: 1; }
/* Wide screens: the facts read inline ("Joined Sep 2026", "41 products"), so
   the Location word is not shown. */
.sul-listing__seller-card__stat--location .sul-listing__seller-card__lead { display: none; }
/* Phone (2026-10-12, mocked up first, option B): the header is the photo, the
   name and the social icons; under it a strip of equal cells, each a small
   caption over its value; then a full-width Visit store. The card's wrappers
   use display: contents so the pieces can be placed on one grid. */
@media (max-width: 599px) {
	.sul-listing__seller-card {
		display: grid;
		grid-template-columns: 1fr;
		align-items: center;
		gap: 0;
		padding: var(--sul-space-4);
		overflow: hidden;
	}
	.sul-listing__seller-card:has(.sul-listing__seller-card__avatar) { grid-template-columns: auto 1fr; column-gap: var(--sul-space-3); }
	.sul-listing__seller-card__identity,
	.sul-listing__seller-card__text,
	.sul-listing__seller-card__actions { display: contents; }
	.sul-listing__seller-card__avatar { grid-column: 1; grid-row: 1 / span 2; }
	.sul-listing__seller-card__name { grid-column: -2 / -1; grid-row: 1; }
	.sul-listing__seller-card__socials { grid-column: -2 / -1; grid-row: 2; margin-top: var(--sul-space-2); justify-self: start; }
	.sul-listing__seller-card__stats {
		grid-column: 1 / -1;
		grid-row: 3;
		display: flex;
		flex-wrap: nowrap;
		gap: 0;
		margin: var(--sul-space-4) calc(-1 * var(--sul-space-4)) 0;
		background: var(--sul-surface-2);
		border-top: 1px solid var(--sul-border);
	}
	.sul-listing__seller-card__stat {
		flex: 1 1 0;
		min-width: 0;
		flex-direction: column;
		align-items: flex-start;
		gap: 0;
		padding: var(--sul-space-2) var(--sul-space-3);
	}
	.sul-listing__seller-card__stat--location { flex-grow: 1.5; }
	.sul-listing__seller-card__stat + .sul-listing__seller-card__stat { border-left: 1px solid var(--sul-border); }
	.sul-listing__seller-card__stat-icon { display: none; }
	.sul-listing__seller-card__stat--location .sul-listing__seller-card__lead { display: inline; }
	.sul-listing__seller-card__lead { font-size: var(--sul-fs-label); text-transform: capitalize; }
	.sul-listing__seller-card__lead--after { order: -1; }
	.sul-listing__seller-card__val { font-size: var(--sul-fs-sm); font-weight: 600; color: var(--sul-ink); overflow-wrap: anywhere; }
	.sul-listing__seller-card__visit { grid-column: 1 / -1; grid-row: 4; margin-top: var(--sul-space-3); text-align: center; padding: 0.55rem 1rem; }
}

/* -- seller contact cluster (2026-09-14) --
   Shared between the listing detail page (.sul-listing-layout__identity,
   status-gated: only rendered for an available/reserved listing) and the
   storefront header (single-sulitista_store.php, unconditional - a store
   itself has no "sold" state). Replaces the old single silently-prioritized
   "Message seller" button: one button per contact method the seller
   actually set (never more than one is hidden in favor of the other), plus
   a row of chips for their other socials (includes/brand-icons.php,
   includes/meta-store.php's SUL_SOCIALS whitelist). */
.sul-contact-cluster { margin-top: var(--sul-space-8); }
.sul-cluster-label {
	font-size: var(--sul-fs-label);
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	color: var(--sul-muted);
	margin: 0 0 var(--sul-space-2);
}
.sul-cluster-label:not(:first-child) { margin-top: var(--sul-space-4); }

/* --sul-contact-scale (default 1, full size) - inherited from
   .sul-contact-row/.sul-affiliate-cta__messages down to every
   .sul-contact-btn inside, set inline by contact-buttons.js only when a
   row's buttons would otherwise wrap. Every scalable dimension below
   (font-size, padding, gaps, icon size) multiplies against it, so a row
   shrinks by exactly the amount it needs to fit one line - not a fixed
   "compact" step - and a row with room to spare never shrinks past 1.
   Direct feedback on the earlier fixed-step version: "I think it's too
   small. Is it not possible to resize based on the current width and
   then fit that width?" */
.sul-contact-row { display: flex; flex-wrap: wrap; gap: calc(var(--sul-space-2) * var(--sul-contact-scale)); --sul-contact-scale: 1; }
.sul-contact-btn {
	/* Not flex: 1 1 150px (its old value) - a lone button wrapping onto
	   its own line (e.g. a 4th contact method after 3 already fill the
	   row) would flex-grow to fill that whole line by itself, reported
	   directly as a giant stretched SMS button sitting under three
	   normal-sized ones. Every real usage of .sul-contact-btn already
	   overrode this away (.sul-storefront-compact__ctas,
	   .sul-affiliate-cta__messages) except this one base rule, which
	   only a plain .sul-contact-row (Direct listings' "Message us") ever
	   actually used - content-sized to match how Affiliate listings'
	   "Have a question first?" row already looked. */
	flex: 0 1 auto;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: calc(var(--sul-space-2) * var(--sul-contact-scale));
	/* Not font: inherit (its old value) - that shorthand resets every
	   font property to inherited, leaving no base size here to scale
	   against. An explicit 1rem is what this context already inherited
	   in practice, just no longer implicit. */
	font-size: calc(1rem * var(--sul-contact-scale));
	font-weight: 600;
	color: var(--sul-btn-fg);
	background: var(--sul-btn-bg);
	border: none;
	border-radius: var(--sul-radius-sm);
	padding: calc(0.72rem * var(--sul-contact-scale)) calc(1rem * var(--sul-contact-scale));
	text-decoration: none;
	cursor: pointer;
	transition: background-color 0.15s ease;
}
.sul-contact-btn:hover { background: var(--sul-btn-bg-hover); }
.sul-contact-btn svg { width: calc(18px * var(--sul-contact-scale)); height: calc(18px * var(--sul-contact-scale)); flex-shrink: 0; }

/* Outbound "View on retailer's site" (single-sulitista_listing.php,
   Sulitista Affiliate Links, CTA-hierarchy round 2026-09-22) - the real
   primary action on an affiliate listing (completing checkout), so it
   keeps the same filled .sul-contact-btn styling "Message us" uses
   elsewhere rather than a visually secondary outline variant - reported
   directly that the earlier outline treatment had the hierarchy
   backwards. Its own centered block, not sharing .sul-contact-row's
   full-width flex-row layout. */
.sul-affiliate-cta {
	display: flex;
	flex-direction: column;
	/* Left-aligned (2026-09-25, direct request - was centered) - matches
	   .sul-affiliate-cta__divider below, which already had to un-inherit a
	   center alignment from here for the SAME reason on an earlier round;
	   that override is now redundant (this already defaults to left) but
	   left in place as an explicit statement rather than relying on
	   inheritance alone. */
	align-items: flex-start;
	text-align: left;
	/* Its own default, same reason .sul-contact-row/.sul-affiliate-cta__messages
	   declare one - the "View on retailer's site" button below is a
	   .sul-contact-btn but not inside either of those, so without this its
	   font-size/padding/gap calc()s would reference an undefined custom
	   property and go invalid. */
	--sul-contact-scale: 1;
}
/* .sul-contact-btn's own flex: 1 1 150px (08-storefront.css, below) was
   written for its original context - a row-direction .sul-contact-row,
   where flex-grow just balances width among siblings. Reused verbatim
   here inside a COLUMN-direction container, that same rule makes the
   button grow to fill the column's whole height instead of sizing to its
   own content - confirmed live (a giant square block, not a normal
   button). Same override .sul-affiliate-cta__messages already applies to
   ITS buttons below, just missed here on the main button itself. */
.sul-affiliate-cta > .sul-contact-btn { flex: 0 0 auto; }
.sul-affiliate-cta__hint {
	font-size: var(--sul-fs-label);
	color: var(--sul-muted);
	margin: var(--sul-space-2) 0 0;
}
.sul-affiliate-cta__divider {
	width: 100%;
	border-top: 1px solid var(--sul-border);
	margin-top: var(--sul-space-4);
	padding-top: var(--sul-space-4);
	/* Left-aligned - .sul-affiliate-cta itself is left-aligned too now
	   (2026-09-25), so this is redundant with inheritance alone, but kept
	   explicit rather than relying on that staying true forever. */
	text-align: left;
}
.sul-affiliate-cta__messages { display: flex; flex-wrap: wrap; justify-content: flex-start; gap: calc(var(--sul-space-2) * var(--sul-contact-scale)); --sul-contact-scale: 1; }
/* Un-stretched, unlike .sul-contact-row's own buttons - this row sits
   inside a narrower block, not spanning the full card width. */
.sul-affiliate-cta__messages .sul-contact-btn { flex: 0 1 auto; }

/* History of this rule, kept because the same complaints are likely to
   resurface if it's ever rebuilt from scratch: up to 4 contact buttons
   (Messenger, WhatsApp, Call, SMS) in one row don't fit their labels once
   enough methods are configured. Tried, in order: icon-only below a
   viewport-width breakpoint (disliked - "I don't like 'just icons'");
   a FIXED smaller size below a width breakpoint (disliked twice more -
   shrank every seller's row regardless of how many methods they actually
   had, so a 1-2-button row looked cramped for no reason; also went
   through two wrong breakpoint values chasing a misread of "too
   aggressive"); this settled on `--sul-contact-scale` (declared just
   above, defaulted to 1) instead - contact-buttons.js measures each
   row's real natural width against its available space and sets this
   variable to the EXACT ratio needed to fit one line, only when a row
   would otherwise wrap, so nothing shrinks further than it has to and a
   row with room to spare never shrinks at all. */

.sul-social-row { display: flex; flex-wrap: wrap; gap: var(--sul-space-4); align-items: center; }
/* Bare icon, no circle - a live mockup round (2026-09-14) compared this
   against the original 40px bordered chip (still the shape Rentl-v2's own
   .rentl-social-icon uses) and picked bare + larger instead: 30px, no
   border/background, a simple opacity dip on hover rather than the chip's
   invert-to-solid-fill, since there's no longer a background shape to
   invert. */
.sul-social-icon { color: var(--sul-guava-strong); display: inline-flex; transition: opacity 0.15s ease; }
.sul-social-icon svg { width: 22.5px; height: 22.5px; display: block; } /* 75% of the first-cut 30px, per direct feedback */
@media (hover: hover) and (pointer: fine) {
	.sul-social-icon:hover { opacity: 0.72; }
}

/* -- share row (2026-09-14) --
   Deliberately NOT placed next to .sul-social-row above, even though both
   are icon rows about "other places to find this" - confirmed via a live
   mockup that grouping them read as the same category when they're not:
   .sul-social-row is the SELLER's own identity (inbound - who they are),
   this is a buyer pushing the listing/store outward to their own network
   (outbound). On the listing page this sits in the gallery column instead
   (order: 2 above, right after the photos), physically separate from
   .sul-social-row in the identity column. On the storefront page it sits
   up by the header, same separation principle, just no gallery column to
   anchor to there. Secondary/utility styling (bordered pill, not a solid
   guava fill) - this isn't the primary action the CTA buttons are.
   No pill/circle chrome (direct request, 2026-09-14) - plain icon +
   text, no background/border, hover shifts color instead of a fill. */
.sul-share-row { display: flex; align-items: center; gap: var(--sul-space-3); flex-wrap: wrap; }
.sul-share-btn {
	display: inline-flex;
	align-items: center;
	/* space-2, not space-1 (its old value) - direct feedback that the icon
	   needed a bit more breathing room from the "Share" label now that
	   it's a visually distinct element (below), not glued-on chrome. */
	gap: var(--sul-space-2);
	font: inherit;
	font-size: var(--sul-fs-sm);
	font-weight: 600;
	color: var(--sul-ink);
	background: none;
	border: none;
	padding: 0;
	cursor: pointer;
	transition: color 0.15s ease;
}
.sul-share-btn:hover { color: var(--sul-guava-strong); }
/* The icon is wrapped in its own .sul-share-icon span (single-sulitista_
   listing.php, storefront-head-sidebar.php) - same class the standalone
   Facebook/Messenger icons use, reused rather than duplicated, so this
   icon gets their exact color AND hover treatment (dims to 0.65 opacity
   on its own :hover) instead of only reacting to .sul-share-btn's
   button-wide hover the way it did before. Direct feedback: the icon
   needs to behave like a sibling icon, not a part of the text label it
   sits next to - still one clickable button overall (same function),
   just two visually/behaviorally distinct pieces inside it now. */
.sul-share-icon {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	color: var(--sul-guava-strong);
	transition: opacity 0.15s ease;
	/* Safe no-op on the <a> tags this was originally written for; needed
	   now that the storefront's "Share this store" button (below) also
	   uses this class, to strip the UA's default button chrome. */
	background: none;
	border: none;
	padding: 0;
	cursor: pointer;
}
.sul-share-icon:hover { opacity: 0.65; }
.sul-share-icon svg { width: 22px; height: 22px; }
.sul-share-feedback { font-size: var(--sul-fs-sm); color: var(--sul-muted); }

/* Messenger's copy-the-message step on a product page (assets/js/contact-buttons.js): a small
   box with the message, a Copy button and an Open Messenger button. */
.sul-copy-field { position: fixed; top: 0; left: 0; width: 1px; height: 1px; padding: 0; border: 0; opacity: 0; font-size: 16px; }
.sul-copy-box {
	position: fixed;
	inset: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: var(--sul-space-4);
	background: rgba(20, 14, 17, .55);
	z-index: 1001;
}
.sul-copy-box__panel {
	position: relative;
	width: 100%;
	max-width: 420px;
	max-height: calc(100vh - 2 * var(--sul-space-4));
	overflow-y: auto;
	box-sizing: border-box;
	padding: var(--sul-space-6);
	background: var(--sul-surface);
	border: 1px solid var(--sul-border);
	border-radius: 16px;
	box-shadow: 0 18px 50px -12px rgba(20, 14, 17, .45);
}
.sul-copy-box__close {
	position: absolute;
	top: var(--sul-space-3);
	right: var(--sul-space-3);
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 32px;
	height: 32px;
	padding: 0;
	background: none;
	border: 0;
	border-radius: var(--sul-radius-sm);
	color: var(--sul-muted);
	cursor: pointer;
}
.sul-copy-box__close:hover { background: var(--sul-surface-2); color: var(--sul-ink); }
.sul-copy-box__head { display: flex; align-items: flex-start; gap: var(--sul-space-3); padding-right: var(--sul-space-6); }
.sul-copy-box__icon {
	flex-shrink: 0;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 40px;
	height: 40px;
	border-radius: 10px;
	background: var(--sul-surface-2);
	border: 1px solid var(--sul-border);
	color: var(--sul-ink);
}
.sul-copy-box__icon svg { width: 22px; height: 22px; }
.sul-copy-box__title { margin: 0; font-family: var(--sul-font-display); font-weight: 700; font-size: 1.1rem; color: var(--sul-ink); }
.sul-copy-box__sub { margin: var(--sul-space-1) 0 0; font-size: var(--sul-fs-sm); color: var(--sul-muted); }
/* The message, drawn like a chat bubble: it is what will be pasted. */
.sul-copy-box__text {
	display: block;
	width: 100%;
	box-sizing: border-box;
	margin-top: var(--sul-space-4);
	padding: var(--sul-space-3) var(--sul-space-4);
	font: inherit;
	font-size: 16px; /* 16px stops iOS zooming into the field */
	line-height: 1.45;
	color: var(--sul-ink);
	background: var(--sul-surface-2);
	border: 1px solid var(--sul-border);
	border-radius: 16px 16px 16px 4px;
	overflow: hidden;
	overflow-wrap: anywhere;
	resize: none;
}
.sul-copy-box__text:focus { outline: none; border-color: var(--sul-guava-strong); box-shadow: 0 0 0 3px color-mix(in srgb, var(--sul-guava-strong) 14%, transparent); }
.sul-copy-box__steps { display: flex; flex-direction: column; gap: var(--sul-space-3); margin-top: var(--sul-space-4); }
.sul-copy-box__steps .sul-btn { justify-content: center; gap: var(--sul-space-2); }
.sul-copy-box__step {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 1.4em;
	height: 1.4em;
	border-radius: 50%;
	background: color-mix(in srgb, currentColor 18%, transparent);
	font-size: .85em;
	font-weight: 700;
}
/* Step 1 becomes a tick once the copy worked. */
.sul-copy-box__copy.is-done { background: var(--sul-calamansi-fill); border-color: var(--sul-calamansi-fill); color: #fff; }
.sul-copy-box__copy.is-done .sul-copy-box__step { font-size: 0; }
.sul-copy-box__copy.is-done .sul-copy-box__step::before { content: "\2713"; font-size: .85rem; }
.sul-copy-box__status { min-height: 1.4em; margin: var(--sul-space-3) 0 0; font-size: var(--sul-fs-sm); color: var(--sul-muted); text-align: center; }

/* assets/css/09-auth.css */
/* Auth pages — register / login. */

/* The root cause this max-width: none originally fixed (found via DevTools
   computed styles, not guessed): a bare `p { max-width: var(--sul-measure) }`
   in 02-typography.css - a readability line-length reset meant for article
   prose, wrong for UI microcopy like a form hint, which should match its own
   field's width instead. That global rule was removed entirely (2026-09-14,
   --sul-measure retired) once it turned out to be too narrow even for the
   prose it was meant for, so nothing sitewide competes for this element's
   width anymore - kept explicit here anyway, defensively, rather than
   trusting no future global paragraph rule ever reintroduces the same
   conflict. width: 100% and text-wrap: pretty stay for their own reasons
   (matching the field's own width, avoiding a lonely short last line). */
.sul-form__hint { font-size: var(--sul-fs-sm); color: var(--sul-muted); margin: 0; width: 100%; max-width: none; text-wrap: pretty; }
.sul-form__hint.is-error { color: var(--sul-sili); }
/* A hint under a row of two fields (sign-up location): sits close to the row, like a hint under one field. */
.sul-form__hint--after-row { margin-top: calc(var(--sul-space-2) * -1); }
/* The sign-up form needed more air (Joefer, 2026-10-16): a hint under a field looked like text for the next
   field. Fields are a full step apart (1.5rem), a hint sits tight under its own field, and a hint under a row
   of two fields closes up to that row. Scoped to the sign-up form; other forms keep their spacing. */
#sul-register-form { gap: var(--sul-space-6); }
#sul-register-form .sul-form__row { gap: var(--sul-space-6) var(--sul-space-4); }
#sul-register-form .sul-form__field > .sul-form__hint { margin-top: calc(var(--sul-space-1) * -1); }
#sul-register-form .sul-form__hint--after-row { margin-top: calc((var(--sul-space-6) - var(--sul-space-2)) * -1); }
/* A read-only value shown where a field normally sits (the locked location in the
   seller dashboard): reads as a value, not as an input you could edit. */
.sul-form__static { margin: 0; font-size: var(--sul-fs-body); font-weight: 600; color: var(--sul-ink); }

/* "What do you sell?" label + the inline "See what's allowed" toggle.
   Note: no trailing arrow on this or any link/CTA — Joefer's call, the
   link text itself should carry the meaning, not a directional glyph. */
.sul-form__label-row {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--sul-space-2);
}
.sul-policy-toggle {
	background: none;
	border: none;
	padding: 0;
	font: inherit;
	font-size: 12px;
	font-weight: 400;
	color: var(--sul-guava-strong);
	text-decoration: underline;
	text-underline-offset: 2px;
	cursor: pointer;
	white-space: nowrap;
}
.sul-policy-toggle:hover,
.sul-policy-toggle[aria-expanded="true"] { color: var(--sul-btn-bg-hover); }

.sul-policy-panel {
	background: var(--sul-surface-2);
	border-radius: var(--sul-radius-sm);
	padding: var(--sul-space-3) var(--sul-space-4);
	font-size: 12.5px;
	color: var(--sul-muted);
}
.sul-policy-panel p { margin: 0 0 var(--sul-space-2); }
.sul-policy-panel strong { color: var(--sul-ink); }
.sul-policy-panel ul { margin: 0 0 var(--sul-space-2); padding-left: 1.1em; }
.sul-policy-panel li { margin-bottom: var(--sul-space-1); }
.sul-policy-panel__link {
	font-size: 12.5px;
	font-weight: 600;
	color: var(--sul-guava-strong);
	text-decoration: underline;
	text-underline-offset: 2px;
}

.sul-form__check label {
	display: flex;
	gap: var(--sul-space-2);
	align-items: flex-start;
	font-weight: 400;
	font-size: var(--sul-fs-sm);
	line-height: 1.5;
}
.sul-form__check input[type="checkbox"] { margin-top: 0.2em; flex: none; }
/* the text + any inline links flow as one block beside the box */
.sul-form__check label > span { flex: 1; min-width: 0; }
.sul-form__check a { color: var(--sul-guava-strong); }

.sul-handle-input {
	display: flex;
	align-items: stretch;
	border: 1px solid var(--sul-field-border);
	border-radius: var(--sul-radius-sm);
	overflow: hidden;
	background: var(--sul-surface);
}
.sul-handle-input__prefix {
	padding: 0.65rem 0.5rem 0.65rem 0.8rem;
	color: var(--sul-muted);
	font-size: var(--sul-fs-sm);
	white-space: nowrap;
	background: var(--sul-surface-2);
	border-right: 1px solid var(--sul-border);
}
.sul-handle-input input {
	border: 0 !important;
	border-radius: 0 !important;
	flex: 1;
	min-width: 0;
}
.sul-handle-status { font-size: var(--sul-fs-sm); margin: var(--sul-space-1) 0 0; min-height: 1.2em; }
/* Nothing to say yet (the result comes after the visitor types): take no room. The field is a flex column with an
   8px gap, so a negative bottom margin cancels the second gap and the callout sits 8px under the input. The line
   stays in the page, so a screen reader still hears it when it fills. */
.sul-handle-status:empty { min-height: 0; margin: 0 0 calc(-1 * var(--sul-space-2)); }
.sul-handle-status.is-ok { color: var(--sul-calamansi-ink); }
.sul-handle-status.is-error { color: var(--sul-sili); }

.sul-password-input {
	display: flex;
	align-items: stretch;
	border: 1px solid var(--sul-field-border);
	border-radius: var(--sul-radius-sm);
	overflow: hidden;
	background: var(--sul-surface);
}
.sul-password-input input {
	border: 0 !important;
	border-radius: 0 !important;
	flex: 1;
	min-width: 0;
}
.sul-password-toggle {
	flex: none;
	background: none;
	border: 0;
	border-left: 1px solid var(--sul-border);
	padding: 0 0.9rem;
	font: inherit;
	font-size: var(--sul-fs-sm);
	font-weight: 600;
	color: var(--sul-guava-strong);
	cursor: pointer;
}
.sul-password-toggle:hover { color: var(--sul-btn-bg-hover); }

/* WP's login form, restyled to match */
#loginform, #sul-login-form {
	display: flex;
	flex-direction: column;
	gap: var(--sul-space-4);
}
#loginform label, #sul-login-form label {
	font-weight: 600;
	font-size: var(--sul-fs-sm);
	display: block;
}
#loginform input[type="text"],
#loginform input[type="password"],
#sul-login-form input[type="text"],
#sul-login-form input[type="password"] {
	font: inherit;
	width: 100%;
	padding: 0.65rem 0.8rem;
	margin-top: var(--sul-space-2);
	background: var(--sul-surface);
	color: var(--sul-ink);
	border: 1px solid var(--sul-field-border);
	border-radius: var(--sul-radius-sm);
}
#loginform .login-remember label,
#sul-login-form .login-remember label {
	font-weight: 400;
	display: flex;
	gap: var(--sul-space-2);
	align-items: center;
}
#loginform #wp-submit,
#sul-login-form #wp-submit {
	font: inherit;
	font-weight: 600;
	background: var(--sul-btn-bg);
	color: var(--sul-btn-fg);
	border: none;
	border-radius: var(--sul-radius-sm);
	padding: 0.7rem 1.15rem;
	cursor: pointer;
	width: 100%;
}
#loginform #wp-submit:hover,
#sul-login-form #wp-submit:hover { background: var(--sul-btn-bg-hover); }

/* A name that arrives from the About page (?handle=) is hard to miss on the register form (2026-10-15, option B of a
   mockup round): a result banner at the top of the form (checking, available, taken) and the handle field in a tinted
   block that pulses once. Available and taken use the Brand Guide's semantic colours (calamansi, sili), and every
   state also carries words and an icon, never colour alone. The field's highlight is NEVER guava (it looked red and
   read as "not available"): it is neutral ink while the name is being checked, then takes the SAME colours as the
   banner, green when available and red when taken (Joefer, 2026-10-15); register.js sets .is-ok / .is-taken on it. */
.sul-handle-banner {
	display: flex;
	align-items: center;
	gap: var(--sul-space-3);
	margin: 0 0 var(--sul-space-4);
	padding: var(--sul-space-3) var(--sul-space-4);
	background: var(--sul-surface-2);
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius-sm);
}
.sul-handle-banner[hidden] { display: none; }
.sul-handle-banner__icon {
	flex: none;
	width: 34px;
	height: 34px;
	border-radius: 50%;
	background: var(--sul-muted) center / 18px 18px no-repeat;
}
.sul-handle-banner__text { min-width: 0; overflow-wrap: anywhere; }
.sul-handle-banner__text strong { display: block; font-size: var(--sul-fs-body); line-height: 1.25; }
.sul-handle-banner__text span { display: block; margin-top: 2px; font-size: var(--sul-fs-sm); color: var(--sul-muted); }
.sul-handle-banner.is-ok {
	background: color-mix(in srgb, var(--sul-calamansi) 14%, var(--sul-surface));
	border-color: color-mix(in srgb, var(--sul-calamansi) 50%, var(--sul-border));
}
.sul-handle-banner.is-ok .sul-handle-banner__icon {
	background-color: var(--sul-calamansi-fill);
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12l5 5l10 -10'/%3E%3C/svg%3E");
}
.sul-handle-banner.is-taken {
	background: color-mix(in srgb, var(--sul-sili) 12%, var(--sul-surface));
	border-color: color-mix(in srgb, var(--sul-sili) 50%, var(--sul-border));
}
.sul-handle-banner.is-taken .sul-handle-banner__icon {
	background-color: var(--sul-sili);
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 6l12 12M18 6l-12 12'/%3E%3C/svg%3E");
}
.sul-form__field--spotlight {
	padding: var(--sul-space-3);
	--sul-spot: var(--sul-ink);
	background: color-mix(in srgb, var(--sul-ink) 7%, var(--sul-surface));
	border-left: 3px solid var(--sul-spot);
	border-radius: var(--sul-radius-sm);
}
.sul-form__field--spotlight .sul-handle-input { border-color: var(--sul-spot); }
/* The pulse plays when the result arrives (the animation starts with the state class). */
.sul-form__field--spotlight.is-ok {
	--sul-spot: var(--sul-calamansi-fill);
	background: color-mix(in srgb, var(--sul-calamansi) 14%, var(--sul-surface));
	animation: sul-spotlight 1.6s ease-out 1;
}
.sul-form__field--spotlight.is-taken {
	--sul-spot: var(--sul-sili);
	background: color-mix(in srgb, var(--sul-sili) 12%, var(--sul-surface));
	animation: sul-spotlight 1.6s ease-out 1;
}
.sul-form__field--spotlight .sul-handle-status { font-size: var(--sul-fs-body); font-weight: 700; }
@keyframes sul-spotlight {
	0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--sul-spot) 45%, transparent); }
	60% { box-shadow: 0 0 0 8px color-mix(in srgb, var(--sul-spot) 0%, transparent); }
	100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--sul-spot) 0%, transparent); }
}
@media (prefers-reduced-motion: reduce) {
	.sul-form__field--spotlight.is-ok,
	.sul-form__field--spotlight.is-taken { animation: none; }
}

/* The handle lock and hold notice under the register form's handle field (2026-10-15): serious and neutral (ink), not
   a hint. It says the handle cannot be changed, and that it is only held while the application is reviewed. */
.sul-handle-lock {
	display: flex;
	gap: var(--sul-space-3);
	margin-top: var(--sul-space-1);
	padding: var(--sul-space-3) var(--sul-space-4);
	background: var(--sul-surface-2);
	border: 1px solid var(--sul-border);
	border-left: 3px solid var(--sul-ink);
	border-radius: var(--sul-radius-sm);
	font-size: var(--sul-fs-sm);
}
.sul-handle-lock__icon { flex: none; align-self: flex-start; width: 22px; height: 22px; margin-top: 2px; color: var(--sul-ink); }
.sul-handle-lock p { margin: 0; color: var(--sul-muted); }
.sul-handle-lock .sul-handle-lock__title { margin: 0 0 var(--sul-space-1); font-weight: 700; color: var(--sul-ink); }

/* assets/css/10-dashboard.css */
/* Seller dashboard — mobile-first. */

/* No max-width of its own (2026-09-15, dropped an arbitrary 820px) - the
   page template already wraps this in .sul-container (03-layout.css,
   1080px, the same class the sitewide header uses for its own nav), so
   the extra cap here was just making the dashboard content narrower than
   the header sitting right above it for no documented reason - reported
   directly against a screenshot showing the mismatch at the header's own
   right edge. */
.sul-dash__loading { color: var(--sul-muted); }

/* Store profile opted back into a narrower width (2026-09-15) - unlike
   Listings/Photos' grid-shaped content, which uses the extra room, a form
   full container width just stretched its fields wide with nothing to
   fill the space usefully. 760px, not a new number - .sul-container--md's
   own value (03-layout.css), the site's one standing "readable column"
   width, same one importView()'s own two sections already use directly
   via that class rather than this modifier.

   margin-inline: auto for the same reason importView()'s own sections
   needed it (PR #174, same report pattern) - a max-width alone just
   leaves the extra space on the right once the surrounding panel is wider
   than this cap, rather than actually centering the narrower content
   inside it. */
.sul-dash__panel--narrow { max-width: 760px; margin-inline: auto; }

/* Moderation notice card */
.sul-mod-notice { border-left: 4px solid var(--sul-marigold); }
.sul-mod-notice .sul-page-title { margin-top: 0; }

.sul-dash__head {
	display: flex;
	flex-wrap: wrap;
	justify-content: space-between;
	align-items: flex-start;
	gap: var(--sul-space-4);
	padding-bottom: var(--sul-space-4);
	border-bottom: 1px solid var(--sul-border);
	margin-bottom: var(--sul-space-6);
}
.sul-dash__name { margin: 0 0 var(--sul-space-1); font-size: var(--sul-fs-h2); }
.sul-dash__state {
	display: inline-flex;
	align-items: center;
	gap: 0.35rem;
	vertical-align: middle;
	font-size: var(--sul-fs-label);
	font-weight: 600;
	text-transform: uppercase;
	letter-spacing: 0.03em;
	color: var(--sul-muted);
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius-pill);
	padding: 0.15rem 0.55rem;
}
.sul-dash__state::before { content: ""; width: 7px; height: 7px; border-radius: 999px; background: var(--sul-muted); }
/* The one genuinely "live/available" state — calamansi, same as the
   listing-status pill and the Brand Guide's reference mock's "Available"
   dot. Everything else on this badge stays neutral. */
.sul-dash__state.is-live {
	color: var(--sul-calamansi-ink);
	border-color: color-mix(in srgb, var(--sul-calamansi) 40%, var(--sul-border));
}
.sul-dash__state.is-live::before { background: var(--sul-calamansi); }
.sul-dash__handle { margin: 0; color: var(--sul-muted); font-size: var(--sul-fs-sm); }
.sul-dash__handle a { color: var(--sul-guava-strong); }

/* Header nav, reworked 2026-09-25 - see header()'s own comment
   (dashboard.js) for why Listings/Store profile/Photos/Import-export no
   longer sit as four equal-weight pills. */
.sul-dash__nav { display: flex; align-items: center; gap: var(--sul-space-2); }

/* Explicit, always-visible "Listings" button (2026-09-25) - replaced an
   earlier attempt where the store name itself doubled as "back to
   Listings" and this button only appeared on OTHER screens (both
   dropped: not discoverable enough, and popping in/out of the nav row
   on every navigation read as jarring - direct feedback on both).
   Ghost-style, not filled, but gets the same .is-active treatment as
   Settings/Tools below while it's the current view. */
.sul-dash__back-btn {
	display: inline-flex;
	align-items: center;
	gap: 0.3rem;
	font: inherit;
	font-weight: 600;
	font-size: var(--sul-fs-sm);
	padding: 0.4rem 0.7rem 0.4rem 0.5rem;
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius-pill);
	background: var(--sul-surface);
	color: var(--sul-ink);
	cursor: pointer;
}
.sul-dash__back-btn svg { width: 15px; height: 15px; flex-shrink: 0; }
.sul-dash__back-btn:hover { background: var(--sul-surface-2); }
.sul-dash__back-btn.is-active {
	background: color-mix(in srgb, var(--sul-guava-strong) 14%, var(--sul-surface));
	color: var(--sul-guava-strong);
	border-color: color-mix(in srgb, var(--sul-guava-strong) 45%, var(--sul-border));
}

.sul-dash__settings-btn {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 34px;
	height: 34px;
	flex-shrink: 0;
	background: var(--sul-surface);
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius-pill);
	color: var(--sul-ink);
	cursor: pointer;
	text-decoration: none;
}
.sul-dash__settings-btn svg { width: 16px; height: 16px; }

.sul-dash__tools { position: relative; }
.sul-dash__tools-btn {
	display: inline-flex;
	align-items: center;
	gap: 0.35rem;
	font: inherit;
	font-weight: 600;
	font-size: var(--sul-fs-sm);
	padding: 0.4rem 0.8rem;
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius-pill);
	background: var(--sul-surface);
	color: var(--sul-ink);
	cursor: pointer;
}
.sul-dash__tools-btn svg { width: 15px; height: 15px; flex-shrink: 0; }
.sul-dash__tools-btn.is-active {
	background: color-mix(in srgb, var(--sul-sili) 14%, var(--sul-surface));
	color: var(--sul-sili);
	border-color: color-mix(in srgb, var(--sul-sili) 45%, var(--sul-border));
}
/* Grouping Photos + Import/export together under one dropdown is the
   whole point of this rework - both are occasional bulk-maintenance
   TOOLS, a different category from Store profile's own ongoing
   configuration, so they share one destination rather than each getting
   separate top-level weight. */
.sul-dash__tools-menu {
	position: absolute;
	top: calc(100% + 8px);
	right: 0;
	z-index: 5;
	min-width: 180px;
	width: max-content; /* a label never wraps onto a second line next to its plan tag */
	background: var(--sul-surface);
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius-sm);
	box-shadow: var(--sul-shadow);
	overflow: hidden;
	padding: 0.3rem;
}
.sul-dash__tools-menu button {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	width: 100%;
	font: inherit;
	font-size: var(--sul-fs-sm);
	text-align: left;
	padding: 0.5rem 0.6rem;
	background: none;
	border: none;
	border-radius: calc(var(--sul-radius-sm) - 4px);
	color: var(--sul-ink);
	cursor: pointer;
}
.sul-dash__tools-menu button:hover { background: var(--sul-surface-2); }
.sul-dash__tools-menu button svg { width: 15px; height: 15px; flex-shrink: 0; color: var(--sul-muted); }

.sul-dash__bar {
	display: flex;
	justify-content: space-between;
	align-items: center;
	gap: var(--sul-space-4);
	margin-bottom: var(--sul-space-4);
	flex-wrap: wrap;
}
.sul-dash__count { margin: 0; color: var(--sul-muted); font-size: var(--sul-fs-sm); font-variant-numeric: tabular-nums; }
.sul-dash__bar-actions { display: flex; align-items: center; gap: var(--sul-space-2); flex-wrap: wrap; }

/* Trash screen (dashboard.js trashView()). Rows reuse .sul-dash-listing; only
   the countdown, the Restore button and the intro are new. */
.sul-dash-trash__title { margin: 0; font-size: var(--sul-fs-h3); }
.sul-dash-trash__intro { margin: 0 0 var(--sul-space-2); color: var(--sul-muted); }
/* "Why is there no permanent delete?" - a disclosure, not a hover tooltip, so it works on a phone. */
.sul-dash-trash__why { margin: 0 0 var(--sul-space-4); font-size: var(--sul-fs-sm); color: var(--sul-muted); }
.sul-dash-trash__why summary { cursor: pointer; width: fit-content; text-decoration: underline; text-underline-offset: 2px; }
.sul-dash-trash__why p { margin: var(--sul-space-2) 0 0; }
.sul-dash-trash__actions { flex: 0 0 auto; }
.sul-dash-trash__left.is-soon { color: var(--sul-sili); font-weight: 600; }
@media (max-width: 559px) {
	/* Same offset the listing rows use for their actions (checkbox + thumb + gaps):
	   lines a wrapped Restore button up with the title. */
	.sul-dash-trash__actions { margin-left: calc( 18px + var(--sul-space-3) + 40px + var(--sul-space-3) ); }
}

/* Listings toolbar (search/filter/sort) + bulk actions bar. Scoped rules
   rather than reusing .sul-form's input/select styling (05-forms.css) —
   that's a block-stacked, full-width layout meant for an actual <form>;
   this is a horizontal toolbar outside any form at all. */
/* Search and the filter/sort/page-size trio each get their own row
   (mocked up first: reported directly that the two kinds of control were
   blurring into one inconsistently-wrapping row on mobile) - a plain
   column of two rows, not a flex-wrap relying on width to decide the
   split. The filter row's three selects are flex:1 each, evenly split,
   so even a narrow phone fits all three without wrapping or scrolling -
   .sul-dash__filter-row's own gap plus three equal flex-basis-0 items
   is the whole mechanism, nothing width-specific to break at another size. */
.sul-dash__toolbar { display: flex; flex-direction: column; gap: var(--sul-space-2); margin-bottom: var(--sul-space-3); }
.sul-dash__search-row { position: relative; }
.sul-dash__search-icon {
	position: absolute;
	left: 0.7rem;
	top: 50%;
	transform: translateY(-50%);
	color: var(--sul-guava-strong);
	pointer-events: none;
}
.sul-dash__filter-row { display: flex; gap: var(--sul-space-2); }
.sul-dash__toolbar input[type="search"],
.sul-dash__toolbar select,
.sul-dash__bulkbar select {
	font: inherit;
	font-size: var(--sul-fs-sm);
	padding: 0.45rem 0.7rem;
	border: 1px solid var(--sul-field-border);
	border-radius: var(--sul-radius-sm);
	background: var(--sul-surface);
	color: var(--sul-ink);
}
.sul-dash__toolbar input[type="search"] {
	width: 100%;
	box-sizing: border-box;
	padding-left: 2.1rem;
}
/* padding-right alone (first attempt) verified via DevTools as correctly
   applying, yet the arrow still read as glued to the edge - a native
   <select>'s own arrow glyph is UA-drawn chrome anchored near the box
   edge, not something that reliably respects author padding-right the
   way real text does. Same fix as every other native select on the front
   end (06-header-footer.css, 12-directory.css, 05-forms.css) - strip the
   native arrow, draw our own SVG chevron at an exact position, the same
   pattern .sul-dash-listing__status just above already used. Scoped to
   the two select-only rules, not the search input, which doesn't have
   this arrow at all. */
.sul-dash__toolbar select,
.sul-dash__bulkbar select {
	appearance: none;
	-webkit-appearance: none;
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%237A6B72' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
	background-repeat: no-repeat;
	background-position: right 0.7rem center;
	padding-right: 1.75rem;
}
/* Guava-strong on focus, matching the search icon beside it - the one
   accent-per-view treatment; the three filters below stay on the plain
   --sul-field-border resting style even when focused, so guava reads as
   "the active control" rather than being spread across every field. */
.sul-dash__toolbar input[type="search"]:focus {
	border-color: var(--sul-guava-strong);
	outline: none;
	/* Not a border-width change on :focus - swapping 1px for 1.5px shifts
	   the input's content by half a pixel and reads as a small jump. The
	   glow ring alone carries the "thicker/active" feeling instead. */
	box-shadow: 0 0 0 3px color-mix(in srgb, var(--sul-guava-strong) 12%, transparent);
}
.sul-dash__filter-row select { flex: 1 1 0; min-width: 0; font-weight: 600; text-overflow: ellipsis; }
/* Mobile-only split - reported directly that the two-row layout above
   was only ever meant to fix the narrow-viewport wrapping problem, not
   replace the desktop layout too. 560px matches this file's own existing
   breakpoint (the form-row 2-column switch above). At this width and up,
   collapse back to one row - search flexes to fill the remaining space
   next to the three filters, which return to their natural compact
   width instead of stretching to fill a whole row each. The icon and the
   guava-strong focus treatment apply at every width; only the row
   structure itself is mobile-specific. */
@media (min-width: 560px) {
	.sul-dash__toolbar { flex-direction: row; flex-wrap: wrap; align-items: center; }
	.sul-dash__search-row { flex: 1 1 200px; min-width: 140px; }
	.sul-dash__filter-row { flex: 0 0 auto; }
	.sul-dash__filter-row select { flex: 0 0 auto; }
}
.sul-dash__bulkbar {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--sul-space-2);
	padding: var(--sul-space-2) var(--sul-space-3);
	margin-bottom: var(--sul-space-3);
	background: var(--sul-surface-2);
	border-radius: var(--sul-radius-sm);
}
.sul-dash__selectall { display: flex; align-items: center; gap: 0.35rem; font-size: var(--sul-fs-sm); font-weight: 600; cursor: pointer; margin: 0; }
.sul-dash__pager {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: var(--sul-space-3);
	margin-top: var(--sul-space-3);
	font-size: var(--sul-fs-sm);
}
.sul-dash__pager button {
	font: inherit;
	font-size: var(--sul-fs-sm);
	font-weight: 600;
	padding: 0.35rem 0.7rem;
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius-sm);
	background: var(--sul-surface);
	color: var(--sul-ink);
	cursor: pointer;
}
.sul-dash__pager button:disabled { opacity: 0.4; cursor: default; }
.sul-dash-listing__check { flex: 0 0 auto; width: 18px; height: 18px; cursor: pointer; accent-color: var(--sul-guava-strong); }
.sul-dash__analytics {
	margin: 0 0 var(--sul-space-4);
	padding: var(--sul-space-3) var(--sul-space-4);
	background: var(--sul-surface-2);
	border-radius: var(--sul-radius-sm);
	color: var(--sul-muted);
	font-size: var(--sul-fs-sm);
	font-variant-numeric: tabular-nums;
}

.sul-dash__back {
	font: inherit;
	background: none;
	border: none;
	color: var(--sul-guava-strong);
	cursor: pointer;
	padding: 0 0 var(--sul-space-3);
	font-weight: 600;
}

/* listing rows — one compact strip each: thumb, title+meta inline, then
   actions, all in a single flex row. No separate actions row/border-top
   divider (the previous layout — title, then a meta line below it, then a
   full-width bordered actions row — cost 3 lines of vertical space per
   listing; a seller scanning 20+ listings pays for that on every one).
   flex-wrap on the outer row is the only fallback for a narrow viewport —
   actions drop to their own line ONLY when they don't fit, not always. */
.sul-dash__listings { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--sul-space-2); }
.sul-dash-listing {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: var(--sul-space-2) var(--sul-space-3);
	padding: var(--sul-space-2) var(--sul-space-3);
	background: var(--sul-surface);
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius);
}
.sul-dash-listing__thumb {
	width: 40px; height: 40px;
	flex: 0 0 auto;
	border-radius: var(--sul-radius-sm);
	overflow: hidden;
	background: var(--sul-surface-2);
}
.sul-dash-listing__thumb img { width: 100%; height: 100%; object-fit: cover; }
.sul-dash-listing__body {
	flex: 1 1 220px;
	min-width: 0; /* required for the title's own ellipsis truncation below to actually take effect in a flex child */
	display: flex;
	/* Column, not a wrapping row - reported directly as inconsistent: with
	   flex-wrap, whether price/views landed beside the title or dropped to
	   their own line depended entirely on how long that one title happened
	   to render at the current width, so a short title (e.g. "Ninja Foodi
	   8-in-1 Air Fryer") kept its meta line inline while a longer one
	   wrapped it - two different-looking rows in the same list. Always
	   stacking removes the fit-dependent branch entirely, on any width. */
	flex-direction: column;
	/* Tight, not the token spacing scale's own smallest step - this is a
	   gap between two lines of the same text block, not space between
	   separate UI pieces. Paired with the tighter line-heights below:
	   asked directly to bring the two-line block's total height down
	   close to the 40px thumb beside it, so centered alignment (the row's
	   own align-items: center) reads as the text actually spanning
	   top-to-bottom of the photo, not floating in extra padding around a
	   visibly shorter block. */
	gap: 0.05rem;
	justify-content: center;
}
.sul-dash-listing__title {
	margin: 0;
	font-weight: 600;
	line-height: 1.25;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	max-width: 100%;
	flex: 0 1 auto;
	min-width: 0; /* a flex item's implicit min-width:auto would otherwise refuse to shrink below its own text length, defeating the ellipsis above */
}
.sul-dash-listing__meta { margin: 0; font-size: var(--sul-fs-sm); line-height: 1.25; color: var(--sul-muted); display: flex; align-items: center; gap: var(--sul-space-2); flex-wrap: wrap; }
.sul-dash-listing__actions {
	flex: 0 0 auto;
	display: flex;
	gap: var(--sul-space-2);
	flex-wrap: wrap;
}
/* Below 560px (this file's own existing breakpoint - the toolbar switch
   above), actions reliably wraps to its own line rather than sharing the
   row with the checkbox/thumb/title. Reported directly: unindented, that
   wrapped line starts flush under the checkbox - a selection utility, not
   real content - so status/Edit/View/Delete visually read as attached to
   it instead of to the listing above them. Offsetting by checkbox width +
   thumb width + both column-gaps lines the wrapped row up with the title
   instead. Same flat single-row structure as before - this is a targeted
   fix for the common case (actions wraps on mobile), not a guarantee for
   every possible width/title-length combination the way a nested-column
   restructure would be; checking live before deciding whether that's
   worth the larger change. */
@media (max-width: 559px) {
	.sul-dash-listing__actions {
		/* Same offset on both properties - margin-left alone pushed the
		   actions box right without shrinking it, so Delete just overflowed
		   past the card's own right edge instead of wrapping to a second
		   line inside the (still flex-wrap: wrap) actions block itself. */
		margin-left: calc( 18px + var(--sul-space-3) + 40px + var(--sul-space-3) );
		max-width: calc( 100% - 18px - var(--sul-space-3) - 40px - var(--sul-space-3) );
	}
}
/* Icon squares, not text buttons (2026-09-30, direct request - the text
   version reliably wrapped Edit/Duplicate/View/Delete onto a second line
   for any row with more than 2-3 of them). Fixed 30px square regardless
   of content - was auto-sized to whatever text a button held before,
   which is exactly what caused the wrap; an icon has one fixed width, so
   this row's total width no longer depends on the listing's own status
   text length the way it implicitly did. */
.sul-dash-listing__actions button:not(.sul-lb__trigger),
.sul-dash-listing__actions a {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 30px;
	height: 30px;
	flex-shrink: 0;
	padding: 0;
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius-sm);
	background: var(--sul-surface);
	color: var(--sul-ink);
	cursor: pointer;
	text-decoration: none;
}
.sul-dash-listing__actions button svg,
.sul-dash-listing__actions a svg { width: 15px; height: 15px; pointer-events: none; }
.sul-dash-listing__actions button.is-danger {
	color: var(--sul-sili);
	border-color: color-mix(in srgb, var(--sul-sili) 40%, var(--sul-border));
}

/* The row's status control - a <select>, not a separate read-only pill
   plus Sold/Hide buttons that quietly did the same job (reported directly
   as confusing). Colored to match statusPill()'s own per-status palette
   (04-components.css) so it still reads as a status pill, not a generic
   form field, despite being one. */
.sul-dash-listing__status {
	font: inherit;
	font-size: var(--sul-fs-sm);
	font-weight: 600;
	padding: 0.3rem 1.6rem 0.3rem 0.6rem;
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius-pill);
	background: var(--sul-surface);
	color: var(--sul-ink);
	cursor: pointer;
	/* A plain <select> arrow reads as "form field", not "pill" - a custom
	   chevron keeps the pill-like shape the rest of the dashboard already
	   uses for status. */
	appearance: none;
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%237A6B72' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
	background-repeat: no-repeat;
	background-position: right 0.6rem center;
}
.sul-dash-listing__status--available { color: var(--sul-calamansi-ink); border-color: color-mix(in srgb, var(--sul-calamansi) 40%, var(--sul-border)); }
.sul-dash-listing__status--reserved  { color: var(--sul-marigold-ink);  border-color: color-mix(in srgb, var(--sul-marigold) 45%, var(--sul-border)); }
.sul-dash-listing__status--sold      { color: var(--sul-sili);          border-color: color-mix(in srgb, var(--sul-sili) 40%, var(--sul-border)); }

/* forms in the dashboard */
.sul-dash__panel .sul-form__row { display: grid; gap: var(--sul-space-4); }
@media (min-width: 560px) { .sul-dash__panel .sul-form__row { grid-template-columns: 1fr 1fr; } }
/* Status/Condition/Authenticity (2026-09-30, direct request) - three
   fields, not two, so the base 2-column rule above isn't enough room.
   Same single-column mobile fallback as every other row (no override
   below 560px); needs a bit more width than a plain 2-up row to read
   comfortably, hence the later breakpoint. Same specificity as the base
   rule (two classes each) - wins on source order, declared after it. */
@media (min-width: 700px) { .sul-dash__panel .sul-form__row--3 { grid-template-columns: repeat(3, 1fr); } }
.sul-dash__panel h2 { font-size: var(--sul-fs-h3); margin: 0 0 var(--sul-space-4); }

/* gallery editor */
.sul-gallery-editor__grid {
	list-style: none;
	margin: 0 0 var(--sul-space-3);
	padding: 0;
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(88px, 1fr));
	gap: var(--sul-space-2);
}
.sul-gallery-cell {
	position: relative;
	aspect-ratio: 1;
	border-radius: var(--sul-radius-sm);
	overflow: hidden;
	border: 1px solid var(--sul-border);
	background: var(--sul-surface-2);
}
.sul-gallery-cell img {
	width: 100%; height: 100%; object-fit: cover; cursor: grab;
	/* Belt-and-suspenders alongside the draggable="false" attribute — some
	   older WebKit versions needed this property too to fully suppress the
	   browser's own native "drag this image out" behavior, which otherwise
	   hijacks the gesture before the Pointer Events code below ever runs. */
	-webkit-user-drag: none;
	user-select: none;
	-webkit-user-select: none;
	/* The mobile counterpart of the drag-out problem above: a touch-and-hold
	   on an <img> normally opens the browser's native "save/share image"
	   menu, which competes with the drag gesture below. Covers Safari/iOS;
	   dashboard.js also preventDefault()s the contextmenu event itself for
	   browsers (Android Chrome included) that don't fully respect this
	   property. */
	-webkit-touch-callout: none;
	/* Tells the browser not to interpret a touch starting here as a scroll/
	   pan gesture at all — JS owns it via Pointer Events instead. This is
	   what actually lets touch arm a drag immediately, the same way mouse
	   already does, instead of needing an artificial long-press delay to
	   guess whether a touch was "a hold" vs. "a scroll" (the previous
	   approach, which turned out unreliable in practice). The real tradeoff:
	   a touch that starts on a photo no longer scrolls the page — it has to
	   start from elsewhere in the grid/page instead. */
	touch-action: none;
}
/* Drag-to-reorder: a fixed-position clone follows the pointer (dashboard.js
   beginDrag()) while the real cell it was lifted from dims in place so the
   seller can still see where it currently sits in the order. */
.sul-gallery-cell--ghost {
	position: fixed;
	z-index: 1000;
	pointer-events: none;
	box-shadow: 0 8px 24px rgba(0, 0, 0, .25);
	transform: scale(1.05);
	cursor: grabbing;
}
.sul-gallery-cell--ghost img { cursor: grabbing; }
.sul-gallery-cell.is-dragging-source { opacity: .35; }
.sul-gallery-cell.is-video::after {
	content: "▶";
	position: absolute; inset: 0; margin: auto;
	width: 1.6em; height: 1.6em;
	display: grid; place-items: center;
	background: rgba(0,0,0,.55); color: #fff;
	border-radius: 999px; font-size: .7rem;
}
/* Marks whichever photo is currently first — sul_gallery_cover_id()
   (includes/gallery.php) uses that position as the listing's cover image
   everywhere, so this needs to stay obviously attached to the position, not
   a specific photo, as the seller drags things around. */
.sul-gallery-cell__main {
	position: absolute;
	left: 4px;
	top: 4px;
	background: rgba(0, 0, 0, .6);
	color: #fff;
	font-size: .65rem;
	font-weight: 600;
	text-transform: uppercase;
	letter-spacing: .02em;
	padding: 0.15rem 0.4rem;
	border-radius: var(--sul-radius-pill);
	pointer-events: none;
}
.sul-gallery-cell__ops {
	position: absolute; left: 0; right: 0; bottom: 0;
	display: flex; justify-content: space-between;
	background: rgba(0,0,0,.5);
}
.sul-gallery-cell__ops button {
	font: inherit; font-size: .8rem; line-height: 1;
	background: none; border: none; color: #fff;
	padding: 0.25rem 0.35rem; cursor: pointer;
}
.sul-gallery-cell__ops button:disabled { opacity: .35; cursor: default; }
.sul-gallery-editor__controls { display: flex; gap: var(--sul-space-2); flex-wrap: wrap; }
.sul-gallery-editor__controls .sul-btn[disabled],
.sul-gallery-editor__controls label:has(input:disabled) { opacity: .5; pointer-events: none; }

/* bulk photo upload (Pro) — a seller's own media, independent of any one
   listing's gallery. A dropzone IS the upload control (drag a whole batch
   in at once, or fall back to the same file input via its own button).
   Uploaded photos render as a card grid below it - wider cells than the
   gallery editor's own thumbnails (.sul-gallery-editor__grid, above) since
   each one also carries a filename and a copy button, not just a bare
   image. */
.sul-dash-photos__intro { margin-bottom: var(--sul-space-4); }

.sul-dash-photos__dropzone {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--sul-space-3);
	padding: var(--sul-space-6) var(--sul-space-4);
	margin-bottom: var(--sul-space-4);
	border: 2px dashed var(--sul-field-border);
	border-radius: var(--sul-radius);
	background: var(--sul-surface-2);
	cursor: pointer;
	transition: background 0.15s ease, border-color 0.15s ease;
}
.sul-dash-photos__dropzone:hover,
.sul-dash-photos__dropzone.is-dragover {
	background: var(--sul-surface);
	border-color: var(--sul-guava-strong);
}
.sul-dash-photos__dropzone-text { color: var(--sul-muted); font-size: var(--sul-fs-sm); }

/* A message put inside a photo grid ("No photos uploaded yet.", "Loading your photos...", a load error) is a
   grid item like any other, so it was squeezed into ONE 140px column and wrapped word by word (Joefer,
   2026-10-16: "this always happens"). A paragraph in any of these grids spans every column. A message
   written into a grid needs this; do not add a new grid with messages in it without it. */
.sul-dash-photos__grid > p,
.sul-media-picker__grid > p,
.sul-gallery-editor__grid > p { grid-column: 1 / -1; margin: 0; }

.sul-dash-photos__grid {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
	gap: var(--sul-space-3);
}
.sul-dash-photos__item {
	position: relative;
	display: flex;
	flex-direction: column;
	gap: var(--sul-space-2);
	padding: var(--sul-space-2);
	background: var(--sul-surface);
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius-sm);
	/* Same reasoning as any other flex item holding an ellipsised child:
	   without this it takes its min-width from the filename's own
	   intrinsic width and never actually truncates it. */
	min-width: 0;
}
.sul-dash-photos__item img {
	width: 100%;
	aspect-ratio: 1 / 1;
	object-fit: cover;
	border-radius: calc(var(--sul-radius-sm) - 2px);
}
/* One line, ellipsised, with the full name in a title tooltip - a long
   camera/screenshot filename (the common case) used to wrap across 4-5
   lines and blow out that one card's height past every other card in
   the grid instead of just naming the photo. */
.sul-dash-photos__retention { margin: 0 0 var(--sul-space-3); font-size: var(--sul-fs-sm); }
.sul-dash-photos__unused { font-size: var(--sul-fs-sm); color: var(--sul-muted); }
.sul-dash-photos__unused.is-soon { color: var(--sul-sili); font-weight: 600; }
.sul-dash-photos__filename {
	display: block;
	min-width: 0;
	overflow: hidden;
	white-space: nowrap;
	text-overflow: ellipsis;
	font-size: var(--sul-fs-sm);
	color: var(--sul-muted);
}
/* A photo's description for screen readers: the text (two lines at most) and a quiet link, or the field while editing. */
.sul-dash-photos__alt { display: flex; flex-direction: column; gap: var(--sul-space-1); min-width: 0; font-size: var(--sul-fs-sm); }
.sul-dash-photos__alttext {
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
	color: var(--sul-ink);
	overflow-wrap: anywhere;
}
.sul-dash-photos__altbtn {
	align-self: flex-start;
	padding: 0;
	font: inherit;
	font-size: var(--sul-fs-sm);
	color: var(--sul-muted);
	text-decoration: underline;
	background: none;
	border: 0;
	cursor: pointer;
}
.sul-dash-photos__altbtn:hover { color: var(--sul-guava-strong); }
/* The description dialog: the photo and its filename, a roomy text box that wraps, and a character count. */
.sul-dash-altdialog { display: flex; flex-direction: column; gap: var(--sul-space-3); width: min(30rem, calc(100vw - 2rem)); max-height: calc(100vh - 2rem); overflow-y: auto; }
.sul-dash-altdialog .sul-modal__title { margin: 0; }
.sul-dash-altdialog__photo { display: flex; align-items: center; gap: var(--sul-space-3); min-width: 0; }
.sul-dash-altdialog__photo img { flex: 0 0 auto; width: 6rem; height: 6rem; object-fit: cover; border-radius: var(--sul-radius-sm); border: 1px solid var(--sul-border); }
.sul-dash-altdialog__photo code { min-width: 0; overflow-wrap: anywhere; font-size: var(--sul-fs-sm); color: var(--sul-muted); }
.sul-dash-altdialog__field { display: flex; flex-direction: column; gap: var(--sul-space-1); font-weight: 600; font-size: var(--sul-fs-sm); }
.sul-dash-altdialog__field textarea {
	width: 100%;
	box-sizing: border-box;
	min-height: 5.5rem;
	resize: vertical;
	font: inherit;
	font-size: var(--sul-fs-body);
	font-weight: 400;
	line-height: 1.5;
	padding: 0.6rem 0.75rem;
	background: var(--sul-surface);
	color: var(--sul-ink);
	border: 1px solid var(--sul-field-border);
	border-radius: var(--sul-radius-sm);
}
.sul-dash-altdialog__field textarea:focus-visible { outline: 2px solid var(--sul-guava-strong); outline-offset: 1px; border-color: var(--sul-guava-strong); }
.sul-dash-altdialog__count { margin: calc(var(--sul-space-2) * -1) 0 0; font-size: var(--sul-fs-sm); text-align: right; }
.sul-dash-altdialog__hint { margin: 0; font-size: var(--sul-fs-sm); }
.sul-dash-photos__remove {
	position: absolute;
	top: calc(var(--sul-space-2) * -1 + 4px);
	right: calc(var(--sul-space-2) * -1 + 4px);
	width: 24px;
	height: 24px;
	display: flex;
	align-items: center;
	justify-content: center;
	line-height: 1;
	padding: 0;
	border: 1px solid var(--sul-border);
	border-radius: 50%;
	background: var(--sul-surface);
	color: var(--sul-muted);
	cursor: pointer;
}
.sul-dash-photos__remove:hover {
	background: var(--sul-sili);
	border-color: var(--sul-sili);
	color: #fff;
}

/* store logo */
.sul-store-logo { display: flex; align-items: center; gap: var(--sul-space-3); }
.sul-store-logo img { width: 64px; height: 64px; border-radius: var(--sul-radius); object-fit: cover; border: 1px solid var(--sul-border); }

/* Cover preview is 8:3 (matching sul-cover / the storefront band), not the
   logo's square box — a wide photo forced into a 64x64 square would show
   only a thin sliver of it, misleading about what's actually being
   uploaded. */
.sul-store-cover { display: flex; align-items: center; gap: var(--sul-space-3); flex-wrap: wrap; }
.sul-store-cover img { width: 160px; height: 60px; border-radius: var(--sul-radius-sm); object-fit: cover; border: 1px solid var(--sul-border); }

/* Live preview for the How to order / Payment & shipping textareas
   (storeEditor() in dashboard.js) — filled in async via sul_dash_preview_prose,
   the same sul_format_prose() the storefront itself renders through, but
   collapsed behind a "Show preview" <details> by default (direct
   feedback: the box stayed permanently expanded before this, which on a
   several-line field doubled the visible height of every prose field for
   something most sellers only need to check occasionally). Ships open
   only when the field already has content (storeEditor() sets the `open`
   attribute) - a brand-new blank field has nothing to preview yet, so
   there's no reason to default it open there. */
.sul-prose-preview-toggle summary {
	font-size: var(--sul-fs-sm);
	color: var(--sul-muted);
	cursor: pointer;
	margin-top: var(--sul-space-2);
	width: fit-content;
}
.sul-prose-preview:empty { display: none; }
.sul-prose-preview {
	margin-top: var(--sul-space-2);
	padding: var(--sul-space-3) var(--sul-space-4);
	background: var(--sul-surface-2);
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius-sm);
	font-size: var(--sul-fs-sm);
}
.sul-prose-preview :first-child { margin-top: 0; }
.sul-prose-preview :last-child { margin-bottom: 0; }
/* Mirrors the Compact storefront layout's own list rules exactly (not a
   fixed indent guessed independently) - .sul-storefront-compact__section
   sets list-style-position: inside on ul specifically and leaves ol fully
   at browser default (08-storefront.css); a prior version of this rule
   zeroed padding on both, which stripped the indent .sul-storefront-
   compact__section's ol actually has and made the preview LESS indented
   than what ships - backwards from the point of a preview. Sidebar indents
   lists a bit further via its own reduced padding-left, so this can't
   match both layouts exactly; Compact is the site default. */
.sul-prose-preview ul { margin: 0 0 var(--sul-space-2); list-style-position: inside; }
.sul-prose-preview ol { margin: 0 0 var(--sul-space-2); }

/* socials repeater (store profile editor) */
#sul-socials-repeater { display: flex; flex-direction: column; gap: var(--sul-space-2); margin-bottom: var(--sul-space-2); }
/* .sul-socials-row is the outer per-link wrapper (controls + optional
   domain-mismatch warning stacked below); .sul-socials-controls is the
   actual select/input/remove-button row. */
.sul-socials-row { display: flex; flex-direction: column; gap: 4px; }
.sul-socials-controls { display: flex; gap: var(--sul-space-2); align-items: center; }
.sul-socials-controls select { flex: 0 0 auto; width: auto; }
.sul-socials-controls input[type="url"] { flex: 1 1 auto; }
.sul-socials-remove { flex: 0 0 auto; padding: 0.4rem 0.7rem; }
/* Live, non-blocking heads-up (assets/js/dashboard.js's checkDomain()) that
   a pasted link doesn't match the selected platform's real domain — the
   server (sul_sanitize_social_links()) is the actual enforcement, this is
   only an early warning so the mistake is obvious before saving. */
.sul-socials-warn { font-size: var(--sul-fs-sm); color: var(--sul-sili); margin: 0; }

/* toast - two zones (.sul-dash-toast__main, always shown; .sul-dash-toast__tip,
   shown only when toast()'s own $tip param is passed) rather than one flat
   box, so an optional "positive note" never has to fight the error
   background for contrast with an accent color (2026-09-24 - tried
   colored text directly against --sul-sili first: green clashed as red's
   complement regardless of shade, a same-hue accent or an inline tinted
   chip both still read as "trying too hard" against the mockups). The
   tip band reuses var(--sul-surface-2), an existing neutral tint, not a
   new color - the outer box's own overflow:hidden clips both zones to
   match its border-radius, so this needs no per-zone radius of its own. */
.sul-dash-toast {
	position: fixed;
	left: 50%;
	bottom: 1.25rem;
	transform: translate(-50%, 200%);
	background: var(--sul-ink);
	color: var(--sul-ground);
	border-radius: var(--sul-radius-pill);
	font-size: var(--sul-fs-sm);
	font-weight: 600;
	box-shadow: var(--sul-shadow);
	transition: transform 0.25s ease;
	z-index: 2000;
	max-width: calc(100vw - 2rem);
	overflow: hidden;
}
.sul-dash-toast__main {
	display: flex;
	align-items: center;
	gap: 0.4rem;
	padding: 0.7rem 0.6rem 0.7rem 1.1rem;
}
.sul-dash-toast.is-visible { transform: translate(-50%, 0); }
/* A fixed pill radius (above) only reads right on a short, single-line
   toast - once error toasts stopped auto-dismissing (so a real,
   full-sentence message can sit there instead of a brief "Saved."), a
   wrapped 3-4 line message under that same radius rounded almost into a
   blob/oval on a narrow mobile viewport (reported directly against a
   screenshot). --sul-radius, a normal rounded-rectangle, for error toasts
   specifically - success toasts stay short enough for the pill to still
   make sense. */
/* width, not just max-width - position: fixed + width: auto sizes to
   fit content (shrink-to-fit), not the max-width upper bound, so a long
   error message was wrapping into more/narrower lines than it needed to
   and growing taller than necessary as a result (direct follow-up: not
   the toast's shape that needed fixing further, the width it wasn't
   actually using). max-width caps it so it doesn't stretch absurdly
   wide on a large desktop viewport - only mobile's narrower own
   viewport actually needs the full calc(100vw - 2rem), since that's
   always smaller than the cap there anyway. 560px, not the original
   420px - reported directly against a desktop screenshot as still
   reading "mobile-sized," an untested guess that hadn't accounted for
   the message growing longer once the Tip sentence was added. Scoped
   to .is-error only - a short "Saved." success toast should stay a
   tight, content-sized pill, not stretch wide for no reason. */
.sul-dash-toast.is-error {
	background: var(--sul-sili);
	color: #fff;
	border-radius: var(--sul-radius);
	width: calc(100vw - 2rem);
	max-width: 560px;
}
.sul-dash-toast.is-error .sul-dash-toast__main { align-items: flex-start; }
.sul-dash-toast__tip {
	display: flex;
	align-items: center;
	gap: 0.4rem;
	padding: 0.6rem 1.1rem;
	background: var(--sul-surface-2);
	color: var(--sul-ink);
	font-weight: 400;
}
.sul-dash-toast__tip[hidden] { display: none; }
.sul-dash-toast__tip svg { width: 15px; height: 15px; flex-shrink: 0; color: var(--sul-guava-strong); }
/* Dismiss control - only ever needed on an error toast (the one that no
   longer auto-dismisses, see dashboard.js's toast()), but present on
   every toast so it doesn't pop in/out as a layout shift between the two. */
.sul-dash-toast__close {
	background: none;
	border: none;
	padding: 0.15rem 0.3rem;
	margin: 0;
	line-height: 1;
	font-size: 1.1rem;
	font-weight: 700;
	color: inherit;
	cursor: pointer;
	flex-shrink: 0;
	opacity: 0.75;
}
.sul-dash-toast__close:hover { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .sul-dash-toast { transition: none; } }

/* Upload-collision modal — a real three-button choice (Replace / Keep
   both / Cancel), since a native confirm() can't relabel its two
   hardcoded buttons enough to fit a genuine third option. Above the
   toast (z-index 2000 above) since a "Compressing…" toast can still be
   fading out right as this appears. */
.sul-modal-overlay {
	position: fixed;
	inset: 0;
	background: rgba(0, 0, 0, 0.5);
	display: flex;
	align-items: center;
	justify-content: center;
	padding: var(--sul-space-4);
	z-index: 3000;
}
.sul-modal {
	background: var(--sul-surface);
	color: var(--sul-ink);
	border-radius: var(--sul-radius);
	box-shadow: var(--sul-shadow);
	padding: var(--sul-space-6);
	max-width: 420px;
	width: 100%;
}
.sul-modal__title { font-weight: 700; margin: 0 0 var(--sul-space-2); }
.sul-modal__title code {
	background: var(--sul-surface-2);
	padding: 0.05rem 0.35rem;
	border-radius: 4px;
	font-size: 0.92em;
}
.sul-modal__body { color: var(--sul-muted); font-size: var(--sul-fs-sm); margin: 0 0 var(--sul-space-4); }
.sul-modal__actions { display: flex; flex-wrap: wrap; gap: var(--sul-space-2); }

/* "Pick from gallery" (listing gallery editor, dashboard.js's
   openMediaPicker()) - the collision modal's own 420px is a small
   confirm-style dialog, too narrow for a real thumbnail grid. */
.sul-modal--wide { max-width: 640px; max-height: 85vh; display: flex; flex-direction: column; }
.sul-media-picker__search {
	width: 100%;
	margin: 0 0 var(--sul-space-3);
	padding: 0.55rem 0.7rem;
	font: inherit;
	background: var(--sul-surface);
	color: var(--sul-ink);
	border: 1px solid var(--sul-field-border);
	border-radius: var(--sul-radius-sm);
}
.sul-media-picker__grid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
	gap: var(--sul-space-2);
	overflow-y: auto;
	margin: 0 0 var(--sul-space-4);
}
.sul-media-picker__item {
	position: relative;
	padding: 0;
	background: none;
	border: 2px solid transparent;
	border-radius: var(--sul-radius-sm);
	cursor: pointer;
	line-height: 0;
}
.sul-media-picker__item img { width: 100%; aspect-ratio: 1 / 1; object-fit: cover; border-radius: calc(var(--sul-radius-sm) - 2px); display: block; }
.sul-media-picker__item:hover { border-color: var(--sul-guava-strong); }
.sul-media-picker__item.is-selected { border-color: var(--sul-guava-strong); }
/* Checkbox-style selection badge (2026-09-25) - deliberate picking, not
   the old "click = instantly added" behavior. Circular, white-outlined
   dot in the corner unchecked, filled + a checkmark once selected - same
   visual language a native OS/media-library multi-select grid already
   uses, so it reads as familiar rather than a bespoke control. */
.sul-media-picker__check {
	position: absolute;
	top: 6px;
	left: 6px;
	width: 20px;
	height: 20px;
	border-radius: 50%;
	border: 2px solid #fff;
	background: rgba(0, 0, 0, 0.35);
	box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.15);
	z-index: 1;
}
.sul-media-picker__item.is-selected .sul-media-picker__check {
	background: var(--sul-guava-strong);
	border-color: var(--sul-guava-strong);
}
.sul-media-picker__item.is-selected .sul-media-picker__check::after {
	content: "";
	position: absolute;
	left: 5px;
	top: 2px;
	width: 6px;
	height: 10px;
	border: solid #fff;
	border-width: 0 2px 2px 0;
	transform: rotate(45deg);
}

/* A small (?) trigger revealing a tip bubble on click (dashboard.js's
   infoTipHtml()/bindInfoTips()) - click/tap, not hover, since a native
   title="" tooltip never shows on a touchscreen at all and this
   dashboard is mobile-first. Currently just the Gallery field's YouTube
   tip, but generic - any control can carry one. */
.sul-info-tip { position: relative; display: inline-flex; margin-left: var(--sul-space-1); vertical-align: middle; }
.sul-info-tip__btn {
	width: 1.35rem;
	height: 1.35rem;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	font: inherit;
	font-size: 0.75rem;
	font-weight: 700;
	background: var(--sul-surface-2);
	color: var(--sul-muted);
	border: 1px solid var(--sul-field-border);
	border-radius: 50%;
	cursor: pointer;
	padding: 0;
}
.sul-info-tip__btn:hover,
.sul-info-tip__btn[aria-expanded="true"] { color: var(--sul-guava-strong); border-color: var(--sul-guava-strong); }
.sul-info-tip__bubble {
	position: absolute;
	bottom: calc(100% + var(--sul-space-2));
	left: 0;
	z-index: 10;
	width: 240px;
	max-width: 70vw;
	padding: var(--sul-space-3);
	background: var(--sul-ink);
	color: var(--sul-surface);
	font-size: var(--sul-fs-sm);
	font-weight: 400;
	line-height: 1.4;
	border-radius: var(--sul-radius-sm);
	box-shadow: var(--sul-shadow);
}

/* Generic inline notice card — currently just the "warn" variant, used for
   the CSV import feature's persistent per-listing missing-photos reminder
   (assets/js/dashboard.js's listingEditor()) and its upfront explainer
   (importView()). Same tinted-pill recipe as .sul-badge-verified/Featured/
   Sponsored (04-components.css, 11-home.css, 13-search.css) — verified
   there for AA contrast, reused here rather than re-deriving a new one. */
.sul-notice {
	border-radius: var(--sul-radius-sm);
	padding: var(--sul-space-3) var(--sul-space-4);
	margin: 0 0 var(--sul-space-4);
	font-size: var(--sul-fs-sm);
	line-height: 1.5;
}
.sul-notice--warn {
	color: var(--sul-marigold-ink);
	background: color-mix(in srgb, var(--sul-marigold) 16%, var(--sul-surface));
	border: 1px solid color-mix(in srgb, var(--sul-marigold) 45%, var(--sul-border));
}
.sul-notice code {
	background: color-mix(in srgb, var(--sul-marigold) 20%, var(--sul-surface));
	padding: 0.05rem 0.35rem;
	border-radius: 4px;
	font-size: 0.92em;
}
.sul-notice__dismiss { margin-top: var(--sul-space-3); }

/* CSV import/export screen */
/* Full panel width again as of 2026-09-25 (direct request, matching
   Listings/Photos/Store profile) - the .sul-container--md (760px) cap
   this section had is gone from the markup. Worth remembering if a
   flush-left/empty-space-on-the-right complaint resurfaces: that's
   exactly what the PREVIOUS full-width attempt (PR #172/#173) was
   reported for, which is why the cap existed at all - accepted as a
   real risk this time rather than an oversight; tweak (e.g. an actual
   two-column Export/Import layout that uses the width, rather than one
   narrow column stretching into empty space) if it recurs. */
.sul-dash-import__section { margin-bottom: var(--sul-space-8); }
.sul-dash-import__section h2 { margin: 0 0 var(--sul-space-2); }
.sul-dash-import__actions { margin: var(--sul-space-3) 0; }
.sul-dash-import__dropzone {
	border: 2px dashed var(--sul-field-border);
	border-radius: var(--sul-radius);
	padding: var(--sul-space-6);
	text-align: center;
	color: var(--sul-muted);
}
.sul-dash-import__dropzone.is-dragover {
	border-color: var(--sul-guava-strong);
	color: var(--sul-ink);
}
.sul-dash-import__results { margin-top: var(--sul-space-4); }
.sul-dash-import__summary {
	display: flex;
	flex-wrap: wrap;
	gap: var(--sul-space-2);
	margin: 0 0 var(--sul-space-3);
}
.sul-dash-import__summary span {
	font-size: var(--sul-fs-sm);
	font-weight: 600;
	padding: 0.25rem 0.65rem;
	border-radius: var(--sul-radius-pill);
	background: var(--sul-surface-2);
}
.sul-dash-import__rows { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; max-height: 420px; overflow-y: auto; }
.sul-dash-import__rows li {
	display: flex;
	flex-wrap: wrap;
	gap: var(--sul-space-2);
	align-items: baseline;
	padding: 0.45rem 0.6rem;
	border-radius: var(--sul-radius-sm);
	font-size: var(--sul-fs-sm);
}
.sul-dash-import__rows li.is-error { background: color-mix(in srgb, var(--sul-sili) 12%, var(--sul-surface)); }
.sul-dash-import__rows li.is-missing { background: color-mix(in srgb, var(--sul-marigold) 14%, var(--sul-surface)); }
.sul-dash-import__rows .sul-muted { flex-basis: 100%; }

/* Import preview — one card per row, nothing written yet. A field about to
   go blank (.is-cleared) is called out in the same sili red used for a
   hard error elsewhere in this dashboard, not the softer marigold "missing
   photo" tint — the two mean different things (a real value about to be
   destroyed vs. a photo simply not uploaded yet) and shouldn't read the
   same. */
.sul-dash-import__rows--preview {
	display: flex;
	flex-direction: column;
	gap: var(--sul-space-2);
	max-height: none;
	overflow: visible;
}
.sul-dash-import__preview-row {
	display: block;
	padding: var(--sul-space-3);
	border-radius: var(--sul-radius-sm);
	background: var(--sul-surface-2);
}
.sul-dash-import__preview-title { margin: 0 0 var(--sul-space-2); font-weight: 600; }
.sul-dash-import__badge {
	display: inline-block;
	font-size: var(--sul-fs-label);
	font-weight: 700;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	color: var(--sul-calamansi-ink);
	background: color-mix(in srgb, var(--sul-calamansi) 22%, var(--sul-surface));
	border-radius: var(--sul-radius-pill);
	padding: 0.1rem 0.5rem;
	margin-right: 0.35rem;
}
.sul-dash-import__badge--muted { color: var(--sul-muted); background: var(--sul-surface); }
.sul-dash-import__fieldlist { list-style: none; margin: 0; padding: 0; font-size: var(--sul-fs-sm); }
.sul-dash-import__fieldlist li { padding: 0.15rem 0; }
.sul-dash-import__fieldlist li.is-cleared { color: var(--sul-sili); }
.sul-dash-import__fieldlist code { color: var(--sul-muted); margin-right: 0.35rem; }
.sul-dash-import__summary span.is-cleared { color: var(--sul-sili); background: color-mix(in srgb, var(--sul-sili) 14%, var(--sul-surface)); }

/* Themed dropdown (05-forms.css .sul-lb) in the dashboard's own contexts
   (2026-10-01): the Listings toolbar + bulk bar triggers stand in for the
   native selects styled at the top of this file, and the socials row keeps
   its auto-width platform picker. The per-row status pill needs nothing
   here - listbox-select.js copies the select's .sul-dash-listing__status
   classes onto the trigger, so the pill rules above apply to it directly. */
.sul-dash__toolbar .sul-lb__trigger,
.sul-dash__bulkbar .sul-lb__trigger {
	font-size: var(--sul-fs-sm);
	padding: 0.45rem 1.75rem 0.45rem 0.7rem;
	border-color: var(--sul-field-border);
	background-position: right 0.7rem center;
}
.sul-dash__filter-row .sul-lb { flex: 1 1 0; min-width: 0; }
.sul-dash__filter-row .sul-lb__trigger { width: 100%; font-weight: 600; }
@media (min-width: 560px) {
	.sul-dash__filter-row .sul-lb { flex: 0 0 auto; }
	.sul-dash__filter-row .sul-lb__trigger { width: auto; }
}
.sul-socials-controls .sul-lb { flex: 0 0 auto; width: auto; }
.sul-socials-controls .sul-lb__trigger { width: auto; }

/* The tools row at the top of the dashboard (includes/dashboard-strip.php,
   2026-10-10): Verified (a chip, or a Get Verified button) and Invite a store, each
   button opening its own panel. Replaces two tall cards that pushed the listings
   down. A closed row is one line; an open panel takes the full row below its
   button. */
.sul-dash-strip { margin-bottom: var(--sul-space-6); }
.sul-dash-strip > .sul-note { margin: 0 0 var(--sul-space-3); }
.sul-dash-strip__row { display: flex; flex-wrap: wrap; align-items: flex-start; gap: var(--sul-space-2) var(--sul-space-3); }
.sul-dash-chip {
	display: inline-flex;
	align-items: center;
	gap: 0.35rem;
	font-size: var(--sul-fs-sm);
	font-weight: 600;
	color: var(--sul-ink);
	background: var(--sul-surface);
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius-pill);
	padding: 0.3rem 0.8rem 0.3rem 0.5rem;
}
.sul-dash-panel { flex: 0 0 auto; max-width: 100%; }
.sul-dash-panel[open] { flex: 1 1 100%; }
.sul-dash-btn {
	display: inline-flex;
	align-items: center;
	gap: 0.5rem;
	list-style: none;
	cursor: pointer;
	font-size: var(--sul-fs-sm);
	font-weight: 600;
	color: var(--sul-ink);
	background: var(--sul-surface);
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius-sm);
	padding: 0.45rem 0.8rem;
}
.sul-dash-btn::-webkit-details-marker { display: none; }
.sul-dash-btn::after {
	content: "";
	width: 0.4rem;
	height: 0.4rem;
	border-right: 2px solid currentColor;
	border-bottom: 2px solid currentColor;
	transform: rotate(45deg) translateY(-1px);
	opacity: 0.55;
}
.sul-dash-panel[open] > .sul-dash-btn::after { transform: rotate(-135deg) translateY(-1px); }
.sul-dash-btn:hover { background: var(--sul-surface-2); }
.sul-dash-btn:focus-visible { outline: 2px solid var(--sul-ink); outline-offset: 2px; }
.sul-dash-btn__note { font-weight: 400; color: var(--sul-muted); }
.sul-dash-btn__note.is-alert { font-weight: 600; color: var(--sul-sili); }
.sul-dash-panel__body {
	display: flex;
	flex-direction: column;
	gap: var(--sul-space-3);
	margin-top: var(--sul-space-3);
	padding: var(--sul-space-4);
	background: var(--sul-surface);
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius);
	box-shadow: var(--sul-shadow-sm);
}
.sul-dash-panel__body p { margin: 0; }
/* The PANEL keeps a readable width, not the whole 1080px row, and the text and form inside fill it (Joefer,
   2026-10-16, from the Get Verified screen: the form was 600px inside a panel as wide as the page, so the intro
   ran far past the fields). Narrow the container, never the text. */
.sul-dash-panel__body { max-width: 42rem; }
.sul-dash-copy { display: flex; flex-wrap: wrap; gap: var(--sul-space-2); }
.sul-dash-copy input {
	flex: 1 1 16rem;
	min-width: 0;
	font: inherit;
	font-size: var(--sul-fs-sm);
	padding: 0.5rem 0.7rem;
	color: var(--sul-ink);
	background: var(--sul-surface);
	border: 1px solid var(--sul-field-border);
	border-radius: var(--sul-radius-sm);
}
/* .sul-btn sets display, which would beat the [hidden] the Share button starts with. */
.sul-dash-copy .sul-btn[hidden] { display: none; }
.sul-dash-panel__again { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sul-space-2); font-size: var(--sul-fs-sm); }
/* Polish after the first test (2026-10-10): the chip and the buttons share a
   centre line, a #link to a panel does not land under the sticky header, the
   notes after a label read as a status. Text inside a panel is NOT width-capped:
   it fills the panel (a 70ch cap added here left the line ending well short of the
   box, 2026-10-10, and was removed). */
.sul-dash-strip__row { align-items: center; }
.sul-dash-panel { scroll-margin-top: 5rem; }
.sul-dash-btn { padding-block: 0.6rem; }
.sul-dash-btn__note::before { content: "\00b7"; margin-right: 0.5rem; }

/* The gear is a small menu now (Edit store, Your account), reusing the Tools menu's panel. A link
   in the menu looks like a button in it. */
.sul-dash__tools-menu a {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	width: 100%;
	box-sizing: border-box;
	font-size: var(--sul-fs-sm);
	text-align: left;
	padding: 0.5rem 0.6rem;
	border-radius: calc(var(--sul-radius-sm) - 4px);
	color: var(--sul-ink);
	text-decoration: none;
}
.sul-dash__tools-menu a:hover { background: var(--sul-surface-2); }
.sul-dash__tools-menu a svg { width: 15px; height: 15px; flex-shrink: 0; color: var(--sul-muted); }
.sul-dash__gear .sul-dash__tools-menu { min-width: 170px; }
.sul-dash__settings-btn.is-active {
	background: color-mix(in srgb, var(--sul-sili) 14%, var(--sul-surface));
	color: var(--sul-sili);
	border-color: color-mix(in srgb, var(--sul-sili) 45%, var(--sul-border));
}

/* "Your account" (account-deletion.php): the plan, then the one place a store is deleted from. */
.sul-dash-account__plan { margin-bottom: var(--sul-space-6); }
.sul-dash-account__plan p { margin: 0; }
.sul-dash-account__plan p + p { margin-top: var(--sul-space-1); }
.sul-dash-account__danger { margin-top: var(--sul-space-8); padding-top: var(--sul-space-4); border-top: 1px solid var(--sul-border); }
.sul-del { border: 1px solid var(--sul-border); border-radius: var(--sul-radius-sm); }
.sul-del__summary {
	padding: var(--sul-space-3) var(--sul-space-4);
	cursor: pointer;
	font-weight: 600;
	color: var(--sul-sili);
}
.sul-del__body { padding: 0 var(--sul-space-4) var(--sul-space-4); }
.sul-del__form { margin-top: var(--sul-space-4); }
.sul-del-banner { margin-bottom: var(--sul-space-4); }

/* Saved screen (2026-10-13): one row per saved listing. */
.sul-dash-saved { display: flex; flex-direction: column; gap: var(--sul-space-2); }
.sul-dash-saved__row {
	display: flex;
	align-items: center;
	gap: var(--sul-space-3);
	padding: var(--sul-space-3);
	background: var(--sul-surface);
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius);
}
.sul-dash-saved__row .sul-dash-listing__thumb { width: 56px; height: 56px; }
.sul-dash-saved__text { flex: 1 1 auto; min-width: 0; }
.sul-dash-saved__title { display: block; font-weight: 600; color: var(--sul-ink); text-decoration: none; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sul-dash-saved__title:hover { text-decoration: underline; }
.sul-dash-saved__meta { margin: 2px 0 0; font-size: var(--sul-fs-sm); color: var(--sul-muted); }

/* assets/css/11-home.css */
/* The About page hero (page-about.php, the old homepage) + the store-card
   shared by Discover and the front page (12-directory.css). */

.sul-hero {
	background: var(--sul-ground);
	border-bottom: 1px solid var(--sul-border);
	/* The animated cursor in the address field glides past the right edge of a phone; clip it here (the page-wide
	   rule in 01-base.css is the safety net). */
	overflow-x: clip;
	padding-block: clamp(2.5rem, 8vw, 5rem);
}
/* 880px (was 760): the heading, now "...vanish overnight.", wrapped to three short lines in the old 696px of text
   (489px, 487px, 437px at 57.6px); in about 816px it is two. Narrowing the container is how to shape the text. */
.sul-hero__inner { max-width: 880px; }
.sul-hero__title {
	font-size: var(--sul-fs-display);
	line-height: 1.1;
	margin: 0 0 var(--sul-space-4);
}
/* The one emphasised word: deep guava (the Brand Guide's colour for large display and anything read at length). */
.sul-hero__em { color: var(--sul-guava-strong); }
.sul-hero__lede {
	font-size: var(--sul-fs-body);
	color: var(--sul-muted);
	margin: 0 0 var(--sul-space-6);
}

/* The About page below the hero (2026-10-15 polish). Cards fill their grid cells and the text fills
   its card (no ch caps): narrow a container, not its text. Bright guava is only the big step
   numbers and the card icons are the deep guava, as the Brand Guide's contrast rule asks. */
/* The address as a field to try (2026-10-15). Placeholder text types sample names by itself (about-handle.js);
   a small cursor "clicks" the search button every 8 seconds. Both stop when the visitor clicks in, and a
   reduced-motion setting turns every animation off. Colours are tokens, so all three theme states resolve. */
.sul-handle-try { position: relative; max-width: 30rem; margin: 0 0 var(--sul-space-6); }
.sul-handle-try__field {
	display: flex;
	align-items: center;
	height: 48px;
	padding: 0 6px 0 var(--sul-space-4);
	background: var(--sul-surface);
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius-sm);
	transition: border-color 0.2s, box-shadow 0.2s;
}
.sul-handle-try__field:hover { border-color: color-mix(in srgb, var(--sul-guava) 45%, var(--sul-border)); }
.sul-handle-try__field:focus-within {
	border-color: var(--sul-guava-strong);
	box-shadow: 0 0 0 3px color-mix(in srgb, var(--sul-guava-strong) 22%, transparent);
}
.sul-handle-try__prefix { font-weight: 600; color: var(--sul-muted); white-space: nowrap; }
.sul-handle-try__input {
	flex: 1;
	min-width: 0;
	height: 46px;
	padding: 0 6px 0 0;
	border: 0;
	border-radius: 0;
	background: transparent;
	box-shadow: none;
	font: inherit;
	font-weight: 700;
	color: var(--sul-guava-strong);
}
.sul-handle-try__input:focus,
.sul-handle-try__input:focus-visible { outline: 0; box-shadow: none; }
.sul-handle-try__input::placeholder { color: var(--sul-guava-strong); opacity: 1; font-weight: 700; }
.sul-handle-try__go {
	position: relative;
	flex: none;
	display: flex;
	align-items: center;
	justify-content: center;
	width: 36px;
	height: 36px;
	padding: 0;
	border: 0;
	border-radius: 8px;
	background: var(--sul-btn-bg);
	color: var(--sul-btn-fg);
	cursor: pointer;
	animation: sul-ht-press 8s ease-in-out infinite;
}
.sul-handle-try__go:hover { background: var(--sul-btn-bg-hover); }
.sul-handle-try__go::after {
	content: "";
	position: absolute;
	inset: 0;
	border-radius: 8px;
	border: 2px solid var(--sul-guava-strong);
	opacity: 0;
	pointer-events: none;
	animation: sul-ht-ring 8s ease-out infinite;
}
.sul-handle-try__cursor {
	position: absolute;
	right: 14px;
	top: 22px;
	width: 22px;
	height: 26px;
	fill: var(--sul-ink);
	stroke: var(--sul-surface);
	opacity: 0;
	pointer-events: none;
	animation: sul-ht-glide 8s ease-in-out infinite;
}
.sul-handle-try:focus-within .sul-handle-try__cursor,
.sul-handle-try:focus-within .sul-handle-try__go,
.sul-handle-try:focus-within .sul-handle-try__go::after { animation: none; }
.sul-handle-try:focus-within .sul-handle-try__cursor,
.sul-handle-try:focus-within .sul-handle-try__go::after { opacity: 0; }
.sul-handle-try__cap { margin: var(--sul-space-2) 0 0; font-size: var(--sul-fs-sm); color: var(--sul-muted); }
@keyframes sul-ht-glide {
	0%, 8% { opacity: 0; transform: translate(46px, 40px); }
	18% { opacity: 1; transform: translate(46px, 40px); }
	34% { opacity: 1; transform: translate(0, 0); }
	39% { opacity: 1; transform: translate(1px, 1px) scale(0.88); }
	44% { opacity: 1; transform: translate(0, 0) scale(1); }
	58% { opacity: 1; transform: translate(0, 0); }
	70%, 100% { opacity: 0; transform: translate(26px, 22px); }
}
@keyframes sul-ht-press {
	0%, 37% { transform: scale(1); }
	39.5% { transform: scale(0.86); }
	43%, 100% { transform: scale(1); }
}
@keyframes sul-ht-ring {
	0%, 38.5% { opacity: 0; transform: scale(1); }
	39.5% { opacity: 0.7; transform: scale(1); }
	52% { opacity: 0; transform: scale(1.9); }
	100% { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
	.sul-handle-try__go,
	.sul-handle-try__go::after,
	.sul-handle-try__cursor { animation: none; }
	.sul-handle-try__cursor { opacity: 0; }
}
.sul-about-section .sul-eyebrow { margin-bottom: var(--sul-space-4); }
/* Tighter gaps between the sections (2026-10-15, "ridiculously generous", then "dial back a bit" when 24px read
   too tight): the sitewide .sul-section gives up to 64px above and below, so two sections sat 128px apart; here
   each gives 36px, so they sit 72px apart. */
.sul-section.sul-about-section { padding-block: clamp(1.25rem, 3vw, 2.25rem); }
/* Section bands (2026-10-16, Joefer: the sections were too alike to tell apart; then the blush Surface-2 was
   disliked, so the tint is gone: only colours the Brand Guide names). The page background alternates the page
   ground and white from section to section with a hairline (the guide's Border colour) between them, and the
   cards flip to the other shade so they stay visible: the hero is ground, How it works white (ground cards),
   Keeping it honest ground (white cards), Read the rules white, and the closing band is solid guava strong.
   THE HAIRLINE STAYS (Joefer, 2026-10-16: it was removed on request, then "the hairlines looked better", and
   rounded panels were tried and reverted: do not remove it again). The bands carry their own padding, so each
   section inside gives a little more. */
.sul-about-band { border-top: 1px solid var(--sul-border); }
.sul-about-band--white { background: var(--sul-surface); }
.sul-about-band--ground { background: var(--sul-ground); }
.sul-about-band .sul-section.sul-about-section { padding-block: clamp(1.75rem, 4.5vw, 3rem); }
.sul-about-band--white .sul-about-steps > li { background: var(--sul-ground); }
.sul-about-band--ground .sul-about-card { background: var(--sul-surface); }
.sul-about-band--white .sul-btn--ghost { background: var(--sul-ground); }
.sul-about-lead { margin: 0 0 var(--sul-space-6); font-size: var(--sul-fs-body); color: var(--sul-muted); }
.sul-about-steps,
.sul-about-cards {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	grid-template-columns: repeat(3, minmax(0, 1fr));
	gap: var(--sul-space-6);
}
.sul-about-steps > li,
.sul-about-card {
	background: var(--sul-surface-2);
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius);
	padding: var(--sul-space-6);
}
.sul-about-step__n {
	display: block;
	margin: 0 0 var(--sul-space-1);
	font-size: var(--sul-fs-label);
	font-weight: 600;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--sul-guava-strong);
}
.sul-about-steps h3,
.sul-about-card h3 { text-wrap: wrap; margin: 0 0 var(--sul-space-2); }
.sul-about-steps p,
.sul-about-card p { margin: 0; color: var(--sul-muted); }
/* The icon (40px, no box behind it) shares a row with the card's title; a step's small "Step n"
   label stacks above its title. The titles drop their own bottom margin: the row carries it. */
.sul-about-head {
	display: flex;
	align-items: center;
	gap: var(--sul-space-3);
	margin: 0 0 var(--sul-space-3);
}
.sul-about-head h3 { margin: 0; }
.sul-about-card__icon { flex: none; display: block; color: var(--sul-guava-strong); }
/* The closing band: solid guava strong (2026-10-16, Joefer; it was ink for a day, and a soft guava tint before that,
   which was a wash). Its colours are two tokens (white on #CE3358; in the dark theme #2A1016 on #F27389, the
   guide's button pair). The main button is the band's text colour on the band's colour, the second is an outline,
   both at full contrast. */
.sul-about-close {
	background: var(--sul-band-bg);
	color: var(--sul-band-fg);
	padding-block: clamp(2.25rem, 5.5vw, 3.5rem);
}
.sul-about-close h2 { font-size: var(--sul-fs-h1); text-wrap: wrap; margin-bottom: var(--sul-space-2); color: var(--sul-band-fg); }
.sul-about-close p { margin: 0 0 var(--sul-space-4); color: var(--sul-band-fg); }
.sul-about-close .sul-btn { background: var(--sul-band-fg); color: var(--sul-band-bg); border-color: var(--sul-band-fg); }
.sul-about-close .sul-btn--ghost { background: transparent; color: var(--sul-band-fg); border-color: var(--sul-band-fg); }
.sul-about-close .sul-btn:hover { opacity: 0.9; }
/* The closing band runs down to the footer, so the footer's own top margin would leave a plain strip between them. */
body.page-template-page-about .sul-footer { margin-top: 0; }
/* The dots are for the phone's swipe row only (about-swipe.js adds them). */
.sul-about-dots { display: none; }
@media (max-width: 760px) {
	/* On a phone (2026-10-16, Joefer) each set of cards is a row of FULL-WIDTH cards that take turns, like the
	   popular posts: a scroll-snap track you can swipe, moved along by about-swipe.js, with dots and a pause
	   button underneath. Above 760px it stays a grid. */
	.sul-about-steps,
	.sul-about-cards {
		display: flex;
		gap: var(--sul-space-3);
		overflow-x: auto;
		overflow-y: hidden;
		scroll-snap-type: x mandatory;
		overscroll-behavior-x: contain;
		scrollbar-width: none;
		-webkit-overflow-scrolling: touch;
	}
	.sul-about-steps::-webkit-scrollbar,
	.sul-about-cards::-webkit-scrollbar { display: none; }
	.sul-about-steps > li,
	.sul-about-card { flex: 0 0 100%; scroll-snap-align: start; box-sizing: border-box; }

	.sul-about-dots { display: flex; justify-content: center; align-items: center; margin-top: var(--sul-space-3); }
	.sul-about-dot {
		display: inline-flex;
		align-items: center;
		justify-content: center;
		width: 24px;
		height: 24px;
		margin: 0;
		padding: 0;
		border: 0;
		background: none;
		cursor: pointer;
	}
	.sul-about-dot::before {
		content: "";
		width: 8px;
		height: 8px;
		border-radius: 50%;
		background: color-mix(in srgb, var(--sul-muted) 45%, transparent);
		transition: background 0.2s ease, transform 0.2s ease;
	}
	.sul-about-dot.is-active::before { background: var(--sul-guava-strong); transform: scale(1.25); }
	.sul-about-dot:focus-visible { outline: 2px solid var(--sul-ink); outline-offset: 1px; }

	/* The pause button sits after the dots, the same small circle the blog slideshow uses. */
	.sul-about-toggle {
		display: inline-flex;
		align-items: center;
		justify-content: center;
		width: 24px;
		height: 24px;
		margin: 0 0 0 0.25rem;
		padding: 0;
		border: 0;
		border-radius: 50%;
		background: none;
		color: var(--sul-muted);
		cursor: pointer;
	}
	.sul-about-toggle svg { fill: currentColor; }
	.sul-about-toggle:hover { color: var(--sul-ink); }
	.sul-about-toggle:focus-visible { outline: 2px solid var(--sul-ink); outline-offset: 1px; }
	.sul-about-toggle__play { display: none; }
	.sul-about-dots.is-paused .sul-about-toggle__play { display: block; }
	.sul-about-dots.is-paused .sul-about-toggle__pause { display: none; }
}
@media (prefers-reduced-motion: reduce) {
	.sul-about-dot::before { transition: none; }
}

/* Fade-in on scroll (2026-10-15): about-reveal.js adds .is-visible when a section nears the screen. Hidden only
   under .sul-reveal-on (set by page-about.php while script is running), shown for everyone if the script has not
   started after 3 seconds (.sul-reveal-all), and not animated at all under a reduced-motion setting. Cards inside a section follow
   it 150ms apart. Slowed after "too fast" (was 0.7s and 0.6s, 90ms apart): a long, soft ease-out. */
.sul-reveal-on .sul-reveal {
	opacity: 0;
	transform: translateY(18px);
	transition: opacity 1.3s cubic-bezier(0.22, 0.61, 0.36, 1), transform 1.3s cubic-bezier(0.22, 0.61, 0.36, 1);
}
.sul-reveal-on .sul-reveal.is-visible,
.sul-reveal-on.sul-reveal-all .sul-reveal {
	opacity: 1;
	transform: none;
}
.sul-reveal-on .sul-reveal .sul-about-steps > li,
.sul-reveal-on .sul-reveal .sul-about-card {
	opacity: 0;
	transform: translateY(14px);
	transition: opacity 1.1s cubic-bezier(0.22, 0.61, 0.36, 1), transform 1.1s cubic-bezier(0.22, 0.61, 0.36, 1);
}
.sul-reveal-on .sul-reveal.is-visible .sul-about-steps > li,
.sul-reveal-on .sul-reveal.is-visible .sul-about-card,
.sul-reveal-on.sul-reveal-all .sul-reveal .sul-about-steps > li,
.sul-reveal-on.sul-reveal-all .sul-reveal .sul-about-card {
	opacity: 1;
	transform: none;
}
.sul-reveal.is-visible .sul-about-steps > li:nth-child(2),
.sul-reveal.is-visible .sul-about-card:nth-child(2) { transition-delay: 150ms; }
.sul-reveal.is-visible .sul-about-steps > li:nth-child(3),
.sul-reveal.is-visible .sul-about-card:nth-child(3) { transition-delay: 300ms; }
@media (prefers-reduced-motion: reduce) {
	.sul-reveal-on .sul-reveal,
	.sul-reveal-on .sul-reveal .sul-about-steps > li,
	.sul-reveal-on .sul-reveal .sul-about-card { opacity: 1; transform: none; transition: none; }
}

/* -- store card -- */
/* Rework (2026-09-25, direct request, several rounds) - deliberately
   different shape from the product card (search-result-card.php/
   13-search.css), which keeps a portrait photo-tile-then-text shape.
   The store's own cover photo (optional, same _sul_cover/'sul-cover' the
   storefront's Compact layout already uses - storefront-head-
   compact.php) is a full-bleed LANDSCAPE background this time (a prior
   pass used this same cover-photo idea but portrait, 2:3 - corrected:
   "it should now be in landscape, not portrait"; a pass in between
   dropped the photo entirely - corrected again: "Keep the cover as the
   bg". This is the reconciled version: photo background, landscape
   shape). Text anchored to the bottom over a scrim, same treatment
   storefront-head-compact.php's own cover photo already established.
   No cover photo (absent by default - Decision Log) used to fall back
   to a flat --sul-surface-2 tint - superseded 2026-09-30 (direct
   request, "make it consistent across all cards") by a generic
   branded placeholder pattern instead, so every card in a grid reads
   as the same photo-style shape regardless of whether a seller
   uploaded a real cover - see store-card.php's own docblock and the
   .sul-store-card--ph-* rules below. */
.sul-store-card {
	position: relative;
	aspect-ratio: 3 / 2;
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius);
	overflow: hidden;
	background-color: var(--sul-surface-2);
	background-size: cover;
	background-position: center;
}
.sul-store-card__link {
	display: flex;
	flex-direction: column;
	justify-content: flex-end;
	height: 100%;
	color: inherit;
	text-decoration: none;
}
/* Same scrim gradient storefront-head-compact.php's own cover photo
   already uses (08-storefront.css) - one established "text over a
   seller-uploaded photo" treatment, not a second one invented here. */
.sul-store-card__scrim {
	position: absolute;
	inset: 0;
	background: linear-gradient(to top, rgba(20, 14, 17, .92) 0%, rgba(20, 14, 17, .55) 45%, rgba(20, 14, 17, .05) 80%);
}
.sul-store-card--ph-0,
.sul-store-card--ph-1,
.sul-store-card--ph-2 {
	background-image: repeating-linear-gradient(135deg, var(--sul-accent-tint) 0, var(--sul-accent-tint) 2px, transparent 2px, transparent 16px),
		linear-gradient(135deg, color-mix(in srgb, var(--sul-ink) 88%, transparent), color-mix(in srgb, var(--sul-ink) 96%, transparent));
}
.sul-store-card--ph-0 { --sul-accent-tint: color-mix(in srgb, var(--sul-guava) 30%, transparent); }
.sul-store-card--ph-1 { --sul-accent-tint: color-mix(in srgb, var(--sul-marigold) 30%, transparent); }
.sul-store-card--ph-2 { --sul-accent-tint: color-mix(in srgb, var(--sul-calamansi) 30%, transparent); }
.sul-store-card__body { position: relative; padding: var(--sul-space-4); }
/* Logo beside a two-line name+handle stack (2026-09-25, direct request:
   "rounded square and should occupy two lines") - rounded square, not
   the circle avatar the product card's own seller byline uses
   (.sul-search-card__seller-logo, 13-search.css) - deliberately
   different from that one now, sized to roughly span the combined
   height of the name + handle lines beside it rather than sitting on
   just the name's own line. */
.sul-store-card__identity {
	display: flex;
	align-items: center;
	gap: var(--sul-space-2);
}
.sul-store-card__identity-text { min-width: 0; }
.sul-store-card__logo {
	width: 40px;
	height: 40px;
	border-radius: var(--sul-radius-sm);
	object-fit: cover;
	border: 1px solid var(--sul-surface);
	box-shadow: 0 0 0 1px var(--sul-border);
	flex-shrink: 0;
}
.sul-store-card__logo--noimg {
	display: flex;
	align-items: center;
	justify-content: center;
	background: var(--sul-surface-2);
	color: var(--sul-muted);
	font-size: 1rem;
	font-weight: 700;
	text-transform: uppercase;
}
/* On a real cover photo, a plain --sul-surface/--sul-border ring reads
   too quiet against a photo - matches storefront-head-compact.php's own
   .logo--oncover treatment (a solid white-ish ring) instead. */
.has-cover .sul-store-card__logo,
.has-cover .sul-store-card__logo--noimg {
	border: 2px solid rgba(255, 255, 255, .85);
	box-shadow: none;
}
.has-cover .sul-store-card__logo--noimg { background: rgba(20, 14, 17, .55); color: #fff; }
/* Same tinted-pill mix as .sul-badge-verified (04-components.css) -
   marigold, matching its documented "sulit / worth it" meaning, not
   guava. White text on solid marigold measures only ~2:1 contrast (a
   hard AA fail, the same class of bug the Brand Guide already caught
   once for bright guava); a heavier tint (checked: 88% marigold) still
   only clears ~4.0:1, short of the 4.5:1 body-text minimum. This 22%
   mix measures 6.37:1 - verified before use, not assumed. */
.sul-store-card__top-left {
	position: absolute;
	top: var(--sul-space-2);
	left: var(--sul-space-2);
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: var(--sul-space-1);
}
.sul-store-card__featured {
	font-size: var(--sul-fs-label);
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.03em;
	color: var(--sul-marigold-ink);
	background: color-mix(in srgb, var(--sul-marigold) 22%, var(--sul-surface));
	border: 1px solid color-mix(in srgb, var(--sul-marigold) 45%, var(--sul-border));
	padding: 0.2rem 0.5rem;
	border-radius: var(--sul-radius-pill);
}
/* Solid chip, not the translucent .sul-pill this used to be (2026-09-30,
   direct feedback: lost contrast against the lighter-toned placeholder
   patterns above) - same dark ink tone the scrim already darkens toward,
   so it reads correctly against a real photo, any of the 3 placeholder
   tints, or the featured badge's own marigold sitting right above it. */
.sul-store-card__location {
	display: inline-flex;
	align-items: center;
	gap: 0.25rem;
	font-size: var(--sul-fs-label);
	font-weight: 500;
	color: #fff;
	background: rgba(20, 14, 17, .78);
	padding: 0.2rem 0.5rem;
	border-radius: var(--sul-radius-sm);
}
.sul-store-card__location svg { width: 13px; height: 13px; flex-shrink: 0; }
/* $args['sponsored'] (2026-09-25, direct feedback: "don't paid-tier
   stores deserve the sponsored treatment [in store search too]?") - the
   caller decides (front-page.php's search contexts only); this
   template has no notion of "search result" on its own, so it never
   leaks into plain Discover browsing or the homepage's Featured/New
   strips.
   Plain flush corner tag, bottom-right, not a diagonal ribbon
   (2026-09-30) - the ribbon direction went through five rounds of real
   bugs on this exact card pair (search-result-card.php +
   store-card.php, PRs #446-450) before being abandoned outright:
   "They look inconsistent across desktop and mobile and between
   stores and products so let's reconsider a simpler approach" (a real
   marketplace's own plain corner tag used as the reference). See
   .sul-search-card__sponsored's own comment (13-search.css) for the
   full reasoning on why a flat tag has none of the ribbon's problems,
   the neutral-gray color choice ("same styling... for both store and
   products" / "Maybe same 'color' as well?"), and the flush-corner
   positioning ("it should touch the right bottom corner/edges").
   Exact same values reused here, unchanged. Opposite corner from
   Featured/Location so the two can coexist on the same card without
   overlapping (a store can be both) - unchanged reasoning from every
   earlier version of this badge, only the visual treatment changed.
   .sul-store-card already has its own overflow:hidden (this file, its
   own rule) so the tag's outer corner clips to match the card's own
   rounding automatically, same as the search card. */
.sul-store-card__sponsored {
	position: absolute;
	bottom: 0;
	right: 0;
	z-index: 1;
	background: var(--sul-surface);
	color: var(--sul-muted);
	font-size: var(--sul-fs-label);
	font-weight: 600;
	padding: 0.2rem 0.5rem;
	border: 1px solid var(--sul-border);
	border-top-left-radius: var(--sul-radius-sm);
}
.sul-store-card__link:hover .sul-store-card__name { color: var(--sul-guava-strong); }
/* Plain block now, not flex - the logo+name inline row moved up a level
   to .sul-store-card__identity, this is just the top line of the
   two-line text stack beside it. */
.sul-store-card__name {
	margin: 0;
	font-size: var(--sul-fs-h3);
	display: flex;
	align-items: center;
	gap: 0.35rem;
}
/* The name truncates, the Verified seal after it never does. */
.sul-store-card__name-text {
	min-width: 0;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}
.sul-store-card__name .sul-verified-mark { flex: none; }
.sul-store-card__handle {
	margin: 0.1rem 0 0;
	font-size: var(--sul-fs-sm);
	color: var(--sul-muted);
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}
.sul-store-card__tagline {
	margin: var(--sul-space-2) 0;
	font-size: var(--sul-fs-sm);
	color: var(--sul-muted);
	overflow: hidden;
	text-overflow: ellipsis;
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
}
.sul-store-card .sul-cluster { margin-top: var(--sul-space-2); gap: var(--sul-space-2); }

/* Text-over-photo legibility - same #fff/opacity-.88 split storefront-
   head-compact.php's own on-cover identity block already uses
   (08-storefront.css), reused rather than inventing new values. */
.has-cover .sul-store-card__name { color: #fff; }
.has-cover .sul-store-card__handle,
.has-cover .sul-store-card__tagline { color: rgba(255, 255, 255, .88); }
/* The Verified pill is the same solid marigold over a photo (04-components.css), so it needs no on-cover override. */
/* .has-cover .sul-pill removed 2026-09-30 - location no longer renders
   as a .sul-pill inside this card (see .sul-store-card__location above)
   after the contrast fix; .sul-pill itself is still used elsewhere
   sitewide, untouched. */

/* assets/css/12-directory.css */
/* Directory (front-page.php) — search/filter bar + pagination.
   Store-card styles live in 11-home.css, shared with the homepage. Result
   sections for the query-driven state (Stores/Sponsored/organic grid,
   divider) reuse 13-search.css's classes — that file's own comment
   explains why they weren't renamed when /search/ was absorbed here. */

/* The filter area (2026-10-14, option 2 of a mockup round). The header search is the one text
   search on every page now (header.php), so this page has no search bar of its own: two rows on
   a wide screen, the tabs and Sort, then the filters (Location, Condition, Status,
   Authenticity). On a phone both rows are display:contents (the mobile block below), so every
   piece is a flex item of .sul-discover-header and is ordered there. The base rules sit above
   the mobile block on purpose: equal specificity, so source order decides, and a base rule after
   it would cancel its display:contents. */
.sul-discover-header {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--sul-space-3);
	margin: var(--sul-space-6) 0;
}
.sul-discover-row {
	display: flex;
	flex: 1 1 100%;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--sul-space-3) var(--sul-space-4);
	min-width: 0;
}
.sul-discover-sort { flex: 0 0 auto; }
/* Sort at the right end of the one controls row (2026-10-14, no tabs any more). */
.sul-discover-row--controls .sul-discover-sort { margin-left: auto; }
.sul-discover-tabs__filters {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--sul-space-4);
}
.sul-discover-loc {
	display: flex;
	align-items: center;
	gap: var(--sul-space-2);
}
.sul-discover-loc__label {
	font-size: var(--sul-fs-sm);
	font-weight: 600;
	color: var(--sul-muted);
	white-space: nowrap;
}
/* The filters have no visible headings (2026-10-14, Joefer: the four labelled dropdowns wrapped to a second
   line on a tablet; "reuse the storefront's, but with icons"). The storefront's product filters already
   drop their headings at every width because each dropdown says what it is ("Any Condition"); here a small
   icon stands in for the heading. The words stay in the page for screen readers (same clip technique as
   .sul-visually-hidden, 07-utilities.css). "Sort by" keeps its word. */
.sul-discover-row--controls .sul-discover-filter__label,
.sul-discover-row--controls .sul-discover-loc__label {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	overflow: hidden;
	clip: rect(0 0 0 0);
	white-space: nowrap;
	border: 0;
}
.sul-discover-row--controls .sul-discover-filter { position: relative; }
.sul-discover-filter__icon,
.sul-discover-loc__icon {
	position: absolute;
	left: 0.65rem;
	top: 50%;
	z-index: 1;
	width: 16px;
	height: 16px;
	transform: translateY(-50%);
	color: var(--sul-muted);
	pointer-events: none;
}
.sul-discover-row--controls .sul-discover-filter .sul-lb__trigger,
.sul-discover-row--controls .sul-location-picker__trigger { padding-left: 2.2rem; }
/* The front page has no breadcrumb or title above this, so it starts closer to the header. */
.sul-section--flush-top { padding-top: var(--sul-space-6); }
/* On the plain front page the heading is hidden (absolutely positioned), so the filter area is the first
   thing and its own top margin would double the 24px above it: 48px was too much space (2026-10-14). */
.sul-visually-hidden + .sul-discover-header { margin-top: 0; }
/* The results line (2026-10-14): the site's 32px display page title dwarfed the 11px labels and the
   filters under it, and "looked off" (Joefer). A quieter line in the body font, "Results for" muted and
   the search term in the full text colour, 16px above the tabs. */
.sul-results-title {
	margin: 0 0 var(--sul-space-4);
	font-family: var(--sul-font-body);
	font-size: var(--sul-fs-h2);
	font-weight: 600;
	line-height: 1.3;
	color: var(--sul-muted);
	text-wrap: wrap;
	overflow-wrap: anywhere;
}
.sul-results-title__q { color: var(--sul-ink); }
.sul-results-title + .sul-discover-header { margin-top: 0; }

/* Location filter (2026-09-30) - a custom listbox, not a native <select>; see front-page.php's
   own comment on why. A filter like Condition and Status since 2026-10-14. */
.sul-location-picker { position: relative; flex: 0 0 auto; }
/* Looks like the Condition/Status/Authenticity selects beside it (2026-10-14: it is a filter now,
   not the fused half of the old search bar). */
.sul-location-picker__trigger {
	display: flex;
	align-items: center;
	gap: 0.4rem;
	max-width: 16rem;
	padding: 0.4rem 1.75rem 0.4rem 0.6rem;
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius-sm);
	background-color: var(--sul-surface);
	color: var(--sul-ink);
	font: inherit;
	font-size: var(--sul-fs-sm);
	white-space: nowrap;
	cursor: pointer;
	appearance: none;
	-webkit-appearance: none;
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%237A6B72' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
	background-repeat: no-repeat;
	background-position: right 0.6rem center;
}
.sul-location-picker__trigger span { overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.sul-location-picker__trigger:focus-visible {
	outline: none;
	box-shadow: inset 0 0 0 2px var(--sul-guava-strong);
}
/* position/top/left are set inline by location-picker.js at open time, not here (2026-09-30: the
   panel is moved to <body> and positioned from the trigger's real viewport coordinates, which
   also keeps it out of any clipping ancestor). min-width here is only the pre-JS floor. */
.sul-location-picker__panel {
	min-width: 240px;
	max-height: 320px;
	overflow-y: auto;
	background: var(--sul-surface);
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius-sm);
	box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
	z-index: 100;
}
/* The type-to-search box stays at the top while the list scrolls under it. */
.sul-location-picker__search {
	position: sticky;
	top: 0;
	z-index: 1;
	padding: 0.5rem;
	background: var(--sul-surface);
	border-bottom: 1px solid var(--sul-border);
}
.sul-location-picker__input {
	width: 100%;
	padding: 0.45rem 0.6rem;
	border: 1px solid var(--sul-field-border);
	border-radius: var(--sul-radius-sm);
	background: var(--sul-surface);
	color: var(--sul-ink);
	font: inherit;
	font-size: var(--sul-fs-sm);
}
.sul-location-picker__input:focus-visible { outline: 2px solid var(--sul-guava-strong); outline-offset: 1px; }
.sul-location-picker__empty { padding: 0.75rem; font-size: var(--sul-fs-sm); color: var(--sul-muted); }
.sul-location-picker__option {
	padding: 0.5rem 0.75rem 0.5rem 1.5rem;
	font-size: var(--sul-fs-sm);
	color: var(--sul-ink);
	cursor: pointer;
}
.sul-location-picker__option:first-child { padding-left: 0.75rem; }
/* The region row itself is a real, clickable pick (matches every province
   in that region) - not just a visual header the way an <optgroup> label
   would be. Bold + its own top border are the ONLY differentiators from a
   plain province row (direct feedback, 2026-09-30: match Option A's plain
   look exactly, no accent color/icon) - hover/focus below carries the
   rest of the "this is clickable" signal. */
.sul-location-picker__option--region {
	padding-left: 0.75rem;
	font-weight: 600;
	border-top: 1px solid var(--sul-border);
}
.sul-location-picker__option:first-child.sul-location-picker__option--region { border-top: none; }
.sul-location-picker__option:hover,
.sul-location-picker__option:focus {
	background: color-mix(in srgb, var(--sul-ink) 8%, var(--sul-surface));
	outline: none;
}
.sul-location-picker__option[aria-selected="true"] {
	background: color-mix(in srgb, var(--sul-ink) 12%, var(--sul-surface));
}
/* Narrower screens (2026-10-14, option C: no tabs). Under 1080px (Location, the three dropdowns and Sort need
   about 985px of container, so a 900-1050px window wrapped) the three filters fold into the Filter
   button and its bottom sheet, as the storefront's product filters do (store-search.js there,
   mobile-filter-sheet.js here); Location stays, and Sort stays at the right end. On a phone the row is
   Location (taking the room that is left), Filter and Sort on one line, with Sort's word hidden. The
   wrapper is display:contents so its trigger is a flex item of the row; the backdrop and sheet are
   position:fixed and escape it. Above 900px the wrapper is not shown at all. */
.sul-discover-mobile-filter { display: none; }
@media (max-width: 1079px) {
	.sul-discover-tabs__filters { display: none; }
	.sul-discover-mobile-filter { display: contents; }
}
@media (max-width: 600px) {
	.sul-discover-row { flex-wrap: nowrap; gap: var(--sul-space-2); }
	.sul-discover-row--controls .sul-discover-sort { margin-left: 0; }
	.sul-discover-loc { flex: 1 1 0; min-width: 0; }
	.sul-location-picker { flex: 1 1 auto; min-width: 0; }
	.sul-location-picker__trigger { width: 100%; max-width: none; min-width: 0; }
	/* Hides the "Sort by" text, not the control (2026-09-30, "375 still has issues"); same clip
	   technique as .sul-visually-hidden, so the label stays associated for screen readers. */
	.sul-discover-sort__label {
		position: absolute;
		width: 1px;
		height: 1px;
		padding: 0;
		margin: -1px;
		overflow: hidden;
		clip: rect(0 0 0 0);
		white-space: nowrap;
		border: 0;
	}
}

/* Mobile Filter sheet itself (2026-10-01) - trigger button, then the
   sheet's own static box styling. Open/closed is the `hidden` attribute,
   toggled by mobile-filter-sheet.js - deliberately no `display` declared
   on .sul-discover-mobile-filter__backdrop/__sheet's own base rules so
   the UA stylesheet's own `[hidden] { display: none; }` isn't fighting
   an equal-specificity author rule for the visible state (the same class
   of cascade-order footgun as the @media-vs-unconditional-rule bug fixed
   earlier this file, just origin-order instead of source-order) - the
   default block display already the sheet/backdrop need when visible. */
.sul-discover-mobile-filter__trigger {
	display: inline-flex;
	align-items: center;
	gap: var(--sul-space-1, 4px);
	padding: 0.4rem 0.75rem;
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius-sm);
	background: var(--sul-surface);
	color: var(--sul-ink);
	font: inherit;
	font-size: var(--sul-fs-sm);
}
.sul-discover-mobile-filter__count {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 16px;
	height: 16px;
	padding: 0 4px;
	border-radius: 999px;
	background: var(--sul-guava-strong);
	color: #fff;
	font-size: 11px;
	font-weight: 700;
}
.sul-discover-mobile-filter__backdrop {
	position: fixed;
	inset: 0;
	background: color-mix(in srgb, var(--sul-ink) 45%, transparent);
	/* Above the sticky header's own z-index:100 (06-header-footer.css) -
	   not the header-local 60/61 this started as, which left the header
	   rendering above the dimmed backdrop, still fully bright and
	   clickable while the sheet was supposedly modal. */
	z-index: 1010;
}
.sul-discover-mobile-filter__sheet {
	position: fixed;
	left: 0;
	right: 0;
	bottom: 0;
	z-index: 1011;
	max-height: 80vh;
	overflow-y: auto;
	background: var(--sul-surface);
	border-radius: var(--sul-radius) var(--sul-radius) 0 0;
	padding: var(--sul-space-4);
	box-shadow: var(--sul-shadow-sm);
}
.sul-discover-mobile-filter__header {
	display: flex;
	align-items: center;
	justify-content: space-between;
	margin-bottom: var(--sul-space-4);
	font-weight: 600;
}
.sul-discover-mobile-filter__close {
	background: none;
	border: none;
	color: var(--sul-muted);
	padding: 0;
	line-height: 0;
}
.sul-discover-mobile-filter__label {
	margin: 0 0 var(--sul-space-2);
	font-size: var(--sul-fs-sm);
	font-weight: 600;
	color: var(--sul-muted);
}
.sul-discover-mobile-filter__chips {
	display: flex;
	flex-wrap: wrap;
	gap: var(--sul-space-2);
	margin-bottom: var(--sul-space-4);
}
.sul-discover-mobile-filter__chip {
	border: 1px solid var(--sul-border);
	border-radius: 999px;
	background: var(--sul-surface);
	color: var(--sul-ink);
	padding: 0.4rem 0.8rem;
	font-size: var(--sul-fs-sm);
}
.sul-discover-mobile-filter__chip.is-active {
	border-color: var(--sul-guava-strong);
	background: color-mix(in srgb, var(--sul-guava-strong) 12%, var(--sul-surface));
	color: var(--sul-guava-strong);
	font-weight: 600;
}
.sul-discover-mobile-filter__actions {
	display: flex;
	gap: var(--sul-space-3);
	margin-top: var(--sul-space-2);
}
.sul-discover-mobile-filter__reset {
	flex: 1 1 0;
	padding: 0.6rem;
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius-sm);
	background: var(--sul-surface);
	color: var(--sul-ink);
	font: inherit;
}
.sul-discover-mobile-filter__apply {
	flex: 1 1 0;
	padding: 0.6rem;
	border: none;
	border-radius: var(--sul-radius-sm);
	background: var(--sul-guava-strong);
	color: #fff;
	font: inherit;
	font-weight: 600;
}

/* Sort by control (2026-09-25, direct request - "add a Sorting option
   like the one from Rentl [no randomizer]") - sul_render_discover_sort()
   in front-page.php. A plain inline label + select, not fused into
   the search bar above it (different form, different purpose - this one
   only ever carries 'sort' plus whatever hidden fields it needs to
   preserve, auto-submits on change). */
.sul-discover-sort {
	display: flex;
	align-items: center;
	gap: var(--sul-space-2);
}
.sul-discover-sort__label {
	font-size: var(--sul-fs-sm);
	font-weight: 600;
	color: var(--sul-muted);
	white-space: nowrap;
}
.sul-discover-sort select {
	padding: 0.4rem 1.75rem 0.4rem 0.6rem;
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius-sm);
	background: var(--sul-surface);
	color: var(--sul-ink);
	font: inherit;
	font-size: var(--sul-fs-sm);
	/* Same custom-chevron fix every other native select on the front end
	   already needed (06-header-footer.css/05-forms.css/10-dashboard.css)
	   - padding-right alone doesn't reliably push a native select's own
	   arrow away from the edge; appearance:none + a real SVG chevron does. */
	appearance: none;
	-webkit-appearance: none;
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%237A6B72' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
	background-repeat: no-repeat;
	background-position: right 0.6rem center;
}
.sul-discover-sort select:focus-visible {
	outline: none;
	box-shadow: inset 0 0 0 2px var(--sul-guava-strong);
}

/* Condition/Status filters (2026-10-01, direct request, desktop only for
   now) - sul_render_discover_filter() in front-page.php, same
   label+select shape as .sul-discover-sort right above (direct request:
   "Maybe still dropdowns like the existing Sort By filter" - an earlier
   filter-chip mockup was rejected). Positioned on the tabs row itself, not
   this bar - see the @media (min-width: 601px) block further down, and
   the file-level note on why mobile placement is out of scope here. */
.sul-discover-filter {
	display: flex;
	align-items: center;
	gap: var(--sul-space-2);
}
.sul-discover-filter__label {
	font-size: var(--sul-fs-sm);
	font-weight: 600;
	color: var(--sul-muted);
	white-space: nowrap;
}
.sul-discover-filter select {
	padding: 0.4rem 1.75rem 0.4rem 0.6rem;
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius-sm);
	background: var(--sul-surface);
	color: var(--sul-ink);
	font: inherit;
	font-size: var(--sul-fs-sm);
	appearance: none;
	-webkit-appearance: none;
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%237A6B72' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
	background-repeat: no-repeat;
	background-position: right 0.6rem center;
}
.sul-discover-filter select:focus-visible {
	outline: none;
	box-shadow: inset 0 0 0 2px var(--sul-guava-strong);
}

/* Near me (includes/near-me.php): the strip under the toolbar while Sort by is
   "Near me" - either the one-line explanation, or the short "Where are you?"
   form. Quiet on purpose: it explains the order, it is not a second toolbar. */
.sul-near {
	margin: 0 0 var(--sul-space-6);
	padding: var(--sul-space-3) var(--sul-space-4);
	background: var(--sul-surface-2);
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius-sm);
}
.sul-near--active { font-size: var(--sul-fs-sm); color: var(--sul-muted); }
.sul-near--active a { margin-left: var(--sul-space-2); font-weight: 600; color: var(--sul-guava-strong); }
/* The chosen place stands out from the muted sentence: a chip with a pin, semibold, in the ink colour, with a plain
   border (the first version had a guava border and bold text; Joefer, 2026-10-14: "a bit too loud"). */
.sul-near__place {
	display: inline-flex;
	align-items: center;
	gap: 0.3rem;
	padding: 0.1rem 0.6rem 0.1rem 0.45rem;
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius-sm);
	background: var(--sul-surface);
	font-weight: 600;
	color: var(--sul-ink);
	vertical-align: baseline;
}
.sul-near__place-icon { flex: none; color: var(--sul-guava-strong); }
.sul-near__title { margin: 0 0 var(--sul-space-2); font-weight: 600; color: var(--sul-ink); }
.sul-near__row { display: flex; flex-wrap: wrap; gap: var(--sul-space-3); align-items: center; }
.sul-near__row > select,
.sul-near__row > .sul-lb { flex: 1 1 12rem; min-width: 0; }
.sul-near .sul-form__hint { margin-top: var(--sul-space-2); }
@media (max-width: 600px) {
	.sul-near__row > .sul-btn { flex: 1 1 100%; }
}

/* Near me, second pass: the "where are you?" form gets a "Use my location" link. */
.sul-near--active .sul-near__line { margin: 0; }
.sul-near__locate { margin: var(--sul-space-2) 0 0; display: flex; flex-wrap: wrap; gap: var(--sul-space-2); align-items: baseline; }
.sul-near__locate[hidden] { display: none; }
/* While the device is being asked (near-me.js adds .is-locating): only the status line and "Pick a place instead"
   show; the boxes come back if it fails, is declined or is skipped. */
.sul-near.is-locating .sul-near__title,
.sul-near.is-locating .sul-near__row,
.sul-near.is-locating .sul-form__hint,
.sul-near.is-locating .sul-near__locate-btn:not(.sul-near__skip) { display: none; }
.sul-near__skip { margin-left: var(--sul-space-2); }
.sul-near__locate-btn {
	font: inherit;
	font-size: var(--sul-fs-sm);
	font-weight: 600;
	padding: 0;
	cursor: pointer;
	color: var(--sul-guava-strong);
	background: none;
	border: 0;
	text-decoration: underline;
}
.sul-near__locate-btn:disabled { cursor: progress; text-decoration: none; opacity: .7; }
.sul-near__locate-btn:focus-visible { outline: 2px solid var(--sul-guava-strong); outline-offset: 2px; }
.sul-near__locate-note { font-size: var(--sul-fs-sm); color: var(--sul-muted); }
.sul-near__locate-note.is-error { color: var(--sul-sili); }

/* The few-word data credit the coordinate license asks for: present, but quiet. */
.sul-near__credit { margin: var(--sul-space-1) 0 0; font-size: .75rem; color: var(--sul-muted); }
.sul-near__credit a { color: inherit; text-decoration: underline; }

/* assets/css/13-search.css */
/* Cross-store product search (2026-09-13 reframe). Originally search.php's
   own stylesheet; that template was retired 2026-09-25 when Discover
   absorbed search (front-page.php now renders these same sections/
   classes) — kept this filename/class prefix rather than a blanket rename,
   since "search" still accurately describes what these specific rules are
   for (the query-driven result cards/sections), just not which .php file
   uses them anymore. */

.sul-search-card {
	display: flex;
	flex-direction: column;
	background: var(--sul-surface);
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius);
	overflow: hidden;
	box-shadow: var(--sul-shadow-sm);
}
/* Same positioning context as .sul-listing-card__photo (08-storefront.css). */
.sul-search-card__photo { position: relative; }
.sul-search-card__media {
	position: relative;
	/* Needed for the Sponsored ribbon below (2026-09-30) to clip cleanly
	   at the photo's own edges - .sul-search-card's own overflow:hidden
	   only clips at the whole-card level, and this block's bottom edge
	   is interior to the card (body content follows below it), so a
	   diagonal ribbon reaching past it would otherwise bleed into that
	   space instead of stopping at the photo. */
	overflow: hidden;
	display: flex;
	align-items: center;
	justify-content: center;
	/* 1:1, matching the storefront grid card and detail gallery
	   (08-storefront.css) - the marketplace convention (Shopee, Lazada,
	   Carousell), not the earlier 4:3. */
	aspect-ratio: 1 / 1;
	/* Same treatment as the storefront grid card (08-storefront.css) -
	   deliberately NOT the detail gallery's transparent --sul-image-bg, and
	   the same tint .sul-search-card__noimg's "No photo yet" state already
	   uses, so a letterboxed photo and the empty state read as one
	   placeholder canvas. Only the top corners round (card's own
	   border-radius + overflow:hidden clip the media block's top edge; its
	   bottom edge is interior, above the body, so it stays square). */
	background: var(--sul-surface-2);
	/* Same line + shadow fix as the storefront grid card (08-storefront.css)
	   - a light/white photo melded into the card's own white body below
	   it without a real edge between the two. */
	border-bottom: 1px solid var(--sul-border);
	box-shadow: 0 2px 4px -2px color-mix(in srgb, var(--sul-ink) 18%, transparent);
}
/* No border-radius on the <img> itself - same reversal as the storefront
   grid card (08-storefront.css): a search-result photo should fill its
   tile edge-to-edge like the placeholder states do, not read as its own
   rounded tile floating inside the square. */
.sul-search-card__media img {
	max-width: 100%;
	max-height: 100%;
	width: auto;
	height: auto;
	object-fit: contain;
}
/* No background of its own - same fix as .sul-listing-card__noimg
   (08-storefront.css): .sul-search-card__media's own flat
   var(--sul-surface-2) already shows through, a separate gradient here
   just drifted a shade off from the same color behind a real photo. */
.sul-search-card__noimg {
	display: flex; align-items: center; justify-content: center;
	width: 100%; height: 100%;
}
/* Same "No photo yet" treatment as .sul-listing-card__noimg
   (08-storefront.css) - see its own comment for why a plain gradient tile
   wasn't enough on its own (field-muting/photo-optional round, 2026-09-22). */
.sul-search-card__noimg::after {
	content: "No photo yet";
	font-size: var(--sul-fs-label);
	font-weight: 600;
	color: var(--sul-muted);
}
/* Condition, top-right (2026-09-30) - mirrors the status pill's own
   top-left spot, and .sul-listing-card__condition's identical fix
   (08-storefront.css) - see that rule's own comment for why. */
.sul-search-card__condition {
	position: absolute;
	/* Top-left (2026-10-13), where the status pill used to be, above the Sold / Reserved mark. */
	left: var(--sul-space-2);
	top: var(--sul-space-2);
	z-index: 2;
	background: var(--sul-surface);
}
/* "Ad" - a plain flush corner tag, bottom-right, NOT a diagonal ribbon
   (2026-09-30). The ribbon direction (this file's own history: PRs
   #446-450) went through five rounds of real bugs - wrong rotation,
   band clipped to a sliver, invisible text in dark mode, not flush
   against the bottom edge - all inherent to the rotated-band technique
   itself. Direct call to abandon it: "They look inconsistent across
   desktop and mobile and between stores and products so let's
   reconsider a simpler approach" (pointed at a real marketplace's own
   plain corner tag as the reference). A flat, unrotated tag has none
   of the ribbon's problems - no rotation math, no clipping-length
   tuning, no corner-mirroring symmetry to get wrong, so it's
   inherently consistent across every card/breakpoint with nothing
   card-specific to tune.
   Neutral gray (var(--sul-muted) on var(--sul-surface)), not marigold -
   direct follow-up question, "Maybe same 'color' as well?", confirmed:
   reuse the theme's own existing muted/surface tokens rather than
   introduce a new color, matching the reference's own understated
   gray tag. Both tokens already flip correctly per theme, so this
   sidesteps the dark-mode contrast bug the marigold ribbon had -
   nothing custom to get wrong.
   bottom: 0; right: 0 (flush, not floating) - direct follow-up: "I
   want it to not 'float' - it should touch the right bottom
   corner/edges." Only the inner (top-left) corner of the tag is
   rounded - the outer corner sits exactly at the card's own bottom-
   right corner, where the card's own overflow:hidden + border-radius
   already clips it to match, no need to hand-round that corner too.
   Never a ranking signal - just a label on cards
   sul_get_sponsored_matches() already decided to place in the separate
   strip above. Same styling reused as-is on .sul-store-card__sponsored
   (11-home.css) - direct request: "same styling (color, etc.) for both
   store and products".
   border: var(--sul-border) added (2026-09-30, same day) - direct
   feedback: "look at these cards with white background - notice the
   Ad bg being indistinguishable." Status/Condition (both above) get
   this same border for free via the shared .sul-pill class; this tag
   never used .sul-pill (its asymmetric corner radius doesn't fit that
   class's own pill-radius shape), so it needed the border declared
   explicitly instead of inherited. */
.sul-search-card__sponsored {
	position: absolute;
	bottom: 0;
	right: 0;
	z-index: 1;
	background: var(--sul-surface);
	color: var(--sul-muted);
	font-size: var(--sul-fs-label);
	font-weight: 600;
	padding: 0.2rem 0.5rem;
	border: 1px solid var(--sul-border);
	border-top-left-radius: var(--sul-radius-sm);
}
/* flex: 1 - same fix as the storefront grid card's .sul-listing-card__body
   (08-storefront.css) - absorbs the extra height .sul-grid's row-stretch
   gives a shorter card, so the card's own bottom edge lines up evenly
   across a row instead of leaving blank space inside a taller
   neighbor's border. */
.sul-search-card__body { padding: var(--sul-space-3) var(--sul-space-4) var(--sul-space-4); display: flex; flex-direction: column; gap: var(--sul-space-2); flex: 1; }
/* Clamped to 2 lines + min-height reserving exactly that much space,
   same fix as .sul-listing-card__title (08-storefront.css) - see that
   rule's own comment for the full reasoning (line-clamp caps height but
   doesn't reserve it, so a 1-line title still sat shorter than a 2-line
   neighbor without the explicit min-height too). Direct report on this
   card specifically, 2026-10-01: "Notice how the Price are not lining up
   due to varying title line numbers. We fixed this on the cards for the
   Product section under the Storefronts. Let's use that fix." */
.sul-search-card__title {
	font-size: 1rem;
	margin: 0;
	font-weight: 600;
	line-height: 1.3;
	min-height: calc(1rem * 1.3 * 2);
	text-wrap: wrap;
	display: -webkit-box;
	-webkit-line-clamp: 2;
	line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
}
.sul-search-card__title a { color: var(--sul-ink); text-decoration: none; }
.sul-search-card__title a:hover { color: var(--sul-guava-strong); }
/* Price + condition share one row instead of stacking as separate lines -
   same space-saving pattern as the storefront grid card's
   .sul-listing-card__meta (08-storefront.css). space-between pins price
   left / pill right when both fit; flex-wrap drops the pill to its own
   line on a narrow card instead of squeezing. */
.sul-search-card__meta {
	display: flex;
	align-items: center;
	justify-content: space-between;
	flex-wrap: wrap;
	gap: var(--sul-space-2);
}
/* Display font, not the body font a plain 700-weight left it in - every
   other price on the site (.sul-listing__price, .sul-storefront__listings
   product cards) already uses --sul-font-display for exactly this reason:
   it's the number a buyer actually scans a result grid for, and it read as
   barely distinct from the title above it at the same font family. */
.sul-search-card__price { font-family: var(--sul-font-display); font-weight: 700; font-size: 1.2rem; margin: 0; color: var(--sul-guava-strong); }
/* "Was" price (2026-09-30, discount pricing) - body font/weight (not
   display), muted, sized down from the real price beside it - a card has
   no room for the single listing page's own pill treatment, so the
   strikethrough alone carries the discount signal here. */
.sul-search-card__price-original { font-family: var(--sul-font-body); font-weight: 400; color: var(--sul-muted); font-size: var(--sul-fs-sm); }

/* Seller identity - a byline above the title, not a full-width block
   below it (card rethink, 2026-09-23 - space-saving). Still its own
   bold, colored text rather than a quiet caption line - the 2026-09-13
   seller-bias positioning (a result never stops being "this seller's
   product," never an anonymous line item) isn't reopened here, only
   where that identity sits and how it's styled. No pill background
   (2026-09-24 - direct feedback: too small, and not a fan of the pill
   treatment) - plain avatar + name, sized up instead (28px logo, body
   text size) so it reads as a real byline, not a caption. width:
   fit-content, not a full-width block - a byline reads left-aligned and
   short, the way a headline's byline does, not as its own separate
   full-bleed row. */
.sul-search-card__seller {
	display: inline-flex;
	align-items: center;
	gap: var(--sul-space-2);
	width: fit-content;
	text-decoration: none;
}
.sul-search-card__seller:hover .sul-search-card__seller-name { color: var(--sul-guava); }
.sul-search-card__seller-logo {
	width: 28px;
	height: 28px;
	border-radius: 7px; /* a rounded square, the sitewide profile-photo shape (2026-10-13) */
	object-fit: cover;
	border: 1px solid var(--sul-surface);
	box-shadow: 0 0 0 1px var(--sul-border);
	flex-shrink: 0;
}
.sul-search-card__seller-name {
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	max-width: 14rem;
	font-size: var(--sul-fs-body);
	font-weight: 700;
	color: var(--sul-guava-strong);
}

/* Shared wrapper for every result section on this bar — Stores matching,
   Sponsored, and (implicitly, no wrapper needed) the organic grid itself.
   Named for its original single use (the Sponsored strip, 2026-09-13)
   before the Stores section reused it 2026-09-25 — kept the name rather
   than rename an already-live class for a styling-only reuse. */
.sul-search-sponsored { margin-bottom: var(--sul-space-6); }
/* Section labels on the All tab (2026-10-14, option A of a mockup round): "Stores" and "Products", or
   "... matching "camera"" for a search, with a "See all stores" link on the stores one when more than
   the three shown match. .sul-eyebrow is the site's label style; its own bottom margin moves onto the
   row so the link lines up with the label's baseline. No arrow on the link (Joefer dislikes them). */
.sul-section-head {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--sul-space-4);
	margin: 0 0 var(--sul-space-3);
}
.sul-section-head__title { margin: 0; text-wrap: wrap; }
.sul-section-head__link {
	flex: none;
	display: inline-flex;
	align-items: center;
	gap: 2px;
	font-size: var(--sul-fs-sm);
	font-weight: 600;
	color: var(--sul-guava-strong);
	text-decoration: none;
}
.sul-section-head__link:hover { color: var(--sul-btn-bg-hover); }
/* The chevron (2026-10-14, Joefer: the Toolkit's "portal" idea from joefertraya.com, once per hover, and only
   ever ONE chevron in view): a one-chevron-wide window with a single chevron in it. On hover or focus it
   slides out through the side it points to, jumps to the other side while out of sight (the 42% keyframe pair,
   so it is fully hidden at the jump), and slides back in, ease-in out and ease-out in, 0.8s, once. Leaving
   mid-way drops the animation, so it snaps back to rest (the same as the Toolkit's). Still for anyone who asks
   for reduced motion, and on a screen with no hover only a keyboard focus plays it. */
.sul-chev { position: relative; width: 16px; height: 16px; overflow: hidden; flex: none; }
.sul-chev svg { position: absolute; top: 0; left: 0; width: 16px; height: 16px; }
@keyframes sul-chev-next {
	0% { transform: translateX(0); animation-timing-function: ease-in; }
	42% { transform: translateX(18px); }
	42.01% { transform: translateX(-18px); animation-timing-function: ease-out; }
	100% { transform: translateX(0); }
}
@keyframes sul-chev-prev {
	0% { transform: translateX(0); animation-timing-function: ease-in; }
	42% { transform: translateX(-18px); }
	42.01% { transform: translateX(18px); animation-timing-function: ease-out; }
	100% { transform: translateX(0); }
}
/* "See all stores" plays it on its own, one pass every 4s (0.8s of movement, then rest), and STOPS while
   hovered or focused (2026-10-14, Joefer: an extra nudge toward the stores list, like the Toolkit's arrow
   that stops under the pointer; removing the animation drops it straight to rest). "Back to products" stays
   still until hovered or focused, then plays once. Nothing moves under reduced motion. */
@keyframes sul-chev-idle {
	0% { transform: translateX(0); animation-timing-function: ease-in; }
	8.4% { transform: translateX(18px); }
	8.41% { transform: translateX(-18px); animation-timing-function: ease-out; }
	20%, 100% { transform: translateX(0); }
}
@media (prefers-reduced-motion: no-preference) {
	.sul-section-head__link--next .sul-chev svg { animation: sul-chev-idle 4s infinite; }
	.sul-section-head__link--next:focus-visible .sul-chev svg { animation: none; }
	.sul-section-head__link--prev:focus-visible .sul-chev svg { animation: sul-chev-prev 0.8s 1; }
}
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
	.sul-section-head__link--next:hover .sul-chev svg { animation: none; }
	.sul-section-head__link--prev:hover .sul-chev svg { animation: sul-chev-prev 0.8s 1; }
}
/* The stores row is its own block above the products, with room instead of the old dotted line. */
.sul-search-sponsored--stores { margin-bottom: var(--sul-space-8); }
/* One quiet line above the products when a search matched products but no store (2026-10-14). */
.sul-results-note { margin: 0 0 var(--sul-space-4); font-size: var(--sul-fs-sm); color: var(--sul-muted); }
.sul-results-note a { font-weight: 600; }
/* On a phone the stores shelf is a row you swipe, not three full-width cards stacked (2026-10-14, option
   C): the products start right under it, and the next card peeking in says there is more. The row bleeds
   to the screen edge (the container's own side padding is added back inside it). */
@media (max-width: 600px) {
	.sul-search-sponsored--stores .sul-grid {
		display: flex;
		gap: var(--sul-space-3);
		overflow-x: auto;
		scroll-snap-type: x mandatory;
		margin-inline: calc(-1 * clamp(1rem, 4vw, 2rem));
		padding-inline: clamp(1rem, 4vw, 2rem);
		scroll-padding-inline: clamp(1rem, 4vw, 2rem);
		scrollbar-width: none;
		-webkit-overflow-scrolling: touch;
	}
	.sul-search-sponsored--stores .sul-grid::-webkit-scrollbar { display: none; }
	.sul-search-sponsored--stores .sul-grid > * {
		flex: 0 0 min(78%, 300px);
		scroll-snap-align: start;
	}
}
/* .sul-search-sponsored__heading removed (2026-09-25) - each section's own
   <h2> is now .sul-visually-hidden (front-page.php); the old visible
   styling isn't needed anymore, see the divider below for what replaced it. */

/* Marks the boundary between the Sponsored strip and the organic grid
   below it (2026-09-25) - only rendered alongside a real sponsored strip
   (search.php), so it never appears as an orphaned rule above results
   with nothing to divide. A thin line with the page's own background
   "punched through" at center, not a label - the per-card badges above
   and their absence below already say what each group is; this only
   needs to mark that a new group starts here, not repeat why. */
.sul-search-divider {
	position: relative;
	height: 1px;
	background: var(--sul-border);
	margin: var(--sul-space-8) 0 var(--sul-space-6);
}
.sul-search-divider::after {
	content: "•";
	position: absolute;
	left: 50%;
	top: 50%;
	transform: translate(-50%, -50%);
	background: var(--sul-ground);
	padding: 0 var(--sul-space-3);
	color: var(--sul-muted);
	font-size: var(--sul-fs-sm);
	line-height: 1;
}

/* assets/css/15-carousel.css */
/* Listing detail — gallery carousel + lightbox (2026-09-14).
   Ported from Rentl-v2's gear-carousel.css/.js: a main viewer with prev/
   next arrows and a counter, a scrollable thumbnail strip below it, and a
   click-to-zoom native <dialog> lightbox — trimmed to Sulitista's simpler
   gallery data model (no captions, no status badges, no self-hosted video;
   see assets/js/carousel.js's own docblock).

   Bundled sitewide via sul_css_source_files() rather than conditionally
   enqueued the way Rentl keeps its own copy out of its main bundle — this
   theme already bundles other page-specific stylesheets that way
   (13-search.css, 12-directory.css), so this follows the same local
   convention rather than introducing a second one. */

.sul-carousel__main {
	position: relative;
	/* 1:1, matching every other product-photo context in the theme, not
	   Rentl's 4:3 (08-storefront.css). */
	aspect-ratio: 1 / 1;
	/* Centers the image at its own rendered size within the square frame
	   (below) instead of stretching the <img> box to fill it — needed so
	   border-radius rounds the photo's own visible edges, not just the
	   frame's corners (see that rule's comment). */
	display: flex;
	align-items: center;
	justify-content: center;
}
.sul-carousel__main img {
	display: block;
	/* max-width/max-height + auto, not width/height: 100% — a portrait or
	   landscape photo under object-fit: contain doesn't reach every edge
	   of a square frame, so a width/height: 100% <img> box only rounds
	   the corners of the (here, invisible/transparent) frame, not the
	   photo's own visible boundary sitting inside it — this sizing lets
	   the <img>'s own box shrink to match exactly what's rendered, the
	   same technique the lightbox's own image already uses further down
	   this file (.sul-lightbox__img). */
	max-width: 100%;
	max-height: 100%;
	width: auto;
	height: auto;
	object-fit: contain;
	/* 8px, not var(--sul-radius) (16px, shared sitewide by cards/buttons/
	   dashboard panels etc.) — chosen against mockup variations at this
	   viewer's own larger scale, where the shared 16px token read as more
	   "rounded card" than this single full-bleed photo wanted. Deliberately
	   its own literal value, not a new token, since it's this one
	   component's own call rather than a shape decision meant to apply
	   elsewhere; the video iframe below is kept in sync with it by hand. */
	border-radius: 8px;
	transition: opacity 0.2s ease;
}
#sul-carousel-main-img { cursor: zoom-in; }

.sul-carousel__arrow {
	position: absolute; top: 50%; transform: translateY(-50%);
	background: none; color: var(--sul-ink); border: none;
	font-size: 40px; font-weight: 300; line-height: 1;
	cursor: pointer; padding: 0 var(--sul-space-2);
	opacity: 0.35; transition: opacity 0.2s ease;
	z-index: 2; user-select: none;
}
.sul-carousel__main:hover .sul-carousel__arrow { opacity: 0.85; }
.sul-carousel__arrow--prev { left: 0; }
.sul-carousel__arrow--next { right: 0; }

.sul-carousel__zoom-hint {
	position: absolute; bottom: var(--sul-space-3); left: var(--sul-space-3);
	width: 30px; height: 30px; border-radius: 50%;
	background: rgba(0, 0, 0, 0.45); color: #fff;
	display: flex; align-items: center; justify-content: center;
	opacity: 0; transition: opacity 0.2s ease; pointer-events: none; z-index: 2;
}
.sul-carousel__main:hover .sul-carousel__zoom-hint { opacity: 1; }

/* Video states on the main figure — default (a plain image),
   .is-video-active (the YouTube thumbnail + a play button, cursor swapped
   from zoom-in to pointer), .is-playing (the real, sandboxed
   youtube-nocookie.com iframe injected on click — see carousel.js). */
.sul-carousel__play-btn {
	position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
	width: 64px; height: 64px; border-radius: 50%;
	background: rgba(0, 0, 0, 0.55); color: #fff; border: 2px solid rgba(255, 255, 255, 0.85);
	display: none; align-items: center; justify-content: center;
	cursor: pointer; z-index: 2; transition: background 0.2s ease;
}
.sul-carousel__play-btn svg { margin-left: 3px; }
.sul-carousel__main:hover .sul-carousel__play-btn { background: rgba(0, 0, 0, 0.75); }
.sul-carousel__main.is-video-active .sul-carousel__play-btn { display: flex; }
.sul-carousel__main.is-video-active .sul-carousel__zoom-hint { display: none; }
.sul-carousel__main.is-video-active #sul-carousel-main-img { cursor: pointer; }
.sul-carousel__main.is-playing #sul-carousel-main-img,
.sul-carousel__main.is-playing .sul-carousel__play-btn { display: none; }
.sul-carousel__main iframe {
	position: absolute; inset: 0; width: 100%; height: 100%; border: 0;
	/* Matches the photo <img>'s own 8px above, not var(--sul-radius) — kept
	   in sync by hand so the frame doesn't visibly change shape between the
	   photo and video-playing states. */
	border-radius: 8px;
	background: #000;
}

.sul-carousel__counter {
	position: absolute; bottom: var(--sul-space-3); right: var(--sul-space-3);
	background: rgba(0, 0, 0, 0.45); color: #fff;
	font-size: 12px; padding: 3px 8px; border-radius: var(--sul-radius-pill);
	pointer-events: none; z-index: 2;
}

/* -- thumbnail strip -- */
.sul-carousel__thumbs-row {
	display: flex; align-items: center; gap: var(--sul-space-2);
	margin-top: var(--sul-space-2);
}
.sul-carousel__thumbs {
	display: flex; gap: var(--sul-space-2);
	overflow-x: auto; scrollbar-width: none; scroll-behavior: smooth;
	flex: 1; min-width: 0;
}
.sul-carousel__thumbs::-webkit-scrollbar { display: none; }
.sul-carousel__thumb {
	position: relative;
	flex: 0 0 64px;
	aspect-ratio: 1 / 1;
	padding: 0;
	border: 2px solid transparent;
	border-radius: calc(var(--sul-radius-sm) - 2px);
	overflow: hidden;
	cursor: pointer;
	/* Unlike the main viewer's canvas, this tile is always fully covered
	   by its (pre-cropped square) thumbnail once loaded — the tint is
	   just a placeholder for the lazy-loading gap, not part of the
	   "infinite canvas" system. */
	background: var(--sul-surface-2);
	transition: border-color 0.15s ease;
}
.sul-carousel__thumb.is-active { border-color: var(--sul-guava-strong); }
.sul-carousel__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.sul-carousel__thumb-play {
	position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
	width: 22px; height: 22px; border-radius: 50%;
	background: rgba(0, 0, 0, 0.6); color: #fff;
	display: flex; align-items: center; justify-content: center;
	pointer-events: none;
}
.sul-carousel__thumb-play svg { margin-left: 1px; }

/* Scroll-nav either side of the strip — hidden entirely when the strip
   doesn't overflow (visibility, not display, toggled by JS; see
   carousel.js's updateThumbNav() for why display would shift the strip's
   siblings on the post-paint measurement this runs). */
.sul-carousel__thumb-nav {
	flex: 0 0 auto; width: 28px; height: 28px; padding: 0;
	border-radius: 50%; border: 1px solid var(--sul-field-border);
	background: var(--sul-surface); color: var(--sul-ink);
	font-size: 18px; line-height: 1; cursor: pointer;
	display: flex; align-items: center; justify-content: center;
	visibility: hidden;
	transition: border-color 0.15s ease, color 0.15s ease;
}
.sul-carousel__thumb-nav:hover:not(:disabled) { border-color: var(--sul-guava-strong); color: var(--sul-guava-strong); }
.sul-carousel__thumb-nav:disabled { opacity: 0.3; cursor: default; }

/* -- lightbox — native <dialog> for the built-in focus trap + Escape-to-
   close, opened only from the active main image, never a thumbnail. -- */
.sul-lightbox {
	border: none; padding: 0; background: transparent;
	width: fit-content; height: fit-content; max-width: 92vw; max-height: 92vh;
	/* 01-base.css's *{margin:0} reset kills the browser's own
	   dialog:modal auto-centering — re-declared explicitly (Rentl-v2
	   precedent, same gotcha applies here). */
	position: fixed; inset: 0; margin: auto;
}
.sul-lightbox::backdrop { background: rgba(10, 10, 10, 0.9); }
.sul-lightbox__img {
	display: block; margin: 0 auto;
	max-width: 92vw; max-height: 92vh; width: auto; height: auto;
	object-fit: contain; border-radius: var(--sul-radius);
}
.sul-lightbox__close, .sul-lightbox__arrow {
	position: fixed; background: rgba(0, 0, 0, 0.5); color: #fff; border: none;
	cursor: pointer; z-index: 3; transition: opacity 0.2s ease; opacity: 0.85;
}
.sul-lightbox__close:hover, .sul-lightbox__arrow:hover { opacity: 1; }
.sul-lightbox__close {
	top: var(--sul-space-4); right: var(--sul-space-4);
	width: 40px; height: 40px; border-radius: 50%; font-size: 22px; line-height: 1;
}
.sul-lightbox__arrow {
	top: 50%; transform: translateY(-50%);
	width: 52px; height: 52px; border-radius: 50%; font-size: 30px; font-weight: 300; line-height: 1;
}
.sul-lightbox__arrow--prev { left: var(--sul-space-6); }
.sul-lightbox__arrow--next { right: var(--sul-space-6); }
.sul-lightbox__counter {
	position: fixed; bottom: var(--sul-space-4); left: 50%; transform: translateX(-50%);
	background: rgba(0, 0, 0, 0.5); color: #fff; font-size: 13px; padding: 4px 12px;
	border-radius: var(--sul-radius-pill); z-index: 3;
}
@media (max-width: 40rem) {
	.sul-lightbox__arrow { width: 42px; height: 42px; font-size: 22px; }
	.sul-lightbox__arrow--prev { left: var(--sul-space-2); }
	.sul-lightbox__arrow--next { right: var(--sul-space-2); }
}

/* Lightbox video states — same three-state shape as the main figure's own,
   reusing .sul-lightbox__img for both a photo and a video's thumbnail. */
.sul-lightbox__play-btn {
	position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);
	width: 72px; height: 72px; border-radius: 50%;
	background: rgba(0, 0, 0, 0.55); color: #fff; border: 2px solid rgba(255, 255, 255, 0.85);
	display: none; align-items: center; justify-content: center;
	cursor: pointer; z-index: 4;
}
.sul-lightbox__play-btn svg { margin-left: 3px; }
.sul-lightbox.is-video-active .sul-lightbox__play-btn { display: flex; }
.sul-lightbox.is-video-active .sul-lightbox__img { cursor: pointer; }
/* Sized to match the iframe's own footprint below rather than the
   thumbnail's tiny native pixel size, so there's no jump when playback
   starts (Rentl-v2 precedent). */
.sul-lightbox.is-video-active .sul-lightbox__img {
	width: min(92vw, 960px); height: min(88vh, 540px);
	object-fit: contain;
}
.sul-lightbox__iframe {
	display: block; width: min(92vw, 960px); height: min(88vh, 540px);
	border: 0; border-radius: var(--sul-radius);
	background: #000;
}
.sul-lightbox.is-playing .sul-lightbox__img,
.sul-lightbox.is-playing .sul-lightbox__play-btn { display: none; }

/* Fallback for a listing with no gallery items but a legacy featured
   image (single-sulitista_listing.php) — same canvas treatment as the
   carousel's own main image, just without any of the interactive chrome. */
.sul-listing__fallback-media {
	display: block;
	width: 100%;
	aspect-ratio: 1 / 1;
	object-fit: contain;
	background: var(--sul-image-bg);
	border-radius: var(--sul-radius);
}
/* No gallery AND no featured image - same gradient tile
   listing-card.php/search-result-card.php already use for a cover-less
   listing (.sul-listing-card__noimg, 08-storefront.css), sized like the
   two real-photo cases above it so the gallery column never collapses to
   nothing regardless of which of the three states a listing is in. */
.sul-listing__noimg {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 100%;
	aspect-ratio: 1 / 1;
	border-radius: var(--sul-radius);
	background: linear-gradient(135deg, var(--sul-surface-2), var(--sul-border));
}
/* Same "No photo yet" treatment as the card placeholders
   (.sul-listing-card__noimg, 08-storefront.css) - see its own comment for
   why (field-muting/photo-optional round, 2026-09-22). */
.sul-listing__noimg::after {
	content: "No photo yet";
	font-size: var(--sul-fs-sm);
	font-weight: 600;
	color: var(--sul-muted);
}

/* assets/css/16-pricing.css */
/* ==========================================================================
   Pricing page — the Free / Suki / Suki Boss comparison table
   (page-pricing.php, includes/pricing-page.php). Logged-in only for now.
   ========================================================================== */

.sul-pricing-table-wrap {
	margin-top: var(--sul-space-8);
	overflow-x: auto;
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius);
	box-shadow: var(--sul-shadow-sm);
}

.sul-pricing-table {
	width: 100%;
	min-width: 560px;
	border-collapse: collapse;
	background: var(--sul-surface);
	font-size: var(--sul-fs-sm);
}

.sul-pricing-table th,
.sul-pricing-table td {
	padding: var(--sul-space-3) var(--sul-space-4);
	text-align: left;
	border-bottom: 1px solid var(--sul-border);
	vertical-align: top;
}

.sul-pricing-table thead th {
	font-family: var(--sul-font-display);
	font-size: var(--sul-fs-h3);
	font-weight: 600;
	background: var(--sul-surface-2);
}

/* Row labels (first column) read as headers, not data — kept left-aligned
   and un-bolded past a point so a long feature name doesn't visually
   compete with the tier names above it. */
.sul-pricing-table tbody th {
	font-weight: 600;
	color: var(--sul-ink);
	white-space: nowrap;
}

.sul-pricing-table tbody tr:last-child th,
.sul-pricing-table tbody tr:last-child td {
	border-bottom: none;
}

/* Suki is the tier this page is actually trying to sell — the free/
   Suki-Boss decision is Joefer's own, but Suki is the default upgrade
   path a first-time reader should land on, same reasoning
   .sul-pill--available-style accenting gets used for elsewhere. */
.sul-pricing-table__highlight {
	background: color-mix(in srgb, var(--sul-guava) 8%, var(--sul-surface));
}
.sul-pricing-table thead .sul-pricing-table__highlight {
	background: color-mix(in srgb, var(--sul-guava) 16%, var(--sul-surface-2));
	color: var(--sul-guava-strong);
}

/* assets/css/17-inline-edit.css */
/* Storefront inline editing (includes/inline-edit.php, assets/js/inline-edit.js).
   Owner-only: none of this markup exists for visitors. All colour comes from
   the --sul-* tokens, so light, guarded dark and [data-theme] all resolve. */

/* Idle state (2026-10-12, "B" from a mockup round): a slim fixed bar along the bottom of the
   screen with the outlined Edit store button. It was a strip ABOVE the storefront, which made the
   owner's top (strip, gap, rounded cover) differ from what a buyer sees; out of the flow, the page
   an owner previews is the page a buyer gets. It is the same place the edit bar below takes over
   (the strip hides and the bar shows), so one bar changes what it says. Looks and sits like the
   edit bar; the header is at z-index 100, so it stays just under it. */
.sul-ie-strip {
	position: fixed;
	left: 0;
	right: 0;
	bottom: 0;
	z-index: 90;
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--sul-space-4);
	padding: var(--sul-space-3) var(--sul-space-4);
	padding-bottom: calc(var(--sul-space-3) + env(safe-area-inset-bottom, 0px));
	background: var(--sul-surface);
	border-top: 1px solid var(--sul-border);
	box-shadow: 0 -2px 12px rgba(0, 0, 0, .12);
}
.sul-ie-strip__text { margin: 0; font-size: var(--sul-fs-sm); color: var(--sul-muted); }
.sul-ie-start {
	display: inline-flex;
	align-items: center;
	gap: var(--sul-space-2);
	flex: none;
	font: inherit;
	font-size: var(--sul-fs-sm);
	font-weight: 600;
	line-height: 1.2;
	padding: 0.4rem 0.85rem;
	cursor: pointer;
	color: var(--sul-guava-strong);
	background: transparent;
	border: 1px solid var(--sul-guava-strong);
	border-radius: var(--sul-radius-sm);
}
.sul-ie-start:hover { background: var(--sul-surface-2); }
a.sul-ie-start { text-decoration: none; }
.sul-ie-start:focus-visible { outline: 2px solid var(--sul-guava-strong); outline-offset: 2px; }
.sul-ie-start svg { width: 15px; height: 15px; display: block; }
body.sul-ie-on .sul-ie-strip { display: none; }
/* Room so the bar never covers the last thing on the page. */
body:has(.sul-ie-strip) { padding-bottom: 4.5rem; }

/* Edit state: a fixed bar at the bottom (thumb reach on a phone). The header
   sits at z-index 100, so this stays just below it. */
.sul-ie-bar {
	position: fixed;
	left: 0;
	right: 0;
	bottom: 0;
	z-index: 90;
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--sul-space-4);
	padding: var(--sul-space-3) var(--sul-space-4);
	padding-bottom: calc(var(--sul-space-3) + env(safe-area-inset-bottom, 0px));
	background: var(--sul-surface);
	border-top: 1px solid var(--sul-border);
	box-shadow: 0 -2px 12px rgba(0, 0, 0, .12);
}
.sul-ie-bar[hidden] { display: none; }
.sul-ie-bar__status { margin: 0; font-size: var(--sul-fs-sm); color: var(--sul-muted); }
.sul-ie-bar__status.is-dirty { color: var(--sul-ink); font-weight: 600; }
.sul-ie-bar__status.is-error { color: var(--sul-sili); font-weight: 600; }
.sul-ie-bar__actions { display: flex; gap: var(--sul-space-2); flex-shrink: 0; }
/* Room so the bar never covers the last thing on the page. */
body.sul-ie-on { padding-bottom: 5rem; }

/* A field being edited: the rendered element is hidden and its editor takes
   its place. !important because each layout sets its own display on these
   elements (flex on the sidebar's centred column, etc.). */
.sul-ie-hide { display: none !important; }
.sul-ie-editor { width: 100%; margin: var(--sul-space-2) 0; text-align: left; }
.sul-ie-editor input,
.sul-ie-editor textarea {
	display: block;
	width: 100%;
	box-sizing: border-box;
	font: inherit;
	font-size: var(--sul-fs-body);
	font-weight: 400;
	letter-spacing: normal;
	padding: 0.65rem 0.8rem;
	background: var(--sul-surface);
	color: var(--sul-ink);
	/* Dashed guava outline = "this is editable here". */
	border: 1px dashed var(--sul-guava-strong);
	border-radius: var(--sul-radius-sm);
}
.sul-ie-editor textarea { resize: vertical; min-height: 5rem; overflow: hidden; }
.sul-ie-editor input:focus-visible,
.sul-ie-editor textarea:focus-visible {
	outline: 2px solid var(--sul-guava-strong);
	outline-offset: 1px;
	border-style: solid;
}
.sul-ie-hint { margin: var(--sul-space-1) 0 0; font-size: var(--sul-fs-sm); color: var(--sul-muted); }

/* Container whose sections are all empty: stays out of the layout until edit
   mode (JS removes this class on Edit, restores it on Cancel). Same
   specificity as the layouts' own display rules but later in the bundle. */
.sul-ie-container-empty { display: none; }

/* "Store saved." confirmation, shown once after the page reloads. */
.sul-ie-toast {
	position: fixed;
	left: 50%;
	bottom: var(--sul-space-6);
	transform: translateX(-50%);
	z-index: 95;
	padding: var(--sul-space-2) var(--sul-space-4);
	background: var(--sul-ink);
	color: var(--sul-surface);
	border-radius: var(--sul-radius-sm);
	font-size: var(--sul-fs-sm);
	box-shadow: var(--sul-shadow);
}

/* Contact and social links panel (edit mode only). Sits where the contact
   buttons and social icons normally are, which are hidden while it is open. */
.sul-ie-panel {
	display: flex;
	flex-direction: column;
	gap: var(--sul-space-4);
	width: 100%;
	box-sizing: border-box;
	margin: var(--sul-space-4) 0;
	padding: var(--sul-space-4);
	text-align: left;
	background: var(--sul-surface-2);
	border: 1px dashed var(--sul-guava-strong);
	border-radius: var(--sul-radius-sm);
}
.sul-ie-panel__title { margin: 0; font-size: var(--sul-fs-h3); }
.sul-ie-row { display: flex; flex-wrap: wrap; gap: var(--sul-space-3); }
.sul-ie-row > .sul-ie-field,
.sul-ie-row > .sul-ie-iconfield { flex: 1 1 12rem; min-width: 0; }
.sul-ie-field { display: flex; flex-direction: column; gap: var(--sul-space-1); }
.sul-ie-field__label { font-size: var(--sul-fs-sm); font-weight: 600; color: var(--sul-ink); }
.sul-ie-panel input,
.sul-ie-panel select,
.sul-ie-dialog input,
.sul-ie-dialog select {
	width: 100%;
	box-sizing: border-box;
	font: inherit;
	font-size: var(--sul-fs-body);
	padding: 0.6rem 0.75rem;
	background: var(--sul-surface);
	color: var(--sul-ink);
	border: 1px solid var(--sul-field-border);
	border-radius: var(--sul-radius-sm);
}
.sul-ie-panel input:focus-visible,
.sul-ie-panel select:focus-visible,
.sul-ie-dialog input:focus-visible,
.sul-ie-dialog select:focus-visible {
	outline: 2px solid var(--sul-guava-strong);
	outline-offset: 1px;
	border-color: var(--sul-guava-strong);
}
/* The photo dialog (caption and description): a stack of labelled lines inside the standard dialog box. */
.sul-ie-dialog { display: flex; flex-direction: column; gap: var(--sul-space-3); width: min(32rem, calc(100vw - 2rem)); max-height: calc(100vh - 2rem); overflow-y: auto; }
.sul-ie-dialog .sul-modal__title { margin: 0; }
/* A quiet text link that opens the optional field beside it. */
.sul-ie-linkbtn {
	align-self: flex-start;
	padding: 0;
	font: inherit;
	font-size: var(--sul-fs-sm);
	color: var(--sul-ink);
	text-decoration: underline;
	background: none;
	border: 0;
	cursor: pointer;
}
.sul-ie-linkbtn:hover { color: var(--sul-guava-strong); }
.sul-ie-linkbtn[hidden] { display: none; }
.sul-ie-warn { margin: 0; font-size: var(--sul-fs-sm); color: var(--sul-sili); }
.sul-ie-warn[hidden] { display: none; }
.sul-ie-hint { margin: 0; font-size: var(--sul-fs-sm); color: var(--sul-muted); }
/* "Not shown on your storefront" note in the Store details part: the form's
   version sits under the title, the collapsed summary's under its own title. */
.sul-ie-note { margin: 0; font-size: var(--sul-fs-sm); color: var(--sul-muted); line-height: 1.5; }
.sul-ie-summary__note { flex: 1 0 100%; margin-top: -0.35rem; font-size: var(--sul-fs-sm); color: var(--sul-muted); }
.sul-ie-socials { display: flex; flex-direction: column; gap: var(--sul-space-2); }
.sul-ie-socials__row { display: flex; flex-wrap: wrap; gap: var(--sul-space-2); align-items: center; }
.sul-ie-socials__name {
	flex: 0 0 8.5rem;
	display: inline-flex;
	align-items: center;
	gap: var(--sul-space-2);
	font-size: var(--sul-fs-sm);
	font-weight: 600;
	color: var(--sul-ink);
}
.sul-ie-socials__url { flex: 1 1 10rem; min-width: 0; }
.sul-ie-socials__row .sul-ie-warn { flex-basis: 100%; }
/* Brand icons name the fields and the platform choices (no text labels). */
.sul-ie-ico { display: inline-flex; color: var(--sul-guava-strong); }
.sul-ie-ico svg { width: 20px; height: 20px; display: block; }
.sul-ie-iconfield { position: relative; display: block; }
.sul-ie-iconfield > .sul-ie-ico { position: absolute; left: 0.75rem; top: 50%; transform: translateY(-50%); pointer-events: none; }
.sul-ie-panel .sul-ie-iconfield input { padding-left: 2.5rem; }
.sul-ie-iconbtn {
	flex: 0 0 auto;
	width: 2.25rem;
	height: 2.25rem;
	font: inherit;
	font-size: 1.25rem;
	line-height: 1;
	cursor: pointer;
	color: var(--sul-muted);
	background: var(--sul-surface);
	border: 1px solid var(--sul-field-border);
	border-radius: var(--sul-radius-sm);
}
.sul-ie-iconbtn:hover { color: var(--sul-sili); }
.sul-ie-chooser-wrap { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sul-space-2); }
.sul-ie-chooser-wrap[hidden] { display: none; }
.sul-ie-chooser__label { font-size: var(--sul-fs-sm); color: var(--sul-muted); }
.sul-ie-chooser { display: flex; flex-wrap: wrap; gap: var(--sul-space-2); }
.sul-ie-chip {
	display: inline-flex;
	align-items: center;
	gap: var(--sul-space-2);
	min-height: 2.25rem;
	padding: 0 0.8rem 0 0.65rem;
	font: inherit;
	font-size: var(--sul-fs-sm);
	color: var(--sul-ink);
	cursor: pointer;
	background: var(--sul-surface);
	border: 1px dashed var(--sul-guava-strong);
	border-radius: 999px;
}
.sul-ie-chip[hidden] { display: none; }
/* Category picks in the Store details part: tickable pills. The panel's
   blanket "input { width: 100% }" rule has to be undone for the checkbox. */
.sul-ie-checks { display: flex; flex-wrap: wrap; gap: var(--sul-space-2); }
.sul-ie-check {
	display: inline-flex;
	align-items: center;
	gap: var(--sul-space-2);
	min-height: 2.25rem;
	padding: 0 0.8rem 0 0.65rem;
	font-size: var(--sul-fs-sm);
	color: var(--sul-ink);
	cursor: pointer;
	background: var(--sul-surface);
	border: 1px solid var(--sul-field-border);
	border-radius: 999px;
}
.sul-ie-check:has(input:checked) { border-color: var(--sul-guava-strong); }
.sul-ie-check:has(input:disabled) { cursor: not-allowed; opacity: 0.55; }
.sul-ie-panel .sul-ie-check input { width: auto; padding: 0; margin: 0; accent-color: var(--sul-guava-strong); }
.sul-ie-chip:hover { background: var(--sul-surface-2); }
.sul-ie-chip:focus-visible { outline: 2px solid var(--sul-guava-strong); outline-offset: 1px; }

/* Everything that is not editable (Products, filters, share buttons, the
   Report link) and the rendered contact buttons/icons the panel replaces:
   gone while editing, so what remains is what can be changed. */
body.sul-ie-on [data-sul-ie-hide] { display: none !important; }

/* Photo slots (logo, cover): one pencil in the top-right corner; it opens a
   small menu (Change photo / Remove photo). A slot with no photo yet shows a
   single "add" button instead. */
body.sul-ie-on [data-sul-ie-photo] { position: relative; }
.sul-ie-photo-actions { position: absolute; z-index: 3; }
[data-sul-ie-photo="cover"] > .sul-ie-photo-actions { top: var(--sul-space-3); right: var(--sul-space-3); }
[data-sul-ie-photo="logo"] > .sul-ie-photo-actions { top: -8px; right: -8px; }
.sul-ie-pencil {
	position: relative;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 2.25rem;
	height: 2.25rem;
	padding: 0;
	cursor: pointer;
	/* Dark chip in a white ring (tokens in 00-tokens.css), the same on a photo,
	   the cream page and the dark theme: it reads as a tool, not store content. */
	color: var(--sul-edit-chip-fg);
	background: var(--sul-edit-chip);
	border: 2px solid var(--sul-edit-chip-ring);
	border-radius: 50%;
	box-shadow: 0 1px 6px rgba(0, 0, 0, .3);
}
.sul-ie-pencil:hover { background: var(--sul-edit-chip-hover); }
[data-sul-ie-photo="logo"] .sul-ie-pencil { width: 1.75rem; height: 1.75rem; }
[data-sul-ie-photo="logo"] .sul-ie-pencil svg { width: 13px; height: 13px; }
/* A 44px touch target without a bigger visible button. */
.sul-ie-pencil::after { content: ''; position: absolute; inset: -6px; }
.sul-ie-pencil:focus-visible,
.sul-ie-addphoto:focus-visible,
.sul-ie-menu__item:focus-visible { outline: 2px solid var(--sul-guava-strong); outline-offset: 1px; }
.sul-ie-pencil[disabled],
.sul-ie-addphoto[disabled] { opacity: .6; cursor: progress; }
/* [hidden] loses to an author display value at equal specificity. */
.sul-ie-pencil[hidden],
.sul-ie-addphoto[hidden],
.sul-ie-menu[hidden] { display: none; }
.sul-ie-addphoto {
	display: inline-flex;
	align-items: center;
	gap: var(--sul-space-2);
	font: inherit;
	font-size: var(--sul-fs-sm);
	font-weight: 600;
	color: var(--sul-ink);
	background: var(--sul-surface);
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius-sm);
	padding: 0.5rem 0.8rem;
	min-height: 2.25rem;
	cursor: pointer;
	box-shadow: 0 1px 6px rgba(0, 0, 0, .25);
}
/* The menu sits on <body> (positioned from the pencil by the script) so the
   cover's overflow:hidden can never clip it. */
.sul-ie-menu {
	position: fixed;
	z-index: 96;
	display: flex;
	flex-direction: column;
	min-width: 10.5rem;
	padding: 4px;
	background: var(--sul-surface);
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius-sm);
	box-shadow: 0 6px 20px rgba(0, 0, 0, .3);
}
.sul-ie-menu__item {
	font: inherit;
	font-size: var(--sul-fs-sm);
	text-align: left;
	min-height: 2.5rem;
	padding: 0.5rem 0.7rem;
	color: var(--sul-ink);
	background: none;
	border: 0;
	border-radius: var(--sul-radius-sm);
	cursor: pointer;
}
.sul-ie-menu__item:hover,
.sul-ie-menu__item:focus-visible { background: var(--sul-surface-2); }
.sul-ie-menu__item.is-danger { color: var(--sul-sili); }
/* Empty slots (no photo yet): a dashed box with the add button inside. */
.sul-ie-cover-empty {
	aspect-ratio: 8 / 3;
	margin-bottom: var(--sul-space-4);
	border: 1px dashed var(--sul-guava-strong);
	border-radius: var(--sul-radius);
	background-color: var(--sul-surface-2);
	background-size: cover;
	background-position: center;
}
[data-sul-ie-photo].sul-ie-cover-empty > .sul-ie-photo-actions { inset: 0; display: flex; align-items: center; justify-content: center; }
/* Empty logo: the whole dashed box is the add button. */
[data-sul-ie-photo="logo"].sul-ie-photo-blank > .sul-ie-photo-actions { inset: 0; }
[data-sul-ie-photo="logo"].sul-ie-photo-blank .sul-ie-addphoto { width: 100%; height: 100%; padding: 0; justify-content: center; box-shadow: none; background: transparent; border: 0; }
[data-sul-ie-photo="logo"].sul-ie-photo-blank {
	width: 80px;
	height: 80px;
	border: 1px dashed var(--sul-guava-strong);
	border-radius: var(--sul-radius);
	background: var(--sul-surface-2);
}
.sul-storefront-sidebar__logo[data-sul-ie-photo="logo"].sul-ie-photo-blank { width: 80px; height: 80px; margin-bottom: var(--sul-space-4); }
[data-sul-ie-photo="logo"].sul-ie-photo-blank > img { display: none; }


/* ---------- Click-to-edit parts (text fields and the contact panel) ----------
   Edit mode keeps the real page. Each editable part gets a dashed outline and
   a pencil; opening one swaps just that part for an editor. The outline is
   drawn INSIDE the element (negative offset) so a parent with overflow:hidden,
   like the Compact sections, never clips it. */
.sul-ie-target {
	position: relative;
	cursor: pointer;
	min-height: 2.5rem;
	outline: 1px dashed color-mix(in srgb, var(--sul-guava-strong) 55%, transparent);
	outline-offset: -4px;
	border-radius: var(--sul-radius-sm);
}
.sul-ie-target:hover { outline-color: var(--sul-guava-strong); }
.sul-ie-target.is-edited { outline-style: solid; outline-color: var(--sul-guava-strong); }
.sul-ie-target.is-loading { opacity: .65; }
/* A short tagline would sit under its own pencil. */
.sul-ie-target[data-sul-ie="tagline"] { min-width: 10rem; padding-right: 2.5rem; }
.sul-ie-target.is-editing { cursor: default; outline: 0; }

/* Nothing there yet: say what would go there. */
.sul-ie-empty { color: var(--sul-muted); font-style: italic; font-weight: 400; font-family: inherit; }
.sul-ie-empty::before { content: attr(data-ie-ph); }

/* The pencil, inside the part's top-right corner. */
.sul-ie-ctl { position: absolute; z-index: 2; }
.sul-ie-pencil--sm { width: 1.75rem; height: 1.75rem; top: 6px; right: 6px; }
.sul-ie-pencil--sm svg { width: 13px; height: 13px; }
.sul-ie-pencil.is-edited::before {
	content: '';
	position: absolute;
	top: -2px;
	right: -2px;
	width: 8px;
	height: 8px;
	border-radius: 50%;
	background: var(--sul-guava-strong);
	border: 1px solid var(--sul-edit-chip-ring);
}
/* An edited part shows its state with the solid outline and the dot on its
   pencil. There are no per-part buttons: Save and Cancel live only in the
   bottom bar, and an open editor closes by clicking away or pressing Esc. */
.sul-ie-editor[hidden] { display: none; }
/* The storefront's folder tabs (08-storefront.css) while editing: no strip, every panel open
   one above the next, each with its own heading row, so every part can be edited in place. */
body.sul-ie-on .sul-sf-tabs__nav { display: none; }
body.sul-ie-on .sul-sf-tabs__panel { display: block; margin-bottom: var(--sul-space-4); border-radius: var(--sul-radius); }
body.sul-ie-on .sul-sf-tabs .sul-sf-tabs__head { display: flex; margin-bottom: var(--sul-space-3); }
body.sul-ie-on .sul-sf-tabs__head-id { position: static; width: auto; height: auto; overflow: visible; clip: auto; white-space: normal; display: inline-flex; align-items: center; gap: var(--sul-space-2); }
/* The shipping answer sits in the heading row while editing, not on the panel's edge. */
body.sul-ie-on .sul-sf-tabs__panel .sul-section-ship { position: static; transform: none; margin: 0; }
.sul-sf-tabs__panel > .sul-ie-editor { padding: 0 0 var(--sul-space-3); }

/* The contact panel as one part: a summary of icon chips that opens into the form. */
.sul-ie-target--panel { margin: var(--sul-space-4) 0; width: 100%; box-sizing: border-box; text-align: left; }
.sul-ie-summary {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--sul-space-3);
	padding: var(--sul-space-4) 2.75rem var(--sul-space-4) var(--sul-space-4);
}
.sul-ie-summary[hidden] { display: none; }
.sul-ie-summary__title { flex: 1 0 100%; font-size: var(--sul-fs-sm); font-weight: 600; color: var(--sul-muted); }
.sul-ie-summary__chips { display: flex; flex-wrap: wrap; gap: var(--sul-space-2); }
.sul-ie-summary__chip {
	display: inline-flex;
	align-items: center;
	gap: var(--sul-space-2);
	min-height: 2.25rem;
	padding: 0 0.8rem 0 0.65rem;
	font-size: var(--sul-fs-sm);
	color: var(--sul-ink);
	background: var(--sul-surface);
	border: 1px solid var(--sul-border);
	border-radius: 999px;
}
.sul-ie-summary__empty { font-size: var(--sul-fs-sm); color: var(--sul-muted); font-style: italic; }
.sul-ie-target--panel .sul-ie-panel { margin: 0; border: 0; background: none; }
.sul-ie-target--panel .sul-ie-panel[hidden] { display: none; }
.sul-listing-layout__gallery > [data-lst="gallery"] { order: 1; }
/* The photos part's summary: a strip of the first few thumbnails and a count. */
.sul-ie-gal-strip { display: flex; flex-wrap: wrap; gap: var(--sul-space-2); flex: 1 0 100%; }
.sul-ie-gal-thumb { width: 56px; height: 56px; object-fit: cover; border-radius: var(--sul-radius-sm); border: 1px solid var(--sul-border); }
/* Category and Tags, boxed with the note that is only about them. */
.sul-ie-group {
	display: flex;
	flex-direction: column;
	gap: var(--sul-space-3);
	padding: var(--sul-space-3) var(--sul-space-4);
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius-sm);
}
/* A correction made to what the seller typed: said out loud, never silent. */
.sul-ie-adjusted {
	display: block;
	padding-left: var(--sul-space-2);
	border-left: 3px solid var(--sul-marigold);
	font-size: var(--sul-fs-sm);
	color: var(--sul-marigold-ink);
}
.sul-ie-adjusted[hidden] { display: none; }
.sul-ie-link input.is-corrected { outline: 2px solid var(--sul-marigold); outline-offset: 1px; }
/* The custom link field: the fixed stem of the address, then the part the seller sets. */
.sul-ie-link { display: flex; flex-wrap: wrap; align-items: center; gap: 2px; }
.sul-ie-link__base { font-size: var(--sul-fs-sm); color: var(--sul-muted); word-break: break-all; }
.sul-ie-link input { flex: 1 1 8rem; min-width: 0; }
/* The description part's summary shows the text itself, not a row of chips. */
.sul-ie-summary--prose { display: block; }
.sul-ie-summary--prose .sul-ie-summary__title { display: block; margin-bottom: var(--sul-space-2); }
.sul-ie-summary--prose .sul-prose a { text-decoration: underline; }
/* Parts of a listing page that belong to the store (How to order, Payment,
   Meetup, the message buttons, social links). Only meaningful while editing, so
   the note and the lock are hidden otherwise; the buttons are greyed and
   unclickable (inline-edit.js also drops them from the tab order). */
.sul-ie-storenote,
.sul-listing-tabs__lock { display: none; }
body.sul-ie-on .sul-ie-storenote {
	display: flex;
	align-items: flex-start;
	gap: var(--sul-space-2);
	margin: 0 0 var(--sul-space-4);
	font-size: var(--sul-fs-sm);
	line-height: 1.45;
	color: var(--sul-muted);
}
body.sul-ie-on .sul-ie-storenote svg { width: 14px; height: 14px; flex: 0 0 auto; margin-top: 0.15em; }
body.sul-ie-on .sul-ie-storenote a { color: inherit; text-decoration: underline; }
body.sul-ie-on .sul-listing-tabs__lock { display: block; width: 12px; height: 12px; opacity: 0.7; }
body.sul-ie-on .sul-contact-btn[data-sul-inquiry],
body.sul-ie-on .sul-social-row .sul-social-icon {
	opacity: 0.4;
	filter: grayscale(1);
	pointer-events: none;
	cursor: not-allowed;
}
/* The "See buyer safety tips" link under them: same treatment, as plain muted text. */
body.sul-ie-on .sul-listing__disclaimer a {
	color: inherit;
	text-decoration: none;
	opacity: 0.6;
	pointer-events: none;
	cursor: not-allowed;
}
/* The editable title and quantity on a listing: the dashed outline is drawn inside the box, so the text
   needs room inside it (it used to sit on the outline), and the pencil sits at the right edge, so long text
   needs room to end before it. Vertical padding centres short text, such as "2 available", in the box. */
.sul-listing-layout__identity > .sul-ie-target:not(.sul-ie-target--panel),
.sul-listing__price-block > .sul-ie-target {
	box-sizing: border-box;
	padding: 0.45rem 2.5rem 0.45rem 0.75rem;
}
.sul-listing__price-block > .sul-ie-target { margin-top: var(--sul-space-2); }
/* The text editor that opens in place of the title is a direct child of the identity column and so
   needs the title's own place in the order (08-storefront.css gives the title 4); with no order it
   defaults to 0 and jumps above everything, the photos included. */
.sul-listing-layout__identity > .sul-ie-editor { order: 4; }
/* "Sold by <store>" under the title links off the page: plain muted text while editing. */
body.sul-ie-on .sul-listing__sold-by a {
	color: inherit;
	font-weight: inherit;
	text-decoration: none;
	opacity: 0.7;
	pointer-events: none;
	cursor: not-allowed;
}
/* A listing's parts sit in the identity column, ordered like the thing each one
   replaces (08-storefront.css: title and sold-by 4, price block 5, condition
   row 6). Status ties with sold-by and follows it in the markup. */
.sul-listing-layout__identity > [data-lst="type"] { order: 4; }
.sul-listing-layout__identity > [data-lst="status"] { order: 4; }
.sul-listing-layout__identity > [data-lst="price"] { order: 5; }
.sul-listing-layout__identity > [data-lst="cond"] { order: 6; }
/* A blog post's cover photo: the picture it has now, then the buttons to change or remove it. */
.sul-ie-cover-edit { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sul-space-2); }
.sul-ie-cover-edit { flex-direction: column; align-items: stretch; }
.sul-ie-cover-edit__img { display: block; width: 100%; height: auto; border-radius: var(--sul-radius); }
.sul-ie-cover-edit__buttons { display: flex; flex-wrap: wrap; gap: var(--sul-space-2); }
/* The cover in its part's summary: the same picture, at the same width, as the page draws it. */
.sul-ie-cover-live { margin: 0; }
.sul-ie-cover-blank {
	display: flex;
	align-items: center;
	justify-content: center;
	aspect-ratio: 3 / 1;
	font-size: var(--sul-fs-sm);
	color: var(--sul-muted);
	background: var(--sul-surface-2);
	border: 1px dashed var(--sul-guava-strong);
	border-radius: var(--sul-radius);
}
/* The title in its part's summary: the page's own heading, with no margin of its own. */
.sul-ie-summary__heading { margin: 0; }
.sul-ie-addphoto--undo { margin-left: var(--sul-space-2); }
.sul-ie-addphoto--undo[hidden] { display: none; }

/* ---------- Location, edited on the identity line ----------
   The location text is the target: dashed underline and a small pencil after
   it, a dot once edited. Inherits the line's own colour (white on the cover). */
.sul-ie-loc {
	cursor: pointer;
	text-decoration: underline dashed;
	text-underline-offset: 3px;
}
/* The same dark chip as every other pencil, not a text-coloured glyph: the
   chip colours are fixed (not currentColor), so the pencil is a white stroke
   drawn over the chip background. */
.sul-ie-loc::after {
	content: '';
	display: inline-block;
	box-sizing: border-box;
	width: 1.5em;
	height: 1.5em;
	margin-left: .4em;
	vertical-align: -.35em;
	border-radius: 50%;
	border: 2px solid var(--sul-edit-chip-ring);
	background: var(--sul-edit-chip) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 20h9'/%3E%3Cpath d='M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4Z'/%3E%3C/svg%3E") center / 62% no-repeat;
	box-shadow: 0 1px 4px rgba(0, 0, 0, .3);
}
.sul-ie-loc:hover::after { background-color: var(--sul-edit-chip-hover); }
.sul-ie-loc:focus-visible { outline: 2px solid var(--sul-guava-strong); outline-offset: 2px; }
.sul-ie-loc.is-edited::before {
	content: '';
	display: inline-block;
	width: .5em;
	height: .5em;
	margin-right: .35em;
	vertical-align: .05em;
	border-radius: 50%;
	background: var(--sul-guava-strong);
	box-shadow: 0 0 0 1px var(--sul-surface);
}
/* An empty location reads as a prompt; on the cover it must stay legible. */
.sul-ie-loc.sul-ie-empty { color: inherit; opacity: .85; }

/* The card under the location text. On <body> so the cover's overflow:hidden
   can't clip it; positioned from the text by the script. */
.sul-ie-pop {
	position: fixed;
	z-index: 96;
	display: flex;
	flex-direction: column;
	gap: var(--sul-space-3);
	box-sizing: border-box;
	width: min(22rem, calc(100vw - 16px));
	padding: var(--sul-space-4);
	color: var(--sul-ink);
	background: var(--sul-surface);
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius-sm);
	box-shadow: 0 6px 20px rgba(0, 0, 0, .3);
}
.sul-ie-pop[hidden] { display: none; }
.sul-ie-pop__title { margin: 0; font-size: var(--sul-fs-sm); font-weight: 600; color: var(--sul-muted); }
.sul-ie-pop select {
	width: 100%;
	box-sizing: border-box;
	font: inherit;
	font-size: var(--sul-fs-body);
	padding: 0.6rem 0.75rem;
	background: var(--sul-surface);
	color: var(--sul-ink);
	border: 1px solid var(--sul-field-border);
	border-radius: var(--sul-radius-sm);
}
.sul-ie-pop select:focus-visible { outline: 2px solid var(--sul-guava-strong); outline-offset: 1px; border-color: var(--sul-guava-strong); }
/* Cleared after an edit: keep the "Add location" prompt instead of the dot. */
.sul-ie-loc.is-edited.sul-ie-empty::before {
	content: attr(data-ie-ph);
	display: inline;
	width: auto;
	height: auto;
	margin: 0;
	border-radius: 0;
	background: none;
	box-shadow: none;
	vertical-align: baseline;
}

/* "Only you can see this page": shown above a listing the public cannot see
   (Hidden, Draft, brand new) while its owner edits it. */
.sul-ie-notice {
	margin: 0 0 var(--sul-space-4);
	padding: var(--sul-space-3) var(--sul-space-4);
	font-size: var(--sul-fs-sm);
	color: var(--sul-ink);
	background: var(--sul-surface-2);
	border: 1px dashed var(--sul-guava-strong);
	border-radius: var(--sul-radius-sm);
}
.sul-ie-notice strong { margin-right: var(--sul-space-1); }

/* assets/css/18-blog.css */
/* Seller blog (includes/blog.php): a store's blog list, a post, the card, and the
   "From the blog" strip on a storefront. Tokens only, so light, dark and the
   data-theme override all resolve. */

/* The blog list's header: a one-line store strip above, then the title with the search beside it. */
.sul-blog-head { margin-bottom: var(--sul-space-8); }
.sul-blog-head__store {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: var(--sul-space-2) var(--sul-space-3);
	margin: 0 0 var(--sul-space-3);
	font-size: var(--sul-fs-sm);
	color: var(--sul-muted);
}
.sul-blog-head__avatar { flex-shrink: 0; display: block; width: 28px; height: 28px; border-radius: 7px; overflow: hidden; }
.sul-blog-head__avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
.sul-blog-head__name { font-weight: 600; color: var(--sul-ink); text-decoration: none; }
.sul-blog-head__name:hover { text-decoration: underline; }
.sul-blog-head__tagline { min-width: 0; }
.sul-blog-head__tagline::before { content: "\00b7"; margin-right: var(--sul-space-3); }
.sul-blog-head__row { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: var(--sul-space-3) var(--sul-space-6); }
.sul-blog-head .sul-page-title { margin: 0; }
.sul-blog-search { width: 100%; max-width: 320px; }
/* The sitewide blog: category chips under the title, and a card's category and store lines. */
.sul-blog-chips { display: flex; flex-wrap: wrap; gap: var(--sul-space-2); margin-top: var(--sul-space-4); }
.sul-blog-chip { display: inline-block; padding: 6px 14px; border: 1px solid var(--sul-border); border-radius: var(--sul-radius-sm); color: var(--sul-ink); text-decoration: none; font-size: var(--sul-text-sm, 0.9rem); background: var(--sul-surface); }
.sul-blog-chip:hover { border-color: var(--sul-ink); }
.sul-blog-chip.is-active { background: var(--sul-ink); border-color: var(--sul-ink); color: var(--sul-surface); }
/* The category: an ink label laid over the photo's top-left corner (a card with no photo shows it over the grey block; on the sidebar's picture cards it sits above the card's full-cover link). The ink is the edit chips' own colour, fixed in every theme, with a faint light edge so it also shows on a dark page. */
.sul-blog-card__mediabox { position: relative; }
.sul-blog-card__pill {
	position: absolute; left: 10px; top: 10px; z-index: 3; max-width: calc(100% - 96px);
	padding: 3px 10px; border-radius: 6px; border: 1px solid rgba(255, 255, 255, .22);
	background: var(--sul-edit-chip); color: var(--sul-edit-chip-fg);
	font-size: var(--sul-fs-label); font-weight: 600; line-height: 1.3; text-decoration: none;
	overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sul-blog-card__views {
	position: absolute; right: 10px; top: 10px; z-index: 3;
	display: inline-flex; align-items: center; gap: 5px; padding: 3px 9px; border-radius: 6px;
	border: 1px solid rgba(255, 255, 255, .22); background: var(--sul-edit-chip); color: var(--sul-edit-chip-fg);
	font-size: var(--sul-fs-label); font-weight: 600; line-height: 1.3;
}
.sul-blog-card__views svg { flex: 0 0 auto; width: 14px; height: 14px; }
.sul-blog-card__pill:hover { background: var(--sul-edit-chip-hover); color: var(--sul-edit-chip-fg); }
/* The category chip above a post's title. */
.sul-blog-post__chip {
	align-self: flex-start; margin-bottom: calc(-1 * var(--sul-space-3)); display: inline-block; padding: 3px 10px; border-radius: 6px;
	border: 1px solid rgba(255, 255, 255, .22); background: var(--sul-edit-chip); color: var(--sul-edit-chip-fg);
	font-size: var(--sul-fs-label); font-weight: 600; text-decoration: none;
}
.sul-blog-post__chip:hover { background: var(--sul-edit-chip-hover); color: var(--sul-edit-chip-fg); }
.sul-blog-card__store { margin: 0 0 var(--sul-space-2); font-size: var(--sul-text-sm, 0.9rem); color: var(--sul-muted); }
.sul-blog-card__store a { color: inherit; }
/* The category dropdown (only when two or more are in use) sits beside the search box. */
.sul-blog-head__tools { display: flex; align-items: center; flex-wrap: wrap; gap: var(--sul-space-3); }
.sul-blog-catform { margin: 0; }
@media (max-width: 559px) {
	/* On a phone the tagline wraps onto its own line, so the dot before it would sit alone. */
	.sul-blog-head__tagline { flex-basis: 100%; }
	.sul-blog-head__tagline::before { content: none; }
	.sul-blog-search { max-width: none; }
}

/* The grid of cards, on every surface that lists posts. */
.sul-blog-grid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
	gap: var(--sul-space-6);
}

.sul-blog-card {
	display: flex;
	flex-direction: column;
	min-width: 0;
	background: var(--sul-surface);
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius);
	overflow: hidden;
	transition: box-shadow .2s ease, transform .2s ease, border-color .2s ease;
}
.sul-blog-card:hover,
.sul-blog-card:focus-within {
	box-shadow: var(--sul-shadow);
	border-color: var(--sul-field-border);
	transform: translateY(-3px);
}
.sul-blog-card__media {
	display: block;
	position: relative;
	aspect-ratio: 4 / 3;
	overflow: hidden;
	background: var(--sul-surface-2);
	border-bottom: 1px solid var(--sul-border);
}
/* Absolute, so a tall or wide photo is cropped to the 4:3 box instead of setting the box's height
   (height: 100% of an auto-height box falls back to the photo's own shape). */
.sul-blog-card__media img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; transition: transform .35s ease; }
.sul-blog-card:hover .sul-blog-card__media img { transform: scale(1.04); }
.sul-blog-card__noimg { display: block; width: 100%; height: 100%; }
.sul-blog-card__body { flex: 1; display: flex; flex-direction: column; gap: var(--sul-space-2); padding: var(--sul-space-4); min-width: 0; }
.sul-blog-card__meta { margin: 0; font-size: var(--sul-fs-label); color: var(--sul-muted); }
.sul-blog-card__title {
	margin: 0; font-size: 1.05rem; line-height: 1.3; text-wrap: wrap; /* plain wrapping, as the product card title does (08-storefront.css) */
	height: 2.6em; /* always two lines tall, so the summary starts at the same spot on every card */
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
}
.sul-blog-card__title a { color: var(--sul-ink); text-decoration: none; }
.sul-blog-card__title a:hover { text-decoration: underline; }
.sul-blog-card__summary {
	margin: 0;
	font-size: var(--sul-fs-sm);
	line-height: 1.55;
	height: 4.65em; /* always three lines tall */
	color: var(--sul-muted);
	display: -webkit-box;
	-webkit-line-clamp: 3;
	-webkit-box-orient: vertical;
	overflow: hidden;
}
.sul-blog-card__more {
	margin-top: auto;
	padding-top: var(--sul-space-2);
	font-size: var(--sul-fs-sm);
	font-weight: 600;
	color: var(--sul-guava-strong);
	text-decoration: none;
}
.sul-blog-card__more:hover { text-decoration: underline; }
@media (prefers-reduced-motion: reduce) {
	.sul-blog-card, .sul-blog-card__media img { transition: none; }
	.sul-blog-card:hover, .sul-blog-card:focus-within { transform: none; }
	.sul-blog-card:hover .sul-blog-card__media img { transform: none; }
}

/* A post, and its sidebar. One column (760px) below 1024px with the sidebar under the post; from
   1024px the page uses the whole container: the post's text column stays about 685px (the
   reading width the research favours) and the sidebar takes the rest. */
.sul-container--blogpost { max-width: 760px; }
@media (min-width: 1024px) {
	.sul-container--blogpost { max-width: var(--sul-container); }
}
.sul-blog-layout {
	display: grid;
	grid-template-columns: minmax(0, 1fr);
	gap: var(--sul-space-8);
	align-items: start;
}
@media (min-width: 1024px) {
	.sul-blog-layout { grid-template-columns: minmax(0, 1fr) 300px; }
}
/* Both sidebar blocks switched off (or empty): no empty 300px column, the post is centred. */
@media (min-width: 1024px) {
	.sul-blog-layout:not(:has(> .sul-blog-side)) { grid-template-columns: minmax(0, 700px); justify-content: center; }
}
/* While the owner edits, the sidebar shows as readers see it, but muted and unclickable: it belongs to the
   whole blog, not to this post. A card laid over the middle of it says where to change it: solid, in
   full-strength text, so it cannot be mistaken for the muted lists beneath. */
body.sul-ie-on .sul-blog-side--locked { position: relative; min-height: 15rem; }
body.sul-ie-on .sul-blog-side--locked .sul-blog-side__section {
	opacity: 0.4;
	filter: grayscale( 1 );
	pointer-events: none;
	cursor: not-allowed;
}
body.sul-ie-on .sul-blog-side--locked > .sul-ie-storenote {
	position: absolute;
	z-index: 2;
	top: 50%;
	left: var(--sul-space-3);
	right: var(--sul-space-3);
	transform: translateY( -50% );
	display: block;
	margin: 0;
	padding: var(--sul-space-3) var(--sul-space-4);
	text-align: center;
	color: var(--sul-ink);
	background: var(--sul-surface);
	border: 1px dashed var(--sul-guava-strong);
	border-radius: var(--sul-radius-sm);
}
body.sul-ie-on .sul-blog-side--locked > .sul-ie-storenote svg { display: inline-block; vertical-align: -2px; margin: 0 0.3em 0 0; }
body.sul-ie-on .sul-blog-side--locked > .sul-ie-storenote a { font-weight: 600; }
.sul-blog-layout__main { display: flex; flex-direction: column; gap: var(--sul-space-6); min-width: 0; }

/* The sidebar sits straight on the page, with no box around it. It is beside the post from 1024px and under it
   below that. The two groups differ at a glance: the posts are picture cards (the picture IS the card, as on
   Rentl's Digest), the products small rows, with room between them. */
.sul-blog-side {
	display: flex;
	flex-direction: column;
	gap: var(--sul-space-8);
	min-width: 0;
}
/* Beside the post (from 1024px) the sidebar stays in view as the page scrolls: it sticks just under the site header
   (about 65px tall). A sidebar taller than its window scrolls INSIDE itself (scrollbar hidden). Over the sidebar the
   sidebar scrolls first, then the page, as one motion: assets/js/blog-sidebar.js decides every wheel tick itself,
   because the browser's own handoff between the two (it picks one when a gesture starts) stalled and lurched at the
   sidebar's ends (2026-10-12). Without the script the native handoff still works (overscroll-behavior is left at auto).
   The window is sized so its bottom is on screen even at the top of the page, where the sidebar sits about 135px down:
   100vh less 9.5rem. Under the post (below 1024px) it is a normal block. */
@media (min-width: 1024px) {
	.sul-blog-side {
		position: sticky;
		top: 5rem;
		align-self: start;
		max-height: calc( 100vh - 9.5rem );
		overflow-y: auto;
		scrollbar-width: none; /* Firefox and current Chrome and Edge */
		-ms-overflow-style: none;
	}
	.sul-blog-side::-webkit-scrollbar { display: none; } /* Safari and older Chrome */
	/* The owner's edit view keeps it too (the edit view's rule above, position: relative, would otherwise win). */
	body.sul-ie-on .sul-blog-side--locked { position: sticky; }
}
/* A group's header: its name in capitals in the strong brand colour, and "See all" as a small outlined button on
   the right (nearly square, 2px corners), so it looks pressable. */
.sul-blog-side__head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--sul-space-4);
	margin-bottom: var(--sul-space-3);
}
.sul-blog-side__head .sul-cluster-label {
	margin: 0;
	font-size: var(--sul-fs-sm);
	font-weight: 700;
	letter-spacing: 0.06em;
	color: var(--sul-guava-strong);
}
.sul-blog-side__head a {
	flex-shrink: 0;
	padding: 0.25rem 0.75rem;
	font-size: var(--sul-fs-label);
	font-weight: 600;
	line-height: 1.4;
	color: var(--sul-ink);
	text-decoration: none;
	border: 1px solid var(--sul-ink);
	border-radius: 3px; /* just softened, not rounded: the same as the "Visit store" button */
}
.sul-blog-side__head a:hover,
.sul-blog-side__head a:focus-visible { color: var(--sul-surface); background: var(--sul-ink); }
.sul-blog-side__list { display: flex; flex-direction: column; gap: var(--sul-space-3); }

/* A post: a 3:2 picture card. The title and the date sit on a dark fade over the bottom of the picture (fixed
   whites, not theme colours: they sit on a photo and a dark fade, which do not change between light and dark). The
   link covers the whole card; with no picture the card is a plain dark block. */
.sul-blog-side__list--cards { gap: var(--sul-space-4); }
.sul-blog-side__card {
	position: relative;
	aspect-ratio: 3 / 2;
	overflow: hidden;
	background: #2A1F26;
	border-radius: var(--sul-radius-sm);
}
.sul-blog-side__cardlink { position: absolute; inset: 0; z-index: 2; }
.sul-blog-side__cardimg { position: absolute; inset: 0; }
.sul-blog-side__cardimg img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform 0.4s ease; }
@media (hover: hover) and (pointer: fine) {
	/* The title is underlined when the card is pointed at (no picture zoom: as the sidebar scrolls under the pointer, a zoom
	   flickered from card to card). */
	.sul-blog-side__card:hover .sul-blog-side__cardtitle { text-decoration: underline; }
}
.sul-blog-side__cardtext {
	position: absolute;
	inset: auto 0 0 0;
	padding: var(--sul-space-8) var(--sul-space-3) var(--sul-space-3);
	background: linear-gradient( to top, rgba( 0, 0, 0, 0.85 ) 30%, rgba( 0, 0, 0, 0 ) );
}
.sul-blog-side__cardtitle {
	margin: 0 0 2px;
	font-size: var(--sul-fs-body);
	font-weight: 700;
	line-height: 1.25;
	color: #fff;
	text-wrap: wrap;
	display: -webkit-box;
	-webkit-line-clamp: 3;
	-webkit-box-orient: vertical;
	overflow: hidden;
}
.sul-blog-side__cardmeta { font-size: var(--sul-fs-label); color: rgba( 255, 255, 255, 0.78 ); }

/* The posts take turns in ONE card (assets/js/blog-carousel.js slides them across on a timer). The track is a row of
   full-width slides that snap into place, so a swipe or a drag works natively and the script only moves it along;
   the scrollbar is hidden, and horizontal overscroll is kept from triggering the browser's back gesture. */
.sul-blog-carousel__track {
	display: flex;
	overflow-x: auto;
	scroll-snap-type: x mandatory;
	overscroll-behavior-x: contain;
	scrollbar-width: none;
	border-radius: var(--sul-radius-sm);
}
.sul-blog-carousel__track::-webkit-scrollbar { display: none; }
.sul-blog-carousel__slide { flex: 0 0 100%; min-width: 0; scroll-snap-align: start; }
.sul-blog-carousel__controls { display: flex; align-items: center; justify-content: center; gap: 0.25rem; margin-top: var(--sul-space-2); }
.sul-blog-carousel__dot,
.sul-blog-carousel__toggle {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	margin: 0;
	padding: 0;
	border: 0;
	background: none;
	color: var(--sul-muted);
	cursor: pointer;
}
/* A small dot with a bigger place to press (the 24px button around it). */
.sul-blog-carousel__dot { width: 24px; height: 24px; }
.sul-blog-carousel__dot::before {
	content: "";
	width: 8px;
	height: 8px;
	border-radius: 50%;
	background: color-mix( in srgb, var(--sul-muted) 45%, transparent );
	transition: background 0.2s ease, transform 0.2s ease;
}
.sul-blog-carousel__dot.is-active::before { background: var(--sul-guava-strong); transform: scale(1.25); }
.sul-blog-carousel__toggle { width: 24px; height: 24px; margin-left: 0.25rem; border-radius: 50%; }
.sul-blog-carousel__toggle svg { fill: currentColor; }
.sul-blog-carousel__toggle:hover { color: var(--sul-ink); }
.sul-blog-carousel__dot:focus-visible,
.sul-blog-carousel__toggle:focus-visible { outline: 2px solid var(--sul-ink); outline-offset: 1px; }
.sul-blog-carousel__icon-play { display: none; }
.sul-blog-carousel.is-paused .sul-blog-carousel__icon-play { display: block; }
.sul-blog-carousel.is-paused .sul-blog-carousel__icon-pause { display: none; }

/* A product: a small row with its picture, name and price. */
.sul-blog-side__row {
	display: flex;
	align-items: center;
	gap: var(--sul-space-3);
	color: inherit;
	text-decoration: none;
}
.sul-blog-side__row:not(:last-child) { padding-bottom: var(--sul-space-3); border-bottom: 1px solid var(--sul-border); }
.sul-blog-side__thumb {
	flex-shrink: 0;
	width: 72px;
	height: 72px;
	background: var(--sul-surface-2);
	border-radius: var(--sul-radius-sm);
	overflow: hidden;
}
.sul-blog-side__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.sul-blog-side__info { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.sul-blog-side__title {
	font-size: var(--sul-fs-sm);
	font-weight: 600;
	line-height: 1.3;
	color: var(--sul-ink);
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
}
.sul-blog-side__row:hover .sul-blog-side__title { text-decoration: underline; }
.sul-blog-side__meta { font-size: var(--sul-fs-label); color: var(--sul-muted); }

.sul-blog-post__meta { margin: 0; font-size: var(--sul-fs-sm); color: var(--sul-muted); }
.sul-blog-post__meta a { color: inherit; }

/* The byline under the title: the store's avatar, its name (with the Verified seal), the date and read time (the "Visit store" button lives on the card below the post, not here).
   Its avatar is a rounded square (the card below the post uses the same one, larger). */
.sul-blog-byline { display: flex; align-items: center; gap: var(--sul-space-3); min-width: 0; }
.sul-blog-byline__avatar {
	flex-shrink: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	width: 40px;
	height: 40px;
	border-radius: 6px; /* a rounded square, not a circle */
	overflow: hidden;
	background: var(--sul-surface-2);
	color: var(--sul-ink);
	font-weight: 700;
	text-decoration: none;
}
.sul-blog-byline__avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
.sul-blog-byline__text { flex: 1; min-width: 0; }
/* The views, at the right end of the byline and level with the date line. */
.sul-blog-byline__views { align-self: flex-end; flex: 0 0 auto; display: inline-flex; align-items: center; gap: 5px; font-size: var(--sul-fs-label); font-weight: 600; color: var(--sul-ink); }
.sul-blog-byline__views svg { color: var(--sul-muted); }
.sul-blog-byline__name { margin: 0; display: flex; align-items: center; gap: 0.25rem; font-weight: 600; color: var(--sul-ink); }
.sul-blog-byline__name a { color: inherit; text-decoration: none; overflow-wrap: anywhere; }
.sul-blog-byline__name a:hover { text-decoration: underline; }
/* The store card under the post: the same avatar and name as the byline. The top row holds the avatar, the name
   and the button; the store's own line and the facts line sit below it and fill the card's whole width. */
.sul-blog-storecard {
	margin-top: var(--sul-space-8);
	padding: var(--sul-space-4) var(--sul-space-6);
	background: var(--sul-surface);
	border: 1px solid var(--sul-field-border);
	border-radius: 4px;
}
.sul-blog-storecard__top { display: flex; align-items: center; gap: var(--sul-space-4); min-width: 0; }
.sul-blog-storecard__avatar { width: 56px; height: 56px; font-size: 1.25rem; border-radius: 8px; }
.sul-blog-storecard__text { flex: 1; min-width: 0; }
.sul-blog-storecard__eyebrow { margin: 0 0 2px; font-size: var(--sul-fs-label); font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: var(--sul-muted); }
.sul-blog-storecard .sul-blog-byline__name { font-size: var(--sul-fs-md, 1rem); }
.sul-blog-storecard__about { margin: var(--sul-space-4) 0 0; font-size: var(--sul-fs-sm); color: var(--sul-ink); }
.sul-blog-storecard__facts { margin: var(--sul-space-2) 0 0; font-size: var(--sul-fs-label); color: var(--sul-muted); }
/* The button stays in the card's upper right corner at every width; a long store name wraps beside it. */
.sul-blog-storecard .sul-blog-byline__visit { align-self: flex-start; }
/* "Visit store": the site's solid pink button colours (the same tokens as "Start your store", so it follows the
   light, dark and chosen themes), small, with only slightly rounded corners. */
.sul-blog-byline__visit {
	flex-shrink: 0;
	padding: 0.4rem 0.9rem;
	font-size: var(--sul-fs-label);
	font-weight: 600;
	line-height: 1.4;
	color: var(--sul-btn-fg);
	text-decoration: none;
	background: var(--sul-btn-bg);
	border-radius: 3px;
}
.sul-blog-byline__visit:hover,
.sul-blog-byline__visit:focus-visible { color: var(--sul-btn-fg); background: var(--sul-btn-bg-hover); }
.sul-blog-post__cover { margin: var(--sul-space-8) 0 0; }

/* The title and the air around it (2026-10-12). A post is read, so its title leads: 48px on a desktop
   (about 30px on a phone) and tight. 16px from the title to the byline, 32px from the byline to the
   cover (the breadcrumb's own spacing, 16px under the site header and 32px above the title, is the
   same on every page: 03-layout.css and 04-components.css). Blog posts only; other pages keep the
   page-title size.
   Headings here use plain wrapping (text-wrap: wrap), NOT the sitewide text-wrap: balance of
   02-typography.css: balance evens out the lines of a heading that wraps, so each line stops well
   short of its column (the title and the section headings must fill it, like every other text). */
.sul-blog-post h1,
.sul-blog-post h2,
.sul-blog-post h3 { text-wrap: wrap; }
.sul-blog-post > header { gap: var(--sul-space-4); }
.sul-blog-post .sul-page-title {
	margin: 0;
	font-size: clamp(1.9rem, 5vw, 3rem);
	line-height: 1.08;
	letter-spacing: -0.03em;
}
.sul-blog-post__cover img { width: 100%; height: auto; display: block; border-radius: var(--sul-radius); }
.sul-blog-post__body { margin-top: var(--sul-space-6); }
/* The affiliate note: one quiet line of small grey text between two hairlines, no box. */
.sul-blog-post__disclosure {
	margin: var(--sul-space-6) 0 0;
	padding: var(--sul-space-2) 0;
	font-size: var(--sul-fs-sm);
	line-height: 1.5;
	color: var(--sul-muted);
	border-top: 1px solid var(--sul-border);
	border-bottom: 1px solid var(--sul-border);
}
.sul-blog-post__disclosure + .sul-blog-post__body { margin-top: var(--sul-space-4); }

/* A photo inside the text: the full width of the column, like the cover. */
.sul-blog-photo { margin: var(--sul-space-6) 0; }
.sul-blog-photo img { width: 100%; height: auto; display: block; border-radius: var(--sul-radius); }
/* A photo's size and alignment (chosen in the photo dialog). Large, the default, is the full width of the
   text. Small and medium are narrower and centred, or, with left or right, put to one side with the text
   running beside them. On a phone every photo is full width and centred: a narrow photo with text beside it
   is too cramped there. */
.sul-blog-photo.is-small,
.sul-blog-photo.is-medium { margin-left: auto; margin-right: auto; }
.sul-blog-photo.is-small { width: 40%; }
.sul-blog-photo.is-medium { width: 60%; }
.sul-blog-photo.is-left { float: left; margin: var(--sul-space-2) var(--sul-space-4) var(--sul-space-3) 0; }
.sul-blog-photo.is-right { float: right; margin: var(--sul-space-2) 0 var(--sul-space-3) var(--sul-space-4); }
/* Anything that is not itself beside the text starts below a photo that is. */
.sul-blog-photo:not(.is-left):not(.is-right),
.sul-blog-video,
.sul-blog-post__body h2,
.sul-blog-post__body h3 { clear: both; }
.sul-blog-post__body { display: flow-root; }
@media (max-width: 640px) {
	.sul-blog-photo.is-small,
	.sul-blog-photo.is-medium { width: 100%; }
	.sul-blog-photo.is-left,
	.sul-blog-photo.is-right { float: none; margin: var(--sul-space-6) 0; }
}
/* A photo's caption, in small muted text under it. */
.sul-blog-photo__caption { margin: var(--sul-space-2) 0 0; font-size: var(--sul-fs-sm); line-height: 1.4; color: var(--sul-muted); text-align: center; }

/* A video inside the text: a play button until it is pressed (assets/js/blog-video.js), then the player. */
.sul-blog-video {
	position: relative;
	margin: var(--sul-space-6) 0;
	aspect-ratio: 16 / 9;
	background: var(--sul-surface-2);
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius);
	overflow: hidden;
}
.sul-blog-video.is-playing { border: 0; background: #000; }
.sul-blog-video iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.sul-blog-video__play {
	position: absolute;
	inset: 0;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: var(--sul-space-2);
	padding: var(--sul-space-4);
	color: var(--sul-ink);
	text-align: center;
	text-decoration: none;
}
.sul-blog-video__play:hover .sul-blog-video__icon { background: var(--sul-guava-strong); }
.sul-blog-video__play:focus-visible { outline: 2px solid var(--sul-guava-strong); outline-offset: -4px; }
.sul-blog-video__icon {
	position: relative;
	width: 3.5rem;
	height: 3.5rem;
	border-radius: 50%;
	background: var(--sul-ink);
}
.sul-blog-video__icon::after {
	content: '';
	position: absolute;
	top: 50%;
	left: 50%;
	transform: translate( -35%, -50% );
	border-style: solid;
	border-width: 0.7rem 0 0.7rem 1.1rem;
	border-color: transparent transparent transparent var(--sul-surface);
}
.sul-blog-video__label { font-weight: 600; }

/* A YouTube video shows its still picture: the picture fills the box, the play button sits in the
   middle, and the words go on a dark fade at the bottom so they read on any picture. */
.sul-blog-video__thumb { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; } /* hqdefault carries black bars: cover crops them in a 16:9 box */
.sul-blog-video__play--thumb {
	justify-content: flex-end;
	align-items: flex-start;
	text-align: left;
	color: #fff;
	background: linear-gradient( to top, rgba( 0, 0, 0, 0.72 ), rgba( 0, 0, 0, 0 ) 55% );
}
.sul-blog-video__play--thumb .sul-blog-video__icon {
	position: absolute;
	top: 50%;
	left: 50%;
	transform: translate( -50%, -50% );
	box-shadow: 0 0 0 3px rgba( 255, 255, 255, 0.9 );
}
.sul-blog-video__play--thumb .sul-blog-video__label { position: relative; color: #fff; }

/* ==========================================================================
   The seller's Blog screen in the dashboard (assets/js/blog-dashboard.js)
   ========================================================================== */

.sul-dash__tag {
	display: inline-block;
	margin-left: var(--sul-space-1);
	padding: 0 6px;
	font-size: 0.68rem;
	font-weight: 700;
	line-height: 1.6;
	border-radius: 999px;
	color: var(--sul-muted);
	border: 1px solid var(--sul-border);
	vertical-align: middle;
}

.sul-dash-blog__quota {
	margin: 0 0 var(--sul-space-4);
	padding: var(--sul-space-3) var(--sul-space-4);
	background: var(--sul-surface-2);
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius-sm);
	font-size: var(--sul-fs-sm);
}
.sul-dash-blog__quota p { margin: 0; }
.sul-dash-blog__quota p + p { margin-top: var(--sul-space-1); }

/* The Sidebar card: the same box as the numbers card above it. */
.sul-dash-blog__side {
	margin: 0 0 var(--sul-space-4);
	padding: var(--sul-space-3) var(--sul-space-4);
	background: var(--sul-surface-2);
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius-sm);
	font-size: var(--sul-fs-sm);
}
.sul-dash-blog__side p { margin: 0 0 var(--sul-space-2); }
.sul-dash-blog__side-row { display: flex; flex-wrap: wrap; gap: var(--sul-space-4); margin-bottom: var(--sul-space-3); }
.sul-dash-blog__side-row .sul-dash-blog__field { flex: 1 1 200px; min-width: 0; }
.sul-dash-blog__quota .sul-muted:empty { display: none; }

.sul-dash-blog__upsell {
	padding: var(--sul-space-6);
	background: var(--sul-surface-2);
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius);
}
.sul-dash-blog__upsell h3 { margin: 0 0 var(--sul-space-2); }
.sul-dash-blog__upsell p { margin: 0 0 var(--sul-space-3); }
.sul-dash-blog__upsell p:last-child { margin-bottom: 0; }

.sul-dash-blog__tabs { display: flex; flex-wrap: wrap; gap: var(--sul-space-2); margin: 0 0 var(--sul-space-4); }
.sul-dash-blog__tab {
	padding: 0.35rem 0.8rem;
	font: inherit;
	font-size: var(--sul-fs-sm);
	color: var(--sul-ink);
	background: var(--sul-surface);
	border: 1px solid var(--sul-border);
	border-radius: 999px;
	cursor: pointer;
}
.sul-dash-blog__tab span { color: var(--sul-muted); }
.sul-dash-blog__tab.is-active {
	border-color: var(--sul-guava-strong);
	background: color-mix(in srgb, var(--sul-guava-strong) 12%, var(--sul-surface));
	color: var(--sul-guava-strong);
	font-weight: 600;
}
.sul-dash-blog__tab.is-active span { color: inherit; }

.sul-dash-blog__list { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--sul-border); }
.sul-dash-blog__row {
	display: flex;
	align-items: center;
	gap: var(--sul-space-3);
	padding: var(--sul-space-3) 0;
	border-bottom: 1px solid var(--sul-border);
}
.sul-dash-blog__thumb {
	flex: 0 0 64px;
	width: 64px;
	height: 48px;
	background: var(--sul-surface-2);
	border-radius: var(--sul-radius-sm);
	overflow: hidden;
}
.sul-dash-blog__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.sul-dash-blog__main { flex: 1 1 auto; min-width: 0; }
.sul-dash-blog__title { margin: 0; font-weight: 600; overflow-wrap: anywhere; }
.sul-dash-blog__sub { margin: 2px 0 0; font-size: var(--sul-fs-sm); color: var(--sul-muted); }
.sul-dash-blog__actions { display: flex; flex-wrap: wrap; gap: var(--sul-space-2); justify-content: flex-end; }
@media (max-width: 640px) {
	.sul-dash-blog__row { flex-wrap: wrap; }
	.sul-dash-blog__actions { flex: 1 1 100%; justify-content: flex-start; }
}

.sul-dash-blog__pill {
	display: inline-block;
	margin-left: var(--sul-space-1);
	padding: 0 8px;
	font-size: 0.72rem;
	font-weight: 600;
	line-height: 1.7;
	border-radius: 999px;
	border: 1px solid var(--sul-border);
	color: var(--sul-muted);
	vertical-align: middle;
}
.sul-dash-blog__pill--publish { color: var(--sul-calamansi-ink); border-color: color-mix(in srgb, var(--sul-calamansi) 40%, var(--sul-border)); }
.sul-dash-blog__pill--future { color: var(--sul-marigold-ink); border-color: color-mix(in srgb, var(--sul-marigold) 45%, var(--sul-border)); }

/* A label on the Sidebar card's two lists. */
.sul-dash-blog__field label { display: block; margin-bottom: var(--sul-space-1); font-weight: 600; }


/* The featured Sulitista post, pinned above the sitewide blog's first page: the card laid out sideways from tablet up. */
.sul-blog-featured { margin-bottom: var(--sul-space-6); }
/* The featured card has room, so it reads larger and shows more of the post's own text (2026-10-22, Joefer: the
   short excerpt sat alone in a tall panel). The summary is the start of the body, about 380 characters, up to 6 lines. */
.sul-blog-featured .sul-blog-card__meta { font-size: 0.85rem; }
.sul-blog-featured .sul-blog-card__store { font-size: var(--sul-fs-body); }
.sul-blog-featured .sul-blog-card__summary--featured { height: auto; -webkit-line-clamp: 6; font-size: var(--sul-fs-body); line-height: 1.65; }
.sul-blog-featured .sul-blog-card__more { font-size: var(--sul-fs-body); }
@media (min-width: 720px) {
	.sul-blog-featured .sul-blog-card { display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); align-items: stretch; }
	.sul-blog-featured .sul-blog-card__body { justify-content: center; padding: var(--sul-space-6); }
	/* The picture fills the whole left side, whatever height the text makes it, instead of stopping at its own 4:3
	   and leaving a strip of card colour below it (2026-10-22). */
	.sul-blog-featured .sul-blog-card__mediabox { min-height: 20rem; }
	.sul-blog-featured .sul-blog-card__media { position: absolute; inset: 0; aspect-ratio: auto; border-bottom: 0; }
	.sul-blog-featured .sul-blog-card__title { font-size: 1.75rem; height: auto; -webkit-line-clamp: 3; }
	.sul-blog-featured .sul-blog-card__more { margin-top: var(--sul-space-3); }
}

/* assets/css/19-likes.css */
/* Likes (2026-10-13): the heart on a listing's photo, with the public count.
   Top-right of the photo on the product page and on cards alike (the card's
   Condition and discount pills sit bottom-left). */
.sul-like {
	display: inline-flex;
	align-items: center;
	gap: 5px;
	min-height: 36px;
	min-width: 36px;
	justify-content: center;
	padding: 0 10px;
	background: var(--sul-surface);
	color: var(--sul-ink);
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius-pill);
	font: inherit;
	font-size: var(--sul-fs-sm);
	font-weight: 600;
	line-height: 1;
	cursor: pointer;
	position: absolute;
	z-index: 3;
}
.sul-like--photo { top: var(--sul-space-3); right: var(--sul-space-3); }
.sul-like--card { top: var(--sul-space-2); right: var(--sul-space-2); min-height: 32px; min-width: 32px; padding: 0 8px; }
.sul-like__icon { width: 18px; height: 18px; flex-shrink: 0; }
.sul-like__count:empty { display: none; }
.sul-like.is-liked { color: var(--sul-guava-strong); }
.sul-like.is-liked .sul-like__icon { fill: currentColor; }
.sul-like--static { cursor: default; }
@media (hover: hover) {
	.sul-like:not(.sul-like--static):hover { border-color: var(--sul-guava-strong); color: var(--sul-guava-strong); }
}
.sul-like:focus-visible { outline: 2px solid var(--sul-ink); outline-offset: 2px; }

/* The photo-less listing page has no figure to hold the heart, so the column does. */
.sul-listing-layout__gallery { position: relative; }

/* assets/css/20-doc-pages.css */
/* Long text pages and the Contact page (2026-10-13): the page uses the full container, in line with
   the header, and the text sits in a reading column beside a second column, the same shape as a
   blog post (.sul-blog-layout, 18-blog.css). Below 1024px everything stacks. */

/* -- Text pages: reading column + "On this page" -- */
.sul-doc {
	display: grid;
	grid-template-columns: minmax(0, 1fr);
	gap: var(--sul-space-6);
	align-items: start;
	margin-top: var(--sul-space-6);
}
.sul-doc__toc { display: none; }

/* Phone and tablet: the list is a closed fold above the text. */
.sul-doc__fold {
	border: 1px solid var(--sul-border);
	border-radius: var(--sul-radius);
	background: var(--sul-surface);
	padding: var(--sul-space-3) var(--sul-space-4);
}
.sul-doc__fold summary {
	cursor: pointer;
	font-weight: 600;
	color: var(--sul-ink);
}
.sul-doc__fold ol { margin-top: var(--sul-space-3); }

.sul-doc ol {
	list-style: none;
	margin: 0;
	padding: 0;
}
.sul-doc ol li + li { margin-top: var(--sul-space-2); }
.sul-doc ol a {
	color: var(--sul-ink);
	text-decoration: none;
}
.sul-doc ol a:hover,
.sul-doc ol a:focus-visible { text-decoration: underline; }

/* A page that opens with a heading (Listing Rules) would start its text one heading-margin lower than
   the list beside it; the first thing in the column sits flush with the top of the list. */
.sul-doc > .sul-prose > :first-child { margin-top: 0; }

/* A heading reached from the list stops below the sticky site header, not under it. */
.sul-doc .sul-prose h2 { scroll-margin-top: 5.5rem; }

@media (min-width: 1024px) {
	.sul-doc {
		grid-template-columns: minmax(0, 1fr) 260px;
		gap: var(--sul-space-8);
	}
	.sul-doc__fold { display: none; }
	.sul-doc > .sul-prose { grid-column: 1; grid-row: 1; }
	.sul-doc__toc {
		display: block;
		grid-column: 2;
		grid-row: 1;
		position: sticky;
		top: 5rem;
		max-height: calc(100vh - 9.5rem);
		overflow-y: auto;
		padding-left: var(--sul-space-4);
		border-left: 2px solid var(--sul-border);
	}
	.sul-doc__toc-title {
		margin: 0 0 var(--sul-space-3);
		font-size: var(--sul-fs-sm);
		font-weight: 700;
		letter-spacing: 0.06em;
		text-transform: uppercase;
		color: var(--sul-guava-strong);
	}
	.sul-doc__toc ol { font-size: var(--sul-fs-sm); }
}

/* -- Contact: the form and a short "before you write" card -- */
.sul-contact-layout {
	display: grid;
	grid-template-columns: minmax(0, 1fr);
	gap: var(--sul-space-6);
	align-items: start;
	margin-top: var(--sul-space-6);
}
.sul-contact-aside h2 {
	margin: 0 0 var(--sul-space-3);
	font-size: var(--sul-fs-h3);
}
.sul-contact-aside p:last-child { margin-bottom: 0; }
.sul-contact-aside a { color: var(--sul-guava-strong); }
@media (min-width: 1024px) {
	.sul-contact-layout { grid-template-columns: minmax(0, 1fr) 320px; gap: var(--sul-space-8); }
}

/* -- "Ready to start your store?" card at the end of the Listing Rules and the Community Guidelines (2026-10-15):
   logged-out visitors only, in the reading column under the last section. A quiet card with a guava-tinted edge
   and one button (Joefer picked the small card over a full-width band, which would read as an ad on a policy page). -- */
.sul-start-card {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--sul-space-4);
	margin-top: var(--sul-space-8);
	padding: var(--sul-space-6);
	background: var(--sul-surface-2);
	border: 1px solid color-mix(in srgb, var(--sul-guava) 40%, var(--sul-border));
	border-radius: var(--sul-radius);
}
.sul-start-card__text { flex: 1 1 14rem; min-width: 0; }
.sul-start-card p { margin: 0; color: var(--sul-muted); }
.sul-start-card .sul-start-card__title {
	margin: 0 0 var(--sul-space-1);
	font-family: var(--sul-font-display);
	font-weight: 700;
	font-size: var(--sul-fs-h3);
	color: var(--sul-ink);
}
/* `.sul-prose a` (02-typography.css) colours every link in the text deep guava, and it beats `.sul-btn` on
   specificity, so the button's label came out guava on a guava button (reported from a screenshot, 2026-10-15).
   Restate the button's own text colour here, for rest and hover. */
.sul-start-card .sul-btn,
.sul-start-card .sul-btn:hover { color: var(--sul-btn-fg); text-decoration: none; }

/* assets/css/21-handbook.css */
/* Store Owner Handbook (page-store-owner-handbook.php, includes/store-owner-handbook.php, 2026-10-15).
   It reuses the long-page layout (20-doc-pages.css) and the plan table (16-pricing.css); this file only
   adds the search box and the limits table. */

/* The search and Expand all / Collapse all share one row under the intro paragraph (Joefer, 2026-10-16): the
   search on the left, the button on the right. */
.sul-hb-intro { margin-bottom: var(--sul-space-6); }
.sul-hb-tools { display: flex; flex-wrap: nowrap; align-items: center; gap: var(--sul-space-3); margin: 0 0 var(--sul-space-3); }
.sul-hb-search { flex: 1 1 0; min-width: 0; margin: 0; }
.sul-hb-search__field { position: relative; display: block; }
.sul-hb-search__icon {
	position: absolute;
	left: 0.85rem;
	top: 50%;
	transform: translateY(-50%);
	color: var(--sul-muted);
	pointer-events: none;
}
.sul-hb-search input[type="search"] {
	font: inherit;
	font-size: var(--sul-fs-body);
	width: 100%;
	padding: 0.7rem 0.75rem 0.7rem 2.5rem;
	text-overflow: ellipsis;
	background: var(--sul-surface);
	color: var(--sul-ink);
	border: 1px solid var(--sul-field-border);
	border-radius: 999px;
	box-shadow: var(--sul-shadow-sm);
}
.sul-hb-search input[type="search"]::placeholder { color: var(--sul-muted); }
.sul-hb-search input[type="search"]:focus-visible {
	outline: 2px solid var(--sul-guava-strong);
	outline-offset: 1px;
	border-color: var(--sul-guava-strong);
}

/* The one Expand all / Collapse all button; its label follows the state of the folds. */
.sul-hb-all {
	flex: none;
	display: inline-flex;
	align-items: center;
	gap: 0.4rem;
	padding: 0.35rem 0.8rem;
	background: none;
	border: 1px solid var(--sul-border);
	border-radius: 999px;
	font: inherit;
	font-size: var(--sul-fs-sm);
	font-weight: 600;
	color: var(--sul-guava-strong);
	cursor: pointer;
}
.sul-hb-all:hover { background: var(--sul-surface-2); border-color: var(--sul-guava-strong); }
.sul-hb-all:focus-visible { outline: 2px solid var(--sul-guava-strong); outline-offset: 2px; }
.sul-hb-all svg { transition: transform 0.15s ease; }
.sul-hb-all.is-open svg { transform: rotate(180deg); }
@media (prefers-reduced-motion: reduce) { .sul-hb-all svg { transition: none; } }
.sul-hb-count { margin: var(--sul-space-2) 0 0; font-size: var(--sul-fs-sm); color: var(--sul-muted); }
/* A search hit, and the one Enter is on. Marigold behind ink text reads in both themes. */
.sul-hb-mark {
	background: color-mix(in srgb, var(--sul-marigold) 45%, transparent);
	color: inherit;
	border-radius: 2px;
	padding: 0 1px;
}
.sul-hb-mark.is-current { background: var(--sul-marigold); color: #1f1a17; }
.sul-hb-empty { margin: var(--sul-space-3) 0 0; color: var(--sul-muted); }

/* No "On this page" fold on a phone or tablet (Joefer, 2026-10-16): the groups are already a short closed list
   there, so a second list above them only repeats it. The sticky list beside the text from 1024px stays. */
.sul-doc:has(> .sul-hb) > .sul-doc__fold { display: none; }

/* The title sat far above the intro (about 60px on a phone): the heading's own bottom margin and the text
   block's top margin added up. Tighten both for the Handbook (Joefer, 2026-10-16). */
.sul-stack:has(+ .sul-doc > .sul-hb) .sul-page-title { margin-bottom: 0; }
.sul-stack + .sul-doc:has(> .sul-hb) { margin-top: var(--sul-space-3); }

/* Hidden sections must really go, whatever display the section would otherwise get. */
.sul-hb-section[hidden],
.sul-doc ol li[hidden] { display: none; }

/* Each section is a fold: the heading is the button, a chevron shows the state. */
.sul-hb-section + .sul-hb-section { margin-top: 0; }
/* No boxes (Joefer, 2026-10-16): the sections sit on the page itself, split by a thin line, with the
   heading as a full-width row and a chevron at the right. */
.sul-hb-fold { border-top: 1px solid var(--sul-border); }
/* The last fold of a group closes it with a line. */
.sul-hb-section:not(:has(+ .sul-hb-section)) .sul-hb-fold { border-bottom: 1px solid var(--sul-border); }
.sul-hb-fold > summary {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--sul-space-3);
	padding: var(--sul-space-4) 0;
	cursor: pointer;
	list-style: none;
}
.sul-hb-fold > summary::-webkit-details-marker { display: none; }
.sul-hb-fold > summary::after {
	content: "";
	flex: none;
	width: 0.55rem;
	height: 0.55rem;
	border-right: 2px solid var(--sul-guava-strong);
	border-bottom: 2px solid var(--sul-guava-strong);
	transform: rotate(45deg);
	transition: transform 0.15s ease;
}
.sul-hb-fold[open] > summary::after { transform: rotate(-135deg); }
.sul-hb-fold > summary:focus-visible { outline: 2px solid var(--sul-guava-strong); outline-offset: 2px; }
/* Hierarchy (Joefer, 2026-10-16, option A): the GROUP is the strongest thing on the page (a large display
   heading under a heavy rule) and the fold headings under it are plain, medium-weight rows, indented a
   little, so a fold never outweighs the group it belongs to. */
.sul-hb-fold > summary h2 {
	margin: 0;
	font-family: var(--sul-font-display);
	font-size: 1.25rem;
	font-weight: 600;
	line-height: 1.3;
	color: var(--sul-guava-strong);
	text-wrap: wrap;
}
/* A colour of its own gives the eye something to land on (Joefer, 2026-10-16: the section headings had no
   anchor in ink, the same colour as the text). The group above stays ink. */
.sul-hb-fold > summary:hover h2 { color: var(--sul-ink); text-decoration: underline; }
.sul-hb-fold > summary { padding: var(--sul-space-3) 0 var(--sul-space-3) var(--sul-space-4); }

/* The contents list zeroes every list in .sul-doc (20-doc-pages.css), which also took the numbers off the
   steps in the text. Put them back inside a section. */
.sul-hb-body ol { list-style: decimal; padding-left: 1.4rem; }
.sul-hb-body ol li + li { margin-top: var(--sul-space-2); }
.sul-hb-body ul { list-style: disc; padding-left: 1.4rem; }
.sul-hb-body { padding: 0 0 var(--sul-space-6) var(--sul-space-4); }
.sul-hb-body > :first-child { margin-top: 0; }
.sul-hb-body > :last-child { margin-bottom: 0; }
@media (prefers-reduced-motion: reduce) {
	.sul-hb-fold > summary::after { transition: none; }
}

/* A group: a heavy rule, the name as a large heading, a one-line note. */
/* The group is a fold too (Joefer, 2026-10-16: the categories collapse and expand, closed on load), so the
   page opens as a short list of groups and the sections are not repeated beside "On this page". */
.sul-hb-gf { margin-top: var(--sul-space-6); border-top: 2px solid var(--sul-ink); }
.sul-hb-gf[hidden] { display: none; }
.sul-hb-gf + .sul-hb-gf { margin-top: 0; }
.sul-hb-group {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--sul-space-3);
	padding: var(--sul-space-4) 0;
	cursor: pointer;
	list-style: none;
}
.sul-hb-group::-webkit-details-marker { display: none; }
.sul-hb-group::after {
	content: "";
	flex: none;
	width: 0.7rem;
	height: 0.7rem;
	border-right: 2px solid var(--sul-ink);
	border-bottom: 2px solid var(--sul-ink);
	transform: rotate(45deg);
	transition: transform 0.15s ease;
}
.sul-hb-gf[open] > .sul-hb-group::after { transform: rotate(-135deg); }
.sul-hb-group:focus-visible { outline: 2px solid var(--sul-guava-strong); outline-offset: 2px; }
.sul-hb-group__text { display: block; min-width: 0; }
.sul-hb-group__label {
	display: block;
	font-family: var(--sul-font-display);
	font-size: 1.6rem;
	font-weight: 700;
	line-height: 1.2;
	color: var(--sul-ink);
}
.sul-hb-group:hover .sul-hb-group__label { color: var(--sul-guava-strong); }
.sul-hb-group__note { display: block; margin-top: var(--sul-space-1); color: var(--sul-muted); }
.sul-hb-gf__body { padding-bottom: var(--sul-space-4); }
@media (prefers-reduced-motion: reduce) {
	.sul-hb-group::after { transition: none; }
}

/* The same group names inside "On this page" (added by handbook.js). */
.sul-doc ol li.sul-hb-toc-group {
	margin-top: var(--sul-space-4);
	font-weight: 700;
	color: var(--sul-ink);
}
.sul-doc ol li.sul-hb-toc-group:first-child { margin-top: 0; }
.sul-doc ol li.sul-hb-toc-group + li { margin-top: var(--sul-space-2); }
.sul-doc ol li.sul-hb-toc-group ~ li:not(.sul-hb-toc-group) { padding-left: var(--sul-space-3); }

.sul-hb-back { margin: 0 0 var(--sul-space-4); font-size: var(--sul-fs-sm); }
.sul-hb-back a { color: var(--sul-guava-strong); }


/* Limits at a glance: a plain two-column table in the reading column. */
.sul-hb-limits {
	width: 100%;
	border-collapse: collapse;
	background: var(--sul-surface);
	border: 1px solid var(--sul-border);
	font-size: var(--sul-fs-sm);
}
.sul-hb-limits th,
.sul-hb-limits td {
	padding: var(--sul-space-3) var(--sul-space-4);
	text-align: left;
	vertical-align: top;
	border-bottom: 1px solid var(--sul-border);
}
.sul-hb-limits thead th { background: var(--sul-surface-2); }
.sul-hb-limits tbody th { font-weight: 600; color: var(--sul-ink); width: 45%; }
.sul-hb-limits tbody tr:last-child th,
.sul-hb-limits tbody tr:last-child td { border-bottom: none; }


/* assets/css/22-test-store.css */
/* The "Test store" switch (includes/test-store.php, 2026-10-16): a banner under the header on a test store's
   pages, and a small "Test" mark on its cards. Marigold with a dashed edge reads as "temporary, not real" and
   is not one of the status colours a listing uses. */

.sul-test-banner {
	background: color-mix(in srgb, var(--sul-marigold) 24%, var(--sul-surface));
	border-bottom: 2px dashed var(--sul-marigold-ink);
	color: var(--sul-ink);
	font-size: var(--sul-fs-sm);
	font-weight: 600;
}
.sul-test-banner .sul-container {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: var(--sul-space-2);
	padding-block: 0.6rem;
	text-align: center;
}
.sul-test-banner svg { flex: none; color: var(--sul-marigold-ink); }

.sul-test-mark {
	position: absolute;
	z-index: 4;
	padding: 2px 9px;
	background: var(--sul-ink);
	color: var(--sul-surface);
	border: 1px dashed var(--sul-marigold);
	border-radius: 999px;
	font-size: 0.68rem;
	font-weight: 700;
	line-height: 1.6;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	pointer-events: none;
}
/* Where it sits on each card: a photo's bottom-left, a store card's top-right (its top-left holds Featured and
   the place, its bottom-right the Ad ribbon). */
.sul-listing-card__photo > .sul-test-mark,
.sul-search-card__photo > .sul-test-mark,
.sul-blog-card__mediabox > .sul-test-mark { left: 8px; bottom: 8px; }
.sul-store-card > .sul-test-mark,
.sul-store-card__link > .sul-test-mark { top: 10px; right: 10px; }

