/* ============================================================
   Dot Experience — FooEvents Attendee Details
   ============================================================
   Restyles the classic FooEvents CORE `[fooevents_attendee_form]`
   shortcode output (web/app/plugins/fooevents/templates/attendee/
   attendee-page.php, a vendor template this theme never edits
   directly) using this theme's design tokens (assets/css/tokens.css).
   Enqueued (and the `dot-attendee-details` body class added) only on
   the resolved Attendee Details page — see
   inc/fooevents-attendee-details.php.

   This page sits in the same purchase-flow position as Cart/Checkout,
   but is a PLAIN WordPress Page, not a classic-checkout shortcode
   surface, so assets/css/woocommerce-cart-checkout.css never loads here
   and this file has to re-establish the same field/button language
   independently. As of 2026-07-22 (see "Desktop two-column layout"
   below) it does have its own dedicated template,
   templates/page-attendee-details.html, auto-selected by WP's standard
   `page-{slug}` hierarchy — before that it used the generic
   templates/page.html like any other Page. The markup is different too: FooEvents
   renders its own `.fooevents-attendee-group` / `.fooevents-attendee-item`
   wrappers rather than WooCommerce's `#customer_details` structure, but
   the actual field rows are WooCommerce's own `.form-row` / `.input-text`
   convention (FooEvents_Form::text() emits real `<label for>` +
   `<input class="input-text">`, confirmed by reading
   web/app/plugins/fooevents/templates/attendee/form-text.php directly) —
   so the FIELD styling below is deliberately the same shape as the
   checkout/My Account precedent, just re-scoped to this page's own DOM.

   SPECIFICITY — this plain Page is never dequeued from WooCommerce's own
   core stylesheets the way the classic cart/checkout surface is
   (inc/woocommerce-classic-checkout.php's has_shortcode() gate doesn't
   apply here — this is a different shortcode on a different page).
   woocommerce-blocktheme.css / woocommerce-layout.css / twenty-twenty-
   three.css all still load, unconditionally, and several of their rules
   (`.woocommerce form .form-row .input-text, .woocommerce form .form-row
   select { background-color: var(--wc-form-color-background,#fff); … }`,
   specificity 0,3,1) out-specify a plain `body.dot-attendee-details
   .input-text` (0,2,1) — confirmed live via document.styleSheets that
   this exact rule was the one winning and forcing a hardcoded white input
   fill even under the dark theme variant. Every selector below that
   targets an element actually inside the shortcode's own
   `#fooevents-attendee-page` wrapper therefore also includes that ID, so
   it always wins on specificity alone — no `!important` needed except
   where noted. (The page H1 and the error notices below are genuine
   exceptions: both render OUTSIDE `#fooevents-attendee-page`, confirmed
   by reading attendee-page.php — output_errors() runs before the wrapper
   div opens, and the H1 is the page's own wp:post-title block in
   templates/page-attendee-details.html — so those two keep the plain
   body-class scope.)

   FIELD BORDERS — resting field borders are var(--muted), not the 8%-
   opacity var(--line), for the same WCAG 1.4.11 non-text-contrast reason
   documented in woocommerce-cart-checkout.css (a var(--bg)-filled field on
   a var(--bg) page must be identified by its border).

   BOOKING DETAILS — FooEvents Bookings (web/app/plugins/fooevents_bookings)
   renders either a read-only "already selected on the product page" block
   (templates/attendee/selected-options.php, class `fooevents-booking-group`)
   or an editable date/slot picker (templates/attendee/booking-fields.php,
   class `fooevents-bookings-group` — note the vendor's own singular/plural
   inconsistency between the two template files, confirmed by reading both
   directly). Both selectors are covered below since which one renders
   depends on whether date/slot was already chosen on the product page.

   COPY LINK — the "Copy attendee details" control
   (form-copy.php) renders as TWO separate `<a class="copy-btn">` elements
   when `globalWooCommerceEventsAddCopyPurchaserDetails` is "textandicon"
   (confirmed the live option value is exactly that) — one dashicon-only
   anchor, one text anchor, each in its own `<p class="form-row form-row-
   wide">`. Both are left fully functional (neither hidden) since each is a
   real, independent trigger for the same JS copy behavior
   (fooevents-attendee-page.js) — hiding either would be a functional
   change, out of scope here. They're just laid out as one visual group.
   ============================================================ */

/* ---- Scope root ----
   Both the body class (inc/fooevents-attendee-details.php) and the
   shortcode's own wrapper ID are used together so this file can never
   leak to any other page even if the body class were ever missing. */
body.dot-attendee-details #fooevents-attendee-page {
	--dot-fed-gap: clamp(1.5rem, 4vw, 2.25rem);
}

/* ---- Page title (wp:post-title, this page's real H1) ----
   Renders OUTSIDE #fooevents-attendee-page (it's the page's own title
   block, a sibling before the shortcode output) — body-class scope only.
   base.css's global rule (§1, "Typography") leaves headings at browser-
   default size until an instance opts into a size utility — this page's
   H1 never did, which is the "plain black Arial-ish heading" reported.
   var(--step-3) matches base.css's `.display-3`, documented there as the
   "workhorse section heading" — appropriate for a utility form page like
   this one, one step down from the About/Contact authored-hero treatment
   (`.display-2`/--step-4), which this page is not. */
body.dot-attendee-details .wp-block-post-title {
	font-size: var(--step-3);
	margin-bottom: 0.5em;
}

