/* ===========================================================
   Home — Screen 01, `01-home.md`.

   One stylesheet for one page. Since Board 20 (2 Aug) the page
   carries TWO grounds (§2): black from the opening screen to
   the map, paper for the invitation, and the footer's own
   black closes it. The paper claim and the single at-tier
   photograph it sat above are retired — that photograph was
   cut by the fold at every width by construction — so E1.4's
   paper opening is simply unused here and Home falls back to
   E1's own row for the narrative page.

   Nothing is ever set on a photograph here — the band's labels
   sit beneath its cells and each room's evidence name sits in
   the strip beneath the print, so the hard rule is satisfied
   by composition and no scrim exists on this page.

   #A6A6A6 appears only off-photo (8.63:1 on black). The one
   value that is not a colour token's own name is the
   structural line on dark: #595959 is --color-ink-secondary's
   hex, and continuity §9 fixes it as the value a line may take
   when it BOUNDS something on black. --color-hairline-dark
   (#333333, 1.66:1) may only separate text sections, which is
   the one place it is used below.
   =========================================================== */

/* -----------------------------------------------------------
   1 · The opening screen — the sentence and the band (§4.1)
   Board 20 rev 7, 2 Aug.
   ----------------------------------------------------------- */

/* One screen: the sentence at the top, the band anchored to the fold, and
   the air BETWEEN them rather than dumped below the band. The column is
   what does that — `margin-block-start: auto` on the band takes whatever
   the sentence leaves, so the arrangement re-balances itself when the type
   reflows instead of needing a second measured value per breakpoint. */
.hm-open {
  display: flex;
  flex-direction: column;
  min-block-size: 100vh;
  padding-block-start: calc(var(--gap-component) + var(--gap-heading) + var(--gap-block));
  background: var(--color-surface-dark);
}

/* The top padding CLEARS THE CHROME, at every width. The plate hangs from
   the top edge into the page (§3.1) and the trigger sits opposite it, so
   the sentence has to begin below them both. Measured against the rendered
   ink, not the element box: a box test passes a page that collides.

   The register is .t-hero — the system's uppercase display class, lifted to
   white by `.surface-dark`. Its measure and its WEIGHT are overridden here,
   and both numbers were measured rather than judged (Session XIV, 5 Aug,
   Mahesh: "since 2006 looks too bold … base it on the text on the proarc
   logo").

   WEIGHT 600, and the mark is the reason it is not lighter. The wordmark's
   own letterforms were measured off `389a3e9:images/ui/logo-mark.webp` —
   de-sheared by its 12.5° slant, thresholded at >50% alpha — and they are
   HEAVIER than this line ever was: stem ÷ x-height 0.368 against General
   Sans Bold's 0.321, ink coverage 70.8% against Bold's 62.6%. So "match the
   logo" cannot licence a lighter line; 600 (54.0%) is a decided step BELOW
   the mark, taken because 700 closed the counters up around the commas. 500
   (43.0%) and 400 (32.0%) were rendered and refused — they stop reading as
   an opening claim.

   ⏳ THE FILE THAT SENTENCE MEASURED IS SUPERSEDED, AND THE MARGIN IT
   QUOTES HAS NARROWED. ProArc supplied the authoritative masters on 20 Aug
   2026 and they are not a recolour of what was here: the word is flatter
   (aspect 4.91 against 4.18) and lighter (raw ink coverage 47.6% against
   53.3%). Carried through the same de-shear that produced 70.8, the new
   mark lands near 63% — against General Sans Bold's 62.6%, where the old
   one stood at 70.8%.

   🔴 THE DECISION SURVIVES AND ITS REASON DOES NOT. 600 is still below the
   mark, so the value does not move. But "the mark is markedly heavier than
   Bold" was the argument, and the mark is now level with Bold — so the next
   session that reaches for this reasoning must re-measure rather than quote
   it. Re-measuring is not free: it re-opens a rendered decision of Mahesh's
   (Session XIV), so it is flagged here and taken separately, never as a
   rider on an asset swap. The superseded file is at the git ref above.

   TRACKING IS UNCHANGED and that is a finding, not an omission. Matching the
   mark's setting exactly — it sets six letters in 0.921 x-heights, its
   letters actually TOUCH — needs -0.0346em at 700. --ls-hero is -0.03em,
   which is 0.24px away at 52px. It was rendered; it is indistinguishable.
   The tracking half of that instruction was already satisfied.

   MEASURE 900px, replacing `none`. --measure-hero (20ch) is right for a
   five-word claim and wrong for a 78-character sentence, but `none` couples
   the rag to the weight: at 600 the container's full 1120px pulls "works,"
   up and leaves a stub third line. 900px is not tuned to 600 — it holds this
   sentence's three-line stair at 400, 500, 600 AND 700 alike, so the number
   belongs to the sentence and not to a chosen weight. The window is 868–925px
   across those four weights (846–949 at 600 alone); 900 sits inside it with
   32px of slack at the tighter edge. It does nothing below 1024 — the rag is
   already identical at 600 and 700 at 1024, 768 and 640, because the type
   steps down with the container. Verified by p9-home. */
.hm-open__line {
  margin: 0;
  font-weight: var(--fw-semibold);
  max-inline-size: 900px;
}

/* The SETUP is hollow and the verbs are solid (H3, 5 Aug, Mahesh). The
   sentence's payload is its five verbs — they are the five bands below — so
   the outline goes on the clause that frames them, never on them. Measured,
   because this is the one claim that decides it: hollowing the verbs instead
   drops them to 10.4% ink against the setup's 39.1%, about a quarter, and
   the scaffolding ends up louder than the claim. This way round the numbers
   read 10.4% on the setup and 38.8% on the verbs.

   Hollowing only the individual verbs was rendered and refused as well: the
   commas and "and" stay solid between hollow words and "AND" becomes the
   loudest thing in the last line.

   The device is .t-marker's, borrowed rather than invented — the rooms bands
   below already carry it at Mega Splash, so the page has one hollow idea and
   not two. No `.surface-dark` prefix, unlike the marker's: .t-marker is a
   role that can land on paper and this class exists on exactly one element
   on one always-dark band.

   THE @supports IS LOAD-BEARING, exactly as it is on the marker: without it
   an unsupported -webkit-text-stroke leaves `color: transparent` standing and
   HALF THE OPENING SENTENCE goes invisible — silently. The span therefore
   ships SOLID, inheriting white from `.surface-dark .t-hero`, and the query
   arms the hollow. A rule targeting the span beats that inheritance whatever
   its specificity, so no !important and no restatement of the selector. */
/* THE PEN IS THE MARKER'S, SCALED TO THE HERO'S LETTERS (Session XV, 5 Aug,
   Mahesh). Both hollow registers were drawn with a flat 1px, and a flat pen
   is heavier on smaller letters: measured on the built page, the opening's
   outline ran 19.23/1000em against the marker's 11.90 — 1.62x the weight per
   letter, two bands apart on one screen.

   The value is DERIVED, not chosen: 1px x --fs-h1 / --fs-mega. So the pen is
   the same pen, taken to the size the letters are set at. 52/84 = 0.619px
   here; the tablet restatement below carries 40/60 = 0.667px.

   🔴 THE MISMATCH IS NOT ONE NUMBER, and the ratio is why the restatement
   exists rather than a single value: both sizes step at 1023, but by
   different proportions — the hero 52 -> 40 (x0.769) and Mega 84 -> 60
   (x0.714) — so the flat pen was 1.615x on desktop and 1.500x on tablet.
   A rule made of tokens inherits THOSE tokens' breakpoints, and both of
   these step at 1023, which is where the restatement is keyed.

   Sub-pixel was the stated risk and it was measured rather than assumed:
   ink coverage of a fixed rectangle over the setup is LINEAR in stroke
   width at dpr 1 AND dpr 2 — 0.619px returns 61.9% / 62.0% of 1px's ink,
   0.75px returns 74.9% / 74.9% — so Skia antialiases the width faithfully
   and nothing snaps to a whole pixel. At matched cap height the opening now
   measures 0.0437 mean luminance against the marker's 0.0414, from 0.0697.
   Verified by p9-home, which asserts the RATIO against the marker on the
   page rather than this literal — a number in a stylesheet cannot tell you
   it stopped matching the thing it was derived from. */
@supports (-webkit-text-stroke: 1px currentcolor) {
  .hm-open__setup {
    -webkit-text-stroke: 0.619px var(--color-on-dark);
    color: transparent;
  }
}

