/*
 * Component: Performance Engineering
 * Scope: the /gt-performance/ page templates only (enqueued per-template via gtm_load).
 *
 * Holds the ONE deliberate upgrade for these pages — a tasteful metallic/chrome
 * treatment built in our own style with our own tokens — plus the parts-brand
 * placeholder chips for the "OUR BRANDS" strip. The big outline hero title keeps
 * the theme's existing white-stroke style (secondary-header.css); the silver here
 * is a separate, reusable accent so title contrast over the dark hero is untouched.
 *
 * The chrome ramp is expressed as local custom properties so the same silver reads
 * consistently on the hero plate and the button, and is easy to retune in one place.
 */

:root {
    /* Chrome ramp — light highlight → mid steel → shadow. Neutral, brand-safe. */
    --gtm-chrome-1: #f6f6f8; /* top highlight */
    --gtm-chrome-2: #dcdce1;
    --gtm-chrome-3: #b8b8c0;
    --gtm-chrome-4: #8f8f99; /* core shadow line */
    --gtm-chrome-ink: #2b2630; /* charcoal text that sits ON the silver */
}

/* =====================================================================
   1. HERO — Performance Engineering variant
   Adds a metallic "nameplate" band that carries the eyebrow, sitting above
   the existing outline title. Reusable across all five perf-eng pages.
   ===================================================================== */

/* Give the perf hero a touch more presence than the standard interior hero. */
/* Taller, top-weighted hero for the perf variant. Higher specificity
   (.hero-secondary.hero-secondary--perf) so it wins over the base .hero-secondary
   min-height regardless of stylesheet load order. Content is top-aligned so the
   eyebrow + title sit ABOVE the racing-bike cutout that fills the lower band. */
.hero-secondary.hero-secondary--perf {
    min-height: 520px;
    /* Hero is a flex COLUMN → justify-content is the vertical axis; top-weight the
       content so eyebrow + title sit above the bike (align-items stays centered). */
    justify-content: flex-start;
}

.hero-secondary--perf .hero-secondary__content {
    padding-top: clamp(32px, 6vh, 72px);
}

@media (min-width: 1200px) {
    .hero-secondary.hero-secondary--perf {
        min-height: 600px;
    }
}

/* Racing-bike cutout layer (the MT-livery bike exported from the Figma). Renders
   only when a bike_image arg is supplied to the hero part (see hero-secondary.php).
   Sits above the bg image + overlay, below the content, and occupies the lower band
   of the hero so the eyebrow + title stay clear above it. */
.hero-secondary__bike {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 60%;
    z-index: 1;
    background-repeat: no-repeat;
    background-position: center bottom;
    background-size: contain;
    pointer-events: none;
}

/* The silver band / nameplate that holds the eyebrow. */
.hero-secondary--perf .hero-secondary__plate {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 10px clamp(20px, 3vw, 40px);
    border-radius: 6px;
    /* Vertical chrome ramp with a crisp bright core line, like brushed nameplate steel. */
    background: linear-gradient(
        180deg,
        var(--gtm-chrome-1) 0%,
        var(--gtm-chrome-2) 40%,
        var(--gtm-chrome-4) 50%,
        var(--gtm-chrome-2) 60%,
        var(--gtm-chrome-1) 100%
    );
    border: 1px solid rgba(255, 255, 255, 0.35);
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.9),
        inset 0 -1px 0 rgba(0, 0, 0, 0.35),
        0 8px 22px -10px rgba(0, 0, 0, 0.7);
    overflow: hidden;
}

/* Soft diagonal sheen sweeping across the plate — kept subtle. */
.hero-secondary--perf .hero-secondary__plate::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(
        105deg,
        rgba(255, 255, 255, 0) 30%,
        rgba(255, 255, 255, 0.55) 48%,
        rgba(255, 255, 255, 0) 66%
    );
    pointer-events: none;
}

.hero-secondary--perf .hero-secondary__plate-text {
    position: relative;
    z-index: 1;
    font-weight: 900;
    text-transform: uppercase;
    letter-spacing: 0.16em;
    font-size: clamp(0.72rem, 1.4vw, 1rem);
    line-height: 1.2;
    color: var(--gtm-chrome-ink);
    /* Engraved feel: light lift below the ink, matches nameplate etching. */
    text-shadow: 0 1px 0 rgba(255, 255, 255, 0.45);
}

@media (prefers-reduced-motion: reduce) {
    .hero-secondary--perf .hero-secondary__plate::after { display: none; }
}

/* =====================================================================
   2. "OUR BRANDS" strip — parts-brand logos on a black sponsor band
   Reuses the .gtm-logo-bar shell (logo-bar.php custom-logos mode). The logos were
   exported from the Figma as opaque tiles — mostly black-backed, Brembo white — so
   the section is a full-bleed BLACK band where the black tiles blend into a clean
   sponsor board; each image is normalised to a uniform height.
   ===================================================================== */

/* Quieter than a section headline — an eyebrow-scale "our brands" label, light on black.
   Centred over the row: the logos are centre-justified, so a left-aligned label
   left the two halves of the block disagreeing about where the section sits. */
.gtm-perf-brands .gtm-section-title {
    text-align: center;
    color: #cfcfd6;
    opacity: 0.82;
    font-size: clamp(0.72rem, 1.4vw, 0.95rem);
    letter-spacing: 0.22em;
    font-weight: 700;
    /* All caps, no descenders — the inherited leading is dead space that pushed
       the label 11px below the plate's optical centre and padded the gap down
       to the logos. Setting it to the cap height makes the padding read as
       symmetric as it measures. */
    line-height: 1;
    margin: 0;
}

/* The label sat ~110px above the marques, reading as two unrelated things
   rather than one captioned board. Three separate spacings were stacking: the
   heading's own margin, the section-inner's 32px flex gap, and the logo bar's
   16px block margin — the flex column means they add rather than collapse.
   Zero the margins and let the one gap own the rhythm. */
.gtm-perf-brands .gtm-section-inner {
    gap: clamp(12px, 1.3vw, 17px);
}

.gtm-perf-brands .gtm-logo-bar--brands {
    margin-block: 0;
}

.gtm-logo-bar--brands .gtm-logo-bar__list {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: clamp(10px, 1.4vw, 22px);
    margin: 0;
    padding: 0;
    list-style: none;
}

/* Natural-width items, height-normalised so the row reads as a tidy sponsor board. */
.gtm-logo-bar--brands .gtm-logo-bar__item {
    flex: 0 0 auto;
    min-width: 0;
}

.gtm-logo-bar--brands .gtm-logo-bar__link {
    display: block;
    line-height: 0;
}

/* One row is the design's whole point — 13 marques read as a sponsor board only if
   they sit on a single line. Until now the logos carried no intrinsic size, so they
   measured 0 wide and "fitted" by accident; with real widths, 40px tall overflows to
   two rows. Sized so 13 logos plus gaps fit the 1201px content width, which lands at
   the same proportion the design uses (25px tall across its 1400px strip). */
.gtm-logo-bar--brands .gtm-logo-bar__img {
    display: block;
    /* The base logo-bar dims its logos (opacity .5) and shrinks them (scale .9),
       resting states for the homepage's interactive brand filter where hover
       brings a logo forward. This strip is a static credentials board with
       nothing to hover, so those resting states just render every mark washed
       out and undersized on a black ground. Reset both. */
    opacity: 1;
    transform: none;
    height: clamp(18px, 1.7vw, 24px);
    width: auto;
    max-width: 160px;
    object-fit: contain;
}

/* Below desktop the row is allowed to wrap — 13 marques cannot stay on one line on
   a phone and shrinking them further would make them unreadable. The clamp already
   floors the height at 16px. */
@media (max-width: 576px) {
    .gtm-logo-bar--brands .gtm-logo-bar__img { max-width: 120px; }
}

/* Neutral placeholder chip — defensive fallback for any brand shipped without a
   logo asset; legible on the dark band. (All 13 brands currently have logos.) */
.gtm-logo-bar__placeholder {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 8px 12px;
    border: 1px dashed rgba(255, 255, 255, 0.3);
    border-radius: 8px;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-size: clamp(11px, 1vw, 13px);
    line-height: 1.15;
    color: rgba(255, 255, 255, 0.6);
}

/* ===========================================================================
   Carbon ground + silver stripes
   The page runs on a dark carbon-fibre weave with the hero's stripe band in the
   same chrome ramp as the nameplate, so hero and page read as one material.
   =========================================================================== */

/* Woven carbon, generated rather than shipped as an image: two 45deg twill
   directions offset by half a tile, over the dark base. ~0 bytes, resolution
   independent, and it tiles seamlessly at any width. */
/* Real 2x2 twill: the weave is built from gradients at 27deg / 207deg, offset a
   half-tile against each other so the bundles interlock instead of running as
   parallel lines. The first four layers are the two fibre directions (two tones
   each, so alternate bundles catch light differently); the fifth is the vertical
   bundle seam; the sixth is the horizontal banding that gives the tile its depth.
   A 20px tile is one full weave cell — the 8px 45deg hatch this replaced was too
   fine to read as anything but noise.

   Tones are tinted toward the theme charcoal (#1C1922 / #342E37) rather than the
   neutral greys the stock recipe uses, so the carbon belongs to GT's palette
   instead of looking like a bolted-on texture. */
