/* -------------------------------------------------------------------------
   Page Builder sections — the real, front-end rendering of a Page
   Builder section's background/width/padding/alignment settings
   (inc/custom-editor.php's vf_ce_section_wrapper_open()). Width and
   padding are separate axes on purpose: a full-bleed background often
   still wants contained inner content, so .vf-pb-section--width-* sizes
   .vf-pb-section__inner, not the <section> itself. Background color/
   image is set inline per-instance (arbitrary per-section data), so it
   isn't a class here.

   Split out of style.css so the Page-Builder-specific rendering rules
   don't grow the main theme stylesheet indefinitely as more block types
   and layout options are added.
   ---------------------------------------------------------------------- */
.vf-pb-section { position: relative; background-color: var(--vf-bg); }
.vf-pb-section + .vf-pb-section { border-top: 1px solid var(--vf-border); }

/* Conditional visibility — presentational only, not access control; see
   the doc comment on vf_ce_sanitize_section_settings() in
   inc/custom-editor.php. body.logged-in is a class WordPress core already
   adds via body_class() for authenticated visitors. */
.vf-pb-section--visibility-logged-in { display: none; }
body.logged-in .vf-pb-section--visibility-logged-in { display: block; }
body.logged-in .vf-pb-section--visibility-logged-out { display: none; }
@media (max-width: 600px) {
	.vf-pb-section--hide-mobile { display: none; }
}
@media (min-width: 601px) {
	.vf-pb-section--hide-desktop { display: none; }
}

.vf-pb-section--pad-none .vf-pb-section__inner { padding-top: 0; padding-bottom: 0; }
.vf-pb-section--pad-small .vf-pb-section__inner { padding-top: 48px; padding-bottom: 48px; }
.vf-pb-section--pad-medium .vf-pb-section__inner { padding-top: 110px; padding-bottom: 110px; }
.vf-pb-section--pad-large .vf-pb-section__inner { padding-top: 160px; padding-bottom: 160px; }

.vf-pb-section--width-contained .vf-pb-section__inner { max-width: var(--vf-container); margin: 0 auto; padding-left: 32px; padding-right: 32px; }
.vf-pb-section--width-wide .vf-pb-section__inner { max-width: 1400px; margin: 0 auto; padding-left: 32px; padding-right: 32px; }
.vf-pb-section--width-full .vf-pb-section__inner { padding-left: 32px; padding-right: 32px; }
/* Custom (5.1.8) — same margin/padding treatment as Contained/Wide; only
   max-width itself differs, and that one arbitrary number comes from an
   inline style on this same element (vf_ce_section_wrapper_open()), not
   a class, since it's a number the admin typed rather than a preset. */
.vf-pb-section--width-custom .vf-pb-section__inner { margin: 0 auto; padding-left: 32px; padding-right: 32px; }

.vf-pb-section--align-center .vf-pb-section__inner { text-align: center; }
.vf-pb-section--align-center .vf-pb-section__inner .vf-content-btn,
.vf-pb-section--align-center .vf-pb-section__inner img { margin-left: auto; margin-right: auto; }

/* Section height (5.1.4/5.1.7) — orthogonal to 'padding' (which only ever
   adds space, never guarantees a minimum). Full Screen and Custom share
   the same flex-centering treatment; only the min-height value itself
   differs (a fixed vh calc vs. an exact px the admin typed in — see
   min_height_custom's inline style in vf_ce_section_wrapper_open()).
   100px reserved off Full Screen's bottom is the fixed dock's real
   footprint (.vf-dock-wrap: 78px tall + 22px bottom offset), so centered
   hero content never lands underneath it — Custom has no such reservation
   since its value is a deliberate, exact number, not "the whole screen." */
.vf-pb-section--height-viewport,
.vf-pb-section--height-custom {
	display: flex;
	flex-direction: column;
}
.vf-pb-section--height-viewport {
	min-height: 100vh;
	min-height: calc(100vh - 100px);
	min-height: calc(100svh - 100px);
}
.vf-pb-section--height-viewport > .vf-pb-section__inner,
.vf-pb-section--height-custom > .vf-pb-section__inner {
	display: flex;
	flex-direction: column;
	justify-content: center;
	flex: 1 1 auto;
	width: 100%;
}

.vf-pb-spacer--small { height: 24px; }
.vf-pb-spacer--medium { height: 56px; }
.vf-pb-spacer--large { height: 110px; }

/* -------------------------------------------------------------------------
   Mobile — matches the padding conventions style.css already uses for the
   theme's own built-in sections, so a Page Builder page doesn't read as
   disproportionately padded next to them on a phone.
   ---------------------------------------------------------------------- */
@media (max-width: 600px) {
	.vf-pb-section--pad-medium .vf-pb-section__inner { padding-top: 64px; padding-bottom: 64px; }
	.vf-pb-section--pad-large .vf-pb-section__inner { padding-top: 90px; padding-bottom: 90px; }
}

@media (max-width: 480px) {
	.vf-pb-section--width-contained .vf-pb-section__inner,
	.vf-pb-section--width-wide .vf-pb-section__inner,
	.vf-pb-section--width-full .vf-pb-section__inner {
		padding-left: 20px;
		padding-right: 20px;
	}
}

/* -------------------------------------------------------------------------
   Container — the recursive nested-block tree (any block type as a child,
   containers included). 1-4 column grid per level; every level collapses
   to a single column on phones.
   ---------------------------------------------------------------------- */
.vf-pb-container { display: grid; grid-template-columns: 1fr; }
.vf-pb-container--cols-2 { grid-template-columns: repeat(2, 1fr); }
.vf-pb-container--cols-3 { grid-template-columns: repeat(3, 1fr); }
.vf-pb-container--cols-4 { grid-template-columns: repeat(4, 1fr); }
.vf-pb-container--gap-small { gap: 16px; }
.vf-pb-container--gap-medium { gap: 32px; }
.vf-pb-container--gap-large { gap: 56px; }
.vf-pb-container__cell > *:first-child { margin-top: 0; }
.vf-pb-container__cell > *:last-child { margin-bottom: 0; }
@media (max-width: 600px) {
	.vf-pb-container { grid-template-columns: 1fr !important; }
	.vf-pb-container--gap-large { gap: 32px; }
}

