/* TNC Home hero — rebuilt to match client reference design (2026-08-10).
   All decorative shapes below are original CSS/SVG built for this project —
   not copied from the reference site's own asset files.
   Layout values below are converted from the reference's exact computed
   values (measured against a 1920x745 canvas) into percentages of this
   hero section's own box, rather than hardcoded pixels — see the build
   conversation for the full px -> % table. */

/* ============ OUTER HERO CONTAINER (static backdrop) ============ */

.tnc-hero-section {
	position: relative;
	overflow: hidden !important; /* clips green shapes + bottom-anchored figure at section edge; !important guards against Elementor's own redundant overflow:visible rule on this container, same issue documented earlier in this file for .tnc-hero-figure-frame */
}

/* Decorative layer sits behind the slider, absolutely filling the section.
   Position/sizing is forced on the WIDGET'S OWN outer wrapper
   (.tnc-hero-decor-widget, applied via the HTML widget's own
   _css_classes, landing on .elementor-element) rather than only on the
   inner content div — Elementor containers (.e-con) default to
   position:relative themselves, and every level of nesting between the
   inner div and the true section boundary is a place `inset:0` could
   resolve against the wrong box. Forcing top/right/bottom/left:0
   !important directly on the outer wrapper removes that ambiguity. */
.tnc-hero-decor-widget {
	position: absolute !important;
	top: 0 !important;
	right: 0 !important;
	bottom: 0 !important;
	left: 0 !important;
	width: auto !important;
	z-index: 1;
	pointer-events: none;
}
.tnc-hero-decor-widget .elementor-widget-container {
	position: relative;
	width: 100%;
	height: 100%;
}
.tnc-hero-decor {
	position: relative;
	width: 100%;
	height: 100%;
}

/* Circle: reference measured 598px diameter / 61px top / 151px left on a
   1920x745 canvas = 80% height, 8% top, 8% left, centre at 48% height /
   23.6% width. Expressed as height + aspect-ratio (not a fixed diameter)
   so it scales correctly with the section's own height at any breakpoint
   without needing separate per-breakpoint pixel overrides. */
.tnc-hero-decor .tnc-hero-circle {
	position: absolute;
	left: 7.86%; /* 151px / 1920px reference — was flat 8% (154px), 3px off spec */
	top: 8%;
	height: 80%;
	aspect-ratio: 1;
	border-radius: 50%;
	background: #FFFFFF;
}

/* ============ MOUNTAIN + SPINNER REMOVED (2026-09-28, client request)
   The decor layer used to carry two full-hero .tnc-hero-mountain SVG
   layers — the faceted blue range filling the section's right half, the
   two swapped with a slide-up/slide-in animation on every slide change —
   plus a .tnc-hero-spinner-wrap gold badge rotating in the bottom-right
   corner. Client asked for both to go, and for the space they freed to be
   given to the slide photograph instead: see .tnc-hero-figure further
   down, whose box was widened and raised in the same pass.
   The markup lived in the hero's Elementor HTML widget (home page 10777,
   widget id 6959ab8) and was deleted from _elementor_data rather than
   merely hidden here, so neither the SVGs nor the spinner PNG are sent to
   the browser any more. The pre-removal copy of that page's data —
   including the exact polygon geometry and fills, if the range is ever
   wanted back — is at
   _project-docs/backups/home-10777-hero-decor-removal-backup.json.
   The layer-swap logic in tnc-hero-transitions.js was left in place: it
   looks the layers up by id and no-ops when they are absent, so it costs
   nothing and a revert needs only the markup restored. ============ */

/* The slider itself must sit above the decorative layer */
.hero-slider {
	position: relative;
	z-index: 2;
}

/* KNOWN PLUGIN WORKAROUND — ElementsKit Advanced Slider fade effect.
   Regression note (2026-08-10): this rule was accidentally dropped when
   this file got rewritten from scratch for the reference-hero rebuild —
   confirmed missing via live browser inspection, which caught slides 1
   and 2 rendering fully overlapped mid-transition (both headlines,
   sublines, and button rows visible stacked on top of each other). This
   is the SAME bug diagnosed earlier in the build: with the default
   `fadeEffect.crossFade:false` (no UI control to change it), Swiper
   keeps a slide at opacity:1 until it's already past active, so without
   this override every slide can render simultaneously. Do not remove
   this block, and if this file is ever rewritten wholesale again, copy
   it forward first — it does not get regenerated by anything else. */
.hero-slider .swiper-slide {
	opacity: 0 !important;
	transition: opacity 0.4s ease !important; /* was 0.8s — reference frame analysis (2026-08-12) confirmed both headlines legible simultaneously mid-transition at the old speed; matches ekit_slider_speed_time (also lowered to 400) */
}
.hero-slider .swiper-slide.swiper-slide-active {
	opacity: 1 !important;
}

@media (max-width: 1024px) {
	.tnc-hero-decor .tnc-hero-circle {
		display: none;
	}
}

/* SLIDER ARROWS HIDDEN BELOW 768px (2026-09-20). These are ElementsKit's
   own Advanced Slider navigation, not Swiper's built-in and not ours:
   .ekit-swiper-arrow-button > .swiper-nav-button.swiper-button-next/prev,
   emitted by advanced-slider.php behind `ekit_slider_nav_show_controls`.
   That control is registered with add_control(), NOT
   add_responsive_control() — it is a plain switcher with no per-breakpoint
   variant — so turning the arrows off on mobile alone is not expressible
   as a widget setting and has to happen here.
   Why: at 390px the 35x45 buttons sit at y 588-633, straight on top of
   the CTA row (primary 560-612, secondary 628-681), overlapping both.
   Hidden via the WRAPPER rather than the two buttons individually so the
   empty .ekit-swiper-arrow-button flow box goes too. Swiper's navigation
   module still initialises and still holds its references — this only
   removes them from paint/hit-testing below 768px; pagination dots remain
   as the mobile slide control, and desktop navigation is untouched. */
@media (max-width: 1024px) {
	.hero-slider .ekit-swiper-arrow-button {
		display: none !important;
	}
}

