/* ==========================================================================
   GrowthStudioz, services band

   A flame with the service icons sitting round it. As the section scrolls
   past, the icons circle the fire together, clockwise, and each peels off when
   it reaches the point facing its own service, then drops into its row; each
   service's words appear as its icon lands (Adarsh, 6th October 2026). Not
   pinned: the page scrolls normally throughout.

   Type and geometry are off the Figma inspector (Adarsh, 24th September 2026):

     heading        600, 64px desktop / 32px phone, line height 100%,
                    tracking -3.02px desktop and 0 on the phone, #0E0E0E
     service name   600, 24px / 16px, line height 100%, #1B1B1B
     service body   400, 20px / 12px, line height 100%, #838383
     ring tile      43.35sq, radius 10.51, border 1.31
     list tile      80.31 x 77.36, radius 18.9, border 2.36
     tile fill      #EBEBEB
     flame          131.57 x 138.22 / 100.15 x 105.21

   The tile itself, grey field, division-coloured edge, radius, is drawn INTO
   the supplied SVGs, so none of it is styled here. Each division ships its own
   icon set and therefore its own colour; nothing in CSS needs to know.
   ========================================================================== */

.gs-services {
	/* Tile size in the list. The ring's smaller tile is the same artwork
	   scaled: 43.35 to 80.31 is x1.85, and the phone's 33 to 62.9 is x1.91 ,
	   near enough identical that one scale expression covers both. */
	--gs-svc-tile-w: 80.31px;
	--gs-svc-tile-h: 77.36px;

	/* Tile size out on the ring, which the trails measure their far end from.
	   The icons themselves reach it by scaling: 43.35 to 80.31 is the x1.85
	   above, run backwards. */
	--gs-svc-ring-tile: 43.35px;

	--gs-svc-flame-w: 131.57px;

	/* How far out the ring reaches, sideways, expressed AS A MULTIPLE OF THE
	   FLAME rather than as a length of its own. The reference's flame spans
	   275px and its ring reaches 372: the ring is 1.353 flames wide, and that
	   ratio is the thing the design actually fixes. Tie the two together and
	   the ring can never drift out of proportion with the artwork again, at any
	   size, on any screen, which is exactly what went wrong when this was a
	   viewport-relative length and the fire ended up lost in the middle of it.

	   The reading checks out against a number we were given rather than one
	   measured off the same picture: at the scale that makes the reference's
	   flame 131.57px, its tiles come to 41px, against the 43.35 in the Figma
	   inspector.

	   PHP supplies each slot's own direction as a pair of fractions of this,
	   because the design's ring is wide and shallow rather than circular, see
	   growthstudioz_services_ring(). */
	--gs-svc-reach: 1.353;
	--gs-svc-radius: calc(var(--gs-svc-flame-w) * var(--gs-svc-reach));

	/* The ring's own height: how far it reaches above the flame plus how far
	   below, plus a tile. --gs-svc-up and --gs-svc-down are written by PHP and
	   differ, because the design hangs the ring low rather than centring it on
	   the fire. */
	--gs-svc-stage-h: calc(var(--gs-svc-radius) * (var(--gs-svc-up, 1) + var(--gs-svc-down, 1)) + var(--gs-svc-ring-tile));

	/* The dot trails between the flame and each icon. UX19's yellow, which is
	   also the edge colour drawn into that division's icon SVGs; a division
	   with another colour overrides this one property. */
	--gs-svc-dot: #FFDD00;

	/* No min-height and no pin: the band is its natural height, and the motion
	   runs while it scrolls past. */
	padding-block: var(--gs-section-pad);
	/* CLIP, not hidden, and only on the x axis: an icon out at the ring must
	   not be able to widen the document, while anything that overhangs
	   vertically still shows. `clip` does that without creating a scroll
	   container. */
	overflow-x: clip;
}

/* More air between the six-icon ring and the first service (Adarsh, 25th
   September 2026): the ring's lowest icon sat 24px above Brand Strategy. */
.gs-services[data-gs-count="6"] .gs-services__stage {
	margin-block-end: var(--gs-space-7);   /* 48px */
}