/* -------------------------------------------------------------------------
   Columns/Grid — layout sets grid-template-columns; every layout collapses
   to a single column on phones, since a macOS-portfolio-style narrow
   column has no room for a real multi-column layout under ~600px. Each
   .vf-pb-column is a real nested-block tree now (any block type, any
   depth) — it just never needs its own grid-cell wrapper the way
   Container's children do, since a column is always a single vertical
   stack, so the existing :first-child/:last-child margin-collapse rules
   below still target the real content directly.
   ---------------------------------------------------------------------- */
.vf-pb-columns { display: grid; gap: 32px; }
.vf-pb-columns--2-equal { grid-template-columns: 1fr 1fr; }
.vf-pb-columns--3-equal { grid-template-columns: repeat(3, 1fr); }
.vf-pb-columns--left-narrow { grid-template-columns: 1fr 2fr; }
.vf-pb-columns--right-narrow { grid-template-columns: 2fr 1fr; }
.vf-pb-column > *:first-child { margin-top: 0; }
.vf-pb-column > *:last-child { margin-bottom: 0; }
@media (max-width: 600px) {
	.vf-pb-columns { grid-template-columns: 1fr !important; gap: 24px; }
}

/* -------------------------------------------------------------------------
   Pricing table
   ---------------------------------------------------------------------- */
.vf-pb-pricing {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
	gap: 24px;
	align-items: start;
}
.vf-pb-pricing-plan {
	background: var(--vf-surface);
	border: 1px solid var(--vf-border);
	border-radius: var(--vf-radius-md);
	padding: 32px 28px;
	text-align: center;
}
.vf-pb-pricing-plan.is-featured {
	border-color: var(--vf-accent);
	box-shadow: 0 12px 32px rgba(59, 91, 253, 0.16);
	transform: translateY(-8px);
}
.vf-pb-pricing-plan__name { margin: 0 0 12px; font-size: 18px; }
.vf-pb-pricing-plan__price { font-size: 36px; font-weight: 700; font-family: var(--vf-font-display); margin-bottom: 18px; }
.vf-pb-pricing-plan__period { font-size: 15px; font-weight: 500; color: var(--vf-text-muted); }
.vf-pb-pricing-plan__features { margin: 0 0 24px; padding: 0; list-style: none; text-align: left; }
.vf-pb-pricing-plan__features li {
	position: relative;
	padding: 8px 0 8px 22px;
	border-top: 1px solid var(--vf-border);
	font-size: 14px;
	color: var(--vf-text-muted);
}
.vf-pb-pricing-plan__features li:first-child { border-top: none; }
.vf-pb-pricing-plan__feature::before {
	content: "\2713";
	position: absolute;
	left: 0;
	top: 8px;
	color: var(--vf-accent);
	font-weight: 700;
}
.vf-pb-pricing-plan__feature.is-excluded {
	text-decoration: line-through;
	opacity: 0.6;
}
.vf-pb-pricing-plan__feature.is-excluded::before {
	content: "\00d7";
	color: var(--vf-text-muted);
}
@media (max-width: 480px) {
	.vf-pb-pricing-plan.is-featured { transform: none; }
}

/* -------------------------------------------------------------------------
   Accordion (FAQ) — native <details>/<summary>, no JS.
   ---------------------------------------------------------------------- */
.vf-pb-accordion__item {
	border-bottom: 1px solid var(--vf-border);
	padding: 18px 0;
}
.vf-pb-accordion__item:first-child { border-top: 1px solid var(--vf-border); }
.vf-pb-accordion__question {
	cursor: pointer;
	font-weight: 600;
	font-size: 17px;
	list-style: none;
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 16px;
}
.vf-pb-accordion__question::-webkit-details-marker { display: none; }
.vf-pb-accordion__question::after {
	content: "+";
	flex: none;
	font-size: 22px;
	font-weight: 400;
	color: var(--vf-text-muted);
	transition: transform 0.2s ease;
}
.vf-pb-accordion__item[open] .vf-pb-accordion__question::after { transform: rotate(45deg); }
.vf-pb-accordion__answer { margin-top: 14px; color: var(--vf-text-muted); }
.vf-pb-accordion__answer > *:last-child { margin-bottom: 0; }
@media (prefers-reduced-motion: reduce) {
	.vf-pb-accordion__question::after { transition: none; }
}

/* -------------------------------------------------------------------------
   Tabs — see assets/js/tabs.js for the ARIA tablist behavior.
   ---------------------------------------------------------------------- */
.vf-pb-tabs__list {
	display: flex;
	flex-wrap: wrap;
	gap: 4px;
	border-bottom: 1px solid var(--vf-border);
	margin-bottom: 24px;
}
.vf-pb-tabs__tab {
	font: inherit;
	font-weight: 600;
	font-size: 15px;
	color: var(--vf-text-muted);
	background: none;
	border: none;
	border-bottom: 2px solid transparent;
	padding: 12px 18px;
	margin-bottom: -1px;
	cursor: pointer;
	min-height: 44px;
}
.vf-pb-tabs__tab.is-active { color: var(--vf-text); border-bottom-color: var(--vf-accent); }
.vf-pb-tabs__tab:focus-visible { outline: 2px solid var(--vf-accent); outline-offset: 2px; }
.vf-pb-tabs__panel > *:first-child { margin-top: 0; }
.vf-pb-tabs__panel > *:last-child { margin-bottom: 0; }
@media (max-width: 480px) {
	.vf-pb-tabs__list { gap: 0; }
	.vf-pb-tabs__tab { flex: 1 1 auto; padding: 12px 10px; font-size: 14px; }
}

