/* ==========================================================================
   FRONTGRADE  ·  fg2 INTERIOR PAGE
   The company pages: About, Leadership, Locations, Doing Business With Us,
   Supplier Resources and its two children, Ethics, Support and Contact.
   The leadership listing and the biography pages beneath it are the same
   pattern: a view and a node bundle rather than a company page, but the same
   band, the same reading column and the same closing call to action.
   Requires tokens.css and components.css to be loaded first.
   --------------------------------------------------------------------------
   WHAT THIS FILE IS FOR. The interior pattern is a reshape, not a rewrite.
   Every one of these pages already holds its content in paragraph components
   with markup and templates this work does not own, so the templates supply
   the chrome and this stylesheet supplies the frame: the bands, the measures,
   the surfaces and the type scale that put existing content into the
   redesign's language without moving a word of it.

   THAT IS WHY THE SELECTORS NAME .component-*. Those classes are the site's,
   not this design's. A rule here does not restyle a component everywhere it
   appears; every one of them is scoped under .fg-interior, which the three
   node wrappers and the leadership view put on the page's root element and
   nowhere else. A paragraph of the same type on a page outside the pattern is
   untouched, which is what lets the pattern land on the whole family without
   a sitewide component rewrite.

   IT MUST WIN AGAINST THE LEGACY SKIN, AND IT DOES SO ON SPECIFICITY ALONE.
   page.html.twig still puts .fg2-legacy-skin on the <main> these pages render
   inside, because the decision that drives it lives in the theme file and is
   returned as wiring rather than taken here. That skin is written for a light
   ground and every rule in it is at most 0-1-1 (.fg2-legacy-skin p, and its
   :where() rules score 0-1-0). Every rule below carries .fg-interior plus at
   least one more class or element, so it scores 0-2-0 or higher and outranks
   the skin no matter which order the aggregator emits the two files in. The
   pattern therefore renders correctly today, and renders identically if the
   skin is later switched off for these bundles.

   WHY EVERY COMPONENT RULE ALSO NAMES #block-frontgrade-content. The site's
   own component stylesheet anchors its paragraph layouts on that block
   wrapper: #block-frontgrade-content .component-link-list__links, and dozens
   like it, each scoring 1-1-0. Those rules place the components on a twelve
   column grid whose track widths are computed from the viewport rather than
   from the container the component is in, which is exactly what a band with
   its own measure has to be able to override. No selector built from classes
   alone can, so the rules that reshape a component match the same wrapper the
   site's rules match, one id and one more class. The id is read, never
   written, and .fg-interior still holds every rule to the pages in this
   pattern. Rules that touch only this design's own markup stay on classes.

   CONTRAST. Ratios are computed from the sRGB relative-luminance formula
   tokens.css states, against the exact surface the text sits on. The
   single-orange rule holds: #e35530 is the only accent, and it is used for
   marks, rules and hover states rather than for body text on a dark ground.
     --text-display on --surface-base     17.90:1
     --text-primary on --surface-base     16.22:1
     --text-muted   on --surface-base      5.57:1
     --accent       on --surface-base      5.22:1
     --text-primary on --surface-panel    14.55:1
     --text-muted   on --surface-panel     4.99:1
   The hero draws a geometric ground under its own copy, so every backdrop it
   can produce is enumerated and measured separately; those tables are in
   section 3a, one per variant, and the tightest figure across all three is
   4.54:1 against a 4.5 minimum.
   ========================================================================== */


/* --------------------------------------------------------------------------
   1. ROOT
   The article paints the page ground itself. The skin sets a light background
   on <main>, and although every band below paints over it, the element's own
   ground would still show through wherever a band does not reach.
   -------------------------------------------------------------------------- */

.fg-interior {
  background: var(--surface-base);
  color: var(--text-muted);
  font-family: var(--font-body);

  /* The gap between the breadcrumb line and the mark below it, and the trail's
     own line box. Both are declared on the root rather than on the band so
     every template that emits the pattern reads one value.

     --fg-crumb-line is the contract's stated line box for the trail, and it is
     set on the strip because the legacy sheet gives the list a 28px leading of
     its own; without it the trail's line box drifts with whatever the body
     leading happens to be. It previously carried a second, unrelated job as a
     height reserve for a trail printed outside the band; that shape is gone,
     and section 3c records why. */
  --fg-crumb-gap: var(--space-5);
  --fg-crumb-line: 22px;
}

.fg-interior :where(h1, h2, h3, h4, h5, h6) {
  font-family: var(--font-display);
  color: var(--text-primary);
}

.fg-interior .fg-h1 { color: var(--text-display); }

/* THE FLOOR COLOURS, STATED AT THE SAME WEIGHT THE SITE STATES THEM.
   The previous design set type colours for a white page under the content
   block id: #block-frontgrade-content h2 and #block-frontgrade-content li
   are both black. Left standing they put black headings on a near black band
   and black list items under orange bullets, which is not a small mismatch,
   it is invisible text. Every element that carries copy inside this pattern
   therefore gets its ground appropriate colour here, at a weight that
   outranks those rules, and the component sections further down override
   this floor wherever a particular element wants something else. */
#block-frontgrade-content .fg-interior :is(h1, h2, h3, h4, h5, h6) { color: var(--text-primary); }
#block-frontgrade-content .fg-interior .fg-h1 { color: var(--text-display); }

#block-frontgrade-content .fg-interior :is(p, li, dd, dt, td, th, figcaption, blockquote) {
  color: var(--text-muted);
}

#block-frontgrade-content .fg-interior .fg-eyebrow { color: var(--accent-text-safe); }
#block-frontgrade-content .fg-interior .fg-meta,
#block-frontgrade-content .fg-interior .fg-small { color: var(--text-secondary); }


/* --------------------------------------------------------------------------
   2. THE BREADCRUMB LINE
   The trail is Drupal's breadcrumb block, so the markup below is the block's,
   styled rather than replaced.

   IT IS THE HERO'S FIRST LINE, NOT A BAR OF ITS OWN. The trail is part of the
   copy stack: crumbs, one tight gap, then the mark, the headline and the
   lede, reading as one block. Nothing stands between the site header and the
   trail except the band's own top padding.

   BOTH TEMPLATES PUT IT IN THE SAME PLACE. The node partial and the
   leadership view each print the trail inside the band's own container, where
   it is simply the first item in the stack: the gap under it is
   --fg-crumb-gap and the mark below it is the same 32px slot. The listing
   used to print it as a sibling above the band, which cost a whole section of
   accommodation; section 3c records what was removed.

   THE SLOT IS NOT A LANDMARK. Both templates carry this class on a plain
   container, because the rendered breadcrumb block emits its own
   <nav aria-labelledby="system-breadcrumb"> and a second one around it made
   two nested navigation landmarks on every page in the pattern. Nothing here
   selects on the element, so the styling is the same either way.

   THE STRIP PAINTS NO GROUND OF ITS OWN. The band behind it already carries
   --surface-base and draws its field there.
   -------------------------------------------------------------------------- */

.fg-interior .fg-interior__crumbs {
  margin: 0 0 var(--fg-crumb-gap);
  padding-block: 0;
  background: transparent;
  font-size: var(--fs-small);
  line-height: var(--fg-crumb-line);
}

/* THE LIST'S OWN TYPE IS RESET HERE, NOT LEFT TO THE WRAPPER.

   Setting the size on .fg-interior__crumbs above is not enough, and the
   measurement is what says so. styles/dist/component.css carries
   `.breadcrumb { font-size: .8125rem; letter-spacing: .03125rem;
   text-transform: uppercase }` at (0,1,0), and that class is on the <ol> the
   breadcrumb block renders, INSIDE the wrapper. The wrapper's 14px is
   therefore inherited by nothing: the list restates 13px on itself and the
   items and links inherit that instead. Measured on /about at 1280 before
   this rule: wrapper 14px, but <ol>, every <li> and every <a> computing
   13px uppercase at 0.5px tracking, against a contract that states 14px,
   sentence case, no tracking. The rule below is (0,2,1) and outranks the
   legacy declaration for exactly the three properties it took. */
.fg-interior .fg-interior__crumbs ol {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: inherit;                  /* the 14px above, not the legacy 13px */
  letter-spacing: normal;
  text-transform: none;
}

.fg-interior .fg-interior__crumbs li {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--text-secondary);
}

/* THE SEPARATOR AND THE ITEM GAP NAME .breadcrumb, WHICH IS NOT DECORATION.
   component.css draws the same glyph from
   `.breadcrumb > li.breadcrumb-item + .breadcrumb-item::before` at (0,3,2),
   in #fff, and adds a 3px left margin from the sibling rule at (0,3,1).
   A two class selector plus elements scores (0,2,3) and loses to the first of
   those on the class column, so the glyph rendered white rather than
   --border-ui and the flex gap had 3px added to it. Naming the list's own
   class takes both selectors to (0,3,3) and (0,3,2), which win. Measured on
   /about at 1280 before this rule: separator rgb(255,255,255), second item
   margin-left 3px. */
.fg-interior .fg-interior__crumbs .breadcrumb li + li::before {
  content: "/";
  color: var(--border-ui);
}

.fg-interior .fg-interior__crumbs .breadcrumb li + li { margin-left: 0; }

/* A CRUMB THAT IS A LINK HAS TO LOOK UNLIKE THE CRUMB THAT IS NOT.

   This is the same treatment page-header.css gives its own trail, and it is
   stated in both files on purpose: the two page-head systems own their own
   breadcrumbs and neither imports the other, so a rule written once would
   leave whichever family did not carry it behind.

   Measured on this pattern before the rule: the crumb links and the non-link
   current-page item were the identical rgb(125,137,163) at the same weight
   with no underline, a link-to-text ratio of 1.00:1, which fails WCAG 1.4.1.
   The repair is technique G183: a resting tone that measures 3.22:1 against
   the current item (--text-display against --text-muted; --text-primary was
   the alternative and reaches only 2.91:1), plus an underline on hover and on
   focus so the cue is not colour alone. Both tones keep their own ground,
   17.90:1 and 5.57:1 on --surface-base. */
.fg-interior .fg-interior__crumbs .breadcrumb a {
  color: var(--text-display);
  text-decoration: none;
  text-underline-offset: var(--fg-link-offset);
  transition: color var(--dur-quick) var(--ease-standard);
}

.fg-interior .fg-interior__crumbs .breadcrumb a:hover,
.fg-interior .fg-interior__crumbs .breadcrumb a:focus-visible {
  color: var(--accent-text-safe);
  text-decoration: underline;
}

/* The current page is the one crumb that is not a link and it keeps the quiet
   tone, which is what makes the 3.22:1 above a property of the pair. */
.fg-interior .fg-interior__crumbs .breadcrumb .breadcrumb-item:last-child {
  color: var(--text-muted);
}

/* HOME IS A WORD HERE, NOT A HOUSE.

   component.css replaces the first link's text with an 18x18 mask image at
   font-size 0, from
   `.breadcrumb-item:first-child > a[href="/"]` and its :link and :visited
   arms. Measured on /about at 1280 before this rule: the Home link computed
   font-size 0px, an 18 by 18 box and a white mask, so the trail opened with an
   unlabelled glyph whose hit area was 18px against a 24px minimum, and the
   contract's "Home is its first item and is always present" was true of the
   accessibility tree and false of the page.

   THE SELECTOR IS DELIBERATELY LONG. That rule's strongest arm is
   `...a[href="/"]:link:hover` at (0,5,1), and the reset has to beat it rather
   than tie and rely on which sheet the aggregator emits last. Adding the two
   scope classes and the list's own class takes this to (0,6,1), which wins
   against every arm including the hover and focus fills, so one rule replaces
   the whole of that declaration and its three companions.

   The reset is scoped to this band. The same glyph elsewhere on the site is
   untouched, which is correct: this file owns the interior pattern's trail and
   nothing else. */
.fg-interior .fg-interior__crumbs .breadcrumb .breadcrumb-item:first-child > a[href="/"] {
  -webkit-mask-image: none;
  mask-image: none;
  background-color: transparent;
  width: auto;
  height: auto;
  font-size: inherit;
  line-height: inherit;
  vertical-align: baseline;
  display: inline;
}

/* The block ships a visually hidden heading and a second nav element inside
   the first. Neither draws anything; the inner nav is flattened so the flex
   row below it is the only layout in play. */
.fg-interior .fg-interior__crumbs nav { display: contents; }


/* --------------------------------------------------------------------------
   3. THE HERO
   One band, one order of information, on every page the pattern serves:
   breadcrumb (the strip above), section token, headline, lede. The last two
   are printed only where the page holds the copy for them, so the rules here
   have to hold the band's shape and its rhythm steady whether they are
   present or not.

   THE BAND IS SIZED BY ITS CONTENT, WITH A FLOOR. The floor is the larger of
   a pixel value and a small fraction of the viewport, and it exists only so a
   one word headline does not collapse the band; every page with a trail, a
   token, a headline and a lede is taller than the floor and is sized by that
   stack. svh rather than vh because vh on a mobile browser is the height the
   viewport has with the toolbars retracted, so a vh floor overshoots by the
   toolbar every time; the vh line above each svh line is the fallback for a
   browser that does not know the unit and is deliberately the same number.

   THE COPY IS TOP SET AND IN NATURAL FLOW. align-items is flex-start, so the
   stack begins under the band's own top padding and the band ends a fixed
   distance below the last line of it. There is no empty field: the band has
   no room the copy is not using, and the drawn ground runs behind and beside
   the copy rather than above it.

   THE HEADLINE CARRIES THE MASS. The display clamp is the band's whole
   weight now, and it is the same clamp on all three grounds.

   THREE TEMPLATES EMIT THIS BAND: the node partial for the two company page
   bundles, views-view--leadership--page.html.twig for the listing, and
   node--person--full.html.twig for a biography. All three write
   .fg-interior__hero-inner on the container and all three carry the trail and
   the section token; the lede is printed only where the page holds the copy
   for it, which today is a company page with a hero subheading and a person's
   role. Every rule below is written against .fg-container rather than against
   the modifier, so a template that emits a smaller subset of the stack still
   gets the same band and the missing rules simply find nothing to match.

   THE MARKER SLOT IS ALWAYS 32px TALL. A page whose breadcrumb names a
   section prints the token row: 12px of type at leading-none plus a 20px
   gap. A page that sits directly under Home has no section to name and
   prints the accent rule instead: 2px of rule plus a 30px gap. The two are
   the same height by construction, so the headline starts at the same
   distance below whichever mark the page carries.
   -------------------------------------------------------------------------- */

.fg-interior .fg-interior__hero {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: flex;
  align-items: flex-start;
  min-height: max(300px, 32vh);
  min-height: max(300px, 32svh);
  padding-block: var(--fg-band-pad-top) var(--fg-band-pad-bottom);
  background: var(--surface-base);
  border-bottom: var(--border-hairline-w) solid var(--border-hairline);

  /* The band's own padding is the only thing between the site header and the
     first line of the page, so it is named rather than left to a section
     token that also spaces bands from each other. */
  --fg-band-pad-top: clamp(20px, 0.9rem + 1.1vw, 30px);
  --fg-band-pad-bottom: clamp(40px, 1.9rem + 2.2vw, 64px);

  /* Display scale, for every variant. The band carries no empty field any
     more, so the headline is what gives it its mass.

     THIS IS THE PAGE HEADLINE ROLE, NOT A SCALE OF ITS OWN. It used to state
     its own clamp, 52 -> 104, which was the value --fg-h1 was originally
     lifted from. When the page headline token was cut to 32 -> 58 the interior
     family kept the old clamp and so rendered its h1 at nearly twice the size
     every other page family used. Reading the role token is what keeps the
     four interior pages (leadership, locations, about, the form page) on the
     same headline as the pagehead family, and moves them with it in future. */
  --fg-hero-h1: var(--fg-h1);                              /*  32 ->  58  */
  --fg-hero-measure: var(--fg-h1-measure);
}

