/*
 * Ruppert components — the small layer Elementor settings cannot express.
 * Everything else (type, colour, spacing, layout) lives in widget settings bound to the kit.
 */

/* ---------- Header: soft shadow once Pro's sticky header detaches ---------- */
.site-header.elementor-sticky--effects {
	box-shadow: 0 8px 24px -16px rgba(7, 21, 64, .35);
}

/* ---------- Decorative "RUPPERT" wordmark — an SVG background (Elementor reserves the container before-layer for overlays) ---------- */
.ghost-wordmark {
	background-image: url(../img/wordmark-navy.svg);
	background-repeat: no-repeat;
	background-position: left 1rem center;
	background-size: auto 16.25rem;
}

.ghost-wordmark--light {
	background-image: url(../img/wordmark-white.svg);
	background-position: center;
	background-size: auto 18.75rem;
}

@media (max-width: 1024px) {
	.ghost-wordmark:not(.ghost-wordmark--light) {
		background-image: none;
	}

	.ghost-wordmark--light {
		background-size: auto 10rem;
	}
}

/* ---------- Hero portrait: gold-edged offset block that settles after the photo lands ---------- */
.hero-portrait {
	--frame: .5rem;
	box-shadow: var(--frame) var(--frame) 0 0 var(--ruppert-stone), var(--frame) var(--frame) 0 1px rgba(191, 161, 69, .5);
	/* X19: `!important` — once Elementor's own scroll-reveal JS adds .animated.fadeInUp (build_home.py builds this
	   with anim=200), tokens.css's `.elementor-element.animated.fadeInUp{animation-name:ruppert-rise}` (3 classes)
	   otherwise outranks this 1-class selector and silently replaces the frame reveal with the generic fade —
	   it never visibly played. The reduced-motion override below is `!important` too, to still win against this. */
	animation: ruppert-frame-in .9s cubic-bezier(.16, 1, .3, 1) .6s both !important;
}

@media (min-width: 640px) {
	.hero-portrait {
		--frame: 1rem;
	}
}

@keyframes ruppert-frame-in {
	from {
		box-shadow: 0 0 0 0 rgba(243, 241, 236, 0), 0 0 0 0 rgba(191, 161, 69, 0);
	}
	to {
		box-shadow: var(--frame) var(--frame) 0 0 var(--ruppert-stone), var(--frame) var(--frame) 0 1px rgba(191, 161, 69, .5);
	}
}

/* ---------- Practice areas: rows + the desktop preview panel ---------- */
.practice-row {
	transition: background-color .2s cubic-bezier(.16, 1, .3, 1), box-shadow .2s cubic-bezier(.16, 1, .3, 1);
}

/* X3: gated — an ungated :hover "sticks" after a tap on touch (no pointer-leave event to clear it). */
@media (hover: hover) {
	.practice-row:hover {
		background-color: var(--ruppert-cream);
	}
}

@media (min-width: 1025px) {
	.practice-row.is-active {
		background-color: var(--ruppert-cream);
		box-shadow: inset 2px 0 0 var(--ruppert-gold); /* the row's hairline border owns border-width, so draw the rule */
	}
}

.practice-row {
	position: relative;
}

.practice-row::after {
	content: '\2197';
	position: absolute;
	top: 1.5rem;
	right: 1rem;
	font-size: 1.25rem;
	line-height: 1;
	color: var(--ruppert-slate);
	transition: transform .2s cubic-bezier(.16, 1, .3, 1), color .2s cubic-bezier(.16, 1, .3, 1);
}

@media (hover: hover) {
	.practice-row:hover::after {
		color: var(--ruppert-navy);
		transform: translate(3px, -3px);
	}
}

.practice-row__num {
	display: inline-block;
	width: 2.75rem;
	font-family: var(--ruppert-font-body);
	font-size: .75rem;
	font-weight: 600;
	letter-spacing: .14em;
	vertical-align: middle;
	color: var(--ruppert-gold-text);
}

@media (max-width: 767px) {
	.practice-row__num {
		display: none;
	}
}

/* The long description + image live inside each row (editable in Elementor) and feed the preview panel. */
.practice-row__detail,
.practice-row__image {
	display: none !important;
}

.practice-preview__image img,
.practice-preview__text {
	transition: opacity .25s cubic-bezier(.16, 1, .3, 1);
}

.practice-preview.is-swapping .practice-preview__image img,
.practice-preview.is-swapping .practice-preview__text {
	opacity: 0;
}

/* ---------- Cards: lift on hover (pointer devices only) ---------- */
@media (hover: hover) {
	.attorney-card {
		transition: transform .2s cubic-bezier(.16, 1, .3, 1), box-shadow .2s cubic-bezier(.16, 1, .3, 1);
	}

	.attorney-card:hover {
		transform: translateY(-4px);
		box-shadow: 0 16px 32px -16px rgba(7, 21, 64, .25);
	}

	.attorney-card img,
	.blog-lead img {
		transition: transform .7s cubic-bezier(.16, 1, .3, 1);
	}

	.attorney-card:hover img,
	.blog-lead:hover img {
		transform: scale(1.04);
	}
}

.attorney-card .elementor-widget-image,
.blog-lead .elementor-widget-image {
	overflow: hidden;
}

/* ---------- Blog index: one hairline per row, arrow nudge ---------- */
.blog-index__item {
	border-bottom: 1px solid rgba(95, 100, 106, .15);
}

