/* ===========================================================
   The project page (×47) — Screen 04, `04-project.md`.

   One layout, forty-seven times. The only thing that varies
   between records is the photograph's own proportion, which is
   why there is no per-record rule anywhere in this file.

   Ground: paper (E1) for the ARGUMENT — the lede, the prose and
   the gallery. The page's APPARATUS takes black, and since 3 Aug
   (H5-a) that is three bands in one run: the neighbours rail, the
   closing band and the footer. They share a ground by E1.2a and
   are separated by structure and the footer's own hairline —
   never by a change of ground.

   The rail is black because it is the site navigating BETWEEN
   records; the specs and the gallery are paper because they are
   evidence about THIS one. Never "to break the monotony" — E1
   bars a ground chosen because it looks better, and that
   phrasing reopens every band on all fifty-eight pages.
   =========================================================== */

/* -----------------------------------------------------------
   The lede: the plate and the column
   ----------------------------------------------------------- */

/* The photograph bleeds to the page's inline-start edge — no
   outer margin on that side — and the text column keeps the
   page's normal margin. In RTL both follow the reading
   direction by construction: this is a grid with logical
   padding, so nothing is mirrored by hand. */
/* The 58% is of the PAGE, so the padding that keeps the text column off the
   page edge belongs to that column and not to the grid: a percentage
   resolves against the grid's content box, so padding here would quietly
   shrink the photograph — and shrink it by a different amount than the
   gallery's lead image, which §6 requires to be exactly as wide and on
   exactly the same edge. */
/* ---- THE ARRIVAL CLEARANCE (H2-b, measured) ----

   This is the one arrival on the site whose own column runs UNDER the
   plate: the column spans 915→1360 at 1440 against a plate at 990→1200.
   So its clearance is measured against the PLATE's bottom edge, not the
   trigger bars' — and the margin it owes is the plate's own clear space
   (`--logo-clear-space-desktop`, 25px at 160; Board 17).

   At `--gap-component` alone that failed on every record and it was visible,
   not marginal: the back link's ink ran INSIDE the plate's rectangle on 30
   of the 47 (up to 55.5px of "← Malls & shops" hidden behind the black
   field), and the name's first line sat 1.0–1.7px inside the same band on
   all of them. Live at every width above 1024.

   The value is derived twice and the two agree. The plate's clear space
   asks for ≥123.8 here; `--gap-component + --gap-heading` is 128, which is
   what the other arrivals have carried since 4f362d2. So the record page
   needs no number of its own — measured after: back ink 29.2 below the
   plate, name 63.1, both clear of the 25px clear space.

   Nothing moves relative to anything else. The photograph does NOT bleed
   from the top — its top edge is this padding (64→128), the same line the
   back link's box has always started on — so the whole lede translates and
   the column keeps its footing on the photograph's top edge. */
.pd-lede {
  display: grid;
  grid-template-columns: 58% 1fr;
  column-gap: var(--gutter-desktop);
  align-items: start;
  padding-block-start: var(--arrival-clearance);
}

/* The ≤1023 restatement that used to sit here is GONE — the value now lives in
   --arrival-clearance (tokens-layout.css), which restates itself at the gap
   tokens' own breakpoint. Seven files carried this identical block; the whole
   point of P3 is that there is one place to change it. */

.pd-column {
  padding-inline-end: var(--gutter-desktop);
}

/* E3.6, and the whole reason this layout exists: the image is
   shown at its OWN aspect ratio, height-capped, never cropped.
   No width/height is asserted here — the intrinsic size does
   the work, so a wide record is wide-and-short in this box and
   a tower is narrow-and-tall. `width: min(100%, cap × ratio)`
   falls out of max-inline-size + max-block-size, which is what
   keeps forty-seven proportions on one rule. */
.pd-plate {
  margin: 0;
}

.pd-plate__img {
  max-inline-size: 100%;
  max-block-size: 68vh;
  inline-size: auto;
  block-size: auto;
}

/* The caption is off-image, always (§2.2). Place, then genre —
   and the genre reads as a fact rather than as a caption's
   tail, which is why it is ink at weight 600 beside secondary
   grey. E9's two labels only; no synonyms. */
