/*
 Theme Name:   Bricks Child Theme
 Theme URI:    https://bricksbuilder.io/
 Description:  Use this child theme to extend Bricks.
 Author:       Bricks
 Author URI:   https://bricksbuilder.io/
 Template:     bricks
 Version:      1.1
 Text Domain:  bricks
*/
/* ===========================================================================
   BRAND GLYPH HEXES — the only hard-coded colours left in this file
   ---------------------------------------------------------------------------
   Three SVG data URIs carry a literal hex. All three are now #F3F2F8, the
   brand's ink, because all three sit on a dark surface:

     1. search glyph   .browse-filter__search-input input[type="search"]
     2. select chevron .browse-filter select.browse-filter__facet-select
     3. chevron, active  ...:has(option:checked:not(.placeholder))

   ⚠️ Two of them were DARK (#2A1418) when this file arrived, because the
   property it came from had light form surfaces. The light→dark conversion on
   2026-09-22 would have made the search icon and the active chevron invisible
   if these had been left alone — a token sweep cannot see inside a data URI,
   so they have to be changed by hand, every time the surface under them flips.

   WHY THEY ARE STILL LITERAL. Custom properties do not substitute inside a
   `url()` data URI, so the usual fix is `mask-image` + `background-color`.
   That does not work on any of these three: a mask clips EVERYTHING the
   element paints, including its text. It is fine on a pseudo-element with no
   content (see the play overlay on .video-card::after, converted that way),
   but masking an <input> or a <select> would hide the typed value and the
   option label along with the background.

   THE REAL FIX is to move each glyph onto a wrapper pseudo-element. The
   desktop facets already have one (.browse-filter__facet, position: relative)
   and so does the search field (.browse-filter__search-input) — but the
   MOBILE SHEET selects (.browse-filter__facet-select--in-sheet) are NOT
   wrapped, so converting only the desktop copies would silently drop the
   chevron on mobile. Wrapping the sheet selects is a Bricks TEMPLATE change,
   not a CSS one, which is why it was left out of the CSS neutralization pass.
   Do that first, then all three can become tokens.
   =========================================================================== */

/* ===========================================================================
   BRAND FONTS — Goth Coven
   ---------------------------------------------------------------------------
   Self-hosted variable woff2, latin + latin-ext. Newsreader carries an `opsz`
   axis as well as `wght`; Inter carries `wght`.

   WHY THESE LIVE HERE AND NOT AS BRICKS CUSTOM FONTS. Bricks builds every
   @font-face from a single weight integer (`custom-fonts.php` ->
   generate_font_face_rules, FILTER_SANITIZE_NUMBER_INT), so it cannot emit a
   `font-weight` RANGE. A variable file registered there is pinned to the one
   weight, and any weight you did not declare falls back instead of
   interpolating -- which defeats the point of shipping a variable font.
   Full reasoning: docs/acss-brand-map.md section 10.

   CONSEQUENCE: these families are NOT in the Bricks builder font picker.
   They do not need to be -- every assignment goes through ACSS's per-level
   font-family fields, which are free-text and emit UNQUOTED. (Bricks' own
   _typography.font-family quotes and would break the stack: see
   docs/stack-gotchas.md, "Bricks `_typography.font-family` quotes the value".)
   If the picker is ever genuinely needed, register these same files as Bricks
   Custom Fonts AND DELETE THIS BLOCK in the same edit -- running both
   duplicates the declaration.

   `opsz` is set per heading level further down; it is an element-level axis
   and deliberately not expressed here.
   =========================================================================== */

