/* egypt-malls.com — "مؤشر" (Index)
   An investment-grade property index, not a compound brochure.
   Deliberately unlike every sister site: warm graphite ink (not navy #0d2135,
   not green-black #14201c), terracotta signal (not #c9a24a gold), Readex Pro
   + Inter (not Alexandria, not IBM Plex Sans Arabic).
   version rides CSS_V in _inc/site.php */

/* ── Type ───────────────────────────────────────────────────────────────────
   Two variable families, split by script with unicode-range so each language
   gets a face designed for it rather than one font's compromise for the other.

   Arabic — Readex Pro. Drawn by Nadine Chahine (who ran the legibility work
   behind Frutiger Arabic) with Bonnie Shaver-Troup, on reading-proficiency
   research. One 22 KB file covers 200–700, replacing three static Almarai cuts.

   Latin — Inter. Built for screens, tall x-height, and — the reason it wins
   here — real tabular figures. This site is price tables; digits fall in the
   Latin range, so every number on the page now sets on a fixed advance width
   and columns line up. System mono did that too but rendered differently on
   every OS.
   ────────────────────────────────────────────────────────────────────────── */
@font-face {
  font-family: 'Readex Pro';
  font-style: normal;
  font-weight: 200 700;
  font-display: swap;
  src: url('/assets/fonts/readex-arabic.woff2') format('woff2');
  unicode-range: U+0600-06FF, U+0750-077F, U+0870-088E, U+0890-0891, U+08A0-08FF,
                 U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF, U+FE70-FEFF;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('/assets/fonts/inter-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                 U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  /* Warm graphite — the whole palette is tinted toward the terracotta hue,
     so neutrals never read as blue-grey. */
  --ink:        oklch(0.19 0.008 55);
  --ink-2:      oklch(0.28 0.010 55);
  --ink-3:      oklch(0.405 0.014 55);
  --ink-mute:   oklch(0.505 0.012 55);   /* AA on the lightest surface, measured */
  /* Lifted so photographs and figures sit forward of the page instead of
     competing with it. Still tinted toward the terracotta hue — a neutral grey
     here would fight the signal colour and flatten the identity. */
  --paper:      oklch(0.982 0.004 85);
  --paper-2:    oklch(0.962 0.006 85);
  --paper-3:    oklch(0.936 0.008 85);
  --line:       oklch(0.855 0.010 75);
  --line-soft:  oklch(0.905 0.008 75);
  --card:       oklch(0.998 0.002 85);

  /* One signal colour. Egyptian brick — commerce, not luxury. */
  --signal:     oklch(0.55 0.140 45);
  --signal-ink: oklch(0.42 0.130 42);
  --signal-bg:  oklch(0.955 0.022 60);

  /* Data colours: used only on figures, never decoratively. */
  --pos:        oklch(0.50 0.090 175);
  --pos-bg:     oklch(0.955 0.020 175);

  /* Inverted surfaces (footer, index band) need a token that stays dark in
     BOTH themes. Using --ink here made the footer flip to near-white in dark
     mode while its text stayed light grey — unreadable. */
  /* Deep navy (#142d59). The rest of the palette stays tinted toward the
     terracotta, so this is a deliberate second hue: a cool ground for the
     figures and the footer against a warm page. slab-2 is lifted well clear of
     the new slab — at its old value it matched the surface and every border on
     the dark ground would have vanished. */
  --slab:      oklch(0.304 0.085 261);
  --slab-2:    oklch(0.415 0.078 261);
  --slab-text: oklch(0.930 0.014 250);
  --slab-mute: oklch(0.775 0.028 250);

  --radius: 3px;
  --tap: 44px;
  --maxw: 1220px;

  /* Inter first so Latin and digits resolve to it; Readex picks up Arabic via
     its unicode-range regardless of order. */
  --sans: 'Inter', 'Readex Pro', ui-sans-serif, system-ui, 'Segoe UI', Tahoma, sans-serif;

  /* One weight scale, used everywhere. Both families are variable, so these
     cost nothing extra. */
  --w-body: 400;
  --w-med:  500;
  --w-semi: 600;
  --w-bold: 700;
  --mono: ui-monospace, 'SFMono-Regular', 'Roboto Mono', Menlo, monospace;
}

/* Light only, by decision (2026-08-07).
   The product here is photographs and price tables, both of which read better
   on a light surface; this market circulates listings as WhatsApp screenshots,
   where a dark table reads as broken; and every portal a buyer compares against
   is light. Dark mode had also already cost real bugs — the footer inverted to
   near-white and needed its own token set. So the palette commits. */

*, *::before, *::after { box-sizing: border-box; }

html, body {
  overflow-x: hidden;
  overflow-x: clip; /* older Safari ignores clip and falls back to hidden */
}

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  /* Contextual alternates on, and Latin digits set tabular so every price
     column aligns on the decimal without a monospace face. */
  font-feature-settings: 'tnum' 1, 'calt' 1;
  font-variant-numeric: tabular-nums;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 17px;
  line-height: 1.75;           /* Arabic needs air */
  font-feature-settings: 'kern' 1;
  -webkit-font-smoothing: antialiased;
}

/* Arabic headings collapse below ~1.34 — hard floor. */
h1, h2, h3, h4 {
  line-height: 1.38;
  margin: 0 0 .5em;
  font-weight: var(--w-bold);
  letter-spacing: -0.01em;
  color: var(--ink);
}
h1 { font-size: clamp(1.75rem, 1.15rem + 2.4vw, 2.9rem); }
h2 { font-size: clamp(1.35rem, 1.05rem + 1.3vw, 1.95rem); }
h3 { font-size: clamp(1.1rem,  0.98rem + 0.6vw, 1.3rem); }
p  { margin: 0 0 1.05em; max-width: 72ch; }

/* Arabic is cursive — tracking belongs to Latin runs only. The heading/label
   rules above carry hairline letter-spacing for Latin; zero it in RTL so the
   joining is never disturbed. (Voxire/W3C alreq checklist, _research/RESEARCH-2026.md) */