/* ---- Page intro (client feedback 2026-07-22) ----
   Client's own words: "at the top of the page we need to include helpful
   information to let the visitor now what this step is for, otherwise, we
   are just throwing a step in front of them without any real helpful
   expectations." Previously this page had NOTHING between the H1 and
   FooEvents' own per-product heading — a visitor landing here from Cart
   had no idea why they were suddenly asked for attendee names before
   checkout. Added as real page content (WP page #789's own post_content,
   a plain paragraph before the `[fooevents_attendee_form]` shortcode),
   not hardcoded here — this file only supplies the visual treatment.

   Rendered via `[dot_attendee_intro]` (inc/fooevents-attendee-details.php)
   inside templates/page-attendee-details.html's own H1 wrapper `<div>`,
   not by `wp:post-content` directly — see that template + this file's
   "Desktop two-column layout" section below for why (a real sticky-column
   bug this replaced). Still body-class scope only, since this file must
   never leak to any other page.

   Font size reduced from --step-1 (client feedback 2026-07-22, second
   round: "the content to be bumped down in text size" once the column
   itself got narrower — see the layout section below) to --step--1,
   tokens.css's smallest defined step, reading clearly as secondary/
   supporting copy under the H1 rather than competing with it. `max-width:
   none` (removing the previous 60ch cap) is required, not cosmetic: WP's
   generated `is-layout-constrained > *` rule gives any direct child of
   the H1 wrapper `margin-inline: auto`, so a paragraph narrower than its
   column (60ch was, once the column shrank) gets auto-centered inside it
   — confirmed live via getBoundingClientRect that this, not any margin/
   padding mismatch, was the actual cause of the "title and content don't
   share a left edge" bug reported 2026-07-22. Letting the paragraph fill
   the column's actual (now narrow) width keeps it genuinely flush-left
   under the H1 at every width. */
body.dot-attendee-details .dot-attendee-details-intro {
	max-width: none;
	margin: 0 0 2rem;
	color: var(--muted);
	font-size: var(--step--1);
	line-height: 1.6;
}

/* ---- Desktop two-column layout, title/intro sticky (client feedback
   2026-07-22, two rounds) ----
   2026-09-25: sticky removed. The left column now scrolls with the page;
   the history below explains the single-wrapper structure it left behind.

   Round 1 — client's own words: "for desktop, I would like to move the
   attendee panels to the right so that the page title and the helpful
   text can become sticky as the user scrolls down the page for a better
   ux."

   Round 2 — a real bug report on round 1's first implementation, with a
   screenshot: "the attendee details on the left is doing something
   strange. The title and the content appears to be disconnected... it
   overlaps when you hit the bottom of the page and things become
   unsticky." Root cause, confirmed live by scrolling a real long attendee
   list all the way past where the right column ends: the first
   implementation made the H1 and the intro paragraph two SEPARATE
   `position: sticky` elements sharing one flattened `display: contents`
   grid cell (a `<main>`-level hack worked around the fact that
   templates/page.html's own structure puts the H1 in a wrapper that is a
   SIBLING of the entire `wp:post-content` wrapper, not a parent of it —
   real DOM, confirmed live, not just template source). Two independent
   sticky elements anchored to the same containing block, but positioned
   at different `top` offsets with different heights, do not release
   ("un-stick") at the same scroll depth — the one positioned/sized
   further down (the intro paragraph, using more of the shared cell's
   height) always runs out of "room to stick" first, so as the right
   column's content ends it slides upward and crosses the still-stuck H1.
   This isn't a tunable offset mistake, it's structural: two siblings
   sharing one sticky containing block can't be made to co-release
   without either matching their footprints exactly (fragile — both
   depend on font/content that can change independently) or making them
   one real box with one sticky rule.

   FIX: templates/page-attendee-details.html (a dedicated template,
   auto-selected by WP's standard `page-{slug}` hierarchy for this page's
   `attendee-details` slug) renders the H1 and the intro paragraph
   (`[dot_attendee_intro]`, inc/fooevents-attendee-details.php) inside ONE
   real, shared wrapper `<div>` — true DOM siblings, not a CSS trick. That
   div is now the ONLY sticky element for this whole column; H1 and the
   intro just stack inside it via normal block flow, exactly like any
   other in-flow content. There is now exactly one `position: sticky`
   rule governing this column, so there is no second element that can
   release independently and cross it — confirmed live scrolling a
   3-attendee/2-product cart all the way past the end of the right
   column: no overlap at any scroll position, clean single release at the
   natural end of the row.

   `#main-content` (this page's `<main>`) is the grid container; its two
   real children (the H1+intro wrapper, and `wp:post-content` — which now
   renders ONLY the shortcode's own output, since the content filter
   above strips the intro out of it) are its two column items directly.
   No `display: contents` needed anywhere anymore. Each wrapper still
   carries its own `has-global-padding`/`is-layout-constrained` inset from
   the template; `padding-inline`/`max-width`/`margin-inline` are reset to
   0/none on both here so the horizontal inset is established ONCE, on
   `#main-content` itself (same calc() already used for this in
   woocommerce-myaccount.css), instead of once per column (which would
   have doubled up as dead space at the column gap).

   The wrapper gets `grid-row: 1 / -1` + `align-self: start` so its own
   grid cell is as tall as `wp:post-content`'s (the only other item in the
   grid), giving it "room" to stay stuck for the right column's full
   height — the same reason the classic-checkout redesign gave
   `#order_review` an artificial `grid-row: 1 / 999` span instead of
   relying on its own natural height (woocommerce-cart-checkout.css) —
   except here there is only the one sticky item, so there's no crossing
   risk left to reason about.

   `top: 5.5rem` is this project's own already-established sitewide value
   for "clears the standard sticky header once its utility bar tucks
   away" (reused verbatim from woocommerce-myaccount.css's nav rail and
   woocommerce-cart-checkout.css's `#order_review`, both against this same
   standard header).

   Column ratio narrowed a second time (client feedback 2026-07-22, round
   2: "the column they live in to be smaller to give our details on the
   right more space"). Round 1 shipped `minmax(280px, 0.85fr) minmax(0,
   1.35fr)` (~39/61), itself the checkout's own `.woocommerce-
   checkout.checkout` ratio with sides swapped. This narrowed the left
   track further to `minmax(240px, 0.55fr) minmax(0, 1.85fr)` (~23/77) —
   comfortably fit the H1 and the (now smaller, --step--1) intro
   paragraph without cramping, confirmed live at 1024–1920px, while giving
   the attendee cards meaningfully more room, not just a marginal trim.

   Widened a third time (client feedback 2026-07-22, round 3, against the
   ticket summary card added the same day — see that section below):
   "make the panel on the left a little wider so it gives the ticket
   details a bit more space to breath." At 23% the ticket summary card's
   `.dot-ticket-summary__name` (real product names like "General
   Admission - Children") had no room to sit on one line next to its
   price even after the font-size reduction below — the column itself was
   the binding constraint, not just type size. Now `minmax(260px, 0.75fr)
   minmax(0, 1.65fr)` (~31/69) — confirmed live at 1024–1920px this still
   reads comfortably for the H1/intro (unchanged content) and gives the
   attendee-form column on the right no meaningfully worse a squeeze than
   round 2 did, while the extra ~8 points of width is what actually lets
   real ticket rows (name + qty + price) fit on one line — see the ticket
   summary card section for the font-size half of that fix. The column
   gap (`clamp(2.5rem, 5vw, 4.5rem)`) is unchanged, reused from the same
   checkout rule for the same reason as round 1 — no new value invented. ---- */