@font-face {
	font-family: 'Newsreader';
	src: url('assets/fonts/newsreader-latin.woff2') format('woff2');
	font-weight: 200 800;
	font-style: normal;
	font-display: swap;
	unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
	font-family: 'Newsreader';
	src: url('assets/fonts/newsreader-latin-ext.woff2') format('woff2');
	font-weight: 200 800;
	font-style: normal;
	font-display: swap;
	unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
	font-family: 'Newsreader';
	src: url('assets/fonts/newsreader-italic-latin.woff2') format('woff2');
	font-weight: 200 800;
	font-style: italic;
	font-display: swap;
	unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
	font-family: 'Newsreader';
	src: url('assets/fonts/newsreader-italic-latin-ext.woff2') format('woff2');
	font-weight: 200 800;
	font-style: italic;
	font-display: swap;
	unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
	font-family: 'Inter';
	src: url('assets/fonts/inter-latin.woff2') format('woff2');
	font-weight: 100 900;
	font-style: normal;
	font-display: swap;
	unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
	font-family: 'Inter';
	src: url('assets/fonts/inter-latin-ext.woff2') format('woff2');
	font-weight: 100 900;
	font-style: normal;
	font-display: swap;
	unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Newsreader's optical-size axis. The brand guide pins the display cut on the
   two serif levels rather than letting `font-optical-sizing: auto` track the
   rendered size -- which matters most at the mobile end, where auto would drop
   h1 to opsz ~40. Name ONLY `opsz`: adding `wght` here would override the
   weight ACSS sets per level and silently detach headings from those settings. */
h1, .h1 { font-variation-settings: 'opsz' 72; }
h2, .h2 { font-variation-settings: 'opsz' 36; }


/* ===========================================================================
   THE CUT CORNER — the brand's one repeated shape
   ---------------------------------------------------------------------------
   A clip-path corner cut, NOT a border-radius. `--chamfer` (16px) and
   `--chamfer-s` (7px) are pinned in ACSS Global CSS, which is the only home for
   brand tokens (01 -> Pinned custom tokens); this file only consumes them.

   Site-wide device, so it lives here rather than on any one component
   (01 -> component vs site-wide). Apply by adding `cham` or `cham-s` to an
   element's classes. Brand guide section 04: thumbnails, avatars, buttons, tags,
   panels, form fields.

   ⚠️ An element carrying `cham` CANNOT use ACSS's `.clickable-parent` — the
   stretched pseudo-element breaks under clip-path (01). Cards use the
   visible-anchor pattern instead, which is the documented default anyway.

   ⚠️ clip-path clips EVERYTHING the element paints, including any focus ring
   drawn outside the box and a border's corner. On a field it is safe only with
   an INSET focus ring (below) and, when the field has a border, the diagonal
   drawn back in (FORMS). The facet selects and form fields do both. A wrapper
   is still the simpler home where one exists.
   =========================================================================== */

.cham,
.performer-archive-card,
.performer-card,
.login__form-container,
.home-latest__img-wrapper,
/* tour-only panels */
.join-plan,
.home-cta__card,
.network__item,
.video-cta {
	clip-path: polygon(
		var(--chamfer) 0, 100% 0,
		100% calc(100% - var(--chamfer)), calc(100% - var(--chamfer)) 100%,
		0 100%, 0 var(--chamfer)
	);
}

/* The small cut. Applied to the controls directly rather than by adding a class
   to each Bricks element: these are all styled here by class already, and the
   filter controls are generated across two templates. One polygon, one rule. */
.cham-s,
.badge,
.browse-active-filters__chip,
.browse-filter__search-input,
.browse-filter__search-input.brxe-filter-search,
.browse-filter .browse-filter__facet-select,
.browse-filter select.browse-filter__facet-select,
.browse-filter__facet-select--in-sheet,
.browse-filter .browse-filter__clear,
.browse-filter .browse-filter__mobile-toggle,
.browse-filter__sheet-close,
.browse-filter__sheet-footer-clear,
.home-notice__close,
.form-brand button[type="submit"],
.btn,
[class*="btn--"],
/* tour-only controls */
.header__join,
.header__login,
.join-plan__badge {
	clip-path: polygon(
		var(--chamfer-s) 0, 100% 0,
		100% calc(100% - var(--chamfer-s)), calc(100% - var(--chamfer-s)) 100%,
		0 100%, 0 var(--chamfer-s)
	);
}

/* ⚠️ A chamfered control CANNOT show the default focus ring. ACSS draws focus as
   an `outline` at `--focus-offset` — outside the border box — and clip-path
   removes everything painted outside the polygon, so the ring would be silently
   cut away and keyboard users would lose the indicator entirely. Swap to an
   INSET ring of the same colour and width: box-shadow inset paints inside the
   border box, so it survives the clip and follows the cut corners with it. */
.cham:focus-visible,
.cham-s:focus-visible,
.home-latest__img-wrapper:focus-visible,
.badge:focus-visible,
.browse-active-filters__chip:focus-visible,
.browse-filter .browse-filter__facet-select:focus-visible,
.browse-filter select.browse-filter__facet-select:focus-visible,
.browse-filter__facet-select--in-sheet:focus-visible,
.browse-filter .browse-filter__clear:focus-visible,
.browse-filter .browse-filter__mobile-toggle:focus-visible,
.browse-filter__sheet-close:focus-visible,
.browse-filter__sheet-footer-clear:focus-visible,
.home-notice__close:focus-visible,
.form-brand button[type="submit"]:focus-visible,
.btn:focus-visible,
[class*="btn--"]:focus-visible,
.header__join:focus-visible,
.header__login:focus-visible {
	/* An inset OUTLINE, not a box-shadow. Both paint inside the border box and
	   so survive the clip, but ACSS already sets `focus-style: outline` and
	   blanket-clears `box-shadow` on :focus — so the outline route keeps one
	   focus vocabulary instead of fighting that rule with specificity. */
	outline: var(--focus-width, 2px) solid var(--focus-color);
	outline-offset: calc(-1 * var(--focus-width, 2px));
}

/* ===========================================================================
   VIDEO CARD — brand guide §05, "All together"
   ---------------------------------------------------------------------------
   A 16:9 thumb on --raise carrying the dot texture, then a two-line-clamped
   title in ink and a muted meta line.

   ⚠️ The guide also insets a duration PILL at the thumb's bottom-left. That was
   built and then REVERTED on request (2026-09-22) — the meta row stays below the
   thumb, where the seed had it. Do not re-add it from the guide without asking:
   it is a deliberate divergence, not an oversight.

   ⚠️ Titles keep their own case. The guide sets no text-transform on .ct, and
   the point it makes about video titles is that they arrive in every possible
   shape and the card has to absorb them — so the clamp does the work, not caps.
   =========================================================================== */

.video-card {
	position: relative;
	background-color: var(--base-ultra-dark);
	border: 0;
	clip-path: polygon(
		var(--chamfer) 0, 100% 0,
		100% calc(100% - var(--chamfer)), calc(100% - var(--chamfer)) 100%,
		0 100%, 0 var(--chamfer)
	);
}

/* the thumb: --raise shows through while the poster loads, and behind any
   poster that does not fill the frame */
.video-card__img {
	position: relative;
	display: block;
	background-color: var(--raise);
}

/* the dot texture. ⚠️ On the <a> wrapper, never the <img> — a replaced element
   cannot carry a pseudo-element. pointer-events:none so it never eats the click. */
.video-card__img::after {
	content: "";
	position: absolute;
	inset: 0;
	background-image: var(--dither);
	pointer-events: none;
}

/* the title — guide .ct: 16px / 800 / 1.2 (was 500 / 1.35 — raised 2026-09-29), ink, clamped to three lines,
   with a two-line min-height so cards in a row keep their baselines */
.video-card__heading {
	/* size / weight / line-height / colour are TYPED on the global class —
	   only the clamp lives here, because Bricks has no control for it. */
	margin-top: 0;
	margin-bottom: 4px;
	min-height: 2lh;
	display: -webkit-box;
	-webkit-line-clamp: 3;
	-webkit-box-orient: vertical;
	overflow: hidden;
}

/* guide .cs — the quiet line under the title */
.video-card__description {
	/* typed on the global class; kept here only as the documented intent */
	max-width: 100%;
}

/* ⚠️ The card is chamfered, so its focus ring must be INSET — an outline at a
   positive offset is painted outside the border box and clip-path removes it.
   Orchid, not violet: violet is 2.94:1 on the page (brand guide §02). */
.video-card:focus-within,
.performer-archive-card:focus-within,
.performer-card:focus-within {
	outline: var(--focus-width, 2px) solid var(--focus-color);
	outline-offset: calc(-1 * var(--focus-width, 2px));
}

/* The search field is a wrapper, so the ring goes on it when the inner input
   takes focus — :focus-visible on the input itself would draw inside the cut. */
.browse-filter__search-input:focus-within {
	outline: var(--focus-width, 2px) solid var(--focus-color);
	outline-offset: calc(-1 * var(--focus-width, 2px));
}

/* Bricks' own skip link rounds its bottom corner (8px, from the theme's
   frontend CSS). It is the first thing a keyboard user meets, so it should look
   like the rest of the system. Squared rather than chamfered: it is a
   transient, high-contrast affordance and the cut would read as decoration on
   something whose whole job is to be unmistakable. */
.skip-link,
.skip-link:focus { border-radius: 0; }

/* Nothing in this system rounds. ACSS's radius ladder is zeroed at source
   (base-radius = 0); BricksExtras rounds its burger bars separately.
   ⚠️ Override the TOKEN, not the rule (01): BE's own rule consumes
   `--x-burger-line-radius`, and BE declares it `:root{...4px}` in a stylesheet
   that enqueues AFTER this one — so a matching `:root` override here loses on
   source order at equal specificity. Declaring it on the trigger itself wins
   instead, because a direct declaration beats an inherited one whatever the
   sheet order. Cheaper and more durable than a doubled-class selector fight. */
.brxe-xburgertrigger { --x-burger-line-radius: 0; }

/* ===========================================================================
   FORMS — Bricks native Form element only (this site has no form framework)
   ---------------------------------------------------------------------------
   Every form carries the generic `form-brand` global class FIRST, then its own
   (login__form). Colours, border, type and the submit button's
   fill are TYPED on `form-brand`; only what the Form element has no control for
   lives here: the cut corner, the focus ring, button padding + hover, and the
   checkbox.

   ⚠️ A cut corner on a BORDERED field slices the border off at the corner and
   leaves a visible gap in the outline. So the field draws the diagonal itself:
   a line at the cut (`--stroke`), one gradient tile per cut corner, painted from the
   border box. `--edge` is the border colour, so focus recolours all of it.
   (The filled selects and buttons elsewhere have no border, so they never
   needed this.)
   =========================================================================== */

.form-brand input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]),
.form-brand textarea,
.form-brand select {
	--cut: var(--chamfer-s);
	--edge: var(--muted);
	--stroke: 1.2px;  /* the diagonal's width — tracks the edge: 1px border, 2px focused */
	--diag: calc(var(--cut) * 0.7071); /* the cut line's distance along a 45deg gradient */
	clip-path: polygon(
		var(--cut) 0, 100% 0,
		100% calc(100% - var(--cut)), calc(100% - var(--cut)) 100%,
		0 100%, 0 var(--cut)
	);
	background-image:
		linear-gradient(135deg, transparent var(--diag), var(--edge) var(--diag) calc(var(--diag) + var(--stroke)), transparent calc(var(--diag) + var(--stroke))),
		linear-gradient(315deg, transparent var(--diag), var(--edge) var(--diag) calc(var(--diag) + var(--stroke)), transparent calc(var(--diag) + var(--stroke)));
	background-repeat: no-repeat;
	background-origin: border-box;
	background-size: calc(var(--cut) + 2px) calc(var(--cut) + 2px);
	background-position: top left, bottom right;
}

/* Focus: the whole edge goes orchid and thickens to 2px with an inset outline —
   an outline at a positive offset would be clipped away (see THE CUT CORNER). */
.form-brand input:not([type="checkbox"]):not([type="radio"]):focus-visible,
.form-brand textarea:focus-visible,
.form-brand select:focus-visible,
.form-brand input:not([type="checkbox"]):not([type="radio"]):focus,
.form-brand textarea:focus,
.form-brand select:focus {
	--edge: var(--focus-color);
	--stroke: 2.2px;
	border-color: var(--focus-color);
	outline: 1px solid var(--focus-color);
	outline-offset: -2px;
}

/* The submit button is a bare `bricks-button` — the Form element cannot give it
   ACSS's btn classes — so this completes the match with .btn--primary: padding
   from the ACSS button tokens, and its hover. Fill, border and type are typed. */
.form-brand button[type="submit"] {
	padding: var(--btn-padding-block) var(--btn-padding-inline);
	transition: background-color .2s ease, border-color .2s ease;
}

.form-brand button[type="submit"]:hover {
	background-color: var(--primary-hover);
	border-color: var(--primary-hover);
}

.form-brand input[type="checkbox"] {
	accent-color: var(--primary);
}