.blog-index__item a {
	display: flex;
	justify-content: space-between;
	gap: 1.5rem;
	padding: 1rem 0;
}

.blog-index__item a::after {
	content: '→';
	color: var(--ruppert-slate);
	transition: transform .2s cubic-bezier(.16, 1, .3, 1), color .2s cubic-bezier(.16, 1, .3, 1);
}

@media (hover: hover) {
	.blog-index__item a:hover::after {
		color: var(--ruppert-navy);
		transform: translateX(4px);
	}
}

/* ---------- Office maps: quiet until hovered ---------- */
.office-map iframe {
	filter: grayscale(1) contrast(.95);
	transition: filter .5s cubic-bezier(.16, 1, .3, 1);
}

@media (hover: hover) {
	.office-map:hover iframe {
		filter: none;
	}
}

/* ---------- Text links that sit in body copy ---------- */
.elementor .link-underline a {
	text-decoration: underline;
	text-decoration-color: rgba(191, 161, 69, .6);
	text-underline-offset: .25em;
}

/* ---------- Buttons: build_home.py's button() sets only the resting-state colour now (X3/X4) — Elementor's OWN
   hover-colour controls render an ungated `:hover` rule that "sticks" after a tap (no pointer-leave event to
   clear it), so hover lives here instead, gated to real hover devices. ---------- */
/* R1: Elementor's own per-widget NORMAL-state rule — `.elementor-{ID} .elementor-element.elementor-element-{elID}
   .elementor-button` — chains 4 classes, (0,4,0); this hover rule was only 3, (0,3,0), so the normal-state
   background/color/border-color won even while :hover was active (specificity decides when neither side is
   gated — hover vs. normal is not itself a tiebreak). `!important` here is safe: Elementor's normal-state rule
   is never `!important` (verified live, diag-cascade), so it's the only push needed to win. */
@media (hover: hover) {
	.btn-primary .elementor-button:hover {
		background-color: var(--ruppert-gold) !important;
		color: var(--ruppert-navy) !important;
	}

	.btn-gold .elementor-button:hover {
		background-color: var(--ruppert-parchment) !important;
		color: var(--ruppert-navy) !important;
	}

	.btn-ghost .elementor-button:hover {
		border-color: var(--ruppert-navy) !important;
	}
}

/* X4: the old hover (a light "stone" fill) was invisible on the ghost/phone button's own stone-family surfaces —
   a stronger border reads on any of them; 56px explicit, though the existing padding already lands close to it. */
.btn-ghost .elementor-button {
	min-height: 56px;
}

/* ---------- Heading links (F4): Elementor's heading-widget links render `color: inherit`, which — like the
   `.ruppert-dark` text-link override just above — has enough specificity to beat the base `:where(a:hover)`
   rule (tokens.css), so these showed no hover state at all: phone numbers, office names, "Directions",
   "View profile", "All articles", "Read more", the footer email and Disclaimer. `!important` here is not
   optional insurance — `color: inherit` is a real, non-zero-specificity rule these must actually outrank. ---------- */
@media (hover: hover) {
	.elementor-widget-heading a:hover {
		color: var(--ruppert-gold-text) !important;
	}

	.ruppert-dark .elementor-widget-heading a:hover,
	.ruppert-dark .elementor-widget-text-editor a:hover {
		color: var(--ruppert-gold) !important;
	}
}

/* Arrow links: the shared class build_home.py/designed.py/build_pages.py put on every "…→" heading link
   (View profile, Family law overview, All articles, Directions, Learn more) — hover colour comes from the
   generic heading-link rule above; this adds the arrow-glyph nudge and, on touch, a taller invisible tap
   target (padding on an inline <a> does not reflow anything around it — the target stays display: inline). */
.arrow-link .elementor-heading-title span[aria-hidden] {
	display: inline-block;
	transition: transform .2s cubic-bezier(.16, 1, .3, 1);
}

@media (hover: hover) {
	.arrow-link .elementor-heading-title a:hover span[aria-hidden] {
		transform: translateX(3px);
	}
}

@media (pointer: coarse) {
	.arrow-link .elementor-heading-title a {
		padding-block: 1rem;
	}
}

/* X18: touch targets under 44px tall, sized only by their own text/icon — phone numbers, the footer email,
   breadcrumb crumbs, office names, post-card "Read more", body-copy links. Same technique: padding on an inline
   element does not reflow anything around it (measured offenders: sweep.md, 2026-09-28 first pass). */
@media (pointer: coarse) {
	.elementor-heading-title a,
	.elementor-widget-text-editor a,
	.ruppert-prose a,
	.elementor-post__read-more {
		padding-block: 1rem;
	}

	.site-logo a {
		padding-block: .25rem;
	}
}

.footer-social .elementor-social-icon {
	min-width: 44px;
	min-height: 44px;
}