@media (min-width: 1024px) {
	body.dot-attendee-details #main-content {
		display: grid;
		grid-template-columns: minmax(260px, 0.75fr) minmax(0, 1.65fr);
		column-gap: clamp(2.5rem, 5vw, 4.5rem);
		align-items: start;
		max-width: calc(
			var(--wp--style--global--content-size, 1240px) +
			var(--wp--style--root--padding-left, 28px) +
			var(--wp--style--root--padding-right, 28px)
		);
		margin-inline: auto;
		padding-inline: var(--wp--style--root--padding-left, 28px) var(--wp--style--root--padding-right, 28px);
		box-sizing: border-box;
	}

	/* The H1 + intro wrapper (templates/page-attendee-details.html). */
	body.dot-attendee-details #main-content > .wp-block-group {
		grid-column: 1;
		grid-row: 1 / -1;
		align-self: start;
		/* Its own has-global-padding/is-layout-constrained inset is
		   superseded by #main-content's (see comment above). */
		padding-inline: 0;
		max-width: none;
		margin-inline: 0;
	}

	/* wp:post-content — now renders ONLY the shortcode's own output
	   (the content filter above strips the intro paragraph out of it). */
	body.dot-attendee-details #main-content > .wp-block-post-content {
		grid-column: 2;
		grid-row: 1;
		padding-inline: 0;
		max-width: none;
		margin-inline: 0;
	}
}

/* ---- Ticket summary card (left column, client feedback
   2026-07-22: "let's display the currently selected ticket information on
   the left hand side so that it is clear to the visitor when they
   selected") ----
   Rendered by `[dot_attendee_ticket_summary]` (inc/fooevents-attendee-
   details.php), live from WC()->cart — see that function's own header
   comment for the data-source reasoning. It renders inside the SAME
   sticky wrapper `<div>` as the H1/intro (templates/page-attendee-
   details.html), directly below the intro paragraph, so it scrolls as
   part of that one real box rather than being a second, independently
   sticky element — see the "Desktop two-column layout" section above for
   why two independent sticky siblings in this column is a real bug, not a
   hypothetical one.

   Card recipe matches this project's other cards (the .fooevents-
   attendee-item card above, Cart line-item card, My Account content card,
   #order_review panel) — hairline var(--line) border + var(--surface)
   fill + var(--r-lg) radius, no new tokens invented.

   ROW FONT SIZE / ONE-LINE FIT (client feedback 2026-07-22, round 3):
   "improve the layout for the ticket details on the left hand side so it
   can fit on one row" / "I also want to see the price and the title on
   the same line, and reduce the font size a bit but not too much, never
   go below 12px for this site." Name/qty/price previously had no
   font-size set here at all, so they inherited the page's ambient body
   size (1rem, ~16px at default scale) — combined with the narrow 23%
   column (see "Desktop two-column layout" above, now widened to 31%),
   real product names like "General Admission - Children" wrapped to 2-3
   lines while the one-line price sat at the row's baseline, visually
   disconnected from the title.

   `var(--step--1)` (tokens.css) is reused here — the same token this
   file already uses for the card heading and the variation dt/dd rows
   just below, so no new type-scale value is invented for this card.
   `--step--1` is `clamp(0.82rem, 0.78rem + 0.2vw, 0.95rem)`: at the
   default font-scale (1) and this rule's `min-width: 1024px` floor, that
   resolves to ~13.1px at 1024px width up to its 15.2px ceiling — never
   below 12px real rendered size, confirmed via getComputedStyle at
   1024/1440/1920px. Accessibility-preferences profiles that raise
   `--font-scale` above 1 only make this larger (the rem cascade through
   `html { font-size: calc(100% * var(--font-scale)) }` in tokens.css),
   so the 12px floor only needed checking at the default scale, not the
   larger ones.

   `align-items: center` (was `baseline`) on `.dot-ticket-summary__row`:
   once the name reliably fits on one line at this width/size, baseline
   and center read almost identically for two same-size one-line
   siblings, but center is the more robust choice here since it doesn't
   depend on both spans sharing the same font metrics/line-height to look
   aligned — confirmed live this doesn't shift anything visually from the
   baseline result once both are one line. */
body.dot-attendee-details .dot-ticket-summary {
	background: var(--surface);
	border: 1px solid var(--line);
	border-radius: var(--r-lg);
	padding: clamp(1.25rem, 3vw, 1.75rem);
}

body.dot-attendee-details .dot-ticket-summary__heading {
	font-size: var(--step--1);
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	color: var(--muted);
	margin: 0 0 1rem;
}

body.dot-attendee-details .dot-ticket-summary__list {
	list-style: none;
	margin: 0;
	padding: 0;
}

body.dot-attendee-details .dot-ticket-summary__item {
	padding: 0.9rem 0;
	border-top: 1px solid var(--line);
}

body.dot-attendee-details .dot-ticket-summary__item:first-child {
	padding-top: 0;
	border-top: none;
}

body.dot-attendee-details .dot-ticket-summary__row {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 0.75rem;
}

