/* ==========================================================================
   Mundo Pur — site styles
   Sober industrial B2B palette drawn from the logo, generous whitespace, and
   Archivo: a neo-grotesque with the technical coolness the material sector asks
   for, without tipping into sci-fi.
   ========================================================================== */

/* Self-hosted so the page has no external font dependency. */

/* ExtraLight and Light, for the oversized display headings. Archivo covers the
   whole 100–900 range, so these are the same typeface rather than a second family
   mixed in — no fallback mismatch and no extra font stack to reason about. */
@font-face {
    font-family: 'Archivo';
    src: url('/fonts/archivo-latin-200-normal.woff2') format('woff2');
    font-weight: 200;
    font-display: swap;
}
@font-face {
    font-family: 'Archivo';
    src: url('/fonts/archivo-latin-300-normal.woff2') format('woff2');
    font-weight: 300;
    font-display: swap;
}
@font-face {
    font-family: 'Archivo';
    src: url('/fonts/archivo-latin-400-normal.woff2') format('woff2');
    font-weight: 400;
    font-display: swap;
}
@font-face {
    font-family: 'Archivo';
    src: url('/fonts/archivo-latin-500-normal.woff2') format('woff2');
    font-weight: 500;
    font-display: swap;
}
@font-face {
    font-family: 'Archivo';
    src: url('/fonts/archivo-latin-600-normal.woff2') format('woff2');
    font-weight: 600;
    font-display: swap;
}
@font-face {
    font-family: 'Archivo';
    src: url('/fonts/archivo-latin-700-normal.woff2') format('woff2');
    font-weight: 700;
    font-display: swap;
}
@font-face {
    font-family: 'Archivo';
    src: url('/fonts/archivo-latin-800-normal.woff2') format('woff2');
    font-weight: 800;
    font-display: swap;
}


/*
 * Body-text candidates. All three pair well with Archivo headings while staying in a
 * technical register; switch between them on the --font-body line in :root below.
 *
 *   Inter          — the workhorse UI sans. Tall x-height, very legible at small
 *                    sizes, neutral. Safest choice; almost invisible as a "font".
 *   Source Sans 3  — humanist, slightly warmer and narrower. Reads well in long
 *                    paragraphs; a touch more character than Inter.
 *   IBM Plex Sans  — engineered feel with subtle quirks. Strongest thematic fit for
 *                    an industrial materials brand, and a real contrast to Archivo.
 */
@font-face {
    font-family: 'Inter';
    src: url('/fonts/inter-latin-400-normal.woff2') format('woff2');
    font-weight: 400;
    font-display: swap;
}
@font-face {
    font-family: 'Inter';
    src: url('/fonts/inter-latin-500-normal.woff2') format('woff2');
    font-weight: 500;
    font-display: swap;
}
@font-face {
    font-family: 'Inter';
    src: url('/fonts/inter-latin-600-normal.woff2') format('woff2');
    font-weight: 600;
    font-display: swap;
}
@font-face {
    font-family: 'Source Sans 3';
    src: url('/fonts/source-sans-3-latin-400-normal.woff2') format('woff2');
    font-weight: 400;
    font-display: swap;
}
@font-face {
    font-family: 'Source Sans 3';
    src: url('/fonts/source-sans-3-latin-500-normal.woff2') format('woff2');
    font-weight: 500;
    font-display: swap;
}
@font-face {
    font-family: 'Source Sans 3';
    src: url('/fonts/source-sans-3-latin-600-normal.woff2') format('woff2');
    font-weight: 600;
    font-display: swap;
}
@font-face {
    font-family: 'IBM Plex Sans';
    src: url('/fonts/ibm-plex-sans-latin-400-normal.woff2') format('woff2');
    font-weight: 400;
    font-display: swap;
}
@font-face {
    font-family: 'IBM Plex Sans';
    src: url('/fonts/ibm-plex-sans-latin-500-normal.woff2') format('woff2');
    font-weight: 500;
    font-display: swap;
}
@font-face {
    font-family: 'IBM Plex Sans';
    src: url('/fonts/ibm-plex-sans-latin-600-normal.woff2') format('woff2');
    font-weight: 600;
    font-display: swap;
}

/* Narrow carries the technical labels: spec lists, eyebrows, counters. */
@font-face {
    font-family: 'Archivo Narrow';
    src: url('/fonts/archivo-narrow-latin-600-normal.woff2') format('woff2');
    font-weight: 600;
    font-display: swap;
}

:root {
    /* Greys and accent taken from the Mundo Pur logo: #c90c0f red,
       #474645 dark grey, #9c9b9b light grey. */
    --ink: #474645;
    --ink-soft: #6b6a69;
    --ink-faint: #9c9b9b;
    --paper: #ffffff;
    --paper-tint: #f5f5f4;
    --line: #e2e1e0;

    --accent: #c90c0f;
    --accent-dark: #a00a0c;
    --accent-tint: #fdeced;

    --measure: 78ch;
    --container: 1480px;
    --radius: 4px;

    /*
     * Body text. Swap the first family to try the alternatives -- see the @font-face
     * block above for what each one brings:
     *   'Inter'          -- neutral, safest
     *   'Source Sans 3'  -- humanist, warmer
     *   'IBM Plex Sans'  -- engineered, strongest thematic fit
     */
    --font-body: 'IBM Plex Sans', 'Archivo', 'Segoe UI', system-ui, -apple-system, sans-serif;

    /* Headings stay Archivo: the neo-grotesque is what gives the brand its technical
       register, and it now contrasts with the body face rather than matching it. */
    --font-display: 'Archivo', 'Segoe UI', system-ui, -apple-system, sans-serif;

    --font-technical: 'Archivo Narrow', 'Archivo', system-ui, sans-serif;

    --step-0: clamp(1rem, 0.97rem + 0.15vw, 1.0625rem);
    --step-1: clamp(1.2rem, 1.13rem + 0.35vw, 1.4rem);
    --step-2: clamp(1.5rem, 1.35rem + 0.7vw, 1.95rem);
    --step-3: clamp(1.9rem, 1.6rem + 1.4vw, 2.75rem);
    --step-4: clamp(2.3rem, 1.75rem + 2.6vw, 3.9rem);

    --space: clamp(3rem, 2rem + 4vw, 5.5rem);
}

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

/* In-page anchors glide instead of jumping. Honoured only when the visitor has not
   asked for reduced motion. */
@media (prefers-reduced-motion: no-preference) {
    html { scroll-behavior: smooth; }
}

/* The header is sticky, so anchor targets must stop clear of it rather than sliding
   underneath. The offset tracks the header's height, which changes when it condenses
   on scroll — hence a variable rather than a fixed value. */
:root { --header-offset: 5.5rem; }
:root.has-condensed-header { --header-offset: 4rem; }

[id] { scroll-margin-top: var(--header-offset); }

/* #main is the whole document body, and the back-to-top link targets it: offsetting
   it would leave a gap above the header instead of landing at the very top. */
#main { scroll-margin-top: 0; }

body {
    margin: 0;
    font-family: var(--font-body);
    font-size: var(--step-0);
    line-height: 1.65;
    color: var(--ink);
    background: var(--paper);
}

/*
 * Single heading typology across the site: medium weight with a touch of positive
 * tracking. Per-component overrides were removed so this rule is the one place that
 * decides how a title looks.
 */
h1, h2, h3, h4 {
    font-family: var(--font-display);
    line-height: 1.15;
    letter-spacing: 0.5px;
    margin: 0 0 0.5em;
    font-weight: 300;
}

/* Small uppercase labels read as instrument markings in the narrow cut. */
.card__meta,
.spec-list dt,
.article__date,
.hero__eyebrow,
.hero-zones__intro,
.scroller__hint,
.scroller__index {
    font-family: var(--font-technical);
}

p { margin: 0 0 1.1em; }
p:last-child { margin-bottom: 0; }

a { color: var(--accent-dark); text-decoration-thickness: 1px; text-underline-offset: 2px; }
a:hover { color: var(--accent); }

img { max-width: 100%; height: auto; display: block; }

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


/*
 * Page scroll lock while the mobile nav overlay is open. Without it the content
 * scrolls behind the panel, which reads as broken on a phone.
 */
body.has-nav-open {
    overflow: hidden;
}

/* Backdrop behind the open panel, so the overlay reads as a layer above the page and
   the tap-to-dismiss area is visible rather than implied. */
body.has-nav-open::after {
    content: "";
    position: fixed;
    inset: 0;
    background: rgba(22, 33, 31, 0.34);
    z-index: 40;
    animation: nav-backdrop-in 0.2s ease;
}

@keyframes nav-backdrop-in {
    from { opacity: 0; }
    to { opacity: 1; }
}

/* Desktop never opens the panel, so neither lock nor backdrop should ever apply. */
@media (min-width: 781px) {
    body.has-nav-open { overflow: visible; }
    body.has-nav-open::after { display: none; }
}

/* --- Layout primitives -------------------------------------------------- */

.container {
    width: 100%;
    max-width: var(--container);
    margin-inline: auto;
    padding-inline: clamp(1.15rem, 4vw, 2.5rem);
}

/* Full-bleed variant: same gutters as .container, no max-width cap. */
.container-fluid {
    width: 100%;
    margin-inline: auto;
    padding-inline: clamp(1.15rem, 4vw, 2.5rem);
}

.narrow { max-width: var(--measure); }
.container.narrow { max-width: calc(var(--measure) + 5rem); }

.section { padding-block: var(--space); }
.section--tinted { background: var(--paper-tint); }

.section__title { font-size: var(--step-3); }
.section__subtitle { font-size: var(--step-2); margin-top: 2.5rem; }
.section__subtitle:first-child { margin-top: 0; }

.section__lead {
    font-size: var(--step-1);
    color: var(--ink-soft);
    max-width: var(--measure);
    margin-bottom: 2.5rem;
}

.skip-link {
    position: absolute;
    left: -9999px;
    top: 0;
    background: var(--ink);
    color: #fff;
    padding: 0.7rem 1.2rem;
    z-index: 100;
}
.skip-link:focus { left: 0; }

/* --- Header ------------------------------------------------------------- */

/*
 * Desktop header arrangement.
 *
 * .primary-nav spans the centre and right tracks: its link list is centred in the
 * grid's middle column while the language switch sits at the far right. Keeping the
 * language list inside <nav> matters because the mobile panel expects it there.
 */



.site-header {
    position: sticky;
    top: 0;
    z-index: 50;
    background: rgba(255, 255, 255, 0.94);
    backdrop-filter: blur(8px);
    border-bottom: 1px solid var(--line);
}

/*
 * Three columns with equal outer tracks: logo left, nav centred, language right. The
 * 1fr side tracks are what actually centre the nav on the VIEWPORT -- with flex and
 * space-between the nav would only centre in the space the logo leaves over, so it
 * would drift as the logo or the language list changed width.
 */
.site-header__inner {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: 1.5rem;
    min-height: 4.5rem;
    transition: min-height 0.25s ease;
}

/* The nav is the middle track; the language list is pushed to the end of the last. */
.site-header__inner > .logo { grid-column: 1; justify-self: start; }

/*
 * Condensed state, added by site.js past a scroll threshold on desktop. The header
 * loses about a third of its height: the bar tightens and the logo shrinks with it.
 * A deeper shadow replaces the extra bulk so it still separates from the content.
 */
.site-header.is-condensed .site-header__inner { min-height: 3.1rem; }

.site-header.is-condensed { box-shadow: 0 0.15rem 0.5rem rgba(0, 0, 0, 0.06); }

.site-header.is-condensed .logo img { height: clamp(1.6rem, 1.3rem + 0.9vw, 2.1rem); }

/* The nav links carry vertical padding of their own, which would otherwise hold the
   bar open at its full height. Scoped to the desktop layout: below 781px these links
   are rows in the fixed dropdown panel, where the padding is the tap target. */
@media (min-width: 781px) {
    /*
     * Condensed: the underline moves up with the label. The gap closes to a hairline
     * rather than to nothing, so the rule still reads as an underline and not as part
     * of the glyphs, and the border thins with it.
     */
    .site-header.is-condensed .primary-nav a {
        padding-top: 0.05rem;
        padding-bottom: 0.15rem;
        border-bottom-width: 1.5px;
    }
}

.logo {
    display: block;
    flex-shrink: 0;
    line-height: 0;
}

.logo img {
    height: clamp(2.1rem, 1.6rem + 1.6vw, 2.9rem);
    width: auto;
    transition: height 0.25s ease;
}

.primary-nav { display: flex; align-items: center; gap: 2rem; }