/* The copy column, above the drawn ground and above the scrim. The band is a
   flex container and the art layers are absolutely positioned, so they are
   not flex items and the container is the band's only one. */
/* THE COPY STACK IS AN ORDERED COLUMN, and the order is what lets one
   stylesheet serve all three templates. The mark below the trail is drawn as
   this container's ::before, which is a flex item like any other child, so it
   would otherwise be laid out above the trail every template prints first.
   Naming an order for each level fixes the reading regardless of which
   children a given page actually emits, so a page with no lede gets the same
   sequence with the last slot empty. */
.fg-interior .fg-interior__hero > .fg-container {
  position: relative;
  z-index: 3;
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

.fg-interior .fg-interior__hero > .fg-container > .fg-interior__crumbs { order: 1; }
.fg-interior .fg-interior__hero > .fg-container::before                { order: 2; }
.fg-interior .fg-interior__hero > .fg-container > .fg-interior__eyebrow { order: 2; }
.fg-interior .fg-interior__hero > .fg-container > .fg-h1               { order: 3; }
.fg-interior .fg-interior__hero > .fg-container > .fg-interior__lede   { order: 4; }

/* THE MARKER, FORM ONE: the short accent rule, for a page with no section to
   name. It is the mark this pattern has always used and it stays the default:
   a top level page such as /about or /locations sits directly under Home and
   IS its own section, so there is nothing truthful to put in the slot. */
.fg-interior .fg-interior__hero .fg-container::before {
  content: "";
  display: block;
  width: var(--space-10);
  height: 2px;
  margin-bottom: calc(var(--space-7) - var(--space-05));
  background: var(--accent);           /* 5.22:1 on --surface-base, non-text */
}

/* THE MARKER, FORM TWO: the section token. Where one is printed the rule is
   withdrawn, so the slot holds one mark and not two. :has() is already the
   switch this stylesheet uses for the link-list shape further down; a browser
   without it draws both marks rather than losing either. */
.fg-interior .fg-interior__hero .fg-container:has(.fg-interior__eyebrow)::before {
  display: none;
}

/* THE SECTION TOKEN.

   ONE ROW, NOT TWO. The genre puts a bracketed kicker above the headline and
   a row of monospace metadata beside it. This band prints one row carrying
   both treatments, because there is exactly one true datum to put there. The
   section name is read off the breadcrumb by the template, and the two digit
   index beside it is the page's own depth in that same trail: a page one
   level under Home is 01, a page under a section is 02, a page under a
   section's child is 03. Nothing here is authored per page and nothing is
   invented; a second row would have had to be.

   THE BRACKETS AND THE INDEX ARE NOT ANNOUNCED. Both are marked aria-hidden
   in the template, so the accessible name of the row is the section name on
   its own, exactly what it was before this treatment. The brackets are real
   elements rather than generated content for the same reason: generated
   content is exposed in the accessibility tree in current browsers and there
   is no way to hide it from there.

   THE MONOSPACE FACE NEEDS NO FEATURE TEST. --font-mono is a system stack
   ending in the generic monospace keyword, which every browser resolves to
   something, so there is no case where this falls back to nothing. */
.fg-interior .fg-interior__hero .fg-interior__eyebrow {
  display: flex;
  align-items: center;
  gap: 0.62em;
  margin: 0 0 var(--space-5);
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  font-weight: var(--fw-semibold);
  line-height: 1;
  letter-spacing: 0.16em;
  color: var(--accent);                /* 5.22:1 on --surface-base */
}

/* The brackets and the index are the quiet grey, not the orange, so the row
   holds one accent and the section name is the thing carrying it. Both are
   flat colours at full opacity: nothing in this row is faded, because a
   faded colour is a composite and would need a table row of its own. */
.fg-interior .fg-interior__hero .fg-interior__eyebrow-bracket {
  color: var(--text-secondary);        /* 5.57:1 on --surface-base */
}

.fg-interior .fg-interior__hero .fg-interior__eyebrow-idx {
  color: var(--text-secondary);        /* 5.57:1 on --surface-base */
  letter-spacing: 0.10em;
  padding-inline-end: 0.62em;
  border-inline-end: 1px solid var(--border-ui);
}

.fg-interior .fg-interior__hero .fg-h1 {
  margin: 0;
  max-width: var(--fg-hero-measure);
  font-size: var(--fg-hero-h1);
  line-height: var(--lh-display);
  overflow-wrap: break-word;
}

.fg-interior .fg-interior__hero .fg-interior__lede {
  margin: var(--space-5) 0 0;
  max-width: var(--measure-body);
  color: var(--text-muted);            /* 5.57:1 on --surface-base */
}

/* THE HEADLINE'S WORDS.

   The template prints the headline one word to a span so the line can arrive
   in sequence rather than all at once. inline-block is what lets a word carry
   a transform; the space between two spans is a real text node in the
   template's output, so the accessible name, text selection and copy are the
   sentence they always were.

   THIS IS THE STATIC STATE and it is the complete one: fully visible, no
   animation named. Everything that moves is in section 3b, inside a
   no-preference query, so a visitor with reduced motion set sees this. */
.fg-interior .fg-interior__hero .fg-hw {
  display: inline-block;
  overflow-wrap: break-word;
}


/* --------------------------------------------------------------------------
   3a. THE DRAWN GROUND  ·  three variants, one switch

   THE GROUND IS DRAWN, NEVER PHOTOGRAPHED. Everything below is an inline SVG
   or a CSS gradient carried in this file: no request, no asset, no image from
   the node.

   HOW TO SWITCH IT. Each template that emits the band sets one variable,
   fg_hero_art, and prints it as data-fg-hero-art on the hero section. The
   three accepted values are signal, beacon and typo, and each is a block of
   custom properties below; every template has to be set together for the
   pattern to move as one. The default variant's block also matches
   :not([data-fg-hero-art]) so that a band emitted without the attribute is
   still drawn rather than left bare; nothing on the site relies on that arm
   today, and moving the selector line to another block is what would move
   such a page with the rest.

   FOUR LAYERS, IN A FIXED ORDER. The band isolates, so the order is the
   whole of the stacking question and it is stated as z-index on every layer
   rather than left to document order:

     z 0   ::before               the field: hairlines, planes, bloom, grain
     z 1   .fg-interior__hero-art the accent layer, one orange mark per page
     z 2   ::after                the scrim
     z 3   > .fg-container        the copy

   The accent layer is a real element because it is the one layer that has to
   be able to move independently of the field. Every variant's field layer is
   nonetheless a complete drawing on its own and the accent layer only ever
   adds to it, so a band emitted without the element still reads as finished.
   All three templates emit it today.

   EVERY MARK IS AN OPAQUE LITERAL, AND THAT IS THE CONTRAST ARGUMENT.
   A drawn ground made of translucent strokes has no single backdrop: two
   strokes that cross composite to something lighter than either, and the
   figure a table would quote is not the figure behind the text. Each mark is
   therefore painted at full opacity in the colour it would have HAD as a
   translucent mark over --surface-base, so an overlap resolves to whichever
   literal is on top and can never be lighter than the lightest one used.

     #090c14  --surface-base, the ground itself, wherever no mark falls
     #10141d  --stroke-quiet at 0.07 over the ground   fine hairlines
     #131721  --stroke-quiet at 0.10 over the ground   traces
     #151923  --stroke-quiet at 0.12 over the ground   pads, rules
     #281618  --accent       at 0.14 over the ground   accent marks
     #1f1317  --accent       at 0.10 over the ground   the bloom's brightest
     #141015  --accent       at 0.05 over the ground   the bloom's mid stop
     #0e1220  --surface-raised   |  planes, taken off the surface ladder
     #131928  --surface-panel    |  rather than mixed as a new colour

   THE TWO PLACES THAT ARE NOT A FLAT LITERAL, AND WHY EACH IS STILL
   ENUMERABLE.

   The bloom is a ramp, not a step. Its three stops are --accent over
   --surface-base at 0.10, 0.05 and 0, so every value the ramp passes through
   is that same mix at some alpha between 0 and 0.10. Accent over base raises
   all three channels monotonically with alpha, so luminance rises
   monotonically with it too and every intermediate value sits between the
   two endpoints. The table quotes the brighter endpoint, #1f1317, which is
   therefore the worst case for the whole ramp.

   The grain is noise, so its output is not a colour that can be written
   down; its alpha ceiling is. The filter ends in an feColorMatrix that forces
   the result to white and multiplies the turbulence into alpha by 0.012, so
   no grain pixel is ever more than white at 0.012. White at 0.012 over the
   lightest literal any field layer paints is the worst backdrop the grain can
   make, and that is the row the table carries. Grain is the top layer of the
   field and the accent layer sits above it, so no accent literal is ever
   under grain.

   Ratios below are the sRGB relative-luminance formula tokens.css states,
   computed for every text level this hero can print against every backdrop
   each variant can produce. Thresholds: 4.5:1 for the token row, the lede
   and the crumb text; 3:1 for the h1, which is 52.28px at the 390px floor and
   104px at the ceiling and is large text at both, and for the focus ring.

     VARIANT signal
     backdrop   h1      lede/   token   focus   what it is
                        crumb   name    ring
     #090c14    17.90   5.57    5.22    19.55   ground
     #10141d    16.87   5.25    4.92    18.42   fine hairline
     #131721    16.40   5.10    4.78    17.91   trace
     #151923    16.09   5.00    4.69    17.57   pad
     #181c26    15.59   4.85    4.55    17.03   grain over the lightest mark
     #281618    15.78   4.91    4.60    17.24   accent trace and pad

     VARIANT beacon
     #090c14    17.90   5.57    5.22    19.55   ground
     #1f1317    16.52   5.14    4.82    18.04   bloom, brightest point
     #0e1220    17.08   5.31    4.98    18.65   plane 1, --surface-raised
     #131928    16.06   4.99    4.68    17.54   plane 2, --surface-panel
     #161c2b    15.57   4.84    4.54    17.00   grain over the lightest plane
     #281618    15.78   4.91    4.60    17.24   accent edge

     VARIANT typo
     #090c14    17.90   5.57    5.22    19.55   ground
     #151923    16.09   5.00    4.69    17.57   hairline rule
     #181c26    15.59   4.85    4.55    17.03   grain over the hairline
     #281618    15.78   4.91    4.60    17.24   accent rule

   THE GEOMETRY MOVED AND THE PALETTE DID NOT. The three grounds were redrawn
   for a compact band that the copy fills, so the traces, the planes and the
   bloom now run behind and beside the copy instead of above it. Every mark is
   still one of the literals listed above and no new colour was introduced, so
   the rows below are the rows for the new drawings as much as for the old
   ones: the set of backdrops a variant can put behind text is a property of
   its palette, not of where its marks fall.

   Each row is measured against the literal as written above and as painted,
   not against the unrounded composite it was derived from, so the figure is
   the one the screen actually produces. The tightest figure anywhere in the
   three tables is 4.54:1, the token row over grain on beacon's lightest
   plane, against a 4.5 threshold.

   THE SCRIM IS NOT IN THE TABLE, ON PURPOSE. Every figure above is computed
   with the scrim ignored, which is the only way the guarantee can be
   independent of where a mark happens to fall. The scrim is a wash of
   --surface-base at decreasing alpha across the copy column, so it can only
   move a backdrop back towards --surface-base, and --surface-base is the
   best row in every table. It can therefore only raise a figure, never lower
   one, and the hero clears AA at every level with the scrim taken away
   entirely. What the scrim does is composition: it holds the art back behind
   the copy so display type is not read over a field.
   -------------------------------------------------------------------------- */

/* THE FIELD. */
.fg-interior .fg-interior__hero::before {
  content: "";
  position: absolute;
  inset: var(--fg-hero-field-inset, 0);
  z-index: 0;
  pointer-events: none;
  background-image: var(--fg-hero-art-image);
  background-size: var(--fg-hero-art-size);
  background-position: var(--fg-hero-art-position);
  background-repeat: var(--fg-hero-art-repeat);
}

/* THE ACCENT LAYER. One element, emitted by the node template only. */
.fg-interior .fg-interior__hero-art {
  position: absolute;
  inset: var(--fg-hero-accent-inset, 0);
  z-index: 1;
  pointer-events: none;
  background-image: var(--fg-hero-accent-image);
  background-size: var(--fg-hero-accent-size);
  background-position: var(--fg-hero-accent-position);
  background-repeat: no-repeat;
}

/* THE SCRIM. The same zone on every variant, so the copy column is held back
   from the art by a layer that does not know what is under it. */
.fg-interior .fg-interior__hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background-image: var(--fg-hero-scrim);
}

.fg-interior .fg-interior__hero {
  --fg-hero-scrim: linear-gradient(90deg,
    rgba(9, 12, 20, 0.88) 0%,
    rgba(9, 12, 20, 0.80) 38%,
    rgba(9, 12, 20, 0.58) 64%,
    rgba(9, 12, 20, 0.30) 86%,
    rgba(9, 12, 20, 0.16) 100%);
}


/* ---- VARIANT: signal  ·  the electronics backbone -----------------------
   THE DEFAULT, and the most literal drawing of what the company makes: a
   trace system. Orthogonal runs with chamfered corners, square pads at the
   turns, a quiet reference hairline behind them, and one run in the accent
   carrying a pad of its own. The homepage draws connection as arcs between
   platforms; a board is the same system at the other end of the scale, and
   an interior page is where that reads.

   CHAMFERS, NOT RIGHT ANGLES, because a routed trace is never mitred square;
   the 45 degree corner is the one detail that stops this reading as a chart
   grid. Every run is a single stroke of one literal, so a run crossing a run
   resolves to whichever is painted later and never to a lighter sum.        */
.fg-interior .fg-interior__hero:not([data-fg-hero-art]),
.fg-interior .fg-interior__hero[data-fg-hero-art="signal"] {
  --fg-hero-art-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='g' x='0' y='0' width='100%25' height='100%25' color-interpolation-filters='sRGB'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.012 0'/%3E%3C/filter%3E%3Crect width='220' height='220' filter='url(%23g)'/%3E%3C/svg%3E"),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1200 340' preserveAspectRatio='xMidYMid slice'%3E%3Cg fill='none' stroke='%2310141d' stroke-width='1'%3E%3Cpath d='M0 84H1200M0 256H1200M368 0V340M884 0V340'/%3E%3C/g%3E%3Cg fill='none' stroke='%23131721' stroke-width='1.6'%3E%3Cpath d='M0 292H188L244 236H648L700 184H1200'/%3E%3Cpath d='M0 148H96L152 92H520L568 44H1200'/%3E%3Cpath d='M264 340V284L316 232H1200'/%3E%3Cpath d='M0 208H72L124 156H436L484 204H1200'/%3E%3Cpath d='M1104 0V340'/%3E%3C/g%3E%3Cg fill='%23151923'%3E%3Crect x='240' y='232' width='9' height='9'/%3E%3Crect x='696' y='180' width='9' height='9'/%3E%3Crect x='148' y='88' width='9' height='9'/%3E%3Crect x='564' y='40' width='9' height='9'/%3E%3Crect x='312' y='228' width='9' height='9'/%3E%3Crect x='480' y='200' width='9' height='9'/%3E%3Crect x='1100' y='160' width='9' height='9'/%3E%3C/g%3E%3C/svg%3E");
  --fg-hero-art-size: 220px 220px, cover;
  --fg-hero-art-position: 0 0, center;
  --fg-hero-art-repeat: repeat, no-repeat;
  --fg-hero-accent-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1200 340' preserveAspectRatio='xMidYMid slice'%3E%3Cpath d='M0 320H132L200 252H592L656 188H1200' fill='none' stroke='%23281618' stroke-width='2'/%3E%3Crect x='195' y='247' width='11' height='11' fill='%23281618'/%3E%3Crect x='651' y='183' width='11' height='11' fill='%23281618'/%3E%3C/svg%3E");
  --fg-hero-accent-size: cover;
  --fg-hero-accent-position: center;
}