body.dot-attendee-details .dot-ticket-summary__name {
	font-size: var(--step--1);
	font-weight: 600;
	color: var(--ink);
	line-height: 1.4;
}

body.dot-attendee-details .dot-ticket-summary__qty {
	margin-left: 0.3rem;
	font-size: var(--step--1);
	font-weight: 500;
	color: var(--muted);
}

body.dot-attendee-details .dot-ticket-summary__price {
	flex-shrink: 0;
	font-size: var(--step--1);
	font-weight: 600;
	color: var(--ink);
	font-variant-numeric: tabular-nums;
	white-space: nowrap;
}

/* Variation/booking meta (a `<dl class="variation">`, straight from core's
   wc_get_formatted_cart_item_data() — same helper the classic Cart/
   Checkout review table uses). Block-level stacked rows rather than core's
   own default float layout, for the same reason as the Mini-Cart's own
   item-data fix (assets/css/woocommerce-mini-cart.css, "Item metadata
   rows" section): this column is similarly narrow (23% of the content
   width at desktop — see the "Desktop two-column layout" section above),
   so an inline "Label: value" line wraps awkwardly at that width; one row
   per entry reads far more clearly here, matching that established
   precedent rather than inventing a second, different pattern. */
body.dot-attendee-details .dot-ticket-summary .variation {
	margin: 0.5rem 0 0;
	padding: 0;
}

body.dot-attendee-details .dot-ticket-summary .variation dt,
body.dot-attendee-details .dot-ticket-summary .variation dd {
	display: block;
	float: none;
	margin: 0;
	font-size: var(--step--1);
	line-height: 1.5;
}

body.dot-attendee-details .dot-ticket-summary .variation dt {
	font-weight: 600;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	color: var(--muted);
	margin-top: 0.5rem;
}

body.dot-attendee-details .dot-ticket-summary .variation dt:first-child {
	margin-top: 0;
}

body.dot-attendee-details .dot-ticket-summary .variation dd {
	color: var(--ink);
	font-weight: 500;
}

body.dot-attendee-details .dot-ticket-summary .variation dd p {
	margin: 0;
}

/* ---- Errors (FooEvents_Attendee_Page::output_errors()) ----
   Also renders OUTSIDE #fooevents-attendee-page (output_errors() runs
   before the wrapper div opens) — body-class scope only. Plain
   `<ul class="woocommerce-error" role="alert">`, unstyled by any
   stylesheet that loads on this plain-Page surface. Same monochrome
   notice treatment already used on My Account / classic checkout. */
body.dot-attendee-details .fooevents-attendee-page-errors {
	margin: 0 0 var(--dot-fed-gap);
}

body.dot-attendee-details .woocommerce-error {
	background: var(--placeholder-base);
	color: var(--ink);
	border: 1px solid var(--line);
	border-left: 3px solid var(--accent);
	border-radius: var(--r-sm);
	padding: 1rem 1.1rem;
	margin: 0;
	list-style: none;
	font-weight: 500;
}

body.dot-attendee-details .woocommerce-error li + li {
	margin-top: 0.5rem;
}

/* WooCommerce core (twenty-twenty-three.css, unconditionally loaded on
   this page — see file header "SPECIFICITY") gives
   `.woocommerce-error[role=alert]` a decorative `::before` icon badge,
   absolutely positioned (`position: absolute; top: 1em; left: 1.5em`)
   assuming the notice's own left padding reserves a 3.5rem gutter for it.
   The plain `padding: 1rem 1.1rem` above (this page's minimal bordered-
   card notice style, matching My Account/checkout, no icon anywhere
   else) removes that gutter, so the badge renders directly on top of the
   notice text instead — confirmed live by reading getComputedStyle(li,
   '::before').content (a literal `"X"` character, from that same core
   rule's own `content: "X"` override) and by triggering a real validation
   failure: "Invalid email..." rendered with an "X" badge overlapping the
   "nva" of "Invalid", exactly the illegible overlap the client reported.
   Confirmed this page-specific: the checkout surface uses the identical
   `1rem 1.1rem` padding (woocommerce-cart-checkout.css) without this bug,
   because checkout dequeues twenty-twenty-three.css entirely (inc/
   woocommerce-classic-checkout.php's has_shortcode() gate) — the rule
   supplying this icon in the first place never loads there. This page's
   design doesn't use a decorative icon on any notice, so removing it
   outright is cleaner than re-reserving room for it. */
body.dot-attendee-details .woocommerce-error::before {
	display: none;
}

/* ---- Event group (one per ticket product with attendee capture on —
   e.g. "Factory Tours") ---- */
body.dot-attendee-details #fooevents-attendee-page .fooevents-attendee-group {
	margin: 0 0 var(--dot-fed-gap);
}

/* A second (or third) event group in the same order — a hairline section
   break, matching the checkout "Contact / Delivery / Attendee" section
   device (assets/css/woocommerce-cart-checkout.css). */
body.dot-attendee-details #fooevents-attendee-page .fooevents-attendee-group + .fooevents-attendee-group {
	margin-top: clamp(2.5rem, 6vw, 3.5rem);
	padding-top: clamp(2rem, 5vw, 2.75rem);
	border-top: 1px solid var(--line);
}

body.dot-attendee-details #fooevents-attendee-page .fooevents-attendee-group > h2 {
	font-size: var(--step-1);
	font-weight: 800;
	letter-spacing: var(--letter-spacing-display);
	margin: 0 0 1.25rem;
}

/* ---- Attendee card ("Attendee 1 – Adult", "Attendee 2 – Adult", …) ----
   Real card boundaries (surface fill + hairline border + --r-lg radius),
   matching the token values this project already settled on for cards
   (Cart line-item card / My Account content card / #order_review panel
   all use the same recipe). Vendor CSS's own `margin-bottom: 1em` on
   `.fooevents-attendee-item` is superseded by the gap below. */
body.dot-attendee-details #fooevents-attendee-page .fooevents-attendee-item {
	background: var(--surface);
	border: 1px solid var(--line);
	border-radius: var(--r-lg);
	padding: clamp(1.5rem, 4vw, 2.25rem);
	margin: 0 0 1.5rem;
}