/* The tablet band, keyed at 1023 with the two type tokens it is derived
   from: 1px x 40/60. Outside the @supports on purpose, exactly as the ≤767
   collapse below is — a browser with no text-stroke ignores the width and
   keeps the solid fill the @supports never voided. Placed BEFORE that
   collapse, because both selectors carry the same specificity and ≤767 must
   win on source order. */
@media (max-width: 1023px) {
  .hm-open__setup {
    -webkit-text-stroke-width: 0.667px;
  }
}

/* And it collapses to solid below 768, for .t-marker's own stated reason:
   at this size a 1px stroke is a grey suggestion of a word rather than a
   register, and the counters close up. The hero is 40px at 768 and 34px at
   640 — rendered at both, and 34 is where it stops working. Placed AFTER the
   @supports block deliberately: same specificity, so this wins on source
   order. The differentiation is a desktop reading and is meant to be. */
@media (max-width: 767px) {
  .hm-open__setup {
    -webkit-text-stroke-width: 0;
    color: var(--color-on-dark);
  }
}

/* And the sentence CENTRES in the air the band leaves it (2 Aug, Mahesh, on
   the live site: it read as sitting too high). The padding above is a
   CLEARANCE — it states where the sentence may not go, which is under the
   chrome — and a clearance floor is not a composition. Left at the floor
   the ink sat 53px below the plate and 226px above the band: the screen
   read top-heavy because the eye measures from the chrome's own bottom
   edge, not from the top of the page.

   Three auto margins now share the free space — this one's two and the
   band's one — so the sentence takes a third of it above and the band stays
   anchored to the fold, which is the whole arrangement's point. It is a
   RATIO rather than a second measured value, so it re-balances itself when
   the type reflows, and at a viewport too short to have free space it
   collapses to zero and the sentence sits back on the clearance floor
   rather than colliding with the band. Below 1024 the section gives up its
   viewport height, free space is zero by construction, and this is inert. */
.hm-open > .container {
  margin-block: auto;
}

/* Full bleed: the band is the header, so it reaches both page edges and is
   deliberately outside .container. */
.hm-open__band {
  display: flex;
  gap: var(--gap-block);
  margin-block-start: auto;
  padding-block-end: var(--gap-component);
}

/* A zone is one territory. The paired zone holds two cells and must come out
   exactly twice a single zone PLUS the gap between them, or its cells are
   narrower, its images shorter, and its label falls out of line with the
   others. That is what the flex-basis is: growth alone cannot express it. */
.hm-open__zone {
  flex: 1 1 0;
  min-inline-size: 0;
}

.hm-open__zone--pair {
  flex: 2 1 var(--gap-inline);
}

/* No cell carries a flex value of its own. Inside a zone every cell already
   shares one proportion, so equal growth is correct — and an authored
   per-cell value would be an inline style that no stylesheet rule can
   override, which is exactly how this band lost its alignment once already. */
.hm-open__cells {
  display: flex;
  gap: var(--gap-inline);
}

.hm-open__cell {
  flex: 1 1 0;
  min-inline-size: 0;
  margin: 0;
}

/* 4:5, one proportion for the whole band — which is the first of E3.6's two
   conditions for a fixed band, and the art-directed crop below is the second.
   Nothing is set on any of these photographs, so no scrim and no contrast
   measurement is owed. */
.hm-open__img {
  display: block;
  inline-size: 100%;
  block-size: auto;
  aspect-ratio: 4 / 5;
  object-fit: cover;
}

/* The crop centres. Each is a judged position, not a default: a centre crop
   is a guess, and three of these six also changed which FRAME they cut from
   (see build/lib/home.js). Reasoning per cell is in the punch list, H2-f. */
/* 🔴 HABITAT SCHOOL IS CROPPED TO ITS SIGN, NOT TO ITS CENTRE (3 Aug).
   This cell shared `50% center` with City University and it was wrong for
   this photograph: the fascia carries the building's name in both scripts,
   and a centre crop lands mid-word — the tile shipped reading "T SCHOOL".
   A cut word does not read as a decision, it reads as a mistake, and it is
   the kind of fault a composition metric cannot see because the frame is
   otherwise well filled.

   At 0% the English name is whole with its mark. The Arabic completes at
   the OTHER end (100%), so the two scripts cannot both survive one value —
   noted for whoever builds the AR side, which does not exist yet and for
   which no `[dir="rtl"]` rule is therefore invented here. */
.hm-open__img--habitatschool {
  object-position: 0% center;
}

/* The university cell changed record and frame on 3 Aug (Mahesh) — see
   build/lib/home.js. The academic block is symmetrical and its lawn runs the
   full width, so the centre is the judged position here as well as the
   default one; it is written out because a value that agrees with the
   default by accident and one that agrees with it by measurement look
   identical, and only one of them survives a reframe. */
.hm-open__img--edu-cityuniversity {
  object-position: 50% center;
}

/* 🔴 THE MALLS CELL, AND THE VALUE IS WHAT KEEPS A PHONE NUMBER OFF HOME
   (22 Aug 2026, ProArc review point 2 — "show a mall"). The cell is
   citylifealtallah/gallery-04; see build/lib/home.js's OPEN_BAND_FRAME for
   why this record ships at all, given E10.

   MEASURED, then rendered and looked at. The "LEASING NOW 052 6055 255"
   panel occupies x 0.60-0.82 of the source. gallery-04 is 1920x1149, so the
   4:5 cell's window is 0.8 / (1920/1149) = 47.9% of the source width, and
   the window's right edge sits at p·(1−w) + w. Anything past ~23% pulls the
   hoarding into the cell; 0% leaves twelve points of clearance.

   0% is also the better picture, which is the reason it is not merely the
   safest. The mall's signage and entrance — the part that says "mall" — run
   x 0.09-0.30, landing 23-52% across the window: comfortably inside it and
   just left of centre. At 15% the same sign is jammed 7-36% and reads as
   falling off the edge, and the Ajman Markets logo clips awkwardly at the
   right. Both were rendered at the true 4:5 before this was chosen.

   ⚠ Unlike the ajmanbank note below, this is NOT an overflow-distribution
   correction of a judged centre — the subject is a RANGE to be kept inside
   the window, not a point to be centred on, so the value is read off the
   window's edges rather than converted. */
.hm-open__img--citylifealtallah {
  object-position: 0% center;
}

/* Souk Salah's cell was retired from this band on 22 Aug 2026 (point 2 asked
   for a mall in its place), and its crop went with it rather than being left
   to match nothing. It was `41% center` — the one-point perspective centred
   on its vanishing point — and it is recorded here in case the souk ever
   returns to a 4:5 cell, which is the only context that value is true of. */

/* 🔴 THE BAND IS BACK ON THE HERO (3 Aug, Mahesh) — gallery-02 carried a row
   of parked cars along its bottom edge, and a landscape frame in a 4:5 cell
   overflows HORIZONTALLY, so the whole height ships and no value here can
   crop them out. The works room takes gallery-02 in exchange; the two
   surfaces still show two different photographs of one building.

   The hero's own mass runs 0.30–0.65, so its centre is 0.48 — the number
   that was already recorded for this frame when the band moved off it, and
   it needed no re-derivation. Rendered at 35 / 46 / 55 and looked at: 46 is
   the derived one and it is also the one that keeps the white blade whole.

   ⚠ 48 is the CENTRE; 46 is the percentage that puts the window there.
   `object-position` does not name a point in the source — it distributes
   the OVERFLOW, so the window's centre lands at p·(1−w) + w/2 where w is
   the 4:5 window as a fraction of the source width. Every value in this
   block is the second number, and the first one is in the punch list's
   crop table; writing the crop centre straight into the CSS moved this
   cell 4.5% right and cut "Ajman" off its own sign. */
.hm-open__img--ajmanbank {
  object-position: 46% center;
}

/* The stepped corner, not the long façade. */
.hm-open__img--seasidehills {
  object-position: 74% center;
}

/* The one cell that gives up NO width: this source is natively 800×1200,
   already taller than 4:5, so it crops top to bottom. Anchored high so the
   minaret keeps its finial — what is dropped is foreground paving. */
.hm-open__img--alghalamosque {
  object-position: center 12%;
}

/* Named, not linked — the band shows and the sections below navigate. */
.hm-open__label {
  margin: var(--gap-tight) 0 0;
  padding-inline-start: 2px;
  font-size: var(--fs-metadata);
  font-weight: var(--fw-bold);
  text-transform: uppercase;
  color: var(--color-on-dark);
}