/* Round 3: the social-icons widget's own hover_primary_color/hover_secondary_color/hover_border_color controls
   were never set here (build_chrome.py, checked) — nothing to remove there. The sticky hover instead comes
   from ELEMENTOR CORE's own base widget CSS, completely ungated and outside any per-instance setting:
   `.elementor-social-icon:hover{color:#fff;opacity:.9}` (widget-social-icons.min.css) on top of
   `.elementor-icon{color:#69727d}` (frontend.min.css) for the resting state — confirmed live via
   CSS.getMatchedStylesForNode (scratchpad/fix3/recon.mjs): the anchor's OWN `color`/`opacity` are what move
   (the glyph's paint is `fill`, pinned separately by icon_secondary_color, and never changes). "Sticks" after a
   tap on touch (no pointer-leave event ever clears it): device-sweep sticky-hover, 2026-09-28, "element color:
   rgb(105, 114, 125) → rgb(255, 255, 255)". Pin the resting colour/opacity always — same (0,2,0) specificity as
   the ungated rule, later in source, so this wins outright even without `!important`; kept anyway to match this
   file's convention for the same class of fight — then restore the hover change only for real hover devices. */
.footer-social .elementor-social-icon {
	color: #69727d !important;
	opacity: 1 !important;
}

@media (hover: hover) {
	.footer-social .elementor-social-icon:hover {
		color: #fff !important;
		opacity: .9 !important;
	}
}

/* ---------- Lead form (Fluent Forms) in the brand ---------- */
.lead-form .ff-el-input--label label {
	font-family: var(--ruppert-font-body);
	font-size: .75rem;
	font-weight: 600;
	letter-spacing: .16em;
	text-transform: uppercase;
	color: var(--ruppert-navy);
}

.lead-form .ff-el-form-control {
	min-height: 3rem;
	border: 1px solid var(--ruppert-slate);
	border-radius: 0;
	background-color: #fff;
	color: var(--ruppert-ink);
	font-family: var(--ruppert-font-body);
	font-size: 1rem;
}

.lead-form .ff-el-form-control:focus {
	border-color: var(--ruppert-navy);
	box-shadow: 0 0 0 1px var(--ruppert-navy);
}

.lead-form .ff-el-is-error .ff-el-form-control {
	border-color: #9B1C1C;
	box-shadow: 0 0 0 1px #9B1C1C;
}

/* F3: Fluent's own error text — `.text-danger{color:var(--fluentform-danger)}`, `--fluentform-danger:#f56c6c`,
   `.ff-el-is-error .text-danger{font-size:12px}` (fluent-forms-public.css, source-verified) — is 2.8:1 on this
   page's parchment/white, below the 4.5:1 text minimum. Neither rule carries `!important`, so a plain, more
   specific selector is enough to win; #9B1C1C is the same corrected red already used on the field border above. */
.lead-form .text-danger {
	color: #9B1C1C;
	font-size: .8125rem; /* 13px, up from the plugin's 12px */
}

.lead-form .ff-btn-submit {
	min-height: 3.5rem;
	padding: 0 2.5rem;
	border: 0;
	border-radius: 0;
	background-color: var(--ruppert-navy);
	color: #fff;
	font-family: var(--ruppert-font-body);
	font-size: .8125rem;
	font-weight: 600;
	letter-spacing: .14em;
	text-transform: uppercase;
	opacity: 1;
	/* R2 follow-up: `transform` used to be eased here too (.2s), same curve as color/background. Its `:active`
	   press effect (translateY(1px), below) releases the instant touchend clears :active — but the RELEASE was
	   still eased, and device-sweep's sticky-hover check (tap, sample at 300ms, tap elsewhere, resample) caught
	   a sub-pixel residual (matrix(1,0,0,1,0,0.0061288) — six-thousandths of a CSS pixel, invisible) mid-release
	   often enough to flag it on every page with this form. Colour/background are unaffected either way (verified:
	   the captured a/b/d signatures never differed on color, background-color, or anything but transform) — this
	   was never a real stuck HOVER colour, just an easing tail colliding with the check's fixed sample delay.
	   Dropping `transform` from the transition makes the 1px press/release instant (imperceptible at 1px) and
	   removes the mid-flight value a sampler could ever catch, at any delay. */
	transition: background-color .2s cubic-bezier(.16, 1, .3, 1), color .2s cubic-bezier(.16, 1, .3, 1);
}

/* F2: Fluent's OWN `.ff-default .ff_btn_style:focus,:hover{opacity:.8;outline:0}` (fluentform-public-default.css,
   source-verified) otherwise wins on every state — real specificity beats the site's zero-specificity
   `:where(:focus-visible)` ring, and fades the button whether focused, hovered or both. This stylesheet already
   enqueues after Fluent's (functions.php, priority 20), so plain specificity should already favour these; the
   opacity is also `!important` as a belt-and-braces override on an accessibility-critical rule. */
.lead-form .ff-btn-submit:focus,
.lead-form .ff-btn-submit:hover,
.lead-form .ff-btn-submit:active {
	opacity: 1 !important;
}

.lead-form .ff-btn-submit:focus-visible {
	outline: 2px solid var(--ruppert-navy);
	outline-offset: 3px;
}

/* R2: build_home.py's fluent_form() used to also set the widget's OWN `form_submit_button_*_hover` controls —
   those render UNGATED (no `hover: hover` gate) and "stick" after a tap on touch (no pointer-leave event ever
   clears :hover there). Those controls are gone now (normal-state colours only); hover lives here instead,
   gated to real hover devices. `!important` beats the normal-state colour rule Elementor still renders from
   `form_submit_button_bg_color_normal`/`_text_color_normal` (`{{WRAPPER}} .fluentform-widget-wrapper
   .ff-el-group .ff-btn-submit`, 6 chained classes — (0,6,0), well above this selector's (0,3,0)). */