/* ---- VARIANT: beacon  ·  bloom and planes -------------------------------
   Tone and light. A low orange bloom sits in the lower right of the band and
   two planes cut on the site's shear angle step up the surface ladder over
   it, so the band reads as depth rather than as drawing. The single orange
   is the bloom and the one hairline edge on the upper plane's leading side.

   THE BLOOM IS THE BOTTOM LAYER, which is a contrast decision as much as a
   compositional one: the planes paint over it at full opacity, so no pixel is
   ever bloom composited onto a plane and the table needs no row for a mix
   that cannot occur.

   THE PLANE STOPS ARE HARD. Every colour stop pair shares a position, so
   there is no ramp between two surfaces and no band of intermediate values.
   The only ramp in the variant is the bloom, and section 3a states why that
   one is still enumerable.                                                  */
.fg-interior .fg-interior__hero[data-fg-hero-art="beacon"] {
  --fg-hero-art-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='g' x='0' y='0' width='100%25' height='100%25' color-interpolation-filters='sRGB'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.012 0'/%3E%3C/filter%3E%3Crect width='220' height='220' filter='url(%23g)'/%3E%3C/svg%3E"),
    linear-gradient(100deg, transparent 0 72%, #131928 72% 100%),
    linear-gradient(100deg, transparent 0 52%, #0e1220 52% 100%),
    radial-gradient(78% 132% at 68% 104%, #1f1317 0%, #141015 40%, #090c14 76%, #090c14 100%);
  --fg-hero-art-size: 220px 220px, 100% 100%, 100% 100%, 100% 100%;
  --fg-hero-art-position: 0 0, center, center, center;
  --fg-hero-art-repeat: repeat, no-repeat, no-repeat, no-repeat;
  --fg-hero-accent-inset: -10% 0;
  --fg-hero-accent-image:
    linear-gradient(100deg, transparent 0 71.9%, #281618 71.9% 72.06%, transparent 72.06% 100%),
    linear-gradient(100deg, transparent 0 51.94%, #281618 51.94% 52.02%, transparent 52.02% 100%);
  --fg-hero-accent-size: 100% 100%, 100% 100%;
  --fg-hero-accent-position: center, center;
}

/* ---- VARIANT: typo  ·  the type is the hero -----------------------------
   Near zero ornament. Two quiet vertical hairlines set the band's measure,
   one accent rule stands at the foot of the first of them, and the grain
   keeps the ground from reading as flat black. Everything else is the
   headline, which is set a full step larger here than on the other two
   variants and is the only thing in the band with any weight.               */
.fg-interior .fg-interior__hero[data-fg-hero-art="typo"] {
  --fg-hero-art-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='g' x='0' y='0' width='100%25' height='100%25' color-interpolation-filters='sRGB'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.012 0'/%3E%3C/filter%3E%3Crect width='220' height='220' filter='url(%23g)'/%3E%3C/svg%3E"),
    linear-gradient(#151923, #151923),
    linear-gradient(#151923, #151923);
  --fg-hero-art-size: 220px 220px, 1px 100%, 1px 100%;
  --fg-hero-art-position: 0 0, 56% 0, 74% 0;
  --fg-hero-art-repeat: repeat, no-repeat, no-repeat;
  --fg-hero-accent-image: linear-gradient(#281618, #281618);
  --fg-hero-accent-size: 2px 38%;
  --fg-hero-accent-position: calc(56% - 1px) 100%;
}


/* --------------------------------------------------------------------------
   3b. THE ARRIVAL  ·  everything in this stylesheet that moves

   NO MOTION FIRST. Sections 3 and 3a above are the complete hero as it
   stands still: the field drawn, the accent drawn, the words visible. Not one
   rule up there names an animation. Everything below is inside a
   prefers-reduced-motion: no-preference query, so a visitor who has asked for
   less motion is not served a stopped animation, they are served the static
   band, which is the only state the stylesheet has to get right twice.

   ALL OF IT IS ONE SHOT, WITH ONE EXCEPTION. The field draws itself, the
   accent draws itself behind it, and the headline arrives a word at a time;
   each runs once on load and ends in the state above. Nothing loops, so
   there is nothing for the motion registry to own and motion.js correctly
   leaves the Pause control hidden on these pages: it looks for
   [data-ambient] or .fg-ambient in the markup and this band emits neither.

   THE EXCEPTION IS beacon's SCROLL OFFSET, which is tied to a scroll
   timeline rather than to a clock. It advances only while the visitor
   scrolls and it holds still when they do, so it is not ambient either, but
   it is the one thing here a visitor might want stopped and it is cheap to
   let them: the two rules after the @supports block drop it whenever
   motion.js has put <html> into paused or reduced. The @supports guard means
   a browser without scroll-driven animations gets the static field, which is
   the same thing reduced motion gets.

   THE WORD STAGGER'S NUMBERS. 520ms per word, first word at 90ms, 42ms
   between words. A four word headline, which is the long end of what these
   pages carry, has its last word start at 216ms and finish at 736ms. The
   enumeration stops at ten; an eleventh word and beyond ride the tenth
   delay, so no headline can drag the band's arrival past about a second.

   WHY THE WORDS ARE NOT [data-reveal]. That idiom is an intersection
   observer plus a transition, one element at a time, and it is still what
   the token row and the lede use because they are single elements arriving
   as a block. A per word cascade wants a delay per child and no observer at
   all, since the hero is on screen at load by definition. The two do not
   fight because the h1 carries neither the attribute nor the class.
   -------------------------------------------------------------------------- */

@keyframes fg-hero-draw {
  from { clip-path: inset(0 100% 0 0); }
  to   { clip-path: inset(0 0 0 0); }
}

@keyframes fg-hero-word {
  from { opacity: 0; transform: translate3d(0, 0.34em, 0); }
  to   { opacity: 1; transform: none; }
}

@keyframes fg-hero-parallax {
  from { transform: translate3d(0, -5.5%, 0); }
  to   { transform: translate3d(0, 5.5%, 0); }
}

@media (prefers-reduced-motion: no-preference) {
  .fg-interior .fg-interior__hero::before {
    animation: fg-hero-draw 900ms var(--ease-out-quart) both;
  }

  .fg-interior .fg-interior__hero-art {
    animation: fg-hero-draw 1000ms var(--ease-out-quart) 240ms both;
  }

  .fg-interior .fg-interior__hero .fg-hw {
    animation: fg-hero-word 520ms var(--ease-entrance) both;
    animation-delay: 468ms;
  }
  .fg-interior .fg-interior__hero .fg-hw:nth-child(1)  { animation-delay:  90ms; }
  .fg-interior .fg-interior__hero .fg-hw:nth-child(2)  { animation-delay: 132ms; }
  .fg-interior .fg-interior__hero .fg-hw:nth-child(3)  { animation-delay: 174ms; }
  .fg-interior .fg-interior__hero .fg-hw:nth-child(4)  { animation-delay: 216ms; }
  .fg-interior .fg-interior__hero .fg-hw:nth-child(5)  { animation-delay: 258ms; }
  .fg-interior .fg-interior__hero .fg-hw:nth-child(6)  { animation-delay: 300ms; }
  .fg-interior .fg-interior__hero .fg-hw:nth-child(7)  { animation-delay: 342ms; }
  .fg-interior .fg-interior__hero .fg-hw:nth-child(8)  { animation-delay: 384ms; }
  .fg-interior .fg-interior__hero .fg-hw:nth-child(9)  { animation-delay: 426ms; }
  .fg-interior .fg-interior__hero .fg-hw:nth-child(10) { animation-delay: 468ms; }
}

/* beacon's accent edges take a scroll offset in place of the draw, so the two
   orange lines shift against the planes behind them as the band passes. It
   replaces that layer's one shot rather than joining it, which is why the
   selector is longer than the rule it overrides and why neither element ever
   runs more than one animation.

   IT IS ON THE ACCENT LAYER, NOT THE FIELD. The field is drawn on every page
   the pattern serves, so anything attached to it has to behave the same with
   the attribute and without it. The accent layer is emitted only by a
   template that also prints the attribute, so a rule keyed to the attribute
   can never describe a page that does not carry one: making beacon the
   default moves the ground and the load draw to every page, and the scroll
   offset simply has no layer to act on where there is no accent layer. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .fg-interior .fg-interior__hero[data-fg-hero-art="beacon"] .fg-interior__hero-art {
      animation: fg-hero-parallax linear both;
      animation-timeline: view();
      animation-range: cover;
    }
  }
}

/* The global Pause reaches the one thing here that is not a one shot.
   motion.js only shows its control on a page carrying [data-ambient] or
   .fg-ambient, and this band emits neither because nothing here loops; the
   two rules below are components.css's kill switch written out for this
   layer anyway, so a visitor who stopped motion elsewhere on the site keeps
   the band still. They outrank the block above on the <html> selector. */
html[data-motion="paused"] .fg-interior .fg-interior__hero[data-fg-hero-art="beacon"] .fg-interior__hero-art,
html[data-motion="reduced"] .fg-interior .fg-interior__hero[data-fg-hero-art="beacon"] .fg-interior__hero-art {
  animation: none;
}

/* --------------------------------------------------------------------------
   3c. RETIRED  ·  the sibling breadcrumb accommodation

   This section used to rebuild a second hero shape. The leadership view
   printed its trail as a sibling ABOVE the band rather than inside it, so the
   band's top padding, its floor and the vertical offset of its drawn ground
   all had to be recomputed under .fg-interior--listing to make the two shapes
   read alike, at four breakpoints.

   The template now emits the trail inside the band's own container, in the
   canon's order, so there is one shape and the rules in section 3 describe it
   for both templates. Everything that was here is deleted rather than left
   inert: a second set of band-height rules that no page matches is a trap for
   whoever reads this file next.

   .fg-interior--listing survives with exactly one job, the raised surface
   under the people grid in section 14. --fg-crumb-line survives too, but only
   in the meaning the contract gives it, the trail's own line box, which
   section 2 sets on the strip. Its second job here, reserving height for a
   trail that sat outside the band, went with the shape that needed it.
   -------------------------------------------------------------------------- */


/* Paper has no arrival. */
@media print {
  .fg-interior .fg-interior__hero::before,
  .fg-interior .fg-interior__hero-art,
  .fg-interior .fg-interior__hero .fg-hw {
    animation: none;
    clip-path: none;
    opacity: 1;
    transform: none;
  }
}


/* --------------------------------------------------------------------------
   4. A HERO PARAGRAPH LEFT IN THE BODY
   The pattern reads the page's hero paragraph for its heading and its
   subheading and then leaves it out of the body, so on every page today
   nothing here matches. It exists for the page that puts a SECOND hero
   paragraph further down its component list: the template lifts the first
   one and prints the rest of the list as it stands, so a second one would
   arrive in the body carrying the photograph and the layout this design
   removed.

   The rule is therefore not a restyle of that component but a floor under it.
   The media well is not rendered, and what is left is the heading and the
   subheading as an ordinary block of copy on the band's measure. Nothing in
   this pattern can put a legacy hero image on the page.
   -------------------------------------------------------------------------- */

#block-frontgrade-content .fg-interior .component-hero-landing {
  position: relative;
  background: var(--surface-base);
}

#block-frontgrade-content .fg-interior .component-hero-landing__image,
#block-frontgrade-content .fg-interior .component-hero-landing__video,
#block-frontgrade-content .fg-interior .component-hero-landing__video-controls,
#block-frontgrade-content .fg-interior .component-hero-landing__gradient-overlay {
  display: none;
}

#block-frontgrade-content .fg-interior .component-hero-landing__background {
  position: relative;
  min-height: 0;
  display: block;
}

#block-frontgrade-content .fg-interior .component-hero-landing__content {
  position: relative;
  width: 100%;
  padding-block: var(--section-y);
}

#block-frontgrade-content .fg-interior .component-hero-landing__content-inner {
  width: 100%;
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

#block-frontgrade-content .fg-interior .component-hero-landing__heading {
  margin: 0;
  max-width: var(--measure-tight);
  font: var(--type-h2);
  letter-spacing: var(--ls-h2);
  color: var(--text-primary);
}

#block-frontgrade-content .fg-interior .component-hero-landing__subheading {
  margin-top: var(--space-3);
  max-width: var(--measure-body);
  font-size: var(--fs-lede);
  line-height: var(--lh-lede);
  color: var(--text-muted);
}

#block-frontgrade-content .fg-interior .component-hero-landing__links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin: var(--space-5) 0 0;
  padding: 0;
  list-style: none;
}

#block-frontgrade-content .fg-interior .component-hero-landing__links a {
  display: inline-flex;
  align-items: center;
  padding: var(--space-3) var(--space-5);
  background: var(--accent);
  color: var(--text-on-accent);
  font-size: var(--fs-small);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-btn);
  text-transform: uppercase;
  text-decoration: none;
  transition: background var(--dur-quick) var(--ease-standard);
}

#block-frontgrade-content .fg-interior .component-hero-landing__links a:hover { background: var(--accent-hover); }


/* --------------------------------------------------------------------------
   5. THE BODY FRAME
   Each component becomes a band. The band, not the component, owns the
   vertical rhythm, so two components of the same kind in a row read as one
   continuous section instead of two stacked blocks with doubled padding.
   -------------------------------------------------------------------------- */

.fg-interior .fg-interior__body { background: var(--surface-base); }

#block-frontgrade-content .fg-interior .fg-interior__body :where(.component-text,
                                       .component-two-columns,
                                       .component-image,
                                       .component-link-list,
                                       .component-file-attachments,
                                       .component-switchback,
                                       .component-featured-news) {
  padding-block: var(--section-y);
}

/* Consecutive components on the same surface share one band. */
#block-frontgrade-content .fg-interior .component-text + .component-text,
#block-frontgrade-content .fg-interior .component-text + .component-two-columns,
#block-frontgrade-content .fg-interior .component-text + .component-image,
#block-frontgrade-content .fg-interior .component-image + .component-text { padding-top: 0; }