body.dot-attendee-details #fooevents-attendee-page .fooevents-attendee-item:last-child {
	margin-bottom: 0;
}

/* "Attendee N – <Type>" heading — the trailing " – Adult"/variation text
   is plain text appended inline by FooEvents_Form::variation() (form-
   variation.php), not a separate element, so it's styled as part of the
   same heading rather than independently.

   `display: flex; align-items: center` (client feedback 2026-07-22: "the
   icon to the right of the title needs to be more vertically aligned")
   fixes the checkmark badge FooEvents CORE's own updateAttendeeStatus()
   (fooevents-attendee-page.js) appends here once an attendee's required
   fields all validate (`<span class="fooevents-attendee-status dashicons
   dashicons-yes-alt">` — vendor behavior, not built here). Confirmed live
   the cause was the dashicons font's own `vertical-align: top` (its
   stylesheet's default for every dashicon glyph) sitting the icon against
   the *line box's* top edge rather than the heading text's optical
   center once appended as trailing inline content. Flex centering fixes
   this independent of any icon-font baseline metric, rather than
   guessing at a vertical-align/translateY offset tied to this specific
   font. `flex-wrap: wrap` keeps long "Attendee N – <Type>" text
   (long custom variation names) from forcing the icon onto a clipped
   single line at narrow widths; `gap` replaces the implicit inline
   spacing the icon used to get for free. */
body.dot-attendee-details #fooevents-attendee-page .fooevents-attendee-item > h3 {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: 0.5em;
	font-size: var(--step-1);
	font-weight: 800;
	letter-spacing: var(--letter-spacing-display);
	margin: 0 0 1.25rem;
}

/* Vendor CSS gives the badge `line-height: 1.6em` inside dashicons' fixed
   20px box, so the glyph overflows below the box flex is centering, and
   its `margin-left: .5em` doubles the heading's `gap`. Sizing the box to
   the glyph lets the centering above land on the glyph itself. */
body.dot-attendee-details #fooevents-attendee-page .fooevents-attendee-status {
	width: 1em;
	height: 1em;
	line-height: 1;
	margin: 0;
}

/* Reveal micro-animation for the same checkmark (client feedback
   2026-07-22, same round: "I would like for there to be a nicer micro
   animation that happens to this icon when it appears"). The `<span>`
   doesn't exist in the DOM until the moment validation passes — a CSS
   `transition` has nothing to interpolate from on first appearance, so
   this uses an `animation` instead, which plays on initial insertion.
   Same `--motion-med`/`--ease` tokens and the same `prefers-reduced-
   motion` + this project's own `html[data-motion="reduce"]` (set by the
   accessibility-preferences plugin) dual guard already established for
   this exact kind of reveal moment (blocks/ticket-cards/style.css's
   `dotTicketCardIn`; this same file's own `.dot-copy-status` fade-in
   above) — reused rather than inventing a new motion language. */
@media (prefers-reduced-motion: no-preference) {
	body.dot-attendee-details #fooevents-attendee-page .fooevents-attendee-status {
		animation: dot-attendee-status-in var(--motion-med) var(--ease);
	}
}

@keyframes dot-attendee-status-in {
	from {
		opacity: 0;
		transform: scale(0.5);
	}

	to {
		opacity: 1;
		transform: scale(1);
	}
}

html[data-motion="reduce"] body.dot-attendee-details #fooevents-attendee-page .fooevents-attendee-status {
	animation: none !important;
}

/* ---- Booking Details sub-block (Date / Slot) ----
   Plain text on the card's own normal surface color — no tinted fill or
   bordered sub-panel. (Originally shipped as a quietly-tinted sub-panel,
   the same recipe as the checkout payment_box: placeholder-base fill +
   hairline border + --r-sm radius. Client feedback 2026-07-22: the gray
   background read as a "strange" boxed-in treatment, so the fill/border
   are dropped here — spacing and the label/value typography below are
   unchanged.) Covers both the read-only (`fooevents-booking-group`) and
   editable (`fooevents-bookings-group`) vendor markup — see file header. */
body.dot-attendee-details #fooevents-attendee-page .fooevents-booking-group,
body.dot-attendee-details #fooevents-attendee-page .fooevents-bookings-group {
	padding: 1rem 0;
	margin: 0 0 1.1rem;
}

body.dot-attendee-details #fooevents-attendee-page .fooevents-booking-group label,
body.dot-attendee-details #fooevents-attendee-page .fooevents-bookings-group label {
	display: block;
	margin-bottom: 0.5rem;
	font-size: var(--step--1);
	font-weight: 600;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	color: var(--muted);
}

body.dot-attendee-details #fooevents-attendee-page .fooevents-booking-group strong,
body.dot-attendee-details #fooevents-attendee-page .fooevents-bookings-group strong {
	font-weight: 600;
	color: var(--ink);
}

body.dot-attendee-details #fooevents-attendee-page .fooevents-booking-group br,
body.dot-attendee-details #fooevents-attendee-page .fooevents-bookings-group br {
	display: block;
	content: "";
	margin-top: 0.35rem;
}

/* Editable date/slot selects, when that variant renders. */
body.dot-attendee-details #fooevents-attendee-page .fooevents-bookings-group select {
	margin-top: 0.4rem;
}

/* ---- Form rows (First/Last/Email/Telephone/Company/Designation) ---- */
body.dot-attendee-details #fooevents-attendee-page .form-row {
	margin: 0 0 1.1rem;
	padding: 0;
}

body.dot-attendee-details #fooevents-attendee-page .form-row label {
	display: block;
	margin-bottom: 8px;
	font-size: 1.25rem;
	font-weight: 300;
	line-height: 1.3;
	color: var(--field-label);
}

body.dot-attendee-details #fooevents-attendee-page .required {
	color: var(--muted);
	text-decoration: none;
	border: 0;
}