@media (hover: hover) {
	.lead-form .ff-btn-submit:hover {
		background-color: var(--ruppert-gold) !important;
		color: var(--ruppert-navy) !important;
	}

	/* Round 3: the 1px press effect below was ungated — a tap on touch sets :active and, with no pointer-leave
	   event to ever clear it, the translateY(1px) offset stuck until the next tap elsewhere (device-sweep
	   sticky-hover, 2026-09-28: "transform: none → matrix(1,0,0,1,0,1)"). Real pointer devices only: a mouse
	   click still presses and releases instantly (transform is not in the transition list above, R2 follow-up);
	   touch never gets the offset at all now, so it can never stay. */
	.lead-form .ff-btn-submit:active {
		transform: translateY(1px);
	}
}

@media (prefers-reduced-motion: reduce) {
	.hero-portrait {
		animation: none !important; /* must also win against the !important rule above (X19) */
	}
}

/* ---------- Small layout helpers ---------- */
.nowrap .elementor-heading-title {
	white-space: nowrap;
}

.band-cta {
	margin-top: 1.5rem;
}

/* ---------- Footer menus (Pro nav-menu, vertical) ---------- */
.footer-menu .elementor-nav-menu--main .elementor-item {
	min-height: 44px; /* touch target */
	padding: 0;
	display: flex;
	align-items: center;
}

.footer-menu--two-col .elementor-nav-menu--main > .elementor-nav-menu {
	display: block;
	columns: 2;
	column-gap: 1.5rem;
}

.footer-menu--two-col .elementor-nav-menu--main > .elementor-nav-menu > li {
	break-inside: avoid;
}

/* X3: build_chrome.py's FOOTER_COLORS no longer sets `color_menu_item_hover` (Elementor's own control there was
   an ungated `:hover` rule that "sticks" after a tap) — style it here instead, gated to real hover devices. */
@media (hover: hover) {
	.footer-menu .elementor-item:hover {
		color: var(--ruppert-white) !important;
	}
}

.footer-addresses strong {
	color: #fff;
	font-weight: 600;
}

/* ---------- Long-form content: inner-page text widgets and blog post bodies ---------- */
.ruppert-prose > :first-child {
	margin-top: 0;
}

.ruppert-prose > :last-child {
	margin-bottom: 0;
}

.ruppert-prose :is(p, ul, ol, blockquote, table, figure) {
	margin: 0 0 1.25em;
}

.ruppert-prose :is(ul, ol) {
	padding-left: 1.25em;
}

.ruppert-prose li {
	margin-bottom: .5em;
	padding-left: .25em;
}

.ruppert-prose li::marker {
	color: var(--ruppert-gold-text);
}

.ruppert-prose ol > li::marker {
	font-weight: 600;
}

.ruppert-prose strong {
	color: var(--ruppert-ink);
	font-weight: 600;
}

.ruppert-prose a {
	color: var(--ruppert-navy);
	text-decoration: underline;
	text-decoration-color: rgba(191, 161, 69, .6);
	text-decoration-thickness: 1px;
	text-underline-offset: .2em;
	transition: color .2s, text-decoration-color .2s;
}

@media (hover: hover) {
	.ruppert-prose a:hover {
		color: var(--ruppert-gold-text);
		text-decoration-color: currentColor;
	}
}

.ruppert-prose :is(h2, h3, h4) {
	margin: 2em 0 .6em;
	color: var(--ruppert-navy);
	font-family: var(--ruppert-font-head);
	font-weight: 400;
	line-height: 1.2;
}

.ruppert-prose h2 {
	font-size: clamp(1.625rem, 1.3rem + 1.2vw, 2rem);
}

.ruppert-prose h3 {
	font-size: clamp(1.25rem, 1.1rem + .6vw, 1.5rem);
}

.ruppert-prose h4 {
	font-family: var(--ruppert-font-body);
	font-size: 1.0625rem;
	font-weight: 600;
}

.ruppert-prose blockquote {
	padding: .25em 0 .25em 1.25em;
	border-left: 2px solid var(--ruppert-gold);
	color: var(--ruppert-navy);
	font-family: var(--ruppert-font-head);
	font-size: 1.25rem;
	font-style: italic;
	line-height: 1.5;
}

.ruppert-prose img {
	max-width: 100%;
	height: auto;
}

.ruppert-prose figcaption {
	margin-top: .5em;
	color: var(--ruppert-slate);
	font-size: .875rem;
}

.ruppert-prose table {
	width: 100%;
	border-collapse: collapse;
	font-size: .9375rem;
}

.ruppert-prose :is(th, td) {
	padding: .75em .5em;
	border-bottom: 1px solid var(--ruppert-mist);
	text-align: left;
	vertical-align: top;
}

.ruppert-prose th {
	color: var(--ruppert-navy);
	font-weight: 600;
}

.ruppert-prose iframe {
	width: 100%;
	max-width: 100%;
	height: auto;
	aspect-ratio: 16 / 9;
}

/* ---------- Breadcrumbs (text widget on inner-page heroes) ---------- */
.breadcrumbs {
	color: var(--ruppert-slate);
	font-size: .875rem;
}

.breadcrumbs a {
	color: inherit;
	text-decoration: none;
}

@media (hover: hover) {
	.breadcrumbs a:hover {
		color: var(--ruppert-navy);
		text-decoration: underline;
	}
}

.breadcrumbs .sep {
	margin: 0 .5em;
	color: var(--ruppert-gold-text);
}