/* The label is a link (2 Aug, Mahesh). It keeps the label's register — the
   type does not change — and takes only the black-ground link idiom: the
   rule is DRAWN under it and thickens on hover, the same gesture .hm-link
   carries in the beat and the rooms. No colour (the site is monochrome),
   no arrow (five of them would read as a menu across the fold), and no
   change of weight, so the band still reads as five captions that happen
   to be reachable rather than as a navigation row above the sentence. */
.hm-open__labellink {
  position: relative;
  display: inline-block;
  border-block-end: 1px solid currentcolor;
  padding-block-end: 2px;
  color: inherit;
}

/* 🔴 THE HOVER DRAWS, AND THE WHOLE TERRITORY CARRIES IT (3 Aug, Mahesh: the
   text underneath needs a mouse-over). It had one — the rule went 1px to 2px
   — and a one-pixel step that arrives everywhere at once is not perceptible
   as a response: nothing moves, so nothing answers. The gesture is now the
   menu's own (`.menu-nav__row::after`): a rule that DRAWS from the reading
   edge across the label's width. The resting hairline stays, so the label is
   still visibly a link before anything is hovered.

   The TRIGGER is the zone, not the link. A reader points at the photograph,
   not at the twelve-pixel caption under it, and the picture and its name are
   one door in the reader's mind — but only the label is focusable, so this
   adds no link, no tab stop and no second destination, and the band's five
   links are still five. `:focus-within` gives the keyboard exactly what the
   mouse gets, which this file owes under the site's own rule.

   Reduced motion needs no rule here: tokens.css clamps every transition to
   0.01ms, so the rule arrives instead of drawing, and the state still reads. */
.hm-open__labellink::after {
  position: absolute;
  inset-inline: 0;
  inset-block-end: -2px;
  display: block;
  block-size: 2px;
  background: currentcolor;
  transform: scaleX(0);

  /* transform-origin has no logical keyword, so the drawing edge is a
     percentage flipped by direction — the menu's own one-value flip. */
  transform-origin: 0 50%;
  transition: transform var(--dur-fast) var(--ease-out);
  content: "";
}

[dir="rtl"] .hm-open__labellink::after {
  transform-origin: 100% 50%;
}

.hm-open__zone:hover .hm-open__labellink::after,
.hm-open__zone:focus-within .hm-open__labellink::after {
  transform: scaleX(1);
}

.hm-open__labellink:focus-visible {
  outline: 2px solid var(--color-on-dark);
  outline-offset: 4px;
}

/* Below the desktop layout the fold stops being the point: six cells across
   a tablet are too small to be photographs of anything. The screen gives up
   its viewport height and the zones wrap — territories keep their cells
   together, so a pair never splits across rows. */
@media (max-width: 1023px) {
  /* The spacing tokens themselves shrink below 1024 (gap-heading 64 -> 40 ->
     24), so the desktop calc that clears the chrome by 62px would clear it by
     18 here and by 4 at 375 — the plate's clear space very nearly landing on
     the sentence's first line. Restated per breakpoint against the RENDERED
     plate, which is what the old claim band's comment warned about. */
  .hm-open {
    min-block-size: 0;
    padding-block-start: calc(var(--gap-component) + var(--gap-heading) + var(--gap-component));
    padding-block-end: var(--gap-component);
  }

  /* The zone gap drops to the cell gap once the band wraps. Two singles
     sharing a row must come out at exactly the width the paired zone gives
     its two cells, and they only do that when both gaps are the same value —
     otherwise every second row is 6px shorter than the one above it. */
  /* And it stops bleeding. Full width is the DESKTOP header's whole point —
     the cell reaches the page edge and its label sits with it, which reads as
     a caption to a bleeding image. Wrapped into rows it reads as a label
     jammed against the screen edge, so the band takes the page's own gutter
     from here down.

     🔴 THE PAGE'S OWN GUTTER, WHICH MEANS THE PAGE'S OWN BREAKPOINTS — see
     the 780 block below. This value is a RESTATEMENT of `.container`'s, not
     an independent one, so its steps belong to layout.css (1024 / 780) and
     never to the type system (1023 / 767). The tablet step is safe at 1023
     only because the band BLEEDS at 1024 and a bleeding band has no axis to
     disagree on; the mobile step was not, and shipped for three sessions. */
  .hm-open__band {
    flex-wrap: wrap;
    /* Column gap and ROW gap are different jobs here. The column gap has to
       equal the cell gap or the rows come out different widths; the row gap
       has to be bigger than the label beneath a row, or the label reads as a
       heading for the row below it rather than a caption for its own. */
    gap: var(--gap-component) var(--gap-inline);
    margin-block-start: var(--gap-heading);
    padding-inline: var(--gutter-tablet);
    padding-block-end: 0;
  }

  .hm-open__zone {
    flex: 1 1 40%;
  }

  .hm-open__zone--pair {
    flex: 1 1 100%;
  }
}

/* 🔴 780, NOT 767 — THE BAND'S INSET INHERITS THE CONTAINER'S BREAKPOINTS
   (5 Aug, Session XIII, H29).

   `.container`'s gutter steps 80 → 40 at ≤1024 and 40 → 20 at ≤780
   (layout.css), and `--chrome-axis` and the footer follow it. This band's
   inset was written into the ≤767 block beside two rules that genuinely
   belong to the type system — so between 768 and 780 the sentence and the
   trigger stood on the 20px axis while the photographs stood on the 40px
   one, twenty pixels apart, on the page's first screen. Thirteen pixels
   wide, and 768 is one of the three widths this project checks at.

   H4-c said a rule made of tokens inherits THOSE tokens' breakpoints. This
   is the same fault read the other way: a rule that RESTATES another rule
   inherits that rule's breakpoints, whatever tokens it happens to be made
   of. Every other restatement of the page inset on the site is already
   keyed at 1024/780; this was the only one at 1023/767.

   Kept in a block of its own rather than folded into ≤767, because the two
   keys are now different questions and merging them is how this happened. */
@media (max-width: 780px) {
  .hm-open__band {
    padding-inline: var(--gutter-mobile);
  }
}

@media (max-width: 767px) {
  .hm-open {
    padding-block-start: calc(var(--gap-component) * 2 + var(--gap-heading));
  }

  .hm-open__label {
    font-size: var(--fs-caption);
  }
}

/* -----------------------------------------------------------
   3 · Links on black — the draw, never a fade
   ----------------------------------------------------------- */

/* The route links: "Who we are →" and the room verbs. Uppercase label with a
   1px rule at rest that thickens rather than fades. ("One Emirate, drawn →"
   was the map band's caption link and was deleted on 7 Aug; this comment went
   on naming it as an example for a day.) */
.hm-link {
  display: inline-block;
  border-block-end: 1px solid currentcolor;
  padding-block-end: 2px;
  font-size: var(--fs-metadata);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-nav);
  text-transform: uppercase;
  color: var(--color-on-dark);
  transition: border-block-end-width var(--dur-fast) var(--ease-out);
}

.hm-link:hover,
.hm-link:focus-visible {
  border-block-end-width: 2px;
}

.hm-link:focus-visible {
  outline: 2px solid var(--color-on-dark);
  outline-offset: 4px;
}

/* Inside running prose (the mosque's name), where an uppercase label
   would break the sentence. Weight carries it, never colour. */
.hm-link-inline {
  border-block-end: 1px solid currentcolor;
  font-weight: var(--fw-semibold);
  color: var(--color-on-dark);
}

.hm-link-inline:hover,
.hm-link-inline:focus-visible {
  border-block-end-width: 2px;
}

.hm-link-inline:focus-visible {
  outline: 2px solid var(--color-on-dark);
  outline-offset: 4px;
}

/* The middot between names — #595959 against #A6A6A6 names, so the
   separator recedes without either becoming a colour signal. */
.hm-sep {
  color: var(--color-ink-secondary);
}

/* -----------------------------------------------------------
   4 · The practice — the sentence and the mark (§5.1, §5.1a)
   ----------------------------------------------------------- */

