/* ---------------------------------------------------------------------------
   Components

   The public site has no component library. It is a purchased Bootstrap 4
   theme wired into React by hand, so nothing owned its own states: a button's
   appearance was assembled from theme classes at each call site, and whichever
   stylesheet loaded last won. That is how `.thumb button { width: 12px
   !important }`, filed under a "Pricing list" comment, silently shrank the two
   primary actions on every card.

   These are the pieces this project owns. Every value comes from
   global/_variables.css and nothing here invents one. Each component declares
   its full set of states, because a state that was never designed is a state
   the theme gets to decide.

   Loaded last, so it wins the cascade against the theme by source order rather
   than by !important.
--------------------------------------------------------------------------- */

/* ── Button ────────────────────────────────────────────────────────────────
   Three intents. Primary is the brand fill carrying dark ink at 7.9:1, never
   white, which measured 2.27:1 and was the least readable thing on the page.
   Every state differs by more than hue, so it survives a greyscale print and a
   colourblind reader.
   ------------------------------------------------------------------------ */
.ui-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  min-height: var(--tap);
  padding: 0 var(--sp-5);
  border: 1px solid transparent;
  /* One radius for every control on the site. It was 4px here and 4px on the
     fields, inside cards drawn at 20px, so a form read as a 2014 admin panel
     dropped into a 2026 page. The field sets the number (12px, see below) and
     the button that submits it has to be the same object. */
  border-radius: var(--r-lg);
  font-family: var(--body-font);
  font-size: var(--fs-200);
  font-weight: var(--fw-semibold);
  line-height: 1;
  text-align: center;
  cursor: pointer;
  white-space: nowrap;
  transition:
    background var(--dur) var(--ease-out),
    border-color var(--dur) var(--ease-out),
    color var(--dur) var(--ease-out),
    box-shadow var(--dur) var(--ease-out);
}
.ui-btn-lg { min-height: 52px; padding: 0 var(--sp-6); font-size: var(--fs-300); }
.ui-btn-block { width: 100%; }

.ui-btn-primary {
  background: var(--brand-fill);
  border-color: var(--brand-fill);
  color: var(--brand-on-fill);
}
.ui-btn-primary:hover {
  background: #A6C74B;
  border-color: #A6C74B;
  color: var(--brand-on-fill);
}
.ui-btn-primary:active { background: #86A82D; border-color: #86A82D; }

.ui-btn-secondary {
  background: #fff;
  border-color: var(--field-line);
  color: var(--heading-color);
}
.ui-btn-secondary:hover { border-color: var(--brand-ink); background: var(--brand-tint); }
.ui-btn-secondary:active { background: #E4EDCE; }

.ui-btn-quiet {
  background: transparent;
  border-color: transparent;
  color: var(--brand-ink);
  padding: 0 var(--sp-2);
}
.ui-btn-quiet:hover { background: var(--brand-tint); }

/* Disabled is a skin, not an opacity. Bootstrap's .65 resolved the label on a
   sending button to 1.67:1, the worst contrast on the site, on the one word a
   person is watching. */
.ui-btn[disabled],
.ui-btn[aria-disabled="true"] {
  background: #E3E5DE;
  border-color: #E3E5DE;
  color: #5A5F55;
  cursor: not-allowed;
  box-shadow: none;
}

/* Loading is NOT disabled. The disabled attribute strips the button from the
   tab order at the exact moment a keyboard user needs to know what happened,
   so the button stays focusable, keeps its accessible name, and holds its
   width so the box does not jump mid-press. */
.ui-btn-loading { position: relative; }
.ui-btn-spinner {
  width: 16px;
  height: 16px;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: var(--r-full);
  animation: ui-spin 700ms linear infinite;
}
@keyframes ui-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .ui-btn-spinner { animation-duration: 2s; }
}

/* ── Field ─────────────────────────────────────────────────────────────────
   A visible label above every control. There were zero <label> elements in
   apps/main before this: every field named itself with a placeholder, which
   is the one piece of text that disappears the moment the field is used.
   ------------------------------------------------------------------------ */
.ui-field { display: block; }
.ui-field + .ui-field { margin-top: var(--sp-3); }

.ui-field-label {
  display: block;
  font-size: var(--fs-200);
  font-weight: var(--fw-semibold);
  color: var(--heading-color);
  /* 8px to its own control, against 24px to the next group. When those two
     gaps are equal, grouping is destroyed and every field reads as a peer of
     every other. */
  margin-bottom: var(--sp-2);
}
.ui-field-optional {
  font-weight: var(--fw-regular);
  color: var(--paragraph-color);
  margin-left: var(--sp-1);
}

.ui-input,
.ui-select,
.ui-textarea {
  width: 100%;
  /* 48, not the 44px tap minimum. A field is not a button: it is the thing a
     person is about to put a sentence into, and four extra pixels is the
     difference between a control that invites typing and one that tolerates
     it. */
  min-height: 48px;
  padding: 0 var(--sp-4);
  font-family: var(--body-font);
  /* 16px, not 14. Safari on iOS zooms the whole page in when a focused field
     is under 16px, and then leaves it zoomed: every form on this site shoved
     the layout sideways on the first tap. */
  font-size: var(--fs-300);
  color: var(--heading-color);
  background: #fff;
  /* 3:1 against white, which is what WCAG 1.4.11 asks of a control boundary.
     The theme's #d4d4d4 measured 1.6:1, so the field had no visible edge. */
  border: 1px solid var(--field-line);
  border-radius: var(--r-lg);
  transition:
    border-color var(--dur) var(--ease-out),
    box-shadow var(--dur) var(--ease-out);
}
.ui-textarea { padding: var(--sp-3) var(--sp-4); min-height: 132px; line-height: var(--lh-300); }
.ui-input:hover,
.ui-select:hover,
.ui-textarea:hover { border-color: var(--brand-ink); }

/* Focus on a field is brand green, not the site's dark halo.
   The global ring in enhancements.css is a 2px ink outline held off the
   element by a white gap, which is right for a button or a card sitting on a
   photograph and wrong here: a field's own border is already its shape, so
   focus should thicken and colour that shape rather than draw a second one
   around it. (0,2,0) beats the global `input:focus-visible` at (0,1,1). */
.ui-input:focus-visible,
.ui-select:focus-visible,
.ui-textarea:focus-visible {
  outline: none;
  border-color: var(--brand-ink);
  /* --brand-ink at 18%. The first cut used --brand-tint, which measures
     1.06:1 on white: a ring nobody can see is not a ring. */
  box-shadow: 0 0 0 3px rgba(87, 118, 28, 0.18);
}
/* Safari does not support :focus-visible on a text input in every version
   still in the field, and a text field is one of the few controls where a
   plain :focus ring is always wanted anyway: nobody focuses a field by
   accident. */
.ui-input:focus,
.ui-select:focus,
.ui-textarea:focus {
  outline: none;
  border-color: var(--brand-ink);
  /* --brand-ink at 18%. The first cut used --brand-tint, which measures
     1.06:1 on white: a ring nobody can see is not a ring. */
  box-shadow: 0 0 0 3px rgba(87, 118, 28, 0.18);
}

.ui-input[aria-invalid="true"],
.ui-select[aria-invalid="true"],
.ui-textarea[aria-invalid="true"] {
  border-width: 2px;
  border-color: var(--state-danger);
}
.ui-input[disabled],
.ui-select[disabled],
.ui-textarea[disabled] {
  background: #F1F2EE;
  border-color: #C6C9C1;
  color: #5A5F55;
  cursor: not-allowed;
}

.ui-field-hint {
  margin: var(--sp-2) 0 0;
  font-size: var(--fs-100);
  color: var(--paragraph-color);
}
/* An error says what happened and what to do about it, and it is announced,
   not just coloured. Colour is never the only signal. */
.ui-field-error {
  margin: var(--sp-2) 0 0;
  font-size: var(--fs-100);
  font-weight: var(--fw-semibold);
  color: var(--state-danger);
  display: flex;
  align-items: baseline;
  gap: var(--sp-2);
}

/* ── Badge ─────────────────────────────────────────────────────────────────
   The listing badges used four unrelated stock Material colours in one row
   next to brand green. These are one hue in three roles, plus slate for a
   listing that is gone: nothing went wrong, so nothing is red.
   ------------------------------------------------------------------------ */
.ui-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  padding: var(--sp-1) var(--sp-2);
  border-radius: var(--r-sm);
  font-size: var(--fs-100);
  font-weight: var(--fw-semibold);
  letter-spacing: 0.04em;
  line-height: 1.4;
  white-space: nowrap;
}
.ui-badge-solid { background: var(--brand-fill); color: var(--brand-on-fill); }
.ui-badge-tint  { background: var(--brand-tint); color: var(--brand-ink); border: 1px solid var(--brand-line); }
.ui-badge-muted { background: #EDEEF0; color: var(--state-muted); border: 1px solid #D8DBE0; }

/* ── Wall label ────────────────────────────────────────────────────────────
   The one move.

   A museum states the facts about an object it did not make pretty: an
   accession number in small tracked caps, a rule, the facts in a fixed order
   on one axis. Three conditions here make that the right register rather than
   a costume. 169 of 200 listings have no name, so four Janabiya villas carry
   four identical machine-composed headlines and the reference is the only
   unique string a listing has. The photography is phone snaps, so anything
   leaning on imagery dies on the first scroll. And the address is withheld on
   purpose, so the page is not a window onto a property, it is a record of one.

   The row set is per kind, sharing one order and one vocabulary. A fixed
   seven-row label would print four en dashes on a plot of land, and a museum
   label does not list the brushwork on a bronze.
   ------------------------------------------------------------------------ */
.wall-label { border-top: 1px solid var(--rule-strong); padding-top: var(--sp-4); }

.wall-label-ref {
  font-size: var(--fs-100);
  font-weight: var(--fw-semibold);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--paragraph-color);
  font-variant-numeric: tabular-nums;
  margin: 0 0 var(--sp-3);
}

.wall-label-facts {
  display: grid;
  grid-template-columns: max-content 1fr;
  column-gap: var(--sp-5);
  row-gap: var(--sp-2);
  margin: 0;
}
.wall-label-facts dt {
  font-size: var(--fs-200);
  font-weight: var(--fw-regular);
  color: var(--paragraph-color);
}
.wall-label-facts dd {
  margin: 0;
  font-size: var(--fs-200);
  font-weight: var(--fw-semibold);
  color: var(--heading-color);
  font-variant-numeric: tabular-nums;
}

/* The price is the last thing, under its own rule, the way a catalogue puts
   the estimate at the foot of the entry. */
.wall-label-price {
  margin-top: var(--sp-4);
  padding-top: var(--sp-4);
  border-top: 1px solid var(--brand-line);
  font-size: var(--fs-700);
  line-height: var(--lh-700);
  font-weight: var(--fw-bold);
  color: var(--brand-ink);
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
}
.wall-label-period {
  font-size: var(--fs-400);
  font-weight: var(--fw-semibold);
  color: var(--paragraph-color);
  letter-spacing: 0;
}

/* ── Governorate chips ─────────────────────────────────────────────────────
   A reachability net, not a filter. Northern holds 50.5% of inventory and
   Southern holds 2.1%, which is not a balanced control, and it does not need
   to be: the job is that no listing is stranded behind a spelling. Southern's
   15 listings are unreachable by any coarse control without it, and a row
   labelled with Bahrain's governorates that omits one is wrong about the
   country.

   Chips never disable and never hide. A control that vanishes under the thumb
   is worse than one that returns nothing.
   ----------------------------------------------------------------------- */

.gov-row {
  display: flex;
  gap: var(--sp-2);
  margin-bottom: var(--sp-4);
  overflow-x: auto;
  scroll-snap-type: x proximity;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.gov-row::-webkit-scrollbar { display: none; }

.gov-chip {
  flex: 0 0 auto;
  scroll-snap-align: start;
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: var(--tap);
  padding: 0 var(--sp-3);
  background: #fff;
  border: 1px solid var(--field-line);
  border-radius: var(--r-sm);
  color: var(--heading-color);
  font-size: var(--fs-200);
  font-weight: var(--fw-semibold);
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--dur) var(--ease-out), border-color var(--dur) var(--ease-out);
}
.gov-chip:hover { border-color: var(--brand-ink); background: var(--brand-tint); }
.gov-chip.is-active {
  background: var(--brand-fill);
  border-color: var(--brand-fill);
  color: var(--brand-on-fill);
}
.gov-count {
  font-weight: var(--fw-regular);
  color: var(--paragraph-color);
  font-variant-numeric: tabular-nums;
}
.gov-chip.is-active .gov-count { color: var(--brand-on-fill); }

@media (min-width: 992px) {
  .gov-chip { min-height: 36px; }
}

/* ── The inner page header ─────────────────────────────────────────────────
   Replaces a full-bleed stock skyline that cost 316px on a 390px phone, on
   nine routes, none of which are about the skyline. On the results page it
   pushed the search field below the fold.

   The breadcrumb leads because it is the smaller fact and it orients you; the
   title follows because it is the larger one. Both sit on the same left axis
   as everything below them, so the page starts on a line rather than on a
   photograph.
   ----------------------------------------------------------------------- */

.page-head {
  padding: calc(var(--nav-h) + var(--sp-6)) 0 var(--sp-5);
  border-bottom: 1px solid var(--rule);
  background: #fff;
}
.page-head-crumb {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--fs-100);
  font-weight: var(--fw-semibold);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--paragraph-color);
  margin-bottom: var(--sp-3);
}
.page-head-crumb a {
  color: var(--brand-ink);
}
.page-head-crumb a:hover {
  text-decoration: underline;
  text-underline-offset: 3px;
}
/* The h1 of /property, /projects, /about, /career, /contact, /saved-properties
   and /compare: seven of the site's pages, so whatever this measures is what
   "an h1" measures to a visitor. It was 28/600 below 992px and 36/600 above,
   while the h1 on a listing was 36/700 and the one on /property was 28/700.
   One role, one rendering, no breakpoint of its own: page-title already
   steps down on a phone because it reads --fs-700. */
.page-head-title {
  margin: 0;
  font-family: var(--t-page-title-font);
  font-size: var(--t-page-title-size);
  line-height: var(--t-page-title-lh);
  font-weight: var(--t-page-title-weight);
  letter-spacing: var(--t-page-title-track);
  color: var(--heading-color);
}

@media (min-width: 992px) {
  .page-head {
    padding: calc(var(--nav-h) + var(--sp-7)) 0 var(--sp-6);
  }
}

/* ── Results tools ─────────────────────────────────────────────────────────
   Filter used to be a full-width brand-fill bar sitting between the count and
   the first property, which made the way to narrow the list the loudest object
   on a page whose subject is the list. Secondary, inline, and beside the sort
   control: the two things you do to a result set belong together.
   ----------------------------------------------------------------------- */

.result-tools {
  display: flex;
  gap: var(--sp-2);
  margin-bottom: var(--sp-4);
}
.result-tools-btn {
  flex: 0 0 auto;
}
.result-tools-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 20px;
  padding: 0 var(--sp-1);
  border-radius: var(--r-full);
  background: var(--brand-fill);
  color: var(--brand-on-fill);
  font-size: var(--fs-100);
  font-variant-numeric: tabular-nums;
}

/* One row on a phone: Filter and Sort side by side under the count, rather
   than two full-width blocks stacked, which cost a third of the screen before
   the first property. */
@media (max-width: 991.98px) {
  .result-sort-col {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    margin-top: var(--sp-3);
  }
  .result-sort {
    flex: 1 1 auto;
    min-width: 0;
  }
}

/* The active filters, on the tokens. These were inline-styled with a 999px
   radius, #d8dcd2, 13px and two literal brand hexes: four values off the
   system in one control, next to a chip row that is on it. */