@media (min-width: 781px) {
    /*
     * display:contents dissolves the <nav> box so its two lists become grid items of
     * .site-header__inner directly -- the link list lands in the centre track, the
     * language list in the right one. This is what makes the links centre on the
     * viewport instead of on whatever space the logo leaves over.
     */
    .primary-nav { display: contents; }

    .primary-nav > ul:first-child {
        grid-column: 2;
        justify-content: center;
    }

    .primary-nav .lang-switch {
        grid-column: 3;
        justify-self: end;
        /* The divider and inset belonged to the old side-by-side arrangement; as its
           own right-hand column it needs neither. */
        border-left: 0;
        padding-left: 0 !important;
    }

    /* Sentence case, being tested against the uppercase version. The tracking is
       reduced with it: caps need the extra spacing, lower case does not. */
    .primary-nav > ul:first-child a {
        text-transform: none;
        letter-spacing: 0.01em;
        font-size: 0.92rem;
        font-weight: 500;
    }
}

.primary-nav ul {
    display: flex;
    align-items: center;
    gap: 1.75rem;
    list-style: none;
    margin: 0;
    padding: 0;
}

.primary-nav a {
    text-decoration: none;
    color: var(--ink-soft);
    font-size: 0.94rem;
    font-weight: 500;
    padding-block: 0.4rem;
    border-bottom: 2px solid transparent;
    white-space: nowrap;
    /* Matches the header's own 0.25s, so the underline travels with the bar as it
       condenses instead of snapping to its new position. */
    transition: padding-block 0.25s ease, border-bottom-width 0.25s ease, color 0.2s ease;
}

.primary-nav a:hover { color: var(--ink); }
.primary-nav a.is-active { color: var(--ink); border-bottom-color: var(--accent); }

.lang-switch {
    /* Tight: the codes now have their own padding, so a wide gap would leave the
       active rectangle floating away from its neighbours. */
    gap: 0.15rem !important;
    padding-left: 1.5rem !important;
    border-left: 1px solid var(--line);
}

.lang-switch a {
    display: block;
    font-size: 0.8rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    color: var(--ink-faint);
    border-bottom: none;
    /* Side padding gives the active rectangle its width. Inactive codes carry the same
       padding so nothing shifts when the language changes. */
    padding: 0.28rem 0.5rem;
    /* Square, like the site's other controls. */
    border-radius: 0;
    transition: color 0.2s ease, background-color 0.2s ease;
}

.lang-switch a:hover { color: var(--ink); }

/* Current language: solid brand red with white text. */
.lang-switch a.is-active,
.lang-switch a.is-active:hover {
    background: var(--accent);
    color: #fff;
}

.nav-toggle {
    display: none;
    flex-direction: column;
    gap: 5px;
    background: none;
    border: 0;
    cursor: pointer;
    padding: 0.6rem;
}
.nav-toggle span {
    display: block;
    width: 24px;
    height: 2px;
    background: var(--ink);
    transition: transform 0.2s, opacity 0.2s;
}

/* --- Hero --------------------------------------------------------------- */

.hero {
    position: relative;
    isolation: isolate;
    background: var(--ink);
    color: #fff;
    overflow: hidden;
}

.hero__bg {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    opacity: 0.42;
    z-index: -1;
}

.hero--plain {
    background: linear-gradient(135deg, #16211f 0%, #1f7a5c 140%);
}

.hero__inner {
    padding-block: clamp(4.5rem, 3rem + 10vw, 9rem);
    max-width: 56ch;
}

.hero__title {
    font-size: var(--step-4);
    margin-bottom: 0.6em;
    text-wrap: balance;
}

.hero__subtitle {
    font-size: var(--step-1);
    color: rgba(255, 255, 255, 0.85);
    margin-bottom: 2rem;
}

/* --- Granule hero ------------------------------------------------------- */

/*
 * Light hero: near-white ground, oversized wordmark, and a granule field that
 * floats between the two halves of the name. Falls back to the static image
 * hero when the 3D scene is skipped.
 */
.hero--granules {
    /*
     * The tint fades to the page white over the last stretch of the hero, so it
     * meets the next section as a gradient rather than a hard edge. The stop sits
     * high enough that the fade is gradual instead of a visible band.
     */
    background: linear-gradient(
        180deg,
        var(--paper-tint) 0%,
        var(--paper-tint) 55%,
        var(--paper) 100%
    );
    color: var(--ink);
    /*
     * The hero's content is vertically centred, so any height beyond what the
     * scroller and detail panel need becomes dead space at the bottom. 88vh left a
     * conspicuous empty band before the next section; 74vh keeps the model generous
     * without the slack. The rem cap stops it growing on very tall windows.
     */
    min-height: min(74vh, 38rem);
    /* Sits above the following section so the soft edge is not overlapped. */
    position: relative;
    z-index: 1;
}

.hero--granules .hero__bg {
    opacity: 0.16;
    filter: grayscale(1);
    /* Feathered at the bottom so the fallback image dissolves into the page
       instead of ending on a straight line. */
    mask-image: linear-gradient(180deg, #000 0%, #000 60%, transparent 100%);
}

/* The backdrop only carries the hero when the 3D scene is skipped; once the model
   is on screen it would just add noise behind it. */
.hero--scene-ready .hero__bg { opacity: 0; }

.hero-scene {
    position: absolute;
    inset: 0;
    z-index: 1;
    pointer-events: none;
    /* An explicit height matters: the canvas is sized from this element, and an
       absolutely-positioned parent can measure as zero on first layout. */
    min-height: 100%;
}

.hero-scene__canvas {
    width: 100%;
    height: 100%;
    display: block;
    /* Feathered to match the background fade, so the model never appears to be
       sliced off at the section boundary. The stops sit lower than the background's
       because the hero is shorter now — starting the fade at 72% would have caught
       the bottom of the model itself. */
    mask-image: linear-gradient(180deg, #000 0%, #000 84%, transparent 100%);
}

/* --- Sector scroller ---------------------------------------------------- */

/*
 * The hero's only text. Sits on the right, vertically centred, while the granule
 * field forms the selected product in the open space to its left.
 */
.scroller {
    position: absolute;
    z-index: 3;
    top: 50%;
    right: clamp(1.15rem, 4vw, 3.5rem);
    transform: translateY(-50%);
    width: min(20rem, 34vw);
    display: flex;
    flex-direction: column;
    gap: 1.1rem;
}

.scroller__intro {
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.15em;
    text-transform: uppercase;
    color: var(--ink-faint);
    margin: 0;
    padding-bottom: 0.9rem;
    border-bottom: 1px solid var(--line);
}

.scroller__list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
}

.scroller__item {
    display: grid;
    grid-template-columns: 2.4rem 1fr;
    grid-template-areas:
        'number label'
        'number caption';
    align-items: baseline;
    padding: 0.7rem 0;
    text-decoration: none;
    color: var(--ink-faint);
    border-top: 1px solid transparent;
    transition: color 0.2s ease, opacity 0.2s ease;
}

.scroller__number {
    grid-area: number;
    font-family: var(--font-technical);
    font-size: 0.72rem;
    letter-spacing: 0.1em;
    color: var(--ink-faint);
    transition: color 0.2s ease;
}

.scroller__label {
    grid-area: label;
    font-size: 1.12rem;
    /* Reads as a title in the hero, so it follows the heading typology. */
    font-weight: 500;
    letter-spacing: 0.5px;
    line-height: 1.2;
    color: var(--ink-soft);
    transition: color 0.2s ease;
}

/* The caption belongs to the selected item only, so the list stays quiet. */
.scroller__caption {
    grid-area: caption;
    font-size: 0.82rem;
    line-height: 1.4;
    color: var(--ink-faint);
    max-height: 0;
    opacity: 0;
    overflow: hidden;
    transition: max-height 0.3s ease, opacity 0.3s ease, margin-top 0.3s ease;
}

.scroller__item.is-active .scroller__number { color: var(--accent); }
.scroller__item.is-active .scroller__label { color: var(--ink); }

.scroller__item.is-active .scroller__caption {
    max-height: 4rem;
    opacity: 1;
    margin-top: 0.3rem;
}

.scroller__item:hover .scroller__label,
.scroller__item:focus-visible .scroller__label {
    color: var(--accent);
}

/* --- Scroll hint -------------------------------------------------------- */

.scroller__hint {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin: 0;
    padding-top: 1rem;
    border-top: 1px solid var(--line);
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--ink-faint);
    transition: opacity 0.4s ease;
}

/* Fades once the visitor has actually scrolled the panel. */
.scroller__hint.is-dismissed { opacity: 0.25; }

.scroller__mouse {
    width: 0.85rem;
    height: 1.3rem;
    flex-shrink: 0;
    color: var(--accent);
}

/* The wheel line inside the mouse nudges up and down to suggest the gesture. */
.scroller__mouse line {
    animation: scroll-nudge 1.8s ease-in-out infinite;
}

@keyframes scroll-nudge {
    0%, 100% { transform: translateY(0); opacity: 1; }
    50% { transform: translateY(3px); opacity: 0.35; }
}

.scroller__hint.is-dismissed .scroller__mouse line { animation: none; }

/* --- Selected-sector description ---------------------------------------- */

/*
 * Mirrors the scroller on the opposite side: the scroller names the sectors on the
 * right, the granule field forms the piece in the middle, and this describes what
 * is being formed on the left.
 *
 * The panels are stacked in a grid rather than shown one at a time in flow, so the
 * block's height is that of the longest description and the layout never shifts
 * as the visitor steps through sectors.
 */
.zone-detail {
    position: absolute;
    z-index: 3;
    top: 50%;
    /* Set in noticeably from the viewport edge — more than the scroller's offset —
       so the description reads as a deliberate column rather than being pinned to
       the side of the window. */
    left: clamp(2rem, 7vw, 6rem);
    transform: translateY(-50%);
    width: min(23rem, 30vw);
    /* Row 1 stacks the swapping panels, row 2 holds the fixed jump button beneath
       them. Without the explicit rows the button would land in the panels' cell. */
    display: grid;
    grid-template-rows: auto auto;
    justify-items: start;
}

.zone-detail__panel {
    /* Every panel occupies the same cell; only the active one is visible. */
    grid-area: 1 / 1;
    /* Hidden panels must not swallow clicks meant for the visible one. */
    pointer-events: none;
    opacity: 0;
    visibility: hidden;
    transform: translateY(0.5rem);
    transition: opacity 0.35s ease, transform 0.35s ease, visibility 0.35s;
}

.zone-detail__panel.is-active {
    opacity: 1;
    visibility: visible;
    transform: none;
    /* Only the visible panel is interactive, so hidden links are not clickable. */
    pointer-events: auto;
}

.zone-detail__eyebrow {
    font-family: var(--font-technical);
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.15em;
    text-transform: uppercase;
    color: var(--accent);
    margin: 0 0 0.7rem;
}

.zone-detail__title {
    font-size: var(--step-2);
    line-height: 1.15;
    margin: 0 0 0.7rem;
    text-wrap: balance;
}

/* The hero clips its overflow, so an editor writing a long `introduction` must not
   be able to push the link out of the panel. Clamped rather than scrolled: the full
   text is on the sector page the link points at. */
.zone-detail__text {
    font-size: 0.95rem;
    line-height: 1.55;
    color: var(--ink-soft);
    margin: 0 0 1.2rem;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 6;
    line-clamp: 6;
    overflow: hidden;
}

.zone-detail__link {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    font-family: var(--font-technical);
    font-size: 0.76rem;
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--ink);
    text-decoration: none;
    border-bottom: 1px solid var(--line);
    padding-bottom: 0.3rem;
    transition: color 0.2s ease, border-color 0.2s ease;
}

.zone-detail__link svg {
    width: 0.85rem;
    height: 0.85rem;
    flex-shrink: 0;
}

.zone-detail__link:hover {
    color: var(--accent);
    border-bottom-color: var(--accent);
}

/* --- Drifting pellet backdrop -------------------------------------------- */

/*
 * Faint grey pellets behind everything below the hero, drifting slowly enough that
 * the movement is felt rather than watched. Purely decorative.
 *
 * The wrapper establishes the stacking context and clips the layer; the sections
 * inside it are transparent, so they read over the pellets without any z-index
 * bookkeeping of their own. `isolation` keeps the negative z-index child from
 * escaping behind the page background.
 */
.pellet-field {
    position: relative;
    isolation: isolate;
    /* The pellets must not widen the page or add scroll. */
    overflow: hidden;
}

.pellet-field__layer {
    position: absolute;
    inset: 0;
    z-index: -1;
    pointer-events: none;
}

