/* ==========================================================================
   Design system v2, test centre family sheet.

   Loaded only on the 60 test centre routes, after tokens.css and school.css,
   because test-centres.11tydata.js declares designSheet: "centre". Nothing
   here is shared: if a rule belongs to every page it goes in school.css,
   centrally, and this sheet does not duplicate it or override it site-wide.

   The markup uses only the sm- classes school.css already defines, so every
   class on a test centre page has a rule in school.css too, which is what
   check-unstyled-classes.mjs reads. The family's own presentation hangs off
   data-centre* attributes instead, the same way school.css hangs the watermark
   numeral off data-watermark. A second set of class names for one family would
   be a second vocabulary.

   Tokens only. Values live in tokens.css, and the spacing scale is named by
   VALUE and skips numbers: --space-13 does not exist, and an undefined var()
   drops the whole declaration silently.

   Every rule that sets a column count sits inside a min-width query, so the
   single-column phone layout in school.css is never out-specified. A two-class
   or attribute selector beats a single-class responsive collapse at every
   width, which is how a lessons row once stayed in two columns on a phone.
   ========================================================================== */

/* ---------- 1. Hero ---------- */

/* One column. The hero grid in school.css reserves a second track for the town
   page's pricing aside, and this family has no aside: the address and the
   opening hours are facts about a building, so they belong beside the map in
   the first section rather than in a card over the photograph. Stated outside
   a media query because it is one column at every width. */
[data-centre-hero-grid] {
  grid-template-columns: minmax(0, 1fr);
  align-items: end;
}

/* The copy column holds its measure rather than running the address line the
   full width of a 1280px wrap. */
[data-centre-hero-grid] > div { max-width: 56ch; }

/* The hero lede is the centre's address, so it is set as an address: a little
   larger than body copy and not clamped to the lede's reading measure. */
[data-centre-hero] .sm-lede {
  max-width: 44ch;
  color: var(--color-text-lede);
}

/* The road sign carries the centre name, and the pack reserves the sign for
   places. At the town page's 36px the longest centre names ("Carrick-on-
   Shannon") pushed the sign past the hero's measure, so it is a step smaller
   here and wraps rather than overflowing. */
[data-centre-hero] .sm-sign__inner { min-width: 160px; padding: 16px 28px; }
[data-centre-hero] .sm-sign__english { font-size: 28px; }

/* The hero eyebrow has no numeral: it names the kind of page, and the lane
   markers that carry numbers start at the first content section. */
[data-centre-hero] .sm-eyebrow { margin-bottom: var(--space-6); }

/* A closed centre is caution content, which the pack gives the amber variant
   of the lane chip. It is a notice, not a label, so it is set at the chip's
   larger step and spaced off the heading. */
[data-centre-closed] {
  margin-bottom: var(--space-10);
  padding: var(--space-3) var(--space-10) 5px var(--space-5);
  background: var(--rsa-amber);
  font-size: var(--fs-label);
  letter-spacing: var(--ls-eyebrow-sm);
  text-transform: uppercase;
}

/* ---------- 2. Proof bar ---------- */

/* The trust strip's own words, welded under the hero. The strip has two cells
   rather than the pack's four, because the live page carries two facts and
   this is a re-skin: inventing a third would be writing content.
   The note that stood here had the two cells hold their own width instead of
   stretching. Measured at 1440 that left 248px and 341px of cells in a 1280px
   strip with 54 per cent of the hero measure empty, which is finding 3 of the
   design review. The pack's rule 2 for P1 is equal cells, and its own
   construction is this grid, so the strip takes it: two cells become two halves
   of the measure, and auto-fit folds them onto one row each on a phone without
   a second rule needing to say so. Adding a third cell would have been writing
   copy, so it is not done. */
[data-centre-proof] {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 190px), 1fr));
}

