/* Performance page, task 14 — «Сюжет і теми» (Figma «Plot And Themes»
   3399:9475 / 4255:14947), «Коментар режисера» («Director Comment»
   3135:3771 / 4255:14965), «Актори» («Cast Section» 3135:3779 /
   «Actors Section» 4372:22696) and «Що кажуть виконавці» («Performer
   Testimonials» 3159:1964 / «Actor Testimonials Section» 4255:15859).
   The card itself is actor-card.css; the slider chrome is slider.css;
   the «Читати повністю» button is the shared «View All» (view-all.css).

   ## Widths

   Only 390 and 1440 are drawn for this page (inventory.md,
   global-constraints.md's node table). As in performance-hero.css and
   performance-about.css, the mobile layout stays fluid up to 1440 and
   the 1440 layout then holds through 1920, with the content capped at
   the frame's own 1328 and centred. Type sizes still step at 768 and
   1920 — those steps are the .fig's own variable modes, carried by
   tokens.css, not invented here.

   ## Vertical rhythm

   The frames sit 120px apart at 390 and 180px at 1440. That gap belongs
   to no single section, and the page's other sections each solve it
   differently (about.php carries its own padding), so it is a `margin`
   here rather than padding: a margin is not part of the element's box,
   so the frames still measure against spec-diff at exactly the .fig's
   own sizes. Flagged for the controller in task-14-report.md.

   ## Colour substitutions

   - «Коментар режисера» label, the .fig's #12AD99 on the #F3F4F6 panel:
     2.6:1, under WCAG 1.4.3's 4.5:1 for text → --color-primary-700
     (5.0:1 on that panel), the same substitution repertoire.css and
     event-card.css already make for this teal;
   - the director's role, the .fig's #697482 (--color-neutral-500) on the
     same panel: 4.3:1, just under 4.5:1 → --color-neutral-600 (6.9:1).
   Both are in designer-questions.md. On the DARK testimonial ground the
   .fig's own #12AD99 passes (5.1:1) and is kept. */

/* Kilok is not licensed yet: the site runs Exo 2 under its name (fonts.css,
   size-adjust 95.5 %). Figma trims these layers to Kilok's cap height
   (0.70 em), the substitute's is 0.659 em, so a `text-box`-trimmed box lands
   0.041 em short; the difference is added back as bottom padding — the same
   remedy performance-shows.css and visit.css already use. */
.performance-plot,
.performance-director,
.performance-cast,
.performance-testimonials,
.c-actor-card {
	--podil-cap-fix: 0.041em;
}

/* ============================================================ Сюжет і теми */

.performance-plot {
	box-sizing: border-box;

	/* pad=0/16/0/16 on the 390 frame; the 1440 frame has no padding of its
	   own and places its content on the 56px margins instead. */
	padding: 0 16px;
	margin-top: var(--podil-section-gap);
	overflow: hidden; /* clipsContent on 3399:9475 / 4255:14947. */
}

.performance-plot__inner {
	display: grid;
	box-sizing: border-box;
	max-width: 1328px;
	margin: 0 auto;
	grid-template-areas:
		"title"
		"media"
		"body";

	/* `minmax(0, 1fr)`, not the implicit `auto`: an image's intrinsic width
	   would otherwise size the column and blow the section open. */
	grid-template-columns: minmax(0, 1fr);
}

.performance-plot__title {
	margin: 0;
	font-family: var(--font-title);
	font-size: var(--fs-h2-s);
	font-weight: 400;
	grid-area: title;
	letter-spacing: var(--ls-h2-s);
	line-height: var(--lh-h2-s);

	/* leadingTrim CAP_HEIGHT on «Visit Title» 3126:3603 / 4255:14955. */
	padding-bottom: var(--podil-cap-fix);
	text-box: trim-both cap alphabetic;
	color: var(--color-neutral-800);
	text-transform: uppercase;
}

.performance-plot__media {
	align-self: start;
	margin-top: 40px;
	grid-area: media;
}

/* 496×404 at 1440, 358×291.6 at 390 — the same 1.2277 ratio at both. */
.performance-plot__photo {
	display: block;
	width: 100%;

	/* `height: auto` first: WordPress prints the attachment's own
	   width/height attributes, and those are a presentational hint that
	   `aspect-ratio` alone does not override. */
	height: auto;
	aspect-ratio: 496 / 404;
	object-fit: cover;
}

.performance-plot__body {
	display: flex;
	flex-direction: column;
	margin-top: 24px;
	gap: 40px;
	grid-area: body;
}

.performance-plot__copy {
	display: flex;
	flex-direction: column;
	gap: 20px;
}

.performance-plot__lead {
	margin: 0;
	font-family: var(--font-body);
	font-size: var(--fs-menu-button);
	font-weight: 600;
	line-height: var(--lh-menu-button);
	letter-spacing: var(--ls-menu-button);
	color: var(--color-neutral-800);
	text-transform: uppercase;
}