body.gtm-perf-page {
    /* Deliberately a TIGHT, dark ramp. The weave only needs to be felt, not read —
       the first pass spanned #14131a to #262430, and those lighter bundles produced
       pale patches that fought the copy sitting on them. Everything now lives inside
       ~#0f0e14–#18171f, so the ground stays visually flat for text while the twill
       is still there at close range. Retune contrast by widening/narrowing this
       range, not by changing the geometry below. */
    --gtm-carbon-base: #0f0e14;
    --gtm-carbon-1: #111016;  /* fibre, direction A — shadowed bundle */
    --gtm-carbon-2: #17161e;  /* fibre, direction A — lit bundle */
    --gtm-carbon-3: #121118;  /* vertical seam between bundles */
    --gtm-carbon-4: #131219;  /* horizontal band, upper */
    --gtm-carbon-5: #100f15;  /* horizontal band, lower */
    --gtm-carbon-6: #18171f;  /* highlight band */

    /* Held in a custom property so every carbon surface — page ground, hero floor,
       anything added later — paints the identical stack instead of six gradients
       being copy-pasted and drifting apart. */
    /* NB images and their offsets are separate properties: background-image only
       accepts image values, so folding "…) 0 5px" into it (as the shorthand form
       allows) makes the whole declaration invalid and the texture silently vanishes.
       The half-tile offsets are what interlock the weave, so they matter. */
    --gtm-carbon-image:
        linear-gradient(27deg, var(--gtm-carbon-1) 5px, transparent 5px),
        linear-gradient(207deg, var(--gtm-carbon-1) 5px, transparent 5px),
        linear-gradient(27deg, var(--gtm-carbon-2) 5px, transparent 5px),
        linear-gradient(207deg, var(--gtm-carbon-2) 5px, transparent 5px),
        linear-gradient(90deg, var(--gtm-carbon-3) 10px, transparent 10px),
        linear-gradient(
            var(--gtm-carbon-4) 25%,
            var(--gtm-carbon-5) 25%, var(--gtm-carbon-5) 50%,
            transparent 50%, transparent 75%,
            var(--gtm-carbon-6) 75%, var(--gtm-carbon-6)
        );
    --gtm-carbon-position: 0 5px, 10px 0, 0 10px, 10px 5px, 0 0, 0 0;

    background-color: var(--gtm-carbon-base);
    background-image: var(--gtm-carbon-image);
    background-position: var(--gtm-carbon-position);
    background-size: 20px 20px;
    /* deliberately NOT background-attachment: fixed — a fixed background repaints
       the whole viewport on every scroll frame and janks on mobile Safari. The
       tile is seamless, so scrolling with the content looks identical. */
}

/* Sections that ship an opaque white/grey ground would cover the weave, so on
   this page they go transparent and let the carbon through. The brands band
   keeps its own black — the logo tiles are black-backed and need it. */
body.gtm-perf-page .gtm-section.gtm-bg-white,
body.gtm-perf-page .gtm-section.gtm-bg-grey {
    background: transparent;
}

/* --- The machined plate ----------------------------------------------------
 *
 * A shared surface, opted into with .gtm-perf-plate. The brands strip used to
 * be flat #000 and the explore band a flat charcoal veil; both sat on the
 * carbon page with no edge of their own, reading as holes rather than panels,
 * and both painted straight over the page's sheen so nothing at page level
 * could reach them. One surface now serves both, and any future band can ask
 * for it by name instead of copying the layer stack.
 *
 * The face is five layers over a near-black base. A knurl crosshatch — the
 * diamond cut on a tool handle or a footpeg — built as a lit edge immediately
 * followed by a dark one, because a groove reads as machined only when it has
 * both a catch and a shadow. Under it a fine lathe grain running across the
 * plate. Then a raking highlight as though light falls from the upper left,
 * and a soft pool of light at the centre so content sits lit while the plate
 * falls off to black at its edges.
 *
 * Every layer is a few percent alpha. Up close it is unmistakably metal; at a
 * glance it is just a surface that catches light, which is the whole intent —
 * the content stays the subject.
 *
 * Specificity note: this has to out-rank the .gtm-bg-white / .gtm-bg-grey
 * transparency rule above, which is also (0,3,0), so it relies on coming after
 * it. Keep it below that rule.
 */
/* The layer stack lives in custom properties, exactly as --gtm-carbon-image does,
   so any surface can ask for the plate by name. The class below is one consumer,
   not the definition — before this, the only way to get a plate was to be a
   .gtm-section with the class, so the parts panels ended up copying a similar
   stack by hand and drifting from it. A surface used in more than one place has
   to be nameable in more than one place. */
body.gtm-perf-page {
    --gtm-plate-base: #08080b;
    --gtm-plate-image:
        repeating-linear-gradient(62deg, rgba(255, 255, 255, .055) 0 1px, rgba(0, 0, 0, .38) 1px 2px, transparent 2px 8px),
        repeating-linear-gradient(-62deg, rgba(255, 255, 255, .04) 0 1px, rgba(0, 0, 0, .30) 1px 2px, transparent 2px 8px),
        repeating-linear-gradient(0deg, rgba(255, 255, 255, .022) 0 1px, transparent 1px 3px),
        linear-gradient(100deg, rgba(255, 255, 255, .055) 0%, rgba(255, 255, 255, 0) 46%),
        radial-gradient(130% 180% at 50% 50%, rgba(255, 255, 255, .07) 0%, rgba(255, 255, 255, 0) 62%);
    /* Milled edges. A lit top lip and a fainter bottom one are what separate the
       plate from the carbon above and below it — without them a band has no
       boundary and stops reading as a discrete panel. */
    --gtm-plate-shadow:
        inset 0 1px 0 rgba(255, 255, 255, .11),
        inset 0 -1px 0 rgba(255, 255, 255, .05);
}

body.gtm-perf-page .gtm-section.gtm-perf-plate {
    background-color: var(--gtm-plate-base);
    background-image: var(--gtm-plate-image);
    box-shadow: var(--gtm-plate-shadow);
}


/* Copy has to survive the dark ground. */
body.gtm-perf-page .gtm-section-title,
body.gtm-perf-page .gtm-heading-lg,
body.gtm-perf-page h2,
body.gtm-perf-page h3 { color: #ffffff; }

body.gtm-perf-page .gtm-body-text,
body.gtm-perf-page .gtm-section p { color: rgba(255, 255, 255, 0.78); }

/* --- Hero stripes: silver, not a flat brand colour -------------------------
   The stripe bars normally paint --stripe-color / --stripe-2 flat. On the perf
   hero they take the chrome ramp instead. Scoped to .perf-hero (0,2,0) so it
   out-specifies the base rule without !important, and the JS colour cross-fade
   simply has nothing to animate here. */
/* Real metal, not a gradient pretending to be metal. Three things separate the
   two, and a flat ramp had none of them:

   1. GRAIN. Brushed aluminium is scored along its length, so the bars carry fine
      horizontal lines. The four periods below (2, 3, 5, 7px) are coprime, so they
      never coincide into a visible repeat the way a single period does.
   2. GRIT. Real metal is not clean — there is dirt and micro-pitting in the
      surface. That is the fractal-noise layer, laid over at low opacity in
      `overlay` so it darkens the shadows and brightens the highlights instead of
      greying the whole bar down.
   3. A SPECULAR SWEEP. Metal has one bright band where the surface faces the
      light, not an even sheen end to end.

   isolation:isolate keeps the blend modes inside the bar rather than letting them
   composite against the hero photograph behind it. */
.perf-hero .gt-stripes__s1,
.perf-hero .gt-stripes__s2 {
    /* NOT position:relative. The base rule pins these absolutely — s1 to the top
       of the band, s2 to the bottom — and the 16px of floor showing between them
       is what makes the hero read as TWO stripes, exactly as the homepage and
       brand heroes do. Making them relative dropped both into normal flow, so s2
       stacked straight under s1 and the pair fused into one 88px slab.
       Absolute already establishes the containing block that overflow and
       isolation need, so nothing is lost by leaving the base positioning alone. */
    overflow: hidden;
    isolation: isolate;
}

.perf-hero .gt-stripes__s1 {
    background-color: var(--gtm-chrome-2);
    background-image:
        repeating-linear-gradient(180deg, rgba(255, 255, 255, .22) 0 1px, rgba(0, 0, 0, .18) 1px 2px, transparent 2px 3px),
        repeating-linear-gradient(180deg, rgba(255, 255, 255, .12) 0 1px, transparent 1px 5px),
        repeating-linear-gradient(180deg, rgba(0, 0, 0, .12) 0 1px, transparent 1px 7px),
        linear-gradient(
            180deg,
            var(--gtm-chrome-1) 0%,
            var(--gtm-chrome-2) 38%,
            var(--gtm-chrome-4) 52%,
            var(--gtm-chrome-2) 66%,
            var(--gtm-chrome-1) 100%
        );
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, .58),
        inset 0 -1px 0 rgba(0, 0, 0, .45);
}

/* Lower bar runs the ramp inverted, so the two bars catch the light from
   opposite sides — the same metal, lit as two separate pieces of trim
   rather than two identical strips. Its grain is offset half a period so the
   two bars do not look like one texture cut in half. */
.perf-hero .gt-stripes__s2 {
    background-color: var(--gtm-chrome-3);
    background-image:
        repeating-linear-gradient(180deg, rgba(255, 255, 255, .16) 0 1px, rgba(0, 0, 0, .20) 1px 2px, transparent 2px 3px),
        repeating-linear-gradient(180deg, rgba(0, 0, 0, .14) 0 1px, transparent 1px 4px),
        repeating-linear-gradient(180deg, rgba(255, 255, 255, .09) 0 1px, transparent 1px 7px),
        linear-gradient(
            180deg,
            var(--gtm-chrome-3) 0%,
            var(--gtm-chrome-4) 45%,
            var(--gtm-chrome-2) 100%
        );
    background-position: 0 1px, 0 2px, 0 0, 0 0;
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, .48),
        inset 0 -1px 0 rgba(0, 0, 0, .40);
}

/* The dirt. */
.perf-hero .gt-stripes__s1::after,
.perf-hero .gt-stripes__s2::after {
    content: "";
    position: absolute;
    inset: 0;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23g)'/%3E%3C/svg%3E");
    background-size: 160px 160px;
    opacity: .38;
    mix-blend-mode: overlay;
    pointer-events: none;
}

