/* ─────────────────────────────────────────────────────────────
   GTD Desco — the driller quote banner.

   WHAT WAS WRONG

   Three near-identical blocks — #nick-banner-image,
   #big-mike-banner-image and #joc-banner-image — each with its
   own twenty-four lines of CSS in sass/style.css that differed
   only in a background-image URL, and each hard-coding its quote
   into eight templates.

   The type was white, bottom right, absolutely positioned, over
   no scrim at all. In all three photographs the subject stands
   centre frame in high-vis, so the quote ran straight across a
   fluorescent yellow vest. The attribution was 45px against a
   65px quote, which is not a hierarchy. And the photograph was a
   CSS background at 1800px max-width, so it shipped the full
   original to a phone and left a white gutter on a wide screen.

   WHAT THIS DOES

   One component, one photograph as a real <img> with srcset,
   shown whole rather than cropped, and a hierarchy: the quote is
   the thing, the person is the credit. No scrim, no panel, no
   slab of grey over the picture — the type carries its own
   contrast with a shadow.

   THE MOMENT

   The photograph pushes in the whole time the band is on screen
   — the full frame as it appears, 16% closer by the time it
   leaves — and the rule and the words land over it as it
   arrives. Nothing slides across the picture.

   THE TRAP THIS FILE EXISTS TO ANSWER

   The legacy rules in sass/style.css are id-scoped (1,0,0) and
   are still in that file, which is Compass output and is not
   edited. They set background-image, a fixed 822px height, a
   1800px max-width, text-align: right, and absolute positioning
   on every descendant span. A class rule cannot beat any of it.
   So the neutralising block below is written at id strength, the
   same way #text-banner-image-bkg.gtd-plate is in facilities.css.
   ───────────────────────────────────────────────────────────── */

.gtd-quote {
	--q-red:    var(--gtd-red, #c60000);
	--q-onDark: #ff5b4a;
	--q-ink:    #0d1414;
	--q-ease:   cubic-bezier(.16, 1, .3, 1);
}

/* ═════════════════════════════════════════════════════════════
   NEUTRALISING THE LEGACY IDS

   Every declaration here answers one in sass/style.css. Nothing
   is here for its own sake — if a line looks redundant, it is
   beating an id-scoped rule of the same name.
   ═════════════════════════════════════════════════════════════ */

#nick-banner-image.gtd-quote,
#big-mike-banner-image.gtd-quote,
#joc-banner-image.gtd-quote {
	height: auto;
	max-width: none;
	width: 100vw;
	margin: 0 0 0 50%;
	background-image: none;
	text-align: left;
}

/*
 * css/type.css also carries `#<id> span { font-size: var(--t-h3) !important }`
 * — an id selector WITH !important, which nothing at class strength can beat.
 * The company line came out at 45px because of it.
 */
#nick-banner-image.gtd-quote .gtd-quote__company,
#big-mike-banner-image.gtd-quote .gtd-quote__company,
#joc-banner-image.gtd-quote .gtd-quote__company {
	font-size: clamp(.72rem, .66rem + .22vw, .85rem) !important;
}

/* `#<id> span { position: absolute; bottom: 50px; right: 100px }` caught the
   red rule and the company name and threw both into the corner. */
#nick-banner-image.gtd-quote span,
#big-mike-banner-image.gtd-quote span,
#joc-banner-image.gtd-quote span,
#nick-banner-image.gtd-quote h2,
#big-mike-banner-image.gtd-quote h2,
#joc-banner-image.gtd-quote h2 {
	position: static;
	inset: auto;
	max-width: none;
	text-align: inherit;
}

/* ═════════════════════════════════════════════════════════════
   THE BAND
   ═════════════════════════════════════════════════════════════ */

.gtd-quote {
	position: relative;
	isolation: isolate;
	display: flex;
	flex-direction: column;
	justify-content: flex-end;

	/*
	 * All three photographs are 1728 x 805 — 46.58% of their width. Deriving
	 * the band's height from that means that at every ordinary viewport the
	 * picture is shown WHOLE, with nothing cropped at all.
	 *
	 * The floor is for a phone, where the quote needs more room than a 2.15:1
	 * band gives it. The ceiling is so the band does not swallow a very wide
	 * screen. min-height rather than height, so a long quote grows the band
	 * instead of being clipped by it.
	 */
	min-height: clamp(30rem, 46.58vw, min(92vh, 1000px));
	padding: clamp(3rem, 7vw, 6rem) 0 clamp(2.5rem, 5vw, 4.5rem);
	background: var(--q-ink);
	overflow: clip;
	color: #fff;

	/* Full-bleed out of whatever container the template put it in. */
	width: 100vw;
	margin-left: 50%;
	transform: translateX(-50%);
}