/* PAGINATION TAP TARGET (2026-09-20). With the arrows hidden above, the
   dots are the only slide control left on mobile, and they are 8x8px —
   far under the ~44px minimum. The visible dot is NOT the bullet's own
   box: .swiper-pagination-bullet has background-color:transparent and
   ElementsKit paints the dot with ::before, so ::before is taken and the
   hit area goes on ::after (confirmed empty: computed content was `none`).

   Size note — the target is 16x44, not 44x44, DELIBERATELY. The three
   bullets sit 16px apart centre-to-centre (8px wide + 4px margin each
   side). Giving each a 44px-wide area would overlap its neighbours by
   28px, and since the pseudo-elements stack in DOM order the rightmost
   dot would silently swallow taps meant for the one left of it — an
   unpredictable control is worse than a small one. 16px keeps the areas
   exactly contiguous instead: the full 48x44 row is live, and every
   column maps to the dot directly above it, with no dead zones and no
   ambiguity. Reaching 44px horizontally as well would mean spreading the
   dots to 44px apart, which changes the visible design; not done here.

   Visual output is unchanged: the ::after is transparent and the dots
   keep their 8px size, 4px margins and spacing exactly. */
/* PAGINATION CLEARS THE PHOTO (2026-09-21). Measured across 390/414/768/
   820/834/1024 and all three slides, the dot row was painting ON the
   photograph at EVERY one of those 18 states — Elementor's own responsive
   offset puts it 40-50px up from the section's bottom edge, while the
   restacked photo now ends only 42-60px up, so the two always intersected.
   Worst case is a near-invisible control: white dots over a white shirt at
   1024. Pulling the row down to 12px, and reserving 16px under the photo,
   separates them everywhere with ~30px to spare at the tightest slide
   (slide 2, whose photo ends 41.6px up at 390px).
   Two mechanics matter here. First, the dot row's offset parent is
   .hero-slider, NOT .tnc-hero-section — so its `bottom` is measured from
   the slider's box, which the photo also sits inside. Moving the dots down
   alone can therefore never clear the photo; the slider has to get taller
   underneath it, which is what the figure's margin-bottom does.
   Second, that margin needs !important: Elementor emits its own
   `.elementor-NNNN .elementor-element.elementor-element-XXX` rule
   (specificity 0,0,3,0) that zeroes it, and it outranks .tnc-hero-figure
   (0,0,1,0) — a first attempt without !important computed to
   margin-bottom:0 and changed nothing.
   36px reserved + a 10px offset was chosen over simply shrinking the photo,
   because the photo is the thing this hero is meant to lead with; the cost
   is 36px of section height rather than a smaller image. Desktop keeps
   Elementor's own offset untouched. */
@media (max-width: 1024px) {
	.hero-slider .swiper-pagination {
		bottom: 10px !important;
	}
}

@media (max-width: 1024px) {
	.hero-slider .swiper-pagination-bullet {
		position: relative;
	}
	.hero-slider .swiper-pagination-bullet::after {
		content: "";
		position: absolute;
		top: 50%;
		left: 50%;
		width: 16px;
		height: 44px;
		transform: translate(-50%, -50%);
		background: transparent;
	}
}

/* ============ SLIDE ROOT — keeps swiper's autoHeight matched to the
   section's own min-height, so bottom-anchored elements (the figure)
   land at the section's true bottom edge, not a shorter content-driven
   box. See the .tnc-hero-figure-frame comment above for the earlier,
   related autoHeight/min-height mismatch this same pattern fixes. ============ */
/* 745px matches the reference canvas height exactly (spec audit,
   2026-08-12). Raised from an original 660px base + a slide-1-only
   720px override (see git history / TNC-Build-Plan.md session notes)
   that had been tuned to bring slide 1's content-driven height in line
   with slides 2/3's longer 4-line headlines (~695-722px natural
   height) — 745px safely exceeds ALL THREE slides' natural content
   height, so it now floors every slide to the same exact value
   uniformly, making the old per-slide override redundant (removed).
   This also fixes .tnc-hero-figure's height:89%, which resolves
   against this element's height, not the outer section's — it was
   still measuring short (615px vs target 663px) even after the outer
   section itself was corrected to 745px, because this box's own
   height hadn't changed. */
.tnc-hero-slide-root {
	min-height: 745px !important;
	position: relative;
}

@media (max-width: 1024px) {
	.tnc-hero-slide-root {
		min-height: 480px !important;
	}
}

@media (max-width: 767px) {
	.tnc-hero-slide-root {
		min-height: 0 !important;
	}
}

/* ============ TEXT COLUMN — Elementor's own `width` control on
   Containers only applies when content_width==='full' (a hard condition
   in Elementor's own source); this text column doesn't set that, so its
   width is handled entirely here instead rather than via the fragile
   Elementor control. min-width:0 is required too — flex children default
   to a minimum width based on their own content, so without it a wide
   heading can force the column wider than intended instead of wrapping. ============ */
.tnc-hero-text-column {
	width: min(45%, 700px) !important;
	min-width: 0 !important;
	flex-shrink: 1 !important;
	margin-left: 60px !important; /* headline start ~320px -> ~380px per 2026-08-12 reference re-check; circle's own left (7.86%) was already on-target */
}
@media (max-width: 1024px) {
	.tnc-hero-text-column {
		margin-left: 0 !important;
	}
}
@media (max-width: 1024px) {
	.tnc-hero-text-column {
		width: 100% !important;
	}
}

/* ============ TYPOGRAPHY — exact values from the reference's own
   computed styles (font sizes, weights, colours, letter-spacing), not
   estimated. Vertical rhythm (margins between eyebrow/headline/subline/
   button) is *informed* by the reference's measured top-offsets
   (19% / 26% / 70% of section height) rather than reproduced as literal
   absolute positions — kept as a flex-column stack for resilience against
   wrapping differences, per the approach agreed on.
   Margins tightened further (2026-08-10) — confirmed via live browser
   measurement that the text block was overflowing the fixed-size white
   circle on slides with a 4-line headline (worst case: buttons sitting
   ~61px below the circle's bottom edge on Slide 3). The circle is a
   FIXED size shared across all slides (built once on the static
   backdrop, sized to the reference's own measured proportions) while
   headline length varies per slide, so this can only be partially fixed
   by margins alone — cutting all four gaps to near-zero recovers at most
   ~31px, since the block is vertically centred and any height reduction
   splits roughly in half between the top and bottom edges. If a 4-line
   slide still overflows noticeably after this, the actual fix is a
   shorter headline (3 lines, matching Slide 1's rhythm) or enlarging the
   circle for that slide specifically — not more margin squeezing. ============ */

