/* ---------------------------------------------------------------------------
   Sankartik — page components: hero, audience router, service tiers,
   promise panel, process, locations
--------------------------------------------------------------------------- */

/* Hero -------------------------------------------------------------------
   Dark ground, full-bleed image, offset text block.
   Two headline versions, A and B, are both built. The active one is chosen in
   src/_data/site.json (heroVersion) so they can be compared without editing
   markup. See LAUNCH-BLOCKERS.md — the A/B test is a Gate 2 requirement. */

.hero {
  position: relative;
  background: var(--ground-dark);
  color: var(--on-dark);
  overflow: hidden;
}

.hero-media {
  position: absolute;
  inset: 0;
  z-index: 0;
}

.hero-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Scrim. The hero image is decorative behind text, so the text pair must hold
   against the image, not only against the ground colour. */
.hero-media::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    100deg,
    rgba(14, 42, 36, 0.94) 0%,
    rgba(14, 42, 36, 0.86) 46%,
    rgba(14, 42, 36, 0.58) 100%
  );
}

/* Empty state. No hero photograph exists yet (site photographs outstanding).
   The hero renders on the flat dark ground rather than reserving a grey box:
   an empty section is removed, not filled. */
.hero-media:empty::after { background: none; }

/* The hero's AI disclosure is the .ai-disclosure component; its placement rules
   live with that component further down this file, not here. They must come
   AFTER it in source order — both are single-class selectors, so at equal
   specificity source order decides, and a placement rule written here would
   lose to the component's own declarations. */

.hero-inner {
  position: relative;
  z-index: 1;
  padding-block: var(--space-3xl);
  display: grid;
  gap: var(--space-l);
}

.hero-text { max-width: 46ch; }

.hero-location {
  font-family: var(--font-ui);
  font-size: var(--step--1);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--on-dark-2);
  margin: 0 0 var(--space-m);
}

.hero h1 { margin-bottom: var(--space-m); }

.hero-support {
  font-size: var(--step-1);
  color: var(--on-dark-2);
  max-width: 52ch;
}

/* At 767px and below, when .hero-media holds an image, .hero-support takes
   var(--on-dark) #F7F8F6 instead of var(--on-dark-2) #A8B5AE. Measured against
   the lightest pixels painted behind it: 5.17:1 at 390px and 5.16:1 at 440px,
   both above the 4.5 AA threshold for text under 24px. #A8B5AE on those same
   backdrops measures 2.59:1 at both widths.

   The :not(:empty) scope means the rule cannot match when .hero-media has no
   child. With no image, .hero-support keeps var(--on-dark-2) on flat
   var(--ground-dark), which is 7.19:1.

   Above 767px the rule does not apply: .hero-support keeps var(--on-dark-2),
   measured 6.04:1 at 1440px. .hero-location is not covered by this rule. */
@media (max-width: 767px) {
  .hero-media:not(:empty) ~ .hero-inner .hero-support { color: var(--on-dark); }
}

.hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-s);
  margin-top: var(--space-l);
}

@media (min-width: 900px) {
  .hero-inner { padding-block: calc(var(--space-3xl) * 1.2); }
}

/* Audience router --------------------------------------------------------
   Light ground, immediately after the hero. Five bordered tiles.
   These are links, not a form: each goes to the page that answers it.
   The tiles are ordered as approved and must not be re-sorted. */

.router-heading { margin-bottom: var(--space-l); }

/* Explicit column counts, not auto-fit — owner decision, 23 September 2026.

   auto-fit with minmax(210px, 1fr) chose the count by arithmetic, and the
   arithmetic gave FOUR columns at 1024, 1120, 1440 and 1920, leaving the fifth
   tile alone on a second row. Measured, not guessed: only 1280 happened to fit
   five. A five-item router that renders 4 + 1 reads as four options and an
   afterthought, which is not the approved order.

   The breakpoints are the ones this stylesheet already uses: 700px is where
   .concept-grid and .contact-details take two columns, and 1120px is the
   site-wide desktop split (base.css:218, measured against the 68ch measure).
   No new breakpoint was introduced. */
.router-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-m);
  list-style: none;
  margin: 0;
  padding: 0;
}

/* These are layout items, not prose. base.css:125 caps every li at --measure
   (68ch) for readability, which silently defeated the grid-column span below:
   the fifth tile was told to span both columns and did, but rendered 614.7px
   inside an 833.3px row at 894px. Same neutralisation, same reason, as
   `.chain li` further down this file. Measured 25 September 2026. */
.router-grid > li { max-width: none; }

/* Tablet: two columns, the fifth tile spanning both so no tile is orphaned. */
@media (min-width: 700px) {
  .router-grid { grid-template-columns: repeat(2, 1fr); }
  .router-grid > li:nth-child(5) { grid-column: 1 / -1; }
}

/* Desktop: all five in one row, equal width. */
@media (min-width: 1120px) {
  .router-grid { grid-template-columns: repeat(5, 1fr); }
  .router-grid > li:nth-child(5) { grid-column: auto; }
}

.router-tile {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-m);
  /* Height comes from the label plus padding, not a fixed minimum. min-height
     100% still stretches a tile to its GRID ROW, which is what keeps the five
     equal on desktop — the row is sized by the tallest label, so a shorter
     label no longer forces the reserved height of a taller one. 44px is the
     tap-target floor and binds only where a label is short enough to fall
     under it, which padding alone already prevents at every measured width. */
  min-height: max(44px, 100%);
  padding: var(--space-l);
  border: 1px solid var(--on-light);
  border-radius: var(--radius-s);
  background: transparent;
  color: var(--on-light);
  font-family: var(--font-ui);
  font-size: var(--step-0);
  text-decoration: none;
  transition: background-color var(--dur-enter) var(--ease-out),
              color var(--dur-enter) var(--ease-out);
}

/* One filled on hover and focus, with an arrow badge on the active tile.
   The border is a real 1px edge, not a panel step: the 1.26:1 panel step is
   below the 3:1 non-text threshold and cannot distinguish a control. */
.router-tile:hover,
.router-tile:focus-visible {
  background: var(--panel);
  color: var(--ground-light);
}

.router-arrow {
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  opacity: 0;
  transition: opacity var(--dur-enter) var(--ease-out),
              transform var(--dur-enter) var(--ease-out);
}

.router-tile:hover .router-arrow,
.router-tile:focus-visible .router-arrow {
  opacity: 1;
  transform: translateX(3px);
}