/* ---------- Link columns (e.g. the practice-area list carried over from Wix's sidebar) ---------- */
.link-columns ul {
	margin: 0;
	padding: 0;
	list-style: none;
	columns: 3 15rem;
	column-gap: 2rem;
}

.link-columns li {
	border-top: 1px solid var(--ruppert-mist);
	break-inside: avoid;
}

.link-columns a {
	display: block;
	padding: .75rem 0;
	color: var(--ruppert-navy);
	text-decoration: none;
	transition: color .2s;
}

@media (hover: hover) {
	.link-columns a:hover {
		color: var(--ruppert-gold-text);
	}
}

/* X1: "Book a Consultation" (menu item 199) mirrors the live Wix site's own top-level nav item, mobile-only —
   desktop already has the separate header CTA button (site-header .btn-primary) and the phone icon. Elementor's
   nav-menu widget renders the item TWICE (source-verified, live DOM): once inside .elementor-nav-menu--main (the
   horizontal desktop bar, itself only ever shown ≥1025px) and once inside .elementor-nav-menu--dropdown (the
   burger panel) — hiding the --main copy specifically leaves the --dropdown copy alone. */
.elementor-nav-menu--main .mobile-menu-cta {
	display: none;
}

.elementor-nav-menu--dropdown .mobile-menu-cta {
	margin-block-start: .5rem;
	border-top: 1px solid rgba(191, 161, 69, .35);
}

/* Elementor post-84 paints every dropdown link with
   `.elementor-84 .elementor-element.elementor-element-1a00110 .elementor-nav-menu--dropdown a`
   (0,4,1), which beats the class rule above on `color` only. The navy fill still applied, so the
   label was ink-on-navy. `!important` matches the other header overrides in this file: Elementor's
   normal and hover colour rules are not `!important`. Hover stays inside `(hover: hover)` so a tap
   does not leave the gold state stuck. */
.elementor-nav-menu--dropdown .mobile-menu-cta > .elementor-item,
.elementor-nav-menu--dropdown .mobile-menu-cta > .elementor-item:focus,
.elementor-nav-menu--dropdown .mobile-menu-cta > .elementor-item:focus-visible {
	justify-content: center;
	margin-top: .5rem;
	background-color: var(--ruppert-navy) !important;
	color: #fff !important;
	fill: #fff;
	font-weight: 600;
	text-transform: uppercase;
	letter-spacing: .08em;
}

@media (hover: hover) {
	.elementor-nav-menu--dropdown .mobile-menu-cta > .elementor-item:hover {
		background-color: var(--ruppert-gold) !important;
		color: var(--ruppert-navy) !important;
	}
}

/* X1: the overview items ("Attorney Profiles" repeated as its own first child link, "Family Law" likewise) read
   as a lighter-weight entry than their sibling practice/attorney links. */
.menu-overview-item > .elementor-item {
	font-style: italic;
	opacity: .8;
}

/* X2: desktop dropdowns had a border (build_chrome.py's dropdown_border_* keys) but no shadow to lift them off
   the page; Family Law's 11 items (menu item id 41, confirmed via `wp menu item list` — a stable selector, not
   an Elementor-generated one) read as an overly tall single column next to Attorney Profiles' 7. */
.elementor-nav-menu--dropdown {
	box-shadow: 0 16px 32px -12px rgba(7, 21, 64, .25);
}

.menu-item-41 > .elementor-nav-menu--dropdown {
	columns: 2;
	column-gap: 0;
	width: max-content !important;
	max-width: 34rem;
}

.menu-item-41 > .elementor-nav-menu--dropdown > li {
	break-inside: avoid;
}

/* ---------- Header touch targets (44px): Pro hard-codes the burger's padding (.25em); the call icon is a bare glyph ---------- */
.site-nav .elementor-menu-toggle {
	min-width: 44px;
	min-height: 44px;
	justify-content: center;
}

.header-phone-icon .elementor-icon {
	padding: 11px;
}

/* X16: neither had a hover colour at all. `!important`: both icons' normal-state colour is a bound kit global
   (build_chrome.py `toggle_color`; build_home.py `icon()`'s `primary_color`), which Elementor renders at a
   per-instance specificity these simple hover selectors are not guaranteed to outrank. */
@media (hover: hover) {
	.site-nav .elementor-menu-toggle:hover {
		color: var(--ruppert-gold-text) !important;
	}

	.header-phone-icon:hover .elementor-icon,
	.header-phone-icon:hover .elementor-icon i {
		color: var(--ruppert-gold-text) !important;
	}
}

/* ---------- FAQ rows: the nested accordion has no title-padding control ---------- */
.faq-accordion .e-n-accordion-item-title {
	padding-block: 1.5rem;
}

@media (max-width: 767px) {
	.faq-accordion .e-n-accordion-item-title {
		padding-block: 1.25rem;
	}
}

/* X17/C7: no hover affordance at all on a clickable row; the title/icon colour Elementor's OWN per-state
   controls used to add for 'hover' is gone from the builders (build_home.py faq_widget(), build_pages.py
   faq_widget_generic()) — same ungated-rule problem as X3 — so both the tint and the colour live here now,
   gated to real hover devices. */
@media (hover: hover) {
	.faq-accordion .e-n-accordion-item-title:hover {
		background-color: var(--ruppert-stone) !important;
		color: var(--ruppert-gold-text) !important;
	}
}