/* The light. One bright band and one weaker secondary, both off-axis so they
   read as a reflection rather than as a gradient someone drew. */
.perf-hero .gt-stripes__s1::before,
.perf-hero .gt-stripes__s2::before {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(
        104deg,
        rgba(255, 255, 255, 0) 10%,
        rgba(255, 255, 255, .50) 24%,
        rgba(255, 255, 255, 0) 38%,
        rgba(0, 0, 0, .10) 52%,
        rgba(255, 255, 255, 0) 62%,
        rgba(255, 255, 255, .26) 73%,
        rgba(255, 255, 255, 0) 84%
    );
    pointer-events: none;
    z-index: 1;
}

@media (prefers-reduced-motion: reduce) {
    .perf-hero .gt-stripes__s1::before,
    .perf-hero .gt-stripes__s2::before { opacity: .7; }
}

/* The hero's "floor" below the stripes is white by default (--hero-page). On this
   page it takes the full weave, not just the base colour, so the hero reads as the
   same material as the page rather than a dark panel sitting on carbon. Both the
   token fallback and the painted surface are set: --hero-page covers anything else
   in hero-banner.css that reads it, and the background-image does the actual weave. */
.perf-hero { --hero-page: var(--gtm-carbon-base, #14131a); }

.perf-hero .gtm-hero-floor {
    background-color: var(--gtm-carbon-base, #14131a);
    background-image: var(--gtm-carbon-image);
    background-position: var(--gtm-carbon-position);
    background-size: 20px 20px;
}

/* Marque credentials sitting under the welcome copy (NSR / V4). They are white or
   yellow marks on a dark ground, shown small and muted so they read as provenance
   rather than competing with the heading. */
.gtm-perf-marks {
    display: flex;
    align-items: center;
    gap: 22px;
    margin-top: 26px;
    flex-wrap: wrap;
}

/* Both marks share a 100x120 canvas but their artwork fills it differently — NSR
   75% of the height, V4 31% — so a single canvas height reproduces the design's
   relative sizing (NSR reads taller than V4) rather than distorting it. 42px was
   measured off the v6 export, scaled from its 1920 artboard to our container. */
.gtm-perf-marks__logo {
    /* 42px -> 56px (+33%). At 42 the NSR and V4 marks were credentials nobody
       could read - the whole point of putting them under the copy is that a
       reader recognises them. The 100x120 artboard means the relative sizing
       between the two is preserved as they scale. */
    height: 56px;
    width: auto;
    opacity: 0.9;
}

/* --- Explore card tiles ----------------------------------------------------
   a.gtm-card-i paints its media with a background-image div at height:100%,
   which resolves only against a definite height. section.css stretches the
   wrapper to its grid row, so the card is fine whenever something else in that
   row has real content height, and collapses to nothing when it doesn't — fine
   in a 4-up row beside Bikes in Stock, 2px tall the moment a 2-up layout puts it
   on a row of its own. (Pre-existing and site-wide: /ducati/ at 1000px reports
   gridTemplateRows "447.312px 2px", the second row being a collapsed Legends
   card. Reported separately; fixed here only where this page is responsible.)

   NEVER fix that with aspect-ratio in the mixed grid. 1fr is minmax(auto, 1fr),
   so a track cannot shrink below its item's min-content width, and aspect-ratio
   turns the row height into precisely that. Setting 4/3 on the two photo cards
   made them demand 321 * 4/3 = 428px of track each and crushed Bikes in Stock to
   147px and Parts & Accessories to 231px. A min-height floor gives the row the
   height it needs and never feeds back into width. */
body.gtm-perf-page .gtm-explore-grid > *:has(> .gtm-card-i) {
    min-height: clamp(200px, 24vw, 300px);
    /* STRETCH, not percentage. height:100% on the card resolves only against a
       DEFINITE parent height: aspect-ratio supplies one, min-height does not. So
       the moment the ratio came off below 1000px the card stopped filling its
       tile and sat 96px tall inside a 300px box with a 204px hole under it.
       Making the wrapper a flex container fills the card by box alignment
       instead, which works whatever produced the tile's height - ratio, floor, or
       a stretched grid row - and takes the percentage out of the picture. */
    display: flex;
}

body.gtm-perf-page .gtm-explore-grid > * > .gtm-card-i {
    flex: 1 1 auto;
    min-width: 0;
    height: auto;
    /* .card carries a 16px bottom margin, which used to be invisible because the
       card was sized by percentage. Stretched, it leaves the card 16px short of
       its tile. The grid gap already owns the space between tiles. */
    margin-bottom: 0;
}

/* The media inside the card has the same percentage-height dependency, so give it
   the same treatment: the card is already a flex column, so let the media flex
   into whatever height is left rather than ask for 100% of a parent that may not
   have a definite one. */
body.gtm-perf-page .gtm-explore-grid > * > .gtm-card-i > .gtm-cardImage {
    flex: 1 1 auto;
    min-height: 0;
}

/* The perf-explore grid is the one case where a ratio IS safe: every card in it
   is the same type, so an identical ratio asks every track for the same width
   and the grid stays even. It also wants the ratio — with no content-height
   card anywhere in the row, nothing else can size it. The floor above would only
   fight the ratio here, so it is cleared. */
/* aspect-ratio and min-height must NOT be set together here. The ratio derives
   the tile's height from its width, but it also runs BACKWARDS: a 300px floor
   with a 4/3 ratio gives the item a 400px min-content WIDTH, which inflates the
   grid track. That overflowed the page by 16px at 768 and collapsed 753 to a
   single 706px column. So: a plain floor below 1000px where the columns are too
   narrow for the ratio anyway, and the ratio alone above it, where a column is
   464px+ and 4/3 already clears the card's content height.
   (Third time this trap has appeared in this build — the rule is that
   aspect-ratio is only ever safe on an item whose width nothing else depends on.) */
body.gtm-perf-page .gtm-perf-explore .gtm-explore-grid > * {
    min-width: 0;
    /* The card's own content needs ~300px: below that it overflowed its tile by
       48px at 768 and spilled its text 32px into the tile beneath. */
    min-height: 300px;
}

@media (min-width: 1000px) {
    body.gtm-perf-page .gtm-perf-explore .gtm-explore-grid > * {
        aspect-ratio: 4 / 3;
        min-height: 0;
    }
}

/* --- The service cards on a phone: a swipe rail ----------------------------
 *
 * Stacked one per row, these four cards ran to 1378px on a 390px screen — most
 * of the page spent on a single section, and no way to see the range of work
 * without a long scroll. The obvious fix is the compact row the Explore GT
 * Motorcycles tiles collapse to, but that throws away the photography, and on a
 * page selling engineering work the pictures ARE the argument.
 *
 * So: one row, swiped. Cards keep their full size and the section costs one
 * card's height instead of four. The next card is deliberately left peeking at
 * the right edge — that overhang is the only affordance a rail needs, and it is
 * more honest than a row of dots that says "there is more" without showing it.
 * Nothing auto-advances and nothing is hidden behind a timer; the reader moves
 * it or it stays where they left it.
 *
 * The rail bleeds through the section gutter to the screen edge so cards run
 * off the side rather than stopping short, which is what makes it read as a
 * rail rather than a clipped grid. scroll-padding puts the snapped card back on
 * the gutter line so it still aligns with the heading above it.
 *
 * Phone only - and "phone" has to stop at 600px, not 768px. An iPad reports a
 * 768px viewport, but a visible scrollbar makes the media query see 753px, so
 * max-width:767.98 caught the tablet and served it the phone rail: 551px cards
 * carrying a layout drawn for a 390px screen. From 600px up there are two real
 * grid columns, which is the better answer anyway because all four services are
 * on screen at once instead of two and a peek.
 */
@media (max-width: 599.98px) {
    body.gtm-perf-page .gtm-perf-explore .gtm-explore-grid {
        display: flex;
        grid-template-columns: none;
        overflow-x: auto;
        overflow-y: hidden;
        scroll-snap-type: x mandatory;
        scroll-padding-inline: 16px;
        /* Bleed past .gtm-section-inner's 16px gutter, then pad it back so the
           first card still starts on the gutter line. */
        margin-inline: -16px;
        padding-inline: 16px;
        /* Room for the card shadow, which a clipped overflow would shear off. */
        padding-block: 4px;
        gap: 12px;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: none;
    }

    body.gtm-perf-page .gtm-perf-explore .gtm-explore-grid::-webkit-scrollbar {
        display: none;
    }

    /* 78% leaves roughly a fifth of the next card showing. Wide enough that the
       title and its line of copy still sit comfortably, narrow enough that the
       overhang is unmistakable rather than a suspicious sliver. */
    body.gtm-perf-page .gtm-perf-explore .gtm-explore-grid > * {
        flex: 0 0 78%;
        scroll-snap-align: start;
    }

    /* cards.css hides card taglines below 768px, which is right for a tile
       squeezed into a compact row but wrong here: the rail keeps these cards at
       full size, so there is room for the line that says what the service
       actually is. "TUNING" on its own does not tell anyone anything. Scoped to
       this rail, so the compact tiles below still drop theirs. */
    body.gtm-perf-page .gtm-perf-explore a.gtm-card-i .card-body .brand-card-paragraph {
        display: block;
        /* A flex item will not shrink below its own content width unless told it
           may, so at the inherited 14px three of the four taglines ran straight
           out past the card's right edge. min-width lets them wrap, and the
           smaller size means they mostly don't have to — the tagline is support
           for the title, not a competitor to it. */
        min-width: 0;
        /* cards.css keeps the tagline on one line, which is what pushed it out
           past the card edge (30px on Tuning) and dragged the chevron with it —
           the flex row simply grew wider than the card. There is 97px of unused
           height under this row, so wrapping costs nothing and truncating the
           copy would cost the sentence. */
        white-space: normal;
        font-size: 12px;
        line-height: 1.35;
    }

    /* Below 768px the theme turns .card-body into a horizontal row so a compact
       tile can put its title and chevron on one line. These cards are full size
       and carry a tagline, and in a row that tagline sits BESIDE the title and
       the three of them are wider than the card — measured with .link running
       111→353px inside a card ending at 295px, dragging the chevron out with it.
       Back to the column these cards use on desktop: title, then tagline and
       chevron on the line beneath. */
    body.gtm-perf-page .gtm-perf-explore a.gtm-card-i .card-body {
        flex-direction: column;
        align-items: flex-start;
        /* Changing the DIRECTION without also resetting justify-content left the
           compact-tile rules' `space-between` acting on the vertical axis: the
           title pinned to the top of the card and its own tagline pinned to the
           bottom, 245px and an entire photograph between two things that are one
           caption. They are a title and its subtitle - they group, or neither
           means anything. Bottom-anchored at 6px apart, which is what every other
           breakpoint already does and what the card's own bottom scrim is drawn
           for. */
        justify-content: flex-end;
        gap: 6px;
        /* The compact-tile rules leave no space under the copy, so every tagline
           sat 1px off the card's bottom edge — fine when the text is the last
           thing in a short row, cramped when it is a wrapped line over a photo.
           !important only because the rule being overridden is itself
           "padding: 0 !important", which no amount of specificity can outrank.
           Horizontal padding is untouched; the card supplies its own 24px. */
        padding-bottom: 18px !important;
    }

    /* width so the chevron still lands on the card's right edge, min-width so the
       row may actually shrink — a flex item defaults to min-width:auto and would
       otherwise refuse to go below its content width, which is the whole reason
       the line escaped the card. */
    body.gtm-perf-page .gtm-perf-explore a.gtm-card-i .card-body .link {
        width: 100%;
        min-width: 0;
        justify-content: space-between;
        gap: 12px;
    }

    /* A card can only snap to the gutter line if there is track left after it.
       Without a trailing spacer the last card runs out of runway and rests
       wherever the scroll ends — measured at 79px in, while the other three sat
       at 16px, so the fourth card alone looked misaligned. The spacer is exactly
       the width the card does not occupy (100% - 78%, less the gap), which buys
       the last card the same snap position as its siblings and reads as the end
       of the rail rather than a card stuck against the edge. */
    body.gtm-perf-page .gtm-perf-explore .gtm-explore-grid::after {
        content: "";
        flex: 0 0 calc(22% - 12px);
    }
}

/* The explore band used a flat charcoal veil to separate its four tiles from
   the carbon. It now takes the shared machined plate instead — same job, and
   the two dark bands on the page read as the same material rather than two
   unrelated greys. See .gtm-perf-plate above. */

/* --- Legibility on the carbon ground ---------------------------------------
   The theme's eyebrow (.gtm-pre-heading) is charcoal #342E37 — correct on the
   light sections it was designed for, invisible on carbon. Anything else that
   inherits a dark ink on this page gets the same treatment. Muted rather than
   pure white so the eyebrow still sits BELOW the headline in the hierarchy. */
body.gtm-perf-page .gtm-pre-heading {
    color: rgba(255, 255, 255, 0.72);
}

/* Links inside body copy need to read as links on the dark ground. */
body.gtm-perf-page .gtm-section .gtm-body-text a,
body.gtm-perf-page .gtm-card-open-link .gtm-col-content a {
    color: #FFC424;
    text-decoration-color: rgba(255, 196, 36, 0.5);
}

/* --- Machined light over the weave -----------------------------------------
   A tiled texture alone reads as wallpaper: identical everywhere, no sense of a
   physical object. Real carbon is a moulded panel, so light rakes across it and
   the weave brightens and dulls in broad bands.

   This adds that as one fixed, viewport-scale layer above the tile and below the
   content: a soft off-axis sheen (the panel catching light), a second wider counter
   sweep, and fine machining lines running with the sweep. All at very low alpha —
   the effect should be felt as depth, not seen as a gradient.

   Fixed + pointer-events:none + a single compositor layer, so it costs nothing to
   scroll past and never intercepts clicks. */
body.gtm-perf-page::before {
    content: "";
    position: fixed;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    background:
        /* fine machining grain, aligned to the sweep */
        repeating-linear-gradient(
            115deg,
            rgba(255, 255, 255, 0.014) 0 1px,
            transparent 1px 7px
        ),
        /* primary raking highlight across the panel */
        linear-gradient(
            115deg,
            transparent 0%,
            rgba(255, 255, 255, 0.035) 26%,
            rgba(255, 255, 255, 0.055) 34%,
            rgba(255, 255, 255, 0.02) 44%,
            transparent 62%
        ),
        /* counter sweep, wider and weaker, keeps it from looking like one stripe */
        linear-gradient(
            292deg,
            transparent 30%,
            rgba(255, 255, 255, 0.022) 62%,
            transparent 88%
        ),
        /* corner fall-off so the panel has edges rather than tiling to infinity */
        radial-gradient(
            120% 90% at 50% 0%,
            transparent 45%,
            rgba(0, 0, 0, 0.35) 100%
        );
}

/* REMOVED: the "content must sit above the sheen" stacking rule.

   It existed only to lift content over body.gtm-perf-page::before, and that
   sheen is switched off further down this file, so it had no job left. It was
   also doing harm: it forced .mast from its own z-index 10 (mast.css) down to
   1, which is the SAME level it gave .site-main. Equal z-index means DOM order
   decides, .site-main comes after the masthead, and the hero therefore painted
   over the logo and the phone number and swallowed their clicks.

   Verified against the home page, which never had this rule: mast z-index 10
   there, logo and phone clickable. Deleting it restores the same on these pages.

   If the sheen is ever restored, do NOT restore this alongside it -- lift only
   .site-main, and leave the masthead's own stacking alone. */

/* The projects section uses the shared .gtm-perf-plate exactly as the brands
 * strip does — no local override. It was briefly turned up here because the
 * plate is mostly hidden behind the hero and cards and read as unchanged, but
 * matching the other plate sections matters more than making this one shout:
 * three dark bands on one page should be the same material, not three strengths
 * of it. The surface is declared once, in .gtm-perf-plate.
 */

/* --- Latest projects -------------------------------------------------------
 *
 * The build diary, as an index with a preview rather than a hero panel plus a
 * carousel. The old shape spent 1076px on desktop and 1.22 screens on a phone
 * to show eight projects; this holds ~605px for seven and reaches all 44.
 *
 * Reading order is machine-first. Someone scanning this is asking "has this
 * workshop done my bike", which is a question about the machine, not about a
 * project title — so the machine is the primary line and the title is support.
 * Where a title only restates the machine it is suppressed rather than printed
 * twice (gtm_project_title_is_stutter()).
 *
 * Tokens are scoped to the section and prefixed --pj-. They sat on :root in the
 * mockup, which is fine for a standalone page but would leak generic names like
 * --fs-primary across the whole theme from here.
 */
.gtm-perf-projects {
    /* Declare the ink WITH the surface. The plate is near-black and every
       rgba(255,255,255,…) below assumes white text, but the theme's body colour
       is #333 — so the machine name, which is the primary line and takes its
       colour by inheritance, rendered at 1.58:1 against the plate and was to all
       intents invisible. The standalone mockup never showed this because its own
       body was white-on-black; the assumption only became visible once the
       markup sat inside the real page. */
    color: #fff;

    --pj-accent: #FFC424;
    --pj-rule: rgba(255, 255, 255, .09);

    /* Spacing — the 8px grid. Every gap below is one of these, so no number in
       this block was picked to make one screenshot look right. */
    --pj-s1: 4px;
    --pj-s2: 8px;
    --pj-s3: 12px;
    --pj-s4: 16px;
    --pj-s6: 24px;

    /* FOUR type roles, not six sizes. 12px and 11px previously did different
       jobs while being visually identical, which is a distinction the reader
       cannot see and therefore is not a distinction. The phone re-declares
       these VALUES below, so the roles never multiply — only their sizes step. */
    --pj-fs-primary: 17px;
    --pj-fs-secondary: 13px;
    --pj-fs-meta: 11px;
    --pj-lh-primary: 22px;
    --pj-lh-secondary: 16px;
    --pj-lh-meta: 14px;

    /* Row: 22 + 2 + 16 = 40 of content, 8 top, 8 bottom = 56. On the grid. */
    --pj-row-h: 56px;
    --pj-col-num: 28px;
    --pj-col-status: 148px;
    --pj-preview-w: 416px;
}

/* ONE spacing owns the heading-to-list gap.
   It was two: this block's own margin-bottom (23px) plus .gtm-section-inner's
   32px flex gap — and a flex gap does not collapse with a margin, so they added
   to 55px and the title floated clear of the list it names. Exactly the stack
   that pushed the OUR BRANDS label off its own strip. Zero the margin, let the
   gap own the rhythm, and the number is now in one place. */
.gtm-perf-projects .gtm-section-inner {
    /* Floor is 22px, not 18: .gtm-heading-lg is a fixed 32px at every width, so
       2vw bottoms out on a tablet and left a 32px heading sitting 16px off its
       list - tighter than the desktop it is meant to match. The heading does not
       shrink, so neither should its gap. */
    gap: clamp(22px, 2vw, 28px);
}

.gtm-perf-projects__head {
    display: flex;
    align-items: baseline;
    gap: var(--pj-s4);
    flex-wrap: wrap;
    margin-bottom: 0;
}

.gtm-perf-projects__head .gtm-heading-lg {
    /* 5px, not 4: measured against the page, this eyebrow-to-heading gap landed
       at 7px while WELCOME and WHAT WE DO BEST both sit at 8px. */
    margin: 5px 0 0;

    /* .gtm-heading-lg is 32px on a 40px line. That 8px of leading is invisible
       space on a line that is all caps and therefore has no descenders — the
       same dead space that pushed the OUR BRANDS label off its own strip.
       Scoped here rather than changed on the shared class. */
    line-height: 1.05;
}

/* The count is the section's one piece of scale — 44 projects is the claim the
   seven rows are evidence for. Pushed to the far end of the heading line so it
   reads as a caption to the section, not as a third heading. */
.gtm-pj-legend {
    margin-left: auto;
    color: rgba(255, 255, 255, .52);
    font-size: var(--pj-fs-meta);
    line-height: var(--pj-lh-meta);
    font-weight: 800;
    letter-spacing: .16em;
    text-transform: uppercase;
}

/* White, not accent. Gold was doing four jobs at once here — these counts, the
   selected row, the in-workshop state and the CTA — so it had stopped meaning
   anything in particular. It now means only: live, selected, actionable. */
.gtm-pj-legend b {
    color: #fff;
}

/* --- The index -------------------------------------------------------------
   List left, preview right, a fixed 48px channel between them. The middle
   column is empty rather than a gap so the preview can be a real 416px and the
   list takes whatever remains. */
.gtm-pj-index {
    display: grid;
    grid-template-columns: 1fr 48px var(--pj-preview-w);
}

/* min-width:0 because `1fr` is `minmax(auto, 1fr)`: a track can never shrink
   below its content's min-content width, and this column is full of nowrap
   machine names. Without it the list inflates and pushes the row past the
   viewport. This trap has caused three separate layout breaks in this build —
   prefer minmax(0, 1fr) by default anywhere in this theme. */
.gtm-pj-list {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.gtm-pj-rows {
    list-style: none;
    margin: 0;
    /* The left pad is the gutter the active-row bar lives in. */
    padding: 0 0 0 var(--pj-s3);
    border-top: 1px solid var(--pj-rule);
}

.gtm-pj-rows li {
    border-bottom: 1px solid var(--pj-rule);
}

/* --- A row -----------------------------------------------------------------
   Three columns on one baseline: index, machine (+ its supporting line), status.
   align-items:baseline matters — with the number centred in the row and the
   machine sitting at the top, the row read as three floating fragments rather
   than one line. The status track is FIXED and left-aligned; as `auto` it began
   at x622 on one row and x683 on the next, and that ragged edge down the list
   is what reads as "not aligned". */
.gtm-pj-row {
    position: relative;
    display: grid;
    grid-template-columns: var(--pj-col-num) 1fr var(--pj-col-status);
    align-items: baseline;
    gap: 0 var(--pj-s4);
    height: var(--pj-row-h);
    padding: var(--pj-s2) 0;
    text-decoration: none;
    color: inherit;
}

/* Every grid child, for the ellipsis to engage — see .gtm-pj-list above. */
.gtm-pj-row > * {
    min-width: 0;
}

/* The index encodes recency — 01 is the newest thing through the workshop — so
   it is content, not decoration, and has to be readable. At .38 alpha it
   measured 3.48:1 on the plate, under the 4.5 AA needs at this size; .48 lands
   at 4.7. It stays far quieter than the machine name regardless: 11px grey
   against 17px/800 white is a difference of hierarchy, not of contrast. */
.gtm-pj-row__n {
    font-size: var(--pj-fs-meta);
    line-height: var(--pj-lh-primary);
    font-weight: 700;
    letter-spacing: .12em;
    color: rgba(255, 255, 255, .48);
    font-variant-numeric: tabular-nums;
}

.gtm-pj-row__machine {
    display: block;
    font-size: var(--pj-fs-primary);
    font-weight: 800;
    line-height: var(--pj-lh-primary);
    letter-spacing: -.005em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* A fixed 4ch slot so machine names start at the same x whether or not the year
   is known. gtm_perf_projects() drops the slot entirely when NOTHING in the set
   has a year — holding space for data that does not exist is not alignment. */
.gtm-pj-row__year {
    font-variant-numeric: tabular-nums;
    font-weight: 500;
    color: rgba(255, 255, 255, .55);
    display: inline-block;
    min-width: 4ch;
}

/* Machine and title are ONE group: 2px apart, against 16px to anything else. */
.gtm-pj-row__sub {
    display: flex;
    align-items: baseline;
    gap: var(--pj-s2);
    margin-top: 2px;
    min-width: 0;
    font-size: var(--pj-fs-secondary);
    line-height: var(--pj-lh-secondary);
    color: rgba(255, 255, 255, .56);
    white-space: nowrap;
    overflow: hidden;
}

.gtm-pj-row__sub b {
    flex: 0 1 auto;
    overflow: hidden;
    text-overflow: ellipsis;
    font-weight: 600;
    color: rgba(255, 255, 255, .78);
}

.gtm-pj-row__tick {
    flex: 0 0 auto;
    width: 1px;
    height: 9px;
    background: rgba(255, 255, 255, .20);
    transform: translateY(1px);
}

.gtm-pj-row__work {
    flex: 1 1 auto;
    overflow: hidden;
    text-overflow: ellipsis;
}

.gtm-pj-row__status {
    font-size: var(--pj-fs-meta);
    line-height: var(--pj-lh-primary);
    font-weight: 800;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, .46);
    white-space: nowrap;
}

/* In-workshop is the notable state; completed is the default and stays quiet. */
.gtm-pj-row__status.is-wip {
    color: var(--pj-accent);
}

.gtm-pj-row__dot {
    display: inline-block;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: currentColor;
    margin-right: 7px;
    vertical-align: middle;
}

/* The active-row marker sits in the list's left gutter, NOT over the index
   number — drawn at left:0 it landed on top of the "01". */
.gtm-pj-row::before {
    content: "";
    position: absolute;
    left: calc(var(--pj-s3) * -1);
    top: var(--pj-s2);
    bottom: var(--pj-s2);
    width: 3px;
    background: var(--pj-accent);
    opacity: 0;
    transition: opacity .12s;
}

.gtm-pj-row:hover::before,
.gtm-pj-row:focus-visible::before,
.gtm-pj-row.is-current::before {
    opacity: 1;
}

.gtm-pj-row:hover,
.gtm-pj-row.is-current {
    background: rgba(255, 255, 255, .035);
}

.gtm-pj-row.is-current .gtm-pj-row__n {
    color: var(--pj-accent);
}

.gtm-pj-row:focus-visible {
    outline: 2px solid var(--pj-accent);
    outline-offset: -2px;
}

/* --- The preview -----------------------------------------------------------
   A flex column with the caption pushed to the bottom, so the two columns end
   together at any row count rather than by hand-tuning the image height. */
.gtm-pj-preview {
    position: relative;
    display: flex;
    flex-direction: column;
}

/* The photograph takes the height the list gives it rather than a fixed 278px.
   That is what lets the caption sit directly beneath it AND still have the two
   columns finish level — previously the column ended together only because the
   caption was shoved to the bottom with margin-top:auto, which left it 86px
   adrift of the photograph it describes while its own two lines sat 2px apart.
   Proximity is the whole signal that they belong to each other. */
.gtm-pj-preview__stack {
    position: relative;
    flex: 1 1 auto;
    width: var(--pj-preview-w);
    min-height: 278px;
    border: 1px solid rgba(255, 255, 255, .10);
    /* 16px is the theme's media radius — every card in cards.css uses it. The
       panel was the one square-cornered image on the page. */
    border-radius: 16px;
    overflow: hidden;
}

.gtm-pj-preview__stack img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    opacity: 0;
    transition: opacity .16s;
}

.gtm-pj-preview__stack img.is-current {
    opacity: 1;
}

.gtm-pj-preview__cap {
    padding-top: var(--pj-s3);
}

.gtm-pj-preview__machine {
    font-size: var(--pj-fs-primary);
    font-weight: 700;
    line-height: var(--pj-lh-primary);
    letter-spacing: -.005em;
}

.gtm-pj-preview__work {
    margin-top: 2px;
    font-size: var(--pj-fs-secondary);
    line-height: var(--pj-lh-secondary);
    color: rgba(255, 255, 255, .62);
}

.gtm-pj-preview__cta {
    margin-top: var(--pj-s2);
    font-size: var(--pj-fs-secondary);
    line-height: var(--pj-lh-secondary);
    font-weight: 700;
    color: var(--pj-accent);
}

/* The archive exit belongs to the LIST — it extends the list, not the
   photograph being previewed. It lived in the preview column, which is
   display:none on a phone, so the phone showed 7 of 44 and offered no route to
   the rest. Muted with a rule above it so it reads as a separate group and does
   not compete with the preview's own CTA. padding, not margin, so the rule
   spans the column like the row rules do while the text indents to the same x
   as the index numbers. */
.gtm-pj-all {
    display: block;
    /* No border-top. The list's last row already draws a rule, so this added a
       SECOND one 16px below it - two lines doing one job, which reads as a
       mistake rather than as a boundary. Space and the muted ink separate this
       from the list on their own; the rule was belt and braces. */
    /* 11/14px type with no vertical padding gave this a 14px tap target, the
       smallest on the page and the only way out of the section. The padding
       takes it to 44px and the margin is reduced by the same 15px, so the text
       sits exactly where it did and only the hit area grows. */
    margin-top: calc(var(--pj-s6) - 15px);
    padding: 15px 0 15px var(--pj-s3);
    font-size: var(--pj-fs-meta);
    line-height: var(--pj-lh-meta);
    font-weight: 800;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, .62);
    text-decoration: none;
}

.gtm-pj-all:hover,
.gtm-pj-all:focus-visible {
    color: var(--pj-accent);
}

/* --- Tablet and below: the panel goes, the photograph joins the row --------
 * The preview is a hover affordance, and a tablet has no hover — so below the
 * desktop breakpoint it sat there showing project 01 forever while starving the
 * list of width. At 768px its fixed 416px left the list 225px and ellipsised
 * ALL SEVEN machine names, the one line this section leads with.
 *
 * So the panel goes and the photograph moves INTO the row as a thumbnail: the
 * same content, doing a job it can actually do on touch, and the list gets the
 * full width back.
 */
@media (max-width: 1199.98px) {
    .gtm-perf-projects {
        --pj-row-h: 76px;   /* comfortably past the 44px touch-target minimum */
    }

    .gtm-pj-index {
        grid-template-columns: 1fr;
    }

    /* Removed from the layout, not hidden — otherwise its seven full-size
       photographs are downloaded for something nobody can ever see. */
    .gtm-pj-preview {
        display: none;
    }

    /* Two lines, not one:
           [photo]  MACHINE NAME . . . . . . . . . . . . . . . (full width)
                    project title · the work            Completed
       which is the truer grouping anyway: the machine is primary and owns its
       own line; title, work and status are all supporting and sit together
       beneath it. Sharing one line ellipsised the machine name, and a machine
       name is the thing a reader is scanning for. */
    .gtm-pj-row {
        grid-template-columns: 96px minmax(0, 1fr) auto;
        grid-template-rows: auto auto;
        column-gap: var(--pj-s4);
        row-gap: 0;
        align-content: center;
    }

    /* display:contents dissolves the text wrapper so the machine and the
       sub-line become grid items in their own right — inside one box they
       cannot be placed on separate rows. */
    .gtm-pj-row__txt {
        display: contents;
    }

    .gtm-pj-row__n {
        display: none;
    }

    .gtm-pj-row__thumb {
        display: block;
        grid-column: 1;
        grid-row: 1 / 3;
        align-self: center;
        width: 96px;
        height: 56px;
        background: #1b1a22 center / cover no-repeat;
        /* Half the panel's 16px. Radius that scales with the element reads as
           one family; 16px on a 96px thumb would read as a different one. */
        border-radius: 8px;
    }

    .gtm-pj-row__machine {
        grid-column: 2 / 4;
        grid-row: 1;
    }

    .gtm-pj-row__sub {
        grid-column: 2;
        grid-row: 2;
        align-self: baseline;
    }

    .gtm-pj-row__status {
        grid-column: 3;
        grid-row: 2;
        justify-self: end;
        align-self: baseline;
    }
}

/* --- Phone -----------------------------------------------------------------
   The phone does not get its OWN type scale — it gets the SAME four roles at
   stepped-down values. Re-declaring the tokens is what keeps one system across
   every breakpoint; everything else here is layout. */
@media (max-width: 767.98px) {
    .gtm-perf-projects {
        --pj-fs-primary: 16px;
        --pj-lh-primary: 21px;
    }

    /* The legend drops to its own line here, and the head's 16px gap then acts
       as a ROW gap too - so the count sat as far from the heading as the list
       did. It is a caption to the heading; 8px says so. */
    .gtm-perf-projects__head {
        gap: var(--pj-s2) var(--pj-s4);
    }

    .gtm-pj-legend {
        margin-left: 0;
        flex-basis: 100%;
    }

    .gtm-pj-row {
        grid-template-columns: 76px minmax(0, 1fr) auto;
        column-gap: var(--pj-s3);
    }

    .gtm-pj-row__thumb {
        width: 76px;
        height: 56px;
        border-radius: 6px;
    }

    /* Seven 76px rows put the phone at 761px, past the ~500-600px this section
       was briefed to fit. Five land it at ~600 and still clear the three-makes
       bar, because the max-3-per-make cap guarantees breadth in the first five.
       A tablet has the height for all seven, so this is phone-only. */
    .gtm-pj-rows li:nth-child(n+6) {
        display: none;
    }

    /* The work line is the first thing to go when space is short — the title
       carries more. There is room for both from tablet up. */
    .gtm-pj-row__tick,
    .gtm-pj-row__work {
        display: none;
    }

    .gtm-pj-row__status {
        letter-spacing: .10em;   /* tighter tracking returns ~14px to the name */
    }
}

/* The thumbnail is the touch-breakpoint stand-in for the index number. */
@media (min-width: 1200px) {
    .gtm-pj-row__thumb {
        display: none;
    }
}

@media (prefers-reduced-motion: reduce) {
    .gtm-pj-row::before,
    .gtm-pj-preview__stack img {
        transition: none;
    }
}


/* --- A project's own page --------------------------------------------------
   The single template is the news article layout, whose hero title is
   transparent with a white stroke because it is designed to sit over the post's
   photograph. A project without one got that outlined title on a flat #222
   block, which is why the page read as belonging to a different site. Give the
   hero the same machined surface the section uses, so the treatment is
   deliberate whether or not there is a photograph behind it. */
body.single-gtm_project .hero-secondary,
body.post-type-archive-gtm_project .hero-secondary {
    background-color: #08080b;
    background-image:
        repeating-linear-gradient(62deg, rgba(255, 255, 255, .05) 0 1px, rgba(0, 0, 0, .34) 1px 2px, transparent 2px 8px),
        repeating-linear-gradient(-62deg, rgba(255, 255, 255, .036) 0 1px, rgba(0, 0, 0, .28) 1px 2px, transparent 2px 8px),
        linear-gradient(100deg, rgba(255, 255, 255, .05) 0%, rgba(255, 255, 255, 0) 46%),
        radial-gradient(130% 180% at 50% 45%, rgba(255, 255, 255, .07) 0%, rgba(255, 255, 255, 0) 62%);
    box-shadow: inset 0 -1px 0 rgba(255, 255, 255, .06);
}

/* A scrim over the hero photograph.
 *
 * The title is transparent with a white stroke, so its legibility depends
 * entirely on what the photograph happens to be doing behind it. Measuring all
 * 44 project images composited at the real hero geometry, 7 failed the 3:1
 * large-text threshold and 18 more only scraped it — worst was the Superstock
 * tuning dyno plot at 1.04, which is white paper with a thin trace on it.
 *
 * Rather than curate photographs to suit a text treatment, put a floor under it.
 * Weighted to the bottom where the title sits, so the top of the image, which is
 * usually the part worth seeing, keeps most of its light.
 *
 * The bg element carries its url in an inline style, so this has to be a
 * pseudo-element; a background-image rule would simply lose to the attribute. */
body.single-gtm_project .hero-secondary__bg,
body.post-type-archive-gtm_project .hero-secondary__bg {
    position: absolute;
    inset: 0;
}

body.single-gtm_project .hero-secondary__bg::after,
body.post-type-archive-gtm_project .hero-secondary__bg::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(to top,
        rgba(0, 0, 0, .84) 0%,
        rgba(0, 0, 0, .66) 40%,
        rgba(0, 0, 0, .38) 75%,
        rgba(0, 0, 0, .26) 100%);
}


/* ===========================================================================
   PERFORMANCE ENGINEERING SUB-PAGES  (Tuning / Repairs / Servicing)
   Shared by the three sub-pages, rendered from inc/perf-pages.php via
   template-parts/perf-sub-page.php.
   =========================================================================== */

/* --- The services table = the site's tech-specs component, in metal ---------
 *
 * The design board for these tables IS the PDP tech-specs block: the same mast
 * with a logo over an uppercase title, the same pipe-separated tab strip
 * (.gtm-spec-link:not(:last-child)::after draws that "|"), the same panel and
 * label-over-value rows. So the markup here is that component's markup, class
 * for class, and what follows changes only the MATERIAL: the mast is the
 * machined silver rather than a brand colour, and the panel sits on carbon
 * rather than white.
 *
 * The rules are re-declared here rather than by enqueueing pages/pdp.css,
 * which carries the whole product-page layout — buy box, gallery, sticky bar —
 * and would drop all of it onto three brochure pages to borrow one block. The
 * markup contract is what makes this the same component; the stylesheet it
 * happens to be declared in is not.
 */
.gtm-perf-specs .gtm-specs-wrap {
    width: 100%;
}

/* The mast: same geometry as the PDP (rounded top, centred, logo over title),
   in the chrome ramp the hero band and the CTA already use. */
.gtm-perf-specs .gtm-specs-mast {
    background: linear-gradient(180deg,
        var(--gtm-chrome-1) 0%,
        var(--gtm-chrome-2) 40%,
        var(--gtm-chrome-4) 50%,
        var(--gtm-chrome-2) 60%,
        var(--gtm-chrome-1) 100%);
    border: 1px solid rgba(255, 255, 255, .35);
    border-bottom: 0;
    border-radius: 16px 16px 0 0;
    padding: clamp(20px, 3vw, 34px) 24px;
    text-align: center;
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, .9),
        inset 0 -1px 0 rgba(0, 0, 0, .28),
        0 10px 26px -14px rgba(0, 0, 0, .75);
}