/* The seal is a physical object, so it is built in type and held in by an
   inset keyline, which is how the pack draws every object: a border would sit
   outside the radius and read as a ring round a bubble.
   The glyph the markup carries is the live page's shield emoji, which stays
   there because the parity gate counts it as a chunk. It is a vendor's picture:
   it ignores weight, colour and the display face, and it is a different drawing
   on every platform, which is why the seal rendered as a blue-grey badge inside
   a green disc. So the emoji is sized out and the seal draws its own mark. The
   glyph is a literal, never a CSS escape: an escape written from a script
   becomes Latin-1 bytes, which is finding 16. */
[data-centre-proof] .sm-seal {
  font-size: 0;
  box-shadow: inset 0 0 0 3px rgba(11, 11, 12, 0.15);
}
[data-centre-proof] .sm-seal::before {
  content: "✓";
  font-family: var(--font-body);
  font-size: 28px;
  font-weight: var(--fw-bold);
  line-height: 1;
}

/* ---------- 3. Centre details and map (S3) ---------- */

/* Facts across, map under. This was facts left and map right, which made it a
   split, and the section after it is the lessons split: two splits in a row,
   which is finding 6 of the design review and rule 5 of the pack's rhythm.
   Measured at 1440 they were 879px and 794px of the same shape.
   The facts are a building's facts, so they take the pack's hairline strip
   (section 3.11, the construction where the 2px gaps ARE the hairlines) and the
   map then runs the full measure as the object it is. Different shape above and
   below, and the map gains about 600px of width while it is at it.
   Column counts are picked from the item count, never from the width: five of
   the nine sections on this page are conditional on the centre's data, so the
   strip holds three facts, four or five depending on the centre, and auto-fit
   would strand a short row on the counts that do not divide. */
@media (min-width: 1101px) {
  [data-centre-detail-split] { grid-template-columns: minmax(0, 1fr); }
  /* 4 facts read as 2 + 2. */
  [data-centre-facts] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* 3 facts read as one row, 5 as 3 + 2. */
  [data-centre-facts]:has(> :nth-child(3):last-child),
  [data-centre-facts]:has(> :nth-child(5):last-child) {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
  [data-centre-mapcol] { margin-top: var(--space-20); }
}

/* The container carried .sm-split, which supplied the grid. It is one column
   now, at every width, so it brings its own. */
[data-centre-detail-split] {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
}

/* One column until the query above widens it. Stated as the grid's own default
   rather than as a declaration here: a grid with no template is one column, and
   naming it here would out-order the count rules above, which is exactly what
   it did on the first attempt (four facts stayed in one column at 1440). */
[data-centre-facts] {
  display: grid;
  gap: 2px;
  background: var(--rsa-line-10);
  border: 1px solid var(--rsa-line-10);
}

/* Each fact is a panel with a coloured left edge, not a card with a shadow:
   shadows belong to objects. Green by default, red on the address because the
   address is the one fact someone came for, amber where the fact is a caution. */
[data-centre-fact] {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding: var(--space-10) var(--space-12);
  background: var(--color-panel);
  border-left: 3px solid var(--color-accent);
}
[data-centre-fact]:first-child { border-left-color: var(--color-action); }
[data-centre-fact][data-centre-fact-caution] { border-left-color: var(--rsa-amber); }

/* A cell in a hairline strip has to be a different near-black from the section
   it sits in, or the gaps are the only thing that reads. The details strip is
   always on Surface, and the tricky rows take whichever ground the cycle hands
   them, so both say it the same way: Surface section, Road Black cells. */
.sm-section--surface [data-centre-fact],
.sm-section--surface [data-centre-pair] > .sm-card { background: var(--color-bg); }

[data-centre-fact-label] {
  font-size: var(--fs-caption);
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--color-text-muted);
}
[data-centre-fact-value] {
  font-family: var(--font-display);
  font-size: var(--fs-body);
  font-weight: var(--fw-semi);
  line-height: var(--lh-snug);
  color: var(--color-text);
}
/* The arrival note is a paragraph of prose, not a one-line fact: five lines of
   Poppins 600 read as a shout, so it keeps the body face and leading. */
[data-centre-fact-prose] {
  font-family: var(--font-body);
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-regular);
  line-height: var(--lh-body);
  color: var(--color-text-body);
  text-wrap: pretty;
}
[data-centre-fact-sub] {
  font-size: var(--fs-small);
  color: var(--color-text-muted);
}