.tnc-hero-eyebrow {
	display: flex;
	align-items: center;
	gap: 17px;
	margin-bottom: 6px !important;
}
.tnc-hero-eyebrow .elementor-heading-title {
	font-size: 16px !important;
	font-weight: 600 !important;
	color: #004AAD !important;
	letter-spacing: normal !important;
	text-transform: none !important;
	white-space: nowrap;
}
/* Icon + text-mask sizing/style — 2026-08-21, new hand-drawn arrow icon
   (per client-provided sample: SpinnerAndRightSideGraphic.html's
   "dream-kicker") replacing the old plain chevron. gap bumped 10px->17px
   and icon 37px->38px to match the new icon's own visual weight and
   native 38x38 viewBox — see the entrance/exit choreography further down
   for how this animates. */
.tnc-hero-eyebrow-icon {
	display: inline-flex;
	width: 38px;
	height: 38px;
	flex-shrink: 0;
	transform-origin: 50% 50%;
}
.tnc-hero-eyebrow-icon svg {
	display: block;
	width: 100%;
	height: 100%;
	overflow: visible;
}
.tnc-hero-eyebrow-icon svg path {
	fill: none;
	stroke: currentColor;
	stroke-width: 1.55;
	stroke-linecap: round;
	stroke-linejoin: round;
	vector-effect: non-scaling-stroke;
}
.tnc-hero-eyebrow {
	color: #004AAD;
}
/* Masked text-reveal wrapper — Elementor already wraps the eyebrow's
   heading widget in its own .elementor-widget-heading div, so turning it
   into a reveal mask needs no markup change: overflow:hidden here, the
   slide-in transform lives on .elementor-heading-title itself below. The
   3px/4px padding (from the sample) keeps descenders (g, y, p) from
   clipping against the mask edge. */
.tnc-hero-eyebrow .elementor-widget-heading {
	overflow: hidden;
	padding: 9px 3px 4px 0; /* top bumped 3px->9px (2026-08-21) to nudge the text down toward the arrow icon's own visual centre — measured 6.4px too high before this, confirmed live via getBoundingClientRect on both, not just eyeballed */
}
@media (max-width: 767px) {
	.tnc-hero-eyebrow-icon {
		width: 28px;
		height: 28px;
	}
	/* Eyebrow wrap fix (2026-09-20). The icon and kicker text live in the
	   eyebrow container's .e-con-inner, which is flex-ROW with Elementor's
	   default 20px gap. At 390px that row has 298px of inner width, and
	   slide 1's kicker measured 28 (icon) + 20 (gap) + 257.4 (text) =
	   305.4px — overflowing by just 7.4px and wrapping the icon onto its
	   own line, which is why the eyebrow measured 107.7px tall there
	   (10 + 35.5 + 20 row-gap + 32.2 + 10) against 66.2px at desktop.
	   The text cannot shrink to absorb it: .tnc-hero-eyebrow
	   .elementor-heading-title sets white-space:nowrap.
	   Slide 2's shorter kicker ("First Home Buyers") already fitted on one
	   line, which is why that slide measured 913px tall against slide 1's
	   991px — the wrap, not the content, was the difference.
	   Dropping the gap to 10px brings slide 1 to 295.4px, inside the 298px
	   available. Mobile only; the desktop row has 545px and never wrapped. */
	.tnc-hero-eyebrow > .e-con-inner {
		gap: 10px !important;
	}
}

.tnc-hero-headline.elementor-widget-heading .elementor-heading-title {
	font-family: "DM Sans", sans-serif !important;
	/* 85px -> 74px (2026-09-29, client asked for the main hero title to come
	   down "a bit"). Measured before changing: at 85px in this column's 565px
	   of text width, "Your First Home Is Closer Than You Think" and "Advice You
	   Can Trust, When It Matters Most" both ran to FOUR lines at 1440 and 1920,
	   and five at 1200. 74px is ~13% smaller and pulls the long slides back to
	   three lines at desktop widths, which also relieves the headline/button
	   crowding this file documents further down.
	   Letter-spacing eased -4px -> -3px to match: -4px was set for 85px and is
	   proportionally tighter at 74px, which starts to close up the counters.
	   Tablet (56px) and mobile (36px) are deliberately untouched — they were
	   tuned separately for wrapping at those widths and the client's example
	   was the desktop rendering. */
	font-size: 74px;
	font-weight: 700;
	color: #0F3567;
	letter-spacing: -3px;
	line-height: 1.12;
	margin-bottom: 2px !important;
}
@media (max-width: 1024px) {
	.tnc-hero-headline.elementor-widget-heading .elementor-heading-title {
		font-size: 56px;
		letter-spacing: -2px;
	}
}
@media (max-width: 767px) {
	.tnc-hero-headline.elementor-widget-heading .elementor-heading-title {
		font-size: 36px;
		letter-spacing: -1px;
	}
}

.tnc-hero-subline {
	font-size: 17px;
	font-weight: 400;
	color: #8A96A3;
	margin-top: 4px !important;
}

.tnc-hero-btn-primary .elementor-button,
.tnc-hero-btn-secondary .elementor-button {
	font-family: "DM Sans", sans-serif !important;
	font-size: 17px !important;
	font-weight: 500 !important;
	letter-spacing: 1px !important;
	line-height: 27px !important;
	padding: 12px 35px !important;
	border-radius: 9px !important;
}
.tnc-hero-btn-primary .elementor-button {
	background-color: #004AAD !important;
	color: #FFFFFF !important;
}
.tnc-hero-btn-primary .elementor-button-text::after {
	content: "\2192";
	margin-left: 8px;
	display: inline-block;
}
.tnc-hero-btn-secondary .elementor-button {
	background-color: transparent !important;
	color: #0F3567 !important;
	border: 1px solid #0F3567 !important;
}

