/*
 * Foundry theme — global stylesheet.
 *
 * theme.json owns the design tokens and most element styling. This file carries
 * only the cross-cutting rules theme.json can't express, all keyed off the
 * theme.json CSS custom properties so a brand style variation re-themes them for
 * free. Per-block styling belongs in that block's own style.css, not here.
 */

/* ---- Back-compat: legacy heading font-size var names ----------------------- */

/* Through 0.2.0 the fontSizes presets used slugs "h1"/"h2"/"h3", which WP
   kebab-cases to --wp--preset--font-size--h-1/--h-2/--h-3 when emitting the
   :root vars — so every reference to --h1/--h2/--h3 (element styles, block CSS,
   child-theme brand.css) resolved to nothing. 0.2.1 renamed the slugs to
   h-1/h-2/h-3 so definitions and references agree. These body-scoped aliases
   keep the OLD --h1/--h2/--h3 names working for any child theme or saved
   content that still references them. Permanent; new code uses --h-1/--h-2/--h-3. */
body {
	--wp--preset--font-size--h1: var(--wp--preset--font-size--h-1);
	--wp--preset--font-size--h2: var(--wp--preset--font-size--h-2);
	--wp--preset--font-size--h3: var(--wp--preset--font-size--h-3);
}

/* ---- Back-compat: legacy 2xl/2xs spacing var names ------------------------- */

/* Same kebab-case bug class as the heading fix above (WP splits the digit→letter
   boundary): the spacing presets slugged "2xs"/"2xl" emit
   --wp--preset--spacing--2-xs/--2-xl, but any reference written
   var:preset|spacing|2xs → --2xs (undefined) — so the "2X-Small"/"2X-Large"
   spacing presets silently collapsed to 0 wherever they were used, including
   saved block content that stored var:preset|spacing|2xl before 0.2.2. 0.2.2
   renamed the slugs to 2-xs/2-xl so definitions and references agree (emitted
   var and .has-2-xl-*-* class names are byte-identical, so saved content that
   used the class form is unaffected). These aliases keep the OLD un-split
   --2xs/--2xl names resolving for saved content and child brand.css that stored
   the buggy reference. Permanent; new code uses --2-xs/--2-xl. */
body {
	--wp--preset--spacing--2xs: var(--wp--preset--spacing--2-xs);
	--wp--preset--spacing--2xl: var(--wp--preset--spacing--2-xl);
}

/* ---- Base behaviour -------------------------------------------------------- */

html {
	scroll-behavior: smooth;
}

body {
	-webkit-font-smoothing: antialiased;
	text-rendering: optimizeLegibility;
}

::selection {
	background-color: var(--wp--preset--color--primary);
	color: var(--wp--preset--color--on-primary);
}

/* ---- Accessibility --------------------------------------------------------- */

/* Visible keyboard focus ring on interactive elements (theme.json can't set
   :focus-visible globally). */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
.wp-block-button__link:focus-visible,
[tabindex]:focus-visible {
	outline: 2px solid var(--wp--preset--color--primary);
	outline-offset: 2px;
	border-radius: 2px;
}

/* Skip-to-content link — hidden until focused. */
.skip-link.screen-reader-text:focus {
	background-color: var(--wp--preset--color--base);
	color: var(--wp--preset--color--primary);
	border-radius: 4px;
	box-shadow: var(--wp--preset--shadow--md);
	padding: 0.75rem 1.25rem;
	position: fixed;
	left: 1rem;
	top: 1rem;
	z-index: 100000;
}

@media (prefers-reduced-motion: reduce) {
	html {
		scroll-behavior: auto;
	}
}

/* ---- Theme structure ------------------------------------------------------- */

/* Sticky, subtly separated site header. Brand variations restyle via tokens. */
.foundry-header {
	position: sticky;
	top: 0;
	z-index: 1000;
	border-bottom: 1px solid var(--wp--preset--color--border);
}