.gtm-perf-specs .gtm-specs-brand-logo {
    height: 30px;
    width: auto;
    margin: 0 auto 12px;
    display: block;
    /* Black artwork on transparency, which is what silver wants. */
    opacity: .92;
}

/* body.gtm-perf-page h2 forces white on this page — right on carbon, wrong on
   metal. This title is charcoal on silver and needs to out-specify that. */
body.gtm-perf-page .gtm-perf-specs .gtm-specs-title {
    margin: 0;
    font-size: clamp(22px, 3.4vw, 44px);
    font-weight: 900;
    line-height: 1.05;
    text-transform: uppercase;
    color: var(--gtm-chrome-ink);
    text-shadow: 0 1px 0 rgba(255, 255, 255, .5);
}

/* The tab strip. On a PDP this sits on white; here it sits on the plate, so the
   ink inverts and the divider follows. Everything else — the pipe, the active
   underline colour, the pill padding — is the PDP's. */
.gtm-perf-specs .gtm-spec-nav {
    display: flex;
    justify-content: center;
    flex-wrap: wrap;
    gap: 12px;
    padding: 20px 16px;
    margin: 0;
    background: rgba(255, 255, 255, .03);
    border-left: 1px solid rgba(255, 255, 255, .12);
    border-right: 1px solid rgba(255, 255, 255, .12);
    border-bottom: 1px solid rgba(255, 255, 255, .12);
}