/* Two columns from the desktop layout up: the words, and the mark. The mark
   column is `auto` rather than a measured width — it is sized by its own
   block-size below, so the column is whatever that leaves and the two never
   disagree about it.

   THE TWO COLUMNS SHARE BOTH LINES. The ground line is the decided one
   (2 Aug, Mahesh): crest.js ends the mark's box on the drawing's own datum,
   so the route links' rules and the mark's ground are one horizontal, with
   no offset on either side. Centred was the first pass and it read as two
   objects sharing a row; the hairline that used to open the beat went with
   it, because the ground line does the job it was drawn to do.

   `end` used to carry that ground line ALONE, and it cost the whole void
   above the sentence (H10, 3 Aug, Mahesh on the live page). The words'
   column is 269px tall against a 520px drawing, so `end` dropped the text
   251px to reach the datum: the reader met 519px of black after the band's
   last label, and the two columns' TOP halves related to nothing.

   `stretch` + `space-between` gives the beat its second horizontal instead.
   The words now hang from the drawing's top and the route still stands on
   the drawing's ground — the datum is unchanged and still an alignment by
   construction, and the beat is bounded top and bottom rather than only
   underneath. 519 → 203.

   Both halves of that only work at one drawing height: at 520 the 251px
   reappears INSIDE this column, between the sentence and its own two links.
   The block-size below is the other half of this decision, not a separate
   value. */
.hm-sentence {
  padding-block: var(--gap-section) 0;
}

/* The approach is measured INK TO INK. The band ends with a tail of its own
   — `.hm-open__band`'s `padding-block-end` — and `--gap-section` was then
   stacked on top of it, so the reader was given 264 where the site's rhythm
   says 200. This is the one page-local statement of that rhythm on the site
   and it is deliberate: the band's tail is PART of the section gap here,
   not added to it. It is still made of the tokens, so it moves when they
   do; it does not restate their value.

   🔴 1024, NOT 1025, and it is not a typo — it is H4-c's lesson applied.
   This rule is subtracting the BAND's tail, so it is keyed where the band
   changes, and the band wraps (losing that tail to `padding-block-end: 0`
   and gaining a `--gap-heading` margin instead) at 1023. The grid below is
   keyed at 1025 because it is about the MARK, which is `display: none`
   until then. Two adjacent breakpoints, each derived from what its own rule
   is made of. Keyed with the grid at 1025 instead, 1024 — a real iPad
   landscape width — keeps the whole double-count on a one-pixel band and
   the approach steps 144 → 200 → 136 across two pixels. */
@media (min-width: 1024px) {
  .hm-sentence {
    padding-block-start: calc(var(--gap-section) - var(--gap-component));
  }
}

@media (min-width: 1025px) {
  .hm-sentence {
    display: grid;
    grid-template-columns: 1fr auto;
    column-gap: var(--gap-component);
    align-items: stretch;
  }

  /* The words' column fills the row and puts its two ends on the row's two
     edges. The sentence keeps the top, the route keeps the datum, and the
     space between them is whatever the drawing's height leaves — which is
     why that height is a decision and not a default. */
  .hm-sentence__col {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
  }
}

.hm-sentence__line {
  margin: 0;
}

/* Two links, side by side. The gap is --gap-heading rather than a smaller
   one on purpose: at --gap-block they read as one control with a separator,
   which is the nav they must not become. */
.hm-sentence__route {
  display: flex;
  flex-wrap: wrap;
  gap: var(--gap-heading);
  margin: var(--gap-heading) 0 0;
}

/* -----------------------------------------------------------
   4a · The mark (§5.1a) — 47 courses, generated from the records
   ----------------------------------------------------------- */

/* Sized by HEIGHT, never by width: the drawing's proportion is the
   portfolio's own and it changes when the data does, so pinning its width
   would let a future count decide how tall the beat is.

   380 is the beat's OTHER decided number (H10, 3 Aug, Mahesh, off rendered
   options). It is not derived and it cannot be: it sets the row's height,
   and the row's height is the gap between the sentence and its route links
   — 520 leaves 315 there and reads as two unrelated things, 270 aligns by
   arithmetic but renders the mark 95px wide, an icon beside a 52px
   sentence. Every 1px moved here is 1px moved in that gap, so move it by
   looking at the beat, never to make another number come out round. */
.hm-crest {
  display: none;
  color: var(--color-on-dark);
}

@media (min-width: 1025px) {
  .hm-crest {
    display: block;
  }
}

.hm-crest__svg {
  display: block;
  block-size: 380px;
  inline-size: auto;
  max-inline-size: 100%;
}

/* The mark SHIPS DRAWN. js/home.js arms the dash pair, so a script that
   never runs leaves a finished drawing rather than an empty column — the
   same contract the map has carried since the first build. */
.hm-crest--armed .hm-crest__ln {
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
}

/* A stroke is seen being DRAWN — --dur-reveal, not --dur-fast — and the
   length of the gesture is the stagger, which build/lib/crest.js emits per
   element as --d. Last course lands at ~4.2s. Both halves were slowed on
   2 Aug (Mahesh, on the live site): at 0.4s a course arrived rather than
   drew, and the mark read as a flicker of lines switching on. The two route
   links are NOT animated — their underline is a border on the link itself,
   so scaling it would scale the words, and a pseudo-element would fork the
   hover rule the map link shares. */
.hm-crest--drawing .hm-crest__ln {
  animation: hm-crest-draw var(--dur-reveal) var(--ease-out) both;
  animation-delay: var(--d);
}

@keyframes hm-crest-draw {
  to {
    stroke-dashoffset: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .hm-crest--armed .hm-crest__ln,
  .hm-crest--drawing .hm-crest__ln {
    stroke-dasharray: none;
    stroke-dashoffset: 0;
    animation: none;
  }
}

/* -----------------------------------------------------------
   5 · The four rooms (§5.2) — fallback forms until G6 lands
   ----------------------------------------------------------- */

.hm-room {
  padding-block-start: var(--gap-section);
}

/* A room's parts sit INSIDE one .container rather than each carrying the
   class: a measure-limited element that is also the container measures
   its own max-inline-size and then pays the gutters out of it, which
   rendered the names wall 169px wide inside a 960px column. The recorded
   rule — a measure-limited paragraph never carries .container itself. */
.hm-room__print {
  margin: 0;
}

/* The hairline-framed print — the fallback scale a mid-tier photograph
   can honestly hold. One rule serves both proportions: the height cap
   binds a landscape frame, the width cap binds a portrait one, and
   neither is ever cropped (E3.6).

   The frame BOUNDS an image, so it takes the structural value on black
   (#595959, 3.00:1) and not --color-hairline-dark, which is 1.66:1 and
   may only separate text sections (continuity §9). */
.hm-room__img {
  max-inline-size: 100%;
  max-block-size: 56vh;
  inline-size: auto;
  block-size: auto;
  border: 1px solid var(--color-ink-secondary);
}

/* learns' fallback: the names wall. It is the room's visual, so it is
   set at display scale rather than at metadata scale — and it is a
   SELECTION like every other room's quiet line, which is why it ends in
   an ellipsis and states no total. */
.hm-room__wall {
  margin: 0;
  /* A wall of names is a field, not reading prose — the display measure
     (24ch) would stack ten names into a column. --measure-body at this
     size runs three lines, which is what a wall looks like. */
  max-inline-size: var(--measure-body);
  font-size: var(--fs-h2);
  font-weight: var(--fw-light);
  line-height: var(--lh-editorial);
  letter-spacing: var(--ls-editorial);
  color: var(--color-on-dark-body);
}

/* The strip: the verb at inline-start is the room's ONE link; the
   evidence name sits opposite it. The print carries no caption of its
   own — this line is the caption. */
.hm-room__strip {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--gap-block);
  padding-block-start: var(--gap-block);
}

/* The colour is stated, not inherited: base.css gives every heading
   --color-ink, and tokens.css lifts headings on a dark ground only for
   the .t-* display classes. Without this the verb is #111111 on #000000
   — invisible the moment any of it sits outside the link. */
.hm-room__verb {
  margin: 0;
  font-size: var(--fs-h3);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-h3);
  letter-spacing: var(--ls-h3);
  color: var(--color-on-dark);
}

/* The verb link is display-scale, so it takes the drawn underline
   rather than the label rule: nothing at rest, drawn from the
   inline-start edge on hover and focus. */
.hm-room__verb .hm-link {
  border-block-end: 0;
  padding-block-end: 0;
  font-size: inherit;
  font-weight: inherit;
  letter-spacing: inherit;
  text-transform: none;
  position: relative;
}

/* The rule grows from the reading edge — `inline-size`, not scaleX, so
   the draw runs the right way in Arabic without a mirrored rule.
   transform-origin has no logical keyword; this does. */
