/* =============================================================================
   Quote carousel — the "What our clients say" section.

   A component rather than page CSS because it is the one part of Our Work that
   the two Figma frames describe as two different things, and the difference is
   structural rather than a width:

     · desktop — Figma uH7Mitm4GF53rGZGa6URrr, frame 961:4985. A 3x2 grid of
       black tiles carrying a client logo. Hovering one flips it to a light
       panel with the quote. Six tiles, all visible, one face at a time.

     · mobile — Figma v4zuHnfQeDWmJsqAyNqiDq, frame 1795:106. ONE client at a
       time — literally one: a slide fills the track, so no neighbour is ever
       on screen — and its two panels are stacked and both visible: a 298x134
       logo panel above a 298x385 quote panel. Nothing flips. Under it sit six
       square dots and the line "← swipe to explore →".

   So the markup carries both faces at every width and this file decides what
   they are. Above the mobile cut the faces are stacked in 3D and
   components/flip-card.css turns them; at 767 and below they unstack into
   ordinary flow and the track becomes a scroll-snap carousel.

   WHY THAT MATTERS BEYOND LAYOUT. The desktop flip is hover-and-keyboard only
   — a deliberate narrowing recorded in flip-card.css — so on a touch screen
   there was no way to see a quote at all; it existed only in the accessibility
   tree. The mobile frame does not solve that with a tap target, it solves it by
   not hiding the quote in the first place. This file is where that lands.

   The quote panel is also a different colour in the two frames: light
   (--color-sand-pale) on desktop, BLACK on mobile, with the quote in cream and
   the role in --color-sand-soft. Both sampled off the frame renders, not
   guessed — the mobile panel is #000 and its quote is #f0eee9.

   Nothing here names a page. Two pages could use it; one does.
   ========================================================================== */

/* --- The section wrapper -------------------------------------------------- */

.quote-carousel {
	margin-top: 41px;
}

/* Desktop is a grid, and the "track" name is the carousel's. It is the same box
   in both layouts, which is what lets the switch below be a handful of
   declarations rather than two component trees. */
.quote-carousel__track {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	column-gap: 23px;
	row-gap: 26.5px;
}

/* The dots and the swipe hint belong to the mobile layout only. display: none
   rather than aria-hidden or a hidden attribute, on purpose: it takes the
   buttons out of the tab order along with the layout that gives them meaning,
   and it comes back on its own at the breakpoint. */
.quote-carousel__controls {
	display: none;
}

/* --- The tile ------------------------------------------------------------- */
/* tabindex="0" in the markup, not a button — see the note there. */

.quote-tile {
	margin: 0;
	height: 362px;
}

.quote-tile:focus-visible {
	outline: 2px solid var(--color-brand);
	outline-offset: 3px;
	border-radius: 30px;
}

/* The shadow is soft, and it was wrong here until 2026-09-14 — 2px of blur
   against the frames' ~8. Measured off both renders by sampling the falloff
   under a tile and solving for offset and blur: on the desktop frame it holds
   full 0.25 alpha to 1px below the edge and reaches the page colour at 9px; on
   the mobile frame, full at the edge and gone by 7. Both fit 4/8 within a pixel.
   At 2px of blur it read as a hard edge sitting under the card rather than a
   shadow cast by it. */
.quote-tile__face {
	border-radius: 30px;
	background: var(--color-black);
	box-shadow: 0 4px 8px color-mix(in srgb, var(--color-black) 25%, transparent);
}

.quote-tile__logo {
	/* The mark keeps its measured size; the tile is what is uniform, not the
	   logo inside it. */
	flex: none;
	max-width: calc(100% - 2 * var(--space-8));
	height: auto;
}

/* The six back faces carry six different vertical paddings in Figma — 36, 50,
   53, 68, 78, 87 — which looks like six decisions and is one. Add each to twice
   itself plus its own content height and every card comes to 362: the padding is
   auto-layout expressing "centre this content", nothing more. Checked on all six
   rather than assumed.

   The designers confirmed the intent behind it in a second comment on the frame:
   the cards are never seen side by side in the same state, so each is tuned to
   read as balanced on its own. Centring is what delivers that for any content
   length, so this is one rule rather than six.

   Which leaves the padding here as a minimum inset, not a measured gap — hence
   24 rather than the frame's smallest 36. The Opt-in quote is 306 characters,
   the longest of the six, and at 36 it overflowed the 362px tile by 4px and was
   silently clipped by the face's overflow: hidden. */
.quote-tile__face--back {
	flex-direction: column;
	justify-content: center;
	gap: 7px;
	padding: 24px 35px;
	background: var(--color-sand-pale);
	text-align: center;
}

.quote-tile__avatar {
	width: 56px;
	height: 56px;
	border-radius: var(--radius-pill);
	object-fit: cover;
}

.quote-tile__name {
	font: var(--label-int);
	letter-spacing: var(--ls-label);
	color: var(--color-brand);
}

.quote-tile__role {
	font: var(--label-s);
	letter-spacing: var(--ls-label);
	color: var(--color-black);
}

.quote-tile__quote {
	margin: 10px 0 0;
	font: var(--body-s);
	color: var(--color-black);
}

/* =============================================================================
   Tablet — DERIVED, NOT DESIGNED

   No frame exists between 392 and 1440. Two columns is what stops three
   362px-tall tiles from crushing; it carries no authority. When a tablet frame
   arrives, re-derive this against it rather than extending it.
   ========================================================================== */

@media (max-width: 1100px) {
	.quote-carousel__track {
		grid-template-columns: repeat(2, 1fr);
	}
}