/* 24px line-height + 8px padding came to 40px, just under the 44px touch
   minimum the project rows are sized against. 10px takes it to 44 without
   moving anything else; the separator below follows the same value. */
.gtm-perf-specs .gtm-spec-link {
    position: relative;
    display: inline-block;
    padding: 10px 16px;
    margin: 0;
    background: none;
    border: none;
    cursor: pointer;
    font-weight: 700;
    font-size: 15px;
    line-height: 24px;
    text-transform: uppercase;
    text-decoration: none;
    color: rgba(255, 255, 255, .58);
    transition: color .2s ease;
}

.gtm-perf-specs .gtm-spec-link:not(:last-child)::after {
    content: "|";
    position: absolute;
    right: -8px;
    top: 10px;   /* tracks the link's vertical padding */
    font-weight: 400;
    color: rgba(255, 255, 255, .22);
    pointer-events: none;
}

.gtm-perf-specs .gtm-spec-link:hover {
    color: #fff;
}

.gtm-perf-specs .gtm-spec-link.active {
    color: #FFC424;
}

.gtm-perf-specs .gtm-spec-link:focus-visible {
    outline: 2px solid #FFC424;
    outline-offset: 2px;
    border-radius: 4px;
}

/* Panels: hidden until active, exactly as on a PDP. With one panel the strip
   never renders and the single panel is always active, so a page with nothing
   to switch between still shows its table. */