/* The map card is topped by the pack's green dashed rule, which is what tells
   a map card from an iframe in a box. */
[data-centre-mapcol] { position: relative; padding-top: var(--space-3); }
[data-centre-mapcol]::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 6px;
  background: repeating-linear-gradient(90deg, var(--color-accent) 0 28px, transparent 28px 40px);
}

/* The pack's map treatment: inverted and desaturated, so an OpenStreetMap tile
   sits on a black page instead of punching a white hole in it. Applied to the
   tile pane only, so the RSA marker and the route line keep their own colours.
   Leaflet adds these class names at runtime. */
[data-centre-mapcol] .leaflet-tile-pane,
[data-centre-routemap] .leaflet-tile-pane {
  filter: invert(0.92) hue-rotate(180deg) saturate(0.55) brightness(0.9) contrast(1.05);
}
/* Attribution stays legible against the inverted tiles. */
[data-centre-mapcol] .leaflet-control-attribution,
[data-centre-routemap] .leaflet-control-attribution {
  background: rgba(11, 11, 12, 0.72);
  color: var(--color-text-muted);
  font-size: var(--fs-micro);
}
[data-centre-mapcol] .leaflet-control-attribution a { color: var(--color-text-body); }

/* Nine centres carry no coordinates, and the live page says so in the map's
   place rather than dropping the band. Same words, in a panel that reads as a
   deliberate placeholder. */
[data-centre-nomap] {
  display: grid;
  place-items: center;
  background: var(--color-panel);
  color: var(--color-text-muted);
  font-size: var(--fs-body-sm);
}

/* ---------- 4. On test day (S4, the road stops) ---------- */

/* The dashed road line itself is school.css's, and is not repeated here. It
   was drawn as `border-left: var(--road-dash-v)` until this family's build
   measured it: a gradient is not a valid border value, so the declaration was
   dropped and the ::before painted nothing at 0px wide. That is now fixed
   centrally and the conformance gate measures it rather than querying it.

   The numbered discs are punched out of the road line by a border in the
   page's own ground, so the ground they punch has to be the section's. Three
   of the nine grounds this family cycles through are not Road Black. */
[data-centre="expect"].sm-section--surface .sm-stop__num { border-color: var(--color-panel); }
[data-centre="expect"].sm-section--deep .sm-stop__num { border-color: var(--color-bg-deep); }

[data-centre-steps] .sm-stops { padding-top: var(--space-2); }
[data-centre-steps] .sm-stop:last-child { margin-bottom: 0; }
[data-centre-steps] .sm-stop h3 { margin-bottom: var(--space-2); }

/* ---------- 5. Review wall (C2) ---------- */

/* Two columns, from 2 and 4 reviews. Two never strands a row short by more
   than one cell, which is the arrangement that reads as a fault. */
@media (min-width: 769px) {
  [data-centre-reviews] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* A photographed reviewer keeps the photograph; the initial disc is the
   fallback the live page already has. Student pass photographs stay
   unfiltered, so no lift and no scrim. */
[data-centre-avatar] {
  background: var(--color-panel);
  overflow: hidden;
  padding: 0;
}
[data-centre-avatar] img {
  width: 42px;
  height: 42px;
  object-fit: cover;
  border-radius: var(--radius-round);
}

[data-centre-who] { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; }
[data-centre-who] .sm-review__meta { display: block; }
[data-centre-who-row] {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-5);
  margin-top: var(--space-2);
}

/* The pass tag. The pack's own badge: Go Green ground, dark text, 10px caps. */
[data-centre-tag="pass"] {
  padding: 5px var(--space-4) 4px;
  background: var(--color-accent);
  border-radius: var(--radius-xs);
  color: var(--color-on-accent);
  font-family: var(--font-display);
  font-size: var(--fs-micro);
  font-weight: var(--fw-black);
  letter-spacing: var(--ls-eyebrow-sm);
  line-height: 1;
}

