/**
 * GDV Public UI — shared front-end styles.
 *
 * The front-end counterpart to gdv-admin-ui. Consumer plugins opt in with
 * \GDV\PublicUi::request() and get the design tokens, the button system, the
 * modal chrome and the collapsible below.
 *
 * Scope: unlike gdv-admin-ui, nothing here is scoped to a body class. A body
 * class on the front end would repaint pages the theme owns. Component classes
 * carry their own prefix (.gdv-ui-modal, .gdv-ui-btn) and only ever match markup
 * this library or a consumer plugin drew.
 */

/* ============================================================
 * Design tokens
 *
 * Every token resolves the theme's own custom property first and falls back to
 * a literal. On a GDV site the theme's value wins, so a colour changed there
 * reaches every plugin with nothing else to edit. On a site with no such theme,
 * or anywhere the theme's stylesheet is not loaded, the literal renders instead
 * and nothing comes out unstyled.
 *
 * The fallbacks mirror the theme's own :root palette. They are named here by
 * token rather than by line number on purpose: gdv-admin-ui's header cites
 * "neve-child/style.css lines 276-303" and those lines have already moved once.
 * ============================================================ */

:root {
	--gdv-ui-primary:       var(--yellow, #e1ad4e);
	--gdv-ui-primary-hover: var(--hover, #f0605c);
	--gdv-ui-success:       var(--green, #427a7b);
	--gdv-ui-danger:        #b32d2e;
	--gdv-ui-danger-hover:  #8a2222;

	--gdv-ui-surface:       #ffffff;
	--gdv-ui-bg:            var(--back-light, #fafbf5);
	--gdv-ui-border:        var(--grey, #d6d6d6);

	/*
	A separate, darker border token for anything a person is meant to operate.

	--gdv-ui-border is right for a divider or a card edge, and too light for the
	outline of a button: at #d6d6d6 on a near-white page it lands around 1.3:1,
	where the accessibility floor for the boundary of an interactive control is
	3:1. gdv-aroma-deep-dive's citation button had already solved this locally
	with #767676 and its ratios written in the stylesheet, which is how the gap
	in this library came to light.
	*/
	--gdv-ui-border-strong: var(--grey-text, #797979);

	--gdv-ui-text:          var(--text-color, #444444);

	/*
	Faint text is a literal, not var(--grey-text).

	#666666 is 5.74:1 on #ffffff and 5.52:1 on --gdv-ui-bg, against the 4.5:1
	floor for normal text. It is also exactly what the theme renders today, so
	nothing on the front end changes colour: neve-child declares --grey-text
	twice, #797979 in the documented palette and #666666 in a later :root block
	that wins. Pinning the literal here is what stops the chain, because the
	fallback was the failing #797979 and that is the value that rendered in
	wp-admin, where the theme stylesheet is not loaded at all.

	The dark block redeclares this. A token left out of it keeps its light value
	against a dark ground, and #666666 is 2.8:1 on #212121.
	*/
	--gdv-ui-text-faint:    #666666;

	/*
	The focus ring has its own token because --gdv-ui-primary cannot serve as
	one. That token is --yellow #e1ad4e, which is 2.04:1 on #ffffff and 1.96:1
	on #fafbf5, against a 3:1 floor for a focus indicator: the ring was there
	and invisible.

	#8a6100 is the same hue carried dark enough to read: 5.54:1 on #ffffff and
	5.32:1 on #fafbf5. In dark mode the light yellow is the one that works
	(7.89:1 on #212121), so the dark block below puts it back.
	*/
	--gdv-ui-focus:         #8a6100;

	/*
	The glyph inside a round gold .gdv-ui-icon-btn, and it is white. Why it is
	white at 2.04:1, and why it is not to be darkened on its own, is written
	beside that class rather than here.
	*/
	--gdv-ui-on-primary-icon: #ffffff;

	/*
	The circle's own fill, and it is --gdv-ui-primary in light mode. It has a
	token of its own only so the dark block below can move it without moving
	every filled button with it.
	*/
	--gdv-ui-icon-btn-bg: var(--gdv-ui-primary);

	/*
	The danger button rings in its own colour. --gdv-ui-danger #b32d2e is 6.3:1
	on white and serves in light mode, but only 2.55:1 on #212121, so it needs
	the same dark-mode swap the main ring gets.
	*/
	--gdv-ui-focus-danger:  var(--gdv-ui-danger);

	--gdv-ui-radius:        8px;
	--gdv-ui-radius-lg:     12px;

	--gdv-ui-backdrop:      rgba(0, 0, 0, 0.45);
	--gdv-ui-shadow-modal:  0 24px 64px rgba(0, 0, 0, 0.25);
}

@media (prefers-color-scheme: dark) {
	:root {
		--gdv-ui-surface: var(--darkmode-bg, #212121);
		--gdv-ui-bg:      var(--darkmode-bg, #212121);
		--gdv-ui-border:  var(--darkmode-border-color, #3a3a3a);
		--gdv-ui-text:    var(--darkmode-text-color, #fcfcfc);

		/*
		Both of these invert. A token left out of this block keeps its light
		value against a dark ground, which is how the old faint grey ended up
		at 3.7:1 here while reading as acceptable in the light scheme.

		#9a9a9a is 5.72:1 on #212121. #e1ad4e is 7.89:1 on it, so the brand
		yellow that fails as a ring in light mode is the right ring in dark.
		*/
		--gdv-ui-text-faint:   #9a9a9a;
		--gdv-ui-focus:        var(--yellow, #e1ad4e);
		--gdv-ui-focus-danger: #e87572;

		/*
		The round icon button's circle goes a shade deeper here, and it is the
		one place white on our gold is not a deviation: #b88d4c carries white at
		3.02:1, over the 3:1 a glyph needs, where #e1ad4e is 2.04:1.

		Not this plugin's invention. neve-child has drawn the header bar's
		circles in it under prefers-color-scheme: dark since long before this
		class existed, and the bar is where the shape comes from, so following it
		is what keeps the two rows identical in both schemes.

		--gdv-ui-primary itself is deliberately left alone: the filled button
		stays the brand yellow in dark mode, which is a separate decision and
		belongs to the readability pass, not here.
		*/
		--gdv-ui-icon-btn-bg: #b88d4c;
	}
}

/* ============================================================
 * Buttons
 *
 * Three layers. A button carries the base, exactly one kind, and any number of
 * the optional classes:
 *
 *   base       .gdv-ui-btn
 *   kind       --primary | --secondary | --danger
 *   size       --small
 *   treatment  --dashed | --pulse
 *
 * Disabled is not a class. :disabled and [aria-disabled="true"] are styled here,
 * so nobody has to remember a modifier for it.
 * ============================================================ */

/*
**A button is as wide as its own label, at every width, and is centred.**

No full-width buttons, no percentage widths, no breakpoint where a button grows
to fill its row. There is no --block modifier and none is to be added: the moment
one exists it gets used and the rule stops holding.

max-width: 100% is the one cap, and it resolves against the parent, so a label
too long for a narrow phone wraps inside the button instead of pushing the page
sideways. That limits a button, it never stretches one.

margin-inline: auto centres a button that sits alone in its own row. A button
inside a flex or grid row is positioned by that row instead, which is why the
rule is a margin and not a text-align on the parent: it does nothing in a
toolbar and everything under a form.

The theme this ships beside sets width: 90% on the bare button element at its
phone breakpoint, with a selector carrying eight classes' worth of specificity.
Nothing here can outrank that, so neve-child excludes .gdv-ui-btn by name once
and permanently. See style.css:8301 there.
*/
/*
display: flex with width: max-content, not inline-flex.

An inline-flex box ignores `margin-inline: auto`: auto margins compute to zero on
an inline-level box, so the button would sit wherever its container happened to
put it, which is the left. A block-level flex box sized to max-content is as wide
as its label and can be centred by its own margins, with no cooperation from the
parent. That is what makes "content width and centred" hold everywhere rather
than only where somebody remembered to centre the container.

The cost is that a lone button takes its own line. Two buttons side by side go in
a .gdv-ui-btn-row, which is what that class is for, and inside it they become
flex items again and sit together.
*/
/*
**Why every button selector repeats its class.**

An attribute selector counts at class level, so `input[type=submit]` is one
element plus one class, which outranks a single `.gdv-ui-btn` class. Neve styles
form controls exactly that way and wins twice over:

  input[type=submit] { background: var(--primarybtnbg); color: ...; padding: ...;
                       border-radius: ...; font-weight: ... }
  input[type=submit] { display: inline-block; appearance: none; ... }

The first paints the button in the parent theme's colours. The second is worse
than it looks: `display: inline-block` makes `margin-inline: auto` compute to
zero, so the button stops centring, which is the one rule this library exists to
hold. Their :hover variants are one step higher again, so the kinds' hover states
have to clear that too.

Repeating the class lifts every declaration here above both, with no !important
and without naming Neve. A consumer that genuinely needs to override still can:
two classes is an ordinary weight to beat, unlike !important.

This applies to any `<input type="submit">` or `<input type="button">` carrying
.gdv-ui-btn, in the theme or in any plugin, not to one screen.
*/
.gdv-ui-btn.gdv-ui-btn {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 0.5em;

	width: max-content;
	max-width: 100%;
	margin-inline: auto;

	padding: 0.7em 1.4em;
	border: 1px solid transparent;
	border-radius: var(--gdv-ui-radius);

	font-family: inherit;
	font-size: 1rem;
	font-weight: 600;
	line-height: 1.3;
	text-align: center;
	text-decoration: none;
	white-space: normal;

	cursor: pointer;
	transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}

/* A row of buttons is centred as a group, and wraps rather than overflowing. */
.gdv-ui-btn-row {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: center;
	gap: 0.75rem;
}

/* Inside a row the buttons are placed by the row, so each one gives up its own
centring. Without this every button would also try to centre itself in the space
the flex layout gave it, which reads as uneven gaps. */
.gdv-ui-btn-row .gdv-ui-btn.gdv-ui-btn {
	margin-inline: 0;
}

/* A call to action: a line saying what the buttons under it are for, then the
buttons.

The line has to be centred with them or it reads as belonging to whatever is
above it: a button centres itself and a paragraph does not, so left alone the
sentence sits hard against the content edge while its own buttons sit in the
middle. That is what it did on the Aroma Café page.

It lives here rather than in the plugin that draws it because the buttons already
do, and because a consumer rendering on a page for a visitor who is not signed in
has no stylesheet of its own loaded there to put it in. */
.gdv-ui-cta {
	text-align: center;

	/*
	A quiet panel, so the three parts read as one thing and not as three loose
	paragraphs adrift in whatever page they were dropped into. Built from the
	tokens above and nothing else: the page's own soft background, the ordinary
	border, the ordinary radius. No shadow and no accent colour, because this is
	a block of text with buttons in it and not a dialog.

	Content-width and centred, capped so a long line does not run the width of a
	wide page. A page that wants it wider can say so itself.
	*/
	max-width: 38rem;
	margin-inline: auto;
	padding: 1.5rem;
	background: var(--gdv-ui-bg);
	border: 1px solid var(--gdv-ui-border);
	border-radius: var(--gdv-ui-radius);
}

/* The lead keeps a paragraph's own rhythm and gives the row a gap to sit in.
Not a margin on the row, which would also apply where a consumer draws buttons
with no line above them. */
.gdv-ui-cta__lead {
	margin: 0 0 1rem;
}

/* The fact the block opens on: what taking part costs, or what state this reader
is already in. Quieter than the lead under it, because it is the condition rather
than the instruction, and a reader who does not need it should be able to skip
past it to the sentence that tells them what to do. */
.gdv-ui-cta__state {
	margin: 0 0 0.5rem;
	color: var(--gdv-ui-text-faint);
	font-size: 0.9375rem;
}

.gdv-ui-btn:focus {
	outline: none;
}

.gdv-ui-btn:focus-visible {
	outline: 2px solid var(--gdv-ui-focus);
	outline-offset: 2px;
}

/* One disabled look for every kind. aria-disabled as well as the attribute: a
link cannot be :disabled, and a button that must stay focusable for a screen
reader is disabled the same way. */
.gdv-ui-btn.gdv-ui-btn:disabled,
.gdv-ui-btn.gdv-ui-btn[aria-disabled="true"] {
	opacity: 0.5;
	cursor: not-allowed;
	pointer-events: none;
}

/*
The main action. Filled, and the only loud button on a screen.

**White label, his decision of 2026-09-10**, so a filled button reads the same
here as everywhere else on the site. The label stays white on hover too, where
the fill turns red.

**The contrast is known to be low and is being revisited separately.** White on
#e1ad4e measures about 1.9:1, against the 4.5:1 body-size text usually needs, and
white on the hover red #f0605c about 3.5:1. neve-child had previously patched its
auth buttons to a dark label by id for this reason, with the ratios written
beside the rule. Reversing it here is deliberate and the readability pass is
booked: see item 22 of _plans/plan-theme-adopts-shared-buttons.md. Do not
"correct" this back without that decision.

The fix, when it comes, is most likely a darker fill rather than a dark label:
white on the theme's green #427a7b measures about 4.7:1, which is the pairing
this same button already uses in dark mode.
*/
.gdv-ui-btn.gdv-ui-btn--primary {
	background: var(--gdv-ui-primary);
	border-color: var(--gdv-ui-primary);
	color: #ffffff;
}

.gdv-ui-btn.gdv-ui-btn--primary:is(:hover, :focus, :active) {
	background: var(--gdv-ui-primary-hover);
	border-color: var(--gdv-ui-primary-hover);
	color: #ffffff;
}

/* The alternative beside it. Outlined, so the pair reads as one choice. The
border is the strong token: this outline is the control's boundary, not a
divider, and has to stay visible against a near-white page. */
.gdv-ui-btn.gdv-ui-btn--secondary {
	background: transparent;
	border-color: var(--gdv-ui-border-strong);
	color: var(--gdv-ui-text);
}

.gdv-ui-btn.gdv-ui-btn--secondary:is(:hover, :focus, :active) {
	background: rgba(0, 0, 0, 0.04);
	border-color: var(--gdv-ui-text);
	color: var(--gdv-ui-text);
}

/*
**The quietest button on the screen, never the loudest.**

Prominence pulls the eye and the click, so it has to run opposite to
consequence: this is the button that deletes something for good, and a filled
red one advertises itself to everybody who was not looking for it. No fill, and
muted text.

**It does have a border, since 0.5.4.** It had none, and that went one step too
far: with no fill and no outline, a button in a column of text renders as a
sentence. It was read as one on the My Events screen twice over, and the OK in
this library's own confirm dialog, which is --danger, looked like a caption
beside the way out. Quiet is a matter of degree; being a button at all is not.
The outline is the soft --gdv-ui-border rather than the --gdv-ui-border-strong
that --secondary carries, so the two stay ranked: the way out is the firmer
outline, going ahead is the fainter one.

The colour appears on hover and focus only, where it tells somebody who has
already reached for the control what it is, at the moment that is useful to
them. The weight of the decision belongs in the confirmation that follows, and
in the words on it, not in the trigger.
*/
.gdv-ui-btn.gdv-ui-btn--danger {
	background: transparent;
	border-color: var(--gdv-ui-border);
	color: var(--gdv-ui-text-faint);
	font-weight: 400;
}

.gdv-ui-btn.gdv-ui-btn--danger:is(:hover, :focus, :active) {
	background: transparent;
	border-color: var(--gdv-ui-danger);
	color: var(--gdv-ui-danger);
}

.gdv-ui-btn--danger:focus-visible {
	outline-color: var(--gdv-ui-focus-danger);
}

/* Row actions and toolbars, where a full-size button would crowd the row. */
.gdv-ui-btn.gdv-ui-btn--small {
	padding: 0.45em 0.9em;
	font-size: 0.875rem;

	/*
	**The button stays small. Its touch target does not.**

	At 0.875rem, line-height 1.3 and 0.45em padding this box is about 33px tall,
	under the 44px a finger needs. Growing the box to 44px would delete the only
	reason the modifier exists, so the hit area is grown instead of the button:
	the ::after below is a transparent overlay that reaches 44px in both
	directions and takes the tap.

	position: relative is what the overlay is positioned against, so it belongs
	here rather than in the base class.
	*/
	position: relative;
}

/*
position: absolute, not a flex child. The base button is display: flex with a
0.5em gap, so an in-flow pseudo-element would be treated as a second flex item
and open a gap beside the label. An absolutely positioned child is out of flow
and is not a flex item, so the button looks exactly as it did.

transform rather than the translate property, for the older browsers that still
reach the public site.
*/
.gdv-ui-btn.gdv-ui-btn--small::after {
	content: "";
	position: absolute;
	top: 50%;
	left: 50%;
	transform: translate(-50%, -50%);
	width: 100%;
	height: 100%;
	min-width: 44px;
	min-height: 44px;
}

/*
"Add one of these": a placeholder that reads as an empty slot rather than as an
action already taken. From gdv-planner, shared because any plugin with a list
somebody extends wants it.

The dashed border replaces the kind's own, so --secondary --dashed is the
expected pairing and --primary --dashed is not.
*/
.gdv-ui-btn.gdv-ui-btn--dashed {
	border-style: dashed;
	border-color: var(--gdv-ui-border);
	background: transparent;
	color: var(--gdv-ui-text-faint);
	font-weight: 400;
}

.gdv-ui-btn.gdv-ui-btn--dashed:is(:hover, :focus, :active) {
	border-color: var(--gdv-ui-primary);
	color: var(--gdv-ui-text);
}

/*
One call to action asking to be noticed. From gdv-cowrite, shared because it is
a pattern rather than a decoration for one screen.

Deliberately a slow, small pulse on the shadow rather than on the size: animating
width or transform moves the layout around it and, on a button, invites a mis-tap
on a phone.

Never more than one on a page. Two things pulsing is neither of them noticed.
*/
@keyframes gdv-ui-btn-pulse {
	0%,
	100% {
		box-shadow: 0 0 0 0 rgba(225, 173, 78, 0.55);
	}

	50% {
		box-shadow: 0 0 0 0.5rem rgba(225, 173, 78, 0);
	}
}

.gdv-ui-btn--pulse {
	animation: gdv-ui-btn-pulse 2.4s ease-out infinite;
}

/* Motion that repeats forever is the kind this setting exists for. The button
keeps working and simply stops moving. */
@media (prefers-reduced-motion: reduce) {
	.gdv-ui-btn--pulse {
		animation: none;
	}
}

/* ============================================================
 * Icon button
 *
 * A round gold circle with a white glyph in it: the control that is an icon
 * and nothing else. The site's own header bar is where this shape comes from,
 * neve-child's .hdr-iconbtn and .hdr-hamburger, and a plugin drawing one on a
 * page under that bar should read as the same control rather than as a near
 * miss of it.
 *
 *   <button type="button" class="gdv-ui-icon-btn" aria-label="Close">
 *     <svg …>
 *   </button>
 *
 * The accessible name is the consumer's job and is not optional: there is no
 * text in here for a screen reader to fall back on, so every one of these
 * carries aria-label, and title as well where a mouse should be able to name
 * it. An SVG inside is aria-hidden.
 *
 * It is not a .gdv-ui-btn variant. That family is a pill that sizes itself to
 * a label and centres itself in its row; this is a fixed square box with no
 * label at all, and every declaration it needs is one the pill would have to
 * undo.
 *
 * Not for a control that carries a word. A word on this gold needs 4.5:1 and
 * white does not reach it, so a labelled control is .gdv-ui-btn--primary.
 * ============================================================ */

/*
**The glyph is white, and the contrast is a known deviation.**

White on --gdv-ui-primary, the theme's --yellow #e1ad4e, is 2.04:1, under the
3:1 an icon carrying meaning is meant to hold. It is white anyway, and that is
a decision rather than an oversight: his call of 2026-09-10, that a filled gold
control carries a white mark everywhere on the site, recorded beside
.gdv-ui-btn--primary further up this file. The readability pass that settles it
is item 22 of _plans/plan-theme-adopts-shared-buttons.md, and its answer is a
darker fill rather than a dark glyph, so it belongs to --yellow and not here.

**Do not darken this glyph on its own.** Doing so pulls these circles out of
step with the six in the site header, which are white on the same gold, and
that mismatch is what a reader sees. It has been reversed once already, in
gdv-chat on 2026-09-19.

The value is --gdv-ui-on-primary-icon, declared with the other tokens at the top
of this file rather than here: this sheet keeps one :root and a second one is
how a token ends up with two values. It is not folded into
.gdv-ui-btn--primary's own #ffffff, which carries a "do not correct this back"
note of its own and is a word rather than a glyph.
*/

/*
The size is forced, and this is the defence, not decoration.

A bare <button> is styled by whatever theme the consumer is rendering inside,
with that theme's padding and, on a narrow screen, a width the plugin cannot
see coming. As a flex item that refuses to shrink, such a button takes the
whole row it sits in: gdv-chat's header cross drew as a gold ellipse across the
bar and squeezed the title into a column of single words, on the phone and in
any browser set to a phone width.

min-width and max-width are what actually settle it. A flex item takes
min-width: auto, which is its content plus that padding, and a min-content
wider than the width wins over the width. aspect-ratio keeps the circle if both
ever fail.

The class is repeated for weight, the same trick .gdv-ui-btn uses above and for
the same reason: Neve reaches these at two classes and a repeated class clears
it without !important and without naming Neve, so a consumer can still override
at two classes of its own.

font-size is reset because it is what every em below is measured against, and a
theme that sizes its dialog crosses in rem hands this box a number it never
asked for.

**A consumer that needs a different size sets --gdv-ui-icon-btn-size, never a
width.** The repeated class puts the width out of reach of an ordinary rule, and
a consumer answering that with more classes or !important is how a shared
control stops being shared. The two properties that legitimately vary by place,
the diameter and the glyph inside it, are custom properties for that reason.
Everything else is the same everywhere on purpose: one circle, one gold, one
white mark, whatever room it is in.
*/
.gdv-ui-icon-btn.gdv-ui-icon-btn {
	--gdv-ui-icon-btn-size: 2.1em;
	--gdv-ui-icon-btn-glyph: 1.05em;

	display: inline-flex;
	align-items: center;
	justify-content: center;
	box-sizing: border-box;
	flex: 0 0 auto;

	width: var(--gdv-ui-icon-btn-size);
	min-width: var(--gdv-ui-icon-btn-size);
	max-width: var(--gdv-ui-icon-btn-size);
	height: var(--gdv-ui-icon-btn-size);
	aspect-ratio: 1 / 1;
	padding: 0;

	background: var(--gdv-ui-icon-btn-bg);
	border: 1px solid transparent;
	border-radius: 50%;
	color: var(--gdv-ui-on-primary-icon);

	font-size: 1em;
	line-height: 1;
	/* For the <a> form of this control, which a theme underlines like any other
	   link. There is no word in here for an underline to sit under. */
	text-decoration: none;
	cursor: pointer;
	transition: background-color 0.15s ease, color 0.15s ease;
	/*
	No shadow and no lift on hover.

	The site header's circles carried both until 0.5.11 and the chat panel's
	never did, which is the difference that made two rows of the same control
	read as two different controls. One of the two had to go and it is this one:
	a drop shadow under a 34px circle is decoration the control does not need,
	and a transform that moves a button on hover invites a mis-tap on the phone
	where the same circle is a touch target.
	*/
	box-shadow: none;
	transform: none;
}

/*
The glyph, at a size the box actually holds.

An inline SVG does not size itself from the button's font-size the way an icon
font did, so without this it arrives at the browser's default 24px and overruns
the circle. Lucide strokes are currentColor, so the colour needs no rule of its
own and follows the button.
*/
.gdv-ui-icon-btn > svg {
	flex: 0 0 auto;
	width: var(--gdv-ui-icon-btn-glyph, 1.05em);
	height: var(--gdv-ui-icon-btn-glyph, 1.05em);
	color: inherit;
	pointer-events: none;
}

/*
Coral on the way in, on hover alone.

:focus is deliberately not in this list. A dialog hands focus to the first
control inside it the moment it opens, and browsers count that as keyboard
navigation, so a cross that took coral on focus showed its way out in the
colour that means "pressing this" before anybody had touched it. Keyboard focus
shows through the ring below instead.

White on the coral #f0605c is 3.21:1, which clears the 3:1 an icon needs, so
the hover state is the one pairing here that is not a deviation.
*/
.gdv-ui-icon-btn.gdv-ui-icon-btn:hover {
	background: var(--gdv-ui-primary-hover);
	color: var(--gdv-ui-on-primary-icon);
}

/*
The ring, and only for somebody who reached the control from the keyboard.

--gdv-ui-focus is the dark gold #8a6100, the ring every other control in this
file takes. The offset is what makes it work here: 2px of clearance puts the
whole ring on the surface behind the button rather than on the circle, where it
is 5.54:1 against white and clears the 3:1 a focus indicator needs. Drawn
against the gold itself it would be 2.71:1, which is why it is offset and not
merely outlined.
*/
.gdv-ui-icon-btn:focus {
	outline: none;
}

.gdv-ui-icon-btn:focus-visible {
	outline: 2px solid var(--gdv-ui-focus);
	outline-offset: 2px;
}

/*
Hidden means hidden. The sizing above carries a repeated class, and a consumer
that toggles one of these with the hidden attribute needs at least that weight
or the button is drawn anyway: gdv-chat had the gear and the back chevron on
screen at once for exactly this reason.
*/
.gdv-ui-icon-btn[hidden] {
	display: none;
}

/*
On a coarse pointer the circle is 2.1em, around 34px, and the floor for a
finger is 44px. The box is not grown, because the row it sits in is measured
for the circle; an invisible ::after centred on it takes the press instead.

position: relative is on the button for it to sit in, and a consumer that needs
the button positioned for its own reasons already has to reckon with that.
*/
@media (pointer: coarse) {
	.gdv-ui-icon-btn.gdv-ui-icon-btn {
		position: relative;
	}

	.gdv-ui-icon-btn::after {
		content: '';
		position: absolute;
		top: 50%;
		left: 50%;
		width: 44px;
		height: 44px;
		transform: translate(-50%, -50%);
	}
}

/* ============================================================
 * Collapsible
 *
 * A section a reader opens and closes for themselves. Searchable under the
 * other names this pattern goes by: show/hide, disclosure, details.
 *
 *   <details class="gdv-ui-collapsible">
 *     <summary class="gdv-ui-collapsible__summary">Sources (5)</summary>
 *     …
 *   </details>
 *
 * The word is the codebase's own: gdv-faq-manager has rendered show/hide on
 * the front end since long before this library existed and calls it
 * collapsible throughout, down to a public shortcode attribute somebody types
 * into a page (collapsible="true"). A second word for one pattern reads as two
 * patterns.
 *
 * Not an accordion. An accordion is a group where opening one panel closes its
 * siblings, and nothing here does that. The word stays unused so it is free
 * the day a real one is needed.
 *
 * Markup is the browser's own <details>, so the open and closed state costs no
 * JavaScript, survives a cached page (the state lives in the browser, never in
 * the HTML), and works for a reader who has scripting switched off. A consumer
 * whose markup cannot be a <details> is not served by this class; a button
 * carrying aria-expanded would take a __toggle part, which is again the FAQ
 * manager's existing word.
 * ============================================================ */

/*
No display on the summary, and this is the whole trap.

display: flex or grid on a <summary> removes the disclosure triangle in Chrome,
silently, with nothing on the screen to say the affordance has gone. neve-child
paid for that once and wrote it down at style.css:8596. So the summary stays a
list-item, which is what the browser makes it, and the marker is coloured
rather than replaced: no icon file, nothing to keep in step, and the arrow the
reader already knows from every other <details> on the web.
*/
.gdv-ui-collapsible__summary {
	cursor: pointer;
	color: var(--gdv-ui-text-faint);
}

.gdv-ui-collapsible__summary::marker {
	color: var(--gdv-ui-text-faint);
}

/* Safari draws its own marker and ignores ::marker on a summary. */
.gdv-ui-collapsible__summary::-webkit-details-marker {
	color: var(--gdv-ui-text-faint);
}

/*
Open, the label steps up to the reading colour: it stops being an invitation
and becomes the heading of what is now on the screen under it.
*/
.gdv-ui-collapsible[open] > .gdv-ui-collapsible__summary {
	color: var(--gdv-ui-text);
}

.gdv-ui-collapsible__summary:hover {
	color: var(--gdv-ui-text);
}

/* The same ring every focusable thing in this library uses. A summary is
   focusable by keyboard, so leaving this out would lose the only sign of where
   the keyboard is. */
.gdv-ui-collapsible__summary:focus-visible {
	outline: 2px solid var(--gdv-ui-focus);
	outline-offset: 2px;
}

/*
On paper it is open, whatever the reader left it at.

A collapsible exists to keep a screen readable, and a sheet of paper is not
being scrolled: content printed missing is content lost, with no control on the
page to open it. Both declarations are needed because browsers hide the closed
content two different ways: ::details-content with content-visibility is the
current mechanism, and display on the children is what the older one answers
to.
*/
@media print {
	.gdv-ui-collapsible::details-content {
		content-visibility: visible;
	}

	.gdv-ui-collapsible > *:not(summary) {
		display: block;
	}

	/* The marker goes with it. A closed arrow printed above content that is
	   plainly there says the opposite of what the page shows, and on paper it
	   points at nothing a reader can press. The label stays: it is the only
	   thing naming what follows. */
	.gdv-ui-collapsible__summary {
		list-style: none;
	}

	.gdv-ui-collapsible__summary::-webkit-details-marker {
		display: none;
	}
}

/* ============================================================
 * Modal
 *
 * Generic chrome only. Callers style their own form content inside
 * .gdv-ui-modal__body. Drawn by assets/js/public-ui-modal.js.
 * ============================================================ */

.gdv-ui-modal {
	max-width: 480px;
	width: 88vw;
	padding: 0;
	border: 1px solid var(--gdv-ui-border);
	border-radius: var(--gdv-ui-radius-lg);
	box-shadow: var(--gdv-ui-shadow-modal);
	background: var(--gdv-ui-bg);
	color: var(--gdv-ui-text);
	/*
	A dialog taller than the screen scrolls inside itself. Without this it is
	simply cut off at the bottom edge and whatever sits at the foot of it cannot
	be reached at all, while the wheel goes through to the page behind, which is
	how gdv-chat's settings sheet lost its two erase controls: they were still
	drawn, below the cut.

	Stated here rather than left to the browser, which applies its own cap only
	to a dialog opened as a modal and only where nothing else has spoken. Every
	consumer of this class gets the behaviour, and a dialog that fits the screen
	is unaffected.

	dvh first, with vh under it for a browser that does not know the unit: a
	phone's address bar slides away and 100vh is the taller of the two heights,
	so the foot of a full-height sheet sits under the bar until you scroll.

	overscroll-behavior stops the page behind from taking over once the sheet is
	scrolled to its end, which is what makes a modal feel like one.
	*/
	max-height: calc(100vh - 4em);
	max-height: calc(100dvh - 4em);
	overflow-y: auto;
	overscroll-behavior: contain;
}

.gdv-ui-modal::backdrop {
	background: var(--gdv-ui-backdrop);
}

.gdv-ui-modal__form {
	display: flex;
	flex-direction: column;
	gap: 20px;
	padding: 28px;
	margin: 0;
}

.gdv-ui-modal__message {
	margin: 0;
	font-size: 1rem;
	line-height: 1.5;
	white-space: pre-line;
}

/*
A modal's actions row behaves exactly like .gdv-ui-btn-row: centred as a group,
wrapping rather than overflowing. The properties are repeated here rather than
the class being required in the markup, because consumer plugins already render
this class on their own dialogs and their layout must not depend on remembering
a second one.

Centred, not right-aligned as it was in the theme. That is the standing rule for
every button on the site, and it changes gdv-chat's preferences sheet from
right-aligned to centred the first time this loads.
*/
.gdv-ui-modal__actions {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: center;
	gap: 0.75rem;
}

.gdv-ui-modal__actions .gdv-ui-btn {
	margin-inline: 0;
}

.gdv-ui-modal__close {
	position: absolute;
	top: 16px;
	right: 16px;
	display: flex;
	align-items: center;
	justify-content: center;
	width: 32px;
	height: 32px;
	padding: 0;
	background: transparent;
	border: none;
	border-radius: 50%;
	font-size: 1.4rem;
	line-height: 1;
	cursor: pointer;
	color: var(--gdv-ui-text-faint);
}

.gdv-ui-modal__close:hover {
	background: var(--gdv-ui-border);
}

.gdv-ui-modal__close:focus-visible {
	outline: 2px solid var(--gdv-ui-focus);
	outline-offset: 2px;
}

.gdv-ui-modal__body {
	position: relative;
	padding: 36px 32px 32px;
}