.gtd-quote__figure {
	position: absolute;
	inset: 0;
	z-index: -1;
	margin: 0;
	/*
	 * clip, NOT hidden. `overflow: hidden` makes an element a scroll
	 * container, and a descendant's `animation-timeline: view()` then resolves
	 * against THAT scrollport instead of the viewport. The image exactly fills
	 * this box and this box never scrolls, so the timeline froze at a constant
	 * 42.5% and the zoom never moved — while the rule and the words, which sit
	 * outside this element, animated correctly. `clip` clips without creating
	 * a scroll container.
	 */
	overflow: clip;
}

.gtd-quote__img {
	display: block;
	width: 100%;
	height: 100%;
	max-width: none;
	object-fit: cover;
	/*
	 * `0` on the vertical, and it is not a style choice. In all three
	 * photographs the subject's head is within a few percent of the top edge,
	 * so any crop taken from the top takes the top of his head with it — which
	 * is exactly what a centred crop did. Anything the band cannot show now
	 * comes off the bottom, which is mud.
	 */
	object-position: 50% 0;
	/*
	 * The zoom below scales about the TOP edge, not the centre. Scaling about
	 * the centre pushes the frame up as well as down, and in these photographs
	 * the subject's head is within a few percent of the top — which is how the
	 * helmet came off in the first place. Pinned here, every pixel the zoom
	 * takes comes off the bottom.
	 */
	transform-origin: 50% 0;
}

/* ═════════════════════════════════════════════════════════════
   THE WORDS

   `!important` throughout answers css/type.css, which sets p, a,
   li and every bare span with !important of its own.
   ═════════════════════════════════════════════════════════════ */

.gtd-quote__body,
.gtd-quote__caption {
	position: relative;
	z-index: 1;
	width: min(100% - 2.5rem, 1520px);
	margin-inline: auto;
	padding-inline: 12px;
}

.gtd-quote__body { margin: 0 auto; }

.gtd-quote__text {
	margin: 0;
	max-width: 20ch;
	font-size: clamp(1.75rem, 1rem + 3.1vw, 4.25rem) !important;
	font-weight: 900;
	line-height: 1.02;
	letter-spacing: -.035em;
	color: #fff !important;
	text-wrap: balance;
	/*
	 * No panel behind the type — a halo instead. Three layers: a tight ring
	 * that separates the letterforms from whatever is directly behind them,
	 * and two softer pools that darken the immediate surroundings. Measured
	 * against the Mike Ryan photograph, where the quote falls across a light
	 * grey hoodie, this is what keeps the letterforms readable without
	 * putting a slab of grey over the picture.
	 */
	text-shadow:
		0 0 2px rgba(13, 20, 20, .95),
		0 1px 6px rgba(13, 20, 20, .92),
		0 2px 24px rgba(13, 20, 20, .85);
}

/* The quotation marks belong to the sentence, not to a decorative glyph the
   size of a house — which is what the old block used. */
.gtd-quote__text::before { content: "\201C"; }
.gtd-quote__text::after  { content: "\201D"; }

.gtd-quote__caption {
	display: block;
	margin-top: clamp(1.5rem, 3vw, 2.5rem);
	font-style: normal;
}

.gtd-quote__rule {
	display: block;
	width: clamp(2.5rem, 6vw, 4.5rem);
	height: 3px;
	margin-bottom: clamp(.9rem, 1.8vw, 1.4rem);
	background: var(--q-red);
	transform-origin: left;
}

.gtd-quote__name {
	display: block;
	font-style: normal;
	font-size: clamp(1rem, .9rem + .45vw, 1.3rem) !important;
	font-weight: 700;
	letter-spacing: .01em;
	color: #fff !important;
	text-shadow:
		0 0 2px rgba(13, 20, 20, .95),
		0 1px 5px rgba(13, 20, 20, .92),
		0 1px 18px rgba(13, 20, 20, .85);
}

.gtd-quote__company {
	display: block;
	margin-top: .3rem;
	font-size: clamp(.72rem, .66rem + .22vw, .85rem) !important;
	font-weight: 700;
	letter-spacing: .14em;
	text-transform: uppercase;
	/*
	 * White, not red. #ff5b4a over the light grey hoodie in the Mike Ryan
	 * photograph measures 1.47:1 — the line is simply not there. The caps and
	 * the tracking carry the distinction instead of the colour.
	 */
	color: rgba(255, 255, 255, .82) !important;
	text-shadow:
		0 0 2px rgba(13, 20, 20, .95),
		0 1px 5px rgba(13, 20, 20, .92),
		0 1px 16px rgba(13, 20, 20, .85);
}