.gtm-perf-specs .gtm-spec-panel {
    display: none;
}

.gtm-perf-specs .gtm-spec-panel.active {
    display: block;
}

.gtm-perf-specs .gtm-specs-content {
    padding: clamp(20px, 2.6vw, 34px) clamp(16px, 2.6vw, 34px);
    border: 1px solid rgba(255, 255, 255, .12);
    border-top: 0;
    border-radius: 0 0 16px 16px;
    background: rgba(255, 255, 255, .022);
}

/* Rows. The PDP runs three across because a spec is two or three words; these
   values are a sentence, so two columns at desktop and one on a phone. The
   label-over-value pairing is the PDP's and is what makes a row read as one
   thing rather than two. */
.gtm-perf-specs .gtm-spec-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 26px 40px;
}

.gtm-perf-specs .gtm-spec-row {
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 0 0 18px;
    border-bottom: 1px solid rgba(255, 255, 255, .09);
    min-width: 0;
}

.gtm-perf-specs .gtm-spec-label {
    font-weight: 700;
    font-size: 12px;
    line-height: 1.25;
    letter-spacing: .06em;
    text-transform: uppercase;
    /* The PDP's #999 is a grey chosen against white. On carbon the same value
       measures under 4.5:1, so this is the plate's equivalent, not its hex. */
    color: rgba(255, 255, 255, .62);
}