/* MOBILE BUTTONS (2026-09-20) — below 768px the two buttons were sitting
   at their natural widths (249.9px + 184.6px) inside a 298px flex row, so
   they overflowed by 20px+ and wrapped to two ragged, unequal-width lines
   with a prev/next arrow landing on top of them. Full-width stacked here
   instead: equal width, deliberate rather than squeezed, and the row's
   own geometry becomes predictable.
   NOTE: this does NOT by itself shorten the row — the buttons already
   occupied two lines via wrapping, so stacking them costs the same
   vertical space. It is a layout/appearance fix; the height saving comes
   from the row-gap and the eyebrow, handled separately.
   Kept as `display:inline-block` + `text-align:center` rather than
   switching to flex, so .tnc-hero-btn-primary .elementor-button-text::after
   (the arrow glyph) keeps its existing inline layout untouched. */
@media (max-width: 767px) {
	.tnc-hero-btn-primary,
	.tnc-hero-btn-secondary {
		width: 100% !important;
	}
	.tnc-hero-btn-primary .elementor-button,
	.tnc-hero-btn-secondary .elementor-button {
		width: 100% !important;
		text-align: center !important;
		padding: 12px 24px !important;
	}
}

/* Button row's own top margin (16px, down from an earlier 32px) is set
   directly in the widget's own `margin` setting in the build script, not
   here — informed by the tight ~42px reference gap remaining after
   headline + subline (328px available between headline-top and
   button-top, minus ~286px for a 3-line headline at this line-height,
   minus the subline's own single line). */

/* ============ STAGED ENTRANCE CHOREOGRAPHY (2026-08-12) ============
   Replaces an earlier per-widget Elementor `_animation` setup (single
   shared preset, near-simultaneous ~10-300ms delays, no image entrance)
   that was ALSO the source of a confirmed bug: ElementsKit's replay
   function only re-triggers elements matching `.elementor-widget`,
   never `.e-con` containers, so anything animated at the container
   level got stuck permanently `elementor-invisible` after the first
   slide change. Rather than keep working around that fragile
   mechanism, this entrance system is entirely custom and entirely
   ours: driven by tnc-hero-transitions.js, which listens for Swiper's
   `slideChangeTransitionStart` directly and re-triggers every time —
   not dependent on ElementsKit's own animation replay at all.

   Safety design: elements are ONLY hidden (opacity:0) once the JS has
   confirmed it's running and added `.tnc-hero-js-ready` — see the JS
   file. If the script fails to load for any reason, nothing here ever
   applies and the hero renders fully visible, unanimated, immediately.
   This is deliberate: a missing entrance animation is a cosmetic
   miss; hero copy stuck invisible (the old bug) is not. */
@keyframes tncEntranceSettle {
	from {
		opacity: 0;
		transform: translateY(-15px);
	}
	to {
		opacity: 1;
		transform: translateY(0);
	}
}
@keyframes tncEntranceRise {
	from {
		opacity: 0;
		transform: translateY(20px);
	}
	to {
		opacity: 1;
		transform: translateY(0);
	}
}

.tnc-hero-js-ready .tnc-hero-headline,
.tnc-hero-js-ready .tnc-hero-subline,
.tnc-hero-js-ready .tnc-hero-subline + .e-con-boxed,
.tnc-hero-js-ready .tnc-hero-figure {
	opacity: 0;
}

/* ============ TITLE KICKER ENTRANCE/EXIT (2026-08-21) — the eyebrow no
   longer uses the shared tncEntranceSettle fade above (removed from the
   .tnc-hero-js-ready hide-list and no longer gets .tnc-entrance-play):
   it has its own two-part choreography instead — icon slides in with a
   small overshoot, text follows through the masked reveal set up above —
   per client-provided design (SpinnerAndRightSideGraphic.html's
   "dream-kicker"). Adapted to keep each slide's own eyebrow text instead
   of a single static caption, so this replays per-slide the same way the
   rest of the entrance choreography does — see tnc-hero-transitions.js
   for the JS trigger (a separate, parallel system to playEntrance(),
   not folded into it, since the animation shape is genuinely different:
   two elements choreographed with an offset delay plus a distinct exit
   state on slide change, vs. playEntrance()'s single fade-in per element).
   Recolored from the sample's #199476 to this site's blue #004AAD (set
   above via `color` on .tnc-hero-eyebrow, inherited by the icon's stroke
   through currentColor).
   Same safety pattern as the rest of this file: only hidden once
   .tnc-hero-title-js-ready is added by JS, so a script failure never
   leaves this copy invisible — a missing flourish is cosmetic, hero copy
   stuck invisible is not. */
.tnc-hero-title-js-ready .tnc-hero-eyebrow-icon,
.tnc-hero-title-js-ready .tnc-hero-eyebrow .elementor-heading-title {
	opacity: 0;
}
.tnc-hero-eyebrow-icon.tnc-title-play {
	animation: tncTitleIconIn 760ms cubic-bezier(0.16, 1, 0.3, 1) both;
}
/* TYPEWRITER (2026-09-29, client request: "title gets typed timely just after
   arrow appears"). The eyebrow text used to slide in from the left via
   tncTitleTextIn, overlapping the icon's own entrance. It now stays hidden
   until the arrow has visibly landed, then types out character by character —
   the characters themselves are driven from tnc-hero-transitions.js, since a
   proportional font cannot be revealed convincingly by animating width.
   The 480ms delay is tied to the icon above: tncTitleIconIn runs 760ms and
   hits its overshoot peak at 62% (~470ms), which is the moment the arrow reads
   as "arrived". Fading the text in at 480ms puts the first character right
   behind it rather than leaving a dead gap.
   Budget check: the longest eyebrow is "Protecting New Zealand Families" at 31
   characters; at the 42ms/character set in the JS that is ~1.3s of typing,
   finishing around 1.8s. The carousel's own autoplay delay is 7800ms, so the
   line is complete and readable for ~6s before the slide changes.
   tncTitleTextIn is kept below — .tnc-hero-headline and the exit state still
   reference this file's other keyframes, and leaving it costs nothing if the
   slide-in is ever wanted back. */