/* Every component that has no container of its own gets one here. */
#block-frontgrade-content .fg-interior :where(.component-text__inner,
                    .component-image__inner,
                    .component-link-list__inner,
                    .component-two-columns) {
  width: 100%;
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* Body copy reads on the text measure, not the full container. */
#block-frontgrade-content .fg-interior .component-text__inner { max-width: var(--container-text); }


/* --------------------------------------------------------------------------
   6. PROSE
   The pages that are one dense rich text block with internal structure, which
   is most of them. Headings, lists and links all come back from the reset in
   components.css, on the dark ground rather than the light one.
   -------------------------------------------------------------------------- */

/* WHERE THE PROSE COLUMN STARTS.

   Section 5 gives every component that has no container of its own the
   pattern's container: full width, capped at a measure, and centred with
   margin-inline auto. On a band whose content is as wide as the cap that is
   invisible, but long-form prose is capped again on the next line to the
   narrower text container, and a narrow box centred in a wide frame is an
   island: on a 1280px viewport the reading column starts 236px in from the
   body frame's edge with a similar margin left over on the right, while the
   headline above it, the switchbacks, the card grids and the closing call to
   action all start at the frame's own edge.

   Nothing about it is content carried, and the text was never centred. Every
   paragraph in every body in the pattern computes to text-align start; the
   only centred elements anywhere in these bodies are buttons, submit
   controls, one call to action block and a logo grid heading, and none of
   them is prose. It is a layout default meeting a narrower cap, so it is
   corrected as one: prose starts where the rest of the pattern starts and
   runs to the measure it is already capped at.

   THE GRID PLACEMENT IS NAMED TOO, because the site's own component
   stylesheet lays .component-text out as twenty four named tracks and places
   the inner block on column-2-start / column-5-end. Left alone that would
   hold the block inside the middle third of the frame no matter what its
   margins said, so the area is widened to the whole frame and the block is
   pinned to its start.

   ONLY THE BLOCKS THAT ARE A BAND OF THEIR OWN. The child combinators are
   load bearing. A .component-text nested inside a two column layout or a
   switchback is a column within that layout and its indent is that layout's
   business, so those are left exactly as they are; on this site that is five
   blocks, against forty two that are bands in their own right.               */
#block-frontgrade-content .fg-interior .fg-interior__body > .component-text > .component-text__inner {
  grid-column: 1 / -1;
  justify-self: stretch;
  width: 100%;
  max-width: calc(var(--container-max) + (var(--gutter) * 2));
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* --------------------------------------------------------------------------
   6a. THE READING COLUMN AND ITS RAIL

   Flush left is honest but it is not composed: a page of prose starting at
   the same edge as a full width card grid has no hierarchy between the two.
   From 1025px the reading column steps in by one deliberate interval and a
   hairline runs down the space that opens up, so the indent reads as a
   decision rather than as a margin that got away.

   THE INTERVAL IS A TOKEN, on the order of a twelfth of the frame. It is the
   only horizontal measurement in the body that is not the gutter or the
   container, which is what makes it legible as structure.

   THE MEASURE IS HELD, NOT SPENT. The column's cap is widened by exactly the
   interval, so stepping in moves the reading column without narrowing it and
   the paragraphs keep the measure section 6 already gives them.

   ONLY PROSE STEPS IN. Card grids, switchbacks, logo grids and the closing
   band keep the full frame, so the rail marks the difference between copy
   that is read and content that is scanned. Below 1025px there is not enough
   frame to spend on an interval, so the column returns to the plain gutter
   and the rail is not drawn at all.

   THE RAIL IS THE HERO'S LINE, CONTINUED. It is drawn in the same two quiet
   literals the signal ground uses for its traces and pads, #151923 into
   #131721, so the band's geometry carries into the page rather than stopping
   at the band's lower edge. It fades to nothing over its own length, which is
   what ends it after the first section or two without needing to know where
   the sections are. It carries the page's section token at its head, set in
   the same monospace face as the token in the band, turned to read down the
   rail. The element is empty, marked aria-hidden and pointer transparent: it
   is a drawing, and the token it repeats is already announced once in the
   band above.
   -------------------------------------------------------------------------- */

/* THE FRAME EDGE IS WHERE THE CONTAINER STARTS, NOT WHERE THE VIEWPORT DOES.

   Every band in the pattern lays its content out inside .fg-container, which
   caps at --container-max plus a gutter each side and centres what is left
   over. The body frame is full width, so anything positioned from the frame's
   own edge is in a different coordinate system than the headline above it,
   and the two only agree while the viewport is narrower than the cap. Past
   that the container starts moving right and a fixed offset from the frame
   edge starts falling behind it, until on a wide enough screen the reading
   column sits to the LEFT of the headline it belongs under and the step-in
   reads as a step-out.

   This token is the distance from the frame's edge to the container's, which
   is zero until the cap is reached and half the overflow after it. Adding it
   to any frame-level offset puts that offset in the container's coordinate
   space, so the interval measures from the same edge the headline does at
   every width. The reading column itself needs no token: it is given the
   container's own cap and auto margins, so it simply is the container, and
   the interval is added to its leading padding.                             */
.fg-interior {
  --fg-prose-indent: clamp(96px, 8vw, 120px);
  --fg-frame-edge: max(0px, (100% - (var(--container-max) + (var(--gutter) * 2))) / 2);
}

.fg-interior .fg-interior__body { position: relative; }
.fg-interior .fg-interior__rail { display: none; }

/* The rail is drawn only where the indent it marks is actually applied,
   and the test names the same selector the indent rule names, so the two
   cannot drift apart. A browser without :has() ignores both :has() rules
   and the base display: none above stands, so the rail simply never
   renders there. */
.fg-interior .fg-interior__body:not(:has(> .component-text > .component-text__inner)) .fg-interior__rail {
  display: none;
}

@media (min-width: 1025px) {
  #block-frontgrade-content .fg-interior .fg-interior__body > .component-text > .component-text__inner {
    padding-inline-start: calc(var(--gutter) + var(--fg-prose-indent));
  }

  .fg-interior .fg-interior__body:has(> .component-text > .component-text__inner) .fg-interior__rail {
    display: block;
    position: absolute;
    z-index: 0;
    top: var(--section-y);
    left: calc(var(--fg-frame-edge) + var(--gutter) + (var(--fg-prose-indent) / 2));
    width: 1px;
    height: min(1080px, 68%);
    background: linear-gradient(180deg, #151923 0%, #131721 52%, transparent 100%);
    pointer-events: none;
  }

  .fg-interior .fg-interior__rail::before {
    content: attr(data-fg-section);
    position: absolute;
    top: var(--space-7);
    left: 50%;
    transform: translateX(-50%) translateX(-9px);
    writing-mode: vertical-rl;
    font-family: var(--font-mono);
    font-size: var(--fs-2xs);
    font-weight: var(--fw-semibold);
    letter-spacing: 0.24em;
    text-transform: uppercase;
    white-space: nowrap;
    color: var(--text-secondary);      /* 5.57:1 on --surface-base */
  }
}

#block-frontgrade-content .fg-interior .component-text p {
  margin: 0 0 var(--space-4);
  max-width: var(--measure-prose);
  font-size: var(--fs-prose);
  line-height: var(--lh-prose);
  color: var(--text-muted);
}

#block-frontgrade-content .fg-interior .component-text p:last-child { margin-bottom: 0; }

#block-frontgrade-content .fg-interior .component-text p.intro-text {
  font-size: var(--fs-lede);
  line-height: var(--lh-lede);
  color: var(--text-primary);
}

/* A HEADING SHARES THE COLUMN OF THE PROSE IT HEADS.

   WHAT WAS WRONG. These two took --measure-tight, which is 380px, while the
   paragraphs directly beneath them take --measure-prose and run 729.3px at
   1440. The heading was wrapping in a box half the width of its own body at
   the same left edge, and the longer the heading the worse it read: measured
   at 1440, "B. Frontgrade Technologies' data protection responsibilities" on
   /legal/privacy set 4 lines and a 179.4px box, 11 of that page's 18 section
   headings ran past two lines, and the single heading on
   /old-doing-business-us set 9 lines and a 403.6px box.

   WHY THE HEADING CANNOT SIMPLY TAKE --measure-prose. That token is 68ch, and
   ch is a character of the element's OWN font. On a paragraph that is 68
   characters of Inter at --fs-prose, which is the reading measure it is meant
   to be. On a heading it is 68 characters of Barlow Condensed at 44px, which
   is 1,276.8px at 1440: measured, and 547px wider than the body it heads, so
   pointing the heading at the same token overshoots in the other direction
   and misaligns the two just as badly.

   WHAT IT TAKES INSTEAD. The same column, stated in the body's characters
   rather than the heading's. 68 characters of the body face is 42.9 times
   --fs-prose: Inter's ch is a font metric and measures 0.6307em, constant at
   every size in the clamp, so the product tracks --fs-prose through its own
   clamp exactly. Verified against the live paragraph column at six widths:
   685.9px at the clamp's 16px floor, 714.7px at 768, and 729.3px from 1024
   up, matching the measured paragraph to within a pixel at each. The 100%
   term is what caps both of them to the container on a phone, where the
   column is the container and neither reaches its measure.

   Nothing else about either heading changes: same size, same face, same
   colour, same margins, same left edge. --measure-tight keeps its other three
   uses in this file, where it caps narrow furniture rather than reading. */
/* Declared on the pattern's root rather than on the text block, because the
   jump list in section 6b is a sibling of the text block and has to line up
   with the same column. */
#block-frontgrade-content .fg-interior {
  --measure-reading: min(100%, calc(42.9 * var(--fs-prose)));
}

#block-frontgrade-content .fg-interior .component-text h2 {
  margin: var(--space-7) 0 var(--space-4);
  font: var(--type-h2);
  letter-spacing: var(--ls-h2);
  max-width: var(--measure-reading);
  color: var(--text-primary);
}

#block-frontgrade-content .fg-interior .component-text h3 {
  margin: var(--space-6) 0 var(--space-3);
  font: var(--type-h3);
  letter-spacing: var(--ls-h3);
  max-width: var(--measure-reading);
  color: var(--text-primary);
}

#block-frontgrade-content .fg-interior .component-text h4 {
  margin: var(--space-5) 0 var(--space-2);
  font-size: var(--fs-h4);
  font-weight: var(--fw-semibold);
  color: var(--text-primary);
}

#block-frontgrade-content .fg-interior .component-text :where(h2, h3, h4):first-child { margin-top: 0; }

#block-frontgrade-content .fg-interior .component-text :where(ul, ol) {
  margin: 0 0 var(--space-4);
  padding-left: var(--space-5);
  max-width: var(--measure-prose);
  color: var(--text-muted);
  font-size: var(--fs-prose);
  line-height: var(--lh-prose);
}

#block-frontgrade-content .fg-interior .component-text ul { list-style: disc; }
#block-frontgrade-content .fg-interior .component-text ol { list-style: decimal; }
#block-frontgrade-content .fg-interior .component-text li { margin-bottom: var(--space-1); }
#block-frontgrade-content .fg-interior .component-text li::marker { color: var(--accent); }

#block-frontgrade-content .fg-interior .component-text strong { color: var(--text-primary); font-weight: var(--fw-semibold); }

#block-frontgrade-content .fg-interior .component-text a {
  color: var(--accent-text-safe);
  text-decoration: underline;
  text-underline-offset: 0.2em;
  text-decoration-thickness: 1px;
  transition: color var(--dur-quick) var(--ease-standard);
}

#block-frontgrade-content .fg-interior .component-text a:hover,
#block-frontgrade-content .fg-interior .component-text a:focus-visible { color: var(--text-primary); }

#block-frontgrade-content .fg-interior .component-text hr {
  margin: var(--space-6) 0;
  border: 0;
  border-top: var(--border-hairline-w) solid var(--border-hairline);
}


/* --------------------------------------------------------------------------
   6b. THE LONG DOCUMENT  ·  the heading rank and the jump list

   WHAT A LONG DOCUMENT IS, AND WHY IT IS COUNTED RATHER THAN LISTED. A page
   in this pattern earns .fg-interior--doc from the template when its body
   carries six or more section headings. The threshold is measured, not
   chosen: across all 33 interior-pattern pages the heading counts are 18 on
   /legal/privacy and then 4, 3, 2 and a long tail of 1 and 0, so six sits in
   an empty gap fourteen headings wide and the class reaches exactly the one
   page that is a document rather than a page with sections. A second policy
   document earns it by being written, with nothing to add here.

   WHY THE RANK STEPS DOWN. --fs-h2 is a section heading on a page of four
   sections. On a document of eighteen sections and roughly 14,800px it is
   display type used as a signpost, and it was the worst-looking type on the
   site: 44px headings, three and four lines each, over 17px body. The step
   takes the section heading to the --fs-h3 role and the subsection to --fs-h4,
   so the ladder inside the document keeps three distinct ranks and no new
   size is introduced. Measured on /legal/privacy, the tallest heading box
   goes from 179.4px to 26.5px at 1440 and from 179.4px to 41.1px at 390, and
   the page's own height falls with it.

   THE JUMP LIST IS THE DOCUMENT'S TABLE OF CONTENTS. It is generated by the
   template from the headings already in the body, so nothing about the
   content changes and a section added by an editor appears in it. Its anchors
   need no scroll offset of their own: components.css already gives every
   element carrying an id a scroll-margin-top clear of the fixed header.
   -------------------------------------------------------------------------- */

#block-frontgrade-content .fg-interior--doc .component-text h2 {
  margin: var(--space-6) 0 var(--space-3);
  font-size: var(--fs-h3);
  letter-spacing: var(--ls-h3);
}

#block-frontgrade-content .fg-interior--doc .component-text h3 {
  margin: var(--space-5) 0 var(--space-2);
  font-size: var(--fs-h4);
}

/* THE LIST SITS IN THE READING COLUMN, not in a rail: the pattern's rail is
   already taken by the section token, and a document's contents belong with
   the document. It is a panel on the next surface up with one accent edge,
   which is the same treatment section 9's card band and section 10's download
   band use, so it reads as part of this design language rather than as a
   widget dropped into it. */
#block-frontgrade-content .fg-interior--doc .fg-doc-nav {
  margin: 0 0 var(--space-7);
  padding: var(--space-5) var(--space-5) var(--space-5) var(--space-6);
  max-width: var(--measure-reading);
  background: var(--surface-raised);
  border-inline-start: 2px solid var(--accent);
}

#block-frontgrade-content .fg-interior--doc .fg-doc-nav__title {
  margin: 0 0 var(--space-3);
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  font-weight: var(--fw-semibold);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-secondary);       /* 5.57:1 on --surface-raised */
}

/* The numerals are the document's own lettering, which is already the first
   token of every heading it lists, so the list is not numbered a second
   time. */
#block-frontgrade-content .fg-interior--doc .fg-doc-nav__list {
  margin: 0;
  padding: 0;
  max-width: none;
  list-style: none;
  display: grid;
  gap: var(--space-2);
}

#block-frontgrade-content .fg-interior--doc .fg-doc-nav__item { margin: 0; }

#block-frontgrade-content .fg-interior--doc .fg-doc-nav__link {
  display: inline-block;
  font-size: var(--fs-prose);
  line-height: var(--lh-heading);
  color: var(--text-muted);           /* 5.57:1 on --surface-raised */
  text-decoration: none;
  transition: color var(--dur-quick) var(--ease-standard);
}

#block-frontgrade-content .fg-interior--doc .fg-doc-nav__link:hover,
#block-frontgrade-content .fg-interior--doc .fg-doc-nav__link:focus-visible {
  color: var(--text-primary);
  text-decoration: underline;
  text-underline-offset: 0.2em;
}

/* Two columns where there is room for them, so an eighteen entry list does
   not push the document itself off the first screen. Below this the list is
   one column, which is what the grid above already gives it. */
@media (min-width: 800px) {
  #block-frontgrade-content .fg-interior--doc .fg-doc-nav__list {
    grid-template-columns: 1fr 1fr;
    column-gap: var(--space-5);
  }
}

@media (forced-colors: active) {
  #block-frontgrade-content .fg-interior--doc .fg-doc-nav {
    border: 1px solid CanvasText;
    border-inline-start-width: 2px;
  }
  #block-frontgrade-content .fg-interior--doc .fg-doc-nav__link { color: LinkText; }
}


/* --------------------------------------------------------------------------
   7. TWO COLUMNS  ·  Locations
   Locations is eight sites written by hand into two rich text columns. It is
   data wearing prose, and restructuring it is a content change, not a
   template one, so the columns render as they stand. What the frame adds is a
   panel per site so the eight read as a set: the h3 inside each column starts
   a site, and the run of paragraphs under it is that site's address and
   contacts.
   -------------------------------------------------------------------------- */