/* The site-wide label cut. Used by the mega-menu on all 16 pages and by the
   article headers.

   This rule was shared with .facts dt until 23 September 2026, when the .facts
   grid was removed from the homepage by owner decision and that selector went
   with it. It had earlier been shared with the .intro-rows eyebrow labels,
   removed 21 September 2026. Both superseded reasons are recorded rather than
   deleted: the rule was written to hold ONE label style for the site instead of
   copies free to drift apart, and that is still why it is a shared rule and not
   an inline declaration — .eyebrow is simply its only selector now. */
.eyebrow {
  font-family: var(--font-ui);
  font-size: var(--step--1);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--on-light-2);
  margin-bottom: var(--space-2xs);
}

/* The three intro paragraphs of the Who we are section. Ordinary body prose:
   they take
   p { max-width: var(--measure) } and the normal paragraph rhythm from the
   base layer, like every other section's copy.

   Until 21 September 2026 this was a two-column grid — an eyebrow label in
   column 1, its paragraph in column 2, sharing column lines with the .facts
   list that then followed. That was built on 20 September on the reasoning
   that alignment with .facts wins over the measure. The reasoning no longer
   applies, for two reasons now: the labels it aligned had no source in the
   content file and were removed 21 September 2026, and .facts itself was
   removed 23 September 2026, so there is nothing left to align against.
   The element is kept only for the bottom margin that spaces the section. */
.intro-rows {
  margin: 0 0 var(--space-xl);
}

/* Lead paragraph — owner decision, 23 September 2026.

   The "Who we are" block has NO approved heading. The content file marks the
   section as `## Who we are *(light)*` but supplies no `**Heading:**` line,
   and every homepage section that renders a visible heading has one — the
   router, "What we do" and "Concept visuals" all do. So the h2 stays
   visually-hidden and the first paragraph carries the entry weight instead.

   --step-1 is not a new size. It is the step this stylesheet already uses for
   exactly this job: .page-header p (the intro paragraph on every inner page)
   and .hero-support. No new token, no new pattern, and no new copy. */
.intro-rows > p:first-child {
  font-size: var(--step-1);
}

/* Service tiers ----------------------------------------------------------
   Unequal treatments: house construction dominant, interiors on the panel,
   commercial and developments tertiary. The hierarchy is a strategy
   statement, not a layout convenience. */

.services { display: grid; gap: var(--space-l); margin-top: var(--space-xl); }

.service {
  padding: var(--space-xl);
  border-radius: var(--radius-s);
}

/* Primary — house construction. */
.service--primary {
  border: 1px solid var(--on-light);
  background: var(--card-fill);
}
.service--primary h3 { font-size: var(--step-3); }

/* Secondary — interiors, on the panel ground. Text switches to the dark pair. */
.service--secondary {
  background: var(--panel);
  color: var(--on-dark);
}
.service--secondary .service-sub { color: var(--on-dark-2); }

/* Tertiary — commercial and developments, side by side. */
.services-tertiary {
  display: grid;
  gap: var(--space-l);
}

@media (min-width: 800px) {
  .services-tertiary { grid-template-columns: 1fr 1fr; }
}

/* The tertiary cards sit inside column 2 of the split, then halve again, so at
   1280 and 1440 "Discuss a commercial project" wrapped to two lines inside its
   pill - measured w=266 h=85, two lines against a 29px line-height.

   Cause, measured rather than guessed: the pill is inline-flex inside a block
   paragraph in a column flexbox, so it takes the FULL available width (266px)
   and then wraps its own text inside itself - it was never short of room, it
   was too wide. Tested in the page: width:max-content puts it back on one line
   at the same 266px, while parent display:flex does not. max-width:100% keeps
   it inside the card at every width, and --space-m instead of --space-l gives
   the text a little more room without changing type, height or shape. Scoped to
   these cards only; every other button on the site keeps --space-l.

   SCOPED to >=1120px, the same breakpoint .section--split uses, because that is
   the only range where the wrap happens - and because an unscoped max-content
   made the card grid min-width max-content and overflowed the document by 27px
   at 320px. Measured: 16 overflow failures on / and /what-we-do/, none in the
   baseline. The narrow range needs no fix and must not get one. */
@media (min-width: 1120px) {
  .service--tertiary .btn { width: max-content; max-width: 100%; padding-inline: var(--space-m); }
}

/* The two cards are grid siblings and stretch to the taller of the pair. As
   blocks, the surplus height fell after the button: at 894px Developments ran
   114.9px of empty card below its button against Commercial's 57px. A column
   flexbox with the button paragraph pushed down puts the surplus above the
   button instead, so both buttons sit on one line. Measured 25 September 2026. */
.service--tertiary {
  border: 1px solid var(--hairline);
  background: transparent;
  display: flex;
  flex-direction: column;
}

.service--tertiary > p:last-child { margin-top: auto; margin-bottom: 0; }

.service-sub {
  font-family: var(--font-ui);
  font-size: var(--step--1);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--on-light-2);
  margin: 0 0 var(--space-s);
}

/* Prices carry ₹, which Jost does not have — see the note in tokens.css.
   This deliberately uses --font-price (Source Serif 4), not --font-ui. */
.service-price {
  font-family: var(--font-price);
  font-size: var(--step-0);
  margin-block: var(--space-m);
}

.service ul { margin-bottom: var(--space-m); }

/* Promise panel ----------------------------------------------------------- */

.promise-grid {
  display: grid;
  gap: var(--space-l);
  margin-top: var(--space-xl);
}

@media (min-width: 800px) {
  .promise-grid { grid-template-columns: repeat(3, 1fr); }
}

.promise-item h3 {
  font-family: var(--font-ui);
  font-size: var(--step-1);
  font-weight: 400;
  margin-bottom: var(--space-xs);
}

/* On the panel the secondary is captions only, never body, so the promise
   body text stays on the primary #F7F8F6. */
.promise-item p { color: var(--on-dark); }

/* Process ----------------------------------------------------------------
   Narrative stages, not an icon row. */

.process-stages {
  display: grid;
  gap: var(--space-xl);
  margin-top: var(--space-xl);
  padding: 0;
  list-style: none;
  counter-reset: stage;
}

.process-stage {
  display: grid;
  gap: var(--space-xs);
  padding-top: var(--space-m);
  border-top: 1px solid var(--hairline);
  max-width: none;
}

.process-stage-when {
  font-family: var(--font-ui);
  font-size: var(--step--1);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--on-light-2);
}

.process-stage h3 { margin: 0 0 var(--space-2xs); font-size: var(--step-2); }

