/**
 * Voodoo Contact Panel
 *
 * Everything is scoped under .voodoo-panel or .voodoo-backdrop. Themes vary wildly in
 * how aggressively they style form controls with broad element selectors, so every
 * control here sets its own values explicitly rather than inheriting whatever the
 * theme last said.
 *
 * The palette is entirely custom properties: retheme a client site by overriding the
 * block below in the theme stylesheet, without touching a single selector.
 */

.voodoo-panel,
.voodoo-backdrop,
.voodoo-fab {
	/*
	 * Palette. --voodoo-accent is overridden inline from the settings screen when an
	 * accent colour has been set there. The value below is a deliberately neutral
	 * slate so an unconfigured panel looks unbranded rather than wrong: it is what
	 * shows until a client colour is entered. #1F2933 on white is 13.6:1.
	 */
	--voodoo-accent: #1F2933;
	--voodoo-accent-hover: #131a1f;                 /* fallback: accent darkened 10% */
	--voodoo-accent-contrast: #ffffff;
	--voodoo-ink: #11181c;
	--voodoo-ink-muted: #4a5568;
	--voodoo-surface: #ffffff;

	/*
	 * The panel's own background, kept separate from --voodoo-surface because that also
	 * paints the input fields: setting one colour must not silently recolour the other.
	 * Overridden inline from the settings screen when a background colour is set there.
	 */
	--voodoo-panel-bg: var(--voodoo-surface);
	--voodoo-border: #cbd2d9;
	--voodoo-border-strong: #8c96a0;
	--voodoo-error: #b32d2e;
	--voodoo-error-surface: #fdf2f2;
	--voodoo-backdrop-colour: rgba(0, 0, 0, 0.5);

	/* Geometry */
	--voodoo-width: 460px;
	--voodoo-fab-size: 56px;
	--voodoo-fab-gap: 1.25rem;
	--voodoo-radius: 10px;
	--voodoo-gap: 1.25rem;
	--voodoo-pad: clamp(1.25rem, 4vw, 2rem);

	/* Motion */
	--voodoo-duration: 280ms;
	--voodoo-easing: cubic-bezier(0.32, 0.72, 0, 1);

	/*
	 * Near the top of the stacking range, because the panel is a modal dialog and must
	 * sit above sticky headers, cookie banners and chat widgets on any client site.
	 * Deliberately not 2147483647: leaving headroom means a genuinely higher-priority
	 * overlay can still be placed above it without an integer-overflow arms race.
	 * Override --voodoo-z in the theme if a client site needs something different.
	 */
	--voodoo-z: 2147483000;
}

/*
 * color-mix gives an exact 10% darken where supported; browsers without it keep the
 * hand-picked --voodoo-accent-hover above. Declared second so it wins when parsed.
 */