/* Form notices (Bricks renders `.message.error` / `.message.success` inside the
   form after a submit, including a failed login). Bricks' defaults are pastel
   LIGHT boxes with coloured text, 2.94:1 and 2.34:1, so they fail AA and are the
   only light surfaces on the site. Same shape as `login-form__description`: a
   dark box, ink text, a 4px left rule carrying the state. Oxblood marks the
   error, the brand's reserved alarm colour (as a rule, never as text, since it
   reads ~2:1 on dark); orchid marks success.
   Doubled class: Bricks' own notice rules sit at (0,3,0). */
.form-brand.form-brand .message {
	background-color: var(--base-ultra-dark);
	color: var(--neutral);
	border-left: 4px solid var(--secondary);
	padding: var(--space-s);
}

.form-brand.form-brand .message.error {
	border-left-color: var(--accent);
}

.form-brand.form-brand .message .text {
	color: inherit;
}




/* =========================================================================
   Brand Utilities
   Items from the project's brand guide (see the project docs) that
   ACSS does not auto-generate. ACSS owns the color/spacing/type tokens —
   these compose them into the brand-spec components and override the
   ACSS defaults that the brand contradicts.
   ========================================================================= */

/* --- Token-level overrides (brand guide §6.5) --------------------------
   ACSS exposes button + link styling as CSS custom properties; overriding
   them here lets ACSS's own .btn / link rules pick up the brand spec
   without re-declaring the underlying selectors.
   ----------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------
   REMOVED 2026-09-22 — a `:root` block inherited from the property this seed
   was cut from. It was a second source of truth over ACSS, which owns the
   design system (CLAUDE.md), and every value in it belonged to the OLD brand:

     --btn-radius: 9999px          pill buttons. Goth Coven does not round —
                                   ACSS `base-radius` is 0, and the cut corner
                                   is the shape. See the CUT CORNER block above.
     --btn-padding-block/inline    now ACSS `btn-padding-block` 0.8em /
                                   `btn-padding-inline` 1.6em = the brand's
                                   12px / 24px at a 15px button.
     --link-color: var(--accent)   "Cherry links." Accent here is oxblood, which
                                   the brand holds in reserve for badges and tier
                                   markers — and it read 2.18:1 on the page.
                                   Links are orchid: ACSS `link-color` =
                                   var(--secondary), 5.08:1.
     --link-color-hover            now var(--secondary-hover).

   ⚠️ Brand tokens do not live in this file. ACSS Global CSS is the only home
   (01 -> Pinned custom tokens); the pinned set is --body/--muted/--faint/
   --raise/--violet-ud/--chamfer/--chamfer-s/--eyebrow-*.
   --------------------------------------------------------------------------- */

/* --- Alternate section band ---------------------------------------------
   The brand guide defines no alternating band: its sections sit on the page
   colour, separated by a hairline in --line (= --base-dark). What it does
   define is the surface ladder (page #0E0D11 -> card #18161D -> raise #27252D)
   and two rules that settle the choice:
     - "a fine dot texture sits over thumbnails and ANY LARGE AREA OF COLOR"
     - "raised panels carry short text, not paragraphs"
   So the alt band is --raise under the dot texture. Not --card: the video
   cards are --base-ultra-dark (= card) and would vanish into it, which is the
   same failure `video-related` had. Orchid falls to 4.0:1 on --raise: fine for
   links and headings, not for long copy (guide §02).
   Registered as the `section-alt` Bricks global class so it is in the picker.
   Replaces the unused `.section-primary` (and ACSS `bg--base-light-trans-20`,
   a neutral grey wash that is off the brand's violet-tinted ladder).
   ----------------------------------------------------------------------- */
.section-alt {
	background-color: var(--raise);
	background-image: var(--dither);
}

/* Small orchid text on --raise is 3.96:1 — below AA 4.5:1 (the guide's own
   "fine for links and headings" note covers large text only). Every --raise
   surface lifts its links and eyebrows one step to --secondary-light. Overrides
   the TOKEN ACSS's link rule consumes (`var(--link-color, …)`), not the rule,
   so buttons (`btn--*`) are untouched. Keep this list in step with any new
   class whose background is --raise. */
.section-alt,
.video-cta,
.join-features,
.login__form-container {
	--link-color: var(--secondary-light);
}
.section-alt .eyebrow,
.video-cta .eyebrow,
.join-features .eyebrow {
	color: var(--secondary-light);
}
.section-base    { background: var(--base-ultra-dark); }

/* --- Eyebrow label (brand guide §3.2) ----------------------------------
   Small uppercase Cherry label. Use for non-heading eyebrows
   (<span class="eyebrow">…</span>) — H6 already gets the eyebrow
   treatment from ACSS dashboard config.
   ----------------------------------------------------------------------- */
.eyebrow {
	display: inline-block;
	font-family: var(--text-font-family);
	font-size: var(--text-s);
	font-weight: 600;
	text-transform: uppercase;
	letter-spacing: 0.08em;
	color: var(--secondary);
	margin-bottom: var(--space-s);
}

/* --- Lead paragraph (brand guide §3.2) ---------------------------------
   Larger intro paragraph below an H1/H2. Slightly muted text vs. body so
   the heading still owns the eye.
   ----------------------------------------------------------------------- */
.lead {
	font-size: var(--text-xxl);
	line-height: 1.5;
	color: var(--body);
	max-width: 65ch;
	margin-top: var(--space-m);
}

/* --- Badges (brand guide §5.3) -----------------------------------------
   Pill badges using surface-tint backgrounds and dark-shade text. Use
   `.badge` plus a variant; .badge--accent renders solid Cherry on Cream.
   Named .badge (not .tag) to avoid collision with Bricks' internal `tag`
   class added to every linked/wrapped image element. (Bricks
   includes/elements/image.php sets class="tag" on the link/picture/
   figure wrapper as a structural marker — using .tag here would leak
   onto every wrapped image site-wide.)
   ----------------------------------------------------------------------- */
.badge {
	display: inline-block;
	padding: 0.3rem 0.85rem;
	font-size: var(--text-xs);
	font-weight: 600;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	font-family: var(--text-font-family);
	border: 1px solid transparent;
}
.badge--primary {
	background: var(--raise);
	color: var(--secondary);
	border-color: var(--base-dark);
}
.badge--accent {
	background: var(--accent);
	color: var(--neutral);
}
.badge--info {
	background: var(--info-ultra-light);
	color: var(--info-dark);
	border-color: var(--info-light);
}
.badge--success {
	background: var(--success-ultra-light);
	color: var(--success-dark);
	border-color: var(--success-light);
}
.badge--warning {
	background: var(--warning-ultra-light);
	color: var(--warning-dark);
	border-color: var(--warning-light);
}
.badge--neutral {
	background: var(--raise);
	color: var(--body);
	border-color: var(--base-dark);
}

/* --- Callouts (brand guide §5.4) ---------------------------------------
   Soft alert boxes with a left rule and tinted background. Pair with
   <strong> as the leading label.
   ----------------------------------------------------------------------- */
.callout {
	margin-top: var(--space-m);
	padding: var(--space-m);
	background: var(--raise);
	border-left: 3px solid var(--primary);
	font-size: var(--text-m);
}
.callout strong {
	display: block;
	margin-bottom: 4px;
	color: var(--secondary);
	font-weight: 700;
}
.callout--info {
	background: var(--info-ultra-light);
	border-left-color: var(--info);
}
.callout--info strong { color: var(--info-dark); }
.callout--warning {
	background: var(--warning-ultra-light);
	border-left-color: var(--warning);
}
.callout--warning strong { color: var(--warning-dark); }

/* --- Italic lockout (brand guide §3.3) ---------------------------------
   System loads no italic font weights. Render emphasis via weight only.
   ----------------------------------------------------------------------- */
em,
i {
	font-style: normal;
	font-weight: 600;
}

/* =========================================================================
   Video Grid + Card
   Reusable across archive, taxonomy, related, search. Class names match
   the Bricks Global Classes (BEM-style) registered on those elements.
   ========================================================================= */

/* --- Grid: responsive column count + explicit gap --- */
.video-grid__wrapper {
	display: grid;
	grid-template-columns: var(--grid-1);
	gap: var(--space-m);
}

@media (min-width: 768px) {
	.video-grid__wrapper { grid-template-columns: var(--grid-2); }
}

@media (min-width: 992px) {
	.video-grid__wrapper { grid-template-columns: var(--grid-3); }
}

@media (min-width: 1366px) {
	.video-grid__wrapper { grid-template-columns: var(--grid-4); }
}