/* An aside inside a stage — the italic notes in the approved copy. */
.process-note {
  font-style: italic;
  color: var(--on-light-2);
  max-width: var(--measure);
}

/* Compact stage chain, used on the homepage summary. */
.chain {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-xs) var(--space-s);
  list-style: none;
  margin: var(--space-l) 0;
  padding: 0;
  font-family: var(--font-ui);
}

.chain li { margin: 0; max-width: none; }

/* The arrow trails its step rather than leading the next one. As a ::before on
   every li but the first, a wrapped row opened with an arrow pointing at
   nothing to its left — 4 of 5 rows at 390px, 5 of 6 at 320px. The separator
   is between steps in the source line, so either edge is faithful to it; only
   this one cannot start a line. See
   content:124 ("Consultation → Site visit → Design → Estimate")
   Glyph, colour, size and the
   12px gap are unchanged — the margin moves to the other side of the arrow.
   A trailing arrow can still end a row. Measured 25 September 2026. */
.chain li:not(:last-child)::after {
  content: "→";
  margin-left: var(--space-s);
  color: var(--on-light-2);
}

/* Locations --------------------------------------------------------------- */

.locations { display: grid; gap: var(--space-m); margin-top: var(--space-l); }

@media (min-width: 700px) {
  .locations { grid-template-columns: repeat(3, 1fr); }
}

.location-primary { font-size: var(--step-1); }

/* Concept visuals + AI disclosure ---------------------------------------
   The disclosure is a component with fixed wording, adjacent to the visual and
   ALWAYS VISIBLE. Never hover-only, never inside a lightbox caption. It is a
   solid bar, not translucent over the image. */

.concept-grid {
  display: grid;
  gap: var(--space-l);
  margin-top: var(--space-l);
}

@media (min-width: 700px) {
  .concept-grid { grid-template-columns: repeat(2, 1fr); }
}

.concept-figure { margin: 0; }

.concept-image {
  background: var(--frame-back);
  aspect-ratio: 4 / 3;
  max-width: 100%;
}

.ai-disclosure {
  display: block;
  background: var(--ground-light);
  color: var(--on-light);
  border: 1px solid var(--hairline);
  border-top: 0;
  padding: var(--space-s) var(--space-m);
  font-family: var(--font-ui);
  font-size: var(--step--1);
  margin: 0;
}

/* Hero placement for the same component. The component above is used unchanged
   — same fixed wording, same solid backing, same always-visible behaviour. What
   follows places it and nothing else: no colour, font or padding is overridden.

   THESE RULES MUST STAY AFTER .ai-disclosure. Both are single-class selectors,
   so at equal specificity source order decides. Written before the component,
   the restored border-top below silently loses to its border-top: 0 — which is
   exactly what happened on the first attempt at this.

   The label sits in .hero-inner (z-index 1, in flow), so it paints above the
   scrim rather than behind it. .hero-media is position:absolute inset:0
   z-index:0 and carries the scrim as ::after, so a label placed inside it would
   render BEHIND the scrim and be pinned to the image box.

   border-top is restored because in the hero the bar does not butt against an
   image frame above it. The width is capped so it reads as a label on the
   image, not as a full-bleed band across the section.

   The :empty guard mirrors .hero-media:empty::after: with no image there is
   nothing to disclose, so the label does not render at all. That is what keeps
   the disclosure honest when the temporary image is removed — it cannot be left
   behind describing an image that is not there. */
.hero-disclosure {
  border-top: 1px solid var(--hairline);
  max-width: 62ch;
  justify-self: start;
}

.hero-media:empty ~ .hero-inner .hero-disclosure { display: none; }

/* There is deliberately NO build-note or empty-state component.

   A visible "remove this before launch" note in a shipping page is the same
   class of risk as a plausible placeholder: it only fails if someone misses it,
   and it will be missed. A section with no qualifying content does not render
   at all — see the "Our work" section on the homepage and the team section on
   About. What is outstanding is tracked in LAUNCH-BLOCKERS.md, which is read
   deliberately, not noticed by luck. */

/* Page intro -------------------------------------------------------------- */

.page-header { padding-block: var(--space-2xl) var(--space-xl); }
.page-header p { font-size: var(--step-1); }

.prose > * + h2 { margin-top: var(--space-xl); }
.prose > * + h3 { margin-top: var(--space-l); }

.definition-list { display: grid; gap: var(--space-m); margin: var(--space-l) 0; padding: 0; }
.definition-list dt { font-family: var(--font-ui); margin-bottom: var(--space-2xs); }
.definition-list dd { margin: 0; max-width: var(--measure); }

/* Segment pages ----------------------------------------------------------
   House construction, building from abroad, interiors, commercial,
   developments. Shared components, since the five pages carry the same kinds
   of block in different orders. */

/* Page hero — a lighter treatment than the homepage hero: no image, no scrim,
   just the dark ground carrying a headline and a standfirst. */

.segment-hero {
  background: var(--ground-dark);
  color: var(--on-dark);
  padding-block: var(--space-2xl);
}

.segment-hero h1 { margin-bottom: var(--space-s); }

.segment-hero .segment-sub {
  font-family: var(--font-text);
  font-size: var(--step-2);
  font-weight: 400;
  color: var(--on-dark-2);
  margin: 0 0 var(--space-m);
  max-width: 40ch;
}

.segment-hero .segment-standfirst {
  font-size: var(--step-1);
  color: var(--on-dark-2);
  max-width: 54ch;
}

/* An inline run of items separated by middots, used for scope lists that the
   copy sets as a single line. Rendered as a real list for screen readers. */

/* Inline lists — PROTOTYPE, branch layout-prototype, 21 September 2026.

   WAS: a wrapping flex row with a " · " separator injected by ::after on every
   item but the last. Three lists use it, on /house-construction/ (the ten
   stages) and /commercial-construction/ (what we take up, who we work with).

   TWO REASONS IT CHANGED. First, the separator is a child of its own item, so
   when a line wrapped the trailing "·" stayed at the end of that line, pointing
   at nothing — measured at 6 line-ends at 1440 and 10 at 390. Second, run
   together on one line the items did not read as a list of discrete things;
   the ten stages in particular read as prose.

   NOW: a real two-column list at desktop, one column on phones. The separator
   is gone entirely, so the dangling-"·" case cannot occur at any width.

   column-count rather than grid: these lists vary from 3 to 10 items and
   column-count balances them without needing a row count per list. It fills
   column 1 before column 2, so DOM order reads down the left column then down
   the right — the same order as the markup. */

.inline-list {
  list-style: none;
  margin: var(--space-m) 0;
  padding: 0;
  max-width: 72ch;
}