/* -------------------------------------------------------------------------
   Video — responsive-embeds theme support (functions.php) already handles
   the real [embed] output's aspect ratio; this only styles the editor's
   own static preview placeholder (see vf_ce_render_video()'s $preview
   branch), which never reaches the real front end.
   ---------------------------------------------------------------------- */
.vf-pb-video--preview {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 10px;
	aspect-ratio: 16 / 9;
	background: #1d2327;
	color: #fff;
	border-radius: var(--vf-radius-sm);
}
.vf-pb-video--preview span { font-size: 32px; opacity: 0.8; }
.vf-pb-video--preview p { margin: 0; font-size: 13px; opacity: 0.7; word-break: break-all; padding: 0 20px; }

/* -------------------------------------------------------------------------
   Gallery — native horizontal scroll-snap (touch swipe needs no JS);
   assets/js/gallery.js adds the Prev/Next buttons' click behavior and
   arrow-key support.
   ---------------------------------------------------------------------- */
.vf-pb-gallery { position: relative; }
.vf-pb-gallery__track {
	display: flex;
	gap: 16px;
	overflow-x: auto;
	scroll-snap-type: x mandatory;
	scroll-behavior: smooth;
	-webkit-overflow-scrolling: touch;
	padding-bottom: 4px;
}
.vf-pb-gallery__slide { flex: 0 0 min(480px, 85%); scroll-snap-align: start; }
.vf-pb-gallery__image { width: 100%; height: 100%; aspect-ratio: 4 / 3; object-fit: cover; border-radius: var(--vf-radius-sm); }
.vf-pb-gallery__nav {
	position: absolute;
	top: 50%;
	transform: translateY(-50%);
	width: 40px;
	height: 40px;
	min-width: 44px;
	min-height: 44px;
	display: flex;
	align-items: center;
	justify-content: center;
	background: rgba(255, 255, 255, 0.9);
	border: 1px solid var(--vf-border);
	border-radius: 50%;
	cursor: pointer;
	font-size: 16px;
}
.vf-pb-gallery__nav--prev { left: -8px; }
.vf-pb-gallery__nav--next { right: -8px; }
.vf-pb-gallery__nav:focus-visible { outline: 2px solid var(--vf-accent); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) {
	.vf-pb-gallery__track { scroll-behavior: auto; }
}
@media (max-width: 600px) {
	.vf-pb-gallery__slide { flex-basis: 90%; }
	.vf-pb-gallery__nav { display: none; }
}

/* -------------------------------------------------------------------------
   Dynamic blocks (inc/default-templates.php) — the actual title/price/
   content of whichever post or product is currently being viewed, used
   only inside a Default Template. Minimal styling since most of these
   render real WooCommerce/WordPress template output that already carries
   its own markup and classes (price, add-to-cart forms, product gallery,
   ratings) — these rules just fit the pieces this theme renders itself.
   ---------------------------------------------------------------------- */
.vf-dynamic-featured-image img { border-radius: var(--vf-radius-sm); width: 100%; }
.vf-dynamic-excerpt { color: var(--vf-text-muted); }
.vf-dynamic-price { font-size: 22px; font-weight: 700; font-family: var(--vf-font-display); }

/* -------------------------------------------------------------------------
   Per-block Style group (4.2) — vf_ce_apply_block_style() wraps a block in
   .vf-pb-bs only when at least one style setting is set, so unstyled blocks
   never gain a wrapper. Spacing is vertical padding (predictable, never
   margin-collapses); max-width self-centers; the m-* classes are the
   mobile-only overrides; hide toggles are real display rules, force-shown
   inside the editor canvas by CANVAS_EDITOR_CSS the same way section-level
   visibility is.
   ---------------------------------------------------------------------- */
.vf-pb-bs--pad-none { padding-top: 0; padding-bottom: 0; }
.vf-pb-bs--pad-small { padding-top: var(--vf-space-small, 8px); padding-bottom: var(--vf-space-small, 8px); }
.vf-pb-bs--pad-medium { padding-top: var(--vf-space-medium, 20px); padding-bottom: var(--vf-space-medium, 20px); }
.vf-pb-bs--pad-large { padding-top: var(--vf-space-large, 44px); padding-bottom: var(--vf-space-large, 44px); }
.vf-pb-bs--align-left { text-align: left; }
.vf-pb-bs--align-center { text-align: center; }
.vf-pb-bs--align-right { text-align: right; }
.vf-pb-bs--align-justify { text-align: justify; }
.vf-pb-bs--w-small { max-width: 480px; margin-left: auto; margin-right: auto; }
.vf-pb-bs--w-medium { max-width: 720px; margin-left: auto; margin-right: auto; }

/* Effects layer (5.1) — clip-path presets. A fixed shape list, not a
   free-text value; see vf_ce_sanitize_block_style()'s own comment on why. */
.vf-pb-bs--clip-circle { clip-path: circle(50%); }
.vf-pb-bs--clip-ellipse { clip-path: ellipse(50% 40% at 50% 50%); }
.vf-pb-bs--clip-diagonal { clip-path: polygon(0 0, 100% 0, 100% 85%, 0 100%); }
.vf-pb-bs--clip-blob { clip-path: polygon(8% 22%, 38% 4%, 74% 10%, 96% 38%, 92% 74%, 62% 96%, 24% 88%, 4% 56%); }

@media (max-width: 782px) {
	.vf-pb-bs--m-pad-none { padding-top: 0; padding-bottom: 0; }
	.vf-pb-bs--m-pad-small { padding-top: 8px; padding-bottom: 8px; }
	.vf-pb-bs--m-pad-medium { padding-top: 20px; padding-bottom: 20px; }
	.vf-pb-bs--m-pad-large { padding-top: 44px; padding-bottom: 44px; }
	.vf-pb-bs--m-align-left { text-align: left; }
	.vf-pb-bs--m-align-center { text-align: center; }
	.vf-pb-bs--m-align-right { text-align: right; }
	.vf-pb-bs--m-align-justify { text-align: justify; }
	.vf-pb-bs--hide-mobile { display: none; }
}
@media (min-width: 783px) {
	.vf-pb-bs--hide-desktop { display: none; }
}