.tnc-hero-eyebrow .elementor-heading-title.tnc-title-play {
	animation: tncTitleTextFade 160ms 480ms both;
}
@keyframes tncTitleTextFade {
	from { opacity: 0; }
	to   { opacity: 1; }
}
/* Stops the line collapsing to zero height while its text is momentarily
   empty, which would otherwise nudge the headline below it on every slide. */
.tnc-hero-eyebrow .elementor-heading-title {
	min-height: 1em;
}
/* Blinking caret, present only while characters are still being added. */
.tnc-hero-eyebrow .elementor-heading-title.tnc-is-typing::after {
	content: "";
	display: inline-block;
	width: 2px;
	height: 1em;
	margin-left: 3px;
	vertical-align: -0.12em;
	background: currentColor;
	animation: tncCaretBlink 900ms steps( 1, end ) infinite;
}
@keyframes tncCaretBlink {
	0%, 49%   { opacity: 1; }
	50%, 100% { opacity: 0; }
}
.tnc-hero-eyebrow.tnc-title-exit .tnc-hero-eyebrow-icon,
.tnc-hero-eyebrow.tnc-title-exit .elementor-heading-title {
	animation: tncTitleExit 230ms cubic-bezier(0.55, 0, 0.9, 0.45) both;
}
@keyframes tncTitleIconIn {
	0% {
		opacity: 0;
		transform: translate3d(-42px, 0, 0) rotate(-7deg) scale(0.82);
	}
	62% {
		opacity: 1;
		transform: translate3d(4px, 0, 0) rotate(1.5deg) scale(1.035);
	}
	100% {
		opacity: 1;
		transform: translate3d(0, 0, 0) rotate(0) scale(1);
	}
}
@keyframes tncTitleTextIn {
	0% {
		opacity: 0;
		transform: translate3d(-46px, 0, 0);
	}
	68% {
		opacity: 1;
		transform: translate3d(3px, 0, 0);
	}
	100% {
		opacity: 1;
		transform: translate3d(0, 0, 0);
	}
}
@keyframes tncTitleExit {
	from {
		opacity: 1;
		transform: translate3d(0, 0, 0);
	}
	to {
		opacity: 0;
		transform: translate3d(-28px, 0, 0);
	}
}
/* NOT `animation: none` here — that was tried and confirmed (live,
   getComputedStyle) to leave the icon/text stuck at opacity:0 forever,
   because the only thing that ever sets opacity:1 is the animation's own
   `forwards` fill, via .tnc-title-play. Removing the animation without
   also setting the end state directly recreates the exact "hero copy
   stuck invisible" failure this file's safety design elsewhere is built
   to avoid. Setting opacity/transform directly here, on the base
   .tnc-hero-eyebrow-icon / .elementor-heading-title (not gated behind
   .tnc-title-play at all), guarantees visible content regardless of
   whether JS ever adds that class — same "cosmetic miss, not broken
   content" guarantee as the rest of this file, just achieved by setting
   the state directly instead of relying on an animation nobody plays. */
@media (prefers-reduced-motion: reduce) {
	.tnc-hero-eyebrow-icon,
	.tnc-hero-eyebrow .elementor-heading-title {
		opacity: 1 !important; /* must beat .tnc-hero-title-js-ready ... {opacity:0} above (2-class specificity) — this selector alone is only 1, and losing that fight is exactly the stuck-invisible bug this rule exists to prevent */
		transform: none !important;
		animation: none !important; /* also needed !important — .tnc-title-play (2-class) otherwise still wins and the animation keeps technically running, just silently overridden on the two properties above; confirmed via getComputedStyle before adding this */
	}
}

.tnc-hero-headline.tnc-entrance-play {
	animation: tncEntranceSettle 600ms ease-out 350ms forwards;
}
.tnc-hero-subline.tnc-entrance-play {
	animation: tncEntranceSettle 500ms ease-out 500ms forwards;
}
.tnc-hero-subline + .e-con-boxed.tnc-entrance-play {
	animation: tncEntranceSettle 500ms ease-out 600ms forwards;
}
.tnc-hero-figure.tnc-entrance-play {
	animation: tncEntranceRise 700ms ease-out 450ms forwards;
}

/* ============ PERSON IMAGE — bottom-anchored, sized primarily by
   HEIGHT (89% of section height, matching the reference's 663/745
   measurement) with width following naturally from the source's own
   aspect ratio — no forced crop, no fixed frame. max-width:40% is a wide
   backstop only: high enough that a correctly-proportioned portrait
   cut-out never hits it (height keeps binding first), low enough that a
   mismatched landscape source degrades gracefully via clipping instead
   of running off the section uncontained. Positioned via `left` (not
   `right`) to match the reference's own measurement (49.3% from the
   section's left edge).
   Height is set on the WRAPPER, not the <img> — percentage height on the
   <img> alone can't resolve, because ITS parent (this wrapper) has no
   definite height of its own (only bottom:0, otherwise auto/content-
   sized) — a percentage-height child needs a definite-height parent to
   resolve against, and "auto" doesn't count even when a floor is visibly
   enforced elsewhere. Setting height:89% here instead resolves against
   .tnc-hero-slide-root's own min-height (660px, treated as definite once
   resolved, since content doesn't exceed it) — the wrapper then HAS a
   definite pixel height, which the img can safely inherit via height:100%.
   top:auto is required too — confirmed via live browser inspection
   (2026-08-10) that without it, Elementor's own leftover per-element CSS
   (`_offset_orientation_v` defaults to 'start' even when unset, which
   generates a plain, non-important `top:0px`) combines with this
   element's explicit `height` to over-constrain the box. Per the CSS2.1
   absolute-positioning resolution rules, when top/height/bottom are ALL
   non-auto, `bottom` is the one that gets ignored and recomputed from
   top+height — REGARDLESS of !important on bottom, because this is a
   positioning-algorithm rule, not a cascade/specificity contest that
   !important can win. The element was anchoring to the top and leaving a
   visible gap at the section's actual floor. Setting top:auto removes it
   from the over-constrained set, letting bottom+height govern as intended.
   object-fit was never explicitly set here — confirmed via live browser
   inspection (2026-08-10) that it defaults to `fill`, which is harmless
   for images narrow enough to never hit max-width:40%, but for a WIDE
   (landscape) source that DOES hit that cap, `fill` stretches it to
   match both the capped width AND the explicit height:100% independently,
   distorting the aspect ratio (measured: a 1.571 source rendering at
   1.206 — a real ~23% horizontal squish, not just an upscaling softness
   issue). object-fit:contain fixes this by scaling to fit within
   whichever limit binds without distorting; object-position:bottom
   keeps the person's feet at the box's bottom edge (matching the
   bottom-anchored intent) rather than centering, which would otherwise
   float the figure with a gap underneath whenever width is the binding
   constraint. */