/*
 * The panel: heading, then the ring, then the list, each in its own place.
 * Nothing inside it moves but the icons (and the words fading up).
 */
.gs-services__panel {
	display: grid;
}

/* --- heading -------------------------------------------------------------- */

.gs-services__title {
	margin: 0;
	text-align: center;
	line-height: 1;
	/* base.css gives every h1-h6 the display tracking; the design's -3.02px at
	   64px is a different amount, and the phone takes none at all. */
	letter-spacing: -0.0472em;
	color: #0E0E0E;
}

/* --- the ring and the list ------------------------------------------------ */

/*
 * Stacked in normal flow, with the icons travelling the gap between them. That
 * gap IS the animation, so the two are laid out exactly as they are read.
 */
.gs-services__scenes {
	display: grid;
}

/* --- the hub -------------------------------------------------------------- */

/*
 * The stage RESERVES the ring's whole bounding box, rather than guessing a top
 * margin. The icons are positioned by transform, so they take no layout space
 * of their own and would otherwise sit on the heading.
 *
 * A previous version used `radius * 0.7`, which is a number that happens to
 * clear the heading at some viewport sizes and not others, the collision came
 * back the moment the radius or the flame changed.
 *
 * The margin below is what separates the ring from the first service, so the
 * reserve has to be right or that margin is eaten from the inside.
 */
.gs-services__stage {
	display: grid;
	place-items: center;
	min-height: var(--gs-svc-stage-h);
	/*
	 * Padding, to put the flame where the ring needs it rather than in the
	 * middle.
	 *
	 * The stage centres its contents, which is wrong here: the ring hangs low,
	 * so a centred flame pushes its bottom icon out of the box and onto the
	 * first service. Box-sizing is border-box, so this padding comes OUT of the
	 * height above, and centring within what is left lands the flame exactly
	 * --gs-svc-up radii from the top. The difference between the two reaches is
	 * therefore paid for once, at the bottom, where the ring actually goes.
	 */
	padding-block-end: calc(var(--gs-svc-radius) * (var(--gs-svc-down, 1) - var(--gs-svc-up, 1)));
	margin-block: var(--gs-space-5);
}

/* The flame and the ring occupy the same cell, so the ring is centred on the
   flame without either being positioned against the other. */
.gs-services__stage > * {
	grid-area: 1 / 1;
}

.gs-services__flame {
	display: block;
	width: var(--gs-svc-flame-w);
}

.gs-services__flame-art {
	display: block;
	width: 100%;
	height: auto;
}

/* --- the ring layer ------------------------------------------------------- */

/*
 * Holds the dot trails, and only those: the icons that gather on the ring are
 * the list's own, so a second set in the same place would double every tile.
 */
.gs-services__ring {
	display: grid;
	place-items: center;
	margin: 0;
	padding: 0;
	list-style: none;
	/* Below the flame, so a trail's near end tucks under the artwork rather
	   than stopping short of it in mid-air. Grid items take z-index without
	   being positioned, so this needs no position and creates no new
	   containing block. */
	z-index: 0;
}

.gs-services__flame {
	z-index: 1;
}

/*
 * This slot's direction from the flame. The ring layer reads these for the
 * resting spots and the dot trails; the moving icons are placed by the script.
 */
.gs-service,
.gs-services__orbit {
	--rfx: var(--gs-svc-fx);
	--rfy: var(--gs-svc-fy);
}


.gs-services__orbit {
	grid-area: 1 / 1;
	display: grid;
	place-items: center;
	position: relative;
	width: var(--gs-svc-ring-tile);
	height: var(--gs-svc-ring-tile);

	/* Only the scale is CSS's, so the ring can answer to viewport width without
	   a re-measure. */
	translate:
		calc(var(--gs-svc-radius) * var(--rfx))
		calc(var(--gs-svc-radius) * var(--rfy));
}

/* --- the dot trails ------------------------------------------------------- */