/* -------------------------------------------------------------------------
   Repeater block (4.2) — one designed card, stamped out once per queried
   item. Reuses the container grid's column/gap classes; this only adds
   what's repeater-specific.
   ---------------------------------------------------------------------- */
.vf-pb-repeater .vf-pb-container__cell {
	background: var(--vf-surface);
	border: 1px solid var(--vf-border);
	border-radius: var(--vf-radius-sm);
	padding: 18px;
}
.vf-pb-repeater--plain .vf-pb-container__cell {
	background: none;
	border: none;
	border-radius: 0;
	padding: 0;
}

/* Stack display (4.5) — a resting card deck that fans out on hover,
   pure CSS: each cell is absolutely centered at rest with a slight
   cascading offset/rotation (a loosely-stacked-papers look), and
   :hover spreads them into a row. No JS at all — the crossfade-style
   feature people actually asked to SEE data in (the Preview List,
   below) is the one that needed script; this is a simpler reveal.
   Explicit per-position rules cover up to 6 cards, matching the
   Repeater's realistic range for this display (a handful of recent
   items, not a full archive grid) — a 7th+ card just rides along
   with the 6th's position rather than getting its own rule. */
.vf-pb-repeater--stack {
	position: relative;
	min-height: 320px;
	margin: 0 auto;
	max-width: 640px;
}
.vf-pb-repeater--stack .vf-pb-container__cell {
	position: absolute;
	top: 0;
	left: 50%;
	width: min(260px, 90%);
	margin-left: min(-130px, -45%);
	transition: transform 0.35s cubic-bezier(0.22, 1, 0.36, 1), box-shadow 0.35s ease;
	box-shadow: 0 10px 26px rgba(0, 0, 0, 0.12);
}
.vf-pb-repeater--stack .vf-pb-container__cell:nth-child(1) { transform: translate(0, 0) rotate(0deg); z-index: 6; }
.vf-pb-repeater--stack .vf-pb-container__cell:nth-child(2) { transform: translate(10px, 10px) rotate(3deg); z-index: 5; }
.vf-pb-repeater--stack .vf-pb-container__cell:nth-child(3) { transform: translate(20px, 20px) rotate(-3deg); z-index: 4; }
.vf-pb-repeater--stack .vf-pb-container__cell:nth-child(4) { transform: translate(30px, 30px) rotate(4deg); z-index: 3; }
.vf-pb-repeater--stack .vf-pb-container__cell:nth-child(5) { transform: translate(40px, 40px) rotate(-4deg); z-index: 2; }
.vf-pb-repeater--stack .vf-pb-container__cell:nth-child(n+6) { transform: translate(48px, 48px) rotate(5deg); z-index: 1; }
.vf-pb-repeater--stack:hover .vf-pb-container__cell,
.vf-pb-repeater--stack:focus-within .vf-pb-container__cell { box-shadow: 0 18px 38px rgba(0, 0, 0, 0.18); }
.vf-pb-repeater--stack:hover .vf-pb-container__cell:nth-child(1),
.vf-pb-repeater--stack:focus-within .vf-pb-container__cell:nth-child(1) { transform: translate(-220px, 0) rotate(-6deg); }
.vf-pb-repeater--stack:hover .vf-pb-container__cell:nth-child(2),
.vf-pb-repeater--stack:focus-within .vf-pb-container__cell:nth-child(2) { transform: translate(-110px, 6px) rotate(-3deg); }
.vf-pb-repeater--stack:hover .vf-pb-container__cell:nth-child(3),
.vf-pb-repeater--stack:focus-within .vf-pb-container__cell:nth-child(3) { transform: translate(0, 10px) rotate(0deg); z-index: 7; }
.vf-pb-repeater--stack:hover .vf-pb-container__cell:nth-child(4),
.vf-pb-repeater--stack:focus-within .vf-pb-container__cell:nth-child(4) { transform: translate(110px, 6px) rotate(3deg); }
.vf-pb-repeater--stack:hover .vf-pb-container__cell:nth-child(5),
.vf-pb-repeater--stack:focus-within .vf-pb-container__cell:nth-child(5) { transform: translate(220px, 0) rotate(6deg); }
.vf-pb-repeater--stack:hover .vf-pb-container__cell:nth-child(n+6),
.vf-pb-repeater--stack:focus-within .vf-pb-container__cell:nth-child(n+6) { transform: translate(330px, -4px) rotate(8deg); }
@media (max-width: 600px) {
	/* A 5-6-wide fan has nowhere to go on a phone screen — fall back
	   to the plain resting stack (no spread) rather than clipping. */
	.vf-pb-repeater--stack:hover .vf-pb-container__cell,
	.vf-pb-repeater--stack:focus-within .vf-pb-container__cell { transform: none !important; }
	.vf-pb-repeater--stack .vf-pb-container__cell:nth-child(1) { transform: translate(0, 0) rotate(0deg); }
	.vf-pb-repeater--stack .vf-pb-container__cell:nth-child(n+2) { transform: translate(14px, 14px) rotate(3deg); }
}
@media (prefers-reduced-motion: reduce) {
	.vf-pb-repeater--stack .vf-pb-container__cell { transition: none; }
}

/* -------------------------------------------------------------------------
   4.2 vocabulary blocks: divider, icon, countdown, map, modal.
   ---------------------------------------------------------------------- */
.vf-pb-divider { border: none; border-top: 1px solid var(--vf-border); margin: 8px auto; width: 100%; }
.vf-pb-divider--short { width: 80px; }
.vf-pb-divider--dots {
	border: none;
	text-align: center;
	letter-spacing: 14px;
	color: var(--vf-text-muted);
	height: auto;
}
.vf-pb-divider--dots::before { content: "···"; font-size: 22px; line-height: 1; }