/* --- Performer single layout: SPLIT GRIDS (2026-10-01, ported from goth-members)
   The profile takes a 1/3 column on the left and is sticky. The videos sit in their own
   2-column grid in the other 2/3. Below desktop (<=991px) everything stacks into one
   column. Both grids are TYPED on the global classes (`performer-videos__grid`,
   `performer-videos__list`), so no layout rules live here. A rule here would load after
   the class CSS and win. Replaces the shared 3x3, where the first video row stretched
   to the profile's height. */

/* --- Performer profile card: the card vocabulary — 16px cut (`.cham` list),
   --base-ultra-dark surface, photo on --raise, inset focus ring; surface and
   type are TYPED on the global classes.
   The dot texture goes on the CARD, sized to the photo: the image element puts
   its class on the <img> itself (no link, so no <a> wrapper), and a replaced
   element cannot carry a pseudo-element. ⚠️ The aspect-ratio here must track
   `performer-card__image`'s typed `_aspectRatio` (1/1.5) — change one, change both. */
.performer-card {
	position: relative;
}

/* Sticky profile: holds beside the (much longer) video column. Only where the
   two columns sit side by side (typed grid stacks at <=991px) AND the viewport
   can show the whole card — the card runs ~800px at its widest, and a sticky
   element taller than the viewport hides its own bottom (the bio) until the
   grid ends. ⚠️ A much longer real bio would need this height gate raised.
   Sticky is still a containing block, so the ::after texture keeps working.
   --wp-admin--admin-bar--height is WP core's own var (unset = no bar). */
@media (min-width: 992px) and (min-height: 860px) {
	.performer-card {
		position: sticky;
		top: calc(var(--wp-admin--admin-bar--height, 0px) + var(--space-m));
	}
}

.performer-card::after {
	content: "";
	position: absolute;
	top: 0;
	left: 0;
	width: 100%;
	aspect-ratio: 1 / 1.5;
	background-image: var(--dither);
	pointer-events: none;
}

/* --- Performer archive grid: same escalating steps as .video-grid__wrapper,
   including the 1366px->4 column step (no fixed-count brief here, unlike
   the profile page's 3x3 grid above). --- */
.performer-grid__wrapper {
	display: grid;
	grid-template-columns: var(--grid-1);
	gap: var(--space-m);
}

@media (min-width: 768px) {
	.performer-grid__wrapper { grid-template-columns: var(--grid-2); }
}

@media (min-width: 992px) {
	.performer-grid__wrapper { grid-template-columns: var(--grid-3); }
}

@media (min-width: 1366px) {
	.performer-grid__wrapper { grid-template-columns: var(--grid-4); }
}

/* --- Performer archive card: the video card's brand vocabulary on a 2:3
   portrait — the 16px cut (in the `.cham` list above), the photo on --raise
   under the dot texture, a --base scrim, the inset orchid focus ring (VIDEO
   CARD block). Surface, scrim colour and type are TYPED on the global
   classes; only what Bricks has no control for lives here.

   Hover is the same lift + photo zoom as .video-card, without a shadow —
   clip-path removes anything painted outside the border box.

   No stretched link: the photo's own `link` covers the card, and the
   overlaid name links to the same place. The overlay sits ABOVE the photo,
   so it passes clicks through to it and only the name link takes them back —
   otherwise the bottom third of every card (the "View Performer" line
   included) was a dead zone. */
.performer-archive-card {
	transition: transform .2s ease;
}

.performer-archive-card:hover {
	transform: translateY(-2px);
}

.performer-archive-card__overlay {
	pointer-events: none;
}

.performer-archive-card__title a {
	pointer-events: auto;
}

.performer-archive-card__image {
	position: relative;
	display: block;
	transition: transform .3s ease;
}

/* the dot texture — on the <a> wrapper, never the <img> (a replaced element
   cannot carry a pseudo-element); pointer-events:none so it never eats the click */
.performer-archive-card__image::after {
	content: "";
	position: absolute;
	inset: 0;
	background-image: var(--dither);
	pointer-events: none;
}

.performer-archive-card:hover .performer-archive-card__image {
	transform: scale(1.03);
}

/* --- Card: override the pure-white bg, set up for stretched link + hover ---
   `article.video-card` bumps specificity above the Bricks Global Class output
   (which uses the bare `.video-card` selector) so the bg override wins
   regardless of inline-style load order. */
article.video-card {
	background-color: var(--base-ultra-dark);
}

.video-card {
	position: relative;
	transition:
		transform .2s ease,
		box-shadow .2s ease,
		border-color .2s ease;
}

/* Stretched link — the image's <a> and the title's <a> each cover the
   whole card, so a click anywhere lands on the video. Deliberately scoped
   to just these two rather than a blanket `.video-card a[href]::after`:
   that broader selector stretched EVERY anchor in the card, and once the
   card grew a real inner link (Performers, added 2026-08-30) the
   later-in-DOM performer anchor painted its own full-card stretch on top
   of the video's, silently sending every click on a performer-having card
   to the performer instead of the video (`CLAUDE.md` → Video Card —
   Performers Display). Scoping to the two intended anchors is the fix;
   any future inner link added to the card is safe by default instead of
   needing its own exclusion. This is a paywalled member site, so the
   duplicate-link-per-card tradeoff against strict a11y purism is accepted
   here — see `01`'s clickable-card convention for the visible-anchor
   default this reverts to. */
.video-card__img[href]::after,
.video-card__heading a[href]::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 1;
}

/* Both stretched overlays above cover the ENTIRE card, not just their own
   anchor's visible footprint — `inset: 0` is relative to `.video-card`
   (the nearest `position: relative` ancestor), so they blanket the
   performer link's area too despite living nowhere near it in the DOM.
   Scoping the stretched rule to two anchors (previous block) fixed which
   link wins when they overlap; it does nothing about a plain-flow element
   sitting underneath either overlay having no way to win a click at all.
   Lifting the performer link into its own stacking context above them
   (z-index: 2) is what makes it independently clickable again — this is
   the standard "elevate the secondary link above a stretched-link
   overlay" pattern, not a variant of the scoping fix. */
.video-card__performer-link {
	position: relative;
	z-index: 2;
}

/* Hover / focus feedback --- */
/* No box-shadow: the card is chamfered and clip-path removes anything painted
   outside the border box. Focus is the inset orchid ring in the VIDEO CARD
   block above — a violet outline at +2px used to sit here, won on source
   order, and was clipped away entirely, leaving keyboard users no ring. */
.video-card:hover {
	transform: translateY(-2px);
}

/* Image subtly zooms on card hover. `overflow: hidden` is already on
   .video-card via its Bricks Global Class. */
.video-card__img {
	transition: transform .3s ease;
}

.video-card:hover .video-card__img {
	transform: scale(1.03);
}

/* Play-icon overlay — sits on the poster area only (aspect 16/9, top of card),
   fades in on hover. `pointer-events: none` keeps the stretched-link click
   target intact.
   Split across two pseudo-elements on purpose: ::before is the scrim, ::after
   is the glyph. They cannot share one element because the glyph is drawn with
   `mask-image`, and a mask clips EVERYTHING the element paints — including the
   scrim's background-color.
   The glyph mask is a disc with the play triangle knocked out via
   `fill-rule='evenodd'`, so the scrim shows through the triangle. That keeps the
   original two-tone look while needing only ONE colour, which means it can be a
   token (`--base`) instead of a hard-coded hex. A literal two-colour SVG could
   not be tokenised at all: custom properties do not substitute inside a
   `url()` data URI. */
.video-card::before,
.video-card::after {
	content: "";
	position: absolute;
	top: 0;
	left: 0;
	right: 0;
	aspect-ratio: 16 / 9;
	opacity: 0;
	transition: opacity .25s ease;
	pointer-events: none;
	z-index: 2;
}

.video-card::before {
	background-color: var(--neutral-trans-40);
}

.video-card::after {
	background-color: var(--neutral);
	-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill-rule='evenodd' d='M12 1a11 11 0 100 22 11 11 0 000-22zM10 8v8l6-4z'/%3E%3C/svg%3E");
	mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill-rule='evenodd' d='M12 1a11 11 0 100 22 11 11 0 000-22zM10 8v8l6-4z'/%3E%3C/svg%3E");
	-webkit-mask-position: center;
	mask-position: center;
	-webkit-mask-repeat: no-repeat;
	mask-repeat: no-repeat;
	-webkit-mask-size: 56px auto;
	mask-size: 56px auto;
	z-index: 3;
}