/* Card hover lift on archive/index grids (the post-template group). */
.wp-block-query .wp-block-post-template > .wp-block-group {
	overflow: hidden;
	border-radius: 8px;
	transition: transform 0.3s ease, box-shadow 0.3s ease;
}

.wp-block-query .wp-block-post-template > .wp-block-group:hover {
	transform: translateY(-4px);
	box-shadow: var(--wp--preset--shadow--md);
}

/* Featured images inside cards fill their box cleanly. */
.wp-block-query .wp-block-post-featured-image img {
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/* ===========================================================================
 * Motion & decoration system (brand-neutral, token-driven)
 * ---------------------------------------------------------------------------
 * The house "aliveness" layer: float / pulse / shimmer / blob keyframes, a
 * scroll-reveal entrance, and hover-lift + glass utilities. Foundry blocks
 * EMIT the opt-in classes below (is-floating, has-glow, has-shimmer,
 * foundry-reveal, …); this file owns the animation vocabulary so every site on
 * the theme gets it for free. Everything is keyed off tokens — a brand tunes
 * intensity/colour via the --foundry-motion-* custom properties (theme.json
 * `custom` is off, so these live here) — and every animation is disabled under
 * prefers-reduced-motion at the foot of this file.
 * ======================================================================== */

:root {
	/* Tuning knobs — a brand style variation or child theme overrides these. */
	--foundry-motion-lift: -4px;                 /* hover-lift travel */
	--foundry-motion-speed: 0.3s;                /* hover/transition speed */
	--foundry-float-distance: 10px;              /* gentle bob travel */
	--foundry-float-duration: 6s;
	--foundry-pulse-duration: 2.4s;
	--foundry-shimmer-duration: 3s;
	--foundry-blob-duration: 7s;
	--foundry-reveal-distance: 24px;             /* scroll-reveal slide-up */
	--foundry-reveal-duration: 0.6s;
	--foundry-reveal-stagger: 90ms;              /* per-index delay step */
	/* Decorative colours default to the brand palette (with fallbacks). */
	--foundry-blob-color-1: var(--wp--preset--color--accent, #e2b23c);
	--foundry-blob-color-2: var(--wp--preset--color--primary, #c8963e);
	--foundry-blob-color-3: var(--wp--preset--color--secondary, #3a8f8f);
	--foundry-glow-color: var(--wp--preset--color--primary, #c8963e);
}

/* ---- Keyframes ------------------------------------------------------------- */

@keyframes foundry-float {
	0%, 100% { transform: translateY(0); }
	50%      { transform: translateY(calc(-1 * var(--foundry-float-distance))); }
}

@keyframes foundry-pulse {
	0%, 100% { opacity: 1;    box-shadow: 0 0 0 0 rgb(from var(--foundry-glow-color) r g b / 0.5); }
	50%      { opacity: 0.65; box-shadow: 0 0 0 6px rgb(from var(--foundry-glow-color) r g b / 0); }
}

@keyframes foundry-shimmer {
	0%   { transform: translateX(-120%) skewX(-18deg); }
	100% { transform: translateX(220%)  skewX(-18deg); }
}

@keyframes foundry-blob {
	0%, 100% { transform: translate(0, 0) scale(1); }
	33%      { transform: translate(6%, -4%) scale(1.08); }
	66%      { transform: translate(-5%, 5%) scale(0.94); }
}

/* ---- Opt-in motion classes (blocks add these) ----------------------------- */

/* Gentle vertical bob — hero cards, floating asides. */
.is-floating {
	animation: foundry-float var(--foundry-float-duration) ease-in-out infinite;
	will-change: transform;
}

/* Pulsing status dot — the "now accepting work" badge, etc. Put on the dot. */
.is-pulsing {
	animation: foundry-pulse var(--foundry-pulse-duration) ease-in-out infinite;
	border-radius: 50%;
}

/* Radial glow behind a card (cta-card, hero card). */
.has-glow { position: relative; }
.has-glow::before {
	content: "";
	position: absolute;
	inset: -18%;
	z-index: -1;
	background: radial-gradient(closest-side, rgb(from var(--foundry-glow-color) r g b / 0.28), transparent 72%);
	pointer-events: none;
}

/* Shimmer sweep across a surface (cta-card). Element needs overflow clipping. */
.has-shimmer { position: relative; overflow: hidden; }
.has-shimmer::after {
	content: "";
	position: absolute;
	top: 0;
	left: 0;
	width: 45%;
	height: 100%;
	background: linear-gradient(100deg, transparent, rgb(255 255 255 / 0.28), transparent);
	animation: foundry-shimmer var(--foundry-shimmer-duration) ease-in-out infinite;
	pointer-events: none;
}

/* Hover-lift utility — promoted from the archive-card default so any block can
   opt a card into the lift. (The query-loop cards above already have it.) */
.has-hover-lift {
	transition: transform var(--foundry-motion-speed) ease, box-shadow var(--foundry-motion-speed) ease;
}
.has-hover-lift:hover {
	transform: translateY(var(--foundry-motion-lift));
	box-shadow: var(--wp--preset--shadow--lg);
}

/* Frosted-glass card treatment. Solid fallback first so browsers without
   relative-colour syntax still render a readable surface. */
.has-glass {
	background-color: var(--wp--preset--color--surface, #fff);
	background-color: rgb(from var(--wp--preset--color--surface, #fff) r g b / 0.72);
	backdrop-filter: blur(10px);
	-webkit-backdrop-filter: blur(10px);
	border: 1px solid rgb(from var(--wp--preset--color--border, #e5e5e5) r g b / 0.6);
}

/* ---- Decorative blob layer ------------------------------------------------ */
/* A section adds a .foundry-blobs child holding up to 3 .foundry-blob spans;
   they drift behind the content. Purely decorative, aria-hidden in markup. */
.foundry-blobs {
	position: absolute;
	inset: 0;
	overflow: hidden;
	pointer-events: none;
	z-index: 0;
}
.foundry-blobs + * { position: relative; z-index: 1; }
.foundry-blob {
	position: absolute;
	width: 42vmax;
	max-width: 640px;
	aspect-ratio: 1;
	border-radius: 50%;
	filter: blur(60px);
	opacity: 0.35;
	animation: foundry-blob var(--foundry-blob-duration) ease-in-out infinite;
}
.foundry-blob:nth-child(1) { top: -12%; left: -8%;  background: var(--foundry-blob-color-1); }
.foundry-blob:nth-child(2) { top: 30%;  right: -10%; background: var(--foundry-blob-color-2); animation-delay: -2.3s; }
.foundry-blob:nth-child(3) { bottom: -14%; left: 25%; background: var(--foundry-blob-color-3); animation-delay: -4.6s; }

/* ---- Scroll-reveal -------------------------------------------------------- */
/* Sections tagged .foundry-reveal start hidden ONLY once motion.js has flagged
   the document ready (.foundry-motion-ready) — so with JS off, content is fully
   visible (no hidden-forever risk). motion.js then adds .is-revealed on enter.
   Stagger via inline style="--foundry-reveal-index:N". */
.foundry-motion-ready .foundry-reveal {
	opacity: 0;
	transform: translateY(var(--foundry-reveal-distance));
	transition: opacity var(--foundry-reveal-duration) ease-out,
	            transform var(--foundry-reveal-duration) ease-out;
	transition-delay: calc(var(--foundry-reveal-index, 0) * var(--foundry-reveal-stagger));
	will-change: opacity, transform;
}
.foundry-motion-ready .foundry-reveal.is-revealed {
	opacity: 1;
	transform: none;
}

/* ---- Reduced-motion: disable the whole vocabulary ------------------------- */
@media (prefers-reduced-motion: reduce) {
	.is-floating,
	.is-pulsing,
	.has-shimmer::after,
	.foundry-blob {
		animation: none !important;
	}
	.has-hover-lift { transition: none; }
	.has-hover-lift:hover { transform: none; }
	/* Reveal targets snap visible — motion.js also short-circuits, but guard CSS-side. */
	.foundry-motion-ready .foundry-reveal {
		opacity: 1;
		transform: none;
		transition: none;
	}
}

/* ===========================================================================
 * AffiliateWP form skin (brand-neutral, token-driven)
 * ---------------------------------------------------------------------------
 * AffiliateWP renders its registration / login / opt-in / dashboard-settings
 * forms as `.affwp-form`, and its own forms.css styles almost nothing: a single
 * `rgba(51,51,51,.1)` hairline plus `box-sizing`. Font, radius, padding and the
 * submit button all fall through to the user-agent default, so an affiliate
 * page shows Arial inputs and a grey system button inside otherwise fully
 * branded chrome. Gravity Forms never had this problem only because every child
 * theme carries its own `.gform_wrapper` skin; nothing was ever scoped to
 * `.affwp-form`, on any site.
 *
 * This is the shared fix, and it lives here rather than in a child brand.css
 * (which would restate it 8+ times and drift) or in the Foundry plugin (which
 * owns functionality, not presentation — and reaches exactly the same sites).
 * It reads theme.json tokens only, so each brand's palette and typeface apply
 * with no per-site CSS. Child brand.css is enqueued with `foundry-global` as a
 * dependency, so it still cascades after this and can override at equal
 * specificity — binghamtontreeservice's existing affwp block continues to win.
 *
 * The --foundry-field-* knobs let a child align these fields to its own Gravity
 * Forms skin in one line instead of restating the block. Everything is scoped
 * to `.affwp-form`: bare `.button` / `input[type="submit"]` are far too generic
 * to claim globally.
 * ======================================================================== */

:root {
	/* Defaults match theme.json's button element (6px radius) so the affiliate
	   forms read as the same system as the theme's own buttons. */
	--foundry-field-radius: 6px;
	--foundry-field-padding: 0.7rem 0.9rem;
	--foundry-field-gap: 1.25rem;             /* vertical rhythm between fields */
}

/* ---- Fieldset / legend ----------------------------------------------------- */

/* AffiliateWP already drops the fieldset border; give the legend the heading
   voice so "Register a new affiliate account" reads as a section title. */
.affwp-form legend {
	font-family: var(--wp--preset--font-family--heading);
	color: var(--wp--preset--color--contrast);
	font-weight: 700;
}

.affwp-form p {
	margin-bottom: var(--foundry-field-gap);
}

/* ---- Labels ---------------------------------------------------------------- */

/* Stacked above their control. Unlike the child Gravity Forms skins, labels are
   NOT hidden: those forms are compact placeholder-only quote blocks, whereas a
   registration form has no placeholders and needs its labels. */
.affwp-form label {
	display: block;
	font-family: var(--wp--preset--font-family--heading);
	font-weight: 600;
	font-size: 0.875rem;
	color: var(--wp--preset--color--contrast);
	margin-bottom: 0.35rem;
}

/* The "(required)" hint after a label. Deliberately `span.required`, not
   `.required`: AffiliateWP also puts that class on the LOGIN form's username
   and password INPUTS, which must not be dimmed to the hint colour. */
.affwp-form span.required {
	color: var(--wp--preset--color--muted);
	font-weight: 400;
}

/* Labels that WRAP their control ("Remember Me", multi-checkbox, radio) must
   stay on one line with it. Kept in its own rule from the :has() pair below —
   an unsupported :has() invalidates the whole selector list it appears in. */
.affwp-form .affwp-user-remember,
.affwp-form .affwp-label-radio,
.affwp-form .affwp-label-checkbox-multiple {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	font-family: var(--wp--preset--font-family--body);
	font-weight: 400;
	margin-bottom: 0;
}

/* Same treatment for any wrapping label AffiliateWP renders without one of the
   classes above. Progressive enhancement: browsers without :has() just leave
   such a label stacked, which is untidy but not broken. */
.affwp-form label:has(input[type="checkbox"]),
.affwp-form label:has(input[type="radio"]) {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	font-family: var(--wp--preset--font-family--body);
	font-weight: 400;
	margin-bottom: 0;
}

/* ---- Text controls --------------------------------------------------------- */

/* Type-scoped, so this never reaches the submit, the checkboxes, or the hidden
   nonce/action inputs. (0,2,1) clears AffiliateWP's own `.affwp-form input`
   border at (0,1,1) without needing !important. */
.affwp-form input[type="text"],
.affwp-form input[type="email"],
.affwp-form input[type="url"],
.affwp-form input[type="tel"],
.affwp-form input[type="number"],
.affwp-form input[type="search"],
.affwp-form input[type="password"],
.affwp-form select,
.affwp-form textarea {
	box-sizing: border-box;
	width: 100%;
	max-width: 100%;
	border: 1px solid var(--wp--preset--color--border, rgba(0, 0, 0, 0.12));
	border-radius: var(--foundry-field-radius);
	padding: var(--foundry-field-padding);
	font-family: var(--wp--preset--font-family--body);
	font-size: 1rem;
	color: var(--wp--preset--color--contrast);
	background-color: var(--wp--preset--color--surface, #fff);
	transition: border-color var(--foundry-motion-speed, 0.3s) ease;
}

.affwp-form input[type="text"]:focus,
.affwp-form input[type="email"]:focus,
.affwp-form input[type="url"]:focus,
.affwp-form input[type="tel"]:focus,
.affwp-form input[type="number"]:focus,
.affwp-form input[type="search"]:focus,
.affwp-form input[type="password"]:focus,
.affwp-form select:focus,
.affwp-form textarea:focus {
	border-color: var(--wp--preset--color--primary);
}

.affwp-form textarea {
	min-height: 8rem;
	resize: vertical;
}

/* Checkboxes and radios take the brand tint but keep their intrinsic size —
   the width:100% above deliberately does not reach them. */
.affwp-form input[type="checkbox"],
.affwp-form input[type="radio"] {
	accent-color: var(--wp--preset--color--primary);
	width: auto;
	margin: 0;
	flex: none;
}

/* ---- Submit ---------------------------------------------------------------- */

/* Mirrors theme.json's button element (primary / on-primary / 6px / 600) so the
   affiliate submit matches every other button on the site. AffiliateWP styles
   no button at all, so plain specificity is enough — no !important. */
.affwp-form input[type="submit"],
.affwp-form button[type="submit"] {
	background-color: var(--wp--preset--color--primary);
	color: var(--wp--preset--color--on-primary);
	border: none;
	border-radius: var(--foundry-field-radius);
	padding: var(--wp--preset--spacing--xs, 0.75rem) var(--wp--preset--spacing--md, 1.5rem);
	font-family: var(--wp--preset--font-family--heading);
	font-size: 1rem;
	font-weight: 600;
	line-height: 1.2;
	width: auto;
	max-width: 100%;
	cursor: pointer;
	transition: background-color var(--foundry-motion-speed, 0.3s) ease;
}

.affwp-form input[type="submit"]:hover,
.affwp-form button[type="submit"]:hover {
	background-color: var(--wp--preset--color--primary-hover);
}

/* ---- Panels AffiliateWP hardcodes in light-mode colours -------------------- */

/* The terms-of-use scroll box ships `#fff` / `#444` / a black hairline, which
   strands it as a white card on any dark or tinted brand. Re-tokenised so it
   sits on the same surface as the fields. */
.affwp-form .affwp-field-terms-of-use-content {
	background-color: var(--wp--preset--color--surface, #fff);
	border: 1px solid var(--wp--preset--color--border, rgba(0, 0, 0, 0.12));
	border-radius: var(--foundry-field-radius);
	color: var(--wp--preset--color--contrast);
}

/* AffiliateWP's spam honeypot is a real, empty text input that must stay in the
   accessibility tree and out of sight. It ships `type="hidden"` on current
   builds (making this inert) but has rendered as a visible text field on older
   ones — where the width:100% above would otherwise expose it as a stray box.
   Moved off-screen rather than display:none, which bots read as a honeypot. */
.affwp-form input[name="affwp_honeypot"] {
	position: absolute;
	left: -9999px;
}