.performance-plot__text {
	font-family: var(--font-body);
	font-size: var(--fs-s-text);
	font-weight: 400;
	line-height: var(--lh-s-text);
	letter-spacing: var(--ls-s-text);
	color: var(--color-neutral-800);
}

.performance-plot__part {
	display: flex;
	flex-direction: column;
	gap: 20px;
}

.performance-plot__part p {
	margin: 0;
}

/* A class selector outranks base.css's `[hidden]`, so the half that is not
   shown has to be told again — otherwise `readmore.js` would leave both the
   collapsed and the full copy on the page. */
.performance-plot__part[hidden] {
	display: none;
}

/* The body's last visible block when the text is collapsed: the .fig's own
   truncation — «Це вистава про свободу…» 3419:10998 / 4255:14954 is a
   766×24 (358×24 at 390) text node with textAutoResize TRUNCATE, i.e. one
   clipped line with an ellipsis. `min-height`, not `height`, so the box
   still grows with the a11y text-size setting instead of cutting the line
   in half at 150 %. */
.performance-plot__clipped {
	min-height: 24px;
}

.performance-plot__clipped p {
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

/* `<br>` breaks a line even under `white-space: nowrap`, and a `<br>` is
   ordinary output of the wysiwyg field — the clipped block has to stay ONE
   line, so its own breaks are suppressed here. The full copy the button
   swaps in keeps every one of them. */
.performance-plot__clipped br {
	display: none;
}

/* «Читати повністю» is the shared «View All» (view-all.css, Figma 940:4021)
   on a `<button>` instead of a link, because here it expands text on this
   page instead of going anywhere — so the element's own UA chrome has to go
   (a `<button>`'s 2px border would make the box 68 instead of the frame's
   64, and its ButtonFace would replace the white). */
.performance-plot__more {
	appearance: none;
	border: 0;
	background: none;
	cursor: pointer;
}

/* ==================================================== Коментар режисера */

.performance-director {
	box-sizing: border-box;
	padding: 0 16px;
	margin-top: var(--podil-section-gap);
}

.performance-director__inner {
	position: relative;
	display: grid;
	box-sizing: border-box;
	max-width: 1328px;
	padding: 32px 20px;
	margin: 0 auto;
	background: var(--color-neutral-100);
	gap: 32px; /* the frame's own auto-layout gap, at both widths. */
	grid-template-areas:
		"head"
		"portrait"
		"footer";
	grid-template-columns: minmax(0, 1fr);
}

/* «“» 3286:10971 (300px) / 4255:14980 (200px): the .fig's own oversized
   Kilok glyph, flush with the panel's right edge and overhanging its top.
   Decorative (aria-hidden) — the quotation marks a reader needs are in the
   quote's own text. */
.performance-director__mark {
	position: absolute;
	top: -17px;
	right: 0;
	width: 84px;
	height: 140px;
	color: var(--color-primary-500);
	font-family: var(--font-title);
	font-size: 200px;
	font-weight: 400;
	line-height: 36px;
	letter-spacing: -1px;

	/* leadingTrim CAP_HEIGHT on the glyph's own node (3286:10971,
	   3159:2014, 4255:14980, 4255:15870): the trim is what pins the
	   glyph to the top of its box instead of hanging it off a 36px
	   line box 154px higher. */
	text-box: trim-both cap alphabetic;
	pointer-events: none;
	user-select: none;
}

/* The two frames group the same three texts differently: at 1440 «Quote
   Block» 3135:3746 holds the label and the quote (gap 20) inside «Director
   Comment Section» (gap 32); at 390 «Director Quote Content» 4255:14985
   holds the quote and the text (gap 32) inside «Director Comment Content»
   (gap 16). One grouping has to be chosen — this is the 1440 one, and the
   gaps are set so every leaf lands where BOTH frames put it. */
.performance-director__head {
	display: flex;
	flex-direction: column;
	gap: 32px;
	grid-area: head;
}

.performance-director__quote-block {
	display: flex;
	flex-direction: column;
	gap: 16px;
}

.performance-director__label {
	margin: 0;
	font-family: var(--font-body);
	font-size: var(--fs-menu-button);
	font-weight: 600;
	line-height: var(--lh-menu-button);
	letter-spacing: var(--ls-menu-button);
	color: var(--color-primary-700);
	text-transform: uppercase;
}

.performance-director__quote {
	margin: 0;
	font-family: var(--font-title);
	font-size: var(--fs-h3);
	font-weight: 400;
	line-height: var(--lh-h3);
	letter-spacing: var(--ls-h3);

	/* leadingTrim CAP_HEIGHT on «Director Quote» 3135:3748 / 4255:14972. */
	padding-bottom: var(--podil-cap-fix);
	text-box: trim-both cap alphabetic;
	color: var(--color-neutral-900);
	text-transform: uppercase;
}

.performance-director__quote p {
	margin: 0;
}

.performance-director__text {
	font-family: var(--font-body);
	font-size: var(--fs-m-text);
	font-weight: 400;
	line-height: var(--lh-m-text);
	letter-spacing: var(--ls-m-text);

	/* leadingTrim CAP_HEIGHT, «Production Description» 3135:3750 / 4255:14973. */
	text-box: trim-both cap alphabetic;
	color: var(--color-neutral-800);
}

.performance-director__text p {
	margin: 0;
}

/* Quote → portrait 32 (the frame's own gap), portrait → who said it 12
   («Director Portrait Container» 4255:14984's gap), who said it → link 32.
   The 12 is the 32 the grid already gives, pulled back by 20. */
.performance-director__portrait {
	grid-area: portrait;
}

.performance-director__photo {
	display: block;
	width: 100%;
	height: auto;
	aspect-ratio: 1;
	object-fit: cover;
	background: var(--color-neutral-200);
}

.performance-director__footer {
	display: flex;
	align-items: flex-start;
	flex-direction: column;
	margin-top: -20px;
	gap: 32px;
	grid-area: footer;
}

.performance-director__person {
	display: flex;
	flex-direction: column;
}

.performance-director__name {
	font-family: var(--font-body);
	font-size: var(--fs-menu-button);
	font-weight: 600;
	line-height: var(--lh-menu-button);
	letter-spacing: var(--ls-menu-button);
	color: var(--color-neutral-800);
	text-transform: uppercase;
}

.performance-director__role {
	font-family: var(--font-body);
	font-size: var(--fs-s-text);
	font-weight: 400;
	line-height: var(--lh-s-text);
	letter-spacing: var(--ls-s-text);
	color: var(--color-neutral-600);
}

/* ============================================================ «Детальніше» */

/* Figma «Details Link» 3014:2350 (white=off) / 3014:2351 (white=on). The box,
   the type and the transition are the shared component (details-link.css); both
   links carry `.c-details-link` and keep only what is theirs here — the colour
   on this section's own ground, the hover (the link drives it here, unlike in
   the repertoire card), and the second arrow export's box below. */
.performance-director__link:hover {
	gap: 12px;

	/* #0C9586 is 3.4:1 on the panel and fails AA for text → the
	   substitution this file's header records. */
	color: var(--color-primary-700);
}

.performance-director__link svg,
.performance-testimonials__link svg {
	display: block;

	/* The export's viewBox is `0 0 28 12` and its path ends at x = 27.1111,
	   so the box has to be 28 for the glyph to render at the .fig's own
	   27.1111 — sizing it at 27.1111 squeezed the path by 3 %. The 0.889 the
	   box gains is taken off the row again, so the link still hugs to the
	   «Details Link» frame's own width. */
	width: 28px;
	height: 12px;
	align-self: center;
	margin-right: -0.889px;
}

/* ============================================================ Актори */

.performance-cast {
	box-sizing: border-box;
	padding: 0 16px;
	margin-top: var(--podil-section-gap);
}

.performance-cast__inner {
	display: grid;
	box-sizing: border-box;
	max-width: 1328px;
	margin: 0 auto;
	grid-template-areas:
		"title"
		"grid";
	grid-template-columns: minmax(0, 1fr);
}

.performance-cast__title {
	margin: 0;

	/* The shared sticky mode («Sticky effect для заголовків» `4497:25442`,
	   section-title.css) only has somewhere to hold from 1440 up, where this
	   heading becomes an 84×820 column beside the grid; at 390/768 the frames
	   draw it as a line above the cards and there is nothing to pin. */
	position: static;
	font-family: var(--font-title);
	font-size: var(--fs-h2-m);
	font-weight: 400;
	grid-area: title;
	letter-spacing: var(--ls-h2-m);
	line-height: var(--lh-h2-m);
	color: var(--color-neutral-800);
	text-transform: uppercase;
}

/* The word itself: the heading is only its box (84×820 at 1440), so the
   trim and the substitute's cap-height make-up belong here, on the text. */
.performance-cast__title-text {
	display: block;

	/* leadingTrim CAP_HEIGHT on «Section Label» 3135:3781 / 4372:22697. */
	padding-bottom: var(--podil-cap-fix);
	text-box: trim-both cap alphabetic;
}

.performance-cast__grid {
	display: grid;
	padding: 0;
	margin: 52px 0 0;
	gap: 40px;
	grid-area: grid;
	grid-template-columns: minmax(0, 1fr);
	list-style: none;
}

/* ============================================================ 768 */

@media (min-width: 768px) {

	.performance-cast__grid {
		column-gap: var(--grid-gutter, 24px);

		/* No performance-page frame at this width: two columns rather
		   than one 736-wide portrait card, which nobody drew either. */
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}
}

/* ============================================================ 1440 */

@media (min-width: 1440px) {

	.performance-plot {
		padding: 0;
	}

	.performance-plot__inner {

		/* The 1440 frame is full-bleed and its content column is the
		   56px margins — so the inner is the FRAME (1440), capped there
		   and centred above it, not the 1328 column. */
		max-width: 1440px;
		padding: 0 56px;
		column-gap: 67px;
		grid-template-areas:
			"title title"
			"media body";
		grid-template-columns: 496px minmax(0, 1fr);
		row-gap: 54px;
	}

	.performance-plot__media,
	.performance-plot__body {
		margin-top: 0;
	}

	.performance-plot__body {
		gap: 32px;
	}

	/* «View All» 3336:16939 is 760 wide in its own 765-wide column — the
	   instance's own box, kept rather than stretched. */
	.performance-plot__more {
		width: 760px;
		max-width: 100%;
	}

	.performance-director {
		padding: 0 56px;
	}

	.performance-director__inner {
		padding: 32px;
		gap: 32px;
		grid-template-areas:
			"portrait head"
			"portrait footer";
		grid-template-columns: 400px minmax(0, 1fr);
		grid-template-rows: auto 1fr;
	}

	.performance-director__mark {
		top: -20px;
		width: 147px;
		height: 245px;
		font-size: 300px;
	}

	/* «Director Portrait» 3286:10969 is a 400×400 hug frame in the row,
	   not a column that grows. */
	.performance-director__portrait {
		width: 400px;
	}

	.performance-director__quote-block {
		gap: 20px;
	}

	.performance-director__footer {
		flex-direction: row;
		align-items: flex-end;
		align-self: end;
		margin-top: 0;
		gap: 12px;
	}

	.performance-director__person {
		flex: 1 1 auto;
	}

	.performance-cast {
		padding: 0 56px;
	}

	.performance-cast__inner {
		column-gap: 67px;
		grid-template-areas: "title grid";
		grid-template-columns: 84px minmax(0, 1fr);
	}

	/* «Carousel Label» 3135:3780: an 84×820 column holding the word turned a
	   quarter turn, centred along its 820px. The direction is the .fig's own
	   matrix on «Section Label» 3135:3781 — m00 ≈ 0, m10 = −1, i.e. −90° in a
	   y-down space, which on screen reads BOTTOM TO TOP. That is the same
	   matrix every other vertical label in the file carries («Афіша»
	   1159:7387, «Актори» 1159:7501, «ще цікаве» 3206:3741), and the same
	   `rotate(-90deg) translateX(-100%)` home.css, performance-sections.css
	   and person.css already use. This rule read it as a clockwise turn and
	   set the only top-to-bottom heading in the project. Rotating a horizontal
	   box keeps `text-box` trimming and the token type scale;
	   `translateX(-100%)` after the rotation pins it to the column's left edge
	   whatever the substitute font's cap height turns out to be. */
	.performance-cast__title {

		/* Sticky, not relative: the box holds `--c-sticky-title-top` while its
		   own grid scrolls past and is carried off by that grid's bottom edge
		   («рухається далі, коли секція закінчується», `4497:25442`). It stays
		   the containing block for the rotated word inside it either way.
		   `grid-row` spans every row of `.performance-cast__inner` so the area
		   the travel is bounded by is the whole section at 1440 too, where the
		   grid is one row anyway — and stays so if a row is ever added. */
		position: sticky;
		grid-row: 1 / -1;
		width: 84px;
		height: 820px;
	}

	.performance-cast__title-text {
		position: absolute;
		top: 0;
		left: 0;
		width: 820px;
		text-align: center;
		transform: rotate(-90deg) translateX(-100%);
		transform-origin: 0 0;
	}

	/* The three-column stagger of «Actors Carousel» 3135:3789: columns
	   start 48 / 120 / 0 lower than the grid, rows repeat every 572
	   (card 472 + 100). The offset is a top margin cancelled by an equal
	   negative bottom margin, so it moves the card without moving the row
	   line — which is what makes the stagger repeat on every row instead
	   of only the first; the container's bottom padding is the largest of
	   the offsets, so the last row stays inside the section. */
	.performance-cast__grid {
		padding-bottom: 120px;
		margin-top: 0;
		column-gap: 0;
		grid-template-columns: repeat(3, 376px);
		justify-content: space-between;
		row-gap: 100px;
	}

	.performance-cast__item {
		align-self: start;
	}

	.performance-cast__item:nth-child(3n+1) {
		margin-top: 48px;
		margin-bottom: -48px;
	}

	.performance-cast__item:nth-child(3n+2) {
		margin-top: 120px;
		margin-bottom: -120px;
	}
}

/* ================================================= Що кажуть виконавці */

/* Figma «Performer Testimonials» 3159:1964 (1440×800) / «Actor Testimonials
   Section» 4255:15859 (390×723). Two different compositions of the same
   parts, not one responsive layout:

   - 1440: the stage photo IS the section, 800 tall; the actor's card, the
     quote panel, the title, the arrows and the dots all sit over it;
   - 390: a solid #272B30 section stacking title → card → a 358×199 photo
     (the dots over its bottom) → quote; the frame draws no arrows at all
     and no gradients, because there is no light text over a photo there.

   The slider's own chrome is slider.css. This file only sets the two offsets
   the component exposes and, below 768, re-parents the dots over the photo
   with `display: contents`, since there the photo is one band of the slide
   rather than the whole of it. */

.performance-testimonials {
	position: relative;
	padding: 80px 0;
	margin-top: var(--podil-section-gap);
	background: var(--color-neutral-800);

	/* `clip`, not `hidden`: `overflow: hidden` makes a box a scroll container,
	   and a scroll container ANYWHERE above a parallax layer's box becomes the
	   scroller of its `view-timeline` — the section never scrolls, so the
	   progress sat still and the layer did not move at all (fix round 1, C2).
	   `clip` clips and is not scrollable, which is the same idiom people.css
	   already uses deliberately for the portrait strip. */
	overflow: clip; /* clipsContent on both frames. */
}

.performance-testimonials__title {
	margin: 0 16px 40px;
	font-family: var(--font-title);
	font-size: var(--fs-h2-s);
	font-weight: 400;
	line-height: var(--lh-h2-s);
	letter-spacing: var(--ls-h2-s);

	/* leadingTrim CAP_HEIGHT on «Visit Title» 3159:1966 / 4255:15884. */
	padding-bottom: var(--podil-cap-fix);
	text-box: trim-both cap alphabetic;
	color: var(--color-base-white);
	text-transform: uppercase;
}

.performance-testimonials__slide {
	display: grid;
	grid-template-areas:
		"card"
		"media"
		"quote";
}

/* ---- the actor's dark card ---- */

.performance-testimonials__card {
	display: flex;
	box-sizing: border-box;
	align-items: center;
	padding: 0 16px 16px;
	gap: 16px;
	grid-area: card;
	background: var(--color-neutral-800);
}

.performance-testimonials__portrait {
	display: block;
	width: 94px;
	height: 94px;
	flex: none;
	object-fit: cover;

	/* «Actor Photo» 3159:2000 / 4255:15877 paints #ECECEC under the
	   portrait — a colour bound to no variable in the file; the nearest
	   token is used and the question is in designer-questions.md. Nothing
	   of it is ever visible: the photo covers it. */
	background: var(--color-neutral-200);
}

.performance-testimonials__person {
	display: flex;
	min-width: 0;
	flex: 1 1 auto;
	flex-direction: column;
	gap: 20px;
}

.performance-testimonials__who {
	display: flex;
	flex-direction: column;
}

.performance-testimonials__name cite,
.performance-director__name cite {
	font-style: normal;
}

.performance-testimonials__name {
	margin: 0;
	font-family: var(--font-body);
	font-size: var(--fs-menu-button);
	font-weight: 600;
	line-height: var(--lh-menu-button);
	letter-spacing: var(--ls-menu-button);
	color: var(--color-base-white);
	text-transform: uppercase;
}

.performance-testimonials__role {
	margin: 0;
	font-family: var(--font-body);
	font-size: var(--fs-s-text);
	font-weight: 400;
	line-height: var(--lh-s-text);
	letter-spacing: var(--ls-s-text);
	color: var(--color-base-white);
}

.performance-testimonials__link {
	align-self: flex-start; /* «About Actor Link» hugs its label. */
	color: var(--color-base-white);
}

/* animations.md, row «Детальніше», white=on 3014:2351 → Hover 4312:24184:
   gap 8 → 12 and the label turns teal. On this dark ground the .fig's own
   #12AD99 passes (5.1:1), so it is kept as drawn. */
.performance-testimonials__link:hover {
	gap: 12px;
	color: var(--color-primary-500);
}

/* ---- the photo ---- */

.performance-testimonials__media {
	position: relative;
	margin: 0 16px;
	aspect-ratio: 358 / 199;
	grid-area: media;

	/* The shared parallax (note 4497:25446, parallax.css) makes the photo
	   taller than this box; both it and the scrim are `inset: 0` children, so
	   clipping here changes nothing else and is what keeps the moving layer's
	   edge from showing. */
	overflow: hidden;
	background: var(--color-neutral-700);
}

.performance-testimonials__photo {
	position: absolute;
	inset: 0;
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.performance-testimonials__scrim {
	display: none;
}

/* ---- the quote panel ---- */

.performance-testimonials__quote {
	position: relative;
	display: flex;
	box-sizing: border-box;
	flex-direction: column;
	padding: 32px 20px 0;
	margin: 0;
	gap: 24px; /* «Quote Section» 4255:15868's own auto-layout gap. */
	grid-area: quote;
	background: var(--color-neutral-800);
}

.performance-testimonials__mark {
	position: absolute;
	top: -18px;
	left: 10px;
	width: 147px;
	height: 245px;
	color: var(--color-primary-500);
	font-family: var(--font-title);
	font-size: 200px;
	font-weight: 400;
	line-height: 36px;
	letter-spacing: -1px;

	/* leadingTrim CAP_HEIGHT on the glyph's own node (3286:10971,
	   3159:2014, 4255:14980, 4255:15870): the trim is what pins the
	   glyph to the top of its box instead of hanging it off a 36px
	   line box 154px higher. */
	text-box: trim-both cap alphabetic;
	pointer-events: none;
	user-select: none;
}

.performance-testimonials__quote-text {
	position: relative;
	margin: 0;
	font-family: var(--font-body);
	font-size: var(--fs-m-text);
	font-weight: 400;
	line-height: var(--lh-m-text);
	letter-spacing: var(--ls-m-text);

	/* leadingTrim CAP_HEIGHT on «Quote Text» 3159:1975 / 4255:15869. */
	text-box: trim-both cap alphabetic;
	color: var(--color-base-white);
}

.performance-testimonials__quote-text p {
	margin: 0;
}

.performance-testimonials__video {
	margin: 0;
}

/* «Watch Video Button» 3159:2035 / 4255:15874 — 40 tall, pad 14/36, gap 10,
   a 2px INSIDE stroke (= inset box-shadow, global-constraints.md). #12AD99
   on #272B30 is 5.1:1, past WCAG 1.4.11's 3:1, so the .fig's own teal
   stands. */
.performance-testimonials__watch {
	display: flex;
	box-sizing: border-box;
	width: 100%;
	height: 40px;
	align-items: center;
	justify-content: center;
	padding: 14px 36px;
	gap: 10px;
	box-shadow: inset 0 0 0 2px var(--color-primary-500);
	color: var(--color-base-white);
	font-family: var(--font-body);
	font-size: var(--fs-s-text);
	font-weight: 600;
	line-height: var(--lh-s-text);
	letter-spacing: var(--ls-s-text);
	text-decoration: none;
	text-transform: uppercase;
}

/* Hover — the 22.09 .fig moved this button to a component set of its own,
   «Watch Video Button» 4548:25897, and the set brought a hover the local frame
   3159:2035 never had. Default (4548:25888) —ON_HOVER SMART_ANIMATE spring
   (mass 1, stiffness 80, damping 20), 1250ms→ Hover 1 (4548:25903) —ON_HOVER,
   same spring, 1250ms→ Hover 2 (4548:25962) —MOUSE_LEAVE SMART_ANIMATE linear,
   300ms→ Default. Read from the .fig (`fig-canvas.js interactions` plus a
   per-layer diff of the three variants).

   The State note the designer attached to the set, `4548:25996`, says «Hover /
   Smart animate, Slow, 600 ms», which contradicts the set's own prototype data
   in the same file. animations.md's rule for that case is that the prototype
   data is the senior source, so 1250 / 1250 / 300 is what is built and the
   note is a question for the designer (designer-questions.md).

   Four 2px #FFFFFF lines, on top of the teal inside stroke, opacity 0 → 1
   while they grow:
   · «Vector 11» top, y=1: x 0..2 → 0..297 (grows left → right);
   · «Vector 12» bottom, y=39: x 295..297 → 0..297 (grows right → left);
   · «Rectangle 6138» left, x=1: y 38..40 → 0..40 (bottom → top), phase 2;
   · «Rectangle 6139» right, x=296: y 0..2 → 0..40 (top → bottom), phase 2.
   Phase 2 starts when phase 1 has finished (Figma fires Hover 1's own ON_HOVER
   after the swap), hence the 1250ms delay. The label and the teal stroke do not
   change; the `fontSize` 20 on the Hover labels is the same stale field the
   ticket button's variants carry.

   Same two-pseudo-element construction as ticket-button.css, whose
   `--ease-spring-80-20` samples this exact spring; only the thickness (2 vs 3),
   the colour (white on the dark button) and the corners the bars grow from
   differ, and they are the set's own. */
.performance-testimonials__watch {
	position: relative;
}

.performance-testimonials__watch::before,
.performance-testimonials__watch::after {
	content: "";
	position: absolute;
	inset: 0;
	background-image:
		linear-gradient(var(--color-base-white), var(--color-base-white)),
		linear-gradient(var(--color-base-white), var(--color-base-white));
	background-repeat: no-repeat;
	opacity: 0;
	pointer-events: none;
	transition:
		background-size 300ms linear,
		opacity 300ms linear;
}

.performance-testimonials__watch::before {
	background-position: left top, right bottom;
	background-size: 2px 2px, 2px 2px;
}

.performance-testimonials__watch::after {
	background-position: left bottom, right top;
	background-size: 2px 2px, 2px 2px;
}

.performance-testimonials__watch:focus-visible::before,
.performance-testimonials__watch:focus-visible::after {
	opacity: 1;
	transition:
		background-size 1250ms var(--ease-spring-80-20),
		opacity 1250ms var(--ease-spring-80-20);
}

.performance-testimonials__watch:focus-visible::before {
	background-size: 100% 2px, 100% 2px;
}

.performance-testimonials__watch:focus-visible::after {
	background-size: 2px 100%, 2px 100%;
	transition-delay: 1250ms;
}

/* :hover only where the pointer can hover — on touch a tap would leave :hover
   stuck and the 2.5s draw playing after the tap (ticket-button.css's own
   reasoning). */
@media (hover: hover) {

	.performance-testimonials__watch:hover::before,
	.performance-testimonials__watch:hover::after {
		opacity: 1;
		transition:
			background-size 1250ms var(--ease-spring-80-20),
			opacity 1250ms var(--ease-spring-80-20);
	}

	.performance-testimonials__watch:hover::before {
		background-size: 100% 2px, 100% 2px;
	}

	.performance-testimonials__watch:hover::after {
		background-size: 2px 100%, 2px 100%;
		transition-delay: 1250ms;
	}
}

/* The global rules zero durations only; the phase-2 delay has to go too. */
html[data-motion="reduced"] .performance-testimonials__watch::after {
	/* stylelint-disable-next-line declaration-no-important -- reduced motion */
	transition-delay: 0s !important;
}

@media (prefers-reduced-motion: reduce) {

	.performance-testimonials__watch::after {
		/* stylelint-disable-next-line declaration-no-important -- reduced motion */
		transition-delay: 0s !important;
	}
}

/* «Play Icon» 3159:2039 — «Play Circle» (ELLIPSE 20×20 #12AD99) and «Play
   Triangle» (REGULAR_POLYGON 10×10 #FFFFFF, rotated a quarter turn so it
   points right), centred in it. Two primitives with their exact sizes in
   the spec — not an icon export, so there is nothing to draw by hand. */
.performance-testimonials__play {
	position: relative;
	display: block;
	width: 20px;
	height: 20px;
	flex: none;
	border-radius: 50%;
	background: var(--color-primary-500);
}

.performance-testimonials__play::after {
	position: absolute;
	content: "";
	inset: 5px;
	background: var(--color-base-white);
	clip-path: polygon(0 0, 100% 50%, 0 100%);
}

/* Below 768 the dots belong over the PHOTO, which is one band of the slide
   rather than the whole slider — so the slider's own rows have to be the
   slide's rows. The viewport is a SUBGRID of them (not `display: contents`:
   it carries `tabindex="0"` and `role="group"`, and an element with no box
   cannot be focused, which would cost the component its arrow-key control);
   the boxes below it, which nothing focuses, do go transparent. The hidden
   slides keep `display: none` — that is what holds them out of the tab
   order — so the rule only ever touches the current one. */
@media (max-width: 767px) {

	.performance-testimonials .c-slider {
		display: grid;
		gap: 0;
		grid-template-areas:
			"card"
			"media"
			"quote";
		grid-template-columns: minmax(0, 1fr);
	}

	.performance-testimonials .c-slider__viewport {
		display: grid;
		grid-column: 1;
		grid-row: card-start / quote-end;
		grid-template-columns: minmax(0, 1fr);
		grid-template-rows: subgrid;
	}

	/* `.c-slider__track` is part of the chain too (it is what carries the
	   `per_view` window). */
	.performance-testimonials .c-slider__track,
	.performance-testimonials .c-slider__slide:not([hidden]),
	.performance-testimonials .performance-testimonials__slide {
		display: contents;
	}

	/* `relative`, not `static`: the dots stay in the grid's flow here, and
	   the plate behind them needs a positioned ancestor. `inset: auto`
	   undoes the overlay mode's own `bottom`/`left`, which relative
	   positioning would otherwise apply as an offset. */
	.performance-testimonials .c-slider--dots-overlay .c-slider__dots,
	.performance-testimonials .c-slider__dots {
		position: relative;
		align-self: end;
		justify-self: center;
		inset: auto;
		margin-bottom: 12px;
		gap: 12px;
		grid-area: media;
		transform: none;
	}

	/* «Pagination Dots Container» 4255:15863 — 8×8 dots 12 apart at 390,
	   half the 1440 pair, with a 1px ring on the current one. */
	.performance-testimonials .c-slider__dot {
		width: 8px;
		height: 8px;
	}

	.performance-testimonials .c-slider__dot[aria-current="true"] {
		box-shadow: inset 0 0 0 1px var(--color-base-white);
	}
}

/* ====================================== 1440: the photo IS the section */

@media (min-width: 1440px) {

	.performance-testimonials {
		height: 800px;
		padding: 0;

		/* «Frame 1437254892» 3159:1997 sits 39px above the section's
		   bottom edge (y 745 of 800); the arrows keep the component's own
		   24px inset («Navigation Container» 3312:11317, 1392 of 1440). */
		--c-slider-dots-offset: 39px;
	}

	/* Above 1440 the frame's own 1328 content column stays centred, as in
	   performance-about.css; the photo, the arrows, the dots and the quote
	   panel keep their edge anchors, which is where the frame puts them. The
	   centring is `--page-gutter` (components/container.css) — two literal
	   copies of its formula lived here until plan 06's final review. */
	.performance-testimonials__title {
		position: absolute;
		z-index: 3;
		top: 100px;
		left: var(--page-gutter-1328);
		margin: 0;
	}

	.performance-testimonials .c-slider {
		position: absolute;
		inset: 0;
	}

	.performance-testimonials .c-slider__viewport,
	.performance-testimonials .c-slider__track,
	.performance-testimonials .c-slider__slide {
		height: 100%;
	}

	.performance-testimonials__slide {
		position: relative;
		display: block;
		height: 100%;
	}

	.performance-testimonials__media {
		position: absolute;
		inset: 0;
		margin: 0;
		aspect-ratio: auto;
	}

	/* «Rectangle 6049» (top band 1440×321), «6050» (right 321×800) and
	   «6051» (left 321×799): the same GRADIENT_LINEAR, handles (0.60,1.0)
	   → (0.60,0.055), i.e. straight up, transparent → rgba(0,0,0,0.8) by
	   94.5 % of the run. */
	.performance-testimonials__scrim {
		position: absolute;
		display: block;
		inset: 0;
		background-image:
			linear-gradient(to top, rgb(0 0 0 / 0%) 0%, rgb(0 0 0 / 80%) 94.5%),
			linear-gradient(to top, rgb(0 0 0 / 0%) 0%, rgb(0 0 0 / 80%) 94.5%),
			linear-gradient(to top, rgb(0 0 0 / 0%) 0%, rgb(0 0 0 / 80%) 94.5%);
		background-position: left top, right top, left top;
		background-repeat: no-repeat;
		background-size: 321px 100%, 321px 100%, 100% 321px;
	}

	.performance-testimonials__card {
		position: absolute;
		z-index: 2;
		top: 193px;
		left: var(--page-gutter-1328);
		width: 400px;
		padding: 8px;
		gap: 20px;
	}

	.performance-testimonials__portrait {
		width: 112px;
		height: 112px;
	}

	.performance-testimonials__person {
		align-self: stretch;
		justify-content: space-between;
		padding: 8px 0;
		gap: 0;
	}

	.performance-testimonials__quote {
		position: absolute;
		z-index: 2;
		right: 0;
		bottom: 39px;
		width: 562px;
		min-height: 222px;
		justify-content: space-between;
		padding: 40px 56px 24px 24px;
	}

	.performance-testimonials__mark {
		top: -33px;
		left: 8px;
		font-size: 300px;
	}

	/* «Watch Video Button» 3159:2035 hugs its label at 1440 (297 wide)
	   and fills the panel at 390 (350). */
	.performance-testimonials__watch {
		width: fit-content;
	}
}

/* ======================================== slider chrome over a photograph */

/* The dark slider's controls sit ON the stage photo, and a photograph is
   not a colour: nothing guarantees them WCAG 1.4.11's 3:1. accessibility-ua.md
   §10 names the remedy — «затемнення під текстом» — and the `.fig`'s own scrim
   already does it for the 1440 title and card. It does NOT reach these two:

   - the ARROWS at 390. The frame (`4255:15859`) draws none at all; they are
     this section's own addition, required by accessibility-ua.md §3.5, and
     they land in the middle of a 358×199 photo with a white stroke, a white
     glyph and a white focus ring. Measured against the current stage photos
     before this rule: 2.34:1. A plate of the scrim's own `rgb(0 0 0 / 80%)`
     under the button — not a new colour, the gradient's own end stop — puts
     the glyph on a fixed ground at any photograph. Two layers on hover, so
     the `.fig`'s `rgba(255,255,255,0.2)` fill still reads over it.
     At 1440 the scrim's 321px side columns already give 19.9:1, so the plate
     is not drawn there.
   - the DOTS at both widths: `3159:1997` / `4255:15863` sit below the top
     band and between the side columns, i.e. on bare photograph, and on the
     390 image the inactive dot measured 2.38:1 and the current one 1.18:1.
     The plate is a `::before` behind them, so their own 84×16 / 48×8 box —
     the one spec-diff measures — is untouched. Recorded for the designer. */

/* No `position` here: in overlay mode slider.css already makes the dots
   `absolute`, which is the containing block the plate needs, and repeating
   `relative` at equal specificity would undo that placement. Below 768 the
   dots are a grid item, and the media query there makes them `relative`. */
.performance-testimonials .c-slider__dots::before {
	position: absolute;
	z-index: -1;
	content: "";
	inset: -8px -12px;
	background: rgb(0 0 0 / 80%);
	pointer-events: none;
}

@media (max-width: 767px) {

	.performance-testimonials .c-slider--dark .c-slider__arrow {
		background-image: linear-gradient(rgb(0 0 0 / 80%), rgb(0 0 0 / 80%));
	}

	.performance-testimonials
	.c-slider--dark
	.c-slider__arrow:not([aria-disabled="true"]):hover {
		background-image:
			linear-gradient(rgb(255 255 255 / 20%), rgb(255 255 255 / 20%)),
			linear-gradient(rgb(0 0 0 / 80%), rgb(0 0 0 / 80%));
	}
}

/* Windows High Contrast: Chromium drops author `box-shadow`, and the inside
   stroke is this button's only edge — base.css's own convention and list.
   The play icon is two author-coloured shapes that both flatten to `Canvas`
   on a `Canvas` panel, i.e. vanish; it is `aria-hidden`, so keeping its own
   colours is the honest fix. */
@media (forced-colors: active) {

	.performance-testimonials__watch {
		outline: 1px solid LinkText;
		outline-offset: -1px;
	}

	.performance-testimonials__play,
	.performance-testimonials__play::after {
		forced-color-adjust: none;
	}
}