.filter-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: 32px;
  padding: 0 var(--sp-3);
  background: #fff;
  border: 1px solid var(--field-line);
  border-radius: var(--r-sm);
  color: var(--heading-color);
  font-size: var(--fs-200);
  cursor: pointer;
  transition: border-color var(--dur) var(--ease-out), background var(--dur) var(--ease-out);
}
.filter-chip:hover {
  border-color: var(--brand-ink);
  background: var(--brand-tint);
}

/* ── The card on a phone ───────────────────────────────────────────────────
   The locked row heights exist so that a ROW of cards resolves to one price
   baseline and a column of results can be scanned down the numbers. On a
   single-column phone there is no row to align to, so the lock buys nothing
   and costs what you can see: a one-line title still reserves two lines, which
   reads as a gap between the title and the facts rather than as rhythm.

   Below 768px the rows relax to their content and the card closes up. The
   grammar is unchanged; only the alignment mechanism, which had nothing left
   to align, is dropped.
   ----------------------------------------------------------------------- */

@media (max-width: 767.98px) {
  .pc-title {
    height: auto;
    min-height: 0;
  }
  .pc-ref,
  .pc-facts {
    height: auto;
  }
  .pc-facts {
    /* Still one line, still ellipsised: three facts wrapping to two lines is
       what the fixed height was also preventing. */
    line-height: var(--lh-200);
  }
}

/* Bootstrap sets `small { font-size: 80% }`, which resolves to 12.8px against
   16px body copy: the last value on the site that was not a token, and one no
   one chose. The scale has a step for this. */
small,
.small {
  font-size: var(--fs-100);
}

/* Bootstrap's heading utilities (.h1 through .h6) carry their own rem sizes,
   which is a second type scale living inside the first. Point them at ours so
   a .h4 on an h3 cannot quietly reintroduce a 24px step the scale does not
   have. */
/* The class form of a rank. It used to map to a different set of steps than
   the element form did (.h5 was 18 while h5 was 22), so the same rank had two
   sizes depending on whether you wrote it as a tag or a class. Both read the
   roles now, so they cannot disagree. */
.h1 { font-size: var(--t-page-title-size); line-height: var(--t-page-title-lh); font-weight: var(--t-page-title-weight); letter-spacing: var(--t-page-title-track); }
.h2 { font-size: var(--t-section-title-size); line-height: var(--t-section-title-lh); font-weight: var(--t-section-title-weight); letter-spacing: var(--t-section-title-track); }
.h3 { font-size: var(--t-panel-title-size); line-height: var(--t-panel-title-lh); font-weight: var(--t-panel-title-weight); letter-spacing: var(--t-panel-title-track); }
.h4, .h5, .h6 { font-size: var(--t-card-title-size); line-height: var(--t-card-title-lh); font-weight: var(--t-card-title-weight); letter-spacing: var(--t-card-title-track); }

/* ── The area tiles ────────────────────────────────────────────────────────
   Four cities used to stack as four full-width 345x200 photographs: 1,445px
   on a phone, 51% of it image, to publish four words. Each card named a place
   and said nothing about it, so there was no reason to tap one over another.

   The count now rides on the card. On a phone the four cards are one
   horizontally scrolling row rather than a column, which is the shape the
   governorate chips already use on the results page, so the two ways of
   narrowing by place read as the same idea rather than two designs.

   Rebuilt again, because the card still failed the only test that matters on
   a photograph: nothing about it said it was a link. A place name in heading
   ink and a count in body grey, both below the picture, is a caption. The name
   and the count are on the photograph now, over a scrim, with an arrow that
   travels on hover, and the tile is one link end to end.
   ----------------------------------------------------------------------- */

.nb-area {
  padding: var(--sp-section) 0 0;
}

.nb-row {
  display: flex;
  gap: var(--sp-3);
  margin: 0;
  padding: 0 0 var(--sp-2);
  list-style: none;
  overflow-x: auto;
  scroll-snap-type: x proximity;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.nb-row::-webkit-scrollbar { display: none; }

.nb-cell {
  flex: 0 0 216px;
  scroll-snap-align: start;
}

.nb-tile {
  position: relative;
  display: block;
  overflow: hidden;
  border-radius: var(--r-md);
  background: var(--brand-tint);
  color: #fff;
  text-decoration: none;
  /* 4:3. The source photographs are every ratio the office's phone produced;
     a fixed box keeps four tiles on one baseline and gives the name and the
     count somewhere to sit without covering the building. */
  aspect-ratio: 4 / 3;
  transition: transform var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease-out);
}
.nb-tile:hover,
.nb-tile:focus-visible { color: #fff; text-decoration: none; }

.nb-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform var(--dur-slow) var(--ease-out);
}

/* The scrim is the reason the type is readable over a photograph nobody has
   art-directed. It reaches 0.86 at the bottom edge, which puts white text at
   9:1 even over a white wall in full sun, and it is transparent for the top
   half so the building is still the picture. */
.nb-meta {
  position: absolute;
  inset: auto 0 0 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--sp-8) var(--sp-4) var(--sp-3);
  background: linear-gradient(
    180deg,
    rgba(16, 20, 37, 0) 0%,
    rgba(16, 20, 37, 0.5) 42%,
    rgba(16, 20, 37, 0.86) 100%
  );
}
.nb-name {
  display: block;
  font-size: var(--fs-400);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-400);
  color: #fff;
}
/* The arrow is the same one the Start here row uses, so a way in looks like a
   way in wherever it appears on this page. */
.nb-name::after {
  content: "\2192";
  margin-left: var(--sp-2);
  display: inline-block;
  transition: transform var(--dur) var(--ease-out);
}
.nb-count {
  display: block;
  min-height: calc(var(--fs-200) * var(--lh-200));
  font-size: var(--fs-200);
  line-height: var(--lh-200);
  /* Was --paragraph-color under the picture, which on a photograph is the
     first thing to disappear. White at 88% keeps it secondary to the name
     without making it the weakest thing on the tile. */
  color: rgba(255, 255, 255, 0.88);
  font-variant-numeric: tabular-nums;
}

.nb-tile:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-lift);
}
.nb-tile:hover .nb-img { transform: scale(1.04); }
.nb-tile:hover .nb-name::after { transform: translateX(4px); }

/* Aspect-ratio is unsupported in the Safari versions this theme still gets
   opened in; the padding box does the same job there. */
@supports not (aspect-ratio: 1) {
  .nb-tile { height: 0; padding-bottom: 75%; }
  .nb-img { position: absolute; inset: 0; }
}

/* Past four cards there is nothing left to scroll to, so the rail becomes a
   grid and the cards take the width they are given. */
@media (min-width: 768px) {
  .nb-row {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: var(--sp-5);
    overflow: visible;
    padding-bottom: 0;
  }
  .nb-cell { flex: none; }
}

/* ── Where to start ────────────────────────────────────────────────────────
   The four ways into the inventory, replacing a stats block that quoted the
   same numbers as decoration (753 Total Properties, at y=2028, unlinked) and a
   "Core Values" section that paired each value with an unrelated service's
   icon and button by array index.

   The grammar is the wall label's, so a way in and a listing are recognisably
   the same system: a rule across the top, the kind in tracked caps above it,
   the fact underneath. The number is the fact, and it links to the list it
   counts.
   ----------------------------------------------------------------------- */

.entry-area {
  padding: var(--sp-section) 0;
}

.entry-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--sp-5) var(--sp-4);
}

.entry-item {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  min-height: var(--tap);
  padding-top: var(--sp-3);
  border-top: 1px solid var(--rule-strong);
  color: inherit;
  text-decoration: none;
  transition: border-color var(--dur) var(--ease-out);
}
.entry-item:hover,
.entry-item:focus-visible { color: inherit; text-decoration: none; }
.entry-item:hover { border-top-color: var(--brand-ink); }

.entry-kind {
  font-size: var(--fs-100);
  font-weight: var(--fw-semibold);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--state-muted);
}
.entry-fact {
  font-size: var(--fs-400);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-400);
  color: var(--heading-color);
  font-variant-numeric: tabular-nums;
}
.entry-fact::after {
  content: "\2192";
  margin-left: var(--sp-2);
  color: var(--brand-ink);
  /* The arrow travels on hover; the rest of the card does not move, because
     four cards lifting at once is four times the motion of one doing it. */
  display: inline-block;
  transition: transform var(--dur) var(--ease-out);
}
.entry-item:hover .entry-fact::after { transform: translateX(4px); }
.entry-item:hover .entry-fact { color: var(--brand-ink); }

/* Two columns on a phone leaves each fact about 164px, where "Talk to the
   office" and "40 developments" wrap and the second row sits 25px taller than
   the first. One step down the scale fits both on their line. */
@media (max-width: 575.98px) {
  .entry-fact { font-size: var(--fs-300); line-height: var(--lh-400); }
}

/* The same four columns as the neighbourhood grid above, so each hairline
   starts exactly under a card edge. At --sp-6 the two grids drifted apart by
   0, 2, 4 and 6px across the row. */
@media (min-width: 768px) {
  .entry-row { grid-template-columns: repeat(4, 1fr); gap: var(--sp-5); }
}

@media (prefers-reduced-motion: reduce) {
  .entry-fact::after,
  .nb-tile,
  .nb-img,
  .nb-name::after { transition: none; }
  .entry-item:hover .entry-fact::after,
  .nb-tile:hover,
  .nb-tile:hover .nb-img,
  .nb-tile:hover .nb-name::after { transform: none; }
}

/* ── The card with no photograph ───────────────────────────────────────────
   Light under the grid and row layouts, where the text sits below the image
   on white. Dark under the carousel, which lays white text over the image:
   the old #f0f0f0 put white on 1.09:1, and a listing lands here whenever its
   image URL fails, which the signed storage URLs eventually do.
   ----------------------------------------------------------------------- */

/* The cover a listing has when its image 404s. The storage URLs are signed
   and expire, and a slice of the catalogue currently points at objects that
   are simply not there, so this is not a rare path: it is the normal state of
   any tab left open for an hour. It needs a height, because the thumb is a
   ratio box and a flex child with no height collapses to its glyph and leaves
   a band of tint under it. */
.pc-placeholder {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Transparent: the thumb behind it already carries the watermark. Painting
     the tint again here only hid it. */
  background: transparent;
}
/* The glyph is redundant now that the thumb carries the mark, so the
   placeholder is an empty, correctly sized box that lets it show through. */
.pc-placeholder .fa {
  display: none;
}

/* ── Clipped content ───────────────────────────────────────────────────────
   The description on a listing runs as long as whoever wrote it. One live
   villa's ran 1,144px on a phone, which put the map, the agent and the
   similar listings about three screens below the photograph.

   The fade is the affordance: a hard cut reads as the end of the text, a
   fade reads as more of it.
   ----------------------------------------------------------------------- */

.collapsible-body {
  position: relative;
}
.collapsible-body.is-clamped {
  overflow: hidden;
}
.collapsible-body.is-clamped::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: var(--sp-8);
  background: linear-gradient(to bottom, rgba(255, 255, 255, 0), #fff);
  pointer-events: none;
}
.collapsible-toggle {
  margin-top: var(--sp-3);
}

/* ── Similar listings ──────────────────────────────────────────────────────
   Four cards under the listing you are already reading. Stacked on a phone
   they ran 1,396px, longer than the listing itself, so they became the tail
   that wagged the page. One scrolling row, the same shape as the
   neighbourhood rail and the home page rails.
   ----------------------------------------------------------------------- */

.sim-row {
  display: flex;
  gap: var(--sp-3);
  margin: 0;
  padding: 0 0 var(--sp-2);
  list-style: none;
  overflow-x: auto;
  scroll-snap-type: x proximity;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.sim-row::-webkit-scrollbar { display: none; }

.sim-cell {
  flex: 0 0 280px;
  /* Flex items default to min-width:auto, so the card's own content was
     pushing each cell out to the full 347px container width and the rail had
     nothing left to scroll. */
  min-width: 0;
  scroll-snap-align: start;
}

@media (min-width: 768px) {
  .sim-row {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: var(--sp-5);
    overflow: visible;
    padding-bottom: 0;
  }
  .sim-cell { flex: none; }
}

@media (min-width: 992px) {
  .sim-row { grid-template-columns: repeat(4, 1fr); }
}

/* ── The footer, on a phone ────────────────────────────────────────────────
   1,038px on a 375px screen, on all thirteen routes. 96px of it was the
   padding before the first thing in it, and its links were 29px tall on a
   site whose own standard is a 44px target.

   The RERA block stays. It is not a second company logo: it is the Real
   Estate Regulatory Authority mark that the licence number belongs to, and a
   Bahraini agency publishes both.
   ----------------------------------------------------------------------- */

@media (max-width: 991.98px) {
  /* These selectors carry the same weight as the theme's own
     (.footer-area .footer-top, and a .footer-logoft img marked !important),
     because a one-class rule loses to them however late it is loaded. */
  .footer-area { padding-top: var(--sp-7); }
  .footer-area .footer-top { padding-bottom: var(--sp-6); }
  .footer-area .footer-bottom { padding-top: var(--sp-6); }
  .footer-area .widget { margin-bottom: var(--sp-6); }
  .footer-area .footer-logo { margin-bottom: var(--sp-4); }
  .footer-area .footer-logo img { max-width: 120px; }

  /* Five short labels down a single column is a column of gaps. Two columns
     hold them in three rows, which is also what makes room for a real target
     without the block growing. */
  .footer-area .widget_nav_menu ul {
    columns: 2;
    column-gap: var(--sp-5);
  }

  .footer-area .footer-logoft img { max-width: 180px !important; }
}

/* The site sets a 44px target and then shipped 29px links in the footer. */
.footer-area .widget_nav_menu li a {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
}

@media (min-width: 992px) {
  /* On a pointer the row height is the constraint, not the fingertip. */
  .footer-area .widget_nav_menu li a { min-height: 32px; }
}

/* The listing page's own action bar carries a WhatsApp button that quotes the
   listing's reference. While it is up, the global one is both a duplicate and,
   at z-index 9998 against the bar's 1030, sitting on top of "Call". */
@media (min-width: 992px) {
  /* The bar is hidden above 992px, so the global button comes back. */
  }

/* The footer has two labels of the same rank, "Quick Links" and "FOLLOW US",
   and they were set at 22px bold and 16px caps. Both are section markers in
   the footer, so both take the tracked-caps treatment the rest of the site
   uses for one: the eyebrow on a card, the kind on a way in, the reference on
   a listing. */
.footer-area .widget-title,
.footer-area .footer-social span {
  font-size: var(--fs-200);
  font-weight: var(--fw-semibold);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--state-muted);
}
.footer-area .widget-title {
  margin-bottom: var(--sp-3);
}

/* ── The listing above the fold ────────────────────────────────────────────
   On a 1440x900 desktop the gallery section ran 0 to 883px: the whole first
   screen was one photograph, with the title 86px below the fold and the price
   with it. On the page whose entire job is to answer "what is it and what
   does it cost", both answers were one scroll away.

   The photograph does not need 600px to be the lead. At 460 the section ends
   at about 695, so the headline, the reference and the price card all clear
   the fold on a laptop, and the photograph is still the largest thing on the
   page by a wide margin.

   Mobile already runs a 300px gallery and is untouched.
   ----------------------------------------------------------------------- */

/* ── Clearing the header ───────────────────────────────────────────────────
   The header is fixed, so nothing at the top of a page is below it by
   default: it has to be pushed. Every other route does that with
   `calc(var(--nav-h) + …)`, but the detail pages opened their gallery band
   with the theme's `pd-top-100`, which this system remaps to --sp-section.
   That is 64 against a 112px header on a desktop and 40 against a 56px one on
   a phone, so the largest photograph on the site started 48px, and on a phone
   16px, BEHIND the header. Measured at 390: the hero's top edge sat at y=52
   under a header 56px tall.

   Same value as .listing-head, which is the same object: a band of colour
   directly under the header at the top of a page. It is padding on the band,
   not a margin on the page, so the grey still runs to the top of the document
   and the header has a ground to sit on rather than a white seam.
   ----------------------------------------------------------------------- */