.hm-room__verb .hm-link::after {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  inset-block-end: -4px;
  inline-size: 0;
  block-size: 1px;
  background: currentcolor;
  transition: inline-size var(--dur-fast) var(--ease-out);
}

.hm-room__verb .hm-link:hover::after,
.hm-room__verb .hm-link:focus-visible::after {
  inline-size: 100%;
}

.hm-room__evidence {
  margin: 0;
  font-size: var(--fs-metadata);
  line-height: var(--lh-metadata);
  color: var(--color-on-dark-secondary);
}

/* A selection of the rest — plain text, never links, so the room keeps
   exactly one destination. The ellipsis is the argument. */
.hm-room__quiet {
  margin-block-start: var(--gap-tight);
  max-inline-size: var(--measure-body);
  font-size: var(--fs-metadata);
  font-weight: var(--fw-light);
  line-height: var(--lh-metadata);
  color: var(--color-on-dark-secondary);
}

/* -----------------------------------------------------------
   6 · The Prays moment (§5.3)
   ----------------------------------------------------------- */

.hm-prays {
  padding-block-start: var(--gap-section);
}

/* 🔴 THE DARK RUN CLOSES ITSELF NOW, AND IT DID NOT BEFORE (7 Aug).
 *
 * The run's air after the mosque used to be paid by the MAP: the band sat
 * inside this run and carried `margin-block-start: var(--gap-section)`. The
 * map left the run when it took its own paper ground, and that margin went
 * with it — leaving the run ending flush on its last element's box, with a
 * full-bleed ground change immediately below.
 *
 * 🔴 AND THE INK IS 5px PAST THE BOX, so the mosque's letters were CLIPPED by
 * the white. `.ha-marker` is 84px display type at `line-height: 84px` — an em
 * box exactly one em tall — so the glyphs' own descent falls outside the line
 * box and outside every rect a Range or a getBoundingClientRect can report.
 * The box measurement said the run ended at 8260 and so did the map; the
 * PIXELS said the ink reached 8265. A bounding-box test passes a colliding
 * page, and it did: this was found by cropping the seam and looking at it.
 *
 * `--gap-component` rather than the 200 the map used to spend here. A ground
 * change is the strongest separator on this site and does not need 200px of
 * black in front of it as well — that stacking is what put 388px between the
 * mosque's last ink and this band's first. 64 closes the run symmetrically
 * with the mosque band's own opening gap, clears the glyph ink by 59px, and
 * brings the reader's crossing to 256px. */
.hm-dark {
  padding-block-end: var(--gap-component);
}

/* 🔴 IT SETS NO TYPE OF ITS OWN ANY MORE, because the verb became the beat's
   door (Mahesh, 9 Aug, Session XXXVI) and .ha-room__verb's rule is the site's:
   an h3 whose register is stated by the link inside it. It carried --fs-h2
   light for as long as it was the only verb on the page that was NOT a link —
   a 32px sentence-close against the four rooms' uppercase route labels. Now
   that it is a door it takes the door's register, and the fifth beat's first
   object finally matches the four above it. */
.hm-prays__verb {
  margin: 0;
}

.hm-prays__line {
  margin: var(--gap-block) 0 0;
  max-inline-size: var(--measure-body);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--color-on-dark-secondary);
}

/* -----------------------------------------------------------
   7 · The map — Option B, the constellation (§6.1, E7.1–E7.3)
   ----------------------------------------------------------- */

/* Separated from the mosque line by a #333333 hairline: decorative,
   separating two text sections, which is the one thing 1.66:1 is
   permitted to do. */
/* 🔴 AND IT CLOSES THE BLACK RUN, WHICH IT DID NOT DO BEFORE (5 Aug). The
   bottom is new and it is the other half of the restoration: this section is
   now the last thing on the dark ground, so the distance to the GROUND CHANGE
   is its debt. `.hm-prays` was carrying it while the map was deleted, and
   handing it back without stating it here left the link's ink 3.6px off the
   paper at every width — measured, not noticed, because 3.6px of black under
   a link looks like a tight seam rather than like a missing 200.

   The value is --gap-section rather than --gap-section-major for the reason
   home-alt.css § 3 already gave when the mosque owed it: a room's 280 is the
   distance to the next full-bleed PHOTOGRAPH, which opens with no margin of
   its own, and this is the distance to a ground change whose paper opens on
   its own padding. Same seam, same token, moved to whichever section is
   standing last. */
/* The marks are TRUE 5px (E7.1), which is only true if the drawing does
   not scale under them. So the field takes one of two KNOWN widths —
   never a fluid one — and --mark-scale cancels each one exactly:

     ≥640     560 wide (the authored size)     → marks × 1
     <640     320 wide (0.5714×)               → marks × 1.75

   🔴 THE 840 TIER IS GONE (8 Aug). The drawing used to be drawn 1.5× at
   ≥961 and it cannot be, now that it shares its row with the card column:
   the content column is 960px at every width from 1120 up, so 840 leaves
   120px beside it. At the authored 560 the column is 336px at 1440 and
   240px at 1024, which is where the two-column arrangement starts and why
   it starts there. The drawing is now ONE size from 640 up, so nothing
   about it changes across the arrangement — only where it sits.

   The positions scale and the marks do not, which is the whole of E7.1:
   a constellation that spreads across the page while every mark stays
   the size the spec fixed. No script is involved in any of it.

   🔴 THE WIDTH AND ITS SCALE ARE DECLARED AS A PAIR, ON THE BAND (6 Aug).
   They used to sit apart — the scale on `.hm-map__field`, the width on
   `.hm-map__svg` — restated at each of three breakpoints, which is three
   chances for a width to move without its scale and break the one property
   the whole field rests on. Stated together, a breakpoint that changes one
   has the other in front of it.

   And the band is where they live rather than the field, because the CLOSING
   LINE reads the width too: the drawing's right edge is what the link lands
   on, and a sibling cannot read a custom property set on the field. */
/* 🔴 THE BAND IS ON PAPER (Mahesh, 7 Aug), AND THAT DELETED TWO THINGS.
 *
 * THE MARGIN. It used to carry `margin-block-start: var(--gap-section)` on
 * top of its own 200 padding, because it followed the mosque ON THE SAME
 * BLACK and two text sections on one ground need air between them. A GROUND
 * CHANGE is the separation — it is the strongest one this site has — so 200px
 * of black margin before a white band is 200px of nothing, and it read as
 * exactly that: 388px from the mosque's last ink to this head's first.
 *
 * THE HAIRLINE, for the same reason. `--color-hairline-dark` at 1.66:1 exists
 * to separate two TEXT sections sharing a ground, which is the one thing that
 * value is permitted to do. There is no shared ground here any more, and a
 * hairline drawn at the seam of a ground change is a line nobody can see
 * doing a job nothing needs.
 *
 * What stays is the padding: the band's own air, inside its own paper. */
/* 🔴 THE TWO PADDINGS DIFFER, AND THE READOUT IS THE WHOLE REASON.
 *
 * Measured at 1440: the white above the head is 200. Below the drawing the
 * band also carries the readout — `--gap-block` of separation plus one
 * reserved line — which is 50px that is INVISIBLE AT REST. At a symmetric
 * 200 the band therefore reads 200 above and 250 below, bottom-heavy by
 * exactly the amount the reader cannot see.
 *
 * So the closing value is the SITE's closing-band padding,
 * `--gap-section-major / 2` — what `.hm-invitation`, `.aj-band`, `.sv-band`
 * and `.ab-band` all use — which lands the visible air at 190 against the
 * top's 200. No new value, and nothing here is tuned to the readout's exact
 * height: a subtraction of it would go stale the moment the type token moved,
 * which is this project's recorded "a rule made of tokens inherits THOSE
 * tokens' breakpoints" fault wearing a different coat. */
.hm-map {
  --map-width: 560px;
  --mark-scale: 1;

  padding-block: var(--gap-section) calc(var(--gap-section-major) / 2);
  background: var(--color-surface);
}

.hm-map__svg {
  inline-size: var(--map-width);
  max-inline-size: 100%;
  block-size: auto;
}

/* THE SHORELINE, and it is the same object /ajman draws — same path, same
   source, same 2px. `vector-effect: non-scaling-stroke` is what keeps it 2px:
   without it the stroke scales with the drawing and this field is 1.5× the
   authored viewBox at desktop and 0.571× below 640, so one line would render
   at three different weights. Same argument as E7.1's true-5px mark, applied
   to the line instead of the dot.

   It does not draw — see build/lib/home.js. The land is present and the
   records arrive on it. */