#block-frontgrade-content .fg-interior .component-two-columns {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--grid-gap);
  align-items: start;
}

/* The columns and the text block inside them each carry a fixed pixel width
   from the previous design, measured against a page that was wider than this
   band. A max-width cannot undo a width, so both are released to the track,
   and min-width stops a long unbroken string in an address or a product list
   from pushing the track back open. */
#block-frontgrade-content .fg-interior .component-two-columns > * {
  min-width: 0;
  width: auto;
  max-width: none;
}

/* Same story one level down, and the same reason for the id: the text block
   inside a column is placed by a viewport sized grid declared under
   #block-frontgrade-content, so a class-only rule cannot release it. */
#block-frontgrade-content .fg-interior .component-two-columns .component-text,
#block-frontgrade-content .fg-interior .component-two-columns .component-text__inner {
  display: block;
  width: auto;
  max-width: none;
  margin-inline: 0;
  padding-inline: 0;
}

#block-frontgrade-content .fg-interior .component-two-columns .component-text { padding-block: 0; }

/* An h3 here is the same level as an h3 in a single column block, so it takes
   the same role. It read --fs-h4 while .component-text h3 a few rules up reads
   --type-h3, which put the same element at 18.22px inside a two column layout
   and 25.28px outside one, on the same page. The rule below keeps the divider
   treatment that makes these read as subsections and only corrects the size. */
#block-frontgrade-content .fg-interior .component-two-columns h3 {
  margin-top: var(--space-7);
  padding-top: var(--space-5);
  border-top: var(--border-hairline-w) solid var(--border-hairline);
  font-size: var(--fs-h3);
  letter-spacing: var(--ls-h3);
  color: var(--text-primary);
}

#block-frontgrade-content .fg-interior .component-two-columns h3:first-child { margin-top: 0; }

#block-frontgrade-content .fg-interior .component-two-columns p,
#block-frontgrade-content .fg-interior .component-two-columns :where(ul, ol) {
  font-size: var(--fs-body);
  line-height: var(--lh-body);
}


/* --------------------------------------------------------------------------
   8. IMAGE
   Any image component in the pattern. Bounded, on the container's width, with
   the hairline the rest of the pattern uses instead of a frame of its own.
   Locations used to be the only page that carried one; its map is now drawn
   rather than photographed and is section 8a, so this rule is what a future
   image component on any of these pages gets.
   -------------------------------------------------------------------------- */

#block-frontgrade-content .fg-interior .component-image__image img {
  width: 100%;
  height: auto;
  border: var(--border-hairline-w) solid var(--border-hairline);
  background: var(--surface-raised);
}


/* --------------------------------------------------------------------------
   8a. THE DRAWN LOCATIONS MAP
   The eight sites on one world map, in the image component's own slot on
   /locations. The ground is an inline SVG dot lattice, the marks are a second
   SVG on the same coordinate space, and the labels are HTML chips over both;
   templates/paragraph/paragraph--image--fg2-locations-map.html.twig holds the
   markup and says how the page reaches it.

   NOTHING HERE DECLARES A COLOUR OF ITS OWN. The ground file ships with no
   fill at all and every mark, chip and rule below is painted from the token
   set, so the map joins the contrast table rather than standing outside it,
   and a token change carries it. Three greys and one orange: --surface-base
   for the sea, --stroke-quiet for the land and the leaders, --surface-raised
   and --border-hairline for the chips, --text-primary and --text-muted for
   their two lines, and --accent for the markers and the Headquarters edge.
   There is no second orange anywhere in the component.

   THE LEADER LINE IS THE ONE VALUE THAT MOVED. The approved artwork drew it
   at rgba(255,255,255,0.22), which is not in the token set. --stroke-quiet at
   0.55, which is the opacity the marker rings already use, composites on
   --surface-base to a relative luminance of 0.069 against that value's 0.056,
   so it is the same weight of hairline stated in the system's own terms.

   ONE COORDINATE SPACE, AND IT IS THE PICTURE'S, NOT THE VIEWPORT'S. Both
   SVGs carry viewBox 0 0 1200 675 and the box holds that ratio, so a marker
   at x=338.43 is at 28.2 percent of the width whatever the width is. The
   chips have to agree with that or the leaders stop meeting them, so the box
   is an inline-size container and EVERY chip metric below is in cqw, one
   hundredth of the map's own width: at a 1200px map they are the pixel values
   the artwork was approved at, and at any other width the whole picture is
   the same picture at a different size.

   THAT IS DELIBERATELY NOT WHAT THE REVIEW FILE DID. It sized the chips with
   clamp() against vw, which ties a chip's type to the window while its
   position and width are tied to the map, and the two stop agreeing as soon
   as the map is not a fixed fraction of the window: at a narrow window the
   chip's floor-clamped type made the chip render relatively taller than the
   1200-unit model assumed and squeezed a 16px column gap down to 3px, and at
   the same time the longest city line overran a chip that had not grown with
   it. Both faults are the same fault, and stating every metric in cqw is what
   removes it rather than re-tuning around it. The 84 and 92 unit column
   pitches are then true at every width instead of at one.

   UNDER 700px THE CHIP BECOMES A NUMBERED BADGE, AND THE ADDRESS LIST TAKES
   THE SAME NUMBERS. The words cannot survive that width: at a 390px viewport
   the map is 318px across, which puts the city line at 4.5px and the marker
   dot at 1.4px. Dropping them is right. Dropping the whole layer, which is
   what this section did first, is not: it left an unlabelled dot field that
   states nothing, and the map is on the page to state where the eight sites
   are. So the layer stays and every metric in it is restated at a size that
   reads, in the step-down block at the end of this section.

   THE BADGES SIT ON THE CHIPS' OWN ANCHORS, WHICH IS WHY THE LEADERS STILL
   MEET THEM. A chip's anchor is its vertical centre and the pinned edge is a
   modifier, and every leader was drawn to stop 6 units outside that edge at
   exactly that centre height. A badge that keeps the anchor and the modifier
   and changes only its own width and height inherits that relationship
   unchanged, so nothing about the leader geometry is re-derived here.

   AND IT IS WHY THE BADGES ARE FANNED RATHER THAN PLACED ON THE MARKERS.
   Measured at a 318px map: Hauppauge and Plainview are 0.4px apart, York is
   4.9px from Hauppauge and Exeter is 4.2px from it, so the four north-east
   sites all fall inside a 9px circle. Any badge large enough to carry a digit
   is larger than that, so badges at the true points would merge those four
   into one mark whatever their size. The chips answer that problem already,
   by fanning the crowded sites into a column with a leader back to each true
   point, and the badges inherit the answer instead of restating it.

   THE BADGE IS THE MARKER, AND NOTHING UNDER 7px IS DRAWN. The marker groups
   are hidden at these widths rather than enlarged: a ring is a 1 unit stroke,
   which is 0.26px at a 318px map, and a dot grown to a legible 7px would put
   four overlapping discs on the north-east cluster, which is the smudge the
   enlargement was meant to avoid. What remains is the leader, given a
   non-scaling stroke so it renders at a true pixel instead of a quarter of
   one, running from the site's real coordinate to the badge that names it.
   -------------------------------------------------------------------------- */

/* THE BOX. It carries the ratio, it clips, and it is the container every
   metric in this section is measured against. */
#block-frontgrade-content .fg-interior .fg-locmap {
  position: relative;
  width: 100%;
  aspect-ratio: 1200 / 675;
  overflow: hidden;
  background: var(--surface-base);
  border: var(--border-hairline-w) solid var(--border-hairline);
  container-type: inline-size;
}

#block-frontgrade-content .fg-interior .fg-locmap__ground,
#block-frontgrade-content .fg-interior .fg-locmap__marks {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
}

/* THE GROUND. 5,512 land dots as subpaths of one path, so the field is one
   element rather than five thousand. */
#block-frontgrade-content .fg-interior .fg-locmap__sea { fill: var(--surface-base); }
#block-frontgrade-content .fg-interior .fg-locmap__land { fill: var(--stroke-quiet); }

/* THE CALLOUT LAYER. The marks and the chips are one layer so the rule that
   drops them at narrow widths is one rule.

   IT TAKES NO POINTER EVENTS AND THE CHIPS TAKE THEM BACK. The layer covers
   the whole map, so leaving it live would put an invisible sheet over the
   band; the chips themselves are given events again because they are real
   text and a reader has to be able to select it. */
#block-frontgrade-content .fg-interior .fg-locmap__callouts {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

#block-frontgrade-content .fg-interior .fg-locmap__marks { overflow: visible; }

#block-frontgrade-content .fg-interior .fg-locmap__leader {
  stroke: var(--stroke-quiet);
  stroke-opacity: 0.55;
  stroke-width: 1;
}

#block-frontgrade-content .fg-interior .fg-locmap__halo {
  fill: none;
  stroke: var(--accent-wash-30);
  stroke-width: 1;
}

#block-frontgrade-content .fg-interior .fg-locmap__ring {
  fill: none;
  stroke: var(--accent);
  stroke-opacity: 0.55;
  stroke-width: 1;
}

/* Headquarters is the one marker allowed to differ: a third ring, a fully
   opaque middle one and a heavier centre. Still the one accent. */
#block-frontgrade-content .fg-interior .fg-locmap__ring--hq {
  stroke-opacity: 1;
  stroke-width: 1.4;
}

#block-frontgrade-content .fg-interior .fg-locmap__dot { fill: var(--accent); }

/* THE CHIP. 16 percent of the map's width, which is the width measured
   against the real font rather than guessed: "Colorado Springs" is the
   longest city line on the page and it needs a shade over 15 percent at this
   type size, so 16 leaves the design's 8px a side at the map's full size and
   the same proportion at every other size.

   The anchor is inline on the element because left and top ARE the data; the
   edge the anchor pins is a modifier, because that is a layout decision. */
#block-frontgrade-content .fg-interior .fg-locmap__chip {
  position: absolute;
  display: block;
  width: 16%;
  padding: 0.4167cqw 0.8333cqw 0.5cqw;
  background: var(--surface-raised);
  border: var(--border-hairline-w) solid var(--border-hairline);
  white-space: nowrap;
  transform: translate(0, -50%);
  pointer-events: auto;
}

#block-frontgrade-content .fg-interior .fg-locmap__chip--pin-right {
  transform: translate(-100%, -50%);
}

#block-frontgrade-content .fg-interior .fg-locmap__chip--hq {
  border-color: var(--border-accent);
}

#block-frontgrade-content .fg-interior .fg-locmap__city {
  display: block;
  font: var(--fw-display) 1.4167cqw/1.05 var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--text-primary);
}

#block-frontgrade-content .fg-interior .fg-locmap__region {
  display: block;
  margin-top: 0.1667cqw;
  font: var(--fw-regular) 0.9167cqw/1.3 var(--font-body);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--text-muted);
}

#block-frontgrade-content .fg-interior .fg-locmap__tag {
  display: block;
  margin-bottom: 0.25cqw;
  font: var(--fw-regular) 0.8333cqw/1.2 var(--font-body);
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--accent);
}

/* The badge's digit is markup at every width and is drawn at one. A chip
   that carries its words has no use for an index, and hiding the element
   rather than the text is also what keeps it out of the accessibility tree
   on the widths where the words are read. */
#block-frontgrade-content .fg-interior .fg-locmap__num { display: none; }


/* ---- THE HEADING AND THE MAP ARE ONE BAND -------------------------------

   "Find the Frontgrade team you need" is a text component holding a single
   h2 and nothing else, and the drawn map is the component immediately after
   it. Section 5 already says two components in a row share one band and
   zeroes the second one's top padding, but a band has two ends: the heading
   still spent the band's full bottom padding and the module's own 4rem
   outer margin before the picture it introduces began. Measured at a 1440px
   viewport that was 167.7px between the two, against the 16px this pattern
   puts between a heading and the prose it introduces on every other page in
   the family. Both ends are closed here, so what separates them is the
   heading's own margin and nothing else.

   IT IS WRITTEN AGAINST .fg-locmap-component RATHER THAN .component-image
   so that the rule can only ever describe this one relationship. The map's
   own template adds that class and no other component on the site carries
   it; a future image component on some other page in the pattern gets
   section 5's band, unchanged, which is what it should get.               */
#block-frontgrade-content .fg-interior .component-text:has(+ .fg-locmap-component) {
  padding-bottom: 0;
  margin-bottom: 0;
}

#block-frontgrade-content .fg-interior .component-text + .fg-locmap-component { margin-top: 0; }


/* ---- UNDER 700px  ·  THE BADGES AND THE NUMBERED LIST --------------------
   The step-down is here rather than in section 19 for the same reason the
   portrait wall's own is in section 14: it restates one component, it is not
   the grid ramp the pattern shares. Every figure quoted below is measured at
   a 390px viewport, where this map renders 318px wide.                     */
@media (max-width: 700px) {

  /* The words go. Everything they said is in the address list below, at
     reading size, and at this width they would set at 4.5px. */
  #block-frontgrade-content .fg-interior .fg-locmap__tag,
  #block-frontgrade-content .fg-interior .fg-locmap__city,
  #block-frontgrade-content .fg-interior .fg-locmap__region { display: none; }

  /* The marks go with them. See the section head: nothing this layer can
     draw at a 318px map clears 7px without merging the north-east four. */
  #block-frontgrade-content .fg-interior .fg-locmap__mark { display: none; }

  /* The leader stays, at a true pixel rather than the quarter of one the
     viewBox scale would give it. Colour and opacity are the section's own. */
  #block-frontgrade-content .fg-interior .fg-locmap__leader {
    vector-effect: non-scaling-stroke;
  }

  /* THE BADGE. 16px is the smallest circle that holds a 10px digit with
     3px of ground on every side of it, and it is what the column pitches
     allow: the anchors are 22.3px apart down the eastern column and 22.3px
     and 24.4px down the western one, so 16px leaves 6.3px between badges at
     the tightest. Nothing here touches transform, so the anchor and the
     pinned edge stay exactly the ones the chip rules above set. */
  #block-frontgrade-content .fg-interior .fg-locmap__chip {
    display: grid;
    place-items: center;
    width: 16px;
    height: 16px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: var(--accent);
  }

  /* Headquarters keeps the one distinction it has at wide widths, restated
     as the third ring its marker carries rather than as a border colour it
     no longer has. An outline draws outside the box, so it costs the badge
     no room: 2px of extra radius against 8.4px of clearance to Carlsbad. */
  #block-frontgrade-content .fg-interior .fg-locmap__chip--hq {
    outline: var(--border-hairline-w) solid var(--accent);
    outline-offset: 1px;
  }

  /* --text-on-accent on --accent is 5.22:1, the row tokens.css states. */
  #block-frontgrade-content .fg-interior .fg-locmap__num {
    display: block;
    font: var(--fw-display) 10px/1 var(--font-display);
    font-variant-numeric: tabular-nums;
    letter-spacing: 0;
    color: var(--text-on-accent);
  }

  /* THE ADDRESS LIST TAKES THE SAME NUMBERS, and the counter is what makes
     them the same numbers: it runs over the list's own headings in document
     order, which is the order the eight values in the template were read
     off. The list is one column at this width already, from section 19's
     860px rule, so document order is reading order too.

     THE PAGE IS NAMED BY :has(), not by an alias or an id. This component
     is only ever printed on the page the map is printed on, so a body that
     contains the map is the page that wants numbered addresses; any other
     page carrying a two column component is untouched, and no rule here
     needs to know a path or a node id.

     THE ACCESSIBLE NAMES DO NOT MOVE, and that was measured rather than
     assumed. Chrome's accessibility tree was read for these eight headings
     at 390 and at 1440 and the names are the same eight strings at both,
     with no digit in front: the counter is painted, and the layout tree at
     390 does carry the glyphs 1 to 8 in order, but the name computation
     does not take it. So the number is a visual index for a sighted reader
     pairing the list against the badges, and a reader who is not looking at
     the map hears the list exactly as it has always read. */
  #block-frontgrade-content .fg-interior:has(.fg-locmap-component) .component-two-columns {
    counter-reset: fg-locmap-site;
  }

  #block-frontgrade-content .fg-interior:has(.fg-locmap-component) .component-two-columns h3 {
    counter-increment: fg-locmap-site;
  }

  #block-frontgrade-content .fg-interior:has(.fg-locmap-component) .component-two-columns h3::before {
    content: counter(fg-locmap-site);
    display: inline-block;
    min-width: 1.5em;
    font-variant-numeric: tabular-nums;
    color: var(--accent);
  }
}