.property-details-area > .bg-gray {
  padding-top: calc(var(--nav-h) + var(--sp-6));
}

/* The three states that replace the page entirely (not found, API down, and
   the news detail's own error) need the same clearance, or the first thing a
   visitor reads on a failure is half hidden behind the navigation. */
.detail-state {
  padding-top: calc(var(--nav-h) + var(--sp-7));
  padding-bottom: var(--sp-9);
}

@media (min-width: 992px) {
  .prop-detail-gallery { height: 460px; }
  .property-details-area > .bg-gray { padding-bottom: var(--sp-7); }
}


/* ===========================================================================
   ONE CARD, ONE RAIL, ONE PAGE
   ---------------------------------------------------------------------------
   Everything below this line was added in the 2026-08-27 restructure. The
   short version of what it replaces:

   The home page drew listings as dark cards with the title and price printed
   over the photograph, and /property drew the same listings as white record
   cards. Two card systems on one site is the site failing to say what it
   thinks a property is, so there is now one card and the rails render it.

   The rails were react-slick with dots and autoplay. They are scroll
   containers with arrows and a link to the full list.

   Nothing here invents a value. Every number comes from global/_variables.css.
   ========================================================================= */

/* ── Section heading ───────────────────────────────────────────────────────
   Every section on the site used to size its own heading, so "Properties For
   Sale", "Neighborhoods" and "Featured Projects" were three different sizes
   with a solid green block behind one of them. One rule, one size.
   ------------------------------------------------------------------------ */
.section-title {
  margin: 0;
  font-family: var(--t-section-title-font);
  font-size: var(--t-section-title-size);
  line-height: var(--t-section-title-lh);
  font-weight: var(--t-section-title-weight);
  letter-spacing: var(--t-section-title-track);
  color: var(--heading-color);
  /* Undoes the green pill the featured projects heading used to sit in: a
     solid fill behind one heading and nothing behind the other five. */
  background: none;
  padding: 0;
  display: block;
}

.section-lede {
  margin: 0 0 var(--sp-6);
  font-size: var(--fs-300);
  line-height: var(--lh-300);
  color: var(--paragraph-color);
  /* 50 to 75 characters. Past that a line is measurably harder to track back. */
  max-width: 62ch;
}

/* ── The rail ──────────────────────────────────────────────────────────────
   A heading, a link to everything, two arrows, and a row you can swipe.
   ------------------------------------------------------------------------ */
.rail-area {
  padding-top: var(--sp-section);
  padding-bottom: var(--sp-section);
}
/* Two rails back to back would otherwise put 160px between two rows of cards,
   which reads as the page having ended. Consecutive rails share one gap. */
.rail-area + .rail-area {
  padding-top: 0;
}

.rail-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  margin-bottom: var(--sp-5);
}

.rail-count {
  margin-left: var(--sp-3);
  font-size: var(--fs-200);
  font-weight: var(--fw-regular);
  color: var(--paragraph-color);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0;
}

.rail-tools {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  flex: 0 0 auto;
}

.rail-viewall {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: var(--tap);
  font-size: var(--fs-200);
  font-weight: var(--fw-semibold);
  color: var(--brand-ink);
  text-decoration: none;
  white-space: nowrap;
}
.rail-viewall:hover,
.rail-viewall:focus-visible {
  color: var(--brand-ink);
  text-decoration: underline;
  text-underline-offset: 3px;
}
/* The chevron moves, the text does not. A label that shifts on hover is a
   label you have to re-read. */
.rail-viewall .ic {
  transition: transform var(--dur) var(--ease-out);
}
.rail-viewall:hover .ic {
  transform: translateX(3px);
}

.rail-arrows {
  display: inline-flex;
  gap: var(--sp-2);
}
.rail-arrow {
  width: var(--tap);
  height: var(--tap);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: #fff;
  border: 1px solid var(--field-line);
  color: var(--heading-color);
  font-size: var(--fs-400);
  cursor: pointer;
  transition:
    background var(--dur) var(--ease-out),
    border-color var(--dur) var(--ease-out),
    color var(--dur) var(--ease-out);
}
/* Two discs with air between them. They used to share an edge, which meant the
   right-hand one had to have its left border switched off to avoid a 2px seam,
   and a suppressed left border is still a left border. Every icon control on
   this site is now the same 44px disc, separated from its neighbour by space
   and never by a line, so there is no seam to suppress. */
.rail-arrow {
  border-radius: var(--r-full);
}
.rail-arrow:hover:not([disabled]) {
  background: var(--brand-tint);
  border-color: var(--brand-ink);
  color: var(--brand-ink);
}
/* Disabled still reads. An arrow at 30% on a white page is a smudge, and a
   person cannot tell a dead control from a broken one. */
.rail-arrow[disabled] {
  background: #F1F2EE;
  border-color: #C6C9C1;
  color: #9AA093;
  cursor: default;
}

.rail-row {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 82%;
  gap: var(--rail-gap);
  list-style: none;
  margin: 0;
  padding: 0 0 var(--sp-2);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.rail-row::-webkit-scrollbar {
  display: none;
}
.rail-cell {
  scroll-snap-align: start;
  min-width: 0;
  display: flex;
}
.rail-cell > * {
  width: 100%;
}
/* The card inside a rail carries the row's gutter, not its own bottom margin. */
.rail-cell .single-feature {
  margin-bottom: 0;
}

/* On a phone the rail bleeds to the screen edge so the next card peeks rather
   than being sliced by the container gutter. -15px matches the Bootstrap
   container padding exactly, so the row is the width of the viewport and not
   one pixel more: anything wider defeats the overflow-x: clip on <html>. */
@media (max-width: 767.98px) {
  .rail-row {
    margin-left: -15px;
    margin-right: -15px;
    padding-left: 15px;
    padding-right: 15px;
  }
  /* Touch has the gesture. Two buttons that duplicate a swipe are two more
     things in the way of the heading. */
  .rail-arrows {
    display: none;
  }
}

@media (min-width: 576px) {
  .rail-row {
    grid-auto-columns: calc((100% - var(--rail-gap)) / 2);
  }
}
@media (min-width: 992px) {
  .rail-row {
    grid-auto-columns: calc((100% - var(--rail-gap) * 2) / 3);
  }
}

@media (prefers-reduced-motion: reduce) {
  .rail-row {
    scroll-behavior: auto;
  }
  .rail-viewall .ic {
    transition: none;
  }
  .rail-viewall:hover .ic {
    transform: none;
  }
}

/* ── Browse ────────────────────────────────────────────────────────────────
   The stat counters and the neighbourhood photographs used to sit at opposite
   ends of the page, both below every listing on it, both being a way into the
   catalogue. One block, three tiers, directly under the hero: broadest first,
   then kind, then place.
   ------------------------------------------------------------------------ */
.browse-area {
  padding-top: var(--sp-section);
  padding-bottom: var(--sp-section);
  background: #FAFBF7;
  border-bottom: 1px solid var(--rule);
}
.browse-area .entry-row {
  margin-top: var(--sp-5);
}
.browse-group {
  margin-top: var(--sp-7);
}
.browse-label {
  margin: 0 0 var(--sp-3);
  font-family: var(--t-eyebrow-font);
  font-size: var(--t-eyebrow-size);
  line-height: var(--t-eyebrow-lh);
  font-weight: var(--t-eyebrow-weight);
  letter-spacing: var(--t-eyebrow-track);
  text-transform: uppercase;
  color: var(--paragraph-color);
}
/* The type chips are links now, not buttons, because they go somewhere. */
a.gov-chip {
  text-decoration: none;
}
/* ── Why the office ────────────────────────────────────────────────────────
   Three cards reading Professionalism, Integrity and Commitment, each with an
   unrelated stock icon and a button reading "Explore Now". Every brokerage in
   Bahrain claims all three. Three checkable facts, set as a list, closing the
   page rather than occupying a feature slot.
   ------------------------------------------------------------------------ */
.why-area {
  padding-top: var(--sp-section);
  padding-bottom: var(--sp-section);
  border-top: 1px solid var(--rule);
}
.why-inner {
  display: grid;
  gap: var(--sp-6);
}
.why-sub {
  margin: var(--sp-3) 0 var(--sp-5);
  font-size: var(--fs-300);
  line-height: var(--lh-300);
  color: var(--paragraph-color);
  max-width: 40ch;
}
.why-list {
  margin: 0;
}
.why-item + .why-item {
  margin-top: var(--sp-5);
  padding-top: var(--sp-5);
  border-top: 1px solid var(--rule);
}
.why-term {
  font-family: var(--heading-font);
  font-size: var(--fs-400);
  line-height: var(--lh-400);
  font-weight: var(--fw-semibold);
  color: var(--heading-color);
}
.why-def {
  margin: var(--sp-2) 0 0;
  font-size: var(--fs-200);
  line-height: var(--lh-300);
  color: var(--paragraph-color);
  max-width: 68ch;
}
.why-contact {
  margin: var(--sp-7) 0 0;
  padding-top: var(--sp-5);
  border-top: 1px solid var(--rule);
  font-size: var(--fs-200);
  color: var(--paragraph-color);
}
.why-contact a {
  color: var(--brand-ink);
  font-weight: var(--fw-semibold);
  text-decoration: underline;
  text-underline-offset: 3px;
}

@media (min-width: 992px) {
  .why-inner {
    grid-template-columns: minmax(0, 340px) 1fr;
    gap: var(--sp-8);
    align-items: start;
  }
}

/* ── The header ────────────────────────────────────────────────────────────
   The <header> and the <nav> inside it both carried .navbar-area, which is
   position: fixed. The header therefore had no in-flow child, resolved to zero
   height, and the scroll handler kept painting the white sticky background
   onto that zero-height element. Dark navigation links floated over
   photographs and over card text on every page of the site. The nav no longer
   carries the class; these rules give the header back the geometry the theme
   was applying to the wrong element.
   ------------------------------------------------------------------------ */
.navbar-area .navbar {
  display: block;
  padding: 0;
  width: 100%;
}
.main-anchor {
  display: block;
  height: 0;
  overflow: hidden;
}

/* Desktop only, and it must SAY so. style.css hides this at 991px, but
   components.css loads last, so an unscoped `display: flex` here beat that
   media query on source order and put the Saved link and the CTA back into a
   390px header beside the hamburger. The page then measured wider than the
   viewport and every section on the home page was cut off down the right
   edge. Media queries do not raise specificity; source order still decides. */
@media (min-width: 768px) {
  .nav-right-part-desktop {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
  }
}

/* Saved was a primary navigation item, so an empty shortlist sat at the same
   weight as Property and Projects, competing with the links that lead to the
   inventory. Icon and count, beside the CTA. */
.nav-saved {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  min-width: var(--tap);
  min-height: var(--tap);
  padding: 0 var(--sp-3);
  border-radius: var(--r-sm);
  color: var(--heading-color);
  font-size: var(--fs-200);
  font-weight: var(--fw-semibold);
  text-decoration: none;
  transition:
    background var(--dur) var(--ease-out),
    color var(--dur) var(--ease-out);
}
.nav-saved:hover,
.nav-saved:focus-visible,
.nav-saved.is-active {
  background: var(--brand-tint);
  color: var(--brand-ink);
  text-decoration: none;
}
.nav-saved i {
  font-size: var(--fs-400);
}
/* The count exists only when there is something to count. A zero badge is a
   notification that nothing happened. */
.nav-saved-count {
  min-width: 20px;
  height: 20px;
  padding: 0 5px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--r-full);
  background: var(--brand-fill);
  color: var(--brand-on-fill);
  font-size: var(--fs-100);
  font-weight: var(--fw-semibold);
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
/* Desktop is an icon and a number. The word is carried by the accessible name
   there, and shown in full in the drawer where there is room for it. */
@media (min-width: 768px) {
  .nav-right-part-desktop .nav-saved-label {
    display: none;
  }
}


/* ── The footer ────────────────────────────────────────────────────────────
   A logo, one Instagram icon, five links and a copyright over 700px of mostly
   empty page. Phase 4.2 names the cost: "a 200,000 BHD decision is being asked
   of an anonymous entity."
   ------------------------------------------------------------------------ */
.footer-area {
  background: #F7F8F4;
  border-top: 1px solid var(--rule);
  padding-top: var(--sp-9);
  padding-bottom: var(--sp-6);
}

.footer-grid {
  display: grid;
  gap: var(--sp-7);
}
@media (min-width: 576px) {
  .footer-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  /* Three columns do not fold into two evenly: Browse and Office are a pair
     and the identity block is not, so it takes the full width above them
     rather than leaving one orphan column on a second row. */
  .footer-col-office {
    grid-column: 1 / -1;
  }
}
@media (min-width: 992px) {
  .footer-grid {
    /* Sized to content and spaced apart, not three equal fractions of 1,110px.
       The newsletter used to hold the fourth seat, and dividing its width
       between three columns left Browse with six short links floating in
       265px: 220px of nothing between "Saved properties" and the next
       heading, and two gutters of visibly different size. Tracks that take
       what they need, with space-between putting the slack into the two
       gutters equally, gives one rhythm across the row and lands the last
       column on the container's right edge. */
    grid-template-columns: minmax(0, 420px) minmax(0, max-content) 240px;
    justify-content: space-between;
    column-gap: var(--sp-8);
    row-gap: var(--sp-7);
  }
  .footer-col-office {
    grid-column: auto;
  }
}

.footer-col {
  min-width: 0;
}
.footer-logo {
  display: inline-block;
  margin-bottom: var(--sp-4);
}
.footer-logo img {
  max-width: 150px;
  height: auto;
}
.footer-blurb {
  margin: 0 0 var(--sp-4);
  font-size: var(--fs-200);
  line-height: var(--lh-300);
  color: var(--paragraph-color);
  max-width: 42ch;
}

/* Browse, Office, Opening hours. These name a group of links, so by the one
   family rule they are the heading font, and they were the single exception
   on the site: the only heading element set in the body font, at the same
   size and weight as the browse labels on the home page, which are Poppins.
   Two families for one label, on a component that renders on all thirteen
   pages, is most of what "the fonts feel inconsistent" was pointing at. */
.footer-title {
  margin: 0 0 var(--sp-4);
  font-family: var(--t-eyebrow-font);
  font-size: var(--t-eyebrow-size);
  line-height: var(--t-eyebrow-lh);
  font-weight: var(--t-eyebrow-weight);
  letter-spacing: var(--t-eyebrow-track);
  text-transform: uppercase;
  color: var(--heading-color);
}
.footer-title-sub {
  margin-top: var(--sp-6);
}

.footer-links,
.footer-contact,
.footer-hours,
.footer-social {
  list-style: none;
  margin: 0;
  padding: 0;
}

.footer-links a {
  display: inline-flex;
  align-items: center;
  min-height: 32px;
  font-size: var(--fs-200);
  color: var(--paragraph-color);
  text-decoration: none;
}
.footer-links a:hover,
.footer-links a:focus-visible {
  color: var(--brand-ink);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.footer-contact li {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  margin-bottom: var(--sp-3);
  font-size: var(--fs-200);
  line-height: var(--lh-300);
  color: var(--paragraph-color);
}
.footer-contact > .ic {
  flex: 0 0 18px;
  margin-top: 2px;
  color: var(--brand-ink);
}
.footer-contact a {
  color: var(--paragraph-color);
  text-decoration: none;
}
.footer-contact a:hover,
.footer-contact a:focus-visible {
  color: var(--brand-ink);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.footer-hours li {
  display: flex;
  justify-content: space-between;
  gap: var(--sp-4);
  padding: var(--sp-2) 0;
  font-size: var(--fs-200);
  color: var(--paragraph-color);
  border-bottom: 1px solid var(--rule);
}
.footer-hours li:last-child {
  border-bottom: 0;
}
.footer-hours-time {
  flex: 0 0 auto;
  color: var(--heading-color);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.footer-social {
  display: flex;
  gap: var(--sp-2);
  margin-top: var(--sp-4);
}
.footer-social a {
  width: var(--tap);
  height: var(--tap);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--rule);
  border-radius: var(--r-sm);
  background: #fff;
  color: var(--heading-color);
  font-size: var(--fs-400);
  transition:
    background var(--dur) var(--ease-out),
    border-color var(--dur) var(--ease-out),
    color var(--dur) var(--ease-out);
}
.footer-social a:hover,
.footer-social a:focus-visible {
  background: var(--brand-tint);
  border-color: var(--brand-ink);
  color: var(--brand-ink);
}

/* ── The licence band ─────────────────────────────────────────────────────
   The RERA mark was a 40px thumbnail in the bottom right corner, beside the
   copyright, with the licence number set in 12px grey next to it. For a
   Bahraini brokerage that badge is the strongest thing on the page: it is the
   difference between a website and a regulated business, and it was the
   smallest object in the footer.

   Its own band, centred, at 260px, with the two lines under it in body size
   and heading ink. The asset is a 2,746px lossless WebP on a transparent
   ground, so it stays crisp at 2x and sits directly on the footer without a
   plate behind it.
   ----------------------------------------------------------------------- */
.footer-licence {
  margin-top: var(--sp-8);
  padding: var(--sp-8) 0;
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  text-align: center;
}
.footer-licence-badge {
  display: block;
  /* Comfortably past the 220px the mark needs to be read at arm's length, and
     the asset is 2,746px wide, so it is still oversampled at 2x. */
  width: 300px;
  max-width: 100%;
  height: auto;
  margin: 0 auto var(--sp-5);
}
.footer-licence-line {
  margin: 0;
  font-size: var(--fs-300);
  line-height: var(--lh-300);
  font-weight: var(--fw-semibold);
  color: var(--heading-color);
}
.footer-licence-no {
  margin: var(--sp-1) 0 0;
  font-size: var(--fs-300);
  line-height: var(--lh-300);
  color: var(--heading-color);
  font-variant-numeric: tabular-nums;
}

/* One line, on the left, and nothing on the right competing with it. */
.footer-legal {
  margin-top: var(--sp-5);
}
.footer-legal-line {
  margin: 0;
  font-size: var(--fs-100);
  color: var(--paragraph-color);
}
@media (max-width: 575.98px) {
  .footer-licence {
    padding: var(--sp-7) 0;
  }
  .footer-licence-badge {
    width: 200px;
  }
}

/* ===========================================================================
   THE DETAIL PAGE
   ---------------------------------------------------------------------------
   One page for property, land and building, where there used to be three files
   that had already drifted apart on where the price sits and whether an
   unassigned listing records a lead at all.
   ========================================================================= */

.prop-header {
  margin-bottom: var(--sp-6);
}
.prop-header-badges {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin-bottom: var(--sp-4);
}
.prop-detail-title {
  margin: 0 0 var(--sp-3);
  font-family: var(--t-page-title-font);
  font-size: var(--t-page-title-size);
  line-height: var(--t-page-title-lh);
  font-weight: var(--t-page-title-weight);
  letter-spacing: var(--t-page-title-track);
  color: var(--heading-color);
}
/* The Arabic name of a project, where the office has entered one. It is the
   same name, not a subtitle, so it takes the title's colour and its own
   direction and drops one step in size and all of the weight. It used to be
   an h3, which put a third heading level on a page with two. */
.prop-detail-title-ar {
  margin: 0 0 var(--sp-3);
  font-size: var(--fs-500);
  line-height: var(--lh-500);
  font-weight: var(--fw-regular);
  color: var(--heading-color);
}
.prop-header-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2) var(--sp-5);
  margin: 0 0 var(--sp-5);
  font-size: var(--fs-200);
  color: var(--paragraph-color);
}
.prop-header-area {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
}
.prop-header-area .ic {
  color: var(--brand-ink);
}
.prop-header-ref {
  letter-spacing: 0.12em;
  text-transform: uppercase;
  font-size: var(--fs-100);
  font-weight: var(--fw-semibold);
  font-variant-numeric: tabular-nums;
}

/* ── The action row ────────────────────────────────────────────────────────
   Was three chips in three different colours: a grey "Copy Link" outline, a
   green "Share" outline, and a bare heart whose background and shadow had been
   stripped off inline so it did not match either of them. Then it was four
   labelled pills, 380px of "Save Compare Copy link Share" under the title,
   reading as a sentence rather than as a set of tools.

   Four discs now, in the order a person uses them: keep it, compare it, take
   the link, send it to someone. Identical shape and 8px apart is what makes
   them read as one group; they are never fused into a segmented bar, because
   the seam between two fused controls is a line down the left edge of the
   second one and this site does not draw those. Nothing here is ambiguous
   enough to need its word: a heart, two arrows, a chain and three linked
   nodes are the four most-copied glyphs in software.
   ------------------------------------------------------------------------ */
.prop-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2);
}