.pellet-field__dot {
    position: absolute;
    border-radius: 50%;
    /* Same moulded-pellet shading as the jump button, drained of colour: a rim
       shadow and an off-centre highlight are what stop it reading as a flat dot. */
    background:
        radial-gradient(circle at 34% 28%, rgba(255, 255, 255, 0.9) 0%, rgba(255, 255, 255, 0) 38%),
        radial-gradient(circle at 50% 46%, #dededc 0%, #d2d1cf 52%, #c0bfbd 100%);
    opacity: 0.5;
    will-change: transform;
    animation: pellet-drift 34s ease-in-out infinite alternate;
}

/*
 * Two gentle drifts. Both stay under ~40px of travel so nothing reads as sliding
 * across the page; alternating direction avoids a visible restart.
 */
@keyframes pellet-drift {
    0%   { transform: translate3d(0, 0, 0) rotate(0deg); }
    50%  { transform: translate3d(1.4rem, -1.9rem, 0) rotate(8deg); }
    100% { transform: translate3d(-1rem, -3.2rem, 0) rotate(-6deg); }
}

@keyframes pellet-drift-alt {
    0%   { transform: translate3d(0, 0, 0) rotate(0deg); }
    50%  { transform: translate3d(-1.7rem, -1.5rem, 0) rotate(-9deg); }
    100% { transform: translate3d(1.2rem, -2.8rem, 0) rotate(7deg); }
}

/*
 * Scattered by hand rather than by loop: positions in percentages so the field
 * reflows with the page, with staggered sizes, delays and durations so no two
 * pellets move together. Odd ones take the alternate path.
 */
.pellet-field__dot--1  { top: 4%;  left: 7%;  width: 1.5rem; height: 1.35rem; animation-duration: 38s; animation-delay: -2s;  animation-name: pellet-drift-alt; }
.pellet-field__dot--2  { top: 11%; left: 82%; width: 2.3rem; height: 2.05rem; animation-duration: 44s; animation-delay: -9s; }
.pellet-field__dot--3  { top: 19%; left: 31%; width: 1.1rem; height: 1rem;    animation-duration: 31s; animation-delay: -14s; animation-name: pellet-drift-alt; }
.pellet-field__dot--4  { top: 26%; left: 63%; width: 1.8rem; height: 1.6rem;  animation-duration: 49s; animation-delay: -5s; }
.pellet-field__dot--5  { top: 33%; left: 12%; width: 2.6rem; height: 2.3rem;  animation-duration: 41s; animation-delay: -20s; animation-name: pellet-drift-alt; }
.pellet-field__dot--6  { top: 39%; left: 91%; width: 1.3rem; height: 1.2rem;  animation-duration: 36s; animation-delay: -11s; }
.pellet-field__dot--7  { top: 46%; left: 45%; width: 1.6rem; height: 1.45rem; animation-duration: 52s; animation-delay: -3s;  animation-name: pellet-drift-alt; }
.pellet-field__dot--8  { top: 54%; left: 74%; width: 2.1rem; height: 1.9rem;  animation-duration: 33s; animation-delay: -17s; }
.pellet-field__dot--9  { top: 61%; left: 21%; width: 1.2rem; height: 1.1rem;  animation-duration: 46s; animation-delay: -7s;  animation-name: pellet-drift-alt; }
.pellet-field__dot--10 { top: 68%; left: 57%; width: 2.4rem; height: 2.15rem; animation-duration: 39s; animation-delay: -23s; }
.pellet-field__dot--11 { top: 74%; left: 4%;  width: 1.7rem; height: 1.55rem; animation-duration: 43s; animation-delay: -12s; animation-name: pellet-drift-alt; }
.pellet-field__dot--12 { top: 81%; left: 87%; width: 1.4rem; height: 1.3rem;  animation-duration: 35s; animation-delay: -6s; }
.pellet-field__dot--13 { top: 88%; left: 37%; width: 2rem;   height: 1.8rem;  animation-duration: 50s; animation-delay: -18s; animation-name: pellet-drift-alt; }
.pellet-field__dot--14 { top: 94%; left: 68%; width: 1.15rem; height: 1.05rem; animation-duration: 37s; animation-delay: -1s; }

/* Denser than it needs to be on small screens; thin it out and calm it down. */
@media (max-width: 780px) {
    .pellet-field__dot { opacity: 0.34; }
    .pellet-field__dot--3,
    .pellet-field__dot--7,
    .pellet-field__dot--12 { display: none; }
}

/* Decorative motion, so it stops entirely when reduced motion is requested. The
   pellets stay as a static texture. */
@media (prefers-reduced-motion: reduce) {
    .pellet-field__dot { animation: none; }
}

/* --- Video jump button --------------------------------------------------- */

/*
 * A quiet play glyph with the label beside it. Hand-drawn inline SVG rather than an
 * icon library: the project already draws its handful of icons this way, and one
 * triangle is not worth a dependency. The triangle matches .player__play so the
 * link and the video's own control read as the same icon.
 */
.video-jump {
    /* Row 2 of .zone-detail, below the swapping panels. */
    grid-row: 2;
    display: inline-flex;
    align-items: center;
    gap: 0.7rem;
    margin-top: 1.5rem;
    text-decoration: none;
    color: var(--ink-soft);
    /* The container's panels opt out of pointer events; this must opt back in. */
    pointer-events: auto;
}

.video-jump__icon {
    display: block;
    flex-shrink: 0;
    width: 2.1rem;
    height: 2.1rem;
    color: var(--accent);
    transition: transform 0.25s ease, color 0.2s ease;
}

.video-jump__icon svg {
    display: block;
    width: 100%;
    height: 100%;
}

/* The ring lightens off the accent so the triangle stays the focal point. */
.video-jump__ring {
    opacity: 0.45;
    transition: opacity 0.25s ease;
}

/* Restrained on hover: the ring firms up and the glyph edges forward. No lift, no
   spin — this sits next to body copy and should not pull the eye off it. */
.video-jump:hover .video-jump__icon,
.video-jump:focus-visible .video-jump__icon {
    transform: translateX(0.15rem);
}

.video-jump:hover .video-jump__ring,
.video-jump:focus-visible .video-jump__ring { opacity: 1; }

.video-jump__label {
    font-family: var(--font-technical);
    font-size: 0.76rem;
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    line-height: 1.3;
    transition: color 0.2s ease;
}

.video-jump:hover .video-jump__label,
.video-jump:focus-visible .video-jump__label { color: var(--ink); }

@media (prefers-reduced-motion: reduce) {
    .video-jump__icon,
    .video-jump__ring { transition: none; }
}

@media (max-width: 980px) {
    .scroller { width: min(17rem, 40vw); }

    /* The generous left offset has to give way here, or the panel gets too narrow
       to hold a sentence. */
    .zone-detail {
        left: clamp(1.5rem, 4vw, 2.5rem);
        width: min(18rem, 32vw);
    }
}

/* The two side panels would collide before the stacked layout kicks in. The jump
   button is not a panel, so it stays. */
@media (max-width: 860px) and (min-width: 781px) {
    /* display:none rather than hidden, so the panels' row collapses and the button
       is not left floating below empty space. */
    .zone-detail__panel { display: none; }
    .zone-detail { width: auto; }
    .video-jump { margin-top: 0; }
}

@media (max-width: 780px) {
    /* Stacked below the (static) hero image on small screens, where there is no
       3D scene to sit beside. */
    /*
     * Stacked order. The zone-detail block sits before the scroller in the DOM because on
     * desktop it is absolutely positioned on the opposite side; here everything is in
     * flow, so flex order puts the sector list first and the video link last.
     */
    .hero--granules {
        min-height: auto;
        display: flex;
        flex-direction: column;
    }

    /* .hero__bg is absolutely positioned, so it takes no part in the order. */
    .scroller { order: 1; }
    .zone-detail { order: 2; }

    /* Darker than the desktop grey: over the backdrop image the faint tone was barely
       legible, and this label is what introduces the whole list. */
    .scroller__intro { color: var(--ink); }

    /*
     * The scene gets its own band at the top of the stack rather than being hidden. It
     * was overlapping the sector list because it is absolutely positioned on desktop;
     * here it becomes a static block with a fixed height, so the model has room of its
     * own and nothing sits underneath it.
     */
    .hero-scene {
        position: static;
        order: 0;
        width: 100%;
        /* The vw term alone gives only ~157px at 375px wide, which crops the model;
           the 14rem floor is what actually applies on a phone. */
        height: clamp(14rem, 55vw, 20rem);
        min-height: 0;
    }

    /*
     * The backdrop is only the fallback. It was briefly forced visible here, from when the
     * 3D scene was hidden on mobile -- now that the model has its own band, that left a
     * leather texture showing behind it. So .hero--scene-ready keeps clearing it, exactly
     * as on desktop, and the tint only shows when no scene is running.
     */
    .hero--granules .hero__bg { opacity: 0.22; }

    /* Restated after the line above, which would otherwise win on source order and put
       the texture back behind the model. */
    .hero--granules.hero--scene-ready .hero__bg { opacity: 0; }

    /* Larger tap targets, and room for the captions that are all shown at this width. */

    .scroller {
        position: static;
        transform: none;
        width: 100%;
        max-width: var(--container);
        margin-inline: auto;
        padding: 2rem clamp(1.15rem, 4vw, 2.5rem) 2.5rem;
    }

    /* Superseded by the sector stepper further down, which shows one item at a time. */
    .scroller__hint { display: none; }

    /* No 3D scene and no wheel stepping here, so the overlay has nothing to sit
       beside. The scroller's own captions already describe each sector on this
       breakpoint, which would make these panels redundant — but the jump button is
       not per-sector, so it drops into normal flow instead of disappearing. */
    .zone-detail__panel { display: none; }

    .zone-detail {
        position: static;
        transform: none;
        width: 100%;
        max-width: var(--container);
        margin-inline: auto;
        padding: 0 clamp(1.15rem, 4vw, 2.5rem) 2.5rem;
    }

    .video-jump { margin-top: 0; }
}

/* --- Page hero ----------------------------------------------------------- */

/*
 * Detail-page hero: a tall image panel inset from the viewport by a white margin, with
 * the title centred over it and a scroll trigger at the foot. Square corners, matching
 * the rest of the site.
 */
/* Full-bleed: the frame runs to the viewport edges with no surrounding margin. */
.page-hero {
    padding: 0;
    background: var(--paper);
}

.page-hero__frame {
    position: relative;
    isolation: isolate;
    overflow: hidden;
    /* Square, not rounded. */
    border-radius: 0;
    background: var(--paper-tint);
    min-height: clamp(20rem, 16rem + 22vh, 30rem);
    display: flex;
}

.page-hero--image .page-hero__frame {
    background: var(--ink);
    color: #fff;
}

.page-hero__bg {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    /* Darker than the old header's 0.4: the title now sits directly over the image and
       needs the contrast behind it. */
    opacity: 0.55;
    z-index: -1;
}

/*
 * A gradient scrim under the text. An image alone cannot guarantee contrast for white
 * type, and dimming the whole photograph further would flatten it.
 */
.page-hero--image .page-hero__frame::before {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(
        180deg,
        rgba(22, 20, 19, 0.45) 0%,
        rgba(22, 20, 19, 0.15) 45%,
        rgba(22, 20, 19, 0.55) 100%
    );
    z-index: -1;
}

.page-hero__inner {
    position: relative;
    display: flex;
    flex-direction: column;
    flex-grow: 1;
    width: 100%;
    max-width: var(--container);
    margin-inline: auto;
    padding: clamp(1.5rem, 1rem + 2vw, 2.5rem) clamp(1.15rem, 4vw, 2.5rem);
}

/* Centres the title block in the space between breadcrumb and trigger. */
.page-hero__middle {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    flex-grow: 1;
    text-align: center;
}

.page-hero__title {
    font-size: var(--step-4);
    margin: 0;
    text-wrap: balance;
}

.page-hero__intro {
    font-size: var(--step-1);
    color: var(--ink-soft);
    max-width: 52ch;
    margin: 1rem 0 0;
}

.page-hero--image .page-hero__intro { color: rgba(255, 255, 255, 0.85); }

/* --- Scroll trigger ------------------------------------------------------ */

/*
 * Sits at the foot of the hero, centred. A real anchor to #page-content, so it works
 * with JS disabled; site.js upgrades it to a centred smooth scroll.
 */
.page-hero__trigger {
    align-self: center;
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    gap: 0.6rem;
    text-decoration: none;
    color: inherit;
}

.page-hero__trigger-label {
    font-family: var(--font-technical);
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    opacity: 0.75;
    transition: opacity 0.2s ease;
}

.page-hero__trigger-icon {
    display: grid;
    place-items: center;
    width: 2.8rem;
    height: 2.8rem;
    border: 1px solid currentColor;
    /* Square, like the site's other controls. */
    border-radius: 0;
    transition: background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}

.page-hero__trigger-icon svg {
    width: 1.2rem;
    height: 1.2rem;
    /* The nudge that suggests the gesture. */
    animation: hero-trigger-nudge 2s ease-in-out infinite;
}

.page-hero__trigger:hover .page-hero__trigger-label,
.page-hero__trigger:focus-visible .page-hero__trigger-label { opacity: 1; }

.page-hero__trigger:hover .page-hero__trigger-icon,
.page-hero__trigger:focus-visible .page-hero__trigger-icon {
    background: var(--accent);
    border-color: var(--accent);
    color: #fff;
}

@keyframes hero-trigger-nudge {
    0%, 100% { transform: translateY(0); }
    50% { transform: translateY(0.22rem); }
}

/* The breadcrumb inherits the frame's colour scheme. */
.page-hero--image .breadcrumb ol { color: rgba(255, 255, 255, 0.7); }

/* Anchor target: zero-height, so it never adds space of its own. */
#page-content {
    display: block;
    height: 0;
}