/* C7: the FAQ answer had no measure cap at all. */
.faq-accordion__answer {
	max-width: 37rem;
}

/* ---------- Practice rows: the lede lines up with the title, not the number ---------- */
@media (min-width: 768px) {
	.practice-row .elementor-widget-text-editor {
		padding-left: 2.75rem;
	}
}

/* ---------- Contact panel rows: a small label above each value, one link per row ---------- */
.contact-item__label {
	display: block;
	margin-bottom: .25rem;
	color: var(--ruppert-mist);
	font-family: var(--ruppert-font-body);
	font-size: .75rem;
	font-weight: 600;
	letter-spacing: .16em;
	line-height: 1.3;
	text-transform: uppercase;
}

.contact-item a {
	overflow-wrap: anywhere;
}

/* ---------- Team caption: over the photo it fades in (gradient); on a phone it sits below the photo, so it is solid
   navy (the gradient over cream failed AA). Elementor's gradient has no mobile rule unless the angle has one; five
   classes outrank its instance rule (four) without !important. ---------- */
@media (max-width: 767px) {
	.elementor .elementor-element.e-con.team-caption:not(.elementor-motion-effects-element-type-background) {
		background: var(--ruppert-navy);
	}
}

/* ==========================================================================
   Inner pages — the Stitch inner-page designs (layout from Stitch, copy from the live site)
   ========================================================================== */

/* Breadcrumb in the designs' label style */
.breadcrumbs--label {
	font-size: .75rem;
	font-weight: 500;
	letter-spacing: .12em;
	text-transform: uppercase;
}

.breadcrumbs--label [aria-current] {
	color: var(--ruppert-navy);
	font-weight: 600;
}

/* Chip: a small bordered label with a gold dot */
.chip .elementor-heading-title {
	display: inline-flex;
	align-items: center;
	gap: .625rem;
	padding: .375rem .75rem;
	border: 1px solid rgba(191, 161, 69, .28);
	background: var(--ruppert-white);
}

.chip .elementor-heading-title::before {
	content: "";
	flex: none;
	width: 6px;
	height: 6px;
	border-radius: 50%;
	background: var(--ruppert-gold);
}

/* Framed photo card; its caption sits over the photo's lower edge */
.image-card {
	position: relative; /* C3: the positioning context for .portrait-caption below */
	box-shadow: 0 12px 32px -12px rgba(7, 21, 64, .18);
}

.image-card__caption {
	position: relative;
	z-index: 1;
}

/* Practice cards: a quiet lift on hover; "Learn more" pinned to the bottom */
.practice-card {
	transition: transform .3s cubic-bezier(.16, 1, .3, 1), box-shadow .3s cubic-bezier(.16, 1, .3, 1), border-color .3s;
}

.practice-card__more {
	margin-top: auto;
}

/* C10: same bottom-pin as .practice-card__more above, for the other two card CTAs whose card height varies with
   its own content (a bio's length; how many address/sublink lines an office has). */
.attorney-card__cta,
.office-card__cta {
	margin-top: auto;
}

@media (hover: hover) {
	.practice-card:hover {
		transform: translateY(-2px);
		border-color: rgba(191, 161, 69, .6);
		box-shadow: 0 16px 32px -16px rgba(7, 21, 64, .2);
	}
}

/* Contact and intake cards */
.contact-card,
.intake-card {
	box-shadow: 0 12px 32px -16px rgba(7, 21, 64, .12);
}

.contact-card__offices strong {
	color: var(--ruppert-navy);
	font-weight: 600;
}

/* Portrait caption laid over the photo (one heading). C3: absolutely positioned against .image-card's bottom
   edge instead of the old fixed -112px negative margin, which assumed the caption's own rendered height and
   was wrong the moment a caption wrapped to a different number of lines (Josh's shorter portrait frame). */
.portrait-caption {
	position: absolute;
	inset: auto 12px 12px;
	z-index: 1;
}

.portrait-caption__role {
	display: block;
	margin-bottom: .375rem;
	color: var(--ruppert-gold);
	font-family: var(--ruppert-font-body);
	font-size: .75rem;
	font-weight: 600;
	letter-spacing: .16em;
	line-height: 1.3;
	text-transform: uppercase;
}

/* Credential lists (attorney profiles) */
.cred-list ul {
	display: grid;
	gap: .5rem;
	margin: 0;
	padding: 0;
	list-style: none;
}

.cred-list li {
	margin: 0;
	padding: .875rem 1rem;
	border-left: 2px solid var(--ruppert-gold);
	background: var(--ruppert-parchment);
	color: var(--ruppert-ink);
}

.cred-list--education li strong {
	display: block;
	color: var(--ruppert-navy);
	font-family: var(--ruppert-font-head);
	font-size: 1.125rem;
	font-weight: 500;
}

.cred-list--education li span {
	display: block;
	margin-top: .25rem;
	color: var(--ruppert-slate);
	font-size: .875rem;
}

/* ---------- Dark heroes (the article + blog-index designs): breadcrumb, chip and meta on navy ---------- */
.ruppert-dark .breadcrumbs {
	color: var(--ruppert-mist);
}

.ruppert-dark .breadcrumbs .sep,
.ruppert-dark .breadcrumbs [aria-current] {
	color: var(--ruppert-gold);
}