/* ── The aside ─────────────────────────────────────────────────────────────
   Phase 1.2 measured a 2,168px left column against 368px of content on the
   right, so 1,800px of the most important page in the product was blank. It is
   sticky, and it carries the things worth keeping on screen while somebody
   reads a long description: the price, the facts, and the two buttons that
   create a lead.

   position: sticky only works because html and body use overflow-x: clip
   rather than hidden. hidden makes an element a scroll container and a scroll
   container silently disables sticky for everything inside it. See the note in
   enhancements.css before changing either.
   ------------------------------------------------------------------------ */
.prop-aside {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
}
@media (min-width: 992px) {
  .prop-aside {
    position: sticky;
    top: calc(var(--nav-h) + var(--sp-3));
  }
}
/* Stacked, the aside is a column above another column and Bootstrap puts no
   space between them, so "Request a viewing" sat flush against the badges of
   the block below it and the two read as one run-on card. */
@media (max-width: 991.98px) {
  .prop-aside {
    margin-bottom: var(--sp-7);
  }
}

.prop-summary-card {
  border: 1px solid var(--rule);
  border-radius: var(--r-md);
  padding: var(--sp-5);
  background: #fff;
  box-shadow: var(--shadow-rest);
}
/* The one price on the page. It used to print twice, small and plain in this
   card and again at --fs-700 in the middle of the left column, so the same
   number had two sizes and two importances on one screen. */
.prop-summary-price {
  margin: 0;
  font-family: var(--heading-font);
  font-size: var(--fs-700);
  line-height: var(--lh-700);
  font-weight: var(--fw-bold);
  letter-spacing: -0.02em;
  /* Dark, not green, and for the same reason the card's price is dark: green
     is the accent, and an accent that also carries the price is an accent
     doing two jobs. The price is loud by size, which is the one channel that
     survives a greyscale print and a colourblind reader. */
  color: var(--heading-color);
  font-variant-numeric: tabular-nums;
}
.prop-summary-period {
  margin-left: var(--sp-2);
  font-size: var(--fs-300);
  font-weight: var(--fw-regular);
  letter-spacing: 0;
  color: var(--paragraph-color);
}
/* A project has no price: it is a development, not one priced thing. The slot
   is kept because a visitor arriving from a listing looks there first, and a
   card that starts on metadata reads as one with its top line missing. What
   fills it is a sentence, not a number, so it drops to the heading step: a
   two-word statement set at 36px is shouting a thing nobody asked. */
.prop-summary-quiet {
  font-size: var(--fs-500);
  line-height: var(--lh-500);
  letter-spacing: 0;
}
.prop-summary-meta {
  margin: var(--sp-2) 0 0;
  font-size: var(--fs-200);
  color: var(--paragraph-color);
}
.prop-summary-card .wall-label {
  margin-top: var(--sp-5);
}

/* The secondary action in the aside.

   It was a 350px outlined slab directly under a 190px filled button, so the
   aside carried two buttons and left the visitor to work out which one the
   page wanted. A stack reads as a hierarchy when the tiers look different:
   one fill, then text. This is the .rail-viewall shape, which is already what
   a secondary action looks like everywhere else on the site, so nothing new
   has to be learned to recognise it. */
.prop-aside-link {
  display: inline-flex;
  align-self: center;
  align-items: center;
  gap: var(--sp-2);
  min-height: var(--tap);
  color: var(--brand-ink);
  font-size: var(--fs-200);
  font-weight: var(--fw-semibold);
  text-decoration: none;
}
.prop-aside-link:hover,
.prop-aside-link:focus-visible {
  color: var(--brand-ink);
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* ── Body sections ─────────────────────────────────────────────────────────
   Every block was a .property-news-single-card with a yellow bottom border, a
   theme class from a news template. One rule between sections, one heading
   size, one rhythm.
   ------------------------------------------------------------------------ */
.prop-section {
  margin-top: var(--sp-6);
  padding-top: var(--sp-6);
  border-top: 1px solid var(--rule);
}
.prop-section:first-of-type {
  margin-top: 0;
}
/* "Description", "Features", "Location". These open a block of the page body,
   which is the same job "Latest for sale" does on the home page, so it is the
   same role and it renders the same. It used to be 22/600 while the rail
   heading further down the SAME page was 28/700, which put the suggestion of
   other listings a step above the description of this one. */
.prop-section-title {
  margin: 0 0 var(--sp-4);
  font-family: var(--t-section-title-font);
  font-size: var(--t-section-title-size);
  line-height: var(--t-section-title-lh);
  font-weight: var(--t-section-title-weight);
  letter-spacing: var(--t-section-title-track);
  color: var(--heading-color);
}

.prop-checks {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: var(--sp-2) var(--sp-5);
  list-style: none;
  margin: 0;
  padding: 0;
}
.prop-checks li {
  display: flex;
  align-items: baseline;
  gap: var(--sp-2);
  font-size: var(--fs-200);
  line-height: var(--lh-300);
  color: var(--heading-color);
}
.prop-checks .ic {
  flex: 0 0 auto;
  color: var(--brand-ink);
  /* baseline alignment on a flex row leaves an SVG sitting on the line box
     rather than on the type it is ticking off. */
  align-self: center;
}

/* ── The description, in two languages ─────────────────────────────────────
   24 of 200 listings carry the whole listing twice, Arabic and English in one
   blob. Rendered as one stack, a reader of either language scrolls through a
   wall of the other to find theirs, and the right-aligned Arabic block left a
   560px empty column down the left of the page on a full-width paragraph.

   Two tabs, and a capped measure so the ragged edge falls where the script
   expects it rather than 560px away.
   ------------------------------------------------------------------------ */
.ld-tabs {
  display: flex;
  gap: var(--sp-1);
  margin-bottom: var(--sp-5);
  border-bottom: 1px solid var(--rule);
}
.ld-tab {
  min-height: var(--tap);
  padding: 0 var(--sp-4);
  background: none;
  border: 0;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  color: var(--paragraph-color);
  font-family: var(--body-font);
  font-size: var(--fs-200);
  font-weight: var(--fw-semibold);
  cursor: pointer;
  transition:
    color var(--dur) var(--ease-out),
    border-color var(--dur) var(--ease-out);
}
.ld-tab:hover {
  color: var(--heading-color);
}
.ld-tab.is-active {
  color: var(--heading-color);
  border-bottom-color: var(--brand-fill);
}

.ld-block {
  /* The readable measure. Without it a 920px column of Arabic set flush right
     leaves the whole left half of the page empty. */
  max-width: 68ch;
}
.ld-block.is-rtl {
  font-size: var(--fs-400);
}
.ld-p {
  margin: 0 0 var(--sp-4);
  font-size: var(--fs-300);
  line-height: 1.8;
  color: var(--paragraph-color);
  /* Single newlines inside a paragraph are meaningful in these descriptions:
     they are the room list. */
  white-space: pre-line;
}
.ld-block.is-rtl .ld-p {
  font-size: var(--fs-400);
}
.ld-p:last-child {
  margin-bottom: 0;
}

/* ── Pagination ────────────────────────────────────────────────────────────
   Numbered pages survive the back button, can be linked, and tell a buyer how
   much stock exists.
   ------------------------------------------------------------------------ */
.pager {
  margin-top: var(--sp-7);
  text-align: center;
}
.pager-count {
  margin: 0 0 var(--sp-3);
  font-size: var(--fs-200);
  color: var(--paragraph-color);
  font-variant-numeric: tabular-nums;
}
.pager-controls {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-4);
}
.pager-position {
  font-size: var(--fs-200);
  color: var(--heading-color);
  font-variant-numeric: tabular-nums;
}

/* The similar rail sits on its own ground so the page has a foot to it. */
.prop-similar-section {
  background: #FAFBF7;
  border-top: 1px solid var(--rule);
}
.prop-similar-section .rail-area {
  padding-top: var(--sp-7);
  padding-bottom: var(--sp-7);
}

/* ── The hero mode switch ──────────────────────────────────────────────────
   The strip was white at 16% over a photograph, so "Buy" and "Rent" sat on
   whatever the sky happened to be doing: over the pale band in the middle of
   this particular image the labels measured under 3:1. A dark ground holds
   white type at any exposure, and it also makes the green selected state read
   as lit rather than as merely different.
   ------------------------------------------------------------------------ */
.hero-modes {
  background: rgba(16, 20, 37, 0.46);
}
.hero-mode:hover {
  background: rgba(255, 255, 255, 0.16);
}

/* ── The header over a photograph ──────────────────────────────────────────
   Before it scrolls, the header has no background and its links are #111, so
   on the home page they sit directly on the hero video and their legibility
   is whatever that frame happens to be. A white-to-transparent scrim gives the
   dark type a ground at any exposure. On every other route the page behind it
   is already white, so the scrim is invisible and costs nothing.
   ------------------------------------------------------------------------ */
.navbar-area-over-hero:not(.navbar-area-fixed) {
  background: linear-gradient(
    to bottom,
    rgba(255, 255, 255, 0.94) 0%,
    rgba(255, 255, 255, 0.78) 55%,
    rgba(255, 255, 255, 0) 100%
  );
}

/* ── Section rhythm between browse and the first rail ──────────────────────
   80px under Browse plus 80px over the first rail put 160px of nothing between
   the last area card and "Latest for sale", which reads as the page having
   finished. Browse closes on its own rule, so the rail does not need to
   restate the separation with space as well.
   ------------------------------------------------------------------------ */
.browse-area + article .rail-area:first-child {
  padding-top: var(--sp-8);
}
@media (max-width: 767.98px) {
  .browse-area + article .rail-area:first-child {
    padding-top: var(--sp-6);
  }
}

/* The hamburger, deleted along with the drawer it opened. Three bars in the
   header ink, drawn by one pseudo-element, replacing four green hairlines that
   measured 1.6:1 on the hero photograph. It was a real fix to a real problem
   and the whole control is gone anyway: the navigation it hid is on screen now.
   ------------------------------------------------------------------------ */

/* The agent avatar, when the presigned URL has expired. Same treatment as the
   card: initials on the brand tint, never a silhouette. */
.prop-agent-initials {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--brand-tint);
  border: 1px solid var(--brand-line);
  color: var(--brand-ink);
  font-size: var(--fs-200);
  font-weight: var(--fw-semibold);
  letter-spacing: 0.02em;
}

/* ── A gallery frame with nothing behind it ────────────────────────────────
   Same watermark as the card cover, so a listing whose photographs have gone
   missing looks like a listing awaiting photographs rather than like a page
   that failed to render. The detail hero is the largest surface on the site;
   a grey box with alt text spilling across it is the worst thing a visitor
   can meet there.
   ------------------------------------------------------------------------ */