.pd-caption {
  display: flex;
  flex-wrap: wrap;
  gap: var(--gap-inline);
  margin-block-start: var(--gap-tight);
  /* The photograph bleeds to the page edge; its caption must not. Type is
     never set flush against the viewport. */
  padding-inline-start: var(--gutter-desktop);
}

.pd-caption__place {
  text-transform: uppercase;
}

.pd-caption__genre {
  font-size: var(--fs-caption);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-caption);
  line-height: var(--lh-caption);
  text-transform: uppercase;
  color: var(--color-ink);
}

.pd-column > * + * {
  margin-block-start: var(--gap-block);
}

.pd-back {
  display: inline-block;
  text-transform: uppercase;
}

/* The site draws: the underline is drawn rather than switched
   on. Link underlines are the one motion this page carries. */
.pd-back::after {
  content: "";
  display: block;
  block-size: 1px;
  inline-size: 0;
  margin-block-start: 4px;
  background: var(--color-ink);
  transition: inline-size var(--dur-fast) var(--ease-out);
}

.pd-back:hover::after,
.pd-back:focus-visible::after {
  inline-size: 100%;
}

.pd-name {
  margin: 0;
  max-inline-size: var(--measure-editorial);
}

.pd-statement {
  margin: 0;
  color: var(--color-ink-secondary);
}

/* -----------------------------------------------------------
   The specification table — §4
   Ruled rows, no frame, no heading. A border around three rows
   is what makes three facts look like a form with fields
   missing; 18 records hold only two.
   ----------------------------------------------------------- */

.pd-specs {
  inline-size: 100%;
  border-collapse: collapse;
  margin-block-start: var(--gap-block);
}

.pd-specs__row + .pd-specs__row .pd-specs__label,
.pd-specs__row + .pd-specs__row .pd-specs__value {
  border-block-start: 1px solid var(--color-hairline);
}

.pd-specs__label,
.pd-specs__value {
  padding-block: var(--pad-table-row);
  vertical-align: baseline;
}

.pd-specs__label {
  inline-size: 40%;
  padding-inline-end: var(--gap-tight);
  text-transform: uppercase;
  font-weight: var(--fw-medium);
}

/* THE DIRECTIONS DOOR ON THE LOCATION ROW — 22 Aug 2026, off ProArc's own
   placemarks (data/locations.json). It is /contact's §7 shape reused rather
   than re-decided: the row's VALUE stays text and the link sits beside it,
   because an address turned into a link is the thing that clause refuses.

   The layout — inline-flex, the 12x16 pin, the inline gap — comes from the
   shared rule in components.css, where this selector now sits beside
   .ct-facts__way. What is stated here is only what this SURFACE decides:
   the row is narrow, so the door drops onto its own line rather than
   competing with the district for the same one.

   Underline on the WORD and not the pin, thickening on hover and focus —
   .ct-facts__way's own construction, for its recorded reason: currentcolor
   is ink at 18.88:1 where --color-hairline is 1.45:1 and cannot carry a
   distinction. The pin is aria-hidden, so the accessible name is the word. */
.pd-specs__way {
  margin-block-start: var(--gap-inline);
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
  letter-spacing: var(--ls-caption);
  color: var(--color-ink);
}

.pd-specs__way span {
  border-block-end: 1px solid currentcolor;
  padding-block-end: 2px;
  transition: border-block-end-width var(--dur-fast) var(--ease-out);
}

.pd-specs__way:hover span,
.pd-specs__way:focus-visible span {
  border-block-end-width: 2px;
}

.pd-specs__way:focus-visible {
  outline: 2px solid var(--color-ink);
  outline-offset: 4px;
}

/* -----------------------------------------------------------
   The prose — §5. No heading: "Overview" is an invented
   section label. The measure comes from the token (E11), not
   from a number typed here.
   ----------------------------------------------------------- */

.pd-prose {
  padding-block-start: var(--gap-component);
}

.pd-prose__p + .pd-prose__p {
  margin-block-start: var(--gap-block);
}

.pd-prose__p:first-child {
  font-size: var(--fs-body-lg);
  font-weight: var(--fw-light);
  line-height: var(--lh-body-lg);
  max-inline-size: var(--measure-body-lg);
}

