/**
 * The shared modal engine, styles.
 *
 * ── WHY THIS IS IN CORE AND NOT IN THE MODAL PLUGIN ──────────────────────
 *
 * Five of the twenty-four generators emit modals: Modal, Quote-Callout,
 * Tab-Content-With-Modal, List-With-Images-Panel and Chart-Block. In generator
 * form each one carried its own copy of the overlay CSS and the open/close
 * script, and the way a tool got a modal it had not generated itself was the
 * "bridge": you generated a modal in one tool, copied the markup, and pasted it
 * into a field in another tool.
 *
 * THE BRIDGE DOES NOT EXIST IN PLUGIN FORM AND MUST NOT BE RECREATED.
 *
 * A plugin does not paste markup into another plugin. Every tool that wants a
 * modal calls this engine directly: core owns the overlay, the runtime, the
 * focus handling and the styles, and the tool supplies only its content. So a
 * fix to the focus trap lands once rather than five times, and two different
 * blocks opening modals on the same page share one overlay implementation
 * rather than fighting over the scroll lock.
 *
 * Plain CSS rather than SCSS on purpose: core's build step compiles the editor
 * bundle only, and a stylesheet that needs no build is one less thing between
 * a bug and its fix.
 *
 * Geometry ported from Modal-generator.html output, verified against a real
 * generated modal rather than against the generator source.
 */

.petit-modal {
	display: none;
	position: fixed;
	inset: 0;
	z-index: 9999;
	align-items: center;
	justify-content: center;
	padding: 20px;
	background: rgba(30, 20, 50, 0.55);
	box-sizing: border-box;
}

.petit-modal.is-open {
	display: flex;
}

/*
 * Immune to a theme's constrained layout.
 *
 * WordPress gives every direct child of post content a max-width and
 * auto side margins. A tool that prints its modal next to its own wrapper
 * (rather than inside it) makes the overlay one of those children, and a
 * fixed, inset:0 element with a max-width and auto margins shrinks to a
 * centred strip instead of covering the screen. Doubling the class beats
 * the theme's selector on specificity, and the values only ever restate
 * what a full-screen overlay already means, so nothing legitimate is lost.
 */
.petit-modal.petit-modal {
	max-width: none !important;
	margin: 0 !important;
}

/*
 * Two variables, not one, and the split matters.
 *
 * --petit-modal-refs-set is what the block chose and never changes.
 * --petit-modal-refs-size is what actually gets used, derived from it, so the
 * short-screen query can reduce it without overwriting the choice. Redefining
 * a custom property in terms of itself is circular and simply does not apply,
 * which is a quiet way to lose a setting.
 */
/*
 * Both wrappers, because the editor's is not .petit-modal.
 *
 * On the page the overlay carries the class; in the editor the block renders
 * into .petit-modal-editor instead. The derived variable was only declared on
 * the first, so changing the reference size in the editor visibly did nothing
 * and then worked correctly once published, which is the worst way round.
 */
.petit-modal,
.petit-modal-editor {
	--petit-modal-refs-size: var(--petit-modal-refs-set, 85);
}