/* X15: the light-mode hover above (`.breadcrumbs a:hover{color:navy}`) would be near-invisible on this navy bg. */
@media (hover: hover) {
	.ruppert-dark .breadcrumbs a:hover {
		color: var(--ruppert-gold);
		text-decoration: underline;
	}
}

.ruppert-dark .chip .elementor-heading-title {
	border-color: rgba(191, 161, 69, .5);
	background: transparent;
	color: var(--ruppert-gold);
}

/* ---------- Blog: framed featured image, category pills, post cards ---------- */
.post-featured img {
	box-shadow: 0 12px 32px -12px rgba(7, 21, 64, .18);
}

.category-pills p {
	display: flex;
	flex-wrap: wrap;
	gap: .5rem;
	margin: 0;
}

/* X13: at 390 the 8 pills wrapped across several lines; one horizontally scrolling row reads as filter chips
   instead — the whole point of a pill row on a narrow screen. */
@media (max-width: 767px) {
	.category-pills p {
		flex-wrap: nowrap;
		overflow-x: auto;
		padding-bottom: .25rem; /* clears the scrollbar some browsers draw inline */
		-webkit-overflow-scrolling: touch;
	}

	.category-pills a {
		flex: none;
	}
}

.category-pills a {
	display: inline-flex;
	align-items: center;
	min-height: 44px;
	padding: 0 1rem;
	border: 1px solid rgba(191, 161, 69, .35);
	background: var(--ruppert-white);
	color: var(--ruppert-navy);
	font-size: .75rem;
	font-weight: 600;
	letter-spacing: .12em;
	text-decoration: none;
	text-transform: uppercase;
	transition: background-color .2s, color .2s, border-color .2s;
}

.category-pills a[aria-current="page"] {
	border-color: var(--ruppert-navy);
	background: var(--ruppert-navy);
	color: var(--ruppert-white);
}

/* X3: split from the current-page state above — that is a persistent selector state, not a hover effect, and
   must stay ungated; only :hover needs the pointer-capability gate. */
@media (hover: hover) {
	.category-pills a:hover {
		border-color: var(--ruppert-navy);
		background: var(--ruppert-navy);
		color: var(--ruppert-white);
	}
}

.elementor .designed-posts .elementor-post__card {
	position: relative; /* X10: containing block for the title's stretched-link overlay below */
	border: 1px solid rgba(191, 161, 69, .28);
	border-radius: 0;
	background: var(--ruppert-white);
	box-shadow: none;
	transition: transform .3s cubic-bezier(.16, 1, .3, 1), box-shadow .3s cubic-bezier(.16, 1, .3, 1);
}

/* X10: the title link now covers the whole card (the classic "stretched link" — ::after at inset:0), so a tap
   anywhere on the card opens the post, not just the title text. The category badge stays independently
   clickable to its own (different) URL by sitting above the overlay. */
.elementor .designed-posts .elementor-post__title a {
	position: relative;
}

.elementor .designed-posts .elementor-post__title a::after {
	content: '';
	position: absolute;
	inset: 0;
}

.elementor .designed-posts .elementor-post__badge {
	position: relative;
	z-index: 1;
}

@media (hover: hover) {
	.elementor .designed-posts .elementor-post__title a:hover {
		color: var(--ruppert-gold-text);
	}
}

.elementor .designed-posts .elementor-post__title,
.elementor .designed-posts .elementor-post__title a {
	color: var(--ruppert-navy);
	font-family: var(--ruppert-font-head);
	font-size: 1.375rem;
	font-weight: 500;
	line-height: 1.3;
}

/* X11: a full-width single-column card at 390 read small next to its own photo; 16:9 crops consistently
   regardless of each image's own source dimensions (measured: cards rendered their source crop's 3:2). */
@media (max-width: 767px) {
	.elementor .designed-posts .elementor-post__title,
	.elementor .designed-posts .elementor-post__title a {
		font-size: 1.875rem;
	}
}

.elementor .designed-posts .elementor-post__thumbnail__link img,
.elementor .designed-posts .elementor-post__thumbnail img {
	aspect-ratio: 16 / 9;
	object-fit: cover;
}

/* X14: excerpts cut mid-sentence at a fixed word count; clamp to 3 lines with an ellipsis instead of a raw cut. */
.elementor .designed-posts .elementor-post__excerpt p {
	display: -webkit-box;
	overflow: hidden;
	-webkit-box-orient: vertical;
	-webkit-line-clamp: 3;
	color: var(--ruppert-body);
	font-size: .875rem;
	line-height: 1.57;
}

.elementor .designed-posts .elementor-post__meta-data {
	border-top-color: rgba(191, 161, 69, .2);
	color: var(--ruppert-slate);
	font-size: .75rem;
	letter-spacing: .08em;
	text-transform: uppercase;
}

.elementor .designed-posts .elementor-post__read-more {
	color: var(--ruppert-navy);
	font-size: .75rem;
	font-weight: 600;
	letter-spacing: .12em;
	text-transform: uppercase;
}

/* R6: no hover affordance at all on the post-card "Read more" link. */
@media (hover: hover) {
	.elementor .designed-posts .elementor-post__read-more:hover {
		color: var(--ruppert-gold-text);
	}
}

.elementor .designed-posts .elementor-post__badge {
	border-radius: 0;
	background: var(--ruppert-navy);
	font-size: .6875rem;
	letter-spacing: .12em;
	text-transform: uppercase;
}