@media (max-width: 780px) {
    .page-hero__frame { min-height: clamp(16rem, 14rem + 14vh, 22rem); }
}

/* --- Breadcrumb --------------------------------------------------------- */

.breadcrumb ol {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    list-style: none;
    margin: 0 0 1.1rem;
    padding: 0;
    font-size: 0.85rem;
    color: var(--ink-faint);
}

.breadcrumb li + li::before { content: "/"; margin-right: 0.4rem; opacity: 0.6; }
.breadcrumb a { color: inherit; text-decoration: none; }
.breadcrumb a:hover { text-decoration: underline; }


/* --- Buttons and links -------------------------------------------------- */

.btn {
    display: inline-block;
    padding: 0.8rem 1.7rem;
    border: 0;
    /* Square, matching the site's other controls. */
    border-radius: 0;
    font: inherit;
    font-weight: 600;
    text-decoration: none;
    cursor: pointer;
    transition: background-color 0.15s, transform 0.15s;
}

.btn--primary { background: var(--accent); color: #fff; }
.btn--primary:hover { background: var(--accent-dark); color: #fff; }

.btn--light { background: #fff; color: var(--ink); }
.btn--light:hover { background: var(--paper-tint); color: var(--ink); }

.link-arrow {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    font-weight: 600;
    font-size: 0.92rem;
    text-decoration: none;
    letter-spacing: 0.01em;
}
.link-arrow::after { content: "→"; transition: transform 0.15s; }
.link-arrow:hover::after { transform: translateX(3px); }
.link-arrow--back { margin-top: 2.5rem; }
.link-arrow--back::after { content: none; }
.link-arrow--back::before { content: "←"; }

/* --- Cards -------------------------------------------------------------- */

.card-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
    gap: 1.5rem;
}

/*
 * Two per row instead of the auto-fit default. The four product ranges each carry a
 * brand lockup and a full sentence, which the 17rem auto-fit columns squeezed into
 * four narrow strips at the 1480px container width. Halving the count gives the copy
 * room to breathe and pairs the ranges 2x2.
 *
 * Scoped to a modifier: .card-grid also lays out the news and products listings,
 * which want as many columns as they can fit.
 */
.card-grid--pairs {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: clamp(1.5rem, 1rem + 2vw, 2.75rem);
}

/* Single column before the cards get too narrow to hold a sentence. */
@media (max-width: 720px) {
    .card-grid--pairs { grid-template-columns: 1fr; }
}

.card {
    /* Positioning context for .card__corner, which anchors to the bottom-right. */
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.5rem;
    background: var(--paper);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    padding: 1.75rem;
    /* Only the shadow animates now; the border no longer changes on hover. */
    transition: box-shadow 0.25s ease;
}

/* Lift on a shadow rather than a red outline: the border staying neutral keeps the
   row calm, and the shadow alone is enough to signal the card is interactive. */
.card:hover {
    box-shadow: 0 0.4rem 1.4rem rgba(22, 33, 31, 0.13);
}

.section--tinted .card { background: #fff; }

.card__title { font-size: var(--step-1); margin: 0; }

/*
 * Range cards carry the brand lockup instead of a text heading. The three logos have
 * quite different aspect ratios (4.7:1, 2.6:1, 3.7:1), so they are sized by HEIGHT
 * with width left auto — matching on width would render them at visibly different
 * sizes.
 *
 * The shared min-height is what keeps the four cards aligned: Pur Lastic has no logo
 * and falls back to a text heading, which would otherwise sit at a different height
 * from the lockups beside it. Scoped to this modifier rather than .card__title,
 * which also styles news headlines that must stay free to wrap.
 */
.card__title--range {
    display: flex;
    /* Centred horizontally: the lockup sits in the middle of the card rather than
       hugging the left edge. The card itself is align-items:flex-start, so the
       heading needs full width for centring to have anything to work with. */
    align-self: stretch;
    justify-content: center;
    align-items: center;
    min-height: 3rem;
    /* Small gap below the lockup, separating it from the description. */
    margin-bottom: 0.6rem;
}

.card__logo {
    /* Slightly larger than before: the two-column grid gives each card enough room
       for the lockup to carry more weight. */
    height: 2.6rem;
    width: auto;
    /* Cap the widest lockup so it cannot outgrow the card. */
    max-width: min(100%, 15rem);
    object-fit: contain;
    /* Centred to match the heading box. */
    object-position: center;
}
.card__text { color: var(--ink-soft); margin: 0; flex-grow: 1; }

.card__meta {
    font-size: 0.8rem;
    font-weight: 600;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--accent);
}

.card--product { border-left: 3px solid var(--accent); }

/* --- News block --------------------------------------------------------- */

/*
 * Editorial news layout: eyebrow and a two-line headline on the left, action
 * aligned right, then three cards with the last one inverted. Square corners
 * throughout.
 */
.news-block__head {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 2rem;
    margin-bottom: clamp(1.75rem, 1.25rem + 2vw, 2.75rem);
}

.news-block__eyebrow {
    font-family: var(--font-technical);
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--ink-soft);
    margin: 0 0 0.9rem;
}

.news-block__title {
    font-size: clamp(1.9rem, 1.3rem + 2.2vw, 3.1rem);
    line-height: 1.08;
    margin: 0;
    /* Held to a narrow measure so the headline breaks across two lines, as in the
       reference, instead of running the full width. */
    max-width: 22ch;
}

/* --- Pill button -------------------------------------------------------- */

.pill-btn {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    flex-shrink: 0;
    padding: 0.85rem 1.35rem;
    background: var(--ink);
    color: #fff;
    font-family: var(--font-technical);
    font-size: 0.74rem;
    font-weight: 600;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    text-decoration: none;
    /* Clips the sweeping fill to the button box. */
    overflow: hidden;
}

/*
 * Accent sweeps in from the left on hover, matching .arrow-btn. This button is dark
 * by default rather than outlined, so the sweep lays the accent over the ink instead
 * of filling an empty box.
 */
.pill-btn::before {
    content: "";
    position: absolute;
    inset: 0;
    background: var(--accent);
    transform: scaleX(0);
    transform-origin: left center;
    transition: transform 0.32s cubic-bezier(0.45, 0.05, 0.3, 1);
}

.pill-btn:hover::before,
.pill-btn:focus-visible::before {
    transform: scaleX(1);
}

/* Label and glyph ride above the fill. */
.pill-btn > * {
    position: relative;
    z-index: 1;
}

.pill-btn svg {
    width: 0.9rem;
    height: 0.9rem;
    transition: transform 0.25s ease;
}

/* The arrow leans out as the fill arrives. */
.pill-btn:hover svg,
.pill-btn:focus-visible svg {
    transform: translate(0.1rem, -0.1rem);
}

.pill-btn:hover,
.pill-btn:focus-visible { color: #fff; }

.pill-btn--accent {
    background: var(--accent);
    color: #fff;
}
/* Already accent-filled, so the sweep would be invisible; it darkens instead. */
.pill-btn--accent::before { background: var(--accent-dark); }

/* --- News cards --------------------------------------------------------- */

.news-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
    gap: 1.25rem;
}

.news-card { position: relative; }

.news-card__link {
    display: flex;
    flex-direction: column;
    height: 100%;
    text-decoration: none;
    color: inherit;
    background: var(--paper-tint);
    transition: background-color 0.2s ease;
}

.news-card:hover .news-card__link { background: #eeeeeb; }

.news-card__media {
    position: relative;
    aspect-ratio: 4 / 3;
    overflow: hidden;
}

.news-card__media img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform 0.5s ease;
}

.news-card:hover .news-card__media img { transform: scale(1.04); }

.news-card__body {
    padding: 1.4rem;
    /* Pushes the body to the bottom so titles align across cards of differing
       text length. */
    margin-top: auto;
}

.news-card__category {
    font-family: var(--font-technical);
    font-size: 0.68rem;
    font-weight: 600;
    letter-spacing: 0.15em;
    text-transform: uppercase;
    color: var(--ink-faint);
    margin: 0 0 0.7rem;
}

.news-card__title {
    font-size: 1.06rem;
    line-height: 1.32;
    margin: 0;
    color: var(--ink);
}

/* Inverted card. The image sits behind the whole card rather than in a band, so
   the dark ground reads as one surface. */
.news-card--feature .news-card__link {
    background: #1b1a18;
    color: #fff;
}

.news-card--feature:hover .news-card__link { background: #232120; }

.news-card--feature .news-card__media {
    aspect-ratio: auto;
    position: absolute;
    inset: 0;
}

.news-card--feature .news-card__media img { opacity: 0.5; }

/*
 * The feature card's image is a full-card background, so its body needs a spacer
 * that matches the 4:3 image band on the other cards. A padding-top in percent
 * resolves against the card's own width, which is what 4:3 amounts to (75%),
 * keeping the three bodies on the same line at every column width.
 */
.news-card--feature .news-card__body {
    position: relative;
    padding-top: calc(75% + 1.4rem);
}

.news-card--feature .news-card__category { color: rgba(255, 255, 255, 0.65); }
.news-card--feature .news-card__title { color: #fff; }

/*
 * The read-more pill sits centred on the image band, not the whole card, so it
 * lines up with the imagery on the neighbouring cards. 37.5% is half of the 75%
 * (4:3) band height.
 */
.news-card__cta {
    position: absolute;
    top: 37.5%;
    left: 50%;
    transform: translate(-50%, -50%);
    z-index: 1;
    pointer-events: none;
}

@media (max-width: 720px) {
    .news-block__head {
        flex-direction: column;
        align-items: flex-start;
        gap: 1.5rem;
    }

    .news-block__title { max-width: none; }
}

/* --- Sectors ------------------------------------------------------------ */

.sector-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
    gap: 1.5rem;
}

.sector { padding: 1.5rem 0; border-top: 2px solid var(--ink); }
.sector__title { font-size: var(--step-1); }
.sector__text { color: var(--ink-soft); margin: 0; }

.sector--card {
    border-top: 0;
    padding: 0;
    overflow: hidden;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: #fff;
    transition: border-color 0.15s, box-shadow 0.15s;
}

.sector--card:hover {
    border-color: var(--accent);
    box-shadow: 0 6px 24px rgba(71, 70, 69, 0.1);
}

.sector--card .sector__body { padding: 1.6rem; }

.sector__image {
    aspect-ratio: 16 / 10;
    object-fit: cover;
    width: 100%;
    transition: transform 0.4s ease;
}
.sector--card:hover .sector__image { transform: scale(1.03); }

.sector__title a { color: inherit; text-decoration: none; }
.sector__title a:hover { color: var(--accent); }

/* --- Video player ------------------------------------------------------- */

/*
 * The hero above ends in empty space of its own — its content is vertically centred,
 * and the background fades to white over the last stretch — so a full section pad on
 * top of that read as one large void. The top padding is cut back to roughly a third
 * while the bottom keeps the standard rhythm.
 */
.video-section { padding-top: clamp(1rem, 0.5rem + 1.5vw, 2rem); }

.video-section__head {
    max-width: 100%;
    margin-inline: auto;
    margin-bottom: clamp(1.75rem, 1.25rem + 2vw, 2.75rem);
    text-align: center;
}

.video-section__title {
    /* step-3 rather than step-4: this is a full sentence, not a short phrase,
       so the larger step would dominate the section. */
    font-size: var(--step-3);
    line-height: 1.2;
    margin: 0;
    text-wrap: balance;
}

.player {
    margin: 0;
    background: var(--ink);
    border-radius: var(--radius);
    overflow: hidden;
}

/*
 * Wider than 16:9 to keep the player from dominating the viewport now that the
 * container runs to 1480px. The video itself is `object-fit: cover`, so the shorter
 * stage crops the top and bottom rather than letterboxing — and `max-height` caps
 * it on very wide screens, where a ratio alone would grow without limit.
 */
.player__stage {
    position: relative;
    aspect-ratio: 21 / 9;
    max-height: 72vh;
    background: var(--ink);
}

.player__video {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

/* Cover: over the poster until first play. */
.player__cover {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    justify-content: space-between;
    padding: clamp(1.25rem, 1rem + 2vw, 2.5rem);
    background: linear-gradient(
        180deg,
        rgba(22, 21, 20, 0.55) 0%,
        rgba(22, 21, 20, 0.25) 45%,
        rgba(22, 21, 20, 0.6) 100%
    );
    transition: opacity 0.4s ease, visibility 0.4s ease;
}

.player.has-played .player__cover {
    opacity: 0;
    visibility: hidden;
}

.player__intro { color: #fff; }

.player__eyebrow {
    font-family: var(--font-technical);
    font-size: 0.74rem;
    font-weight: 600;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, 0.72);
    margin: 0 0 0.35rem;
}

.player__title {
    font-size: var(--step-2);
    margin: 0;
    color: #fff;
}

/* Large play affordance, centred on the stage. */
.player__play {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: clamp(3.75rem, 3rem + 3vw, 5.25rem);
    aspect-ratio: 1;
    border: 0;
    /* Square with hard corners, no radius. */
    border-radius: 0;
    background: var(--accent);
    color: #fff;
    cursor: pointer;
    display: grid;
    place-items: center;
    transition: transform 0.2s ease, background-color 0.2s ease;
}

.player__play svg { width: 45%; height: 45%; margin-left: 6%; }

.player__play:hover,
.player__play:focus-visible {
    background: var(--accent-dark);
    transform: translate(-50%, -50%) scale(1.06);
}

/* --- Loading ------------------------------------------------------------ */

/*
 * The file is 138 MB with preload off, so the wait after the first press is real
 * and needs acknowledging — otherwise the press reads as broken.
 */
.player__loading {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
    background: rgba(22, 21, 20, 0.4);
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.2s ease, visibility 0.2s ease;
}

.player.is-loading .player__loading {
    opacity: 1;
    visibility: visible;
}

.player__spinner {
    width: 2.5rem;
    height: 2.5rem;
    border: 2px solid rgba(255, 255, 255, 0.25);
    border-top-color: #fff;
    border-radius: 50%;
    animation: player-spin 0.8s linear infinite;
}

@keyframes player-spin {
    to { transform: rotate(360deg); }
}

/* --- Controls ----------------------------------------------------------- */

.player__controls {
    display: flex;
    align-items: center;
    gap: 0.9rem;
    padding: 0.85rem clamp(0.9rem, 0.6rem + 1vw, 1.4rem);
    background: #1f1e1c;
}

/* Hidden until the custom controls are wired up, so the native ones are not
   doubled by a dead control bar. */
.player:not(.is-ready) .player__controls { display: none; }

.player__btn {
    flex-shrink: 0;
    width: 2.1rem;
    height: 2.1rem;
    padding: 0;
    border: 0;
    background: none;
    color: rgba(255, 255, 255, 0.8);
    cursor: pointer;
    display: grid;
    place-items: center;
    border-radius: var(--radius);
    transition: color 0.15s ease, background-color 0.15s ease;
}

.player__btn:hover,
.player__btn:focus-visible {
    color: #fff;
    background: rgba(255, 255, 255, 0.1);
}

.player__btn svg { width: 1.35rem; height: 1.35rem; }

/* Play/pause and mute each hold both icon states; only one shows at a time. */
.player__icon--pause,
.player.is-playing .player__icon--play { display: none; }
.player.is-playing .player__icon--pause { display: block; }

.player__icon--muted,
.player.is-muted .player__icon--unmuted { display: none; }
.player.is-muted .player__icon--muted { display: block; }

.player__time {
    font-family: var(--font-technical);
    font-size: 0.78rem;
    letter-spacing: 0.04em;
    color: rgba(255, 255, 255, 0.6);
    flex-shrink: 0;
    /* Fixed width stops the bar shuffling as the digits change. */
    min-width: 2.75rem;
    text-align: center;
}

.player__track {
    position: relative;
    flex-grow: 1;
    height: 0.25rem;
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.18);
    cursor: pointer;
    /* Bigger hit area than the visible bar. */
    padding-block: 0.6rem;
    background-clip: content-box;
}

.player__buffered,
.player__progress {
    position: absolute;
    left: 0;
    top: 0.6rem;
    height: 0.25rem;
    width: 100%;
    border-radius: 999px;
    transform: scaleX(0);
    transform-origin: left center;
}

.player__buffered { background: rgba(255, 255, 255, 0.28); }
.player__progress { background: var(--accent); }

.player__track:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 4px;
}

/* 21:9 would be a thin letterbox strip at phone widths, and the cover's title and
   play button need vertical room, so the stage steps back toward 16:9. */
@media (max-width: 780px) {
    .player__stage {
        aspect-ratio: 16 / 9;
        max-height: none;
    }
}

@media (max-width: 560px) {
    /* Time labels are the first thing to go when space is tight. */
    .player__time { display: none; }
    .player__controls { gap: 0.6rem; }
}

/* --- Sustainability pillars -------------------------------------------- */

.pillar-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
    gap: 2rem;
}

