/* Sketch Fit Finder — frontend (mobile-first) */

.sff, .sff * { box-sizing: border-box; }

.sff {
	--sff-accent: #7B58FF;
	--sff-accent-hover: #6B46F0;
	--sff-bg: #0C082E;
	--sff-bg-soft: #14103A;
	--sff-green: #BDF34D;
	--sff-pill: #F5F5FA;
	--sff-pill-2: #ECECF3;
	--sff-ink: #121022;
	--sff-muted: #8F8CA1;
	--sff-lav: #B6A2FF;
	--sff-body-lav: #B9B4D8;
	--sff-pad: 31px;
	--sff-radius: 18px;

	position: relative;
	/* Full width: fill the container completely (the standalone CSS forces the
	   theme's own containers to full width so this spans the whole page). */
	width: 100%;
	max-width: 100%;
	margin: 0;
	background: var(--sff-bg);
	color: #fff;
	/* Brand typeface — Saans, self-hosted by this plugin (assets/css/font.css).
	   Falls back to --sk-font so that on a site where Sketch Suite owns the type
	   (and this plugin defers to it) the family still resolves. */
	font-family: var(--sff-font, var(--sk-font, "Saans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif));
	font-size: 16px;
	line-height: 1.5;
	overflow-x: clip;
	-webkit-font-smoothing: antialiased;
}

.sff button { font-family: inherit; cursor: pointer; border: 0; }
/* icon-only buttons: no padding / no fill (kept separate so text buttons keep their own padding) */
.sff-play, .sff-drawer-close { padding: 0; background: none; }
.sff-acc-q { background: transparent; }
.sff img { max-width: 100%; display: block; }

/* ---------- Header ---------- */
.sff-header {
	position: sticky;
	top: 0;
	z-index: 30;
	display: flex;
	align-items: center;
	gap: 8px;
	padding: 13px 14px;
	background: rgba(12, 8, 46, 0.86);
	backdrop-filter: blur(10px);
}
.sff-logo {
	flex: 0 0 auto;
	width: 52px; height: 52px;
	border-radius: 15px;
	background: #17143A; border: 1px solid rgba(255,255,255,.06);
	display: flex; align-items: center; justify-content: center;
}
.sff-logo img { width: 32px; height: 32px; }
/* Padded as a block rather than padding `.sff-phone` alone: the availability
   line sits directly above the number and the two are left-aligned, so moving
   only the number would knock them out of line with each other. */
.sff-header-mid { flex: 1 1 auto; min-width: 0; padding-left: 8px; display: flex; flex-direction: column; justify-content: center; line-height: 1.2; }
.sff-avail { display: inline-flex; align-items: center; gap: 6px; font-size: 11px; color: var(--sff-muted); white-space: nowrap; }
.sff-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--sff-green); box-shadow: 0 0 0 4px rgba(189,243,77,.2); flex: 0 0 auto; }
.sff-phone { font-size: 15px; font-weight: 700; color: #fff; text-decoration: none; letter-spacing: .2px; white-space: nowrap; }
/* Book-a-Call + burger grouped inside one rounded panel */
.sff-header-actions {
	flex: 0 0 auto; display: inline-flex; align-items: center; gap: 2px;
	background: #17143A; border: 1px solid rgba(255,255,255,.08);
	border-radius: 18px; padding: 5px 6px;
}
.sff-book-btn {
	background: var(--sff-accent); color: #fff; font-weight: 600; font-size: 15px;
	padding: 14px 22px; border-radius: 14px; white-space: nowrap; line-height: 1;
}
.sff-book-btn:hover { background: var(--sff-accent-hover); }

/* ---------- Hero ---------- */
.sff-hero { padding: 40px var(--sff-pad) 48px; text-align: center; }
/* Figma 246:15956 — SemiBold 45/48, ls −0.9, #F5F5FA, centred, three lines.
   The breaks are authored in the content field rather than left to wrapping. */
/* The -12 side margins are wrap headroom, not a layout change: the text is
   centred, and the designed lines are 321 at their widest, so nothing here
   moves. They exist because these lines are *authored* — anything that re-wraps
   is a bug, and without the headroom a line a few px over the 378 content width
   silently breaks at the last opportunity, which strands a trailing full stop on
   a line of its own. Two of the variation headlines land in exactly that band.

   Why 12 and not 8: the type scales with the viewport (10.23vw = 45/440) but the
   31px gutters don't, so the squeeze gets *worse* as the screen narrows, not
   better. A line that just fits at 440 needs roughly `31 - 0.067 x width` px of
   headroom — 1.5px at 440, but 9.6px at 320. 12 clears the narrowest handset
   anyone still carries; 8 broke below ~360. */
.sff-hero-title {
	position: relative; margin: 0 -12px 22px; color: #F5F5FA; font-weight: 600;
	font-size: min(45px, 10.23vw); line-height: 1.067; letter-spacing: -.02em;
}
.sff-hero-line { display: block; }
.sff-hero-word { display: inline-block; }
/* Wraps the underlined phrase so the rule spans exactly those words. */
.sff-hero-mark { position: relative; display: inline-block; }
/* Figma 246:15965 — 5px, round caps, and the gradient *starts on the page
   background* (#0C082E) so the left end dissolves rather than stopping dead. */
.sff-hero-underline {
	position: absolute; left: 0; right: 0; bottom: -3px; height: 5px; border-radius: 5px;
	background: linear-gradient(90deg, #0C082E 3.85%, #6D46FF 56.25%, #24B6FF 100%);
	overflow: hidden; isolation: isolate;
}

/* ---------- Hero entrance ----------
   Pure CSS so it runs without waiting on JS — this is the first thing anyone
   sees. Each word carries its own index (--sff-w) from the template, so the
   stagger reads continuously across all three lines regardless of word count. */
@keyframes sff-hero-word-in {
	from { opacity: 0; transform: translateY(.42em); }
	to   { opacity: 1; transform: none; }
}
.sff-hero-word {
	opacity: 0;
	animation: sff-hero-word-in .62s cubic-bezier(.22, .61, .36, 1) both;
	animation-delay: calc(var(--sff-w, 0) * 72ms + 120ms);
}

/* ---------- Hero colour sweep ----------
   The underline's own gradient (#6D46FF → #24B6FF) travels through the
   headline: each line goes white → gradient → white, one line after the next.

   The gradient is clipped on the **line**, not the word, so it is continuous
   across the whole line and flows through every letter — clipping per word
   restarts the gradient at each word boundary, which reads as words changing
   colour rather than a sweep passing through the text.

   That conflicts with the entrance, though: a background clipped on an ancestor
   is not gated by a descendant's opacity, so a line-level clip would paint every
   word at once and kill the stagger. Hence the handoff — the words paint their
   own white while they land, and frontend.js adds `is-lit` once the last one
   has, from which point the line's gradient supplies the glyph pixels instead.
   No JS just means no sweep; the headline is still white and correct. */
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
	/* `animation: none` is load-bearing, not tidying. While the entrance
	   animation is still attached — even finished, holding its `both` fill —
	   the word stays a composited layer, and a composited descendant's glyphs
	   do not contribute to an ancestor's `background-clip: text` shape. The
	   line's gradient then clips to nothing while the words are already
	   transparent, and the headline vanishes into the background. Dropping the
	   animation (and setting the opacity it was holding) puts the glyphs back
	   into the clip. */
	.sff-hero-title.is-lit .sff-hero-word {
		animation: none; opacity: 1;
		-webkit-text-fill-color: transparent; color: transparent;
	}
	/* The underlined phrase opts out of the clip and keeps painting its own
	   colour.

	   `.sff-hero-mark` wraps exactly those words AND holds `.sff-hero-underline`,
	   whose `::after` carries `mix-blend-mode: screen` and an infinite animation.
	   That is enough for iOS Safari to promote the mark to its own composited
	   layer — and, exactly as the note above describes for the entrance
	   animation, a composited descendant's glyphs do not contribute to an
	   ancestor's `background-clip: text` shape. So the line's gradient clips to
	   nothing across just those words while they are already
	   `text-fill-color: transparent`, and the underlined phrase disappears.
	   Reported on an iPhone 16 Pro Max; desktop Chrome makes different
	   compositing choices and does not show it, which is why it survived review.

	   The cost is that the sweep passes behind these words rather than through
	   them. That is a much smaller price than a headline that silently loses two
	   words on the device most of this traffic arrives on. */
	.sff-hero-title.is-lit .sff-hero-mark .sff-hero-word {
		-webkit-text-fill-color: #F5F5FA; color: #F5F5FA;
	}
	.sff-hero-title.is-lit .sff-hero-line {
		background-image: linear-gradient(100deg,
			#F5F5FA 0%, #F5F5FA 38%,
			#6D46FF 46%, #24B6FF 54%,
			#F5F5FA 62%, #F5F5FA 100%);
		background-size: 300% 100%;
		background-repeat: no-repeat;
		-webkit-background-clip: text; background-clip: text;
		animation: sff-hero-sweep 6s linear infinite;
		animation-delay: calc(var(--sff-l, 0) * 620ms);
	}
}
/* At 0% the visible slice of the 300%-wide layer is its first third and at 100%
   its last — both entirely inside the white runs, so the line rests pure white.
   The colour only crosses during the first 35% of the cycle (~2.1s), which is
   the "white → gradient → white" pass.
   Runs 100% → 0%, not 0% → 100%: raising `background-position` shifts an
   oversized layer *left*, so counting up would send the colour right-to-left.
   Counting down travels left-to-right, matching the word entrance and the
   underline's flash. */
@keyframes sff-hero-sweep {
	0%   { background-position: 100% 0; }
	35%  { background-position: 0% 0; }
	100% { background-position: 0% 0; }
}
/* Wipes in left-to-right once its own word has landed. */
@keyframes sff-hero-underline-in {
	from { transform: scaleX(0); }
	to   { transform: scaleX(1); }
}
.sff-hero-underline {
	transform-origin: left center;
	animation: sff-hero-underline-in .5s cubic-bezier(.22, .61, .36, 1) both;
	animation-delay: calc(var(--sff-w, 0) * 72ms + 420ms);
}
/* …then a highlight keeps sweeping across it.
   The start and end positions put the highlight *entirely clear* of the bar:
   with a 45%-wide layer, `background-position` maps 0→100% across the leftover
   55%, so it only leaves the bar past -82% / 182%. Anything short of that parks
   a white blob over one end between sweeps, which flattens the gradient exactly
   where it should be showing. `screen` + `isolate` lightens the gradient rather
   than painting over it. */
.sff-hero-underline::after {
	content: ""; position: absolute; inset: 0;
	background: linear-gradient(90deg, transparent 0%, rgba(255, 255, 255, .7) 50%, transparent 100%);
	background-size: 45% 100%; background-repeat: no-repeat;
	mix-blend-mode: screen;
	animation: sff-hero-underline-flash 3.6s ease-in-out infinite;
	animation-delay: calc(var(--sff-w, 0) * 72ms + 1000ms);
}
@keyframes sff-hero-underline-flash {
	0%        { background-position: -140% 0; }
	38%, 100% { background-position: 240% 0; }
}
/* ---------- Hero cascade ----------
   Everything below the headline follows it in sequence: intro, then button,
   then the reviews badge. The first delay picks up where the headline finishes
   — last word starts at 9 x 72 + 120 = 768ms and runs 620ms, so ~1390ms — and
   each one after starts while the previous is still landing, which reads as a
   cascade rather than a queue. Pure CSS: it's all above the fold and runs on
   load, so there's nothing to wait for. */
@keyframes sff-hero-rise {
	from { opacity: 0; transform: translateY(14px); }
	to   { opacity: 1; transform: none; }
}
.sff-hero-intro,
.sff-hero .sff-cta,
.sff-reviews {
	animation: sff-hero-rise .62s cubic-bezier(.22, .61, .36, 1) both;
}
.sff-hero-intro    { animation-delay: 1420ms; }
.sff-hero .sff-cta { animation-delay: 1700ms; }
.sff-reviews       { animation-delay: 1960ms; }

@media (prefers-reduced-motion: reduce) {
	.sff-hero-intro, .sff-hero .sff-cta, .sff-reviews {
		animation: none; opacity: 1; transform: none;
	}
	/* Drop the clip too, not just the animation — a frozen sweep would park a
	   band of colour mid-line. */
	.sff-hero-word { opacity: 1; animation: none; }
	.sff-hero-title.is-lit .sff-hero-word,
	.sff-hero-word { -webkit-text-fill-color: #F5F5FA; color: #F5F5FA; }
	.sff-hero-title.is-lit .sff-hero-line { background-image: none; animation: none; }
	.sff-hero-underline { animation: none; transform: none; }
	.sff-hero-underline::after { display: none; }
}
.sff-hero-intro {
	margin: 0 auto 30px; max-width: 380px; font-size: 16px; line-height: 1.55; color: #D8D6EC;
}

/* ---------- CTA button ----------
   264×55, radius 14, left-to-right #6653FF → #1EC1FF, with a white 64×45
   radius-12 tile inset 5 from the top/bottom and 6 from the right. Identical in
   all three places the button appears (Figma 246:15983 hero, 293:16422 more,
   297:16936 final), so the geometry lives here once.
   Note: the design sets the label in Mont SemiBold; that family isn't loaded
   anywhere on this site, so it inherits Saans. */
.sff-cta {
	display: inline-flex; align-items: center; justify-content: space-between; gap: 10px;
	width: 264px; max-width: 100%; height: 55px; padding: 5px 6px 5px 20px; border-radius: 14px;
	background: linear-gradient(90deg, #6653FF 0%, #1EC1FF 100%);
	color: #fff; font-weight: 600; font-size: 17px; letter-spacing: -.34px; line-height: 1;
	transition: filter .15s ease;
}
.sff-cta:hover { filter: brightness(1.05); }
/* Kept as an alias: the gradient is now the default for every CTA. */
.sff-cta--gradient { background: linear-gradient(90deg, #6653FF 0%, #1EC1FF 100%); }
.sff-cta-arrow {
	width: 64px; height: 45px; border-radius: 12px; background: #fff; color: #6552FF;
	display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto;
}
.sff-cta-light { background: var(--sff-accent); }

/* ---------- Google Reviews badge ---------- */
.sff-reviews {
	display: inline-flex; align-items: center; gap: 12px; margin: 26px auto 0;
	background: #fff; border-radius: 14px; padding: 12px 20px; box-shadow: 0 10px 30px rgba(0,0,0,.22);
	text-align: left;
}
.sff-google-g { flex: 0 0 auto; display: inline-flex; }
.sff-reviews-body { display: flex; flex-direction: column; line-height: 1.25; }
.sff-reviews-title { font-size: 13px; color: #5f6368; font-weight: 500; }
.sff-reviews-rating { display: inline-flex; align-items: center; gap: 6px; }
.sff-reviews-rating strong { font-size: 15px; color: #202124; font-weight: 700; }
.sff-stars { display: inline-flex; gap: 1px; }
.sff-reviews-count { font-size: 11px; color: #80868b; }

/* ---------- Section headings ---------- */
/* Explicit colour so a theme's direct `h2 { color }` rule can't override it (a
   direct rule beats an inherited value regardless of specificity). */
.sff-h2 { margin: 0 0 14px; font-size: 27px; line-height: 1.22; font-weight: 700; letter-spacing: -.3px; color: #fff; }
.sff-h2-light { color: #fff; }
.sff-eyebrow {
	display: inline-block; font-size: 11px; font-weight: 700; letter-spacing: 1.6px; text-transform: uppercase;
	color: var(--sff-lav); margin-bottom: 12px;
}
.sff-eyebrow-green { color: var(--sff-green); }
.sff-muted-p { font-size: 15px; line-height: 1.6; color: var(--sff-body-lav); margin: 0 0 20px; }

/* ---------- Quiz intro + Q1 ----------
   Two halves on the 440 artboard: a white block carrying the heading and intro
   copy (both left-aligned), then Q1 inside a #2B2270 card with 40px top corners
   that the bottom drawer visually continues. Figma 246:15992 / 246:15994 /
   360:9779 and the option nodes 246:16005 / 246:16016.
   The design's inner gutters wander between 22 and 28; using one 27 lines the
   options, title and progress bar up with each other, which is what reads. */
.sff-quiz-intro { padding: 40px 0 0; background: #fff; color: var(--sff-ink); text-align: left; }
.sff-quiz-head { padding: 0 30px 19px; }
.sff-quiz-heading {
	margin: 0; color: #0C082E; font-weight: 600; text-align: left;
	/* 40/45 on the artboard, scaled below it so the two-line wrap survives. */
	font-size: min(40px, 9.09vw); line-height: 1.125; letter-spacing: -.02em;
}
/* Blank lines in the dashboard field become paragraphs; the design's gap
   between them is one empty 15px line. */
.sff-quiz-lead { margin: 11px 0 0; }
.sff-quiz-lead p {
	margin: 0 0 19px; color: #000;
	font-size: 15px; font-weight: 400; line-height: 1.27; letter-spacing: -.3px;
}
.sff-quiz-lead p:last-child { margin-bottom: 0; }

.sff-quiz-modal {
	margin: 0; padding: 36px 28px 47px; background: #2B2270; color: #fff;
	border-radius: 40px 40px 0 0; text-align: left;
}

/* Shared by the inline card and the drawer panels. Line-heights are pinned to
   the artboard's text-node heights (15 / 33.5 / 15) rather than left to the
   font's default — those three add up to the 212px drop from the modal top to
   the first option, and guessing them put the whole stack 3px out. */
.sff-q-label {
	display: block; margin: 0 0 12px; font-size: 12px; line-height: 15px; font-weight: 600;
	letter-spacing: 1.2px; text-transform: uppercase; color: #fff;
}
.sff-q-title {
	margin: 0 0 23px; font-size: 24px; line-height: 33.5px; font-weight: 600;
	letter-spacing: -.48px; color: #fff; text-align: left;
}
.sff-select-label {
	display: block; margin: 0 0 14px; font-size: 12px; line-height: 15px; font-weight: 600;
	letter-spacing: 1.2px; text-transform: uppercase; color: #C9C3FF;
}
/* The option cards sit 22 from the left where the text above them sits 28, so
   they overhang the gutter by 6 on the left and stop 4 short on the right.
   That's in the design and visible in the UI export, not a rounding artefact. */
.sff-options { display: flex; flex-direction: column; gap: 9px; margin: 0 4px 0 -6px; }
/* The design puts a 30px radio at the left of each card; dropped on review in
   favour of the label centred on its own. The markup keeps `role="radio"` and
   `aria-checked`, so this is purely visual — restore by deleting the
   `.sff-radio { display: none }` below and switching `justify-content` back. */
.sff-option {
	display: flex; align-items: center; justify-content: center; gap: 14px; width: 100%; text-align: center;
	background: #F5F5FA; border: 1px solid #E4E4E4; border-radius: 10px;
	padding: 12px 20px; min-height: 71px; color: #0C082E;
	transition: background .15s ease, border-color .15s ease, transform .05s ease;
}
.sff-option:hover { background: #EDE8FF; border-color: #D6CCFF; }
.sff-option:active { transform: scale(.995); }
.sff-radio { display: none; }
.sff-option-label { font-size: 18px; font-weight: 500; letter-spacing: -.18px; }
/* Selected: #7C59FF at 90% over the modal. Kept as rgba rather than `opacity`
   so the label and the radio stay fully opaque. */
.sff-option[aria-checked="true"] {
	background: rgba(124, 89, 255, .9); border-color: transparent; color: #fff;
}
.sff-option[aria-checked="true"]:hover { background: rgba(124, 89, 255, 1); }
/* A 30px white disc with the pill colour showing through a 22px centre. */
.sff-option[aria-checked="true"] .sff-radio { background: #fff; border-color: #fff; }
.sff-option[aria-checked="true"] .sff-radio::after {
	content: ""; position: absolute; inset: 4px; border-radius: 50%; background: #7453F1;
}

/* ---------- Testimonial carousel ----------
   Transcribed 1:1 from Figma "Landing Page - Mobile" (440px artboard, node
   293:16307 / 293:16319). Vertical rhythm, in design px:
     section top 38 → heading (38/41) → 35 → card 414×378 r30 → 25 → quote
     (16/23) → 36 → cite row 65 → 114 to the section end.
   The design uses a different side gutter per element (heading 27, quote and
   cite 19, card 13, arrows 29), so the section pads to 19 and the odd ones out
   are nudged with their own inset. */
.sff-testimonial { padding: 38px 19px 114px; }
.sff-tsm-heading {
	margin: 0; padding: 0 8px; text-align: center; color: #fff;
	/* 38px on the 440 artboard; scales below that so the headline keeps its
	   two-line wrap on narrower handsets. em tracking follows the size. */
	font-size: min(38px, 8.64vw); line-height: 1.079; letter-spacing: -.02em; font-weight: 400;
}
/* Solid blue accent (company names etc). */
.sff-accent-text { color: #478EFF; }
/* Gradient accent words (e.g. "helped grow") — semibold against the regular
   heading. text-fill-color keeps the gradient even if a theme tries to force a
   solid colour on the heading. */
.sff-grad-text {
	font-weight: 600;
	background: linear-gradient(90deg, #7C59FF 0%, #33A1FF 100%);
	-webkit-background-clip: text; background-clip: text;
	-webkit-text-fill-color: transparent; color: transparent;
}

.sff-tsm-carousel { margin-top: 35px; }
.sff-tsm-slide[hidden] { display: none; }
/* Cross-fade between testimonials — the outgoing slide leaves in the direction
   of travel, the incoming one arrives from the opposite side. Opacity settles
   faster than the drift so the swap never reads as two slides at once. */
.sff-tsm-slide { transition: opacity .2s ease, transform .26s cubic-bezier(.22, .61, .36, 1); }
.sff-tsm-slide.is-out-left  { opacity: 0; transform: translateX(-18px); }
.sff-tsm-slide.is-out-right { opacity: 0; transform: translateX(18px); }
.sff-tsm-slide.is-in-left   { opacity: 0; transform: translateX(-18px); transition: none; }
.sff-tsm-slide.is-in-right  { opacity: 0; transform: translateX(18px); transition: none; }
.sff-tsm-arrow:active { transform: scale(.94); }
@media (prefers-reduced-motion: reduce) {
	.sff-tsm-slide { transition: none; }
}
.sff-video {
	position: relative; margin: 0 -6px; border-radius: 30px; overflow: hidden;
	aspect-ratio: 414 / 378; background: #17133F;
}
/* `--sff-preview-nudge` (set per testimonial, see Content → Testimonial) shifts
   the preview crop vertically. Positive moves the video DOWN in the card, which
   reveals more of its top — the fix for a portrait video whose subject is framed
   high and loses their head to a centred crop. `object-position` counts the
   other way (0% = top edge), hence the subtraction. */
.sff-video img,
.sff-video-loop {
	position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
	object-position: 50% calc(50% - var(--sff-preview-nudge, 0%));
}
/* Silent looping preview sits over the poster; the play pill stays above it. */
.sff-video-loop { z-index: 1; pointer-events: none; }
/* A Vimeo embed letterboxes the video inside its iframe rather than cropping to
   fill, so the iframe is over-sized and centred until the letterboxed video
   covers the card. Scaling both axes by 2.5 covers any aspect from ~0.44 to
   ~2.74 (portrait through cinemascope) without knowing the video's ratio — the
   JS narrows this to the exact size if the player reports its dimensions. */
.sff-video-loop--embed {
	inset: auto; top: 50%; left: 50%;
	/* Same nudge as the poster above. Here the percentage is of the iframe's own
	   (over-sized) height rather than of the overflow, so the same number moves
	   the embed further than it moves an `object-fit` image — tune per video. */
	transform: translate(-50%, calc(-50% + var(--sff-preview-nudge, 0%)));
	width: 250%; height: 250%; border: 0;
	/* Themes ship a blanket `max-width:100%` for embedded media, which silently
	   clamps the over-sized iframe back to the card and reinstates the letterbox
	   — the width is applied, then capped. BeTheme does exactly this. */
	max-width: none; max-height: none;
}

/* Play pill overlay, bottom-left */
.sff-play-pill {
	position: absolute; left: 13px; bottom: 19px; z-index: 2;
	display: inline-flex; align-items: center; gap: 14px;
	background: rgba(6, 6, 6, .7);
	-webkit-backdrop-filter: blur(5px); backdrop-filter: blur(5px);
	border-radius: 100px; padding: 9px 35px 9px 12px;
}
.sff-play-ic {
	width: 55px; height: 55px; border-radius: 50%; flex: 0 0 auto;
	background: var(--sff-green); display: flex; align-items: center; justify-content: center;
}
/* Optical nudge: the design sits the triangle 1.5/0.5px off dead centre. */
.sff-play-ic svg { transform: translate(1.5px, .5px); }
.sff-play-txt { color: #fff; font-size: 20px; line-height: 24px; font-weight: 500; letter-spacing: -.4px; white-space: nowrap; }

/* Element + two classes to outrank BeTheme's `.style-simple blockquote`, which
   forces italic and line-height 25px on every blockquote in the content area.
   That skin also sets `position:relative; top:15px`, which leaves the box where
   it is but paints the text ~9px low — enough to close the designed gap under
   the quote. Reset both, or every quote on the page sits off its grid. */
.sff blockquote.sff-quote {
	margin: 25px 0 0; font-size: 16px; line-height: 23px; letter-spacing: -.32px;
	font-weight: 400; font-style: normal; color: #fff;
	position: static; top: auto;
}

.sff-tsm-foot { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 36px; padding-right: 10px; }
.sff-cite-row { display: flex; align-items: center; gap: 13px; }
.sff-avatar { width: 61px; height: 61px; border-radius: 10px; overflow: hidden; flex: 0 0 auto; background: #17143A; }
.sff-avatar img { width: 100%; height: 100%; object-fit: cover; }
.sff-cite { display: flex; flex-direction: column; }
.sff-cite-name { font-size: 22px; line-height: 27px; font-weight: 600; letter-spacing: -.44px; color: #fff; }
.sff-cite-co { font-size: 16px; line-height: 20px; font-weight: 400; letter-spacing: -.32px; }

/* Pagination dots. Not in the Figma frames — styled to echo the arrows (active
   green, inactive white at 30%) so the carousel reads as one control. */
.sff-tsm-dots { display: flex; align-items: center; justify-content: center; gap: 8px; margin-top: 30px; }
.sff-tsm-dot {
	width: 8px; height: 8px; padding: 0; border-radius: 100px;
	background: rgba(255, 255, 255, .3);
	transition: width .25s ease, background .25s ease;
}
.sff-tsm-dot:hover { background: rgba(255, 255, 255, .5); }
.sff-tsm-dot.is-active { width: 26px; background: var(--sff-green); }
@media (prefers-reduced-motion: reduce) {
	.sff-tsm-dot { transition: none; }
}

.sff-tsm-nav { display: flex; align-items: center; gap: 12px; flex: 0 0 auto; }
.sff-tsm-arrow {
	width: 65px; height: 65px; border-radius: 20px; display: flex; align-items: center; justify-content: center;
	color: #0C082E; transition: filter .15s ease, background .15s ease;
}
.sff-tsm-prev { background: rgba(255, 255, 255, .3); }
.sff-tsm-prev svg { transform: rotate(180deg); opacity: .3; }
.sff-tsm-prev:hover { background: rgba(255, 255, 255, .38); }
.sff-tsm-next { background: var(--sff-green); }
.sff-tsm-next:hover { filter: brightness(1.05); }

/* Video lightbox */
.sff-video-modal { position: fixed; inset: 0; z-index: 10000; display: flex; align-items: center; justify-content: center; padding: 20px; }
.sff-video-modal[hidden] { display: none; }
.sff-video-modal-bg { position: absolute; inset: 0; background: rgba(6,4,24,.82); backdrop-filter: blur(3px); }
/* `--sff-modal-ratio` is set by frontend.js from the video's real aspect ratio —
   server-side via the Vimeo oEmbed lookup, or read off a self-hosted file's own
   metadata. Unitless so the same value drives `aspect-ratio` and the width calc
   below. Falls back to 16:9 when the orientation can't be determined (YouTube). */
.sff-video-modal-inner {
	position: relative; z-index: 2; width: 100%;
	/* Cap the WIDTH by what the viewport can give in HEIGHT, so a portrait video
	   shrinks to fit rather than running off the top and bottom of the screen.
	   The 132px is the modal's own padding (20+20) plus the close button that
	   sits above the frame (-46px) and its breathing room. */
	max-width: 900px;
	max-width: min(900px, calc((100vh - 132px) * var(--sff-modal-ratio, 1.7778)));
	max-width: min(900px, calc((100svh - 132px) * var(--sff-modal-ratio, 1.7778)));
}
.sff-video-modal-frame {
	position: relative; width: 100%; aspect-ratio: var(--sff-modal-ratio, 16 / 9); border-radius: 16px; overflow: hidden; background: #000;
}
.sff-video-modal-frame iframe, .sff-video-modal-frame video {
	position: absolute; inset: 0; width: 100%; height: 100%; border: 0;
}
.sff-video-modal-close {
	position: absolute; top: -46px; right: 0; width: 38px; height: 38px; border-radius: 50%;
	background: rgba(255,255,255,.15); color: #fff; font-size: 24px; line-height: 1; display: flex; align-items: center; justify-content: center;
}

/* ---------- Hero image showcase (marquee) ---------- */
.sff-showcase {
	background: var(--sff-bg); padding: 6px 0 42px; display: flex; flex-direction: column; gap: 14px; overflow: hidden;
}
.sff-marquee {
	overflow: hidden; width: 100%;
	-webkit-mask-image: linear-gradient(90deg, transparent 0, #000 7%, #000 93%, transparent 100%);
	mask-image: linear-gradient(90deg, transparent 0, #000 7%, #000 93%, transparent 100%);
}
.sff-marquee-track { display: flex; gap: 14px; width: max-content; will-change: transform; }
.sff-marquee-right { animation: sff-marq-right var(--sff-marquee-speed, 55s) linear infinite; }
.sff-marquee-left  { animation: sff-marq-left  var(--sff-marquee-speed, 55s) linear infinite; }
.sff-marquee-item {
	flex: 0 0 auto; width: 232px; height: 156px; border-radius: 20px; overflow: hidden;
	background: #17133F; box-shadow: 0 12px 30px rgba(0,0,0,.28);
}
.sff-marquee-item img { width: 100%; height: 100%; object-fit: cover; }
@keyframes sff-marq-left  { from { transform: translateX(0); }   to { transform: translateX(-50%); } }
@keyframes sff-marq-right { from { transform: translateX(-50%); } to { transform: translateX(0); } }

/* ---------- More than a website (LIGHT section) ----------
   Transcribed from Figma nodes 293:16352 / 293:16354 / 293:16364 / 293:16420 /
   293:16388 / 293:16421 / 293:16422 on the 440px artboard. Section runs y2608
   → y3925. Background is white, not tinted — the cards are the grey.
   The artboard has two centres 2px apart — text blocks on 220, cards and button
   on 222 — but that asymmetry reads as "everything leans right" on screen, so
   it is deliberately NOT reproduced: one centre, 220, for the whole section. */
.sff-more { padding: 35px 28px 71px; background: #fff; color: var(--sff-ink); text-align: center; }
.sff-more-heading {
	margin: 0; color: #0C082E; font-weight: 600;
	/* 38/41 on the artboard, scaled below it so the two-line wrap survives. */
	font-size: min(38px, 8.64vw); line-height: 1.079; letter-spacing: -.02em;
}
/* "Businesses rarely struggle…" — max-width pins the designed two-line wrap. */
.sff-lead {
	margin: 22px auto 0; max-width: 264px; color: #0C082E; opacity: .56;
	font-size: 18px; font-weight: 500; line-height: 25px; letter-spacing: -.36px;
}
/* 2px right of the text column — see the section note. Cards land on 30→414. */
/* `.sff ul.sff-crosslist`, not `.sff-crosslist`: BeTheme ships
   `.the_content_wrapper ul { margin: 0 0 15px 30px }` — specificity (0,1,1),
   which outranks a single class, so a plain `.sff-crosslist { margin }` is
   silently discarded. That phantom 30px indent (on top of the real 19px inset)
   is what made the cards look left-padded, the 0 top margin is what closed the
   gap to the lead, and the 15px bottom is what pushed the orbit down. Same trap
   as `.sff-ticklist` below. */
.sff ul.sff-crosslist { list-style: none; margin: 26px 0 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.sff .sff-crosslist li {
	display: flex; align-items: center; gap: 18px; margin: 0; min-height: 75px; text-align: left;
	background: #F5F5FA; border-radius: 10px; padding: 12px 24px 12px 19px;
	font-size: 18px; font-weight: 500; line-height: 22px; letter-spacing: -.36px; color: #000;
}
.sff-x { flex: 0 0 auto; display: block; width: 32px; height: 32px; }
.sff-x svg { display: block; }
/* Staggered entrance. The hidden state is only applied once JS adds `is-armed`,
   so the cards stay visible if the script never runs. */
.sff .sff-crosslist.is-armed li {
	opacity: 0; transform: translateY(16px);
	transition: opacity .55s ease, transform .55s cubic-bezier(.22, .61, .36, 1);
}
.sff .sff-crosslist.is-armed li.is-in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
	.sff .sff-crosslist.is-armed li { opacity: 1; transform: none; transition: none; }
}
/* "For over a decade…" — the bold summary line. */
.sff-more-body {
	margin: -3px 0 0; color: #0C082E;
	font-size: 22px; font-weight: 600; line-height: 28px; letter-spacing: -.44px;
}
/* "From branding, websites…" — the supporting paragraph, narrower on purpose. */
.sff-more-note {
	margin: 18px auto 0; max-width: 332px; color: #000; opacity: .6;
	font-size: 15px; font-weight: 500; line-height: 21px; letter-spacing: -.3px;
}
.sff-more .sff-cta { margin-top: 26px; }

/* ---------- Orbit (concentric rings + service pills) ----------
   Figma node 293:16420 — 418×184 box. All three rings are concentric on a
   centre 90px down from the box top (the 180px ring's own centre), so the
   pills are positioned as px offsets from that point rather than in percent:
   that keeps their relationship to the rings identical at any box width. */
.sff-orbit { position: relative; width: 100%; max-width: 418px; height: 184px; margin: 58px auto 0; }
.sff-orbit-rings { position: absolute; inset: 0; }
.sff-ring {
	position: absolute; top: 90px; left: 50%; transform: translate(-50%, -50%);
	border: 1px solid #C6C6C6; border-radius: 50%;
}
.sff-ring-1 { width: 100px; height: 100px; opacity: 1; }
.sff-ring-2 { width: 140px; height: 140px; opacity: .6; }
.sff-ring-3 { width: 180px; height: 180px; opacity: .4; }
.sff-orbit-logo {
	position: absolute; top: 90px; left: 50%; transform: translate(-50%, -50%); z-index: 2;
	width: 60px; height: 59.19px; border-radius: 10px; background: #F5F5FA; border: 1px solid #EFEFEF;
	display: flex; align-items: center; justify-content: center;
}
.sff-orbit-logo img { width: 45px; height: 45px; }
.sff-orbit-pill {
	position: absolute; top: 90px; left: 50%; z-index: 6;
	display: flex; align-items: center; justify-content: center;
	width: 77px; height: 25px; padding: 0; border-radius: 100px;
	background: var(--pill-bg, var(--sff-green)); color: var(--pill-fg, #000);
	font-size: 14px; font-weight: 500; letter-spacing: -.28px; white-space: nowrap;
	box-shadow: 0 5px 14px var(--pill-glow, rgba(12, 8, 46, .10));
	/* `translate` is its own property, so the float composes with the positioning
	   `transform` below instead of overwriting it. */
	animation: sff-pill-float var(--fd, 8s) ease-in-out var(--fdelay, 0s) infinite;
}
/* Pill centres as offsets from the ring centre (Figma centre is 220,3216). */
.sff-slot-top-left    { transform: translate(calc(-50% - 51.5px),  calc(-50% - 53.5px)); }
.sff-slot-right       { transform: translate(calc(-50% + 95.5px),  calc(-50% - 33.5px)); }
.sff-slot-left        { transform: translate(calc(-50% - 109.5px), calc(-50% + 19.5px)); }
.sff-slot-bottom-right{ transform: translate(calc(-50% + 74.5px),  calc(-50% + 44.5px)); }
.sff-slot-top-right   { transform: translate(calc(-50% + 51.5px),  calc(-50% - 53.5px)); }
.sff-slot-bottom-left { transform: translate(calc(-50% - 74.5px),  calc(-50% + 44.5px)); }
/* Rectangle 63 — a white veil fading the lower half of the orbit into the page.
   In the design it sits above everything, which is what greys "Content" out;
   now that the pills are animated and coloured they sit above it instead (z6 vs
   z5), so the veil only softens the rings. A permanently half-faded pill read as
   a bug once the others were moving. */
.sff-orbit-veil {
	position: absolute; left: 0; right: 0; top: 90px; height: 94px; z-index: 5; pointer-events: none;
	background: linear-gradient(to bottom, rgba(255,255,255,0) 0%, #fff 100%);
}

/* ---------- More: gallery row ----------
   Figma 293:16711 / 16713 / 16717 — 185.28×223 cards spaced 196.4 apart (an
   11.1px gap), sitting 71 below the CTA and 57 above the dark section. Same
   marquee mechanic as the hero and driven by the hero's speed setting, so the
   two rows drift in step. Full-bleed, so it cancels the section's 28px gutter.
   No edge fade here — unlike the hero, the design runs the cards to the edge. */
/* The closing gap is section padding, not a margin on the row: as the last
   child of a block with no bottom padding, a margin-bottom here collapses
   straight out of the section and lands below the white instead of inside it.
   50 rather than the artboard's 57 — asked for on review. */
.sff-more-gallery { margin: 71px -28px 0; overflow: hidden; }
.sff-more:has(.sff-more-gallery) { padding-bottom: 50px; }
.sff-marquee--plain { -webkit-mask-image: none; mask-image: none; }
.sff-more-gallery .sff-marquee-track { gap: 11px; }
.sff-more-gallery .sff-gallery-item {
	width: 185.28px; height: 223px; border-radius: 20px;
	background: #F5F5FA; box-shadow: none;
}

/* ---------- Orbit motion ----------
   Three layers, all on their own timings so the loop never visibly repeats:
   the guide rings breathe, a sonar ping sweeps out from the mark, and each pill
   drifts on its own path. Everything is transform/opacity only — no layout
   thrash — and the whole lot is switched off under prefers-reduced-motion. */

/* Sonar: coloured rings emitted from the mark. These are the only rings here
   now — the design's three static grey guides were dropped, so there are four
   on a tighter stagger (~0.98s apart) to keep the space continuously occupied,
   and they hold a little more opacity than they did over the grey. */
.sff-orbit-ping {
	position: absolute; top: 90px; left: 50%; z-index: 1;
	width: 100px; height: 100px; margin: -50px 0 0 -50px;
	border: 1px solid var(--sff-accent); border-radius: 50%; opacity: 0;
	animation: sff-orbit-ping 3.9s cubic-bezier(.16, .62, .36, 1) infinite;
}
.sff-orbit-ping--2 { animation-delay: -.98s;  border-color: #1EC1FF; }
.sff-orbit-ping--3 { animation-delay: -1.96s; border-color: var(--sff-green); }
.sff-orbit-ping--4 { animation-delay: -2.94s; border-color: #478EFF; }
@keyframes sff-orbit-ping {
	0%   { scale: .58; opacity: 0; }
	14%  { opacity: .62; }
	100% { scale: 1.95; opacity: 0; }
}

/* The mark itself: a shallow breath with a soft brand glow at the peak. */
@keyframes sff-orbit-logo-breathe {
	0%, 100% { scale: 1;    box-shadow: 0 0 0 0 rgba(123, 88, 255, 0); }
	50%      { scale: 1.06; box-shadow: 0 0 26px 0 rgba(123, 88, 255, .3); }
}
.sff-orbit-logo { animation: sff-orbit-logo-breathe 5.2s ease-in-out infinite; }

/* Pills drift, each with its own amplitude, period and phase. Negative delays
   start them mid-cycle so they never line up. */
@keyframes sff-pill-float {
	0%, 100% { translate: 0 0; }
	50%      { translate: var(--fx, 0px) var(--fy, -7px); }
}
/* Colour runs green → purple → blue → cyan around the mark, picking up the
   gradient in the logo. Numbered by position in the dashboard list, so
   reordering the pills reorders the colours with them. */
.sff-pill-1 { --pill-bg: #BDF34D; --pill-fg: #0C082E; --pill-glow: rgba(140, 175, 60, .30); --fx: -6px; --fy: -8px; --fd: 7.5s;  --fdelay: -0.4s; }
.sff-pill-2 { --pill-bg: #7B58FF; --pill-fg: #fff;    --pill-glow: rgba(123, 88, 255, .34); --fx:  7px; --fy: -5px; --fd: 9.2s;  --fdelay: -2.6s; }
.sff-pill-3 { --pill-bg: #478EFF; --pill-fg: #fff;    --pill-glow: rgba(71, 142, 255, .32); --fx: -7px; --fy:  6px; --fd: 8.4s;  --fdelay: -5.1s; }
.sff-pill-4 { --pill-bg: #1EC1FF; --pill-fg: #0C082E; --pill-glow: rgba(30, 193, 255, .32); --fx:  5px; --fy:  8px; --fd: 10.1s; --fdelay: -1.7s; }
.sff-pill-5 { --pill-bg: #BDF34D; --pill-fg: #0C082E; --fx: -5px; --fy:  7px; --fd: 8.8s; --fdelay: -3.3s; }
.sff-pill-6 { --pill-bg: #7B58FF; --pill-fg: #fff;    --fx:  6px; --fy: -6px; --fd: 9.6s; --fdelay: -0.9s; }

@media (prefers-reduced-motion: reduce) {
	.sff-marquee-track, .sff-ring, .sff-orbit-logo, .sff-orbit-pill { animation: none; }
	.sff-orbit-ping { display: none; }
}

/* ---------- Framework ----------
   The section closes on a diagonal (Figma 293:16433 — `M0 0H440V2180L0 2094.17V0Z`,
   i.e. the right edge runs 85.83px lower than the left over the 440 artboard,
   about 11°). The wedge is drawn as a white triangle sitting in the bottom of
   this section rather than by clipping it, because what shows through has to be
   the light Why Choose section below, not the page's dark background.
   Below 440 it scales with the viewport so the angle itself stays constant. */
.sff-framework {
	position: relative;
	--sff-wedge: min(86px, 19.55vw);
	padding: 34px var(--sff-pad) calc(46px + var(--sff-wedge));
}
.sff-framework::after {
	content: ""; position: absolute; left: 0; right: 0; bottom: 0;
	height: var(--sff-wedge); background: #fff;
	clip-path: polygon(0 0, 100% 100%, 0 100%);
	pointer-events: none;
}
/* Centred two-line heading with the second line gradient-filled, then the green
   pill *under* it — same ramp as the Trusted and FAQ headings (38/41 SemiBold on
   the 440 artboard, scaled by vw below that so the two-line wrap survives). */
.sff-framework-heading {
	margin: 0 0 20px; padding: 0; text-align: center; color: #fff;
	font-size: min(38px, 8.64vw); line-height: 1.079; letter-spacing: -.02em; font-weight: 400;
}
/* Hard-break after the first line rather than relying on the wrap, as the
   design does — and `background-image`, not the `background` shorthand, which
   would reset background-clip and paint the gradient as a solid bar. */
.sff-framework-heading .sff-grad-text {
	display: block;
	background-image: linear-gradient(90deg, #7C59FF 0%, #33A1FF 100%);
}
/* The pill's own 12px bottom margin is for sitting above a heading; under one it
   needs the gap on top instead. */
.sff-pill-eyebrow--under { margin: 0 auto; }

.sff-steps { display: flex; flex-direction: column; gap: 16px; margin-top: 34px; }
.sff-step {
	background: var(--sff-bg-soft); border: 1px solid rgba(255,255,255,.07);
	border-radius: 20px; padding: 24px;
}
/* Baseline, not flex-start: the number and the (much larger) title sit on the
   same line rather than both hanging off the top of the row. */
.sff-step-top { display: flex; align-items: baseline; justify-content: space-between; gap: 14px; }
/* Deliberately loud — the step name is the thing to remember, so it runs close
   to hero scale and well above the section heading. Capped by vw so "Optimise"
   still clears the number on sub-440 handsets. */
.sff-step-title {
	margin: 0; font-size: min(38px, 9.4vw); line-height: 1.02; font-weight: 700;
	letter-spacing: -.95px; color: #fff;
}
.sff-step-num {
	font-size: 34px; font-weight: 800; line-height: 1; letter-spacing: -.68px;
	color: var(--sff-green); text-shadow: 0 0 26px rgba(189,243,77,.28);
}
.sff-step-sub { margin: 12px 0 12px; font-size: 15px; font-weight: 600; color: var(--sff-lav); }
.sff-step-body { margin: 0 0 16px; font-size: 14px; line-height: 1.6; color: var(--sff-body-lav); }
.sff-tags { display: flex; flex-wrap: wrap; gap: 8px; }
.sff-tag {
	font-size: 12px; font-weight: 600; padding: 7px 14px; border-radius: 20px;
	background: rgba(123,88,255,.16); color: var(--sff-lav);
}

/* Staggered entrance — the four steps introduce themselves one after another
   rather than arriving as a block. The attribute is added by frontend.js
   (`revealGroup()`, shared with the Why Choose tick rows), so without JS — or
   under reduced motion — the cards are simply there. Each card's own delay
   comes from --sff-reveal-delay, set per card as it enters view; the number and
   tags then cascade inside it. */
.sff-steps[data-sff-reveal] .sff-step {
	opacity: 0; transform: translateY(26px);
	transition: opacity .62s cubic-bezier(.22,.61,.36,1), transform .62s cubic-bezier(.22,.61,.36,1);
	transition-delay: var(--sff-reveal-delay, 0ms);
	will-change: opacity, transform;
}
.sff-steps[data-sff-reveal] .sff-step.is-revealed { opacity: 1; transform: none; }

.sff-steps[data-sff-reveal] .sff-step-num {
	opacity: 0; transform: translateY(-8px) scale(.82);
	transition: opacity .5s ease, transform .5s cubic-bezier(.34,1.36,.64,1);
	transition-delay: calc(var(--sff-reveal-delay, 0ms) + 240ms);
}
.sff-steps[data-sff-reveal] .sff-step.is-revealed .sff-step-num { opacity: 1; transform: none; }

.sff-steps[data-sff-reveal] .sff-tag {
	opacity: 0; transform: translateY(10px);
	transition: opacity .45s ease, transform .45s cubic-bezier(.22,.61,.36,1);
	transition-delay: calc(var(--sff-reveal-delay, 0ms) + 340ms);
}
.sff-steps[data-sff-reveal] .sff-tag:nth-child(2) { transition-delay: calc(var(--sff-reveal-delay, 0ms) + 410ms); }
.sff-steps[data-sff-reveal] .sff-tag:nth-child(3) { transition-delay: calc(var(--sff-reveal-delay, 0ms) + 480ms); }
.sff-steps[data-sff-reveal] .sff-tag:nth-child(n+4) { transition-delay: calc(var(--sff-reveal-delay, 0ms) + 550ms); }
.sff-steps[data-sff-reveal] .sff-step.is-revealed .sff-tag { opacity: 1; transform: none; }

/* Same treatment for the Why Choose rows — they come in one by one, each tick
   landing just after its row. Shorter travel and a quicker curve than the step
   cards: these are 75px rows, five of them, so the cards' 26px lift would read
   as sloppy at this size. `.sff .sff-ticklist li` to clear BeTheme, as above. */
.sff .sff-ticklist[data-sff-reveal] li {
	opacity: 0; transform: translateY(16px);
	transition: opacity .5s cubic-bezier(.22,.61,.36,1), transform .5s cubic-bezier(.22,.61,.36,1);
	transition-delay: var(--sff-reveal-delay, 0ms);
	will-change: opacity, transform;
}
.sff .sff-ticklist[data-sff-reveal] li.is-revealed { opacity: 1; transform: none; }

.sff-ticklist[data-sff-reveal] .sff-tick {
	opacity: 0; transform: scale(.6);
	transition: opacity .4s ease, transform .45s cubic-bezier(.34,1.36,.64,1);
	transition-delay: calc(var(--sff-reveal-delay, 0ms) + 160ms);
}
.sff-ticklist[data-sff-reveal] li.is-revealed .sff-tick { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
	.sff-steps[data-sff-reveal] .sff-step,
	.sff-steps[data-sff-reveal] .sff-step-num,
	.sff-steps[data-sff-reveal] .sff-tag,
	.sff-ticklist[data-sff-reveal] li,
	.sff-ticklist[data-sff-reveal] .sff-tick {
		opacity: 1; transform: none; transition: none;
	}
}

/* ---------- "Let's work together" band ----------
   Sketch Suite's [sketch_scroller], reused as-is. It down-scales itself on small
   viewports (--sksc-scale: 0.4 at ≤480), which is right for a full-width site
   band but wrong here: this page is a fixed 440 artboard, so it's pinned to 1
   and the sizes come from the shortcode attributes instead. */
.sff-lwg { margin: 44px 0 0; }
.sff-lwg .sksc { --sksc-scale: 1; }
.sff-lwg .sksc-item { line-height: 1.05; letter-spacing: -1.22px; }
.sff-fw-cta { margin-top: 40px; text-align: center; }

/* ---------- Why + stats ----------
   The light half of the page. Figma: pill 293:16564, heading 293:16531, rows
   293:16533/16543/16553/16570/16582, photo 293:16690. Side gutter here is 28,
   not --sff-pad, because the rows are 384 wide on the 440 artboard. */
.sff-why { background: #fff; color: var(--sff-bg); padding: 43px 0 0; }
.sff-why-inner { padding: 0 28px; }
.sff-pill-eyebrow {
	display: block; width: fit-content; margin: 0 auto 12px;
	background: var(--sff-green); color: #000; border-radius: 100px;
	padding: 5px 15px; font-size: 15px; font-weight: 600; letter-spacing: -.3px; line-height: 18px;
}
/* Shared by both light sections — same 38/41 SemiBold ramp in Figma (293:16531,
   297:16820). They only differ in the gap under the heading, set per-section. */
.sff-why-h2, .sff-faq-h2 {
	margin: 0 0 28px; text-align: center; color: var(--sff-bg);
	font-size: min(38px, 8.64vw); line-height: 1.08; font-weight: 600; letter-spacing: -.76px;
}
/* `.sff ul.sff-ticklist`, not `.sff-ticklist`: BeTheme ships `ul{margin-left:30px}`
   and `li{margin-bottom:10px}`, which outrank a single class — the first pushed
   the rows 30px right (354 wide instead of the designed 384) and the second
   doubled the 10px gap. Same trap as the testimonial blockquote. */
.sff ul.sff-ticklist {
	list-style: none; margin: 0; padding: 0;
	display: flex; flex-direction: column; gap: 10px;
}
.sff .sff-ticklist li {
	display: flex; align-items: center; gap: 16px; margin: 0;
	min-height: 75px; background: #F5F5FA; border-radius: 10px; padding: 12px 19px;
}
/* Own colour + weight: BeTheme sets `li` styling globally on the front end. */
.sff-tick-text { font-size: 18px; line-height: 22px; font-weight: 500; letter-spacing: -.36px; color: #000; }
.sff-tick { flex: 0 0 auto; display: block; width: 32px; height: 32px; }
.sff-tick svg { display: block; width: 32px; height: 32px; }

/* Full-bleed photo closing the section (Figma 440×472, the source image
   cropped from above centre so both figures stay in frame). */
.sff-why-photo { margin: 65px 0 0; }
.sff-why-photo img { width: 100%; height: min(472px, 107vw); object-fit: cover; object-position: center 38%; }

/* ---------- Discovery call steps ----------
   "Your First 30 Minutes With Us", a variation-only band sitting immediately
   above the questionnaire. It borrows the Why Choose anatomy wholesale — same
   pill, same 38/41 heading (`.sff-why-h2`), same 28 gutter giving 384-wide rows
   at 75 tall on the 440 artboard — so only the numeral column is new here.

   Bottom padding is 47 rather than the section's usual spacing because
   `.sff-quiz-intro` below is also white and adds its own 40: the two run
   together as one light block, and 47 + 40 is the gap the design leaves above
   the quiz heading. Change one and the join moves. */
.sff-disco { background: #fff; color: var(--sff-bg); padding: 43px 0 47px; }
.sff-disco-inner { padding: 0 28px; }

/* Authored lines, not wraps — see the note in frontend.php. */
.sff-disco-line { display: block; }

/* `.sff ol.sff-disco-steps` for the same reason as the ticklist: BeTheme ships
   `ul,ol{margin-left:30px}` and `li{margin-bottom:10px}`, which outrank a single
   class and would narrow the rows and double the gap. It also draws its own
   list marker on page content, hence `list-style: none` plus the ::before. */
.sff ol.sff-disco-steps {
	list-style: none; margin: 0; padding: 0;
	display: flex; flex-direction: column; gap: 10px;
	counter-reset: none;
}
.sff .sff-disco-steps li {
	display: flex; align-items: center; gap: 16px; margin: 0;
	min-height: 75px; background: #F5F5FA; border-radius: 10px; padding: 12px 24px 12px 19px;
}
.sff .sff-disco-steps li::before { content: none; }

/* Fixed 33px column so every row's text starts on the same x whatever the
   numeral's own width — "1" is a good deal narrower than "2". */
.sff-disco-num {
	flex: 0 0 33px; text-align: center;
	font-size: 44px; line-height: 1; font-weight: 600; letter-spacing: -.88px; color: #000;
}
/* Own colour + weight: BeTheme sets `li` styling globally on the front end. */
.sff-disco-text { font-size: 18px; line-height: 22px; font-weight: 500; letter-spacing: -.36px; color: #000; }

/* Same staggered entrance as the Why Choose rows — same size of row, same pace. */
.sff .sff-disco-steps[data-sff-reveal] li {
	opacity: 0; transform: translateY(16px);
	transition: opacity .5s cubic-bezier(.22,.61,.36,1), transform .5s cubic-bezier(.22,.61,.36,1);
	transition-delay: var(--sff-reveal-delay, 0ms);
	will-change: opacity, transform;
}
.sff .sff-disco-steps[data-sff-reveal] li.is-revealed { opacity: 1; transform: none; }

.sff-disco-steps[data-sff-reveal] .sff-disco-num {
	opacity: 0; transform: translateY(-6px) scale(.82);
	transition: opacity .4s ease, transform .45s cubic-bezier(.34,1.36,.64,1);
	transition-delay: calc(var(--sff-reveal-delay, 0ms) + 160ms);
}
.sff-disco-steps[data-sff-reveal] li.is-revealed .sff-disco-num { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
	.sff-disco-steps[data-sff-reveal] li,
	.sff-disco-steps[data-sff-reveal] .sff-disco-num {
		opacity: 1; transform: none; transition: none;
	}
}

/* .sff-stats / .sff-stat are unstyled on purpose — the stats block is parked
   (see the note in frontend.php). Style it with the design when it's rebuilt as
   the Figma carousel rather than reviving the old two-up row. */

/* ---------- Trusted (Figma heading 293:16725, cards 293:16765 / :16766 / :16786)
   Not a light card list — the same dark band and card anatomy as the testimonial:
   a full-bleed image tile, then the quote and the cite row *outside* it.
   Vertical rhythm, measured off the 1:1 Figma render of the 440 artboard
   (section band y7814–9447, all values in design px):
     53 top → heading (38/41, centred, 2nd line gradient) → 39 → tile 414×224 r30
     → 25 → quote (16/23) → 30 → cite row 61 → 51 between cards → 61 to the band
     end.
   Gutters differ per element (heading 27, quote and cite 19, tile 13), so the
   section pads to 19 and the tile is pulled back out by 6 — same trick as the
   testimonial. */
.sff-trusted { padding: 53px 19px 61px; }
.sff-trusted-heading {
	margin: 0; padding: 0; text-align: center; color: #fff;
	/* 38px on the 440 artboard; scales below that so both lines keep their wrap.
	   No side padding: "Ambitious Businesses" is 377 of the 402 available at 440,
	   and the type scales with the viewport, so any inset makes it wrap to three
	   lines on a 375 handset. */
	font-size: min(38px, 8.64vw); line-height: 1.079; letter-spacing: -.02em; font-weight: 600;
}
/* The design hard-breaks after the first line rather than relying on the wrap. */
.sff-trusted-heading .sff-grad-text { display: block; }
.sff-trusted-cards { display: flex; flex-direction: column; gap: 51px; margin-top: 39px; }

.sff-tcard-media {
	position: relative; margin: 0 -6px; border-radius: 30px; overflow: hidden;
	aspect-ratio: 414 / 224; background: #17133F;
}
.sff-tcard-photo { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
/* With a separate logo the design blurs the photo behind it and lays #0C082E at
   67% over the top. Scaling past the frame keeps the blur from feathering the
   rounded edge. A pre-composited tile (photo, scrim and logo already flattened
   into one export) carries its own treatment, so it renders untouched. */
.sff-tcard-media.has-logo .sff-tcard-photo { filter: blur(13.5px); transform: scale(1.14); }
.sff-tcard-scrim { position: absolute; inset: 0; background: rgba(12, 8, 46, .67); }
/* 298×60 inside 414×224 in the design — held as a ratio so a taller or wider
   client logo still sits in the same optical box. */
.sff-tcard-logo {
	position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
	width: 71.98%; height: 26.79%; object-fit: contain;
}
.sff-tcard .sff-cite-row { margin-top: 30px; }

/* Staggered entrance — the cards introduce themselves one after another instead
   of arriving as a block, on the same `revealGroup()` machinery as the framework
   steps and the Why Choose rows. frontend.js adds [data-sff-reveal] itself, so
   with JS off — or under reduced motion, where it bails first — the cards are
   simply there rather than stuck invisible.
   Inside a card the tile settles out of a slight scale, then the quote, then the
   cite row, so each one reads as a small sequence of its own. */
.sff-trusted-cards[data-sff-reveal] .sff-tcard {
	opacity: 0; transform: translateY(26px);
	transition: opacity .62s cubic-bezier(.22,.61,.36,1), transform .62s cubic-bezier(.22,.61,.36,1);
	transition-delay: var(--sff-reveal-delay, 0ms);
	will-change: opacity, transform;
}
.sff-trusted-cards[data-sff-reveal] .sff-tcard.is-revealed { opacity: 1; transform: none; }

.sff-trusted-cards[data-sff-reveal] .sff-tcard-media {
	transform: scale(.97);
	transition: transform .72s cubic-bezier(.22,.61,.36,1);
	transition-delay: var(--sff-reveal-delay, 0ms);
}
.sff-trusted-cards[data-sff-reveal] .sff-tcard.is-revealed .sff-tcard-media { transform: none; }

.sff-trusted-cards[data-sff-reveal] .sff-quote,
.sff-trusted-cards[data-sff-reveal] .sff-cite-row {
	opacity: 0; transform: translateY(10px);
	transition: opacity .45s ease, transform .45s cubic-bezier(.22,.61,.36,1);
}
.sff-trusted-cards[data-sff-reveal] .sff-quote { transition-delay: calc(var(--sff-reveal-delay, 0ms) + 190ms); }
.sff-trusted-cards[data-sff-reveal] .sff-cite-row { transition-delay: calc(var(--sff-reveal-delay, 0ms) + 290ms); }
.sff-trusted-cards[data-sff-reveal] .sff-tcard.is-revealed .sff-quote,
.sff-trusted-cards[data-sff-reveal] .sff-tcard.is-revealed .sff-cite-row { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
	.sff-trusted-cards[data-sff-reveal] .sff-tcard,
	.sff-trusted-cards[data-sff-reveal] .sff-tcard-media,
	.sff-trusted-cards[data-sff-reveal] .sff-quote,
	.sff-trusted-cards[data-sff-reveal] .sff-cite-row {
		opacity: 1; transform: none; transition: none;
	}
}

/* ---------- FAQ ----------
   White block, not a dark one: the Figma rows use #0C082E text over a 10%-black
   rule (297:16873…16901), which only reads on light. The rules are full-bleed
   (0→440 on the artboard), so the section itself carries no side padding — the
   head and the rows inset themselves, at 26 and 31/32 respectively. */
/* `flow-root` matters: the ring mark closing this section carries a -90 bottom
   margin, and with zero bottom padding that margin would collapse *out* of the
   section — the white would then run to the mark's foot instead of its centre. */
.sff-faq { background: #fff; color: var(--sff-bg); padding: 55px 0 0; display: flow-root; }
.sff-faq-head { padding: 0 26px; }
.sff-faq-h2 { margin-bottom: 48px; }
/* This pill is drawn 140 wide around a 107 label (297:16822), a touch roomier
   than the Why Choose one, so it takes its own inset. */
.sff-faq .sff-pill-eyebrow { padding-left: 16.5px; padding-right: 16.5px; }

.sff-accordion { display: block; }
.sff-acc-item { border-bottom: 1px solid rgba(0,0,0,.1); }
/* First row butts straight up under the heading's 48px gap; every later row
   opens its own 23px above the question, giving the 88px pitch in the design. */
.sff-acc-item + .sff-acc-item .sff-acc-q { padding-top: 23px; }
.sff-acc-q {
	width: 100%; display: flex; align-items: flex-start; justify-content: space-between; gap: 24px;
	padding: 0 32px 21px 31px; text-align: left;
	font-size: 18px; line-height: 22px; font-weight: 500; letter-spacing: -.36px; color: var(--sff-bg);
}
/* 18×18 box holding a 10.5748×18 glyph turned on its side, so the chevron reads
   18 wide × ~10.6 tall exactly as exported. 3px nudge centres it on line one. */
.sff-acc-icon { flex: 0 0 auto; display: flex; align-items: center; justify-content: center; width: 18px; height: 18px; margin-top: 3px; }
.sff-acc-icon svg { display: block; transform: rotate(90deg); transition: transform .25s ease; }
.sff-acc-q[aria-expanded="true"] .sff-acc-icon svg { transform: rotate(-90deg); }
.sff-acc-a { padding: 0 32px 24px 31px; }
.sff-acc-a p { margin: 0; font-size: 15px; line-height: 1.6; color: #4A4767; }

/* ---------- FAQ → final CTA ring mark (Figma 297:16912) ----------
   180×180, 10 below the last FAQ rule, and it *straddles the join*: the section
   ends on its centre line, so the top half sits on white and the bottom half on
   the page's dark background. The -90 bottom margin is what pulls the join up to
   the centre — change it and the mark stops being centred on the edge.
   Reuses `.sff-ring` / `.sff-orbit-logo` from the More section, whose 90px
   centre line is this box's centre too. Ring stroke differs though: this one is
   #B6B6B6 at 1 / .66 / .33, not #C6C6C6 at 1 / .6 / .4. */
.sff-bridge {
	position: relative; z-index: 1;
	width: 180px; height: 180px; margin: 10px auto -90px;
}
.sff-bridge .sff-ring { border-color: #B6B6B6; animation: sff-ring-pulse 3.2s ease-in-out infinite; }
.sff-bridge .sff-ring-2 { opacity: .66; animation-delay: .35s; }
.sff-bridge .sff-ring-3 { opacity: .33; animation-delay: .7s; }
/* Scale only — the per-ring opacity has to survive, and the rest position is the
   design's exact geometry, so 0%/100% must be an untouched `scale(1)`. */
@keyframes sff-ring-pulse {
	0%, 100% { transform: translate(-50%, -50%) scale(1); }
	50%      { transform: translate(-50%, -50%) scale(1.045); }
}

/* ---------- Final CTA ---------- */
/* Centred, and 113 at the top rather than the section's own spacing: the ring
   mark closing the FAQ overhangs this section by 90, and the design leaves 23
   between the foot of that mark and the heading. Side gutter is 26, which is
   what holds the body copy's designed three-line wrap. The artboard closes on
   201 of empty dark below the badge — that's just where the frame ends, and
   with a real footer under it that much space reads as a mistake, so it's 88. */
.sff-final { padding: 113px 26px 88px; text-align: center; }
.sff-final-heading {
	margin: 0 0 18px; padding: 0; text-align: center; color: #fff;
	font-size: min(38px, 8.64vw); line-height: 1.079; letter-spacing: -.02em; font-weight: 600;
}
/* Own line (the design hard-breaks rather than relying on the wrap), and its own
   ramp — bluer at both ends than the testimonial/trusted accent. */
/* `background-image`, not the `background` shorthand — the shorthand resets
   `background-clip` back to border-box and the gradient paints as a solid bar. */
.sff-final-heading .sff-grad-text {
	display: block;
	background-image: linear-gradient(90deg, #6D46FF 0%, #1EC1FF 100%);
}
.sff-final-p {
	margin: 0 0 32px; font-size: 18px; line-height: 22px; font-weight: 500;
	letter-spacing: -.36px; color: #fff;
}
/* The hero's badge, reused. Only the gap above it is this section's: 32 from the
   button, per the design. */
.sff-final .sff-reviews { margin-top: 32px; }

/* ---------- Footer ----------
   Not from the design — the artboard stops after the CTA. It sits in the 201px
   of dark the final section already closes on, separated by a hairline, and is
   deliberately quiet: nothing here should pull the eye off the CTA above it.
   `li` gets its own reset because BeTheme styles list items globally. */
.sff-footer { padding: 26px var(--sff-pad) 34px; border-top: 1px solid rgba(255,255,255,.08); text-align: center; }
.sff-footer-links {
	list-style: none; margin: 0 0 14px; padding: 0;
	display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 8px 22px;
}
/* `list-style` and the `::before` reset both matter: the theme draws its own
   bullet on list items inside page content, which the ul's `list-style: none`
   doesn't touch. Off by default, but it bit the moment a link was added. */
.sff-footer-links li { margin: 0; padding: 0; font-size: 13px; line-height: 1.4; list-style: none; }
.sff-footer-links li::before { content: none; display: none; }
.sff-footer-links a, .sff-footer-links span { color: rgba(255,255,255,.62); text-decoration: none; }
.sff-footer-links a:hover { color: #fff; }
.sff-footer-copy { margin: 0; font-size: 13px; line-height: 1.5; letter-spacing: -.13px; color: rgba(255,255,255,.42); }

/* ---------- Drawer ---------- */
.sff-drawer-overlay {
	position: fixed; inset: 0; z-index: 9999; display: flex; align-items: flex-end; justify-content: center;
}
.sff-drawer-overlay[hidden] { display: none; }
.sff-drawer-backdrop { position: absolute; inset: 0; background: rgba(6,4,24,.6); backdrop-filter: blur(2px); opacity: 0; transition: opacity .3s ease; }
/* The drawer is the same card as the inline Q1 modal — same purple, same 40px
   top corners, same inner gutter — so stepping from Q1 into Q2 reads as one
   continuous surface rather than a different screen. */
/* Full width, not capped at the 440 artboard: this opens inside an already
   narrow app shell, so capping it again just wasted the sides. */
.sff-drawer {
	position: relative; width: 100%; max-width: none; max-height: 92vh;
	background: #2B2270; color: #fff;
	border-radius: 40px 40px 0 0; padding: 0; overflow: hidden;
	transform: translateY(100%); transition: transform .34s cubic-bezier(.22,1,.36,1);
	box-shadow: 0 -20px 60px rgba(0,0,0,.4);
}
.sff-drawer-overlay.is-open .sff-drawer-backdrop { opacity: 1; }
.sff-drawer-overlay.is-open .sff-drawer { transform: translateY(0); }
.sff-drawer::before {
	content: ""; position: absolute; top: 10px; left: 50%; transform: translateX(-50%);
	width: 44px; height: 5px; border-radius: 3px; background: rgba(255, 255, 255, .28);
}
.sff-drawer-close {
	position: absolute; top: 16px; right: 18px; z-index: 3; width: 34px; height: 34px; border-radius: 50%;
	background: rgba(255, 255, 255, .12); color: #fff; font-size: 22px; line-height: 1; display: flex; align-items: center; justify-content: center;
}
.sff-drawer-close:hover { background: rgba(255, 255, 255, .2); }
.sff-drawer-inner { padding: 36px 28px 47px; overflow-y: auto; max-height: 92vh; -webkit-overflow-scrolling: touch; }

/* Progress — four rounded 3px rules, the reached ones white, the rest at 20%
   (Figma 246:16031). One copy per panel; updateProgress() keeps them in step. */
.sff-progress { display: flex; gap: 14px; margin: 0 0 27px; }
.sff-progress-seg { flex: 1; height: 3px; border-radius: 3px; background: rgba(255, 255, 255, .2); transition: background .25s ease; }
.sff-progress-seg.is-done { background: #fff; }

/* Drawer question panels — identical to the inline card, so the only rules
   left here are the ones the shared block can't cover. */
.sff-q-label-center { text-align: center; }

/* Drawer actions */
.sff-step-actions { display: flex; align-items: center; gap: 14px; margin-top: 30px; }
.sff-step-actions-center { justify-content: center; }
/* Recoloured for the purple drawer — these were all dark-on-white. */
.sff-back {
	background: rgba(255, 255, 255, .12); color: #fff; font-weight: 600; font-size: 15px;
	padding: 15px 28px; border-radius: 12px;
}
.sff-back:hover { background: rgba(255, 255, 255, .2); }
.sff-schedule {
	flex: 1; background: var(--sff-accent); color: #fff; font-weight: 600; font-size: 15px;
	padding: 15px 22px; border-radius: 12px; text-align: center;
}
.sff-schedule:hover { background: var(--sff-accent-hover); }

/* ---------- Booking step ----------
   The calendar is the one screen that goes back to white and full-bleed. Two
   reasons: Calendly renders its own white UI, which fought the purple; and the
   embed needs a definite full-height box or it clips its own scroll area.
   `is-booking` is set on the drawer by goToStep(). */
.sff-booking-title { margin: 4px 0 20px; font-size: 24px; font-weight: 600; color: #fff; text-align: center; }

.sff-drawer.is-booking {
	background: #fff; color: var(--sff-ink);
	height: 100dvh; max-height: 100dvh; border-radius: 0;
}
.sff-drawer.is-booking::before { background: #E2E1EC; }
.sff-drawer.is-booking .sff-drawer-close { background: #F1F0F6; color: #6E6B84; }
.sff-drawer.is-booking .sff-drawer-close:hover { background: #E5E4EE; }
.sff-drawer.is-booking .sff-q-label { color: var(--sff-accent); }
.sff-drawer.is-booking .sff-booking-title { color: var(--sff-ink); }
.sff-drawer.is-booking .sff-back { background: var(--sff-pill-2); color: var(--sff-ink); }
.sff-drawer.is-booking .sff-back:hover { background: #E1E0EC; }
/* Definite height the whole way down, so the embed can fill instead of clip. */
.sff-drawer.is-booking .sff-drawer-inner {
	height: 100%; max-height: 100dvh; display: flex; flex-direction: column; padding: 26px 18px 0;
}
.sff-drawer.is-booking .sff-booking { flex: 1 1 auto; display: flex; flex-direction: column; min-height: 0; }
.sff-calendly { flex: 1 1 auto; min-height: 460px; width: 100%; }
.sff-drawer.is-booking .sff-step-actions { flex: 0 0 auto; margin: 10px 0; }
.sff-calendly-fallback {
	background: var(--sff-pill); border: 1px dashed #CFCEDC; border-radius: 14px; padding: 28px 22px;
	text-align: center; color: #6E6B84; font-size: 14px; line-height: 1.6;
}

@media (max-width: 384px) {
	/* `.sff-hero-title { font-size: 40px }` used to sit here, and it defeated the
	   `min(45px, 10.23vw)` ramp above: below 384 the type stopped scaling and went
	   flat, while the 31px gutters stayed put, so the authored lines re-wrapped.
	   The original headline already broke as "to the next level" / "?" on a 320
	   handset because of it — it predates the variations. The ramp is the intended
	   behaviour (it's what keeps the authored stack from reflowing sub-440), and
	   40px happens to be roughly what the ramp gives at this breakpoint anyway, so
	   removing the override is continuous at 384 and simply keeps scaling below it. */
	/* Keep the header on one line on small phones (iPhone SE, small Androids). */
	.sff-header { padding: 11px 10px; gap: 6px; }
	.sff-logo { width: 46px; height: 46px; border-radius: 14px; }
	.sff-logo img { width: 28px; height: 28px; }
	/* Trimmed on small phones — the header has to stay on one line here. */
	.sff-header-mid { padding-left: 5px; }
	.sff-avail { font-size: 10px; }
	.sff-phone { font-size: 13px; }
	.sff-header-actions { padding: 4px 4px; gap: 1px; }
	.sff-book-btn { padding: 12px 14px; font-size: 13px; }
}


/* =========================================================================
   Desktop
   =========================================================================
   Everything above is the mobile artboard and stays the single source of
   truth — this layer only widens it. Two rules of thumb:

   1. Sections keep their full-bleed background band; it is the CONTENT that
      gets capped and centred. So the gutter is padding on the section (or on
      whichever inner wrapper already owned the side padding), never a
      max-width on the section itself.
   2. Text caps narrower than layout. A 1080px line of body copy is unreadable,
      so prose sits in `--sff-read` while grids and media get `--sff-max`.

   `max()` keeps a sane minimum gutter between the breakpoint and the point
   where the container actually fills, so nothing ever touches the edge.
   ========================================================================= */
@media (min-width: 768px) {
	.sff {
		--sff-max: 1080px;
		--sff-read: 680px;
		--sff-gut: max(40px, calc((100% - var(--sff-max)) / 2));
		--sff-gut-read: max(40px, calc((100% - var(--sff-read)) / 2));
	}

	/* ---- Containment ---- */
	.sff-header { padding-left: var(--sff-gut); padding-right: var(--sff-gut); }
	.sff-hero { padding: 72px var(--sff-gut-read) 56px; }
	.sff-quiz-head { padding-left: var(--sff-gut); padding-right: var(--sff-gut); }
	.sff-testimonial { padding-left: var(--sff-gut); padding-right: var(--sff-gut); }
	.sff-more { padding-left: var(--sff-gut); padding-right: var(--sff-gut); }
	.sff-why-inner { padding-left: var(--sff-gut); padding-right: var(--sff-gut); }
	.sff-trusted { padding-left: var(--sff-gut); padding-right: var(--sff-gut); }
	.sff-faq-head { padding-left: var(--sff-gut); padding-right: var(--sff-gut); }
	.sff-final { padding-left: var(--sff-gut-read); padding-right: var(--sff-gut-read); }
	.sff-footer { padding-left: var(--sff-gut); padding-right: var(--sff-gut); }
	/* FAQ rows carry their own asymmetric padding, so add the gutter to it
	   rather than replacing it — the 31/32 inset is the design's, not a stand-in
	   for a container. */
	.sff-acc-q { padding-left: calc(var(--sff-gut) + 31px); padding-right: calc(var(--sff-gut) + 32px); }
	.sff-acc-a { padding-left: calc(var(--sff-gut) + 31px); padding-right: calc(var(--sff-gut) + 32px); }

	/* ---- Type scale ---- */
	/* The mobile rule is min(45px, 10.23vw) — the vw arm only ever bites below
	   440, so on desktop this is just a straight bump off the 45px cap. */
	.sff-hero-title { font-size: 60px; margin-bottom: 26px; }
	.sff-hero-intro { max-width: 520px; font-size: 17px; }

	/* ---- Quiz: options two-up ---- */
	.sff-options {
		display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin: 0;
	}
	/* An odd option count would leave the last one stranded at half width. */
	.sff-option:last-child:nth-child(odd) { grid-column: 1 / -1; }
	.sff-option { padding-top: 20px; padding-bottom: 20px; }
	/* Contained, the inline Q1 card reads as a card rather than a sheet stuck to
	   the foot of the section, so it gets all four corners and room beneath. */
	.sff-quiz-intro { padding-bottom: 64px; }
	.sff-quiz-modal {
		max-width: var(--sff-max);
		margin: 0 auto;
		border-radius: 32px;
		padding: 44px 44px 48px;
	}

	/* ---- Testimonial: cap the card, it is portrait-ish media ---- */
	/* Left at full container width the 414:378 card would be ~1080 wide and
	   nearly as tall, which dwarfs everything around it. */
	.sff-tsm-carousel { max-width: 560px; margin-left: auto; margin-right: auto; }

	/* ---- The quiz drawer becomes a centred modal ---- */
	/* A bottom sheet is a thumb affordance; on a pointer device it reads as a
	   mobile artefact. Same card, re-anchored — and the grab handle goes, since
	   there is nothing to drag. */
	.sff-drawer-overlay { align-items: center; padding: 40px 20px; }
	.sff-drawer {
		max-width: 620px; max-height: 86vh;
		border-radius: 28px;
		transform: translateY(20px) scale(.985); opacity: 0;
		transition: transform .3s cubic-bezier(.22,1,.36,1), opacity .22s ease;
	}
	.sff-drawer-overlay.is-open .sff-drawer { transform: none; opacity: 1; }
	.sff-drawer::before { display: none; }
}

/* Three testimonial cards only go side by side once each one still has room to
   breathe — below this they stay stacked rather than becoming a cramped row or
   leaving an orphan on a second line. */
@media (min-width: 1000px) {
	.sff-trusted-cards {
		display: grid; grid-template-columns: repeat(3, 1fr);
		gap: 32px; align-items: stretch;
	}
	/* Quotes differ in length, so left to flow the three cite rows land at three
	   different heights and the row reads as ragged. Each card becomes a column
	   of full height with the cite pushed to the bottom, which lines the names up
	   across all three regardless of how long the quote above them runs. */
	.sff-tcard { display: flex; flex-direction: column; height: 100%; }
	.sff-tcard .sff-cite-row { margin-top: auto; }
}

@media (prefers-reduced-motion: reduce) {
	.sff-drawer { transition: none; }
}

/* -------------------------------------------------------------------------
   Testimonial, two-up (desktop)
   -------------------------------------------------------------------------
   Video left, words right. The heading is a sibling of the carousel rather
   than part of the slide, so the grid has to live on the section — and the
   two wrappers in between are flattened with `display: contents` to lift the
   video, quote and cite row up into it as real grid items. No markup change.

   Scoped to a SINGLE testimonial on purpose. `display: contents` removes the
   slide's box, and the multi-item carousel cross-fades by animating opacity
   and transform on exactly that box — so a second testimonial keeps the
   stacked layout, which still works, rather than silently losing its
   transition. `.is-multi` is already on the carousel for this reason.
   ------------------------------------------------------------------------- */
@media (min-width: 900px) {
	.sff-testimonial:has(.sff-tsm-carousel:not(.is-multi)) {
		display: grid;
		grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
		/* Equal fr rows top and bottom centre the text stack against the video,
		   which is the taller of the two columns. Top-aligning instead leaves the
		   words hanging off the top edge with a pool of dead space beneath them. */
		grid-template-rows: 1fr auto auto auto 1fr;
		column-gap: 56px;
		align-items: start;
		/* The 114px foot is mobile rhythm — the stacked layout needed it to
		   separate the cite row from the next section. Side by side it is just
		   a hole. */
		padding-bottom: 72px;
	}
	.sff-testimonial:has(.sff-tsm-carousel:not(.is-multi)) .sff-tsm-carousel,
	.sff-testimonial:has(.sff-tsm-carousel:not(.is-multi)) .sff-tsm-slide {
		display: contents;
	}
	.sff-testimonial:has(.sff-tsm-carousel:not(.is-multi)) .sff-video {
		grid-column: 1; grid-row: 1 / -1;
		align-self: start; margin: 0;
	}
	.sff-testimonial:has(.sff-tsm-carousel:not(.is-multi)) .sff-tsm-heading {
		grid-column: 2; grid-row: 2;
		text-align: left; padding: 0; margin-bottom: 22px;
	}
	.sff-testimonial:has(.sff-tsm-carousel:not(.is-multi)) .sff-quote {
		grid-column: 2; grid-row: 3;
		text-align: left; margin-top: 0;
	}
	.sff-testimonial:has(.sff-tsm-carousel:not(.is-multi)) .sff-tsm-foot {
		grid-column: 2; grid-row: 4;
	}
}

/* -------------------------------------------------------------------------
   Growth framework + Why choose, two-up (desktop)
   ------------------------------------------------------------------------- */
@media (min-width: 900px) {
	/* The framework section keeps its own vertical padding — the bottom value
	   carries the angled wedge that overlaps the next section — so only the
	   sides are replaced with the container gutter. */
	.sff-framework { padding-left: var(--sff-gut); padding-right: var(--sff-gut); }

	/* Steps 1–4 two-up. The heading and its eyebrow stay centred above, as they
	   are: the grid is on the step list, not on the section. */
	.sff-steps {
		display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
		gap: 20px; align-items: start;
	}

	/* Why choose: copy left, photo right. */
	.sff-why {
		display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
		column-gap: 56px; align-items: center;
		padding-left: var(--sff-gut); padding-right: var(--sff-gut);
		/* Was 0 — the photo used to run to the section's bottom edge. Inset in a
		   column it needs the section to close properly underneath it. */
		padding-bottom: 72px;
	}
	/* The gutter moved up to the section, so the inner must give its own back
	   or the left column is padded twice. */
	.sff-why-inner { padding: 0; }
	/* Centred type is right when this is one narrow column; ranged left it reads
	   as a deliberate pair with the photo rather than a stack that lost its
	   centre. Scoped to `.sff-why` so the FAQ heading, which shares
	   `.sff-faq-h2` with it, keeps its centring. */
	.sff-why .sff-why-h2 { text-align: left; }
	.sff-why .sff-pill-eyebrow { margin-left: 0; margin-right: auto; }
	.sff-why-photo { margin: 0; }
	.sff-why-photo img { height: 480px; border-radius: 24px; }
}

/* -------------------------------------------------------------------------
   Booking-only mode (Content → Questionnaire → "Show Calendly embed")
   -------------------------------------------------------------------------
   The calendar takes the whole section instead of the questions. It sits on
   the quiz section's white ground, so it cannot reuse `.sff-booking-title`
   and friends — those are coloured for the purple drawer and would come out
   white on white.
   ------------------------------------------------------------------------- */
.sff-quiz-intro--booking { padding: 40px 0 48px; text-align: center; }
.sff-booking-inline { padding: 0 var(--sff-pad); }
.sff-booking-inline .sff-pill-eyebrow { margin-bottom: 14px; }
.sff-booking-inline-h2 {
	margin: 0 0 24px; color: #0C082E; font-weight: 600; text-align: center;
	font-size: min(38px, 8.64vw); line-height: 1.08; letter-spacing: -.76px;
}
/* Calendly measures its own layout on init and will not re-flow into a box that
   had no height — the iframe takes the container's height and scrolls its own
   content inside if that is too small. So the height is set here, generously:
   too short and the visitor scrolls a calendar inside a scrolling page, which is
   the one interaction guaranteed to lose a booking.

   Mobile stacks the whole flow — month grid, then the time list under it — so it
   needs far more room than the desktop two-pane view. It also runs edge to edge:
   the widget's own layout is tight at 328px, and the section gutter was costing
   62px it could not spare. The negative margins break it out of
   `.sff-booking-inline` without pulling the heading out with it. */
.sff-calendly--inline {
	/* `height`, not `min-height`: Calendly's iframe is sized `height: 100%`, and a
	   percentage height only resolves against a parent with a DEFINITE height. On
	   `min-height` alone it stays indefinite and the iframe collapses to the HTML
	   default of 150px inside a correctly-tall box. (The drawer's copy of this
	   embed escapes it by being a flex item, which gives it a definite used
	   height — which is why that one has always looked right.) */
	height: 1180px; width: auto; margin: 0 calc(var(--sff-pad) * -1);
	background: #fff; overflow: hidden;
}
.sff-quiz-intro--booking .sff-calendly-fallback { margin: 0 auto; max-width: 520px; }

@media (min-width: 768px) {
	.sff-quiz-intro--booking { padding-bottom: 64px; }
	.sff-booking-inline { padding-left: var(--sff-gut); padding-right: var(--sff-gut); }
	/* Desktop puts the month and the times side by side, so it needs far less
	   height than the stacked mobile layout — and here the container has width to
	   spare, so it sits inside the gutter with its corners back rather than
	   breaking out full-bleed. */
	.sff-calendly--inline {
		height: 820px; margin-left: auto; margin-right: auto; width: 100%;
		border-radius: 18px;
	}
}

/* -------------------------------------------------------------------------
   Thank-you page ([sketch_fit_finder_thanks])
   -------------------------------------------------------------------------
   Where Calendly hands off after a booking. Shares the landing page's ground,
   tokens and typeface so it reads as the same site — it is just far shorter,
   and centred, because it has one thing to say.

   `min-height: 100svh` (not vh) so it fills the screen without the mobile
   browser chrome pushing the footer of it out of reach.
   ------------------------------------------------------------------------- */
.sff-thanks {
	min-height: 100vh;
	min-height: 100svh;
	display: flex; flex-direction: column;
	background: var(--sff-bg); color: #fff;
}
.sff-thanks .sff-header { justify-content: flex-start; }
.sff-thanks-body {
	flex: 1 1 auto;
	display: flex; flex-direction: column; align-items: center; justify-content: center;
	text-align: center; padding: 48px var(--sff-pad) 72px;
}
.sff-thanks-tick { display: block; width: 56px; height: 56px; margin: 0 auto 22px; }
.sff-thanks-tick svg { width: 100%; height: 100%; display: block; }
.sff-thanks .sff-pill-eyebrow { margin-bottom: 16px; }
.sff-thanks-h1 {
	margin: 0 0 18px; color: #F5F5FA; font-weight: 600;
	font-size: min(45px, 10.23vw); line-height: 1.067; letter-spacing: -.02em;
}
.sff-thanks-lead { max-width: 480px; margin: 0 auto; }
.sff-thanks-lead p {
	margin: 0 0 14px; font-size: 16px; line-height: 1.55; color: #D8D6EC;
}
.sff-thanks-lead p:last-child { margin-bottom: 0; }
/* The CTA is an <a> here rather than the <button> it is on the landing page,
   so it needs the anchor's defaults unpicked. */
.sff-thanks-cta { margin-top: 32px; text-decoration: none; }

@media (min-width: 768px) {
	.sff-thanks-body { padding: 64px var(--sff-gut-read) 88px; }
	.sff-thanks-h1 { font-size: 56px; }
	.sff-thanks-lead p { font-size: 17px; }
}

/* -------------------------------------------------------------------------
   Thank-you page — the arrival
   -------------------------------------------------------------------------
   This page only ever loads once, straight after someone books, so the whole
   thing is a page-load animation and needs no JS or scroll trigger.

   The tick lands first with a spring and throws a ring outwards, then the
   words rise in behind it. The stagger is what sells it: everything arriving
   at once reads as a page render, one after another reads as a response to
   something the visitor just did.
   ------------------------------------------------------------------------- */
@keyframes sff-thanks-pop {
	0%   { opacity: 0; transform: scale(.3); }
	55%  { opacity: 1; transform: scale(1.12); }
	75%  { transform: scale(.96); }
	100% { opacity: 1; transform: scale(1); }
}
/* Two rings on the same path, the second delayed, so it reads as a pulse
   rather than a single puff. */
/* Starts exactly on the tick's edge and travels outward, so it always reads as
   something leaving the disc rather than a circle arriving over it. */
@keyframes sff-thanks-ring {
	0%   { opacity: .55; transform: scale(1); }
	100% { opacity: 0;   transform: scale(2.5); }
}
@keyframes sff-thanks-rise {
	from { opacity: 0; transform: translateY(16px); }
	to   { opacity: 1; transform: none; }
}

.sff-thanks-tick {
	position: relative;
	animation: sff-thanks-pop .72s cubic-bezier(.34, 1.56, .64, 1) both;
}
/* Coloured off the tick glyph's own disc (#32BA7C from check_svg), NOT off
   --sff-green (#BDF34D). They are different greens: the lime is the pill/CTA
   accent, and a lime halo around a jade disc reads as an unrelated circle that
   happens to be passing through rather than as part of the tick.

   `inset: 0` rather than a centred translate, so the ring's own transform is
   pure scale — that keeps it concentric with the tick with no translate to
   preserve in the keyframes, and means it can never eat clicks or shift layout
   without needing a negative z-index to stay out of the way. */
.sff-thanks-tick::before,
.sff-thanks-tick::after {
	content: ""; position: absolute; inset: 0;
	border-radius: 50%;
	border: 2px solid #32BA7C;
	pointer-events: none;
	/* Runs forever rather than once. The two rings share a 2.4s cycle offset by
	   exactly half of it, so one is always mid-flight and the pulse reads as
	   continuous instead of as a burst followed by silence. */
	animation: sff-thanks-ring 2.4s ease-out infinite;
}
.sff-thanks-tick::before { animation-delay: .34s; }
.sff-thanks-tick::after  { animation-delay: 1.54s; }

.sff-thanks .sff-pill-eyebrow,
.sff-thanks-h1,
.sff-thanks-lead,
.sff-thanks-cta {
	animation: sff-thanks-rise .6s cubic-bezier(.22, .61, .36, 1) both;
}
.sff-thanks .sff-pill-eyebrow { animation-delay: .30s; }
.sff-thanks-h1                { animation-delay: .40s; }
.sff-thanks-lead              { animation-delay: .52s; }
.sff-thanks-cta               { animation-delay: .66s; }

/* A confirmation is exactly the wrong place to make someone sit through
   motion they have asked not to see — everything lands immediately instead. */
@media (prefers-reduced-motion: reduce) {
	.sff-thanks-tick,
	.sff-thanks .sff-pill-eyebrow,
	.sff-thanks-h1,
	.sff-thanks-lead,
	.sff-thanks-cta { animation: none; }
	.sff-thanks-tick::before,
	.sff-thanks-tick::after { display: none; }
}