@media (hover: hover) {
	.elementor .designed-posts .elementor-post__card:hover {
		transform: translateY(-2px);
		box-shadow: 0 16px 32px -16px rgba(7, 21, 64, .2);
	}
}

/* X12: "More from the Blog" fetches exactly 3 posts for a 2-column tablet grid (build_blog.py) — hide the 3rd
   rather than leave it alone on its own row. */
@media (min-width: 768px) and (max-width: 1024px) {
	.more-posts .elementor-post__card:nth-child(3) {
		display: none;
	}
}

/* ---------- Design-critic batch (inner pages) ---------- */
/* Phone numbers never break across lines */
.nowrap-inline {
	white-space: nowrap;
}

/* A readable measure for long-form text (~66-75 characters).
   R5: capping the CHILDREN (`.ruppert-prose > :is(p, ul, ol, blockquote)`) only ever worked where a post/page
   happened to have no h2/h3/h4 in its body (the child selector never listed headings) — and for the blog
   single-post template specifically, that measured 0: F8 (`.ruppert-prose` itself via getBoundingClientRect)
   found no cap at 1024/1440 because the selector was never asked to constrain `.ruppert-prose` — only certain
   tag-types nested inside it. Capping `.ruppert-prose` itself is the real fix: it bounds the whole block
   (headings included) regardless of internal markup depth, and needs no `.inner-body` ancestor to do it —
   `max-width` with no `width` shrinks to fit on narrower viewports the same as before. `!important`: Elementor's
   OWN frontend.min.css sets `.elementor.elementor .e-con > .elementor-widget { max-width: 100% }` — two chained
   classes plus 2 more, (0,4,0) — which otherwise beats this selector's (0,1,0) and was the actual reason the
   measured width never moved (confirmed live: getComputedStyle reported max-width:100%, not 37rem, and
   `document.styleSheets` showed Elementor's rule as the one that matched); that rule is not itself `!important`. */
.ruppert-prose,
.inner-body > .elementor-widget-heading {
	max-width: 37rem !important; /* ~66-75 characters of body text; 'ch' is this font's wide zero, so 66ch came to 773px */
}

/* X5/X6: the hero lead, the quote card and the aside contact card each take their FULL column width, which is
   only ever comfortable at desktop (a photo hero's 56%, or a 34% aside beside body text) — a no-photo hero's
   68% column, or ANY of these stacked to width_tablet: 100% at 768-1024, ran a line out to ~105 characters. A
   flat max-width caps all three regardless of which column width produced the overflow — a flex child's own
   max-width still wins under `align-items: stretch`, so this needs no breakpoint of its own. */
.hero-lead,
.quote-card,
.contact-card {
	max-width: 38rem;
}

/* X7: a long breadcrumb chain (3 real segments: Home / Parent / Current) wrapped to 5 lines at 390 — collapse
   to the immediate parent + current page. build_pages.py joins every crumb + separator as siblings of one <p>,
   so ":first-child" + "at least 4 more siblings" (a, sep, a, sep, span = 5 nodes) selects "Home" + its
   separator ONLY on a 3-segment trail; a 2-segment trail (Home / Current — nothing to shorten) is left alone. */
@media (max-width: 767px) {
	.breadcrumbs p > a:first-child:nth-last-child(n+4),
	.breadcrumbs p > a:first-child:nth-last-child(n+4) + .sep {
		display: none;
	}
}

/* When a page opens with text rather than a heading, its first paragraph reads as the lead */
.inner-body > .elementor-widget-text-editor:first-child > p:first-child {
	color: var(--ruppert-ink);
	font-size: 1.1875rem;
	line-height: 1.65;
}

/* A list of quotations: italic serif on a gold rule */
.ruppert-prose ul.quote-list {
	padding-left: 1.25rem;
	border-left: 2px solid var(--ruppert-gold);
	list-style: none;
}

.ruppert-prose ul.quote-list li {
	padding-left: 0;
	color: var(--ruppert-navy);
	font-family: var(--ruppert-font-head);
	font-size: 1.125rem;
	font-style: italic;
	line-height: 1.5;
}

/* The philosophy line breaks evenly */
.quote-card__text .elementor-heading-title {
	text-wrap: balance;
}

/* Practice grid: a last card alone on its row spans the row; on phones the cards are compact title rows */
@media (min-width: 1025px) {
	.practice-grid > .e-con:last-child:nth-child(3n + 1),
	.practice-grid > .e-con-inner > .e-con:last-child:nth-child(3n + 1) {
		grid-column: 1 / -1;
	}
}

@media (min-width: 768px) and (max-width: 1024px) {
	.practice-grid > .e-con:last-child:nth-child(odd),
	.practice-grid > .e-con-inner > .e-con:last-child:nth-child(odd) {
		grid-column: 1 / -1;
	}
}

@media (max-width: 767px) {
	.practice-card__blurb,
	.practice-card__more {
		display: none;
	}

	/* X8: with the blurb and the "Learn more →" line both hidden, the compact card's title was the only thing
	   left — with no visible link cue. Add one back onto the title itself. */
	.practice-card > .elementor-widget-heading:first-child .elementor-heading-title::after {
		content: ' \2192';
		color: var(--ruppert-gold-text);
	}
}

/* The submit button spans the form, like every other primary action on a phone */
.lead-form .ff-btn-submit {
	width: 100%;
}