.petit-modal__panel {
	position: relative;
	display: flex;
	flex-direction: column;
	width: var(--petit-modal-w, 960px);
	max-width: 100%;
	/* A custom height (Modal engine, Custom size) scrolls inside the body. */
	height: var(--petit-modal-h, auto);
	max-height: 88vh;
	overflow: hidden;
	border-radius: 20px;
	background: var(--petit-modal-bg, #FFFFFF);
	box-shadow: 0 24px 72px rgba(30, 20, 50, 0.22);
	box-sizing: border-box;
}

.petit-modal__close {
	position: absolute;
	top: 14px;
	right: 16px;
	z-index: 10;
	flex-shrink: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	width: 32px;
	height: 32px;
	border: none;
	border-radius: 50%;
	background: rgba(0, 0, 0, 0.09);
	color: var(--petit-modal-x, #555);
	font-size: 16px;
	cursor: pointer;
	transition: background 0.15s;
}

.petit-modal__close:hover {
	background: rgba(0, 0, 0, 0.18);
}

.petit-modal__body {
	flex: 1;
	overflow-y: auto;
	padding: 22px 32px 20px;
	box-sizing: border-box;
	display: flex;
	flex-direction: column;
}

/* ── Header band ──────────────────────────────────────────────────────── */

.petit-modal__badge {
	display: inline-block;
	/*
	 * 27% is the generator's own value, and it is why the badge reads as a bar
	 * rather than a pill: a fixed proportion of the modal with its text on the
	 * left, so a short word leaves a long tail of gradient. Kept as the default
	 * because changing it would redraw every page already using it, and offered
	 * as a setting instead.
	 */
	width: 27%;
	padding: 5px 11px;
	margin-bottom: 8px;
	border-radius: 5px;
	text-align: left;
	font-size: var(--petit-modal-badge, 15px);
	font-weight: 800;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	box-sizing: border-box;
}

.petit-modal__badge--fit {
	width: auto;
}

.petit-modal__title {
	margin: 0 0 8px;
	font-size: var(--petit-modal-title, 28px);
	font-weight: 800;
	line-height: 1.15;
}

/*
 * Gradient text, guarded.
 *
 * background-clip: text with a transparent fill shows NOTHING at all where it
 * is unsupported, so the fill is only made transparent inside the @supports
 * block. Without the guard an unsupported browser gets an invisible title,
 * which is the worst possible failure for the one line that says what the
 * modal is about.
 */
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
	.petit-modal__title--gradient {
		-webkit-background-clip: text;
		background-clip: text;
		-webkit-text-fill-color: transparent;
	}
}

.petit-modal__intro {
	margin: 0 0 10px;
	font-size: var(--petit-modal-intro, 0.95em);
	font-weight: 600;
	line-height: 1.55;
	color: var(--petit-modal-i, var(--petit-c2, #04222C));
}

.petit-modal__divider {
	border: none;
	border-top: 2px solid var(--petit-modal-rule, #E4322E);
	margin: 0 0 14px;
}

.petit-modal__head--center { text-align: center; }
.petit-modal__head--right { text-align: right; }

/* A centred or right header used to force the badge to Fit. Width and position
   are separate settings now, and the shell writes the layout inline. */

.petit-modal__head--center .petit-modal__divider,
.petit-modal__head--right .petit-modal__divider {
	width: 48%;
}

.petit-modal__head--center .petit-modal__divider {
	margin-left: auto;
	margin-right: auto;
}

.petit-modal__head--right .petit-modal__divider {
	margin-left: auto;
}

/* ── Rows and columns ─────────────────────────────────────────────────── */

/*
 * A row is a horizontal band across the modal. Rows stack down it.
 * A column is a vertical division inside one row.
 *
 * The old engine had one grid for the whole modal, which is why a real
 * generated modal contains `modal-ctn-text-push` with `margin-top: 5%`: a
 * class that exists only to nudge one column's content down so two images do
 * not line up across the gap. That is hand-tuning vertical alignment because
 * the two columns are independent stacks. With rows, an image and its text sit
 * in the same row and align because they are in the same row, and the push
 * class is not needed at all.
 */
.petit-modal__row {
	display: grid;
	gap: 24px;
	margin-bottom: 14px;
}

.petit-modal__row:last-child {
	margin-bottom: 0;
}

.petit-modal__row--1 {
	grid-template-columns: 1fr;
	/* width: 100% matters. The row is a flex child with auto side margins, and
	   an auto-margined flex child shrinks to its content, which made a single
	   column exactly as wide as its widest paragraph. A picture block has no
	   such width, so it came out about 300px wide in a 960px modal. */
	width: 100%;
	max-width: 800px;
	margin-left: auto;
	margin-right: auto;
}

/* Single column controls on the row: how wide the column may be, and where it sits. */
.petit-modal__row--1.petit-modal__row--cw-narrow { max-width: 560px; }
.petit-modal__row--1.petit-modal__row--cw-wide { max-width: 960px; }
.petit-modal__row--1.petit-modal__row--cw-full { max-width: none; }
.petit-modal__row--1.petit-modal__row--al-left { margin-left: 0; margin-right: auto; }

/*
 * Fit to content: the modal is as wide as what is in it. The panel takes the
 * width of its widest row (never wider than the screen, never narrower than
 * 420px), so a picture card sets the size of the modal instead of floating in
 * the middle of a larger one. The column steps out of the way, and text gets a
 * reading width so a modal of only words does not stretch across the screen.
 */
.petit-modal--fit .petit-modal__panel {
	width: max-content;
	min-width: min(420px, 100%);
	max-width: 100%;
}
.petit-modal--fit .petit-modal__row--1 { max-width: none; }
.petit-modal--fit .petit-modal__col > p,
.petit-modal--fit .petit-modal__col > ul,
.petit-modal--fit .petit-modal__col > ol,
.petit-modal--fit .petit-modal__col > h2,
.petit-modal--fit .petit-modal__col > h3,
.petit-modal--fit .petit-modal__col > h4,
.petit-modal--fit .petit-modal__col > blockquote { max-width: 60ch; }

/* Divider: core's Separator block in a column. Default is the generator's soft
   lilac line; a colour picked from the palette overrides it. */
.petit-modal__col hr.wp-block-separator {
	border: none;
	border-bottom: 2.5px solid #e4ddef;
	margin: 20px 0;
	opacity: 1;
	width: 100%;
	max-width: none;
	background: none;
	height: 0;
}

.petit-modal__col hr.wp-block-separator.has-background {
	border-bottom-color: currentColor;
	background: none;
}

.petit-modal__row--2 { grid-template-columns: 1fr 1fr; }
.petit-modal__row--60 { grid-template-columns: 1.5fr 1fr; }
.petit-modal__row--40 { grid-template-columns: 1fr 1.5fr; }
.petit-modal__row--70 { grid-template-columns: 2.33fr 1fr; }
.petit-modal__row--30 { grid-template-columns: 1fr 2.33fr; }

/* The only asymmetric ratio anywhere in the toolkit, ported to the decimal. */
.petit-modal__row--3 { grid-template-columns: 0.90fr 1.2fr 0.85fr; }

.petit-modal__col {
	display: flex;
	flex-direction: column;
	gap: 8px;
	min-width: 0;
}

/* ── Content inside a column ──────────────────────────────────────────── */

/*
 * The modal's look, applied to ordinary blocks.
 *
 * An earlier draft left these out on the grounds that InnerBlocks handles the
 * content. That was wrong: InnerBlocks handles the structure, but everything
 * that makes a modal look like a modal rather than like a page lives here.
 * Without it a modal is plain theme text in a rounded box.
 *
 * Scoped under .petit-modal__col so it cannot leak onto the page, and written
 * against core block classes so an editor inserting a normal paragraph gets
 * the modal treatment without choosing anything.
 */

.petit-modal__col p {
	margin: 0 0 10px;
	font-size: 0.95em;
	line-height: 1.58;
	color: var(--petit-modal-b, var(--petit-c2, #04222C));
}

.petit-modal__col p:last-child {
	margin-bottom: 0;
}

.petit-modal__col ul,
.petit-modal__col ol {
	margin: 0 0 10px;
	padding-left: 20px;
	font-size: 0.95em;
	line-height: 1.58;
	color: var(--petit-modal-b, var(--petit-c2, #04222C));
}

/* A heading in a column is the generator's subheading: small, heavy, accent
   coloured. Not a page heading, which is why the sizes do not scale by level. */
.petit-modal__col h2,
.petit-modal__col h3,
.petit-modal__col h4,
.petit-modal__col h5,
.petit-modal__col h6 {
	margin: 4px 0 0;
	font-size: 0.95em;
	font-weight: 800;
	line-height: 1.3;
	color: var(--petit-modal-h, var(--petit-modal-accent, #E4322E));
}

/*
 * Images keep their own proportions.
 *
 * The generator uses width: 100%; height: auto; object-fit: unset, so 171px is
 * a minimum height for the area and not a box the image is cropped into. An
 * earlier draft of this cropped, which is wrong: a tall photograph in a modal
 * column should get taller, not lose its top and bottom.
 */
.petit-modal__col figure {
	margin: 0;
}

.petit-modal__col img {
	display: block;
	width: 100%;
	height: auto;
	object-fit: unset;
	border-radius: 12px;
}

.petit-modal__col figure.is-style-petit-modal-media {
	display: flex;
	align-items: center;
	justify-content: center;
	min-height: 171px;
	overflow: hidden;
	border-radius: 12px;
}

.petit-modal__col figure.is-style-petit-modal-media--sm {
	min-height: 145px;
	max-height: 145px;
}

/* A quote in a column, matching the generator's own. */
.petit-modal__col blockquote {
	display: flex;
	flex-direction: column;
	gap: 8px;
	margin: 0;
	padding-left: 4px;
	border: none;
}

.petit-modal__col blockquote::before {
	content: "\201C";
	font-family: Georgia, serif;
	font-size: 4em;
	line-height: 1;
	letter-spacing: -4px;
	/* -15%, and the mark is #E8A0B0, a soft pink. Both taken from a real
	   generated modal rather than from the generator source, where an earlier
	   reading of mine had them as -10% and the accent orange. */
	margin-bottom: -15%;
	color: var(--petit-modal-quote, #E8A0B0);
}

.petit-modal__col blockquote p {
	margin: 0;
	font-size: 0.92em;
	font-weight: 700;
	line-height: 1.5;
	color: var(--petit-modal-q, var(--petit-c2, #04222C));
}

.petit-modal__col blockquote cite {
	margin: 2px 0 0;
	font-size: 0.86em;
	font-weight: 800;
	font-style: normal;
	color: var(--petit-modal-cn, var(--petit-modal-accent, #E4322E));
}

.petit-modal__col blockquote cite span {
	display: block;
	font-size: 0.93em;
	font-weight: 400;
	color: var(--petit-modal-cr, var(--petit-c2, #04222C));
	line-height: 1.45;
}

/* ── References ───────────────────────────────────────────────────────── */

/*
 * Two placements, both available at once, which is how the generator worked
 * and not a choice between them. A references list can sit in any column next
 * to the text it supports, and a footer band can sit below every row for
 * sources covering the whole modal.
 */
/*
 * Size and colour are settings, not constants.
 *
 * The size is a PERCENTAGE of the modal's body text rather than an em value,
 * because the modal already scales by em and the short-screen query shrinks
 * everything again. An absolute size would fight both; a percentage rides
 * along with them.
 *
 * One setting drives both placements, the footer band and a references list
 * inside a column, because they are the same kind of content. Two settings for
 * one thing is how the Stats generator ended up with three different number
 * sizes in a single row.
 */
.petit-modal__refs {
	padding-top: 12px;
	border-top: 1px solid #E4CFC4;
	font-size: calc(var(--petit-modal-refs-size, 85) * 0.01em);
	line-height: 1.5;
	color: var(--petit-modal-refs-colour, #666666);
}

.petit-modal__refs p {
	margin: 0 0 6px;
	padding-left: 20px;
	/* The hanging indent, so a wrapped reference lines up under its text
	   rather than under its number. */
	text-indent: -20px;
	font-size: 1em;
	color: inherit;
}

.petit-modal__refs--footer {
	margin-top: 16px;
	flex-shrink: 0;
}

/* ── Triggers ─────────────────────────────────────────────────────────── */

/*
 * The trigger, as a link. Colour and weight are variables so a tool can style
 * its own trigger without a second class or a fight over specificity.
 */
.petit-modal-trigger {
	color: var(--petit-modal-trigger, #E4322E);
	font-family: inherit;
	font-size: var(--petit-modal-trigger-size, inherit);
	font-weight: var(--petit-modal-trigger-weight, 700);
	text-decoration: none;
	cursor: pointer;
}

.petit-modal-trigger:hover {
	text-decoration: underline;
}

/*
 * A phrase inside body text marked as a trigger.
 *
 * The same class a button uses, so the two look alike deliberately, but a span
 * is not focusable by default and the runtime listens for clicks on anything
 * carrying the attribute. tabindex and a role make it reachable by keyboard,
 * which the generator's inline anchor got for free and a span does not.
 */
span.petit-modal-trigger {
	text-decoration: underline;
	text-underline-offset: 2px;
}

/* A button element styled as a link, for triggers that are not anchors. */
button.petit-modal-trigger {
	padding: 0;
	border: none;
	background: none;
	font-family: inherit;
	font-size: var(--petit-modal-trigger-size, inherit);
	font-weight: var(--petit-modal-trigger-weight, 700);
}

/*
 * The button variant, written with the base class repeated.
 *
 * `.petit-modal-trigger--button` alone ties with `.petit-modal-trigger` on
 * specificity, so whichever stylesheet happens to load second wins the text
 * colour. That is how the button came out orange on red: the link colour won
 * the argument and nobody could see the label. Repeating the base class ends
 * the argument without reaching for !important.
 */
.petit-modal-trigger.petit-modal-trigger--button {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	padding: 12px 26px;
	border: none;
	border-radius: 60px;
	background: var(--petit-modal-trigger, #E4322E);
	/* Computed from the background, not chosen, so a pale button cannot end up
	   with white text on it. */
	color: var(--petit-modal-trigger-on, #FFFFFF);
	font-size: var(--petit-modal-trigger-size, 0.85rem);
	font-weight: var(--petit-modal-trigger-weight, 700);
	letter-spacing: 0.05em;
	text-transform: uppercase;
	text-decoration: none;
	cursor: pointer;
	transition: opacity 0.2s;
}

.petit-modal-trigger.petit-modal-trigger--button:hover {
	opacity: 0.85;
	text-decoration: none;
}

/* ── Short screens ────────────────────────────────────────────────────── */

/*
 * A height query, not a width one, and it was nearly missed.
 *
 * It does not appear anywhere in the generator's control set: it is emitted
 * unconditionally and only turned up when a real generated modal was read. A
 * panel capped at 88vh on a 700px-tall laptop has very little room, so eleven
 * values shrink at once. Ported exactly.
 */
@media (max-height: 920px) {
	.petit-modal__panel { max-height: 90vh; }
	.petit-modal__body { padding: 16px 28px; }
	/* The same proportions as before (24/28, 0.82/0.95, 9/15), now of the chosen sizes. */
	.petit-modal__title { font-size: calc(var(--petit-modal-title, 28px) * 0.857143); }
	.petit-modal__intro { font-size: calc(var(--petit-modal-intro, 0.95em) * 0.863158); margin-bottom: 8px; }
	.petit-modal__badge { font-size: calc(var(--petit-modal-badge, 15px) * 0.6); padding: 4px 9px; margin-bottom: 6px; }
	.petit-modal__divider { margin: 0 0 10px; }
	.petit-modal__row { gap: 18px; }
	.petit-modal__col { gap: 6px; }
	.petit-modal__col p,
	.petit-modal__col ul,
	.petit-modal__col ol { font-size: 0.80em; line-height: 1.52; }
	.petit-modal__col h2,
	.petit-modal__col h3,
	.petit-modal__col h4 { font-size: 0.82em; margin-top: 2px; }
	.petit-modal__col blockquote p { font-size: 0.86em; }
	.petit-modal__col blockquote::before { margin-bottom: -10%; }
	.petit-modal__col figure.is-style-petit-modal-media { min-height: 126px; }
	/*
	 * Padding only, not size.
	 *
	 * This query used to set the references to 0.85em outright, which would
	 * silently overwrite whatever size had been chosen the moment somebody
	 * opened the page on a laptop. A relative reduction is applied to the
	 * variable instead, so a chosen size still means something here.
	 */
	.petit-modal__refs { padding-top: 8px; }
	.petit-modal__refs--footer { margin-top: 10px; }
	.petit-modal,
	.petit-modal-editor { --petit-modal-refs-size: calc(var(--petit-modal-refs-set, 85) * 0.94); }
}

@media (max-width: 960px) {
	.petit-modal__panel { max-height: 92vh; }

	/* Three columns become two before they become one, the same two-stage
	   collapse the rest of the toolkit uses. */
	.petit-modal__row--3 { grid-template-columns: 1fr 1fr; }
}

@media (max-width: 700px) {
	.petit-modal__row--2,
	.petit-modal__row--3,
	.petit-modal__row--60,
	.petit-modal__row--40,
	.petit-modal__row--70,
	.petit-modal__row--30 {
		grid-template-columns: 1fr;
	}

	.petit-modal__refs { margin-top: 0; }
}

@media (max-width: 650px) {
	.petit-modal__body { padding: 18px 16px; }
	.petit-modal__title { font-size: calc(var(--petit-modal-title, 28px) * 0.785714); }
	.petit-modal__intro { font-size: calc(var(--petit-modal-intro, 0.95em) * 0.894737); }
}

/* ── Editor affordances ───────────────────────────────────────────────── */

/*
 * Editor only, by construction rather than by a guard.
 *
 * In the editor useBlockProps adds `wp-block-petit-modal-col` alongside our
 * own class; on the front end render.php writes `petit-modal__col` and nothing
 * else. So a selector naming the WordPress class can never match a published
 * page, and no is-admin check is needed.
 */
.wp-block-petit-modal-col {
	/* An empty column is a flex box with no content and no height, which makes
	   it invisible and impossible to click into. */
	min-height: 64px;
	padding: 6px;
	border: 1px dashed #D6D3DE;
	border-radius: 4px;
}

.wp-block-petit-modal-col:hover {
	border-color: #A8A2C0;
}

/*
 * A row in the editor needs the same treatment, but only its outline: without
 * it a row with two empty columns reads as two floating dashed boxes with no
 * indication that they belong together.
 */
.wp-block-petit-modal-row {
	padding: 6px;
	border-radius: 6px;
}

.wp-block-petit-modal-row:hover {
	outline: 1px dashed #C6BEF7;
}

/* ── Dark backgrounds ─────────────────────────────────────────────────── */

/*
 * The text colours above are dark blues chosen for a light panel. A modal
 * whose background is dark gets this class (core works it out from the colour
 * with the same luminance test that picks black or white text on a button,
 * and the editor adds it to its preview), and these switch the copy, the
 * quote, the rules and the close button to light values. Accent colours and
 * the badge and title fills are left as they are.
 */
.petit-modal--dark .petit-modal__close {
	background: rgba(255, 255, 255, 0.16);
	color: var(--petit-modal-x, #F3F0FA);
}

.petit-modal--dark .petit-modal__close:hover {
	background: rgba(255, 255, 255, 0.28);
}

.petit-modal--dark .petit-modal__intro,
.petit-modal--dark .petit-modal__intro {
	color: var(--petit-modal-i, #FFFFFF);
}

.petit-modal--dark .petit-modal__col blockquote p {
	color: var(--petit-modal-q, #FFFFFF);
}

.petit-modal--dark .petit-modal__col p,
.petit-modal--dark .petit-modal__col ul,
.petit-modal--dark .petit-modal__col ol {
	color: var(--petit-modal-b, #E3DEF0);
}

.petit-modal--dark .petit-modal__col blockquote cite span {
	color: var(--petit-modal-cr, #E3DEF0);
}

.petit-modal--dark .petit-modal__col hr.wp-block-separator:not(.has-background) {
	border-bottom-color: rgba(255, 255, 255, 0.28);
}

.petit-modal--dark .petit-modal__refs,
.petit-modal--dark .petit-refs--divider {
	border-top-color: rgba(255, 255, 255, 0.25);
}

/* Links in body text follow the Modal engine's link colour only when one is
   set (the class is added by the engine), so an untouched modal keeps the
   theme's own link colour. */
.petit-modal--links .petit-modal__col a:not(.wp-block-button__link) {
	color: var(--petit-modal-l);
}