.inline-list li {
  margin: 0 0 var(--space-xs);
  max-width: none;
  break-inside: avoid;
}

/* Two columns from the same breakpoint the split layout uses, so a split
   section's right column and the list inside it change at one width, not two. */
@media (min-width: 1120px) {
  .inline-list {
    column-count: 2;
    column-gap: var(--space-xl);
  }
}

/* Commitment list — the NRI page's seven commitments. Each is a bolded lead-in
   followed by an explanation, so it is a description list, not a bullet list. */

.commitments {
  display: grid;
  gap: var(--space-m);
  margin: var(--space-l) 0 0;
  padding: 0;
}

.commitments > div {
  padding-top: var(--space-m);
  border-top: 1px solid var(--hairline);
}

.commitments dt {
  font-family: var(--font-ui);
  font-size: var(--step-0);
  margin-bottom: var(--space-2xs);
}

.commitments dd { margin: 0; max-width: 60ch; }

.ground-dark .commitments dd,
.ground-panel .commitments dd { color: var(--on-dark-2); }

/* Stage groups — the NRI journey, set as four named phases each holding an
   ordered run of steps. */

.stage-groups {
  display: grid;
  gap: var(--space-l);
  margin-top: var(--space-l);
}

@media (min-width: 860px) {
  .stage-groups { grid-template-columns: repeat(2, 1fr); }
}

.stage-group {
  padding: var(--space-l);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-s);
}

.stage-group h3 {
  font-family: var(--font-ui);
  font-size: var(--step-0);
  font-weight: 400;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  margin-bottom: var(--space-s);
}

.stage-group ol {
  margin: 0;
  padding-left: 1.2em;
}

/* FAQ — an accessible disclosure. Content stays in the DOM for SEO, and each
   question is a real <summary>, so it is keyboard-operable without JS. */

.faq {
  margin-top: var(--space-l);
  max-width: 76ch;
}

.faq-item {
  border-top: 1px solid var(--hairline);
}

.faq-item:last-child { border-bottom: 1px solid var(--hairline); }

.faq-item > summary {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-m);
  padding: var(--space-m) 0;
  cursor: pointer;
  font-family: var(--font-ui);
  font-size: var(--step-0);
  list-style: none;
}

.faq-item > summary::-webkit-details-marker { display: none; }

.faq-item > summary::after {
  content: "+";
  flex: 0 0 auto;
  font-size: var(--step-1);
  line-height: 1;
  transition: transform var(--dur-enter) var(--ease-out);
}

.faq-item[open] > summary::after { content: "−"; }

.faq-answer { padding: 0 0 var(--space-m); }
.faq-answer p:last-child { margin-bottom: 0; }

/* Note inside prose — the italic qualifiers in the approved copy. */
.qualifier {
  font-style: italic;
  color: var(--on-light-2);
}

.ground-dark .qualifier, .ground-panel .qualifier { color: var(--on-dark-2); }

/* Audience blocks — "for landowners", "for investors" on Developments. */

.audience-blocks {
  display: grid;
  gap: var(--space-l);
  margin-top: var(--space-l);
}

@media (min-width: 800px) {
  .audience-blocks { grid-template-columns: 1fr 1fr; }
}

.audience-block {
  padding: var(--space-xl);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-s);
}

/* [28 October 2026: the headings of these two blocks were the global h3 size
   (step 2) while the development cards' own h3 are step 1: two sizes of h3 on
   one page. They now share step 1.] */
.audience-block h3 { font-size: var(--step-1); }

/* A highlighted offer block — Arasur on Developments. */
.offer {
  padding: var(--space-xl);
  background: var(--panel);
  color: var(--on-dark);
  border-radius: var(--radius-s);
  margin-top: var(--space-l);
}

.offer .caption { color: var(--on-dark-2); }

/* Specialisation block ---------------------------------------------------
   A subordinate treatment nested inside a service block — used on
   /what-we-do for "Building your home from abroad", which is a specialisation
   within house construction, not a peer segment. The hairline and the indent
   are what carry the subordination; it is deliberately not a card, because a
   card would read as a sibling. */

.specialisation {
  margin-top: var(--space-l);
  padding-top: var(--space-m);
  border-top: 1px solid var(--hairline);
}

.specialisation h4 {
  font-family: var(--font-ui);
  font-size: var(--step-0);
  font-weight: 400;
  margin-bottom: var(--space-xs);
}

.specialisation p { font-size: var(--step--1); }
.specialisation p:last-child { margin-bottom: 0; }

/* Insights ---------------------------------------------------------------
   Articles and long-form are light ground throughout. */

/* A narrower measure than the page container: long-form prose needs one. */
.article-measure {
  max-width: 46rem;
  margin-inline: auto;
}

.article-header { padding-block: var(--space-2xl) var(--space-l); }

.article-header .eyebrow a { text-decoration: none; }
.article-header .eyebrow a:hover { text-decoration: underline; }

.article-standfirst {
  font-size: var(--step-1);
  color: var(--on-light-2);
  margin-bottom: var(--space-m);
}

.article-meta {
  font-family: var(--font-ui);
  font-size: var(--step--1);
  margin: 0;
}

/* Author block — rendered only when site.showBylines is true and an author is
   named. Gated on Q7; see layouts/article.njk. */
.article-author {
  margin-top: var(--space-l);
  padding-top: var(--space-m);
  border-top: 1px solid var(--hairline);
}

.article-author-name { font-family: var(--font-ui); margin-bottom: 0; }
.article-author-role { font-size: var(--step--1); margin-bottom: var(--space-xs); }

/* Body prose. Headings inside an article step down one level from the page
   h1, so h2 is a section and h3 a sub-point. */
.article-body > h2 {
  font-size: var(--step-2);
  margin-top: var(--space-xl);
}

.article-body > h3 {
  font-size: var(--step-1);
  margin-top: var(--space-l);
}

.article-body > ul,
.article-body > ol { margin-bottom: var(--space-l); }

.article-body li { margin-bottom: var(--space-xs); }

.article-body strong { font-weight: 600; }

/* The italic closing note that ends each article. Carries verified
   commitments only. */
.article-closing {
  font-style: italic;
  padding-top: var(--space-l);
  border-top: 1px solid var(--hairline);
  color: var(--on-light-2);
}

.article-related-heading {
  font-family: var(--font-ui);
  font-size: var(--step--1);
  font-weight: 400;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--on-light-2);
}

.article-related {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-xs);
}

/* Index listing ----------------------------------------------------------- */