/*
 * A bar reaching from the flame out to this slot's icon, painted with four
 * dots of different sizes. Drawn rather than supplied: the flame SVG carries
 * one stray dot and nothing else, so the trails in the design are not artwork
 * this build has.
 *
 * Laid out from the ring's centre and rotated to point at the icon, so one rule
 * serves every slot. It sits in the same grid cell as the icon, which is out at
 * the ring, so it is pulled back by the same translate the icon was pushed by.
 */
.gs-services__trail {
	/*
	 * ABSOLUTE, not a grid item. The bar is far wider than the tile it sits in,
	 * and centring an item that overflows its area falls back to start
	 * alignment, so as a grid item it landed a variable distance off, in a way
	 * that changed with the radius. Positioned from the tile's centre it is
	 * exact at any size.
	 *
	 * left: 50% puts its LEFT EDGE on the tile's centre; the negative top
	 * margin puts its vertical middle there too.
	 */
	position: absolute;
	left: 50%;
	top: 50%;
	margin-top: -16px;
	width: calc(var(--gs-svc-radius) * var(--gs-svc-len));
	height: 32px;

	/*
	 * Undo the orbit's own offset, which lands that left edge on the flame's
	 * centre, then rotate about it to swing the bar back out to the icon.
	 * translate runs before rotate in the individual-property order, which is
	 * what makes this one step rather than two elements.
	 */
	translate:
		calc(var(--gs-svc-radius) * var(--rfx) * -1)
		calc(var(--gs-svc-radius) * var(--rfy) * -1);
	/* Points at its own icon. */
	rotate: var(--gs-svc-rot);
	transform-origin: 0 50%;

	/*
	 * Dots placed at real distances from the flame, not at percentages of the
	 * trail.
	 *
	 * Percentages put the first dot 63px out on a trail whose flame is 66px
	 * across, so it landed ON the artwork, and the last one within 8px of the
	 * tile. A percentage cannot know either of those edges; the trails are five
	 * different lengths, so the same percentage sits in a different place on
	 * each one.
	 *
	 * Measured from the two things that must stay clear instead: the flame's own
	 * half-width at the near end and the tile's half-width at the far end, with
	 * a gap beyond each. The gap is a share of the flame, so it holds at every
	 * size, the phone's smaller fire gets a proportionally smaller one.
	 *
	 * Three dots rather than four: the shortest trail, down to the eye, has only
	 * about 15px of clear run on it, and a fourth would have nowhere to go.
	 */
	--gs-svc-dot-gap: calc(var(--gs-svc-flame-w) * 0.11);
	--gs-svc-dot-in: calc(var(--gs-svc-flame-w) / 2 + var(--gs-svc-dot-gap));
	--gs-svc-dot-out: calc(
		var(--gs-svc-radius) * var(--gs-svc-len)
		- var(--gs-svc-ring-tile) / 2
		- var(--gs-svc-dot-gap)
	);
	--gs-svc-dot-mid: calc((var(--gs-svc-dot-in) + var(--gs-svc-dot-out)) / 2);

	/* Largest nearest the tile, as in the reference, and none on the centreline.
	   The vertical scatter rotates with the bar, so no two trails read alike
	   even though one rule draws all five. */
	background-image:
		radial-gradient(circle 3.5px at var(--gs-svc-dot-in) 24%, var(--gs-svc-dot) 98%, transparent 100%),
		radial-gradient(circle 2px at var(--gs-svc-dot-mid) 72%, var(--gs-svc-dot) 98%, transparent 100%),
		radial-gradient(circle 4.5px at var(--gs-svc-dot-out) 44%, var(--gs-svc-dot) 98%, transparent 100%);
	background-repeat: no-repeat;
}

/* --- the list ------------------------------------------------------------- */

/*
 * COLUMN flow, three rows. The design reads down the left column then the
 * right; a default row flow would put services 1 and 2 side by side and the
 * reading order would disagree with the design.
 */
/*
 * 1150px wide and 80px between the columns in the 1440 design, kept as
 * proportions (Adarsh, 26th September 2026): 87% of the page column, gap
 * 6.96% of the list, so a smaller laptop gets the same picture. Capped at the
 * design's 1150 so a big screen does not stretch it, and never narrower than
 * 960 (or the whole column, if that is less), so a small laptop keeps room for
 * two columns.
 */
