/**
 * BellsWellOrg Design System — foundation
 *
 * Tokens, fonts, base element styles and layout primitives.
 * Derived from the Figma file "Bellswellorg-v1":
 *   - Design System - Typography (node 163:11)
 *   - Desktop - HiFi           (node 266:3607)
 *
 * Loaded after style.css, so it intentionally overrides Underscores defaults.
 *
 * @package BellsWellOrg
 */

/*--------------------------------------------------------------
>>> TABLE OF CONTENTS:
----------------------------------------------------------------
# Fonts
# Tokens
	- Color
	- Typography
	- Spacing & layout
	- Motion, radius, elevation
# Base
# Layout primitives
# Typography utilities
--------------------------------------------------------------*/

/*--------------------------------------------------------------
# Fonts
--------------------------------------------------------------*/

/* Kanit — Light 300 */
@font-face {
	font-family: "Kanit";
	font-style: normal;
	font-weight: 300;
	font-display: swap;
	src: url("../fonts/kanit-300-latin-ext.woff2") format("woff2");
	unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
	font-family: "Kanit";
	font-style: normal;
	font-weight: 300;
	font-display: swap;
	src: url("../fonts/kanit-300-latin.woff2") format("woff2");
	unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Kanit — Medium 500 */
@font-face {
	font-family: "Kanit";
	font-style: normal;
	font-weight: 500;
	font-display: swap;
	src: url("../fonts/kanit-500-latin-ext.woff2") format("woff2");
	unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
	font-family: "Kanit";
	font-style: normal;
	font-weight: 500;
	font-display: swap;
	src: url("../fonts/kanit-500-latin.woff2") format("woff2");
	unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Kanit — SemiBold 600 */
@font-face {
	font-family: "Kanit";
	font-style: normal;
	font-weight: 600;
	font-display: swap;
	src: url("../fonts/kanit-600-latin-ext.woff2") format("woff2");
	unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
	font-family: "Kanit";
	font-style: normal;
	font-weight: 600;
	font-display: swap;
	src: url("../fonts/kanit-600-latin.woff2") format("woff2");
	unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Kanit — Bold 700 */
@font-face {
	font-family: "Kanit";
	font-style: normal;
	font-weight: 700;
	font-display: swap;
	src: url("../fonts/kanit-700-latin-ext.woff2") format("woff2");
	unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
	font-family: "Kanit";
	font-style: normal;
	font-weight: 700;
	font-display: swap;
	src: url("../fonts/kanit-700-latin.woff2") format("woff2");
	unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Saira — Regular 400 (the section subtitle) */
@font-face {
	font-family: "Saira";
	font-style: normal;
	font-weight: 400;
	font-display: swap;
	src: url("../fonts/saira-400-latin-ext.woff2") format("woff2");
	unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
	font-family: "Saira";
	font-style: normal;
	font-weight: 400;
	font-display: swap;
	src: url("../fonts/saira-400-latin.woff2") format("woff2");
	unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Saira — Bold 700 (display only) */
@font-face {
	font-family: "Saira";
	font-style: normal;
	font-weight: 700;
	font-display: swap;
	src: url("../fonts/saira-700-latin-ext.woff2") format("woff2");
	unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
	font-family: "Saira";
	font-style: normal;
	font-weight: 700;
	font-display: swap;
	src: url("../fonts/saira-700-latin.woff2") format("woff2");
	unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/*--------------------------------------------------------------
# Tokens
--------------------------------------------------------------*/

:root {

	/* ---- Color -------------------------------------------------
	 * Figma variables. Only --bw-primary and --bw-grey-600 are
	 * exclusive to the HiFi page; the greys also exist in LoFi.
	 *
	 * Two of them are NOT the Figma value, because the Figma value
	 * cannot carry text (accessibility audit, 2026-08-12):
	 *
	 * - Brand red is #fd0000 in the design file. On white that is
	 *   4.05:1, and under off-white 4.02:1 — both short of the 4.5:1
	 *   SC 1.4.3 asks of body text. It is applied here at #d40000,
	 *   the darkened brand red the design already carried as the
	 *   button hover, which reads 5.53:1 on white and 5.48:1 against
	 *   --bw-primary-contrast. Every red on the site is text or a
	 *   fill with text on it, so there is no second, brighter red:
	 *   two reds a shade apart on one page read as a bug, and no
	 *   surface here is large enough for the vivid one to earn.
	 *
	 * - --bw-grey-600 (#909090) is 3.19:1 on white. It stays as the
	 *   Figma record and for non-text use; muted TEXT resolves to
	 *   --bw-grey-700 instead.
	 *
	 * The -on-grey pair is for the two surfaces that are themselves
	 * grey. Both of the values above clear 4.5:1 on white and miss it
	 * on #e5e5e5 / #d0d0d0, so those surfaces re-point --bw-accent
	 * and --bw-text-muted at these — see "Grey surfaces" below.
	 */
	--bw-primary: #d40000;
	--bw-primary-hover: #b00000;
	--bw-primary-contrast: #fefefe;
	--bw-primary-on-grey: #b00000;

	--bw-black: #000;
	--bw-white: #fff;

	--bw-grey-600: #909090;
	--bw-grey-700: #6b6b6b;
	--bw-grey-800: #555;
	--bw-grey-300: #e5e5e5;
	--bw-border: #b5b4b4;
	--bw-picture-frame: #d0d0d0;

	/* Semantic aliases — prefer these in component CSS. */
	--bw-text: var(--bw-black);
	--bw-text-muted: var(--bw-grey-700);
	--bw-text-inverse: var(--bw-primary-contrast);
	--bw-surface: var(--bw-white);
	--bw-surface-alt: var(--bw-grey-300);
	--bw-surface-inverse: var(--bw-black);
	--bw-accent: var(--bw-primary);
	--bw-rule: var(--bw-border);

	/* ---- Typography --------------------------------------------
	 * Sizes are fluid: clamp( mobile, design-viewport vw, desktop ).
	 * The vw term is size / 1728 * 100, so at a 1728px viewport every
	 * value lands exactly on the Figma spec.
	 *
	 * Figma reports letter-spacing as a percentage of the font size,
	 * so it is expressed here in em and scales with the clamp.
	 */
	--bw-font-display: "Saira", "Kanit", system-ui, sans-serif;
	--bw-font-heading: "Kanit", system-ui, sans-serif;
	--bw-font-body: "Kanit", system-ui, sans-serif;

	--bw-weight-light: 300;
	--bw-weight-medium: 500;
	--bw-weight-semibold: 600;
	--bw-weight-bold: 700;

	/* Display — Saira Bold 84 / 1.04 / 0 */
	--bw-size-display: clamp(2.5rem, 4.861vw, 5.25rem);
	--bw-lh-display: 1.04;

	/* Headings — Kanit SemiBold */
	--bw-size-h1: clamp(2.25rem, 3.935vw, 4.25rem);      /* 68 / 72  / 0    */
	--bw-lh-h1: 1.0588;
	--bw-ls-h1: 0;

	--bw-size-h2: clamp(1.875rem, 3.125vw, 3.375rem);    /* 54 / 64  / 1%   */
	--bw-lh-h2: 1.1852;
	--bw-ls-h2: 0.01em;

	--bw-size-h3: clamp(1.625rem, 2.546vw, 2.75rem);     /* 44 / 56  / 2%   */
	--bw-lh-h3: 1.2727;
	--bw-ls-h3: 0.02em;

	--bw-size-h4: clamp(1.5rem, 2.025vw, 2.1875rem);     /* 35 / 48  / 2%   */
	--bw-lh-h4: 1.3714;
	--bw-ls-h4: 0.02em;

	--bw-size-h5: clamp(1.375rem, 1.62vw, 1.75rem);      /* 28 / 40  / 2%   */
	--bw-lh-h5: 1.4286;
	--bw-ls-h5: 0.02em;

	--bw-size-h6: clamp(1.25rem, 1.273vw, 1.375rem);     /* 22 / 28  / 2%   */
	--bw-lh-h6: 1.2727;
	--bw-ls-h6: 0.02em;

	/* Section — Kanit Medium 48 / 64 / 0 */
	--bw-size-section-title: clamp(1.75rem, 2.778vw, 3rem);
	--bw-lh-section-title: 1.3333;

	/* Section label — Kanit SemiBold 24, tracking 12% (the red eyebrow) */
	--bw-size-section-label: clamp(1rem, 1.389vw, 1.5rem);
	--bw-lh-section-label: 1.5;
	--bw-ls-section-label: 0.12em;

	/* Card label — Kanit Medium 16, tracking 8% */
	--bw-size-card-label: 1rem;
	--bw-lh-card-label: 1.5;
	--bw-ls-card-label: 0.08em;

	/*
	 * Below the 1rem floor the rest of the scale sits on. These two exist
	 * because a label set against a value has to read as the annotation, and
	 * because a control riding inside a table cell cannot take a full-size one.
	 */

	/* Small label — a step under the card label, for label/value pairs */
	--bw-size-label-sm: 0.875rem;
	--bw-lh-label-sm: 1.5;
	--bw-ls-label-sm: 0.08em;

	/* Micro — pills and controls that sit inside a row of content */
	--bw-size-micro: 0.6875rem;
	--bw-lh-micro: 1.45;
	--bw-ls-micro: 0.05em;

	/* Subtitle — Kanit Light 27 / 34 / 0 */
	--bw-size-subtitle: clamp(1.25rem, 1.5625vw, 1.6875rem);
	--bw-lh-subtitle: 1.2593;

	/* Body — Kanit Light, tracking 2% (Large is 0) */
	--bw-size-body-lg: 1.25rem;   /* 20 / 32 / 0  */
	--bw-lh-body-lg: 1.6;
	--bw-size-body-md: 1.125rem;  /* 18 / 24 / 2% */
	--bw-lh-body-md: 1.3333;
	--bw-size-body-sm: 1rem;      /* 16 / 24 / 2% */
	--bw-lh-body-sm: 1.5;
	--bw-ls-body: 0.02em;

	/* Nav links — Kanit Medium 16 / 24 / 2% */
	--bw-size-nav: 1rem;
	--bw-lh-nav: 1.5;
	--bw-ls-nav: 0.02em;

	/* Button text — Kanit Medium 24 / 34 / 2% */
	--bw-size-button: clamp(1.125rem, 1.389vw, 1.5rem);
	--bw-lh-button: 1.4167;
	--bw-ls-button: 0.02em;

	/* ---- Spacing & layout --------------------------------------
	 * 8px base. Named steps map to measurements that recur in the
	 * Figma frames (40px block padding, 32px grid gutter, 144px page
	 * gutter, 88px section padding).
	 */
	--bw-space-1: 0.25rem;   /*   4 */
	--bw-space-2: 0.5rem;    /*   8 */
	--bw-space-3: 0.75rem;   /*  12 */
	--bw-space-4: 1rem;      /*  16 */
	--bw-space-5: 1.5rem;    /*  24 */
	--bw-space-6: 2rem;      /*  32 — grid gutter */
	--bw-space-7: 2.5rem;    /*  40 — block inset */
	--bw-space-8: 3rem;      /*  48 */
	--bw-space-9: 3.5rem;    /*  56 */
	--bw-space-10: 4rem;     /*  64 */
	--bw-space-11: 5rem;     /*  80 */
	--bw-space-12: 5.5rem;   /*  88 — section padding */
	--bw-space-13: 7.5rem;   /* 120 */
	--bw-space-14: 9rem;     /* 144 — page gutter */

	/* Canvas is 1728 wide with 144px gutters → 1440 of content. */
	--bw-canvas: 1728px;
	--bw-container: 1440px;

	/**
	 * The news article column. "News - One" (204:529) insets its content 267px
	 * either side of the 1728 canvas rather than the usual 144, giving a
	 * reading column narrower than any other section on the site: 1728 - 267*2.
	 * It is also exactly two 581px images plus the 32px gutter.
	 */
	--bw-article: 1194px;

	/**
	 * Reading measure for long-form body copy.
	 *
	 * The frame runs its paragraphs the full 1113px of the article column,
	 * which at the body size is ~130 characters a line. That is a lorem block
	 * filling a greyscale LoFi frame, not a measure anyone chose, so it is
	 * capped here — the images still take the full column, which is what makes
	 * the page read as an article rather than a wall. Includes the block inset,
	 * since everything is border-box.
	 */
	--bw-measure: 56rem;
	--bw-gutter: clamp(1.5rem, 8.333vw, 9rem);
	--bw-grid-gutter: clamp(1rem, 1.852vw, 2rem);
	--bw-block-inset: clamp(1rem, 2.315vw, 2.5rem);

	/**
	 * Card grids need more air between rows than between columns — a 32px
	 * gutter both ways runs the rows together once the cards have no surface
	 * of their own. 80px at 1728, set by the Personnel grid and shared with
	 * the News index so listing pages breathe alike.
	 */
	--bw-grid-row-gap: clamp(2.5rem, 4.63vw, 5rem);

	/**
	 * The gap between one full-width section and the next — 180px at 1728.
	 *
	 * The strongest rhythm in the file, and the only measurement all 26 screens
	 * agree on: Home's Frame 103 spaces hero, QHSE, track record, section,
	 * clients, news and footer 180 apart, and every inner page opens banner
	 * (ends 775) → first section (starts 955) on the same 180. Distinct from
	 * --bw-space-12, which is padding *inside* a section; this is between them.
	 *
	 * The 4rem floor is an implementation decision — there is no mobile design.
	 * It keeps the fluid curve alive down to ~614px and stops a phone from
	 * scrolling through 180px of nothing between every band. Two padded
	 * sections used to yield ~96px on a phone, so 64 is close to the rhythm the
	 * site already had there, against sections that are now content-tight.
	 */
	--bw-band-gap: clamp(4rem, 10.417vw, 11.25rem);

	--bw-header-height: 120px;
	--bw-header-height-mobile: 80px;

	/**
	 * Inner-page banner height — one value, with or without a photograph.
	 *
	 * Sized to its content rather than to the Figma frame. The design draws
	 * "Hero 2" at 655px, but 655 plus the 120px navbar fills a laptop viewport,
	 * so every inner page opened looking like a second homepage.
	 *
	 * 440px holds the worst real stack — eyebrow + a two-line h1 + a one-line
	 * subtitle + 88px padding top and bottom = 422px — with room to spare, and
	 * leaves the page's actual content visible on first paint. It is a
	 * min-height, so a longer title still grows the band rather than clipping.
	 */
	--bw-pagehead-height: clamp(17.5rem, 25.46vw, 27.5rem);

	/* ---- Motion, radius, elevation ----------------------------- */
	/**
	 * Corner radius comes in exactly two tiers (design spec, 2026-08-08).
	 * Use these, not raw values — a literal px here is how the two tiers
	 * drifted apart in the first place.
	 *
	 *   control  8px — menu toggle, buttons, link grids, form fields,
	 *                  carousel arrows, social tiles
	 *   surface 12px — section media, section card, news card, gallery,
	 *                  form container, project / asset / partner media
	 *
	 * The rule of thumb: things you click or type into are 8px; panels and
	 * cropped imagery are 12px.
	 */
	--bw-radius-control: 8px;
	--bw-radius-surface: 12px;

	/* ---- Motion ------------------------------------------------
	 * Durations are named for what moves, not for how fast they are, so a
	 * rule reads as an intent: an icon transition is `--bw-duration-icon`
	 * whatever number that turns out to be.
	 *
	 * The scale is the design brief's: 200 / 300 / 500 / 700 / 900. It is
	 * deliberately slower and more graded than a generic fast/slow pair —
	 * heavy industrial subject matter reads better with weight behind it.
	 *
	 * Pick an easing to match the job:
	 *   industrial  general purpose, symmetrical — colour and simple state
	 *   enter       decelerating, for things arriving (panels, reveals)
	 *   exit        accelerating, for things leaving
	 *   mechanical  a little sharper — wipes and underlines
	 *   heavy       long tail, for anything with visual mass
	 */
	--bw-ease-industrial: cubic-bezier(0.4, 0, 0.2, 1);
	--bw-ease-enter: cubic-bezier(0.16, 1, 0.3, 1);
	--bw-ease-exit: cubic-bezier(0.4, 0, 1, 1);
	--bw-ease-mechanical: cubic-bezier(0.45, 0, 0.25, 1);
	--bw-ease-heavy: cubic-bezier(0.22, 1, 0.36, 1);

	--bw-duration-icon: 200ms;      /* icons, small controls, active states */
	--bw-duration-control: 300ms;   /* buttons, links, cards, navigation    */
	--bw-duration-panel: 500ms;     /* dropdowns, tabs, panels              */
	--bw-duration-reveal: 700ms;    /* section reveals, large imagery       */
	--bw-duration-hero: 900ms;      /* restrained hero animation            */

	/* Distances. Kept small on purpose — this is emphasis, not bounce. */
	--bw-lift-control: -2px;
	--bw-lift-card: -6px;
	--bw-reveal-shift: 24px;

	--bw-focus-ring: 2px solid var(--bw-primary);
	--bw-focus-offset: 3px;

	/* ---- Scrim -------------------------------------------------
	 * Overlay that keeps white type legible on a photograph.
	 *
	 * Two layers, because the title sits bottom-left:
	 *   1. bottom-up, doing most of the work where the text actually is;
	 *   2. left-to-right, which is the direction the Figma hero fades.
	 *
	 * Tuned so white text clears 4.5:1 over a bright subject, not just the
	 * dark night shot the design was drawn against. Raise --bw-scrim-strength
	 * on a section whose artwork is unusually light.
	 */
	--bw-scrim-strength: 1;
	--bw-scrim:
		linear-gradient(
			to top,
			rgba(0, 0, 0, calc(0.72 * var(--bw-scrim-strength))) 0%,
			rgba(0, 0, 0, calc(0.45 * var(--bw-scrim-strength))) 38%,
			rgba(0, 0, 0, 0) 78%
		),
		linear-gradient(
			90deg,
			rgba(0, 0, 0, calc(0.5 * var(--bw-scrim-strength))) 0%,
			rgba(0, 0, 0, calc(0.18 * var(--bw-scrim-strength))) 58%,
			rgba(0, 0, 0, 0) 100%
		);

	/*
	 * The same recipe mirrored, for artwork whose text sits at the top rather
	 * than the bottom — currently `.bw-band--top`, the Equipment feature band.
	 *
	 * Not cosmetic. The main layer above reaches zero at 78% up, so type in the
	 * top quarter clears only the horizontal layer: ~0.44 composited against
	 * the banner's ~0.85, which is 3.6:1 over a bright sky. Running the weight
	 * top-down puts it where the type actually is.
	 */
	--bw-scrim-top:
		linear-gradient(
			to bottom,
			rgba(0, 0, 0, calc(0.72 * var(--bw-scrim-strength))) 0%,
			rgba(0, 0, 0, calc(0.45 * var(--bw-scrim-strength))) 38%,
			rgba(0, 0, 0, 0) 78%
		),
		linear-gradient(
			90deg,
			rgba(0, 0, 0, calc(0.5 * var(--bw-scrim-strength))) 0%,
			rgba(0, 0, 0, calc(0.18 * var(--bw-scrim-strength))) 58%,
			rgba(0, 0, 0, 0) 100%
		);
}

@media (max-width: 782px) {

	:root {
		--bw-header-height: var(--bw-header-height-mobile);
	}
}

/*--------------------------------------------------------------
# Base
--------------------------------------------------------------*/

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

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

/**
 * `_s` spaces the post wrapper with `.post, .page { margin: 0 0 1.5em; }` in
 * style.css. body_class() also puts `page` on <body> for a Page, so that rule
 * hits the body too and leaves 1.5em — 27px at the 18px base — of dead space
 * below the footer.
 *
 * A bare `body { margin: 0 }` cannot take it back: a class selector outranks an
 * element one, so the override has to carry the class as well.
 */
body.page,
body.post {
	margin-bottom: 0;
}

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

	html {
		scroll-behavior: auto;
	}

	*,
	*::before,
	*::after {
		animation-duration: 0.01ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: 0.01ms !important;
	}
}

/*--------------------------------------------------------------
# Scroll reveal
--------------------------------------------------------------*/

/**
 * Sections fade and rise as they enter the viewport.
 *
 * The hidden state is applied only once JS has armed the element, so content
 * is never invisible to a visitor without JavaScript — the failure mode of
 * every reveal that starts at `opacity: 0` in the stylesheet.
 *
 * js/motion.js adds `is-armed`, then `is-visible` on intersection.
 */
.bw-reveal.is-armed {
	opacity: 0;
	transform: translateY(var(--bw-reveal-shift));
	transition:
		opacity var(--bw-duration-reveal) var(--bw-ease-enter),
		transform var(--bw-duration-reveal) var(--bw-ease-enter);
	will-change: opacity, transform;
}

.bw-reveal.is-armed.is-visible {
	opacity: 1;
	transform: none;
	will-change: auto;
}

/**
 * Card grids inside a revealed section cascade rather than arriving as a slab.
 * Targeting `.bw-grid` children needs no extra markup — every card grid in the
 * theme already uses that class. The delay is capped so a long list never
 * leaves the reader waiting.
 *
 * A keyframe animation, not a transition, and that is load-bearing. `is-armed`
 * is never removed — the reveal is one-shot — so anything declared here outranks
 * the cards themselves forever: three classes to one. A settled
 * `transform: none` would beat every card's `:hover` lift, and the `transition`
 * shorthand would replace the card's own transition-property list, so its colour
 * change would snap while a child element's own transition still eased. That was
 * a real bug on the Track Record and Project cards; it never showed in the block
 * editor because `bellswellorg_reveal_blocks()` skips `is_admin()`, so the class
 * is only ever on the front end.
 *
 * An animation owns `opacity` and `transform` only while it plays and hands them
 * straight back when it ends. `backwards` fill covers the staggered delay, when
 * the animation exists but has not started — without it a delayed card would
 * flash in at full opacity before dropping back to start.
 */
.bw-reveal.is-armed .bw-grid > * {
	opacity: 0;
}

.bw-reveal.is-armed.is-visible .bw-grid > * {
	animation: bw-reveal-in var(--bw-duration-reveal) var(--bw-ease-enter) backwards;
	opacity: 1;
}

.bw-reveal.is-armed.is-visible .bw-grid > :nth-child(2) { animation-delay: 90ms; }
.bw-reveal.is-armed.is-visible .bw-grid > :nth-child(3) { animation-delay: 180ms; }
.bw-reveal.is-armed.is-visible .bw-grid > :nth-child(4) { animation-delay: 270ms; }
.bw-reveal.is-armed.is-visible .bw-grid > :nth-child(n+5) { animation-delay: 360ms; }

@keyframes bw-reveal-in {

	from {
		opacity: 0;
		transform: translateY(var(--bw-reveal-shift));
	}

	to {
		opacity: 1;
		transform: translateY(0);
	}
}

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

	/*
	 * Motion off, but the content must still appear. Neutralise the offset
	 * rather than relying on a near-zero transition to carry it. js/motion.js
	 * already bails before arming anything; this covers the preference changing
	 * after load, when the classes are on the page already.
	 */
	.bw-reveal.is-armed {
		opacity: 1;
		transform: none;
		transition: none;
	}

	/*
	 * Cancel the animation rather than fighting it — with it off, the cards fall
	 * back to their own rules, which is exactly the resting state wanted here.
	 */
	.bw-reveal.is-armed .bw-grid > * {
		animation: none;
		opacity: 1;
	}
}

body {
	background-color: var(--bw-surface);
	color: var(--bw-text);
	font-family: var(--bw-font-body);
	font-size: var(--bw-size-body-md);
	font-weight: var(--bw-weight-light);
	line-height: var(--bw-lh-body-md);
	letter-spacing: var(--bw-ls-body);
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
}

/**
 * `overflow-wrap` is the reflow guard (SC 1.4.10). Headings here are set at
 * display sizes in uppercase, and the clamps floor at 40-68px: a single word
 * like CONSTRUCTION measures ~290px, wider than a card has to give at a 320px
 * viewport. Left unbroken it took the whole document with it — the failure on
 * Home and both Services templates in the audit of 2026-08-12 (A11Y-03).
 *
 * It fires only when one word cannot fit on a line by itself, so nothing
 * breaks mid-word at any width where the design has room. Set on the base
 * rule rather than per component so a heading added later inherits the guard.
 */
h1,
h2,
h3,
h4,
h5,
h6 {
	color: var(--bw-text);
	font-family: var(--bw-font-heading);
	font-weight: var(--bw-weight-semibold);
	margin: 0 0 var(--bw-space-5);
	overflow-wrap: break-word;
}

/* The display-size classes that are not headings — the band's pull-quote. */
.bw-section-title,
.bw-display {
	overflow-wrap: break-word;
}

h1 {
	font-size: var(--bw-size-h1);
	line-height: var(--bw-lh-h1);
	letter-spacing: var(--bw-ls-h1);
}

h2 {
	font-size: var(--bw-size-h2);
	line-height: var(--bw-lh-h2);
	letter-spacing: var(--bw-ls-h2);
}

h3 {
	font-size: var(--bw-size-h3);
	line-height: var(--bw-lh-h3);
	letter-spacing: var(--bw-ls-h3);
}

h4 {
	font-size: var(--bw-size-h4);
	line-height: var(--bw-lh-h4);
	letter-spacing: var(--bw-ls-h4);
}

h5 {
	font-size: var(--bw-size-h5);
	line-height: var(--bw-lh-h5);
	letter-spacing: var(--bw-ls-h5);
}

h6 {
	font-size: var(--bw-size-h6);
	line-height: var(--bw-lh-h6);
	letter-spacing: var(--bw-ls-h6);
}

p {
	margin: 0 0 var(--bw-space-5);
}

a {
	color: var(--bw-text);
	text-decoration-thickness: 1px;
	text-underline-offset: 0.25em;
	transition: color var(--bw-transition-fast);
}

a:hover,
a:focus {
	color: var(--bw-primary);
}

:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
	outline: var(--bw-focus-ring);
	outline-offset: var(--bw-focus-offset);
}

/**
 * Selection highlight in brand red rather than the browser default blue. Only
 * color and background-color are honoured here, so this stays deliberately
 * minimal — no inherited transition applies to a highlight pseudo-element.
 */
::selection {
	background-color: var(--bw-primary);
	color: var(--bw-primary-contrast);
}

img,
svg,
video {
	max-width: 100%;
	height: auto;
}

hr,
.bw-rule {
	background-color: var(--bw-rule);
	border: 0;
	height: 1px;
	margin: 0;
}

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

/**
 * Page container. Caps content at 1440px and never lets the gutter
 * fall below 24px. The design draws every section edge against this.
 */
.bw-container {
	margin-inline: auto;
	max-width: calc(var(--bw-container) + (var(--bw-gutter) * 2));
	padding-inline: var(--bw-gutter);
	width: 100%;
}

/* Standard vertical section rhythm (88px at the design viewport). */
.bw-section {
	padding-block: clamp(3rem, 5.093vw, 5.5rem);
}

/**
 * 3-column card grid: 459px columns with a 32px gutter inside 1441px.
 * Collapses to 2 columns on tablet and 1 on mobile.
 */
.bw-grid {
	display: grid;
	gap: var(--bw-grid-gutter);
	grid-template-columns: repeat(3, minmax(0, 1fr));
}

.bw-grid--2 {
	grid-template-columns: repeat(2, minmax(0, 1fr));
}

@media (max-width: 1024px) {

	.bw-grid {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}
}

@media (max-width: 640px) {

	.bw-grid,
	.bw-grid--2 {
		grid-template-columns: minmax(0, 1fr);
	}
}

/* Stack: vertical flow with a single spacing knob. */
.bw-stack {
	display: flex;
	flex-direction: column;
	gap: var(--bw-stack-gap, var(--bw-space-5));
}

.bw-stack > * {
	margin-block: 0;
}

/*--------------------------------------------------------------
# Typography utilities
--------------------------------------------------------------*/

.bw-display {
	font-family: var(--bw-font-display);
	font-size: var(--bw-size-display);
	font-weight: var(--bw-weight-bold);
	line-height: var(--bw-lh-display);
	letter-spacing: 0;
}

.bw-display--uppercase {
	font-family: var(--bw-font-heading);
	text-transform: uppercase;
}

.bw-section-title {
	font-family: var(--bw-font-heading);
	font-size: var(--bw-size-section-title);
	font-weight: var(--bw-weight-medium);
	line-height: var(--bw-lh-section-title);
	letter-spacing: 0;
}

/* The red eyebrow above a section title. */
.bw-section-label {
	color: var(--bw-accent);
	font-family: var(--bw-font-heading);
	font-size: var(--bw-size-section-label);
	font-weight: var(--bw-weight-semibold);
	line-height: var(--bw-lh-section-label);
	letter-spacing: var(--bw-ls-section-label);
	text-transform: uppercase;
}

.bw-card-label {
	font-family: var(--bw-font-heading);
	font-size: var(--bw-size-card-label);
	font-weight: var(--bw-weight-medium);
	line-height: var(--bw-lh-card-label);
	letter-spacing: var(--bw-ls-card-label);
	text-transform: uppercase;
}

.bw-subtitle {
	font-family: var(--bw-font-body);
	font-size: var(--bw-size-subtitle);
	font-weight: var(--bw-weight-light);
	line-height: var(--bw-lh-subtitle);
	letter-spacing: 0;
}

.bw-body-lg {
	font-size: var(--bw-size-body-lg);
	line-height: var(--bw-lh-body-lg);
	letter-spacing: 0;
}

.bw-body-md {
	font-size: var(--bw-size-body-md);
	line-height: var(--bw-lh-body-md);
	letter-spacing: var(--bw-ls-body);
}

.bw-body-sm {
	font-size: var(--bw-size-body-sm);
	line-height: var(--bw-lh-body-sm);
	letter-spacing: var(--bw-ls-body);
}

.bw-text-muted {
	color: var(--bw-text-muted);
}

.bw-text-accent {
	color: var(--bw-accent);
}