.article-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-xl);
  max-width: 46rem;
}

.article-card {
  margin: 0;
  max-width: none;
  padding-top: var(--space-l);
  border-top: 1px solid var(--hairline);
}

.article-card-meta {
  font-family: var(--font-ui);
  font-size: var(--step--1);
  margin-bottom: var(--space-xs);
}

.article-card-title {
  font-size: var(--step-2);
  margin-bottom: var(--space-xs);
}

.article-card-title a { text-decoration: none; }
.article-card-title a:hover { text-decoration: underline; }

.article-card-standfirst { color: var(--on-light-2); margin-bottom: 0; }

/* Contact ----------------------------------------------------------------- */

.contact-details {
  display: grid;
  gap: var(--space-l);
  margin: 0;
  padding: 0;
}

@media (min-width: 700px) {
  .contact-details { grid-template-columns: repeat(2, 1fr); }
}

.contact-details > div { margin: 0; }

.contact-details dt {
  font-family: var(--font-ui);
  font-size: var(--step--1);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--on-light-2);
  margin-bottom: var(--space-2xs);
}

.contact-details dd {
  margin: 0;
  font-size: var(--step-1);
  max-width: 40ch;
}

.numbered-steps {
  margin: var(--space-l) 0;
  padding-left: 1.25em;
  max-width: var(--measure);
}

.numbered-steps li { margin-bottom: var(--space-s); }

/* Enquiry form ------------------------------------------------------------ */

.enquiry-form {
  display: grid;
  gap: var(--space-l);
  max-width: 34rem;
  margin-top: var(--space-l);
}

.field { display: grid; gap: var(--space-2xs); }

/* An author `display` beats the UA sheet's `[hidden] { display: none }` whatever
   the specificity, so .field must restore it or every hidden field renders. The
   NRI follow-up (#abroad-detail) ships with `hidden` and is revealed only on
   "Yes" — content:651 ("**if Yes:** Where are you based, and what time suits you")
   and Phase 5 IA :149. Without this rule it showed to everyone, including with
   JavaScript disabled, and form.js's `detail.hidden = !show` had no visible
   effect. .form-fallback[hidden] below and .megamenu[hidden] / .mobile-nav[hidden]
   in components.css are the same pairing; .field was the one that lacked it.
   Found and fixed 25 September 2026. */
.field[hidden] { display: none; }

.field > label,
.field > legend {
  font-family: var(--font-ui);
  font-size: var(--step-0);
}

/* A fieldset needs its border removed before it lays out like the others. */
fieldset.field {
  border: 0;
  padding: 0;
  margin: 0;
  min-width: 0;
}

fieldset.field > legend { padding: 0; margin-bottom: var(--space-xs); }

.field-help {
  font-size: var(--step--1);
  color: var(--on-light-2);
  margin: 0 0 var(--space-2xs);
  max-width: none;
}

/* "(required)" inside the name and phone labels. Owner decision, 25 September
   2026; the wording is owner-approved and exact.

   NO NEW COLOUR OR SIZE: both tokens are .field-help's own — var(--step--1)
   and var(--on-light-2), 6.85:1 on --ground-light, AA at any size. It is the
   established secondary-text pair on this ground, already carrying the three
   field helpers a few pixels away.

   Not bold, and not --warning: the marker states a fact about the field, it
   does not warn. font-weight is inherited deliberately.

   The span is aria-hidden in the template. Both inputs carry the required
   attribute, which already exposes required=true to assistive technology, so
   an announced "(required)" would be the second one. */
.label-required {
  font-size: var(--step--1);
  color: var(--on-light-2);
}

.field input[type="text"],
.field input[type="tel"],
.field input[type="email"],
.field select,
.field textarea {
  font-family: var(--font-text);
  font-size: var(--step-0);
  line-height: 1.4;
  color: var(--on-light);
  background: var(--card-fill);
  border: 1px solid var(--on-light-2);
  border-radius: var(--radius-s);
  padding: var(--space-s) var(--space-m);
  width: 100%;
  /* 16px minimum prevents iOS zooming the page on focus. */
  min-height: 2.75rem;
}

.field textarea { resize: vertical; min-height: 6rem; }

.field input:focus-visible,
.field select:focus-visible,
.field textarea:focus-visible {
  outline: 2px solid var(--on-light);
  outline-offset: 1px;
  border-color: var(--on-light);
}

/* An invalid field is marked by more than colour: the border thickens and the
   status region announces the problem. */
.field [aria-invalid="true"] {
  border-width: 2px;
  border-color: var(--warning);
}

.radio-row { display: flex; flex-wrap: wrap; gap: var(--space-l); }

.radio { display: inline-flex; align-items: center; gap: var(--space-xs); }

.radio input {
  width: 1.15rem;
  height: 1.15rem;
  accent-color: var(--panel);
}

.radio label { font-family: var(--font-text); font-size: var(--step-0); }

/* Honeypot. Hidden from sight and from assistive technology, but NOT with
   display:none — some bots skip those. It stays out of the tab order via
   tabindex="-1" in the markup. */