/* Every outbound link on the page: the arrow is drawn here rather than typed
   into the copy, so the words are the words and the glyph is presentation. */
[data-centre-link] {
  display: inline-flex;
  align-items: center;
  gap: var(--space-4);
  color: var(--color-text-red);
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-semi);
  text-decoration: none;
}
[data-centre-link]:hover { color: var(--color-text); text-decoration: underline; text-underline-offset: 3px; }
[data-centre-link]::after {
  content: "→";
  font-family: var(--font-display);
  font-weight: var(--fw-black);
  transition: transform var(--dur-fast) var(--ease-reveal);
}
[data-centre-link]:hover::after { transform: var(--nudge-x); }

/* The verify link sits inside the attribution row, where it is a note rather
   than an action. */
[data-centre-who-row] [data-centre-link] { font-size: var(--fs-caption); font-weight: var(--fw-bold); }

[data-centre-allreviews] { margin: var(--space-16) 0 0; text-align: center; }

/* ---------- 6. Test routes (S3) ---------- */

/* The route map is the live page's 360px band, kept at that height so the
   OSRM polyline still fits its bounds. */
[data-centre-routemap] { height: 360px; margin-top: var(--space-12); }

[data-centre-note] {
  margin-top: var(--space-5);
  font-size: var(--fs-caption);
  color: var(--color-text-faint);
}

/* A sub-heading inside a section is not another section heading: it is set at
   h4's step, in caps, with the rule 20 margin above it so it never sits flush
   against the block before. */
[data-centre-subhead] {
  margin-top: var(--space-24);
  margin-bottom: var(--space-8);
  font-size: var(--fs-h4);
  letter-spacing: var(--ls-label);
}

/* Junctions are caution content, so they take the amber edge rather than the
   green one, and they sit two to a row from counts of 2, 3 and 4. */
@media (min-width: 769px) {
  [data-centre-junctions] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.sm-cards[data-centre-junctions] > .sm-card {
  border-top-color: var(--rsa-amber);
  border-left: 3px solid var(--rsa-amber);
}
[data-centre-junctions] .sm-card p { margin: 0; }

/* ---------- 7. Tricky spots ---------- */

/* Two to five spots. This was two cards to a row, and the section above it is
   the junction cards, so measuring the page turned up a second adjacent pair of
   the same archetype that the design review did not name: two card grids, both
   two columns, 1140px then 578px.
   The spots are hazards rather than offers, so they read as rows: the place
   named on the left, why it catches people out on the right, hairline apart on
   the pack's 2px-gap construction, with the green edge kept on each row. A row
   list cannot strand a short row either, whichever of the two to five spots a
   centre has. */
@media (min-width: 769px) {
  [data-centre-pair] {
    grid-template-columns: minmax(0, 1fr);
    gap: 2px;
    background: var(--rsa-line-10);
    border: 1px solid var(--rsa-line-10);
  }
  [data-centre-pair] > .sm-card {
    display: grid;
    grid-template-columns: minmax(0, 0.38fr) minmax(0, 0.62fr);
    gap: var(--space-12);
    align-items: baseline;
    background: var(--color-panel);
    border: 0;
    border-left: var(--rule-green);
    border-radius: 0;
  }
  /* A row in a hairline grid does not lift: the 2px gaps are the structure, and
     a translated row opens one of them to 6px. */
  [data-centre-pair] > .sm-card:hover { transform: none; }
}
[data-centre-pair] .sm-card p:last-child { margin-bottom: 0; }

/* ---------- 8. Lists ---------- */

/* A marker and text list with a hanging indent, never a grid: a grid puts
   every inline child of the item in its own cell, and the prose then sets one
   word per line. The tick is the literal glyph, not a CSS escape. */
[data-centre-ticks] {
  margin: 0;
  padding: 0;
  list-style: none;
}
[data-centre-ticks] > li {
  position: relative;
  padding-left: var(--space-14);
  margin-bottom: var(--space-6);
  font-size: var(--fs-body-sm);
  color: var(--color-text-body);
}
[data-centre-ticks] > li::before {
  content: "✓";
  position: absolute;
  left: 0;
  top: 0;
  font-family: var(--font-display);
  font-weight: var(--fw-black);
  color: var(--color-accent);
}
[data-centre-ticks] > li:last-child { margin-bottom: 0; }

/* The requirements list inside a card keeps a plain marker: these are the
   conditions for sitting the test, not things already done. */
[data-centre-list] {
  margin: 0;
  padding: 0;
  list-style: none;
}
[data-centre-list] > li {
  position: relative;
  padding-left: var(--space-12);
  margin-bottom: var(--space-5);
  font-size: var(--fs-body-sm);
  color: var(--color-text-muted);
}
[data-centre-list] > li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 11px;
  width: 10px;
  height: 2px;
  background: var(--color-accent);
}
[data-centre-list] > li:last-child { margin-bottom: 0; }