/* On paper the shoreline takes the paper's own secondary ink. Same ROLE, same
   2px, reassigned rather than re-invented — v1.3's own headline rule: where a
   surface needs a value the other ground assigned, reassign an existing token
   and never mint a grey. #595959 on #FFFFFF is 7.00:1, against #A6A6A6 on
   #000000's 8.63:1 — both rest states, neither an unreadable one. */
.hm-coast {
  fill: none;
  stroke: var(--color-ink-secondary);
  stroke-width: 2;
  vector-effect: non-scaling-stroke;
}

/* The marks are the records, in the page's ink. On black they read as lights;
   on paper they read as a printed map, which is the truer object for a band
   whose sentence is where these buildings stand. /ajman's map keeps the black
   ground and the white mark — the two surfaces do different jobs, and the
   drawing is the same drawing in either ink. */
.hm-mark {
  fill: var(--color-ink);
  transform: scale(var(--mark-scale));
  transform-box: fill-box;
  transform-origin: center;
}

/* The finished map is the SHIPPED state: with no script, and under
   reduced motion, every mark is already placed. js/home.js arms the
   animation by hiding the marks, then plays it once — so a broken
   script leaves a complete map rather than an empty field. */
.hm-map--armed .hm-mark {
  transform: scale(0);
}

/* 140ms per mark, 34ms apart — the menu overlay's own values, because the
   dots there ARE this choreography (09-menu §9 took it from E7.3). */
.hm-map--drawing .hm-mark {
  transform: scale(var(--mark-scale));
  transition: transform 140ms ease-out;
  transition-delay: calc(var(--mark-index) * 34ms);
}

/* The head states the subject and the record states the figure — the split
   E12 asks for. § 6.1's "no section label" is untouched, because this is a
   statement and not an invented eyebrow.

   🔴 IT NO LONGER SITS ABOVE THE DRAWING AT EVERY WIDTH (Mahesh, 8 Aug). Above
   1024 it is placed in the SECOND column, over the roll call — see § 7a. Below
   that width it is still the band's first line, and the margin here is what
   holds that: the layout is a grid, so the head's own `margin-block-end` and
   the grid's `row-gap` ADD. Stated as the difference between the two tokens,
   the stacked head keeps exactly `--gap-heading` of air and keeps it through
   both steps of the ladder — 40 - 24 at ≤1023 and 24 - 24 at ≤767, where the
   two tokens meet and the margin correctly falls to zero. A raw 16px would
   have been the same number at one width and wrong at the other.

   🔴 `text-wrap: balance` IS THE FIGURE'S DOING, and it is a typographic
   decision rather than a polish pass. The head was one line and became two the
   moment it took the magnitude — 1168.9px of sentence in a 960px column at
   52px — and the browser's own greedy break put 944.4px on the first line and
   "around it." alone on the second. Balance minimises the longest line, so it
   re-derives the break at every column width it is given rather than holding a
   number tuned to one of them: in the 960px column it broke at the sentence's
   own hinge, the COMMA; in the 496px column it now has, it breaks three ways.
   This is why the head could be moved into a half-width column at all.

   Chosen over a tuned `max-inline-size`, which is the other way to reach a
   break and needs a raw px value that holds only at this font size, in this
   language, until any of the three moves.

   Note that `--measure-body-lg` below is INERT on this element and always has
   been: a `ch` is the width of a zero in the element's OWN type, so 48ch at
   52px is 1425.22px — larger than any column this head has ever had. It is
   left in place because it would bind on a wider container, but it is not what
   wraps this head and a future reading of it should not think it is. */
.hm-map__head {
  margin: 0 0 calc(var(--gap-heading) - var(--gap-block));
  max-inline-size: var(--measure-body-lg);
  text-wrap: balance;
}

/* THE REGIONS — ten targets over the drawing, and E7 rule 3 is honoured
   rather than worked around, on /ajman's own arithmetic. Rule 3 bars MARKS as
   hit targets because 28 marks at 44×44px do not fit 375px; there are ten
   regions, not twenty-eight, and they tile the whole field, so the smallest
   is far past the minimum at every width.

   `fill: none` and `pointer-events: all` are set as ATTRIBUTES in
   build/lib/home.js, not here — they are what the element is, and a
   stylesheet that failed to load would otherwise put ten opaque plates over
   the map. What the stylesheet owns is the cursor, because that is
   presentation: the region is a link and it says so under the pointer.

   No focus rule and no outline, deliberately: every region is `tabindex="-1"`
   inside an `aria-hidden` svg and is never in the tab order. The band's
   keyboard doors are the roll call's ten links, in text, below.

   🔴 "NO OUTLINE" WAS AN INTENTION, NOT A RULE, AND base.css OVERRODE IT —
   22 Aug 2026, reported twice from the live page. The paragraph above is
   the whole reason these regions must not carry a focus ring, and it was
   written as though saying nothing were enough to get one. It is not:
   base.css's global `:focus-visible { outline: 2px solid var(--color-ink);
   outline-offset: 3px }` reaches every element on the site, and a
   `tabindex="-1"` anchor IS focusable by pointer even though it is not
   tabbable. Clicking a mark therefore drew a 2px rectangle around the hit
   region's BOUNDING BOX — the Voronoi cell's, not the dot's, which is why
   the box a reader sees is so much larger than the mark they aimed at.

   🔴 AND IT HAD TO BE FIXED HERE RATHER THAN IN THE HANDLER. The first
   attempt blurred the region on click (js/home.js) and could not work:
   focus lands on MOUSEDOWN, so the ring has already painted by the time any
   click listener runs, and on a mark the navigation that follows freezes
   that painted frame on screen while the next page loads. A ring that is
   never painted cannot be un-painted afterwards — the suppression has to
   exist before the pointer goes down, which is CSS's turn, not the
   script's.

   REMOVING IT COSTS NO ACCESSIBILITY, and that is the paragraph above
   rather than a concession: the regions are out of the tab order and hidden
   from assistive technology, so no keyboard or screen-reader user can ever
   reach one to need an indicator. `:focus` and not `:focus-visible` because
   pointer focus is exactly the case being suppressed, and the two selectors
   disagree about it by design. */
.hm-hit:focus,
.hm-hit:focus-visible {
  outline: none;
}

/* 🔴 THE WIDTH CHOOSES THE PARTITION, AND THAT CHOICE IS WHAT THE BAND MEANS.
   At ≥1024 the pointer resolves to a MARK and the card names one building;
   below it the pointer resolves to a DISTRICT and the cards name that
   district's buildings. Both layers ship in every build and `pointer-events`
   retires whichever this width does not use — so the answer never depends on
   document order, and neither layer can quietly start answering for the other
   because a mark moved. The measurement behind the number is in
   build/lib/districts.js markRegions().

   🔴 IT MUST BE STATED ON THE PATH, NOT ON THE ANCHOR. The path carries
   `pointer-events="all"` as a presentation ATTRIBUTE, because that is what the
   element IS and a stylesheet that failed to load must not leave the drawing
   unreachable. An inherited `none` from the anchor LOSES to the child's own
   attribute; a CSS rule on the path beats it. Written on the anchor this rule
   is silently inert and both layers stay live at every width — which reads
   exactly like the breakpoint working. */
.hm-hit {
  cursor: pointer;
}

.hm-hit--district > path {
  pointer-events: none;
}

/* THE ECHO, AND IT IS /ajman's, VALUE FOR VALUE. That page's §3.4 answer to
   "which mark is current" is: the current mark holds the display ink while
   every other drops to secondary — two measured tokens from its own
   rest-and-current grammar, and NEVER opacity, which §9.16 prohibits.

   It is copied here rather than re-decided because the two maps draw the same
   marks from the same file, and a reader who meets a selection on Home and
   then on /ajman must meet the same object. Size is not available as a signal
   and that is E7.1, not taste: a mark is a TRUE 5px at every width, so the
   drawing scales under it and growing one would break the property the whole
   field rests on.

   Keyed off `[data-current]` on the FIELD, so the rest state costs nothing
   until a pointer is actually on the map — with nothing current, every mark is
   simply ink and neither rule applies.

   🔴 THESE TWO RULES WERE DELETED BY ACCIDENT ON 8 AUG and the band shipped
   that way for the length of one build. The span replaced when the readout
   became a card ran from the hit layer's comment to the caption link's, and
   the echo sat between them. Nothing caught it: `p25` asserted that
   `.is-current` was APPLIED and `[data-current]` was SET, and both were — the
   class on the right circle, the attribute on the right field — and not one
   pixel moved, because the rules those hooks exist to trigger were gone.
   **A class-based assertion cannot see whether any ink changed.** p25 now
   reads the computed `fill` of the current mark against its neighbours'. */
