/**
 * 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 modal chrome and the design tokens 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);
	--gdv-ui-text-faint:    var(--grey-text, #797979);

	--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);
	}
}

/* ============================================================
 * 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;
}

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

.gdv-ui-btn:focus-visible {
	outline: 2px solid var(--gdv-ui-primary);
	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, no
border, muted text.

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: transparent;
	color: var(--gdv-ui-text-faint);
	font-weight: 400;
}

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

.gdv-ui-btn--danger:focus-visible {
	outline-color: var(--gdv-ui-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;
}

/*
"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;
	}
}

/* ============================================================
 * 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);
}

.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-primary);
	outline-offset: 2px;
}

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