/* -----------------------------------------------------------
   The gallery — §6. One treatment, from one image to twenty.
   The lead takes the hero column's width and edge; the rest
   run two-up with their aspect preserved and cells top-aligned,
   because a forced ratio is what centre-crops the portrait
   images in the library.
   ----------------------------------------------------------- */

.pd-gallery {
  padding-block-start: var(--gap-component);
}

.pd-plate--gallery {
  inline-size: 58%;
}

/* ONE RHYTHM INSIDE THE GALLERY (H8-b, 3 Aug). The lead sat `--gap-block`
   (24) above the row beneath it while the tiles in that same row sat
   `--gutter-desktop` (80) apart — the lead was bound 3.3× more tightly to
   the first row than the tiles were to each other, which is what read as
   the two lower frames being stuck to it.

   The gap and the lead's clearance are now ONE local value, so they cannot
   drift apart at a breakpoint. It binds an existing token per breakpoint
   rather than introducing a value: 80 / 40 / 20, stepping at this file's
   own 1024 and 820. The gutter tokens are fixed, not responsive, so this
   rule does not inherit a breakpoint from them. */
.pd-gallery__rest {
  --gallery-gap: var(--gutter-desktop);

  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--gallery-gap);
  align-items: start;
  padding-block-start: var(--gallery-gap);
}

.pd-gallery__cell {
  margin: 0;
}

.pd-gallery__img {
  inline-size: 100%;
  block-size: auto;
}

.pd-gallery__provenance {
  padding-block-start: var(--gap-tight);
}

/* -----------------------------------------------------------
   The neighbours — §7. One relation per page, named in the
   block's head.
   ----------------------------------------------------------- */

/* The paper→black transition takes the site's existing idiom, the one the
   closing band has carried on all fifty-four pages that hold it: a
   `--gap-section` of PAPER before the ground turns, then the black band's
   own `--gap-section-major / 2` of entry padding. 200 + 140. */
.pd-neighbours {
  margin-block-start: var(--gap-section);
  padding-block-start: calc(var(--gap-section-major) / 2);
}

/* ---- P2-b — THE MARKER DRAWS ITSELF (H5-d, 3 Aug) ----

   MVRDV's arrival, measured, is `scale(0.5) → 1` plus opacity over 300ms.
   🔴 That is NOT what is built here, and the reason is a recorded decision
   rather than a preference: `partials/scripts.html` states R2's motion
   language as *"the site draws — all motion is line-work drawing itself"*
   and rules out **"no decorative fades"** by name. A scale+fade is the one
   shape that decision excludes, and it was excluded when v1's GSAP reveal
   system was deleted rather than ported.

   So the INTENT of P2-b is taken and its mechanism is not. The marker is
   OUTLINE type — it is already line-work — so it draws: the stroke wipes on
   from the inline-start edge, the same vocabulary as the menu's wipe. No
   fade, no scale, no translate, and the text stays real text, which an SVG
   `stroke-dashoffset` version would have cost.

   Ships FINISHED: `js/project.js` adds the armed class, so a script that
   never runs leaves the marker fully drawn rather than invisible. */
/* 🔴 The RESTING state is an explicit inset(), not the absence of a clip.
   `clip-path` does not interpolate between `inset(...)` and `none` — they are
   discrete, so the wipe snapped instead of running and getAnimations()
   reported zero while the end state looked perfect. Both endpoints must be
   inset() functions.

   The -4px bleed is not slack: the 1px text-stroke is centred on the glyph
   outline, so half of it sits OUTSIDE the box on the first and last letters,
   and a flush inset(0) shaves it. */
.pd-neighbours__head {
  clip-path: inset(-4px);
  transition: clip-path var(--dur-fast) var(--ease-out);
}

/* Physical insets, because clip-path has no logical form. RTL is therefore
   restated rather than left to inherit a left-to-right wipe. */
.pd-neighbours--armed .pd-neighbours__head {
  clip-path: inset(-4px 100% -4px -4px);
}

[dir="rtl"] .pd-neighbours--armed .pd-neighbours__head {
  clip-path: inset(-4px -4px -4px 100%);
}

/* Zeroing a duration does not un-arm anything, and the armed state IS a
   hidden marker — so under reduced motion the clip is refused outright.
   js/project.js checks the same query and never arms; this is the second
   half, for an armed class that arrives any other way. */