.prop-detail-hero-empty,
.prop-detail-thumb-empty {
  display: block;
  width: 100%;
  height: 100%;
  background:
    url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="96" height="96" viewBox="0 0 24 24" fill="none" stroke="%2357761C" stroke-opacity="0.26" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"><path d="M3 10.5 12 3l9 7.5"/><path d="M5.5 9.5V21h13V9.5"/><path d="M9.5 21v-6h5v6"/></svg>')
      center / 96px auto no-repeat,
    var(--brand-tint);
}
.prop-detail-thumb-empty {
  background-size: 32px auto;
}

/* ===========================================================================
   THE LISTING CARD
   ---------------------------------------------------------------------------
   Benchmarked against propertyfinder.bh at 1440 and rebuilt to beat it.

   Reading order, top to bottom: when it was listed, the price, the title, any
   badge, the facts as an icon row, the location, a divider, then the person
   selling it beside two equal contact buttons. Price is the largest thing on
   the card because it is the number that decides whether the next click
   happens; ours used to be fourth in the order and set smaller than the title.

   Two per row at 1440, not three. A 555px card shows a photograph worth
   looking at; a 364px one shows a thumbnail.

   One hairline, no resting shadow. Their whole page carries exactly one shadow
   (under the hero search card) and reads calmer than ours did with a shadow on
   every card in a grid of twelve.
   ========================================================================= */

/* ── The listing grid ──────────────────────────────────────────────────────
   Every surface that shows more than one card reads this: /property, /saved,
   /projects, a city page. One declaration of how far apart two cards sit.

   What was here was `.row.custom-gutter` with `.rld-filter-item.col-lg-6`
   inside it, a Bootstrap flex row whose gutter is -15px of row margin against
   15px of column padding. That produces a horizontal gutter and no vertical
   one at all, so the space between rows was left to `margin-bottom` on the
   card itself, and that margin was being discarded: a block child's bottom
   margin has nowhere to go inside a stretched flex column. Measured on
   /property at 390px before this, card0 ended at 1198 and card1 began at 1198.
   The cards were not close together, they were touching.
   ------------------------------------------------------------------------ */
.listing-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--grid-gap);
}
/* Two per row, not three. A 555px card at 1440 shows a photograph worth
   looking at; a 364px one shows a thumbnail. */
@media (min-width: 992px) {
  .listing-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  /* Saved, projects and a city page hold shorter records, so three fit. */
  .listing-grid-3 {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}
@media (min-width: 576px) and (max-width: 991.98px) {
  .listing-grid-3 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* /search-list stacks the same record in its row layout. Same token, so the
   two listing surfaces cannot drift apart. */
.listing-rows {
  display: flex;
  flex-direction: column;
  gap: var(--grid-gap);
}

.single-feature {
  display: flex;
  flex-direction: column;
  height: 100%;
  background: #fff;
  border: 1px solid var(--rule);
  border-radius: var(--r-xl);
  overflow: hidden;
  /* The container owns the gap. A card carries no margin of its own, so it
     cannot lose one to a flex parent or double one against a grid. */
  margin-bottom: 0;
  box-shadow: none;
  transition:
    border-color var(--dur) var(--ease-out),
    box-shadow var(--dur) var(--ease-out),
    transform var(--dur) var(--ease-out);
}
.single-feature:hover {
  /* 2px is a lift. 5px is a jump, and twelve of them in a grid is a page that
     twitches under the mouse. */
  transform: translateY(-2px);
  border-color: var(--brand-line);
  box-shadow: var(--shadow-lift);
}

/* ── Cover ─────────────────────────────────────────────────────────────── */
.single-feature .thumb {
  position: relative;
  width: 100%;
  aspect-ratio: 4 / 3;
  height: auto;
  overflow: hidden;
  border-radius: 0;
  /* Never white, and never empty. A cover has three states that look identical
     to a visitor: not requested yet (below the fold and lazy), in flight, and
     404. onError only catches the third. A watermark painted on the thumb
     covers all three, and the photograph draws over it the moment it decodes. */
  background:
    url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="72" height="72" viewBox="0 0 24 24" fill="none" stroke="%2357761C" stroke-opacity="0.28" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"><path d="M3 10.5 12 3l9 7.5"/><path d="M5.5 9.5V21h13V9.5"/><path d="M9.5 21v-6h5v6"/></svg>')
      center / 72px auto no-repeat,
    var(--brand-tint);
}
/* The cover link, and the reason it needs this much selector.
   style.css:2569 is `.single-feature .thumb a { visibility: hidden; opacity: 0;
   position: absolute; top: 12px }`, the theme's hidden quick-view link, and it
   is (0,2,1). Wrapping the cover in an anchor therefore inherited
   `visibility: hidden` on EVERY card on the site: the photographs, the land
   tiles and the placeholder were all being painted invisible, and only the
   watermark on .thumb itself showed through. It looked exactly like a storage
   outage, which is what made it hard to see. `.single-feature .thumb
   a.pc-cover-link` is (0,2,2) and takes it back.

   Longhand rather than `inset`, so nothing here depends on a shorthand. */
.single-feature .thumb a.pc-cover-link {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: block;
  visibility: visible;
  opacity: 1;
  font-size: inherit;
  color: inherit;
  transition: none;
}
.single-feature .thumb .pc-cover {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform var(--dur-slow) var(--ease-out);
}
.single-feature:hover .thumb .pc-cover {
  /* 1.03, not 1.08. Past about 4% a phone snap starts showing its compression. */
  transform: scale(1.03);
}

/* ── The land tile ─────────────────────────────────────────────────────────
   Phase 5.1: every land cover in the catalogue is a surveyor's plot diagram,
   and a survey cropped to 4:3 at card width is the one image on this site that
   gets worse the larger you print it. Land states its size instead. The survey
   is still in the detail gallery, at full size, where it is useful.
   ------------------------------------------------------------------------ */
.pc-land {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(160deg, #F6FAEC 0%, #E7EFD2 100%);
}
.pc-land-mark {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 72%;
  height: 72%;
  transform: translate(-50%, -50%);
  fill: rgba(149, 185, 53, 0.14);
  stroke: var(--brand-line);
  stroke-width: 1.5;
  stroke-linejoin: round;
}
.pc-land-mark circle {
  fill: var(--brand-line);
  stroke: none;
}
.pc-land-figures {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  text-align: center;
  padding: var(--sp-4);
}
.pc-land-area {
  font-family: var(--heading-font);
  font-size: var(--fs-700);
  line-height: var(--lh-700);
  font-weight: var(--fw-bold);
  color: var(--brand-ink);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
}
.pc-land-unit {
  font-size: var(--fs-200);
  font-weight: var(--fw-semibold);
  color: var(--brand-ink);
}
.pc-land-class {
  margin-top: var(--sp-2);
  padding: 2px var(--sp-3);
  background: rgba(255, 255, 255, 0.88);
  border: 1px solid var(--brand-line);
  border-radius: var(--r-full);
  font-size: var(--fs-100);
  font-weight: var(--fw-semibold);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--brand-ink);
}

/* ── Body ──────────────────────────────────────────────────────────────── */
.single-feature .details {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  background: #fff;
  padding: var(--card-pad);
}
.pc-record {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  color: inherit;
  text-decoration: none;
}
.pc-record:hover,
.pc-record:focus-visible {
  color: inherit;
  text-decoration: none;
}

.pc-listed {
  font-size: var(--fs-100);
  line-height: 16px;
  color: var(--paragraph-color);
}
/* Desktop only. It earns its line on a wide grid, where a returning buyer is
   scanning twelve cards for one they have not opened yet. On a phone, where
   one card fills the screen and the whole card is the answer, it is a grey
   line of copy above the price, and the price is what the card is for. */
@media (max-width: 767.98px) {
  .pc-listed {
    display: none;
  }
}

/* The largest thing on the card, and deliberately NOT green.
   Green is the accent and an accent that appears on every card twelve times is
   not an accent, it is the background. It marks primary actions and active
   states now; the price is loud by size, which is the only channel that also
   works in a greyscale print and for a colourblind reader. */
.pc-price {
  margin-top: 2px;
  font-family: var(--heading-font);
  font-size: var(--fs-600);
  line-height: var(--lh-600);
  font-weight: var(--fw-bold);
  color: var(--heading-color);
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}
.pc-price-period {
  margin-left: var(--sp-2);
  font-family: var(--body-font);
  font-size: var(--fs-200);
  font-weight: var(--fw-regular);
  letter-spacing: 0;
  color: var(--paragraph-color);
}

/* One line. 169 of 200 listings have a machine-composed title, so four villas
   in Janabiya carry four identical headlines: a second wrapped line of the same
   sentence buys nothing and costs the card 24px. */
/* Deliberately the body role, not card-title: on a listing card the price is
   the focal point and 169 of 200 names are machine-composed, so the name is
   supporting text. The project card overrides it below, where the name IS the
   card. line-height was a raw 24px. */
.pc-title {
  margin-top: var(--sp-2);
  font-family: var(--t-body-font);
  font-size: var(--t-body-size);
  line-height: var(--t-body-lh);
  font-weight: var(--t-body-weight);
  color: var(--heading-color);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.pc-badges {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin-top: var(--sp-3);
}

/* The facts, as glyph and number. At 14px the word "Bed" and a bed glyph cost
   the same width, but four glyphs give the row a shape the eye can land on
   without reading it, which is the whole point of a row that appears on every
   card in a list of twelve. */
.pc-facts {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2) var(--sp-4);
  margin-top: var(--sp-3);
  min-height: 20px;
}
.pc-fact {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--fs-200);
  line-height: 20px;
  color: var(--heading-color);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.spec-icon {
  flex: 0 0 auto;
  color: var(--paragraph-color);
}

.pc-location {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: var(--sp-3);
  font-size: var(--fs-200);
  line-height: 20px;
  color: var(--paragraph-color);
  min-width: 0;
}
.pc-location .spec-icon {
  color: var(--brand-ink);
}

/* ── The foot: who is selling it, and the two ways to reach them ──────────
   Call and WhatsApp are equal siblings. Giving WhatsApp a button and Call
   nothing decided for the visitor which channel they preferred, on a market
   where a phone call is still the normal first move.
   ------------------------------------------------------------------------ */
.pc-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  margin-top: var(--sp-4);
  padding-top: var(--sp-4);
  border-top: 1px solid var(--rule);
}
.pc-agent {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  min-width: 0;
  flex: 1 1 auto;
}
.pc-agent-face {
  flex: 0 0 auto;
  width: 32px;
  height: 32px;
  border-radius: var(--r-full);
  object-fit: cover;
  border: 1px solid var(--rule);
  background: var(--brand-tint);
}
/* Initials, never a silhouette. A person glyph on a white disc is the visual
   signature of missing data. */
.pc-agent-initials {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: var(--fs-100);
  font-weight: var(--fw-semibold);
  color: var(--brand-ink);
  border-color: var(--brand-line);
}
.pc-agent-name {
  font-size: var(--fs-200);
  font-weight: var(--fw-semibold);
  color: var(--heading-color);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Two discs. The container query that used to strip these labels below 360px
   of card is gone with them: a card cannot run out of room for a control that
   is the same width at every breakpoint, and the agent's name now has the
   whole rest of the row. Half a person's name reads as a data error, so the
   name is the part that had to win. */
.pc-actions {
  display: flex;
  gap: var(--sp-2);
  flex: 0 0 auto;
}

/* ── The project card ──────────────────────────────────────────────────── */
.pc-project-badge {
  align-self: flex-start;
  margin-bottom: var(--sp-2);
}
/* A project card carries a name and a place, so here the name is the card
   title role. line-height was a raw 26px. */
.project-card .pc-title {
  margin-top: 0;
  font-family: var(--t-card-title-font);
  font-size: var(--t-card-title-size);
  line-height: var(--t-card-title-lh);
  font-weight: var(--t-card-title-weight);
  letter-spacing: var(--t-card-title-track);
}

/* The rail column count is set per rail, because a listing card and a project
   card are not the same object: a listing needs the width to carry a price, an
   icon row and two buttons, a project carries a name and a place. */
@media (min-width: 992px) {
  .rail-row {
    grid-auto-columns: calc(
      (100% - var(--sp-5) * (var(--rail-cols, 3) - 1)) / var(--rail-cols, 3)
    );
  }
}

/* ===========================================================================
   THE HEADER, IN TWO ROWS
   ---------------------------------------------------------------------------
   Row one is identity and the two personal controls. Row two is the search
   intents, set as plain text, which is the propertyfinder.bh pattern and the
   right one: Buy and Rent are not destinations, they are the two questions
   every visitor arrives with.

   The nav no longer carries `.navbar-area` (both the header AND the nav did,
   and that class is position: fixed, so the header resolved to zero height and
   the sticky white background was being painted onto nothing).
   ========================================================================= */

.navbar-area .navbar {
  display: block;
  padding: 0;
  width: 100%;
}
.main-anchor {
  display: block;
  height: 0;
  overflow: hidden;
}

/* Both rows carry an explicit height, and --nav-h is declared as their sum in
   global/_variables.css. Before this the rows were sized by their padding and
   --nav-h was a guess at the total: it said 112 while the header measured 117,
   and 56 on a phone while it measured 60. Every sticky aside, focus ring and
   scrolled-to heading on the site was therefore four or five pixels under the
   header it was supposed to clear. */
.navbar-area .nav-container.nav-identity {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  height: var(--nav-row-identity);
  padding: 0 15px;
}

.nav-links-row {
  height: var(--nav-row-links);
  border-top: 1px solid var(--rule);
  /* The header carried a rule between its two rows and nothing under the
     second, so on /contact, /about and every other white page it had no
     bottom edge and the intents floated into the page. box-sizing is
     border-box site-wide, so this costs the row no height and --nav-h stays
     honest. */
  border-bottom: 1px solid var(--rule);
}
/* Below 768 the intents are in the bottom tab bar, so this row is not hidden
   by a utility class that could be forgotten: it has no height to occupy.
   The hairline it was carrying has to move with it, or the header loses its
   bottom edge on a phone, which is the same defect the desktop header had. */
@media (max-width: 767.98px) {
  .nav-links-row {
    display: none;
  }
  .navbar-area .nav-container.nav-identity {
    border-bottom: 1px solid var(--rule);
  }
  .navbar-area:not(.navbar-area-fixed) .nav-container.nav-identity {
    border-bottom-color: rgba(16, 20, 37, 0.08);
  }
}
.nav-links {
  display: flex;
  align-items: center;
  height: 100%;
  gap: var(--sp-6);
  list-style: none;
  margin: 0;
  padding: 0;
}
.nav-links a {
  display: inline-flex;
  align-items: center;
  height: var(--nav-row-links);
  font-family: var(--body-font);
  font-size: var(--fs-200);
  font-weight: var(--fw-semibold);
  color: var(--heading-color);
  text-decoration: none;
  /* The active marker is a rule under the word, not a colour change: it does
     not compete with the green that marks the actions. */
  border-bottom: 2px solid transparent;
  transition: color var(--dur) var(--ease-out), border-color var(--dur) var(--ease-out);
}
.nav-links a:hover,
.nav-links a:focus-visible {
  color: var(--brand-ink);
  text-decoration: none;
}
.nav-links a.is-active {
  color: var(--brand-ink);
  border-bottom-color: var(--brand-fill);
}

/* The un-scrolled header sits over the hero photograph, and #111 links on a
   photograph are legible by luck. A white-to-transparent scrim gives them a
   ground at any exposure; on every other route the page behind it is already
   white, so it is invisible and costs nothing. */
.navbar-area-over-hero:not(.navbar-area-fixed) {
  background: linear-gradient(
    to bottom,
    rgba(255, 255, 255, 0.96) 0%,
    rgba(255, 255, 255, 0.88) 70%,
    rgba(255, 255, 255, 0.55) 100%
  );
}
/* Every route without a photograph behind the header is opaque immediately.
   The scroll handler that adds .navbar-area-fixed runs a frame late, and in
   that frame a badge or a heading scrolling up was drawing through the logo. */
.navbar-area.navbar-area-solid {
  background-color: #fff;
}
.navbar-area-over-hero:not(.navbar-area-fixed) .nav-links-row {
  border-top-color: rgba(16, 20, 37, 0.08);
  border-bottom-color: rgba(16, 20, 37, 0.08);
}

@media (min-width: 768px) {
  .nav-right-part-desktop {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
  }
}

.nav-saved {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  min-width: var(--tap);
  min-height: var(--tap);
  padding: 0 var(--sp-3);
  border-radius: var(--r-full);
  color: var(--heading-color);
  font-size: var(--fs-200);
  font-weight: var(--fw-semibold);
  text-decoration: none;
  transition: background var(--dur) var(--ease-out), color var(--dur) var(--ease-out);
}
.nav-saved:hover,
.nav-saved:focus-visible,
.nav-saved.is-active {
  background: var(--brand-tint);
  color: var(--brand-ink);
  text-decoration: none;
}
.nav-saved i {
  font-size: var(--fs-400);
}
.nav-saved-count {
  min-width: 20px;
  height: 20px;
  padding: 0 5px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--r-full);
  background: var(--brand-fill);
  color: var(--brand-on-fill);
  font-size: var(--fs-100);
  font-weight: var(--fw-semibold);
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
/* Desktop is an icon and a number; the word is carried by the accessible name
   there and shown in full in the drawer, where there is room. */
@media (min-width: 768px) {
  .nav-right-part-desktop .nav-saved-label {
    display: none;
  }
}
.nav-cta {
  border-radius: var(--r-full);
  padding: 0 var(--sp-5);
}

/* The hamburger and its drawer are deleted; the navigation is on screen. */

@media (max-width: 767.98px) {
  .navbar-area .nav-container.nav-identity .logo img {
    max-width: 118px;
  }
}

/* ===========================================================================
   THE SEARCH PANEL
   ---------------------------------------------------------------------------
   Segmented toggle, one bar, four pills. Shared by the hero and the listing
   page so the two cannot drift apart, which is exactly what happened last time
   these were two separate controls: the hero searched on exact area equality
   while the results page searched properly, and the same word returned
   different numbers depending on where it was typed.

   The panel is the ONE place on the site allowed a shadow. Everything else is
   a hairline. That is what makes it read as the object in front.
   ========================================================================= */

.sp {
  background: #fff;
  border: 1px solid var(--rule);
  border-radius: var(--r-xl);
  padding: var(--sp-5);
  box-shadow: 0 2px 4px rgba(16, 20, 37, 0.05), 0 16px 40px -16px rgba(16, 20, 37, 0.22);
}

/* The h1 of /property, sitting inside the search panel. It was 28 while the
   h1 of every neighbouring page was 36. */
.sp-heading {
  margin: 0 0 var(--sp-2);
  font-family: var(--t-page-title-font);
  font-size: var(--t-page-title-size);
  line-height: var(--t-page-title-lh);
  font-weight: var(--t-page-title-weight);
  letter-spacing: var(--t-page-title-track);
  color: var(--heading-color);
}
.sp-sub {
  margin: 0 0 var(--sp-4);
  font-size: var(--fs-200);
  color: var(--paragraph-color);
}

/* ── The segmented toggle ──────────────────────────────────────────────── */
.sp-modes {
  display: inline-flex;
  gap: 2px;
  padding: 3px;
  margin-bottom: var(--sp-4);
  background: #F1F3ED;
  border-radius: var(--r-full);
}
.sp-mode {
  min-height: 38px;
  padding: 0 var(--sp-5);
  border: 0;
  border-radius: var(--r-full);
  background: transparent;
  color: var(--paragraph-color);
  font-family: var(--body-font);
  font-size: var(--fs-200);
  font-weight: var(--fw-semibold);
  cursor: pointer;
  transition: background var(--dur) var(--ease-out), color var(--dur) var(--ease-out);
}
.sp-mode:hover {
  color: var(--heading-color);
}
/* The selected mode is the readable one. It used to be the opposite: the
   active tab was white on brand green at 2.27:1, so choosing an option made it
   the least legible thing in the control. */
.sp-mode.is-active {
  background: var(--brand-fill);
  color: var(--brand-on-fill);
}

/* ── The bar ───────────────────────────────────────────────────────────── */
.sp-bar {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  background: #fff;
  border: 1px solid var(--field-line);
  border-radius: var(--r-full);
  padding: 4px 4px 4px var(--sp-5);
  transition: border-color var(--dur) var(--ease-out);
}
.sp-bar:focus-within {
  border-color: var(--brand-ink);
}
.sp-bar-icon {
  flex: 0 0 auto;
  color: var(--paragraph-color);
  font-size: var(--fs-300);
}
.sp-bar input {
  flex: 1 1 auto;
  min-width: 0;
  height: 48px;
  border: 0;
  background: transparent;
  font-size: var(--fs-300);
  color: var(--heading-color);
  /* iOS zooms a focused field under 16px. --fs-300 is 16px, so it does not. */
}
.sp-bar input:focus {
  outline: none;
}
.sp-bar input::placeholder {
  color: var(--paragraph-color);
}
.sp-go {
  flex: 0 0 auto;
  min-height: 48px;
  padding: 0 var(--sp-6);
  border: 0;
  border-radius: var(--r-full);
  background: var(--brand-fill);
  color: var(--brand-on-fill);
  font-family: var(--body-font);
  font-size: var(--fs-200);
  font-weight: var(--fw-semibold);
  cursor: pointer;
  transition: background var(--dur) var(--ease-out);
}
.sp-go:hover {
  background: #A6C74B;
}

/* ── The filter pills ──────────────────────────────────────────────────────
   Four pills, one control behind all four: a button that opens the panel in
   FilterPopover. They were native <select>s wearing a pill, which is defensible
   for five options and indefensible for a hundred: the Area pill opened the
   operating system's own full-height list, in the platform's black, carrying
   every location string in the table with no count and no type-ahead.

   The pill is still a bordered wrapper so a set pill can carry a clear button
   beside the trigger. A button inside a button is not markup, so the two are
   siblings and the wrapper draws the outline around them both.
   ------------------------------------------------------------------------ */
.sp-pills {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin-top: var(--sp-3);
}
.sp-pill {
  position: relative;
  display: inline-flex;
  align-items: center;
  min-height: 40px;
  border: 1px solid var(--rule-strong);
  border-radius: var(--r-full);
  background: #fff;
  color: var(--heading-color);
  transition: border-color var(--dur) var(--ease-out), background var(--dur) var(--ease-out);
}
.sp-pill:hover {
  border-color: var(--brand-ink);
}
.sp-pill.is-open {
  border-color: var(--brand-ink);
}
/* A pill that is carrying a value looks different from one that is not, which
   is the only way to see what a search is about to do without reading it. */
.sp-pill.is-set {
  background: var(--brand-tint);
  border-color: var(--brand-line);
  color: var(--brand-ink);
}
.sp-pill.is-set .spec-icon {
  color: var(--brand-ink);
}

.sp-pill-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 38px;
  padding: 0 var(--sp-4);
  border: 0;
  border-radius: var(--r-full);
  background: transparent;
  font-family: var(--body-font);
  font-size: var(--fs-200);
  font-weight: var(--fw-semibold);
  color: inherit;
  cursor: pointer;
}
/* The pill wrapper owns the shape, so the ring belongs on the pill and not on
   the transparent button sitting inside it. */
.sp-pill-btn:focus-visible {
  outline: none;
}
.sp-pill:focus-within {
  outline: 2px solid var(--brand-on-fill);
  outline-offset: 2px;
  box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.95);
}
.sp-pill-text {
  white-space: nowrap;
}
/* The chevron. Drawn from two borders rather than an image, and it inherits
   the pill's ink so a set pill's arrow goes green with the rest of it. */