/* Vertical position + max-width re-tuned (2026-08-20) to close the gap to
   the reference's own measured relationship (top 13.96%/height 89.0% of a
   745px section, right edge 5.76% short of the section edge) — see the
   Step 1 report in this task's conversation for the full derivation.
   top:14% + height:89% intentionally overshoots the section's bottom edge
   by ~22px, matching the reference's own overshoot; relies on the
   pre-existing `.tnc-hero-section{overflow:hidden!important}` to clip it,
   same mechanism already used elsewhere on this backdrop — not a new bug.
   bottom is flipped to auto (was 0) rather than added alongside an
   explicit top, to avoid the exact top/height/bottom over-constraint this
   file already documrents below for object-fit — only one of top/bottom
   can be non-auto at a time.
   max-width 40%->46.2% (recalculated from an initial 45% guess — see
   note below): object-fit stays `contain` (confirmed 2026-08-20 —
   switching to `cover` would crop into the kids' outstretched hands on
   Slide 1, the tops of both heads on Slide 2, and the outer two people's
   shoulders on Slide 3; rejected). Under `contain`, only slides whose
   native aspect ratio is wide enough to hit this cap benefit from raising
   it — Slide 1 (1536x1024, AR 1.5) and Slide 3 (1448x1086, AR 1.33) are
   both cap-bound and land within ~1px of the reference's 5.76% edge gap
   at 45%. Slide 2 (1086x1448, AR 0.75, portrait) is NOT cap-bound at any
   max-width — its rendered width is set entirely by its own aspect ratio
   against the fixed height, so it remains ~16% short of the edge
   regardless. See _project-docs/hero-photo-slide2-note.md — the actual
   fix for Slide 2 is a wider-aspect source photo (~1.3-1.5 AR, matching
   Slides 1/3), not a crop.
   NOTE on the 46.2% figure (superseded below, kept for the specificity
   history): max-width/left percentages on this absolutely-positioned
   element resolve against .tnc-hero-slide-root (an Elementor e-con flex
   container), NOT the 1440px section directly — confirmed live that
   slide-root's own rendered width is 1400px (inset ~20px from the section
   edges), not 1440px. A first pass at 45% (derived assuming a 1440px
   basis) measured 630px rendered / 100px (6.9%) gap from the section's
   right edge — short of the ~83px (5.76%) target. 46.2% was recalculated
   against the real 1400px basis and landed within ~1px of that target on
   the cap-bound slides (1 and 3) — but left Slide 2 narrower (497px,
   portrait-AR-bound, never cap-bound) than Slides 1/3 (647px at 46.2%),
   which was the whole point of that pass: matching the *reference's own*
   edge distance, not matching the three slides to each other.
   2026-08-20 (later same day): client explicitly asked for the opposite
   priority — Slide 2's box treated as correct, Slides 1 and 3 matched to
   IT instead of to the reference's edge gap. Since height/top are already
   identical across all three slides, the only thing that needs to change
   is max-width: retuned from 46.2% to 35.52%, measured live directly off
   Slide 2's own rendered width (497.28px of a confirmed 1400px
   containing-block: 497.28/1400 = 35.52%). Because Slide 2 was never
   cap-bound in the first place, this new cap doesn't affect it at all —
   it was already narrower than 35.52% of 1400px; it only pulls Slides 1
   and 3 down to match. `contain` is unchanged (still no crop) — the
   trade-off is unchanged too: Slides 1 and 3's visible subjects now
   render at roughly half Slide 2's apparent height inside their
   letterboxed, matched box (497x663), confirmed and accepted by the
   client rather than cropping via `cover`. See
   _project-docs/hero-photo-slide2-note.md — Slide 2's own narrower reach
   toward the section's right edge (vs. the reference) is now moot as a
   concern, since matching-across-slides was prioritized over matching the
   reference's edge gap. */