@media (prefers-reduced-motion: reduce) {
  .pd-neighbours--armed .pd-neighbours__head,
  [dir="rtl"] .pd-neighbours--armed .pd-neighbours__head {
    clip-path: none;
    transition: none;
  }
}

.pd-neighbours__head {
  margin: 0 0 var(--gap-heading);
}

/* ---- P1-b — the relation row (H7, 3 Aug) ----

   The rail used to show ONE relation, first match wins, and that discarded
   579 of the 704 related records the set actually holds. Worse, it produced
   seven closed groups whose members only ever showed each other — a reader
   inside the four City Lifes could not leave through the rail.

   The row sits between the marker and the thumbnails, which is where MVRDV
   puts its own filter row. It ships `hidden` and js/project.js reveals it,
   so a no-JS reader gets the default set and never meets a dead control.

   Squared, uppercase, caption-scale — Work's chip vocabulary, restated for
   the dark ground rather than inherited from it (E2: dark roles are assigned,
   never derived by inverting the light ones). */
.pd-neighbours__relations {
  display: flex;
  flex-wrap: wrap;
  gap: var(--gap-inline);
  margin-block-end: var(--gap-heading);
}

.pd-rel {
  padding: var(--gap-inline) var(--gap-tight);
  border: 1px solid var(--color-on-dark);
  background: transparent;
  font-family: inherit;
  font-size: var(--fs-caption);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-caption);
  text-transform: uppercase;
  color: var(--color-on-dark);
  border-radius: 0;
  cursor: pointer;
}

/* State is FILL plus WEIGHT, and aria-pressed carries it for the machine.
   Never colour alone — and on this ground there is no colour to reach for. */
.pd-rel[aria-pressed="true"] {
  background: var(--color-on-dark);
  font-weight: var(--fw-bold);
  color: var(--color-ink);
}

.pd-rel:focus-visible {
  outline: 2px solid var(--color-on-dark);
  outline-offset: 2px;
}

/* The tail link for a relation the rail cannot hold. Three across is the
   decided geometry and six thumbnails in two rows would be a grid of
   identical cards, so a large relation keeps its three and points at the
   surface that holds all of them. */
.pd-neighbours__rest {
  margin: var(--gap-block) 0 0;
}

.pd-neighbours__restlink {
  color: var(--color-on-dark);
  text-decoration: none;
  border-block-end: 1px solid var(--color-on-dark);
  padding-block-end: 2px;
}

.pd-neighbours__restlink:hover,
.pd-neighbours__restlink:focus-visible {
  font-weight: var(--fw-bold);
}

.pd-neighbours__list {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--gutter-desktop);
  margin: 0;
  padding: 0;
  list-style: none;
}

.pd-neighbours__thumb {
  inline-size: 100%;
  block-size: auto;
  margin-block-end: var(--gap-tight);
}

/* §9.16 — hierarchy never comes from opacity, so a thumbnail is
   never dimmed at rest and revealed on hover. The link's own
   underline carries the state. */
.pd-neighbours__name {
  display: block;
}

.pd-neighbours__link:hover .pd-neighbours__name,
.pd-neighbours__link:focus-visible .pd-neighbours__name {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 4px;
}

.pd-neighbours__meta {
  display: block;
  margin-block-start: 4px;
  text-transform: uppercase;
}

/* -----------------------------------------------------------
   The closing band — §9. Rank 1 on the trigger map, and absent
   from all 47 pages of the built site.
   ----------------------------------------------------------- */

/* The band's own padding follows layout.css's idiom for a statement section
   (--gap-section-major halved). Its BOTTOM is smaller on purpose: E1.2a puts
   the band and the footer on one ground, separated by structure and the
   footer's own hairline — so a section-sized void here would recreate the
   280px of dead black that removing prev/next was meant to close. */
.pd-band {
  margin-block-start: var(--gap-section);
  padding-block: calc(var(--gap-section-major) / 2) var(--gap-component);
}

/* Where the rail precedes it the two are ONE black run, and the margin above
   would paint a 200px stripe of PAPER between two black bands: a margin box
   takes the PAGE's ground, never its own element's. So the separation moves
   inside the black and stays exactly one token wide.

   This is a sibling selector rather than a blanket change because
   `projects/alghalamosque.html` carries no rail — it is the only mosque, so
   it has no sector siblings — and on that one page the band still follows
   paper and still owes the margin. Verify there, not by reasoning. */