[dir="rtl"] h1, [dir="rtl"] h2, [dir="rtl"] h3, [dir="rtl"] h4,
[dir="rtl"] .brand, [dir="rtl"] .sec-kicker, [dir="rtl"] .idx .k,
[dir="rtl"] .sr-h, [dir="rtl"] .mnav-k, [dir="rtl"] .u-type,
[dir="rtl"] .tabs-bar label { letter-spacing: 0; }

a { color: var(--signal-ink); text-decoration-thickness: 1px; text-underline-offset: 3px; }
a:hover { color: var(--signal); }

img { max-width: 100%; height: auto; display: block; }

/* Every figure in the site is tabular — this is a data instrument. */
.num, td.num, .tabnum {
  /* was system mono: SF Mono on macOS, Consolas on Windows, something else on
     Android — the same price column looked different on every device. Inter's
     tabular figures align identically everywhere. */
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum' 1, 'ss01' 1;   /* ss01: disambiguated 1 / l / I */
  letter-spacing: -0.005em;
}

/* Phone numbers inside Arabic text flip without BOTH of these. */
.ltr, .tel {
  direction: ltr;
  unicode-bidi: isolate;
  display: inline-block;
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
}

.wrap { max-width: var(--maxw); margin-inline: auto; padding-inline: 20px; }

/* ---------- header ---------- */
.site-head {
  position: sticky; top: 0; z-index: 50;
  background: color-mix(in oklch, var(--paper) 88%, transparent);
  backdrop-filter: saturate(1.4) blur(10px);
  border-bottom: 1px solid var(--line);
}
.head-in {
  display: flex; align-items: center; gap: 18px;
  min-height: 62px;
}
.brand {
  display: inline-flex; align-items: center; gap: .5em;
  font-weight: var(--w-bold); font-size: 1.16rem; letter-spacing: -0.02em;
  color: var(--ink); text-decoration: none; white-space: nowrap;
}
.brand b { color: var(--signal); }
.brand-mark { display: block; width: auto; height: 1.4em; flex: none; }
.brand-word { display: inline-block; }
.nav { display: flex; gap: 4px; margin-inline-start: auto; align-items: center; }
.nav a {
  color: var(--ink-2); text-decoration: none; font-size: .93rem;
  padding: 9px 12px; border-radius: var(--radius); white-space: nowrap;
}
.nav a:hover { background: var(--paper-2); color: var(--ink); }

.call {
  display: inline-flex; align-items: center; gap: .5em;
  min-height: 40px; padding: 0 15px;
  background: var(--signal); color: #fff !important;
  border-radius: var(--radius); text-decoration: none;
  font-weight: var(--w-bold); font-size: .95rem;
}
.call:hover { background: var(--signal-ink); color: #fff !important; }

/* ---------- the index band: the site's signature element ---------- */
.index-band {
  background: var(--slab);
  color: var(--slab-text);
  border-block: 1px solid var(--slab-2);
  padding: 14px 0;
  overflow: hidden;
}
.index-band .wrap {
  display: flex; gap: 34px; align-items: baseline;
  overflow-x: auto; scrollbar-width: none;
  /* `center` pushes the first figure past the edge once the row overflows, and
     in RTL it lands beyond scrollLeft:0 where nothing can reach it — at 390px
     the unit count was silently cut in half. `safe` falls back to start
     alignment exactly when that would happen. */
  justify-content: safe center;
  scroll-padding-inline: 16px;
}
.index-band .wrap::-webkit-scrollbar { display: none; }
.idx {
  display: flex; flex-direction: column; gap: 1px; white-space: nowrap;
}
.idx .k { font-size: .70rem; color: var(--slab-mute); letter-spacing: .02em; }
.idx .v { font-size: 1.06rem; font-weight: var(--w-bold); color: #fff; font-variant-numeric: tabular-nums; }
.idx .v span { font-size: .74rem; color: var(--slab-mute); font-weight: 400; }

/* ---------- hero ---------- */
.hero { padding: 54px 0 34px; border-bottom: 1px solid var(--line); }
.hero .eyebrow {
  display: inline-block; font-size: .78rem; font-weight: var(--w-bold);
  color: var(--signal-ink); background: var(--signal-bg);
  padding: 5px 11px; border-radius: 100px; margin-bottom: 16px;
}
.hero p.lede { font-size: 1.1rem; color: var(--ink-2); max-width: 66ch; }

/* ---------- section rhythm: deliberately uneven ---------- */
section { padding: 46px 0; }
section + section { border-top: 1px solid var(--line-soft); }
section.tight { padding: 30px 0; }
section.wide  { padding: 66px 0; }

.sec-head { margin-bottom: 22px; }
.sec-head p { color: var(--ink-3); margin: 0; }

/* ---------- data table: the primary content form ---------- */
.tablewrap {
  background-color: var(--card);
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  touch-action: pan-x pan-y;        /* claim the horizontal gesture explicitly */
  overscroll-behavior-x: contain;   /* and stop it chaining to the clipped root */
  border: 1px solid var(--line); border-radius: var(--radius); background: var(--paper-2);
}
table.data { width: 100%; border-collapse: collapse; font-size: .95rem; min-width: 640px; }
table.data th, table.data td { padding: 11px 14px; text-align: start; border-bottom: 1px solid var(--line-soft); }
table.data thead th {
  background: var(--paper-2); font-size: .78rem; font-weight: var(--w-bold);
  color: var(--ink-3); letter-spacing: .02em; white-space: nowrap;
  position: sticky; top: 0;
  /* the one heavy rule on the table: an index reads top-down off this line */
  border-bottom: 2px solid var(--ink-2);
}
table.data tbody tr:last-child td { border-bottom: 0; }
table.data tbody tr:hover { background: var(--paper-3); }
table.data td a { font-weight: var(--w-bold); text-decoration: none; }
/* figures carry the page: tabular (already global) and one weight up */
table.data td .num, table.data td.num { font-weight: var(--w-med); }

.pill {
  display: inline-block; padding: 2px 8px; border-radius: 100px;
  font-size: .76rem; font-weight: var(--w-bold);
  background: var(--paper-3); color: var(--ink-3);
}
.pill.pos { background: var(--pos-bg); color: var(--pos); }
.pill.sig { background: var(--signal-bg); color: var(--signal-ink); }
/* verdict pills on /answers/: the negative is the site's trust signal and
   gets the signal colour; a partial yes gets the data-positive tint */
.pill.v-neg  { background: var(--signal-bg); color: var(--signal-ink); }
.pill.v-part { background: var(--pos-bg); color: var(--pos); }

/* ---------- project cards: images are mandatory, so lead with them ---------- */
.grid { display: grid; gap: 18px; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); }
.card {
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--card); overflow: hidden;
  display: flex; flex-direction: column;
}
.card .ph { aspect-ratio: 16/10; background: var(--paper-3); overflow: hidden; }
.card .ph img { width: 100%; height: 100%; object-fit: cover; }
.card .body { padding: 14px 15px 16px; display: flex; flex-direction: column; gap: 7px; flex: 1; }
.card h3 { margin: 0; font-size: 1.02rem; line-height: 1.45; }
.card h3 a { color: var(--ink); text-decoration: none; }
.card h3 a:hover { color: var(--signal); }
.card .meta { font-size: .84rem; color: var(--ink-mute); }
.card .price { margin-top: auto; padding-top: 8px; font-weight: var(--w-bold); color: var(--ink); }
.card .price small { font-weight: 400; color: var(--ink-mute); font-size: .78rem; }