body.dot-attendee-details #fooevents-attendee-page .input-text {
	display: block;
	width: 100%;
	min-height: 56px;
	padding: 7px 21px;
	font-size: 1.125rem;
	line-height: 1.4;
	color: var(--ink);
	background: transparent;
	border: 2pt solid var(--field-border);
	border-radius: 40px;
	box-shadow: none;
	box-sizing: border-box;
	appearance: none;
	-webkit-appearance: none;
	transition:
		border-color var(--motion-med) var(--ease),
		box-shadow var(--motion-med) var(--ease);
}

body.dot-attendee-details #fooevents-attendee-page select {
	min-height: 56px;
	padding: 7px 21px;
	font-size: 1.125rem;
	background-color: transparent;
	border: 2pt solid var(--field-border);
	border-radius: 40px;
}

body.dot-attendee-details #fooevents-attendee-page .input-text:hover {
	border-color: var(--ink);
}

/* Lit-up glow on focus (mouse or keyboard), never replacing the sitewide
   3px `:focus-visible` ring (base.css) which still lands on top for
   keyboard users — only border-color/box-shadow touched here, `outline`
   is left alone so it can't be suppressed. Matches the identical pattern
   in woocommerce-cart-checkout.css / woocommerce-myaccount.css. */
body.dot-attendee-details #fooevents-attendee-page .input-text:focus {
	border-color: var(--accent);
	box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 16%, transparent);
	outline: none;
}

body.dot-attendee-details #fooevents-attendee-page .input-text:focus-visible,
body.dot-attendee-details #fooevents-attendee-page select:focus-visible {
	outline: 3px dashed var(--focus-outline);
	outline-offset: 2px;
}

body.dot-attendee-details #fooevents-attendee-page button[name="fooevents_submit_attendees"]:focus {
	outline: 3px dashed var(--focus-outline);
	outline-offset: 2px;
}

body.dot-attendee-details #fooevents-attendee-page button:not([name="fooevents_submit_attendees"]):focus-visible,
body.dot-attendee-details #fooevents-attendee-page a:focus-visible {
	outline: 3px solid var(--focus);
	outline-offset: 2px;
}

body.dot-attendee-details #fooevents-attendee-page ::placeholder {
	color: var(--muted);
	opacity: 1;
}

/* Two-up First/Last name row on wider screens, matching WooCommerce's own
   form-row-first / form-row-last convention used elsewhere on this site
   (checkout billing/shipping fields). */
@media (min-width: 768px) {
	body.dot-attendee-details #fooevents-attendee-page .form-row-first,
	body.dot-attendee-details #fooevents-attendee-page .form-row-last {
		width: calc(50% - 0.625rem);
		float: left;
	}

	body.dot-attendee-details #fooevents-attendee-page .form-row-last {
		float: right;
	}

	body.dot-attendee-details #fooevents-attendee-page .form-row-wide {
		clear: both;
	}
}

@media (max-width: 767.98px) {
	body.dot-attendee-details #fooevents-attendee-page .form-row-first,
	body.dot-attendee-details #fooevents-attendee-page .form-row-last,
	body.dot-attendee-details #fooevents-attendee-page .form-row-wide {
		width: 100%;
		float: none;
	}
}

/* ---- "Copy attendee details" link(s) ----
   Rendered as one or two `<p class="form-row form-row-wide">` wrappers
   around `<a class="copy-btn">` (text) and/or
   `<a class="dashicons dashicons-admin-page copy-btn">` (icon) — see file
   header. `display: inline-flex` turns each wrapping `<p>` into an
   inline-level box so, when both render (the "textandicon" option value
   this store uses), they sit on the same line as a natural pair instead
   of two stacked block rows — no markup change needed to group them. */
body.dot-attendee-details #fooevents-attendee-page p.form-row:has(> a.copy-btn) {
	display: inline-flex;
	align-items: center;
	width: auto;
	/* Bottom margin tightened from 1.5rem (2026-07-22, client feedback: the
	   helper text below needs to read as directly attached to this action,
	   not a separate paragraph two lines down). */
	margin: 0 0.5rem 0.4rem 0;
	vertical-align: middle;
}

body.dot-attendee-details #fooevents-attendee-page a.copy-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 0.4rem;
	min-height: 44px; /* real, independent control — comfortable touch target */
	padding: 0.4rem 0.1rem;
	color: var(--ink);
	font-weight: 600;
	font-size: var(--step--1);
	text-decoration: underline;
	text-underline-offset: 0.15em;
	cursor: pointer;
}

body.dot-attendee-details #fooevents-attendee-page a.copy-btn:hover {
	color: var(--muted);
}

/* Icon-only variant — a small circular chip (matches the cart's own
   32px "remove" icon-button treatment) rather than a bare, unstyled
   dashicon glyph floating at font-size. */
body.dot-attendee-details #fooevents-attendee-page a.copy-btn.dashicons {
	width: 36px;
	height: 36px;
	min-height: 36px;
	padding: 0;
	font-size: 1rem;
	line-height: 1;
	color: var(--ink);
	background: var(--surface);
	border: 1px solid var(--line);
	border-radius: 9999px;
	text-decoration: none;
	transition:
		background-color var(--motion-med) var(--ease),
		color var(--motion-med) var(--ease),
		border-color var(--motion-med) var(--ease);
}

body.dot-attendee-details #fooevents-attendee-page a.copy-btn.dashicons:hover {
	color: var(--accent-ink);
	background: var(--ink);
	border-color: var(--ink);
}

/* ---- Copy helper text + live confirmation status ----
   Client feedback 2026-07-22: the copy action needed to be self-
   explanatory (what will this actually do?) before it's clicked, and its
   result needed to be real, readable text afterward — not just a visual
   flash — for anyone not looking at the destination fields the moment
   they click, screen-reader users included.

   `.dot-copy-help` is static, always-present copy rendered directly under
   the action, one per `.fooevents-attendee-group`. Its word list ("name,
   email, and phone" etc.) is built live in JS from whichever attendee
   fields THIS product actually has enabled (assets/js/fooevents-attendee-
   copy-animation.js) — never hardcode a field list here, since a
   different ticket product can have Company/Designation on and Telephone
   off.

   `.dot-copy-status` starts empty and is populated by that same script
   after every click. It's a real `role="status"` / `aria-live="polite"`
   region, already present in the DOM before any click (not injected
   together with its text) — the same "populate an already-present live
   region" pattern this project already uses for the single-product
   add-to-cart confirmation (assets/js/single-product-ajax-add-to-cart.js's
   `setSuccessMessage()` / core's own `.widget_shopping_cart_live_region`)
   — so assistive tech announces "Copied to N other attendees" the same
   moment sighted users read it. Both elements are created by that script,
   not by any PHP template (form-copy.php is vendor-owned and untouched). */