.hp-field {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.form-status {
  margin: 0;
  font-family: var(--font-ui);
  font-size: var(--step--1);
  color: var(--warning);
  max-width: none;
}

.form-status:empty { display: none; }

.form-fallback {
  padding: var(--space-m);
  border-left: 3px solid var(--warning);
  background: var(--card-fill);
}

.form-fallback[hidden] { display: none; }
.form-fallback p:last-child { margin-bottom: 0; }

/* Project grid ------------------------------------------------------------
   Every card carries its caption — type, location, date. An uncaptioned image
   cannot be rendered by the partial at all. */

.project-grid {
  list-style: none;
  margin: var(--space-l) 0 0;
  padding: 0;
  display: grid;
  gap: var(--space-l);
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
}

.project-card { margin: 0; max-width: none; }

.project-image {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  background: var(--frame-back);
  border-radius: var(--radius-s);
}

.project-caption {
  margin: var(--space-s) 0 0;
  display: grid;
  gap: 2px;
  max-width: none;
}

.project-type { font-family: var(--font-ui); font-size: var(--step-0); }

.project-meta {
  font-family: var(--font-ui);
  font-size: var(--step--1);
  color: var(--on-light-2);
}

/* Developments location map ------------------------------------------------
   The map on /developments/ and the list under it. Built 9 October 2026;
   redrawn to the owner's reference image, 11 October 2026; polished to the
   owner's list of 14 October 2026.
   Markup: partials/developments-map.njk and src/developments.njk. Positions,
   label sizes and label placement come from src/_data/developmentsView.js,
   written onto the elements; this file sets colour, family and line weight.

   COLOUR. Existing tokens only, as the owner's brief requires; nothing here
   is a new value. The map has three levels, as the reference has:

     developments   a solid --panel pin with its number on a --card-fill
                    disc, and a --card-fill card; the name in --on-light, the
                    home types in --on-light-2
     landmarks      an outlined --panel pin, smaller, with a --panel line icon
                    of its kind, and a plain --on-light label with no card
     the ground     roads, water, parks and airfields in the NON-TEXT support
                    values of the assets/README.md register; road names,
                    place names and edge arrows in --on-light-2

   THE ROADS HAVE THREE WEIGHTS (owner, 14 October 2026): Avinashi Road and
   Trichy Road the broadest and darkest, in --frame-back; the L&T Bypass
   narrower, in --rule-light; the lesser roads a faint hairline in
   --canvas-light. A road's name lies on its road: --on-light-2 is 4.72:1 on
   --frame-back and 5.07:1 on --rule-light.

   (11 October to 14 October: all three named roads were one broad band in
   --pill-hover, and the lesser roads the same hairline. The owner asked for
   the two main roads to read as clearly heavier and darker than the rest.)

   WATER IS --on-dark-2, #A8B5AE, AS A FILL. THIS IS A NAMED EXCEPTION
   (owner, 14 October 2026; LAUNCH-BLOCKERS.md §76; assets/README.md): on this
   map only, #A8B5AE is the fill of the lakes and the line of the Noyyal. It
   is NEVER text, never an icon, never a stroke round text, and never used
   anywhere else on the site. CLAUDE.md bars #A8B5AE on light because it is
   1.99:1 as text there; a water fill is not text, and nothing a visitor reads
   sits on it or depends on it. The register has no blue, and --frame-back,
   the water's colour until then, is now the main roads'.

   PARKS AND FARMLAND are --pill-hover, the palest grey-green in the
   register. Only areas of 0.1 km² and more are drawn, as for lakes. On the
   OpenStreetMap data of 2026 that is almost nothing in this crop; see
   LAUNCH-BLOCKERS.md §76.

   The ground sits well under 3:1 on purpose. It is context; nothing a
   visitor needs depends on telling a road from a field, because every fact
   on the map is repeated as text in the list below it.

   ONE <svg>, TWO LAYERS, FIVE SETS OF MARKS, SWITCHED HERE. Below 640px the
   box is SQUARE and the compact layer shows. The <svg> carries
   preserveAspectRatio="xMidYMid slice", so a square box shows the middle 675
   units of the 1200 x 675 viewBox, which is the window the compact layer is
   drawn into. From 640px the box is 16:9, the whole viewBox, and the full
   layer shows.

   NO LABEL ON THE MAP IS SMALLER THAN 12px — owner decision, 9 October 2026.
   A label is sized in viewBox units, so it grows with the map, and each set
   of marks is drawn for one range of widths. Every pin carries its number
   (owner, 14 October 2026):

     below 360px     compact   numbered pins only: a name at 12px no longer
                               fits
     360px - 639px   compact   numbered pins with their names, place names,
                               edge arrows
     640px - 879px   full      the same
     880px - 1119px  full      the same, with home types
     from 1120px     full      the same, with landmark pins

   Below 320px the road names are hidden as well, and the numerals grow as the
   map shrinks. Sizes are in developmentsView.js; the road and line weights
   for each range are at the foot of this block. 1120px is the breakpoint
   .section--split already uses; 640px and 880px are where a set's smallest
   label would otherwise pass under 12px.

   The compact map is capped at 30rem, which it reaches at 512px. Its type is
   legible on a phone and grows with the box; uncapped it would be 22px at
   639px. (The first build kept this capped square up to 1119px. On a tablet
   it sat at the left of the column with a third of the width empty.)

   NO MOTION. Nothing here transitions. THE HIGHLIGHT (owner, 14 October
   2026) is a change of state only: pointing at a development in the list
   outlines its pin and card on the map and fills its number in the list, and
   pointing at a pin or card does the same. It is CSS, with no script. It
   answers the pointer only: the list's entries hold nothing focusable, and
   the map is one image to a screen reader, so neither side has anything the
   keyboard could focus. Twelve developments are provided for; a thirteenth
   would show without the highlight.

   NO SHADOW either: the reference's cards cast one, and nothing on this site
   does. A card is told from the ground by its fill and a hairline. */

.devmap-figure { margin: var(--space-l) 0 0; }

.devmap {
  width: 100%;
  max-width: 30rem;
  aspect-ratio: 1 / 1;
  background: var(--ground-light);
  border: 1px solid var(--frame-back);
  border-radius: var(--radius-s);
}

.devmap__layer--full { display: none; }

/* The compact layer's two sets of marks. Numbers first, because the narrowest
   screen is the default; names take over at 360px. */
.devmap__layer--compact .devmap__marks--names { display: none; }

@media (min-width: 360px) {
  .devmap__layer--compact .devmap__marks--names { display: inline; }
  .devmap__layer--compact .devmap__marks--numbers { display: none; }
}

/* The full layer's three sets. The narrowest of them is the default here for
   the same reason. */
.devmap__marks--wide,
.devmap__marks--full { display: none; }

@media (min-width: 640px) {
  .devmap { max-width: none; aspect-ratio: 16 / 9; }
  .devmap__layer--full { display: inline; }
  .devmap__layer--compact { display: none; }
}

@media (min-width: 880px) {
  .devmap__marks--mid { display: none; }
  .devmap__marks--wide { display: inline; }
}

@media (min-width: 1120px) {
  .devmap__marks--wide { display: none; }
  .devmap__marks--full { display: inline; }
}

/* Below 320px. The drawing is the viewport less the two 16px gutters and the
   2px of border, across 675 units, and a numeral must be 12px on screen, so
   in viewBox units it needs 8100 / (100vw - 34px): 28.3 at 320px, 39.3 at
   240px. That curve bows downward, so the straight line below, which is above
   it at both ends, is above it everywhere between: 31 at 320px rising to
   39.8 at 240px. The pin was drawn to hold a numeral of that size. A road
   name at 12px does not fit beside the pins on a map this small, so there
   are none.

   CSS overrides the font-size attribute the template writes; that is what
   lets one rule here correct a size set in the data layer. */
@media (max-width: 319.98px) {
  .devmap__marks--numbers .devmap__roadlabel { display: none; }
  .devmap__marks--numbers .devmap__number { font-size: calc(31px + (320px - 100vw) * 0.11); }
}

.devmap__green { fill: var(--pill-hover); }
.devmap__airfield { fill: var(--canvas-light); }
/* The named exception: see the head of this block. A fill and a line, never
   text. */
.devmap__water { fill: var(--on-dark-2); }

.devmap__road,
.devmap__runway,
.devmap__river,
.devmap__arrow,
.devmap__icon {
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.devmap__river { stroke: var(--on-dark-2); }
.devmap__road--minor { stroke: var(--canvas-light); }
.devmap__road--bypass { stroke: var(--rule-light); }
.devmap__road--main { stroke: var(--frame-back); }
/* A runway is a pale line across its airfield, not another road. */
.devmap__runway { stroke: var(--ground-light); }

.devmap__pin,
.devmap__pindot { fill: var(--panel); }
.devmap__pinhole { fill: var(--card-fill); }

/* A landmark's pin is the development pin's outline: the same shape and ink,
   smaller, and open. Filled with the ground so a road does not show through.
   Its icon is drawn in the same ink and weight. */
.devmap__pin--landmark {
  fill: var(--ground-light);
  stroke: var(--panel);
  stroke-linejoin: round;
}

.devmap__icon { stroke: var(--panel); }

/* An edge arrow is drawn in the ink of its words. */
.devmap__arrow { stroke: var(--on-light-2); }

.devmap__card {
  fill: var(--card-fill);
  stroke: var(--hl-card, var(--frame-back));
}

/* `color` is set as well as `fill`, and fill follows it. The rendered
   contrast guard reads an element's computed colour; a fill it cannot see
   would be measured as whatever colour the text inherited. */
.devmap text {
  font-family: var(--font-ui);
  font-weight: 400;
  fill: currentColor;
}

.devmap__name,
.devmap__number,
.devmap__landmark { color: var(--on-light); }

.devmap__types,
.devmap__roadlabel,
.devmap__place,
.devmap__edge { color: var(--on-light-2); }

/* A label that sits on the map itself, not on a card, is drawn over a
   ground-coloured edge: a road or a lake behind does not run through the
   letters. A road's name needs none. It lies on its own road. */
.devmap__landmark,
.devmap__place,
.devmap__edge {
  paint-order: stroke;
  stroke: var(--ground-light);
  stroke-linejoin: round;
}

/* The highlight. Pointing at a list entry, or at a pin or card, sets these on
   the entry and on that development's pin and card; each element uses the
   ones it needs. --panel outlines; the number in the list is filled with it,
   as the pin is, and its digit turns --on-dark, 11.42:1. */
.devmap__pin:not(.devmap__pin--landmark) {
  stroke: var(--hl-pin, none);
  stroke-linejoin: round;
}

.devmap-section:has(.dev-entry:nth-child(1):hover, [data-dev="1"]:hover) :is(.dev-entry:nth-child(1), [data-dev="1"]),
.devmap-section:has(.dev-entry:nth-child(2):hover, [data-dev="2"]:hover) :is(.dev-entry:nth-child(2), [data-dev="2"]),
.devmap-section:has(.dev-entry:nth-child(3):hover, [data-dev="3"]:hover) :is(.dev-entry:nth-child(3), [data-dev="3"]),
.devmap-section:has(.dev-entry:nth-child(4):hover, [data-dev="4"]:hover) :is(.dev-entry:nth-child(4), [data-dev="4"]),
.devmap-section:has(.dev-entry:nth-child(5):hover, [data-dev="5"]:hover) :is(.dev-entry:nth-child(5), [data-dev="5"]),
.devmap-section:has(.dev-entry:nth-child(6):hover, [data-dev="6"]:hover) :is(.dev-entry:nth-child(6), [data-dev="6"]),
.devmap-section:has(.dev-entry:nth-child(7):hover, [data-dev="7"]:hover) :is(.dev-entry:nth-child(7), [data-dev="7"]),
.devmap-section:has(.dev-entry:nth-child(8):hover, [data-dev="8"]:hover) :is(.dev-entry:nth-child(8), [data-dev="8"]),
.devmap-section:has(.dev-entry:nth-child(9):hover, [data-dev="9"]:hover) :is(.dev-entry:nth-child(9), [data-dev="9"]),
.devmap-section:has(.dev-entry:nth-child(10):hover, [data-dev="10"]:hover) :is(.dev-entry:nth-child(10), [data-dev="10"]),
.devmap-section:has(.dev-entry:nth-child(11):hover, [data-dev="11"]:hover) :is(.dev-entry:nth-child(11), [data-dev="11"]),
.devmap-section:has(.dev-entry:nth-child(12):hover, [data-dev="12"]:hover) :is(.dev-entry:nth-child(12), [data-dev="12"]) {
  --hl-pin: var(--panel);
  --hl-card: var(--panel);
  --hl-card-w: 3;
  --hl-badge: var(--panel);
  --hl-badge-ink: var(--on-dark);
}

/* Line weights, in viewBox units, for each range of widths. A unit is about
   half a pixel on a phone and nine tenths of one on a desk, so the figures
   fall as the map widens and the pixels stay much the same. A main road is
   about twice the height of the capitals of the name that lies on it, the
   bypass about one and a half times. */
.devmap__layer--compact .devmap__road--minor { stroke-width: 3; }
.devmap__layer--compact .devmap__road--bypass { stroke-width: 24; }
.devmap__layer--compact .devmap__road--main { stroke-width: 38; }
.devmap__layer--compact .devmap__river { stroke-width: 7; }
.devmap__layer--compact .devmap__runway { stroke-width: 5; }
.devmap__layer--compact .devmap__card { stroke-width: var(--hl-card-w, 2); }
.devmap__layer--compact .devmap__pin:not(.devmap__pin--landmark) { stroke-width: 6; }
.devmap__layer--compact .devmap__place,
.devmap__layer--compact .devmap__edge { stroke-width: 7; }
.devmap__layer--compact .devmap__arrow { stroke-width: 3.5; }

.devmap__layer--full .devmap__road--minor { stroke-width: 3; }
.devmap__layer--full .devmap__road--bypass { stroke-width: 22; }
.devmap__layer--full .devmap__road--main { stroke-width: 34; }
.devmap__layer--full .devmap__river { stroke-width: 6; }
.devmap__layer--full .devmap__runway { stroke-width: 5; }
.devmap__layer--full .devmap__card { stroke-width: var(--hl-card-w, 2); }
.devmap__layer--full .devmap__pin:not(.devmap__pin--landmark) { stroke-width: 6; }
.devmap__layer--full .devmap__pin--landmark,
.devmap__layer--full .devmap__icon { stroke-width: 1.5; }
.devmap__layer--full .devmap__landmark,
.devmap__layer--full .devmap__place,
.devmap__layer--full .devmap__edge { stroke-width: 5; }
.devmap__layer--full .devmap__arrow { stroke-width: 3.2; }

@media (min-width: 880px) {
  .devmap__layer--full .devmap__road--minor { stroke-width: 2.2; }
  .devmap__layer--full .devmap__road--bypass { stroke-width: 17; }
  .devmap__layer--full .devmap__road--main { stroke-width: 27; }
  .devmap__layer--full .devmap__river { stroke-width: 5; }
  .devmap__layer--full .devmap__runway { stroke-width: 4; }
  .devmap__layer--full .devmap__card { stroke-width: var(--hl-card-w, 1.4); }
  .devmap__layer--full .devmap__pin:not(.devmap__pin--landmark) { stroke-width: 5; }
  .devmap__layer--full .devmap__arrow { stroke-width: 2.4; }
}

@media (min-width: 1120px) {
  .devmap__layer--full .devmap__road--minor { stroke-width: 1.6; }
  .devmap__layer--full .devmap__road--bypass { stroke-width: 14; }
  .devmap__layer--full .devmap__road--main { stroke-width: 22; }
  .devmap__layer--full .devmap__river { stroke-width: 4; }
  .devmap__layer--full .devmap__runway { stroke-width: 3; }
  .devmap__layer--full .devmap__card { stroke-width: var(--hl-card-w, 1); }
  .devmap__layer--full .devmap__pin:not(.devmap__pin--landmark) { stroke-width: 4; }
  .devmap__layer--full .devmap__arrow { stroke-width: 1.8; }
}

.devmap-credit,
.devmap-areanote {
  margin-top: var(--space-xs);
  font-family: var(--font-ui);
  font-size: var(--step--1);
  color: var(--on-light-2);
}

/* The credit is a link to OpenStreetMap's copyright page. It takes the base
   link treatment: inherited ink and an underline. */

/* The list. Plain text: a name, its home types, its status, then one row per
   landmark with the drive time at the end of the row. No tile, no border, no
   enlarged number — CLAUDE.md §7 forbids a statistics band, and drive times
   set large are one. The columns are a reading layout, not cards.

   The entries run west to east, and their numbers are the pins' numbers
   (owner, 14 October 2026). At most five landmarks are listed for each.

   The landmark rows are Jost, as the landmark labels on the map are: they
   are labels and figures, not prose. */

.dev-list {
  list-style: none;
  margin: var(--space-xl) 0 0;
  padding: 0;
  display: grid;
  gap: var(--space-xl) var(--space-xl);
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 17rem), 1fr));
}