/* ---------- answer blocks: the AEO surface ----------
   The one element an answer engine comes to lift. It is marked with the same
   device the site's data furniture already uses — one heavy horizontal rule
   (the table opens with 2px under its header, the active tab carries a 2px
   underline) — set in the signal colour. A signal-coloured SIDE stripe was
   tried and rejected: it is the stock "AI callout" pattern, and it hangs an
   asymmetric accent on prose the owner set centred. Near-white card, never
   the section grey — the three atoms must not blur into one box. */
.answer {
  background: var(--card); border: 1px solid var(--line);
  border-block-start: 2px solid var(--signal);
  border-radius: var(--radius); padding: 18px 22px; margin-bottom: 18px;
}
.answer p:last-child { margin-bottom: 0; }
.answer p:first-child { font-size: 1.06rem; font-weight: var(--w-med); color: var(--ink); }

details.faq {
  border-bottom: 1px solid var(--line-soft); padding: 4px 0;
}
details.faq summary {
  cursor: pointer; padding: 14px 0; font-weight: var(--w-bold); font-size: 1.02rem;
  list-style: none; display: flex; gap: 10px; align-items: flex-start;
}
details.faq summary::-webkit-details-marker { display: none; }
details.faq summary::before {
  content: '+'; color: var(--signal); font-family: var(--mono);
  font-weight: var(--w-bold); flex: none; line-height: 1.5;
}
details.faq[open] summary::before { content: '–'; }
details.faq .a { padding: 0 0 14px 0; color: var(--ink-2); }
details.faq .a p:last-child { margin-bottom: 0; }

/* ---------- source / date attribution: a first-class element ---------- */
.src {
  font-size: .8rem; color: var(--ink-mute); margin-top: 8px;
  display: flex; flex-wrap: wrap; gap: 4px 12px; align-items: center;
}
.src b { font-weight: var(--w-bold); color: var(--ink-3); }