.pillar {
    padding: 2rem 1.75rem;
    background: var(--paper-tint);
    border-top: 3px solid var(--accent);
    border-radius: var(--radius);
}
.pillar__icon { height: 3.25rem; width: auto; margin-bottom: 1.1rem; }
.pillar__title { font-size: var(--step-1); color: var(--ink); }
.pillar__text { color: var(--ink-soft); margin: 0; }

/* --- Product detail ----------------------------------------------------- */

.product-layout {
    display: grid;
    grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
    gap: 3rem;
    align-items: start;
}

.product-layout__aside {
    background: var(--paper-tint);
    border-radius: var(--radius);
    overflow: hidden;
    position: sticky;
    top: 6rem;
}

.product-layout__image {
    width: 100%;
    aspect-ratio: 4 / 3;
    object-fit: cover;
}

/* The padding lives on the inner blocks so the image can run edge to edge. */
.product-layout__aside .spec-list { padding: 1.75rem; }

.spec-list { margin: 0; }
.spec-list dt {
    font-size: 0.78rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--ink-faint);
    margin-top: 1.2rem;
}
.spec-list dt:first-child { margin-top: 0; }
.spec-list dd { margin: 0.25rem 0 0; }

.pill-list {
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem;
    list-style: none;
    padding: 0;
    margin: 0;
}
.pill-list a {
    display: inline-block;
    padding: 0.45rem 1rem;
    border: 1px solid var(--line);
    border-radius: 999px;
    text-decoration: none;
    font-size: 0.9rem;
    color: var(--ink-soft);
}
.pill-list a:hover { border-color: var(--accent); color: var(--accent-dark); }

/* --- Rich text ---------------------------------------------------------- */

.rte { max-width: var(--measure); }
.rte > :first-child { margin-top: 0; }
.rte h2 { font-size: var(--step-2); margin-top: 2rem; }
.rte h3 { font-size: var(--step-1); margin-top: 1.75rem; }
.rte ul, .rte ol { padding-left: 1.3rem; margin: 0 0 1.1em; }
.rte li { margin-bottom: 0.4em; }
.rte a { font-weight: 500; }

.pull-quote {
    margin: 0;
    padding-left: 1.5rem;
    border-left: 3px solid var(--accent);
    font-size: var(--step-2);
    line-height: 1.35;
    letter-spacing: -0.01em;
    color: var(--ink);
}

.article__date {
    display: block;
    font-size: 0.85rem;
    font-weight: 600;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--accent);
    margin-bottom: 1.5rem;
}

.empty-state { color: var(--ink-faint); font-style: italic; }

/* --- Closing contact band ------------------------------------------------ */

/*
 * Two columns on a white ground: the question and its action on the left, supporting
 * copy on the right, and a decorative orbital motif anchored to the bottom-right
 * corner. The band is deliberately tall — the reference gives the heading a lot of
 * air — so the padding is generous and scales with the viewport.
 */
.contact-band {
    /* Positioning context for the corner mark: anchoring it to the band rather than to
       the inner .container means the crop is against the viewport edge, and so stays
       consistent instead of drifting into the gutter on wide screens. */
    position: relative;
    background: var(--paper);
    overflow: hidden;
}

.contact-band__inner {
    display: grid;
    /* Heading column is roughly twice the copy column, as in the reference. */
    grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
    gap: clamp(2rem, 1rem + 5vw, 6rem);
    align-items: start;
    padding-block: clamp(0rem, 2rem + 0vw, 5rem);
}

/* Keeps the heading and the button together in the left column. */
.contact-band__lead {
    position: relative;
    /* Above the corner mark, which reaches into this column on narrow viewports. */
    z-index: 1;
}

.contact-band__title {
    /* The largest step: this is the page's closing statement. */
    font-size: var(--step-4);
    line-height: 1.08;
    margin: 0 0 clamp(2rem, 1.5rem + 2vw, 3.5rem);
    max-width: 22ch;
    text-wrap: balance;
}

.contact-band__text {
    position: relative;
    z-index: 1;
    font-size: var(--step-1);
    line-height: 1.5;
    color: var(--ink-soft);
    margin: 0;
    max-width: 34ch;
    /* Sits level with the heading's first line rather than the column top. */
    padding-top: 0.35rem;
}

/*
 * Outlined button: label bottom-left, arrow top-right, generous empty space between
 * them — the shape is the point, so it has a fixed minimum size rather than hugging
 * its text.
 */
.ghost-btn {
    position: relative;
    display: block;
    width: min(100%, 15rem);
    min-height: 4.25rem;
    padding: 0.9rem 1.1rem;
    border: 1px solid var(--line);
    color: var(--ink);
    text-decoration: none;
    transition: border-color 0.2s ease, background-color 0.2s ease, color 0.2s ease;
}

.ghost-btn__arrow {
    position: absolute;
    top: 0.9rem;
    right: 1.1rem;
    width: 1.35rem;
    height: 1.35rem;
    transition: transform 0.25s ease;
}

.ghost-btn__label {
    position: absolute;
    bottom: 0.9rem;
    left: 1.1rem;
    font-family: var(--font-technical);
    font-size: 0.82rem;
    font-weight: 600;
    letter-spacing: 0.08em;
}

.ghost-btn:hover,
.ghost-btn:focus-visible {
    border-color: var(--accent);
    background: var(--accent);
    color: #fff;
}

/* The arrow leans further out on hover, in its own direction of travel. */
.ghost-btn:hover .ghost-btn__arrow,
.ghost-btn:focus-visible .ghost-btn__arrow {
    transform: translate(0.15rem, -0.15rem);
}

/*
 * The brand mark as a corner watermark. Bled past the bottom-right corner so it is
 * cropped by the band's overflow:hidden and only part of the star shows -- the
 * "semi-hidden" effect. Held well back in opacity so it never competes with the copy.
 *
 * Offsets are negative single values, not clamps: clamp() requires min <= max, and a
 * negative range inverts that -- which is what made the previous decoration resolve
 * off-screen and vanish entirely.
 */
.contact-band__mark {
    position: absolute;
    /* Pulled well past the corner so a good third of the star is cut away by the
       band's overflow:hidden. Note the offsets are measured from .contact-band__inner,
       which is a .container and so already inset from the viewport edge -- hence the
       large negative values needed to actually clear it. */
    right: -7rem;
    bottom: -7rem;
    width: clamp(14rem, 26vw, 22rem);
    height: auto;
    opacity: 0.09;
    pointer-events: none;
    user-select: none;
    z-index: 0;
}

@media (max-width: 860px) {
    /* Single column. The button lives inside .contact-band__lead, so on desktop it
       sits under the heading as in the reference; here the supporting copy is pulled
       above it with order, giving heading → copy → action. */
    .contact-band__inner {
        grid-template-columns: 1fr;
        gap: 1.75rem;
    }

    /* display:contents removes the wrapper's box, so its z-index goes with it — the
       children each need their own to stay above the mark. */
    .contact-band__lead { display: contents; }
    .contact-band__title { order: 1; max-width: 26ch; margin-bottom: 0; position: relative; z-index: 1; }
    .contact-band__text { order: 2; padding-top: 0; max-width: 46ch; }
    .ghost-btn { order: 3; position: relative; z-index: 1; }

    /* Smaller and tucked further in at this width, so it stays a hint behind the
       stacked copy rather than a shape competing with it. */
    .contact-band__mark {
        width: min(13rem, 55%);
        right: -4rem;
        bottom: -4rem;
    }
}

/* --- Contact block ------------------------------------------------------- */

/*
 * Two columns: heading and contact rows on the left, the form on a solid red panel
 * on the right. Every corner is square -- the reference's rounded fields and tiles
 * are deliberately squared off to match the rest of this site.
 */
.contact-block__inner {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
    gap: clamp(2rem, 1rem + 4vw, 5rem);
    align-items: start;
}

.contact-block__eyebrow {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    font-family: var(--font-technical);
    font-size: 0.74rem;
    font-weight: 600;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--accent);
    margin: 0 0 1rem;
}

