/**
 * Reusable component-level styles shared across templates and patterns.
 * Keep this list deliberate — add a rule here only once a second template
 * or pattern actually needs it.
 */

/**
 * Site header (Prompt 039). Deliberately compact — a utility/product
 * platform's header bar, not a marketing-site hero band. Previously used
 * `--wp--custom--spacing--section` (a clamp up to 5rem) for its vertical
 * padding, the same token used for large page section spacing; measured
 * live at 187px tall on a 1920px viewport before this change, which read
 * as an oversized, blog-theme-style header rather than a compact product
 * bar. Fixed height range instead, independent of that section-spacing
 * token.
 */
/**
 * `position: sticky` (Prompt 044 visual pass) — genuinely useful here:
 * the header carries the only search/nav entry point, and tool pages
 * (editors, calculators, PDF workspaces) can run long, so keeping it
 * reachable while scrolling is a real usability win, not decoration. No
 * JS, no added shadow beyond the existing hairline border (the sticky
 * state itself provides enough visual separation from scrolled content).
 */
.site-header {
	position: sticky;
	top: 0;
	z-index: 30;
	padding-block: 0.875rem;
	background: var(--wp--preset--color--base, #fff);
	border-bottom: 1px solid var(--wp--preset--color--border);
}

/**
 * Stable, custom-class hook for the header's inner flex row (brand +
 * nav) — deliberately NOT targeted via WordPress's own generated
 * `wp-container-core-group-is-layout-*` hash classes, which are neither
 * stable across edits nor meant to be relied on by hand-written CSS.
 */
.vp-site-header__inner {
	align-items: center;
	flex-wrap: nowrap;
	gap: 1rem;
	/* The mega menu's positioned ancestor — see `.vp-nav-megamenu`'s own
	   comment for why it's centered against this (a near-full-page-width
	   container) rather than the narrow "Tools" link it's nested inside. */
	position: relative;
}

/* Groups the theme toggle + (Invoice-Generator-only) search trigger as
   one flex item — without this, `.vp-site-header__inner`'s own
   `justify-content: space-between` spread all 4 top-level children
   (brand/nav/toggle/search) evenly across the FULL header width,
   leaving a large gap between the toggle and the search icon instead
   of the compact "sit right next to each other" look. On every other
   page this wrapper holds only the toggle — same visual result as
   before, no regression. */
.vp-site-header__controls {
	align-items: center;
	gap: 0.5rem;
}

/**
 * Brand mark (Prompt 044 visual pass) — a small decorative badge beside
 * the site title, original hand-drawn markup (the same developer-tools
 * category glyph from `ToolIcons`, redrawn inline here since the header
 * template part is static HTML and can't call a PHP render method), not
 * a logo image asset. Purely decorative: `aria-hidden`, the real
 * accessible name stays the text site title next to it.
 */
.vp-brand {
	display: flex;
	align-items: center;
	gap: 0.55rem;
}

.vp-brand__mark {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 30px;
	height: 30px;
	flex-shrink: 0;
	border-radius: var(--wp--custom--radius--small, 4px);
	background: var(--wp--preset--color--primary, #1d4ed8);
	color: var(--wp--preset--color--base, #fff);
}

.vp-brand__mark svg {
	width: 17px;
	height: 17px;
}

.wp-block-site-title a {
	font-size: 1.25rem;
	font-weight: 700;
	letter-spacing: -0.01em;
	color: var(--wp--preset--color--primary, #1d4ed8);
	text-decoration: none;
}

.wp-block-site-title a:hover {
	color: var(--wp--preset--color--primary-dark, #17369e);
}

/**
 * At very narrow widths (confirmed live at 320px) the brand icon +
 * "Vedant Platform" at its normal 1.25rem no longer both fit on one
 * line within `.vp-brand`'s own shrunk width — and `.vp-brand` is a
 * `wp:group` block with `layout:{"type":"flex"}` (parts/header.html),
 * which WordPress's own core block styles default to `flex-wrap: wrap`
 * for, so it dropped the title to a second line under the icon instead
 * of overflowing. Smaller text at this width is enough on its own to
 * fit both back on one line; `flex-wrap: nowrap` here is a second,
 * explicit guarantee of that rather than leaving it to arithmetic.
 */
@media (max-width: 400px) {
	.wp-block-site-title a {
		font-size: 1rem;
	}

	.vp-brand {
		flex-wrap: nowrap;
	}
}

/**
 * Anchor targets same-page links land on (hero's "Explore categories",
 * the footer's "Search"/"Categories" resource links) — offsets the jump
 * so the sticky header never covers the destination's own heading.
 */
#vp-home-categories,
#vp-home-search-input {
	scroll-margin-top: 84px;
}

/**
 * Footer (Prompt 044 visual pass) — a real multi-column structure
 * (Brand / Tools / Resources / Company) replacing the previous single
 * line of text + a bare legal-link row. A permanently dark band — fixed
 * colors, not the `contrast`/`base` tokens — by deliberate design (same
 * "one brand identity, no second palette" reasoning as before dark mode
 * existed): the footer stays this same dark shade with white text in
 * BOTH the light and dark site themes, rather than following the global
 * toggle. It must be fixed rather than token-based specifically because
 * dark mode redefines what `contrast`/`base` themselves mean (readable
 * text color vs. near-black brand shade) — using them here as they were
 * originally written left this component looking correct only in light
 * mode; in dark mode the background token flips light while this
 * component's own hardcoded white text stays white, producing white
 * text on a near-white background. Every link column is built from real
 * WordPress data (see vedant_platform_footer_tools()/_resources()/_nav()
 * in functions.php); none of the three ever renders with an empty list.
 */
.site-footer {
	margin-top: var(--wp--custom--spacing--section);
	padding-block: clamp(2.5rem, 5vw, 3.5rem) 0;
	background: #14181f;
	color: rgba(255, 255, 255, 0.75);
}

.vp-footer-grid {
	align-items: start;
	flex-wrap: wrap;
	row-gap: 2.5rem;
	column-gap: 2rem;
}

.vp-footer-col {
	flex: 1 1 150px;
	min-width: 140px;
}

.vp-footer-col--brand {
	flex: 2 1 240px;
	min-width: 220px;
	max-width: 340px;
}

.vp-footer-brand {
	margin: 0 0 0.5rem;
	font-size: var(--wp--preset--font-size--large, 1.25rem);
	font-weight: 700;
	color: #fff;
}

.vp-footer-tagline {
	margin: 0;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	line-height: 1.6;
	color: rgba(255, 255, 255, 0.6);
}

.vp-footer-col__heading {
	margin: 0 0 0.9rem;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.045em;
	color: #fff;
}

.vp-footer-col__list {
	display: flex;
	flex-direction: column;
	gap: 0.65rem;
	list-style: none;
	margin: 0;
	padding: 0;
}

.vp-footer-col__list a {
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	color: rgba(255, 255, 255, 0.68);
	text-decoration: none;
}

.vp-footer-col__list a:hover {
	color: #fff;
	text-decoration: underline;
}

.vp-footer-bottom {
	margin-top: 2.5rem;
	padding-block: 1.25rem;
	border-top: 1px solid rgba(255, 255, 255, 0.12);
}

.vp-footer-bottom p {
	margin: 0;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	color: rgba(255, 255, 255, 0.5);
}

/* Invoice Generator page only (Footer\InvoiceFooterExtras) — "Follow
   Us" reuses `.vp-footer-col__list` above as-is; only the support card
   and the bottom-row "Made with ♥" line need their own rules. */
.vp-footer-col--support {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: 0.5rem;
}

.vp-footer-support-note {
	margin: 0;
	font-size: 0.75rem;
	color: rgba(255, 255, 255, 0.5);
}

@media (max-width: 767px) {
	.vp-footer-grid {
		flex-direction: column;
		gap: 2rem;
	}

	/**
	 * The desktop `flex: N 1 <px>` values on `.vp-footer-col`/`--brand`
	 * size each column's WIDTH in a row layout. Once `flex-direction`
	 * flips to column above, that same flex-basis is read as a HEIGHT
	 * instead (confirmed live: the brand column rendered a fixed 240px
	 * tall box with ~140px of dead space below its actual two lines of
	 * text) — reset to natural content-driven sizing here.
	 */
	.vp-footer-col,
	.vp-footer-col--brand {
		flex: none;
		max-width: none;
	}
}

.vp-tool-status {
	display: inline-block;
	margin-bottom: var(--wp--preset--spacing--30, 0.75rem);
	padding: 0.3em 0.7em;
	border-radius: var(--wp--custom--radius--small, 4px);
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	font-weight: 600;
}

.vp-tool-status--coming-soon {
	background: var(--wp--preset--color--surface, #f5f6f8);
	color: var(--wp--preset--color--contrast, #14181f);
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
}

.vp-tool-coming-soon {
	font-weight: 600;
}

/**
 * Primary navigation (Prompt 039). `.vp-primary-nav-wrap` is the `<nav>`
 * itself; `.vp-primary-nav` is the always-real `<ul>` of links rendered
 * by vedant_platform_primary_nav() (functions.php). Only 3 items exist
 * today (Home/Tools/About) — no dropdown/mega-menu, deliberately, per
 * Prompt 039's own evaluation that 14 tools across 3 categories doesn't
 * warrant one yet.
 */
.vp-primary-nav-wrap {
	display: flex;
	align-items: center;
}

.vp-primary-nav {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: 1.5rem;
	list-style: none;
	margin: 0;
	padding: 0;
}

.vp-primary-nav__link {
	display: inline-block;
	font-weight: 600;
	text-decoration: none;
	color: var(--wp--preset--color--contrast, #14181f);
	padding-block: 0.25rem;
	border-bottom: 2px solid transparent;
	transition: color 0.15s ease, border-color 0.15s ease;
}

.vp-primary-nav__link:hover {
	color: var(--wp--preset--color--primary, #1d4ed8);
}

.vp-primary-nav__link.is-active {
	color: var(--wp--preset--color--primary, #1d4ed8);
	border-bottom-color: var(--wp--preset--color--primary, #1d4ed8);
}

@media (prefers-reduced-motion: reduce) {
	.vp-primary-nav__link {
		transition: none;
	}
}

/**
 * Header search widget — Invoice Generator page ONLY (see
 * Search\InvoiceSearchWidget). Starts `hidden`, revealed by
 * assets/js/invoice-search.js — same no-JS contract as the theme toggle
 * and mobile nav toggle in this same header.
 */
.vp-invoice-header-search {
	position: relative;
	display: flex;
	align-items: center;
}

/* Same box size as the header's `.vp-theme-toggle` (44x44px,
   dark-theme.css:59-71) it sits next to — was 36px, visibly smaller
   than the theme toggle even though both hold an 18x18 icon, so the
   two buttons looked mismatched side by side. */
.vp-invoice-header-search__toggle {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 44px;
	height: 44px;
	padding: 0;
	background: transparent;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--small, 4px);
	color: var(--wp--preset--color--contrast, #14181f);
	cursor: pointer;
	flex-shrink: 0;
}

.vp-invoice-header-search__toggle:hover,
.vp-invoice-header-search__toggle[aria-expanded="true"] {
	border-color: var(--wp--preset--color--primary, #1d4ed8);
	color: var(--wp--preset--color--primary, #1d4ed8);
}

.vp-invoice-header-search__panel {
	position: absolute;
	top: 100%;
	right: 0;
	z-index: 30;
	margin-top: 0.5rem;
	width: min(320px, calc(100vw - 2rem));
	padding: 0.6rem;
	background: var(--wp--preset--color--base, #fff);
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--base, 8px);
	box-shadow: var(--wp--preset--shadow--md, 0 4px 16px rgba(20, 24, 31, .08));
}

.vp-invoice-header-search__input {
	width: 100%;
	box-sizing: border-box;
	font: inherit;
	padding: 0.55em 0.7em;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--small, 6px);
}

.vp-invoice-header-search__results {
	list-style: none;
	margin: 0.5rem 0 0;
	padding: 0;
	max-height: 320px;
	overflow-y: auto;
}

.vp-invoice-header-search__option,
.vp-invoice-header-search__empty {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	padding: 0.5em 0.6em;
	border-radius: var(--wp--custom--radius--small, 6px);
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	cursor: pointer;
}

.vp-invoice-header-search__empty {
	color: var(--wp--preset--color--contrast-2, #57606a);
	cursor: default;
}

.vp-invoice-header-search__option.is-active,
.vp-invoice-header-search__option:hover {
	background: var(--wp--preset--color--surface, #f5f6f8);
}

.vp-invoice-header-search__option-type {
	flex-shrink: 0;
	font-size: 0.7rem;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.03em;
	color: var(--wp--preset--color--primary, #1d4ed8);
	background: var(--wp--preset--color--surface, #f5f6f8);
	padding: 0.15em 0.45em;
	border-radius: 999px;
}

.vp-invoice-header-search__option-title {
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

@media (max-width: 480px) {
	.vp-invoice-header-search__panel {
		position: fixed;
		top: auto;
		left: 1rem;
		right: 1rem;
		width: auto;
	}
}

/**
 * Mobile menu toggle. Starts `hidden` in the HTML (functions.php) and is
 * only ever revealed by assets/js/navigation.js — see that file's
 * docblock for the full progressive-enhancement contract. Shows the
 * hamburger icon by default and the close (X) icon once
 * `aria-expanded="true"`, via a plain attribute selector — no JS needed
 * to swap icons, only to toggle the one aria-expanded value.
 */
.vp-nav-toggle {
	display: none;
	align-items: center;
	justify-content: center;
	width: 44px;
	height: 44px;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--small, 4px);
	background: transparent;
	color: var(--wp--preset--color--contrast, #14181f);
	cursor: pointer;
}

.vp-nav-toggle:hover {
	border-color: var(--wp--preset--color--primary, #1d4ed8);
	color: var(--wp--preset--color--primary, #1d4ed8);
}

.vp-nav-toggle__icon--close {
	display: none;
}

.vp-nav-toggle[aria-expanded="true"] .vp-nav-toggle__icon--menu {
	display: none;
}

.vp-nav-toggle[aria-expanded="true"] .vp-nav-toggle__icon--close {
	display: block;
}

@media (max-width: 767px) {
	/* `.vp-site-header__inner`'s `justify-content: space-between` (the
	   Group block's own "space between" layout option) was set for its 3
	   desktop children — brand, the full-width link list, and controls —
	   spread evenly. On mobile the nav collapses to just this toggle
	   button, so the same space-between still treated it as a 3rd equal
	   item and stranded it dead-center between the logo and the theme
	   toggle instead of sitting next to it (confirmed live). Drop to
	   flex-start and pin the toggle+controls group to the right with an
	   auto margin instead. */
	.vp-site-header__inner {
		justify-content: flex-start;
	}

	/* Scoped through the `.vp-site-header__inner` ancestor, not just
	   `.vp-primary-nav-wrap` alone — WordPress's own core layout rule
	   `.is-layout-flex > :is(*, div) { margin: 0 }` (theme-independent,
	   ships with the block library) is MORE specific than a single class
	   selector, so a bare `.vp-primary-nav-wrap { margin-left: auto }`
	   silently lost to it and never moved (confirmed live: computed
	   margin-left stayed 0px). The extra ancestor class here outweighs
	   that rule's specificity instead of fighting it with `!important`. */
	.vp-site-header__inner .vp-primary-nav-wrap {
		margin-left: auto;
	}

	/* Positioned ancestor for the JS-collapsed mobile menu below
	   (`.vp-nav-js-ready .vp-primary-nav` is `position: absolute`,
	   anchored to this). Scoped to mobile only — set unconditionally on
	   `.vp-primary-nav-wrap` it was the *nearest* positioned ancestor for
	   the desktop mega-menu too, intercepting the containing block before
	   it ever reached `.vp-site-header__inner` and breaking that panel's
	   centering (confirmed live: measured position didn't match either
	   `.vp-site-header__inner`- or viewport-relative expectations). */
	.vp-primary-nav-wrap {
		position: relative;
	}

	/* No-JS fallback: the toggle button never leaves `hidden` (nothing
	   here un-hides it), so the link list just wraps in place — the same
	   safe, always-usable behavior the site already had. */
	.vp-primary-nav-wrap:not(.vp-nav-js-ready) .vp-primary-nav {
		flex-wrap: wrap;
		gap: 1rem 1.25rem;
	}

	/* The toggle is only ever actually shown within this mobile
	   breakpoint, and only once JS has removed [hidden] — on desktop
	   widths it stays display:none regardless of the hidden attribute,
	   since no rule outside this media query ever shows it. */
	.vp-nav-toggle:not([hidden]) {
		display: inline-flex;
	}

	/* JS-enhanced: collapse the list behind the toggle button. 180px
	   min-width read cramped once the "Tools" accordion's category rows
	   (icon + name like "Financial Calculators" + chevron) are in it —
	   long category names wrapped to 2 lines inside that width. Widened
	   to give them room, capped so it still fits inside small phone
	   viewports rather than overflowing off the right edge. */
	.vp-nav-js-ready .vp-primary-nav {
		display: none;
		position: absolute;
		top: 100%;
		right: 0;
		z-index: 20;
		flex-direction: column;
		align-items: stretch;
		gap: 0;
		width: min(320px, calc(100vw - 2rem));
		margin-top: 0.5rem;
		padding: 0.4rem;
		background: var(--wp--preset--color--base, #fff);
		border: 1px solid var(--wp--preset--color--border, #e2e5ea);
		border-radius: var(--wp--custom--radius--base, 8px);
		box-shadow: var(--wp--preset--shadow--md, 0 4px 16px rgba(20, 24, 31, .08));
	}

	.vp-nav-js-ready .vp-primary-nav.is-open {
		display: flex;
	}

	.vp-nav-js-ready .vp-primary-nav__link {
		width: 100%;
		padding: 0.65rem 0.75rem;
		border-bottom: 0;
		border-radius: var(--wp--custom--radius--small, 4px);
	}

	.vp-nav-js-ready .vp-primary-nav__link:hover,
	.vp-nav-js-ready .vp-primary-nav__link.is-active {
		background: var(--wp--preset--color--surface, #f5f6f8);
	}
}

/**
 * "Tools" mega menu (Tools\ToolsMegaMenu) — an accordion (native
 * `<details>`, same zero-JS pattern as the Guide/tool FAQ) of every
 * `tool_category`, each listing up to 10 direct tool links. Stronger
 * internal linking + a real category browser, direct request.
 *
 * Desktop (≥768px): a hover/focus-revealed panel anchored under the
 * "Tools" link, 2 columns of collapsed category rows — collapsed so 11
 * categories don't turn the panel into an overwhelming wall of links;
 * a visitor opens only the category they actually want.
 */
.vp-nav-megamenu {
	display: none;
}

@media (min-width: 768px) {
	/**
	 * Hidden via `visibility`/`opacity`/`pointer-events`, NOT
	 * `display:none` — `display` can't carry a transition delay, and
	 * that hard on/off cutoff was exactly the reported bug: the panel
	 * sits `margin-top: 0.5rem` below the "Tools" link, a gap painted by
	 * neither the trigger nor (while hidden) the panel itself, so
	 * dragging the cursor straight down into the panel briefly left both
	 * elements' hit areas and the whole menu vanished before it arrived.
	 * The 0.3s delay on hiding (below) gives real time to finish that
	 * crossing — pure CSS, no JS/timer needed — while showing stays
	 * instant either way (hover or keyboard :focus-within). `display` is
	 * kept as `grid` unconditionally here (was toggled by the same
	 * hover/focus-within selectors before) since an invisible-but-laid-
	 * out element is what makes the delayed-visibility transition
	 * possible at all.
	 */
	/* 4 columns (was 2) — direct feedback: at 2 columns the panel's own
	   scroll area meant an expanded category's tool list could sit out
	   of easy reach below the fold of the panel itself. 4 columns fits
	   all 11 categories in 3 rows instead of 6, so far less scrolling
	   (or none, on most desktop heights) is needed to reach any of them.
	   `left: 50%` + `translateX(-50%)` centers the (now much wider) panel
	   — but against `.vp-site-header__inner` (that rule's own `position:
	   relative`), not the narrow "Tools" link it's nested inside. Two
	   real bugs confirmed live at different viewport widths before this:
	   centered on the LI itself at 1440px, the panel's 4th column still
	   ran 171px past the right edge (the LI sits well left of the page's
	   own center, so centering ON it doesn't center the PANEL on the
	   page); and at 768px it was actively clipped, with visibly unused
	   space on the left — center-on-the-trigger doesn't help once the
	   panel is wider than the room actually available on the trigger's
	   narrower side. Centering against the full header-width ancestor
	   instead fixes both: the panel is now centered on the *page*,
	   regardless of where "Tools" itself happens to sit in the nav row.
	   `max-width` still caps it at the viewport so it can't overflow at
	   all at the low end of this breakpoint (768px), just narrower there
	   than the full 940px. */
	.vp-nav-megamenu {
		display: grid;
		position: absolute;
		top: 100%;
		left: 50%;
		transform: translateX(-50%);
		z-index: 25;
		width: 940px;
		/* The 100vw version of this cap still let the panel overflow
		   `.vp-site-header__inner` itself at 768px (confirmed live) — the
		   panel centers against THAT container (see comment above), which
		   is narrower than the raw viewport by its own horizontal padding
		   (theme.json's global `clamp(1rem, 4vw, 2rem)` per side, same
		   value this subtracts twice for both sides), not just full-bleed
		   to the viewport edge. Subtracting that padding too, on top of
		   the original 2rem breathing margin, keeps the cap inside the
		   space that's actually available. */
		max-width: calc(100vw - 2rem - 2 * clamp(1rem, 4vw, 2rem));
		max-height: 70vh;
		overflow-y: auto;
		grid-template-columns: repeat(4, 1fr);
		align-content: start;
		gap: 0 1.25rem;
		margin-top: 0.5rem;
		padding: 0.5rem 0.75rem;
		background: var(--wp--preset--color--base, #fff);
		border: 1px solid var(--wp--preset--color--border, #e2e5ea);
		border-radius: var(--wp--custom--radius--base, 8px);
		box-shadow: var(--wp--preset--shadow--md, 0 4px 16px rgba(20, 24, 31, .08));
		visibility: hidden;
		opacity: 0;
		pointer-events: none;
		transition: opacity 0.15s ease, visibility 0s linear 0.3s, pointer-events 0s linear 0.3s;
	}

	.vp-primary-nav__item--has-submenu:hover .vp-nav-megamenu,
	.vp-primary-nav__item--has-submenu:focus-within .vp-nav-megamenu {
		visibility: visible;
		opacity: 1;
		pointer-events: auto;
		transition: opacity 0.15s ease;
	}

	@media (prefers-reduced-motion: reduce) {
		.vp-nav-megamenu {
			transition-duration: 0s, 0s, 0s;
		}
	}
}

/**
 * Mobile (<768px): no hover on touch, so the accordion is always
 * inline instead — it's already only visible at all once the
 * hamburger's own `.vp-primary-nav.is-open` (or the no-JS wrapped
 * list) reveals the "Tools" `<li>` it lives inside, so a second reveal
 * mechanism here would be redundant. Un-gated by `.vp-nav-js-ready` —
 * a no-JS visitor's always-visible wrapped nav list gets the same
 * static, inline treatment.
 */
@media (max-width: 767px) {
	.vp-nav-megamenu {
		display: grid;
		grid-template-columns: repeat(2, 1fr);
		align-items: start;
		gap: 0 0.5rem;
		position: static;
		width: auto;
		max-height: none;
		overflow: visible;
		margin: 0.25rem 0 0.5rem;
		padding: 0;
		background: transparent;
		border: 0;
		box-shadow: none;
	}

	/* An opened category's own tool list needs the full panel width to
	   read comfortably (it's a plain vertical list of tool links, not
	   something that benefits from a 2nd column) — span both grid
	   columns instead of being squeezed into the one it started in. */
	.vp-nav-megamenu__category[open] {
		grid-column: 1 / -1;
	}
}

.vp-nav-megamenu__category {
	border-bottom: 1px solid var(--wp--preset--color--border, #e2e5ea);
}

.vp-nav-megamenu__category:last-child {
	border-bottom: 0;
}

.vp-nav-megamenu__category-summary {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	padding: 0.55rem 0.25rem;
	list-style: none;
	cursor: pointer;
	font-weight: 600;
	font-size: 0.875rem;
	color: var(--wp--preset--color--contrast, #14181f);
}

.vp-nav-megamenu__category-summary::-webkit-details-marker {
	display: none;
}

.vp-nav-megamenu__category-summary:hover {
	color: var(--wp--preset--color--primary, #1d4ed8);
}

.vp-nav-megamenu__category-summary .vp-tool-icon {
	width: 20px;
	height: 20px;
	flex-shrink: 0;
}

.vp-nav-megamenu__category-name {
	flex: 1;
}

.vp-nav-megamenu__chevron {
	flex-shrink: 0;
	opacity: 0.6;
	transition: transform 0.15s ease;
}

.vp-nav-megamenu__category[open] .vp-nav-megamenu__chevron {
	transform: rotate(180deg);
}

.vp-nav-megamenu__tools {
	list-style: none;
	margin: 0 0 0.5rem;
	padding-left: 0.25rem;
	display: flex;
	flex-direction: column;
	gap: 0.1rem;
}

/**
 * A small colored horizontal dash per tool link (was a round dot —
 * direct follow-up feedback asked for a short line instead) — "li
 * items" that read as a real list, not a plain block of wrapped text.
 * A full icon per tool (up to 10 × 11 categories = 110 icons in this
 * one menu) was the other option offered; a dash keeps the list light
 * and scannable and reuses the SAME category-accent color already on
 * that category's own header/icon just above, so the color itself
 * still carries meaning (which category this tool belongs to) rather
 * than being arbitrary decoration. The "View all N" link is excluded
 * via its own `vp-nav-megamenu__viewall-item` class — it isn't a tool.
 */
.vp-nav-megamenu__tools li {
	position: relative;
}

.vp-nav-megamenu__tools li:not(.vp-nav-megamenu__viewall-item)::before {
	content: "";
	position: absolute;
	top: 50%;
	left: 0.45rem;
	width: 8px;
	height: 2px;
	border-radius: 1px;
	transform: translateY(-50%);
	background: var(--wp--preset--color--border, #e2e5ea);
}

/* Each selector repeats the base rule's `:not(.vp-nav-megamenu__viewall-
   item)` on purpose — without it, these lose to the base rule above on
   specificity alone (that `:not()` counts as an extra class, which the
   original single-class version of these rules didn't have), confirmed
   live: every bullet rendered the plain gray fallback regardless of
   category. */
.vp-nav-megamenu__tools--accent-dev li:not(.vp-nav-megamenu__viewall-item)::before { background: var(--wp--preset--color--accent-dev, #4338ca); }
.vp-nav-megamenu__tools--accent-calc li:not(.vp-nav-megamenu__viewall-item)::before { background: var(--wp--preset--color--accent-calc, #0f766e); }
.vp-nav-megamenu__tools--accent-files li:not(.vp-nav-megamenu__viewall-item)::before { background: var(--wp--preset--color--accent-files, #7c3aed); }
.vp-nav-megamenu__tools--accent-image li:not(.vp-nav-megamenu__viewall-item)::before { background: var(--wp--preset--color--accent-image, #be185d); }
.vp-nav-megamenu__tools--accent-finance li:not(.vp-nav-megamenu__viewall-item)::before { background: var(--wp--preset--color--accent-finance, #78350f); }
.vp-nav-megamenu__tools--accent-utility li:not(.vp-nav-megamenu__viewall-item)::before { background: var(--wp--preset--color--accent-utility, #0369a1); }
.vp-nav-megamenu__tools--accent-ai li:not(.vp-nav-megamenu__viewall-item)::before { background: var(--wp--preset--color--accent-ai, #c2410c); }
.vp-nav-megamenu__tools--accent-weather li:not(.vp-nav-megamenu__viewall-item)::before { background: var(--wp--preset--color--accent-weather, #0e7490); }
.vp-nav-megamenu__tools--accent-web li:not(.vp-nav-megamenu__viewall-item)::before { background: var(--wp--preset--color--accent-web, #15803d); }
.vp-nav-megamenu__tools--accent-security li:not(.vp-nav-megamenu__viewall-item)::before { background: var(--wp--preset--color--accent-security, #b91c1c); }
.vp-nav-megamenu__tools--accent-map li:not(.vp-nav-megamenu__viewall-item)::before { background: var(--wp--preset--color--accent-map, #a16207); }
.vp-nav-megamenu__tools--accent-device li:not(.vp-nav-megamenu__viewall-item)::before { background: var(--wp--preset--color--accent-device, #1e3a8a); }

.vp-nav-megamenu__tools a {
	display: block;
	padding: 0.3rem 0.5rem 0.3rem 1.15rem;
	border-radius: var(--wp--custom--radius--small, 4px);
	font-size: 0.8125rem;
	color: var(--wp--preset--color--contrast, #14181f);
	text-decoration: none;
}

.vp-nav-megamenu__tools a:hover {
	background: var(--wp--preset--color--surface, #f5f6f8);
	color: var(--wp--preset--color--primary, #1d4ed8);
}

.vp-nav-megamenu__viewall {
	padding-left: 0.5rem;
	font-weight: 600;
	color: var(--wp--preset--color--primary, #1d4ed8) !important;
}

.vp-breadcrumbs {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0.4em;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	margin-bottom: var(--wp--preset--spacing--40, 1rem);
	opacity: 0.75;
}

.vp-breadcrumbs__link {
	text-decoration: none;
}

.vp-breadcrumbs__link:hover {
	text-decoration: underline;
}

.vp-breadcrumbs__current {
	font-weight: 600;
}

.vp-breadcrumbs__separator {
	opacity: 0.5;
}

.vp-related-tools {
	margin-top: var(--wp--preset--spacing--50, 1.5rem);
}

.vp-related-tools__list {
	display: flex;
	flex-wrap: wrap;
	gap: 0.75rem;
	list-style: none;
	margin: 0;
	padding: 0;
}

.vp-related-tools__list li {
	margin: 0;
}

.vp-related-tools__list a {
	display: inline-block;
	padding: 0.6em 1em;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--small, 4px);
	background: var(--wp--preset--color--base, #fff);
	text-decoration: none;
	font-weight: 600;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
}

.vp-related-tools__list a:hover {
	border-color: var(--wp--preset--color--primary, #1d4ed8);
	color: var(--wp--preset--color--primary, #1d4ed8);
}

/**
 * Previous/Next tool navigation (single.html, core `wp:post-navigation-
 * link` — two independent blocks with no shared wrapper of their own).
 * Previously fully unstyled: each rendered as WordPress core's bare
 * default link, stacked as two full-width block-level lines with a large
 * gap between them. Restyled to match the same pill-button treatment
 * `.vp-related-tools__list a` already uses immediately above it on the
 * page, and switched to `inline-block` so the pair sits side by side
 * instead of stacked. `:empty` hides the "Previous" block entirely on
 * the first tool (alphabetically) / "Next" on the last, rather than
 * leaving a dangling empty box — confirmed live that WordPress renders
 * a genuinely empty `<div>` (no whitespace) when there is no adjacent
 * tool in that direction.
 */
.wp-block-post-navigation-link {
	display: inline-block;
	margin-top: var(--wp--preset--spacing--40, 1rem) !important;
	margin-right: 0.75rem;
}

.wp-block-post-navigation-link:empty {
	display: none;
}

.wp-block-post-navigation-link a {
	display: inline-flex;
	align-items: center;
	gap: 0.4em;
	padding: 0.6em 1em;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--small, 4px);
	background: var(--wp--preset--color--base, #fff);
	color: var(--wp--preset--color--contrast, #14181f);
	text-decoration: none;
	font-weight: 600;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	transition: border-color 0.15s ease, color 0.15s ease;
}

.wp-block-post-navigation-link a:hover {
	border-color: var(--wp--preset--color--primary, #1d4ed8);
	color: var(--wp--preset--color--primary, #1d4ed8);
}

@media (prefers-reduced-motion: reduce) {
	.wp-block-post-navigation-link a {
		transition: none;
	}
}

.post-navigation-link-previous a::before {
	content: "\2190";
}

.post-navigation-link-next a::after {
	content: "\2192";
}

/**
 * Wrapping group added in templates/single.html (Previous/Next were
 * previously two ungrouped sibling blocks with no shared parent to
 * center against). Centered per direct feedback, matching the rest of
 * the tool page's now-centered controls.
 */
.vp-tool-pagination {
	text-align: center;
}

.vedant-card {
	background: var(--wp--preset--color--surface);
	border: 1px solid var(--wp--preset--color--border);
	border-radius: var(--wp--custom--radius--base);
	padding: var(--wp--preset--spacing--50, 1.5rem);
}

.vp-btn {
	display: inline-flex;
	align-items: center;
	gap: 0.4em;
	font: inherit;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	font-weight: 600;
	line-height: 1.2;
	padding: 0.6em 1em;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--small, 4px);
	background: var(--wp--preset--color--base, #fff);
	color: var(--wp--preset--color--contrast, #14181f);
	cursor: pointer;
	/* `.vp-btn` is applied to real `<a>` tags all over the site (hero
	   CTAs, "Explore ..."/"Browse all ..." links, card CTAs) with no
	   reset before this — every one of them showed the browser's default
	   link underline through the button styling. One site-wide fix. */
	text-decoration: none;
}

.vp-btn:hover {
	border-color: var(--wp--preset--color--primary, #1d4ed8);
}

/* `.vp-btn` forces `display: inline-flex` above, which silently defeats
   the plain `hidden` attribute's own `display: none` (this is the first
   tool family to toggle `hidden` on a `.vp-btn` element — Typing
   Practice's "Generate Practice Text" button — so this had no prior
   chance to bite). Same guard-per-component convention already used for
   `.vp-api-tool__result[hidden]` etc. above. */
.vp-btn[hidden] {
	display: none;
}

.vp-btn--primary {
	background: var(--wp--preset--color--primary, #1d4ed8);
	border-color: var(--wp--preset--color--primary, #1d4ed8);
	color: var(--wp--preset--color--base, #fff);
}

.vp-btn--primary:hover {
	background: var(--wp--preset--color--primary-dark, #17369e);
	border-color: var(--wp--preset--color--primary-dark, #17369e);
}

.vp-btn--ghost {
	background: transparent;
}

/* Was already used on several tools' markup (Invoice Generator's own
   "+ Add Item"/"Download HTML" buttons among them) with no matching
   rule anywhere in this file — every one of those buttons was silently
   falling back to the plain `.vp-btn` base look. A real "secondary"
   tier: blue-outlined, filled background, between `.vp-btn--primary`
   (solid fill) and `.vp-btn--ghost` (borderless) in visual weight. */
.vp-btn--secondary {
	background: var(--wp--preset--color--base, #fff);
	border-color: var(--wp--preset--color--primary, #1d4ed8);
	color: var(--wp--preset--color--primary, #1d4ed8);
}

.vp-btn--secondary:hover {
	background: var(--wp--preset--color--surface, #f5f6f8);
	border-color: var(--wp--preset--color--primary-dark, #17369e);
	color: var(--wp--preset--color--primary-dark, #17369e);
}

/* "Info" tier — a light, filled-blue tint (not `--secondary`'s plain
   white) for a group of related secondary actions that should read as
   one visual set distinct from a lone `--primary` action next to them
   (Invoice Generator's Print/Download HTML/Download JSON, sitting
   beside the solid-blue Download PDF button). Same `color-mix()`
   tinting technique as the logo dropzone, so it stays correct in dark
   mode with no separate override needed. */
.vp-btn--info {
	background: color-mix(in srgb, var(--wp--preset--color--primary, #1d4ed8) 10%, var(--wp--preset--color--base, #fff));
	border-color: color-mix(in srgb, var(--wp--preset--color--primary, #1d4ed8) 35%, var(--wp--preset--color--border, #e2e5ea));
	color: var(--wp--preset--color--primary, #1d4ed8);
}

.vp-btn--info:hover {
	background: color-mix(in srgb, var(--wp--preset--color--primary, #1d4ed8) 18%, var(--wp--preset--color--base, #fff));
	border-color: var(--wp--preset--color--primary, #1d4ed8);
	color: var(--wp--preset--color--primary-dark, #17369e);
}

.vp-status {
	display: inline-block;
	margin-top: var(--wp--preset--spacing--30, 0.75rem);
	padding: 0.6em 1em;
	border-radius: var(--wp--custom--radius--small, 4px);
	font-size: var(--wp--preset--font-size--small, 0.875rem);
}

.vp-status:empty {
	display: none;
}

.vp-status--success {
	background: var(--wp--preset--color--success-bg, #e7f7ee);
	color: var(--wp--preset--color--success, #15803d);
}

.vp-status--error {
	background: var(--wp--preset--color--danger-bg, #fdecec);
	color: var(--wp--preset--color--danger, #b91c1c);
}

.vp-status--warning {
	background: var(--wp--preset--color--warning-bg, #fef3e2);
	color: var(--wp--preset--color--warning, #92400e);
}

.vp-status--info {
	background: var(--wp--preset--color--surface, #f5f6f8);
	color: var(--wp--preset--color--contrast, #14181f);
}

/**
 * Generic two-panel tool layout (input/output editor + toolbar). Shared
 * by every text-processing tool (JSON Formatter, URL Encoder, and future
 * ones) so each tool module doesn't ship its own copy of this CSS.
 */

/**
 * Workspace card treatment (Prompt G-24-B). `.vp-tool` is the one root
 * wrapper every tool renderer already emits (TextToolRenderer,
 * CalculatorRenderer, ImageToolRenderer, and every bespoke tool like
 * JwtDecoder/HtmlCssJsCompiler/ColorConverter/UnitConverter) — adding
 * the visual weight here, once, makes every one of the 186 tools read
 * as a distinct, elevated "product" on the page instead of blending
 * into the surrounding prose, with zero PHP/JS change to any individual
 * tool module. Deliberately light (thin border, small radius, the
 * theme's own smallest shadow token) rather than a heavy boxed-in
 * treatment — several tool shapes already nest their own `.vedant-card`
 * sub-panels (e.g. Image tools' controls panel), and a heavy outer card
 * would double up against those; verified visually across image,
 * calculator, JWT, and text-formatter shapes before finalizing.
 */
.vp-tool {
	margin-top: var(--wp--preset--spacing--50, 1.5rem);
	padding: var(--wp--preset--spacing--40, 1rem) var(--wp--preset--spacing--40, 1rem) var(--wp--preset--spacing--50, 1.5rem);
	background: var(--wp--preset--color--base, #fff);
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--large, 16px);
	box-shadow: var(--wp--preset--shadow--sm, 0 1px 2px rgba(20, 24, 31, .06));
}

/**
 * Workspace width tier (Prompt 038). `.vp-tool` already carries the
 * `alignwide` class (intended to reach the theme's 1200px `wideSize`),
 * but interactive tool workspaces (code editors, multi-panel layouts,
 * live previews) benefit from more room than plain wide content — so
 * this raises the tier further, to a dedicated `--wp--custom--width--
 * workspace` token (1440px), for `.vp-tool` specifically.
 *
 * The selector is deliberately as specific as the real DOM it targets
 * (`.wp-block-post-content-is-layout-constrained > .alignwide.vp-tool`)
 * — three class selectors, specificity (0,3,0) — so it reliably beats
 * WordPress core's own generated `.is-layout-constrained > .alignwide
 * {max-width: var(--wp--style--global--wide-size)}` rule (specificity
 * (0,2,0)) regardless of stylesheet load order, rather than depending on
 * cascade position alone.
 */
.wp-block-post-content-is-layout-constrained > .alignwide.vp-tool {
	max-width: var(--wp--custom--width--workspace, 1440px);
}

/**
 * Left-aligned AND narrowed to the theme's own `contentSize` (740px, see
 * theme.json), per two rounds of direct screenshot feedback on the
 * Currency Converter page. Round 1 just set `text-align: left`, which
 * fixed the text's alignment but not the BOX: `.vp-tool` is an `alignwide`
 * block (1200px `wideSize`), so an unconstrained full-width note still
 * started at the wide block's own left edge — well left of the page
 * title, the intro paragraph, and the ad slot above it, which are all
 * normal `contentSize`-width blocks centered within that same wide
 * container. Round 2's screenshot ("start with here", pointing at the
 * title/paragraph's left edge) made the actual ask clear: match THAT
 * position, not the wide block's own. Measured live at 1600px viewport
 * to confirm the fix lands exactly on target: intro paragraph left edge
 * 422.5px, this note's left edge (after adding the 740px max-width +
 * centering below) 422.5px — exact match, not approximate.
 */
.vp-tool__privacy-note {
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	opacity: 0.75;
	margin-bottom: var(--wp--preset--spacing--40, 1rem);
	text-align: left;
	max-width: var(--wp--style--global--content-size, 740px);
	margin-inline: auto;
}

.vp-tool-toolbar {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: 0.5rem;
	margin-bottom: var(--wp--preset--spacing--40, 1rem);
}

/**
 * ~44px touch target (Prompt 043, Phase G; Image to PDF's own primary
 * "Choose Images" button added Prompt 044 after a fresh measurement
 * found it still at 36px — outside the toolbar-scoped rule below since
 * it lives inside the drop zone label, not `.vp-tool-toolbar`). A tool's
 * own action buttons (Format/Convert/Calculate/Decode/Run/Choose Images
 * and their Clear/Reset/Copy/Download siblings) are what a visitor
 * repeatedly taps, so they get the same target Prompt 040 already gave
 * the homepage's large CTAs. Deliberately NOT applied to the small
 * secondary "Copy" button inside a JWT/Compiler panel header, which
 * stays compact by design — a tertiary micro-action beside a label, not
 * a primary control.
 */
.vp-tool-toolbar .vp-btn,
.vp-calculator__actions .vp-btn,
.vp-api-tool__actions .vp-btn,
.vp-i2p-dropzone__button,
.vp-upload-dropzone__button {
	min-height: 44px;
}

.vp-tool-panels {
	display: grid;
	grid-template-columns: 1fr;
	gap: var(--wp--preset--spacing--40, 1rem);
}

@media (min-width: 782px) {
	.vp-tool-panels {
		grid-template-columns: 1fr 1fr;
	}
}

.vp-tool-panel {
	/* Grid items default to min-width: auto, which floors their width at
	   their content's min-content size. Without this, a long unbroken
	   string inside the panel could force the grid track (and the page)
	   wider than the viewport instead of wrapping/scrolling internally. */
	min-width: 0;
}

.vp-tool-panel label {
	display: block;
	font-weight: 600;
	margin-bottom: 0.4em;
}

.vp-tool-editor {
	box-sizing: border-box;
	width: 100%;
	min-height: 320px;
	resize: vertical;
	overflow: auto;
	white-space: pre;
	font-family: var(--wp--preset--font-family--mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	line-height: 1.5;
	padding: 0.85em;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--base, 8px);
	background: var(--wp--preset--color--surface, #f5f6f8);
	color: var(--wp--preset--color--contrast, #14181f);
}

.vp-tool-editor[readonly] {
	background: var(--wp--preset--color--base, #fff);
}

/**
 * Category-accent visual identity for a tool's own interface (Prompt
 * 043) — extends the card system's existing "3px accent border"
 * convention (Prompt 042) into the tool workspace itself: a left-border
 * stripe on code/text editors (a familiar "active file" IDE motif) and a
 * top-border accent on card-shaped sub-panels, keyed by a
 * `.vp-tool--accent-*` modifier set server-side on the tool's own root
 * container from its real `tool_category` (see
 * `TextToolRenderer`/`CalculatorRenderer`/`JwtDecoder`/
 * `HtmlCssJsCompiler`/`ImageToPdf`). Purely a supplementary color cue —
 * every panel it touches already has its own text label, so nothing here
 * is the sole carrier of meaning.
 */
.vp-tool--accent-dev .vp-tool-editor {
	border-left-width: 3px;
	border-left-color: var(--wp--preset--color--accent-dev, #4338ca);
}

/* CodeMirror replaces the `.vp-tool-editor` textarea with its own
   `.CodeMirror.vp-code-editor` sibling element (and hides the original) —
   the accent border above is invisible in that, by far the more common,
   case unless it's repeated on the element people actually see. */
.vp-tool--accent-dev .CodeMirror.vp-code-editor {
	border-left-width: 3px;
	border-left-color: var(--wp--preset--color--accent-dev, #4338ca);
}

.vp-tool--accent-dev .vedant-card,
.vp-tool--accent-dev .vp-compiler-preview__frame-wrap {
	border-top: 3px solid var(--wp--preset--color--accent-dev, #4338ca);
}

.vp-tool--accent-calc .vp-calculator__results {
	border-top: 3px solid var(--wp--preset--color--accent-calc, #0f766e);
}

.vp-tool--accent-files .vp-i2p-dropzone,
.vp-tool--accent-files .vp-upload-dropzone,
.vp-tool--accent-files .vedant-card {
	border-top: 3px solid var(--wp--preset--color--accent-files, #7c3aed);
}

.vp-tool--accent-image .vp-i2p-dropzone,
.vp-tool--accent-image .vp-upload-dropzone,
.vp-tool--accent-image .vedant-card {
	border-top: 3px solid var(--wp--preset--color--accent-image, #be185d);
}

.vp-tool--accent-finance .vp-calculator__results {
	border-top: 3px solid var(--wp--preset--color--accent-finance, #78350f);
}

.vp-tool--accent-utility .vp-calculator__results,
.vp-tool--accent-utility .vp-api-tool__result {
	border-top: 3px solid var(--wp--preset--color--accent-utility, #0369a1);
}

.vp-tool--accent-ai .vp-calculator__results,
.vp-tool--accent-ai .vp-api-tool__result {
	border-top: 3px solid var(--wp--preset--color--accent-ai, #c2410c);
}

.vp-tool--accent-ai .vp-tool-editor {
	border-left-width: 3px;
	border-left-color: var(--wp--preset--color--accent-ai, #c2410c);
}

.vp-tool--accent-weather .vp-calculator__results,
.vp-tool--accent-weather .vp-api-tool__result {
	border-top: 3px solid var(--wp--preset--color--accent-weather, #0e7490);
}

.vp-tool--accent-weather .vp-tool-editor {
	border-left-width: 3px;
	border-left-color: var(--wp--preset--color--accent-weather, #0e7490);
}

.vp-tool--accent-web .vp-calculator__results,
.vp-tool--accent-web .vp-api-tool__result {
	border-top: 3px solid var(--wp--preset--color--accent-web, #15803d);
}

.vp-tool--accent-web .vp-tool-editor {
	border-left-width: 3px;
	border-left-color: var(--wp--preset--color--accent-web, #15803d);
}

.vp-tool--accent-security .vp-calculator__results,
.vp-tool--accent-security .vp-api-tool__result {
	border-top: 3px solid var(--wp--preset--color--accent-security, #b91c1c);
}

.vp-tool--accent-map .vp-calculator__results,
.vp-tool--accent-map .vp-api-tool__result {
	border-top: 3px solid var(--wp--preset--color--accent-map, #a16207);
}

.vp-tool--accent-map .vp-tool-editor {
	border-left-width: 3px;
	border-left-color: var(--wp--preset--color--accent-map, #a16207);
}

.vp-tool--accent-device .vp-calculator__results,
.vp-tool--accent-device .vp-api-tool__result {
	border-top: 3px solid var(--wp--preset--color--accent-device, #1e3a8a);
}

.vp-tool--accent-device .vp-tool-editor {
	border-left-width: 3px;
	border-left-color: var(--wp--preset--color--accent-device, #1e3a8a);
}

.vp-tool--accent-security .vp-tool-editor {
	border-left-width: 3px;
	border-left-color: var(--wp--preset--color--accent-security, #b91c1c);
}

/**
 * Advertisement slot placeholders. Development-only visual — a future ad
 * provider replaces the content via a PHP filter, not by editing this CSS
 * or any template; see includes/Ads/Ad.php.
 */

.vp-ad {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 0.35rem;
	width: 100%;
	box-sizing: border-box;
	margin: var(--wp--preset--spacing--40, 1rem) auto;
	padding: 1rem;
	border: 1px dashed var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--base, 8px);
	background: var(--wp--preset--color--surface, #f5f6f8);
	color: var(--wp--preset--color--contrast, #14181f);
	text-align: center;
}

.vp-ad__label {
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	font-weight: 600;
	letter-spacing: 0.05em;
	text-transform: uppercase;
	opacity: 0.6;
}

.vp-ad__placeholder-text {
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	opacity: 0.5;
}

.vp-ad__dimensions {
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	font-family: var(--wp--preset--font-family--mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
	opacity: 0.5;
}

.vp-ad__responsive-note {
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	opacity: 0.4;
}

.vp-ad--leaderboard {
	max-width: 728px;
	min-height: 90px;
}

.vp-ad--rectangle {
	max-width: 300px;
	min-height: 250px;
}

@media (max-width: 480px) {
	.vp-ad--leaderboard,
	.vp-ad--rectangle {
		min-height: 100px;
	}
}

/**
 * Sidebar / anchor / popup ad slots (Ads\Ad `behavior: sidebar|anchor|
 * popup`) — chrome-level placements auto-injected via `wp_footer`, not
 * shortcode-placed in content. Header is `z-index: 30`; these are layered
 * around it: sidebars sit just below it (30 > 25) so the sticky header
 * always wins if anything ever overlapped it, the anchor bar sits above
 * it (bottom of the screen, never actually adjacent to the header so this
 * only matters in principle), and the popup is topmost as the one true
 * modal overlay.
 */

/**
 * Sticky sidebar rails. Pure CSS — no JS needed. Hidden by default and
 * only shown at `min-width: 1900px`, a real measured breakpoint, not a
 * guess: `.vp-tool`'s own workspace caps at 1440px
 * (`--wp--custom--width--workspace`), so a 160px skyscraper needs
 * (viewport - 1440) / 2 to clear both the workspace edge and a
 * comfortable margin from the viewport edge — confirmed at 1920px this
 * leaves the ad room without touching tool content; at common laptop
 * widths (1366-1440px) there is genuinely no free space for this format,
 * so it stays hidden there rather than overlapping content.
 */
.vp-ad--sidebar {
	display: none;
	position: fixed;
	top: 50%;
	/**
	 * The base `.vp-ad` rule sets `width: 100%` (correct for an inline ad
	 * flowing in content, where 100% means "of its parent container").
	 * For a `position: fixed` element that resolves against the viewport
	 * instead — confirmed live, it stretched to ~1905px wide before this
	 * override. A fixed pixel width (matching the skyscraper profile,
	 * not `max-width`) is what's actually needed here.
	 */
	width: 160px;
	transform: translateY(-50%);
	z-index: 25;
	margin: 0;
}

.vp-ad--sidebar-left {
	left: 16px;
}

.vp-ad--sidebar-right {
	right: 16px;
}

@media (min-width: 1900px) {
	.vp-ad--sidebar {
		display: flex;
	}
}

/**
 * Slide-up bottom banner ("anchor ad"). Starts `hidden` server-side;
 * assets/js/ads/ad-chrome.js reveals it (adds `.is-visible`) after its
 * delay, subject to its dismiss cooldown. `transform`-based reveal (not
 * `display`/height) so it never causes layout shift in the rest of the
 * page — it's an overlay, not part of document flow.
 */
.vp-ad-anchor {
	position: fixed;
	left: 0;
	right: 0;
	bottom: 0;
	z-index: 40;
	display: flex;
	align-items: center;
	gap: 0.75rem;
	padding: 0.6rem 1rem;
	background: var(--wp--preset--color--base, #fff);
	border-top: 1px solid var(--wp--preset--color--border, #e2e5ea);
	box-shadow: var(--wp--preset--shadow--md, 0 4px 16px rgba(20, 24, 31, .08));
	transform: translateY(100%);
	transition: transform 0.3s ease;
}

.vp-ad-anchor[hidden] {
	display: none;
}

.vp-ad-anchor.is-visible {
	transform: translateY(0);
}

@media (prefers-reduced-motion: reduce) {
	.vp-ad-anchor {
		transition: none;
	}
}

.vp-ad-anchor .vp-ad--anchor {
	flex: 1 1 auto;
	min-width: 0;
	margin: 0;
	padding: 0;
	border: 0;
	background: transparent;
	text-align: left;
}

.vp-ad-anchor__close {
	flex-shrink: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	width: 44px;
	height: 44px;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--small, 4px);
	background: var(--wp--preset--color--base, #fff);
	color: var(--wp--preset--color--contrast, #14181f);
	cursor: pointer;
}

.vp-ad-anchor__close:hover {
	border-color: var(--wp--preset--color--primary, #1d4ed8);
	color: var(--wp--preset--color--primary, #1d4ed8);
}

@media (max-width: 480px) {
	.vp-ad-anchor .vp-ad__responsive-note {
		display: none;
	}
}

/**
 * Delayed popup — a centered, dismissible card with a dimmed backdrop.
 * Deliberately NOT a full-screen "vignette" unit (see docs/ARCHITECTURE.md
 * — Google's own interstitial policy research done for this feature).
 * Starts `hidden` server-side; assets/js/ads/ad-chrome.js reveals it once,
 * after its delay, and only if the anchor bar isn't currently visible.
 */
.vp-ad-popup {
	position: fixed;
	inset: 0;
	z-index: 50;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 1rem;
}

.vp-ad-popup[hidden] {
	display: none;
}

.vp-ad-popup__backdrop {
	position: absolute;
	inset: 0;
	background: rgba(20, 24, 31, 0.6);
	opacity: 0;
	transition: opacity 0.2s ease;
}

.vp-ad-popup.is-visible .vp-ad-popup__backdrop {
	opacity: 1;
}

.vp-ad-popup__card {
	position: relative;
	width: 100%;
	max-width: 340px;
	background: var(--wp--preset--color--base, #fff);
	border-radius: var(--wp--custom--radius--base, 8px);
	box-shadow: var(--wp--preset--shadow--lg, 0 12px 32px rgba(20, 24, 31, .12));
	padding: 1.25rem 1rem 1rem;
	transform: scale(0.95);
	opacity: 0;
	transition: transform 0.2s ease, opacity 0.2s ease;
}

.vp-ad-popup.is-visible .vp-ad-popup__card {
	transform: scale(1);
	opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
	.vp-ad-popup__backdrop,
	.vp-ad-popup__card {
		transition: none;
	}
}

.vp-ad-popup__close {
	position: absolute;
	top: 0.5rem;
	right: 0.5rem;
	display: flex;
	align-items: center;
	justify-content: center;
	width: 44px;
	height: 44px;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: 999px;
	background: var(--wp--preset--color--base, #fff);
	color: var(--wp--preset--color--contrast, #14181f);
	cursor: pointer;
}

.vp-ad-popup__close:hover {
	border-color: var(--wp--preset--color--primary, #1d4ed8);
	color: var(--wp--preset--color--primary, #1d4ed8);
}

.vp-ad-popup .vp-ad--popup {
	margin: 1.75rem 0 0;
	border: 0;
	background: transparent;
	padding: 0;
}

/**
 * Tool discovery: the search + category filter bar on the tool archive.
 * See Tools\ToolArchive.
 */

.vp-tool-filters {
	display: flex;
	flex-wrap: wrap;
	align-items: end;
	gap: 1rem;
	margin-block: var(--wp--preset--spacing--40, 1rem);
	padding: var(--wp--preset--spacing--40, 1rem);
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--base, 8px);
	background: var(--wp--preset--color--surface, #f5f6f8);
}

.vp-tool-filters__field {
	display: flex;
	flex-direction: column;
	gap: 0.35em;
	flex: 1 1 200px;
	min-width: 0;
}

.vp-tool-filters__field label {
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	font-weight: 600;
}

.vp-tool-filters__field input,
.vp-tool-filters__field select {
	width: 100%;
	box-sizing: border-box;
	font: inherit;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	padding: 0.6em 0.75em;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--small, 4px);
	background: var(--wp--preset--color--base, #fff);
	color: var(--wp--preset--color--contrast, #14181f);
}

.vp-tool-filters__actions {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem;
}

/**
 * Calculator UI (Tools\Calculators\CalculatorRenderer). A different
 * shape from the two-panel text-tool layout above — a vertical form,
 * not side-by-side editors — so it gets its own dedicated rules rather
 * than reusing .vp-tool-panels/.vp-tool-editor.
 */

/**
 * Centered within the wide `.vp-tool` workspace (up to 1440px) rather
 * than left-aligned — reported live: a calculator's actual form content
 * is far narrower than the two-panel editor workspace width, so without
 * centering it sat flush-left with a large dead area to its right. This
 * also leaves the intended future space for left/right sidebar ad slots
 * symmetric on both sides once those are built, instead of only ever
 * having room on one side.
 */
.vp-calculator__form {
	display: flex;
	flex-direction: column;
	gap: var(--wp--preset--spacing--40, 1rem);
	max-width: 480px;
	margin-inline: auto;
}

.vp-calculator__fields {
	display: flex;
	flex-direction: column;
	gap: var(--wp--preset--spacing--40, 1rem);
}

.vp-calculator__field {
	display: flex;
	flex-direction: column;
	gap: 0.35em;
}

.vp-calculator__field label {
	font-weight: 600;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
}

.vp-calculator__field input,
.vp-calculator__field select {
	box-sizing: border-box;
	width: 100%;
	font: inherit;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	padding: 0.6em 0.75em;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--small, 4px);
	background: var(--wp--preset--color--base, #fff);
	color: var(--wp--preset--color--contrast, #14181f);
}

.vp-calculator__help {
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	opacity: 0.6;
}

.vp-calculator__actions {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem;
}

.vp-calculator__error {
	color: var(--wp--preset--color--danger, #b91c1c);
	font-weight: 600;
	margin-top: var(--wp--preset--spacing--30, 0.75rem);
}

.vp-calculator .vp-calculator__error {
	max-width: 480px;
	margin-inline: auto;
}

.vp-calculator__error:empty {
	display: none;
}

.vp-calculator__results {
	margin-top: var(--wp--preset--spacing--50, 1.5rem);
	margin-inline: auto;
	max-width: 480px;
	padding: var(--wp--preset--spacing--40, 1rem);
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--base, 8px);
	background: var(--wp--preset--color--surface, #f5f6f8);
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
}

.vp-calculator__results[hidden] {
	display: none;
}

.vp-calculator__result {
	display: flex;
	justify-content: space-between;
	align-items: baseline;
	gap: 1rem;
	flex-wrap: wrap;
}

.vp-calculator__result-label {
	font-weight: 600;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	opacity: 0.75;
}

/**
 * Result emphasis (Prompt 043, Phase E/K) — a calculator's whole reason
 * for existing is this number, so it gets the strongest single piece of
 * typography on the page: larger than any heading in the tool interface,
 * tabular-nums so a changing result doesn't visually jiggle horizontally,
 * tinted with the calculator category accent (a restrained color cue,
 * not a giant colored panel).
 */
.vp-calculator__result-value {
	font-family: var(--wp--preset--font-family--mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
	font-variant-numeric: tabular-nums;
	font-weight: 700;
	font-size: 1.75rem;
	color: var(--wp--preset--color--accent-calc, #0f766e);
}

/**
 * Financial Calculator principal-vs-interest donut chart (Prompt 057,
 * direct feedback — drawn by `_shared/calculator-chart.js` into the
 * `.vp-calculator__chart` container `CalculatorRenderer` emits when a
 * calculator opts in via `chart: true`). Stacks chart-then-legend on
 * narrow screens by default; sits side by side once there's room for
 * both without cramping either.
 */
.vp-calculator__chart {
	margin-top: var(--wp--preset--spacing--40, 1rem);
	padding-top: var(--wp--preset--spacing--40, 1rem);
	border-top: 1px solid var(--wp--preset--color--border, #e2e5ea);
}

.vp-calc-chart {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: center;
	gap: 1.25rem;
}

.vp-calc-chart__svg {
	flex-shrink: 0;
	color: var(--wp--preset--color--border, #e2e5ea);
}

.vp-calc-chart__legend {
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
}

.vp-calc-chart__legend-item {
	display: flex;
	align-items: center;
	gap: 0.5em;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	font-variant-numeric: tabular-nums;
}

.vp-calc-chart__dot {
	width: 12px;
	height: 12px;
	border-radius: 50%;
	flex-shrink: 0;
}

/**
 * API tool (Tools\ApiToolRenderer, Prompt 050) — a form + async result
 * shape, deliberately similar field styling to `.vp-calculator__*` (same
 * visual language, this site's tools all look related) but with its own
 * class names since the shape differs: a source-of-data disclosure line
 * (`__source-note`), a loading state, and a result container each tool's
 * own JS fills with bespoke markup instead of a fixed set of labeled rows.
 */
.vp-api-tool__source-note {
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	opacity: 0.75;
	margin-top: -0.5rem;
	text-align: left;
	max-width: var(--wp--style--global--content-size, 740px);
	margin-inline: auto;
}

.vp-api-tool__source-note a {
	color: inherit;
}

.vp-api-tool__form {
	display: flex;
	flex-direction: column;
	gap: var(--wp--preset--spacing--40, 1rem);
	max-width: 480px;
	margin-inline: auto;
}

.vp-api-tool__fields {
	display: flex;
	flex-direction: column;
	gap: var(--wp--preset--spacing--40, 1rem);
}

.vp-api-tool__field {
	display: flex;
	flex-direction: column;
	gap: 0.35em;
}

.vp-api-tool__field label {
	font-weight: 600;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
}

.vp-api-tool__field input,
.vp-api-tool__field select,
.vp-api-tool__field textarea {
	box-sizing: border-box;
	width: 100%;
	font: inherit;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	padding: 0.6em 0.75em;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--small, 4px);
	background: var(--wp--preset--color--base, #fff);
	color: var(--wp--preset--color--contrast, #14181f);
}

.vp-api-tool__field textarea {
	font-family: var(--wp--preset--font-family--mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
	resize: vertical;
}

.vp-api-tool__help {
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	opacity: 0.6;
}

/**
 * City-suggestion combobox dropdown (Weather's autocomplete, added after
 * launch per direct feedback — see weather.js's own docblock for why this
 * lives here rather than as a new shared `_shared/` file). Only the field
 * a tool opts into (via `vp-api-tool__field--autocomplete`) gets
 * `position: relative`, so this costs nothing on the other API tools'
 * plain text fields.
 */
.vp-api-tool__field--autocomplete {
	position: relative;
}

.vp-autocomplete-list {
	position: absolute;
	top: 100%;
	left: 0;
	right: 0;
	z-index: 20;
	margin: 0.25rem 0 0;
	padding: 0.25rem;
	list-style: none;
	max-height: 240px;
	overflow-y: auto;
	box-sizing: border-box;
	background: var(--wp--preset--color--base, #fff);
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--base, 8px);
	box-shadow: 0 8px 24px rgba(20, 24, 31, 0.14);
}

.vp-autocomplete-list[hidden] {
	display: none;
}

.vp-autocomplete-option {
	display: flex;
	align-items: baseline;
	gap: 0.35em;
	padding: 0.55em 0.7em;
	border-radius: var(--wp--custom--radius--small, 4px);
	cursor: pointer;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
}

.vp-autocomplete-option:hover,
.vp-autocomplete-option--active {
	background: var(--wp--preset--color--surface, #f5f6f8);
}

.vp-autocomplete-option__meta {
	opacity: 0.6;
	font-size: 0.85em;
}

.vp-api-tool__actions {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem;
}

.vp-api-tool__error {
	color: var(--wp--preset--color--danger, #b91c1c);
	font-weight: 600;
	margin-top: var(--wp--preset--spacing--30, 0.75rem);
	max-width: 480px;
	margin-inline: auto;
}

.vp-api-tool__error:empty {
	display: none;
}

.vp-api-tool__loading {
	margin-top: var(--wp--preset--spacing--30, 0.75rem);
	max-width: 480px;
	margin-inline: auto;
	opacity: 0.75;
}

.vp-api-tool__loading[hidden] {
	display: none;
}

/* Matches `.vp-tool__privacy-note`'s own width tier (the theme's
   "content-size" reading column, 740px) — was hard-capped at 560px,
   noticeably narrower than the privacy note/description text directly
   above it on every API-based tool (weather, air quality, map tools,
   IP/PIN lookups, GitHub Analyzer, etc., all sharing this one class),
   confirmed live and reported with a screenshot. */
.vp-api-tool__result {
	margin-top: var(--wp--preset--spacing--50, 1.5rem);
	margin-inline: auto;
	max-width: var(--wp--style--global--content-size, 740px);
	padding: var(--wp--preset--spacing--40, 1rem);
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--base, 8px);
	background: var(--wp--preset--color--surface, #f5f6f8);
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
}

.vp-api-tool__result[hidden] {
	display: none;
}

.vp-api-tool__result h3 {
	margin: 0 0 0.25rem;
}

.vp-api-result-headline {
	font-family: var(--wp--preset--font-family--mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
	font-variant-numeric: tabular-nums;
	font-weight: 700;
	font-size: 1.5rem;
	color: var(--wp--preset--color--accent-utility, #0369a1);
}

.vp-api-result-row {
	display: flex;
	justify-content: space-between;
	align-items: baseline;
	gap: 1rem;
	flex-wrap: wrap;
}

.vp-api-result-row__label {
	font-weight: 600;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	opacity: 0.75;
}

.vp-api-result-row__value {
	font-family: var(--wp--preset--font-family--mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
	font-variant-numeric: tabular-nums;
}

.vp-api-result-note {
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	opacity: 0.7;
	margin: 0.25rem 0 0;
}

.vp-api-result-table {
	width: 100%;
	border-collapse: collapse;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
}

.vp-api-result-table th,
.vp-api-result-table td {
	text-align: left;
	padding: 0.5em 0.6em;
	border-bottom: 1px solid var(--wp--preset--color--border, #e2e5ea);
	word-break: break-word;
}

.vp-api-result-table th {
	font-weight: 600;
	opacity: 0.75;
}

/**
 * API Tester (Modules\ApiTester, Prompt 051) — status-code color cue and
 * a monospace, scrollable response-body block. Reuses `--success`/
 * `--warning`/`--danger` tokens already established for status
 * messaging elsewhere on the site, rather than inventing new colors.
 */
.vp-api-tester-status--ok {
	color: var(--wp--preset--color--success, #15803d);
}

.vp-api-tester-status--redirect {
	color: var(--wp--preset--color--warning, #92400e);
}

.vp-api-tester-status--error {
	color: var(--wp--preset--color--danger, #b91c1c);
}

/**
 * Weather hourly/daily forecast strips (Prompt 052 upgrade). Hourly
 * scrolls horizontally (12 items don't fit any reasonable width);
 * daily stacks as rows — a 7-item vertical list reads better than a
 * cramped 7-column grid at mobile widths.
 */
.vp-wx-hourly {
	display: flex;
	gap: 0.5rem;
	overflow-x: auto;
	padding-bottom: 0.25rem;
}

.vp-wx-hour {
	flex: 0 0 auto;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 0.2rem;
	min-width: 56px;
	padding: 0.5em 0.4em;
	border-radius: var(--wp--custom--radius--small, 4px);
	background: var(--wp--preset--color--surface, #f5f6f8);
	font-size: var(--wp--preset--font-size--small, 0.875rem);
}

.vp-wx-hour__time {
	opacity: 0.7;
	font-size: 0.8em;
}

.vp-wx-hour__temp {
	font-weight: 600;
}

.vp-wx-hour__rain {
	opacity: 0.65;
	font-size: 0.8em;
}

.vp-wx-daily {
	display: flex;
	flex-direction: column;
}

.vp-wx-day {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 0.75rem;
	padding: 0.55em 0;
	border-bottom: 1px solid var(--wp--preset--color--border, #e2e5ea);
	font-size: var(--wp--preset--font-size--small, 0.875rem);
}

.vp-wx-day__date {
	font-weight: 600;
	flex: 1 1 auto;
}

.vp-wx-day__cond {
	opacity: 0.75;
	flex: 1 1 auto;
	text-align: center;
}

.vp-wx-day__temp {
	font-variant-numeric: tabular-nums;
	text-align: right;
}

/**
 * Two-column comparison rows (Weather Comparison, Country Comparison).
 * A 3-column grid — label + value A + value B — rather than a <table>,
 * since it needs to collapse gracefully at mobile widths (label stacks
 * above the two values) which a <table> does much less cleanly.
 */
.vp-wxc-row {
	display: grid;
	grid-template-columns: 1fr 1fr 1fr;
	gap: 0.5rem;
	padding: 0.5em 0;
	border-bottom: 1px solid var(--wp--preset--color--border, #e2e5ea);
	align-items: baseline;
}

.vp-wxc-row--head {
	font-weight: 700;
	border-bottom-width: 2px;
}

.vp-wxc-row__label {
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	opacity: 0.75;
}

.vp-wxc-row__value {
	font-variant-numeric: tabular-nums;
}

@media (max-width: 480px) {
	.vp-wxc-row {
		grid-template-columns: 1fr 1fr;
	}
	.vp-wxc-row__label {
		grid-column: 1 / -1;
		font-weight: 600;
		opacity: 0.6;
	}
}

/**
 * Country flag (vendored flag-icons SVGs, Prompt 054 — see
 * assets/vendor/flag-icons/README.md). A small inline glyph next to a
 * place name, never the sole way a country is identified (a text name
 * always sits beside it) — decorative, matching this project's own
 * `aria-hidden` convention for its tool-category icons.
 */
.vp-flag {
	display: inline-block;
	width: 20px;
	height: 15px;
	vertical-align: middle;
	margin-right: 0.4em;
	border-radius: 2px;
	box-shadow: 0 0 0 1px rgba(20, 24, 31, 0.12);
	object-fit: cover;
}

/**
 * Holiday Calendar month grid (Prompt 055). Standard 7-column CSS grid;
 * each day is a small square that grows a bit to fit a holiday label
 * when one applies — `min-height` rather than a fixed height so normal
 * (non-holiday) days stay compact.
 */
.vp-hc-grid {
	display: grid;
	grid-template-columns: repeat(7, 1fr);
	gap: 2px;
	margin-top: 0.75rem;
}

.vp-hc-grid__weekday {
	text-align: center;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	font-weight: 600;
	opacity: 0.6;
	padding-bottom: 0.35rem;
}

.vp-hc-day {
	min-height: 44px;
	padding: 0.3rem;
	border-radius: var(--wp--custom--radius--small, 4px);
	background: var(--wp--preset--color--surface, #f5f6f8);
	display: flex;
	flex-direction: column;
	gap: 0.15rem;
}

.vp-hc-day--blank {
	background: transparent;
}

.vp-hc-day--holiday {
	background: color-mix(in srgb, var(--wp--preset--color--accent-utility, #0369a1) 16%, var(--wp--preset--color--base, #fff));
}

.vp-hc-day__num {
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	font-weight: 600;
	font-variant-numeric: tabular-nums;
}

.vp-hc-day__label {
	font-size: 0.7rem;
	line-height: 1.2;
	opacity: 0.85;
	overflow: hidden;
	text-overflow: ellipsis;
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
}

@media (max-width: 480px) {
	.vp-hc-grid__weekday {
		font-size: 0.7rem;
	}
	.vp-hc-day {
		min-height: 36px;
		padding: 0.2rem;
	}
	.vp-hc-day__label {
		display: none;
	}
}

/**
 * Searchable-select combobox (Prompt 056, direct feedback — the ~204-
 * country holiday dropdowns needed to be type-to-filter). The visible
 * text input sits where the `<select>` used to visually be; the real
 * `<select>` (hidden, `.vp-api-tool__field select` styling no longer
 * applies to it visually but its `id`/`value` are untouched) lives right
 * after it in the same field wrapper, so `.vp-api-tool__field` already
 * being `position: relative` (Weather's autocomplete, Prompt 052) is
 * reused for free — same dropdown-positioning need, same fix.
 */
.vp-searchable-select {
	position: relative;
}

.vp-searchable-select__input {
	box-sizing: border-box;
	width: 100%;
	font: inherit;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	padding: 0.6em 0.75em;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--small, 4px);
	background: var(--wp--preset--color--base, #fff);
	color: var(--wp--preset--color--contrast, #14181f);
}

.vp-gh-profile-header {
	display: flex;
	align-items: flex-start;
	gap: 0.75rem;
}

.vp-gh-avatar {
	width: 64px;
	height: 64px;
	border-radius: 50%;
	flex-shrink: 0;
}

.vp-gh-repo-list {
	display: flex;
	flex-direction: column;
	gap: 0.75rem;
}

.vp-gh-repo {
	padding: 0.6em 0;
	border-top: 1px solid var(--wp--preset--color--border, #e2e5ea);
}

.vp-gh-repo a {
	font-weight: 600;
}

.vp-gh-repo__meta {
	margin-left: 0.5em;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	opacity: 0.7;
}

.vp-gh-repo__desc {
	margin: 0.25em 0 0;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	opacity: 0.85;
}

.vp-api-tester-body {
	max-height: 400px;
	overflow: auto;
	margin: 0;
	padding: 0.75rem;
	font-family: var(--wp--preset--font-family--mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	white-space: pre-wrap;
	word-break: break-word;
	background: var(--wp--preset--color--base, #fff);
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--small, 4px);
}

/**
 * Code editor (CodeMirror, progressively enhancing a tool's textarea via
 * the shared `_shared/code-editor.js` adapter — see Modules\JsonFormatter
 * and Modules\HtmlFormatter, Prompts 019/020). Purely visual: makes a
 * `.CodeMirror` instance match the existing `.vp-tool-editor` textarea's
 * look (border, radius, font, size) — the actual CodeMirror configuration
 * (mode, addons) is JS. Proven reusable as-is by a second code-editor tool
 * (HTML Formatter) without any changes to these rules.
 */

.CodeMirror.vp-code-editor {
	height: 320px;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--base, 8px);
	font-family: var(--wp--preset--font-family--mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	line-height: 1.5;
}

.CodeMirror.vp-code-editor .CodeMirror-gutters {
	border-radius: var(--wp--custom--radius--base, 8px) 0 0 var(--wp--custom--radius--base, 8px);
}

.CodeMirror.vp-code-editor.vp-code-editor--readonly {
	background: var(--wp--preset--color--base, #fff);
}

.CodeMirror.vp-code-editor.vp-code-editor--readonly .CodeMirror-gutters {
	background: var(--wp--preset--color--base, #fff);
}

/* The line an error was reported on — a supplementary visual cue; the
   human-readable message below the editor is never solely dependent on
   this (screen readers get the message via the existing role="status"
   region, unaffected by this highlight). Shared by every code-editor tool
   (renamed from .vp-json-error-line in Prompt 020 once HTML Formatter
   became a second consumer of the same highlight behavior). */
.vp-editor-error-line {
	background: rgba(185, 28, 28, 0.12);
}

/* Prompt 021: a *best-effort* location (JSON Formatter's local structural
   scanner, used only when the browser's own JSON.parse() error gives no
   position at all) gets a visually distinct amber tint rather than the
   confirmed-location red — color is a supplementary cue only, since the
   status message text ("likely near line N") is what actually
   communicates the reduced certainty to screen readers and colorblind
   users. Combined with .vp-editor-error-line above (both classes are
   always applied together), so this only needs to override the color. */
.vp-editor-error-line.vp-editor-error-line--approximate {
	background: rgba(180, 83, 9, 0.12);
}

/**
 * JWT Decoder & Inspector (Modules\JwtDecoder, Prompt 027). A different
 * shape from the two-panel text tool above — one input, several
 * differently-shaped output sections — so it gets its own small set of
 * rules, reusing `.vedant-card`/`.vp-btn`/`.vp-tool-editor` for anything
 * that already fits.
 */

.vp-jwt-decoder__caution {
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	margin-bottom: var(--wp--preset--spacing--40, 1rem);
	padding: 0.6em 1em;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-left: 3px solid var(--wp--preset--color--warning-accent, #b45309);
	border-radius: var(--wp--custom--radius--small, 4px);
	background: var(--wp--preset--color--surface, #f5f6f8);
}

.vp-jwt-results {
	margin-top: var(--wp--preset--spacing--50, 1.5rem);
	display: flex;
	flex-direction: column;
	gap: var(--wp--preset--spacing--40, 1rem);
}

.vp-jwt-section__header {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: 0.5rem;
	margin-bottom: 0.75em;
}

.vp-jwt-section h2 {
	margin: 0 0 0.75em;
	font-size: var(--wp--preset--font-size--medium, 1rem);
}

.vp-jwt-section__header h2 {
	margin: 0;
}

.vp-jwt-pre {
	box-sizing: border-box;
	width: 100%;
	max-height: 320px;
	overflow: auto;
	margin: 0;
	padding: 0.85em;
	font-family: var(--wp--preset--font-family--mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	line-height: 1.5;
	white-space: pre-wrap;
	word-break: break-word;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--small, 4px);
	background: var(--wp--preset--color--base, #fff);
	color: var(--wp--preset--color--contrast, #14181f);
}

.vp-jwt-claims {
	display: grid;
	grid-template-columns: minmax(0, 1fr);
	gap: 0.5em 1em;
	margin: 0;
}

@media (min-width: 560px) {
	.vp-jwt-claims {
		grid-template-columns: minmax(10em, max-content) 1fr;
	}
}

.vp-jwt-claims dt {
	font-weight: 600;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
}

.vp-jwt-claims dd {
	margin: 0 0 0.5em;
	font-family: var(--wp--preset--font-family--mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	word-break: break-word;
}

@media (min-width: 560px) {
	.vp-jwt-claims dd {
		margin-bottom: 0;
	}
}

.vp-jwt-section--warnings {
	border-color: var(--wp--preset--color--warning-accent, #b45309);
}

.vp-jwt-warnings {
	margin: 0;
	padding-left: 1.25em;
}

.vp-jwt-warnings li {
	color: var(--wp--preset--color--warning, #92400e);
	margin-bottom: 0.4em;
}

.vp-jwt-warnings li:last-child {
	margin-bottom: 0;
}

.vp-jwt-section__note {
	margin: 0.6em 0 0;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	opacity: 0.7;
}

.vp-jwt-download {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: 0.75rem;
}

.vp-jwt-download__actions {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem;
}

/**
 * Image to PDF Converter (Modules\ImageToPdf, Prompt 028). The platform's
 * first file-processing UI — a drop zone, a file list with thumbnails
 * and reordering, and a settings panel — so it gets its own rules,
 * reusing `.vp-btn`/`.vp-status`/`.vp-tool__privacy-note`/`.vedant-card`
 * for anything that already fits.
 */

.vp-i2p-limits {
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	opacity: 0.75;
	margin-bottom: var(--wp--preset--spacing--40, 1rem);
}

/**
 * Single-file drop zone — originally Image to PDF's own `.vp-i2p-*`
 * classes (Prompt 028), now also the shared upload component every new
 * single-image tool uses via the generic `.vp-upload-*` names (Prompt
 * 045). Both class families are listed on every rule rather than
 * renaming Image to PDF's markup/JS (zero regression risk) or
 * duplicating this CSS a second time.
 */
.vp-i2p-dropzone,
.vp-upload-dropzone {
	position: relative;
	/* Matches `.vp-tool__privacy-note`/`.vp-api-tool__result`'s own
	   content-size tier (740px) — previously unset, so the dropzone
	   stretched to the full `.vp-tool` card width while the privacy
	   note/description text and the ad slot directly above it stayed at
	   this narrower reading width, visibly breaking out past both on
	   every upload-based tool page (Document Reader, Merge PDF, PDF to
	   Word/Excel, HEIC to JPG, Remove Background, Favicon Generator,
	   Image to PDF, etc. — every tool sharing this one dropzone
	   component). Confirmed live and reported with a screenshot. */
	max-width: var(--wp--style--global--content-size, 740px);
	margin-inline: auto;
	box-sizing: border-box;
	/**
	 * Light-blue treatment (per direct request) instead of the previous
	 * plain neutral-gray box — a soft tint of the site's own primary
	 * blue via `color-mix()`, the same technique already used for the
	 * category-archive hero background, so this reads as one consistent
	 * design system rather than a one-off color. Reading the same
	 * `--primary`/`--base` custom properties `dark-theme.css` already
	 * redefines means this needs no separate dark-mode rule: the tint
	 * becomes a subtle blue-tinted dark surface automatically.
	 */
	border: 2px dashed color-mix(in srgb, var(--wp--preset--color--primary, #1d4ed8) 35%, var(--wp--preset--color--border, #e2e5ea));
	border-radius: var(--wp--custom--radius--base, 8px);
	background: color-mix(in srgb, var(--wp--preset--color--primary, #1d4ed8) 6%, var(--wp--preset--color--base, #fff));
	/* Taller/more generous than the previous 1.5rem all round — direct
	   reference (a CodePen dropzone) asked for a bigger, icon-led box,
	   not the previous compact single-line-of-text strip. */
	padding: 2.5rem 1.5rem;
	text-align: center;
	transition: border-color 0.15s ease, background 0.15s ease;
}

@media (prefers-reduced-motion: reduce) {
	.vp-i2p-dropzone,
	.vp-upload-dropzone {
		transition: none;
	}
}

.vp-i2p-dropzone--active,
.vp-upload-dropzone--active {
	border-color: var(--wp--preset--color--primary, #1d4ed8);
	/* A visibly deeper blue tint than the default resting state, so
	   dragging a file over the zone reads as a clear state change
	   rather than the border color being the only thing that moves. */
	background: color-mix(in srgb, var(--wp--preset--color--primary, #1d4ed8) 14%, var(--wp--preset--color--base, #fff));
}

/**
 * Vertical stack (icon → text → button) — was a single horizontal row
 * of just text + button before, redesigned to match a direct CodePen
 * reference for what a dropzone should look like: icon-led, generously
 * spaced, each piece its own line rather than crammed into one.
 */
.vp-i2p-dropzone__label,
.vp-upload-dropzone__label {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 0.85rem;
	cursor: pointer;
}

/**
 * The category-specific icon (Tools\ToolIcons::upload_icon_url()) —
 * image-upload.png / pdf.png / defulat-upload.png, self-hosted PNGs
 * supplied as finished artwork (not drawn inline like this project's
 * other icons). Sized generously since it's the focal point of the
 * redesigned box, the same way the reference's own folder glyph reads
 * as the box's main visual anchor before any text.
 */
.vp-i2p-dropzone__icon,
.vp-upload-dropzone__icon {
	width: 64px;
	height: 64px;
	object-fit: contain;
}

.vp-i2p-dropzone__text,
.vp-upload-dropzone__text {
	font-size: var(--wp--preset--font-size--medium, 1rem);
	font-weight: 600;
}

/* Kept in the accessibility tree (focusable, announced by screen
   readers) rather than display:none — only visually hidden, since the
   styled label + button above it is the visible trigger. */
.vp-i2p-file-input,
.vp-upload-file-input {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip: rect(0, 0, 0, 0);
	white-space: nowrap;
	border: 0;
}

.vp-i2p-file-input:focus-visible + .vp-i2p-dropzone__label .vp-i2p-dropzone__button,
.vp-i2p-dropzone__label:focus-within .vp-i2p-dropzone__button,
.vp-upload-file-input:focus-visible + .vp-upload-dropzone__label .vp-upload-dropzone__button,
.vp-upload-dropzone__label:focus-within .vp-upload-dropzone__button {
	outline: 2px solid var(--wp--preset--color--primary, #1d4ed8);
	outline-offset: 2px;
}

.vp-i2p-empty {
	margin: var(--wp--preset--spacing--40, 1rem) 0;
	opacity: 0.6;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
}

.vp-i2p-summary {
	margin: var(--wp--preset--spacing--40, 1rem) 0 0.5em;
	font-weight: 600;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
}

.vp-i2p-list {
	list-style: none;
	margin: 0.5em 0 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
}

.vp-i2p-item {
	display: flex;
	align-items: center;
	gap: 0.75rem;
	padding: 0.6em;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--small, 4px);
	background: var(--wp--preset--color--base, #fff);
}

.vp-i2p-item__thumb {
	width: 56px;
	height: 56px;
	object-fit: cover;
	border-radius: var(--wp--custom--radius--small, 4px);
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	flex-shrink: 0;
	background: var(--wp--preset--color--surface, #f5f6f8);
}

.vp-i2p-item__meta {
	min-width: 0;
	flex: 1 1 auto;
}

.vp-i2p-item__name {
	margin: 0;
	font-weight: 600;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	overflow-wrap: break-word;
	word-break: break-word;
}

.vp-i2p-item__detail {
	margin: 0.2em 0 0;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	opacity: 0.7;
}

.vp-i2p-item__actions {
	display: flex;
	flex-wrap: wrap;
	gap: 0.35rem;
	flex-shrink: 0;
}

.vp-i2p-item__move {
	padding-inline: 0.7em;
}

.vp-i2p-settings {
	margin-top: var(--wp--preset--spacing--50, 1.5rem);
}

.vp-i2p-settings__grid {
	display: grid;
	grid-template-columns: 1fr;
	gap: var(--wp--preset--spacing--40, 1rem);
}

@media (min-width: 560px) {
	.vp-i2p-settings__grid {
		grid-template-columns: repeat(auto-fit, minmax(11em, 1fr));
	}
}

.vp-i2p-settings__field {
	display: flex;
	flex-direction: column;
	gap: 0.35em;
}

.vp-i2p-settings__field label {
	font-weight: 600;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
}

.vp-i2p-settings__field select {
	width: 100%;
	box-sizing: border-box;
	font: inherit;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	padding: 0.6em 0.75em;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--small, 4px);
	background: var(--wp--preset--color--base, #fff);
	color: var(--wp--preset--color--contrast, #14181f);
}

.vp-i2p-settings__note {
	margin: 0.75em 0 0;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	opacity: 0.7;
}

/**
 * Shared single-image tool shell (`Tools\ImageToolRenderer`, Prompt 045)
 * — Image Compressor/Resizer/Cropper/Rotator/Watermark and the two
 * Convert tools all share this exact "upload → preview → tool-specific
 * controls → action → result" shape, reusing the `.vp-upload-dropzone*`
 * classes above for the upload step and this block for everything after
 * it, rather than seven near-identical copies.
 */
.vp-img-limits {
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	opacity: 0.75;
	margin-bottom: var(--wp--preset--spacing--40, 1rem);
}

/* Document Reader's own limits line — explicit left alignment,
   independent of whatever width `.vp-tool` ends up at. */
.vp-document-reader__limits {
	text-align: left;
}

.vp-img-preview-section {
	margin-top: var(--wp--preset--spacing--50, 1.5rem);
}

.vp-img-preview {
	display: flex;
	flex-wrap: wrap;
	align-items: flex-start;
	gap: 1rem;
	margin-bottom: var(--wp--preset--spacing--40, 1rem);
}

.vp-img-preview__image {
	max-width: 220px;
	max-height: 220px;
	width: auto;
	height: auto;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--base, 8px);
	background: var(--wp--preset--color--surface, #f5f6f8);
	/* Checkerboard so a transparent PNG/WebP's own alpha is visible
	   against the preview rather than silently blending into the page
	   background — the same reason image editors show one. */
	background-image:
		linear-gradient(45deg, var(--wp--preset--color--border, #e2e5ea) 25%, transparent 25%),
		linear-gradient(-45deg, var(--wp--preset--color--border, #e2e5ea) 25%, transparent 25%),
		linear-gradient(45deg, transparent 75%, var(--wp--preset--color--border, #e2e5ea) 75%),
		linear-gradient(-45deg, transparent 75%, var(--wp--preset--color--border, #e2e5ea) 75%);
	background-size: 16px 16px;
	background-position: 0 0, 0 8px, 8px -8px, -8px 0;
}

.vp-img-preview__meta {
	flex: 1 1 200px;
	min-width: 0;
	margin: 0;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	opacity: 0.75;
	overflow-wrap: break-word;
	word-break: break-word;
}

.vp-img-preview__meta strong {
	display: block;
	opacity: 1;
	font-weight: 600;
	margin-bottom: 0.2em;
}

.vp-img-controls {
	margin-bottom: var(--wp--preset--spacing--40, 1rem);
}

.vp-img-controls h2 {
	margin: 0 0 0.75em;
	font-size: var(--wp--preset--font-size--medium, 1rem);
}

.vp-img-field {
	display: flex;
	flex-direction: column;
	gap: 0.35em;
	margin-bottom: var(--wp--preset--spacing--40, 1rem);
}

.vp-img-field:last-child {
	margin-bottom: 0;
}

.vp-img-field label {
	font-weight: 600;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
}

.vp-img-field input[type="number"],
.vp-img-field input[type="text"],
.vp-img-field input[type="range"],
.vp-img-field select {
	width: 100%;
	box-sizing: border-box;
	font: inherit;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	padding: 0.6em 0.75em;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--small, 4px);
	background: var(--wp--preset--color--base, #fff);
	color: var(--wp--preset--color--contrast, #14181f);
}

.vp-img-field input[type="range"] {
	padding: 0;
	border: 0;
	background: transparent;
}

.vp-img-field--inline {
	flex-direction: row;
	align-items: center;
	gap: 0.5rem;
}

.vp-img-field--row {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 0.75rem;
}

.vp-img-field__value {
	font-family: var(--wp--preset--font-family--mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	opacity: 0.75;
}

.vp-img-result {
	margin-top: var(--wp--preset--spacing--50, 1.5rem);
}

.vp-img-result[hidden] {
	display: none;
}

.vp-img-result__image {
	display: block;
	max-width: 100%;
	height: auto;
	margin-bottom: var(--wp--preset--spacing--40, 1rem);
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--base, 8px);
	background: var(--wp--preset--color--surface, #f5f6f8);
}

.vp-img-result__meta {
	margin: 0 0 var(--wp--preset--spacing--40, 1rem);
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	opacity: 0.85;
}

.vp-img-result__meta strong {
	opacity: 1;
	font-weight: 600;
}

/**
 * Image Cropper's drag-to-select stage (Prompt 045). The `outline: 9999px
 * solid` trick on the selection rectangle darkens everything OUTSIDE the
 * selection without needing 4 separate overlay divs — `overflow: hidden`
 * on the stage clips that huge outline back down to the visible area.
 */
.vp-crop-stage {
	position: relative;
	display: inline-block;
	max-width: 100%;
	overflow: hidden;
	border-radius: var(--wp--custom--radius--small, 4px);
	touch-action: none;
}

.vp-crop-canvas {
	display: block;
	max-width: 100%;
	height: auto;
	cursor: crosshair;
}

.vp-crop-selection {
	position: absolute;
	border: 2px dashed var(--wp--preset--color--base, #fff);
	outline: 9999px solid rgba(0, 0, 0, 0.5);
	box-sizing: border-box;
	pointer-events: none;
}

@media (max-width: 480px) {
	.CodeMirror.vp-code-editor {
		height: 260px;
	}
}

/**
 * HTML, CSS & JS Compiler (Modules\HtmlCssJsCompiler, Prompt 033). Three
 * stacked source editors plus a live preview `<iframe>` — a different
 * shape from every other tool, so it gets its own small set of layout
 * rules, reusing `.vp-tool-toolbar`/`.vp-btn`/`.vp-tool-panel`/
 * `.vp-tool-editor`/`.vp-status` for anything that already fits.
 */

.vp-compiler-layout {
	display: grid;
	grid-template-columns: 1fr;
	gap: var(--wp--preset--spacing--40, 1rem);
}

@media (min-width: 960px) {
	.vp-compiler-layout {
		/* Editors column slightly narrower than the preview column, since
		   a live preview benefits more from extra width than a code panel
		   does. */
		grid-template-columns: 1fr 1.2fr;
		align-items: start;
	}
}

.vp-compiler-editors {
	display: flex;
	flex-direction: column;
	gap: var(--wp--preset--spacing--30, 0.75rem);
	min-width: 0;
}

.vp-compiler-panel__header {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 0.5rem;
	margin-bottom: 0.4em;
}

.vp-compiler-panel__header label,
.vp-compiler-panel__header h2 {
	margin: 0;
	font-weight: 600;
	font-size: var(--wp--preset--font-size--medium, 1rem);
}

.vp-compiler-editor {
	min-height: 160px;
}

.vp-compiler-preview {
	min-width: 0;
	position: sticky;
	top: var(--wp--preset--spacing--40, 1rem);
}

.vp-compiler-preview__frame-wrap {
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--base, 8px);
	overflow: hidden;
	background: var(--wp--preset--color--base, #fff);
}

.vp-compiler-frame {
	display: block;
	width: 100%;
	height: 520px;
	border: 0;
}

@media (max-width: 480px) {
	.vp-compiler-frame {
		height: 360px;
	}

	.vp-compiler-preview {
		position: static;
	}
}

.vp-compiler-error {
	margin-top: 0.5em;
	padding: 0.6em 1em;
	font-family: var(--wp--preset--font-family--mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	white-space: pre-wrap;
	word-break: break-word;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-left: 3px solid var(--wp--preset--color--danger, #b91c1c);
	border-radius: var(--wp--custom--radius--small, 4px);
	background: var(--wp--preset--color--surface, #f5f6f8);
	color: var(--wp--preset--color--contrast, #14181f);
}

/**
 * Compiler console output (Prompt 036) — console.log()/.info()/.warn()/
 * .error()/.debug() calls captured from the preview, reusing
 * `.vp-compiler-panel__header`'s existing header/button layout.
 */

.vp-compiler-console {
	margin: 0.5em 0 0;
	padding: 0.5em 0.75em;
	list-style: none;
	max-height: 240px;
	overflow: auto;
	font-family: var(--wp--preset--font-family--mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--small, 4px);
	background: var(--wp--preset--color--base, #fff);
	color: var(--wp--preset--color--contrast, #14181f);
}

.vp-compiler-console__empty {
	opacity: 0.6;
	font-style: italic;
	font-family: var(--wp--preset--font-family--body, inherit);
}

.vp-compiler-console__entry {
	padding: 0.3em 0;
	border-bottom: 1px solid var(--wp--preset--color--border, #e2e5ea);
	word-break: break-word;
	white-space: pre-wrap;
}

.vp-compiler-console__entry:last-child {
	border-bottom: 0;
}

.vp-compiler-console__method {
	font-weight: 600;
	text-transform: uppercase;
	font-size: 0.75em;
	opacity: 0.6;
	margin-right: 0.5em;
}

.vp-compiler-console__entry--warn {
	color: var(--wp--preset--color--warning, #92400e);
}

.vp-compiler-console__entry--error {
	color: var(--wp--preset--color--danger, #b91c1c);
}

/**
 * Regex Tester (Modules\RegexTester, Prompt 034). Pattern + flags row,
 * plain (non-CodeMirror) inputs, and a highlighted-match view — a
 * different shape from every other tool, so it gets its own small set of
 * rules, reusing `.vp-tool`/`.vp-tool-panel`/`.vp-tool-editor`/`.vp-btn`/
 * `.vedant-card`/`.vp-jwt-section*`/`.vp-jwt-pre` for anything that
 * already fits (no CodeMirror is loaded by this tool — see
 * Modules\RegexTester's docblock for why).
 */

.vp-regex-pattern-row {
	display: flex;
	flex-wrap: wrap;
	gap: var(--wp--preset--spacing--40, 1rem);
	margin-bottom: var(--wp--preset--spacing--40, 1rem);
}

.vp-regex-pattern-field {
	flex: 1 1 260px;
	min-width: 0;
}

.vp-regex-pattern-field label {
	display: block;
	font-weight: 600;
	margin-bottom: 0.4em;
}

.vp-regex-pattern-input-wrap {
	display: flex;
	align-items: center;
	box-sizing: border-box;
	width: 100%;
	font-family: var(--wp--preset--font-family--mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--base, 8px);
	background: var(--wp--preset--color--surface, #f5f6f8);
}

.vp-regex-delimiter {
	padding: 0.6em 0 0.6em 0.6em;
	opacity: 0.5;
}

.vp-regex-delimiter + input {
	padding-left: 0.25em;
}

.vp-regex-pattern-input {
	flex: 1 1 auto;
	min-width: 0;
	box-sizing: border-box;
	width: 100%;
	padding: 0.6em 0.6em 0.6em 0;
	border: 0;
	background: transparent;
	font: inherit;
	color: var(--wp--preset--color--contrast, #14181f);
}

.vp-regex-pattern-input:focus {
	outline: none;
}

.vp-regex-pattern-input-wrap:focus-within {
	outline: 2px solid var(--wp--preset--color--primary, #1d4ed8);
	outline-offset: 1px;
}

.vp-regex-flags {
	flex: 1 1 260px;
	min-width: 0;
	margin: 0;
	padding: 0;
	border: 0;
}

.vp-regex-flags legend {
	font-weight: 600;
	margin-bottom: 0.4em;
	padding: 0;
}

.vp-regex-flags label {
	display: inline-flex;
	align-items: center;
	gap: 0.3em;
	margin: 0 1em 0.4em 0;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
}

.vp-regex-flag-label {
	opacity: 0.7;
}

.vp-regex-replacement-input {
	box-sizing: border-box;
	width: 100%;
	font: inherit;
	font-family: var(--wp--preset--font-family--mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	padding: 0.6em 0.75em;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--base, 8px);
	background: var(--wp--preset--color--surface, #f5f6f8);
	color: var(--wp--preset--color--contrast, #14181f);
}

.vp-regex-results {
	margin-top: var(--wp--preset--spacing--50, 1.5rem);
	display: flex;
	flex-direction: column;
	gap: var(--wp--preset--spacing--40, 1rem);
}

.vp-regex-highlighted {
	box-sizing: border-box;
	width: 100%;
	max-height: 320px;
	overflow: auto;
	padding: 0.85em;
	font-family: var(--wp--preset--font-family--mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	line-height: 1.6;
	white-space: pre-wrap;
	word-break: break-word;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--small, 4px);
	background: var(--wp--preset--color--base, #fff);
	color: var(--wp--preset--color--contrast, #14181f);
}

.vp-regex-highlighted__empty {
	opacity: 0.6;
	font-style: italic;
}

.vp-regex-match {
	background: #fde68a;
	color: #14181f;
	border-radius: 2px;
	padding: 0 1px;
}

.vp-regex-match--zero-length {
	display: inline-block;
	width: 2px;
	height: 1em;
	vertical-align: middle;
	background: var(--wp--preset--color--warning-accent, #b45309);
	border-radius: 1px;
}

.vp-regex-match-list {
	margin: 0;
	padding-left: 1.25em;
	display: flex;
	flex-direction: column;
	gap: 0.75em;
}

.vp-regex-match-list__summary {
	font-family: var(--wp--preset--font-family--mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	word-break: break-word;
}

.vp-regex-match-list__group {
	font-family: var(--wp--preset--font-family--mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	opacity: 0.75;
	word-break: break-word;
}

/**
 * Homepage (Prompt 040). Every section below uses the `alignwide`
 * (1200px) tier — reusing `.vedant-card`/`.vp-btn` for anything that
 * already fits, same as every tool page already does. `.vp-home-section`
 * is the shared vertical-rhythm wrapper for every section after the
 * hero; `.vp-section-heading` is the shared H2 treatment across them.
 */

.vp-home-section {
	padding-block: var(--wp--custom--spacing--section);
}

/**
 * Small brand-colored kicker above every homepage section heading
 * (Prompt 044 visual pass) — a lightweight "section transition" marker
 * (Phase Q) that costs nothing layout-wise, used instead of full-bleed
 * background bands per section (which would need restructuring every
 * pattern's markup for a `100vw` bleed — judged not worth the risk for
 * a purely decorative rhythm cue).
 */
.vp-section-heading {
	position: relative;
	margin: 0 0 var(--wp--preset--spacing--50, 1.5rem);
	text-align: center;
}

/* Below the title (was `::before`, above it) — direct request. */
.vp-section-heading::after {
	content: "";
	display: block;
	width: 40px;
	height: 3px;
	margin: 0.85rem auto 0;
	border-radius: 999px;
	background: var(--wp--preset--color--primary, #1d4ed8);
}

/**
 * A subsection heading ("Government Schemes & Services", "Latest
 * Guides") paired with its own CTA button in the same row, button
 * pinned right — direct request, replacing a plain underlined text
 * link that used to sit alone at the very bottom of the section.
 */
.vp-home-section__subheading {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: 0.75rem;
	margin-bottom: var(--wp--preset--spacing--40, 1rem);
}

.vp-home-section__subheading h3 {
	margin: 0;
}

/**
 * Hero (Prompt 040, reworked Prompt 044 visual pass). Still deliberately
 * compact — a product header bar, not a full-viewport banner. Shares its
 * light `surface` background with the search section directly below it
 * (`.vp-home-search-section`) so the two read as one visual "above the
 * fold" band, with "Recently Added Tools" starting fresh on the page's
 * base background right after — a cheap, layout-free way to give the
 * hero+search combination visual weight without a taller hero.
 * `.vp-hero__grid` stacks copy above the decorative visual on narrow
 * viewports and goes side-by-side at `1024px`+, where `.vp-hero__visual`
 * first becomes visible (see below) — the illustration adds zero height
 * on any viewport where it isn't shown.
 */
/**
 * `padding-inline` added per direct feedback: the hero's own alignwide
 * (1200px) band had zero internal horizontal padding, so at wide
 * viewports `.vp-hero__inner`'s text and `.vp-hero__visual`'s
 * illustration sat flush against the band's own left/right edges —
 * confirmed live via getBoundingClientRect() (both matched `.vp-hero`'s
 * own edges exactly, 0px gap). WordPress's root padding-aware alignment
 * doesn't reach an `alignwide` block the way it does `alignfull`, so this
 * band needed its own explicit breathing room.
 */
.vp-hero {
	padding-block: clamp(2.5rem, 6vw, 3.5rem) clamp(1.75rem, 3vw, 2.25rem);
	padding-inline: clamp(1.5rem, 4vw, 3rem);
	background: var(--wp--preset--color--surface, #f5f6f8);
	text-align: center;
}

.vp-hero__grid {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 2rem;
}

.vp-hero__inner {
	max-width: 760px;
	margin-inline: auto;
}

.vp-hero__eyebrow {
	display: inline-flex;
	align-items: center;
	margin: 0 0 0.75rem;
	padding: 0.3em 0.85em;
	border-radius: 999px;
	background: var(--wp--preset--color--surface, #f5f6f8);
	background: color-mix(in srgb, var(--wp--preset--color--primary, #1d4ed8) 10%, var(--wp--preset--color--base, #fff));
	color: var(--wp--preset--color--primary, #1d4ed8);
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	font-weight: 600;
}

.vp-hero__actions {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: 0.75rem;
	margin-top: var(--wp--preset--spacing--50, 1.5rem);
}

/**
 * Decorative hero illustration — hidden below 1024px so it never
 * competes with hero copy for width or adds height on tablet/mobile.
 * Built entirely from the existing `ToolIcons` category glyphs (see
 * patterns/page-intro.php) inside a small "app window" frame; a gentle
 * float is the one hero animation, guarded by reduced-motion below.
 */
.vp-hero__visual {
	display: none;
}

.vp-hero-window {
	width: 280px;
	padding: 1.1rem;
	background: var(--wp--preset--color--base, #fff);
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--base, 8px);
	box-shadow: var(--wp--preset--shadow--md, 0 4px 16px rgba(20, 24, 31, .08));
	animation: vp-hero-float 6s ease-in-out infinite;
}

@keyframes vp-hero-float {
	0%, 100% { transform: translateY(0); }
	50% { transform: translateY(-8px); }
}

@media (prefers-reduced-motion: reduce) {
	.vp-hero-window {
		animation: none;
	}
}

.vp-hero-window__bar {
	display: flex;
	gap: 0.35rem;
	margin-bottom: 1.1rem;
}

.vp-hero-window__bar span {
	width: 8px;
	height: 8px;
	border-radius: 50%;
	background: var(--wp--preset--color--border, #e2e5ea);
}

.vp-hero-window__grid {
	display: grid;
	grid-template-columns: repeat(2, 1fr);
	gap: 0.75rem;
}

.vp-hero-window__tile {
	display: flex;
	align-items: center;
	justify-content: center;
	aspect-ratio: 1;
	border-radius: var(--wp--custom--radius--small, 4px);
	background: var(--wp--preset--color--surface, #f5f6f8);
}

.vp-hero-window__tile--accent-dev {
	background: color-mix(in srgb, var(--wp--preset--color--accent-dev, #4338ca) 12%, var(--wp--preset--color--base, #fff));
}

.vp-hero-window__tile--accent-calc {
	background: color-mix(in srgb, var(--wp--preset--color--accent-calc, #0f766e) 12%, var(--wp--preset--color--base, #fff));
}

.vp-hero-window__tile--accent-files {
	background: color-mix(in srgb, var(--wp--preset--color--accent-files, #7c3aed) 12%, var(--wp--preset--color--base, #fff));
}

.vp-hero-window__tile--accent-image {
	background: color-mix(in srgb, var(--wp--preset--color--accent-image, #be185d) 12%, var(--wp--preset--color--base, #fff));
}

.vp-hero-window__tile--accent-finance {
	background: color-mix(in srgb, var(--wp--preset--color--accent-finance, #78350f) 12%, var(--wp--preset--color--base, #fff));
}

.vp-hero-window__tile--accent-utility {
	background: color-mix(in srgb, var(--wp--preset--color--accent-utility, #0369a1) 12%, var(--wp--preset--color--base, #fff));
}

.vp-hero-window__tile--accent-ai {
	background: color-mix(in srgb, var(--wp--preset--color--accent-ai, #c2410c) 12%, var(--wp--preset--color--base, #fff));
}

.vp-hero-window__tile--accent-weather {
	background: color-mix(in srgb, var(--wp--preset--color--accent-weather, #0e7490) 12%, var(--wp--preset--color--base, #fff));
}

.vp-hero-window__tile--accent-web {
	background: color-mix(in srgb, var(--wp--preset--color--accent-web, #15803d) 12%, var(--wp--preset--color--base, #fff));
}

.vp-hero-window__tile--accent-security {
	background: color-mix(in srgb, var(--wp--preset--color--accent-security, #b91c1c) 12%, var(--wp--preset--color--base, #fff));
}

.vp-hero-window__tile--accent-map {
	background: color-mix(in srgb, var(--wp--preset--color--accent-map, #a16207) 12%, var(--wp--preset--color--base, #fff));
}

.vp-hero-window__tile--accent-device {
	background: color-mix(in srgb, var(--wp--preset--color--accent-device, #1e3a8a) 12%, var(--wp--preset--color--base, #fff));
}

@media (min-width: 1024px) {
	.vp-hero__grid {
		flex-direction: row;
		justify-content: space-between;
		text-align: left;
		gap: 3rem;
	}

	.vp-hero__inner {
		flex: 1 1 auto;
		max-width: 560px;
		margin-inline: 0;
	}

	.vp-hero__actions,
	.vp-hero__points {
		justify-content: flex-start;
	}

	.vp-hero__visual {
		display: block;
		flex: 0 0 auto;
	}
}

.vp-btn--large {
	min-height: 44px;
	padding: 0.75em 1.5em;
	font-size: var(--wp--preset--font-size--medium, 1rem);
}

.vp-hero__points {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: 0.5rem 1.25rem;
	list-style: none;
	margin: var(--wp--preset--spacing--40, 1rem) 0 0;
	padding: 0;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	opacity: 0.75;
}

.vp-hero__points li::before {
	content: "\2713";
	margin-right: 0.4em;
}

/**
 * Homepage search. Plain GET form (Tools\ToolArchive) — zero JS. The
 * icon is purely decorative (`aria-hidden`); the visible `<label>` above
 * the field is the accessible name, matching the archive filter bar's
 * own labeling convention exactly.
 */
/**
 * Shares the hero's `surface` background (see `.vp-hero` above) and uses
 * a smaller bottom padding than a full home section — it's the close of
 * the hero+search band, not its own section boundary. Previously used
 * the same full `--spacing--section` clamp (up to 5rem) as every section
 * below it, which stacked with that next section's own top padding into
 * a visibly oversized gap before "Recently Added Tools" (measured ~150px
 * of empty space at 375px width) — this is the fix for that.
 */
.vp-home-search-section {
	/* Top was a bare 0 (deliberately, to avoid stacking with the hero's
	   own bottom padding above it) — direct feedback that the "Search
	   tools" label then sat flush against the section's top edge with no
	   breathing room at all. A small 1rem (16px) restores that without
	   reintroducing the doubled-gap problem the 0 was originally fixing. */
	padding-block: 1rem clamp(1.5rem, 3vw, 2rem);
	background: var(--wp--preset--color--surface, #f5f6f8);
}

/**
 * "Recently Added/Featured Tools" is the section directly below the
 * hero+search band — full `--spacing--section` top padding here (on top
 * of the search section's own bottom padding just above) reproduced the
 * same doubled-gap problem the search-section fix above was already
 * meant to solve (re-measured live: ~150px of empty space at 1440px
 * width). Every OTHER homepage section keeps the full section padding
 * (`.vp-home-section`'s own rule) since it's genuinely separating two
 * unrelated sections; only this one top edge is tightened.
 */
.vp-home-section--tools {
	padding-top: clamp(1.5rem, 3vw, 2rem);
}

/**
 * G-25A: the homepage's one ad placement (`home_content` slot) — between
 * "Recently Added Tools" and "Government Services & Information", clearly
 * separated from both by its own margin so it never reads as part of
 * either section, and nowhere near the header/hero/search per this
 * project's ad-placement rules. Uses the shared `.vp-ad` component
 * unchanged, matching every other ad slot in the codebase.
 */
.vp-home-ad {
	margin-block: var(--wp--preset--spacing--50, 1.5rem);
}

.vp-home-search {
	max-width: 640px;
	margin-inline: auto;
}

.vp-home-search label {
	display: block;
	text-align: center;
	font-weight: 600;
	margin-bottom: 0.5rem;
}

.vp-home-search__field {
	position: relative;
	display: flex;
	align-items: center;
	gap: 0.5rem;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--base, 8px);
	background: var(--wp--preset--color--base, #fff);
	padding: 0.4rem 0.4rem 0.4rem 0.85rem;
}

/* ~44px touch target (Prompt 040, Phase F) — the base `.vp-btn` alone
   measures ~36px tall at this font size, short of the target. */
.vp-home-search__field .vp-btn {
	min-height: 44px;
}

.vp-home-search__field:focus-within {
	border-color: var(--wp--preset--color--primary, #1d4ed8);
}

.vp-home-search__icon {
	flex-shrink: 0;
	opacity: 0.5;
}

.vp-home-search__field input[type="search"] {
	flex: 1 1 auto;
	min-width: 0;
	border: 0;
	background: transparent;
	font: inherit;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	padding: 0.5em 0;
	/* Native <input> text doesn't follow the page's own color cascade
	   the way ordinary elements do (no `color-scheme` is declared
	   site-wide, and this rule never set its own `color`), so it kept
	   rendering with the browser's light-mode default (near-black) even
	   against this field's dark-mode background — invisible input text,
	   reported with a screenshot. `.vp-api-tool__field input` already
	   sets this same token; this field was the one place that pattern
	   was missed. */
	color: var(--wp--preset--color--contrast, #14181f);
}

.vp-home-search__field input[type="search"]:focus {
	outline: none;
}

/**
 * Recently Added / Featured tools grid — see patterns/home-tools.php for
 * which heading/selection actually applies. Cards reuse `.vedant-card`;
 * the accent modifier is only ever the one matching the tool's own real
 * category (Prompt 038 tokens), never decorative-only color.
 */
.vp-home-tools-grid,
.vp-category-grid {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
	gap: var(--wp--preset--spacing--40, 1rem);
	list-style: none;
	margin: 0;
	padding: 0;
}

.vp-home-tool-card,
.vp-category-card {
	display: flex;
	flex-direction: column;
	min-height: 220px;
	border-top: 3px solid var(--wp--preset--color--border, #e2e5ea);
	transition: border-color 0.15s ease, box-shadow 0.15s ease;
	box-sizing: border-box;
}

/**
 * Prompt 044 fix: on the homepage, `.vp-home-tool-card` IS the grid item,
 * so it stretches to the row's tallest card automatically. On the tool
 * archive, `core/post-template` wraps each card in its own `<li>` (the
 * actual grid item) — that `<li>` stretches correctly (confirmed live),
 * but the card div one level in did not, so cards in the same row ended
 * up at different heights (measured: 402/439/439/465px in one row) with
 * their "Use Tool" links landing at different vertical positions. Filling
 * the parent height here is what actually fixes the misalignment.
 */
.wp-block-post-template > li {
	display: flex;
}

.wp-block-post-template > li > .vp-home-tool-card {
	flex: 1 1 auto;
}

/**
 * Real bug found live (reported directly against the tool archive): the
 * first card in each row rendered visibly taller than, and offset above,
 * its row-mates. Root cause — `wp:post-template` keeps its default
 * "flow" layout class (`is-layout-flow`) even after being restyled as a
 * CSS Grid via the `vp-home-tools-grid` className, so WordPress's own
 * global block-gap CSS (`:root :where(.is-layout-flow) > * {
 * margin-block-start: 1.5rem }`, applied to every child except the
 * first) was still adding a 24px top margin to every card but the row's
 * first — the grid's own `gap` already provides that spacing, so this
 * margin only ever double-spaced things and, worse, broke row alignment
 * (item 1 sat 24px higher than items 2-4 in the same row, which is what
 * made it look disproportionately tall). Neutralized for both card
 * grids this class targets (tool archive AND homepage/category, in case
 * either is ever rendered via `wp:post-template` too) — grid spacing
 * stays entirely owned by `gap` above.
 */
.vp-home-tools-grid > li,
.vp-category-grid > li {
	margin-block-start: 0;
}

/**
 * Archive pagination (core `wp:query-pagination`) — left-aligned by
 * WordPress core default. Centered per direct feedback against the live
 * tool archive.
 */
.wp-block-query-pagination {
	justify-content: center;
}

@media (prefers-reduced-motion: reduce) {
	.vp-home-tool-card,
	.vp-category-card {
		transition: none;
	}
}

.vp-home-tool-card:hover,
.vp-category-card:hover {
	box-shadow: var(--wp--preset--shadow--sm, 0 1px 3px rgba(0, 0, 0, 0.08));
}

.vp-home-tool-card:focus-within,
.vp-category-card:focus-within {
	border-top-color: var(--wp--preset--color--primary, #1d4ed8);
}

.vp-home-tool-card--accent-dev,
.vp-category-card--accent-dev {
	border-top-color: var(--wp--preset--color--accent-dev, #4338ca);
}

.vp-home-tool-card--accent-calc,
.vp-category-card--accent-calc {
	border-top-color: var(--wp--preset--color--accent-calc, #0f766e);
}

.vp-home-tool-card--accent-files,
.vp-category-card--accent-files {
	border-top-color: var(--wp--preset--color--accent-files, #7c3aed);
}

.vp-home-tool-card--accent-finance,
.vp-category-card--accent-finance {
	border-top-color: var(--wp--preset--color--accent-finance, #78350f);
}

.vp-home-tool-card--accent-utility,
.vp-category-card--accent-utility {
	border-top-color: var(--wp--preset--color--accent-utility, #0369a1);
}

.vp-home-tool-card--accent-ai,
.vp-category-card--accent-ai {
	border-top-color: var(--wp--preset--color--accent-ai, #c2410c);
}

.vp-home-tool-card--accent-weather,
.vp-category-card--accent-weather {
	border-top-color: var(--wp--preset--color--accent-weather, #0e7490);
}

.vp-home-tool-card--accent-web,
.vp-category-card--accent-web {
	border-top-color: var(--wp--preset--color--accent-web, #15803d);
}

.vp-home-tool-card--accent-security,
.vp-category-card--accent-security {
	border-top-color: var(--wp--preset--color--accent-security, #b91c1c);
}

.vp-home-tool-card--accent-map,
.vp-category-card--accent-map {
	border-top-color: var(--wp--preset--color--accent-map, #a16207);
}

.vp-home-tool-card--accent-device,
.vp-category-card--accent-device {
	border-top-color: var(--wp--preset--color--accent-device, #1e3a8a);
}

.vp-home-tool-card--accent-image,
.vp-category-card--accent-image {
	border-top-color: var(--wp--preset--color--accent-image, #be185d);
}

.vp-home-tool-card__title,
.vp-category-card__title,
.vp-home-tool-card .wp-block-post-title {
	margin: 0 0 0.5rem;
	font-size: var(--wp--preset--font-size--large, 1.25rem);
}

.vp-home-tool-card__title a,
.vp-category-card__title a,
.vp-home-tool-card .wp-block-post-title a {
	color: var(--wp--preset--color--accent-utility, #0369a1);
	text-decoration: none;
}

.vp-home-tool-card__title a:hover,
.vp-category-card__title a:hover,
.vp-home-tool-card .wp-block-post-title a:hover {
	text-decoration: underline;
}

/**
 * The archive card (templates/archive-tool.html) uses core's own
 * `wp:post-excerpt`/`wp:read-more` blocks, which render their own
 * `.wp-block-post-excerpt__excerpt`/`.wp-block-read-more` classes rather
 * than the custom classes the homepage's PHP-rendered card
 * (patterns/home-tools.php) uses directly — two different templates
 * producing the same visual "card" shape (Prompt 042/043), so both
 * class names are listed here rather than duplicating this rule (Prompt
 * 044 fix: confirmed live that the archive card's excerpt/CTA were
 * unstyled by this rule before, since neither class actually matched).
 */
.vp-home-tool-card__excerpt,
.vp-category-card__description,
.vp-home-tool-card > .wp-block-post-excerpt {
	flex: 1 1 auto;
	margin: 0 0 1rem;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	opacity: 0.85;
}

.wp-block-post-excerpt__excerpt {
	margin: 0;
}

.vp-category-card__count {
	display: block;
	margin-bottom: 0.75rem;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	font-weight: 600;
	opacity: 0.6;
}

.vp-home-tool-card__link,
.vp-category-card__link,
.wp-block-read-more {
	font-weight: 600;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	text-decoration: none;
}

.vp-home-tool-card__link:hover,
.vp-category-card__link:hover,
.wp-block-read-more:hover {
	text-decoration: underline;
}

/**
 * Wraps the category pill + "Use Tool" link (archive-tool.html,
 * archive.html, taxonomy-tool_category.html) in one row instead of two
 * stacked block-level elements — was two separate lines on every card,
 * with the extra vertical space making one card in a row read as
 * visibly taller than its neighbors whenever its title/excerpt wrapped
 * differently. Pill left, "Use Tool" pinned right via `space-between` —
 * direct request, with a screenshot of the inconsistent-height cards.
 */
.vp-home-tool-card__footer {
	display: flex;
	align-items: center;
	justify-content: space-between;
	flex-wrap: wrap;
	gap: 0.5rem 0.75rem;
}

/* Two separate stray-margin sources fought this row into looking like 2
   lines instead of 1, even with `justify-content: space-between`
   already correctly placing both items left/right: WordPress's own core
   block-gap flow styles add `margin-block-start` to any element that
   isn't the container's first child (still applied here despite this
   being flex — margin on a flex item is respected regardless of layout
   mode), and `.vp-tag-pills` has its own `margin: 0.6rem 0` for the
   contexts it's used standalone in (a tool's own single page). The 2nd
   rule below needs the extra `.vp-home-tool-card__footer` ancestor
   specifically to out-specificity `.vp-tag-pills`'s own rule (same
   single-class specificity otherwise, which source order alone would
   have left to whichever rule happens to load later). */
.vp-home-tool-card__footer > * {
	margin: 0;
}

.vp-home-tool-card__footer .vp-tag-pills {
	margin: 0;
}

/**
 * Why Vedant Platform — 3 verifiable pillars, one icon each.
 */
.vp-trust-grid {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
	gap: var(--wp--preset--spacing--50, 1.5rem);
	list-style: none;
	margin: 0;
	padding: 0;
	text-align: center;
}

.vp-trust-item__icon-wrap {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 52px;
	height: 52px;
	margin-bottom: 0.75rem;
	border-radius: 999px;
	background: var(--wp--preset--color--surface, #f5f6f8);
	background: color-mix(in srgb, var(--wp--preset--color--primary, #1d4ed8) 10%, var(--wp--preset--color--base, #fff));
}

.vp-trust-item__icon {
	color: var(--wp--preset--color--primary, #1d4ed8);
}

.vp-trust-item__title {
	margin: 0 0 0.4rem;
	font-size: var(--wp--preset--font-size--medium, 1rem);
}

.vp-trust-item__text {
	margin: 0;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	opacity: 0.85;
}

/**
 * Final CTA — compact, not a second hero.
 */
.vp-home-final-cta {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: 1rem;
}

.vp-home-final-cta__copy {
	flex: 1 1 auto;
}

.vp-home-final-cta__heading {
	margin: 0;
	font-size: var(--wp--preset--font-size--large, 1.25rem);
}

.vp-home-final-cta__text {
	margin: 0.35rem 0 0;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	opacity: 0.8;
}

@media (max-width: 480px) {
	.vp-home-final-cta {
		flex-direction: column;
		align-items: stretch;
		text-align: center;
	}
}

/**
 * Tool & category icon system (Prompt 042). One component, reused
 * everywhere a tool or category is represented — homepage cards, archive
 * cards, category cards, related-tools cards, the tool page header.
 * Decorative only (icons carry `aria-hidden`, see ToolIcons.php); color
 * defaults to a neutral tone and only shifts to a category accent when
 * `--accent-*` is explicitly applied, so an icon never becomes the sole
 * carrier of meaning.
 */
.vp-tool-icon {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	flex-shrink: 0;
	color: var(--wp--preset--color--contrast, #1e1e1e);
	opacity: 0.7;
}

.vp-tool-icon svg {
	width: 100%;
	height: 100%;
}

.vp-tool-icon img {
	width: 100%;
	height: 100%;
	object-fit: contain;
	border-radius: 0;
}

.vp-tool-icon--sm {
	width: 20px;
	height: 20px;
}

.vp-tool-icon--md {
	width: 28px;
	height: 28px;
}

.vp-tool-icon--lg {
	width: 40px;
	height: 40px;
}

.vp-tool-icon--accent-dev {
	color: var(--wp--preset--color--accent-dev, #4338ca);
	opacity: 1;
}

.vp-tool-icon--accent-calc {
	color: var(--wp--preset--color--accent-calc, #0f766e);
	opacity: 1;
}

.vp-tool-icon--accent-files {
	color: var(--wp--preset--color--accent-files, #7c3aed);
	opacity: 1;
}

.vp-tool-icon--accent-image {
	color: var(--wp--preset--color--accent-image, #be185d);
	opacity: 1;
}

.vp-tool-icon--accent-finance {
	color: var(--wp--preset--color--accent-finance, #78350f);
	opacity: 1;
}

.vp-tool-icon--accent-utility {
	color: var(--wp--preset--color--accent-utility, #0369a1);
	opacity: 1;
}

.vp-tool-icon--accent-weather {
	color: var(--wp--preset--color--accent-weather, #0e7490);
	opacity: 1;
}

.vp-tool-icon--accent-web {
	color: var(--wp--preset--color--accent-web, #15803d);
	opacity: 1;
}

.vp-tool-icon--accent-map {
	color: var(--wp--preset--color--accent-map, #a16207);
	opacity: 1;
}

.vp-tool-icon--accent-device {
	color: var(--wp--preset--color--accent-device, #1e3a8a);
	opacity: 1;
}

.vp-tool-icon--accent-security {
	color: var(--wp--preset--color--accent-security, #b91c1c);
	opacity: 1;
}

.vp-tool-icon--accent-ai {
	color: var(--wp--preset--color--accent-ai, #c2410c);
	opacity: 1;
}

/* Card header row: icon beside title, replacing a bare heading. */
.vp-home-tool-card__head,
.vp-category-card__head {
	display: flex;
	align-items: center;
	gap: 0.6rem;
	margin-bottom: 0.5rem;
}

/**
 * Homepage/archive/category card icon badge (Prompt 044 visual pass) —
 * a tinted rounded square behind the existing bare-stroke `.vp-tool-icon`
 * (same shared icon system, no markup change: this only re-styles it
 * inside these two card head contexts). Neutral surface tint by default;
 * a category-accent card tints its own icon badge to match its border
 * accent, reusing the existing `--accent-*` tokens rather than adding a
 * new color. Scoped narrowly to these two card heads so the icon's
 * appearance everywhere else (tool-page header, Related Tools) is
 * untouched.
 */
.vp-home-tool-card__head .vp-tool-icon,
.vp-category-card__head .vp-tool-icon {
	width: 44px;
	height: 44px;
	padding: 10px;
	box-sizing: border-box;
	border-radius: var(--wp--custom--radius--base, 8px);
	background: var(--wp--preset--color--surface, #f5f6f8);
	opacity: 1;
}

.vp-home-tool-card--accent-dev .vp-tool-icon,
.vp-category-card--accent-dev .vp-tool-icon {
	background: color-mix(in srgb, var(--wp--preset--color--accent-dev, #4338ca) 12%, var(--wp--preset--color--base, #fff));
}

.vp-home-tool-card--accent-calc .vp-tool-icon,
.vp-category-card--accent-calc .vp-tool-icon {
	background: color-mix(in srgb, var(--wp--preset--color--accent-calc, #0f766e) 12%, var(--wp--preset--color--base, #fff));
}

.vp-home-tool-card--accent-files .vp-tool-icon,
.vp-category-card--accent-files .vp-tool-icon {
	background: color-mix(in srgb, var(--wp--preset--color--accent-files, #7c3aed) 12%, var(--wp--preset--color--base, #fff));
}

.vp-home-tool-card--accent-image .vp-tool-icon,
.vp-category-card--accent-image .vp-tool-icon {
	background: color-mix(in srgb, var(--wp--preset--color--accent-image, #be185d) 12%, var(--wp--preset--color--base, #fff));
}

.vp-home-tool-card--accent-finance .vp-tool-icon,
.vp-category-card--accent-finance .vp-tool-icon {
	background: color-mix(in srgb, var(--wp--preset--color--accent-finance, #78350f) 12%, var(--wp--preset--color--base, #fff));
}

.vp-home-tool-card--accent-utility .vp-tool-icon,
.vp-category-card--accent-utility .vp-tool-icon {
	background: color-mix(in srgb, var(--wp--preset--color--accent-utility, #0369a1) 12%, var(--wp--preset--color--base, #fff));
}

.vp-home-tool-card--accent-ai .vp-tool-icon,
.vp-category-card--accent-ai .vp-tool-icon {
	background: color-mix(in srgb, var(--wp--preset--color--accent-ai, #c2410c) 12%, var(--wp--preset--color--base, #fff));
}

.vp-home-tool-card--accent-weather .vp-tool-icon,
.vp-category-card--accent-weather .vp-tool-icon {
	background: color-mix(in srgb, var(--wp--preset--color--accent-weather, #0e7490) 12%, var(--wp--preset--color--base, #fff));
}

.vp-home-tool-card--accent-web .vp-tool-icon,
.vp-category-card--accent-web .vp-tool-icon {
	background: color-mix(in srgb, var(--wp--preset--color--accent-web, #15803d) 12%, var(--wp--preset--color--base, #fff));
}

.vp-home-tool-card--accent-security .vp-tool-icon,
.vp-category-card--accent-security .vp-tool-icon {
	background: color-mix(in srgb, var(--wp--preset--color--accent-security, #b91c1c) 12%, var(--wp--preset--color--base, #fff));
}

.vp-home-tool-card--accent-map .vp-tool-icon,
.vp-category-card--accent-map .vp-tool-icon {
	background: color-mix(in srgb, var(--wp--preset--color--accent-map, #a16207) 12%, var(--wp--preset--color--base, #fff));
}

.vp-home-tool-card--accent-device .vp-tool-icon,
.vp-category-card--accent-device .vp-tool-icon {
	background: color-mix(in srgb, var(--wp--preset--color--accent-device, #1e3a8a) 12%, var(--wp--preset--color--base, #fff));
}

.vp-home-tool-card__head .vp-home-tool-card__title,
.vp-category-card__head .vp-category-card__title,
.vp-home-tool-card__head .wp-block-post-title {
	margin: 0;
}

/* Tool page header (Phase K): icon beside the H1. */
.vp-tool-header {
	display: flex;
	align-items: flex-start;
	gap: 0.75rem;
	margin-bottom: 0.5rem;
}

.vp-tool-header .vp-tool-icon {
	margin-top: 0.5em;
}

.vp-tool-header__title {
	margin: 0;
}

.vp-tool-header__description {
	margin: 0 0 0.25rem;
	color: var(--wp--preset--color--contrast, #1e1e1e);
	opacity: 0.75;
}

/**
 * Tool hero image (Prompt G-24-B). A tool's optional native featured
 * image — as of this prompt, exactly 1 of 186 tools (HTML Formatter)
 * has one set, uploaded manually via wp-admin's own Featured Image
 * panel; every other tool renders nothing here (WordPress core's
 * `wp:post-featured-image` block already emits nothing when no
 * thumbnail is set, so this rule is a pure no-op for 185 of 186 tools).
 *
 * Before this rule, the block rendered at `align:wide` (1200px) with no
 * height cap — at HTML Formatter's actual 1672x941 image, that meant a
 * roughly 675px-tall banner sitting between the header and the tool
 * workspace, pushing the actual tool multiple viewports below the fold
 * on a typical laptop screen. Capped to a compact supporting-visual
 * height instead — enough to still read as a real image, never enough
 * to dominate the page or meaningfully delay reaching the tool. Content
 * width (not `align:wide`) so it reads as part of the page's normal
 * reading column rather than a separate marketing splash.
 */
.vp-tool-hero-image {
	margin: 0.75rem 0 var(--wp--preset--spacing--40, 1rem);
	border-radius: var(--wp--custom--radius--base, 8px);
	overflow: hidden;
}

.vp-tool-hero-image img {
	display: block;
	width: 100%;
	max-height: 220px;
	object-fit: cover;
	object-position: center 30%;
}

@media (max-width: 480px) {
	.vp-tool-hero-image img {
		max-height: 160px;
	}
}

/* Related-tools card icon (Phase E). */
.vp-related-tools__list a {
	display: inline-flex;
	align-items: center;
	gap: 0.4rem;
}

/**
 * Category + tag pills (Prompt 042, Phase J/K). One shared component used
 * on cards and the tool page header — category is deliberately weighted
 * higher (bolder, accent-tinted) than tags (neutral, smaller, lower
 * opacity), per this project's "category = primary, tag = secondary
 * discovery" rule. `color-mix()` gives each category pill a light tint of
 * its own accent without a new set of `-bg` design tokens; the plain
 * `.vp-pill--category` rule above it is the fallback for a browser
 * without `color-mix()` support.
 */
.vp-tag-pills {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0.35rem;
	margin: 0.6rem 0;
}

.vp-pill {
	display: inline-flex;
	align-items: center;
	padding: 0.2em 0.7em;
	border-radius: 999px;
	line-height: 1.6;
	white-space: nowrap;
	text-decoration: none;
}

a.vp-pill:hover,
a.vp-pill:focus-visible {
	text-decoration: underline;
}

.vp-pill--category {
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	font-weight: 600;
	background: var(--wp--preset--color--surface, #f5f6f8);
	color: var(--wp--preset--color--contrast, #1e1e1e);
}

.vp-pill--category.vp-pill--accent-dev {
	background: color-mix(in srgb, var(--wp--preset--color--accent-dev, #4338ca) 14%, var(--wp--preset--color--base, #fff));
	color: var(--wp--preset--color--accent-dev, #4338ca);
}

.vp-pill--category.vp-pill--accent-calc {
	background: color-mix(in srgb, var(--wp--preset--color--accent-calc, #0f766e) 14%, var(--wp--preset--color--base, #fff));
	color: var(--wp--preset--color--accent-calc, #0f766e);
}

.vp-pill--category.vp-pill--accent-files {
	background: color-mix(in srgb, var(--wp--preset--color--accent-files, #7c3aed) 14%, var(--wp--preset--color--base, #fff));
	color: var(--wp--preset--color--accent-files, #7c3aed);
}

.vp-pill--category.vp-pill--accent-image {
	background: color-mix(in srgb, var(--wp--preset--color--accent-image, #be185d) 14%, var(--wp--preset--color--base, #fff));
	color: var(--wp--preset--color--accent-image, #be185d);
}

.vp-pill--category.vp-pill--accent-finance {
	background: color-mix(in srgb, var(--wp--preset--color--accent-finance, #78350f) 14%, var(--wp--preset--color--base, #fff));
	color: var(--wp--preset--color--accent-finance, #78350f);
}

.vp-pill--category.vp-pill--accent-utility {
	background: color-mix(in srgb, var(--wp--preset--color--accent-utility, #0369a1) 14%, var(--wp--preset--color--base, #fff));
	color: var(--wp--preset--color--accent-utility, #0369a1);
}

.vp-pill--category.vp-pill--accent-ai {
	background: color-mix(in srgb, var(--wp--preset--color--accent-ai, #c2410c) 14%, var(--wp--preset--color--base, #fff));
	color: var(--wp--preset--color--accent-ai, #c2410c);
}

.vp-pill--category.vp-pill--accent-weather {
	background: color-mix(in srgb, var(--wp--preset--color--accent-weather, #0e7490) 14%, var(--wp--preset--color--base, #fff));
	color: var(--wp--preset--color--accent-weather, #0e7490);
}

.vp-pill--category.vp-pill--accent-web {
	background: color-mix(in srgb, var(--wp--preset--color--accent-web, #15803d) 14%, var(--wp--preset--color--base, #fff));
	color: var(--wp--preset--color--accent-web, #15803d);
}

.vp-pill--category.vp-pill--accent-security {
	background: color-mix(in srgb, var(--wp--preset--color--accent-security, #b91c1c) 14%, var(--wp--preset--color--base, #fff));
	color: var(--wp--preset--color--accent-security, #b91c1c);
}

.vp-pill--category.vp-pill--accent-map {
	background: color-mix(in srgb, var(--wp--preset--color--accent-map, #a16207) 14%, var(--wp--preset--color--base, #fff));
	color: var(--wp--preset--color--accent-map, #a16207);
}

.vp-pill--category.vp-pill--accent-device {
	background: color-mix(in srgb, var(--wp--preset--color--accent-device, #1e3a8a) 14%, var(--wp--preset--color--base, #fff));
	color: var(--wp--preset--color--accent-device, #1e3a8a);
}

.vp-pill--tag {
	font-size: 0.75rem;
	font-weight: 500;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	color: var(--wp--preset--color--contrast, #1e1e1e);
	opacity: 0.75;
}

.vp-pill--more {
	opacity: 0.55;
	font-style: italic;
}

/**
 * Country Explorer (Tools\Modules\CountryExplorer). Built on the shared
 * `.vp-api-tool__result` shell (see `.vp-ad`/api-tool rules elsewhere in
 * this file) — only the result-list/detail-card markup below is new.
 */
.vp-country-results {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
	gap: 0.75rem;
	list-style: none;
	margin: 0;
	padding: 0;
}

.vp-country-result {
	display: flex;
	align-items: center;
	gap: 0.6rem;
	width: 100%;
	padding: 0.75rem 1rem;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--base, 8px);
	background: var(--wp--preset--color--base, #fff);
	text-align: left;
	cursor: pointer;
	transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.vp-country-result:hover,
.vp-country-result:focus-visible {
	border-color: var(--wp--preset--color--accent-utility, #0369a1);
	box-shadow: var(--wp--preset--shadow--sm, 0 1px 3px rgba(0, 0, 0, .08));
}

@media (prefers-reduced-motion: reduce) {
	.vp-country-result {
		transition: none;
	}
}

.vp-country-result__name {
	font-weight: 600;
}

.vp-country-result__meta {
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	opacity: 0.7;
}

.vp-country-explorer__empty {
	opacity: 0.75;
}

.vp-country-detail__back {
	margin-bottom: 1rem;
}

.vp-country-detail__header {
	display: flex;
	align-items: center;
	gap: 0.85rem;
	margin-bottom: 1.25rem;
}

.vp-country-detail__name {
	margin: 0;
}

.vp-country-detail__official {
	margin: 0.15rem 0 0;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	opacity: 0.7;
}

.vp-country-facts {
	display: grid;
	grid-template-columns: 1fr;
	gap: 0.6rem;
	padding: 1rem;
	background: var(--wp--preset--color--surface, #f5f6f8);
	border-radius: var(--wp--custom--radius--base, 8px);
}

@media (min-width: 560px) {
	.vp-country-facts {
		grid-template-columns: 1fr 1fr;
	}
}

.vp-country-fact {
	display: flex;
	flex-direction: column;
	gap: 0.15rem;
}

.vp-country-fact__label {
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	font-weight: 600;
	opacity: 0.65;
}

.vp-country-detail__subheading {
	margin: 1.5rem 0 0.75rem;
	font-size: var(--wp--preset--font-size--medium, 1rem);
}

.vp-country-neighbors,
.vp-country-related {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem;
	list-style: none;
	margin: 0;
	padding: 0;
}

.vp-country-neighbor {
	display: inline-flex;
	align-items: center;
	gap: 0.4rem;
	padding: 0.4em 0.8em;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: 999px;
	background: var(--wp--preset--color--base, #fff);
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	cursor: pointer;
}

.vp-country-neighbor:hover,
.vp-country-neighbor:focus-visible {
	border-color: var(--wp--preset--color--accent-utility, #0369a1);
	color: var(--wp--preset--color--accent-utility, #0369a1);
}

.vp-country-related a {
	display: inline-block;
	padding: 0.4em 0.8em;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--small, 4px);
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	font-weight: 600;
	text-decoration: none;
}

.vp-country-related a:hover {
	border-color: var(--wp--preset--color--accent-utility, #0369a1);
	color: var(--wp--preset--color--accent-utility, #0369a1);
}

/**
 * Universal Unit Converter (Tools\Modules\UnitConverter). Bespoke shape
 * (see that module's own docblock for why) — reuses `.vp-calculator__help`-
 * style field spacing conventions but isn't built on CalculatorRenderer.
 */
.vp-unit-converter__form {
	display: flex;
	flex-direction: column;
	gap: 1rem;
	max-width: 560px;
	margin-inline: auto;
}

.vp-unit-converter__field {
	display: flex;
	flex-direction: column;
	gap: 0.35em;
}

.vp-unit-converter__field label {
	font-weight: 600;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
}

.vp-unit-converter__field input,
.vp-unit-converter__field select {
	box-sizing: border-box;
	width: 100%;
	font: inherit;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	padding: 0.6em 0.75em;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--small, 4px);
	background: var(--wp--preset--color--base, #fff);
	color: var(--wp--preset--color--contrast, #14181f);
}

.vp-unit-converter__row {
	display: grid;
	grid-template-columns: 1fr auto 1fr;
	align-items: end;
	gap: 0.75rem;
}

.vp-unit-converter__swap {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 44px;
	height: 44px;
	flex-shrink: 0;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--small, 4px);
	background: var(--wp--preset--color--base, #fff);
	color: var(--wp--preset--color--contrast, #14181f);
	cursor: pointer;
}

.vp-unit-converter__swap:hover {
	border-color: var(--wp--preset--color--accent-utility, #0369a1);
	color: var(--wp--preset--color--accent-utility, #0369a1);
}

@media (max-width: 480px) {
	.vp-unit-converter__row {
		grid-template-columns: 1fr;
	}

	.vp-unit-converter__swap {
		justify-self: center;
		transform: rotate(90deg);
	}
}

.vp-unit-converter__error {
	max-width: 560px;
	margin: 0.75rem auto 0;
	color: var(--wp--preset--color--danger, #b91c1c);
	font-weight: 600;
	text-align: center;
}

.vp-unit-converter__error:empty {
	display: none;
}

.vp-unit-converter__result {
	max-width: 560px;
	margin: 1.5rem auto 0;
	padding: 1.25rem;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--base, 8px);
	background: var(--wp--preset--color--surface, #f5f6f8);
	text-align: center;
}

.vp-unit-converter__result-value {
	font-family: var(--wp--preset--font-family--mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
	font-variant-numeric: tabular-nums;
	font-weight: 700;
	font-size: 1.4rem;
	color: var(--wp--preset--color--accent-utility, #0369a1);
	word-break: break-word;
}

/**
 * HTML sitemap page (SEO\HtmlSitemap, `[vedant_sitemap]`) — a card grid,
 * one card per `tool_category`, each a scannable link list. Deliberately
 * plain (no accent-per-category tinting here, unlike tool cards
 * elsewhere) — this page's whole job is fast scanning of ~70 links, and
 * six different accent-tinted cards side by side would compete with that
 * rather than help it; the category icon (already colored via
 * `ToolIcons`) carries the per-category visual cue instead.
 */
.vp-sitemap__grid {
	display: grid;
	grid-template-columns: repeat( auto-fill, minmax( 240px, 1fr ) );
	gap: var( --wp--preset--spacing--50, 1.5rem );
	margin-top: var( --wp--preset--spacing--50, 1.5rem );
	margin-bottom: 5px;
}

.vp-sitemap__category {
	padding: var( --wp--preset--spacing--40, 1rem );
	border: 1px solid var( --wp--preset--color--border, #e2e5ea );
	border-radius: var( --wp--custom--radius--base, 8px );
	background: var( --wp--preset--color--surface, #f5f6f8 );
}

.vp-sitemap__category-heading {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	margin: 0 0 0.75rem;
	font-size: var( --wp--preset--font-size--medium, 1.125rem );
}

.vp-sitemap__category-heading a {
	color: inherit;
	text-decoration: none;
}

.vp-sitemap__category-heading a:hover,
.vp-sitemap__category-heading a:focus {
	text-decoration: underline;
}

.vp-sitemap__count {
	margin-left: auto;
	font-size: var( --wp--preset--font-size--small, 0.875rem );
	font-weight: 400;
	opacity: 0.55;
	font-variant-numeric: tabular-nums;
}

.vp-sitemap__list {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: 0.4rem;
}

.vp-sitemap__list a {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	font-size: var( --wp--preset--font-size--small, 0.875rem );
	text-decoration: none;
	color: var( --wp--preset--color--contrast, #14181f );
	opacity: 0.85;
}

.vp-sitemap__list a:hover,
.vp-sitemap__list a:focus {
	opacity: 1;
	text-decoration: underline;
}

.vp-sitemap__category--pages {
	grid-column: 1 / -1;
}

.vp-sitemap__category--pages .vp-sitemap__list {
	flex-direction: row;
	flex-wrap: wrap;
	gap: 0.4rem 1.25rem;
}

/**
 * Weather Tools category (Prompt 062): a UV Index semicircle gauge, a
 * 7-day temperature bar chart, and a subtle result-reveal animation
 * applied broadly to every tool's result panel (not just Weather) per
 * direct request for "chart and card bit animation" — one universal,
 * low-risk polish rather than one-off per-tool CSS.
 */
.vp-uv-gauge {
	display: flex;
	justify-content: center;
	margin: 0.5rem 0;
}

.vp-uv-gauge__svg {
	max-width: 100%;
	height: auto;
}

.vp-temp-bars {
	display: flex;
	justify-content: space-between;
	gap: 0.4rem;
	margin: 0.75rem 0;
}

.vp-temp-bars__col {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 0.4rem;
	flex: 1 1 0;
	min-width: 0;
}

.vp-temp-bars__track {
	position: relative;
	width: 100%;
	max-width: 28px;
	height: 140px;
	border-radius: 999px;
	background: var(--wp--preset--color--border, #e2e5ea);
}

.vp-temp-bars__bar {
	position: absolute;
	left: 0;
	right: 0;
	border-radius: 999px;
	background: linear-gradient(180deg, #ea580c, var(--wp--preset--color--accent-weather, #0e7490));
	display: flex;
	flex-direction: column;
	justify-content: space-between;
	align-items: center;
	padding: 2px 0;
}

.vp-temp-bars__high,
.vp-temp-bars__low {
	font-size: 0.65rem;
	font-weight: 700;
	color: #fff;
	text-shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
	white-space: nowrap;
}

.vp-temp-bars__day {
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	opacity: 0.75;
	text-align: center;
}

@media (max-width: 480px) {
	.vp-temp-bars__track {
		height: 100px;
	}
	.vp-temp-bars__high,
	.vp-temp-bars__low {
		font-size: 0.6rem;
	}
}

/**
 * Result-reveal animation — a subtle fade + slide-up applied the instant
 * a result panel becomes visible (its `hidden` attribute is removed).
 * `animation` re-triggers automatically each time the attribute toggles
 * off, since the browser treats that as the element (re)entering the
 * document's rendered state — no JS class-toggling needed. Respects
 * `prefers-reduced-motion` per standard accessibility practice.
 */
@keyframes vp-result-reveal {
	from {
		opacity: 0;
		transform: translateY(8px);
	}
	to {
		opacity: 1;
		transform: translateY(0);
	}
}

.vp-api-tool__result:not([hidden]),
.vp-calculator__results:not([hidden]) {
	animation: vp-result-reveal 0.35s ease-out;
}

@media (prefers-reduced-motion: reduce) {
	.vp-api-tool__result:not([hidden]),
	.vp-calculator__results:not([hidden]) {
		animation: none;
	}
}

/**
 * Word Counter / Character Counter (Tools\TextStatsRenderer). Both tools
 * share this exact markup/CSS — only the H1/description and which stat
 * is `--primary` differ (server-side, via TextStatsRenderer's config).
 */
.vp-text-stats__field {
	display: flex;
	flex-direction: column;
	gap: 0.35em;
	max-width: 720px;
	margin-inline: auto;
}

.vp-text-stats__field label {
	font-weight: 600;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
}

.vp-text-stats__field textarea {
	box-sizing: border-box;
	width: 100%;
	font: inherit;
	font-size: var(--wp--preset--font-size--medium, 1rem);
	padding: 0.75em;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--base, 8px);
	background: var(--wp--preset--color--base, #fff);
	color: var(--wp--preset--color--contrast, #14181f);
	resize: vertical;
}

.vp-text-stats__field textarea:focus-visible {
	border-color: var(--wp--preset--color--primary, #1d4ed8);
}

.vp-text-stats__grid {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
	gap: 0.75rem;
	max-width: 720px;
	margin: 1.5rem auto 0;
}

.vp-text-stats__stat {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 0.2rem;
	padding: 1rem 0.5rem;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--base, 8px);
	background: var(--wp--preset--color--surface, #f5f6f8);
	text-align: center;
}

.vp-text-stats__stat--primary {
	background: color-mix(in srgb, var(--wp--preset--color--accent-utility, #0369a1) 10%, var(--wp--preset--color--base, #fff));
	border-color: var(--wp--preset--color--accent-utility, #0369a1);
}

.vp-text-stats__value {
	font-family: var(--wp--preset--font-family--mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
	font-variant-numeric: tabular-nums;
	font-weight: 700;
	font-size: 1.5rem;
	color: var(--wp--preset--color--contrast, #14181f);
}

.vp-text-stats__stat--primary .vp-text-stats__value {
	color: var(--wp--preset--color--accent-utility, #0369a1);
}

.vp-text-stats__label {
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	opacity: 0.7;
}

/**
 * Password Generator (Tools\Modules\PasswordGenerator).
 */
.vp-password-generator__output {
	display: flex;
	gap: 0.5rem;
	max-width: 560px;
	margin-inline: auto;
}

.vp-password-generator__output-field {
	flex: 1 1 auto;
	min-width: 0;
	box-sizing: border-box;
	font-family: var(--wp--preset--font-family--mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
	font-size: 1.1rem;
	padding: 0.6em 0.75em;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--small, 4px);
	background: var(--wp--preset--color--surface, #f5f6f8);
	color: var(--wp--preset--color--contrast, #14181f);
}

.vp-password-generator__strength {
	max-width: 560px;
	margin: 0.5rem auto 0;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	text-align: center;
	opacity: 0.75;
}

.vp-password-generator__options {
	display: flex;
	flex-direction: column;
	gap: 0.75rem;
	max-width: 560px;
	margin: 1.5rem auto 0;
	padding: 1rem;
	background: var(--wp--preset--color--surface, #f5f6f8);
	border-radius: var(--wp--custom--radius--base, 8px);
}

.vp-password-generator__field {
	display: flex;
	flex-direction: column;
	gap: 0.35em;
}

.vp-password-generator__field input[type="range"] {
	width: 100%;
}

.vp-password-generator__checkbox {
	display: flex;
	align-items: center;
	gap: 0.5em;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	cursor: pointer;
}

.vp-password-generator__error {
	max-width: 560px;
	margin: 0.75rem auto 0;
	color: var(--wp--preset--color--danger, #b91c1c);
	font-weight: 600;
	text-align: center;
}

.vp-password-generator__error:empty {
	display: none;
}

.vp-password-generator > .vp-btn--large {
	display: flex;
	margin: 1.5rem auto 0;
}

/**
 * QR Code Generator (Tools\Modules\QrCodeGenerator).
 */
.vp-qr-generator__layout {
	display: grid;
	grid-template-columns: 1fr;
	gap: 1.5rem;
	align-items: start;
}

@media (min-width: 700px) {
	.vp-qr-generator__layout {
		grid-template-columns: 1fr 280px;
	}
}

.vp-qr-generator__form {
	display: flex;
	flex-direction: column;
	gap: 1rem;
}

.vp-qr-generator__form textarea {
	box-sizing: border-box;
	width: 100%;
	font: inherit;
	padding: 0.6em 0.75em;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--small, 4px);
	background: var(--wp--preset--color--base, #fff);
	color: var(--wp--preset--color--contrast, #14181f);
	resize: vertical;
}

.vp-qr-generator__error {
	color: var(--wp--preset--color--danger, #b91c1c);
	font-weight: 600;
}

.vp-qr-generator__error:empty {
	display: none;
}

.vp-qr-generator__preview {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 1rem;
	padding: 1.25rem;
	background: var(--wp--preset--color--surface, #f5f6f8);
	border-radius: var(--wp--custom--radius--base, 8px);
}

.vp-qr-generator__code {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 100%;
	min-height: 180px;
}

.vp-qr-generator__code svg {
	width: 100%;
	height: auto;
	max-width: 220px;
	background: #fff;
	border-radius: var(--wp--custom--radius--small, 4px);
}

.vp-qr-generator__preview .vp-btn {
	width: 100%;
	justify-content: center;
}

.vp-qr-generator__preview .vp-btn:disabled {
	opacity: 0.5;
	cursor: not-allowed;
}

/**
 * Security Tools shared result styling (Prompt 064): a monospace,
 * selectable "generated value" box (password/hash/token/UUID output)
 * and a small strength-meter bar for the Password Strength Checker.
 * Deliberately generic classes (not per-tool) since every hash/random-
 * generation tool in this category renders the exact same shape.
 */
.vp-generated-value {
	font-family: Consolas, Monaco, 'Courier New', monospace;
	font-size: 1rem;
	word-break: break-all;
	background: var(--wp--preset--color--surface, #f5f6f8);
	/* Unlike regular text elements, a `<textarea>` (what this class is
	   almost always applied to — every password/hash/UUID/token/JSON
	   generator's output box) does NOT inherit `color` from its
	   ancestors; it falls back to the browser's own form-control default
	   (effectively black) regardless of the site's theme. That default
	   background above was already dark-mode-aware via the `--surface`
	   token, but with no `color` set here at all the text stayed black
	   on a now-dark background — confirmed live via a dark-mode
	   screenshot on CSV to GeoJSON, one of 13 tools sharing this class. */
	color: var(--wp--preset--color--contrast, #14181f);
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--base, 8px);
	padding: 0.75rem 1rem;
	margin: 0.5rem 0;
	user-select: all;
}

.vp-generated-value--mono {
	font-size: 0.9rem;
}

.vp-generated-value--compact {
	padding: 0.5rem 0.75rem;
	margin: 0.3rem 0;
	font-size: 0.85rem;
}

.vp-uuid-list {
	display: flex;
	flex-direction: column;
}

.vp-strength-meter {
	margin: 0.6rem 0;
}

.vp-strength-meter__bar {
	height: 8px;
	border-radius: 999px;
	background: var(--wp--preset--color--border, #e2e5ea);
	overflow: hidden;
}

.vp-strength-meter__fill {
	height: 100%;
	border-radius: 999px;
	transition: width 0.3s ease-out;
}

.vp-strength-meter__fill--score-1 { background: #dc2626; }
.vp-strength-meter__fill--score-2 { background: #ea580c; }
.vp-strength-meter__fill--score-3 { background: #ca8a04; }
.vp-strength-meter__fill--score-4 { background: #65a30d; }
.vp-strength-meter__fill--score-5 { background: #15803d; }

/**
 * Web & SEO Tools shared result styling (Prompt 065): a social-share
 * card preview (Open Graph Checker, Twitter Card Preview) and a
 * heading-outline list (Heading Analyzer), indented per level.
 */
.vp-og-preview {
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--base, 8px);
	overflow: hidden;
	margin: 0.75rem 0;
	max-width: 500px;
}

.vp-og-preview__image {
	display: block;
	width: 100%;
	max-height: 260px;
	object-fit: cover;
	background: var(--wp--preset--color--surface, #f5f6f8);
}

.vp-og-preview__body {
	padding: 0.75rem 1rem;
}

.vp-og-preview__site {
	display: block;
	font-size: 0.75rem;
	letter-spacing: 0.04em;
	opacity: 0.6;
	margin-bottom: 0.2rem;
}

.vp-og-preview__title {
	margin: 0 0 0.25rem;
	font-size: 1rem;
}

.vp-og-preview__description {
	margin: 0;
	font-size: 0.875rem;
	opacity: 0.75;
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
}

.vp-heading-list {
	display: flex;
	flex-direction: column;
	gap: 0.35rem;
	margin: 0.75rem 0;
}

.vp-heading-list__item {
	display: flex;
	align-items: baseline;
	gap: 0.6rem;
	padding: 0.3rem 0.6rem;
	border-radius: var(--wp--custom--radius--small, 4px);
	background: var(--wp--preset--color--surface, #f5f6f8);
}

.vp-heading-list__item--level-2 { margin-left: 1rem; }
.vp-heading-list__item--level-3 { margin-left: 2rem; }
.vp-heading-list__item--level-4 { margin-left: 3rem; }
.vp-heading-list__item--level-5 { margin-left: 4rem; }
.vp-heading-list__item--level-6 { margin-left: 5rem; }

.vp-heading-list__tag {
	font-family: Consolas, Monaco, 'Courier New', monospace;
	font-size: 0.75rem;
	font-weight: 700;
	opacity: 0.6;
	min-width: 2.2rem;
}

.vp-heading-list__text {
	font-size: 0.9rem;
}

/**
 * Favicon Generator result grid (Prompt 065) — one cell per generated
 * size, each with its own preview, label, and download button (no ZIP
 * bundling — see favicon-generator.js's docblock for why).
 */
.vp-favicon-grid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
	gap: 1rem;
	margin: 1rem 0;
}

.vp-favicon-grid__cell {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 0.5rem;
	padding: 1rem;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--base, 8px);
	text-align: center;
}

.vp-favicon-grid__preview {
	width: 64px;
	height: 64px;
	object-fit: contain;
	background: repeating-conic-gradient(#eee 0% 25%, #fff 0% 50%) 50% / 16px 16px;
	border-radius: var(--wp--custom--radius--small, 4px);
}

.vp-favicon-grid__label {
	font-size: 0.8rem;
	opacity: 0.75;
}

/**
 * Document Reader viewer (Prompt 066) — a shared content area that
 * holds either a PDF's <canvas> page, DOCX-converted HTML, or plain
 * text, depending on what was opened.
 */
.vp-doc-content {
	display: flex;
	justify-content: center;
	margin: 1rem 0;
	min-height: 200px;
}

.vp-doc-pdf-page {
	max-width: 100%;
	height: auto;
	box-shadow: 0 1px 4px rgba(0, 0, 0, 0.15);
	border-radius: var(--wp--custom--radius--small, 4px);
}

.vp-doc-page-info {
	font-size: 0.875rem;
	opacity: 0.75;
	padding: 0 0.5rem;
}

.vp-doc-content__docx {
	width: 100%;
	max-width: 700px;
	padding: 1.5rem;
	background: var(--wp--preset--color--base, #fff);
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--base, 8px);
	line-height: 1.6;
}

.vp-doc-content__docx img {
	max-width: 100%;
	height: auto;
}

.vp-doc-content__docx table {
	border-collapse: collapse;
	width: 100%;
}

.vp-doc-content__docx table td,
.vp-doc-content__docx table th {
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	padding: 0.4rem 0.6rem;
}

.vp-doc-content__txt {
	width: 100%;
	max-width: 700px;
	padding: 1.5rem;
	background: var(--wp--preset--color--surface, #f5f6f8);
	border-radius: var(--wp--custom--radius--base, 8px);
	white-space: pre-wrap;
	word-break: break-word;
	font-family: Consolas, Monaco, 'Courier New', monospace;
	font-size: 0.875rem;
	max-height: 600px;
	overflow-y: auto;
}

/**
 * Government Information Hub (Prompt G-01). Reuses `.vedant-card`,
 * `.vp-pill`/`.vp-tag-pills`, `.vp-btn`, and the `--wp--custom--radius--*`
 * tokens already established elsewhere in this file rather than
 * introducing a parallel design system — only the layout/spacing rules
 * genuinely specific to Government content live here. `accent-government`
 * (theme.json) was reserved, unused, before this feature; this is its
 * first real consumer.
 */
.vp-pill--government {
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	font-weight: 600;
	background: color-mix(in srgb, var(--wp--preset--color--accent-government, #334155) 14%, var(--wp--preset--color--base, #fff));
	color: var(--wp--preset--color--accent-government, #334155);
}

.vp-pill--gov-level,
.vp-pill--gov-state {
	font-size: 0.75rem;
	font-weight: 500;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	color: var(--wp--preset--color--contrast, #1e1e1e);
	opacity: 0.75;
}

.vp-gov-hub-intro {
	margin-bottom: var(--wp--preset--spacing--50, 1.5rem);
}

.vp-gov-hub-grid {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
	gap: var(--wp--preset--spacing--40, 1rem);
	margin: var(--wp--preset--spacing--50, 1.5rem) 0;
}

.vp-gov-hub-grid__item {
	display: flex;
	align-items: center;
	gap: 0.6rem;
	text-decoration: none;
	color: var(--wp--preset--color--contrast, #14181f);
	font-weight: 600;
	transition: border-color 0.15s ease;
}

.vp-gov-hub-grid__item:hover,
.vp-gov-hub-grid__item:focus-visible {
	border-color: var(--wp--preset--color--accent-government, #334155);
}

/* Added when the icon (GuideIcons/TopicIcons) became a 3rd flex child —
   this grows to fill the gap between icon+label and the count, so the
   count still pins to the item's right edge like it did before, instead
   of `justify-content: space-between` spacing the icon away from its
   label too. */
.vp-gov-hub-grid__label {
	flex: 1;
}

.vp-gov-hub-grid__count {
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	font-weight: 400;
	opacity: 0.55;
	font-variant-numeric: tabular-nums;
}

/**
 * Government topic pills (home-government.php's upper row — "Women &
 * Child Welfare", "Education", etc.) — moved here from the scheme/
 * service cards below per direct feedback: the actual scheme/service
 * CARDS read much better as a normal grid you can actually stop and
 * click (a moving target there was annoying to use), while this shorter
 * topic-pill row suits a continuous marquee fine. Shares its animation/
 * mask/pause mechanism with `.vp-gov-marquee` below via the same
 * `vp-gov-marquee-scroll` keyframes — see that block's own comment for
 * the full "why a duplicated track" explanation, identical here.
 */
.vp-gov-topics-marquee {
	overflow: hidden;
	margin: var(--wp--preset--spacing--50, 1.5rem) 0;
	-webkit-mask-image: linear-gradient(to right, transparent, black 3%, black 97%, transparent);
	mask-image: linear-gradient(to right, transparent, black 3%, black 97%, transparent);
}

.vp-gov-topics-marquee__track {
	display: flex;
	width: max-content;
	gap: var(--wp--preset--spacing--40, 1rem);
	animation: vp-gov-marquee-scroll 35s linear infinite;
}

.vp-gov-topics-marquee:hover .vp-gov-topics-marquee__track,
.vp-gov-topics-marquee:focus-within .vp-gov-topics-marquee__track {
	animation-play-state: paused;
}

/* Wider than the plain-grid version's auto-fit sizing (was min 180px) —
   direct request to size these up before turning them into a marquee;
   a fixed width is also required here regardless, since a flex marquee
   track (unlike the grid these cards use elsewhere) has no column
   tracks of its own to size each card against. */
.vp-gov-topics-marquee__track .vp-gov-hub-grid__item {
	width: 240px;
	flex-shrink: 0;
}

@media (prefers-reduced-motion: reduce) {
	.vp-gov-topics-marquee__track {
		animation: none;
		overflow-x: auto;
	}
}

/**
 * Government Schemes & Services homepage teaser's own marquee variant —
 * DEPRECATED (Prompt 057B swapped which section marquees, per direct
 * feedback: the scheme cards themselves went back to a plain grid so
 * they're not a moving target to click — see `.vp-gov-grid` below,
 * which they use now). `.vp-gov-marquee__dup`'s `display:contents`
 * technique lives on unchanged, reused by `.vp-gov-topics-marquee`
 * above via PHP giving its own duplicate wrapper that same class.
 */
.vp-gov-marquee__dup {
	display: contents;
}

@keyframes vp-gov-marquee-scroll {
	from {
		transform: translateX(0);
	}
	to {
		transform: translateX(-50%);
	}
}

.vp-gov-grid {
	display: grid;
	/* 340px (was 260px) so a ~1200px-wide row settles at 3 columns
	   instead of 4 — long real scheme/guide titles were wrapping to 2-3
	   lines at the narrower 4-up width, making cards uneven and tall.
	   Wrapped in `min(340px, 100%)` (G-25A fix): a bare 340px minimum
	   forces that width even when the container itself is narrower (a
	   320px viewport's content area is ~288px after padding) — confirmed
	   live as real horizontal page overflow at 320px. `min()` lets the
	   single remaining column shrink to the container's own width
	   instead of overflowing it; desktop's 3-column behavior above is
	   unchanged since 340px stays smaller than 100% there. */
	grid-template-columns: repeat(auto-fit, minmax(min(340px, 100%), 1fr));
	gap: var(--wp--preset--spacing--50, 1.5rem);
	margin: var(--wp--preset--spacing--50, 1.5rem) 0;
}

/**
 * `.vp-gov-grid` doubles as a `core/post-template` block's own className
 * on every taxonomy archive and the hub's "Latest" section, so WordPress
 * also auto-adds `is-layout-flow`/`wp-block-post-template-is-layout-flow`
 * to the same element. Core's global styles give flow-layout children a
 * `margin-block-start` on every item after the first (normal for a
 * single-column blog-post list), which leaked into this grid and pushed
 * every 2nd/3rd/... card down out of alignment with its row — visible as
 * misaligned, unequal-looking cards whenever a row has more than one
 * item. This grid positions cards with `gap` alone; zero out the
 * flow-margin core adds on top of it.
 */
.vp-gov-grid.is-layout-flow > * {
	margin-block-start: 0;
	margin-block-end: 0;
}

.vp-gov-card {
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
}

/* Every card title site-wide (homepage teasers here, plus the archive/
   taxonomy/related-content selectors further below) uses one
   consistent, always-on color — `accent-utility`, the same blue as the
   page-title accent line — rather than theme.json's raw link-blue
   (looked like an ordinary already-visited hyperlink) or the
   dark-text/blue-on-hover pattern this rule used before (direct
   decision: always-visible color reads more like "this card is a
   link" up front, closer to the nav's own treatment). */
.vp-gov-card h4 a,
.vp-guide-card h4 a,
.vp-gov-card h3 a,
.vp-gov-card .wp-block-post-title a,
.vp-guide-card .wp-block-post-title a {
	color: var(--wp--preset--color--accent-utility, #0369a1);
	text-decoration: none;
}

/**
 * Icon + title row (matches `.vp-home-tool-card__head`'s established
 * pattern exactly). Before this, the visual/icon sat in its own block
 * ABOVE the title as a separate flex child of a column layout — reads as
 * two stacked, disconnected pieces rather than one card header. Also
 * shrinks the icon down to the same 44px square the tool cards use:
 * `.vp-gov-visual--card`'s own 16:9 aspect-ratio (meant for the full
 * hero/hub-grid usage) made no sense at head-row size, and Guide's
 * `.vp-guide-icon--md` (28px) reads better a little larger next to a
 * heading-sized title.
 */
.vp-gov-card__head,
.vp-guide-card__head {
	display: flex;
	align-items: center;
	gap: 0.75rem;
	margin-bottom: 0.25rem;
}

.vp-gov-card__head h4,
.vp-guide-card__head h4,
.vp-guide-card__head .wp-block-post-title {
	margin: 0;
}

.vp-gov-card__head .vp-gov-visual {
	flex-shrink: 0;
	width: 44px;
	height: 44px;
	aspect-ratio: 1;
	margin-bottom: 0;
}

.vp-gov-card__head .vp-gov-visual__label {
	display: none;
}

.vp-gov-card__head .vp-gov-visual__icon svg {
	width: 22px;
	height: 22px;
}

.vp-guide-card__head .vp-guide-icon {
	flex-shrink: 0;
	width: 36px;
	height: 36px;
}

/* Slightly tighter than `.vedant-card`'s own default 1.5rem all around —
   combined with the head-row icon (above) and the 3-column width bump,
   this is most of where "card height thoda kam" comes from: less
   wasted padding, plus far less title-wrapping now that each card is
   noticeably wider. */
.vp-gov-card,
.vp-guide-card {
	padding: 1.1rem 1.25rem 1.25rem;
}

.vp-gov-card h4 a:hover,
.vp-guide-card h4 a:hover,
.vp-gov-card h3 a:hover,
.vp-gov-card .wp-block-post-title a:hover,
.vp-guide-card .wp-block-post-title a:hover {
	text-decoration: underline;
}

.vp-gov-card__excerpt {
	margin: 0;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	opacity: 0.85;
}

/**
 * The content-type badge ("Government Service") and the level/state
 * tags ("Central", "All India") used to be two separate direct children
 * of `.vp-gov-card` — a flex COLUMN with no `align-items` set, which
 * defaults to `stretch`. Each pill's own wrapping `<span>` stretched to
 * the full card width as a result (confirmed live via a screenshot: the
 * badge rendered as a wide horizontal bar, its text left-aligned inside
 * that wasted space, instead of a compact pill), and the two pill rows
 * sat on separate lines. Grouping them into one explicit flex row here
 * fixes both: this row is free to stretch full-width (fine — it's a
 * row, not a pill), while the actual `.vp-pill`s inside it size to
 * their own content as normal flex-row children.
 */
.vp-gov-card__pills {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0.4rem;
}

.vp-gov-card__pills .vp-pill,
.vp-gov-card__pills .vp-tag-pills {
	margin: 0;
}

.vp-gov-info-card {
	margin: var(--wp--preset--spacing--50, 1.5rem) 0;
}

.vp-gov-info-card__heading {
	margin-top: 0;
}

.vp-gov-info-card__list {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
	gap: 1rem;
	margin: 0;
}

/**
 * "Quick Information" card (Prompt G-15 Phase 5) — placed right after
 * the hero visual, before Overview, so a visitor gets the key facts
 * (category/department/level/state/portal/verified date) at a glance
 * without reading the full page. Reuses the same dt/dd row pattern as
 * `.vp-gov-info-card__list` below for visual consistency, with its own
 * top accent so it reads as a distinct "at a glance" block rather than
 * a continuation of the Important Information card further down.
 */
.vp-gov-quick-facts {
	margin: var(--wp--preset--spacing--50, 1.5rem) 0;
	border-top: 3px solid var(--wp--preset--color--primary, #1d4ed8);
}

.vp-gov-quick-facts__heading {
	margin-top: 0;
}

.vp-gov-quick-facts__list {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
	gap: 1rem;
	margin: 0;
}

.vp-gov-quick-facts__row dt {
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	font-weight: 600;
	opacity: 0.7;
	margin-bottom: 0.2rem;
}

.vp-gov-quick-facts__row dd {
	margin: 0;
}

.vp-gov-info-card__row dt {
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	font-weight: 600;
	opacity: 0.7;
	margin-bottom: 0.2rem;
}

.vp-gov-info-card__row dd {
	margin: 0;
}

.vp-gov-source-box {
	margin: var(--wp--preset--spacing--50, 1.5rem) 0;
	border-top: 3px solid var(--wp--preset--color--accent-government, #334155);
}

.vp-gov-source-box p {
	margin: 0 0 0.75rem;
}

.vp-gov-source-box p:last-child {
	margin-bottom: 0;
}

.vp-gov-source-box__notice {
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	opacity: 0.75;
}

.vp-gov-source-box__independence {
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	font-style: italic;
	opacity: 0.6;
}

/**
 * PDF to JPG / PDF to PNG result grid (Prompt 067) — reuses the Favicon
 * Generator's `.vp-favicon-grid`/`.vp-favicon-result` shapes, but each
 * cell holds a portrait page thumbnail instead of a square icon.
 */
.vp-pdf-export-grid__preview {
	width: 100%;
	max-width: 160px;
	height: auto;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--small, 4px);
}

/**
 * SERP Preview card (Prompt 067) — mimics Google's search-result
 * snippet styling (Arial-ish font, blue title, green URL breadcrumb,
 * gray description) using system fonts, not a Google-branded typeface.
 */
.vp-serp-card {
	max-width: 600px;
	padding: 1rem;
	background: #fff;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--base, 8px);
	font-family: Arial, Helvetica, sans-serif;
	margin: 0.75rem 0;
}

.vp-serp-card__url {
	color: #202124;
	font-size: 0.8rem;
	margin-bottom: 0.15rem;
}

.vp-serp-card__title {
	color: #1a0dab;
	font-size: 1.25rem;
	line-height: 1.3;
	margin-bottom: 0.2rem;
}

.vp-serp-card__description {
	color: #4d5156;
	font-size: 0.875rem;
	line-height: 1.5;
}

/**
 * Government frontend hub (Prompt G-05). Reuses `.vp-tool-filters`'
 * layout shape for `.vp-gov-filters` (same flex/gap/border/surface
 * treatment, just under a Government-specific class so a future change
 * to one never accidentally reflows the other), `.vedant-card`/
 * `.vp-btn`/`.vp-pill` everywhere else, and `.vp-home-section`/
 * `.vp-section-heading` for the homepage section — no parallel design
 * system.
 */
.vp-gov-hero {
	text-align: center;
	max-width: 760px;
	margin-inline: auto;
	padding-block: var(--wp--preset--spacing--50, 1.5rem) var(--wp--preset--spacing--40, 1rem);
}

.vp-gov-filters {
	display: flex;
	flex-wrap: wrap;
	align-items: end;
	gap: 1rem;
	margin-block: var(--wp--preset--spacing--40, 1rem);
	padding: var(--wp--preset--spacing--40, 1rem);
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--base, 8px);
	background: var(--wp--preset--color--surface, #f5f6f8);
	text-align: left;
}

.vp-gov-filters__field {
	display: flex;
	flex-direction: column;
	gap: 0.35em;
	flex: 1 1 180px;
	min-width: 0;
}

.vp-gov-filters__field label {
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	font-weight: 600;
}

.vp-gov-filters__field input,
.vp-gov-filters__field select {
	width: 100%;
	box-sizing: border-box;
	font: inherit;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	padding: 0.6em 0.75em;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--small, 4px);
	background: var(--wp--preset--color--base, #fff);
	color: var(--wp--preset--color--contrast, #14181f);
}

.vp-gov-filters__actions {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem;
}

.vp-gov-info-card__section-heading {
	font-size: var(--wp--preset--font-size--medium, 1.125rem);
	margin: 1.25rem 0 0.5rem;
}

.vp-gov-info-card__section-heading:first-of-type {
	margin-top: 0.75rem;
}

.vp-gov-source-box__heading {
	margin-top: 0;
}

.vp-gov-source-box__department {
	font-weight: 600;
	opacity: 0.85;
	margin-top: -0.5rem;
}

.vp-gov-source-box__disclaimer {
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	opacity: 0.65;
	margin-top: -0.5rem;
}

.vp-related-government {
	margin: var(--wp--preset--spacing--50, 1.5rem) 0;
}

/**
 * Official image / neutral visual (Prompt G-12 Phase 2/4/6).
 * `aspect-ratio` reserves the box before the image (or placeholder)
 * paints, so this never causes layout shift — real photos and the
 * decorative placeholder share the exact same box. `--card` is used in
 * every grid/card context; `--hero` (wider, shorter ratio) is used once,
 * at the top of the single page.
 */
.vp-gov-visual {
	display: flex;
	align-items: center;
	justify-content: center;
	overflow: hidden;
	border-radius: var(--wp--custom--radius--base, 8px);
	background: var(--wp--preset--color--surface, #f5f6f8);
	margin-bottom: 0.75rem;
}

.vp-gov-visual--card {
	aspect-ratio: 16 / 9;
}

.vp-gov-visual--hero {
	aspect-ratio: 21 / 9;
	margin-bottom: var(--wp--preset--spacing--40, 1rem);
}

/**
 * Prompt G-25: a placeholder hero (no real featured image set — true for
 * all 97 Government records today, since neither the data.gov.in Pincode
 * API nor any editorially-researched source exposes an image field) has
 * no real visual information to show beyond the content-type icon/label
 * the badge above already states — at the full 21:9 hero size that read
 * as a large, mostly-empty decorative block ahead of the actually useful
 * Quick Facts section. Rendered instead as a compact horizontal strip. A
 * genuine future photo (`.vp-gov-visual__image`) still uses the full
 * 21:9 ratio above, since real photo content deserves the space.
 */
.vp-gov-visual--hero.vp-gov-visual--placeholder {
	aspect-ratio: auto;
	flex-direction: row;
	justify-content: flex-start;
	gap: 0.75rem;
	padding: 0.85rem 1.1rem;
}

.vp-gov-visual--hero.vp-gov-visual--placeholder .vp-gov-visual__label {
	text-align: left;
	padding: 0;
}

.vp-gov-visual__image {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}

.vp-gov-visual--placeholder {
	flex-direction: column;
	gap: 0.5rem;
	background: color-mix(in srgb, var(--wp--preset--color--accent-government, #334155) 8%, var(--wp--preset--color--surface, #f5f6f8));
	color: var(--wp--preset--color--accent-government, #334155);
}

/**
 * Content-type-specific accent for the neutral visual placeholder
 * (Prompt G-14 Phase 3) — reuses existing Tools accent-color tokens
 * (no new palette entries) so Scheme/Service/Scholarship/Jobs each read
 * as visually distinct at a glance, while Dataset keeps the original
 * neutral slate (still fitting for plain reference data).
 */
.vp-gov-visual--scheme {
	background: color-mix(in srgb, var(--wp--preset--color--accent-ai, #c2410c) 10%, var(--wp--preset--color--surface, #f5f6f8));
	color: var(--wp--preset--color--accent-ai, #c2410c);
}

.vp-gov-visual--service {
	background: color-mix(in srgb, var(--wp--preset--color--accent-utility, #0369a1) 10%, var(--wp--preset--color--surface, #f5f6f8));
	color: var(--wp--preset--color--accent-utility, #0369a1);
}

.vp-gov-visual--scholarship {
	background: color-mix(in srgb, var(--wp--preset--color--accent-web, #15803d) 10%, var(--wp--preset--color--surface, #f5f6f8));
	color: var(--wp--preset--color--accent-web, #15803d);
}

.vp-gov-visual--jobs {
	background: color-mix(in srgb, var(--wp--preset--color--accent-files, #7c3aed) 10%, var(--wp--preset--color--surface, #f5f6f8));
	color: var(--wp--preset--color--accent-files, #7c3aed);
}

/**
 * "Useful Tools & Calculators" (Prompt G-14 Phase 5/6) — a small,
 * clearly-Vedant-branded card (not styled like the Official Source
 * box, so it never reads as government information) linking to 1-2
 * genuinely matched existing tools. Left-accent bar uses the site's
 * own primary color, distinguishing it from both the government
 * accent (source box) and advertisement placeholders.
 */
.vp-gov-related-tools {
	margin: var(--wp--preset--spacing--50, 1.5rem) 0;
	border-left: 3px solid var(--wp--preset--color--primary, #1d4ed8);
}

.vp-gov-related-tools__heading {
	margin-top: 0;
}

.vp-gov-related-tools__intro {
	opacity: 0.75;
	margin-top: -0.5rem;
}

.vp-gov-related-tools__list {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
	gap: 1rem;
}

.vp-gov-related-tools__item {
	display: flex;
	flex-direction: column;
	gap: 0.35rem;
	padding: 1rem;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--small, 4px);
	background: var(--wp--preset--color--base, #fff);
	text-decoration: none;
	color: inherit;
	transition: transform 0.15s ease, box-shadow 0.15s ease;
}

.vp-gov-related-tools__item:hover,
.vp-gov-related-tools__item:focus-visible {
	transform: translateY(-2px);
	box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
	outline: 2px solid var(--wp--preset--color--primary, #1d4ed8);
	outline-offset: 2px;
}

.vp-gov-related-tools__item-title {
	font-weight: 600;
}

.vp-gov-related-tools__item-desc {
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	opacity: 0.75;
}

.vp-gov-related-tools__item-cta {
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	font-weight: 600;
	color: var(--wp--preset--color--primary, #1d4ed8);
	margin-top: auto;
}

.vp-gov-visual__icon {
	opacity: 0.55;
}

.vp-gov-visual__label {
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	font-weight: 600;
	opacity: 0.7;
	text-align: center;
	padding: 0 1rem;
}

/**
 * Card polish (Prompt G-12 Phase 4): consistent height, a visible hover
 * and keyboard-focus lift, without any new animation library — a plain
 * CSS transform/shadow, matching the restraint of the rest of this
 * design system. Adds to `.vp-gov-grid`/`.vp-gov-card`'s existing
 * layout rules (defined earlier in this file, G-05) rather than
 * redeclaring them.
 */
.vp-gov-card {
	height: 100%;
	transition: transform 0.15s ease, box-shadow 0.15s ease;
}

.vp-gov-card:hover,
.vp-gov-card:focus-within {
	transform: translateY(-2px);
	box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
}

.vp-gov-card a:focus-visible {
	outline: 2px solid var(--wp--preset--color--primary, #1d4ed8);
	outline-offset: 2px;
}

.vp-gov-card__excerpt {
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	opacity: 0.8;
	margin: 0.25rem 0;
}

.vp-gov-card__meta {
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	opacity: 0.6;
	margin-top: auto;
	padding-top: 0.4rem;
}

.vp-gov-card__cta {
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	font-weight: 600;
	color: var(--wp--preset--color--primary, #1d4ed8);
	text-decoration: none;
}

.vp-gov-card__cta:hover,
.vp-gov-card__cta:focus-visible {
	text-decoration: underline;
}

/**
 * Map Tools shared map container (Prompt 069) — Leaflet requires its
 * container to have an explicit height (it renders at 0px tall
 * otherwise, a well-known Leaflet gotcha), hence the fixed height here
 * rather than relying on content to size it. `.vp-tool-map` is used
 * directly inside an ApiToolRenderer result container; `.vp-calculator__map`
 * is the equivalent slot `CalculatorRenderer` emits when a calculator
 * opts in via `map: true`.
 */
.vp-tool-map,
.vp-calculator__map {
	position: relative;
	height: 320px;
	margin: 0.75rem 0;
	border-radius: var(--wp--custom--radius--base, 8px);
	overflow: hidden;
	z-index: 0;
}

.vp-calculator__map {
	margin-top: var(--wp--preset--spacing--40, 1rem);
}

/**
 * Guide Information Hub (Prompt G-20). Reuses `.vedant-card`, `.vp-pill`/
 * `.vp-tag-pills`, `.vp-btn`, and the same auto-fit-grid /
 * `is-layout-flow`-margin-fix / card-hover-lift techniques already
 * proven on `.vp-gov-grid`/`.vp-gov-card` — only the rules genuinely
 * specific to Guide content live here, not a parallel design system.
 */
.vp-guide-icon {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	flex-shrink: 0;
	color: var(--wp--preset--color--contrast, #1e1e1e);
	opacity: 0.7;
}

.vp-guide-icon svg {
	width: 100%;
	height: 100%;
}

.vp-guide-icon--sm {
	width: 20px;
	height: 20px;
}

.vp-guide-icon--md {
	width: 28px;
	height: 28px;
}

.vp-guide-icon--lg {
	width: 40px;
	height: 40px;
}

.vp-guide-icon--accent-finance {
	color: var(--wp--preset--color--accent-finance, #78350f);
	opacity: 1;
}

.vp-guide-icon--accent-investment {
	color: var(--wp--preset--color--accent-investment, #0d9488);
	opacity: 1;
}

.vp-guide-icon--accent-salary {
	color: var(--wp--preset--color--accent-salary, #7e22ce);
	opacity: 1;
}

.vp-guide-icon--accent-government {
	color: var(--wp--preset--color--accent-government, #334155);
	opacity: 1;
}

.vp-guide-icon--accent-utility {
	color: var(--wp--preset--color--accent-utility, #0369a1);
	opacity: 1;
}

.vp-guide-icon--accent-dev {
	color: var(--wp--preset--color--accent-dev, #4338ca);
	opacity: 1;
}

.vp-guide-icon--accent-map {
	color: var(--wp--preset--color--accent-map, #a16207);
	opacity: 1;
}

.vp-guide-visual--photo img {
	display: block;
	width: 100%;
	height: auto;
	border-radius: var(--wp--custom--radius--base, 8px);
}

.vp-pill--guide-category {
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	font-weight: 600;
	background: var(--wp--preset--color--surface, #f5f6f8);
}

.vp-pill--muted {
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	opacity: 0.65;
	background: transparent;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
}

.vp-guide-hero {
	margin-bottom: var(--wp--preset--spacing--50, 1.5rem);
}

.vp-guide-header {
	display: flex;
	align-items: center;
	gap: 0.75rem;
	margin-bottom: 0.5rem;
}

.vp-guide-header__title {
	margin: 0;
}

.vp-guide-summary {
	font-size: var(--wp--preset--font-size--medium, 1.125rem);
	opacity: 0.85;
	max-width: 65ch;
}

.vp-guide-grid {
	display: grid;
	/* 340px (was 260px) so a ~1200px-wide row settles at 3 columns
	   instead of 4 — see `.vp-gov-grid`'s identical change for why.
	   `min(340px, 100%)` (G-25A fix) — same real 320px-viewport overflow
	   bug as `.vp-gov-grid`, same fix; see that rule's own comment. */
	grid-template-columns: repeat(auto-fit, minmax(min(340px, 100%), 1fr));
	gap: var(--wp--preset--spacing--50, 1.5rem);
	margin: var(--wp--preset--spacing--50, 1.5rem) 0;
}

/* Same core-injected flow-margin fix as `.vp-gov-grid` (see that rule's
   own comment) — this grid also doubles as a `core/post-template`
   className, which gets `is-layout-flow` auto-added by WordPress. */
.vp-guide-grid.is-layout-flow > * {
	margin-block-start: 0;
	margin-block-end: 0;
}

.vp-guide-card {
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
	height: 100%;
	transition: transform 0.15s ease, box-shadow 0.15s ease;
}

.vp-guide-card:hover,
.vp-guide-card:focus-within {
	transform: translateY(-2px);
	box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
}

.vp-guide-card a:focus-visible {
	outline: 2px solid var(--wp--preset--color--primary, #1d4ed8);
	outline-offset: 2px;
}

.vp-guide-category-grid {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
	gap: var(--wp--preset--spacing--40, 1rem);
	margin: var(--wp--preset--spacing--50, 1.5rem) 0;
}

.vp-guide-category-card {
	display: flex;
	align-items: center;
	gap: 0.6rem;
	padding: 1rem 1.2rem;
	background: var(--wp--preset--color--surface, #f5f6f8);
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--base, 8px);
	text-decoration: none;
	color: var(--wp--preset--color--contrast, #14181f);
	transition: border-color 0.15s ease;
}

.vp-guide-category-card:hover,
.vp-guide-category-card:focus-visible {
	border-color: var(--wp--preset--color--primary, #1d4ed8);
}

.vp-guide-category-card__name {
	font-weight: 600;
}

.vp-guide-category-card__count {
	margin-left: auto;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	opacity: 0.55;
	font-variant-numeric: tabular-nums;
}

/* Table of contents — plain anchor list, no JS. */
.vp-guide-toc {
	margin: var(--wp--preset--spacing--50, 1.5rem) 0;
	padding: 1.2rem 1.5rem;
	background: var(--wp--preset--color--surface, #f5f6f8);
	border-radius: var(--wp--custom--radius--base, 8px);
}

.vp-guide-toc__heading {
	margin-top: 0;
	font-size: var(--wp--preset--font-size--medium, 1.125rem);
}

.vp-guide-toc__list {
	margin: 0;
	padding-left: 1.2rem;
}

.vp-guide-toc__list li {
	margin-bottom: 0.3rem;
}

/* "Key Points" callout — a distinct top accent so it reads as an
   at-a-glance summary, same visual weight as `.vp-gov-quick-facts`. */
.vp-guide-key-points {
	margin: var(--wp--preset--spacing--50, 1.5rem) 0;
	border-top: 3px solid var(--wp--preset--color--primary, #1d4ed8);
}

.vp-guide-key-points__heading {
	margin-top: 0;
}

.vp-guide-key-points__list {
	margin: 0;
	padding-left: 1.2rem;
}

.vp-guide-key-points__list li {
	margin-bottom: 0.4rem;
}

/* FAQ — native <details>/<summary>, no JS. */
.vp-guide-faq {
	margin: var(--wp--preset--spacing--50, 1.5rem) 0;
}

.vp-guide-faq__heading {
	margin-top: 0;
}

.vp-guide-faq__item {
	padding: 0.9rem 0;
	border-bottom: 1px solid var(--wp--preset--color--border, #e2e5ea);
}

.vp-guide-faq__item summary {
	cursor: pointer;
	font-weight: 600;
}

.vp-guide-faq__item summary:focus-visible {
	outline: 2px solid var(--wp--preset--color--primary, #1d4ed8);
	outline-offset: 2px;
}

.vp-guide-faq__item p {
	margin: 0.6rem 0 0;
	opacity: 0.85;
}

.vp-guide-source-box {
	margin: var(--wp--preset--spacing--50, 1.5rem) 0;
	border-top: 3px solid var(--wp--preset--color--accent-government, #334155);
}

.vp-guide-source-box__heading {
	margin-top: 0;
}

.vp-guide-source-box__disclaimer {
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	opacity: 0.65;
	margin-bottom: 0;
}

/**
 * "Calculate It Yourself" — the Tool CTA (Prompt G-20 Phase 3). Given a
 * clearly distinct visual treatment (accent-tinted background + border,
 * not just a text link) so it never reads as an ordinary related-content
 * link or, worse, an ad — the prompt's own explicit requirement.
 */
.vp-guide-tool-cta {
	margin: var(--wp--preset--spacing--50, 1.5rem) 0;
}

.vp-guide-tool-cta__heading {
	margin-top: 0;
}

.vp-guide-tool-cta__list {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
	gap: 1rem;
}

.vp-guide-tool-cta__card {
	display: flex;
	flex-direction: column;
	gap: 0.3rem;
	padding: 1.1rem 1.3rem;
	background: color-mix(in srgb, var(--wp--preset--color--primary, #1d4ed8) 8%, var(--wp--preset--color--base, #fff));
	border: 1px solid var(--wp--preset--color--primary, #1d4ed8);
	border-radius: var(--wp--custom--radius--base, 8px);
	text-decoration: none;
	color: var(--wp--preset--color--contrast, #14181f);
	transition: background 0.15s ease;
}

.vp-guide-tool-cta__card:hover,
.vp-guide-tool-cta__card:focus-visible {
	background: color-mix(in srgb, var(--wp--preset--color--primary, #1d4ed8) 16%, var(--wp--preset--color--base, #fff));
	outline: none;
}

.vp-guide-tool-cta__label {
	font-size: var(--wp--preset--font-size--small, 0.75rem);
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.04em;
	color: var(--wp--preset--color--primary, #1d4ed8);
}

.vp-guide-tool-cta__title {
	font-size: var(--wp--preset--font-size--medium, 1.125rem);
	font-weight: 600;
}

.vp-guide-tool-cta__link {
	font-weight: 600;
	color: var(--wp--preset--color--primary, #1d4ed8);
}

.vp-guide-related {
	margin: var(--wp--preset--spacing--50, 1.5rem) 0;
}

.vp-guide-related__list {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	gap: 0.6rem;
}

.vp-guide-related__list li a {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	text-decoration: none;
	color: var(--wp--preset--color--contrast, #14181f);
	font-weight: 500;
}

.vp-guide-related__list li a:hover,
.vp-guide-related__list li a:focus-visible {
	text-decoration: underline;
}

.vp-guide-final-cta {
	margin: var(--wp--preset--spacing--60, 2rem) 0;
	padding: var(--wp--preset--spacing--50, 1.5rem);
	background: var(--wp--preset--color--surface, #f5f6f8);
	border-radius: var(--wp--custom--radius--base, 8px);
	text-align: center;
}

.vp-guide-final-cta__buttons {
	display: flex;
	justify-content: center;
	gap: 0.75rem;
	flex-wrap: wrap;
	margin-top: 0.75rem;
}

@media (max-width: 600px) {
	.vp-guide-header {
		flex-wrap: wrap;
	}
}

/* Color Converter (Prompt G-24) */

.vp-color-converter__layout {
	display: flex;
	flex-wrap: wrap;
	align-items: flex-start;
	gap: 1.5rem;
	margin-bottom: var(--wp--preset--spacing--40, 1rem);
}

.vp-color-converter__swatch-wrap {
	display: flex;
	flex-direction: column;
	gap: 0.6rem;
	flex-shrink: 0;
}

.vp-color-converter__swatch {
	width: 120px;
	height: 120px;
	border-radius: 12px;
	border: 1px solid var(--wp--preset--color--border, #ccc);
	box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.05);
}

/**
 * Redesigned (was a tiny 36px circle overlapping the swatch's corner,
 * `overflow: hidden` + `border-radius: 50%` clipping it into a plain
 * gray dot with no visible color — confirmed live: the browser's own
 * internal color-swatch fill doesn't survive being squeezed into a
 * circular clip like that, a known cross-browser quirk with styled
 * `<input type="color">`. Now a proper labeled control of its own,
 * below the preview swatch, at the swatch's full width so it reads as
 * "the color picker" rather than a decorative corner badge — a visitor
 * asked for exactly this ("color picker chahiye taki user color pic
 * karke code review kar sake").
 */
.vp-color-converter__picker-label {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	width: 120px;
	padding: 0.5rem 0.75rem;
	border: 1px solid var(--wp--preset--color--border, #ccc);
	border-radius: 8px;
	background: var(--wp--preset--color--surface, #f5f6f8);
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	font-weight: 600;
	cursor: pointer;
	box-sizing: border-box;
}

.vp-color-converter__picker-label:hover {
	border-color: var(--wp--preset--color--primary, #1d4ed8);
}

.vp-color-converter__picker {
	width: 28px;
	height: 28px;
	flex-shrink: 0;
	padding: 0;
	border: 1px solid var(--wp--preset--color--border, #ccc);
	border-radius: 6px;
	cursor: pointer;
}

.vp-color-converter__fields {
	flex: 1;
	min-width: 240px;
	display: flex;
	flex-direction: column;
	gap: 0.75rem;
}

.vp-color-field label {
	display: block;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	font-weight: 600;
	margin-bottom: 0.25rem;
}

.vp-color-field__row {
	display: flex;
	gap: 0.5rem;
}

.vp-color-field__input {
	flex: 1;
	min-width: 0;
	padding: 0.5rem 0.75rem;
	border: 1px solid var(--wp--preset--color--contrast-2, #ccc);
	border-radius: 6px;
	font-family: var(--wp--preset--font-family--monospace, monospace);
	font-size: var(--wp--preset--font-size--small, 0.875rem);
}

.vp-color-field__copy {
	flex-shrink: 0;
}

@media (max-width: 480px) {
	.vp-color-converter__layout {
		flex-direction: column;
		align-items: center;
	}
}

/**
 * Category archive (`templates/taxonomy-tool_category.html`,
 * `Tools\CategoryArchiveDisplay`) — top/footer ad rails, hero band, tag
 * filter pills, the 2-column main+sticky-sidebar layout, compact
 * pagination, and the "About {Category}" SEO section. `tool_tag`
 * archives keep using the generic, unstyled `templates/archive.html`
 * untouched — none of this applies there.
 *
 * Card/icon/pill/ad overrides below are deliberately scoped under
 * `.vp-category-main`/`.vp-category-pagination` (this page's own
 * wrapper classes) rather than edited on the shared `.vp-home-tool-card`/
 * `.vp-ad`/`.vp-tool-icon`/`.page-numbers` rules those selectors extend —
 * those shared rules are also used by the homepage and the Tool Archive
 * page (`templates/archive-tool.html`), which this redesign must not
 * change.
 */

.vp-category-top-ad,
.vp-category-inline-ad,
.vp-category-footer-ad {
	margin-block: var(--wp--preset--spacing--40, 1rem);
}

.vp-category-top-ad {
	margin-top: 0.75rem;
}

.vp-category-hero {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: 2rem;
	padding: clamp(1.25rem, 3vw, 1.75rem);
	margin-block: var(--wp--preset--spacing--50, 1.5rem);
	/**
	 * Soft blue tint in light mode, a subtle dark-surface "glow" once the
	 * dark-theme tokens swap in — one `color-mix()` rule covers both,
	 * since it reads the same `--primary`/`--base` custom properties
	 * `dark-theme.css` already redefines wholesale (see that file's own
	 * docblock). No separate dark-mode override needed.
	 */
	background: color-mix(in srgb, var(--wp--preset--color--primary, #1d4ed8) 6%, var(--wp--preset--color--base, #fff));
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--base, 8px);
}

.vp-category-hero__main {
	display: flex;
	align-items: flex-start;
	gap: 1rem;
	flex: 1 1 320px;
}

.vp-category-hero__icon {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 64px;
	height: 64px;
	flex-shrink: 0;
	border-radius: var(--wp--custom--radius--base, 8px);
	background: var(--wp--preset--color--base, #fff);
}

.vp-category-hero__icon .vp-tool-icon {
	width: 32px;
	height: 32px;
	opacity: 1;
}

.vp-category-hero__title {
	margin: 0 0 0.35rem;
}

.vp-category-hero__description {
	margin: 0 0 0.5rem;
	opacity: 0.85;
	max-width: 60ch;
}

.vp-category-hero__meta {
	margin: 0;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	opacity: 0.7;
}

.vp-category-hero__visual {
	display: none;
	flex-shrink: 0;
}

@media (min-width: 1024px) {
	.vp-category-hero__visual {
		display: block;
	}
}

/**
 * Shrinks the shared `.vp-hero-window` component (also used at its
 * default 280px size by the homepage hero, patterns/home-hero.php —
 * left alone there) specifically inside this compact category hero, so
 * the hero band stays within this project's requested ~220-280px
 * desktop height instead of growing to fit the visual's default size.
 */
.vp-category-hero__visual .vp-hero-window {
	width: 150px;
	padding: 0.6rem;
}

.vp-category-hero__visual .vp-hero-window__bar {
	margin-bottom: 0.6rem;
}

.vp-category-hero__visual .vp-hero-window__grid {
	gap: 0.4rem;
}

.vp-category-filter-pills {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0.5rem;
	margin-bottom: var(--wp--preset--spacing--50, 1.5rem);
	/**
	 * A tinted "tray" behind the row — plain white pills on a plain white
	 * page had almost no contrast except their own 1px border, so the
	 * whole control read as flat/unfinished. The tray gives the group a
	 * visible boundary; individual pills still get their own background
	 * below so the active/inactive distinction stays clear.
	 */
	padding: 0.5rem;
	background: var(--wp--preset--color--surface, #f5f6f8);
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--base, 8px);
}

/**
 * THE ACTUAL ROOT CAUSE of the "empty pill / text spilling out below"
 * bug (confirmed via raw HTML source, not the rendered DOM): WordPress
 * core's `core/shortcode` block unconditionally runs `wpautop()` on
 * whatever HTML `[vedant_category_filter_pills]` returns
 * (`wp-includes/blocks/shortcode.php`, `render_block_core_shortcode()`
 * — literally `return wpautop( $content );`). `wpautop()` converts
 * every lone newline into a literal `<br />` — and `Tools\
 * CategoryArchiveDisplay::filter_pills_shortcode()`'s PHP template (like
 * every other shortcode in this codebase) is pretty-printed with a
 * newline after `<a ...>`, one before `</a>`, and one between each
 * sibling pill. Confirmed live in the page source: every one of those
 * became a real `<br />` — one INSIDE each pill's own text (pushing the
 * label onto what LOOKED like a second, unstyled line below the fixed-
 * height pill box) and one BETWEEN every pair of pills (an extra,
 * invisible flex item throwing off the row). Same class of bug as the
 * stray `<br>` fixed earlier in `.vp-category-archive-layout` — same
 * fix here: `display: none` removes it from both the flex layout and
 * the anchor's inline content, with no PHP template rewrite needed.
 */
.vp-category-filter-pills br {
	display: none;
}

.vp-category-filter-pill {
	/**
	 * The actual cause of every earlier "text isn't centered" symptom was
	 * the stray `wpautop()`-inserted `<br>` fixed just above (one landed
	 * INSIDE each pill's own text, pushing the label down) — not a font-
	 * metrics/line-height problem. With that fixed at the source, plain
	 * `line-height` centering (no flexbox needed) is simple and correct.
	 * `height`/`line-height` both `38px`, zero vertical padding: a
	 * browser centers one line of text within its own line-height via
	 * equal leading above/below, which is all that's needed here.
	 *
	 * `flex-shrink: 0` + `white-space: nowrap` (kept even without the
	 * `<br>` bug): `.vp-category-filter-pills` is a wrapping flex row, so
	 * without these a pill could still be squeezed narrower than its own
	 * label on a category with many/long tags and wrap its text — this
	 * guarantees a pill is always exactly as wide as its label, and the
	 * ROW wraps to additional lines instead.
	 */
	display: inline-block;
	height: 38px;
	line-height: 38px;
	flex-shrink: 0;
	white-space: nowrap;
	padding: 0 1.1em;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	/* A soft rounded-rectangle chip instead of a full stadium/pill shape. */
	border-radius: var(--wp--custom--radius--small, 4px);
	background: var(--wp--preset--color--base, #fff);
	color: var(--wp--preset--color--contrast, #14181f);
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	font-weight: 600;
	text-align: center;
	text-decoration: none;
	box-sizing: border-box;
}

.vp-category-filter-pill:hover {
	border-color: var(--wp--preset--color--primary, #1d4ed8);
	color: var(--wp--preset--color--primary, #1d4ed8);
}

.vp-category-filter-pill.is-active {
	background: var(--wp--preset--color--primary, #1d4ed8);
	border-color: var(--wp--preset--color--primary, #1d4ed8);
	color: var(--wp--preset--color--base, #fff);
}

/* Horizontal scroll, not wrap, once the pill row can no longer fit —
   keeps the filter bar one compact line instead of growing tall. */
@media (max-width: 767px) {
	.vp-category-filter-pills {
		flex-wrap: nowrap;
		overflow-x: auto;
		padding-bottom: 0.35rem;
		-webkit-overflow-scrolling: touch;
		scrollbar-width: thin;
	}

	.vp-category-filter-pill {
		flex-shrink: 0;
	}
}

/**
 * Main + sidebar. Sidebar narrows at the 992px tier (280px) and widens
 * once the container has room to breathe at 1200px (320px) — at the
 * theme's own 1200px `wideSize` that puts the main column at ~71% and
 * the sidebar at ~27%, inside this project's requested 70-74%/26-30%
 * split.
 */
.vp-category-archive-layout {
	display: grid;
	grid-template-columns: 1fr;
	align-items: start;
	gap: 1.5rem;
	margin-bottom: var(--wp--custom--spacing--section);
}

@media (min-width: 992px) {
	.vp-category-archive-layout {
		grid-template-columns: minmax(0, 1fr) 280px;
	}
}

@media (min-width: 1200px) {
	.vp-category-archive-layout {
		grid-template-columns: minmax(0, 1fr) 320px;
	}
}

.vp-category-main {
	min-width: 0;
}

/**
 * Real bug, not decoration: WordPress's block-template parser leaves a
 * stray `<br>` as a direct child of `.vp-category-archive-layout`
 * between the `vp-category-main` group and the `[vedant_category_sidebar]`
 * shortcode block (confirmed live in the DOM — an artifact of a `wp:group`
 * block immediately followed by a bare `wp:shortcode` block). Harmless in
 * a flow layout, but inside this 2-column CSS Grid it counts as a real
 * grid item and consumes column 2 of row 1, pushing the actual sidebar
 * into row 2 column 1 — which is exactly why the sidebar previously
 * rendered missing/below instead of beside the grid. `display: none`
 * removes it from grid layout entirely (per spec, a `display: none`
 * element is never a grid item), fixing the 2-column placement with no
 * template change needed.
 */
.vp-category-archive-layout > br {
	display: none;
}

/**
 * Real bug, not decoration (#2): `.vp-category-archive-layout` keeps
 * Gutenberg's default `is-layout-flow` class (no explicit "layout" was
 * set on this group block), which makes WordPress core's own global
 * block-gap stylesheet apply `margin-block-start: 1.5rem` to every one
 * of its children except `:first-child` — a 0-specificity `:where()`
 * rule, so nothing here was already overriding it. `.vp-category-main`
 * (the first child) is exempted by that same core rule, but
 * `.vp-category-sidebar` (the second real child, after the hidden `<br>`
 * above) is not — confirmed live, it was carrying a 24px top margin
 * that neither `.vp-category-main` nor `.vp-category-tools-grid`'s first
 * row had, so the sidebar (and its first ad) started visibly lower than
 * the tool grid instead of lining up with it. Zeroing both children's
 * top margin here makes the alignment independent of DOM order/core's
 * blockGap behavior, rather than relying on which child happens to be
 * exempted.
 */
.vp-category-main,
.vp-category-sidebar {
	margin-top: 0;
}

/**
 * Tool grid inside the (now narrower) main column. `auto-fit` handles
 * >=992px gracefully on its own (3 columns once the column is wide
 * enough, 2 if it isn't yet) — the two explicit breakpoints below only
 * exist to match this project's specific "2 columns at tablet, 1 at
 * phone" requirement, which plain `auto-fit` can't guarantee on its own.
 */
.vp-category-tools-grid {
	grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
}

@media (max-width: 991px) {
	.vp-category-tools-grid {
		grid-template-columns: repeat(2, 1fr);
	}
}

@media (max-width: 767px) {
	.vp-category-tools-grid {
		grid-template-columns: 1fr;
	}
}

/**
 * Card visual refresh for this page only. A full border (subtle,
 * neutral) on top of the shared component's existing 3px category-
 * accent top border — that accent stripe is left alone (still comes
 * from `.vp-home-tool-card--accent-*` in components.css above), this
 * only adds the left/right/bottom edges the shared card doesn't have.
 */
.vp-category-main .vp-home-tool-card {
	border-left: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-right: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-bottom: 1px solid var(--wp--preset--color--border, #e2e5ea);
	padding: 1.1rem;
	transition: border-color 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease;
}

@media (prefers-reduced-motion: reduce) {
	.vp-category-main .vp-home-tool-card {
		transition: none;
	}
}

.vp-category-main .vp-home-tool-card:hover,
.vp-category-main .vp-home-tool-card:focus-within {
	border-color: var(--wp--preset--color--primary, #1d4ed8);
	transform: translateY(-2px);
	box-shadow: var(--wp--preset--shadow--sm, 0 1px 3px rgba(0, 0, 0, 0.08));
}

/**
 * Card titles were inheriting theme.json's global link color (the same
 * blue as every plain text link), so every title read as an already-
 * visited-looking hyperlink rather than a heading. Now always the same
 * `accent-utility` blue every other card title site-wide uses (see the
 * shared rule near `.vp-gov-card h4 a` above) — underline on hover is
 * the interaction cue instead of a color shift.
 */
.vp-category-main .vp-home-tool-card__title a,
.vp-category-main .wp-block-post-title a {
	color: var(--wp--preset--color--accent-utility, #0369a1);
}

.vp-category-main .vp-home-tool-card:hover .wp-block-post-title a,
.vp-category-main .vp-home-tool-card:focus-within .wp-block-post-title a {
	text-decoration: underline;
}

/* 3-line clamp keeps every card in a row the same rough height even
   when excerpt lengths differ, instead of letting one long excerpt
   stretch its whole row. */
.vp-category-main .vp-home-tool-card > .wp-block-post-excerpt {
	display: -webkit-box;
	-webkit-line-clamp: 3;
	-webkit-box-orient: vertical;
	overflow: hidden;
}

.vp-category-sidebar {
	display: flex;
	flex-direction: column;
	gap: 1.5rem;
}

@media (min-width: 992px) {
	.vp-category-sidebar {
		position: sticky;
		/* Clears the sticky site header — measured 73px (44px toggle
		   button + 2×14px vertical padding + 1px border) — plus a
		   comfortable gap, so the sidebar never starts out hidden
		   underneath it. Stops naturally once its grid row ends; no JS
		   needed (see `.site-header`'s own sticky rule above). */
		top: calc(73px + 1rem);
	}
}

.vp-sidebar-widget {
	padding: 1.25rem;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--base, 8px);
	background: var(--wp--preset--color--base, #fff);
}

/**
 * The two sidebar ad slots use the shared `.vp-ad` placeholder (also
 * used on tool detail pages, left unchanged there). Scoped fixes only:
 * `.vp-ad`'s own `margin: 1rem auto` was stacking on top of this
 * sidebar's `gap: 1.5rem` flex spacing, leaving a much bigger gap
 * before/after each ad than between any other two sidebar widgets — the
 * gap alone is enough spacing. A solid (not dashed) border matching the
 * radius of `.vp-sidebar-widget` also makes the ad read as one more
 * sidebar card instead of a rough, disconnected dev placeholder box.
 */
.vp-category-sidebar .vp-ad {
	margin: 0;
	border-style: solid;
	border-radius: var(--wp--custom--radius--base, 8px);
}

.vp-sidebar-widget__title {
	margin: 0 0 1rem;
	font-size: var(--wp--preset--font-size--medium, 1rem);
}

.vp-sidebar-help__text {
	margin: 0 0 0.75rem;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	opacity: 0.85;
}

.vp-sidebar-help__link {
	font-weight: 600;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	text-decoration: none;
}

.vp-sidebar-help__link:hover {
	text-decoration: underline;
}

.vp-sidebar-category-list,
.vp-sidebar-popular-list {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: 0.25rem;
}

.vp-sidebar-popular-list {
	list-style: decimal;
	padding-left: 1.25rem;
}

.vp-sidebar-popular-list li {
	padding-block: 0.35rem;
}

.vp-sidebar-popular-list a {
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	text-decoration: none;
}

.vp-sidebar-popular-list a:hover {
	text-decoration: underline;
}

.vp-sidebar-category-list a {
	display: flex;
	align-items: center;
	gap: 0.6rem;
	padding: 0.5rem;
	border-radius: var(--wp--custom--radius--small, 4px);
	color: var(--wp--preset--color--contrast, #14181f);
	text-decoration: none;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
}

.vp-sidebar-category-list a:hover {
	background: var(--wp--preset--color--surface, #f5f6f8);
}

.vp-sidebar-category-list a.is-active {
	background: var(--wp--preset--color--surface, #f5f6f8);
	font-weight: 600;
	color: var(--wp--preset--color--primary, #1d4ed8);
}

.vp-sidebar-category-list a .vp-tool-icon {
	width: 18px;
	height: 18px;
	flex-shrink: 0;
}

.vp-sidebar-category-list__count {
	margin-left: auto;
	opacity: 0.6;
	font-size: 0.75rem;
}

/* Mobile: no sticky sidebar (spec requirement) — plain stacked flow
   below the main column, already the default outside the >=992px rule
   above. */
@media (max-width: 991px) {
	.vp-category-sidebar {
		position: static;
	}
}

/**
 * Compact square pagination (core `wp:query-pagination`), scoped to
 * this page's own `vp-category-pagination` className so the Tool
 * Archive/Guide archive pagination (same core block, no such className)
 * is untouched.
 */
.vp-category-pagination {
	justify-content: center;
	align-items: center;
	gap: 0.4rem;
	flex-wrap: wrap;
	margin-top: var(--wp--preset--spacing--40, 1rem);
}

.vp-category-pagination .wp-block-query-pagination-numbers {
	display: flex;
	align-items: center;
	gap: 0.35rem;
}

.vp-category-pagination .page-numbers {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	box-sizing: border-box;
	min-width: 40px;
	height: 40px;
	padding: 0 0.65rem;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--small, 4px);
	background: var(--wp--preset--color--base, #fff);
	color: var(--wp--preset--color--contrast, #14181f);
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	font-weight: 600;
	text-decoration: none;
}

.vp-category-pagination a.page-numbers:hover {
	border-color: var(--wp--preset--color--primary, #1d4ed8);
	color: var(--wp--preset--color--primary, #1d4ed8);
}

.vp-category-pagination .page-numbers.current {
	background: var(--wp--preset--color--primary, #1d4ed8);
	border-color: var(--wp--preset--color--primary, #1d4ed8);
	color: var(--wp--preset--color--base, #fff);
}

.vp-category-pagination .page-numbers.dots {
	min-width: auto;
	height: auto;
	padding: 0 0.15rem;
	border: 0;
	background: transparent;
	opacity: 0.6;
}

/**
 * "About {Category}" SEO section (`Tools\CategoryArchiveDisplay::
 * about_shortcode()`) — the term's own full description, only ever
 * rendered when a category actually has one set.
 */
.vp-category-about {
	margin-block: var(--wp--custom--spacing--section);
	padding-top: var(--wp--preset--spacing--50, 1.5rem);
	border-top: 1px solid var(--wp--preset--color--border, #e2e5ea);
}

.vp-category-about__title {
	margin: 0 0 0.75rem;
}

.vp-category-about__body {
	max-width: 70ch;
	opacity: 0.9;
}

.vp-category-about__body p {
	margin: 0 0 1rem;
}

.vp-category-about__body p:last-child {
	margin-bottom: 0;
}

/**
 * Inline `<code>` in body copy (e.g. a tool/guide description mentioning
 * `<script>`, `<style>`, a CLI flag, a filename) had no styling at all —
 * it rendered as plain unstyled text, indistinguishable from surrounding
 * prose. Scoped to `.entry-content` only: CodeMirror's own editors never
 * use a raw `<code>` element, so this can't affect them.
 */
.entry-content code {
	font-family: var(--wp--preset--font-family--mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
	font-size: 0.875em;
	background: var(--wp--preset--color--surface, #f5f6f8);
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: 4px;
	padding: 0.15em 0.4em;
}

/**
 * Prompt G-29 — shared building blocks for the new bespoke-markup
 * generator tools (Dummy/JSON/CSV/SQL Data Generators, HTML Table
 * Generator, Invoice Generator, Random Place Explorer). Every tool using
 * Tools\ApiToolRenderer (Dummy Text/Image, Placeholder, Random Number/
 * String, UUID, Fake Profile, Avatar) already inherits full styling from
 * the existing `.vp-api-tool__*`/`.vp-btn`/`.vp-status`/
 * `.vp-generated-value` rules above — nothing new needed for those.
 */

.vp-ddg-fields-wrap,
.vp-jddg-schema-label,
.vp-ddg-controls,
.vp-ddg-result {
	margin-top: 1.25rem;
}

.vp-ddg-field-row {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	margin-bottom: 0.5rem;
	flex-wrap: wrap;
}

.vp-ddg-field-row input[type='text'] {
	flex: 1 1 12rem;
	min-width: 8rem;
}

.vp-ddg-field-row select {
	flex: 1 1 12rem;
	min-width: 8rem;
}

.vp-ddg-controls {
	display: flex;
	align-items: flex-end;
	gap: 1rem;
	flex-wrap: wrap;
}

.vp-ddg-controls label {
	display: flex;
	flex-direction: column;
	gap: 0.3rem;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	font-weight: 600;
}

.vp-ddg-controls input,
.vp-ddg-controls select {
	font: inherit;
	padding: 0.5em 0.7em;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--small, 6px);
}

.vp-jddg-schema-label {
	display: flex;
	flex-direction: column;
	gap: 0.4rem;
	font-weight: 600;
}

.vp-jddg-schema {
	font-family: var(--wp--preset--font-family--mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
	font-size: 0.875em;
	padding: 0.75em;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--small, 6px);
	width: 100%;
	box-sizing: border-box;
	resize: vertical;
}

.vp-ddg-table-wrap {
	overflow-x: auto;
	margin-top: 1rem;
}

.vp-ddg-table,
.vp-htg-preview table {
	border-collapse: collapse;
	width: 100%;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
}

.vp-ddg-table th,
.vp-ddg-table td,
.vp-htg-preview table th,
.vp-htg-preview table td {
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	padding: 0.5em 0.7em;
	text-align: left;
}

.vp-ddg-table th,
.vp-htg-preview table th {
	background: var(--wp--preset--color--surface, #f5f6f8);
	font-weight: 600;
}

.vp-ddg-output {
	font-family: var(--wp--preset--font-family--mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
	font-size: 0.8125em;
	background: var(--wp--preset--color--surface, #f5f6f8);
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--small, 6px);
	padding: 1em;
	overflow-x: auto;
	white-space: pre;
	max-height: 28rem;
	overflow-y: auto;
}

/* --- Fake Profile / Avatar Generator grids --- */

.vp-fpg-grid,
.vp-avg-grid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
	gap: 1rem;
	margin-top: 1rem;
}

.vp-fpg-card,
.vp-avg-cell {
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--base, 10px);
	padding: 1rem;
	text-align: center;
}

.vp-fpg-avatar,
.vp-avg-preview {
	display: flex;
	justify-content: center;
	margin-bottom: 0.75rem;
}

.vp-fpg-avatar svg,
.vp-avg-preview svg {
	width: 6rem;
	height: 6rem;
}

.vp-fpg-card h3 {
	margin: 0 0 0.25rem;
	font-size: 1.05rem;
}

.vp-fpg-role {
	font-weight: 600;
	color: var(--wp--preset--color--contrast-2, #555);
}

.vp-fpg-bio {
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	color: var(--wp--preset--color--contrast-2, #666);
}

/* --- Invoice Generator --- */

/* Hero polish: the title/icon/description/category-pill block above
   `.vp-invoice-generator` comes from the SHARED `templates/single.html`
   (used by every one of the ~199 tool pages), so it can't be edited
   directly — these rules reach it only through `body.vp-invoice-
   generator-page`, which `InvoiceGenerator::register()` adds solely on
   this page, so every other tool's header is provably untouched. */
body.vp-invoice-generator-page .vp-tool-header {
	display: flex;
	align-items: center;
	gap: 0.9rem;
}

body.vp-invoice-generator-page .vp-tool-header .vp-tool-icon {
	width: 56px;
	height: 56px;
	flex-shrink: 0;
}

body.vp-invoice-generator-page .vp-tool-header__title {
	margin: 0;
	font-size: clamp(1.75rem, 1.4rem + 1.4vw, 2.5rem);
}

body.vp-invoice-generator-page .vp-tool-header__description {
	max-width: 46rem;
	margin-top: 0.5rem;
	font-size: 1.05rem;
	color: var(--wp--preset--color--contrast-2, #57606a);
}

.vp-invoice-disclaimer {
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	font-style: italic;
	color: var(--wp--preset--color--contrast-2, #666);
	max-width: var(--wp--style--global--content-size, 740px);
	margin-inline: auto;
	text-align: left;
}

/* Trust badges (Free to Use / No Registration / PDF Download / 100% Private). */
.vp-invoice-badges {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: 0.5rem 1.25rem;
	list-style: none;
	margin: 0.75rem 0 0;
	padding: 0;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	color: var(--wp--preset--color--contrast-2, #57606a);
}

.vp-invoice-badges li {
	display: flex;
	align-items: center;
	gap: 0.35em;
}

.vp-invoice-badges li::before {
	content: "✓";
	color: var(--wp--preset--color--primary, #1d4ed8);
	font-weight: 700;
}

/* Visual reorder to match the reference layout: trust badges + feature
   strip read as the "hero" band right under the page title/description,
   with the small privacy/legal footnotes pushed below them instead of
   dominating the top of the page. Pure visual reorder on this module's
   own direct children only — DOM/reading order is untouched (none of
   these 4 blocks contain a focusable control), so tab order and screen
   reader order are unaffected. */
.vp-invoice-generator {
	display: flex;
	flex-direction: column;
}

.vp-invoice-generator > .vp-invoice-badges {
	order: 1;
	margin-top: 0;
}

.vp-invoice-generator > .vp-invoice-features {
	order: 2;
}

.vp-invoice-generator > .vp-tool__privacy-note {
	order: 3;
	margin-top: 1rem;
}

.vp-invoice-generator > .vp-invoice-disclaimer {
	order: 4;
	margin-bottom: 1.5rem;
}

.vp-invoice-generator > .vp-invoice-tabs {
	order: 5;
}

.vp-invoice-generator > .vp-invoice-workspace {
	order: 6;
}

.vp-invoice-generator > .vp-invoice-help-card {
	order: 7;
}

/* Feature strip: 4 columns desktop, 2x2 tablet, 1-col mobile. */
.vp-invoice-features {
	display: grid;
	grid-template-columns: repeat(4, 1fr);
	gap: 1rem;
	margin: 1.5rem 0;
	padding: 1rem 0;
	background: var(--wp--preset--color--base, #fff);
	border-top: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-bottom: 1px solid var(--wp--preset--color--border, #e2e5ea);
}

.vp-invoice-feature {
	display: flex;
	align-items: flex-start;
	gap: 0.65rem;
}

.vp-invoice-feature__icon {
	width: 22px;
	height: 22px;
	flex-shrink: 0;
	color: var(--wp--preset--color--primary, #1d4ed8);
	margin-top: 0.15rem;
}

.vp-invoice-feature strong {
	display: block;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
}

.vp-invoice-feature p {
	margin: 0.15rem 0 0;
	font-size: 0.8rem;
	color: var(--wp--preset--color--contrast-2, #57606a);
}

@media (max-width: 767px) {
	.vp-invoice-features {
		grid-template-columns: repeat(2, 1fr);
	}
}

@media (max-width: 480px) {
	.vp-invoice-features {
		grid-template-columns: 1fr;
	}
}

/* Tab bar — quick-jump section navigation (see invoice-generator.js's
   own docblock on `initTabs()`: every section stays in the DOM and
   visible; tabs just scroll + highlight, nothing is hidden). Styled as
   a segmented control (surface track, active tab lifted on a white
   pill) rather than a plain underline, so the active section reads
   unmistakably at a glance. */
.vp-invoice-tabs {
	display: flex;
	gap: 0.25rem;
	overflow-x: auto;
	-webkit-overflow-scrolling: touch;
	margin-bottom: 1.25rem;
	padding: 0.3rem;
	background: var(--wp--preset--color--surface, #f5f6f8);
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--base, 10px);
}

.vp-invoice-tab {
	flex: 1 1 auto;
	flex-shrink: 0;
	background: none;
	border: none;
	border-radius: var(--wp--custom--radius--small, 6px);
	padding: 0.6em 1em;
	font: inherit;
	font-weight: 600;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	color: var(--wp--preset--color--contrast-2, #57606a);
	cursor: pointer;
	transition: background-color 0.15s ease, color 0.15s ease;
	white-space: nowrap;
}

.vp-invoice-tab:hover {
	color: var(--wp--preset--color--contrast, #14181f);
}

.vp-invoice-tab.is-active {
	background: var(--wp--preset--color--base, #fff);
	color: var(--wp--preset--color--primary, #1d4ed8);
	box-shadow: var(--wp--preset--shadow--sm, 0 1px 2px rgba(20, 24, 31, .06));
}

/* Two-column workspace: editor ~62%, live preview ~38%, reflowing to a
   single column (editor first) under 768px per the mobile-experience
   requirement. `minmax(0, ...)` keeps the items table's own horizontal
   scroll contained instead of forcing the grid track to grow. */
.vp-invoice-workspace {
	display: grid;
	grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
	gap: 1.5rem;
	align-items: start;
}

@media (max-width: 1023px) {
	.vp-invoice-workspace {
		grid-template-columns: 1fr;
	}
}

.vp-invoice-editor {
	display: flex;
	flex-direction: column;
	gap: 1.5rem;
	min-width: 0;
}

.vp-invoice-section {
	scroll-margin-top: 1rem;
}

/* `align-items: stretch` (the grid default, but stated explicitly here
   since it matters): Business Information is taller than Customer
   Information (it alone carries the logo uploader), so with `start`
   the shorter card just stopped short, leaving a tall dead gap in ITS
   column below it before the Items section began — looked like a
   layout bug. Stretching both cards to the row's shared height means
   any leftover space is inside the shorter card's own border/
   background instead of an unexplained gap next to it. */
.vp-invoice-cards-row {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 1.25rem;
	align-items: stretch;
}

@media (max-width: 767px) {
	.vp-invoice-cards-row {
		grid-template-columns: 1fr;
	}
}

.vp-invoice-card {
	background: var(--wp--preset--color--base, #fff);
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--base, 10px);
	box-shadow: var(--wp--preset--shadow--sm, 0 1px 2px rgba(20, 24, 31, .06));
	padding: 1.25rem;
}

.vp-invoice-card + .vp-invoice-card {
	margin-top: 1.25rem;
}

.vp-invoice-cards-row .vp-invoice-card + .vp-invoice-card {
	margin-top: 0;
}

/* Card head: small icon + title + optional one-line helper text, used
   by every editor card (Business, Customer, Settings, Payment, Items)
   so each section reads as its own distinct block of an application
   rather than a generic stack of WordPress form fields. */
.vp-invoice-card__head {
	display: flex;
	align-items: flex-start;
	gap: 0.75rem;
	margin-bottom: 1.1rem;
	padding-bottom: 1.1rem;
	border-bottom: 1px solid var(--wp--preset--color--border, #e2e5ea);
}

.vp-invoice-card__icon {
	width: 20px;
	height: 20px;
	flex-shrink: 0;
	margin-top: 0.15rem;
	color: var(--wp--preset--color--accent-dev, #4338ca);
}

.vp-invoice-card__title {
	margin: 0;
	font-size: 1rem;
}

.vp-invoice-card__hint {
	margin: 0.2rem 0 0;
	font-size: 0.8rem;
	color: var(--wp--preset--color--contrast-2, #57606a);
}

.vp-invoice-card__grid {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 0.9rem 1rem;
}

@media (max-width: 480px) {
	.vp-invoice-card__grid {
		grid-template-columns: 1fr;
	}
}

.vp-invoice-card__grid-full {
	grid-column: 1 / -1;
}

/* `.vp-invoice-editor label` is `display:flex; flex-direction:column`
   (label text row, then the field below) — without this wrapper, the
   label text and the required-asterisk span were two SEPARATE bare
   nodes directly inside that flex column, so the column direction put
   the asterisk on its own line under the label text instead of right
   after it. Wrapping both in one inline element makes them a single
   flex item again. */
.vp-invoice-label-text {
	display: inline-flex;
	align-items: baseline;
}

.vp-invoice-required {
	color: var(--wp--preset--color--vivid-red, #cc1818);
	margin-left: 0.2em;
}

/* Logo upload — client-side only (see invoice-generator.js's
   `initLogoUpload()`); never sent to the server. Same icon-led
   dropzone language as the site's shared `.vp-upload-dropzone`
   component (Image to PDF, Document Reader, Merge PDF, etc.) — a
   smaller instance of it, since here it's one field inside a form
   card rather than a whole tool's single reason for existing. */
/* Now the first thing in the Business Information card (moved up from
   the bottom, per direct request — logo upload is this tool's basic,
   most-used functionality and should be seen first) — the separator
   sits below it, not above. */
.vp-invoice-logo-upload {
	margin-bottom: 1rem;
	padding-bottom: 1rem;
	border-bottom: 1px solid var(--wp--preset--color--border, #e2e5ea);
}

/* Dropzone (or, once uploaded, the preview row) sits beside its own
   hint text rather than a separate heading above and a caption below —
   removes a whole text line's worth of height from a card that's
   already one of the taller ones in its row (see `.vp-invoice-cards-row`
   stretch comment above). */
.vp-invoice-logo-upload__body {
	display: flex;
	align-items: center;
	gap: 0.85rem;
	flex-wrap: nowrap;
}

/* A small, icon-only square button (too small at 64px to also hold two
   lines of text inside without crowding) — its label/hint text moved
   out to `.vp-invoice-logo-upload__info`, beside it. */
.vp-invoice-logo-dropzone {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 64px;
	height: 64px;
	flex-shrink: 0;
	box-sizing: border-box;
	padding: 0.5rem;
	border: 2px dashed color-mix(in srgb, var(--wp--preset--color--primary, #1d4ed8) 35%, var(--wp--preset--color--border, #e2e5ea));
	border-radius: var(--wp--custom--radius--base, 8px);
	background: color-mix(in srgb, var(--wp--preset--color--primary, #1d4ed8) 6%, var(--wp--preset--color--base, #fff));
	cursor: pointer;
	transition: border-color 0.15s ease, background 0.15s ease;
}

/* Label/hint text that used to live inside the dropzone box — now
   beside it (box + row are mutually-exclusive alternatives inside
   `.vp-invoice-logo-upload__body`; this stack is the constant third
   item next to whichever of those two is showing). */
.vp-invoice-logo-upload__info {
	display: flex;
	flex-direction: column;
	justify-content: center;
	gap: 0.15rem;
	min-width: 0;
	flex: 1 1 auto;
}

.vp-invoice-logo-upload__info .vp-invoice-logo-dropzone__text,
.vp-invoice-logo-upload__info .vp-invoice-logo-dropzone__hint {
	white-space: normal;
	overflow-wrap: break-word;
}

.vp-invoice-logo-dropzone:hover {
	border-color: var(--wp--preset--color--primary, #1d4ed8);
	background: color-mix(in srgb, var(--wp--preset--color--primary, #1d4ed8) 12%, var(--wp--preset--color--base, #fff));
}

/* The file input is visually hidden (same clip technique as the
   shared `.vp-upload-file-input`) but stays in the tab order — this is
   its keyboard focus indicator, since a hidden input itself can't show
   one. Input comes BEFORE the label in the markup specifically so this
   sibling selector can reach it (`for`/`id` alone makes them
   click-associated but not `:focus-within`-related, since the input
   isn't a DOM descendant of the label). */
.vp-invoice-logo-upload__input:focus-visible + .vp-invoice-logo-dropzone {
	outline: 2px solid var(--wp--preset--color--primary, #1d4ed8);
	outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
	.vp-invoice-logo-dropzone {
		transition: none;
	}
}

.vp-invoice-logo-dropzone__icon {
	width: 28px;
	height: 28px;
	object-fit: contain;
}

.vp-invoice-logo-dropzone__text {
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	font-weight: 600;
}

.vp-invoice-logo-dropzone__hint {
	font-size: 0.75rem;
	color: var(--wp--preset--color--contrast-2, #57606a);
}

/* `.vp-invoice-editor input` (a generic reset earlier in this file)
   sets `width: 100%` at higher specificity (class+tag) than a single
   class here, so it was winning over `width: 1px` below — the "hidden"
   file input was actually rendering at full card width, invisible but
   still in flow, and was the page's actual horizontal-scroll culprit
   (its own box measured wider than the viewport). Qualifying the
   selector with its parent class raises specificity enough to win. */
.vp-invoice-logo-upload .vp-invoice-logo-upload__input {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip: rect(0, 0, 0, 0);
	white-space: nowrap;
	border: 0;
}

/* Once a logo is uploaded, the dropzone prompt gives way to a compact
   thumbnail + filename + Remove row — the `--has-image` class is
   toggled by `initLogoUpload()`/Reset in invoice-generator.js, not CSS
   alone, since which state applies depends on JS state
   (`logoDataUrl`). `.vp-invoice-logo-upload__row` stays in the DOM
   either way; only its visibility toggles. */
.vp-invoice-logo-upload__row {
	display: none;
	align-items: center;
	gap: 0.75rem;
	flex-wrap: wrap;
}

.vp-invoice-logo-upload--has-image .vp-invoice-logo-dropzone {
	display: none;
}

.vp-invoice-logo-upload--has-image .vp-invoice-logo-upload__row {
	display: flex;
}

.vp-invoice-logo-upload__preview-wrap {
	display: inline-flex;
}

.vp-invoice-logo-upload__preview {
	width: 44px;
	height: 44px;
	object-fit: contain;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--small, 6px);
	background: var(--wp--preset--color--surface, #f5f6f8);
}

.vp-invoice-logo-upload__filename {
	flex: 1 1 auto;
	min-width: 0;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}


.vp-invoice-primary-actions {
	display: flex;
	flex-wrap: wrap;
	gap: 0.75rem;
	margin-top: 1.25rem;
}

.vp-invoice-local-note {
	margin: 0.5rem 0 0;
	font-size: 0.75rem;
	color: var(--wp--preset--color--contrast-2, #57606a);
}

/* Live preview panel — its own card, sticky on desktop so it stays in
   view while the (usually taller) editor column scrolls. */
.vp-invoice-preview-panel {
	background: var(--wp--preset--color--base, #fff);
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--base, 10px);
	box-shadow: var(--wp--preset--shadow--sm, 0 1px 2px rgba(20, 24, 31, .06));
	padding: 1.25rem;
	min-width: 0;
}

@media (min-width: 1024px) {
	.vp-invoice-preview-panel {
		position: sticky;
		top: 1.5rem;
	}
}

.vp-invoice-preview-panel__head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	margin-bottom: 1rem;
}

.vp-invoice-preview-panel__head h3 {
	margin: 0;
	font-size: 1rem;
}

.vp-invoice-preview-panel__live {
	display: inline-flex;
	align-items: center;
	gap: 0.4em;
	font-size: 0.72rem;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.04em;
	color: var(--wp--preset--color--success, #15803d);
}

.vp-invoice-preview-panel__live-dot {
	width: 7px;
	height: 7px;
	border-radius: 50%;
	background: var(--wp--preset--color--success, #15803d);
	animation: vp-invoice-live-pulse 2s ease-in-out infinite;
}

@media (prefers-reduced-motion: reduce) {
	.vp-invoice-preview-panel__live-dot {
		animation: none;
	}
}

@keyframes vp-invoice-live-pulse {
	0%, 100% { opacity: 1; }
	50% { opacity: 0.35; }
}

.vp-invoice-preview-actions {
	display: flex;
	flex-wrap: wrap;
	gap: 0.6rem;
	margin-top: 1.25rem;
}

.vp-invoice-action__icon {
	width: 16px;
	height: 16px;
	flex-shrink: 0;
}

/* The uploaded business logo, top-left of the invoice document —
   sized a little larger than the small 44px form-field thumbnail
   (`.vp-invoice-logo-upload__preview`) so it actually reads as a logo
   on the invoice itself, with room below it before the business name. */
.vp-invoice-preview__logo {
	max-width: 160px;
	max-height: 72px;
	object-fit: contain;
	margin-bottom: 0.75rem;
	display: block;
}

.vp-invoice-help-card {
	margin-top: 1.5rem;
	padding: 1rem 1.25rem;
	background: var(--wp--preset--color--base, #fff);
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--base, 10px);
}

.vp-invoice-help-card h3 {
	margin: 0 0 0.4rem;
	font-size: 0.95rem;
}

.vp-invoice-help-card p {
	margin: 0;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	color: var(--wp--preset--color--contrast-2, #57606a);
}

/* Related Tools, on this page only, styled as a polished card grid
   instead of the site-wide pill list — scoped via the `body_class()`
   filter InvoiceGenerator::register() adds (this shortcode renders as a
   SIBLING of `.vp-invoice-generator` in the post content, not a
   descendant, so a descendant selector could never reach it; every
   other tool page keeps the unmodified pill-list look). */
/* Fixed 3 columns, not `auto-fill` — with 6 related tools and this
   list's ~740px content width, `auto-fill, minmax(150px, 1fr)` was
   fitting 4 columns, so the 6 cards broke into a full row of 4 + a
   ragged trailing row of 2 (left-aligned, not centered) — THAT
   uneven row, not the icon/title centering, is what still read as
   "misaligned" after that fix. A fixed 3-column grid always divides
   6 cards into two clean, even rows. */
body.vp-invoice-generator-page .vp-related-tools__list {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: 0.75rem;
	align-items: stretch;
}

@media (max-width: 767px) {
	body.vp-invoice-generator-page .vp-related-tools__list {
		grid-template-columns: repeat(2, 1fr);
	}
}

@media (max-width: 480px) {
	body.vp-invoice-generator-page .vp-related-tools__list {
		grid-template-columns: 1fr;
	}
}

body.vp-invoice-generator-page .vp-related-tools__list li {
	height: 100%;
}

/* Icon + title sit side by side on one row (not stacked) — `align-items:
   center` keeps the icon vertically centered next to the title even
   when a longer title wraps to 2 lines, so a neighbor's taller
   two-line title never shifts where the icon sits relative to it. */
body.vp-invoice-generator-page .vp-related-tools__list a {
	flex-direction: row;
	align-items: center;
	justify-content: flex-start;
	gap: 0.6rem;
	display: flex;
	height: 100%;
	min-height: 84px;
	box-sizing: border-box;
}

body.vp-invoice-generator-page .vp-related-tools__list a .vp-tool-icon {
	flex-shrink: 0;
}

.vp-invoice-editor label {
	display: flex;
	flex-direction: column;
	gap: 0.3rem;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	font-weight: 600;
}

.vp-invoice-editor input,
.vp-invoice-editor textarea,
.vp-invoice-editor select {
	font: inherit;
	font-weight: 400;
	padding: 0.55em 0.7em;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--small, 6px);
	width: 100%;
	box-sizing: border-box;
}

.vp-invoice-editor input:focus,
.vp-invoice-editor textarea:focus,
.vp-invoice-editor select:focus {
	outline: 2px solid var(--wp--preset--color--primary, #1d4ed8);
	outline-offset: 1px;
}

.vp-invoice-notes-label {
	display: flex;
	flex-direction: column;
	gap: 0.3rem;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	font-weight: 600;
	margin-top: 1rem;
}

.vp-invoice-items-wrap {
	overflow-x: auto;
	margin-top: 0.5rem;
}

.vp-invoice-items {
	border-collapse: collapse;
	width: 100%;
	min-width: 42rem;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
}

.vp-invoice-items th,
.vp-invoice-items td {
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	padding: 0.6em 0.6em;
}

.vp-invoice-items th {
	background: var(--wp--preset--color--surface, #f5f6f8);
	text-align: left;
	font-size: 0.75rem;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.03em;
	color: var(--wp--preset--color--contrast-2, #57606a);
}

.vp-inv-col-index,
.vp-inv-item-index {
	width: 2.25rem;
	text-align: center;
	color: var(--wp--preset--color--contrast-2, #57606a);
}

.vp-inv-item-amount {
	font-weight: 600;
	white-space: nowrap;
}

.vp-invoice-items input {
	width: 100%;
	box-sizing: border-box;
	border: 1px solid transparent;
	padding: 0.35em;
	font: inherit;
	border-radius: var(--wp--custom--radius--small, 6px);
}

.vp-invoice-items input:focus {
	border-color: var(--wp--preset--color--primary, #1d4ed8);
	outline: none;
}

.vp-inv-item-actions {
	white-space: nowrap;
	text-align: center;
}

.vp-inv-item-actions .vp-btn {
	padding: 0.3em 0.5em;
	line-height: 1;
}

/* Mobile: the table can't reasonably fit at narrow widths even with
   horizontal scroll inside `.vp-invoice-items-wrap` — instead of
   forcing a second, separate mobile markup to keep in sync with
   `addItemRow()`, the SAME table/row/cell DOM is reflowed into a
   stacked per-item card using `display:block` + `data-label` (added
   by `addItemRow()` in invoice-generator.js) rendered via `::before`.
   The index/actions cells become a small header row for the "card"
   (`<tr>`), everything else stacks label-then-value. */
@media (max-width: 640px) {
	.vp-invoice-items-wrap {
		overflow-x: visible;
	}

	.vp-invoice-items {
		min-width: 0;
		border: none;
	}

	.vp-invoice-items thead {
		position: absolute;
		width: 1px;
		height: 1px;
		overflow: hidden;
		clip: rect(0 0 0 0);
		white-space: nowrap;
	}

	.vp-invoice-items,
	.vp-invoice-items tbody,
	.vp-invoice-item-row,
	.vp-invoice-items td {
		display: block;
		width: 100%;
	}

	.vp-invoice-item-row {
		position: relative;
		margin-bottom: 1rem;
		padding: 2.5rem 0.9rem 0.9rem;
		border: 1px solid var(--wp--preset--color--border, #e2e5ea);
		border-radius: var(--wp--custom--radius--base, 10px);
		background: var(--wp--preset--color--base, #fff);
	}

	.vp-invoice-items td {
		border: none;
		padding: 0.4em 0;
	}

	.vp-invoice-items td[data-label]::before {
		content: attr(data-label);
		display: block;
		margin-bottom: 0.25rem;
		font-size: 0.72rem;
		font-weight: 700;
		text-transform: uppercase;
		letter-spacing: 0.03em;
		color: var(--wp--preset--color--contrast-2, #57606a);
	}

	/* Item number becomes a "Item #N" badge pinned to the card's
	   top-left corner instead of its own labeled row. Specificity has
	   to beat `.vp-invoice-items td { width: 100% }` above (also a
	   class+tag pair) — a bare `.vp-inv-item-index` rule was losing to
	   it and staying full-width/centered instead of corner-pinned. */
	.vp-invoice-items td.vp-inv-item-index {
		position: absolute;
		top: 0.9rem;
		left: 0.9rem;
		width: auto;
		text-align: left;
		white-space: nowrap;
		font-weight: 700;
		color: var(--wp--preset--color--primary, #1d4ed8);
	}

	/* Higher specificity than the generic `td[data-label]::before` rule
	   above (which would otherwise win and print "#" instead of this) —
	   `display`/`margin-bottom` are overridden too, since the cascade
	   applies per-property: without this, the generic rule's
	   `display: block` still won for THIS property (my selector didn't
	   originally redeclare it) and stacked "Item #" on its own line
	   above the "1", doubling the badge's height and pushing it out of
	   its single-line corner-badge look. */
	.vp-invoice-items td.vp-inv-item-index::before {
		content: "Item #";
		display: inline;
		margin-bottom: 0;
	}

	.vp-inv-item-amount {
		font-size: 1.05rem;
	}

	.vp-inv-item-actions {
		text-align: left;
		border-top: 1px solid var(--wp--preset--color--border, #e2e5ea);
		margin-top: 0.5rem;
		padding-top: 0.75rem !important;
	}
}

.vp-invoice-totals {
	max-width: 20rem;
	margin: 1rem 0 0 auto;
}

.vp-invoice-totals > div {
	display: flex;
	justify-content: space-between;
	padding: 0.3em 0;
}

.vp-invoice-totals > div > span:first-child {
	color: var(--wp--preset--color--contrast-2, #57606a);
}

.vp-invoice-totals__grand {
	margin-top: 0.6em;
	padding: 0.75em 0.9em !important;
	margin-inline: -0.9em;
	font-size: 1.3em;
	font-weight: 700;
	background: color-mix(in srgb, var(--wp--preset--color--primary, #1d4ed8) 8%, var(--wp--preset--color--base, #fff));
	border: 1px solid color-mix(in srgb, var(--wp--preset--color--primary, #1d4ed8) 25%, transparent);
	border-radius: var(--wp--custom--radius--base, 10px);
}

.vp-invoice-totals__grand span:last-child {
	color: var(--wp--preset--color--primary, #1d4ed8);
}

.vp-invoice-preview {
	margin-top: 0;
}

/* The generated document itself stays neutral/printable (near-black
   text on white, no brand blue) even though the surrounding app UI is
   blue-accented — see spec: the invoice should look like something
   you'd hand a client, not like the tool that made it. */
.vp-invoice-preview__doc {
	background: #fff;
	color: #14181f;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--base, 10px);
	box-shadow: 0 1px 3px rgba(20, 24, 31, .08);
	padding: 1.75rem;
	max-width: 50rem;
	overflow-x: auto;
	box-sizing: border-box;
}

.vp-invoice-preview__items {
	min-width: 32rem;
}

@media (max-width: 480px) {
	.vp-invoice-preview__doc {
		padding: 1rem;
	}
}

/* Stacked by default — the on-page Live Preview panel is a narrow
   sidebar column (roughly 380-480px on most desktop widths), too
   narrow for a real side-by-side business-info/invoice-meta header
   once an address and email are filled in; `justify-content:
   space-between` + `flex-wrap:wrap` there produced an ugly half-wrapped
   layout with right-aligned lines floating alone under the business
   info. Restored to a proper side-by-side header only for print (see
   `@media print` below), where the page is at full paper width. */
.vp-invoice-preview__header {
	display: flex;
	flex-direction: column;
	gap: 1rem;
	border-bottom: 2px solid #111;
	padding-bottom: 1rem;
	margin-bottom: 1rem;
}

.vp-invoice-preview__meta {
	text-align: left;
}

@media print {
	.vp-invoice-preview__header {
		flex-direction: row;
		justify-content: space-between;
		flex-wrap: wrap;
	}

	.vp-invoice-preview__meta {
		text-align: right;
	}
}

.vp-invoice-preview__meta h1 {
	margin: 0 0 0.5rem;
	font-size: 1.6rem;
	font-weight: 800;
	letter-spacing: 0.08em;
	color: #14181f;
}

.vp-invoice-preview__header h2 {
	margin: 0 0 0.35rem;
	font-size: 1.15rem;
}

.vp-invoice-preview__billto {
	margin-bottom: 1rem;
	padding-top: 0.75rem;
}

.vp-invoice-preview__billto strong {
	display: block;
	margin-bottom: 0.3rem;
	font-size: 0.72rem;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.05em;
	color: var(--wp--preset--color--contrast-2, #57606a);
}

.vp-invoice-preview__items {
	border-collapse: collapse;
	width: 100%;
	margin-bottom: 1rem;
}

.vp-invoice-preview__items th,
.vp-invoice-preview__items td {
	border: 1px solid #ddd;
	padding: 0.5em 0.7em;
	text-align: left;
	font-size: 0.9em;
}

.vp-invoice-preview__items th {
	background: #f5f5f5;
	font-size: 0.72rem;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.04em;
	color: #57606a;
}

.vp-invoice-preview__totals {
	max-width: 20rem;
	margin: 0 0 1rem auto;
}

.vp-invoice-preview__totals > div {
	display: flex;
	justify-content: space-between;
	padding: 0.2em 0;
}

.vp-invoice-preview__grand {
	font-weight: 800;
	font-size: 1.2em;
	border-top: 2px solid #14181f;
	margin-top: 0.3em;
	padding-top: 0.5em !important;
}

.vp-invoice-preview__disclaimer {
	font-size: 0.75em;
	color: #777;
	margin-top: 1.5rem;
}

/* Print: show only the invoice document itself — everything else on
   `templates/single.html` around `.vp-invoice-generator` (breadcrumb,
   status badge, tool header/icon/title, description, category/tag
   pills, featured image, the tool's own intro paragraph, prev/next
   tool pagination) plus everything inside the redesigned workspace
   (badges, feature strip, tabs, the whole editor column, the preview
   panel's own head/action buttons, the help card) must be listed here
   explicitly — nothing here is hidden by default.
   `.breadcrumbs` (no `vp-` prefix) never matched anything; the real
   class from `[vedant_breadcrumbs]` is `.vp-breadcrumbs` — fixed below. */
@media print {
	body.single-tool .site-header,
	body.single-tool .site-footer,
	body.single-tool .vp-breadcrumbs,
	body.single-tool .vp-tool-status,
	body.single-tool .vp-tool-header,
	body.single-tool .vp-tool-header__description,
	body.single-tool .vp-tag-pills,
	body.single-tool .vp-tool-hero-image,
	body.single-tool .vp-tool-pagination,
	body.single-tool .entry-content > p:first-child,
	body.single-tool .vp-tool__privacy-note,
	body.single-tool .vp-invoice-disclaimer,
	body.single-tool .vp-invoice-badges,
	body.single-tool .vp-invoice-features,
	body.single-tool .vp-invoice-tabs,
	body.single-tool .vp-invoice-editor,
	body.single-tool .vp-invoice-preview-panel__head,
	body.single-tool .vp-invoice-preview-actions,
	body.single-tool .vp-invoice-help-card,
	body.single-tool [class*='vp-ad'],
	body.single-tool .vp-related-tools,
	body.single-tool .entry-content > h1,
	body.single-tool .breadcrumbs {
		display: none !important;
	}

	body.single-tool .vp-invoice-workspace {
		display: block;
	}

	body.single-tool .vp-invoice-preview-panel {
		position: static;
		border: none;
		box-shadow: none;
		padding: 0;
	}
}

/* --- Random Place Explorer --- */

.vp-rpe-form__fields {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
	gap: 1rem;
	margin-bottom: 1rem;
}

.vp-rpe-empty {
	text-align: center;
	padding: 2.5rem 1rem;
	background: var(--wp--preset--color--surface, #f5f6f8);
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--base, 10px);
	margin-top: 1.5rem;
}

.vp-rpe-empty h2 {
	margin: 0 0 0.5rem;
	font-size: 1.15rem;
}

.vp-rpe-empty p {
	margin: 0;
	color: var(--wp--preset--color--contrast-2, #57606a);
	max-width: 32rem;
	margin-inline: auto;
}

.vp-rpe-summary {
	margin: 1.5rem 0 0.75rem;
	font-size: 1.05rem;
}

#vp-rpe-error .vp-rpe-retry {
	margin-left: 0.75rem;
}

/* Cards left (~40%) / map right (~60%) — reversed from the original
   map-left layout per direct request, so the map reads as the larger,
   dominant element. Reflows to a single column on tablet/mobile, map
   FIRST (via `order`) so a visitor sees where their results are before
   scrolling through the list — the spec's own recommended mobile flow. */
.vp-rpe-layout {
	display: grid;
	grid-template-columns: minmax(0, 0.7fr) minmax(0, 1fr);
	gap: 1.5rem;
	margin-top: 1.5rem;
	align-items: start;
}

.vp-rpe-cards {
	display: flex;
	flex-direction: column;
	gap: 1rem;
	/* No independent scrollbar — the page itself scrolls naturally
	   (previously `max-height` + `overflow-y` created a second, nested
	   scroll region inside the card list). */
}

/* `min-height` matches `.vp-rpe-map`'s own height exactly (kept in sync
   at each breakpoint below) — without it, this panel's height came
   entirely from its visible children's normal document flow. Opening
   the street-imagery viewer sets `#vp-rpe-map`'s `hidden` attribute
   (removing it from flow) and shows `.vp-rpe-imagery-viewer`
   (`position: absolute`, so it never contributes to flow height
   either) — with nothing left in normal flow, the panel collapsed to
   ~0px and the absolutely-positioned viewer (sized via `inset: 0`
   against that 0-height container) collapsed right along with it,
   producing a blank white area instead of the viewer. Confirmed live:
   `.vp-rpe-map-panel` measured 0px tall with the viewer open before
   this fix. */
.vp-rpe-map-panel {
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
	position: relative;
	min-height: clamp(360px, 55vh, 700px);
}

@media (min-width: 1024px) {
	.vp-rpe-map-panel {
		position: sticky;
		top: 1.5rem;
	}
}

@media (max-width: 782px) {
	.vp-rpe-layout {
		grid-template-columns: 1fr;
	}

	.vp-rpe-map-panel {
		order: -1;
	}
}

.vp-rpe-map {
	height: clamp(360px, 55vh, 700px);
	border-radius: var(--wp--custom--radius--base, 10px);
	overflow: hidden;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	box-shadow: var(--wp--preset--shadow--sm, 0 1px 2px rgba(20, 24, 31, .06));
}

@media (max-width: 1023px) and (min-width: 783px) {
	.vp-rpe-map {
		height: clamp(320px, 50vh, 600px);
	}

	.vp-rpe-map-panel {
		min-height: clamp(320px, 50vh, 600px);
	}
}

@media (max-width: 782px) {
	.vp-rpe-map {
		height: clamp(280px, 45vh, 480px);
	}

	.vp-rpe-map-panel {
		min-height: clamp(280px, 45vh, 480px);
	}
}

/* Number badge + body, hover/active states — color is never the only
   selection signal (also border + left accent bar + marker size). */
.vp-rpe-card {
	display: flex;
	gap: 0.85rem;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-left: 3px solid transparent;
	border-radius: var(--wp--custom--radius--base, 10px);
	padding: 1rem;
	cursor: pointer;
	transition: border-color 0.2s ease, background-color 0.2s ease, box-shadow 0.2s ease;
}

.vp-rpe-card:hover {
	border-color: var(--wp--preset--color--primary, #1d4ed8);
}

.vp-rpe-card:focus-visible {
	outline: 2px solid var(--wp--preset--color--primary, #1d4ed8);
	outline-offset: 2px;
}

.vp-rpe-card.is-active {
	border-left-color: var(--wp--preset--color--accent-map, #2563eb);
	background: var(--wp--preset--color--surface, #f5f6f8);
	box-shadow: var(--wp--preset--shadow--sm, 0 1px 2px rgba(20, 24, 31, .06));
}

.vp-rpe-card__number {
	flex-shrink: 0;
	width: 2rem;
	height: 2rem;
	border-radius: 50%;
	background: var(--wp--preset--color--surface, #f5f6f8);
	color: var(--wp--preset--color--contrast-2, #57606a);
	display: flex;
	align-items: center;
	justify-content: center;
	font-weight: 700;
	font-size: 0.8rem;
}

.vp-rpe-card.is-active .vp-rpe-card__number {
	background: var(--wp--preset--color--accent-map, #2563eb);
	color: #fff;
}

.vp-rpe-card__body {
	min-width: 0;
	flex: 1;
}

.vp-rpe-card__header {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	font-weight: 600;
	margin-bottom: 0.4rem;
}

.vp-rpe-card h3 {
	margin: 0 0 0.6rem;
	font-size: 1.05rem;
}

.vp-rpe-card__meta {
	display: grid;
	grid-template-columns: auto 1fr;
	gap: 0.3rem 0.75rem;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	margin: 0 0 0.75rem;
	align-items: center;
}

.vp-rpe-card__meta dt {
	color: var(--wp--preset--color--contrast-2, #666);
}

.vp-rpe-card__meta dd {
	margin: 0;
}

.vp-rpe-card__coords {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	font-variant-numeric: tabular-nums;
}

.vp-rpe-copy {
	font: inherit;
	font-size: 0.75rem;
	background: none;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--small, 4px);
	padding: 0.1em 0.5em;
	cursor: pointer;
	color: var(--wp--preset--color--contrast-2, #57606a);
}

.vp-rpe-copy:hover {
	border-color: var(--wp--preset--color--primary, #1d4ed8);
	color: var(--wp--preset--color--primary, #1d4ed8);
}

.vp-rpe-marker {
	background: transparent;
	border: none;
}

.vp-rpe-marker span {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 28px;
	height: 28px;
	border-radius: 50%;
	background: var(--wp--preset--color--accent-map, #2563eb);
	color: #fff;
	font-weight: 700;
	font-size: 0.8rem;
	border: 2px solid #fff;
	box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);
	transition: transform 0.2s ease;
}

.vp-rpe-marker.is-active span {
	width: 34px;
	height: 34px;
	font-size: 0.9rem;
	background: var(--wp--preset--color--primary, #1d4ed8);
	transform: scale(1.05);
}

/* In-page street-imagery viewer — replaces the map area (never a
   Google Maps redirect) while open. */
.vp-rpe-imagery-viewer {
	position: absolute;
	inset: 0;
	display: flex;
	flex-direction: column;
	background: var(--wp--preset--color--base, #fff);
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--base, 10px);
	overflow: hidden;
	box-shadow: var(--wp--preset--shadow--md, 0 4px 16px rgba(20, 24, 31, .08));
}

.vp-rpe-imagery-viewer__header {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 0.75rem;
	padding: 0.75rem 1rem;
	border-bottom: 1px solid var(--wp--preset--color--border, #e2e5ea);
}

.vp-rpe-imagery-viewer__header h3 {
	margin: 0;
	font-size: 0.95rem;
}

.vp-rpe-imagery-viewer__body {
	flex: 1;
	position: relative; /* anchors `.vp-mapillary-pano`'s `inset: 0` below */
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	padding: 1rem;
	overflow-y: auto;
	text-align: center;
}

.vp-rpe-imagery-photo {
	max-width: 100%;
	max-height: 100%;
	border-radius: var(--wp--custom--radius--small, 6px);
}

/* The real MapillaryJS pano viewer — swapped in for the static-photo/
   loading/error text states above. MapillaryJS renders a WebGL canvas
   sized to 100% of its container, so this needs a plain, unpadded,
   non-centering block box with an explicit height (never left to
   collapse to its content's height, which is what a zero-height
   MapillaryJS container looks like in practice: a blank panel with no
   console error). `.vp-rpe-imagery-viewer__body--pano` suspends the
   parent's centering/padding/scroll for as long as the pano viewer is
   open; the base rules above still apply for every other state
   (loading text, the "not available" message, the static-photo
   fallback). */
.vp-rpe-imagery-viewer__body--pano {
	display: block;
	padding: 0;
	overflow: hidden;
}

.vp-mapillary-pano {
	position: absolute;
	inset: 0;
}

.vp-mapillary-attribution {
	position: absolute;
	right: 0.6rem;
	bottom: 0.6rem;
	z-index: 2;
	background: rgba(20, 24, 31, 0.6);
	color: #fff;
	font-size: 0.7rem;
	line-height: 1.4;
	padding: 0.15em 0.55em;
	border-radius: var(--wp--custom--radius--small, 4px);
	text-decoration: none;
}

.vp-mapillary-attribution:hover {
	color: #fff;
	text-decoration: underline;
}

/* ==========================================================================
   Device & Utility Tools — What Is My Screen Size?, Typing Speed Test,
   Typing Practice, Device Information. All four use the standard
   `.vp-tool`/`.vp-status`/`.vp-btn` primitives already defined above; the
   rules below only cover what's genuinely new to this family. Every
   selector is scoped under its own `.vp-{tool}` root, per this project's
   CSS convention — nothing here is a bare, unscoped tag/class selector.
   ========================================================================== */

/* Small button variants used by the new tools (duration/mode toggles,
   compact secondary actions) that had no existing `.vp-btn` modifier. */
.vp-btn--toggle {
	background: var(--wp--preset--color--surface, #f5f6f8);
}

.vp-btn--toggle.is-active {
	background: var(--wp--preset--color--primary, #1d4ed8);
	border-color: var(--wp--preset--color--primary, #1d4ed8);
	color: var(--wp--preset--color--base, #fff);
}

.vp-btn--small {
	padding: 0.4em 0.75em;
	font-size: 0.8rem;
}

/* ---- What Is My Screen Size? ------------------------------------------ */

.vp-screen-size__hero {
	text-align: center;
	padding: 1.5rem 1rem;
	margin: 1rem 0;
	background: var(--wp--preset--color--surface, #f5f6f8);
	border-radius: var(--wp--custom--radius--base, 8px);
	border-top: 3px solid var(--wp--preset--color--accent-device, #1e3a8a);
}

.vp-screen-size__primary-value {
	font-size: clamp(2rem, 5vw, 3.25rem);
	font-weight: 700;
	line-height: 1.1;
	color: var(--wp--preset--color--accent-device, #1e3a8a);
}

.vp-screen-size__primary-label {
	margin-top: 0.4em;
	font-size: 0.95rem;
	color: var(--wp--preset--color--contrast, #14181f);
	opacity: 0.75;
}

.vp-screen-size__grid {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
	gap: 1px;
	background: var(--wp--preset--color--border, #e2e5ea);
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--small, 4px);
	overflow: hidden;
}

.vp-screen-size__stat {
	display: flex;
	flex-direction: column;
	gap: 0.25em;
	padding: 0.9em 1em;
	background: var(--wp--preset--color--base, #fff);
}

.vp-screen-size__stat-value {
	font-size: 1.1rem;
	font-weight: 600;
}

.vp-screen-size__stat-label {
	font-size: 0.8rem;
	opacity: 0.7;
}

.vp-screen-size__actions {
	display: flex;
	flex-wrap: wrap;
	gap: 0.6em;
	margin-top: 1.25rem;
}

/* ---- Shared typing surface (Typing Practice only — Typing Speed Test
   now has its own dedicated `.vp-tst-*` passage below, restyled without
   touching this block so Typing Practice's appearance is unaffected) --- */

.vp-typing-surface {
	font-family: var(--wp--preset--font-family--mono, monospace);
	font-size: 1.15rem;
	line-height: 1.9;
	padding: 1.25rem;
	background: var(--wp--preset--color--surface, #f5f6f8);
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--base, 8px);
	letter-spacing: 0.02em;
	word-break: break-word;
}

.vp-typing-surface__char {
	position: relative;
	white-space: pre-wrap;
}

.vp-typing-surface__char--pending {
	opacity: 0.55;
}

.vp-typing-surface__char--correct {
	opacity: 1;
	color: var(--wp--preset--color--success, #15803d);
}

.vp-typing-surface__char--incorrect {
	opacity: 1;
	color: var(--wp--preset--color--danger, #b91c1c);
	text-decoration: underline wavy var(--wp--preset--color--danger, #b91c1c);
}

.vp-typing-surface__char--current {
	opacity: 1;
	border-left: 2px solid var(--wp--preset--color--accent-device, #1e3a8a);
	animation: vp-typing-caret-blink 1s step-end infinite;
}

@keyframes vp-typing-caret-blink {
	50% { border-left-color: transparent; }
}

.vp-typing-speed__surface-wrap {
	position: relative;
	margin: 1rem 0;
}

.vp-typing-speed__capture-label {
	position: absolute;
	width: 1px;
	height: 1px;
	overflow: hidden;
	clip: rect(0 0 0 0);
	white-space: nowrap;
}

/* Visually hidden but focusable/typeable — keeps real mobile-keyboard
   input working (a `display:none` field can't receive input on most
   mobile browsers) while the `.vp-typing-surface` above shows the real,
   styled character-by-character UI. */
.vp-typing-speed__capture {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	opacity: 0;
	resize: none;
	border: 0;
	padding: 0;
	cursor: text;
}

/* ==========================================================================
   Typing Speed Test — premium redesign (THIS TOOL ONLY).
   Every class below is `vp-tst-*`, a namespace unique to this tool — the
   only overlap with any other tool's markup would have been the shared
   `.vp-typing-surface`/`.vp-typing-speed__surface-wrap`/
   `.vp-typing-speed__capture*` classes above, which is exactly why this
   tool's passage was moved to its own `.vp-tst-surface`/`.vp-tst-capture*`
   classes instead of restyling those shared ones. Nothing here changes
   the appearance of Typing Practice or any other tool.
   ========================================================================== */

.vp-tst-icon {
	width: 1.1em;
	height: 1.1em;
	flex-shrink: 0;
	vertical-align: -0.15em;
}

.vp-tst-icon--xs {
	width: 0.9em;
	height: 0.9em;
}

.vp-tst-icon--sm {
	width: 1em;
	height: 1em;
}

.vp-tst-icon--lg {
	width: 2rem;
	height: 2rem;
	opacity: 0.35;
}

.vp-tst-privacy-note {
	display: flex;
	align-items: center;
	gap: 0.5em;
}

/* The application workspace — one bordered "product" container instead of
   several loose form fields directly on the page background. */
.vp-tst-app {
	display: flex;
	flex-direction: column;
	gap: 1.5rem;
	margin-top: 1rem;
	padding: clamp(1rem, 3vw, 1.75rem);
	background: var(--wp--preset--color--base, #fff);
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--base, 8px);
	box-shadow: var(--wp--preset--shadow--sm, 0 1px 2px rgba(20,24,31,.06));
}

.vp-tst-panel {
	padding: 1rem 1.1rem;
	background: var(--wp--preset--color--surface, #f5f6f8);
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--small, 6px);
}

/* Focus mode: while a test is running, the setup panel steps back
   visually (still fully usable, never hidden or disabled) so the timer
   and passage read as the obvious center of attention. */
.vp-tst-app[data-tst-state="running"] .vp-tst-setup {
	opacity: 0.6;
}

@media (prefers-reduced-motion: no-preference) {
	.vp-tst-setup {
		transition: opacity 0.25s ease;
	}
}

.vp-tst-setup__row {
	display: flex;
	flex-wrap: wrap;
	align-items: flex-end;
	gap: 1.5rem;
}

.vp-tst-option-group {
	display: flex;
	flex-direction: column;
	gap: 0.5em;
}

.vp-tst-option-label {
	display: flex;
	align-items: center;
	gap: 0.4em;
	font-size: 0.78rem;
	font-weight: 700;
	letter-spacing: 0.03em;
	text-transform: uppercase;
	opacity: 0.65;
}

.vp-tst-options {
	display: inline-flex;
	gap: 0.25rem;
	padding: 0.25rem;
	background: var(--wp--preset--color--base, #fff);
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--small, 6px);
}

.vp-tst-pill {
	border: 0;
	background: transparent;
	padding: 0.45em 0.9em;
	font-size: 0.85rem;
	font-weight: 600;
	border-radius: 4px;
	color: var(--wp--preset--color--contrast, #14181f);
	cursor: pointer;
}

@media (prefers-reduced-motion: no-preference) {
	.vp-tst-pill {
		transition: background-color 0.15s ease, color 0.15s ease;
	}
}

.vp-tst-pill:hover {
	background: var(--wp--preset--color--surface, #f5f6f8);
}

.vp-tst-pill.is-active {
	background: var(--wp--preset--color--accent-device, #1e3a8a);
	color: var(--wp--preset--color--base, #fff);
}

.vp-tst-pill.is-active:hover {
	background: var(--wp--preset--color--accent-device, #1e3a8a);
}

.vp-tst-start {
	margin-left: auto;
}

.vp-tst-hint {
	margin: 0.75rem 0 0;
	font-size: 0.85rem;
	opacity: 0.65;
}

/* ---- Live performance dashboard ---- */

.vp-tst-dashboard {
	display: grid;
	grid-template-columns: minmax(140px, 200px) 1fr;
	grid-template-areas:
		"timer metrics"
		"chart chart";
	gap: 1rem;
}

.vp-tst-timer {
	grid-area: timer;
	display: flex;
	align-items: center;
	gap: 0.75rem;
	padding: 1rem;
	background: var(--wp--preset--color--surface, #f5f6f8);
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--small, 6px);
	color: var(--wp--preset--color--accent-device, #1e3a8a);
}

.vp-tst-timer__icon {
	width: 1.75rem;
	height: 1.75rem;
	opacity: 0.85;
}

.vp-tst-timer__body {
	display: flex;
	flex-direction: column;
	line-height: 1.15;
}

.vp-tst-timer__value {
	font-size: clamp(1.5rem, 4vw, 2rem);
	font-weight: 700;
	font-variant-numeric: tabular-nums;
}

.vp-tst-timer__label {
	font-size: 0.72rem;
	font-weight: 600;
	letter-spacing: 0.03em;
	text-transform: uppercase;
	opacity: 0.65;
}

@media (prefers-reduced-motion: no-preference) {
	.vp-tst-timer {
		transition: color 0.3s ease, border-color 0.3s ease;
	}
}

.vp-tst-timer[data-state="warning"] {
	color: var(--wp--preset--color--warning-accent, #b45309);
	border-color: var(--wp--preset--color--warning-accent, #b45309);
}

.vp-tst-timer[data-state="critical"] {
	color: var(--wp--preset--color--danger, #b91c1c);
	border-color: var(--wp--preset--color--danger, #b91c1c);
}

.vp-tst-metrics {
	grid-area: metrics;
	display: grid;
	grid-template-columns: 1.3fr repeat(3, 1fr);
	gap: 1px;
	background: var(--wp--preset--color--border, #e2e5ea);
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--small, 6px);
	overflow: hidden;
}

.vp-tst-metric {
	display: flex;
	align-items: center;
	gap: 0.6em;
	padding: 0.85em 1em;
	background: var(--wp--preset--color--base, #fff);
}

.vp-tst-metric__icon {
	width: 1.4rem;
	height: 1.4rem;
	flex-shrink: 0;
	opacity: 0.55;
}

.vp-tst-metric__body {
	display: flex;
	flex-direction: column;
	line-height: 1.15;
	min-width: 0;
}

.vp-tst-metric__value {
	font-size: 1.3rem;
	font-weight: 700;
	font-variant-numeric: tabular-nums;
}

.vp-tst-metric__label {
	font-size: 0.72rem;
	opacity: 0.65;
}

.vp-tst-metric--primary {
	background: var(--wp--preset--color--surface, #f5f6f8);
}

.vp-tst-metric--primary .vp-tst-metric__icon {
	color: var(--wp--preset--color--accent-device, #1e3a8a);
	opacity: 1;
	width: 1.75rem;
	height: 1.75rem;
}

.vp-tst-metric--primary .vp-tst-metric__value {
	font-size: clamp(1.5rem, 4vw, 2rem);
	color: var(--wp--preset--color--accent-device, #1e3a8a);
}

.vp-tst-metric[data-metric="accuracy"][data-level="warn"] .vp-tst-metric__value,
.vp-tst-metric[data-metric="errors"][data-level="warn"] .vp-tst-metric__value {
	color: var(--wp--preset--color--warning-accent, #b45309);
}

.vp-tst-metric[data-metric="accuracy"][data-level="bad"] .vp-tst-metric__value,
.vp-tst-metric[data-metric="errors"][data-level="bad"] .vp-tst-metric__value {
	color: var(--wp--preset--color--danger, #b91c1c);
}

.vp-tst-metric[data-metric="accuracy"][data-level="good"] .vp-tst-metric__value {
	color: var(--wp--preset--color--success, #15803d);
}

/* ---- Live WPM chart (lightweight inline SVG, no chart library) ---- */

.vp-tst-chart {
	grid-area: chart;
	padding: 0.85em 1em;
	background: var(--wp--preset--color--base, #fff);
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--small, 6px);
}

.vp-tst-chart__label {
	display: flex;
	align-items: center;
	gap: 0.4em;
	font-size: 0.72rem;
	font-weight: 700;
	letter-spacing: 0.03em;
	text-transform: uppercase;
	opacity: 0.6;
}

.vp-tst-chart__svg {
	display: block;
	width: 100%;
	height: 60px;
	margin-top: 0.4em;
	overflow: visible;
}

.vp-tst-chart__line {
	fill: none;
	stroke: var(--wp--preset--color--accent-device, #1e3a8a);
	stroke-width: 2;
	stroke-linecap: round;
	stroke-linejoin: round;
	vector-effect: non-scaling-stroke;
}

/* ---- Typing passage (this tool's own — see block header note) ---- */

.vp-tst-surface-wrap {
	position: relative;
	margin: 0;
}

.vp-tst-capture-label {
	position: absolute;
	width: 1px;
	height: 1px;
	overflow: hidden;
	clip: rect(0 0 0 0);
	white-space: nowrap;
}

.vp-tst-surface {
	font-family: var(--wp--preset--font-family--mono, monospace);
	font-size: 1.25rem;
	line-height: 2;
	letter-spacing: 0.01em;
	padding: 1.5rem;
	background: var(--wp--preset--color--base, #fff);
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--base, 8px);
	word-break: break-word;
}

.vp-tst-app[data-tst-state="running"] .vp-tst-surface {
	border-color: var(--wp--preset--color--accent-device, #1e3a8a);
}

.vp-tst-char {
	position: relative;
	white-space: pre-wrap;
	border-radius: 2px;
}

.vp-tst-char--pending {
	opacity: 0.5;
}

.vp-tst-char--correct {
	opacity: 1;
	color: var(--wp--preset--color--success, #15803d);
}

.vp-tst-char--incorrect {
	opacity: 1;
	color: var(--wp--preset--color--danger, #b91c1c);
	background: var(--wp--preset--color--danger-bg, #fdecec);
	text-decoration: underline wavy var(--wp--preset--color--danger, #b91c1c);
}

.vp-tst-char--current {
	opacity: 1;
	border-left: 2px solid var(--wp--preset--color--accent-device, #1e3a8a);
}

@media (prefers-reduced-motion: no-preference) {
	.vp-tst-char--current {
		animation: vp-tst-caret-blink 1s step-end infinite;
	}
}

@keyframes vp-tst-caret-blink {
	50% { border-left-color: transparent; }
}

/* Visually hidden but focusable/typeable — a `display:none` field can't
   receive input on most mobile browsers, so this stays a real, focusable
   textarea layered invisibly over the passage above it. */
.vp-tst-capture {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	opacity: 0;
	resize: none;
	border: 0;
	padding: 0;
	cursor: text;
}

/* ---- Result state ---- */

.vp-tst-result {
	margin-top: 0;
	padding: 1.75rem;
	background: var(--wp--preset--color--surface, #f5f6f8);
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-top: 3px solid var(--wp--preset--color--success, #15803d);
	border-radius: var(--wp--custom--radius--base, 8px);
	text-align: center;
}

.vp-tst-result__title {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 0.5em;
	margin: 0 0 1rem;
	color: var(--wp--preset--color--success, #15803d);
}

.vp-tst-result__hero {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 0.2em;
	margin-bottom: 1.25rem;
}

.vp-tst-result__hero-value {
	font-size: clamp(2.5rem, 8vw, 3.5rem);
	font-weight: 700;
	line-height: 1;
	color: var(--wp--preset--color--accent-device, #1e3a8a);
}

.vp-tst-result__hero-label {
	font-size: 0.85rem;
	opacity: 0.65;
}

.vp-tst-result__badge {
	margin-top: 0.6em;
	padding: 0.3em 0.9em;
	font-size: 0.78rem;
	font-weight: 700;
	color: var(--wp--preset--color--success, #15803d);
	background: var(--wp--preset--color--success-bg, #e7f7ee);
	border-radius: 999px;
}

.vp-tst-result__grid {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
	gap: 1rem;
	margin: 0 0 0.75rem;
}

.vp-tst-result__grid span {
	display: block;
	font-size: 1.3rem;
	font-weight: 700;
}

.vp-tst-result__grid label {
	font-size: 0.75rem;
	opacity: 0.65;
}

.vp-tst-result__note {
	max-width: 46em;
	margin: 0 auto 1.25rem;
	font-size: 0.82rem;
	opacity: 0.65;
}

.vp-tst-result__actions {
	display: flex;
	justify-content: center;
	gap: 0.6em;
	flex-wrap: wrap;
}

/* ---- Recent results ---- */

.vp-tst-history__title,
.vp-tst-history__note {
	display: flex;
	align-items: center;
	gap: 0.4em;
}

.vp-tst-history__title {
	margin: 0 0 0.25rem;
}

.vp-tst-history__note {
	font-size: 0.8rem;
	opacity: 0.6;
	margin: 0 0 0.75rem;
}

.vp-tst-history__list {
	list-style: none;
	margin: 0 0 0.75rem;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: 0.4em;
}

.vp-tst-history__list[hidden],
.vp-tst-history__empty[hidden] {
	display: none;
}

.vp-tst-history__item {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: 0.9em;
	padding: 0.6em 0.9em;
	background: var(--wp--preset--color--surface, #f5f6f8);
	border-radius: var(--wp--custom--radius--small, 6px);
	font-size: 0.88rem;
}

.vp-tst-history__wpm {
	font-weight: 700;
	color: var(--wp--preset--color--accent-device, #1e3a8a);
}

.vp-tst-history__meta {
	opacity: 0.65;
}

.vp-tst-history__empty {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 0.5em;
	padding: 1.5rem 1rem;
	text-align: center;
	font-size: 0.88rem;
	opacity: 0.65;
}

@media (max-width: 680px) {
	.vp-tst-dashboard {
		grid-template-columns: 1fr;
		grid-template-areas:
			"timer"
			"metrics"
			"chart";
	}

	.vp-tst-metrics {
		grid-template-columns: 1fr 1fr;
		grid-template-areas:
			"primary primary"
			"accuracy characters"
			"errors errors";
	}

	.vp-tst-metric--primary {
		grid-area: primary;
	}

	.vp-tst-metric[data-metric="accuracy"] {
		grid-area: accuracy;
	}

	.vp-tst-metric[data-metric="characters"] {
		grid-area: characters;
	}

	.vp-tst-metric[data-metric="errors"] {
		grid-area: errors;
	}

	.vp-tst-setup__row {
		flex-direction: column;
		align-items: stretch;
	}

	.vp-tst-start {
		margin-left: 0;
	}
}

/* ---- Typing Practice ---------------------------------------------------- */

.vp-typing-practice__modes {
	display: flex;
	flex-wrap: wrap;
	gap: 0.4em;
	margin: 1rem 0;
}

.vp-typing-practice__custom {
	display: flex;
	flex-direction: column;
	gap: 0.4em;
	margin-bottom: 1rem;
}

.vp-typing-practice__custom[hidden] {
	display: none;
}

.vp-typing-practice__custom textarea {
	width: 100%;
	padding: 0.6em 0.8em;
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--small, 4px);
	font: inherit;
}

.vp-typing-practice__toolbar {
	display: flex;
	flex-wrap: wrap;
	gap: 0.6em;
	margin-bottom: 1rem;
}

.vp-typing-practice__stats {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
	gap: 1px;
	background: var(--wp--preset--color--border, #e2e5ea);
	border: 1px solid var(--wp--preset--color--border, #e2e5ea);
	border-radius: var(--wp--custom--radius--small, 4px);
	overflow: hidden;
	margin-bottom: 1rem;
}

.vp-typing-practice__stat {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 0.2em;
	padding: 0.75em 0.5em;
	background: var(--wp--preset--color--base, #fff);
}

.vp-typing-practice__stat span {
	font-size: 1.3rem;
	font-weight: 700;
}

.vp-typing-practice__stat label {
	font-size: 0.75rem;
	opacity: 0.7;
}

.vp-typing-practice__feedback {
	text-align: center;
	font-weight: 600;
	margin-top: 0.75rem;
}

/* ---- Device Information -------------------------------------------------- */

.vp-device-info__actions {
	display: flex;
	flex-wrap: wrap;
	gap: 0.6em;
	margin: 1rem 0;
}

.vp-device-info__grid {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
	gap: 1rem;
	margin-top: 1rem;
}

.vp-device-info__card {
	padding: 1.1rem 1.25rem;
	background: var(--wp--preset--color--surface, #f5f6f8);
	border-top: 3px solid var(--wp--preset--color--accent-device, #1e3a8a);
	border-radius: var(--wp--custom--radius--base, 8px);
}

.vp-device-info__card--wide {
	grid-column: 1 / -1;
}

.vp-device-info__card-title {
	margin: 0 0 0.75rem;
	font-size: 1rem;
}

.vp-device-info__rows {
	display: flex;
	flex-direction: column;
	gap: 0.5em;
}

.vp-device-info__row {
	display: flex;
	justify-content: space-between;
	gap: 1em;
	font-size: 0.9rem;
	padding-bottom: 0.4em;
	border-bottom: 1px dashed var(--wp--preset--color--border, #e2e5ea);
}

.vp-device-info__row:last-child {
	border-bottom: 0;
	padding-bottom: 0;
}

.vp-device-info__row-label {
	opacity: 0.7;
}

.vp-device-info__row-value {
	font-weight: 600;
	text-align: right;
	word-break: break-word;
}

.vp-device-info__hint {
	font-size: 0.85rem;
	opacity: 0.7;
	margin-top: 0;
}

.vp-device-info__capabilities {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
	gap: 0.5em;
}

.vp-device-info__capability {
	display: flex;
	justify-content: space-between;
	gap: 0.5em;
	padding: 0.5em 0.75em;
	background: var(--wp--preset--color--base, #fff);
	border-radius: var(--wp--custom--radius--small, 4px);
	font-size: 0.85rem;
}

.vp-device-info__capability--yes span:last-child {
	color: var(--wp--preset--color--success, #15803d);
	font-weight: 600;
}

.vp-device-info__capability--no span:last-child {
	opacity: 0.55;
}

.vp-device-info__privacy-list {
	margin: 0;
	padding-left: 1.2em;
	display: flex;
	flex-direction: column;
	gap: 0.4em;
	font-size: 0.9rem;
}

@media (max-width: 600px) {
	.vp-device-info__grid {
		grid-template-columns: 1fr;
	}

	.vp-screen-size__primary-value {
		font-size: 2.25rem;
	}
}

.vp-rpe-imagery-attribution {
	margin-top: 0.5rem;
	font-size: 0.75rem;
	color: var(--wp--preset--color--contrast-2, #57606a);
}

@media (prefers-reduced-motion: reduce) {
	.vp-rpe-card,
	.vp-rpe-marker span {
		transition: none;
	}
}

/* --- Page title accent line (site-wide) --- */

/* A thin rule under each page's main H1 — one consistent color
   everywhere (direct decision: not per-section, to avoid a second,
   coarser color system competing with the existing per-category accent
   colors already used throughout the site). Reuses the existing
   `accent-utility` token rather than introducing a new color.
   Selectors are deliberately specific rather than a bare
   `.wp-block-post-title`/`.wp-block-heading` rule — those same core
   classes are also used on every *card* title inside archive/loop
   listings (confirmed live: tool/guide/government card titles render
   as `<h2 class="wp-block-post-title">`), so a bare class selector
   would have put this line under hundreds of card titles instead of
   just the one real page title per page:
   - `h1.wp-block-post-title` — the tag+class combo only ever matches
     the genuine singular H1 (page.html's static pages: About, Privacy,
     Terms, Contact, Disclaimer); card titles are H2, so they never
     match even though they share the same class.
   - `.wp-block-query-title` — archive/taxonomy/search page headings
     (Tools, Guides, Government, every taxonomy archive, search
     results) — always rendered `level:1`, never reused for cards.
   - `.vp-tool-header__title` — single government content pages, which
     render this as a standalone H1 with no icon.
   - `.vp-guide-header__title` — single guide pages.
   - `body.error404 h1` — the 404 page's bare, class-less heading.

   Tool pages (single.html) and guide pages (single-guide.html) are a
   further wrinkle: both wrap their title in a flex row alongside an
   icon (`.vp-tool-header`/`.vp-guide-header`), so a border on the title
   text alone would only span the text's own width — sitting oddly next
   to an icon with no line under it, not the full-width look the
   reference screenshot showed. For those two, the line goes on the
   *row* instead so it spans icon+title together; the title-only rule
   above is then explicitly cancelled for the nested case via a
   higher-specificity override, so single-government_content.html's
   standalone (no icon, not nested) use of `.vp-tool-header__title`
   still gets its own line correctly. */
h1.wp-block-post-title,
.wp-block-query-title,
.vp-tool-header__title,
body.error404 h1 {
	padding-bottom: 0.75rem;
	border-bottom: 2px solid var(--wp--preset--color--accent-utility, #0369a1);
}

.vp-tool-header,
.vp-guide-header {
	padding-bottom: 0.75rem;
	border-bottom: 2px solid var(--wp--preset--color--accent-utility, #0369a1);
}

.vp-tool-header > .vp-tool-header__title,
.vp-guide-header > .vp-guide-header__title {
	padding-bottom: 0;
	border-bottom: none;
}