.video-card:hover::before,
.video-card:hover::after {
	opacity: 1;
}

/* --- Line-clamp: lock card heights so rows stay even ---
   Heading was clamped to 1 line, which ellipsised any title longer than the
   card width. Raised to 2 so longer titles wrap instead of truncating.
   Raised to 3 (heading and description) on 2026-09-29.
   There is no text-level limit anywhere — the elements render a bare
   {post_title} — so this clamp is the only thing governing it, and the
   card's global class is shared by all six
   copies of the card markup across five templates: archive (127), taxonomy
   (254), the related grid on video-single (95), the performer grid (382), and
   both homepage carousels (264).
   ⚠️ Rows no longer bottom-align perfectly, but the main driver of that is the
   conditional "Starring:" line varying per card, which predates this; a second
   title line only adds to it. If it matters: min-height: 2lh here, and the
   same on the Starring block. */
.video-card__heading {
	display: -webkit-box;
	-webkit-line-clamp: 3;
	-webkit-box-orient: vertical;
	overflow: hidden;
	text-overflow: ellipsis;
}

.video-card__description {
	display: -webkit-box;
	-webkit-line-clamp: 3;
	-webkit-box-orient: vertical;
	overflow: hidden;
	text-overflow: ellipsis;
}

/* =========================================================================
   Home Notice — dismissable banner between header and Latest Release
   Hide-when-content-empty is handled by a Bricks Condition on the section.
   Dismissal is JS-driven (home-notice.js), keyed by a hash of the visible
   content, so updating the text auto-re-shows the banner for everyone.
   ========================================================================= */

.home-notice {
	padding-block: var(--space-xs);
	background-color: var(--primary-light-trans-10);
	color: var(--neutral);
}

.home-notice__container {
	display: flex;
	flex-direction: row;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-m);
}

.home-notice__content {
	flex: 1 1 auto;
	font-family: "JetBrains MOno";
	font-weight: 500;
	font-size: var(--text-s);
	line-height: 1.5;
	color: var(--neutral);
}

.home-notice__content p {
	margin: 0;
}

.home-notice__content p + p {
	margin-top: var(--space-xs);
}

.home-notice__content a {
	color: var(--secondary);
	text-decoration: underline;
	text-underline-offset: 2px;
}

.home-notice__close {
	flex: 0 0 auto;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 24px;
	height: 24px;
	padding: 0;
	color: var(--neutral);
	background: transparent;
	border: 0;
	cursor: pointer;
	transition: color .15s ease, background .15s ease;
}
.home-notice__close:hover {
	color: var(--secondary);
}
.home-notice__close:focus-visible {
	outline: 2px solid var(--primary);
	outline-offset: 2px;
}

/* =========================================================================
   Video Single — Info section responsive stack
   The 3:1 two-column layout (title/description | Categories aside) is set
   via a Bricks Global Class (.video-info__container). On narrow viewports
   the right column squeezes until the taxonomy values truncate off-screen.
   Stack at ≤767px. Doubled selector beats Bricks' inline CSS cascade.
   ========================================================================= */

@media (max-width: 767px) {
	.video-info__container.video-info__container {
		grid-template-columns: 1fr;
		grid-gap: var(--space-m);
	}
}

/* =========================================================================
   Browse Filter + Active Filters + Empty State
   Single consolidated block. Uses doubled class selectors (e.g.
   `.browse-filter.browse-filter` or compound selectors) to bump specificity
   above any Bricks or ACSS rule. All styling lives here — Bricks Global
   Classes for browse-* should remain empty.
   ========================================================================= */

/* ----- FILTER BAR CONTAINER ----- */

.browse-filter {
	position: sticky;
	top: 0;
	z-index: 10;
	background-color: var(--base);
	border-bottom: 1px solid var(--base-dark);
}

/* ----- PAGE HEADER (shared with video-archive + video-taxonomy) ----- */

/* On narrow viewports the "Member Videos" h1 and its "26 Videos" count
   share a single flex row that wraps mid-line — awkward. Stack them
   vertically instead at ≤767px so each gets its own line. */
@media (max-width: 767px) {
	/* Doubled class name — Bricks emits inline-style CSS for Global Classes
	   later in the cascade than this external sheet, so equal-specificity
	   rules lose. Doubling raises specificity to (0,2,0) and wins cleanly. */
	.page-header__heading-wrapper.page-header__heading-wrapper {
		flex-direction: column;
		align-items: flex-start;
	}
}

/* ----- FORM GRID (3 cols: search | facets | clear) ----- */
/* Compound selector .browse-filter .browse-filter__form beats Bricks 
   .brxe-block at 0-2-0 > 0-1-0 specificity. */

.browse-filter .browse-filter__form {
	display: grid;
	grid-template-columns: 2fr auto auto;
	gap: var(--space-m);
	align-items: center;
	width: 100%;
	flex-direction: unset;
}

/* Prevent grid items from stretching to the cell's full height —
   when facets wrap to 2 rows, the search was inflating with them. */
.browse-filter .browse-filter__form > * {
	align-self: center;
}

@media (max-width: 991px) {
	.browse-filter .browse-filter__form {
		grid-template-columns: 1fr;
	}
}

@media (max-width: 767px) {
	.browse-filter .browse-filter__form {
		grid-template-columns: 1fr auto;
	}
}

/* ----- SEARCH INPUT (wrapper + inner Bricks wrapper + input) ----- */

.browse-filter .browse-filter__search {
	position: relative;
	width: 100%;
	flex-direction: unset;
}

.browse-filter__search-input,
.browse-filter__search-input.brxe-filter-search {
	position: relative;
	width: 100%;
	background-color: var(--raise);
	border: 1px solid var(--base-dark);
	overflow: hidden;
}

.browse-filter__search-input:focus-within,
.browse-filter__search-input.brxe-filter-search:focus-within {
	border-color: var(--primary);
	outline: 2px solid var(--primary);
	outline-offset: 2px;
}

.browse-filter__search-input input[type="search"] {
	box-sizing: border-box;
	width: 100%;
	height: 44px;
	padding-block: 0;
	padding-inline: calc(var(--space-m) * 2.5) var(--space-m);
	font-size: var(--text-s);
	line-height: 44px;
	/* --base is the page canvas (Cream), not a text colour. It read as
	   light-on-light (1.02:1) — inherited from the clone source, whose
	   --base was near-black. */
	color: var(--neutral);
	background-color: transparent;
	background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 24 24' fill='none' stroke='%23F3F2F8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cpath d='m20 20-3.5-3.5'/%3E%3C/svg%3E");
	background-repeat: no-repeat;
	background-position: left var(--space-m) center;
	border: 0;
	outline: none;
	font-family: inherit;
}

.browse-filter__search-input input[type="search"]::placeholder {
	/* --base-semi-dark is an auto-derived shade of warm Cream and resolves
	   to #916622, a mustard. Not in the palette. */
	color: var(--neutral-trans-70);
}

/* Hide Bricks' auto-injected search-clear icon */
.browse-filter__search-input .icon {
	display: none !important;
}

/* ----- FACETS GROUP (horizontal flex) ----- */

.browse-filter .browse-filter__facets {
	display: flex;
	flex-direction: row;
	flex-wrap: wrap;
	gap: var(--space-s);
	width: auto;
	align-items: center;
}

@media (max-width: 991px) {
	.browse-filter .browse-filter__facets {
		justify-content: flex-start;
	}
}

@media (max-width: 767px) {
	.browse-filter .browse-filter__facets {
		display: none;
	}
}

/* Individual facet wrapper (holds a select) */
.browse-filter .browse-filter__facet {
	position: relative;
	width: auto;
	flex-direction: unset;
}

/* Hide the facet that matches the current taxonomy archive.
   On /body/big-ass/ the Body dropdown is redundant and would
   clash with the path-segment filter if used. Piggybacks on WP's native
   tax-<slug> body class (added automatically by body_class() on term
   archives). Covers desktop + sheet facets in one go.
   Keep this list in step with ms_video_taxonomies() in the plugin. */
body.tax-body .browse-filter__facet--body,
body.tax-scene .browse-filter__facet--scene,
body.tax-finish .browse-filter__facet--finish,
body.tax-intensity .browse-filter__facet--intensity,
body.tax-ethnicity .browse-filter__facet--ethnicity {
	display: none;
}