/* ---------- contact: cards, never a table ---------- */
.contact-grid { display: grid; gap: 14px; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
.cc {
  border: 1px solid var(--line); border-radius: var(--radius);
  padding: 16px 17px; background: var(--paper-2);
}
.cc.primary { border-color: var(--signal); background: var(--signal-bg); }
.cc .tag { font-size: .78rem; color: var(--ink-mute); font-weight: var(--w-bold); }
.cc .val { font-size: 1.14rem; font-weight: var(--w-bold); margin: 5px 0 3px; }
.cc .val a { color: var(--ink); text-decoration: none; }
.cc .best { font-size: .83rem; color: var(--ink-3); }

/* ---------- footer ---------- */
.site-foot {
  background: var(--slab); color: var(--slab-text);
  padding: 48px 0 30px; margin-top: 0;
  border-top: 6px solid var(--paper-3);   /* warm step, page -> slab */
  box-shadow: inset 0 1px 0 var(--slab-2);
}
.site-foot a { color: var(--slab-text); text-decoration: none; }
.site-foot a:hover { color: #fff; text-decoration: underline; }
.foot-grid { display: grid; gap: 26px; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
.foot-grid h4 { color: #fff; font-size: .92rem; margin-bottom: 10px; }
.foot-grid ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 7px; font-size: .9rem; }
.foot-bottom {
  margin-top: 30px; padding-top: 18px; border-top: 1px solid var(--slab-2);
  font-size: .84rem; color: var(--slab-mute);
  display: flex; flex-wrap: wrap; gap: 10px 20px; justify-content: space-between;
}

/* ---------- breadcrumbs ---------- */
.crumbs { font-size: .85rem; color: var(--ink-mute); padding: 14px 0 0; }
.crumbs a { color: var(--ink-3); text-decoration: none; }
.crumbs a:hover { color: var(--signal); }

/* ---------- motion: ease-out only, never on layout properties ---------- */
@media (prefers-reduced-motion: no-preference) {
  a, .call, .card h3 a, table.data tbody tr {
    transition: color .18s cubic-bezier(.22,1,.36,1),
                background-color .18s cubic-bezier(.22,1,.36,1);
  }
}

/* ---------- responsive ---------- */
@media (max-width: 900px) {
  .nav a.hide-sm { display: none; }
}
@media (max-width: 640px) {
  body { font-size: 16px; }
  .wrap { padding-inline: 16px; }
  .hero { padding: 34px 0 26px; }
  section { padding: 34px 0; }
  .head-in { gap: 10px; min-height: 56px; }
  .brand { font-size: 1.05rem; }
  .nav a:not(.call) { display: none; }
  .index-band .wrap { gap: 22px; }
}

/* Honeypot / screen-reader-only.
   NEVER use left:-9999px — it extends the document box to ~10,400px and
   creates real horizontal panning on phones. The overflow-x:clip belt hides
   this from emulation but not from an actual device. */
.hp {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px; border: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ============================================================
   Page architecture — added after the owner reported the mall
   page read as scattered. Measured problem: 8,003px tall, with
   2,328px of unbroken prose and the photographs 6,000px down.
   ============================================================ */

/* ---------- hero carrying the project photograph ----------
   The photograph is CONTAINED, never a full-bleed wash. Source photography
   caps at ~1000px and many heroes are 495px; stretched across the viewport
   under a near-opaque slab gradient the photo's measured contribution to the
   final pixels was 3–11% — an invisible smear that only pretended the page
   led with photography. Here the photo renders at a width the asset actually
   covers, as the page's one soft-cornered object, and the copy sits on solid
   slab at full contrast (slab/white pairing, same as the index band). */
.hero-slab { background: var(--slab); border-bottom: 1px solid var(--slab-2); }
.hero-slab > .wrap { padding-block: 44px 38px; }
.hero-slab h1 {
  color: #fff;
  /* a notch below the site h1: the split hero halves the measure */
  font-size: clamp(1.6rem, 1.15rem + 1.8vw, 2.35rem);
}
.hero-slab .eyebrow { background: color-mix(in oklch, var(--signal) 30%, transparent); color: #fff; }
.hero-slab .answer {
  /* opaque mix, not translucent white: same visual on the slab, but the
     colour is self-contained — measurable and independent of what is behind */
  background: color-mix(in oklch, var(--slab) 93%, white);
  border-color: var(--slab-2);
  border-block-start-color: var(--signal);   /* the answer keeps its rule on the slab */
}
.hero-slab .answer p { color: var(--slab-text); }
.hero-slab .src { color: var(--slab-mute); }
.hero-slab .src b { color: var(--slab-text); }
.hg { display: grid; grid-template-columns: minmax(0, 1fr); gap: 24px; align-items: center; }
.hero-ph { margin: 0; }
.hero-ph img {
  width: 100%; height: auto; aspect-ratio: 3 / 2; object-fit: cover;
  border-radius: var(--radius-ph); border: 1px solid var(--slab-2);
  background: var(--slab-2); display: block;
}
@media (min-width: 900px) {
  /* photo column capped at 430px — the widest the ≤1000px assets stay honest at */
  .hg-ph { grid-template-columns: minmax(0, 1fr) minmax(300px, 430px); }
}
@media (max-width: 899px) {
  .hero-ph { width: 100%; max-width: 460px; margin-inline: auto; }
}
@media (max-width: 640px) { .hero-slab > .wrap { padding-block: 30px 26px; } }

/* ---------- CSS-only tabs (no JS: the page must work without it) ---------- */
.tabs { border: 1px solid var(--line); border-radius: var(--radius); background: var(--paper-2); }
.tabs > input { position: absolute; opacity: 0; pointer-events: none; }
.tabs-bar { display: flex; flex-wrap: wrap; gap: 2px; padding: 6px; background: var(--paper-3); border-bottom: 1px solid var(--line); }
.tabs-bar label {
  cursor: pointer; padding: 9px 15px; border-radius: var(--radius);
  font-weight: var(--w-bold); font-size: .92rem; color: var(--ink-3);
  min-height: var(--tap); display: inline-flex; align-items: center;
}
.tabs-bar label:hover { color: var(--ink); background: var(--paper); }
.tabs > .panel { display: none; }
/* :nth-of-type counts ELEMENT TYPE, not class. .tabs-bar is itself the first
   div child, so .panel:nth-of-type(1) selected the bar and every table stayed
   hidden. Panels are div #2, #3, #4. */
.tabs > input:nth-of-type(1):checked ~ .panel:nth-of-type(2),
.tabs > input:nth-of-type(2):checked ~ .panel:nth-of-type(3),
.tabs > input:nth-of-type(3):checked ~ .panel:nth-of-type(4) { display: block; }
.tabs > input:nth-of-type(1):checked ~ .tabs-bar label:nth-of-type(1),
.tabs > input:nth-of-type(2):checked ~ .tabs-bar label:nth-of-type(2),
.tabs > input:nth-of-type(3):checked ~ .tabs-bar label:nth-of-type(3) {
  background: var(--paper); color: var(--ink); box-shadow: inset 0 -2px 0 var(--signal);
}
.tabs .tablewrap { border: 0; border-radius: 0; background: transparent; }
.tabs > input:focus-visible ~ .tabs-bar label { outline: 2px solid var(--signal); outline-offset: 2px; }

/* ---------- long prose: show the opening, fold the rest ---------- */
details.more { margin-top: 2px; }
details.more > summary {
  cursor: pointer; list-style: none; display: inline-flex; align-items: center; gap: .5em;
  font-weight: var(--w-bold); color: var(--signal-ink); padding: 10px 0; min-height: var(--tap);
}
details.more > summary::-webkit-details-marker { display: none; }
details.more > summary::after { content: '⌄'; font-size: 1.1em; line-height: 1; }
details.more[open] > summary::after { content: '⌃'; }
details.more[open] > summary { margin-bottom: 4px; }

/* ---------- compact developer card ---------- */
.dev-card {
  display: grid; gap: 14px; grid-template-columns: minmax(0,1fr);
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--paper-2); padding: 18px 20px;
}
.dev-card .who { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px 14px; }
.dev-card .who b { font-size: 1.16rem; }
.dev-card .who .pill { flex: none; }

/* ---------- section labelling: signal vs background reading ---------- */
.sec-kicker {
  display: inline-block; font-size: .74rem; font-weight: var(--w-bold); letter-spacing: .04em;
  color: var(--ink-mute); text-transform: uppercase; margin-bottom: 6px;
}
section.data-block { background: var(--paper-2); border-block: 1px solid var(--line); }
section.data-block + section.data-block { border-top: 0; }


/* ============================================================
   Centred reading column — owner request. Applies to headings and
   prose. Tables keep start alignment on purpose: centred numeric
   columns cannot be compared down a column, which is the entire
   point of the price tables.
   ============================================================ */
h1, h2, h3, h4 { text-align: center; }
.hero .lede, .hero p { margin-inline: auto; text-align: center; }
section > .wrap > p,
.answer p,
.sec-head p,
.dev-card > div > p,
details.more > p,
details.faq .a p { text-align: center; margin-inline: auto; }
.answer { text-align: center; }
.sec-kicker { display: block; text-align: center; }
.src { justify-content: center; text-align: center; }
details.faq summary { justify-content: center; text-align: center; }
details.more > summary { justify-content: center; width: 100%; }
.tabs-bar { justify-content: center; }
.contact-grid .cc { text-align: center; }
.card .body { text-align: center; align-items: center; }
.crumbs { text-align: center; }
.foot-grid { text-align: center; }
.foot-grid ul { justify-items: center; }
.foot-bottom { justify-content: center; text-align: center; }
.index-band .wrap { justify-content: safe center; }
/* Data stays scannable. */
table.data th, table.data td { text-align: start; }
table.data td.num, table.data th:not(:first-child) { text-align: start; }


/* ============================================================
   Header nav fits at every width. Adding the compare + answers
   links pushed the row 3px past the viewport at 1280px, which is
   a real horizontal scroll on a very common laptop size.
   Tiered hiding: the call button and the three asset pillars are
   never dropped; secondary sections go first.
   ============================================================ */
.nav { min-width: 0; flex-wrap: nowrap; }
.nav a:not(.call) { padding: 9px 10px; font-size: .9rem; }
@media (max-width: 1400px) {
  .nav a.hide-lg { display: none; }
}
@media (max-width: 1150px) {
  .nav a.hide-md { display: none; }
  .nav a:not(.call) { padding: 9px 8px; font-size: .86rem; }
}
.head-in { min-width: 0; }
.brand { flex: none; }


/* ============================================================
   Contact block centring. auto-fit produced four 284px columns and
   then placed only two cards in them, so the pair sat pinned to one
   edge with two empty columns beside it. Flex centres whatever
   number of cards actually exist.
   ============================================================ */
.contact-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  justify-content: center;
  align-items: stretch;
}
.contact-grid > .cc {
  flex: 0 1 320px;
  max-width: 100%;
}
.contact-grid > .cc-form {
  flex: 1 1 100%;
  max-width: 720px;      /* a form wider than this is uncomfortable to scan */
  margin-inline: auto;
}
.cc .val { text-align: center; }
.cc .tag, .cc .best { text-align: center; }
.cc-form form { text-align: start; }   /* inputs read better start-aligned */
.cc-form form button { justify-content: center; }
.cc-form form small { display: block; text-align: center; }
@media (max-width: 640px) {
  .contact-grid > .cc { flex: 1 1 100%; }
}


/* ============================================================
   Search — header trigger, command overlay, /search/ page.

   A palette overlay is the expected affordance for site-wide
   search (⌘K / "/"), so it earns the modal. It is built as an
   instrument, not a SaaS palette: dense rows, the price aligned
   to the far edge like a ledger column, one signal colour.
   ============================================================ */
.srch {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 8px 11px !important; border: 1px solid var(--line);
  border-radius: var(--radius); color: var(--ink-3);
  background: var(--paper); cursor: pointer;
}
.srch:hover { border-color: var(--signal); color: var(--signal); background: var(--paper); }
.srch svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; }
.srch-t { font-size: .9rem; }
@media (max-width: 1150px) { .srch-t { display: none; } .srch { padding: 8px 9px !important; } }

.sr-overlay {
  position: fixed; inset: 0; z-index: 200;
  background: color-mix(in oklch, var(--ink) 46%, transparent);
  display: flex; justify-content: center;
  padding: clamp(12px, 8vh, 88px) 16px 16px;
  overflow-y: auto;
}
.sr-overlay[hidden] { display: none; }
.sr-panel {
  width: min(640px, 100%); align-self: flex-start;
  background: var(--paper); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: 0 24px 60px -12px color-mix(in oklch, var(--ink) 40%, transparent);
  overflow: hidden;
  animation: sr-in .22s cubic-bezier(.22,1,.36,1);
}
@keyframes sr-in { from { opacity: 0; transform: translateY(-8px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .sr-panel { animation: none; } }

.sr-form { display: flex; align-items: center; gap: 10px; padding: 12px 14px; border-bottom: 1px solid var(--line); }
.sr-ico { width: 18px; height: 18px; flex: none; fill: none; stroke: var(--ink-mute); stroke-width: 2; stroke-linecap: round; }
.sr-input {
  flex: 1; min-width: 0; border: 0; background: none; outline: none;
  font: inherit; font-size: 1.05rem; color: var(--ink); padding: 4px 0;
}
.sr-input::placeholder { color: var(--ink-mute); }
.sr-input::-webkit-search-cancel-button { display: none; }
.sr-x {
  flex: none; border: 1px solid var(--line); background: var(--paper-2);
  border-radius: var(--radius); color: var(--ink-mute); cursor: pointer;
  font: inherit; font-size: .72rem; letter-spacing: .04em; padding: 4px 7px;
}
.sr-x:hover { color: var(--ink); border-color: var(--ink-mute); }

.sr-results { max-height: min(62vh, 560px); overflow-y: auto; }
.sr-g + .sr-g { border-top: 1px solid var(--line-soft); }
.sr-h {
  padding: 10px 15px 5px; font-size: .7rem; font-weight: var(--w-bold);
  letter-spacing: .08em; text-transform: uppercase; color: var(--ink-mute);
}
.sr-item {
  display: flex; align-items: baseline; gap: 10px;
  padding: 9px 15px; text-decoration: none; color: var(--ink);
  border: 0; outline: 0;
}
.sr-item:hover, .sr-item:focus { background: var(--paper-2); }
.sr-item:hover .sr-t, .sr-item:focus .sr-t { color: var(--signal); }
.sr-t { font-weight: var(--w-bold); font-size: .97rem; }
.sr-m { font-size: .82rem; color: var(--ink-mute); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sr-r { margin-inline-start: auto; flex: none; font-size: .82rem; color: var(--ink-3); font-variant-numeric: tabular-nums; }
.sr-none { padding: 26px 18px; text-align: center; }
.sr-none b { display: block; margin-bottom: 5px; }
.sr-none span { font-size: .88rem; color: var(--ink-mute); }
.sr-all {
  display: block; padding: 11px; text-align: center; font-size: .88rem;
  color: var(--signal); text-decoration: none; border-top: 1px solid var(--line-soft);
  background: var(--paper-2);
}
.sr-all:hover { background: var(--signal-bg); }

/* /search/ page form */
.sp-form {
  display: flex; align-items: center; gap: 8px;
  max-width: 560px; margin: 18px auto 0;
  background: var(--paper); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 6px 6px 6px 12px;
}
.sp-form:focus-within { border-color: var(--signal); }
.sp-ico { width: 17px; height: 17px; flex: none; fill: none; stroke: var(--ink-mute); stroke-width: 2; stroke-linecap: round; margin-inline-start: 6px; }
.sp-input { flex: 1; min-width: 0; border: 0; background: none; outline: none; font: inherit; font-size: 1rem; color: var(--ink); padding: 8px 4px; }
.sp-go {
  flex: none; border: 0; border-radius: var(--radius); cursor: pointer;
  background: var(--signal); color: #fff; font: inherit; font-weight: var(--w-bold);
  padding: 9px 18px;
}
.sp-go:hover { background: var(--signal-ink); }

/* Chip rows — used by /search/ and any hub that needs a flat link set. */
.chips { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; margin: 14px 0 0; }
.chip {
  display: inline-block; padding: 7px 13px; font-size: .9rem;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--paper); color: var(--ink-2); text-decoration: none;
}
.chip:hover { border-color: var(--signal); color: var(--signal); }
.note { text-align: center; color: var(--ink-mute); font-size: .88rem; margin-top: 14px; }


/* ============================================================
   Mobile navigation + action dock.

   Before this, every nav link was display:none below 640px — a
   phone visitor could reach nothing but the call button. Both
   are CSS-only (checkbox toggle) because the whole site is built
   to work with JavaScript off.
   ============================================================ */
.burger {
  display: none; cursor: pointer;
  width: 42px; height: 42px; flex: none;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--paper);
  flex-direction: column; justify-content: center; align-items: center; gap: 4px;
}
.burger span {
  display: block; width: 17px; height: 2px; border-radius: 2px;
  background: var(--ink-2);
  transition: transform .26s cubic-bezier(.22,1,.36,1), opacity .18s linear;
}
/* The three rules become an X: middle fades, outer two rotate onto each other. */
.mnav-t:checked ~ .site-head .burger span:nth-child(1),
body:has(.mnav-t:checked) .burger span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
body:has(.mnav-t:checked) .burger span:nth-child(2) { opacity: 0; }
body:has(.mnav-t:checked) .burger span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

.mnav {
  position: fixed; inset-block: 0; inset-inline-end: 0;
  width: min(86vw, 380px); z-index: 120;
  background: var(--paper);
  border-inline-start: 1px solid var(--line);
  transform: translateX(var(--mnav-out, 105%));
  transition: transform .3s cubic-bezier(.22,1,.36,1), visibility .3s;
  display: none; flex-direction: column;
  overscroll-behavior: contain;
  visibility: hidden;          /* off-screen panels still take taps otherwise */
}
[dir="rtl"] .mnav { --mnav-out: -105%; }
.mnav-t:checked ~ .mnav { transform: none; visibility: visible; }
.mnav-veil {
  position: fixed; inset: 0; z-index: 110; display: none;
  background: color-mix(in oklch, var(--ink) 44%, transparent);
  opacity: 0; transition: opacity .26s ease-out;
  /* A transparent full-screen <label> still receives every tap. Left
     clickable it swallowed table swipes and toggled the menu on any tap
     anywhere. It must be inert whenever the menu is closed. */
  visibility: hidden; pointer-events: none;
}
.mnav-t:checked ~ .mnav-veil { opacity: 1; visibility: visible; pointer-events: auto; }

.mnav-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 14px 18px; border-bottom: 1px solid var(--line); flex: none;
}
.mnav-x {
  width: 40px; height: 40px; display: grid; place-items: center; cursor: pointer;
  border: 1px solid var(--line); border-radius: var(--radius); background: var(--paper-2);
}
.mnav-x svg { width: 17px; height: 17px; fill: none; stroke: var(--ink-2); stroke-width: 2; stroke-linecap: round; }
.mnav-body { flex: 1; overflow-y: auto; padding: 8px 18px 28px; -webkit-overflow-scrolling: touch; }
.mnav-body > a {
  display: block; padding: 13px 2px; font-size: 1.06rem; font-weight: var(--w-bold);
  color: var(--ink); text-decoration: none; border-bottom: 1px solid var(--line-soft);
}
.mnav-body > a:active { color: var(--signal); }
.mnav-k {
  display: block;
  margin: 22px 0 4px; font-size: .7rem; font-weight: var(--w-bold); letter-spacing: .09em;
  text-transform: uppercase; color: var(--signal); text-align: start;
}
.mnav-search {
  display: flex !important; align-items: center; gap: 9px;
  margin-top: 14px; padding: 12px 14px !important;
  border: 1px solid var(--line) !important; border-radius: var(--radius);
  background: var(--paper-2); font-weight: 400 !important; font-size: .95rem !important;
  color: var(--ink-3) !important;
}
.mnav-search svg { width: 16px; height: 16px; flex: none; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; }
.mnav-chips { display: flex; flex-wrap: wrap; gap: 7px; padding: 6px 0 2px; }
.mnav-chips a {
  padding: 8px 13px; font-size: .92rem; text-decoration: none;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--paper); color: var(--ink-2);
}

/* Action dock. The dimensionality is a real offset base plus a top inner
   highlight, and the button physically travels on press — not a gradient
   pretending to be depth. */
.dock {
  display: none;
  position: fixed; inset-inline: 0; bottom: 0; z-index: 100;
  gap: 10px; padding: 10px 14px calc(10px + env(safe-area-inset-bottom));
  background: linear-gradient(to top, var(--paper) 62%, transparent);
}
.dock a {
  flex: 1; min-height: 52px;
  display: flex; align-items: center; justify-content: center; gap: 8px;
  font-weight: var(--w-bold); font-size: 1rem; letter-spacing: .01em;
  text-decoration: none; color: #fff;
  border-radius: 12px;
  transform: translateY(-3px);
  transition: transform .12s cubic-bezier(.22,1,.36,1), box-shadow .12s ease-out;
}
.dock a svg { width: 19px; height: 19px; flex: none; }
.dock .d-call {
  background: var(--signal);
  box-shadow: 0 3px 0 var(--signal-ink),
              0 10px 22px -8px color-mix(in oklch, var(--signal) 62%, transparent),
              inset 0 1px 0 color-mix(in oklch, #fff 34%, transparent);
}
.dock .d-wa {
  background: #1faa54;                      /* WhatsApp's own mark colour */
  box-shadow: 0 3px 0 #16793c,
              0 10px 22px -8px rgba(31,170,84,.55),
              inset 0 1px 0 rgba(255,255,255,.32);
}
.dock a:active { transform: translateY(0); }
.dock .d-call:active { box-shadow: 0 0 0 var(--signal-ink), inset 0 1px 0 color-mix(in oklch, #fff 22%, transparent); }
.dock .d-wa:active   { box-shadow: 0 0 0 #16793c, inset 0 1px 0 rgba(255,255,255,.2); }
@media (prefers-reduced-motion: reduce) { .dock a { transition: none; } }

@media (max-width: 900px) {
  .burger { display: flex; }
  .mnav, .mnav-veil { display: flex; }
  body:has(.mnav-t:checked) { overflow: hidden; }
}
@media (max-width: 640px) {
  .dock { display: flex; }
  /* The dock floats over the page, so the footer needs room to clear it. */
  .site-foot { padding-bottom: calc(86px + env(safe-area-inset-bottom)); }
  .nav a.call .tel { display: none; }
  .nav a.call { padding-inline: 14px; }
}
/* Cards under an area subheading use h4 so the h2>h3>h4 order stays legal. */
.card h4 { margin: 0; font-size: 1.02rem; line-height: 1.45; }
.card h4 a { color: var(--ink); text-decoration: none; }
.card h4 a:hover { color: var(--signal); }


/* Wide tables on phones. The swipe works now, but an invisible scroll area
   reads as a broken table, so the edges show a shadow that disappears when
   you reach the end (background-attachment: local vs scroll), and the metrics
   tighten so most tables stop overflowing at all. */
.tablewrap {
  background-image:
    linear-gradient(to left,  var(--card), rgba(0,0,0,0)),
    linear-gradient(to right, var(--card), rgba(0,0,0,0)),
    linear-gradient(to left,  color-mix(in oklch, var(--ink) 18%, transparent), rgba(0,0,0,0)),
    linear-gradient(to right, color-mix(in oklch, var(--ink) 18%, transparent), rgba(0,0,0,0));
  background-color: var(--card);
  background-position: left, right, left, right;
  background-repeat: no-repeat;
  background-size: 26px 100%, 26px 100%, 12px 100%, 12px 100%;
  background-attachment: local, local, scroll, scroll;
}
@media (max-width: 640px) {
  table.data { min-width: 480px; font-size: .84rem; }
  table.data th, table.data td { padding: 9px 8px; }
  table.data thead th { font-size: .7rem; position: static; }  /* sticky adds nothing without a max-height */
  .tablewrap { position: relative; }
  .tablewrap + .tw-hint { display: block; }
}
.tw-hint {
  display: none; margin-top: 7px; font-size: .78rem;
  color: var(--ink-mute); text-align: center;
}
/* Master plan: a drawing is only useful at a size you can read, so it spans
   the column and opens full-size on tap. */
.mp-link { display: block; border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; background: var(--paper-2); }
.mp-img { display: block; width: 100%; height: auto; }

/* Gallery photograph. The photo is the only object — wrapping it in a
   bordered card drew a second rectangle inside the one the photograph
   already is, the exact box the project card removed. */
.gph {
  margin: 0; border-radius: var(--radius-ph); overflow: hidden;
  background: var(--paper-3); aspect-ratio: 4 / 3;
}
.gph img { width: 100%; height: 100%; object-fit: cover; }

/* The currency word never competes with the figure it follows. */
.cur { font-size: .8em; font-weight: var(--w-body); color: var(--ink-mute); }

/* Numeric ranges hold min–max left-to-right in both scripts. Without the
   isolation the bidi algorithm renders a logical «60,000–400,000» visually
   as «400,000–60,000» inside RTL cells — technically readable right-to-left,
   ambiguous to anyone scanning a column of figures. */
.rng { direction: ltr; unicode-bidi: isolate; }


/* ============================================================
   Unit cards. These pages listed projects; the units behind them
   are the concrete thing a buyer acts on, and on a phone a card
   beats a row because price and size need to be readable without
   a horizontal swipe.
   ============================================================ */
.ucards {
  display: grid; gap: 12px; margin-top: 20px;
  grid-template-columns: repeat(auto-fill, minmax(258px, 1fr));
}
.ucard {
  display: flex; flex-direction: column; gap: 7px;
  padding: 15px 16px 14px;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--card);
  text-align: start;
}
.u-top { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.u-type {
  font-weight: var(--w-bold); font-size: .82rem; letter-spacing: .02em;
  color: var(--signal-ink);
}
.u-floor { font-size: .74rem; color: var(--ink-mute); }
.u-figures {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
  padding-bottom: 7px; border-bottom: 1px solid var(--line-soft);
}
.u-area  { font-size: 1.18rem; font-weight: var(--w-bold); color: var(--ink); }
.u-price { font-size: 1.02rem; font-weight: var(--w-bold); color: var(--ink); }
.u-ppm   { font-size: .8rem; color: var(--ink-3); }
.u-proj  { font-weight: var(--w-bold); font-size: .95rem; color: var(--ink); text-decoration: none; }
.u-proj:hover { color: var(--signal); }
.u-meta  { font-size: .78rem; color: var(--ink-mute); }
.u-acts  { display: flex; gap: 8px; margin-top: auto; padding-top: 10px; }
.u-acts a {
  flex: 1; min-height: 42px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--radius); font-weight: var(--w-bold); font-size: .92rem;
  text-decoration: none;
}
/* Outlined, not filled: the fixed dock is the page's one primary action, and
   three solid buttons stacked above it turned the bottom of the screen into a
   wall of colour with no hierarchy. These stay obviously tappable and carry
   the unit's own context into the message. */
.u-acts a { border: 1.5px solid currentColor; background: transparent; }
.u-acts a.u-call { color: var(--signal-ink); }
.u-acts a.u-wa   { color: #157a3d; }
.u-call:active { background: var(--signal-bg); }
.u-wa:active   { background: color-mix(in oklch, #1faa54 12%, transparent); }
@media (max-width: 640px) {
  .ucards { grid-template-columns: 1fr; }
  .u-area { font-size: 1.3rem; }
}
/* A derived figure we cannot stand behind is stated as withheld, not guessed. */
.u-ppm-off { color: var(--ink-mute); font-size: .74rem; line-height: 1.4; }

/* ============================================================
   PROJECT CARD — borderless
   The old card drew a 1px box around a photograph, which is a
   second rectangle inside the one the photograph already is.
   Nine of them in a grid read as a form. Here the photo is the
   only object; the gap does the separating the border used to.

   The photo gets a soft corner while every piece of data
   furniture on the site keeps --radius: 3px. That split is the
   point: photographs are the soft thing on the page, prices and
   tables are the sharp thing, and the identity stays an index
   rather than a brochure.
   ============================================================ */
:root { --radius-ph: 12px; }

.grid:has(.pc) { gap: clamp(26px, 3vw, 40px) clamp(18px, 2vw, 26px); }

.pc { display: flex; flex-direction: column; }
.pc-ph {
  display: block; border-radius: var(--radius-ph); overflow: hidden;
  background: var(--paper-3); aspect-ratio: 4 / 3;
}
.pc-ph img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  transition: transform .5s cubic-bezier(.22, 1, .36, 1);
}
.pc:hover .pc-ph img { transform: scale(1.04); }

.pc-b {
  padding-block-start: 13px; display: flex; flex-direction: column;
  gap: 4px; text-align: center; align-items: center; flex: 1;
}
.pc-t { margin: 0; font-size: 1.04rem; line-height: 1.45; font-weight: var(--w-semi); }
.pc-t a { color: var(--ink); text-decoration: none; }
.pc-t a:hover { color: var(--signal); }

/* the developer, finally its own thing rather than a fragment of the title */
.pc-d { font-size: .86rem; line-height: 1.5; }
.pc-d a { color: var(--signal-ink); text-decoration: none; }
.pc-d a:hover { color: var(--signal); text-decoration: underline; text-underline-offset: 3px; }
.pc-d span { color: var(--ink-3); }

.pc-l { font-size: .82rem; color: var(--ink-mute); line-height: 1.5; }

.pc-p {
  margin-block-start: 9px; padding-block-start: 10px;
  border-block-start: 1px solid var(--line-soft);
  align-self: stretch; text-align: center;
  font-weight: var(--w-bold); font-size: 1.12rem;
  color: var(--ink); letter-spacing: -0.01em;
}
.pc-p small { font-weight: var(--w-body); font-size: .76rem; color: var(--ink-mute); }

/* the unit mix replaces one aggregate count: "11 units" tells a buyer
   nothing, "5 shops / 4 offices / 2 clinics" tells them whether the
   project holds the thing they came for */
.pc-mix {
  list-style: none; margin: 8px 0 0; padding: 0;
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: 4px 12px; font-size: .78rem; color: var(--ink-mute);
}
.pc-mix li { display: inline-flex; align-items: baseline; gap: 4px; }
.pc-mix li + li::before {
  content: ""; width: 3px; height: 3px; border-radius: 50%;
  background: var(--line); margin-inline-end: 9px; align-self: center;
}
.pc-mix b { font-weight: var(--w-bold); color: var(--ink-2); }

@media (max-width: 640px) {
  .pc-t { font-size: 1rem; }
  .pc-p { font-size: 1.05rem; }
}

/* area chips on a developer page: a short list of places, not a nav */
.chips { list-style: none; margin: 14px 0 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; }
.chips a {
  display: inline-block; padding: 6px 13px; border-radius: 100px;
  border: 1px solid var(--line); background: var(--card);
  color: var(--ink-2); text-decoration: none; font-size: .88rem;
}
.chips a:hover { border-color: var(--signal); color: var(--signal-ink); }

/* developer byline under a project title. Sits on the dark hero, so the
   label is dimmed and the name carries the weight. */
.byline {
  margin: -2px 0 16px; font-size: .95rem; display: flex;
  flex-wrap: wrap; gap: 8px; align-items: baseline; justify-content: center;
}
.byline span {
  font-size: .74rem; letter-spacing: .06em; text-transform: uppercase;
  color: var(--slab-mute); font-weight: var(--w-med);
}
.byline a, .byline b {
  font-weight: var(--w-bold); font-size: 1.02rem; color: var(--slab-text);
  text-decoration: none;
}
.byline a { border-bottom: 1px solid oklch(0.930 0.014 250 / .45); padding-bottom: 1px; }
.byline a:hover { color: #fff; border-bottom-color: currentColor; }
/* off the slab (a project with no photograph renders on paper) */
.hero:not(.hero-slab) .byline span { color: var(--ink-mute); }
.hero:not(.hero-slab) .byline a,
.hero:not(.hero-slab) .byline b { color: var(--ink); }
.hero:not(.hero-slab) .byline a { border-bottom-color: var(--line); }