.dev-entry { margin: 0; max-width: none; }

.dev-entry h3 {
  display: flex;
  align-items: flex-start;
  gap: 0.5em;
  font-size: var(--step-1);
  margin-bottom: var(--space-xs);
}

/* The number that keys a development to its pin. It is drawn as the head of
   that pin: a --panel ring round a light disc, with the number in --on-light.
   Its size is the heading's, so the two share a line. Highlighted, it fills
   with --panel, as the pin itself is.

   (The first build set it as plain grey type, "not a badge, not a circle",
   to stay clear of the rule against circled drive times. It is not a drive
   time. It is a key, and a key has to look like the thing it points to.) */
.dev-entry__number {
  flex: none;
  display: grid;
  place-items: center;
  width: 1.7em;
  height: 1.7em;
  border: 0.2em solid var(--panel);
  border-radius: 50%;
  background: var(--hl-badge, transparent);
  font-family: var(--font-ui);
  font-weight: 400;
  font-size: 0.7em;
  line-height: 1;
  color: var(--hl-badge-ink, var(--on-light));
}

.dev-entry__types,
.dev-entry__phase {
  margin: 0;
  font-family: var(--font-ui);
}

.dev-entry__phase { color: var(--on-light-2); }

.dev-entry__landmarks {
  list-style: none;
  margin: var(--space-s) 0 0;
  padding: 0;
  font-family: var(--font-ui);
}