/* The small marker before the label, as in the reference. */
.contact-block__dot {
    width: 0.4rem;
    height: 0.4rem;
    background: var(--accent);
    flex-shrink: 0;
}

.contact-block__title {
    font-size: var(--step-4);
    margin: 0 0 1rem;
    text-wrap: balance;
}

.contact-block__lead {
    font-size: var(--step-1);
    color: var(--ink-soft);
    margin: 0;
    max-width: 42ch;
}

/*
 * Contact rows. Pushed to the bottom of the column on tall layouts so they sit
 * against the foot of the panel opposite, as in the reference.
 */
.contact-rows {
    list-style: none;
    margin: clamp(2.5rem, 2rem + 4vw, 5rem) 0 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 1.1rem;
}

.contact-row {
    display: flex;
    align-items: center;
    gap: 1rem;
}

/* Square filled tile holding the glyph. */
/*
 * Outlined tile with a red glyph rather than a solid red block: three filled squares
 * stacked down the column read as heavy, and they competed with the form beside them.
 */
.contact-row__icon {
    display: grid;
    place-items: center;
    flex-shrink: 0;
    width: 2.6rem;
    height: 2.6rem;
    border: 1px solid var(--line);
    color: var(--accent);
    transition: border-color 0.2s ease;
}

.contact-row:hover .contact-row__icon { border-color: var(--accent); }

.contact-row__icon svg {
    width: 1.15rem;
    height: 1.15rem;
}

.contact-row__text {
    font-size: 0.95rem;
    color: var(--ink);
    text-decoration: none;
    transition: color 0.2s ease;
}

a.contact-row__text:hover { color: var(--accent); }

.contact-block__legal {
    margin: 1.75rem 0 0;
    font-size: 0.85rem;
    color: var(--ink-faint);
}

/* --- Contact panel (form) ------------------------------------------------ */

/*
 * A quiet tinted surface rather than a saturated block. A full-red panel overpowered
 * the page and forced every field, label and control to be restyled for it; on the
 * neutral tint the form keeps the site's ordinary field styling and red is left to do
 * what it does elsewhere -- mark the accent and the primary action.
 */
.contact-panel {
    background: var(--paper-tint);
    border: 1px solid var(--line);
    /* A single accent edge keeps the brand present without flooding the block. */
    border-top: 3px solid var(--accent);
    padding: clamp(1.5rem, 1rem + 2.5vw, 2.75rem);
}

.form--panel label {
    color: var(--ink);
    font-size: 0.85rem;
    font-weight: 500;
}

/*
 * White fields on the tint, so the input is the lightest thing in the block and reads
 * as the place to type.
 */
.form--panel input[type="text"],
.form--panel input[type="email"],
.form--panel input[type="tel"],
.form--panel textarea {
    background: #fff;
    color: var(--ink);
    border: 1px solid var(--line);
    border-radius: 0;
}

.form--panel input::placeholder,
.form--panel textarea::placeholder {
    color: var(--ink-faint);
}

.form--panel input:focus,
.form--panel textarea:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 2px var(--accent-tint);
}

/*
 * Custom square checkbox. appearance:none drops the native control, which cannot be
 * made square consistently across browsers; the tick is drawn with a rotated
 * pseudo-element so no icon font or SVG is needed.
 */
.form--panel input[type="checkbox"] {
    appearance: none;
    -webkit-appearance: none;
    position: relative;
    width: 1.05rem;
    height: 1.05rem;
    flex-shrink: 0;
    margin-top: 0.15rem;
    background: #fff;
    border: 1px solid var(--line);
    border-radius: 0;
    cursor: pointer;
    transition: background-color 0.15s ease, border-color 0.15s ease;
}

.form--panel input[type="checkbox"]:hover { border-color: var(--ink-faint); }

.form--panel input[type="checkbox"]:checked {
    background: var(--accent);
    border-color: var(--accent);
}

/* The tick: two borders of a rotated box, so it needs no glyph. */
.form--panel input[type="checkbox"]:checked::after {
    content: "";
    position: absolute;
    left: 0.28rem;
    top: 0.05rem;
    width: 0.3rem;
    height: 0.55rem;
    border: solid #fff;
    border-width: 0 2px 2px 0;
    transform: rotate(45deg);
}

.form--panel input[type="checkbox"]:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.form--panel .form__row--check label {
    color: var(--ink-soft);
    font-weight: 400;
}

/* Red is reserved for the action, which is the one thing that should stand out. */
.btn--panel {
    align-self: flex-start;
    background: var(--accent);
    color: #fff;
    border-radius: 0;
    font-weight: 600;
}

.btn--panel:hover,
.btn--panel:focus-visible {
    background: var(--ink);
    color: #fff;
}

/* The panel is a light surface now, so the shared alert styles apply unchanged. */

.contact-map-section { padding-top: 0; }

@media (max-width: 860px) {
    .contact-block__inner {
        grid-template-columns: 1fr;
        gap: 2.25rem;
    }

    .contact-rows { margin-top: 2rem; }
}

/* Map, square-cornered like everything else. */
.map-embed {
    aspect-ratio: 16 / 9;
    border-radius: 0;
    overflow: hidden;
    border: 1px solid var(--line);
}
.map-embed iframe { width: 100%; height: 100%; border: 0; }

/* --- Forms -------------------------------------------------------------- */

.form { display: flex; flex-direction: column; gap: 1.15rem; }

.form__row { display: flex; flex-direction: column; gap: 0.4rem; }

.form__row--split {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr));
    gap: 1.15rem;
}
.form__row--split > div { display: flex; flex-direction: column; gap: 0.4rem; }

.form label { font-size: 0.9rem; font-weight: 600; color: var(--ink); }

.form input[type="text"],
.form input[type="email"],
.form input[type="tel"],
.form textarea {
    font: inherit;
    color: var(--ink);
    padding: 0.75rem 0.9rem;
    border: 1px solid var(--line);
    /* Square, matching the site's other controls. */
    border-radius: 0;
    background: #fff;
    width: 100%;
}

.form input:focus, .form textarea:focus {
    border-color: var(--accent);
    outline: 2px solid var(--accent-tint);
    outline-offset: 0;
}

.form textarea { resize: vertical; min-height: 8rem; }

.form__row--check {
    flex-direction: row;
    align-items: flex-start;
    gap: 0.65rem;
}
.form__row--check label { font-weight: 400; color: var(--ink-soft); font-size: 0.9rem; }
.form__row--check input { margin-top: 0.3rem; flex-shrink: 0; }

/* Off-screen rather than display:none — some bots skip hidden inputs. */
.form__honeypot {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
}

.form button { align-self: flex-start; }

.alert {
    padding: 1rem 1.2rem;
    border-radius: 0;
    border-left: 3px solid;
}
/*
 * Hide the validation summary until it actually has something to report.
 *
 * .alert:empty cannot do it: ASP.NET always renders the container with a <ul> inside,
 * so the element is never empty. It swaps validation-summary-valid for
 * validation-summary-errors instead, which is the reliable hook.
 */