.gtm-perf-specs .gtm-spec-val {
    font-size: 15px;
    font-weight: 400;
    line-height: 1.5;
    color: rgba(255, 255, 255, .88);
}

/* The quote CTA closes the table. A rule above it separates it from the last
   row without another row's worth of space, and centring it stops it reading as
   a sixth service. It is the SILVER button, not the dark pill the page-level
   band uses, so the two calls to action stay ranked rather than competing. */
.gtm-perf-specs__quote {
    margin-top: clamp(20px, 2.6vw, 32px);
    padding-top: clamp(20px, 2.6vw, 32px);
    border-top: 1px solid rgba(255, 255, 255, .12);
    text-align: center;
}

@media (max-width: 767.98px) {
    .gtm-perf-specs .gtm-spec-grid {
        grid-template-columns: 1fr;
        gap: 18px;
    }

    .gtm-perf-specs .gtm-spec-link {
        font-size: 13px;
        padding: 10px 12px;
    }
}

/* --- Specialist models block ---------------------------------------------- */
.gtm-perf-models__copy {
    color: #fff;
}

.gtm-perf-models__copy .gtm-heading-lg {
    margin: 5px 0 14px;
    line-height: 1.08;
}

.gtm-perf-models__copy p {
    font-size: 14px;
    line-height: 1.6;
    color: rgba(255, 255, 255, .72);
}

.gtm-perf-models__label {
    margin: 16px 0 6px;
    font-size: 13px;
    font-weight: 800;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: #fff;
}

.gtm-perf-models__list {
    margin: 0 0 4px;
    padding-left: 20px;
    list-style: disc;
}

.gtm-perf-models__list li {
    font-size: 14px;
    line-height: 1.6;
    color: rgba(255, 255, 255, .72);
}

.gtm-perf-models__copy .gtm-btn {
    margin-top: 18px;
}

/* --- The silver call-to-action band ---------------------------------------
   A full-bleed brushed panel, brightest at the centre where the copy sits. The
   ink is the same charcoal the hero nameplate uses, so every piece of type that
   sits ON silver anywhere on these pages is one colour. */
.gtm-perf-cta {
    background:
        linear-gradient(100deg,
            var(--gtm-chrome-4) 0%,
            var(--gtm-chrome-3) 18%,
            var(--gtm-chrome-1) 48%,
            var(--gtm-chrome-2) 62%,
            var(--gtm-chrome-3) 84%,
            var(--gtm-chrome-4) 100%);
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, .75),
        inset 0 -1px 0 rgba(0, 0, 0, .35);
    text-align: center;
}

.gtm-perf-cta .gtm-section-inner {
    align-items: center;
    gap: 10px;
}

body.gtm-perf-page .gtm-perf-cta__heading {
    margin: 0;
    font-size: clamp(20px, 2.6vw, 30px);
    font-weight: 900;
    line-height: 1.15;
    letter-spacing: .01em;
    text-transform: uppercase;
    color: var(--gtm-chrome-ink);
    text-shadow: 0 1px 0 rgba(255, 255, 255, .45);
}

body.gtm-perf-page .gtm-perf-cta .gtm-perf-cta__text {
    margin: 0;
    max-width: 62ch;
    font-size: 14px;
    line-height: 1.55;
    color: #35303b;
}

/* Dark pill on silver. Deliberately NOT a site-wide .gtm-btn-dark: this pairing
   exists only on this band, and a global variant that one section uses is a
   token nobody else can reason about. */
.gtm-perf-cta__btn {
    margin-top: 6px;
    background: #2b2630;
    color: #fff;
    border: 1px solid rgba(0, 0, 0, .35);
    box-shadow: 0 6px 16px -8px rgba(0, 0, 0, .7);
}

.gtm-perf-cta__btn:hover,
.gtm-perf-cta__btn:focus-visible {
    background: #171319;
    color: #fff;
}

/* --- The restoration gallery ----------------------------------------------
   A grid of photographs with a lightbox. The tiles are uniform 4:3 frames with
   the image cropped to fill: photographs from a workshop arrive in whatever
   shape a phone happened to be held, and a ragged grid makes the work look
   less considered than it is. The crop is the tile's job, not the client's. */
.gtm-perf-gallery {
    background-color: #08080b;
}

.gtm-perf-gallery__head {
    display: flex;
    align-items: baseline;
    gap: 16px;
    flex-wrap: wrap;
    width: 100%;
}

/* The count is the section's evidence: "24 photographs" is a claim about how
   much work has been through the shop, and it costs one line to make it. */
.gtm-perf-gallery__count {
    margin-left: auto;
    font-size: 11px;
    line-height: 14px;
    font-weight: 800;
    letter-spacing: .16em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, .52);
}

.gtm-perf-gallery__grid {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: clamp(8px, 1vw, 14px);
    list-style: none;
    margin: 0;
    padding: 0;
    width: 100%;
}

.gtm-perf-gallery__cell {
    min-width: 0;
}

.gtm-perf-gallery__item {
    position: relative;
    display: block;
    overflow: hidden;
    border-radius: 10px;
    border: 1px solid rgba(255, 255, 255, .10);
    background: #14131a;
    /* The frame owns the ratio so nothing shifts as images arrive. */
    aspect-ratio: 4 / 3;
}

.gtm-perf-gallery__item img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    /* Scale on hover happens INSIDE the frame, which is what reads as sleek:
       the layout never moves, only the picture behind the window. */
    transition: transform .45s cubic-bezier(.2, .7, .3, 1), opacity .25s;
}

.gtm-perf-gallery__item::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(to top, rgba(0, 0, 0, .55) 0%, rgba(0, 0, 0, 0) 45%);
    opacity: 0;
    transition: opacity .25s;
    pointer-events: none;
}

.gtm-perf-gallery__item:hover img,
.gtm-perf-gallery__item:focus-visible img {
    transform: scale(1.06);
}

.gtm-perf-gallery__item:hover::after,
.gtm-perf-gallery__item:focus-visible::after {
    opacity: 1;
}

.gtm-perf-gallery__item:hover,
.gtm-perf-gallery__item:focus-visible {
    border-color: rgba(255, 196, 36, .55);
}

.gtm-perf-gallery__item:focus-visible {
    outline: 2px solid #FFC424;
    outline-offset: 3px;
}

/* The magnifier says the tile opens rather than navigates. It only appears on
   hover, so the resting grid stays a wall of photographs. */
.gtm-perf-gallery__zoom {
    position: absolute;
    right: 10px;
    bottom: 10px;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    border-radius: 50%;
    background: rgba(0, 0, 0, .55);
    border: 1px solid rgba(255, 255, 255, .28);
    color: #fff;
    opacity: 0;
    transform: translateY(6px);
    transition: opacity .22s, transform .22s;
    pointer-events: none;
}