.sp-pill-caret {
  width: 6px;
  height: 6px;
  margin: -3px 0 0 2px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg);
  opacity: 0.55;
  transition: transform var(--dur) var(--ease-out);
}
.sp-pill.is-open .sp-pill-caret {
  transform: rotate(225deg);
  margin-top: 2px;
}
/* Only on a set pill, because there is nothing to clear before that. It shares
   the pill's outline rather than drawing its own, so the pill stays one object
   with two halves instead of reading as two controls jammed together. */
.sp-pill-clear {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  min-height: 38px;
  margin-right: 4px;
  padding: 0;
  border: 0;
  border-radius: var(--r-full);
  background: transparent;
  color: inherit;
  cursor: pointer;
  opacity: 0.7;
  transition: opacity var(--dur) var(--ease-out), background var(--dur) var(--ease-out);
}
.sp-pill-clear:hover {
  opacity: 1;
  background: rgba(87, 118, 28, 0.12);
}

/* ── The panel ─────────────────────────────────────────────────────────────
   Portalled to <body>, so neither the scrolling pill row on a phone nor the
   350ms transform on .page-transition can clip it. Above the fixed navbar
   (z-index 9999) and below the skip link (10001).
   ------------------------------------------------------------------------ */
.fp-layer {
  position: fixed;
  z-index: 10000;
}
.fp-panel {
  position: fixed;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: #fff;
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
  /* The search panel is the object in front and this sits on top of it, so it
     carries the same two-layer shadow one step stronger. */
  box-shadow: 0 2px 4px rgba(16, 20, 37, 0.06), 0 18px 44px -14px rgba(16, 20, 37, 0.28);
}

.fp-search {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-3) var(--sp-2);
  border-bottom: 1px solid var(--rule);
}
.fp-search-icon {
  flex: 0 0 auto;
  color: var(--paragraph-color);
}
.fp-search input {
  flex: 1 1 auto;
  min-width: 0;
  height: 34px;
  border: 0;
  background: transparent;
  /* 16px: anything smaller and iOS zooms the page when the field takes focus,
     which on a panel anchored to a pill means the pill scrolls off screen. */
  font-size: var(--fs-300);
  color: var(--heading-color);
}
.fp-search input:focus {
  outline: none;
}
.fp-search input::placeholder {
  color: var(--paragraph-color);
}

.fp-list {
  flex: 1 1 auto;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: var(--sp-2) 0;
  overscroll-behavior: contain;
}
.fp-list:focus-visible {
  outline: none;
}
.fp-group {
  padding: var(--sp-3) var(--sp-4) var(--sp-1);
  font-size: var(--fs-100);
  font-weight: var(--fw-semibold);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--state-muted);
}
.fp-group:first-child {
  padding-top: var(--sp-1);
}
.fp-opt {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  min-height: var(--tap);
  padding: var(--sp-2) var(--sp-4);
  font-size: var(--fs-200);
  color: var(--heading-color);
  cursor: pointer;
}
/* One highlight, driven by the keyboard, and the mouse moves the same
   highlight rather than painting a second one. Two rows lit at once is the
   commonest bug in a hand-built listbox. */
.fp-opt.is-active {
  background: var(--brand-tint);
}
.fp-opt.is-set {
  font-weight: var(--fw-semibold);
  color: var(--brand-ink);
}
.fp-opt.is-set .fp-opt-name::after {
  content: "\2713";
  margin-left: var(--sp-2);
}
.fp-opt-count {
  flex: 0 0 auto;
  font-size: var(--fs-100);
  color: var(--paragraph-color);
  font-variant-numeric: tabular-nums;
}
.fp-opt.is-set .fp-opt-count {
  color: var(--brand-ink);
}
.fp-empty {
  margin: 0;
  padding: var(--sp-4);
  font-size: var(--fs-200);
  color: var(--paragraph-color);
}
/* Counts arrive before the list does, because an option that would return
   nothing is never offered and that cannot be known until the numbers land.
   Five bars for the fifth of a second it takes, rather than a list that
   reshuffles under the pointer. */
.fp-loading {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
}
.fp-skel {
  height: 12px;
  border-radius: var(--r-sm);
  background: var(--brand-tint);
}
.fp-skel:nth-child(3) { width: 70%; }
.fp-skel:nth-child(4) { width: 84%; }
.fp-skel:nth-child(5) { width: 62%; }

.fp-clear {
  flex: 0 0 auto;
  min-height: var(--tap);
  padding: 0 var(--sp-4);
  border: 0;
  border-top: 1px solid var(--rule);
  background: #fff;
  font-family: var(--body-font);
  font-size: var(--fs-200);
  font-weight: var(--fw-semibold);
  color: var(--brand-ink);
  text-align: left;
  cursor: pointer;
}
.fp-clear:hover {
  background: var(--brand-tint);
}

.fp-head,
.fp-scrim {
  display: none;
}

/* ── The panel on a phone ──────────────────────────────────────────────────
   A sheet from the bottom edge. At 390px the pill it would anchor to is
   itself halfway along a scrolling row, so there is no honest place to hang a
   panel from, and a sheet is the shape a thumb already knows. Full width, so
   it can never be wider than the viewport.
   ------------------------------------------------------------------------ */
@media (max-width: 767.98px) {
  .fp-layer.is-sheet {
    inset: 0;
  }
  .fp-scrim {
    display: block;
    position: absolute;
    inset: 0;
    background: rgba(16, 20, 37, 0.4);
    animation: fpFade var(--dur) var(--ease-out);
  }
  .fp-layer.is-sheet .fp-panel {
    top: auto;
    right: 0;
    bottom: 0;
    left: 0;
    width: auto;
    max-width: 100%;
    max-height: 76vh;
    border-radius: var(--r-xl) var(--r-xl) 0 0;
    border-bottom: 0;
    padding-bottom: env(safe-area-inset-bottom, 0px);
    animation: fpSheet var(--dur-slow) var(--ease-out);
  }
  .fp-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-3);
    padding: var(--sp-4) var(--sp-3) var(--sp-3) var(--sp-4);
    border-bottom: 1px solid var(--rule);
  }
  .fp-title {
    font-size: var(--fs-400);
    font-weight: var(--fw-semibold);
    color: var(--heading-color);
  }
  .fp-close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--tap);
    height: var(--tap);
    margin: calc(var(--sp-2) * -1) 0;
    border: 0;
    border-radius: var(--r-full);
    background: transparent;
    color: var(--paragraph-color);
    cursor: pointer;
  }
  .fp-opt {
    padding-left: var(--sp-4);
    padding-right: var(--sp-4);
    font-size: var(--fs-300);
  }
  .fp-clear {
    min-height: 52px;
  }
}