.pd-neighbours + .pd-band {
  margin-block-start: 0;
  padding-block-start: var(--gap-section);
}

.pd-band__line {
  margin: 0 0 var(--gap-block);
  font-size: var(--fs-h2);
  font-weight: var(--fw-light);
  letter-spacing: var(--ls-h2);
  line-height: var(--lh-h2);
  max-inline-size: var(--measure-h2);
  color: var(--color-on-dark);
}

.pd-band__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);
}

.pd-band__action:hover,
.pd-band__action:focus-visible {
  background: var(--color-on-dark);
  color: var(--color-surface-dark);
}

/* -----------------------------------------------------------
   Utility
   ----------------------------------------------------------- */

.u-visually-hidden {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* -----------------------------------------------------------
   Breakpoints. The two columns stack at 820px — photograph
   first at its own proportion, then back link, name, statement,
   facts. Nothing is cropped at any width.
   ----------------------------------------------------------- */

@media (max-width: 1024px) {
  .pd-lede {
    column-gap: var(--gutter-tablet);
    padding-inline-end: var(--gutter-tablet);
  }

  .pd-column {
    padding-inline-end: var(--gutter-tablet);
  }

  .pd-caption {
    padding-inline-start: var(--gutter-tablet);
  }

  .pd-gallery__rest {
    --gallery-gap: var(--gutter-tablet);
  }

  .pd-neighbours__list {
    gap: var(--gutter-tablet);
  }
}

@media (max-width: 820px) {
  .pd-lede {
    grid-template-columns: 1fr;
    row-gap: var(--gap-block);
    padding-inline-end: 0;
  }

  /* 🔴 THE STACKED COLUMN TAKES THE PAGE'S GUTTER, NOT THIS BREAKPOINT'S
     (5 Aug, Session XIII, H29 § 4). Stacking is decided at 820; the column's
     INSET is a restatement of `.container`'s, which steps at 1024/780. Both
     were written here, so between 781 and 820 the back link, the name and
     the body stood on 20 while `.container`, `--chrome-axis` (the trigger,
     in the same viewport) and `.pd-gallery__rest` stood on 40. The mobile
     value now lives in its own ≤780 block below, where the page's own gutter
     changes. Home's opening band carried the identical fault; the rule is in
     H29 — a value that RESTATES another rule inherits that rule's
     breakpoints, whatever tokens it names. */
  .pd-column {
    padding-inline: var(--gutter-tablet);
  }

  .pd-plate--gallery {
    inline-size: 100%;
  }

  /* `.pd-caption` needs no statement here at all — the ≤1024 block already
     gives it the tablet gutter and it holds down to 781. One restatement
     removed rather than corrected. */

  .pd-gallery__rest {
    --gallery-gap: var(--gutter-mobile);

    grid-template-columns: 1fr;
  }

  .pd-neighbours__list {
    grid-template-columns: 1fr;
    gap: var(--gap-component);
  }

  .pd-specs__label {
    inline-size: 45%;
  }
}

/* 780, NOT 820 — the page's own gutter step (H29 § 4).
   `.container`'s gutter goes 40 → 20 here (layout.css), and `--chrome-axis`
   and the footer follow it. Everything on this page that carries the PAGE's
   inset steps with them; 820 governs the lede's ARRANGEMENT and nothing
   else. Kept as a separate block rather than folded into the one above,
   because merging two different questions into one key is how this
   happened. */
@media (max-width: 780px) {
  .pd-column {
    padding-inline: var(--gutter-mobile);
  }

  .pd-caption {
    padding-inline-start: var(--gutter-mobile);
  }
}

/* Every animation's finished state is the shipped state, so
   reduced motion removes the transition and keeps the result.
   Durations AND delays both go: a zero duration does not
   cancel a delay. */
@media (prefers-reduced-motion: reduce) {
  .pd-back::after,
  .pd-band__action {
    transition-duration: 0s;
    transition-delay: 0s;
  }

  .pd-back:hover::after,
  .pd-back:focus-visible::after {
    inline-size: 100%;
  }
}