/* --------------------------------------------------------------------------
   9. LINK LISTS  ·  the card and directory band
   Every link list, switchback list and directory on these pages arrives as
   this one component: Support is four of them and is the whole page, Contact
   is eight, Supplier Resources has one of downloads and one of outbound
   government links.

   Two shapes come out of one markup. A list whose entries carry a line of
   description becomes a grid of arrow cards; a list of bare labels becomes a
   stack of rows. The switch is :has(), on the description element the
   paragraph only renders when the field has a value, so the shape follows the
   content instead of a per page override.
   -------------------------------------------------------------------------- */

#block-frontgrade-content .fg-interior .component-link-list {
  background: var(--surface-raised);
  border-top: var(--border-hairline-w) solid var(--border-hairline);
  border-bottom: var(--border-hairline-w) solid var(--border-hairline);
}

#block-frontgrade-content .fg-interior .component-link-list__heading {
  margin: 0 0 var(--space-5);
  font: var(--type-h2);
  letter-spacing: var(--ls-h2);
  color: var(--text-primary);
}

/* The row and the card inside it both carry a track width from the previous
   design's grid. Released, the card fills the column the band gave it. */
#block-frontgrade-content .fg-interior .component-link-list__link-item {
  display: flex;
  width: auto;
  max-width: none;
  margin: 0;
}

#block-frontgrade-content .fg-interior .component-link-list__link { width: 100%; }

#block-frontgrade-content .fg-interior .component-link-list__links {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--grid-gap);
  margin: 0;
  padding: 0;
  list-style: none;
}

#block-frontgrade-content .fg-interior .component-link-list__link {
  display: flex;
  align-items: flex-start;
  gap: var(--space-4);
  height: 100%;
  padding: var(--space-5);
  background: var(--surface-panel);
  border: var(--border-hairline-w) solid var(--border-hairline);
  color: var(--text-muted);
  text-decoration: none;
  transition: border-color var(--dur-quick) var(--ease-standard),
              background var(--dur-quick) var(--ease-standard);
}

#block-frontgrade-content .fg-interior .component-link-list__link:hover,
#block-frontgrade-content .fg-interior .component-link-list__link:focus-visible {
  border-color: var(--border-accent);
  background: var(--surface-elevated);
}

#block-frontgrade-content .fg-interior .component-link-list__content {
  display: block;
  flex: 1 1 auto;
  min-width: 0;
}

#block-frontgrade-content .fg-interior .component-link-list__link-text {
  display: block;
  font-family: var(--font-display);
  font-size: var(--fs-h4);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-heading);
  color: var(--text-primary);
}

#block-frontgrade-content .fg-interior .component-link-list__link-description {
  display: block;
  margin-top: var(--space-2);
  font-size: var(--fs-small);
  line-height: var(--lh-body);
  color: var(--text-muted);
}

/* The arrow is the component's own inline SVG, drawn black for a light
   ground. It is re-pointed at currentColor rather than replaced. */
#block-frontgrade-content .fg-interior .component-link-list__link-icon {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  color: var(--text-secondary);
  transition: color var(--dur-quick) var(--ease-standard),
              transform var(--dur-quick) var(--ease-out-expo);
}

#block-frontgrade-content .fg-interior .component-link-list__link-icon svg { width: 28px; height: auto; }
#block-frontgrade-content .fg-interior .component-link-list__link-icon svg path { fill: currentColor; }

#block-frontgrade-content .fg-interior .component-link-list__link:hover .component-link-list__link-icon,
#block-frontgrade-content .fg-interior .component-link-list__link:focus-visible .component-link-list__link-icon {
  color: var(--accent);
  transform: translateX(3px);
}

/* A list of bare labels has no card to fill, so it becomes rows. */
#block-frontgrade-content .fg-interior .component-link-list__links:not(:has(.component-link-list__link-description)) {
  grid-template-columns: minmax(0, 1fr);
  gap: 0;
}

#block-frontgrade-content .fg-interior .component-link-list__links:not(:has(.component-link-list__link-description)) .component-link-list__link {
  align-items: center;
  padding: var(--space-4) var(--space-5);
  background: transparent;
  border: 0;
  border-bottom: var(--border-hairline-w) solid var(--border-hairline);
}

#block-frontgrade-content .fg-interior .component-link-list__links:not(:has(.component-link-list__link-description)) .component-link-list__link:hover {
  background: var(--surface-panel);
}

#block-frontgrade-content .fg-interior .component-link-list__links:not(:has(.component-link-list__link-description)) .component-link-list__link-text {
  font-size: var(--fs-body);
}


/* --------------------------------------------------------------------------
   10. DOWNLOADS
   Certification and policy documents, on Ethics and on Supplier Resources.
   A stack of rows, not a grid: a document is a name and a file, and a card
   around each one would claim more than the row holds.
   -------------------------------------------------------------------------- */

/* The three elements the previous design laid out as nested grids are reset to
   normal flow first. Their track definitions were written for a sidebar
   column on a light page and, left in place, they size the list to a width
   the band never gave it and defeat the centring below. */
#block-frontgrade-content .fg-interior .component-file-attachments {
  display: block;
  margin: 0;
  background: var(--surface-raised);
  border-top: var(--border-hairline-w) solid var(--border-hairline);
  border-bottom: var(--border-hairline-w) solid var(--border-hairline);
}

#block-frontgrade-content .fg-interior .component-file-attachments > .file-attachments,
#block-frontgrade-content .fg-interior .component-file-attachments > .file-attachments__sidebar {
  display: block;
  width: 100%;
  max-width: var(--container-text);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

#block-frontgrade-content .fg-interior .file-attachments__container {
  display: block;
  margin: 0;
  padding: 0;
  list-style: none;
  border-top: var(--border-hairline-w) solid var(--border-hairline);
}

#block-frontgrade-content .fg-interior .file-attachments__container > li {
  border-bottom: var(--border-hairline-w) solid var(--border-hairline);
}

#block-frontgrade-content .fg-interior .file-attachments__container > li > a {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-4) var(--space-2);
  color: var(--text-primary);
  font-size: var(--fs-body);
  font-weight: var(--fw-medium);
  text-decoration: none;
  transition: color var(--dur-quick) var(--ease-standard),
              background var(--dur-quick) var(--ease-standard);
}

#block-frontgrade-content .fg-interior .file-attachments__container > li > a:hover,
#block-frontgrade-content .fg-interior .file-attachments__container > li > a:focus-visible {
  color: var(--accent-text-safe);
  background: var(--surface-panel);
}

#block-frontgrade-content .fg-interior .file-attachments__container svg { flex: 0 0 auto; }
#block-frontgrade-content .fg-interior .file-attachments__container svg path { fill: currentColor; }


/* --------------------------------------------------------------------------
   11. SWITCHBACKS  ·  About
   Three alternating copy and image blocks. The component already alternates
   its own sides; the frame gives it the band surfaces, the eyebrow treatment
   and the button, none of which it had on a dark ground.
   -------------------------------------------------------------------------- */

#block-frontgrade-content .fg-interior .component-switchback { background: var(--surface-base); }
#block-frontgrade-content .fg-interior .component-switchback:nth-of-type(even) { background: var(--surface-raised); }

#block-frontgrade-content .fg-interior .component-switchback__inner {
  width: 100%;
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

#block-frontgrade-content .fg-interior .component-switchback__accent { color: var(--accent); }
#block-frontgrade-content .fg-interior .component-switchback__accent svg path { fill: currentColor; }

#block-frontgrade-content .fg-interior .component-switchback__eyebrow-text {
  margin-bottom: var(--space-3);
  font-size: var(--fs-eyebrow);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-eyebrow);
  text-transform: uppercase;
  color: var(--accent-text-safe);
}

#block-frontgrade-content .fg-interior .component-switchback__heading {
  margin: 0 0 var(--space-4);
  font: var(--type-h2);
  letter-spacing: var(--ls-h2);
  max-width: var(--measure-tight);
  color: var(--text-primary);
}

#block-frontgrade-content .fg-interior .component-switchback__text p {
  margin: 0 0 var(--space-4);
  max-width: var(--measure-body);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--text-muted);
}

#block-frontgrade-content .fg-interior .component-switchback__button a {
  display: inline-flex;
  align-items: center;
  padding: var(--space-3) var(--space-5);
  background: var(--accent);
  color: var(--text-on-accent);
  font-size: var(--fs-small);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-btn);
  text-transform: uppercase;
  text-decoration: none;
  transition: background var(--dur-quick) var(--ease-standard);
}

#block-frontgrade-content .fg-interior .component-switchback__button a:hover { background: var(--accent-hover); }

/* The longest of the three labels is wider than a phone column, and the
   previous design kept it on one line. It wraps here rather than reaching
   past the edge of the page. */
#block-frontgrade-content .fg-interior .component-switchback__button a {
  max-width: 100%;
  white-space: normal;
  text-align: center;
}

#block-frontgrade-content .fg-interior .component-switchback__image img { width: 100%; height: auto; }


/* --------------------------------------------------------------------------
   12. FEATURED NEWS  ·  About
   The site's own news cards view, embedded in the page. The cards keep their
   template; the band and the heading come from the pattern.
   -------------------------------------------------------------------------- */

#block-frontgrade-content .fg-interior .component-featured-news { background: var(--surface-raised); }

#block-frontgrade-content .fg-interior .component-featured-news__content-wrapper,
#block-frontgrade-content .fg-interior .component-featured-news__cards {
  width: 100%;
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* The heading and standfirst columns are placed on the same viewport sized
   grid the webform uses, declared under the same block id, so they are
   released the same way. See the form band for why the id is in the
   selector. */
#block-frontgrade-content .fg-interior .component-featured-news__content-wrapper,
#block-frontgrade-content .fg-interior .component-featured-news__cards {
  display: block;
  width: 100%;
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

#block-frontgrade-content .fg-interior .component-featured-news__content,
#block-frontgrade-content .fg-interior .component-featured-news__content-column-1,
#block-frontgrade-content .fg-interior .component-featured-news__content-column-2,
#block-frontgrade-content .fg-interior .component-featured-news__text {
  display: block;
  width: auto;
  max-width: none;
  margin-inline: 0;
  padding-inline: 0;
}

#block-frontgrade-content .fg-interior .component-featured-news__heading {
  margin: 0 0 var(--space-3);
  font: var(--type-h2);
  letter-spacing: var(--ls-h2);
  color: var(--text-primary);
}

#block-frontgrade-content .fg-interior .component-featured-news__text p {
  margin: 0;
  max-width: var(--measure-body);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--text-muted);
}

#block-frontgrade-content .fg-interior .component-featured-news__cards { margin-top: var(--space-6); }


/* --------------------------------------------------------------------------
   13. CARDS FROM VIEWS  ·  news teasers
   The stacked card: a picture, then a block of copy under it, then a call to
   action at the foot. Two listings render it, the featured news view and the
   featured blog view, and the difference between them is which fields their
   templates print.

   THE PERSON IS NO LONGER ONE OF THEM. A person used to render this same card
   and section 14 only chose the grid it sat in. The leadership listing is now
   a portrait wall, which is a different object rather than a restyled card, so
   it has its own names and its own section below. Nothing in this section
   moved when that happened: these rules describe the two news listings and
   only the two news listings.
   -------------------------------------------------------------------------- */

#block-frontgrade-content .fg-interior .teaser-card { height: 100%; background: transparent; }

#block-frontgrade-content .fg-interior .teaser-card__link {
  display: flex;
  flex-direction: column;
  height: 100%;
  background: var(--surface-panel);
  border: var(--border-hairline-w) solid var(--border-hairline);
  color: var(--text-muted);
  text-decoration: none;
  transition: border-color var(--dur-quick) var(--ease-standard),
              background var(--dur-quick) var(--ease-standard);
}

#block-frontgrade-content .fg-interior .teaser-card__link:hover,
#block-frontgrade-content .fg-interior .teaser-card__link:focus-visible {
  border-color: var(--border-accent);
  background: var(--surface-elevated);
}

#block-frontgrade-content .fg-interior .teaser-card__image { overflow: hidden; }

#block-frontgrade-content .fg-interior .teaser-card__image img {
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  object-position: center top;
  transition: transform var(--dur-base) var(--ease-out-expo);
}

#block-frontgrade-content .fg-interior .teaser-card__link:hover .teaser-card__image img { transform: scale(1.03); }

#block-frontgrade-content .fg-interior .teaser-card__heading-and-link {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  gap: var(--space-3);
  padding: var(--space-5);
}

#block-frontgrade-content .fg-interior .teaser-card__heading { flex: 1 1 auto; }

#block-frontgrade-content .fg-interior .teaser-card__heading > span {
  display: block;
  font-family: var(--font-display);
  font-size: var(--fs-h4);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-heading);
  color: var(--text-primary);
}

/* The role or the standfirst, whichever the card's template prints under the
   name. It is the only other element in the heading block. */
#block-frontgrade-content .fg-interior .teaser-card__heading > div {
  margin-top: var(--space-2);
  font-size: var(--fs-small);
  line-height: var(--lh-body);
  color: var(--text-muted);
}

#block-frontgrade-content .fg-interior .teaser-card__date p {
  margin: 0;
  padding: var(--space-4) var(--space-5) 0;
  font-size: var(--fs-xs);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--text-secondary);
}

#block-frontgrade-content .fg-interior .teaser-card__cta-text-wrapper {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-btn);
  text-transform: uppercase;
  color: var(--text-secondary);
  transition: color var(--dur-quick) var(--ease-standard);
}

#block-frontgrade-content .fg-interior .teaser-card__cta-icon svg { width: 22px; height: auto; }
#block-frontgrade-content .fg-interior .teaser-card__cta-icon svg path { fill: currentColor; }

#block-frontgrade-content .fg-interior .teaser-card__link:hover .teaser-card__cta-text-wrapper,
#block-frontgrade-content .fg-interior .teaser-card__link:focus-visible .teaser-card__cta-text-wrapper { color: var(--accent); }