@keyframes fpFade {
  from { opacity: 0; }
  to { opacity: 1; }
}
@keyframes fpSheet {
  from { transform: translateY(16px); opacity: 0; }
  to { transform: translateY(0); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .fp-scrim,
  .fp-layer.is-sheet .fp-panel {
    animation: none;
  }
  .sp-pill-caret {
    transition: none;
  }
}
.sp-count {
  margin: var(--sp-4) 0 0;
  font-size: var(--fs-200);
  color: var(--paragraph-color);
}
.sp-count strong {
  color: var(--heading-color);
  font-variant-numeric: tabular-nums;
}

/* ── The hero, with the panel on the seam ──────────────────────────────────
   The card overlaps the bottom edge of the photograph. A hero that ends
   exactly where the picture ends looks like the whole site; a card breaking
   the seam says the page continues.
   ------------------------------------------------------------------------ */
.banner-panel-wrap {
  position: relative;
  z-index: 3;
  margin-top: calc(var(--sp-8) * -1);
  padding-bottom: var(--sp-2);
}
.sp-hero {
  max-width: 860px;
}

@media (max-width: 767.98px) {
  .sp {
    padding: var(--sp-4);
  }
  /* Barely overlapping on a phone. Any more and the panel eats the headline
     it is supposed to sit under: measured at 390, a 48px pull put the card
     over the second line of the H1. */
  .banner-panel-wrap {
    margin-top: calc(var(--sp-5) * -1);
  }
  /* The field and the button stack, because a 44px target and a readable
     placeholder cannot both fit on one 390px row. They become two pills rather
     than a bordered box containing two controls, which read as a panel inside
     a panel. */
  .sp-bar {
    flex-wrap: wrap;
    gap: var(--sp-2);
    border: 0;
    border-radius: 0;
    padding: 0;
  }
  .sp-bar-icon {
    position: absolute;
    left: var(--sp-4);
    top: 24px;
    transform: translateY(-50%);
  }
  .sp-bar input {
    flex: 1 0 100%;
    height: 48px;
    padding: 0 var(--sp-4) 0 var(--sp-7);
    border: 1px solid var(--field-line);
    border-radius: var(--r-full);
  }
  .sp-go {
    flex: 1 0 100%;
  }
  .sp-modes {
    display: flex;
    width: 100%;
  }
  .sp-mode {
    flex: 1 1 0;
    padding: 0 var(--sp-2);
  }
  /* One scrolling row rather than two ragged ones. Same shape as the type
     chips on the listing page, so the two read as one idea. */
  .sp-pills {
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: none;
    margin-left: calc(var(--sp-4) * -1);
    margin-right: calc(var(--sp-4) * -1);
    padding-left: var(--sp-4);
    padding-right: var(--sp-4);
  }
  .sp-pills::-webkit-scrollbar {
    display: none;
  }
  .sp-pill {
    flex: 0 0 auto;
  }
}

/* ── The listing page head ─────────────────────────────────────────────────
   A band of colour behind the search panel, so the panel reads as an object on
   a surface rather than as the first paragraph of the page. Same panel as the
   hero, so the two searches cannot drift apart.
   ------------------------------------------------------------------------ */
.listing-head {
  background: linear-gradient(180deg, #EEF2E4 0%, #FAFBF7 100%);
  border-bottom: 1px solid var(--rule);
  padding-top: calc(var(--nav-h) + var(--sp-6));
  padding-bottom: var(--sp-6);
  margin-bottom: var(--sp-7);
}
.sp-listing {
  max-width: 100%;
}

/* The count on a type chip. Tabular, so a row of chips has its numbers on one
   rhythm rather than five different widths of the digit one. */
.gov-count {
  margin-left: var(--sp-2);
  font-size: var(--fs-100);
  font-weight: var(--fw-semibold);
  color: var(--paragraph-color);
  font-variant-numeric: tabular-nums;
}
.gov-chip.is-active .gov-count {
  color: var(--brand-on-fill);
  opacity: 0.75;
}

/* Pills, not rounded rectangles, and the same radius as every other pill on
   the site. */
.gov-chip {
  border-radius: var(--r-full);
}

/* The listing grid sits under the head, which already provides the space. */
.property-area .container > .row:first-child {
  margin-bottom: var(--sp-5);
}

/* ── The row layout ────────────────────────────────────────────────────────
   /search-list lays the same record on its side. style.css has always declared
   this a flex row, and it stopped working the day `.single-feature { display:
   flex; flex-direction: column }` was added for the grid card: that selector
   sets a property the theme's row rule never declares, so nothing overrode it
   and every result became a full-width poster, one listing per 636px.

   The photo takes a third. Wider than that and it starts competing with the
   facts, and on a phone-snap JPEG the extra width buys blur, not detail.
   ------------------------------------------------------------------------ */
/* The row card carries no margin either. style.css gives .single-feature.style-two
   `margin: 0 0 30px 0`, which is (0,2,0) and so survives the `margin-bottom: 0`
   on .single-feature above no matter what order the sheets load in. Measured on
   /search-list at 390px, that put 46px between rows: the container's 16 plus a
   30 nobody asked for, and 30 is not on the scale. .listing-rows owns the gap. */
.single-feature.style-two {
  margin: 0;
}

@media (min-width: 768px) {
  .single-feature.style-two {
    flex-direction: row;
    flex-wrap: nowrap;
    align-items: stretch;
    height: auto;
  }
  .single-feature.style-two .thumb {
    flex: 0 0 34%;
    /* No ratio here: the cover fills the height the record sets, which is what
       keeps the row's two columns level. The watermark has to be told its own
       size again, because the shorthand that set it also set the ratio box. */
    aspect-ratio: auto;
    height: auto;
    min-height: 240px;
    background-size: 72px auto, auto;
  }
  .single-feature.style-two .details {
    flex: 1 1 auto;
    justify-content: center;
  }
  /* The row is 720px wide, where a 32px circle reads as a bullet. */
  .single-feature.style-two .pc-agent-face {
    width: 40px;
    height: 40px;
  }
}

/* Stacked below 768 it is simply the grid card again. */
@media (max-width: 767.98px) {
  .single-feature.style-two .thumb {
    aspect-ratio: 4 / 3;
  }
}

/* ===========================================================================
   ICON-ONLY CONTROLS
   ---------------------------------------------------------------------------
   The brief was "wherever we can use an icon we use it, text is not needed."
   Taken literally that produces a page of mystery buttons, so the rule this
   sheet enforces is narrower and it is the whole system:

     a glyph replaces a word only where the symbol is already known to the
     person using it AND the surrounding context supplies the noun.

   A handset, the WhatsApp mark, a heart, a chain, a share tree, a pin, a bed,
   a bath: known. "Request a viewing", "Max price", "Property type" before a
   value is chosen, a land classification code: not known, and those keep their
   words. Every control that lost its label kept three things, without
   exception: an aria-label, a title, and a 44px target.

   One shape for all of them. A 44px disc, a hairline, white ground, ink from
   the type around it. Hover tints, on fills. Nothing here has a shadow, a
   gradient or a second colour: forty identical discs down a results page are
   only calm if each of them is quiet.
   ========================================================================= */

/* The glyph itself. display:block kills the baseline gap that would otherwise
   make a 44px disc 47px tall and every icon row a pixel off from its
   neighbour. */
.ic {
  display: block;
  flex: 0 0 auto;
}

.ic-btn {
  position: relative;
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--tap);
  height: var(--tap);
  min-width: var(--tap);
  min-height: var(--tap);
  padding: 0;
  border: 1px solid var(--field-line);
  border-radius: var(--r-full);
  background: #fff;
  box-shadow: none;
  color: var(--heading-color);
  line-height: 1;
  text-decoration: none;
  cursor: pointer;
  transition:
    background var(--dur) var(--ease-out),
    border-color var(--dur) var(--ease-out),
    color var(--dur) var(--ease-out);
}
.ic-btn:hover,
.ic-btn:focus-visible {
  background: var(--brand-tint);
  border-color: var(--brand-ink);
  color: var(--brand-ink);
  text-decoration: none;
}
/* On differs by more than hue: the glyph fills as well as darkens, so a saved
   listing still reads as saved in greyscale and to a colourblind eye. */
.ic-btn[aria-pressed="true"] {
  background: var(--brand-fill);
  border-color: var(--brand-fill);
  color: var(--brand-on-fill);
}
.ic-btn[aria-pressed="true"]:hover {
  background: var(--brand-fill);
  border-color: var(--brand-ink);
  color: var(--brand-on-fill);
}
.ic-btn[disabled] {
  background: #F1F2EE;
  border-color: #C6C9C1;
  color: #9AA093;
  cursor: default;
}

/* 48px where it sits beside a 48px labelled button and has to line up with it. */
.ic-btn-lg {
  width: 48px;
  height: 48px;
  min-width: 48px;
  min-height: 48px;
}

/* 32px where a 44px disc would outweigh the line it belongs to. The box
   shrinks, the target does not: the pseudo-element carries the missing 12px,
   which is the only legal way to draw a control smaller than a thumb. */
.ic-btn-sm {
  width: 32px;
  height: 32px;
  min-width: 32px;
  min-height: 32px;
}
.ic-btn-sm::after {
  content: "";
  position: absolute;
  inset: -6px;
  border-radius: inherit;
}

/* In the header the discs are ghosts. Three outlines and a filled CTA in one
   56px row is four objects competing where there is one decision to make, so
   the border only appears under the cursor. */
.nav-ic {
  border-color: transparent;
  background: transparent;
}

/* ── The header cluster on a phone ─────────────────────────────────────────
   Logo left, WhatsApp and a handset right. Nothing else.

   It used to be a three-column grid centring the logo between a hamburger and
   a pair of icons, which was the right answer to the question "where does a
   centred logo go when the things flanking it are unequal widths". That
   question no longer exists: the hamburger is gone, so there is a left edge to
   sit on, and a logo on the left edge is where a logo goes.

   Saved left with it. A shortlist with a count is a destination, and every
   destination is now in the tab bar; keeping it up here as well would be the
   same tap offered twice, eleven pixels from the WhatsApp icon.
   ------------------------------------------------------------------------ */
.nav-right-part-mobile {
  display: none;
}
@media (max-width: 767.98px) {
  .navbar-area .nav-container.nav-identity {
    justify-content: space-between;
  }
  .nav-right-part-mobile {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: var(--sp-1);
    flex: 0 0 auto;
  }
}
/* 320 to 359: the pair and a 118px logo do not both fit. The logo gives up the
   14px, because it is the only one of the three that is not a target. */
@media (max-width: 359.98px) {
  .navbar-area .nav-container.nav-identity .logo img {
    max-width: 104px;
  }
}

/* ── The rail's way out ────────────────────────────────────────────────────
   On a desktop the heading two inches to the left already names the list this
   opens, so "View all" was the third time one row said one thing. It becomes
   the same disc as everything else, matching the prev/next pair beside it.
   On a phone those arrows are hidden and this would be the only unlabelled
   control on the screen, so there the words stay.
   ------------------------------------------------------------------------ */
@media (min-width: 768px) {
  .rail-viewall {
    width: var(--tap);
    height: var(--tap);
    justify-content: center;
    border: 1px solid var(--field-line);
    border-radius: var(--r-full);
    color: var(--heading-color);
  }
  .rail-viewall:hover,
  .rail-viewall:focus-visible {
    background: var(--brand-tint);
    border-color: var(--brand-ink);
    color: var(--brand-ink);
    text-decoration: none;
  }
  .rail-viewall-label {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
  }
  /* The arrow no longer travels: it is the whole control, and a control that
     slides out from under the cursor is a control you miss. */
  .rail-viewall:hover .ic {
    transform: none;
  }
}

/* ── The kind of property, drawn ───────────────────────────────────────────
   The one fact on a card with no text beside it, so it carries the ink of a
   fact rather than the grey of a unit label. It is also the only glyph on the
   site that changes shape with the data. */
.pc-fact-glyph .spec-icon {
  color: var(--heading-color);
}

/* ── The agent's card ──────────────────────────────────────────────────────
   Three stacked full-width buttons, one of which used a 34-character email
   address as its label, became one row: the channel that gets answered keeps
   its word, the other two are discs.
   ------------------------------------------------------------------------ */
.prop-agent-actions {
  display: flex;
  align-items: stretch;
  gap: var(--sp-2);
}
.prop-agent-actions .prop-whatsapp-btn {
  flex: 1 1 auto;
  width: auto;
  min-height: 48px;
  margin-bottom: 0;
  padding: 0 var(--sp-4);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  border-radius: var(--r-full);
}
.prop-agent-actions .ic-btn-lg {
  border-radius: var(--r-full);
}

/* ── The bar at the bottom of a phone ──────────────────────────────────────
   Two labelled buttons and a price on a 390px row left the price 90px, and the
   price is the reason the bar exists. Call takes the disc; WhatsApp keeps its
   word, because it is the channel most of these leads arrive through and the
   one worth being unmistakable.
   ------------------------------------------------------------------------ */
/* And it is the outline disc, not a second filled one. #95B935 beside
   #25D366 is two greens in one 96px group, which reads as a rendering fault
   rather than as a pair of choices. One filled action per bar. */

/* ── The footer's contact block ────────────────────────────────────────────
   "WhatsApp the office" was the only line here describing a link instead of
   being one, and it pointed at the number printed directly above it. One
   number, one row, the second channel as a disc on the end of it.
   ------------------------------------------------------------------------ */
.footer-contact li {
  align-items: center;
}
.footer-contact > li > .ic {
  flex: 0 0 18px;
  margin-top: 0;
  color: var(--brand-ink);
}
/* ── The contact page's three facts ────────────────────────────────────────
   "Call Us" over a phone number, "Email Us" over an address, "Visit Us" over a
   street. Three captions that said what the value under them already said,
   and the glyph says the rest.
   ------------------------------------------------------------------------ */
.single-contact-info a {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  min-height: var(--tap);
  color: var(--heading-color);
  text-decoration: none;
}
.single-contact-info a:hover,
.single-contact-info a:focus-visible {
  color: var(--brand-ink);
}
.single-contact-info .ic {
  color: var(--brand-ink);
}
.single-contact-info h6 {
  margin: 0;
  font-size: var(--fs-200);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-300);
}

.map-fallback-mark {
  color: var(--brand-ink);
}

/* The social link is an icon-only control like every other one, so it is the
   same disc. It was a 4px square, which made one button on the site the odd
   shape out for no reason anybody could have named. */
.footer-social a {
  border-radius: var(--r-full);
}

/* ===========================================================================
   THE BOTTOM TAB BAR
   ---------------------------------------------------------------------------
   Phones only, below 768. This is the navigation, not a shortcut to it.

   It replaces a hamburger and a 280px slide-in drawer. The drawer was not
   ugly; it was expensive. Every destination on the site sat two taps deep
   behind a glyph that named none of them, at the top of the screen, which on a
   390x844 phone is the corner a thumb reaches last. The two questions this
   whole catalogue exists to answer, buy or rent, were both inside it.

   Five cells, 78px each at 390. 56px tall plus whatever the device reserves
   for its home indicator, which is what env(safe-area-inset-bottom) is for:
   without it the labels sit under the iOS gesture bar on every phone made
   since 2017.

   The active tab is brand green and also a filled glyph where the glyph can
   fill, because a tab told apart only by hue is a tab a colourblind visitor
   cannot find. There is no top-edge indicator bar: the bar has a hairline of
   its own and a second horizontal line 2px from it reads as a rendering fault.
   ========================================================================= */

.tabbar {
  display: none;
}

@media (max-width: 767.98px) {
  .tabbar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    /* Above the page and above the compare tray's shadow, below the lightbox
       and below the header. Nothing else is docked down here now: the floating
       WhatsApp bubble is hidden at this width and the back-to-top disc that
       used to sit under it is deleted. */
    z-index: 1040;
    display: block;
    height: var(--tabbar-total);
    padding-bottom: env(safe-area-inset-bottom, 0px);
    background: #fff;
    border-top: 1px solid var(--rule);
  }

  .tabbar-list {
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    height: var(--tabbar-h);
    margin: 0;
    padding: 0;
    list-style: none;
  }

  .tabbar-item {
    min-width: 0;
  }

  .tabbar-link {
    height: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 3px;
    color: var(--paragraph-color);
    text-decoration: none;
    /* The whole cell is the target: 78 by 56 at 390px, which is well past the
       44px floor in both directions. */
    -webkit-tap-highlight-color: transparent;
    transition: color var(--dur) var(--ease-out);
  }
  .tabbar-link:hover,
  .tabbar-link:focus-visible {
    color: var(--brand-ink);
    text-decoration: none;
  }
  .tabbar-link.is-active {
    color: var(--brand-ink);
  }

  .tabbar-glyph {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    height: 22px;
  }

  /* 10px, and it is a word rather than a decoration, so it keeps the ink of
     the glyph above it instead of fading to a lighter grey. */
  .tabbar-label {
    font-size: 10px;
    line-height: 12px;
    font-weight: var(--fw-semibold);
    letter-spacing: 0.01em;
    white-space: nowrap;
  }
  .tabbar-link.is-active .tabbar-label {
    font-weight: var(--fw-bold);
  }

  /* The count rides the heart rather than sitting beside it, because a cell
     78px wide cannot hold a glyph, a number and a word in a row. */
  .tabbar-count {
    position: absolute;
    top: -5px;
    left: 12px;
    min-width: 16px;
    height: 16px;
    padding: 0 4px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--r-full);
    background: var(--brand-fill);
    color: var(--brand-on-fill);
    font-size: 10px;
    font-weight: var(--fw-bold);
    line-height: 1;
    font-variant-numeric: tabular-nums;
  }

  /* The page has to end above the bar, or its last line is under it. Every
     other docked element measures from the same variable, so nothing can
     overlap the bar or each other. */
  body {
    padding-bottom: var(--tabbar-total);
  }
  .compare-tray {
    bottom: var(--tabbar-total);
  }
  body.has-compare-tray {
    padding-bottom: calc(var(--tabbar-total) + var(--compare-tray-h, 84px));
  }
}