@supports (color: color-mix(in srgb, red 90%, black)) {
	.voodoo-panel,
	.voodoo-backdrop,
	.voodoo-fab {
		--voodoo-accent-hover: color-mix(in srgb, var(--voodoo-accent) 90%, #000);
	}
}

/* ---------------------------------------------------------------------------
 * Backdrop
 * ------------------------------------------------------------------------ */

.voodoo-backdrop {
	position: fixed;
	inset: 0;
	z-index: var(--voodoo-z);
	background: var(--voodoo-backdrop-colour);
	opacity: 0;
	transition: opacity var(--voodoo-duration) var(--voodoo-easing);
}

.voodoo-backdrop.is-open {
	opacity: 1;
}

/* ---------------------------------------------------------------------------
 * Panel shell
 *
 * Mobile first: a bottom sheet under 768px, a right-edge drawer above it.
 * ------------------------------------------------------------------------ */

.voodoo-panel {
	position: fixed;
	z-index: calc(var(--voodoo-z) + 1);
	display: flex;
	flex-direction: column;
	box-sizing: border-box;
	background: var(--voodoo-panel-bg);
	color: var(--voodoo-ink);
	font-family: inherit;
	font-size: 16px;                                /* never below 16px: iOS zooms on focus */
	line-height: 1.5;
	text-align: left;
	box-shadow: 0 0 40px rgba(0, 0, 0, 0.25);
	overflow-y: auto;
	overscroll-behavior: contain;                   /* stop scroll chaining to the page */
	-webkit-overflow-scrolling: touch;

	/* Bottom sheet */
	inset: auto 0 0 0;
	max-height: 90vh;
	max-height: 90dvh;                              /* dvh excludes mobile browser chrome */
	border-radius: var(--voodoo-radius) var(--voodoo-radius) 0 0;
	transform: translateY(100%);
	transition: transform var(--voodoo-duration) var(--voodoo-easing);
}

.voodoo-panel.is-open {
	transform: translateY(0);
}

/* [hidden] must beat display:flex — the panel is hidden until JS opens it. */
.voodoo-panel[hidden],
.voodoo-backdrop[hidden] {
	display: none !important;
}

@media (min-width: 768px) {
	.voodoo-panel {
		inset: 0 0 0 auto;
		width: 100%;
		max-width: var(--voodoo-width);
		max-height: none;
		height: 100%;
		border-radius: 0;
		transform: translateX(100%);
	}

	.voodoo-panel.is-open {
		transform: translateX(0);
	}
}

/* Instant, no motion. Opacity and transform still resolve; only the tween goes. */
@media (prefers-reduced-motion: reduce) {
	.voodoo-backdrop,
	.voodoo-panel {
		transition: none;
	}
}

/* ---------------------------------------------------------------------------
 * Views
 * ------------------------------------------------------------------------ */

.voodoo-panel__view {
	padding: var(--voodoo-pad);
	padding-top: calc(var(--voodoo-pad) + 1.5rem);  /* clear the close button */
	/* Respect notches and home indicators on iOS. */
	padding-bottom: calc(var(--voodoo-pad) + env(safe-area-inset-bottom, 0px));
}

.voodoo-panel__view[hidden] {
	display: none;
}

.voodoo-panel__heading {
	margin: 0 0 0.5rem;
	font-size: clamp(1.5rem, 4vw, 1.875rem);
	font-weight: 700;
	line-height: 1.2;
	color: var(--voodoo-ink);
}

/*
 * The subheading is admin-authored rich text run through wpautop, so it arrives as one
 * or more <p> elements inside this wrapper — and it is where a client's phone number or
 * email address goes when they want one in the panel. Style the anchors here rather
 * than leaving them to the theme: a tel: link the theme has styled as body text does
 * not read as something you can tap.
 */
.voodoo-panel__sub {
	margin: 0 0 var(--voodoo-gap);
	padding-bottom: var(--voodoo-gap);
	border-bottom: 1px solid var(--voodoo-border);
	color: var(--voodoo-ink-muted);
}

.voodoo-panel__sub > :first-child {
	margin-top: 0;
}

.voodoo-panel__sub > :last-child {
	margin-bottom: 0;
}

.voodoo-panel__sub p {
	margin: 0 0 0.5rem;
}

.voodoo-panel__sub a {
	color: var(--voodoo-accent);
	font-weight: 600;
	text-decoration: underline;
	/* Keep a number or address intact: wrap the line before it, not through it. */
	white-space: nowrap;
}

.voodoo-panel__sub a:hover,
.voodoo-panel__sub a:focus {
	color: var(--voodoo-accent-hover);
}

/* ---------------------------------------------------------------------------
 * Close button
 * ------------------------------------------------------------------------ */

.voodoo-close {
	position: absolute;
	top: 0.5rem;
	right: 0.5rem;
	z-index: 2;
	display: flex;
	align-items: center;
	justify-content: center;
	width: 44px;                                    /* brief: 44px minimum tap target */
	height: 44px;
	margin: 0;
	padding: 0;
	border: 0;
	border-radius: 50%;
	background: transparent;
	color: var(--voodoo-ink-muted);
	cursor: pointer;
	-webkit-appearance: none;
	appearance: none;
}

.voodoo-close:hover {
	background: rgba(0, 0, 0, 0.06);
	color: var(--voodoo-ink);
}

/* ---------------------------------------------------------------------------
 * Form controls
 *
 * CF7 wraps every field in <span class="wpcf7-form-control-wrap">, which is inline by
 * default and would collapse the layout.
 * ------------------------------------------------------------------------ */

.voodoo-panel .voodoo-field {
	margin-bottom: var(--voodoo-gap);
}

.voodoo-panel .voodoo-field label {
	display: block;
	margin-bottom: 0.375rem;
	font-size: 0.9375rem;
	font-weight: 600;
	color: var(--voodoo-ink);
}

/*
 * Optional label hiding.
 *
 * Moved out of sight, never removed and never display:none — either of those would take
 * the label out of the accessibility tree too, leaving a screen reader to announce five
 * anonymous edit boxes. This is the standard visually-hidden recipe: the element still
 * has a size and a position, so assistive technology still reads it, but nothing about
 * it is drawn.
 *
 * The doubled root keeps it above the base label rule, which sets `display: block`.
 */
.voodoo-panel.voodoo-panel--hidden-labels .voodoo-field label {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	overflow: hidden;
	clip: rect(0 0 0 0);
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

.voodoo-panel .wpcf7-form-control-wrap {
	display: block;
}

/*
 * CF7 puts _wpcf7, _wpcf7_version and friends in a fieldset. CF7's own stylesheet
 * hides it, but a theme resetting fieldset borders can bring it back as an empty
 * bordered box above the first field — caught in preview. Hidden here so it cannot
 * depend on load order or on CF7's stylesheet being present at all.
 */
.voodoo-panel .hidden-fields-container {
	display: none;
	margin: 0;
	padding: 0;
	border: 0;
}

/*
 * Structural only, and always applied. These four properties are what stop a control
 * overflowing the panel or losing its own line — they are layout, not decoration, and a
 * theme has no business deciding them inside a fixed-width drawer.
 *
 * The font-size is here rather than under the override for a functional reason: iOS
 * Safari zooms the viewport when a focused input is under 16px, and in a position:fixed
 * panel that zoom drags the field out of view and cannot be undone by the visitor.
 */
.voodoo-panel input[type="text"],
.voodoo-panel input[type="email"],
.voodoo-panel input[type="tel"],
.voodoo-panel select,
.voodoo-panel textarea {
	display: block;
	box-sizing: border-box;
	width: 100%;
	max-width: 100%;
	margin: 0;
	font-family: inherit;
	font-size: 16px;
}

/*
 * Everything below is the panel's own appearance for form controls, and applies only
 * when "Override theme input styles" is ticked. Off — the default — the theme's own
 * form styling shows through untouched, which is what makes the panel look native to
 * the site it is on.
 *
 * The doubled class is deliberate: this has to out-specify theme rules, which is the
 * entire reason someone would switch it on.
 */
.voodoo-panel.voodoo-panel--styled-inputs input[type="text"],
.voodoo-panel.voodoo-panel--styled-inputs input[type="email"],
.voodoo-panel.voodoo-panel--styled-inputs input[type="tel"],
.voodoo-panel.voodoo-panel--styled-inputs select,
.voodoo-panel.voodoo-panel--styled-inputs textarea {
	min-height: 48px;
	padding: 0.6875rem 0.875rem;
	border: 1px solid var(--voodoo-border-strong);
	border-radius: 6px;
	background-color: var(--voodoo-surface);
	color: var(--voodoo-ink);
	line-height: 1.4;
	-webkit-appearance: none;
	appearance: none;
}

/* Native select arrow, drawn rather than imported so there is no asset request. */
.voodoo-panel.voodoo-panel--styled-inputs select {
	padding-right: 2.5rem;
	background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none' stroke='%234a5568' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M5 8l5 5 5-5'/%3E%3C/svg%3E");
	background-repeat: no-repeat;
	background-position: right 0.75rem center;
	background-size: 20px 20px;
}

.voodoo-panel.voodoo-panel--styled-inputs input::placeholder,
.voodoo-panel.voodoo-panel--styled-inputs textarea::placeholder {
	color: var(--voodoo-ink-muted);
	opacity: 1;                                     /* Firefox dims placeholders by default */
}

.voodoo-panel.voodoo-panel--styled-inputs input:focus,
.voodoo-panel.voodoo-panel--styled-inputs select:focus,
.voodoo-panel.voodoo-panel--styled-inputs textarea:focus {
	border-color: var(--voodoo-accent);
	outline: 2px solid var(--voodoo-accent);
	outline-offset: 1px;
}

/*
 * Focus rings are restyled, never removed (SPEC.md §8) — and never left to the theme
 * either, which is why this one is outside the override. :focus-visible keeps it off
 * mouse clicks. A theme with its own focus style simply gets both, which is harmless;
 * a theme with none would otherwise leave the panel unusable by keyboard.
 *
 * Note the descendant combinator: this deliberately does not match the panel itself.
 */
.voodoo-panel :focus-visible {
	outline: 2px solid var(--voodoo-accent);
	outline-offset: 2px;
}

/*
 * The dialog container takes focus on open (see panel.js) so the panel is announced
 * without the keyboard springing up over it. That is a programmatic focus on something
 * the visitor cannot tab to, so a ring round the whole panel would mark nothing they
 * can act on — the same reasoning as the confirmation heading further down, and the
 * only other exception to the rule above.
 */
.voodoo-panel:focus,
.voodoo-panel:focus-visible {
	outline: none;
}

.voodoo-panel .voodoo-consent {
	margin: 0 0 var(--voodoo-gap);
	font-size: 0.8125rem;
	line-height: 1.5;
	color: var(--voodoo-ink-muted);
}

.voodoo-panel .voodoo-consent a {
	color: var(--voodoo-accent);
	text-decoration: underline;
}

/* ---------------------------------------------------------------------------
 * Buttons
 * ------------------------------------------------------------------------ */

/*
 * Buttons keep only their cursor and box model by default. Everything that makes them
 * look like *this* plugin's buttons sits under the override, so out of the box the
 * submit button is the theme's button and matches every other button on the site.
 */
.voodoo-panel .voodoo-submit,
.voodoo-panel .voodoo-button {
	box-sizing: border-box;
	font-family: inherit;
	cursor: pointer;
}

.voodoo-panel .voodoo-submit:disabled,
.voodoo-panel .voodoo-button:disabled {
	cursor: default;
	opacity: 0.75;
}

.voodoo-panel.voodoo-panel--styled-buttons .voodoo-submit,
.voodoo-panel.voodoo-panel--styled-buttons .voodoo-button {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 0.5rem;
	width: 100%;
	min-height: 48px;                               /* 44px minimum touch target, SPEC.md §8 */
	margin: 0;
	padding: 0.75rem 1.25rem;
	border: 1px solid transparent;
	border-radius: 6px;
	background-color: var(--voodoo-accent);
	color: var(--voodoo-accent-contrast);
	font-size: 1rem;
	font-weight: 700;
	line-height: 1.25;
	text-align: center;
	text-decoration: none;
	-webkit-appearance: none;
	appearance: none;
	transition: background-color 150ms ease;
}

.voodoo-panel.voodoo-panel--styled-buttons .voodoo-submit:hover,
.voodoo-panel.voodoo-panel--styled-buttons .voodoo-button:hover {
	background-color: var(--voodoo-accent-hover);
}

.voodoo-panel.voodoo-panel--styled-buttons .voodoo-button--secondary {
	background-color: transparent;
	border-color: var(--voodoo-border-strong);
	color: var(--voodoo-ink);
}

.voodoo-panel.voodoo-panel--styled-buttons .voodoo-button--secondary:hover {
	background-color: rgba(0, 0, 0, 0.04);
	color: var(--voodoo-ink);
}

/*
 * Sending state.
 *
 * Brief §4 asks for "a spinner inside the button". CF7 renders [submit] as
 * <input type="submit">, which is a replaced element: it cannot contain a child, and
 * ::before/::after do not render on it. That rules out a nested spinner node, and
 * rotating the control itself would tumble the label with it.
 *
 * So: a static spinner glyph as a background image, with the label padded clear of it,
 * and a slow opacity pulse to signal activity. Replacing [submit] with a <button> is
 * the only way to get a rotating spinner, and CF7 offers no tag that emits one.
 */
/*
 * The pulse works on any background, so it runs whatever the button is styled by. The
 * spinner glyph is drawn in white for the accent background and would be invisible on a
 * pale theme button, so it is confined to the override.
 */
.voodoo-panel .voodoo-submit.is-submitting {
	animation: voodoo-pulse 900ms ease-in-out infinite;
}

.voodoo-panel.voodoo-panel--styled-buttons .voodoo-submit.is-submitting {
	padding-left: 2.75rem;
	background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23ffffff' stroke-width='2.5' stroke-linecap='round'%3E%3Cpath d='M12 3a9 9 0 1 0 9 9' opacity='0.9'/%3E%3Cpath d='M12 3a9 9 0 0 1 9 9' opacity='0.35'/%3E%3C/svg%3E");
	background-repeat: no-repeat;
	background-position: left 1rem center;
	background-size: 20px 20px;
}

@keyframes voodoo-pulse {
	0%,
	100% {
		opacity: 1;
	}

	50% {
		opacity: 0.62;
	}
}

@media (prefers-reduced-motion: reduce) {
	/*
	 * Matched at the same specificity as the rule that sets the transition, which lives
	 * under the button override — a plain ".voodoo-panel .voodoo-submit" here would lose
	 * to it and silently ignore the visitor's motion preference.
	 */
	.voodoo-panel.voodoo-panel--styled-buttons .voodoo-submit,
	.voodoo-panel.voodoo-panel--styled-buttons .voodoo-button {
		transition: none;
	}

	.voodoo-panel .voodoo-submit.is-submitting {
		animation: none;
		opacity: 0.75;
	}
}

/* ---------------------------------------------------------------------------
 * Validation messages
 *
 * .voodoo-error is ours (client-side, Stage 5); .wpcf7-not-valid-tip is CF7's own,
 * shown when the server rejects a field. A visitor must not be able to tell which
 * path produced the message, so both are styled by the same rule (SPEC.md §5.3).
 * ------------------------------------------------------------------------ */

.voodoo-panel .voodoo-error,
.voodoo-panel .wpcf7-not-valid-tip {
	display: block;
	width: auto;
	margin: 0.375rem 0 0;
	padding: 0;
	border: 0;
	background: none;
	color: var(--voodoo-error);
	font-size: 0.875rem;
	font-weight: 600;
	line-height: 1.4;
}

/*
 * Error state, and it applies whether or not the input override is on: a field the
 * visitor cannot submit has to look like one, and no theme can be relied upon to style
 * .wpcf7-not-valid.
 *
 * The doubled root is what makes that true. Without it these rules sit at a lower
 * specificity than the input override above and lose to it — the border and background
 * would quietly revert to the resting style on exactly the fields that most need to
 * stand out. Matched deliberately, and placed after the override so source order
 * settles the tie.
 */
.voodoo-panel.voodoo-panel input[aria-invalid="true"],
.voodoo-panel.voodoo-panel select[aria-invalid="true"],
.voodoo-panel.voodoo-panel textarea[aria-invalid="true"],
.voodoo-panel.voodoo-panel .wpcf7-form-control.wpcf7-not-valid {
	border-color: var(--voodoo-error);
	background-color: var(--voodoo-error-surface);
}

.voodoo-panel.voodoo-panel input[aria-invalid="true"]:focus,
.voodoo-panel.voodoo-panel select[aria-invalid="true"]:focus,
.voodoo-panel.voodoo-panel textarea[aria-invalid="true"]:focus,
.voodoo-panel.voodoo-panel .wpcf7-form-control.wpcf7-not-valid:focus {
	border-color: var(--voodoo-error);
	outline: 2px solid var(--voodoo-error);
	outline-offset: 1px;
}

/*
 * CF7's form-level response banner. Restyled rather than hidden: with JS disabled it
 * is the only feedback a visitor gets.
 */
.voodoo-panel .wpcf7-response-output {
	/*
	 * Hidden until it has something to say. CF7 leaves the element in the markup
	 * permanently and only writes text into it on a response, so styling it
	 * unconditionally draws an empty bordered box under the button — visible in the
	 * sending-state screenshot before this rule was added.
	 */
	display: none;
	margin: var(--voodoo-gap) 0 0;
	padding: 0.75rem 1rem;
	border: 1px solid var(--voodoo-border-strong);
	border-radius: 6px;
	font-size: 0.9375rem;
}

.voodoo-panel .wpcf7-form.invalid .wpcf7-response-output,
.voodoo-panel .wpcf7-form.unaccepted .wpcf7-response-output,
.voodoo-panel .wpcf7-form.failed .wpcf7-response-output,
.voodoo-panel .wpcf7-form.spam .wpcf7-response-output,
.voodoo-panel .wpcf7-form.sent .wpcf7-response-output,
.voodoo-panel .wpcf7-response-output.voodoo-response-visible {
	display: block;
}

.voodoo-panel .wpcf7-form.invalid .wpcf7-response-output,
.voodoo-panel .wpcf7-form.unaccepted .wpcf7-response-output,
.voodoo-panel .wpcf7-form.failed .wpcf7-response-output,
.voodoo-panel .wpcf7-form.spam .wpcf7-response-output {
	border-color: var(--voodoo-error);
	background-color: var(--voodoo-error-surface);
	color: var(--voodoo-error);
}

/* CF7's own spinner: ours is on the button itself. */
.voodoo-panel .wpcf7-spinner {
	display: none;
}

/*
 * The response banner when panel.js writes to it directly. Only happens on a hard
 * network failure, where CF7 logs to the console and dispatches nothing at all.
 */
.voodoo-panel .wpcf7-response-output.voodoo-response-visible {
	display: block;
	border-color: var(--voodoo-error);
	background-color: var(--voodoo-error-surface);
	color: var(--voodoo-error);
}

/* ---------------------------------------------------------------------------
 * Confirmation view
 * ------------------------------------------------------------------------ */

/*
 * No focus ring on the confirmation heading.
 *
 * It carries tabindex="-1" purely so panel.js can move focus to it after a successful
 * send, which is what makes a screen reader announce the confirmation. It is not in the
 * tab order and a keyboard visitor can never land on it by tabbing, so the ring marks
 * nothing the visitor can act on — it just draws a box round the heading at the exact
 * moment the panel is meant to feel finished.
 *
 * Both states are needed. Removing the rule alone would fall back to the browser's own
 * outline, and :focus-visible has to be named explicitly because the panel-wide ring
 * further up would otherwise apply in browsers that match it on programmatic focus.
 * This is the one deliberate exception to "restyle focus rings, never remove them"
 * (SPEC.md §8); every element a visitor can actually reach still has one.
 */
.voodoo-panel .voodoo-confirm__heading:focus,
.voodoo-panel .voodoo-confirm__heading:focus-visible {
	outline: none;
}

/*
 * The visitor's name is written into this span, inside the confirmation <h2>. Themes
 * routinely style spans inside headings — for eyebrow text, highlights, small print —
 * and any such rule would shrink the name to something other than the heading it sits
 * in. Inheriting every text property from the parent puts it back.
 *
 * The doubled root is what wins the argument: a theme rule like ".entry-content h2 span"
 * out-specifies a single class, and this has to beat it.
 */
.voodoo-panel.voodoo-panel .voodoo-confirm__heading .voodoo-confirm__name {
	display: inline;
	font: inherit;
	color: inherit;
	letter-spacing: inherit;
	text-transform: inherit;
	vertical-align: baseline;
}

.voodoo-confirm__body {
	margin-bottom: var(--voodoo-gap);
	color: var(--voodoo-ink-muted);
}

.voodoo-confirm__body p {
	margin: 0 0 0.75rem;
}

.voodoo-confirm__body > :last-child {
	margin-bottom: 0;
}

/*
 * The confirmation body is the only block of copy left in this view, so any "while you
 * wait" link the client writes now lives in it. Styling the anchors here rather than
 * leaving them to the theme is what keeps such a link looking like something to click
 * at the one moment the visitor has nothing else to do.
 */
.voodoo-confirm__body a {
	color: var(--voodoo-accent);
	font-weight: 600;
	text-decoration: underline;
}

.voodoo-confirm__body a:hover,
.voodoo-confirm__body a:focus {
	color: var(--voodoo-accent-hover);
}

.voodoo-confirm__close {
	margin-top: 0.5rem;
}


/* ---------------------------------------------------------------------------
 * Floating button
 *
 * Opt-in, rendered outside the panel and revealed by panel.js. It sits below the
 * backdrop in the stacking order and hides itself while the panel is open, so it can
 * never be the thing a visitor taps when they meant to dismiss the dialog.
 * ------------------------------------------------------------------------ */

.voodoo-fab {
	position: fixed;
	z-index: calc(var(--voodoo-z) - 10);
	display: flex;
	align-items: center;
	justify-content: center;
	box-sizing: border-box;
	width: var(--voodoo-fab-size);
	height: var(--voodoo-fab-size);
	padding: 0;
	border: 0;
	border-radius: 50%;
	background-color: var(--voodoo-accent);
	color: var(--voodoo-accent-contrast);
	box-shadow: 0 4px 14px rgba(0, 0, 0, 0.25);
	cursor: pointer;
	-webkit-appearance: none;
	appearance: none;
	transition: background-color 150ms ease, transform 150ms ease;
}

.voodoo-fab[hidden] {
	display: none;
}

.voodoo-fab:hover {
	background-color: var(--voodoo-accent-hover);
	transform: scale(1.05);
}

/*
 * The button is outside .voodoo-panel, so the panel-wide focus ring does not reach it.
 * Its own, offset clear of the shadow.
 */
.voodoo-fab:focus-visible {
	outline: 2px solid var(--voodoo-accent);
	outline-offset: 3px;
}

.voodoo-fab svg {
	display: block;
	/* The speech-bubble tail sits low; nudge up so the glyph looks centred in the circle. */
	margin-top: -1px;
}

/*
 * Corner placement. env() keeps the button clear of the iOS home indicator and of a
 * notch in landscape; the fallback is the plain gap on everything else.
 */
.voodoo-fab--bottom-right,
.voodoo-fab--bottom-left {
	bottom: calc(var(--voodoo-fab-gap) + env(safe-area-inset-bottom, 0px));
}

.voodoo-fab--top-right,
.voodoo-fab--top-left {
	top: calc(var(--voodoo-fab-gap) + env(safe-area-inset-top, 0px));
}

.voodoo-fab--bottom-right,
.voodoo-fab--top-right {
	right: calc(var(--voodoo-fab-gap) + env(safe-area-inset-right, 0px));
}

.voodoo-fab--bottom-left,
.voodoo-fab--top-left {
	left: calc(var(--voodoo-fab-gap) + env(safe-area-inset-left, 0px));
}

/*
 * Out of the way while the panel is open. Not display:none — that would kill the fade
 * and, more importantly, would move focus if the button happened to hold it.
 */
body.voodoo-panel-open .voodoo-fab {
	opacity: 0;
	visibility: hidden;
	pointer-events: none;
	transition: opacity var(--voodoo-duration) var(--voodoo-easing), visibility 0s linear var(--voodoo-duration);
}

@media (prefers-reduced-motion: reduce) {
	.voodoo-fab {
		transition: none;
	}

	.voodoo-fab:hover {
		transform: none;
	}

	body.voodoo-panel-open .voodoo-fab {
		transition: none;
	}
}

/* ---------------------------------------------------------------------------
 * Body scroll lock (applied by Stage 4)
 * ------------------------------------------------------------------------ */

body.voodoo-panel-open {
	position: fixed;
	width: 100%;
	overflow: hidden;
}