/* --------------------------------------------------------------------------
   14. THE PORTRAIT WALL  ·  leadership
   Twelve published people, four across, three rows, no ragged final row. The
   grid is the one this section always described and its column ramp has not
   moved; what changed is the cell. A person is a square portrait carrying the
   name, and the role and the link to the biography arrive on hover or focus.

   THE TILE IS THREE LAYERS ON ONE STACKING CONTEXT, and they are the three the
   markup emits, in paint order:
     1  the figure, stretched over the whole square with the image covering it
     2  the scrim, a bottom weighted gradient rather than a flat wash, so the
        face is not veiled at rest
     3  the body, holding the name at rest and the role plus the call to action
        on the reveal
   The accent rule is a pseudo element above all three, drawn left to right.

   THE REVEAL COSTS NO LAYOUT. Its one grid row runs 0fr to 1fr, so the hidden
   half genuinely occupies no height at rest and the square never changes size,
   and nothing above or beside a tile moves when one is entered. Everything
   else in the state is opacity, transform, filter and colour, none of which
   reaches the layout path either. A max-height animation would need a guessed
   ceiling and would overshoot every tile whose role is one line; the row
   fraction measures the content instead. It needs Chrome 107 or Safari 16;
   under those the row snaps between its two states and the rest of the reveal
   still runs, which is a degradation and not a break.

   THREE THINGS A DESIGN FILE DOES NOT HAVE TO ANSWER AND A PAGE DOES.
     A COARSE POINTER HAS NO HOVER. Under (hover: none) the reveal is simply
     on. That is a device query and not a width query on purpose: the deciding
     fact is the input device, not the screen size.
     UNDER 700px THE OVERLAY IS DROPPED ALTOGETHER. That is the width where the
     grid is already one column, so a tile is most of the screen, and a name
     that exists only inside an interaction is a name a thumb cannot read. The
     portrait keeps its square and the name and the role sit under it in normal
     flow, permanently, which is the arrangement the square portrait grid this
     pattern is drawn from uses at every width.
     FORCED COLORS TAKES THE FILTER OFF. Greyscale is a colour decision and the
     user's palette has already made it, and the gradient scrim is not painted
     in that mode, so the copy is given a ground of its own and shown.

   CONTRAST. In the narrow arrangement the copy sits on --surface-panel, where
   tokens.css measures --text-display 16.06:1, --text-primary 14.55:1 and
   --accent 4.68:1, and section 3 of that file permits the accent as small text
   on this surface. In the overlay arrangement the copy sits on the scrim's
   opaque end. The reveal's own gradient is at least 0.90 there, which is
   within a hair of --surface-base and better than the panel figures above. The
   name is the one line that shows at rest, over a scrim between 0.65 and 0.76
   where it sits; against the worst backdrop a headshot can put there, a pure
   white pixel at the 0.65 stop, --text-display computes 5.68:1, and the name
   is large text, where the threshold is 3:1.
   -------------------------------------------------------------------------- */

.fg-interior--listing .fg-interior__people { background: var(--surface-raised); }

#block-frontgrade-content .fg-interior .fg-people-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--grid-gap);
  margin: 0;
  padding: 0;
  list-style: none;
}

/* The cell is the numeral's positioning parent as well as the tile's box. The
   numeral is printed by the list template rather than the person template,
   because a node teaser cannot know which position it holds, so it is a
   sibling of the tile and not a child of it. */
#block-frontgrade-content .fg-interior .fg-people-grid__item {
  position: relative;
  display: flex;
}

#block-frontgrade-content .fg-interior .fg-people-grid__item > * { width: 100%; }

/* IT TAKES NO POINTER EVENTS, so the whole square stays one link: the numeral
   sits over the tile's top left corner and a click that lands on it passes
   through to the anchor underneath. */
#block-frontgrade-content .fg-interior .fg-people-grid__idx {
  position: absolute;
  top: var(--space-4);
  left: var(--space-5);
  z-index: 2;
  pointer-events: none;
  font-size: var(--fs-3xs);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-badge);
  color: var(--text-primary);
  opacity: 0.7;
  transition: color var(--dur-quick) var(--ease-standard),
              opacity var(--dur-quick) var(--ease-standard);
}

#block-frontgrade-content .fg-interior .fg-portrait { display: block; }

/* THE TILE. One anchor, one tab stop, and the square is held by aspect-ratio
   rather than by the file, so a portrait shot at any crop lands in the same
   frame as its neighbours.

   The two gradients are declared here as custom properties, the way the hero
   declares --fg-hero-scrim, so the resting wash and the entered wash are one
   pair of values read in two places rather than two literals repeated in a
   state rule. */
#block-frontgrade-content .fg-interior .fg-portrait__link {
  --fg-portrait-scrim: linear-gradient(180deg,
    rgba(9, 12, 20, 0.10) 0%,
    rgba(9, 12, 20, 0.10) 38%,
    rgba(9, 12, 20, 0.72) 74%,
    rgba(9, 12, 20, 0.94) 100%);
  --fg-portrait-scrim-deep: linear-gradient(180deg,
    rgba(9, 12, 20, 0.30) 0%,
    rgba(9, 12, 20, 0.52) 40%,
    rgba(9, 12, 20, 0.90) 78%,
    rgba(9, 12, 20, 0.97) 100%);

  position: relative;
  display: block;
  aspect-ratio: 1 / 1;
  overflow: hidden;
  isolation: isolate;
  background: var(--surface-panel);
  border: var(--border-hairline-w) solid var(--border-hairline);
  color: var(--text-muted);
  text-decoration: none;
  transition: border-color var(--dur-quick) var(--ease-standard);
}

/* LAYER 1  ·  the picture.
   The field arrives inside two wrappers of its own, the field template's and
   the media template's, and a percentage height resolves against the parent
   box, so both wrappers are given the height before the image is asked to
   fill it. Neither wrapper carries a class this theme controls, which is why
   they are reached by position rather than by name. */
#block-frontgrade-content .fg-interior .fg-portrait__fig {
  position: absolute;
  inset: 0;
  z-index: 1;
  margin: 0;
  overflow: hidden;
}

#block-frontgrade-content .fg-interior .fg-portrait__fig > *,
#block-frontgrade-content .fg-interior .fg-portrait__fig > * > * { height: 100%; }

/* Portraits are shot at mixed crops, so the frame is fixed and the face is
   held at the top of it rather than centred. The 1.005 scale at rest is a
   hairline overdraw that stops a subpixel seam showing at the frame edge when
   the entered state scales back down. */
#block-frontgrade-content .fg-interior .fg-portrait__fig img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center top;
  filter: grayscale(100%) contrast(1.05) brightness(0.92);
  transform: scale(1.005);
  transition: filter var(--dur-slow) var(--ease-standard),
              transform var(--dur-slow) var(--ease-out-expo);
}

/* LAYER 2  ·  the wash. */
#block-frontgrade-content .fg-interior .fg-portrait__scrim {
  position: absolute;
  inset: 0;
  z-index: 2;
  background: var(--fg-portrait-scrim);
  transition: background var(--dur-slow) var(--ease-standard);
}

/* LAYER 3  ·  the copy. */
#block-frontgrade-content .fg-interior .fg-portrait__body {
  position: absolute;
  inset: auto 0 0 0;
  z-index: 3;
  padding: var(--space-5);
}

/* THE MARGIN IS STATED, NOT INHERITED. The name is an h3, so the outline of
   the leadership listing has an entry per person; every other property here
   was already stated on the class, but the box was relying on the heading
   reset in components.css, which is `h1, h2, h3, h4, p ... { margin: 0 }` at
   one element of specificity. The legacy base.css on these pages carries
   `body h3 { margin-top: 1.5rem; margin-bottom: 1.5rem }`, which is two
   elements and wins wherever it is in the cascade, so the tile picked up 24px
   above and below the name the moment the div became a heading. Measured: the
   name moved 24px and the page grew 557px at 390. Stating it here settles it
   at this rule's own specificity, which nothing in the legacy sheet reaches,
   and restores the box the tile had as a div. */
#block-frontgrade-content .fg-interior .fg-portrait__name {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--fg-h3);
  font-weight: var(--fw-display);
  line-height: var(--lh-heading);
  letter-spacing: var(--ls-h3);
  text-transform: uppercase;
  text-wrap: balance;
  color: var(--text-display);
}

/* THE HIDDEN HALF. It is laid out at all times and moved and faded instead of
   being taken out of the flow, so the tile's own box is the same in both
   states and the browser has something to transition between. The inner
   element is the grid item the row fraction acts on, and it clips its own
   overflow so the copy is not drawn outside the collapsed row. */
#block-frontgrade-content .fg-interior .fg-portrait__reveal {
  display: grid;
  grid-template-rows: 0fr;
  opacity: 0;
  transform: translateY(var(--space-2));
  transition: grid-template-rows var(--dur-slow) var(--ease-out-expo),
              opacity var(--dur-base) var(--ease-standard),
              transform var(--dur-slow) var(--ease-out-expo);
}

#block-frontgrade-content .fg-interior .fg-portrait__reveal-inner {
  min-height: 0;
  overflow: hidden;
}

#block-frontgrade-content .fg-interior .fg-portrait__role {
  margin-top: var(--space-2);
  font-size: var(--fs-xs);
  line-height: var(--lh-body);
  color: var(--text-primary);
}

#block-frontgrade-content .fg-interior .fg-portrait__cta {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-4);
  font-size: var(--fs-3xs);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--accent);
}

#block-frontgrade-content .fg-interior .fg-portrait__cta-icon { display: inline-flex; }
#block-frontgrade-content .fg-interior .fg-portrait__cta svg { width: 18px; height: auto; flex: none; }
#block-frontgrade-content .fg-interior .fg-portrait__cta svg path { fill: currentColor; }

/* THE ACCENT RULE, drawn across the top edge from the left. Above every layer,
   two pixels tall, and scaled rather than widened so it stays off the layout
   path with the rest of the state. */
#block-frontgrade-content .fg-interior .fg-portrait__link::after {
  content: "";
  position: absolute;
  inset: 0 auto auto 0;
  z-index: 4;
  width: 100%;
  height: 2px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform var(--dur-slow) var(--ease-out-expo);
}

/* ---- THE ENTERED STATE ----------------------------------------------------
   THE KEYBOARD IS NOT AN AFTERTHOUGHT HERE. The tile is one link, so the same
   declarations answer a pointer resting on it and a caret arriving on it, and
   the three pseudo classes are written as one group rather than as a pointer
   rule with a keyboard rule bolted after it.
     :hover          a pointer that can rest
     :focus-visible  the keyboard, which is what the tab order reaches
     :focus-within   any focus at all, including the focus a tap or a click
                     leaves behind, which is what a touch browser gives a link
                     it has not yet followed
   The focus RING itself is not restated: section 18 already draws it on every
   focusable element on these surfaces, and a component that writes its own
   ring is how a site ends up with two.                                       */

#block-frontgrade-content .fg-interior .fg-portrait__link:is(:hover, :focus-visible, :focus-within) {
  border-color: var(--border-accent);
}

#block-frontgrade-content .fg-interior .fg-portrait__link:is(:hover, :focus-visible, :focus-within)::after {
  transform: scaleX(1);
}

#block-frontgrade-content .fg-interior .fg-portrait__link:is(:hover, :focus-visible, :focus-within) .fg-portrait__fig img {
  filter: grayscale(0%) contrast(1) brightness(1);
  transform: scale(1.04);
}

#block-frontgrade-content .fg-interior .fg-portrait__link:is(:hover, :focus-visible, :focus-within) .fg-portrait__scrim {
  background: var(--fg-portrait-scrim-deep);
}

#block-frontgrade-content .fg-interior .fg-portrait__link:is(:hover, :focus-visible, :focus-within) .fg-portrait__reveal {
  grid-template-rows: 1fr;
  opacity: 1;
  transform: translateY(0);
}

/* The numeral belongs to the cell rather than to the tile, so it cannot be
   reached by descending from the anchor. :has is the only combinator that
   goes back up, and this file already uses it in section 14a. */
#block-frontgrade-content .fg-interior .fg-people-grid__item:hover .fg-people-grid__idx,
#block-frontgrade-content .fg-interior .fg-people-grid__item:has(.fg-portrait__link:is(:focus-visible, :focus-within)) .fg-people-grid__idx {
  color: var(--accent);
  opacity: 1;
}

/* ---- A COARSE POINTER HAS NO HOVER ----------------------------------------
   There is no state to enter, so the role is simply shown and the tile is a
   plain link. The wash is lifted part of the way towards the entered one,
   because it is now carrying two lines of copy permanently rather than one,
   and the portrait keeps its colour for the same reason: greyscale is the
   resting half of a two state treatment and there is no second state here. */
@media (hover: none) {
  #block-frontgrade-content .fg-interior .fg-portrait__scrim {
    background: linear-gradient(180deg,
      rgba(9, 12, 20, 0.20) 0%,
      rgba(9, 12, 20, 0.45) 38%,
      rgba(9, 12, 20, 0.88) 76%,
      rgba(9, 12, 20, 0.96) 100%);
  }

  #block-frontgrade-content .fg-interior .fg-portrait__fig img {
    filter: none;
    transform: none;
  }

  #block-frontgrade-content .fg-interior .fg-portrait__reveal {
    grid-template-rows: 1fr;
    opacity: 1;
    transform: none;
  }
}

/* ---- UNDER 700px THE OVERLAY IS DROPPED -----------------------------------
   The grid is one column from this width down, so a tile is the full measure
   of the screen. The three layers come apart: the figure keeps the square and
   returns to the flow, the wash is not drawn at all, and the body sits under
   the picture as ordinary copy. Nothing here is behind an interaction.

   THIS BLOCK FOLLOWS THE (hover: none) BLOCK ON PURPOSE. The two select
   overlapping populations, a narrow touch screen matches both, and they carry
   the same specificity, so the later one is the one that lands.

   The entered state is neutralised rather than left to fire, because a mouse
   at a 690px window would otherwise still scale and recolour a picture that is
   no longer part of a reveal. The accent rule and the border are left alone:
   they mark the tile the keyboard is on, and that is worth having at every
   width.                                                                     */
@media (max-width: 700px) {
  #block-frontgrade-content .fg-interior .fg-portrait__link { aspect-ratio: auto; }

  #block-frontgrade-content .fg-interior .fg-portrait__fig {
    position: static;
    aspect-ratio: 1 / 1;
  }

  #block-frontgrade-content .fg-interior .fg-portrait__scrim { display: none; }

  #block-frontgrade-content .fg-interior .fg-portrait__body {
    position: static;
    padding: var(--space-4);
  }

  #block-frontgrade-content .fg-interior .fg-portrait__reveal {
    grid-template-rows: 1fr;
    opacity: 1;
    transform: none;
  }

  #block-frontgrade-content .fg-interior .fg-portrait__fig img,
  #block-frontgrade-content .fg-interior .fg-portrait__link:is(:hover, :focus-visible, :focus-within) .fg-portrait__fig img {
    filter: none;
    transform: none;
  }
}

/* ---- LESS MOTION ----------------------------------------------------------
   THE SETTING MEANS DO NOT ANIMATE, NOT DO NOT SHOW. The reveal still happens
   on hover and on focus; it arrives at its finished state instead of
   travelling there, and the picture does not scale.

   BOTH SWITCHES ARE ANSWERED, and they are not the same switch. The media
   query is the operating system's. html[data-motion="reduced"] is the site's
   own control, stamped on the root by motion.js, and a component that read
   only the media query would ignore a visitor who used the control on the
   page in front of them.

   THE MEDIA QUERY NEEDS ONLY THE TRANSFORM. tokens.css already collapses every
   --dur-* to 1ms inside the same query, so every transition above is over
   before it can be seen. The data attribute changes no token, so its twin has
   to take the transitions off itself, and it is written at a specificity that
   outranks what it is overriding: html[data-motion="reduced"] adds an element
   and an attribute to selectors that are already an id and two classes, and
   the entered state's transform is answered by a selector carrying the
   entered state as well.                                                     */

html[data-motion="reduced"] #block-frontgrade-content .fg-interior .fg-people-grid *,
html[data-motion="reduced"] #block-frontgrade-content .fg-interior .fg-people-grid *::after {
  transition: none;
}