/* The actual <select> element */
.browse-filter .browse-filter__facet-select,
.browse-filter select.browse-filter__facet-select {
	appearance: none;
	-webkit-appearance: none;
	height: 44px;
	padding: 0 calc(var(--space-m) * 2) 0 var(--space-s);
	font-size: var(--text-s);
	font-weight: 600;
	font-family: inherit;
	color: var(--neutral);
	background-color: var(--primary);
	background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 24 24' fill='none' stroke='%23F3F2F8' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
	background-repeat: no-repeat;
	background-position: right var(--space-m) center;
	background-size: 18px 18px;
	border: 1px solid var(--primary);
	cursor: pointer;
	min-width: 110px;
}

.browse-filter .browse-filter__facet-select:focus {
	outline: 2px solid var(--primary-dark);
	outline-offset: 2px;
	border-color: var(--neutral);
}

/* Active state — Bricks marks the empty "All" option with class="placeholder";
   any selected option that isn't the placeholder means the filter is active. */
.browse-filter .browse-filter__facet-select:has(option:checked:not(.placeholder)) {
	background-color: var(--primary);
	background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 24 24' fill='none' stroke='%23F3F2F8' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
	background-size: 18px 18px;
	color: var(--text-light);
	border-color: var(--primary);
}

/* ----- CLEAR-ALL TEXT LINK (filter bar) ----- */

.browse-filter .browse-filter__clear {
	font-size: var(--text-s);
	font-weight: 500;
	color: var(--neutral);
	text-decoration: underline;
	text-underline-offset: 3px;
	white-space: nowrap;
	padding: var(--space-xs) var(--space-s);
	width: auto;
	transition: color 0.15s ease;
	background-color: var(--raise);
}

.browse-filter .browse-filter__clear:hover {
	color: var(--secondary);
}

@media (max-width: 767px) {
	.browse-filter .browse-filter__clear {
		display: none;
	}
}

/* ----- MOBILE TOGGLE ----- */

.browse-filter .browse-filter__mobile-toggle {
	display: none;
	flex-direction: row;
	align-items: center;
	justify-content: center;
	gap: var(--space-xs);
	width: auto;
	/* Match the search input's visual weight — 44px tall, modest padding,
	   body-font size instead of inheriting a display-size default. */
	height: 44px;
	padding: 0 var(--space-m);
	font-size: var(--text-s);
	font-weight: 500;
	color: var(--neutral);
	background-color: var(--raise);
	border: 1px solid var(--base-dark);
}

.browse-filter .browse-filter__mobile-toggle:hover {
	border-color: var(--primary);
	color: var(--secondary);
}

/* Scope the icon size — the SVG in the button was inheriting Bricks' default
   button-icon sizing, which paints a ~40px hamburger on mobile. */
.browse-filter .browse-filter__mobile-toggle svg {
	width: 18px;
	height: 18px;
	flex-shrink: 0;
}

@media (max-width: 767px) {
	.browse-filter .browse-filter__mobile-toggle {
		display: inline-flex;
	}
}

/* ----- BOTTOM SHEET (mobile modal) ----- */

.browse-filter__sheet-overlay {
	display: none;
	position: fixed;
	inset: 0;
	background-color: var(--black-trans-60);
	z-index: 50;
	opacity: 0;
	transition: opacity 0.25s ease;
}

.browse-filter__sheet-overlay[data-open="true"] {
	display: block;
	opacity: 1;
}

.browse-filter__sheet {
	position: fixed;
	left: 0;
	right: 0;
	bottom: 0;
	z-index: 51;
	height: 85vh;
	max-height: 85vh;
	background-color: var(--base-ultra-dark);
	border-top-left-radius: var(--radius-xl);
	border-top-right-radius: var(--radius-xl);
	box-shadow: 0 -8px 32px var(--black-trans-20);
	transform: translateY(100%);
	transition: transform 0.3s ease;
	display: flex;
	flex-direction: column;
	overflow: hidden;
}

.browse-filter__sheet[data-open="true"] {
	transform: translateY(0);
}

.browse-filter__sheet .browse-filter__sheet-handle {
	display: flex;
	flex-direction: row;
	justify-content: center;
	align-items: center;
	padding: var(--space-s) 0 var(--space-xs);
	flex-shrink: 0;
	width: 100%;
}

.browse-filter__sheet-handle-bar {
	width: 40px;
	height: 4px;
	background-color: var(--muted);
}

.browse-filter__sheet .browse-filter__sheet-header {
	display: flex;
	flex-direction: row;
	align-items: center;
	justify-content: space-between;
	padding: var(--space-s) var(--space-l) var(--space-m);
	border-bottom: 1px solid var(--base-dark);
	flex-shrink: 0;
	width: 100%;
}

.browse-filter__sheet-title {
	font-size: var(--h3);
	font-weight: 600;
	color: var(--neutral);
	margin: 0;
}

.browse-filter__sheet-close {
	display: inline-flex;
	flex-direction: row;
	align-items: center;
	justify-content: center;
	width: 32px;
	height: 32px;
	background: transparent;
	border: 0;
	color: var(--neutral-trans-70);
	cursor: pointer;
}

.browse-filter__sheet-close:hover {
	background-color: var(--raise);
	color: var(--neutral);
}

.browse-filter__sheet-search {
	padding: var(--space-m) var(--space-l);
	background-color: var(--base-ultra-dark);
	border-bottom: 1px solid var(--base-dark);
	flex-shrink: 0;
	width: 100%;
}

.browse-filter__sheet-body {
	flex: 1 1 auto;
	overflow-y: auto;
	padding: var(--space-l);
	width: 100%;
}

.browse-filter__sheet-facets {
	display: flex;
	flex-direction: column;
	gap: var(--space-m);
	width: 100%;
}

.browse-filter__sheet-facet {
	display: flex;
	flex-direction: column;
	width: 100%;
}

.browse-filter__sheet-facet-label {
	display: block;
	font-family: 'JetBrains Mono', ui-monospace, monospace;
	font-size: var(--text-xs);
	font-weight: 600;
	text-transform: uppercase;
	letter-spacing: 0.08em;
	color: var(--neutral-trans-70);
	margin-bottom: var(--space-xs);
}

.browse-filter__facet-select--in-sheet {
	width: 100%;
	font-size: var(--text-m);
	font-weight: 600;
	padding-block: var(--space-xs);
}

.browse-filter__sheet .browse-filter__sheet-footer {
	display: flex;
	flex-direction: row;
	gap: var(--space-s);
	padding: var(--space-m) var(--space-l);
	border-top: 1px solid var(--base-dark);
	background-color: var(--base-ultra-dark);
	flex-shrink: 0;
	width: 100%;
}

.browse-filter__sheet-footer-clear {
	flex: 0 0 auto;
	width: auto;
	padding: var(--space-s) var(--space-m);
	font-size: var(--text-m);
	font-weight: 500;
	color: var(--neutral-trans-70);
	background: transparent;
	border: 1px solid var(--base-dark);
	cursor: pointer;
}

.browse-filter__sheet-footer-clear:hover {
	color: var(--secondary);
	border-color: var(--primary);
}

.browse-filter__sheet-footer-apply {
	flex: 1 1 auto;
	width: auto;
}

/* ----- ACTIVE-FILTER CHIPS SECTION ----- */

.browse-active-filters {
	background-color: var(--base-ultra-dark);
	border-bottom: 1px solid var(--base-dark);
}

/* Hide the whole section when there are no active filter chips to show.
   Bricks' own CSS hides the empty <ul>, but the section's "Active:" label
   and "Clear All Filters" button stay visible, creating an empty band.
   `:has()` re-evaluates when AJAX filter changes add/remove <li> children,
   so this stays accurate without JS. */
.browse-active-filters:has(.browse-active-filters__list:empty) {
	display: none;
}

.browse-active-filters .browse-active-filters__container {
	display: flex;
	flex-direction: row;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-xs);
}

.browse-active-filters__label {
	font-family: 'JetBrains Mono', ui-monospace, monospace;
	font-size: var(--text-xs);
	font-weight: 500;
	text-transform: uppercase;
	letter-spacing: 0.08em;
	color: var(--neutral-trans-70);
	margin: 0 var(--space-xs) 0 0;
	width: auto;
}

.browse-active-filters .browse-active-filters__list {
	display: flex;
	flex-direction: row;
	flex-wrap: wrap;
	gap: var(--space-xs);
	list-style: none;
	margin: 0;
	padding: 0;
	width: auto;
}