.tnc-hero-figure {
	position: absolute !important;
	left: 45.97% !important; /* was 49.3% (2026-08-21) — client-provided reference screenshots (Slide1/2/3Screen.png) measured with pixel analysis: photo's visible left edge sits at 45.96-45.98% of hero width, tightly consistent across all three slides (877/1908, 875/1903, 876/1906 px — within 2px of each other despite very different photo content), well outside normal measurement noise. Moving the shared wrapper position to match, rather than width, since left-edge consistency across slides was the one reliably measurable signal from those screenshots (their bottom edges are cut off in every capture provided so far, which blocks a precise height/width read — see the conversation for the full measurement trail). */
	top: 2% !important;
	bottom: auto !important;
	/* 2026-09-28 — box grown into the space the mountain + spinner used to
	   occupy (see the removal note near the top of this file). `left` is
	   deliberately untouched: every bit of the new room is taken on the
	   right and upward, so the photo can never sit closer to the headline
	   and button column than it already did.
	     height 89% -> 101%, with `top` 14% -> 2%: the BOTTOM edge stays
	       exactly where it was (14+89 = 103% = 2+101), i.e. still a couple
	       of percent past the section's own bottom and still clipped there
	       by .tnc-hero-section's overflow:hidden. Nothing about the photos'
	       bottom anchoring changes; they simply get ~12% taller, which for
	       the two portrait slides (height-bound at AR 0.75) is also the
	       only thing that makes them wider: 497 x 663 -> 564 x 752.
	     width/max-width 35.52% -> an explicit 52%: 35.52% was measured off
	       Slide 2's own portrait-bound width (497.28/1400) on 2026-08-20,
	       when the goal was to pull the cap-bound Slides 1/3 back to match
	       Slide 2's box. That cap is what held Slide 3's landscape photo
	       (AR 1.333) to a letterboxed 477 x 358 under object-fit:contain;
	       at 52% it is width-bound at ~728 x 546 instead.
	       `width`, not `max-width` alone, because this element is
	       position:absolute and therefore shrink-to-fit: with only a cap,
	       each box collapses to its own image's width and the auto side
	       margins on the <img> below have nothing to centre within. An
	       explicit width makes all three boxes span the same region —
	       left edge 45.97%, right edge 45.97+52 = 97.97% of the slide-root
	       — so the portrait photos centre inside the right-hand area
	       instead of hugging the text, and Slide 3 fills it.
	       --container-widget-width is set alongside for the reason the
	       mobile block documents: Slide 3's image widget carries Elementor's
	       own width:48.081%/--container-widget-width with no media query,
	       which would otherwise govern this box at every width above 1024.
	   Why centre rather than pin to the right edge: the photos are bound by
	   the section's fixed height, not its width, so they stay ~564px wide
	   as the viewport grows while the text column keeps widening. Pinned
	   right, a 1920 viewport opens a ~340px hole between headline and
	   photo; centred in the right-hand region the spacing stays even at
	   both 1440 and 1920 (verified by screenshot at both).
	   Only desktop is affected: from 1024px down, the restack blocks at the
	   end of this file override top/left/width/height/max-width with
	   !important and put the photo in normal flow under the text. */
	height: 101%;
	width: 52% !important;
	--container-widget-width: 52% !important;
	max-width: 52% !important;
}

/* Slide 3 only (elementor-element-940af8f, the "advisers meeting with
   clients" photo widget) nudged right, 2026-08-21 — client flagged this
   photo crowding the "Get a Free Quote" secondary button in slide 3's
   button row; a flat px transform (not a % left change, which the shared
   .tnc-hero-figure rule above uses and which would shift by a different
   amount at every breakpoint) keeps the nudge visually consistent across
   widths while leaving slides 1/2 untouched. Scoped to >767px only — below
   that .tnc-hero-figure switches to position:relative/centered and this
   photo is no longer sitting next to the button row at all.
   25px total (was 10px) — client asked for 15px more, same day. */
@media (min-width: 768px) {
	.tnc-hero-figure.elementor-element-940af8f {
		transform: translateX(25px);
	}
}

/* z-index:3 here (1 class, specificity 0,0,1,0) was silently losing to
   Elementor's own `.elementor-element.elementor-absolute{z-index:1}`
   (2 classes, 0,0,2,0) — confirmed via live computed style, which showed 1
   despite this declaring 3. The photo was only painting above the decor
   shapes because of DOM order, not because this z-index was actually
   winning. Fixed by out-specificity-ing Elementor's rule instead of
   reaching for !important: this element always carries `.elementor-absolute`
   alongside `.tnc-hero-figure` (Elementor adds it automatically whenever a
   widget's own "position: absolute" setting is on, which this widget
   requires), so anchoring both classes to the section gives (0,0,3,0),
   which wins outright regardless of stylesheet load order. */
.tnc-hero-section .tnc-hero-figure.elementor-absolute {
	z-index: 3;
}
.tnc-hero-figure img {
	width: auto !important;
	height: 100% !important;
	max-width: 100% !important;
	object-fit: contain;
	object-position: bottom center;
	display: block;
	/* 2026-09-28. With the wrapper given an explicit 52% width (see above),
	   the portrait slides' <img> element is narrower than the box it sits
	   in — height-bound at AR 0.75, it shrink-wraps to ~564px inside ~728px
	   — and a display:block image with no auto margins would sit hard
	   against the box's left edge, leaving all of the reclaimed width as a
	   gap on the right. `object-position: bottom center` does not help
	   here: it positions the painted image inside the ELEMENT box, and on
	   these slides the element box already matches the image exactly.
	   Auto side margins centre the element itself within the wrapper, so
	   the portrait photos sit in the middle of the right-hand area instead
	   of hugging the text column. Slide 3's landscape photo is clamped to
	   the full box width, so it is unaffected by this. */
	margin-left: auto;
	margin-right: auto;
}

@media (max-width: 1024px) {
	.tnc-hero-figure {
		height: 70%;
	}
}