[data-current] .hm-mark {
  fill: var(--color-ink-secondary);
}

[data-current] .hm-mark.is-current {
  fill: var(--color-ink);
}

/* -----------------------------------------------------------
   7a · The layout — the drawing beside its answer (8 Aug)
   ----------------------------------------------------------- */

/* 🔴 THE ANSWER SITS BESIDE THE QUESTION, AND THAT IS THE FAULT THIS BAND WAS
   REOPENED FOR. Measured on the shipped build: the readout stood 587px below
   the topmost mark, and head-plus-drawing-plus-answer needed 893px against an
   813px viewport — so at 1440x813, 1280x720 and 1024x768 a reader who hovered
   with the heading in view got a shade change and an answer off the bottom of
   the screen. A column costs nothing and removes the distance entirely.

   ONE DOM ORDER SERVES BOTH ARRANGEMENTS: drawing, then aside. Above 1024 they
   are two columns; below, the aside is simply the next thing down. */
.hm-map__layout {
  display: grid;
  gap: var(--gap-block);
}

/* 🔴 THE ASIDE IS A ONE-CELL GRID AND BOTH CHILDREN SHARE THE CELL, so the
   slot is as tall as the taller of them and RESERVED — showing a card cannot
   push the roll call down, and nothing under the pointer moves. A floor stated
   as a length would be a number tuned to today's type tokens; this is the same
   guarantee with no number in it.

   The regions TILE the field, so anything that moves the drawing moves the
   region under the cursor and changes the answer to the question being asked.
   That is /ajman's recorded "clicking Al Zorah lit Emirates City" arriving
   through layout instead of through scroll, and it is why this is a grid. */
@media (min-width: 1024px) {
  /* 🔴 THE ROW GAP IS THE HEADING GAP, AND IT IS A GROUPING FIX RATHER THAN
     AIR. At the base `--gap-block` the label "Where Proarc has built" sat 24px
     under the head and 24px of INK above its own first district — the label
     read as the head's second line as readily as the list's title, which is
     the one thing a label must never be ambiguous about. Its own attachment to
     the list is 12px and cannot be tightened further, so the separation has to
     come from above: 64 against 12 is unmistakable at a glance.

     `--gap-heading` is the token by name — this is a heading and this is the
     gap under it, the same one it spent at full width before it moved.

     It is safe to raise here because the drawing SPANS both rows, so this gap
     falls between the head and the roll call and nowhere else. Below 1024 the
     base `--gap-block` still governs the drawing-to-answer stack. */
  .hm-map__layout {
    grid-template-columns: var(--map-width) minmax(0, 1fr);
    column-gap: var(--gap-component);
    row-gap: var(--gap-heading);
    align-items: start;
  }

  /* 🔴 THE FIGURE MOVES INTO THE SECOND COLUMN, OVER THE DISTRICTS IT IS THE
     SUM OF (Mahesh, 8 Aug). It stood full-width above everything, so the band
     opened with a 52px sentence and then a --gap-heading of nothing before the
     drawing began: the title and the thing it titles could not be read in one
     glance, and the right column started 180px lower than the left for no
     reason a reader could see. Placed here, the head and the roll call are one
     block of words facing one drawing, and the band's top edge is the map's.

     THE PLACEMENT IS EXPLICIT ON ALL THREE CHILDREN, never auto-flow. Three
     items into a two-column grid places them 1/1, 2/1, 1/2 — the head beside
     the drawing and the ROLL CALL UNDER IT, which is the same auto-placement
     fault this band already paid for once inside `.hm-card` on 8 Aug. Naming
     every cell is what stops a fourth child, whenever one arrives, silently
     re-flowing the other three.

     The drawing spans both rows, so the second column's height is free: the
     head may take two lines or four and nothing under the pointer moves.

     🔴 THE ROLL CALL NO LONGER YIELDS ITS SLOT (Mahesh, 8 Aug). The aside was
     a one-cell grid and the card took the cell, hiding the ten names behind
     `visibility: hidden` — nothing moved and the band never grew, and it was
     still wrong: the reader lost the doors at the moment they were most likely
     to want one. The card now opens UNDER THE DRAWING and this column holds
     nothing but the roll call, which is therefore always readable. */
  /* 🔴 THE SIZE COMES DOWN ONE STEP IN THIS COLUMN, AND THE NUMBER WAS
     MEASURED, NOT PICKED. Mahesh asked for three lines. The column is 336px
     at 1440 and 320px at 1024, and the sentence is ~1,137px of ink at 52px,
     so it cannot be three lines at H1: it renders FOUR at 1440 and FIVE at
     1024. Rendered at 52 / 44 / 40 / 36 / 32 / 28 in both columns, exactly
     one step gives three lines at BOTH widths — 36 is three at 1440 and four
     at 1024, which is a value that holds at the width it was tuned on and
     breaks 416px later. H2 is also the last size that cannot fall to TWO
     lines: 700px of ink over a 336px column has no two-line solution.

     It stays `.t-editorial` — light, editorial tracking, editorial leading.
     Only the size moves, because only the column moved. This is the same
     sentence in a narrower measure, not a demoted heading, and against the
     roll call's 14px label 32px is still unambiguously the band's title. */
  .hm-map__head {
    grid-column: 2;
    grid-row: 1;
    margin-block-end: 0;
    font-size: var(--fs-h2);
  }

  .hm-map__drawing {
    grid-column: 1;
    grid-row: 1 / span 2;
  }

  .hm-map__aside {
    grid-column: 2;
    grid-row: 2;
  }
}

/* -----------------------------------------------------------
   7b · The caption, the cards and the roll call
   ----------------------------------------------------------- */

/* Mahesh's line, 8 Aug: "Selected Proarc landmarks across Ajman". It belongs
   to the DRAWING at --gap-block, and it replaces both the readout and the
   deleted placeholder note — one standing sentence that is true before any
   pointer arrives, which is the state nearly every reader is in. */
.hm-map__caption {
  max-inline-size: var(--map-width);
  margin: var(--gap-block) 0 0;
  color: var(--color-ink-secondary);
}

/* THE CARDS — one above 1024, a district's worth below it, and the same element
   either way.
 *
 * 🔴 `[hidden]` IS RESTATED AS `display:none` AND THAT IS NOT BELT-AND-BRACES.
 * An author `display` on this element beats the UA's [hidden] rule, so the list
 * would ship VISIBLE on every load and a script-less reader would meet an empty
 * box. That is not hypothetical — it is the state `.wk-sectors` is in on
 * /projects right now, and components.css already records the same trap in the
 * same words. */
/* The set's heading, and its [hidden] restated for the same reason the list's
   is: an author `display` here would beat the UA rule and ship an empty line
   on every load. */
.hm-cards__place {
  margin: 0 0 var(--gap-inline);
  color: var(--color-ink-secondary);
}

.hm-cards__place[hidden] {
  display: none;
}

.hm-map__answer {
  margin-block-start: var(--gap-block);
}

.hm-cards {
  display: grid;
  gap: var(--gap-block);
  margin: 0;
  padding: 0;
  list-style: none;
}

.hm-cards[hidden] {
  display: none;
}

/* Below the two-column width the DISTRICT layer answers and the mark layer
   stands down — the swap the section head measures. Stated on the paths,
   never on the anchors, or the presentation attribute wins and both layers
   stay live. */
@media (max-width: 1023px) {
  .hm-hit--district > path {
    pointer-events: all;
  }

  .hm-hit--mark > path {
    pointer-events: none;
  }

  .hm-cards {
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  }
}

.hm-card {
  display: block;
  text-decoration: none;
}

/* The intrinsic ratio is stated so a row reserves its height before the image
   decodes. Every thumb in images/manifest.json is 800x600 — checked, not
   assumed — so one ratio serves all of them. */
.hm-card__thumb {
  display: block;
  inline-size: 100%;
  block-size: auto;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  background: var(--color-hairline);
}

.hm-card__name {
  display: block;
  margin-block-start: var(--gap-inline);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--color-ink);
}