/* ═════════════════════════════════════════════════════════════
   THE MOMENT
   ═════════════════════════════════════════════════════════════ */

@supports (animation-timeline: view()) {
	@media (prefers-reduced-motion: no-preference) {

		/*
		 * The pop. The photograph arrives a touch large and settles back to
		 * its true size as the band comes into frame — the movement happens on
		 * ARRIVAL and then stops, rather than drifting the whole way past.
		 *
		 * It resolves ON the whole picture rather than away from it: the band
		 * is sized to the photograph's own 1728 x 805, so scale(1) is the
		 * complete frame, and the 4% it starts at is the only moment anything
		 * is held back. With the origin pinned to the top edge, that 4% comes
		 * off the bottom, so the subject never leaves the frame.
		 *
		 * linear, and this matters. A scroll-driven animation is a mapping from
		 * scroll position to progress, so an ease-out curve — which spends
		 * most of its output in the first fraction of its input — finishes the
		 * whole zoom within the first few pixels of scroll. Measured at 11%
		 * into the range, the earlier eased version had already settled to
		 * scale(1.0000): the pop was over before the band was on screen.
		 * Every timing function here is linear for that reason, and the
		 * staging is done with the ranges instead.
		 *
		 * `animation-duration: auto` is not decoration either. A scroll-driven
		 * animation given a real duration — `animation: x 1s linear both` —
		 * does NOT fill its range: measured on this banner, the zoom reported
		 * playState "finished" 42.5% of the way through, so the picture was at
		 * its final size before it had properly arrived. auto is what makes
		 * the range the duration.
		 *
		 * And NOT a wipe — nothing slides across the picture.
		 */
		.gtd-quote__img {
			animation-name: gtdQuoteZoom;
			animation-duration: auto;
			animation-timing-function: linear;
			animation-fill-mode: both;
			animation-timeline: view();
			/*
			 * The WHOLE pass, not just the arrival. Spread over the full cover
			 * range the picture is visibly moving for as long as the band is
			 * on screen, which is what makes it readable as motion at all — an
			 * earlier 6% confined to the entry window measured as travelling
			 * and still could not be seen.
			 */
			animation-range: cover 0% cover 100%;
		}

		.gtd-quote__rule {
			animation-name: gtdQuoteDraw;
			animation-duration: auto;
			animation-timing-function: linear;
			animation-fill-mode: both;
			animation-timeline: view();
			animation-range: entry 22% entry 52%;
		}

		.gtd-quote__text {
			animation-name: gtdQuoteRise;
			animation-duration: auto;
			animation-timing-function: linear;
			animation-fill-mode: both;
			animation-timeline: view();
			animation-range: entry 16% entry 46%;
		}

		.gtd-quote__name,
		.gtd-quote__company {
			animation-name: gtdQuoteRise;
			animation-duration: auto;
			animation-timing-function: linear;
			animation-fill-mode: both;
			animation-timeline: view();
			animation-range: entry 30% entry 60%;
		}
	}
}

/*
 * 16%, and it grows rather than settles — the client asked for the background
 * to enlarge. It begins on the whole photograph as the band appears and pushes
 * in the whole way past. With transform-origin pinned to the top edge, all of
 * that growth runs downward and outward from the centre, so the subject's head
 * never leaves the top of the frame no matter how far it scales.
 */
@keyframes gtdQuoteZoom {
	from { transform: scale(1); }
	to   { transform: scale(1.16); }
}

@keyframes gtdQuoteDraw {
	from { transform: scaleX(0); }
	to   { transform: scaleX(1); }
}

@keyframes gtdQuoteRise {
	from { opacity: 0; transform: translateY(3rem); }
	to   { opacity: 1; transform: none; }
}

/* ═════════════════════════════════════════════════════════════
   NARROWER
   ═════════════════════════════════════════════════════════════ */

@media (max-width: 720px) {
	.gtd-quote {
		min-height: min(68vh, 520px);
	}

	.gtd-quote__text { max-width: 100%; }

	/* On a phone the band is taller than the photograph is shaped, so the crop
	   is horizontal and the vertical is moot. Centre the subject. */
	.gtd-quote__img { object-position: 50% center; }
}