@media (max-width: 767px) {
	.tnc-hero-figure {
		position: relative !important;
		/* top: auto added 2026-09-28. This block never reset `top`, so the
		   base rule's desktop offset kept applying to an in-flow,
		   position:relative element down here as a relative nudge. It
		   resolved to 0 in practice (percentage `top` against an
		   auto-height containing block) so nothing moved, but the desktop
		   value changed in this pass (14% -> 2%) and leaving mobile
		   depending on that coincidence would be asking for a silent shift
		   later. The tablet block further down already resets it the same
		   way. Verified at 375px against a before/after screenshot pair:
		   no change. */
		top: auto !important;
		left: auto !important;
		bottom: auto !important;
		height: auto;
		max-width: 100% !important;
		margin: 24px auto 36px !important;
		text-align: center;
		/* width, not just max-width (2026-09-20). Slide 3's image widget
		   (elementor-element-940af8f) carries an Elementor per-widget width
		   setting, emitted with NO media query so it applies at every width:
		     width: var(--container-widget-width, 48.081%);
		     max-width: 48.081%; --container-widget-width: 48.081%;
		   Slides 1 (2db6765) and 2 (469a407) have no width rule at all and
		   shrink-wrap normally. On DESKTOP this never shows, because
		   .tnc-hero-figure's own max-width:35.52% is TIGHTER than 48.081% and
		   wins, so all three land together (measured 497.3/497.3/477.3).
		   Below 768px the rule above relaxes to max-width:100%, which is
		   LOOSER, so Elementor's width:48.081% governs — and max-width alone
		   can never override a `width`. At 390px that made slide 3's figure
		   162.5px (48.081% of the 338px row) against 240px for slides 1/2,
		   rendering its photo at 142.5x106.9 = 20% of the others' area.
		   Resetting both the property and Elementor's own custom property
		   returns it to shrink-wrap like the other two. */
		width: auto !important;
		--container-widget-width: auto !important;
	}
	/* MATCHED VISUAL WEIGHT (2026-09-20). The three photos do not share an
	   aspect ratio — slides 1 and 2 are portrait (768x1024, AR 0.75), slide 3
	   is landscape (1448x1086, AR 1.333). object-fit:contain is kept (cover
	   was rejected earlier: it crops into faces on all three), and under
	   `contain` mixed ratios can be matched on height, on width, or on area,
	   but never on all three at once.
	   A square bound is used rather than a height cap so each photo is
	   limited by whichever axis is its longer one, which equalises AREA.
	   At the 320px bound (raised from 300px on 2026-09-21, once the
	   mountain was dropped on mobile and stopped competing for the row):
	     AR 0.75  -> 240 x 320 = 76,800px^2
	     AR 1.333 -> 320 x 240 = 76,800px^2   (identical)
	   Matching HEIGHT instead (the desktop convention, all three at 663px)
	   was measured too: it puts slides 1/2 at 190px wide, 49% of a 390px
	   screen, which reads as thin. Equal area reads as consistent even
	   though the boxes differ, which is the point.
	   min(320px, 100%) rather than a flat 320px so the photo still cannot
	   overflow its row on screens narrower than the cap. */
	.tnc-hero-figure img {
		height: auto !important;
		max-height: 320px !important;
		max-width: min(320px, 100%) !important;
		width: auto !important;
		margin: 0 auto;
	}
}


/* ============ TABLET (768-1024px) — RESTACKED, 2026-09-21 ============
   Client: "tablet devices like iPad Mini etc ... currently it is bizarre."
   Confirmed: at 768px and 1024px the photo was painting directly on top of
   the headline, leaving "Protecting What Matters Most to You" unreadable.
   This is the pre-existing tablet overlap referenced in the spinner comment
   further up this file, which had been left unverified rather than fixed.

   Cause: between 768 and 1024 the layout kept the DESKTOP geometry — the
   figure stayed position:absolute at top:14%/height:70% — while
   .tnc-hero-text-column was already forced to width:100% by the
   max-width:1024 rule above. So the text filled the full width and the
   absolutely-positioned photo was laid over it, with nothing reserving
   space for either. Below 768 this never happened, because the mobile
   block returns the figure to the normal flow.

   Fix: give tablets the same RESTACKED structure as mobile (text block,
   then photo beneath it, both in flow), at tablet-appropriate sizes rather
   than phone sizes — buttons stay at their natural side-by-side widths and
   the photo cap is 420px, not 320px. Typography keeps the existing tablet
   values (56px headline from the max-width:1024 rule above).

   Four rules that were written for phones are now shared with tablets
   rather than duplicated here, by widening their own query from
   max-width:767 to max-width:1024 — the decorative circle, the ElementsKit
   arrows, the mountain layer, and the enlarged pagination tap target.
   Tablets are touch devices with the same needs, and the client asked for
   this range to behave as the mobile version does. Everything still scoped
   to 767 is genuinely phone-SIZED (36px headline, 28px eyebrow icon,
   full-width buttons, the 320px photo cap) and deliberately not shared.

   Placed at the end of the file deliberately: several rules here override
   earlier max-width:1024 declarations at equal specificity, so source
   order is what makes them win. Do not move this block upward.
   Nothing here applies above 1024px — desktop is untouched. ============ */
@media (min-width: 768px) and (max-width: 1024px) {
	/* Kill the 480px floor: it was sized for the old overlapping layout and
	   would now leave a gap under a stack that is taller than 480px anyway. */
	.tnc-hero-slide-root {
		min-height: 0 !important;
	}

	/* THE ACTUAL RESTACK. Setting the figure to position:relative is not
	   enough on its own — measured at 768px and 1024px, the slide root's
	   .e-con-inner computes flex-wrap:NOWRAP here, against wrap below 768px.
	   With nowrap the figure cannot move to its own line, so it stayed
	   beside the text and the text column (flex-shrink:1) simply squeezed to
	   make room: 708px of row became 478.2px of text column plus a 209.8px
	   photo. That squeeze is what produced the overlap — the text column was
	   being told width:100% by the max-width:1024 rule while simultaneously
	   being shrunk to fit a sibling on the same line.
	   Allowing wrap, and pinning the text column to a full line so it cannot
	   shrink, is what actually pushes the photo underneath. */
	.tnc-hero-slide-root > .e-con-inner {
		flex-wrap: wrap !important;
	}
	.tnc-hero-text-column {
		flex: 0 0 100% !important;
	}

	/* Return the photo to the normal flow, beneath the text. Mirrors the
	   mobile block's figure rules. width/--container-widget-width are reset
	   for the same reason as on mobile: slide 3's widget carries Elementor's
	   own width:48.081% with no media query, and relaxing max-width here
	   would otherwise let it take over and render that photo undersized. */
	.tnc-hero-figure {
		position: relative !important;
		top: auto !important;
		left: auto !important;
		bottom: auto !important;
		height: auto !important;
		width: auto !important;
		--container-widget-width: auto !important;
		max-width: 100% !important;
		margin: 32px auto 36px !important;
		text-align: center;
	}
	/* Same square-bound trick as mobile so the mixed aspect ratios match on
	   AREA, just larger: 420 suits a tablet column where 320 looks stranded.
	     AR 0.75  -> 315 x 420
	     AR 1.333 -> 420 x 315 */
	.tnc-hero-figure img {
		height: auto !important;
		max-height: 420px !important;
		max-width: min(420px, 100%) !important;
		width: auto !important;
		margin: 0 auto;
	}

	/* Slide 3's desktop-only nudge is meaningless once the photo is centred
	   in flow — it only existed to stop that photo crowding the button row
	   in the side-by-side layout. */
	.tnc-hero-figure.elementor-element-940af8f {
		transform: none !important;
	}
}