/* =============================================================================
   Mobile — DESIGNED. Figma v4zuHnfQeDWmJsqAyNqiDq, frame 1795:106.

   The section stops being a grid of flip cards and becomes a one-at-a-time
   carousel of stacked cards. Measured off the frame at 392:

     slide          the full width of the track; one card visible at a time
     card           298 wide, centred in the slide, 16px from the eyebrow above
     logo panel     298 x 133.72, radius 26, black, logo fitted into 160 x 62
     quote panel    298 x 385,    radius 26, black, 33.5px side padding
     avatar         56, then 7px, name, 7px, role, 17px, quote
     dots           six 10px squares on a 24px pitch, active orange
     hint           13px, 15px under the dots
   ========================================================================== */

@media (max-width: 767px) {
	/* The frame's gap from the eyebrow to the slide is 16. Six of it is the
	   track's own shadow gutter below, so only 10 is margin. */
	.quote-carousel {
		margin-top: 10px;
	}

	/* ONE CARD ON SCREEN AT A TIME, which is the difference between this and a
	   peeking strip. A slide is the full width of the track and the card is
	   centred inside it, so the neighbours sit a whole viewport away instead of
	   showing 31px of themselves at each edge. Swiping therefore moves by
	   exactly one card, which is also what makes "swipe to explore" honest.

	   That is why the track has no gap and no padding: the gutter either side of
	   the card is the slide's own width, not spacing between slides, and it
	   scrolls with the card. The frame's 47px inset is on the card below.

	   Full-bleed, because the section's gutter is removed for this element — see
	   pages/our-work.css. */
	.quote-carousel__track {
		display: flex;
		grid-template-columns: none;
		gap: 0;
		padding-inline: 0;
		overflow-x: auto;
		overscroll-behavior-x: contain;
		scroll-snap-type: x mandatory;
		/* The cards carry a drop shadow and a focus ring; without the vertical
		   room an overflow: auto box clips both. */
		padding-block: 6px;
		scrollbar-width: none;
	}

	.quote-carousel__track::-webkit-scrollbar {
		display: none;
	}

	/* The slide. Full width and invisible — everything you can see is __inner. */
	.quote-tile {
		flex: none;
		width: 100%;
		height: auto;
		scroll-snap-align: center;
	}

	/* The card. 298 at the frame's 392, which is its 47px inset either side. */
	.quote-tile__inner {
		width: min(298px, 100% - 94px);
		margin-inline: auto;
	}

	/* The focus ring moves to the card with the card. On the slide it would
	   outline the full width of the track, most of which is empty. */
	.quote-tile:focus-visible {
		outline: none;
	}

	.quote-tile:focus-visible .quote-tile__inner {
		outline: 2px solid var(--color-brand);
		outline-offset: 3px;
		border-radius: var(--radius-card-sm);
	}

	/* Both panels are visible and stacked. flip-card.css unstacks them out of
	   the 3D space at this width; what is left for this file is their size and
	   their colour.

	   THE BORDER IS WHAT SEPARATES THEM, and it is the whole reason the two
	   panels read as two cards rather than one tall one. They are flush — the
	   frame puts the quote panel's top at exactly the logo panel's bottom,
	   133.72 — so nothing but these two hairlines sits between them, and at the
	   rounded corners they pull apart far enough to show a notch of page cream.
	   Sampling the seam on the frame's own render gives 3px of #4d4a41 at the
	   straight edges, which is two 1.5px borders and no gap.

	   Mobile only, and that is measured rather than cautious: the same sample
	   across the DESKTOP frame's tiles goes straight from cream to black with a
	   single antialiasing pixel and no #4d4a41 anywhere, so those tiles have no
	   border and must not grow one.

	   border-box is global, so the borders sit inside the panel heights below
	   and the two still add up to the frame's 518.72. */
	.quote-tile__face {
		border: 1.5px solid var(--color-sand);
		border-radius: var(--radius-card-sm);
	}

	.quote-tile__face--front {
		min-height: 134px;
		padding: 18px var(--space-4);
	}

	/* The logo is fitted into a 160 x 62 box rather than scaled by a factor:
	   Grubhub's 204-wide mark is width-limited and Loog's 93-tall one is
	   height-limited, and both land on the frame's numbers from this one rule. */
	.quote-tile__logo {
		max-width: 160px;
		max-height: 62px;
		width: auto;
	}

	.quote-tile__face--back {
		min-height: 385px;
		padding: 24px 33.5px;
		background: var(--color-black);
	}

	.quote-tile__name {
		font: var(--label-m);
		letter-spacing: var(--ls-label);
	}

	.quote-tile__role {
		font: 700 var(--text-2xs) / 1.3 var(--font-label);
		color: var(--color-sand-soft);
	}

	.quote-tile__quote {
		margin-top: 17px;
		font: 400 var(--text-sm) / 1.35 var(--font-sans);
		color: var(--color-cream);
	}

	/* --- Carousel chrome ------------------------------------------------- */

	.quote-carousel__controls {
		display: block;
		margin-top: 24px;
		text-align: center;
	}

	.quote-carousel__dots {
		display: flex;
		justify-content: center;
		gap: 14px;
	}

	/* The dots are components/carousel-dots.css, at its defaults: 10px
	   rounded squares, black, orange when current. */

	.quote-carousel__hint {
		margin: 11px 0 0;
		font-family: var(--font-sans);
		font-size: var(--text-xs);
		line-height: 1.16;
		color: var(--color-carbon-soft);
	}
}