html[data-motion="reduced"] #block-frontgrade-content .fg-interior .fg-portrait__fig img,
html[data-motion="reduced"] #block-frontgrade-content .fg-interior .fg-portrait__link:is(:hover, :focus-visible, :focus-within) .fg-portrait__fig img,
html[data-motion="reduced"] #block-frontgrade-content .fg-interior .fg-portrait__reveal {
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  #block-frontgrade-content .fg-interior .fg-portrait__fig img,
  #block-frontgrade-content .fg-interior .fg-portrait__link:is(:hover, :focus-visible, :focus-within) .fg-portrait__fig img,
  #block-frontgrade-content .fg-interior .fg-portrait__reveal {
    transform: none;
  }
}

/* ---- FORCED COLORS --------------------------------------------------------
   The user's palette replaces the theme's, and two things this tile does are
   incompatible with that. A greyscale filter is a colour decision taken on top
   of a palette that has already decided; and the scrim is a gradient, which
   this mode does not paint, so the copy over the picture would have no ground.

   So the filter comes off, the scrim is not drawn, the reveal is simply on,
   and the body takes a real system background of its own. Every colour below
   is a system colour: nothing in this block is a theme value, because a theme
   value in forced colors is a value the user asked not to see.               */
@media (forced-colors: active) {
  #block-frontgrade-content .fg-interior .fg-portrait__fig img {
    filter: none;
    transform: none;
  }

  #block-frontgrade-content .fg-interior .fg-portrait__scrim { display: none; }

  #block-frontgrade-content .fg-interior .fg-portrait__reveal {
    grid-template-rows: 1fr;
    opacity: 1;
    transform: none;
  }

  #block-frontgrade-content .fg-interior .fg-portrait__body {
    background: Canvas;
    color: CanvasText;
  }

  #block-frontgrade-content .fg-interior .fg-portrait__name,
  #block-frontgrade-content .fg-interior .fg-portrait__role { color: CanvasText; }

  #block-frontgrade-content .fg-interior .fg-portrait__cta { color: LinkText; }

  #block-frontgrade-content .fg-interior .fg-people-grid__idx {
    color: CanvasText;
    opacity: 1;
  }
}


/* --------------------------------------------------------------------------
   14a. A BIOGRAPHY
   The page behind every card in the grid above. node--person--full.html.twig
   emits the pattern's hero and then one body block, and that block reuses
   .component-text and .component-text__inner deliberately: those are the two
   names section 5 gives its band padding to, section 6 gives the frame edge
   to, and section 6a keys the reading column's indent and its rail on. A
   fourth name for the same box would mean teaching three documented selector
   pairs about it, so the biography is a text component in the site's own
   vocabulary, which is also what the previous version of that page was.

   THE PORTRAIT IS THE ONLY SHAPE THIS PAGE ADDS. Everything else on it is a
   rule that already existed: the paragraphs take the prose measure and colour
   from section 6, the column steps in and the rail is drawn from section 6a,
   the band spends --section-y from section 5.

   ONE COLUMN UNTIL THERE IS ROOM FOR TWO. Below 768px the portrait sits above
   the copy at its own width rather than the column's, because a 3:4 frame
   stretched to a phone's full measure is most of a screen before the first
   sentence. From 768px the two sit side by side and the copy track is the
   prose measure it is capped at anyway, so the pair is as wide as the reading
   column already was and no wider.

   THE FRAME IS FIXED AND THE FACE IS HELD AT THE TOP OF IT, which is the same
   treatment the wall above gives its tiles. The RATIO is not the same any
   more: the wall is square and this page is still 3:4. That is a difference
   left standing rather than one chosen, because the wall was the piece under
   redesign and this page was not in its scope. Squaring this portrait is a
   one line change here and nothing else depends on the ratio.

   The panel behind it is what a missing file leaves: the box still occupies
   its space, so the copy beside it does not move when an image fails.
   -------------------------------------------------------------------------- */

.fg-interior .fg-bio__inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-7);
  align-items: start;
}

@media (min-width: 768px) {
  .fg-interior .fg-bio__inner {
    grid-template-columns: var(--fg-bio-portrait-w) minmax(0, var(--measure-prose));
    gap: var(--space-8);
  }
}

/* A biography with no headshot takes the whole column rather than leaving the
   portrait's track standing empty beside it. */
.fg-interior .fg-bio__inner:not(:has(> .fg-bio__portrait)) {
  grid-template-columns: minmax(0, var(--measure-prose));
}

.fg-interior { --fg-bio-portrait-w: 260px; }

.fg-interior .fg-bio__portrait {
  margin: 0;
  width: 100%;
  max-width: var(--fg-bio-portrait-w);
}

#block-frontgrade-content .fg-interior .fg-bio__portrait img {
  display: block;
  width: 100%;
  height: auto;
  min-width: 0;
  aspect-ratio: 3 / 4;
  object-fit: cover;
  object-position: center top;
  background: var(--surface-panel);
  border: var(--border-hairline-w) solid var(--border-hairline);
}

/* The prose container carries .fg-prose for the link treatment the token
   contract defines, and that class also caps at the prose measure. The cap is
   released here because the paragraphs inside already carry it from section
   6, and two caps on the same copy would narrow it twice. */
.fg-interior .fg-bio__prose { max-width: none; }


/* --------------------------------------------------------------------------
   15. FORM BAND
   One webform in the pattern, on Become a Supplier, embedded by a paragraph
   inside the file attachment component's own wrapper. The form keeps its
   layout and its markup; only the values that would be invisible on a dark
   ground are re-pointed at tokens, which is the same treatment the product
   page gives its gated download form.
   -------------------------------------------------------------------------- */

#block-frontgrade-content .fg-interior .file-attachments__form-embed {
  padding: var(--space-6);
  background: var(--surface-panel);
  border: var(--border-hairline-w) solid var(--border-hairline);
  color: var(--text-muted);
}

#block-frontgrade-content .fg-interior .file-attachments__form-embed .product-download__title {
  margin: 0 0 var(--space-5);
  font: var(--type-h3);
  letter-spacing: var(--ls-h3);
}

/* The form is a twelve column grid whose tracks are computed from the
   viewport, not from its container, so inside the band's text measure it
   overhangs the panel by the difference. It is returned to normal flow and
   its fields fill the panel instead. No field, label or fieldset is otherwise
   moved, and the form's own markup is untouched.

   THE ID IN THE SELECTOR IS NOT DECORATION. That grid is declared as
   #block-frontgrade-content .webform-submission-form, which scores 1-1-0 and
   outranks any selector built from classes alone; the site's own stylesheet
   anchors every webform rule on that block wrapper for the same reason. The
   id is matched, never written, and .fg-interior still holds the rule to the
   pages in this pattern. */
#block-frontgrade-content .fg-interior .file-attachments__form-embed form { display: block; }

#block-frontgrade-content .fg-interior .file-attachments__form-embed .webform-submission-form__inner {
  width: auto;
  max-width: none;
  margin-inline: 0;
}

#block-frontgrade-content .fg-interior .file-attachments__form-embed .form-item { margin-bottom: var(--space-4); }

#block-frontgrade-content .fg-interior .file-attachments__form-embed label,
#block-frontgrade-content .fg-interior .file-attachments__form-embed .form-item__label {
  display: block;
  margin-bottom: var(--space-2);
  font-size: var(--fs-small);
  font-weight: var(--fw-medium);
  color: var(--text-primary);
}

#block-frontgrade-content .fg-interior .file-attachments__form-embed .form-required::after { color: var(--accent); }

#block-frontgrade-content .fg-interior .file-attachments__form-embed input[type="text"],
#block-frontgrade-content .fg-interior .file-attachments__form-embed input[type="email"],
#block-frontgrade-content .fg-interior .file-attachments__form-embed input[type="tel"],
#block-frontgrade-content .fg-interior .file-attachments__form-embed input[type="url"],
#block-frontgrade-content .fg-interior .file-attachments__form-embed input[type="number"],
#block-frontgrade-content .fg-interior .file-attachments__form-embed select,
#block-frontgrade-content .fg-interior .file-attachments__form-embed textarea {
  width: 100%;
  padding: var(--space-3);
  background: var(--surface-raised);
  border: var(--border-hairline-w) solid var(--border-ui);
  color: var(--text-primary);
  font-family: var(--font-body);
  font-size: var(--fs-body);
}

#block-frontgrade-content .fg-interior .file-attachments__form-embed :where(input, select, textarea)::placeholder {
  color: var(--text-secondary);
}

#block-frontgrade-content .fg-interior .file-attachments__form-embed :where(input, select, textarea):hover {
  border-color: var(--border-strong);
}

#block-frontgrade-content .fg-interior .file-attachments__form-embed input[type="checkbox"],
#block-frontgrade-content .fg-interior .file-attachments__form-embed input[type="radio"] {
  accent-color: var(--accent);
  width: auto;
}

#block-frontgrade-content .fg-interior .file-attachments__form-embed .webform-type-checkbox label { display: inline; }

#block-frontgrade-content .fg-interior .file-attachments__form-embed :where(.form-item__description, .description) {
  margin-top: var(--space-2);
  font-size: var(--fs-small);
  color: var(--text-secondary);
}

#block-frontgrade-content .fg-interior .file-attachments__form-embed a { color: var(--accent-text-safe); }

#block-frontgrade-content .fg-interior .file-attachments__form-embed .form-submit {
  padding: var(--space-3) var(--space-6);
  background: var(--accent);
  border: 0;
  color: var(--text-on-accent);
  font-family: var(--font-body);
  font-size: var(--fs-small);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-btn);
  text-transform: uppercase;
  cursor: pointer;
  transition: background var(--dur-quick) var(--ease-standard);
}

#block-frontgrade-content .fg-interior .file-attachments__form-embed .form-submit:hover { background: var(--accent-hover); }


/* --------------------------------------------------------------------------
   16. FIELD CALL TO ACTION LINKS
   A field on the interior bundle that none of the ten pages fills today. The
   rule exists so that the first page to use it arrives styled rather than as
   an unstyled list of links.
   -------------------------------------------------------------------------- */

.fg-interior .fg-interior__ctas { padding-block: var(--space-6); }

.fg-interior .fg-interior__ctas ul {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin: 0;
  padding: 0;
  list-style: none;
}


/* --------------------------------------------------------------------------
   17. CLOSING BAND
   The shared band, with the copy measure the rest of the pattern uses.
   -------------------------------------------------------------------------- */

.fg-interior .fg-interior__cta .fg-cta-band__copy { max-width: var(--measure-body); }
.fg-interior .fg-interior__cta h2 { margin: 0 0 var(--space-3); }

/* THE BUTTON'S LABEL COLOUR IS NOT STATED HERE, AND NO LONGER NEEDS TO BE.
   It was, briefly. components.css declares it on .fg-btn--primary at 0-1-0
   and .fg2-legacy-skin a scores 0-1-1, so on any page still carrying the
   skin the label was painted in the skin's light-ground ink at 4.4:1 on the
   orange fill, under an underline the skin adds to every anchor. Two
   families in this pattern were in that state: the leadership listing, a
   view that carries no entity for the skin test to read, and a biography,
   whose bundle was not on the redesigned list.

   Both are now off the skin, through _frontgrade_fg2_interior_bundles() and
   _frontgrade_fg2_redesigned_views() in frontgrade.theme. Verified on the
   running site: /who-we-are/company-leadership, /people/* and /about all
   serve zero occurrences of fg2-legacy-skin, and the label measures
   --text-on-accent at 5.22:1 from components.css alone. A rule that restates
   what components.css already says, on a page where nothing is competing for
   the declaration, is one more place a future change to the button has to be
   made, so it is gone rather than kept as insurance. */


/* --------------------------------------------------------------------------
   18. FOCUS
   components.css states the ring inside :where(), which resolves to zero
   specificity, so any page level rule can outrank it. It is restated here at
   this page's specificity for every focusable element on these surfaces.
     --focus-ring-inner on --surface-base    19.55:1
     --focus-ring-inner on --surface-raised  18.65:1
     --focus-ring-inner on --surface-panel   17.54:1
   -------------------------------------------------------------------------- */

.fg-interior a:focus-visible,
.fg-interior button:focus-visible,
.fg-interior summary:focus-visible,
.fg-interior [tabindex]:focus-visible,
.fg-interior input:focus-visible,
.fg-interior select:focus-visible,
.fg-interior textarea:focus-visible {
  outline: var(--focus-ring-width) solid var(--focus-ring-inner);
  outline-offset: var(--focus-ring-offset);
  box-shadow:
    0 0 0 var(--focus-ring-offset) var(--focus-ring-outer),
    0 0 0 5px var(--focus-ring-halo);
}


/* --------------------------------------------------------------------------
   19. NARROWER VIEWPORTS
   The grids step down before the columns do, because a two column card grid
   survives a tablet and a two column address list does not.
   -------------------------------------------------------------------------- */

@media (max-width: 1100px) {
  #block-frontgrade-content .fg-interior .fg-people-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

@media (max-width: 860px) {
  #block-frontgrade-content .fg-interior .fg-people-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  #block-frontgrade-content .fg-interior .component-two-columns { grid-template-columns: minmax(0, 1fr); gap: 0; }
  #block-frontgrade-content .fg-interior .component-two-columns .component-text__inner > h3:first-child { margin-top: var(--space-7); }
}

/* THE BAND'S PROPORTIONS, ONE STEP PER BREAKPOINT.

   The floor and the viewport fraction come down together. The floor stops a
   short headline collapsing the band on a wide, shallow window; the fraction
   stops the floor itself eating a small screen. Neither is a fixed height.

     >= 1025px   max(300px, 32svh)
     <= 1024px   max(280px, 30svh)
     <=  768px   max(240px, 28svh)
     <=  480px   max(220px, 26svh)

   These are floors, not heights. Every page in the pattern prints a trail, a
   mark and a headline at display scale, so the rendered band is taller than
   its floor on every page measured; the floor only guards the shortest
   possible stack.

   THE SCRIM WIDENS AS THE COPY DOES. On a wide band the copy occupies about
   the left half and the art has the rest; below 768px the copy is the whole
   column, so the scrim's opaque end is carried right until the art is a
   margin note rather than a field the headline sits in. Nothing about the
   contrast tables changes here: every figure in them is computed with the
   scrim ignored, and a scrim can only take a backdrop towards
   --surface-base, which is the best row in every one of them. */

@media (max-width: 1024px) {
  .fg-interior .fg-interior__hero { min-height: max(280px, 30vh); min-height: max(280px, 30svh); }
}

@media (max-width: 768px) {
  .fg-interior .fg-interior__hero {
    min-height: max(240px, 28vh);
    min-height: max(240px, 28svh);
    --fg-hero-scrim: linear-gradient(90deg,
      rgba(9, 12, 20, 0.94) 0%,
      rgba(9, 12, 20, 0.90) 54%,
      rgba(9, 12, 20, 0.62) 78%,
      rgba(9, 12, 20, 0.14) 100%);
  }
}

@media (max-width: 480px) {
  .fg-interior .fg-interior__hero { min-height: max(220px, 26vh); min-height: max(220px, 26svh); }
}

@media (max-width: 700px) {
  #block-frontgrade-content .fg-interior .component-link-list__links { grid-template-columns: minmax(0, 1fr); }
  #block-frontgrade-content .fg-interior .fg-people-grid { grid-template-columns: minmax(0, 1fr); }

  #block-frontgrade-content .fg-interior .fg-interior__body :where(.component-text,
                                         .component-two-columns,
                                         .component-image,
                                         .component-link-list,
                                         .component-file-attachments,
                                         .component-switchback,
                                         .component-featured-news) {
    padding-block: var(--section-y-sm);
  }

  #block-frontgrade-content .fg-interior .component-hero-landing__content { padding-block: var(--section-y-sm); }
  #block-frontgrade-content .fg-interior .file-attachments__form-embed { padding: var(--space-4); }
}