.gs-services__list {
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	grid-template-rows: repeat(3, auto);
	grid-auto-flow: column;
	gap: var(--gs-space-6) 6.96%;
	width: 100%;
	max-width: clamp(min(100%, 60rem), 87%, 71.875rem);
	margin: 0 auto;
	padding: 0;
	list-style: none;
}

/*
 * Short screens keep the full column (Adarsh, 26th September 2026). Narrower
 * columns wrap every description one line more, and at 1280x720 the taller
 * list could not share the screen with the ring: a landed row dipped 53px
 * under the bottom edge while the last icons took off.
 */
@media (min-width: 901px) and (max-height: 760px) {
	.gs-services__list {
		max-width: none;
		column-gap: var(--gs-space-8);
	}
}

.gs-service {
	display: flex;
	align-items: flex-start;
	gap: var(--gs-space-4);
	max-width: none;
}

/* --- the tile ------------------------------------------------------------- */

/*
 * NO border, fill or radius here. The supplied SVGs already ARE the tile ,
 * the grey field and the division's yellow edge are drawn into the artwork ,
 * so styling this box too painted a second tile around the first.
 *
 * This element is now purely the sizing box and the thing that moves.
 */
.gs-service__icon {
	display: grid;
	flex: none;
	width: var(--gs-svc-tile-w);
	height: var(--gs-svc-tile-h);
}

/* The artwork, inside the box the script moves. */
.gs-service__icon-art {
	display: block;
	width: 100%;
}

/* ==========================================================================
   Motion

   Everything below is behind `is-live`, which the script adds once it has
   measured. With no JavaScript, a blocked script, an error or reduced motion
   the section stays a plain, complete, readable list.

   The script places each icon every frame (one transform per icon, written
   straight onto the element): where it is on the oval, then along its path
   to its row. CSS only owns what does not move with the scroll.
   ========================================================================== */

/*
 * Out at the ring the icon is the ring's smaller tile, 43.35 against the
 * list's 80.31, so it is scaled from its centre; the script grows it back to
 * full size on the way down.
 *
 * Stacking: on the near half of the oval an icon is in front of the flame, on
 * the far half behind it (the flame is 1), which is what makes the circle
 * read as going round the fire. The script sets which. Words are above both,
 * so an icon passing a finished row goes under its words.
 */
.gs-services.is-live .gs-service__icon {
	position: relative;
	z-index: 12;
	transform-origin: 50% 50%;
	will-change: transform;
}

.gs-services.is-live .gs-service__text {
	position: relative;
	z-index: 30;
}

/* The dot trails mark the resting ring, so they only show once the script
   has put the icons on it; they fade as it starts to turn (the script sets
   the ring layer's opacity). Without the script there is no ring to point
   at, just the list. */
.gs-services__trail {
	opacity: 0;
}

.gs-services.is-live .gs-services__trail {
	opacity: 1;
}

.gs-services.is-live .gs-services__ring {
	will-change: opacity;
}

/* Reading the layout, not the animation. getBoundingClientRect() returns the
   TRANSFORMED box, so the script switches everything off while it measures ,
   otherwise each read starts from wherever the icon currently sits and the
   offsets compound. */
.gs-services.is-measuring .gs-services__title,
.gs-services.is-measuring .gs-services__scenes,
.gs-services.is-measuring .gs-services__stage,
.gs-services.is-measuring .gs-service,
.gs-services.is-measuring .gs-service__icon,
.gs-services.is-measuring .gs-service__icon-art,
.gs-services.is-measuring .gs-services__orbit-art,
.gs-services.is-measuring .gs-service__text {
	translate: none;
	scale: none;
	opacity: 1;
	transition: none;
}

/* Inline transforms are the script's, so the read has to beat them. */
.gs-services.is-measuring .gs-service__icon {
	transform: none !important;
}

.gs-service__icon-img {
	display: block;
	width: 100%;
	height: auto;
}

/* --- the copy ------------------------------------------------------------- */