.vf-pb-icon { display: inline-flex; align-items: center; justify-content: center; color: var(--vf-accent); }
.vf-pb-icon svg { width: 100%; height: 100%; }
.vf-pb-icon--small { width: 24px; height: 24px; }
.vf-pb-icon--medium { width: 40px; height: 40px; }
.vf-pb-icon--large { width: 64px; height: 64px; }
a.vf-pb-icon { transition: transform 0.15s ease; }
a.vf-pb-icon:hover { transform: translateY(-2px); }
.vf-pb-icon--badge {
	color: #fff;
	border-radius: 50%;
	box-shadow: 0 3px 10px rgba(0, 0, 0, 0.18);
	padding: 22%;
	box-sizing: content-box;
}
.vf-pb-icon--badge svg { width: 100%; height: 100%; }

.vf-pb-countdown { display: flex; gap: 18px; justify-content: center; flex-wrap: wrap; }
.vf-pb-countdown__unit { text-align: center; min-width: 64px; }
.vf-pb-countdown__num {
	display: block;
	font-family: var(--vf-font-display);
	font-size: 34px;
	font-weight: 700;
	font-variant-numeric: tabular-nums;
	line-height: 1.1;
}
.vf-pb-countdown__label {
	display: block;
	font-size: 11px;
	text-transform: uppercase;
	letter-spacing: 0.08em;
	color: var(--vf-text-muted);
	margin-top: 4px;
}
.vf-pb-countdown--done { font-weight: 600; }

.vf-pb-map { border-radius: var(--vf-radius-sm); overflow: hidden; border: 1px solid var(--vf-border); }
.vf-pb-map iframe { display: block; width: 100%; border: 0; }
.vf-pb-map--small iframe { height: 240px; }
.vf-pb-map--medium iframe { height: 380px; }
.vf-pb-map--large iframe { height: 520px; }

.vf-pb-modal-overlay {
	position: fixed;
	inset: 0;
	z-index: 90;
	display: none;
	align-items: center;
	justify-content: center;
	padding: 24px;
	background: rgba(15, 18, 24, 0.55);
	backdrop-filter: blur(6px);
	-webkit-backdrop-filter: blur(6px);
}
.vf-pb-modal-overlay.is-open { display: flex; }
/* Device targeting (5.1) — hides the trigger button + overlay together;
   same 600px breakpoint the Style group's hide_mobile/hide_desktop use. */
@media (max-width: 600px) {
	.vf-pb-modal-device-wrap--desktop { display: none; }
}
@media (min-width: 601px) {
	.vf-pb-modal-device-wrap--mobile { display: none; }
}
.vf-pb-modal {
	position: relative;
	max-width: 560px;
	width: 100%;
	max-height: 80vh;
	overflow-y: auto;
	background: var(--vf-surface);
	border: 1px solid var(--vf-border);
	border-radius: var(--vf-radius);
	padding: 28px;
	box-shadow: 0 30px 80px rgba(0, 0, 0, 0.35);
}
.vf-pb-modal__close {
	position: absolute;
	top: 12px;
	right: 12px;
	width: 30px;
	height: 30px;
	border: none;
	border-radius: 50%;
	background: color-mix(in srgb, var(--vf-text) 8%, transparent);
	color: var(--vf-text);
	font-size: 16px;
	line-height: 1;
	cursor: pointer;
}
.vf-pb-modal__close:hover { background: color-mix(in srgb, var(--vf-text) 16%, transparent); }

/* -------------------------------------------------------------------------
   Slider block (4.3) — the hero carousel. Slides stack absolutely and
   crossfade; without JS the first slide simply stands as a static hero.
   ---------------------------------------------------------------------- */