/* ===========================================================================
   THE SECONDARY PAGES
   ---------------------------------------------------------------------------
   /contact, /about, /our-agents, /careers, /saved-properties, /compare and the
   404. These six were never touched when the home, listing and detail pages
   were rebuilt, so they still wore the purchased theme: Bootstrap rows with
   `pd-top-100`, grey #f3f3f3 fields, `btn btn-yellow`, Font Awesome glyphs
   from a webfont this site no longer loads, and a section rhythm decided per
   page. The owner's word for /contact was that it looked like someone from
   high school had made it, and he was describing all six.

   Nothing below invents a value. Every number is a token, every card is the
   listing card's frame, every control is the one in the block above, and the
   spacing is the same --sp-section the rails already use.
   ========================================================================= */

/* ── Page rhythm ───────────────────────────────────────────────────────────
   One section spacing for the whole site. Two sections back to back share one
   gap rather than stacking 64 on 64, which is the rule the rails already
   follow; and the first section after the title band sits closer, because the
   band and the block under it are one page, not two.
   ------------------------------------------------------------------------ */
.page-section {
  padding-top: var(--sp-section);
  padding-bottom: var(--sp-section);
}
.page-section + .page-section {
  padding-top: 0;
}
.page-head + .page-section {
  padding-top: var(--sp-6);
}

/* The opening paragraph of a page. 62ch, so the line does not run the full
   1,140 and force the eye to hunt for the next one. */
.page-lede {
  /* The lede owns the gap to whatever follows it, so a grid, a licence line
     and a two-column form do not each have to remember one. */
  margin: 0 0 var(--sp-6);
  max-width: 62ch;
  font-size: var(--fs-400);
  line-height: var(--lh-300);
  color: var(--paragraph-color);
}

/* A section heading with something on the other end of the row: a count and
   two send buttons on /saved, a link to the agents on /about, Clear all on
   /compare. The rail head does this for rails; this does it everywhere else. */
.section-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--sp-4);
  margin-bottom: var(--sp-5);
}
.section-head-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
}

/* A quiet line of fact under a heading or beside a list: where a shortlist is
   stored, why a row shows a dash, that a request failed. Never an alert. */
.state-line {
  margin: 0 0 var(--sp-5);
  max-width: 62ch;
  font-size: var(--fs-200);
  line-height: var(--lh-300);
  color: var(--paragraph-color);
}
.state-line a { color: var(--brand-ink); }

/* ── The panel ─────────────────────────────────────────────────────────────
   The card frame, without a photograph: the office block on /contact, both
   forms, the roles list on /careers. Same hairline and same 20px radius as a
   listing card, and no shadow, because the hero search card is still the only
   object on this site allowed one.
   ------------------------------------------------------------------------ */
.panel {
  background: #fff;
  border: 1px solid var(--rule);
  border-radius: var(--r-xl);
  padding: var(--card-pad);
}
/* A listing card's 20px padding is right for a card 555px wide carrying a
   photograph and six short lines. A panel is 475px of nothing but content, and
   at 20px the content sits on the hairline. */
@media (min-width: 768px) {
  .panel { padding: var(--sp-6); }
}
/* "The office", "Send us a note", "Send your CV". These head a panel, not a
   section of the page, which is a real distinction and now a real step: 22
   against the section title's 28, one rung down, rather than the 18 it was. */
.panel-title {
  margin: 0 0 var(--sp-5);
  padding-bottom: var(--sp-4);
  border-bottom: 1px solid var(--rule);
  font-family: var(--t-panel-title-font);
  font-size: var(--t-panel-title-size);
  line-height: var(--t-panel-title-lh);
  font-weight: var(--t-panel-title-weight);
  letter-spacing: var(--t-panel-title-track);
  color: var(--heading-color);
}

/* ── Two columns ───────────────────────────────────────────────────────────
   /contact and /careers. The reference column on the left, the thing you fill
   in on the right, and on a phone the form comes up first: somebody who has
   scrolled past a header carrying a handset and a WhatsApp mark has already
   declined both of them.
   ------------------------------------------------------------------------ */
.split-cols {
  display: flex;
  flex-direction: column;
  gap: var(--sp-5);
}
.split-main { order: -1; }

@media (min-width: 992px) {
  .split-cols {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: var(--sp-6);
    align-items: start;
  }
  .split-main { order: 0; }
}


/* ── The office block ──────────────────────────────────────────────────────
   Four facts, each a glyph and the value itself. What was here printed the
   number, the address and the email in the middle of the page AND again as
   three captioned boxes at the bottom, under the captions "Call Us", "Email
   Us" and "Visit Us", which is the value saying itself twice and then
   describing itself.
   ------------------------------------------------------------------------ */
.office-rows {
  list-style: none;
  margin: 0;
  padding: 0;
}
.office-rows > li {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: start;
  gap: var(--sp-3);
  padding: var(--sp-3) 0;
  font-size: var(--fs-300);
  line-height: var(--lh-300);
  color: var(--heading-color);
}
.office-rows > li + li {
  border-top: 1px solid var(--rule);
}
.office-rows > li > .ic {
  color: var(--brand-ink);
  /* Optically centred on the first line of the value beside it rather than on
     the row, so a four-line hours block does not drag the glyph to its
     middle. */
  margin-top: 3px;
}
.office-rows a {
  color: var(--heading-color);
  text-decoration: none;
  overflow-wrap: anywhere;
}
.office-rows a:hover,
.office-rows a:focus-visible {
  color: var(--brand-ink);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.office-hours {
  list-style: none;
  margin: 0;
  padding: 0;
  font-size: var(--fs-200);
}
.office-hours li {
  display: flex;
  justify-content: space-between;
  gap: var(--sp-4);
  max-width: 260px;
}
.office-hours-time {
  color: var(--paragraph-color);
  font-variant-numeric: tabular-nums;
}

.office-actions {
  display: grid;
  gap: var(--sp-3);
  margin-top: var(--sp-5);
}
@media (min-width: 480px) {
  .office-actions { grid-template-columns: 1fr 1fr; }
}

/* ── The map ───────────────────────────────────────────────────────────────
   It was a borderless rectangle at zoom 18, which in a residential block of
   Saar renders as a pale grey field carrying two road numbers, and it read as
   a failed request. Full opacity, one hairline, the panel's radius one step
   down, and a zoom that shows the neighbourhood.
   ------------------------------------------------------------------------ */
.office-map {
  position: relative;
  height: 320px;
  margin-top: var(--sp-5);
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
  overflow: hidden;
  background: var(--brand-tint);
}
/* The Maps canvas and the keyless fallback both sit in here, and the frame is
   this element's job, so neither of them draws a second one inside it. */
.office-map > * {
  height: 100%;
  border: 0;
  border-radius: 0;
}

/* ── The enquiry's subject ─────────────────────────────────────────────────
   Audit L2. When the form is opened from a listing it says which one, so the
   visitor knows the agent will already have it.
   ------------------------------------------------------------------------ */
.enquiry-about {
  margin-bottom: var(--sp-5);
  padding: var(--sp-4);
  background: var(--brand-tint);
  border: 1px solid var(--brand-line);
  border-radius: var(--r-md);
}
.enquiry-about-label {
  margin: 0 0 var(--sp-1);
  font-size: var(--fs-100);
  font-weight: var(--fw-semibold);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--brand-ink);
}
.enquiry-about-name {
  margin: 0;
  font-weight: var(--fw-semibold);
  color: var(--heading-color);
}
.enquiry-about-meta {
  margin: 0;
  font-size: var(--fs-200);
  color: var(--heading-color);
}

/* The outcome of a submission, and the button that caused it. Colour is never
   the only signal: both states also change the wording from a description to
   an instruction. */
.form-result {
  margin: var(--sp-4) 0 0;
  font-size: var(--fs-200);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-300);
}
.form-result.is-ok { color: var(--state-success); }
.form-result.is-bad { color: var(--state-danger); }
.form-submit { margin-top: var(--sp-5); }

/* ── The licence ───────────────────────────────────────────────────────────
   The one claim on /about that can be checked by a stranger, printed as a
   record rather than as a sentence.
   ------------------------------------------------------------------------ */
.licence-line {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--sp-2) var(--sp-4);
  margin: 0;
  padding-top: var(--sp-5);
  border-top: 1px solid var(--rule);
}
.licence-label {
  font-size: var(--fs-100);
  font-weight: var(--fw-semibold);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--paragraph-color);
}
.licence-value {
  font-size: var(--fs-400);
  font-weight: var(--fw-semibold);
  color: var(--heading-color);
  font-variant-numeric: tabular-nums;
}
.licence-org {
  font-size: var(--fs-200);
  color: var(--paragraph-color);
}

/* ── The three values ──────────────────────────────────────────────────────
   They were three 320px cards with a shadow each, whose fixed height was set
   inline for a desktop row and then applied to a single phone column, where
   there is no row to equalise: 80px of dead space inside each card.

   A mark, a term, a line, on a rule. The glyphs are the only ones in
   icons.js that sit beside a word rather than instead of one: three terms in
   a row need something to count them off, and nobody can draw "integrity".
   ------------------------------------------------------------------------ */
.values-row {
  display: grid;
  gap: var(--sp-5);
  margin: var(--sp-6) 0 0;
}
.values-item {
  padding-top: var(--sp-4);
  border-top: 1px solid var(--rule);
}
.values-mark {
  color: var(--brand-ink);
  margin-bottom: var(--sp-3);
}
.values-term {
  margin: 0 0 var(--sp-2);
  font-size: var(--fs-400);
  line-height: var(--lh-400);
  font-weight: var(--fw-semibold);
  color: var(--heading-color);
}
.values-def {
  margin: 0;
  max-width: 62ch;
  font-size: var(--fs-200);
  line-height: var(--lh-300);
  color: var(--paragraph-color);
}
@media (min-width: 768px) {
  .values-row { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--sp-6); }
}

/* A term and its line, stacked: the parts of the office on /careers. Same
   grammar as the values, without the marks, because a role is a noun and the
   glyph would be decoration. */
.fact-list { margin: 0; }
.fact-item + .fact-item {
  margin-top: var(--sp-4);
  padding-top: var(--sp-4);
  border-top: 1px solid var(--rule);
}
.fact-term {
  margin: 0 0 var(--sp-1);
  font-size: var(--fs-300);
  font-weight: var(--fw-semibold);
  color: var(--heading-color);
}
.fact-def {
  margin: 0;
  max-width: 62ch;
  font-size: var(--fs-200);
  line-height: var(--lh-300);
  color: var(--paragraph-color);
}

/* ── The people ────────────────────────────────────────────────────────────
   The team was four Bootstrap rows of 120px circular avatars with three
   coloured Font Awesome glyphs under each name, drawn from a webfont this
   site no longer loads: the only way to contact anybody on the page was three
   invisible squares.

   An agent card is the listing card's frame on purpose. This office sells its
   people rather than reselling somebody else's stock, so a person and a
   property are peers here and the eye should not have to learn two shapes.
   The photograph is square rather than a circle: a circle crops a headshot to
   the middle of a face and throws the shoulders away, which is why every one
   of them read as a passport photo pasted onto a page.
   ------------------------------------------------------------------------ */
.agent-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--grid-gap);
}
@media (min-width: 576px) {
  .agent-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (min-width: 992px) {
  .agent-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

.agent-card {
  display: flex;
  flex-direction: column;
  height: 100%;
  background: #fff;
  border: 1px solid var(--rule);
  border-radius: var(--r-xl);
  overflow: hidden;
  transition:
    border-color var(--dur) var(--ease-out),
    box-shadow var(--dur) var(--ease-out),
    transform var(--dur) var(--ease-out);
}
.agent-card:hover {
  /* 2px is a lift. 5px is a jump, and fifteen of them in a grid is a page
     that twitches under the mouse. */
  transform: translateY(-2px);
  border-color: var(--brand-line);
  box-shadow: var(--shadow-lift);
}
@media (prefers-reduced-motion: reduce) {
  .agent-card { transition: none; }
  .agent-card:hover { transform: none; }
}

.agent-face {
  position: relative;
  aspect-ratio: 1 / 1;
  background: var(--brand-tint);
  overflow: hidden;
}
/* Absolutely positioned, not a flex child with height:100%. As a flex item the
   portrait resolved its own height against a parent whose height came from
   aspect-ratio, and Chrome settled that circle differently per image: one card
   in a row of four came out 90px taller than its neighbours and the whole grid
   went ragged. Longhand offsets rather than `inset`, so nothing here depends
   on a shorthand. */
.agent-face img {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Heads sit in the top third of a portrait, so a centred crop takes the
     chin and the shirt and cuts the forehead. */
  object-position: center 22%;
}
.agent-initials {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--heading-font);
  font-size: var(--fs-600);
  font-weight: var(--fw-semibold);
  color: var(--brand-ink);
  letter-spacing: 0.04em;
}

.agent-body {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  padding: var(--card-pad);
}
.agent-name {
  margin: 0;
  font-family: var(--t-card-title-font);
  font-size: var(--t-card-title-size);
  line-height: var(--t-card-title-lh);
  font-weight: var(--t-card-title-weight);
  letter-spacing: var(--t-card-title-track);
  color: var(--heading-color);
}
.agent-role {
  margin: var(--sp-1) 0 0;
  font-size: var(--fs-200);
  line-height: var(--lh-200);
  color: var(--paragraph-color);
}
/* What this person is handling right now, which is the reason to ring one of
   them rather than the switchboard. It comes from X-Total-Count, so it is the
   real number and not a page of results counted client side. */
.agent-count {
  margin: var(--sp-2) 0 0;
  font-size: var(--fs-100);
  font-weight: var(--fw-semibold);
  letter-spacing: 0.04em;
  color: var(--brand-ink);
  font-variant-numeric: tabular-nums;
}
.agent-actions {
  display: flex;
  gap: var(--sp-2);
  margin-top: auto;
  padding-top: var(--sp-4);
}

/* A card in the shape of the card that is coming, so the grid does not jump
   when seven of them land. */
.agent-card-skeleton .agent-face { border-radius: 0; }
.agent-card-skeleton .skeleton-text { margin-bottom: var(--sp-2); }

/* ── Nothing here yet ──────────────────────────────────────────────────────
   The saved page and the comparison. What was on both was a Font Awesome
   glyph that renders as a blank square, a heading, two paragraphs and a
   button: five pieces of furniture on a screen whose entire message is that
   there is nothing on it. One mark, one line, one way out.
   ------------------------------------------------------------------------ */
.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-4);
  padding: var(--sp-9) 0;
  text-align: center;
}
.empty-state-mark { color: var(--brand-ink); }
.empty-state-title {
  margin: 0;
  font-family: var(--heading-font);
  font-size: var(--fs-600);
  line-height: var(--lh-600);
  font-weight: var(--fw-semibold);
  color: var(--heading-color);
}
/* The wrapper already carries the space that clears the header, so the state
   inside it does not add a second 80px of its own. */
.detail-state .empty-state { padding: 0; }
.empty-state-line {
  margin: 0;
  max-width: 46ch;
  font-size: var(--fs-300);
  line-height: var(--lh-300);
  color: var(--paragraph-color);
}