body.dot-attendee-details #fooevents-attendee-page .dot-copy-help,
body.dot-attendee-details #fooevents-attendee-page .dot-copy-status {
	margin: 0 0 1.1rem;
	font-size: var(--step--1);
	color: var(--muted);
	line-height: 1.4;
}

/* Confirmed 4.5:1+ contrast for var(--muted) at this size against
   var(--surface)/var(--bg) in all three theme variants (light ~5.3–5.6:1,
   dark ~6.8–7.4:1, high-contrast ~17:1) — computed against tokens.css's
   current values, not eyeballed. */

body.dot-attendee-details #fooevents-attendee-page .dot-copy-status:empty {
	display: none;
}

@media (prefers-reduced-motion: no-preference) {
	body.dot-attendee-details #fooevents-attendee-page .dot-copy-status.is-visible {
		animation: dot-copy-status-in var(--motion-med) var(--ease);
	}
}

@keyframes dot-copy-status-in {
	from {
		opacity: 0;
		transform: translateY(-3px);
	}

	to {
		opacity: 1;
		transform: translateY(0);
	}
}

/* ---- "Copy attendee details" tap/press micro-animation ----
   Applied to whichever `<a class="copy-btn">` (icon or text variant —
   both share this class) was actually clicked; assets/js/fooevents-
   attendee-copy-animation.js toggles `.is-copied` on just that one
   element, not its sibling.

   The scale-in/out is genuine transform motion, so — matching this
   file's existing submit-button hover-lift convention below — it's
   confined to the `no-preference` media guard. The dashicon glyph swap
   (Copy → Checkmark) and the text variant's muted-color flash are plain,
   non-moving state changes left unconditional, so reduced-motion users
   still get a real, visible confirmation ("brief color flash, no
   movement") rather than nothing. */
@media (prefers-reduced-motion: no-preference) {
	body.dot-attendee-details #fooevents-attendee-page a.copy-btn.is-copied {
		animation: dot-copy-tap var(--motion-med) var(--ease);
	}
}

@keyframes dot-copy-tap {
	0% {
		transform: scale(1);
	}

	40% {
		transform: scale(0.86);
	}

	100% {
		transform: scale(1);
	}
}

body.dot-attendee-details #fooevents-attendee-page a.copy-btn.dashicons.is-copied {
	color: var(--accent-ink);
	background: var(--ink);
	border-color: var(--ink);
}

body.dot-attendee-details #fooevents-attendee-page a.copy-btn.dashicons.is-copied::before {
	content: "\f12a"; /* dashicons-yes-alt — briefly replaces \f105 (dashicons-admin-page) */
}

body.dot-attendee-details #fooevents-attendee-page a.copy-btn.is-copied:not(.dashicons) {
	color: var(--muted);
}

/* ---- Destination-field glow ----
   The fields that just received a copied value (in every OTHER attendee
   group the copy reached) get a brief highlight — the whole point of the
   action is fields changing in places the visitor may not even be
   looking at. A box-shadow/border-color pulse only, no transform/
   translate, so it already reads as "no movement" even under reduced
   motion; left unconditional (no media guard), matching this file's
   existing input `:focus` glow above, which uses the same box-shadow
   recipe unconditionally for the same reason. */
body.dot-attendee-details #fooevents-attendee-page .input-text.dot-copy-field-highlight {
	animation: dot-copy-field-glow var(--motion-slow) var(--ease);
}

@keyframes dot-copy-field-glow {
	0% {
		border-color: var(--focus);
		box-shadow: 0 0 0 4px color-mix(in srgb, var(--focus) 35%, transparent);
	}

	70% {
		border-color: var(--focus);
		box-shadow: 0 0 0 4px color-mix(in srgb, var(--focus) 0%, transparent);
	}

	100% {
		border-color: var(--field-border);
		box-shadow: none;
	}
}

/* ---- Proceed to checkout (submit) ----
   FooEvents renders `<button class="button alt wp-element-button">`.
   base.css's own sitewide `.button, .wp-element-button { border-radius:
   9999px; … }` rule (§"WooCommerce's actual button markup") should reach
   this element already, but WooCommerce core's `.woocommerce a.button,
   .woocommerce button[type="submit"], …` rule (twenty-twenty-three.css,
   unconditionally enqueued sitewide whenever WooCommerce is active — see
   functions.php — since this page is never gated into the classic-
   checkout dequeue list) carries a `.woocommerce` ancestor-class
   specificity bump that wins over base.css's plain `.button` selector,
   which is the confirmed root cause of the "plain black" (square-ish,
   WooCommerce-default-chrome) button reported. Out-specify it directly
   with the wrapper ID, matching this theme's own documented
   WC-specificity-fight convention (base.css, woocommerce-shop.css). */
body.dot-attendee-details #fooevents-attendee-page button[name="fooevents_submit_attendees"] {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 0.5rem;
	width: 100%;
	min-height: 54px;
	padding: 0.9rem 1.5rem;
	margin-top: 0.5rem;
	font-size: var(--step-0);
	font-weight: 500;
	border-radius: 60px;
	background: var(--btn-primary-bg);
	color: var(--btn-primary-ink);
	border: 1.5px solid var(--btn-primary-bg);
	cursor: pointer;
	transition:
		transform var(--motion-med) var(--ease),
		background-color var(--motion-med) var(--ease);
}