/* ---------- 9. The driving test ---------- */

/* The insurance rule is the one card on the page that is a warning, so it
   takes the red top rule. Written with both classes and the attribute so it
   beats school.css's own nth-child(3n + 1) green edge, which is (0,3,0). */
.sm-cards > .sm-card[data-centre-card="alert"] {
  border-top-color: var(--color-action);
  border-left: 3px solid var(--color-action);
}
.sm-cards > .sm-card[data-centre-card="alert"] h3 { color: var(--color-text-red); }

/* The pills inside a card are the skills list, so they sit in from the copy
   above them. */
[data-centre="test"] .sm-card .sm-pills { margin-top: var(--space-5); }
[data-centre="test"] .sm-card p:last-of-type { margin-bottom: var(--space-4); }

/* ---------- 10. Pretest tickets (F3) ---------- */

/* The price is the content, set at the pack's ticket size. At --fs-num-xs the
   figure read as body copy, and the page then had no display numeral at all. */
[data-centre-tickets] .sm-ticket__price {
  display: block;
  font-size: clamp(44px, 4.6vw, 64px);
  line-height: 0.9;
  letter-spacing: var(--ls-tighter);
}
[data-centre-tickets] .sm-ticket__per {
  display: block;
  margin: var(--space-3) 0 var(--space-12);
}
/* Rule 4: every price card carries its own CTA, pointing at the same shop the
   hero and the closing band point at. */
[data-centre-tickets] .sm-ticket .sm-btn { align-self: stretch; }

[data-centre="pretest"] .sm-price-proof { text-align: center; }

/* The starter callout is the live page's "Just starting out?" box, kept as a
   panel with the green edge it already had. */
[data-centre-starter] { margin-top: var(--space-20); }
[data-centre-starter] h3 { margin-bottom: var(--space-5); }
[data-centre-starter] p { font-size: var(--fs-body-sm); color: var(--color-text-muted); }

/* ---------- 11. Nearby areas ---------- */

/* Catchments run from 2 to 47 areas. Two columns is the only arrangement that
   cannot leave a last row short by two, and a long catchment reads as a
   directory, which is what it is. */