/* The noun and the district, quieter — the site's own set nouns, from
   records.js. Secondary ink at 7.00:1 on paper: a rest state, never an
   unreadable one, and the distinction is colour value and never opacity. */
.hm-card__meta {
  display: block;
  font-size: var(--fs-metadata);
  line-height: var(--lh-metadata);
  color: var(--color-ink-secondary);
}

/* THE ROLL CALL — the ten districts, in text, and the only tab stops this band
   has ever had. It retires the locked v1.3 clause "Home shows the SHAPE,
   /ajman names it" on Mahesh's instruction of 8 Aug; the amendment is in the
   workshop record, not on this published surface. No counts (E12.1) — these
   are selected works, so a number is a claim the set does not make. */
.hm-roll__label {
  margin: 0 0 var(--gap-inline);
  color: var(--color-ink-secondary);
}

.hm-roll__list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.hm-roll__item + .hm-roll__item {
  border-block-start: 1px solid var(--color-hairline);
}

.hm-roll__link {
  display: block;
  padding-block: var(--gap-inline);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--color-ink);
  text-decoration: none;
}

/* 🔴 THE DESKTOP CARD IS STATED HERE, AFTER THE BASE RULES, AND THE POSITION
   IS THE POINT. It was first written inside § 7a's `min-width: 1024` block,
   which sits ABOVE `.hm-card { display: block }` — same specificity, earlier in
   source, so the base rule won and the card rendered stacked at every width.
   It measured 481px tall against a 150px slot and the band grew 330px on
   hover: the rule was present, correct and completely inert. A media query
   grants no extra weight; only order and specificity do. */
@media (min-width: 1024px) {
  /* The answer's floor — the thumbnail's own height at the 200px column below.
     One number, stated once, and the card is built to it rather than the
     reverse. Reserved so that opening a card moves nothing at all: the two
     columns are top-aligned, so the drawing and the roll call are immune by
     arrangement, and this holds the band's own bottom edge still as well. */
  .hm-map__answer {
    min-block-size: 150px;
  }

  /* Horizontal, because the slot is 560 wide and only 150 tall: the thumbnail
     takes its own width and the name and district read beside it, rather than
     under a picture that would push the band down by its whole height. Below
     1024 the same element is a grid of up to six, stacked. */
  .hm-card {
    display: grid;
    grid-template-columns: 200px minmax(0, 1fr);
    grid-template-rows: auto auto;
    column-gap: var(--gap-block);
    align-content: start;
  }

  /* 🔴 PLACED EXPLICITLY, BECAUSE AUTO-PLACEMENT PUT THE META UNDER THE
     THUMBNAIL. Three children into two columns flows img→1/1, name→2/1,
     meta→1/2 — so the card became two rows, 150 + 23, and overran its 150px
     slot by exactly the height of a line nobody meant to put there. The
     picture spans both rows; the text is column two. */
  .hm-card__thumb {
    grid-column: 1;
    grid-row: 1 / span 2;
  }

  .hm-card__name,
  .hm-card__meta {
    grid-column: 2;
  }

  .hm-card__name {
    margin-block-start: 0;
  }
}

/* `.hm-map__line` — the "One Emirate, drawn ->" caption — was DELETED on 7 Aug
   and its keyboard cost stood open until 8 Aug. The roll call closes it: ten
   real links, in the tab order, in text. The map's regions stay `tabindex="-1"`
   and stay pointer-only, which is now a redundancy rather than a gap. */

/* -----------------------------------------------------------
   8 · The invitation (§6.2) — BLACK now, and it runs into the footer
   ----------------------------------------------------------- */

/* 🔴 THE LIT SURFACE MOVED UP ONE BAND (Mahesh, 7 Aug): *"combine the call
   for action to footer like other pages."*
 *
 * STILL E1.2, NOT AN EXCEPTION TO IT. The clause is that the closing band is
 * the inverse of its page, and E1.2a is that on a PAPER page the band owns
 * the ground switch while the footer shares the band's ground — which is
 * precisely why About's ending reads as one block and Home's did not. Home
 * now ENDS on paper, because the map band above took the switch. So the
 * inverse here is black, the footer shares it, and the two become one object
 * separated by structure and scale rather than by colour. Home's ending is a
 * paper page's ending by construction.
 *
 * 📌 AND THE OTHER TWO BLACK PAGES ARE NOT THIS, WHICH IS THE POINT WORTH
 * KEEPING. /ajman and /services still close on a white band between black
 * content and a black footer, because their content still ends on black. The
 * rule did not change; the page did.
 *
 * `.surface-dark` is on the section in the markup, so the type tokens lift
 * themselves and nothing here restates a colour it does not own. The value
 * below is the SITE's closing-band padding — `--gap-section-major / 2`, what
 * `.aj-band`, `.sv-band` and `.ab-band` all use — and `home-alt.css` § 4's
 * 200px override is retired with the horizontal it was built for. */
.hm-invitation {
  padding-block: calc(var(--gap-section-major) / 2);
  background: var(--color-surface-dark);
}

/* LEVELLED at the /ajman build (1 Aug, Mahesh): 32px light, the register
   the 47 record pages, the three sector pages and the ledger already ship.
   §6.2 specified 700 uppercase, which left the site with one band object in
   two registers on 53 built pages — a board-7 leftover on the one page
   drawn before the others, not a decision anyone took. An uppercase
   imperative is also the shouting register E10 bars, and the voice lock is
   state, never justify. The band's ground still inverts per E1.2; only its
   type is levelled. */
.hm-invitation__line {
  margin: 0 0 var(--gap-block);
  max-inline-size: var(--measure-h2);
  font-size: var(--fs-h2);
  font-weight: var(--fw-light);
  line-height: var(--lh-h2);
  letter-spacing: var(--ls-h2);
  color: var(--color-on-dark);
}

/* The action, value for value with `.ab-band__action` — the site's other
   black closing band. Not "the light one inverted": E2 is explicit that dark
   roles have their own assignments and are never derived by inverting the
   light ones, and the way to honour that here is to copy the surface that
   already made the decision rather than to flip this one's values. */
.hm-invitation__action {
  display: inline-block;
  padding-block: var(--gap-tight);
  padding-inline: var(--gap-heading);
  border: 1px solid var(--color-on-dark);
  font-size: var(--fs-metadata);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-nav);
  text-transform: uppercase;
  color: var(--color-on-dark);
  transition: background-color var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out);
}

.hm-invitation__action:hover,
.hm-invitation__action:focus-visible {
  background: var(--color-on-dark);
  color: var(--color-surface-dark);
}

/* -----------------------------------------------------------
   9 · Breakpoints — restack, never shrink
   ----------------------------------------------------------- */

@media (max-width: 767px) {
  /* The strip wraps with the verb on the first line and the name
     beneath it (§5.2), rather than squeezing two items onto one. */
  .hm-room__strip {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--gap-inline);
  }

  /* A phone holds less height than a desktop, so the print is capped by
     width there instead — still its own proportion, still uncropped. */
  .hm-room__img {
    max-block-size: none;
  }
}

@media (max-width: 1023px) {
  /* The restatement, keyed to 1023 and NOT 1024 (H4-c, 3 Aug). The two gap
     tokens are themselves responsive and step at 1023, so a rule keyed one
     pixel above them paid its full value on a SINGLE PIXEL of width and
     less than desktop across the whole band it exists to protect — 152 at
     1024, 104 from 768 to 1023, against 128 on desktop. A rule made of
     tokens inherits THOSE tokens' breakpoints.

     --gap-block twice returns the sum to the desktop 128 once the two 64s
     have become 40s. The plate does not shrink when the spacing does, so
     the clearance must not either. No new value, no new token. */
  .hm-claim-band {
    padding-block-start: var(--arrival-clearance);
  }
}

@media (max-width: 639px) {
  .hm-map {
    --map-width: 320px;
    --mark-scale: 1.75;
  }
}

/* -----------------------------------------------------------
   10 · Reduced motion
   ----------------------------------------------------------- */

/* tokens.css already clamps every transition to 0.01ms under reduce, so
   nothing needs zeroing here. What DOES need stating is the delay: a
   zero duration does not cancel a 34ms-per-mark stagger, and the map
   would sit blank for its whole sweep. js/home.js does not arm at all
   under reduce; this is the belt to that brace. */
@media (prefers-reduced-motion: reduce) {
  .hm-map--armed .hm-mark,
  .hm-map--drawing .hm-mark {
    transform: scale(var(--mark-scale));
    transition-delay: 0s;
  }
}