.gtm-perf-gallery__item:hover .gtm-perf-gallery__zoom,
.gtm-perf-gallery__item:focus-visible .gtm-perf-gallery__zoom {
    opacity: 1;
    transform: translateY(0);
}

.gtm-perf-gallery__btn {
    align-self: center;
    margin-top: clamp(18px, 2.2vw, 28px);
}

/* --- The gallery lightbox -------------------------------------------------- */
.gtm-pg-lightbox {
    position: fixed;
    inset: 0;
    z-index: 9000;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: clamp(8px, 2vw, 28px);
    padding: clamp(16px, 4vw, 56px);
    background: rgba(0, 0, 0, .92);
    opacity: 0;
    transition: opacity .18s;
}

.gtm-pg-lightbox.is-open {
    opacity: 1;
}

body.gtm-pg-open {
    overflow: hidden;
}

.gtm-pg-lightbox__figure {
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 12px;
    max-width: min(1180px, 100%);
    min-width: 0;
}

.gtm-pg-lightbox__figure img {
    display: block;
    margin: 0 auto;
    max-width: 100%;
    /* Leave room for the caption line, or a tall photograph pushes it off. */
    max-height: max(200px, calc(100vh - 190px));
    min-height: 160px;
    object-fit: contain;
    background: #14131a;
    border: 1px solid rgba(255, 255, 255, .14);
    border-radius: 10px;
}

.gtm-pg-lightbox__cap {
    display: flex;
    align-items: baseline;
    justify-content: center;
    gap: 14px;
    flex-wrap: wrap;
    font-size: 13px;
    line-height: 18px;
    color: rgba(255, 255, 255, .70);
    text-align: center;
}

.gtm-pg-lightbox__counter {
    font-variant-numeric: tabular-nums;
    font-weight: 800;
    letter-spacing: .10em;
    color: rgba(255, 255, 255, .48);
}

.gtm-pg-lightbox__close,
.gtm-pg-lightbox__nav {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    /* 48px: past the 44px touch minimum, and these are the controls someone
       taps repeatedly while flicking through a set. */
    width: 48px;
    height: 48px;
    border-radius: 50%;
    background: rgba(255, 255, 255, .08);
    border: 1px solid rgba(255, 255, 255, .18);
    color: #fff;
    cursor: pointer;
    transition: background .18s, border-color .18s;
}

.gtm-pg-lightbox__close {
    position: absolute;
    top: 16px;
    right: 16px;
    font-size: 24px;
    line-height: 1;
}

.gtm-pg-lightbox__close:hover,
.gtm-pg-lightbox__nav:hover,
.gtm-pg-lightbox__close:focus-visible,
.gtm-pg-lightbox__nav:focus-visible {
    background: rgba(255, 255, 255, .18);
    border-color: rgba(255, 196, 36, .6);
}

.gtm-pg-lightbox__close:focus-visible,
.gtm-pg-lightbox__nav:focus-visible {
    outline: 2px solid #FFC424;
    outline-offset: 2px;
}

@media (max-width: 1023.98px) {
    .gtm-perf-gallery__grid {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }
}

@media (max-width: 599.98px) {
    .gtm-perf-gallery__grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    .gtm-perf-gallery__count {
        margin-left: 0;
        flex-basis: 100%;
    }

    /* On a phone the nav buttons sit under the photograph rather than beside it:
       flanking a full-width image, they would either overlap it or squeeze it. */
    .gtm-pg-lightbox {
        flex-wrap: wrap;
        align-content: center;
    }

    .gtm-pg-lightbox__figure {
        flex-basis: 100%;
        order: 1;
    }

    .gtm-pg-lightbox__nav--prev { order: 2; }
    .gtm-pg-lightbox__nav--next { order: 3; }
}

@media (prefers-reduced-motion: reduce) {
    .gtm-perf-gallery__item img,
    .gtm-perf-gallery__item::after,
    .gtm-perf-gallery__zoom,
    .gtm-pg-lightbox {
        transition: none;
    }

    .gtm-perf-gallery__item:hover img,
    .gtm-perf-gallery__item:focus-visible img {
        transform: none;
    }
}


/* ---------------------------------------------------------------------------
   WELCOME photo — anchor to the top, not the centre.

   The card is 576x400 (1.441:1) and the photograph is 1200x900 (1.333:1), so
   background-size:cover scales it to 576x432 and the default `center` throws
   away 16px top and bottom — 33px of source each way. In this photograph the
   men's hair starts about 35px down, so `center` was cropping through their
   heads, which are the entire point of the shot.

   `top` moves that whole 67px of loss to the bottom, where it takes tarmac and
   the very base of the front wheel instead. Scoped to the Performance pages so
   every other .gtm-img-card-simple keeps its centred crop.
   --------------------------------------------------------------------------- */
body.gtm-perf-page .gtm-img-card-simple {
    background-position: center top;
}

/* ---------------------------------------------------------------------------
   ONE continuous carbon ground.

   The hero painted its own charcoal (#1C1922) and its floor painted its own
   weave from its own origin. Two problems followed, both visible at the hero's
   bottom edge:

     1. PHASE. The tile is 20px and the floor starts 592px down the page, so its
        weave sat 12px out of step with the body's. Measured, not guessed. The
        offset is viewport-dependent (the stage is clamp(320px, 49vw + 25px,
        525px)), so a hardcoded background-position correction would be right at
        one width and wrong at every other.
     2. COLOUR. #1C1922 is LIGHTER than the carbon base #0f0e14, so the hero
        block read as a slightly paler panel sitting on the page.

   Both disappear by removing the second surface rather than trying to align it:
   the hero and its floor go transparent and the BODY's carbon becomes the only
   weave on the page, so there is no boundary left to mismatch. The hero's
   photograph is unaffected — it lives on .gtm-hero-bg::before at 0.38 opacity,
   which now sits over carbon instead of charcoal. Carbon is the darker of the
   two, so headline contrast improves rather than suffers.

   background-attachment: fixed would also align the phases, and is deliberately
   NOT used — see the note on the body rule above.
   --------------------------------------------------------------------------- */
body.gtm-perf-page .gtm-hero-bg {
    background-color: transparent;
}

body.gtm-perf-page .perf-hero .gtm-hero-floor {
    background-color: transparent;
    background-image: none;
}

/* ---------------------------------------------------------------------------
   The fixed page sheen is switched OFF on these pages.

   body.gtm-perf-page::before paints a raking highlight across the viewport at up
   to rgba(255,255,255,0.055). It was written to sit above the carbon tile and
   below the content, and it was invisible in the hero for one reason only: the
   hero used to paint an opaque charcoal over it. Making the hero transparent to
   give the page one continuous weave (see the rule above) removed that cover, so
   the sheen started showing THROUGH the hero and washing the photograph out.
   Worse on the sub-pages, where the backdrop sits at 0.28 opacity rather than
   the home page's 0.38, so there is less photograph to survive it.

   Two ways out. Put an opaque ground back in the hero, which fixes the wash but
   reintroduces a boundary between hero and page. Or drop the sheen, which keeps
   the single continuous weave and costs a depth effect that is deliberately at
   the edge of perceptible anyway.

   Taking the second: the seam was the reported problem, the sheen was not, and
   "felt as depth, not seen as a gradient" stops being true the moment it is
   sitting on a photograph. Reversible in one line if the panel depth is wanted
   back, in which case the hero needs an opaque ground again and the boundary
   needs solving another way.
   --------------------------------------------------------------------------- */
body.gtm-perf-page::before {
    display: none;
}

/* ---------------------------------------------------------------------------
   Tone down the hero top scrim on these pages.

   .gtm-hero-topshade lays rgba(0,0,0,0.5) across the top ~364px, fading out, so
   white nav ink stays readable over a bright hero. These heroes are not bright:
   they are carbon plus a photograph at 0.28-0.38 opacity, already dark. At 0.5
   the scrim stops protecting anything and simply flattens the masthead to grey,
   which is what was reported.

   --hero-shade is the knob the component already exposes, so this turns it down
   rather than overriding the gradient and forking the component.
   --------------------------------------------------------------------------- */
body.gtm-perf-page .gtm-hero-bg--dark {
    --hero-shade: 0.18;
}

/* ==========================================================================
   THE HUB CARD — the one card in the explore rail that goes UP a level
   --------------------------------------------------------------------------
   Same card as its three neighbours in every dimension the grid depends on.
   That is deliberate: the geometry in this rail is load-bearing (see the
   aspect-ratio note above, where a floor and a ratio together inflated the
   track and collapsed the row to one column), so the hub is RE-SKINNED and
   never re-sized. An outline rather than a border for the same reason — a
   border would add 2px to the box on every side and shift the whole rail.

   The eyebrow ink is #342E37, not white. White on #FFC424 is 1.59:1, which is
   the exact defect just fixed on the parts drawer title; #342E37 on it is
   8.3:1. Accent backgrounds in this theme take dark ink, always.
   ========================================================================== */
body.gtm-perf-page .gtm-perf-explore a.gtm-card-hub {
    outline: 2px solid #FFC424;
    outline-offset: -2px;
}

body.gtm-perf-page .gtm-perf-explore a.gtm-card-hub:hover,
body.gtm-perf-page .gtm-perf-explore a.gtm-card-hub:focus-visible {
    outline-width: 3px;
}

body.gtm-perf-page .gtm-perf-explore a.gtm-card-hub .gtm-card-hub__eyebrow {
    align-self: flex-start;
    margin: 0 0 6px;
    padding: 3px 8px;
    border-radius: 4px;
    background: #FFC424;
    color: #342E37;
    font-size: 10px;
    font-weight: 900;
    letter-spacing: .12em;
    line-height: 1.1;
    text-transform: uppercase;
    white-space: nowrap;
}