.alert:empty,
.alert.validation-summary-valid { display: none; }
.alert--success { background: var(--accent-tint); border-color: var(--accent); }
.alert--error { background: #fdeeee; border-color: #c0392b; color: #8e2b21; }
.alert ul { margin: 0; padding-left: 1.2rem; }

.field-validation-error { color: #c0392b; font-size: 0.85rem; }

/* --- Footer ------------------------------------------------------------- */

.site-footer {
    background: var(--ink);
    color: rgba(255, 255, 255, 0.72);
    margin-top: auto;
    font-size: 0.92rem;
}

.site-footer__inner {
    padding-block: clamp(2.5rem, 2rem + 3vw, 4rem);
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

.site-footer__brand { margin-bottom: 0.75rem; line-height: 0; }

/* The logo is dark grey on white; invert the greys so it reads on the dark
   footer while keeping the red mark close to its brand colour. */
.site-footer__brand img {
    height: 2.4rem;
    width: auto;
    filter: brightness(0) invert(1) opacity(0.92);
}
.site-footer p { margin: 0; }
.site-footer a { color: rgba(255, 255, 255, 0.9); }
.site-footer a:hover { color: #fff; }
.site-footer__contact { display: flex; flex-wrap: wrap; gap: 0.5rem; }

.site-footer__legal {
    margin-top: 1.25rem !important;
    padding-top: 1.25rem;
    border-top: 1px solid rgba(255, 255, 255, 0.14);
    font-size: 0.85rem;
    color: rgba(255, 255, 255, 0.5);
}

/* --- Responsive --------------------------------------------------------- */

@media (max-width: 900px) {
    .product-layout, .contact-layout { grid-template-columns: 1fr; gap: 2.5rem; }
    .product-layout__aside { position: static; }
}

@media (max-width: 780px) {
    .nav-toggle { display: flex; }

    /* Two columns on mobile: logo and the toggle. The nav is a fixed overlay, so it
       must not hold a grid track open. */
    .site-header__inner { grid-template-columns: 1fr auto; }
    .nav-toggle { grid-column: 2; justify-self: end; }


    .primary-nav {
        position: fixed;
        inset: 4.5rem 0 auto;
        /* Above the backdrop (40), below the sticky header (50) so the bar and its
           toggle stay reachable while the panel is open. */
        z-index: 45;
        flex-direction: column;
        align-items: stretch;
        gap: 0;
        background: #fff;
        border-bottom: 1px solid var(--line);
        padding: 1rem clamp(1.15rem, 4vw, 2.5rem) 1.5rem;
        box-shadow: 0 12px 24px rgba(22, 33, 31, 0.08);
        display: none;
    }

    .primary-nav.is-open { display: flex; }

    .primary-nav ul { flex-direction: column; align-items: stretch; gap: 0; }

    .primary-nav li { border-bottom: 1px solid var(--line); }

    .primary-nav a {
        display: block;
        padding: 0.9rem 0;
        border-bottom: 0;
        font-size: 1.05rem;
    }
    .primary-nav a.is-active { color: var(--accent); }

    .lang-switch {
        flex-direction: row !important;
        border-left: 0 !important;
        padding-left: 0 !important;
        padding-top: 1.1rem !important;
        gap: 0.4rem !important;
    }
    .lang-switch li { border-bottom: 0; }
    /* Larger tap target here, keeping the side padding so the active rectangle still
       reads as a block rather than a stripe. */
    .lang-switch a { padding: 0.45rem 0.7rem; }

    .nav-toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
    .nav-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
    .nav-toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }
}

/* --- Back to top --------------------------------------------------------- */

/*
 * Fixed circular button, bottom-right. Hidden until the visitor is a screenful or
 * so down the page — `is-visible` is added by site.js. Because it is an anchor to
 * #main it still works with the script absent; it would simply always be visible,
 * which is why the hidden state is opt-in rather than the default.
 */
.to-top {
    position: fixed;
    right: clamp(1rem, 3vw, 2rem);
    bottom: clamp(1rem, 3vw, 2rem);
    z-index: 40;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 2.9rem;
    height: 2.9rem;
    /* Square with hard corners, no radius. */
    border-radius: 0;
    background: var(--ink);
    color: #fff;
    box-shadow: 0 0.25rem 0.9rem rgba(0, 0, 0, 0.22);
    /* Lifted slightly and faded out until needed. */
    opacity: 0;
    visibility: hidden;
    transform: translateY(0.5rem);
    transition: opacity 0.25s ease, transform 0.25s ease, visibility 0.25s,
                background-color 0.2s ease;
}

.to-top.is-visible {
    opacity: 1;
    visibility: visible;
    transform: none;
}

.to-top svg {
    width: 1.15rem;
    height: 1.15rem;
}

.to-top:hover,
.to-top:focus-visible {
    background: var(--accent);
    color: #fff;
}

/* Sits above the mobile nav's bottom edge without crowding the corner. */
/*
 * At 320px and below hero-init.js does not start the scene, so the band would be an empty
 * gap. Kept in step with the isNarrow query in that file.
 */
@media (max-width: 320px) {
    .hero-scene { display: none; }
}

@media (max-width: 780px) {
    .to-top {
        width: 2.6rem;
        height: 2.6rem;
    }
}

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        transition-duration: 0.01ms !important;
        animation-duration: 0.01ms !important;
    }
}

/* --- Intro split (image + copy) ------------------------------------------- */

/*
 * Image left, copy right. The image column is slightly the wider of the two so the
 * photograph carries the block, with the text set on a comfortable measure beside it.
 * Vertically centred, so a short paragraph sits against the middle of the image
 * rather than floating at its top.
 */
.intro-split__inner {
    display: grid;
    grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
    gap: clamp(1.75rem, 1rem + 4vw, 4.5rem);
    align-items: center;
}

.intro-split__media {
    margin: 0;
    overflow: hidden;
    border-radius: var(--radius);
    background: var(--paper-tint);
}

.intro-split__media img {
    width: 100%;
    /* A settled landscape crop: tall enough to hold the frame beside a paragraph,
       without turning the block into a full-height banner. */
    aspect-ratio: 4 / 3;
    object-fit: cover;
    display: block;
}

.intro-split__title {
    font-size: var(--step-3);
    line-height: 1.15;
    margin: 0 0 1rem;
    text-wrap: balance;
}

/* The copy keeps its own measure even in a wide column. */
.intro-split__body .rte { max-width: 46ch; }

.intro-split__body .arrow-btn { margin-top: 1.75rem; }

/* Stack before the columns get too narrow for either the image or the text. */
@media (max-width: 780px) {
    .intro-split__inner {
        grid-template-columns: 1fr;
        gap: 1.75rem;
    }

    .intro-split__media img { aspect-ratio: 16 / 10; }
}

/* Screen-reader-only text: kept in the accessibility tree, out of the visual layout. */
.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

/* --- Card corner action --------------------------------------------------- */

/*
 * Small outlined square in the card's bottom-right corner, carrying just the arrow.
 * Same border and radius as the card itself, so it reads as part of the same frame,
 * and it fills with the brand red on hover.
 *
 * Anchored to the corner with position:absolute rather than sitting in the flex flow,
 * which keeps it in exactly the same place on every card no matter how long the
 * description runs.
 */
.card__corner {
    position: absolute;
    /* Flush into the corner: sits on top of the card's own border rather than inset
       from it, so the two edges read as a single frame. */
    right: 0;
    bottom: 0;
    display: grid;
    place-items: center;
    width: 3.4rem;
    height: 3.4rem;
    /* Only the two inner edges: the card's own border already draws the right and
       bottom sides, so repeating them there would double the line weight. */
    border: 0;
    border-top: 1px solid var(--line);
    border-left: 1px solid var(--line);
    /* Hard corners, matching the site's other square controls. */
    border-radius: 0;
    color: var(--ink);
    text-decoration: none;
    transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}

.card__corner svg {
    width: 1.35rem;
    height: 1.35rem;
    transition: transform 0.25s ease;
}

/* Fills with the accent, the arrow leaning out in its direction of travel. */
.card__corner:hover,
.card__corner:focus-visible {
    background: var(--accent);
    border-color: var(--accent);
    color: #fff;
}

.card__corner:hover svg,
.card__corner:focus-visible svg {
    transform: translate(0.1rem, -0.1rem);
}

/*
 * Reserve room for the corner square so a long last line of description cannot run
 * underneath it. Scoped with :has() to cards that actually carry one — .card also
 * styles the news and products listings, which have no corner action.
 */
.card:has(.card__corner) {
    padding-bottom: 4.9rem;
    /* Square corner action against a rounded card would overhang the curve, so the
       bottom-right corner of the card is squared to match it. */
    border-bottom-right-radius: 0;
}

/* --- Arrow button --------------------------------------------------------- */

/*
 * Rectangular outlined button with the same arrow as the cards' corner action, so
 * every "Saber mais" on the site reads as one component. The arrow sits in its own
 * cell divided off from the label, echoing the corner square's boxed glyph.
 */
.arrow-btn {
    position: relative;
    display: inline-grid;
    grid-template-columns: auto auto;
    align-items: stretch;
    border: 1px solid var(--line);
    border-radius: 0;
    color: var(--ink);
    text-decoration: none;
    /* Clips the sweeping fill to the button box. */
    overflow: hidden;
    transition: border-color 0.2s ease, color 0.2s ease;
}

/*
 * The fill sweeps in from the left rather than appearing all at once. A scaled
 * pseudo-element does the work: transform animates on the compositor, where
 * animating width or a gradient position would not.
 *
 * It sits behind the label and glyph, which are lifted with z-index.
 */
.arrow-btn::before {
    content: "";
    position: absolute;
    inset: 0;
    background: var(--accent);
    transform: scaleX(0);
    transform-origin: left center;
    transition: transform 0.32s cubic-bezier(0.45, 0.05, 0.3, 1);
}

.arrow-btn:hover::before,
.arrow-btn:focus-visible::before {
    transform: scaleX(1);
}

.arrow-btn__label {
    display: grid;
    /* Above the sweeping fill in ::before. */
    position: relative;
    z-index: 1;
    place-items: center;
    /* Right padding trimmed to a small gap: the arrow follows the text directly. */
    padding: 0.75rem 0.55rem 0.75rem 1.15rem;
    font-family: var(--font-technical);
    font-size: 0.8rem;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

/* The glyph sits directly after the label, no divider. */
.arrow-btn__icon {
    display: grid;
    position: relative;
    z-index: 1;
    place-items: center;
    padding-right: 1.15rem;
}

.arrow-btn__icon svg {
    width: 1.1rem;
    height: 1.1rem;
    transition: transform 0.25s ease;
}

.arrow-btn:hover,
.arrow-btn:focus-visible {
    border-color: var(--accent);
    color: #fff;
}

.arrow-btn:hover .arrow-btn__icon svg,
.arrow-btn:focus-visible .arrow-btn__icon svg {
    transform: translate(0.1rem, -0.1rem);
}

/*
 * The feature card's read-more pill is a span with pointer-events:none, so it can
 * never match :hover itself — the sweep has to be driven by the card link around it.
 */
.news-card__link:hover .news-card__cta::before,
.news-card__link:focus-visible .news-card__cta::before {
    transform: scaleX(1);
}

.news-card__link:hover .news-card__cta svg,
.news-card__link:focus-visible .news-card__cta svg {
    transform: translate(0.1rem, -0.1rem);
}

/* --- Pager --------------------------------------------------------------- */

/*
 * Numbered pagination, square-cornered like the site's other controls. Real links, so
 * it works without JS; the current page is a span rather than a link because there is
 * nowhere for it to go.
 */
.pager {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
    margin-top: clamp(2rem, 1.5rem + 2vw, 3.5rem);
}

.pager__list {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    list-style: none;
    margin: 0;
    padding: 0;
}

.pager__page,
.pager__step {
    display: grid;
    place-items: center;
    min-width: 2.5rem;
    height: 2.5rem;
    padding-inline: 0.5rem;
    border: 1px solid var(--line);
    font-family: var(--font-technical);
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--ink-soft);
    text-decoration: none;
    transition: border-color 0.2s ease, background-color 0.2s ease, color 0.2s ease;
}

.pager__step svg {
    width: 1rem;
    height: 1rem;
}

a.pager__page:hover,
a.pager__step:hover,
a.pager__page:focus-visible,
a.pager__step:focus-visible {
    border-color: var(--accent);
    color: var(--accent);
}

/* Current page: filled, matching the language switch's active state. */
.pager__page.is-current {
    background: var(--accent);
    border-color: var(--accent);
    color: #fff;
}

/* Ends of the range: shown but inert, so the control keeps its width. */
.pager__step.is-disabled {
    opacity: 0.35;
    color: var(--ink-faint);
}

@media (max-width: 560px) {
    /* Non-current numbers give way when space is tight, but the current one stays so
       the visitor still knows where they are. */
    .pager__list li:not(:has(.is-current)) { display: none; }
}

/* --- Sector band --------------------------------------------------------- */

/*
 * Four panels butted together and bled to the viewport edges: no container, no gap, no
 * radius. Each panel carries its own tint so the row reads as one banded block rather
 * than four cards floating on white.
 */
.sector-band__grid {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    /* No gap: the panels share edges. */
    gap: 0;
}

.sector-panel {
    display: flex;
    flex-direction: column;
    /* Equal heights regardless of how long each summary runs. */
    min-height: clamp(26rem, 20rem + 18vw, 34rem);
    overflow: hidden;
}

/*
 * Four steps from near-white to ink, so the band has a gradient across it. The last
 * panel is dark and flips its text to white.
 */
.sector-panel--0 { background: var(--paper); }
.sector-panel--1 { background: var(--paper-tint); }
.sector-panel--2 { background: #e8e7e5; }
.sector-panel--3 { background: var(--ink); color: #fff; }

.sector-panel__image {
    width: 100%;
    aspect-ratio: 4 / 3;
    object-fit: cover;
    /* Greyscale keeps four different photographs from fighting each other across the
       band; colour returns on hover. */
    filter: grayscale(1);
    transition: filter 0.45s ease, transform 0.45s ease;
}

.sector-panel:hover .sector-panel__image {
    filter: grayscale(0);
    transform: scale(1.04);
}

.sector-panel__body {
    display: flex;
    flex-direction: column;
    flex-grow: 1;
    padding: clamp(1.5rem, 1rem + 1.5vw, 2.25rem);
}

.sector-panel__index {
    font-family: var(--font-technical);
    font-size: 0.74rem;
    font-weight: 600;
    letter-spacing: 0.16em;
    color: var(--accent);
    margin: 0 0 0.75rem;
}

.sector-panel__title {
    font-size: var(--step-2);
    margin: 0 0 0.6rem;
}

.sector-panel__text {
    color: var(--ink-soft);
    font-size: 0.95rem;
    line-height: 1.55;
    margin: 0;
}

/* Dark panel: the muted greys would disappear against the ink. */
.sector-panel--3 .sector-panel__text { color: rgba(255, 255, 255, 0.75); }
.sector-panel--3 .arrow-btn { border-color: rgba(255, 255, 255, 0.4); color: #fff; }

/*
 * The action sits at the foot of the panel. margin-top:auto pushes it down so the four
 * buttons line up however long each summary runs, and the summary carries the gap above
 * it -- max() cannot take 'auto', so the spacing has to live on the sibling.
 */
.sector-panel__text { margin-bottom: clamp(1.5rem, 1rem + 2vw, 2.5rem); }

.sector-panel__cta {
    margin-top: auto;
    align-self: flex-start;
}

@media (max-width: 1100px) {
    .sector-band__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .sector-panel { min-height: 0; }
}

@media (max-width: 640px) {
    .sector-band__grid { grid-template-columns: 1fr; }
}

/* --- Sector model stages -------------------------------------------------- */

/*
 * The shared canvas covers the whole band and is drawn into per panel via the scissor
 * test, so it must sit above the panel backgrounds but below the text.
 */
.sector-band { position: relative; }

.sector-band__canvas {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    pointer-events: none;
    z-index: 1;
}

/* The rectangle a panel's model is rendered into; also holds the fallback image. */
/*
 * The rectangle the shared canvas draws a panel's model into. The canvas is a sibling
 * covering the whole band, not a child, so this element only supplies the geometry the
 * scissor rectangle is measured from -- overflow clipping applies to the fallback
 * photograph, never to the model.
 */
.sector-panel__stage {
    position: relative;
    width: 100%;
    aspect-ratio: 4 / 3;
    overflow: hidden;
}

/* No fade needed: a panel with a model renders no image at all. */
.sector-panel__stage .sector-panel__image {
    width: 100%;
    height: 100%;
    object-fit: cover;
    filter: grayscale(1);
    transition: opacity 0.6s ease, filter 0.45s ease, transform 0.45s ease;
}

/*
 * Once a model is on screen its panel's image fades out, leaving the model against the
 * panel tint. The image stays in the DOM as the fallback for every skipped case
 * (reduced-motion, narrow screens, no WebGL, failed load).
 */

/* Text must stay above the canvas. */
.sector-panel__body { position: relative; z-index: 2; }

/* --- Range cards (products landing) -------------------------------------- */

/*
 * The homepage card, scaled up for the products landing page: taller, more generous
 * padding, a numbered index and a rule above the lockup. Same structure and the same
 * corner action, so the two pages read as one system rather than two designs.
 */
/*
 * Centred on this page: the lockups are themselves centred artwork, so a left-aligned
 * column left them visually off-axis. The action is a labelled button in the flow
 * rather than the homepage's corner square, so no bottom padding is reserved.
 */
.card--range {
    /* Lower floor now that the button is not pushed to the foot: a tall minimum
       would leave dead space below it instead. */
    min-height: clamp(13rem, 11rem + 5vw, 16rem);
    padding: clamp(1.75rem, 1.25rem + 1.5vw, 2.75rem);
    align-items: center;
    text-align: center;
}

.card__index {
    font-family: var(--font-technical);
    font-size: 0.74rem;
    font-weight: 600;
    letter-spacing: 0.16em;
    color: var(--accent);
    margin: 0 0 1.1rem;
    padding-bottom: 1.1rem;
    /* Hairline rule the width of the card, separating the number from the lockup. */
    border-bottom: 1px solid var(--line);
    align-self: stretch;
    text-align: center;
}

/* Larger lockup than the homepage's, matching the bigger card. */
.card--range .card__logo {
    height: 3rem;
    max-width: min(100%, 17rem);
    object-position: center;
}

.card--range .card__title--range {
    min-height: 3.4rem;
    justify-content: center;
}

.card--range .card__text {
    font-size: var(--step-0);
    line-height: 1.6;
    max-width: 46ch;
    /* Centres the measure itself, not just the text inside it. */
    margin-inline: auto;
}

/*
 * The button follows the description closely rather than being pushed to the card's
 * foot: margin-top:auto would absorb the whole leftover height of the card, which made
 * the gap above it unreasonably large and unaffected by any spacing value.
 */
.card__action {
    align-self: center;
}

.card--range .card__text { margin-bottom: clamp(0.75rem, 0.5rem + 0.6vw, 1.1rem); }

/* --- Sustainability: CEO quote -------------------------------------------- */

.sus-quote .container {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: clamp(2rem, 1rem + 4vw, 5rem);
    align-items: start;
}

.sus-quote__figure {
    margin: 0;
    /* A hairline in the neutral line colour: the accent edge was too loud beside the
       other red details on this page. */
    border-left: 1px solid var(--line);
    padding-left: clamp(1.25rem, 1rem + 1.5vw, 2rem);
}

.sus-quote__text {
    margin: 0;
    font-size: var(--step-2);
    font-weight: 300;
    line-height: 1.35;
    color: var(--ink);
    text-wrap: pretty;
}

.sus-quote__cite {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    margin-top: 1.5rem;
    font-style: normal;
}

.sus-quote__name {
    font-family: var(--font-technical);
    font-size: 0.85rem;
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
}

.sus-quote__role { font-size: 0.85rem; color: var(--ink-faint); }

.sus-quote__body { color: var(--ink-soft); }
.sus-quote__body p + p { margin-top: 1.1rem; }

/* --- Sustainability: the three targets ------------------------------------ */

/* White band; the tint moves onto each column instead. */
.sus-targets { background: var(--paper); }

.sus-targets__grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
    /* Tighter than before: the columns are filled panels now, so a wide gap would leave
       them floating apart rather than reading as one row. */
    gap: clamp(1rem, 0.75rem + 1vw, 1.5rem);
}

/*
 * Each column is its own tinted panel on the white band, with the accent rule along its
 * top edge. Padded on all sides now that it is a filled block rather than a bare column.
 */
.sus-target {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    background: var(--paper-tint);
    padding: clamp(1.5rem, 1.25rem + 1vw, 2.25rem);
}

.sus-target__icon {
    height: 3.5rem;
    width: auto;
    margin-bottom: 1.25rem;
}

/*
 * Set large and tight: these are the page's headline numbers, and the source presents
 * them as two stacked words ("LESS / ENERGY").
 */
.sus-target__title {
    font-size: var(--step-3);
    line-height: 1.05;
    margin: 0 0 0.75rem;
    max-width: 10ch;
}

.sus-target__text {
    color: var(--ink-soft);
    margin: 0;
    font-size: 0.95rem;
    line-height: 1.55;
}

/* --- Sustainability: circular principles ---------------------------------- */

.sus-principles__eyebrow {
    font-family: var(--font-technical);
    font-size: 0.74rem;
    font-weight: 600;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--accent);
    margin: 0 0 0.75rem;
}

.sus-principles__title {
    font-size: var(--step-3);
    margin: 0;
    text-wrap: balance;
}

/* --- Sustainability: resource families ------------------------------------ */

/*
 * Dark band: the bioplastic families are the page's turning point.
 *
 * The brand mark is tiled across it as a faint texture. It is applied to a ::before
 * layer rather than to the element itself so the opacity can be dialled right down
 * without fading the text on top -- opacity on the section would take its children
 * with it.
 */
.sus-resources {
    background: var(--ink);
    color: #fff;
}


/* Two equal columns, butted together, filling the block edge to edge. */
.sus-resources__grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0;
}

/*
 * Each family is a photograph with its copy anchored to the foot. The image is a real
 * <img> under the text rather than a CSS background, so it can be lazy-loaded and
 * object-fit cropped; a gradient scrim between the two guarantees the text stays legible
 * whatever the photograph does.
 */
.sus-resource {
    position: relative;
    isolation: isolate;
    display: flex;
    align-items: flex-end;
    min-height: clamp(20rem, 16rem + 14vw, 30rem);
    overflow: hidden;
    background: #201e1d;
}

.sus-resource__media {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    z-index: -2;
    transition: transform 0.6s ease;
}

.sus-resource:hover .sus-resource__media { transform: scale(1.04); }

/* Scrim: dark at the foot where the text sits, clearing towards the top. */
.sus-resource::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(
        180deg,
        rgba(22, 20, 19, 0.15) 0%,
        rgba(22, 20, 19, 0.55) 55%,
        rgba(22, 20, 19, 0.88) 100%
    );
    z-index: -1;
}