.vf-pb-slider {
	position: relative;
	border-radius: var(--vf-radius-sm);
	overflow: hidden;
	background: var(--vf-surface);
}
.vf-pb-slider--small { height: 320px; }
.vf-pb-slider--medium { height: 460px; }
.vf-pb-slider--large { height: 600px; }
.vf-pb-slider--full { height: 82vh; }
.vf-pb-slider__track { position: absolute; inset: 0; }
.vf-pb-slider__slide {
	position: absolute;
	inset: 0;
	background-size: cover;
	background-position: center;
	opacity: 0;
	transition: opacity 0.6s ease;
	display: flex;
	align-items: flex-end;
	pointer-events: none;
}
.vf-pb-slider__slide.is-active { opacity: 1; pointer-events: auto; }
.vf-pb-slider__content {
	position: relative;
	padding: 36px;
	max-width: 640px;
	color: #fff;
	text-shadow: 0 1px 12px rgba(0, 0, 0, 0.45);
}
.vf-pb-slider__slide::before {
	content: "";
	position: absolute;
	inset: 0;
	background: linear-gradient(180deg, rgba(0, 0, 0, 0) 40%, rgba(0, 0, 0, 0.55) 100%);
	pointer-events: none;
}
.vf-pb-slider__heading { color: #fff; margin: 0 0 8px; }
.vf-pb-slider__text { margin: 0 0 16px; color: rgba(255, 255, 255, 0.92); }
.vf-pb-slider__arrow {
	position: absolute;
	top: 50%;
	transform: translateY(-50%);
	z-index: 3;
	width: 40px;
	height: 40px;
	border: none;
	border-radius: 50%;
	background: rgba(255, 255, 255, 0.85);
	color: #1a1d23;
	font-size: 22px;
	line-height: 1;
	cursor: pointer;
	backdrop-filter: blur(8px);
	-webkit-backdrop-filter: blur(8px);
}
.vf-pb-slider__arrow:hover { background: #fff; }
.vf-pb-slider__arrow--prev { left: 14px; }
.vf-pb-slider__arrow--next { right: 14px; }
.vf-pb-slider__dots {
	position: absolute;
	bottom: 14px;
	left: 50%;
	transform: translateX(-50%);
	z-index: 3;
	display: flex;
	gap: 8px;
}
.vf-pb-slider__dot {
	width: 9px;
	height: 9px;
	border: none;
	border-radius: 50%;
	background: rgba(255, 255, 255, 0.5);
	cursor: pointer;
	padding: 0;
}
.vf-pb-slider__dot.is-active { background: #fff; }
.vf-pb-slider__dot:focus-visible,
.vf-pb-slider__arrow:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
@media (max-width: 600px) {
	.vf-pb-slider--small { height: 260px; }
	.vf-pb-slider--medium { height: 340px; }
	.vf-pb-slider--large { height: 420px; }
	.vf-pb-slider__content { padding: 20px; }
	.vf-pb-slider__arrow { display: none; }
}
@media (prefers-reduced-motion: reduce) {
	.vf-pb-slider__slide { transition: none; }
}

/* -------------------------------------------------------------------------
   Section parallax (4.4) — the background moves at a different rate than
   the section's own content while scrolling. Needs its own layer (rather
   than transforming the section itself, which holds real document flow)
   sized taller than the section with negative top/bottom insets so the
   scroll-driven translate (assets/js/pb-extras.js) never reveals a bare
   edge. overflow:hidden is scoped to parallax sections only — normal
   sections never clip anything.
   ---------------------------------------------------------------------- */
.vf-pb-section--parallax { overflow: hidden; }
.vf-pb-section__bg {
	position: absolute;
	left: 0;
	right: 0;
	top: -15%;
	bottom: -15%;
	background-size: cover;
	background-repeat: no-repeat;
	will-change: transform;
	pointer-events: none;
	z-index: 0;
}
.vf-pb-section--parallax .vf-pb-section__inner { position: relative; z-index: 1; }

/* Overlay (5.1.9) — a tint between the background image (plain or
   parallax) and the content; same absolute-layer + relative-content
   stacking .vf-pb-section__bg already needed above, kept separate since
   an overlay can exist with or without parallax also being on. */
.vf-pb-section__overlay {
	position: absolute;
	inset: 0;
	pointer-events: none;
	z-index: 0;
}
.vf-pb-section--has-overlay .vf-pb-section__inner { position: relative; z-index: 1; }

@media (prefers-reduced-motion: reduce) {
	/* pb-extras.js also skips wiring the scroll listener at all for
	   these visitors — this rule is the belt to that suspenders, in
	   case the layer's transform was left applied from a resize event
	   that fired before the media query re-check. */
	.vf-pb-section__bg { transform: none !important; }
}

/* -------------------------------------------------------------------------
   Entrance/scroll animation (block Style group "Animate on scroll") —
   pb-extras.js's initScrollAnimations() adds --pending right before it
   starts observing a block, then swaps to --in once it scrolls into
   view. An element that never gets either class (JS didn't run, or a
   type this browser's CSS doesn't have a --pending rule for) is just
   its own normal, fully visible markup — nothing here can leave content
   permanently hidden.
   ---------------------------------------------------------------------- */
[data-vf-animate].vf-pb-anim--pending { transition: none; }
[data-vf-animate="fade"].vf-pb-anim--pending { opacity: 0; }
[data-vf-animate="slide-up"].vf-pb-anim--pending { opacity: 0; transform: translateY(28px); }
[data-vf-animate="slide-down"].vf-pb-anim--pending { opacity: 0; transform: translateY(-28px); }
[data-vf-animate="slide-left"].vf-pb-anim--pending { opacity: 0; transform: translateX(28px); }
[data-vf-animate="slide-right"].vf-pb-anim--pending { opacity: 0; transform: translateX(-28px); }
[data-vf-animate="scale"].vf-pb-anim--pending { opacity: 0; transform: scale(0.92); }
[data-vf-animate].vf-pb-anim--in {
	opacity: 1;
	transform: none;
	transition: opacity 0.6s ease, transform 0.6s ease;
}
@media (prefers-reduced-motion: reduce) {
	/* pb-extras.js already skips adding --pending at all for these
	   visitors — this rule is the belt to that suspenders. */
	[data-vf-animate].vf-pb-anim--pending { opacity: 1; transform: none; }
}

/* -------------------------------------------------------------------------
   Background focal point (4.4) — vf_ce_section_wrapper_open() now emits
   the picked background-position instead of a hardcoded "center" for
   both the plain and parallax layers; no new selectors needed here.
   ---------------------------------------------------------------------- */

/* -------------------------------------------------------------------------
   Widget (4.4) — the free-form overlay block. Position/rotation/width
   ride entirely on inline styles + the per-widget <style> block emitted
   by vf_ce_render_widget() (arbitrary numeric x/y/rotation can't be
   expressed as a fixed set of utility classes the way every other
   layout control in this editor is); this rule only owns what's
   genuinely shared by every widget instance.
   ---------------------------------------------------------------------- */
.vf-pb-widget {
	position: absolute;
	z-index: 5;
}

/* -------------------------------------------------------------------------
   Preview List (4.5) — colored rows; hovering (or focusing) one
   crossfades in its own preview image in a floating stage. The stage
   overlaps the row list on desktop (matching the reference this was
   built from) but drops to normal flow below the rows on narrow
   screens, where an absolutely-positioned overlap has nowhere good to
   sit. assets/js/pb-extras.js toggles which image is `.is-active`;
   the opacity transition here is what actually produces the fade.
   ---------------------------------------------------------------------- */
.vf-pb-preview-list { position: relative; }
.vf-pb-preview-list__rows { display: flex; flex-direction: column; gap: 12px; }
.vf-pb-preview-list__row {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 16px;
	border: none;
	border-radius: var(--vf-radius-sm);
	padding: 20px 24px;
	font: inherit;
	font-weight: 700;
	font-size: 1.15em;
	color: var(--vf-text);
	text-align: left;
	cursor: pointer;
	transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.vf-pb-preview-list__row:hover,
.vf-pb-preview-list__row:focus-visible { transform: translateY(-2px); box-shadow: 0 10px 24px rgba(0, 0, 0, 0.1); outline: none; }
.vf-pb-preview-list__icon {
	flex: none;
	width: 34px;
	height: 34px;
	display: flex;
	align-items: center;
	justify-content: center;
	border-radius: 50%;
	background: rgba(255, 255, 255, 0.6);
	color: var(--vf-text);
}
.vf-pb-preview-list__icon svg { width: 55%; height: 55%; }
.vf-pb-preview-list__stage {
	position: absolute;
	top: 50%;
	right: 8%;
	transform: translateY(-50%);
	width: 220px;
	pointer-events: none;
	filter: drop-shadow(0 20px 40px rgba(0, 0, 0, 0.25));
}
/* Follow the cursor (5.1.14) — pb-extras.js sets left/top directly once
   the mouse moves inside the list; this just switches OFF the default
   fixed anchor (right/transform) so those pixel values take over
   instead of fighting them, and adds the "catching up" easing that
   makes tracking feel smooth rather than glued to the pointer. Reverts
   to the default anchored position on mouseleave (class removed), and
   never applies at all for reduced-motion/no-hover — see the JS guard. */
.vf-pb-preview-list__stage.is-following {
	right: auto;
	transform: none;
	transition: left 0.12s ease-out, top 0.12s ease-out;
}
.vf-pb-preview-list__image {
	position: absolute;
	inset: 0;
	width: 100%;
	height: auto;
	border-radius: var(--vf-radius-sm);
	opacity: 0;
	transition: opacity 0.35s ease;
}
.vf-pb-preview-list__image.is-active { position: relative; opacity: 1; }
@media (max-width: 782px) {
	.vf-pb-preview-list__stage {
		position: static;
		transform: none;
		width: 100%;
		max-width: 320px;
		margin: 20px auto 0;
	}
}
@media (prefers-reduced-motion: reduce) {
	.vf-pb-preview-list__row,
	.vf-pb-preview-list__image { transition: none; }
}

/* -------------------------------------------------------------------------
   14. New block types (v5.1 — Power/Tool library expansion)
------------------------------------------------------------------------- */

/* Icon Box */
.vf-pb-icon-box {
	display: flex;
	gap: 16px;
	text-decoration: none;
	color: inherit;
}
.vf-pb-icon-box--icon-top { flex-direction: column; align-items: flex-start; }
.vf-pb-icon-box__icon {
	flex: none;
	display: inline-flex;
	width: 44px;
	height: 44px;
	align-items: center;
	justify-content: center;
	border-radius: var(--vf-radius-sm);
	background: var(--vf-surface);
	box-shadow: var(--vf-shadow-soft);
	color: var(--vf-accent);
}
.vf-pb-icon-box__icon svg { width: 55%; height: 55%; }
.vf-pb-icon-box__heading { margin: 0 0 4px; font-size: 1.05em; }
.vf-pb-icon-box__text { margin: 0; color: var(--vf-text-muted); }

/* Star Rating */
.vf-pb-rating { display: inline-flex; align-items: center; gap: 10px; }
.vf-pb-rating__stars {
	position: relative;
	display: inline-block;
	font-size: 20px;
	line-height: 1;
	letter-spacing: 2px;
	color: var(--vf-border);
}
.vf-pb-rating__stars::before { content: "★★★★★"; }
.vf-pb-rating__fill {
	position: absolute;
	top: 0;
	left: 0;
	height: 100%;
	overflow: hidden;
	white-space: nowrap;
	color: #f5a623;
}
.vf-pb-rating__fill::before { content: "★★★★★"; }
.vf-pb-rating__label { color: var(--vf-text-muted); font-size: 0.92em; }

/* Progress Bar */
.vf-pb-progress { width: 100%; }
.vf-pb-progress__label {
	display: flex;
	justify-content: space-between;
	margin-bottom: 6px;
	font-size: 0.9em;
	color: var(--vf-text-muted);
}
.vf-pb-progress__track {
	width: 100%;
	height: 10px;
	border-radius: 999px;
	background: var(--vf-border);
	overflow: hidden;
}
.vf-pb-progress__fill {
	height: 100%;
	width: 0;
	border-radius: 999px;
	background: var(--vf-accent);
	transition: width 1.1s cubic-bezier(0.16, 1, 0.3, 1);
}
.vf-pb-progress__fill.vf-pb-progress__fill--filled { width: var(--vf-progress-target, 0%); }

/* Counter */
.vf-pb-counter { text-align: center; }
.vf-pb-counter__number {
	font-family: var(--vf-font-display);
	font-weight: 700;
	font-size: clamp(28px, 4vw, 44px);
	line-height: 1;
	color: var(--vf-text);
	font-variant-numeric: tabular-nums;
}
.vf-pb-counter__label { margin-top: 6px; color: var(--vf-text-muted); }

/* Before / After image compare */
.vf-pb-compare {
	position: relative;
	overflow: hidden;
	border-radius: var(--vf-radius-md);
	cursor: ew-resize;
	touch-action: pan-y;
	user-select: none;
	line-height: 0;
}
.vf-pb-compare__after,
.vf-pb-compare__before { display: block; width: 100%; height: 100%; object-fit: cover; }
.vf-pb-compare__before-wrap {
	position: absolute;
	inset: 0;
	width: 50%;
	overflow: hidden;
}
.vf-pb-compare__before-wrap .vf-pb-compare__before { width: var(--vf-compare-width, 200%); max-width: none; }
.vf-pb-compare__handle {
	position: absolute;
	top: 0;
	bottom: 0;
	left: 50%;
	width: 3px;
	background: #fff;
	box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.15);
	transform: translateX(-50%);
}
.vf-pb-compare__handle::after {
	content: "";
	position: absolute;
	top: 50%;
	left: 50%;
	width: 36px;
	height: 36px;
	transform: translate(-50%, -50%);
	border-radius: 50%;
	background: #fff;
	box-shadow: 0 4px 14px rgba(0, 0, 0, 0.25);
}
.vf-pb-compare__label {
	position: absolute;
	bottom: 12px;
	padding: 4px 10px;
	border-radius: 999px;
	background: rgba(0, 0, 0, 0.55);
	color: #fff;
	font-size: 0.75em;
	text-transform: uppercase;
	letter-spacing: 0.05em;
}
.vf-pb-compare__label--before { left: 12px; }
.vf-pb-compare__label--after { right: 12px; }

/* Table */
.vf-pb-table {
	width: 100%;
	border-collapse: collapse;
	overflow-x: auto;
	display: block;
}
.vf-pb-table thead, .vf-pb-table tbody, .vf-pb-table tr { display: table; width: 100%; table-layout: fixed; }
.vf-pb-table th, .vf-pb-table td {
	padding: 10px 14px;
	text-align: left;
	border-bottom: 1px solid var(--vf-border);
}
.vf-pb-table thead th {
	font-family: var(--vf-font-display);
	font-weight: 700;
	background: var(--vf-surface);
}

/* Alert */
.vf-pb-alert {
	padding: 14px 18px;
	border-radius: var(--vf-radius-sm);
	border: 1px solid transparent;
	font-size: 0.95em;
}
.vf-pb-alert--info { background: rgba(59, 91, 253, 0.1); border-color: rgba(59, 91, 253, 0.25); color: var(--vf-accent); }
.vf-pb-alert--success { background: rgba(53, 196, 139, 0.12); border-color: rgba(53, 196, 139, 0.3); color: #1f8f5f; }
.vf-pb-alert--warning { background: rgba(255, 178, 56, 0.14); border-color: rgba(255, 178, 56, 0.35); color: #8a5a00; }
.vf-pb-alert--error { background: rgba(255, 107, 87, 0.12); border-color: rgba(255, 107, 87, 0.3); color: #c23b28; }

/* Breadcrumbs */
.vf-pb-breadcrumbs { font-size: 0.9em; color: var(--vf-text-muted); }
.vf-pb-breadcrumbs a { color: inherit; text-decoration: none; }
.vf-pb-breadcrumbs a:hover { color: var(--vf-accent); }
.vf-pb-breadcrumbs__sep { margin: 0 8px; opacity: 0.5; }
.vf-pb-breadcrumbs [aria-current="page"] { color: var(--vf-text); }

/* Author Box */
.vf-pb-author-box {
	display: flex;
	gap: 16px;
	align-items: flex-start;
	padding: 20px;
	border-radius: var(--vf-radius-md);
	background: var(--vf-surface);
	box-shadow: var(--vf-shadow-soft);
}
.vf-pb-author-box__avatar { border-radius: 50%; flex: none; }
.vf-pb-author-box__name { font-family: var(--vf-font-display); font-weight: 700; margin-bottom: 4px; }
.vf-pb-author-box__bio { margin: 0; color: var(--vf-text-muted); }

/* Nav Menu embed */
.vf-pb-nav-menu ul { list-style: none; margin: 0; padding: 0; }
.vf-pb-nav-menu--horizontal ul { display: flex; flex-wrap: wrap; gap: 24px; }
.vf-pb-nav-menu--vertical ul { display: flex; flex-direction: column; gap: 10px; }
.vf-pb-nav-menu a { color: inherit; text-decoration: none; }
.vf-pb-nav-menu a:hover { color: var(--vf-accent); }

/* Search Form embed */
.vf-pb-search-form .search-form { display: flex; gap: 8px; }
.vf-pb-search-form input.search-field {
	flex: 1;
	padding: 10px 14px;
	border: 1px solid var(--vf-border);
	border-radius: var(--vf-radius-sm);
	background: var(--vf-surface);
	color: var(--vf-text);
}
.vf-pb-search-form input.search-submit {
	padding: 10px 18px;
	border: none;
	border-radius: var(--vf-radius-sm);
	background: var(--vf-accent);
	color: var(--vf-accent-ink);
	cursor: pointer;
}

/* Post Navigation */
.vf-pb-post-nav {
	display: flex;
	justify-content: space-between;
	gap: 16px;
	padding-top: 16px;
	border-top: 1px solid var(--vf-border);
}
.vf-pb-post-nav a { color: inherit; text-decoration: none; }
.vf-pb-post-nav a:hover { color: var(--vf-accent); }
.vf-pb-post-nav__next { text-align: right; margin-left: auto; }

/* Related Posts */
.vf-pb-related-posts {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
	gap: 20px;
}
.vf-pb-related-posts__item { color: inherit; text-decoration: none; display: block; }
.vf-pb-related-posts__thumb { width: 100%; height: auto; border-radius: var(--vf-radius-sm); margin-bottom: 8px; }
.vf-pb-related-posts__title { font-weight: 600; }

@media (prefers-reduced-motion: reduce) {
	.vf-pb-progress__fill { transition: none; }
}

/* Company Logo (Logo.dev) */
.vf-pb-company-logo {
	display: inline-flex;
	align-items: center;
	justify-content: center;
}
.vf-pb-company-logo__img {
	display: block;
	max-width: 100%;
	height: auto;
}
.vf-logo-dev-attribution {
	margin: 8px 0 0;
	font-size: 11px;
	text-align: center;
	opacity: 0.6;
}
.vf-logo-dev-attribution a { color: inherit; }

/* AdSense in-article (fluid) unit (6.6) — min-height, not height, so the
   box can grow if a taller creative is served but never collapses to
   zero and snaps the page while the ad request is in flight. */
.vf-ads-slot--in-article {
	min-height: 280px;
	margin: 2em auto;
	text-align: center;
}
@media (max-width: 960px) {
	.vf-ads-slot--in-article { min-height: 120px; }
}