.browse-active-filters__item {
	display: inline-flex;
	width: auto;
}

.browse-active-filters__chip {
	display: inline-flex;
	flex-direction: row;
	align-items: center;
	gap: var(--space-xs);
	width: auto;
	min-width: 80px;
	padding: var(--space-xs) var(--space-s);
	font-size: var(--text-s);
	color: var(--text-light);
	background-color: var(--secondary);
	border: 0;
	cursor: pointer;
	transition: background-color 0.15s ease;
}

.browse-active-filters__chip:hover {
	background-color: var(--primary);
}

.browse-active-filters__chip-x {
	display: inline-flex;
	flex-direction: row;
	align-items: center;
	justify-content: center;
	width: 14px;
	height: 14px;
}

.browse-active-filters__clear-all {
	margin-left: auto;
	width: auto;
	padding: var(--space-xs) var(--space-s);
	font-size: var(--text-s);
	font-weight: 500;
	color: var(--neutral-trans-70);
	background: transparent;
	border: 0;
	cursor: pointer;
	text-decoration: underline;
	text-underline-offset: 3px;
	transition: color 0.15s ease;
}

.browse-active-filters__clear-all:hover {
	color: var(--secondary);
}

@media (max-width: 767px) {
	.browse-active-filters__clear-all {
		margin-left: 0;
		width: 100%;
		text-align: left;
	}
}

/* ----- EMPTY STATE -----
   Show .browse-empty only when the page's grid (videos or performers)
   returned 0 results. The signal is Bricks' own .bricks-posts-nothing-found
   node, which site-functionality supplies as an empty, hidden <li> in the
   grid's <ul> (ms_browse_grid_empty_marker, inc/bricks-queries.php). Bricks
   removes and re-inserts that node on every AJAX filter swap, so :has()
   stays accurate in both directions without JS.

   Not the query trail's data-max-pages: AJAX updates only update Bricks' JS
   state, never that attribute, so it stays frozen at its page-load value.

   NOTE: we intentionally do NOT hide the grid section itself — Bricks' JS
   binds to it for filter AJAX, and display:none on an ancestor would break
   event delegation to the filter-select elements. Only the empty <ul> is
   collapsed, below. */
.browse-empty {
	display: none;
}

/* Scoped from <main>, not a sibling combinator: the video templates place
   .browse-empty after the grid, the performer archive places it before. */
main:has(:is(.video-grid, .performer-grid) .bricks-posts-nothing-found) .browse-empty {
	display: block;
}

/* Collapse the grid's visible footprint when the loop is empty — the <ul>
   then holds only the hidden marker. */
:is(.video-grid__wrapper, .performer-grid__wrapper):has(> .bricks-posts-nothing-found) {
	display: none;
}

.browse-empty__heading {
	font-size: var(--h2);
	font-weight: 600;
	color: var(--neutral);
}

.browse-empty__text {
	font-size: var(--text-m);
	color: var(--neutral-trans-70);
}

.browse-empty__action {
	display: inline-block;
	width: auto;
}

/* ----- REDUCED MOTION ----- */

@media (prefers-reduced-motion: reduce) {
	.browse-filter__sheet-overlay,
	.browse-filter__sheet,
	.browse-active-filters__chip,
	.browse-active-filters__clear-all,
	.browse-filter .browse-filter__clear {
		transition: none;
	}
}

/* ==========================================================================
   HEADER — MOBILE OFFCANVAS (BricksExtras)                       2026-08-11
   ==========================================================================
   Below 991px the header collapses to logo + search + burger, and the main
   nav plus the account links move into a right-side offcanvas panel.

   This replaces the Bricks NATIVE mobile menu, which is retired rather than
   restyled: its toggle icon was a chevron-down (reads as "expand a dropdown",
   not "open the menu"), and its panel was styled in --white / --black /
   --accent, none of which belong to the project's own colour system.

   991px is deliberate — it matches the breakpoint the native mobile menu
   already used (nav-menu `mobileMenu: tablet_portrait`), so the point at
   which navigation changes shape is unchanged for anyone used to the site.
   ========================================================================== */

/* The burger is a mobile-only affordance. */
/* ⚠️ Every visibility rule below uses the DOUBLED-CLASS trick (`.foo.foo`).
   That is not stylistic — a single class loses here. BricksExtras ships
   `.brxe-xburgertrigger { display: inline-flex }` at (0,1,0), and
   `x-burger-trigger-css` enqueues AFTER `bricks-child-css`, so at equal
   specificity the plugin wins on source order and the burger stayed visible
   at every width. `bricks-style-manager-css` also loads after the child
   theme, so the same trap applies to the Bricks global classes below.
   Doubling costs one selector and lifts these to (0,2,0), which beats both
   without reaching for !important. See 03 — "Doubled-class selectors beat
   Bricks inline CSS cascade" and "author rules with equal specificity lose
   to @layer bricks framework rules". */

.header__burger.header__burger {
	display: none;
}

@media (max-width: 991px) {
	.header__burger.header__burger {
		display: flex;
	}

	/* Retire the native mobile menu wholesale — hiding the element also hides
	   its chevron toggle, its slide-out panel and its overlay in one go. */
	.header__menu.header__menu {
		display: none;
	}

	/* The utility bar's two links now live in the panel, so it stops eating
	   vertical space above the fold. */
	.header-utility.header-utility {
		display: none;
	}
}

/* The offcanvas is mobile-only. BE renders the panel markup at every width;
   without this the backdrop and panel remain reachable on desktop, where the
   burger that opens them is hidden. */
@media (min-width: 992px) {
	.header__offcanvas.header__offcanvas {
		display: none;
	}
}

/* =========================================================================
   Homepage — transparent header over the first section
   =========================================================================
   Scoped entirely to body.home; every other page keeps the header exactly
   as built (static, solid --base, in normal document flow) with zero change.

   The utility bar no longer exists anywhere — removed from the Header
   template itself, site-wide, not just hidden here. The header search
   was removed from the template too (2026-09-06), so the bar is
   logo + nav + Login + Join everywhere.

   The header starts at position:absolute; top:0 (removed from flow, so
   the section right after it renders underneath instead of being pushed
   down) with a transparent fill and light-on-dark nav-link colors. Once
   the user scrolls past the header's own height, .is-scrolled swaps it to
   position:fixed with the normal solid styling — a standard
   overlay-then-sticky pattern, not a site-wide header change. */
body.home .header {
	position: absolute;
	top: 0;
	inset-inline: 0;
	z-index: 100;
	background-color: transparent;
	border-bottom-color: transparent;
	transition: background-color 0.25s ease, border-color 0.25s ease;
}
body.home .header.is-scrolled {
	position: fixed;
	top: 0;
	background-color: var(--base);
	border-bottom-color: var(--primary);
}

/* The outlined Login pill (header__login) sits beside the nav and needs the
   same light treatment over the hero video; the filled Join pill does not. */
body.home .header:not(.is-scrolled) .header__login {
	color: var(--neutral);
	border-color: var(--neutral);
}
body.home .header:not(.is-scrolled) .header__login:hover,
body.home .header:not(.is-scrolled) .header__login:focus-visible {
	color: var(--secondary);
	border-color: var(--secondary);
}
body.home .header:not(.is-scrolled) .header__menu .bricks-nav-menu > li > a,
body.home .header:not(.is-scrolled) .header__menu .bricks-nav-menu > li > .brx-submenu-toggle > * {
	color: var(--neutral);
}
body.home .header:not(.is-scrolled) .header__menu .bricks-nav-menu > li:hover > a,
body.home .header:not(.is-scrolled) .header__menu .bricks-nav-menu > li:hover > .brx-submenu-toggle > * {
	color: var(--secondary);
}