body.dot-attendee-details #fooevents-attendee-page button[name="fooevents_submit_attendees"]:where(:not(:disabled)):hover {
	background: var(--btn-primary-hover-bg);
	color: var(--btn-primary-ink);
	border-color: var(--btn-primary-hover-bg);
	text-decoration: underline;
	text-decoration-thickness: 1px;
	text-underline-offset: 5px;
}

@media (prefers-reduced-motion: no-preference) {
	body.dot-attendee-details #fooevents-attendee-page button[name="fooevents_submit_attendees"]:hover {
		transform: translateY(-2px);
	}
}

body.dot-attendee-details #fooevents-attendee-page button[name="fooevents_submit_attendees"]:active {
	transform: scale(0.99);
}

/* The submit button's own `<p class="form-row form-row-wide">` wrapper
   needs its default form-row treatment cleared so it doesn't add an
   extra label-row gap above a control that has no label. */
body.dot-attendee-details #fooevents-attendee-page > form > p.form-row:last-of-type {
	margin-top: clamp(1.5rem, 4vw, 2rem);
}

@media (min-width: 640px) {
	body.dot-attendee-details #fooevents-attendee-page button[name="fooevents_submit_attendees"] {
		width: auto;
		min-width: 16rem;
		margin-left: auto;
	}

	body.dot-attendee-details #fooevents-attendee-page > form > p.form-row:last-of-type {
		display: flex;
		justify-content: flex-end;
	}
}

/* ---- Attendee validation-error modal (client feedback 2026-07-22) ----
   Client's own words, from a screenshot of a real triggered validation
   failure: "the error message should appear as a modal with focus trap
   so that the user can be aware of what is missing and close it to
   continue." Previously FooEvents' own error notice (see the "Errors"
   section above for exactly where/when it renders) was a small, easy-
   to-miss box with no visual connection to anything on the page.

   assets/js/fooevents-attendee-error-modal.js does the actual work: on
   page load, if `.fooevents-attendee-page-errors` is present (meaning a
   real validation failure occurred), it builds the markup this section
   styles and MOVES (never clones/discards) that exact element inside
   `.dot-attendee-error-modal__body` — so the error text/role="alert" is
   exactly FooEvents' own vendor output, only its presentation changes.
   See that file for the focus-trap / Escape / backdrop-click / return-
   focus logic; this is presentation only, not scoped to `body.dot-
   attendee-details` the way the rest of this file is, since the modal
   markup itself only ever exists on this page (the JS only builds it
   when `.fooevents-attendee-page-errors` — itself page-specific vendor
   output — is found), so an extra body-class qualifier would be
   redundant, not a safety net. */
.dot-attendee-error-modal-backdrop {
	position: fixed;
	inset: 0;
	z-index: 1000;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 1.5rem;
	background: color-mix(in srgb, var(--ink) 55%, transparent);
	box-sizing: border-box;
}

.dot-attendee-error-modal {
	width: 100%;
	max-width: 32rem;
	max-height: calc(100vh - 3rem);
	overflow-y: auto;
	background: var(--surface);
	color: var(--ink);
	border: 1px solid var(--line);
	border-radius: var(--r-lg);
	padding: clamp(1.5rem, 4vw, 2rem);
	box-shadow: 0 20px 60px color-mix(in srgb, var(--ink) 25%, transparent);
	box-sizing: border-box;
}

.dot-attendee-error-modal__header {
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	gap: 1rem;
	margin: 0 0 1rem;
}

.dot-attendee-error-modal__heading {
	font-size: var(--step-1);
	font-weight: 800;
	letter-spacing: var(--letter-spacing-display);
	margin: 0;
}

/* The vendor `.fooevents-attendee-page-errors`/`.woocommerce-error`
   styling (see the "Errors" section above) is unchanged by being moved
   in here — those selectors match on class name alone, independent of
   this new ancestor — this just removes the bottom margin that section
   gives the error block when it sits inline in the page flow, since
   inside the dialog it's already the last/only element. */
.dot-attendee-error-modal__body .fooevents-attendee-page-errors {
	margin: 0;
}

/* Close button — same circular icon-chip recipe already used for the
   "Copy attendee details" icon-only control above (36px chip, hairline
   border, filled/inverted on hover), just sized up slightly since this
   is the dialog's primary, most-likely-to-be-used-by-keyboard control
   rather than a secondary action alongside a text link. */
.dot-attendee-error-modal__close {
	flex-shrink: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	width: 2.25rem;
	height: 2.25rem;
	padding: 0;
	font-size: 1.5rem;
	line-height: 1;
	color: var(--ink);
	background: var(--placeholder-base);
	border: 1px solid var(--line);
	border-radius: 9999px;
	cursor: pointer;
	transition:
		background-color var(--motion-med) var(--ease),
		color var(--motion-med) var(--ease);
}

.dot-attendee-error-modal__close:hover {
	background: var(--ink);
	color: var(--accent-ink);
}

.dot-attendee-error-modal__close:focus-visible {
	outline: 3px solid var(--focus);
	outline-offset: 2px;
}

/* Open/close motion — instant show/hide under reduced motion (both the
   backdrop fade and the dialog's own slide+scale are real transform/
   opacity motion), matching this file's own established `prefers-
   reduced-motion: no-preference` guard convention used throughout. */
@media (prefers-reduced-motion: no-preference) {
	.dot-attendee-error-modal-backdrop {
		animation: dot-attendee-error-modal-fade var(--motion-med) var(--ease);
	}

	.dot-attendee-error-modal {
		animation: dot-attendee-error-modal-in var(--motion-med) var(--ease);
	}
}

@keyframes dot-attendee-error-modal-fade {
	from {
		opacity: 0;
	}

	to {
		opacity: 1;
	}
}

@keyframes dot-attendee-error-modal-in {
	from {
		opacity: 0;
		transform: translateY(8px) scale(0.98);
	}

	to {
		opacity: 1;
		transform: translateY(0) scale(1);
	}
}

html[data-motion="reduce"] .dot-attendee-error-modal-backdrop,
html[data-motion="reduce"] .dot-attendee-error-modal {
	animation: none !important;
}