/*
 * Generous padding: with no container around the grid, this is the only thing keeping
 * the copy off the viewport edge.
 */
.sus-resource__body {
    padding: clamp(1.75rem, 1.25rem + 2.5vw, 3.5rem);
}

.sus-resource__icon {
    height: 2.8rem;
    width: auto;
    margin-bottom: 1rem;
    /* The supplied icons are dark line art; invert them onto the photograph. */
    filter: invert(1) brightness(1.8);
}

.sus-resource__title {
    font-size: var(--step-2);
    color: #fff;
    margin: 0 0 0.6rem;
    max-width: 16ch;
}

.sus-resource__text {
    color: rgba(255, 255, 255, 0.85);
    margin: 0;
    line-height: 1.6;
    max-width: 38ch;
}

@media (max-width: 700px) {
    .sus-resources__grid { grid-template-columns: 1fr; }
    .sus-resource { min-height: 16rem; }
}

/* --- Sustainability: long-form detail ------------------------------------- */

.sus-detail__inner {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.6fr);
    gap: clamp(2rem, 1rem + 4vw, 5rem);
    align-items: start;
}

/* The heading column sticks while the body scrolls past it on tall viewports. */
.sus-detail__lead {
    position: sticky;
    top: calc(var(--header-offset) + 1rem);
}

.sus-detail__title {
    font-size: var(--step-3);
    margin: 0 0 1rem;
    text-wrap: balance;
}

.sus-detail__standfirst {
    font-size: var(--step-1);
    color: var(--ink-soft);
    margin: 0;
}

.sus-detail__body h3 {
    font-size: var(--step-1);
    margin-top: 2.5rem;
}

@media (max-width: 860px) {
    .sus-quote .container,
    .sus-detail__inner {
        grid-template-columns: 1fr;
    }

    /* Sticky needs a scroll axis to work against; in one column there is none. */
    .sus-detail__lead { position: static; }

    .sus-target__title { max-width: none; }
}

/* --- Sustainability: circular principles (boxed layout) ------------------- */

/*
 * Title block on the left, the six principles as outlined boxes on the right -- the
 * reference's arrangement. The boxes share edges (gap:0 plus a single border side each)
 * so the grid reads as one connected diagram rather than six detached cards.
 */
.sus-principles__layout {
    display: grid;
    grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.6fr);
    gap: clamp(2rem, 1rem + 4vw, 4.5rem);
    align-items: center;
}

.sus-principles__head {
    max-width: 30ch;
    /* Overrides the stacked-layout margin from the base rule. */
    margin-bottom: 0;
}

.sus-principles__lead {
    color: var(--ink-soft);
    font-size: 0.95rem;
    line-height: 1.6;
    margin: 1rem 0 0;
}

.sus-principles__list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0;
}

.sus-principle {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    padding: clamp(1.25rem, 1rem + 1vw, 1.75rem);
    /* One border per box; neighbours supply the shared edges. */
    border: 1px solid var(--line);
    /* Collapses the doubled line where two boxes meet. */
    margin: 0 -1px -1px 0;
    background: var(--paper);
    transition: background-color 0.25s ease, color 0.25s ease;
}

/* Inverted box, as in the reference: the eye enters the grid here. */
.sus-principle--feature {
    background: var(--ink);
    border-color: var(--ink);
    color: #fff;
    /* Above its neighbours so its dark edge is unbroken. */
    position: relative;
    z-index: 1;
}

/* Square icon tile above the label. */
.sus-principle__icon {
    display: grid;
    place-items: center;
    width: 2.1rem;
    height: 2.1rem;
    margin-bottom: 1rem;
    background: var(--ink);
    color: #fff;
}

.sus-principle--feature .sus-principle__icon {
    background: #fff;
    color: var(--ink);
}

.sus-principle__icon svg {
    width: 1.15rem;
    height: 1.15rem;
}

.sus-principle__index {
    font-family: var(--font-technical);
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.16em;
    color: var(--accent);
    margin: 0 0 0.45rem;
}

.sus-principle--feature .sus-principle__index { color: #fff; opacity: 0.6; }

.sus-principle__title {
    font-size: 1.02rem;
    margin: 0 0 0.5rem;
    text-wrap: balance;
}

.sus-principle__text {
    color: var(--ink-soft);
    font-size: 0.88rem;
    line-height: 1.55;
    margin: 0;
}

.sus-principle--feature .sus-principle__text { color: rgba(255, 255, 255, 0.72); }

/* Hover fills any box, so the whole grid feels responsive rather than only the first. */
.sus-principle:not(.sus-principle--feature):hover {
    background: var(--paper-tint);
}

@media (max-width: 1000px) {
    /* Title above the grid once the two columns get too narrow to sit side by side. */
    .sus-principles__layout { grid-template-columns: 1fr; }
    .sus-principles__head { max-width: 40ch; }
}

@media (max-width: 560px) {
    .sus-principles__list { grid-template-columns: 1fr; }
}

/* --- Sector stepper (mobile) ---------------------------------------------- */

/* Desktop navigates by wheel and hover, so the buttons are not needed there. */
.scroller__step { display: none; }

@media (max-width: 780px) {
    /*
     * One sector at a time, flanked by two arrows. The full list was long enough to push
     * the rest of the hero off the screen, and on touch there is no hover to reveal a
     * caption -- so the stepper shows a single item and the buttons move between them.
     */
    /*
     * The track is the grid: a narrow arrow either side of the selected sector. It has to
     * be the grid rather than the list, because the buttons are siblings of the list --
     * grid-column on them only works if they share a parent with it.
     */
    .scroller__track {
        display: grid;
        grid-template-columns: 1.9rem 1fr 1.9rem;
        align-items: stretch;
        border-block: 1px solid var(--line);
    }

    /*
     * A sliding track rather than a swap. The items sit in one row and the whole row is
     * translated by --scroller-offset (set by hero-scroller.js), so the outgoing sector
     * leaves as the incoming one arrives. display:none on the inactive items would give a
     * hard cut, which is what this replaces.
     */
    .scroller__list {
        grid-column: 2;
        grid-row: 1;
        display: flex;
        flex-direction: row;
        /* The viewport: one item wide, the rest hidden either side. */
        overflow: hidden;
    }

    .scroller__list > li {
        /* Every item is exactly the track's width, so one is framed at a time. */
        flex: 0 0 100%;
        min-width: 0;
        transform: translateX(calc(var(--scroller-offset, 0) * -100%));
        transition: transform 0.45s cubic-bezier(0.45, 0.05, 0.3, 1);
    }

    /*
     * Applied for a single frame when the list wraps between the last sector and the
     * first. Without it the row would slide the full length of the list; with it the far
     * item is simply in place when it appears.
     */
    .scroller__list.is-jumping > li { transition: none; }

    .scroller__item {
        grid-template-columns: 2rem 1fr;
        align-items: center;
        border-top: 0;
        padding-block: 1.15rem;
        /* Steps in from the arrows either side. */
        padding-inline: 0.75rem;
    }

    /* The single visible row is the selected one, so it always reads as active. */
    .scroller__label { font-size: 1.15rem; color: var(--ink); }
    .scroller__number { color: var(--accent); }

    .scroller__caption {
        max-height: 4rem;
        opacity: 1;
        margin-top: 0.2rem;
    }

    /*
     * The arrows: full height of the row, narrow horizontally, square-cornered like the
     * site's other controls.
     */
    .scroller__step {
        display: grid;
        place-items: center;
        grid-row: 1;
        width: 100%;
        padding: 0;
        /* Solid accent with a white glyph: on a phone these are the only way through the
           list, so they should read as buttons rather than as quiet outlines. */
        background: var(--accent);
        border: 0;
        border-radius: 0;
        color: #fff;
        cursor: pointer;
        transition: background-color 0.2s ease;
    }

    .scroller__step--prev { grid-column: 1; grid-row: 1; }
    .scroller__step--next { grid-column: 3; grid-row: 1; }

    .scroller__step svg {
        width: 0.7rem;
        height: 1.4rem;
    }

    /* Pressed: the footer's ink, so the feedback is a colour the site already uses. */
    .scroller__step:hover,
    .scroller__step:focus-visible,
    .scroller__step:active {
        background: var(--ink);
        color: #fff;
    }

}