/* Legibility over the hero video.
   Everything above is light-on-video, which holds on a dark frame and
   washes out on a bright one. A shadow gives each element its own local
   substrate, so contrast depends less on what the video is showing.

   ⚠️ This is a mitigation, not a fix, and the gap is measured. Sampling
   the top 12% of the live clip (th-vanna-rose-prv.mp4) once per second on
   2026-09-15 gave a median luminance of Y=190 left / Y=201 right — light
   text at 1.5–1.7:1, failing AA 4.5:1 for 90% of the runtime on the nav
   side and 98% on the logo side. The bright frames are the rule for this
   clip, not the exception. Shadows measurably help but cannot carry a
   1.5:1 baseline on their own. A 60% dark scrim was built and tested
   (it brings the median to ~5.4:1) and dropped by decision — the band it
   put across the top of the video was not wanted. If nav legibility is
   revisited, the options are that scrim, or inverting the bar to
   dark-on-bright. Which wins depends on the brand's own hero clip:
   re-measure, do not inherit this conclusion.

   Two stops on the text: a tight 2px blur for edge definition against a
   busy frame, and a wide diffuse 14px that darkens the pixels behind the
   glyph itself. One stop alone does one job or the other, not both.

   Scoped to :not(.is-scrolled) throughout — once the header goes solid
   --base it is ink-on-page and a dark shadow would only read as
   muddy. It drops on the same scroll event that swaps the fill. */
body.home .header:not(.is-scrolled) .header__menu .bricks-nav-menu > li > a,
body.home .header:not(.is-scrolled) .header__menu .bricks-nav-menu > li > .brx-submenu-toggle > * {
	text-shadow: 0 1px 2px var(--base-trans-60), 0 0 14px var(--base-trans-50);
}

/* The Login pill needs both forms, and for different parts of itself.
   `drop-shadow()` traces the element's rendered alpha, so it follows the
   2px ink outline around the pill's radius — which `box-shadow` could
   not do without painting a rectangle behind a rounded element. But it
   only ever draws OUTSIDE that traced edge, so it does nothing for the
   ink label sitting in the pill's transparent middle. That still needs
   a text-shadow of its own. */
body.home .header:not(.is-scrolled) .header__login {
	text-shadow: 0 1px 2px var(--base-trans-60), 0 0 14px var(--base-trans-50);
	filter: drop-shadow(0 1px 3px var(--base-trans-60));
}

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

/* ===========================================================================
   CUT CORNER ON BORDERED ELEMENTS — draw the diagonal edge back in
   ---------------------------------------------------------------------------
   clip-path cuts through the border as well as the box, so a chamfered element
   with a visible border shows a GAP at each cut corner: the stroke just stops.
   Same fix as FORMS above, generalised: two gradient tiles paint a line of the
   border's colour along each cut, from the border box, so the outline reads
   as one continuous stroke around the cut shape.

   Per element, set:
     --cut     the element's chamfer (--chamfer for `.cham` panels, --chamfer-s for `.cham-s` controls)
     --edge    the border colour (currentColor where the border follows the text, so hover follows too)
     --stroke  border width + 0.2px (half the line falls outside the clip)

   ⚠️ This owns `background-image` on these elements. An element that needs a
   background image of its own (a texture, an icon) cannot join this list as-is:
   add the gradients to that element's own background-image stack instead.
   ⚠️ Lives at the END of the file on purpose: `.browse-filter__sheet-close` and
   `…__sheet-footer-clear` reset `background` with the shorthand earlier on, and
   equal specificity is settled by source order.
   Not needed where the border is the same colour as the fill (btn--primary,
   the violet facet selects): the cut is invisible there.
   =========================================================================== */

/* controls — the small cut */
.header__login,
.browse-filter__sheet-footer-clear {
	--cut: var(--chamfer-s);
	--edge: currentColor;
	--stroke: 2.2px;
}
/* Outline buttons whose border changes colour on hover while the text does not:
   `currentColor` would leave the diagonals in the old colour, so --edge names the
   border colour for each state. The age gate's Exit (plugin `inc/age-gate.php`)
   mirrors header Login: ink outline, orchid on hover. Its class name contains
   `btn--`, so ACSS's `[class*="btn--"]:not(…)` reset reaches it too — doubled. */
.th-age-gate__btn--disagree.th-age-gate__btn--disagree {
	--cut: var(--chamfer-s);
	--edge: var(--neutral);
	--stroke: 2.2px;
}
.header__login:hover,
.header__login:focus-visible,
.th-age-gate__btn--disagree.th-age-gate__btn--disagree:hover,
.th-age-gate__btn--disagree.th-age-gate__btn--disagree:focus-visible {
	--edge: var(--secondary);
}
/* ANY ACSS outline button, so one added in the builder later needs no list edit.
   ACSS draws the outline in the button's own colour and sets the text to match,
   so currentColor follows it through hover. Doubled for the same reason as the
   sheet buttons below. */
.btn--outline.btn--outline {
	--cut: var(--chamfer-s);
	--edge: currentColor;
	--stroke: 2.2px;
}
.browse-filter__sheet-close {
	--cut: var(--chamfer-s);
	--edge: var(--primary);
	--stroke: 2.2px;
}
.browse-filter__search-input,
.browse-filter .browse-filter__mobile-toggle {
	--cut: var(--chamfer-s);
	--edge: var(--base-dark);
	--stroke: 1.2px;
}

/* panels — the large cut */
.join-plan,
.video-cta {
	--cut: var(--chamfer);
	--edge: var(--base-dark);
	--stroke: 1.2px;
}
.join-plan.join-plan--featured {
	--edge: var(--primary);
	--stroke: 2.2px;
}

/* The two sheet buttons are doubled: ACSS's `[class*="btn--"]:not(.btn--none, …)`
   (automatic-gutenberg.css, (0,2,0)) resets them with a `background` shorthand. */
.header__login,
.th-age-gate__btn--disagree.th-age-gate__btn--disagree,
.btn--outline.btn--outline,
.browse-filter__sheet-footer-clear.browse-filter__sheet-footer-clear,
.browse-filter__sheet-close.browse-filter__sheet-close,
.browse-filter__search-input,
.browse-filter .browse-filter__mobile-toggle,
.join-plan,
.video-cta {
	--diag: calc(var(--cut) * 0.7071); /* the cut line's distance along a 45deg gradient */
	background-image:
		linear-gradient(135deg, transparent var(--diag), var(--edge) var(--diag) calc(var(--diag) + var(--stroke)), transparent calc(var(--diag) + var(--stroke))),
		linear-gradient(315deg, transparent var(--diag), var(--edge) var(--diag) calc(var(--diag) + var(--stroke)), transparent calc(var(--diag) + var(--stroke)));
	background-repeat: no-repeat;
	background-origin: border-box;
	background-size: calc(var(--cut) + 2px) calc(var(--cut) + 2px);
	background-position: top left, bottom right;
}

/* The mobile sheet's selects are the one bordered chamfer that already has a
   background image: Bricks' native select chevron, two 4px gradient tiles from
   `frontend-light-layer.min.css`. Restated here with the two diagonals stacked
   in front of them, so neither replaces the other. The border is Bricks' own
   `--bricks-border-color` (#dddedf), and the diagonal follows it. */
.browse-filter__facet-select--in-sheet.browse-filter__facet-select--in-sheet {
	--cut: var(--chamfer-s);
	--edge: var(--bricks-border-color);
	--stroke: 1.2px;
	--diag: calc(var(--cut) * 0.7071);
	background-image:
		linear-gradient(135deg, transparent var(--diag), var(--edge) var(--diag) calc(var(--diag) + var(--stroke)), transparent calc(var(--diag) + var(--stroke))),
		linear-gradient(315deg, transparent var(--diag), var(--edge) var(--diag) calc(var(--diag) + var(--stroke)), transparent calc(var(--diag) + var(--stroke))),
		linear-gradient(45deg, transparent 50%, gray 0),
		linear-gradient(135deg, gray 50%, transparent 0);
	background-repeat: no-repeat;
	background-origin: border-box, border-box, padding-box, padding-box;
	background-size: calc(var(--cut) + 2px) calc(var(--cut) + 2px), calc(var(--cut) + 2px) calc(var(--cut) + 2px), 4px 4px, 4px 4px;
	background-position: top left, bottom right, calc(100% - 20px) calc(50% + 2px), calc(100% - 16px) calc(50% + 2px);
}

/* The featured plan's badge (.join-plan__badge) straddles the card's top edge
   (top: -14px), and clip-path clips overflow as well as the box, so it was cut
   in half. The polygon is extended UP past the top edge. Nothing else paints
   there, so only the badge shows, and both cuts stay on the border box. Top-left
   runs (0, cut) → (cut, 0) → straight up. */
.join-plan {
	clip-path: polygon(
		var(--chamfer) -2em, 100% -2em,
		100% calc(100% - var(--chamfer)), calc(100% - var(--chamfer)) 100%,
		0 100%, 0 var(--chamfer), var(--chamfer) 0
	);
}