.gs-service__text {
	min-width: 0;
}

.gs-service__name {
	margin: 0;
	max-width: none;
	color: #1B1B1B;
	font-size: 1.5rem;                /* 24px */
	font-weight: var(--gs-weight-semibold);
	line-height: 1;
	letter-spacing: 0;
}

.gs-service__body {
	margin: var(--gs-space-3) 0 0;
	max-width: none;
	color: #838383;
	font-size: 1.25rem;               /* 20px */
	font-weight: var(--gs-weight-regular);
	/* 100% in the design would collide; the design's own frame shows the lines
	   breathing, so this is the one place a literal 100% is not taken. */
	line-height: 1.35;
	letter-spacing: 0;
}

/* --- editor-only empty state ---------------------------------------------- */

.gs-services--empty {
	display: grid;
	place-items: center;
	min-height: 160px;
	padding: var(--gs-space-6);
	border: 1px dashed var(--gs-border);
	background: var(--gs-surface-alt);
	color: var(--gs-body);
}

.gs-services--empty p {
	margin: 0;
	max-width: none;
}

/* ==========================================================================
   Phones
   ========================================================================== */

@media (max-width: 900px) {
	.gs-services {
		--gs-svc-tile-w: 62.9px;
		--gs-svc-tile-h: 60.59px;

		--gs-svc-ring-tile: 33px;

		/* No radius override any more: it follows the flame, so shrinking the
		   flame shrinks the ring with it and the proportion is kept for free.
		   The 26vw floor is what keeps the ring inside a narrow phone, it is
		   wider than it is tall, so width runs out first. It only bites below
		   about 385px wide; at 390 the design's own 100.15 still applies. */
		--gs-svc-flame-w: min(100.15px, 26vw);
	}

	/* The phone's own: 16px to 40px. */
	.gs-services[data-gs-count="6"] .gs-services__stage {
		margin-block-end: 2.5rem;              /* 40px */
	}

	/* No tracking on the phone, where desktop takes -3.02px. */
	.gs-services__title {
		letter-spacing: 0;
	}

	/*
	 * Tighter than the desktop rhythm, because on a phone the ring and the
	 * whole list have to share one screen and the list is five deep rather than
	 * three. At 390x844 this is what takes the section from 856 tall, which
	 * overflows, to 808. Everything the flight needs is then on screen, which is
	 * the condition for an icon to be seen landing rather than just leaving.
	 */
	.gs-services__stage {
		margin-block: var(--gs-space-4);
	}

	.gs-services__list {
		grid-template-columns: minmax(0, 1fr);
		grid-template-rows: none;
		grid-auto-flow: row;
		gap: var(--gs-space-4);
		max-width: none;
	}

	.gs-service__name {
		font-size: 1rem;              /* 16px */
	}

	.gs-service__body {
		font-size: 0.75rem;           /* 12px */
	}
}

/* ==========================================================================
   Reduced motion

   The mechanism is DISMANTLED, not just slowed: the blanket rule in base.css
   only kills durations, which would leave the transparent copy in place. The
   script never sets `is-live` when motion is reduced, and these rules cover the
   case where it had already started before the setting changed.

   The ring is a transition state, so with no transition it has no reason to
   exist, what is left is the flame and a plain, complete list.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
	/* Pinned to their end state, so nothing here depends on the script having
	   run or on whatever value it last wrote. */
	.gs-services .gs-service,
	.gs-services .gs-service__icon,
	.gs-services .gs-service__icon-art,
	.gs-services .gs-service__text,
	.gs-services .gs-services__stage {
		translate: none;
		scale: none;
		opacity: 1;
		transition: none;
	}

	/* The script writes these inline, so the end state has to outrank it. */
	.gs-services .gs-service__icon {
		transform: none !important;
		z-index: auto;
	}

	.gs-services .gs-service__text {
		opacity: 1 !important;
		translate: none !important;
	}

	/* The dot trails point from the flame to a ring that never forms, so with
	   the motion gone they are five lines to nowhere. */
	.gs-services .gs-services__ring {
		display: none;
	}
}