.dev-entry__landmarks li {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-m);
  margin: 0;
  padding-block: var(--space-2xs);
  max-width: none;
}

.dev-entry__drive {
  flex: none;
  color: var(--on-light-2);
}

/* How the drive times were measured. One line under the whole list; the
   wording is the owner's, from site.json. */
.dev-list__note {
  margin: var(--space-xl) 0 0;
  font-family: var(--font-ui);
  font-size: var(--step--1);
  color: var(--on-light-2);
}

/* The status list on /developments/ (owner, 27 October 2026): the same entry
   look as the map's list, without numbers or landmarks, and the owner's
   enquiry line under it. No new colour, no new size. */
.dev-list--status { margin-top: var(--space-l); }

.dev-list__enquiry {
  margin: var(--space-xl) 0 var(--space-s);
}
/* [28 October 2026: the enquiry sentences were set in Jost beside prose in
   Source Serif 4, and the same sentence appeared in both faces on the page.
   They are prose, so they take the prose face (CLAUDE.md §6: Source Serif 4
   for prose, Jost for labels and buttons).] */

/* The enquiry block sits straight under a list that already ends in a
   section's padding; its own top padding would double the gap. */
.dev-enquiry { padding-top: 0; }


/* --- 29 October 2026, owner-approved fixes (LAUNCH-BLOCKERS.md §94) ------- */

/* About and Process now open on the dark page hero. The Process stage chain
   sits inside it: its text takes the hero's ink and its arrows the secondary
   dark-ground ink (--on-light-2 is never used on dark, CLAUDE.md §6). Its own
   bottom margin is dropped: the hero's padding ends the section, which also
   closes the large gap that sat between the chain and the first stage. */
.segment-hero .chain { margin-bottom: 0; color: var(--on-dark); }
.segment-hero .chain li:not(:last-child)::after { color: var(--on-dark-2); }

/* Contact: the steps and the form side by side from 1120px. */
.contact-flow > section + section { margin-top: var(--space-2xl); }
@media (min-width: 1120px) {
  .contact-flow {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 34rem);
    gap: var(--space-2xl);
    align-items: start;
  }
  .contact-flow > section + section { margin-top: 0; }
}

/* A short list (three items or fewer) stays in one column: in two, three items
   left one alone in the second column (Commercial, "What we take up"). */
.inline-list:not(:has(> li:nth-child(4))) { column-count: 1; }