@media (min-width: 769px) {
  [data-centre-places] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
/* Three columns only where three fills its last row. auto-fit picks its column
   count from the width, not from the item count, and a catchment of 37 went
   3 + 3 + ... + 1 and stranded a single area on a row of its own. Two columns
   can never be short by more than one cell, so it is the fallback, and three
   is used for the counts whose remainder is 0 or 2. */
@media (min-width: 1200px) {
  [data-centre-places]:has(> :nth-child(3n):last-child),
  [data-centre-places]:has(> :nth-child(3n + 2):last-child) {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}
/* The district code is a note on the link, not a second label, so the arrow
   keeps the right edge and the code sits with the name.
   The label grows and the code shrinks, because two districts are written as
   long unbroken strings ("dun_laoghaire_rathdown") and with the code holding
   its width the name was squeezed into an 88px column three lines deep at
   390px. The row wraps rather than squeezing. */
[data-centre-places] .sm-place {
  justify-content: flex-start;
  flex-wrap: wrap;
  gap: var(--space-3) var(--space-5);
}
[data-centre-places] .sm-place > span:first-child { flex: 1 1 60%; min-width: 0; }
[data-centre-places] .sm-place::after { margin-left: auto; }
[data-centre-place-sub] {
  flex: 0 1 auto;
  min-width: 0;
  overflow-wrap: anywhere;
  font-size: var(--fs-micro);
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--color-text-faint);
}

/* ---------- 12. Closing band (X3) ---------- */

/* The band's two objects, the 220px L plate and the white centre line, and the
   black accent clause on red, are all drawn in school.css now, for every
   family at once. This sheet drew its own copy of each: the plate as a
   positioned .sm-plate element and the centre line on ::before. Both were
   collisions, not duplicates that merely repeated themselves. Two L plates
   rendered on every centre page, and the central centre line and this sheet's
   fought over ::before. Deleted, so there is one source.
   Only the muted copy colour is family work and stays. */
[data-centre="band"] .sm-or-call { color: var(--color-on-red-muted); }
[data-centre="band"] .sm-or-call a { color: var(--color-on-red-muted); }

/* ---------- 13. Long centre names ---------- */

/* Four centres are named as two places joined by a slash or a bracket, and
   "Dun Laoghaire/Deansgrange" has no break opportunity a browser will take:
   at 390px it ran 51px past the right edge in the h1, in every h2 that names
   the centre, and in the closing band. The name is content, so the heading
   breaks instead. */
[data-centre-hero] h1,
[data-centre-hero] .sm-hero__place,
[data-centre-hero] .sm-sign__english,
.sm-section[data-centre] h2,
[data-centre="band"] h2,
.sm-section[data-centre] h2 em,
[data-centre="band"] h2 .sm-accent {
  overflow-wrap: anywhere;
}

/* ---------- 14. Section rhythm ---------- */

/* Rule 20: a section intro never sits flush against the heading above it. */
.sm-section[data-centre] > .sm-wrap > .sm-lede {
  margin-top: var(--space-5);
  margin-bottom: var(--space-20);
}
.sm-section[data-centre] > .sm-wrap > .sm-split,
.sm-section[data-centre] > .sm-wrap > .sm-cards,
.sm-section[data-centre] > .sm-wrap > .sm-tickets,
.sm-section[data-centre] > .sm-wrap > .sm-places,
.sm-section[data-centre] > .sm-wrap > .sm-reviews,
.sm-section[data-centre] > .sm-wrap > [data-centre-ticks] {
  margin-top: var(--space-20);
}

/* ---------- 15. Phone ---------- */

@media (max-width: 1100px) {
  /* Every column override above is inside a min-width query, so the collapses
     in school.css still win here. These are the three things that need saying
     at this width rather than unsaying. */
  [data-centre-mapcol] { margin-top: var(--space-16); }
}

@media (max-width: 768px) {
  /* Centre names are longer than town names: "Dun Laoghaire/Deansgrange",
     "Carrick-on-Shannon", "Kilkenny (O'Loughlin Gaels)". At the town page's
     phone size the longest of them broke mid-word, so the place line is a step
     smaller here and breaks at its own punctuation instead. */
  [data-centre-hero] .sm-hero__place { font-size: clamp(30px, 9vw, 52px); }
  [data-centre-hero] .sm-hero__service { font-size: clamp(18px, 5vw, 26px); }
  [data-centre-hero] .sm-sign__english { font-size: 24px; }
  [data-centre-hero] .sm-sign__inner { min-width: 0; padding: 12px 20px; }
  [data-centre-routemap] { height: 280px; }
  .sm-section[data-centre] > .sm-wrap > .sm-lede { margin-bottom: var(--space-16); }
}
/* ---------- 16. Hero skins ---------- */

/* Finding 1 of the design review: every route in this family shipped one hero
   skin, so neighbouring pages were the same page above the fold. The skin is
   now dealt deterministically per route in
   data/rsa-school/makeover/hero-skin.cjs, and these are the two skins that
   needed work before they read as different HERE.
   The town template gives route and night markup of their own: route puts the
   photograph in an aside frame and night has no backdrop. This family has one
   full-bleed <picture> and no aside, so both skins are composed from the
   element that is already in the markup rather than by adding any.

   Route, the pack's skin B: the photograph stops being a backdrop and becomes
   a framed object on the right, on a green offset block, with the copy held to
   the left half. The 8px chalk keyline and the 28px offset are the pack's own
   numbers, from sections 3.2 and 1/B. */
@media (min-width: 1100px) {
  /* One column first. school.css gives route a 1fr/0.82fr grid for the town
     template's aside, which out-specifies this family's own single-column rule
     and left the copy in a 686px track: with the 52 per cent below that came
     out at 357px and the hero grew 76px taller to wrap it. Measured, not
     assumed. */
  [data-centre-hero].sm-hero--route .sm-hero__grid { grid-template-columns: minmax(0, 1fr); }
  [data-centre-hero].sm-hero--route .sm-hero__grid > div { max-width: 52%; }
  [data-centre-hero].sm-hero--route .sm-hero__photo {
    top: var(--space-28);
    right: var(--space-16);
    bottom: var(--space-28);
    left: 56%;
    z-index: 0;
    border: 8px solid var(--rsa-chalk);
  }
  /* The offset block. ::before is free on this element: school.css draws the
     foot fade on its ::after and nothing else in either sheet touches either
     one, which finding 19 says to check before drawing on a pseudo-element. */
  [data-centre-hero].sm-hero--route .sm-hero__photo::before {
    content: "";
    position: absolute;
    inset: 28px -28px -28px 28px;
    z-index: -1;
    background: var(--color-accent);
  }
  /* A 220px ramp inside a framed photograph is a dark half rather than a weld.
     The frame has an edge of its own, so the fade only has to stop the crop
     reading as a cut. Still above the viewport gate's 150px floor. */
  [data-centre-hero].sm-hero--route .sm-hero__photo::after { height: 160px; }
  /* The copy side goes to near-black, since there is no longer a photograph
     under it to hold down. */
  [data-centre-hero].sm-hero--route::before { background: var(--overlay-panel); }
}

/* Night, the pack's skin C: a slanted photo panel with an 18px green leading
   edge. school.css composes that edge on .sm-hero--night::after, and
   .sm-hero::after, which paints the 6px green rule that closes every hero, is
   declared later at the same specificity. The two merge into one 18px by 6px
   stub: measured on this family and on the night towns, where the closing rule
   is 18px wide and the leading edge does not exist. Both are put back here, the
   edge on the photo wrapper's own ::before, which nothing else uses. The
   collision is in school.css and is in the report as a shared-CSS request, not
   edited here. */
[data-centre-hero].sm-hero--night::after {
  /* display, because school.css hides this pseudo-element below 1100px: that
     was the right call while it carried the skewed leading edge, and it took
     the closing rule with it. Measured at 390px before this line: the night
     hero ended on the photograph with no green rule under it. */
  display: block;
  top: auto;
  right: 0;
  bottom: 0;
  left: 0;
  width: auto;
  height: 6px;
  background: var(--color-accent);
  transform: none;
  z-index: 0;
}
[data-centre-hero].sm-hero--night .sm-hero__photo::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 18px;
  z-index: 1;
  background: var(--color-accent);
}
@media (max-width: 1100px) {
  /* school.css squares the panel up and runs it full width at this size, so
     the leading edge has no edge to lead and goes with the skew. */
  [data-centre-hero].sm-hero--night .sm-hero__photo::before { display: none; }
}

/* The copy keeps the left 48 per cent, which is where the skewed panel's
   leading edge actually lands at 1440 once the skew is counted: measured at
   x 733, not at the 56 per cent the untransformed rule implies. Without this
   the hub's 1.5fr copy track ran 280px under the photograph. */
@media (min-width: 1100px) {
  [data-centre-hero].sm-hero--night .sm-hero__grid { grid-template-columns: minmax(0, 1fr); }
  [data-centre-hero].sm-hero--night .sm-hero__grid > div { max-width: 48%; }
}
