@charset "UTF-8";
/* =====================================================================
   TRNCHub — Homepage design system  (v2)
   =====================================================================

   WHAT THIS FILE IS

   The homepage, the site header, the mobile tab bar and the footer.
   Nothing else. Every other page keeps its rules in trnchub-new.css,
   which loads first; this sheet loads after it and OWNS the components
   listed above outright — their old definitions were deleted from that
   file rather than overridden here, so each component has exactly one
   rule set and there is nothing to fight with.

   Homepage components are prefixed `thv-` and appear nowhere else on
   the site, so they cannot collide with anything.

   THE VARIABLE CONTRACT

   Admin -> Appearance writes `--th-primary`, `--th-text`, `--th-border`
   and eighteen others into a :root block printed AFTER this sheet.
   Every colour below is derived from those names rather than restating
   a hex value, so a colour picked in the admin still flows through the
   whole homepage. Renaming them breaks that panel.

   PERFORMANCE RULES THAT ARE NOT NEGOTIABLE

   - Animation is `transform` and `opacity` only. Nothing here animates
     width, height, top, left, margin, padding or box-shadow.
   - `backdrop-filter` appears four times: the scrolled header, the tab
     bar, the hero search and the mobile drawer. Every one sits over
     moving content and every one has an @supports fallback. A card
     never gets one — fifty cards would be fifty composited layers.
   - Reveal animation reuses the IntersectionObserver already in
     trnchub.js (`.th-reveal` -> `.is-visible`). No new JavaScript, no
     scroll listeners, nothing attached per card.

   CONTENTS

     01  VARIABLES
     02  RESET
     03  TYPOGRAPHY
     04  LAYOUT
     05  HEADER
     06  HERO
     07  SEARCH
     08  DISCOVERY
     09  CATEGORIES
     10  CARDS
     11  PROPERTIES
     12  JOBS
     13  NEWS
     14  BUSINESS
     15  CTA
     16  FOOTER
     17  MOBILE NAVIGATION
     18  PAGE SHELL          — every page that is not the homepage
     19  TOOLBAR & SORT
     20  CHIPS, RAILS & TABS
     21  FILTERS
     22  RESULTS LAYOUT
     23  PAGINATION & EMPTY STATE
     24  DETAIL PAGES
     25  ARTICLE
     26  BUSINESS
     27  ACCOUNT
     28  ANIMATIONS
     29  UTILITIES
     30  RESPONSIVE
   ===================================================================== */


/* =====================================================================
   01  VARIABLES
   ===================================================================== */

:root {
  /* --- Colour ------------------------------------------------------
     Navy is the type colour and the dark-panel colour. Blue is for
     action only: links, the search button, the active tab. A price is
     never blue — it would compete with every link on the page. */
  --thv-navy:      #0E1D36;
  --thv-navy-deep: #091528;
  --thv-navy-soft: #1B2E4E;

  --thv-blue:      var(--th-primary);
  --thv-blue-dark: var(--th-primary-hover);
  --thv-blue-soft: var(--th-primary-soft);

  --thv-ink:       var(--th-text);
  --thv-ink-2:     var(--th-text-muted);
  --thv-ink-3:     var(--th-text-subtle);

  --thv-page:      #F7F8FA;
  --thv-surface:   var(--th-surface);
  --thv-tint:      #F1F3F7;
  --thv-line:      var(--th-border);
  --thv-line-soft: #EEF0F4;

  /* --- Shape -------------------------------------------------------
     14px cards, 9px buttons, per the brief. */
  --thv-r-card: 14px;
  --thv-r-btn:  9px;
  --thv-r-tile: 12px;
  --thv-r-pill: 999px;

  /* --- Elevation ---------------------------------------------------
     Almost everything separates with a 1px border. Shadows are for
     things that genuinely float, and there are only three. */
  --thv-sh-1: 0 1px 2px rgba(14, 29, 54, .04), 0 1px 3px rgba(14, 29, 54, .05);
  --thv-sh-2: 0 2px 4px rgba(14, 29, 54, .04), 0 8px 20px rgba(14, 29, 54, .07);
  --thv-sh-3: 0 8px 18px rgba(14, 29, 54, .06), 0 24px 48px rgba(14, 29, 54, .12);

  /* --- Rhythm ------------------------------------------------------ */
  --thv-gutter: 16px;
  --thv-maxw:   1200px;
  --thv-gap:    12px;
  --thv-sect-y: 40px;

  /* --- Motion ------------------------------------------------------
     --th-fast / --th-normal are DURATIONS ONLY; Appearance writes bare
     values like `200ms` into them, so every transition below supplies
     its own easing curve. */
  --thv-ease:     cubic-bezier(.4, 0, .2, 1);
  --thv-ease-out: cubic-bezier(.16, 1, .3, 1);
  --thv-t-fast:   var(--th-fast, .15s);
  --thv-t-base:   var(--th-normal, .22s);

  /* --- Chrome heights ----------------------------------------------
     Published as variables because several things need to clear them.
     Nothing hard-codes a guess. */
  --th-header-h:         64px;
  --thv-header-scrolled: 56px;
  --th-tabbar-h:         66px;
  --th-safe-b:           env(safe-area-inset-bottom, 0px);
  --th-z-tabbar:         1020;

  /* -----------------------------------------------------------------
     REPAIRS TO trnchub-new.css
     -----------------------------------------------------------------
     That stylesheet reads 24 custom properties it never defines. 239
     declarations across it therefore resolve to `unset` and are thrown
     away by the browser — including almost every gap, radius, shadow
     and brand colour in its mobile layer, which is why the site looked
     unfinished on a phone.

     Defining them here repairs all 239 without touching that file or
     any markup. The values are the ones the surrounding rules were
     plainly written against; where a rule already supplied an inline
     fallback (`--th-hero-overlay`, `--th-stack-gap`, `--th-cluster-gap`)
     nothing is needed and nothing is added.

     These are for the REST OF THE SITE. Nothing in this file's own
     rules reads them — the homepage uses the `--thv-` set above. When
     the remaining pages are rebuilt, this block is what gets deleted.
     ----------------------------------------------------------------- */
  --th-blue:         var(--th-primary);
  --th-blue-dark:    var(--th-primary-hover);
  --th-blue-darker:  var(--th-primary-active, #1E40AF);
  --th-blue-soft:    var(--th-primary-soft);
  --th-blue-border:  var(--th-primary-border);
  --th-navy:         var(--thv-navy);
  --th-muted:        var(--th-text-muted);
  --th-subtle:       var(--th-text-subtle);
  --th-success:      var(--th-secondary);
  --th-success-soft: var(--th-secondary-soft, #ECFDF5);
  --th-warning:      var(--th-accent);
  --th-warning-soft: var(--th-accent-soft, #FFFBEB);
  --th-surface-sunk: var(--th-bg-subtle, #F1F5F9);
  --th-shadow:       var(--th-shadow-sm);
  --th-transition:   var(--th-fast, .15s) cubic-bezier(.4, 0, .2, 1);
  --th-gap-1: 4px;
  --th-gap-2: 8px;
  --th-gap-3: 12px;
  --th-gap-4: 16px;
  --th-gap-5: 24px;
  --th-gap-6: 32px;
}

@media (min-width: 768px) {
  :root {
    --thv-gutter:  24px;
    --thv-gap:     16px;
    --thv-sect-y:  56px;
    --th-header-h: 72px;
    --thv-header-scrolled: 64px;
  }
}

@media (min-width: 1200px) {
  :root { --thv-sect-y: 72px; }
}


/* =====================================================================
   02  RESET
   =====================================================================
   Scoped to what the homepage and the shared chrome actually need.
   Bootstrap's reset already ran; this corrects it rather than
   repeating it.

   `overflow-x: clip` on the root, never `hidden` — `hidden` on an
   ancestor silently kills every `position: sticky` descendant, which
   would take the header with it.
   ===================================================================== */

html { overflow-x: clip; }

body.th-page {
  background: var(--thv-page);
  color: var(--thv-ink);
  overflow-x: clip;
  -webkit-text-size-adjust: 100%;
}

/* The universal overflow guard the brief asks for, applied to the
   homepage subtree and the chrome rather than to `*`, so it cannot
   reach into a third-party embed. */
.th-m *,
.th-m *::before,
.th-m *::after,
.th-header *,
.th-footer *,
.th-tabbar * { box-sizing: border-box; }

/* EVERY RESET BELOW IS WRAPPED IN :where().
   -------------------------------------------------------------------
   :where() contributes zero specificity, so these are true defaults
   that any component rule overrides by simply existing.

   Without it a blanket reset outranks the components it is meant to
   support: `.th-m a { color: inherit }` is (0,1,1) and quietly beats
   `.thv-btn--solid { color: navy }` at (0,1,0) — which painted white
   text on the white CTA button and turned every "See all" link from
   blue to near-black. `.th-m *` beat `.thv-search { max-width: 720px }`
   the same way and stretched the hero search across the full 1200px
   container. All three were visible on screen before this was fixed. */

:where(.th-m, .th-m *) { max-width: 100%; min-width: 0; }

/* Images: one rule for the whole homepage. */
:where(.th-m img) {
  display: block;
  max-width: 100%;
  height: auto;
}

/* Long unbroken strings — a URL in a headline, a long Turkish place
   name in a narrow card — wrap instead of pushing the layout sideways. */
:where(.th-m h1, .th-m h2, .th-m h3,
       .th-m p, .th-m span, .th-m a) { overflow-wrap: break-word; }

:where(.th-m ul) { list-style: none; margin: 0; padding: 0; }
:where(.th-m a)  { text-decoration: none; color: inherit; }

/* Focus is visible everywhere, and it is never colour alone. */
.th-m a:focus-visible,
.th-m button:focus-visible,
.th-m input:focus-visible,
.th-m select:focus-visible,
.th-header a:focus-visible,
.th-header button:focus-visible,
.th-footer a:focus-visible,
.th-footer button:focus-visible,
.th-tabbar a:focus-visible {
  outline: 2px solid var(--thv-blue);
  outline-offset: 2px;
  border-radius: 4px;
}


/* =====================================================================
   03  TYPOGRAPHY
   =====================================================================
   Four sizes and three weights across the whole page. Hierarchy comes
   from size and weight, not from colour — a heading is never blue.
   ===================================================================== */

.th-m {
  font-family: var(--th-font);
  font-size: 15px;
  line-height: 1.55;
  color: var(--thv-ink);
}

.thv-h1 {
  margin: 0;
  font-size: clamp(26px, 6.6vw, 30px);
  font-weight: 800;
  line-height: 1.14;
  letter-spacing: -.028em;
  text-wrap: balance;
}

.thv-h2 {
  margin: 0;
  font-size: clamp(19px, 4.8vw, 22px);
  font-weight: 800;
  line-height: 1.2;
  letter-spacing: -.022em;
  color: var(--thv-navy);
  text-wrap: balance;
}

.thv-h3 {
  margin: 0;
  font-size: 16px;
  font-weight: 700;
  line-height: 1.3;
  letter-spacing: -.014em;
  color: var(--thv-navy);
}

.thv-lede {
  margin: 0;
  font-size: 14.5px;
  line-height: 1.55;
  color: var(--thv-ink-2);
}

.thv-eyebrow {
  display: block;
  margin: 0;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .14em;
  text-transform: uppercase;
}

/* Prices and counts use tabular figures so a column of them does not
   jitter from listing to listing. */
.thv-num { font-variant-numeric: tabular-nums; }


/* =====================================================================
   04  LAYOUT
   =====================================================================
   One container, one section wrapper, one rail.

   There are NO negative margins in this file. A negative margin inside
   a padded container is the classic cause of horizontal overflow at a
   width nobody tested; a rail reaches the screen edge by being a
   full-width block that supplies its own gutter instead.
   ===================================================================== */

.thv-container {
  width: 100%;
  max-width: calc(var(--thv-maxw) + var(--thv-gutter) * 2);
  margin-inline: auto;
  padding-inline: var(--thv-gutter);
}

.thv-section { padding-block: var(--thv-sect-y) 0; }
.thv-section:last-child { padding-bottom: var(--thv-sect-y); }

.thv-section--tint {
  padding-block: var(--thv-sect-y);
  background: var(--thv-tint);
  border-block: 1px solid var(--thv-line-soft);
}

/* Section heading + "See all" on one row. The link never wraps under
   the title; the title shrinks instead. */
.thv-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 16px;
}
.thv-head__text { min-width: 0; }
.thv-head__sub {
  margin: 4px 0 0;
  font-size: 13.5px;
  color: var(--thv-ink-2);
}
.thv-head__link {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  flex: none;
  min-height: 44px;
  padding-block: 6px;
  font-size: 13.5px;
  font-weight: 650;
  color: var(--thv-blue);
  white-space: nowrap;
  transition: transform var(--thv-t-fast) var(--thv-ease),
              color var(--thv-t-fast) var(--thv-ease);
}
.thv-head__link i { font-size: .8em; }
.thv-head__link:hover { color: var(--thv-blue-dark); transform: translateX(2px); }

/* --- Horizontal rail -------------------------------------------------
   Card widths are fixed px values, never percentages: a percentage is a
   share of the scroll container, so it means something different at
   every viewport. `scroll-padding-inline` puts back the leading gutter
   that scroll-snap otherwise eats on its initial snap. */
.thv-rail {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: max-content;
  gap: var(--thv-gap);
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x proximity;
  scroll-padding-inline: var(--thv-gutter);
  padding-inline: var(--thv-gutter);
  padding-block: 2px 6px;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  overscroll-behavior-x: contain;
}
.thv-rail::-webkit-scrollbar { display: none; }
.thv-rail > * { scroll-snap-align: start; }

/* A rail placed inside .thv-container is already inset by the container
   padding, so it must not add its own. */
.thv-container > .thv-rail { padding-inline: 0; scroll-padding-inline: 0; }

/* A rail that has room for its whole contents stops being a rail.

   Two things have to happen together, and missing either one is a bug
   you only see at one width:

     grid-auto-flow MUST go back to `row`. Left on `column` with an
     explicit template, the grid keeps inventing implicit columns off
     the right-hand edge of the screen.

     the element MUST adopt the container's box. A full-bleed rail that
     merely stops scrolling spans the whole 1440px window while every
     other section stops at 1200px. */
.thv-boxed {
  width: 100%;
  max-width: calc(var(--thv-maxw) + var(--thv-gutter) * 2);
  margin-inline: auto;
  padding-inline: var(--thv-gutter);
}

@media (min-width: 1200px) {
  .thv-rail--grid {
    grid-auto-flow: row;
    grid-template-columns: repeat(var(--thv-cols, 4), 1fr);
    grid-auto-columns: auto;
    overflow: visible;
    scroll-snap-type: none;
    width: 100%;
    max-width: calc(var(--thv-maxw) + var(--thv-gutter) * 2);
    margin-inline: auto;
  }
  .thv-rail--grid > * { width: auto !important; }
}


/* =====================================================================
   05  HEADER
   =====================================================================
   Owns `.th-header` and everything inside it. The old definitions were
   deleted from trnchub-new.css.

   Solid white by default. Glass only in two situations that genuinely
   sit over moving content: the scrolled header, and the overlay variant
   a page can ask for over a full-bleed hero.

   Compression on scroll is a HEIGHT change on one element, driven by
   the `.is-scrolled` class trnchub.js already toggles. Height is not a
   compositor-friendly property, but this is a single element changing
   once per scroll direction, not a per-frame animation — the
   alternative (transform on a fixed shell) puts the whole page content
   under a floating bar and breaks in-flow layout.
   ===================================================================== */

.th-header {
  position: sticky;
  top: 0;
  z-index: var(--th-z-header, 1030);
  background: var(--thv-surface);
  border-bottom: 1px solid transparent;
  transition: box-shadow var(--thv-t-base) var(--thv-ease),
              border-color var(--thv-t-base) var(--thv-ease),
              background-color var(--thv-t-base) var(--thv-ease);
}

.th-header.is-scrolled {
  border-bottom-color: var(--thv-line);
  box-shadow: var(--thv-sh-1);
  background: rgba(255, 255, 255, .86);
  backdrop-filter: blur(16px) saturate(180%);
  -webkit-backdrop-filter: blur(16px) saturate(180%);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .th-header.is-scrolled { background: #fff; }
}

.th-header__inner {
  display: flex;
  align-items: center;
  gap: 16px;
  height: var(--th-header-h);
  transition: height 200ms var(--thv-ease);
}
.th-header.is-scrolled .th-header__inner { height: var(--thv-header-scrolled); }

/* --- Overlay variant --------------------------------------------------
   Transparent over a photographic hero until the page scrolls. No page
   currently asks for it, but navbar.php can still emit it. */
.th-header--overlay {
  position: fixed;
  inset: 0 0 auto 0;
  background: transparent;
  border-bottom-color: transparent;
}
.th-header--overlay:not(.is-scrolled) .th-nav__link,
.th-header--overlay:not(.is-scrolled) .th-logo,
.th-header--overlay:not(.is-scrolled) .th-icon-btn { color: #fff; }
.th-header--overlay:not(.is-scrolled) .th-icon-btn:hover { background: rgba(255, 255, 255, .16); }
.th-header--overlay.is-scrolled { position: sticky; }

/* --- Logo ------------------------------------------------------------ */
.th-logo {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  flex: none;
  font-size: 18px;
  font-weight: 800;
  letter-spacing: -.03em;
  color: var(--thv-navy);
  transition: opacity var(--thv-t-fast) var(--thv-ease);
}
.th-logo:hover { opacity: .82; color: var(--thv-navy); }

.th-logo__mark {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  flex: none;
  border-radius: 10px;
  background: var(--thv-blue);
  color: #fff;
  font-size: 15px;
  font-weight: 800;
  letter-spacing: -.02em;
}

/* A supplied logo file. Height is fixed, width follows the aspect
   ratio, so any reasonable proportion drops in without distortion. */
.th-logo__img {
  height: 30px;
  width: auto;
  max-width: 168px;
  object-fit: contain;
  display: block;
}
/* A 1:1 emblem at 30px reads as a favicon, so it gets a little more
   room and the site name is set beside it. */
.th-logo__img--mark {
  height: 34px;
  width: 34px;
  max-width: none;
  border-radius: 9px;
  flex: none;
}
.th-logo__name {
  font-size: 18px;
  font-weight: 800;
  letter-spacing: -.03em;
  white-space: nowrap;
}

/* --- Desktop navigation ---------------------------------------------- */
.th-nav {
  display: none;
  align-items: center;
  gap: 2px;
  margin: 0;
  padding: 0;
  list-style: none;
  min-width: 0;
}
.th-nav__item { position: relative; }
.th-nav__link {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 9px 12px;
  border-radius: var(--thv-r-btn);
  color: var(--thv-ink-2);
  font-size: 14.5px;
  font-weight: 600;
  white-space: nowrap;
  transition: color var(--thv-t-fast) var(--thv-ease),
              background-color var(--thv-t-fast) var(--thv-ease);
}
.th-nav__link:hover { color: var(--thv-navy); background: var(--thv-tint); }
.th-nav__link.active,
.th-nav__link.is-active { color: var(--thv-blue); }
.th-nav__link .bi-chevron-down {
  font-size: 10px;
  opacity: .55;
  transition: transform var(--thv-t-fast) var(--thv-ease);
}
.th-nav__item:hover .bi-chevron-down { transform: rotate(180deg); }

/* --- Mega menu -------------------------------------------------------
   Anchored to the left of its nav item, not centred on it: half of a
   720px panel is wider than the distance from the logo to the first
   item, so centring pushed it off the left edge. */
.th-mega {
  position: absolute;
  z-index: var(--th-z-dropdown, 1040);
  top: calc(100% + 6px);
  left: 0;
  width: min(720px, calc(100vw - 48px));
  padding: 20px;
  border: 1px solid var(--thv-line);
  border-radius: 16px;
  background: var(--thv-surface);
  box-shadow: var(--thv-sh-3);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-6px);
  transition: opacity var(--thv-t-base) var(--thv-ease),
              transform var(--thv-t-base) var(--thv-ease-out),
              visibility var(--thv-t-base);
}
.th-nav__item:last-child .th-mega { left: auto; right: 0; }
.th-mega--sm { width: min(400px, calc(100vw - 48px)); }

/* An invisible bridge across the gap between link and panel. Without it
   the menu closes the instant the pointer leaves the link. */
.th-mega::before {
  content: "";
  position: absolute;
  top: -10px;
  left: 0;
  right: 0;
  height: 10px;
}
@media (hover: hover) and (min-width: 992px) {
  .th-nav__item:hover > .th-mega,
  .th-nav__item:focus-within > .th-mega {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
  }
}
.th-mega.is-open { opacity: 1; visibility: visible; transform: translateY(0); }

.th-mega__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
  gap: 20px;
}
.th-mega__heading {
  margin: 0 0 8px;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .1em;
  color: var(--thv-ink-3);
}
.th-mega__link {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 7px 8px;
  border-radius: 8px;
  color: var(--thv-ink);
  font-size: 14px;
  transition: background-color var(--thv-t-fast) var(--thv-ease),
              color var(--thv-t-fast) var(--thv-ease);
}
.th-mega__link:hover { background: var(--thv-blue-soft); color: var(--thv-blue-dark); }
.th-mega__link i { color: var(--thv-ink-3); font-size: 15px; width: 18px; flex: none; }
.th-mega__link:hover i { color: var(--thv-blue); }
.th-mega__promo {
  padding: 18px;
  border-radius: 12px;
  background: var(--thv-navy);
  color: #fff;
}

/* --- Right-hand actions -----------------------------------------------
   Never wraps. A header that grows to two rows breaks every sticky
   offset on the page, because --th-header-h stops matching the real
   height. */
.th-header__actions {
  display: flex;
  align-items: center;
  gap: 2px;
  margin-left: auto;
  flex-wrap: nowrap;
  flex: none;
}
.th-header__inner > nav { min-width: 0; }

.th-icon-btn {
  position: relative;
  display: grid;
  place-items: center;
  width: 42px;
  height: 42px;
  flex: none;
  border: 0;
  border-radius: 10px;
  background: transparent;
  color: var(--thv-ink-2);
  font-size: 18px;
  cursor: pointer;
  transition: background-color var(--thv-t-fast) var(--thv-ease),
              color var(--thv-t-fast) var(--thv-ease);
}
.th-icon-btn:hover { background: var(--thv-tint); color: var(--thv-navy); }
.th-icon-btn--sm { width: 34px; height: 34px; font-size: 15px; }

.th-icon-btn__count {
  position: absolute;
  top: 5px;
  right: 5px;
  min-width: 16px;
  height: 16px;
  padding-inline: 4px;
  border-radius: var(--thv-r-pill);
  background: var(--th-danger, #EF4444);
  color: #fff;
  font-size: 10px;
  font-weight: 700;
  line-height: 16px;
  text-align: center;
}

.th-avatar {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  flex: none;
  border-radius: 50%;
  background: var(--thv-navy);
  color: #fff;
  font-size: 13px;
  font-weight: 700;
  overflow: hidden;
  cursor: pointer;
}
.th-avatar img { width: 100%; height: 100%; object-fit: cover; }
.th-avatar--lg { width: 52px; height: 52px; font-size: 16px; }
.th-avatar--sm { width: 28px; height: 28px; font-size: 11px; }

/* 360px: logo + three icons + burger must stay on one row. */
@media (max-width: 400px) {
  .th-icon-btn      { width: 38px; height: 38px; font-size: 17px; }
  .th-logo__name    { font-size: 16px; }
  .th-header__inner { gap: 8px; }
}

@media (min-width: 992px) {
  .th-nav { display: flex; }
  .th-header__actions { gap: 6px; }
}

/* =====================================================================
   06  HERO
   =====================================================================
   Roughly 340px on a phone and 420px on a desktop, measured. NOT a
   full-screen block: the point of the homepage is what is below it, and
   a hero that fills the viewport hides every listing on the page.

   Height comes from padding and content, never from `height: 100vh`.
   On a phone with a browser chrome that hides on scroll, a vh unit
   resizes the hero mid-scroll and reflows the whole page.
   ===================================================================== */

.thv-hero {
  position: relative;
  isolation: isolate;
  display: flex;
  align-items: center;
  min-height: 340px;
  padding-block: 32px 34px;
  background: var(--thv-navy);
  color: #fff;
  overflow: hidden;
}

.thv-hero__media {
  position: absolute;
  inset: 0;
  z-index: -2;
}
.thv-hero__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 40%;
}

/* Navy wash plus a vertical gradient. Two layers rather than one flat
   overlay: the flat version either washes the photograph out or leaves
   the headline unreadable over the bright part of the sky. */
.thv-hero__scrim {
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    linear-gradient(180deg, rgba(9, 21, 40, .58) 0%, rgba(9, 21, 40, .74) 62%, rgba(9, 21, 40, .88) 100%),
    linear-gradient(100deg, rgba(14, 29, 54, .82) 0%, rgba(14, 29, 54, .38) 100%);
}

/* No photograph configured: a navy field with a single soft highlight,
   not a placeholder image. A stretched 4:3 placeholder across a
   full-width banner looks broken in a way an empty field does not. */
.thv-hero--plain {
  background:
    radial-gradient(120% 90% at 78% 8%, rgba(37, 99, 235, .42) 0%, transparent 60%),
    linear-gradient(160deg, var(--thv-navy) 0%, var(--thv-navy-deep) 100%);
}

.thv-hero__inner { width: 100%; }

.thv-hero__eyebrow {
  color: rgba(255, 255, 255, .72);
  margin-bottom: 10px;
}

.thv-hero__title {
  color: #fff;
  margin-bottom: 10px;
  text-shadow: 0 1px 2px rgba(9, 21, 40, .28);
}
/* The second line is a block so the headline breaks where it was
   written to break, instead of wherever the viewport happens to fall. */
.thv-hero__title span { display: block; }

.thv-hero__lede {
  max-width: 46ch;
  margin-bottom: 18px;
  font-size: 14.5px;
  line-height: 1.5;
  color: rgba(255, 255, 255, .84);
}

.thv-hero__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 14px;
  margin: 14px 0 0;
  font-size: 12.5px;
  font-weight: 550;
  color: rgba(255, 255, 255, .72);
}
.thv-hero__meta b { color: #fff; font-weight: 700; }

@media (min-width: 768px) {
  .thv-hero { min-height: 400px; padding-block: 52px; }
  .thv-hero__title { font-size: clamp(34px, 4.4vw, 46px); }
  .thv-hero__lede  { font-size: 16.5px; margin-bottom: 24px; }
  .thv-hero__eyebrow { font-size: 12px; }
}

@media (min-width: 992px) {
  .thv-hero { min-height: 440px; }
  /* Centred on a desktop, left-aligned on a phone: centred text at
     360px wraps into a ragged triangle. */
  .thv-hero__inner  { text-align: center; }
  .thv-hero__lede   { margin-inline: auto; }
  .thv-hero__meta   { justify-content: center; }
}


/* =====================================================================
   07  SEARCH
   =====================================================================
   The primary action on the page, so it is the only glass surface in
   the hero and the only place a filled blue button appears above the
   fold.

   It posts to /search, which already accepts `q` and `in` — so the
   category selector needs no JavaScript to change where the form goes.
   ===================================================================== */

.thv-search {
  display: block;
  width: 100%;
  max-width: 720px;
  padding: 6px;
  border-radius: calc(var(--thv-r-card) + 4px);
  background: rgba(255, 255, 255, .16);
  border: 1px solid rgba(255, 255, 255, .26);
  backdrop-filter: blur(16px) saturate(150%);
  -webkit-backdrop-filter: blur(16px) saturate(150%);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .thv-search { background: rgba(9, 21, 40, .55); }
}
@media (min-width: 992px) {
  .thv-search { margin-inline: auto; }
}

.thv-search__row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 6px;
  padding: 4px 4px 4px 12px;
  border-radius: var(--thv-r-card);
  background: #fff;
}

.thv-search__icon {
  display: grid;
  place-items: center;
  font-size: 16px;
  color: var(--thv-ink-3);
}

.thv-search__input {
  width: 100%;
  min-width: 0;
  height: 46px;
  padding: 0 2px;
  border: 0;
  background: none;
  font-family: inherit;
  font-size: 15px;
  color: var(--thv-ink);
}
.thv-search__input::placeholder { color: var(--thv-ink-3); }
.thv-search__input:focus { outline: none; }
/* The clear "x" Safari and Chrome add to a search field collides with
   the submit button at 360px. */
.thv-search__input::-webkit-search-decoration,
.thv-search__input::-webkit-search-cancel-button { -webkit-appearance: none; }

/* The scope selector is desktop-only: at 360px it takes a third of the
   row and leaves no room to read what you typed. It still submits
   `in=all`, which is what /search defaults to anyway. */
.thv-search__scope { display: none; }

.thv-search__select {
  height: 46px;
  padding: 0 30px 0 12px;
  border: 0;
  border-left: 1px solid var(--thv-line);
  border-radius: 0;
  background: transparent;
  font-family: inherit;
  font-size: 14.5px;
  font-weight: 600;
  color: var(--thv-ink);
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='none' stroke='%236B7280' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round' d='M1 1.5 6 6.5l5-5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 10px center;
}
.thv-search__select:focus { outline: none; }

.thv-search__go {
  display: grid;
  place-items: center;
  width: 46px;
  height: 46px;
  flex: none;
  border: 0;
  border-radius: var(--thv-r-btn);
  background: var(--thv-blue);
  color: #fff;
  font-size: 17px;
  cursor: pointer;
  transition: background-color var(--thv-t-fast) var(--thv-ease),
              transform var(--thv-t-fast) var(--thv-ease);
}
.thv-search__go:hover  { background: var(--thv-blue-dark); }
.thv-search__go:active { transform: scale(.96); }

@media (min-width: 768px) {
  .thv-search__row   { grid-template-columns: auto minmax(0, 1fr) auto auto; padding-left: 16px; }
  .thv-search__scope { display: block; }
  .thv-search__input { height: 52px; font-size: 16px; }
  .thv-search__select{ height: 52px; }
  .thv-search__go    { width: 52px; height: 52px; }
}


/* =====================================================================
   08  DISCOVERY  —  Explore Northern Cyprus
   =====================================================================
   Towns as a rail of 4:5 portrait cards. Portrait rather than square:
   4:5 reads as a place, 1:1 reads as an avatar.

   The rail is the reason this section exists in its own block rather
   than reusing the category rail — a town card carries a photograph and
   needs a legible peek at the next one, so its width is set here and
   nowhere else.
   ===================================================================== */

.thv-town {
  position: relative;
  display: block;
  width: 148px;
  border-radius: var(--thv-r-card);
  overflow: hidden;
  background: var(--thv-navy);
  isolation: isolate;
  transition: transform var(--thv-t-base) var(--thv-ease);
}
.thv-town__img {
  width: 100%;
  height: 185px;
  object-fit: cover;
  transition: transform var(--th-slow, .4s) var(--thv-ease-out);
}
.thv-town__fallback {
  display: block;
  width: 100%;
  height: 185px;
  background: linear-gradient(150deg, var(--thv-navy-soft), var(--thv-blue));
}
.thv-town__scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(9, 21, 40, 0) 38%, rgba(9, 21, 40, .82) 100%);
}
.thv-town__body {
  position: absolute;
  inset: auto 0 0 0;
  padding: 10px 12px 12px;
  color: #fff;
}
.thv-town__name {
  display: block;
  font-size: 15px;
  font-weight: 700;
  letter-spacing: -.01em;
  line-height: 1.25;
}
.thv-town__meta {
  display: block;
  margin-top: 2px;
  font-size: 11.5px;
  font-weight: 500;
  color: rgba(255, 255, 255, .78);
}

@media (hover: hover) {
  .thv-town:hover { transform: translateY(-2px); }
  .thv-town:hover .thv-town__img { transform: scale(1.04); }
}

@media (min-width: 768px) {
  .thv-town { width: 176px; }
  .thv-town__img, .thv-town__fallback { height: 220px; }
}


/* =====================================================================
   09  CATEGORIES
   =====================================================================
   Two shapes:

   `.thv-pick`  the three big "what are you looking for" actions
   `.thv-type`  the compact property-type cards

   Neither is a pill. Both are cards with a 1px border, because the
   pill-shaped version of this row was what made the old page read as a
   template.
   ===================================================================== */

/* --- What are you looking for? --------------------------------------- */
.thv-picks {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--thv-gap);
}

.thv-pick {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 16px;
  border: 1px solid var(--thv-line);
  border-radius: var(--thv-r-card);
  background: var(--thv-surface);
  box-shadow: var(--thv-sh-1);
  transition: transform var(--thv-t-base) var(--thv-ease),
              border-color var(--thv-t-base) var(--thv-ease);
}
.thv-pick__icon {
  display: grid;
  place-items: center;
  width: 46px;
  height: 46px;
  flex: none;
  border-radius: 12px;
  font-size: 21px;
}
.thv-pick__icon--blue  { background: #EFF4FF; color: #2563EB; }
.thv-pick__icon--green { background: #ECFDF5; color: #059669; }
.thv-pick__icon--amber { background: #FFF7ED; color: #C2620A; }

.thv-pick__body { min-width: 0; }
.thv-pick__name {
  display: block;
  font-size: 15.5px;
  font-weight: 700;
  letter-spacing: -.012em;
  color: var(--thv-navy);
}
.thv-pick__sub {
  display: block;
  margin-top: 2px;
  font-size: 13px;
  color: var(--thv-ink-2);
}
.thv-pick__go {
  margin-left: auto;
  flex: none;
  font-size: 15px;
  color: var(--thv-ink-3);
  transition: transform var(--thv-t-base) var(--thv-ease),
              color var(--thv-t-base) var(--thv-ease);
}

@media (hover: hover) {
  .thv-pick:hover { transform: translateY(-2px); border-color: var(--th-primary-border, #BFDBFE); }
  .thv-pick:hover .thv-pick__go { transform: translateX(3px); color: var(--thv-blue); }
}

@media (min-width: 768px) {
  .thv-picks { grid-template-columns: repeat(3, 1fr); }
  .thv-pick {
    flex-direction: column;
    align-items: flex-start;
    gap: 14px;
    padding: 22px;
    min-height: 168px;
  }
  .thv-pick__icon { width: 50px; height: 50px; font-size: 23px; }
  .thv-pick__name { font-size: 17px; }
  .thv-pick__go   { margin: auto 0 0; }
}

/* --- Property types --------------------------------------------------- */
.thv-type {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 158px;
  padding: 12px;
  border: 1px solid var(--thv-line);
  border-radius: var(--thv-r-tile);
  background: var(--thv-surface);
  transition: transform var(--thv-t-base) var(--thv-ease),
              border-color var(--thv-t-base) var(--thv-ease);
}
.thv-type__icon {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  flex: none;
  border-radius: 9px;
  background: var(--thv-tint);
  color: var(--thv-navy);
  font-size: 16px;
}
.thv-type__body { min-width: 0; }
/* Two lines maximum: "Shared Room" wraps at this width and a third
   line would break the row alignment across the rail. */
.thv-type__name {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  font-size: 13.5px;
  font-weight: 650;
  line-height: 1.25;
  color: var(--thv-navy);
}
.thv-type__count {
  display: block;
  margin-top: 1px;
  font-size: 11.5px;
  color: var(--thv-ink-2);
}

@media (hover: hover) {
  .thv-type:hover { transform: translateY(-2px); border-color: var(--th-primary-border, #BFDBFE); }
  .thv-type:hover .thv-type__icon { background: var(--thv-blue-soft); color: var(--thv-blue); }
}

@media (min-width: 768px) {
  .thv-types {
    display: grid;
    grid-auto-flow: row;
    grid-template-columns: repeat(4, 1fr);
    grid-auto-columns: auto;
    gap: var(--thv-gap);
    overflow: visible;
    scroll-snap-type: none;
    width: 100%;
    max-width: calc(var(--thv-maxw) + var(--thv-gutter) * 2);
    margin-inline: auto;
  }
  .thv-type { width: auto; padding: 14px; }
  .thv-type__name { font-size: 14px; }
}


/* =====================================================================
   10  CARDS
   =====================================================================
   The primitives every listing card is built from. One border, one soft
   shadow, one 16:10 image. No glass, no gradient, no blur — a card is
   an opaque white surface and nothing else.
   ===================================================================== */

.thv-card {
  position: relative;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--thv-line);
  border-radius: var(--thv-r-card);
  background: var(--thv-surface);
  box-shadow: var(--thv-sh-1);
  overflow: hidden;
  transition: transform var(--thv-t-base) var(--thv-ease),
              box-shadow var(--thv-t-base) var(--thv-ease);
}

.thv-card__media {
  position: relative;
  display: block;
  aspect-ratio: 16 / 10;
  background: var(--thv-tint);
  overflow: hidden;
}
.thv-card__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--th-slow, .4s) var(--thv-ease-out);
}

.thv-card__body {
  display: block;
  padding: 12px 13px 14px;
}

/* Status badge, top-left of the image. */
.thv-badge {
  position: absolute;
  top: 10px;
  left: 10px;
  z-index: 2;
  padding: 4px 9px;
  border-radius: 7px;
  background: rgba(9, 21, 40, .82);
  color: #fff;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .03em;
  text-transform: uppercase;
}
.thv-badge--accent { background: var(--thv-blue); }
.thv-badge--muted  { background: rgba(9, 21, 40, .62); }

/* Favourite button, top-right of the image.

   It sits OUTSIDE the card's anchor with its own hit area, so a thumb
   aiming for the heart cannot open the listing. 40px is the tap target;
   the visible circle is 34px. */
.thv-fav {
  position: absolute;
  top: 6px;
  right: 6px;
  z-index: 3;
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 0;
  background: none;
  color: #fff;
  cursor: pointer;
}
/* Opaque, NOT glass. This button appears once per card, so a blur here
   would be one composited layer per listing — the exact thing the blur
   budget exists to prevent. A flat 52% navy is legible over every
   photograph in the library and costs nothing. */
.thv-fav::before {
  content: "";
  position: absolute;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: rgba(9, 21, 40, .52);
  transition: background-color var(--thv-t-fast) var(--thv-ease);
}
.thv-fav i {
  position: relative;
  font-size: 16px;
  transition: transform var(--thv-t-fast) var(--thv-ease);
}
.thv-fav:hover::before { background: rgba(9, 21, 40, .74); }
.thv-fav:active i { transform: scale(.88); }
.thv-fav.is-saved { color: #FF5A78; }

/* Two or more overlay buttons need a container; one does not. `.thv-fav`
   keeps its own absolute position for the single-button case, and goes
   static when it sits inside the stack. */
.thv-card__acts {
  position: absolute;
  top: 6px;
  right: 6px;
  z-index: 3;
  display: flex;
  align-items: center;
  gap: 2px;
}
.thv-card__acts .thv-fav { position: static; }

/* Comparison needs two listings side by side, so the button is desktop
   only rather than competing for a thumb at 375px. */
.thv-fav--desk { display: none; }
@media (min-width: 992px) { .thv-fav--desk { display: grid; } }

/* Price. Dominant by size and weight, never by colour. */
.thv-price {
  display: block;
  font-size: 19px;
  font-weight: 800;
  letter-spacing: -.02em;
  line-height: 1.15;
  color: var(--thv-navy);
  font-variant-numeric: tabular-nums;
}
.thv-price .price-period {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--thv-ink-2);
  letter-spacing: 0;
}

.thv-card__title {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  margin-top: 5px;
  font-size: 14px;
  font-weight: 600;
  line-height: 1.35;
  color: var(--thv-ink);
}

/* Metadata row: "Shared Room · 2 beds · Kyrenia". The separator is a
   real element rather than a ::before on a span, so a wrapped line
   never starts with a floating dot. */
.thv-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 7px;
  margin-top: 7px;
  font-size: 12.5px;
  color: var(--thv-ink-2);
}
.thv-meta i { font-size: 12px; opacity: .8; }
.thv-dot {
  width: 3px;
  height: 3px;
  flex: none;
  border-radius: 50%;
  background: currentColor;
  opacity: .45;
}

@media (hover: hover) {
  .thv-card:hover { transform: translateY(-2px); box-shadow: var(--thv-sh-2); }
  .thv-card:hover .thv-card__media img { transform: scale(1.02); }
}

/* =====================================================================
   11  PROPERTIES
   =====================================================================
   One column below 576px. A 2-up grid on a phone crops the photograph
   past the point where it sells anything, and the price and the town
   stop fitting on their own lines.
   ===================================================================== */

.thv-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--thv-gap);
}

@media (min-width: 576px) { .thv-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 992px) { .thv-grid { grid-template-columns: repeat(3, 1fr); gap: 20px; } }
@media (min-width: 1200px) { .thv-grid { grid-template-columns: repeat(4, 1fr); } }

/* The whole card is clickable through the anchor that wraps the media
   and the body. The favourite button is a sibling, above it in z-order,
   so the two hit areas never overlap. */
.thv-card__link {
  display: block;
  color: inherit;
}


/* =====================================================================
   12  JOBS
   =====================================================================
   Same visual language as a property card — 1px border, 14px radius,
   same shadow — but a row rather than a tile, because a job has no
   photograph and a 16:10 block of empty grey is worse than no image.
   ===================================================================== */

.thv-jobs {
  display: grid;
  grid-template-columns: 1fr;
  gap: 10px;
}
@media (min-width: 768px)  { .thv-jobs { grid-template-columns: repeat(2, 1fr); gap: var(--thv-gap); } }
@media (min-width: 1200px) { .thv-jobs { grid-template-columns: repeat(3, 1fr); } }

.thv-job {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 14px;
  border: 1px solid var(--thv-line);
  border-radius: var(--thv-r-card);
  background: var(--thv-surface);
  box-shadow: var(--thv-sh-1);
  transition: transform var(--thv-t-base) var(--thv-ease),
              box-shadow var(--thv-t-base) var(--thv-ease);
}

/* The title's link covers the whole card, so the row is one big target;
   the bookmark sits above it with its own. Two overlapping hit areas
   rather than a link inside a link, which is invalid and unreachable by
   keyboard. */
.thv-job__title::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
}
.thv-job__save {
  position: relative;
  z-index: 2;
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  flex: none;
  margin: -4px -4px 0 0;
  border: 0;
  border-radius: 50%;
  background: none;
  color: var(--thv-ink-3);
  font-size: 16px;
  cursor: pointer;
  transition: background-color var(--thv-t-fast) var(--thv-ease),
              color var(--thv-t-fast) var(--thv-ease);
}
.thv-job__save:hover   { background: var(--thv-tint); color: var(--thv-navy); }
.thv-job__save.is-saved { color: var(--thv-blue); }

/* One line of the description, on the board but not on the homepage
   band — there it would push the third card below the fold. */
.thv-job__desc {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  margin-top: 6px;
  font-size: 13px;
  line-height: 1.5;
  color: var(--thv-ink-2);
}

.thv-job__logo {
  display: grid;
  place-items: center;
  width: 42px;
  height: 42px;
  flex: none;
  border-radius: 10px;
  border: 1px solid var(--thv-line-soft);
  background: var(--thv-tint);
  color: var(--thv-navy);
  font-size: 18px;
  overflow: hidden;
}
.thv-job__logo img { width: 100%; height: 100%; object-fit: contain; }

.thv-job__body { min-width: 0; flex: 1; }

.thv-job__title {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  font-size: 14.5px;
  font-weight: 700;
  line-height: 1.3;
  letter-spacing: -.01em;
  color: var(--thv-navy);
}
.thv-job__company {
  display: block;
  margin-top: 3px;
  font-size: 13px;
  color: var(--thv-ink-2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.thv-job__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin-top: 9px;
}
.thv-job__tag {
  padding: 3px 8px;
  border-radius: 6px;
  background: var(--thv-tint);
  font-size: 11.5px;
  font-weight: 600;
  color: var(--thv-ink-2);
  white-space: nowrap;
}
.thv-job__tag--pay { background: var(--thv-blue-soft); color: var(--thv-blue-dark); }
.thv-job__date {
  margin-left: auto;
  font-size: 11.5px;
  color: var(--thv-ink-3);
  white-space: nowrap;
}

@media (hover: hover) {
  .thv-job:hover { transform: translateY(-2px); box-shadow: var(--thv-sh-2); }
}


/* =====================================================================
   13  NEWS
   =====================================================================
   One lead article and a list beside it. On a phone the lead comes
   first and the list stacks underneath, which is the order someone
   reads them in anyway.
   ===================================================================== */

.thv-news {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--thv-gap);
}
@media (min-width: 992px) {
  .thv-news { grid-template-columns: 1.15fr 1fr; gap: 24px; }
}

/* --- Lead ------------------------------------------------------------- */
.thv-lead {
  display: block;
  border: 1px solid var(--thv-line);
  border-radius: var(--thv-r-card);
  background: var(--thv-surface);
  box-shadow: var(--thv-sh-1);
  overflow: hidden;
  transition: transform var(--thv-t-base) var(--thv-ease),
              box-shadow var(--thv-t-base) var(--thv-ease);
}
.thv-lead__media {
  position: relative;
  display: block;
  aspect-ratio: 16 / 9;
  background: var(--thv-tint);
  overflow: hidden;
}
.thv-lead__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--th-slow, .4s) var(--thv-ease-out);
}
.thv-lead__body { display: block; padding: 14px 15px 16px; }
.thv-lead__title {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  margin: 8px 0 0;
  font-size: 17px;
  font-weight: 750;
  line-height: 1.28;
  letter-spacing: -.018em;
  color: var(--thv-navy);
}
.thv-lead__excerpt {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  margin: 7px 0 0;
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--thv-ink-2);
}

/* Category label. Colour comes from the categories table via an inline
   custom property, with the brand blue as the fallback. */
.thv-cat {
  display: inline-block;
  padding: 3px 8px;
  border-radius: 6px;
  background: color-mix(in srgb, var(--thv-cat, var(--thv-blue)) 12%, #fff);
  color: var(--thv-cat, var(--thv-blue));
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
}
@supports not (background: color-mix(in srgb, red 10%, #fff)) {
  .thv-cat { background: var(--thv-blue-soft); }
}

.thv-date {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 12px;
  color: var(--thv-ink-3);
}
.thv-lead__foot { margin-top: 10px; }

/* --- The news card ----------------------------------------------------
   Image on top, then category, headline, excerpt, date. The headline is
   the biggest thing on it, which is what separates it from a property
   card — there the price leads. Same border, same radius, same shadow. */
.thv-ncard {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--thv-line);
  border-radius: var(--thv-r-card);
  background: var(--thv-surface);
  box-shadow: var(--thv-sh-1);
  overflow: hidden;
  transition: transform var(--thv-t-base) var(--thv-ease),
              box-shadow var(--thv-t-base) var(--thv-ease);
}
.thv-ncard__media {
  position: relative;
  display: block;
  aspect-ratio: 16 / 9;
  background: var(--thv-tint);
  overflow: hidden;
}
.thv-ncard__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--th-slow, .4s) var(--thv-ease-out);
}
.thv-ncard__flag {
  position: absolute;
  top: 10px;
  left: 10px;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 9px;
  border-radius: 7px;
  background: var(--th-danger, #EF4444);
  color: #fff;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .03em;
  text-transform: uppercase;
}
.thv-ncard__body {
  display: block;
  padding: 14px 15px 16px;
}
.thv-ncard__title {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  margin-top: 8px;
  font-size: 16px;
  font-weight: 750;
  line-height: 1.3;
  letter-spacing: -.016em;
  color: var(--thv-navy);
}
.thv-ncard__excerpt {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  margin-top: 7px;
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--thv-ink-2);
}
.thv-ncard__foot { margin-top: 10px; }

@media (hover: hover) {
  .thv-ncard:hover { transform: translateY(-3px); box-shadow: var(--thv-sh-2); }
  .thv-ncard:hover .thv-ncard__media img { transform: scale(1.03); }
}

/* The lead story. Bigger type and a wider crop; from 992px the image
   and the text sit side by side so the fold still reaches the grid. */
@media (min-width: 768px) {
  .thv-ncard--lead .thv-ncard__title   { font-size: 26px; -webkit-line-clamp: 3; }
  .thv-ncard--lead .thv-ncard__excerpt { font-size: 15px; -webkit-line-clamp: 3; }
  .thv-ncard--lead .thv-ncard__body    { padding: 20px 24px 24px; }
}
@media (min-width: 992px) {
  .thv-ncard--lead {
    display: grid;
    grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
    align-items: stretch;
  }
  /* The photograph fills its column but is capped: with a long
     headline the text column grows, and an unbounded `height: 100%`
     stretched the image to 600px and pushed the grid off the screen. */
  .thv-ncard--lead .thv-ncard__media {
    aspect-ratio: auto;
    height: 100%;
    min-height: 300px;
    max-height: 420px;
  }
  .thv-ncard--lead .thv-ncard__body  {
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: 32px;
  }
  .thv-ncard--lead .thv-ncard__title { font-size: 30px; }
}

/* --- Article list ------------------------------------------------------ */
.thv-arts {
  display: grid;
  gap: 10px;
  align-content: start;
}
.thv-art {
  display: grid;
  grid-template-columns: 84px minmax(0, 1fr);
  gap: 12px;
  padding: 10px;
  border: 1px solid var(--thv-line);
  border-radius: var(--thv-r-tile);
  background: var(--thv-surface);
  transition: transform var(--thv-t-base) var(--thv-ease),
              border-color var(--thv-t-base) var(--thv-ease);
}
.thv-art__thumb {
  width: 84px;
  height: 66px;
  border-radius: 8px;
  object-fit: cover;
  background: var(--thv-tint);
}
.thv-art__title {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  margin: 5px 0 0;
  font-size: 13.5px;
  font-weight: 650;
  line-height: 1.34;
  color: var(--thv-navy);
}
.thv-art__foot { margin-top: 5px; }

@media (hover: hover) {
  .thv-lead:hover { transform: translateY(-2px); box-shadow: var(--thv-sh-2); }
  .thv-lead:hover .thv-lead__media img { transform: scale(1.02); }
  .thv-art:hover  { transform: translateY(-2px); border-color: var(--th-primary-border, #BFDBFE); }
}

@media (min-width: 768px) {
  .thv-lead__title   { font-size: 20px; }
  .thv-lead__body    { padding: 18px 20px 20px; }
}


/* =====================================================================
   14  BUSINESS  &  POPULAR AREAS
   =====================================================================
   Both are link sets rather than content, so both are deliberately
   small. The directory is a module, not a headline.
   ===================================================================== */

.thv-tiles {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
}
@media (min-width: 576px) { .thv-tiles { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 992px) { .thv-tiles { grid-template-columns: repeat(4, 1fr); gap: var(--thv-gap); } }
@media (min-width: 1200px){ .thv-tiles { grid-template-columns: repeat(7, 1fr); } }

.thv-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 92px;
  padding: 14px 8px;
  border: 1px solid var(--thv-line);
  border-radius: var(--thv-r-tile);
  background: var(--thv-surface);
  text-align: center;
  transition: transform var(--thv-t-base) var(--thv-ease),
              border-color var(--thv-t-base) var(--thv-ease);
}
.thv-tile__icon {
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  border-radius: 10px;
  background: var(--thv-tint);
  color: var(--thv-navy);
  font-size: 17px;
  transition: background-color var(--thv-t-base) var(--thv-ease),
              color var(--thv-t-base) var(--thv-ease);
}
.thv-tile__name {
  font-size: 12.5px;
  font-weight: 600;
  line-height: 1.25;
  color: var(--thv-navy);
}

@media (hover: hover) {
  .thv-tile:hover { transform: translateY(-2px); border-color: var(--th-primary-border, #BFDBFE); }
  .thv-tile:hover .thv-tile__icon { background: var(--thv-blue-soft); color: var(--thv-blue); }
}

/* --- Popular areas ----------------------------------------------------
   Plain links, wrapped. This is a link list, so it looks like one: no
   photographs, no cards, nothing that competes with the town rail
   further up the page. */
.thv-areas {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.thv-area {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  min-height: 44px;
  padding: 10px 14px;
  border: 1px solid var(--thv-line);
  border-radius: var(--thv-r-btn);
  background: var(--thv-surface);
  font-size: 13.5px;
  font-weight: 600;
  color: var(--thv-navy);
  transition: transform var(--thv-t-fast) var(--thv-ease),
              border-color var(--thv-t-fast) var(--thv-ease),
              background-color var(--thv-t-fast) var(--thv-ease);
}
.thv-area i { font-size: 13px; color: var(--thv-ink-3); }
.thv-area__n {
  font-size: 12px;
  font-weight: 600;
  color: var(--thv-ink-3);
  font-variant-numeric: tabular-nums;
}
@media (hover: hover) {
  .thv-area:hover {
    transform: translateY(-1px);
    border-color: var(--th-primary-border, #BFDBFE);
    background: var(--thv-blue-soft);
  }
}


/* =====================================================================
   15  CTA
   =====================================================================
   A panel, not a banner. Someone who has scrolled this far has seen
   what the site does and is the only person for whom "post your own" is
   a sensible next step.
   ===================================================================== */

.thv-cta {
  padding: 26px 20px;
  border-radius: var(--thv-r-card);
  background:
    radial-gradient(110% 130% at 88% 6%, rgba(37, 99, 235, .38) 0%, transparent 58%),
    linear-gradient(160deg, var(--thv-navy) 0%, var(--thv-navy-deep) 100%);
  color: #fff;
  text-align: center;
}
.thv-cta__title {
  margin: 0;
  font-size: clamp(20px, 5vw, 26px);
  font-weight: 800;
  letter-spacing: -.024em;
  color: #fff;
}
.thv-cta__sub {
  margin: 8px auto 0;
  max-width: 44ch;
  font-size: 14px;
  color: rgba(255, 255, 255, .78);
}
.thv-cta__row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px;
  margin-top: 18px;
}

/* The only two buttons on the homepage that are not part of the search
   box, so they are defined here rather than in a button section that
   would have two members. */
.thv-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 46px;
  padding: 12px 20px;
  border: 1px solid transparent;
  border-radius: var(--thv-r-btn);
  font-family: inherit;
  font-size: 14.5px;
  font-weight: 650;
  cursor: pointer;
  transition: transform var(--thv-t-fast) var(--thv-ease),
              background-color var(--thv-t-fast) var(--thv-ease);
}
.thv-btn--solid { background: #fff; color: var(--thv-navy); }
.thv-btn--solid:hover { background: #fff; color: var(--thv-navy); transform: translateY(-1px); }
.thv-btn--ghost {
  background: rgba(255, 255, 255, .1);
  border-color: rgba(255, 255, 255, .34);
  color: #fff;
}
.thv-btn--ghost:hover { background: rgba(255, 255, 255, .18); color: #fff; transform: translateY(-1px); }
.thv-btn:active { transform: scale(.985); }

@media (min-width: 768px) {
  .thv-cta { padding: 40px 32px; }
  .thv-cta__sub { font-size: 15.5px; }
}

/* =====================================================================
   16  FOOTER
   =====================================================================
   Owns `.th-footer` and `.th-newsletter`, which render on every page.

   No accordion. footer.php emits four link columns as Bootstrap
   `col-6 col-lg-2`, which is already two compact columns on a phone —
   an accordion would add JavaScript, a tap, and an ARIA contract to
   hide sixteen links that occupy less than one screen between them.
   ===================================================================== */

.th-footer {
  background: var(--thv-navy-deep);
  color: rgba(255, 255, 255, .68);
  font-size: 14px;
}
.th-footer__main { padding-block: 36px 28px; }

.th-footer h4,
.th-footer h5,
.th-footer .th-h4,
.th-footer .th-h5 {
  margin: 0 0 12px;
  color: #fff;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
}

.th-footer ul { margin: 0; padding: 0; list-style: none; }
.th-footer li { margin-bottom: 2px; }

.th-footer a {
  display: inline-block;
  padding-block: 6px;
  color: rgba(255, 255, 255, .68);
  font-size: 13.5px;
  transition: color var(--thv-t-fast) var(--thv-ease);
}
.th-footer a:hover { color: #fff; }

.th-footer__brand p {
  max-width: 36ch;
  margin-top: 12px;
  font-size: 13.5px;
  line-height: 1.65;
  color: rgba(255, 255, 255, .6);
}
.th-footer .th-logo,
.th-footer .th-logo__name { color: #fff; }
.th-footer .th-logo__mark { background: var(--thv-blue); }

.th-footer__social { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 16px; }
.th-footer__social a {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  padding: 0;
  border-radius: 10px;
  background: rgba(255, 255, 255, .08);
  color: rgba(255, 255, 255, .8);
  font-size: 16px;
  transition: background-color var(--thv-t-fast) var(--thv-ease),
              transform var(--thv-t-fast) var(--thv-ease);
}
.th-footer__social a:hover { background: var(--thv-blue); color: #fff; transform: translateY(-2px); }

.th-footer__bottom {
  border-top: 1px solid rgba(255, 255, 255, .1);
  padding-block: 16px;
  font-size: 12.5px;
  color: rgba(255, 255, 255, .52);
}
.th-footer__bottom p { margin: 0; }

/* --- Newsletter band --------------------------------------------------
   Sits directly above the footer on every page. */
.th-newsletter {
  padding: 24px 20px;
  border-radius: var(--thv-r-card);
  background:
    radial-gradient(120% 140% at 90% 0%, rgba(37, 99, 235, .34) 0%, transparent 60%),
    linear-gradient(160deg, var(--thv-navy) 0%, var(--thv-navy-deep) 100%);
  color: #fff;
}
.th-newsletter h2 {
  margin: 0 0 6px;
  color: #fff;
  font-size: clamp(19px, 4.6vw, 24px);
  font-weight: 800;
  letter-spacing: -.022em;
}
.th-newsletter p { color: rgba(255, 255, 255, .78); font-size: 14px; }
.th-newsletter .form-control {
  height: 46px;
  border: 1px solid rgba(255, 255, 255, .22);
  border-radius: var(--thv-r-btn);
  background: rgba(255, 255, 255, .12);
  color: #fff;
  font-size: 14.5px;
}
.th-newsletter .form-control::placeholder { color: rgba(255, 255, 255, .58); }
.th-newsletter .form-control:focus {
  border-color: rgba(255, 255, 255, .5);
  background: rgba(255, 255, 255, .18);
  color: #fff;
  box-shadow: none;
}
.th-newsletter .btn {
  min-height: 46px;
  border-radius: var(--thv-r-btn);
  font-weight: 650;
}
.th-newsletter .form-check-label { color: rgba(255, 255, 255, .9); }

/* Bootstrap 5.3 ships `.form-check .form-check-input { float: left;
   margin-left: -1.5em }`, which assumes the label has 1.5em of left
   padding. trnchub-new.css makes `.form-check` a flexbox and zeroes
   that padding, but resets the input with `.form-check-input { margin:
   0 }` — one specificity step below Bootstrap's two-class selector, so
   the float survived and every checkbox sat on top of its own label.
   Visible in the newsletter band on every page of the site. */
.form-check .form-check-input {
  float: none;
  margin-left: 0;
  margin-top: 0;
}

@media (min-width: 768px) {
  .th-footer__main { padding-block: 52px 32px; }
  .th-newsletter   { padding: 36px 32px; }
}


/* =====================================================================
   17  MOBILE NAVIGATION
   =====================================================================
   The fixed tab bar, and the clearance every page needs so it never
   covers the last card.

   `--th-tabbar-h` and `--th-safe-b` are published here because five
   other components read them — the floating action button on
   /properties and /jobs among them. They were being read and had never
   been defined, which is why that button was landing on top of the tab
   bar instead of above it.
   ===================================================================== */

.th-tabbar {
  position: fixed;
  inset: auto 0 0 0;
  z-index: var(--th-z-tabbar, 1020);
  display: none;
  align-items: stretch;
  height: calc(var(--th-tabbar-h) + var(--th-safe-b));
  padding-bottom: var(--th-safe-b);
  border-top: 1px solid var(--thv-line);
  background: rgba(255, 255, 255, .92);
  backdrop-filter: blur(18px) saturate(180%);
  -webkit-backdrop-filter: blur(18px) saturate(180%);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .th-tabbar { background: #fff; }
}

.th-tabbar__item {
  flex: 1 1 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  min-width: 0;
  padding: 8px 2px;
  color: var(--thv-ink-3);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: -.01em;
  text-align: center;
  transition: color var(--thv-t-fast) var(--thv-ease);
}
.th-tabbar__item i { font-size: 19px; line-height: 1; }
.th-tabbar__item span {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.th-tabbar__item.active,
.th-tabbar__item.is-active { color: var(--thv-blue); }

/* Clearance. The homepage wrapper carries it so the CTA and the footer
   are both reachable; the tab bar only exists below 768px, which is
   where navbar.php marks it `d-md-none`. Mismatching those two
   breakpoints is how you get dead space under the footer on a tablet. */
@media (max-width: 767.98px) {
  .th-tabbar { display: flex; }
  body.th-page { padding-bottom: calc(var(--th-tabbar-h) + var(--th-safe-b)); }
}

/* --- Mobile drawer ---------------------------------------------------
   Bootstrap's offcanvas, restyled. It is load-bearing — the entire
   mobile menu is built on it — so only the surface is touched. */
.offcanvas {
  border-left: 1px solid var(--thv-line);
  background: rgba(255, 255, 255, .96);
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .offcanvas { background: #fff; }
}
.offcanvas-header { padding: 14px 16px; }
.offcanvas-body   { padding: 8px 16px 24px; }


/* =====================================================================
   18  PAGE SHELL
   =====================================================================
   Everything that is not the homepage: /properties, /jobs, /news,
   /businesses, /user and every detail page.

   NO PHOTOGRAPHIC HERO. The old interior pages opened with a full-bleed
   photograph, a heading over it and a search box — roughly 260px of
   decoration on a phone before the first listing, and an image request
   competing with the listing thumbnails that are the actual content.
   A results page is not a landing page: it opens with a heading, a
   count and a search field on a flat tinted band.

   The homepage keeps its photograph. It is the only page whose job is
   to sell the idea of the place rather than to answer a query.
   ===================================================================== */

.thv-page {
  padding-bottom: var(--thv-sect-y);
  background: var(--thv-page);
}

.thv-page__head {
  padding-block: 20px 22px;
  background: var(--thv-surface);
  border-bottom: 1px solid var(--thv-line);
}
.thv-page__head--tint { background: var(--thv-tint); }

.thv-page__crumbs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin: 0 0 10px;
  padding: 0;
  list-style: none;
  font-size: 12.5px;
  color: var(--thv-ink-3);
}
.thv-page__crumbs li { display: inline-flex; align-items: center; gap: 6px; }
.thv-page__crumbs li + li::before {
  content: "";
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: currentColor;
  opacity: .5;
}
.thv-page__crumbs a { color: var(--thv-ink-2); }
.thv-page__crumbs a:hover { color: var(--thv-blue); }
.thv-page__crumbs [aria-current="page"] { color: var(--thv-ink); font-weight: 600; }

/* On a phone the last crumb is dropped: it repeats the <h1> immediately
   below it, and a long listing title wrapped onto its own line behind a
   leading separator dot. The full trail is still in the BreadcrumbList
   JSON-LD, so nothing is lost to a crawler. */
@media (max-width: 575.98px) {
  .thv-page__crumbs li:last-child { display: none; }
}

.thv-page__title {
  margin: 0;
  font-size: clamp(23px, 5.6vw, 32px);
  font-weight: 800;
  line-height: 1.16;
  letter-spacing: -.026em;
  color: var(--thv-navy);
  text-wrap: balance;
}
.thv-page__sub {
  margin: 6px 0 0;
  max-width: 60ch;
  font-size: 14.5px;
  line-height: 1.5;
  color: var(--thv-ink-2);
}

.thv-page__tabs { margin-top: 14px; }

@media (min-width: 768px) {
  .thv-page__head { padding-block: 30px 32px; }
  .thv-page__sub  { font-size: 16px; }
}

/* --- The light search field -------------------------------------------
   The hero search is glass over a photograph. This one sits on a white
   band, so it is a plain bordered field — the same shape and the same
   blue submit, without a backdrop-filter that would have nothing to
   filter. */
.thv-find {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 8px;
  width: 100%;
  max-width: 640px;
  margin-top: 16px;
  padding: 5px 5px 5px 13px;
  border: 1px solid var(--thv-line);
  border-radius: var(--thv-r-card);
  background: var(--thv-surface);
  box-shadow: var(--thv-sh-1);
  transition: border-color var(--thv-t-fast) var(--thv-ease),
              box-shadow var(--thv-t-fast) var(--thv-ease);
}
.thv-find:focus-within {
  border-color: var(--th-primary-border, #BFDBFE);
  box-shadow: 0 0 0 4px rgba(37, 99, 235, .12);
}
.thv-find__icon {
  display: grid;
  place-items: center;
  font-size: 15px;
  color: var(--thv-ink-3);
}
.thv-find__input {
  width: 100%;
  min-width: 0;
  height: 42px;
  padding: 0;
  border: 0;
  background: none;
  font-family: inherit;
  font-size: 15px;
  color: var(--thv-ink);
}
.thv-find__input::placeholder { color: var(--thv-ink-3); }
.thv-find__input:focus { outline: none; }
.thv-find__input::-webkit-search-decoration,
.thv-find__input::-webkit-search-cancel-button { -webkit-appearance: none; }

/* A second field — town, location — that only appears when there is
   room for it. Below 768px it would halve the space for the query. */
.thv-find__where { display: none; }
.thv-find__select {
  height: 42px;
  max-width: 170px;
  padding: 0 28px 0 10px;
  border: 0;
  border-left: 1px solid var(--thv-line);
  border-radius: 0;
  background: transparent;
  font-family: inherit;
  font-size: 14px;
  font-weight: 600;
  color: var(--thv-ink);
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='none' stroke='%236B7280' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round' d='M1 1.5 6 6.5l5-5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 9px center;
}
.thv-find__select:focus { outline: none; }

.thv-find__go {
  display: grid;
  place-items: center;
  width: 42px;
  height: 42px;
  flex: none;
  border: 0;
  border-radius: var(--thv-r-btn);
  background: var(--thv-blue);
  color: #fff;
  font-size: 16px;
  cursor: pointer;
  transition: background-color var(--thv-t-fast) var(--thv-ease),
              transform var(--thv-t-fast) var(--thv-ease);
}
.thv-find__go:hover  { background: var(--thv-blue-dark); }
.thv-find__go:active { transform: scale(.96); }

@media (min-width: 768px) {
  .thv-find { grid-template-columns: auto minmax(0, 1fr) auto auto; }
  .thv-find__where { display: block; }
}


/* =====================================================================
   19  TOOLBAR & SORT
   =====================================================================
   The row above the results: how many there are, how they are sorted,
   and — on a phone — the button that opens the filters.

   It is sticky on a phone and static from 992px, where the filters are
   a permanent sidebar and there is nothing left worth following the
   scroll.
   ===================================================================== */

.thv-bar {
  position: sticky;
  top: var(--th-header-h);
  z-index: 20;
  display: flex;
  align-items: center;
  gap: 8px;
  padding-block: 10px;
  background: var(--thv-page);
  /* The band has to hide the cards passing underneath it, so it cannot
     be transparent and it cannot be glass — a blurred strip pinned over
     a scrolling grid of photographs is the most expensive thing this
     page could do. */
  box-shadow: 0 1px 0 var(--thv-line);
}

/* The count keeps its width and the controls give way, not the other
   way round. "268 pro…" is worse than a narrower sort control: the
   number is the one thing on this row somebody is actually reading. */
.thv-bar__count {
  margin: 0;
  flex: none;
  font-size: 13.5px;
  color: var(--thv-ink-2);
  white-space: nowrap;
}
.thv-bar__count b {
  color: var(--thv-navy);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.thv-bar__end {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-left: auto;
  min-width: 0;
}

/* Sort and the filter trigger share one shape so the row reads as a set
   of controls rather than as a select next to a button. */
.thv-ctl {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 38px;
  padding: 0 12px;
  border: 1px solid var(--thv-line);
  border-radius: var(--thv-r-btn);
  background: var(--thv-surface);
  font-family: inherit;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--thv-navy);
  white-space: nowrap;
  cursor: pointer;
  transition: border-color var(--thv-t-fast) var(--thv-ease),
              background-color var(--thv-t-fast) var(--thv-ease);
}
.thv-ctl:hover { border-color: var(--thv-ink-3); color: var(--thv-navy); }
.thv-ctl i { font-size: 14px; color: var(--thv-ink-2); }

select.thv-ctl {
  padding-right: 28px;
  -webkit-appearance: none;
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='none' stroke='%236B7280' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round' d='M1 1.5 6 6.5l5-5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 10px center;
  text-overflow: ellipsis;
}

/* The count of applied filters, on the filter button. */
.thv-ctl__n {
  display: inline-grid;
  place-items: center;
  min-width: 18px;
  height: 18px;
  padding-inline: 5px;
  border-radius: var(--thv-r-pill);
  background: var(--thv-blue);
  color: #fff;
  font-size: 11px;
  font-weight: 700;
}

/* Grid / list toggle. Two buttons in one bordered shell. */
.thv-view {
  display: none;
  align-items: center;
  padding: 3px;
  border: 1px solid var(--thv-line);
  border-radius: var(--thv-r-btn);
  background: var(--thv-surface);
}
.thv-view__btn {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border: 0;
  border-radius: 6px;
  background: none;
  color: var(--thv-ink-3);
  font-size: 15px;
  cursor: pointer;
  transition: background-color var(--thv-t-fast) var(--thv-ease),
              color var(--thv-t-fast) var(--thv-ease);
}
.thv-view__btn:hover { color: var(--thv-navy); }
.thv-view__btn.is-active { background: var(--thv-tint); color: var(--thv-navy); }

/* Below 576px the word "Filters" is dropped and the button becomes the
   slider icon plus its count. The accessible name is on the button, so
   nothing is lost to a screen reader. */
.thv-ctl__label { display: none; }
select.thv-ctl  { min-width: 0; max-width: 148px; }

@media (min-width: 576px) {
  .thv-ctl__label { display: inline; }
  select.thv-ctl  { max-width: none; }
}

@media (min-width: 992px) {
  .thv-bar  { position: static; box-shadow: none; padding-block: 0 16px; }
  .thv-view { display: flex; }
}


/* =====================================================================
   20  CHIPS, RAILS & TABS
   =====================================================================
   Category navigation. One rail, scrolled with a thumb, that never
   wraps and never pushes the page sideways.

   Ten sectors do not fit on one line at 360px and must not be made to:
   the old page shrank them until the labels collided. They scroll.
   ===================================================================== */

.thv-chiprail {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  scroll-snap-type: x proximity;
  scroll-padding-inline: var(--thv-gutter);
  padding: 12px var(--thv-gutter);
  scrollbar-width: none;
  overscroll-behavior-x: contain;
  background: var(--thv-surface);
  border-bottom: 1px solid var(--thv-line);
}
.thv-chiprail::-webkit-scrollbar { display: none; }
.thv-chiprail > * { scroll-snap-align: start; }

.thv-chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  flex: none;
  height: 38px;
  padding: 0 14px;
  border: 1px solid var(--thv-line);
  border-radius: var(--thv-r-pill);
  background: var(--thv-surface);
  font-size: 13.5px;
  font-weight: 600;
  color: var(--thv-ink-2);
  white-space: nowrap;
  transition: border-color var(--thv-t-fast) var(--thv-ease),
              background-color var(--thv-t-fast) var(--thv-ease),
              color var(--thv-t-fast) var(--thv-ease);
}
.thv-chip i { font-size: 14px; opacity: .85; }
.thv-chip__n {
  font-size: 12px;
  font-weight: 600;
  color: var(--thv-ink-3);
  font-variant-numeric: tabular-nums;
}
.thv-chip:hover { border-color: var(--thv-ink-3); color: var(--thv-navy); }

/* Active state is a navy fill, not a blue one: blue is the action
   colour and an active filter is a state, not an action. */
.thv-chip.is-active,
.thv-chip.active {
  background: var(--thv-navy);
  border-color: var(--thv-navy);
  color: #fff;
}
.thv-chip.is-active .thv-chip__n,
.thv-chip.active .thv-chip__n { color: rgba(255, 255, 255, .7); }

/* --- Applied-filter chips, with a remove button ---------------------- */
.thv-applied {
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
  margin-bottom: 14px;
}
.thv-fchip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  height: 32px;
  padding: 0 4px 0 11px;
  border: 1px solid var(--th-primary-border, #BFDBFE);
  border-radius: var(--thv-r-pill);
  background: var(--thv-blue-soft);
  font-size: 12.5px;
  font-weight: 600;
  color: var(--thv-blue-dark);
  max-width: 100%;
}
.thv-fchip > span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.thv-fchip button {
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  flex: none;
  border: 0;
  border-radius: 50%;
  background: none;
  color: inherit;
  font-size: 13px;
  cursor: pointer;
  opacity: .7;
  transition: opacity var(--thv-t-fast) var(--thv-ease),
              background-color var(--thv-t-fast) var(--thv-ease);
}
.thv-fchip button:hover { opacity: 1; background: rgba(37, 99, 235, .12); }

.thv-clear {
  display: inline-flex;
  align-items: center;
  height: 32px;
  padding: 0 10px;
  border: 0;
  border-radius: var(--thv-r-btn);
  background: none;
  font-family: inherit;
  font-size: 12.5px;
  font-weight: 650;
  color: var(--thv-ink-2);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.thv-clear:hover { color: var(--thv-navy); }

/* --- Segmented tabs (For rent / For sale / Short term) ---------------- */
.thv-seg {
  display: inline-flex;
  gap: 2px;
  padding: 3px;
  border: 1px solid var(--thv-line);
  border-radius: var(--thv-r-pill);
  background: var(--thv-tint);
  max-width: 100%;
  overflow-x: auto;
  scrollbar-width: none;
}
.thv-seg::-webkit-scrollbar { display: none; }
.thv-seg__item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: none;
  height: 34px;
  padding: 0 15px;
  border-radius: var(--thv-r-pill);
  font-size: 13.5px;
  font-weight: 650;
  color: var(--thv-ink-2);
  white-space: nowrap;
  transition: background-color var(--thv-t-base) var(--thv-ease),
              color var(--thv-t-base) var(--thv-ease);
}
.thv-seg__item i { font-size: 13px; }
.thv-seg__item:hover { color: var(--thv-navy); }
.thv-seg__item.is-active,
.thv-seg__item.active {
  background: var(--thv-surface);
  color: var(--thv-navy);
  box-shadow: var(--thv-sh-1);
}


/* =====================================================================
   21  FILTERS
   =====================================================================
   ONE form, TWO presentations, NO duplicated markup and NO new
   JavaScript.

   Bootstrap's `.offcanvas-lg` is a bottom sheet below 992px and an
   ordinary block above it. That is exactly the behaviour asked for, it
   is already loaded, and it brings the focus trap, the backdrop, the
   Escape key and the animation with it. Writing a custom drawer would
   have meant re-implementing all four.

   The form keeps `data-filter-form`, so the existing AJAX filtering in
   trnchub.js works untouched in both presentations.
   ===================================================================== */

.thv-filters { min-width: 0; }

/* Bottom sheet on a phone. */
@media (max-width: 991.98px) {
  .offcanvas-lg.thv-filters {
    height: 86svh;
    border-radius: 20px 20px 0 0;
    border: 0;
    background: var(--thv-surface);
  }
  .offcanvas-lg.thv-filters .offcanvas-header {
    position: relative;
    padding: 18px 16px 12px;
    border-bottom: 1px solid var(--thv-line);
  }
  /* The grab handle. Decorative, so it is a pseudo-element rather than
     an empty div in the markup. */
  .offcanvas-lg.thv-filters .offcanvas-header::before {
    content: "";
    position: absolute;
    top: 7px;
    left: 50%;
    width: 38px;
    height: 4px;
    border-radius: 2px;
    background: var(--thv-line);
    transform: translateX(-50%);
  }
  .offcanvas-lg.thv-filters .offcanvas-body { padding: 16px 16px 96px; }
}

.thv-filters__title {
  margin: 0;
  font-size: 16px;
  font-weight: 800;
  letter-spacing: -.016em;
  color: var(--thv-navy);
}

/* Desktop: a card that follows the scroll.

   TWO BOOTSTRAP DEFAULTS HAVE TO BE UNDONE HERE, and both were visible
   on screen before they were:

     `.offcanvas-lg .offcanvas-body` is `display: flex` above the
     breakpoint — meant for a horizontal nav. It laid every filter group
     out in a row, so the labels ran across the top of the panel on top
     of each other and the selects collapsed to nothing.

     `.offcanvas-lg .offcanvas-header` is `display: none` above the
     breakpoint, which took the "Filters" heading with it. */
@media (min-width: 992px) {
  .thv-filters {
    position: sticky;
    top: calc(var(--th-header-h) + 16px);
    align-self: start;
    max-height: calc(100dvh - var(--th-header-h) - 32px);
    overflow-y: auto;
    overscroll-behavior: contain;
    padding: 18px;
    border: 1px solid var(--thv-line);
    border-radius: var(--thv-r-card);
    background: var(--thv-surface);
    box-shadow: var(--thv-sh-1);
    scrollbar-width: thin;
  }
  .thv-filters .offcanvas-body {
    display: block;
    padding: 0;
    overflow-y: visible;
  }
  .thv-filters .offcanvas-header {
    display: flex;
    padding: 0 0 12px;
    border-bottom: 1px solid var(--thv-line);
  }
}

.thv-fgroup { padding-block: 14px; border-top: 1px solid var(--thv-line-soft); }
.thv-fgroup:first-of-type { border-top: 0; padding-top: 4px; }

.thv-flabel {
  display: block;
  margin-bottom: 9px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--thv-ink-3);
}

/* Selects and text inputs inside the panel. One rule, both elements. */
.thv-field {
  width: 100%;
  height: 44px;
  padding: 0 12px;
  border: 1px solid var(--thv-line);
  border-radius: var(--thv-r-btn);
  background: var(--thv-surface);
  font-family: inherit;
  font-size: 14.5px;
  color: var(--thv-ink);
  transition: border-color var(--thv-t-fast) var(--thv-ease),
              box-shadow var(--thv-t-fast) var(--thv-ease);
}
.thv-field:focus {
  outline: none;
  border-color: var(--th-primary-border, #BFDBFE);
  box-shadow: 0 0 0 3px rgba(37, 99, 235, .14);
}
select.thv-field {
  padding-right: 30px;
  -webkit-appearance: none;
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='none' stroke='%236B7280' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round' d='M1 1.5 6 6.5l5-5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 11px center;
}

.thv-frow { display: flex; align-items: center; gap: 8px; }
.thv-frow .thv-field { min-width: 0; }
.thv-frow span { color: var(--thv-ink-3); flex: none; }

/* Radio groups drawn as chips. The input stays in the DOM and keeps
   working with the keyboard; only its box is hidden. */
.thv-fchips { display: flex; flex-wrap: wrap; gap: 6px; }
.thv-fchips label {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  height: 38px;
  padding: 0 12px;
  border: 1px solid var(--thv-line);
  border-radius: var(--thv-r-btn);
  background: var(--thv-surface);
  font-size: 13.5px;
  font-weight: 600;
  color: var(--thv-ink-2);
  cursor: pointer;
  transition: border-color var(--thv-t-fast) var(--thv-ease),
              background-color var(--thv-t-fast) var(--thv-ease),
              color var(--thv-t-fast) var(--thv-ease);
}
.thv-fchips input {
  position: absolute;
  opacity: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  cursor: pointer;
}
.thv-fchips label:hover { border-color: var(--thv-ink-3); }
.thv-fchips input:checked ~ span,
.thv-fchips label:has(input:checked) {
  color: var(--thv-blue-dark);
}
.thv-fchips label:has(input:checked) {
  border-color: var(--thv-blue);
  background: var(--thv-blue-soft);
}
.thv-fchips label:has(input:focus-visible) {
  outline: 2px solid var(--thv-blue);
  outline-offset: 2px;
}

/* Checkbox rows. */
.thv-fcheck {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 40px;
  font-size: 14px;
  color: var(--thv-ink);
  cursor: pointer;
}
.thv-fcheck input {
  width: 18px;
  height: 18px;
  flex: none;
  margin: 0;
  accent-color: var(--thv-blue);
  cursor: pointer;
}

/* The sheet's footer button, phone only — the desktop panel filters as
   you change it and needs no Apply. */
.thv-fapply {
  position: absolute;
  inset: auto 0 0 0;
  display: flex;
  gap: 10px;
  padding: 12px 16px calc(12px + var(--th-safe-b));
  background: var(--thv-surface);
  border-top: 1px solid var(--thv-line);
}
@media (min-width: 992px) { .thv-fapply { display: none; } }


/* =====================================================================
   22  RESULTS LAYOUT
   =====================================================================
   Sidebar plus results from 992px, one column below it.

   `minmax(0, 1fr)` on the results column is not decoration: without it
   a grid child defaults to `min-content` and a long unbroken job title
   makes the whole column — and the page — wider than the screen.
   ===================================================================== */

.thv-results {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 24px;
  padding-top: 4px;
}

@media (min-width: 992px) {
  .thv-results { grid-template-columns: 264px minmax(0, 1fr); gap: 28px; align-items: start; }
}
@media (min-width: 1280px) {
  .thv-results { grid-template-columns: 288px minmax(0, 1fr); gap: 32px; }
}

/* The results grid itself. One column on a phone — a 2-up grid crops a
   property photograph past the point where it sells anything. */
.thv-list {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--thv-gap);
}
@media (min-width: 576px)  { .thv-list { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 992px)  { .thv-list { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 20px; } }
@media (min-width: 1280px) { .thv-list { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

/* Jobs and businesses are rows, not tiles, so they get their own tracks. */
.thv-list--rows { grid-template-columns: minmax(0, 1fr); }
@media (min-width: 576px)  { .thv-list--rows { grid-template-columns: minmax(0, 1fr); } }
@media (min-width: 992px)  { .thv-list--rows { grid-template-columns: minmax(0, 1fr); gap: 10px; } }
@media (min-width: 1280px) { .thv-list--rows { grid-template-columns: minmax(0, 1fr); } }

/* --- List view -------------------------------------------------------
   The toggle adds `.is-list`. The card turns into a row: image left,
   body right, at a fixed 40% so the photograph keeps a usable size and
   the text column does not collapse on a narrow laptop. */
.thv-list.is-list { grid-template-columns: minmax(0, 1fr); gap: 10px; }

@media (min-width: 576px) {
  .thv-list.is-list .thv-card {
    display: grid;
    grid-template-columns: minmax(0, 4fr) minmax(0, 6fr);
    align-items: stretch;
  }
  .thv-list.is-list .thv-card__media { aspect-ratio: auto; height: 100%; min-height: 160px; }
  .thv-list.is-list .thv-card__body  { display: flex; flex-direction: column; justify-content: center; padding: 16px 18px; }
}


/* =====================================================================
   23  PAGINATION & EMPTY STATE
   ===================================================================== */

.thv-pager {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 6px;
  margin-top: 28px;
}
.thv-pager a,
.thv-pager span {
  display: inline-grid;
  place-items: center;
  min-width: 42px;
  height: 42px;
  padding-inline: 12px;
  border: 1px solid var(--thv-line);
  border-radius: var(--thv-r-btn);
  background: var(--thv-surface);
  font-size: 14px;
  font-weight: 600;
  color: var(--thv-ink-2);
  font-variant-numeric: tabular-nums;
  transition: border-color var(--thv-t-fast) var(--thv-ease),
              color var(--thv-t-fast) var(--thv-ease);
}
.thv-pager a:hover { border-color: var(--thv-ink-3); color: var(--thv-navy); }
.thv-pager [aria-current="page"] {
  background: var(--thv-navy);
  border-color: var(--thv-navy);
  color: #fff;
}
.thv-pager .is-gap { border: 0; background: none; min-width: 22px; padding: 0; color: var(--thv-ink-3); }
.thv-pager .is-disabled { opacity: .45; pointer-events: none; }
.thv-pager .is-current {
  background: var(--thv-navy);
  border-color: var(--thv-navy);
  color: #fff;
}
.thv-pager li { display: contents; }

.thv-pager__note {
  margin: 12px 0 0;
  text-align: center;
  font-size: 12.5px;
  color: var(--thv-ink-3);
}

.thv-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 44px 20px;
  border: 1px solid var(--thv-line);
  border-radius: var(--thv-r-card);
  background: var(--thv-surface);
}
.thv-empty__icon {
  display: grid;
  place-items: center;
  width: 56px;
  height: 56px;
  margin-bottom: 14px;
  border-radius: 16px;
  background: var(--thv-tint);
  color: var(--thv-ink-3);
  font-size: 24px;
}
.thv-empty__title {
  margin: 0 0 6px;
  font-size: 17px;
  font-weight: 750;
  letter-spacing: -.016em;
  color: var(--thv-navy);
}
.thv-empty__text {
  margin: 0 0 18px;
  max-width: 44ch;
  font-size: 14px;
  line-height: 1.55;
  color: var(--thv-ink-2);
}

/* The two buttons interior pages need: a filled primary and an outline.
   The homepage CTA pair is white-on-navy and cannot be reused on a
   white page. */
.thv-btn--primary {
  background: var(--thv-blue);
  color: #fff;
  border-color: var(--thv-blue);
}
.thv-btn--primary:hover { background: var(--thv-blue-dark); color: #fff; transform: translateY(-1px); }
.thv-btn--outline {
  background: var(--thv-surface);
  border-color: var(--thv-line);
  color: var(--thv-navy);
}
.thv-btn--outline:hover { border-color: var(--thv-ink-3); color: var(--thv-navy); transform: translateY(-1px); }
/* 40px is the floor for the compact variant. It is used in dense rows
   where a 46px button would dominate, but it is still a thumb target. */
.thv-btn--sm { min-height: 40px; padding: 9px 14px; font-size: 13.5px; }

/* Destructive and quiet variants — used by the account pages (delete an
   account, revoke a session, cancel an edit). */
.thv-btn--danger {
  background: var(--th-danger, #EF4444);
  border-color: var(--th-danger, #EF4444);
  color: #fff;
}
.thv-btn--danger:hover { background: var(--th-danger-hover, #DC2626); color: #fff; transform: translateY(-1px); }
.thv-btn--ghost {
  background: none;
  border-color: transparent;
  color: var(--thv-ink-2);
}
.thv-btn--ghost:hover { background: var(--thv-tint); color: var(--thv-navy); }
.thv-btn--block { display: flex; width: 100%; }


/* =====================================================================
   24  DETAIL PAGES
   =====================================================================
   /properties/<slug>, /jobs/<slug>, /businesses/<slug>.

   One shell for all three: a media block, a title block, a two-column
   body from 992px, and — on a phone — a fixed action bar so the thing
   the page exists to make you do is always one tap away.
   ===================================================================== */

.thv-detail { padding-bottom: var(--thv-sect-y); background: var(--thv-page); }

/* --- Gallery ----------------------------------------------------------
   One large image, and a strip of thumbnails that scrolls. Not a
   carousel: a carousel needs JavaScript, hides most of the photographs
   behind an interaction, and is the single most common place a property
   site loads twenty full-size images nobody looks at. */
.thv-gallery { display: grid; gap: 6px; }

.thv-gallery__main {
  position: relative;
  display: block;
  aspect-ratio: 4 / 3;
  background: var(--thv-tint);
  overflow: hidden;
}
.thv-gallery__main img { width: 100%; height: 100%; object-fit: cover; }

.thv-gallery__count {
  position: absolute;
  right: 12px;
  bottom: 12px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 12px;
  border-radius: var(--thv-r-btn);
  background: rgba(9, 21, 40, .72);
  color: #fff;
  font-size: 12.5px;
  font-weight: 650;
}

.thv-gallery__strip {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 92px;
  gap: 6px;
  overflow-x: auto;
  scrollbar-width: none;
  scroll-snap-type: x proximity;
  padding-inline: var(--thv-gutter);
}
.thv-gallery__strip::-webkit-scrollbar { display: none; }
.thv-gallery__strip > * { scroll-snap-align: start; }
.thv-gallery__thumb {
  display: block;
  aspect-ratio: 4 / 3;
  border-radius: 8px;
  overflow: hidden;
  background: var(--thv-tint);
  border: 2px solid transparent;
  transition: border-color var(--thv-t-fast) var(--thv-ease);
}
.thv-gallery__thumb img { width: 100%; height: 100%; object-fit: cover; }
.thv-gallery__thumb.is-active { border-color: var(--thv-blue); }

@media (min-width: 768px) {
  .thv-gallery { border-radius: var(--thv-r-card); overflow: hidden; }
  .thv-gallery__main   { aspect-ratio: 16 / 9; }
  .thv-gallery__strip  { padding-inline: 0; grid-auto-columns: 116px; }
}

/* --- Title block ------------------------------------------------------ */
.thv-detail__head { padding-block: 18px 4px; }

.thv-detail__title {
  margin: 0;
  font-size: clamp(21px, 5vw, 30px);
  font-weight: 800;
  line-height: 1.18;
  letter-spacing: -.024em;
  color: var(--thv-navy);
  text-wrap: balance;
}
.thv-detail__price {
  display: block;
  margin-bottom: 6px;
  font-size: clamp(24px, 6vw, 32px);
  font-weight: 800;
  letter-spacing: -.026em;
  color: var(--thv-navy);
  font-variant-numeric: tabular-nums;
}
.thv-detail__price .price-period {
  font-size: 15px;
  font-weight: 600;
  color: var(--thv-ink-2);
}
.thv-detail__where {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin: 8px 0 0;
  font-size: 14.5px;
  color: var(--thv-ink-2);
}
.thv-detail__ref { color: var(--thv-ink-3); font-size: 13px; }

/* The container goes full-bleed below 768px so the gallery can reach
   both edges without a negative margin. */
.thv-container--flush { padding-inline: 0; }
@media (min-width: 768px) { .thv-container--flush { padding-inline: var(--thv-gutter); } }

/* Title on the left, the three tools on the right. They sit side by side
   from the narrowest width — a 42px icon row does not need its own line
   and putting it on one wastes the fold. */
.thv-detail__top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
}
.thv-detail__tools { display: flex; gap: 6px; flex: none; }

.thv-iconbtn {
  display: grid;
  place-items: center;
  width: 42px;
  height: 42px;
  border: 1px solid var(--thv-line);
  border-radius: var(--thv-r-btn);
  background: var(--thv-surface);
  color: var(--thv-ink-2);
  font-size: 16px;
  cursor: pointer;
  transition: border-color var(--thv-t-fast) var(--thv-ease),
              color var(--thv-t-fast) var(--thv-ease),
              transform var(--thv-t-fast) var(--thv-ease);
}
.thv-iconbtn:hover    { border-color: var(--thv-ink-3); color: var(--thv-navy); transform: translateY(-1px); }
.thv-iconbtn.is-saved { color: #FF5A78; border-color: #FFD4DC; }
/* Comparison needs two listings side by side, so it is desktop only —
   the same rule the card follows. */
.thv-iconbtn--desk { display: none; }
@media (min-width: 992px) { .thv-iconbtn--desk { display: grid; } }

/* --- Badge row --------------------------------------------------------
   Small, square-ish pills. Navy for what the listing IS, blue for what
   is notable about it, grey for everything else. The price is never one
   of these: it is type, not a badge. */
.thv-badges { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 10px; }

.thv-tagpill {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 10px;
  border: 1px solid transparent;
  border-radius: 7px;
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: .02em;
}
.thv-tagpill--navy  { background: var(--thv-navy); color: #fff; }
.thv-tagpill--blue  { background: var(--thv-blue); color: #fff; }
.thv-tagpill--green { background: #067A50; color: #fff; }

/* Eligibility labels are sentences, not words — "Work permit sponsorship
   available" wraps to three lines at 360px, and as a solid fill that is
   a heavy green block rather than a chip. Soft tint, coloured text. */
.thv-tagpill--eligible {
  background: #E9F7F1;
  border-color: #C6E9DA;
  color: #056445;
  text-align: left;
}
.thv-tagpill--eligible i { flex: none; }
.thv-tagpill--plain { background: var(--thv-tint); border-color: var(--thv-line); color: var(--thv-ink-2); }
.thv-tagpill--muted { background: var(--thv-ink-3); color: #fff; }
a.thv-tagpill--plain:hover { border-color: var(--thv-ink-3); color: var(--thv-navy); }

/* --- Job detail heading -----------------------------------------------
   The employer mark beside the title. It shrinks rather than wrapping
   under the title, because a 56px square on its own line costs a line of
   the headline at 360px. */
.thv-jobhead { display: flex; align-items: flex-start; gap: 13px; min-width: 0; }

.thv-jobhead__logo {
  display: grid;
  place-items: center;
  width: 56px;
  height: 56px;
  flex: none;
  padding: 8px;
  border: 1px solid var(--thv-line);
  border-radius: var(--thv-r-tile);
  background: var(--thv-surface);
  overflow: hidden;
}
.thv-jobhead__logo img { width: 100%; height: 100%; object-fit: contain; }
@media (min-width: 768px) { .thv-jobhead__logo { width: 64px; height: 64px; } }

.thv-detail__company { font-weight: 650; color: var(--thv-navy); }
a.thv-detail__company:hover { color: var(--thv-blue); }

/* A closed vacancy, and the sidebar's deadline warning, reuse .thv-note
   from section 27. This variant is the neutral one. */
.thv-note--closed {
  margin-bottom: 16px;
  border-color: var(--thv-line);
  border-left-color: var(--thv-ink-3);
  background: var(--thv-tint);
  color: var(--thv-ink);
}
.thv-note a { color: var(--thv-blue); text-decoration: underline; text-underline-offset: 2px; }

/* --- Body layout ------------------------------------------------------ */
.thv-detail__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 24px;
  padding-top: 18px;
}
@media (min-width: 992px) {
  .thv-detail__grid { grid-template-columns: minmax(0, 1fr) 332px; gap: 32px; align-items: start; }
}

.thv-panel {
  padding: 18px;
  margin-bottom: 16px;
  border: 1px solid var(--thv-line);
  border-radius: var(--thv-r-card);
  background: var(--thv-surface);
  box-shadow: var(--thv-sh-1);
}
.thv-panel__title {
  margin: 0 0 12px;
  font-size: 16px;
  font-weight: 750;
  letter-spacing: -.016em;
  color: var(--thv-navy);
}
@media (min-width: 768px) { .thv-panel { padding: 22px 24px; } }

/* Key facts. Two columns on a phone, as many as fit from 768px.
   The cells are separated by a 1px gap over a line-coloured background,
   which is cheaper than a border per cell — but it means an unfilled
   trailing cell shows as a grey rectangle. A listing can have anywhere
   between two and five facts, so:
     - `auto-fit` above 768px sizes the row to the number of facts
     - a lone last cell on a phone spans both columns
   Either way there is never an empty box. */
.thv-specs {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1px;
  border: 1px solid var(--thv-line);
  border-radius: var(--thv-r-card);
  background: var(--thv-line);
  overflow: hidden;
}
.thv-spec:last-child:nth-child(odd) { grid-column: 1 / -1; }

@media (min-width: 768px) {
  .thv-specs { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
  .thv-spec:last-child:nth-child(odd) { grid-column: auto; }
}

.thv-spec {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 14px;
  background: var(--thv-surface);
}
.thv-spec__k {
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--thv-ink-3);
}
.thv-spec__v {
  font-size: 15px;
  font-weight: 700;
  color: var(--thv-navy);
}

/* Amenity / feature list. */
.thv-feats {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px 14px;
}
@media (min-width: 768px) { .thv-feats { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.thv-feat {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  color: var(--thv-ink);
}
.thv-feat i { color: var(--th-secondary, #10B981); font-size: 15px; flex: none; }

/* --- Specification list ----------------------------------------------
   A <dl>, not a table. Eighteen rows of two cells is fine on a desktop
   and a squeezed 40/60 split on a phone, so the label stacks above the
   value below 576px and sits beside it above. No scroll container, so a
   long value can never push the page sideways. */
.thv-dl { margin: 0; }
.thv-dl__row {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 2px;
  padding-block: 10px;
  border-top: 1px solid var(--thv-line-soft);
}
.thv-dl__row:first-child { border-top: 0; padding-top: 0; }
.thv-dl dt {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--thv-ink-3);
}
.thv-dl dd {
  margin: 0;
  font-size: 14.5px;
  font-weight: 600;
  color: var(--thv-navy);
  overflow-wrap: break-word;
}
@media (min-width: 576px) {
  .thv-dl__row { grid-template-columns: 40% minmax(0, 1fr); gap: 12px; align-items: baseline; }
  .thv-dl dt   { font-size: 13.5px; }
}

/* The sidebar variant: label and value on one line at every width, since
   the values there are short (a date, a count). */
.thv-dl--tight { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--thv-line); }
.thv-dl--tight .thv-dl__row {
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 10px;
  align-items: baseline;
  padding-block: 5px;
  border-top: 0;
}
.thv-dl--tight dt { font-size: 13px; }
.thv-dl--tight dd { font-size: 13px; text-align: right; }

/* --- Map -------------------------------------------------------------- */
.thv-map {
  height: 260px;
  border-radius: var(--thv-r-tile);
  overflow: hidden;
  background: var(--thv-tint);
}
@media (min-width: 768px) { .thv-map { height: 340px; } }

.thv-hint {
  display: flex;
  align-items: flex-start;
  gap: 6px;
  margin: 10px 0 0;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--thv-ink-3);
}
.thv-hint--center { justify-content: center; margin-top: 14px; }

/* --- Payment terms ---------------------------------------------------- */
.thv-terms {
  margin-top: 14px;
  padding: 12px 13px;
  border: 1px solid var(--thv-line);
  border-radius: var(--thv-r-tile);
  background: var(--thv-tint);
}
.thv-terms__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 6px;
  font-size: 13.5px;
  font-weight: 700;
  color: var(--thv-navy);
}
.thv-terms__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding-block: 3px;
  font-size: 13px;
  color: var(--thv-ink-2);
}
.thv-terms__row span:last-child { font-weight: 650; color: var(--thv-navy); }

.thv-movein {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--thv-line);
  font-size: 13.5px;
  font-weight: 650;
  color: var(--thv-navy);
}
.thv-movein small {
  display: block;
  font-size: 11.5px;
  font-weight: 400;
  color: var(--thv-ink-3);
}
.thv-movein b { font-size: 17px; font-weight: 800; }

.thv-reportwrap { margin-top: 14px; text-align: center; }

/* Long-form description. */
.thv-prose { font-size: 15.5px; line-height: 1.7; color: var(--thv-ink); }
.thv-prose p { margin: 0 0 14px; }
.thv-prose p:last-child { margin-bottom: 0; }
.thv-prose h2, .thv-prose h3 {
  margin: 22px 0 10px;
  font-weight: 750;
  letter-spacing: -.016em;
  color: var(--thv-navy);
}
.thv-prose h2 { font-size: 19px; }
.thv-prose h3 { font-size: 16.5px; }
.thv-prose ul, .thv-prose ol { margin: 0 0 14px; padding-left: 20px; }
.thv-prose li { margin-bottom: 6px; }
.thv-prose a { color: var(--thv-blue); text-decoration: underline; text-underline-offset: 2px; }
.thv-prose img { border-radius: var(--thv-r-tile); margin-block: 8px; }

/* --- Contact / apply panel -------------------------------------------- */
@media (min-width: 992px) {
  .thv-detail__aside { position: sticky; top: calc(var(--th-header-h) + 16px); }
}

.thv-owner {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 14px;
}
.thv-owner__avatar {
  display: grid;
  place-items: center;
  width: 46px;
  height: 46px;
  flex: none;
  border-radius: 50%;
  background: var(--thv-navy);
  color: #fff;
  font-size: 16px;
  font-weight: 700;
  overflow: hidden;
}
.thv-owner__avatar img { width: 100%; height: 100%; object-fit: cover; }
.thv-owner__name { display: block; font-size: 15px; font-weight: 700; color: var(--thv-navy); }
.thv-owner__role { display: block; font-size: 13px; color: var(--thv-ink-2); }
.thv-verified { color: var(--thv-blue); font-size: 13px; }

.thv-actions { display: grid; gap: 8px; }

/* --- Mobile action bar ------------------------------------------------
   Fixed above the tab bar, not on top of it. Both heights are variables
   precisely so this cannot drift. */
/* `.th-wa-sticky` is the existing WhatsApp bar from includes/whatsapp-cta.php.
   It is styled here rather than replaced: the deep link, the
   central-number guarantee and the tracking beacon all live in that
   component, and rebuilding it in new markup would have risked all three
   to change a background colour. One rule, two class names. */
.thv-actbar,
.th-wa-sticky {
  position: fixed;
  left: 0;
  right: 0;
  /* ABOVE the tab bar, never over it.
     `inset: auto 0 0 0` pinned this to the bottom of the viewport and it
     sat on top of Home / Property / Jobs / News / Account — covering the
     site's own navigation to sell a lead. The tab bar is part of the
     approved design and stays visible on every page. */
  bottom: calc(var(--th-tabbar-h) + var(--th-safe-b));
  z-index: calc(var(--th-z-tabbar, 1020) - 1);
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px var(--thv-gutter);
  border-top: 1px solid var(--thv-line);
  background: rgba(255, 255, 255, .94);
  backdrop-filter: blur(16px) saturate(180%);
  -webkit-backdrop-filter: blur(16px) saturate(180%);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .thv-actbar, .th-wa-sticky { background: #fff; }
}

/* From 768px the sidebar's button is always in view, so a second fixed
   one is noise. */
@media (min-width: 768px) { .thv-actbar { display: none; } }
.thv-actbar .thv-btn { flex: 1; }

.thv-actbar__price,
.th-wa-sticky__price {
  display: flex;
  flex-direction: column;
  justify-content: center;
  flex: none;
  min-width: 0;
  font-size: 17px;
  font-weight: 800;
  color: var(--thv-navy);
  font-variant-numeric: tabular-nums;
  line-height: 1.2;
}
.th-wa-sticky__price .price-period,
.th-wa-sticky__price span {
  font-size: 11.5px;
  font-weight: 500;
  color: var(--thv-ink-3);
}
/* The bar's own button fills the rest of the row. */
.th-wa-sticky .btn { flex: 1; min-height: 46px; border-radius: var(--thv-r-btn); font-weight: 650; }

/* Above 768px the sidebar's contact panel is always in view, so a second
   fixed button is noise. */
@media (min-width: 768px) { .th-wa-sticky { display: none; } }

/* Sidebar price: smaller than the one at the top of the page, which is
   the dominant one. */
.thv-detail__price--aside { font-size: 26px; margin-bottom: 2px; }

/* Clearance for both bars stacked. 72px is the action bar's own height
   including its border; the tab bar's is already in --th-tabbar-h. */
@media (max-width: 767.98px) {
  body.th-has-actbar,
  body.th-has-wa-sticky {
    padding-bottom: calc(var(--th-tabbar-h) + var(--th-safe-b) + 72px);
  }

  /* The back-to-top button clears the action bar too. Without this it
     lands on the right-hand end of the Enquire button — the one control
     on the page that must not be mis-tapped. */
  body.th-has-actbar [data-to-top],
  body.th-has-wa-sticky [data-to-top] {
    bottom: calc(var(--th-tabbar-h) + var(--th-safe-b) + 84px);
  }
}


/* =====================================================================
   25  ARTICLE
   =====================================================================
   /news and /news/<slug>.

   Reading comfort is the whole brief here: 17-19px body text, a
   measured line length, and generous space between paragraphs.
   ===================================================================== */

/* --- /news layout ------------------------------------------------------
   The lead story spans the full width; below it the grid takes the main
   column and the sidebar follows on desktop only. On a phone that
   sidebar would be three screens of links between the last article and
   the footer, so it is not rendered there at all. */
.thv-lead-band { padding-top: 22px; }

.thv-news-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 24px;
  padding-top: 22px;
}
@media (min-width: 992px) {
  .thv-news-layout { grid-template-columns: minmax(0, 1fr) 288px; gap: 32px; align-items: start; }
}

.thv-nlist {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--thv-gap);
}
@media (min-width: 576px)  { .thv-nlist { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1280px) { .thv-nlist { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 20px; } }

.thv-aside { display: none; }

@media (min-width: 992px) {
  .thv-aside {
    display: block;
    position: sticky;
    top: calc(var(--th-header-h) + 16px);
    align-self: start;
  }
}
.thv-aside__block {
  padding: 18px;
  margin-bottom: 16px;
  border: 1px solid var(--thv-line);
  border-radius: var(--thv-r-card);
  background: var(--thv-surface);
}
.thv-aside__title {
  margin: 0 0 12px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--thv-ink-3);
}
.thv-aside .thv-art { border: 0; padding: 0 0 10px; }
.thv-aside .thv-art + .thv-art { border-top: 1px solid var(--thv-line-soft); padding-top: 10px; }
.thv-aside .thv-art:hover { transform: none; }

.thv-taglist { display: flex; flex-wrap: wrap; gap: 6px; }
.thv-tag {
  display: inline-flex;
  align-items: center;
  height: 30px;
  padding: 0 10px;
  border: 1px solid var(--thv-line);
  border-radius: var(--thv-r-pill);
  font-size: 12.5px;
  font-weight: 600;
  color: var(--thv-ink-2);
  transition: border-color var(--thv-t-fast) var(--thv-ease),
              color var(--thv-t-fast) var(--thv-ease);
}
.thv-tag:hover { border-color: var(--thv-ink-3); color: var(--thv-navy); }
.thv-tag.is-active { background: var(--thv-navy); border-color: var(--thv-navy); color: #fff; }

/* --- The article itself ------------------------------------------------ */
.thv-article { padding-bottom: var(--thv-sect-y); background: var(--thv-surface); }

/* Reading column plus sidebar. The column is capped by the body's own
   `max-width: 66ch`, so this grid only decides where the sidebar goes. */
.thv-article__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 28px;
}
@media (min-width: 992px) {
  .thv-article__grid { grid-template-columns: minmax(0, 1fr) 288px; gap: 40px; align-items: start; }
}

.thv-article__head { padding-block: 20px 0; }

.thv-tagpill--breaking { background: var(--th-danger, #EF4444); color: #fff; }

/* Share controls sit at the end of the byline rather than in a row of
   their own — one line of chrome instead of two above the headline. */
.thv-share--inline { margin: 0 0 0 auto; gap: 6px; }
.thv-share--inline .thv-iconbtn { width: 38px; height: 38px; font-size: 15px; }

.thv-article__source {
  max-width: 66ch;
  margin: 24px 0 0;
  padding-top: 16px;
  border-top: 1px solid var(--thv-line);
  font-size: 13.5px;
  color: var(--thv-ink-2);
}
.thv-article__source a { color: var(--thv-blue); }

.thv-article__tags {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 7px;
  max-width: 66ch;
  margin-top: 22px;
  padding-top: 18px;
  border-top: 1px solid var(--thv-line);
}

.thv-article__after {
  max-width: 66ch;
  margin-top: 28px;
  padding-top: 22px;
  border-top: 1px solid var(--thv-line);
}

.thv-article__title {
  margin: 10px 0 0;
  font-size: clamp(25px, 6.4vw, 42px);
  font-weight: 800;
  line-height: 1.14;
  letter-spacing: -.028em;
  color: var(--thv-navy);
  text-wrap: balance;
  max-width: 22ch;
}
.thv-article__standfirst {
  margin: 12px 0 0;
  max-width: 60ch;
  font-size: 17px;
  line-height: 1.55;
  color: var(--thv-ink-2);
}
.thv-article__byline {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 14px;
  margin-top: 16px;
  padding-block: 14px;
  border-block: 1px solid var(--thv-line);
  font-size: 13.5px;
  color: var(--thv-ink-2);
}
.thv-article__author { display: inline-flex; align-items: center; gap: 8px; font-weight: 650; color: var(--thv-navy); }

.thv-article__hero {
  margin: 18px 0 0;
  border-radius: var(--thv-r-card);
  overflow: hidden;
  background: var(--thv-tint);
}
/* Cropped to 16:9 rather than left at its natural height. News covers are
   usually landscape, but an editor can upload a portrait photograph and
   `height: auto` then produced a 1,000px-tall block that pushed the whole
   article below the fold. */
.thv-article__hero img {
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  display: block;
}
.thv-article__caption {
  margin: 8px 0 0;
  font-size: 12.5px;
  color: var(--thv-ink-3);
}

/* 66 characters is the measure. Anything wider and the eye loses the
   start of the next line on a desktop. */
.thv-article__body {
  max-width: 66ch;
  margin-top: 22px;
  font-size: 17px;
  line-height: 1.75;
  color: var(--thv-ink);
}
@media (min-width: 768px) { .thv-article__body { font-size: 18px; } }

.thv-article__body p { margin: 0 0 20px; }
.thv-article__body h2 {
  margin: 32px 0 12px;
  font-size: clamp(20px, 3.4vw, 25px);
  font-weight: 800;
  letter-spacing: -.02em;
  color: var(--thv-navy);
}
.thv-article__body h3 {
  margin: 26px 0 10px;
  font-size: clamp(17px, 2.8vw, 20px);
  font-weight: 750;
  color: var(--thv-navy);
}
.thv-article__body ul, .thv-article__body ol { margin: 0 0 20px; padding-left: 22px; }
.thv-article__body li { margin-bottom: 8px; }
.thv-article__body a { color: var(--thv-blue); text-decoration: underline; text-underline-offset: 3px; }
.thv-article__body img { border-radius: var(--thv-r-tile); margin-block: 10px; }
.thv-article__body blockquote {
  margin: 24px 0;
  padding: 4px 0 4px 18px;
  border-left: 3px solid var(--thv-blue);
  font-size: 1.05em;
  color: var(--thv-navy);
}

.thv-share { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 26px; }
.thv-share__label { font-size: 13px; font-weight: 650; color: var(--thv-ink-2); margin-right: 2px; }
.thv-share a, .thv-share button {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border: 1px solid var(--thv-line);
  border-radius: var(--thv-r-btn);
  background: var(--thv-surface);
  color: var(--thv-ink-2);
  font-size: 16px;
  cursor: pointer;
  transition: border-color var(--thv-t-fast) var(--thv-ease),
              color var(--thv-t-fast) var(--thv-ease),
              transform var(--thv-t-fast) var(--thv-ease);
}
.thv-share a:hover, .thv-share button:hover {
  border-color: var(--thv-ink-3);
  color: var(--thv-navy);
  transform: translateY(-1px);
}


/* =====================================================================
   26  BUSINESS
   =====================================================================
   A directory row: logo, name, category, rating, town, one line about
   it, and whether it is open. Not a photo tile — a directory is
   scanned, and a wall of stock interiors makes every entry look alike.
   ===================================================================== */

.thv-biz {
  position: relative;
  display: grid;
  grid-template-columns: 64px minmax(0, 1fr);
  gap: 13px;
  padding: 14px;
  border: 1px solid var(--thv-line);
  border-radius: var(--thv-r-card);
  background: var(--thv-surface);
  box-shadow: var(--thv-sh-1);
  transition: transform var(--thv-t-base) var(--thv-ease),
              box-shadow var(--thv-t-base) var(--thv-ease);
}
@media (hover: hover) {
  .thv-biz:hover { transform: translateY(-2px); box-shadow: var(--thv-sh-2); }
}

.thv-biz__logo {
  display: grid;
  place-items: center;
  width: 64px;
  height: 64px;
  border-radius: var(--thv-r-tile);
  border: 1px solid var(--thv-line-soft);
  background: var(--thv-tint);
  color: var(--thv-ink-3);
  font-size: 22px;
  overflow: hidden;
}
.thv-biz__logo img { width: 100%; height: 100%; object-fit: cover; }

.thv-biz__body { min-width: 0; }

.thv-biz__name {
  display: block;
  font-size: 15.5px;
  font-weight: 750;
  letter-spacing: -.014em;
  line-height: 1.3;
  color: var(--thv-navy);
}
.thv-biz__name::after { content: ""; position: absolute; inset: 0; border-radius: inherit; }

.thv-biz__cat {
  display: block;
  margin-top: 2px;
  font-size: 13px;
  color: var(--thv-ink-2);
}
.thv-biz__desc {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  margin-top: 6px;
  font-size: 13px;
  line-height: 1.5;
  color: var(--thv-ink-2);
}
.thv-biz__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 10px;
  margin-top: 9px;
  font-size: 12.5px;
  color: var(--thv-ink-2);
}

/* Rating. The star is gold, the number is navy — a five-star row in
   brand blue reads as five links. */
.thv-rating {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-weight: 700;
  color: var(--thv-navy);
  font-variant-numeric: tabular-nums;
}
.thv-rating i { color: #F5A524; font-size: 13px; }
.thv-rating span { font-weight: 500; color: var(--thv-ink-3); }

.thv-open {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-weight: 650;
}
.thv-open::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: currentColor;
}
.thv-open--yes { color: #0B8A5A; }
.thv-open--no  { color: var(--thv-ink-3); }


/* --- /businesses/<slug> -----------------------------------------------
   The profile header: a full-bleed cover, the logo rising into it, then
   the name, rating and opening state. */
.thv-bizcover {
  height: clamp(140px, 24vw, 260px);
  overflow: hidden;
  background: var(--thv-tint);
}
.thv-bizcover img { width: 100%; height: 100%; object-fit: cover; }

.thv-bizhero { padding-bottom: 4px; }

/* The overlap is a margin here rather than an inline negative margin on
   the image, which is what used to collide with the text at 360px. */
.thv-bizhero__logo {
  display: grid;
  place-items: center;
  width: 84px;
  height: 84px;
  margin-top: -42px;
  margin-bottom: 12px;
  padding: 8px;
  border: 1px solid var(--thv-line);
  border-radius: var(--thv-r-card);
  background: var(--thv-surface);
  box-shadow: var(--thv-sh-2);
  overflow: hidden;
}
.thv-bizhero__logo img { width: 100%; height: 100%; object-fit: contain; }
@media (min-width: 768px) {
  .thv-bizhero__logo { width: 96px; height: 96px; margin-top: -48px; }
}

.thv-bizhero__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 14px;
  margin-top: 10px;
  font-size: 13.5px;
  color: var(--thv-ink-2);
}
.thv-bizhero__meta .thv-rating a { color: var(--thv-ink-3); font-weight: 500; }
.thv-bizhero__meta .thv-rating a:hover { color: var(--thv-blue); }
.thv-bizhero__where { display: inline-flex; align-items: center; gap: 5px; }

.thv-bizhero .thv-badges { margin-top: 12px; margin-bottom: 0; }

.thv-biztagline {
  margin: 0 0 12px;
  font-size: 16px;
  font-weight: 600;
  line-height: 1.5;
  color: var(--thv-navy);
}

/* Photos. Square thumbnails, three across on a phone. */
.thv-bizshots {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
}
@media (min-width: 768px) { .thv-bizshots { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

.thv-bizshot {
  display: block;
  padding: 0;
  aspect-ratio: 1;
  border: 0;
  border-radius: var(--thv-r-tile);
  overflow: hidden;
  background: var(--thv-tint);
  cursor: pointer;
}
.thv-bizshot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--th-slow, .4s) var(--thv-ease-out);
}
@media (hover: hover) { .thv-bizshot:hover img { transform: scale(1.04); } }

/* Opening hours. A definition list, not a table: seven rows of two short
   cells needed a horizontal scroll container on a phone and never earned
   one. */
.thv-hours { margin: 0; }
.thv-hours__row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 12px;
  align-items: baseline;
  padding: 9px 10px;
  border-radius: 8px;
  font-size: 14px;
}
.thv-hours__row + .thv-hours__row { border-top: 1px solid var(--thv-line-soft); border-radius: 0; }
.thv-hours dt { color: var(--thv-ink-2); }
.thv-hours dd { margin: 0; font-weight: 650; color: var(--thv-navy); text-align: right; }
.thv-hours__none { font-weight: 400; color: var(--thv-ink-3); }

.thv-hours__row.is-today {
  background: var(--thv-blue-soft);
  border-top-color: transparent;
  border-radius: 8px;
}
.thv-hours__row.is-today dt { color: var(--thv-blue-dark); font-weight: 650; }
.thv-hours__today {
  margin-left: 6px;
  padding: 2px 7px;
  border-radius: var(--thv-r-pill);
  background: var(--thv-blue);
  color: #fff;
  font-size: 10.5px;
  font-weight: 700;
}

/* A panel with a heading and one control on the same line. */
.thv-panel__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 12px;
}
.thv-panel__head .thv-panel__title { margin: 0; }

/* The unclaimed-listing prompt. */
.thv-panel--claim { border-color: var(--th-primary-border, #BFDBFE); background: var(--thv-blue-soft); }
.thv-claim__title {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin: 0 0 6px;
  font-size: 14px;
  font-weight: 700;
  color: var(--thv-blue-dark);
}

/* WhatsApp keeps its own colour — it is a recognised brand action, and
   the one place green is allowed on a content page. */
.thv-btn--wa { background: #25D366; border-color: #25D366; color: #08301A; }
.thv-btn--wa:hover { background: #1FBF5B; border-color: #1FBF5B; color: #08301A; transform: translateY(-1px); }


/* =====================================================================
   27  ACCOUNT
   =====================================================================
   A phone app's account tab, not an admin dashboard: a profile header,
   then grouped rows with an icon, a label and a chevron.
   ===================================================================== */

.thv-acct { padding-bottom: var(--thv-sect-y); background: var(--thv-page); }

.thv-acct__head {
  padding-block: 22px;
  background: var(--thv-surface);
  border-bottom: 1px solid var(--thv-line);
}
.thv-acct__id {
  display: flex;
  align-items: center;
  gap: 14px;
}
.thv-acct__avatar {
  display: grid;
  place-items: center;
  width: 62px;
  height: 62px;
  flex: none;
  border-radius: 50%;
  background: var(--thv-navy);
  color: #fff;
  font-size: 22px;
  font-weight: 700;
  overflow: hidden;
}
.thv-acct__avatar img { width: 100%; height: 100%; object-fit: cover; }
.thv-acct__name {
  margin: 0;
  font-size: 20px;
  font-weight: 800;
  letter-spacing: -.02em;
  color: var(--thv-navy);
}
.thv-acct__meta {
  margin: 2px 0 0;
  font-size: 13.5px;
  color: var(--thv-ink-2);
}

.thv-group { margin-top: 22px; }

/* A single standing notice — something of theirs is with a moderator. */
.thv-note {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 13px 15px;
  border: 1px solid var(--th-accent-soft, #FFFBEB);
  border-left: 3px solid var(--th-accent, #F59E0B);
  border-radius: var(--thv-r-tile);
  background: var(--th-accent-soft, #FFFBEB);
  font-size: 13.5px;
  line-height: 1.5;
  color: #7A4E06;
}
.thv-note i { font-size: 15px; flex: none; margin-top: 1px; }
.thv-group__title {
  margin: 0 0 8px;
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--thv-ink-3);
}
.thv-rows {
  border: 1px solid var(--thv-line);
  border-radius: var(--thv-r-card);
  background: var(--thv-surface);
  overflow: hidden;
}
.thv-row {
  display: flex;
  align-items: center;
  gap: 13px;
  min-height: 58px;
  padding: 11px 14px;
  color: var(--thv-ink);
  transition: background-color var(--thv-t-fast) var(--thv-ease);
}
.thv-row + .thv-row { border-top: 1px solid var(--thv-line-soft); }
.thv-row:hover { background: var(--thv-tint); }

.thv-row__icon {
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  flex: none;
  border-radius: 10px;
  background: var(--thv-tint);
  color: var(--thv-navy);
  font-size: 16px;
}
.thv-row__body { min-width: 0; flex: 1; }
.thv-row__label { display: block; font-size: 14.5px; font-weight: 650; color: var(--thv-navy); }
.thv-row__desc {
  display: block;
  margin-top: 1px;
  font-size: 12.5px;
  color: var(--thv-ink-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.thv-row__end {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  flex: none;
  color: var(--thv-ink-3);
  font-size: 13px;
}
.thv-row__n {
  display: inline-grid;
  place-items: center;
  min-width: 22px;
  height: 22px;
  padding-inline: 6px;
  border-radius: var(--thv-r-pill);
  background: var(--thv-tint);
  color: var(--thv-ink-2);
  font-size: 12px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.thv-row--danger .thv-row__label { color: var(--th-danger, #EF4444); }
.thv-row--danger .thv-row__icon  { background: #FEF2F2; color: var(--th-danger, #EF4444); }

/* Desktop: the groups become two columns rather than one long column of
   rows, and the profile header sits beside them. */
@media (min-width: 768px) {
  .thv-acct__groups {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0 28px;
    align-items: start;
  }
}
@media (min-width: 1200px) {
  .thv-acct__groups { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* --- Account subpages -------------------------------------------------
   /user/settings, /saved, /applications, /messages, /security.

   Sidebar plus content from 992px. Below that the navigation becomes a
   scrolling row of tabs above the content: a 400px-tall sidebar stacked
   on top of every account page pushed the actual content off a phone
   screen entirely. */
.thv-acct__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 20px;
  padding-block: 20px;
  align-items: start;
}
@media (min-width: 992px) {
  .thv-acct__grid { grid-template-columns: 258px minmax(0, 1fr); gap: 28px; padding-block: 26px; }
}

.thv-unav__me,
.thv-unav__extra { display: none; }

.thv-unav__list {
  display: flex;
  gap: 6px;
  overflow-x: auto;
  scrollbar-width: none;
  scroll-snap-type: x proximity;
  padding-bottom: 2px;
}
.thv-unav__list::-webkit-scrollbar { display: none; }
.thv-unav__list > * { scroll-snap-align: start; }

.thv-unav__item {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  flex: none;
  min-height: 40px;
  padding: 0 13px;
  border: 1px solid var(--thv-line);
  border-radius: var(--thv-r-pill);
  background: var(--thv-surface);
  font-size: 13.5px;
  font-weight: 600;
  color: var(--thv-ink-2);
  white-space: nowrap;
  transition: border-color var(--thv-t-fast) var(--thv-ease),
              background-color var(--thv-t-fast) var(--thv-ease),
              color var(--thv-t-fast) var(--thv-ease);
}
.thv-unav__item i { font-size: 14px; }
.thv-unav__item:hover { border-color: var(--thv-ink-3); color: var(--thv-navy); }
.thv-unav__item.is-active {
  background: var(--thv-navy);
  border-color: var(--thv-navy);
  color: #fff;
}
.thv-unav__item.is-active .thv-row__n { background: rgba(255, 255, 255, .18); color: #fff; }

@media (min-width: 992px) {
  .thv-unav {
    position: sticky;
    top: calc(var(--th-header-h) + 16px);
    padding: 16px;
    border: 1px solid var(--thv-line);
    border-radius: var(--thv-r-card);
    background: var(--thv-surface);
    box-shadow: var(--thv-sh-1);
  }

  .thv-unav__me {
    display: flex;
    align-items: center;
    gap: 11px;
    padding-bottom: 14px;
    margin-bottom: 12px;
    border-bottom: 1px solid var(--thv-line);
  }
  .thv-unav__name {
    display: block;
    font-size: 14.5px;
    font-weight: 700;
    color: var(--thv-navy);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .thv-unav__mail {
    display: block;
    font-size: 12px;
    color: var(--thv-ink-3);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .thv-unav__list { flex-direction: column; gap: 2px; overflow: visible; }

  .thv-unav__item {
    width: 100%;
    border: 0;
    border-radius: var(--thv-r-btn);
    background: none;
    padding: 0 10px;
    font-size: 14px;
  }
  .thv-unav__item span:not(.thv-row__n) { flex: 1; }
  .thv-unav__item:hover { background: var(--thv-tint); }
  .thv-unav__item.is-active { background: var(--thv-blue-soft); color: var(--thv-blue-dark); }
  .thv-unav__item.is-active .thv-row__n { background: var(--thv-blue); color: #fff; }

  .thv-unav__extra {
    display: block;
    margin-top: 12px;
    padding-top: 12px;
    border-top: 1px solid var(--thv-line);
  }
  .thv-unav__out { margin-top: 12px; }
}

/* The panels the subpages are built from reuse .thv-panel; this is the
   inner wrapper the old card markup carried. */
.thv-panel__inner { display: block; }

/* An application row, and a conversation row. Both are the job card's
   shape reused rather than a third row component. */
.thv-appl { display: flex; align-items: flex-start; gap: 12px; }
.thv-appl .thv-btn { flex: none; margin-left: auto; }

.thv-status {
  display: inline-flex;
  align-items: center;
  padding: 3px 9px;
  border-radius: 6px;
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: .02em;
}
.thv-status--ok   { background: #E9F7F1; color: #056445; }
.thv-status--wait { background: var(--th-accent-soft, #FFFBEB); color: #7A4E06; }
.thv-status--no   { background: #FEF2F2; color: #8A1C1C; }

.thv-thread { display: flex; align-items: center; gap: 12px; color: inherit; }
.thv-thread__who {
  display: block;
  font-size: 14.5px;
  font-weight: 700;
  color: var(--thv-navy);
}
.thv-thread__last {
  display: block;
  margin-top: 2px;
  font-size: 13px;
  color: var(--thv-ink-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.thv-thread__end {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 6px;
  flex: none;
}
.thv-thread__dot { width: 9px; height: 9px; border-radius: 50%; background: var(--thv-blue); }
.thv-thread.is-unread .thv-thread__last { color: var(--thv-navy); font-weight: 600; }
@media (hover: hover) {
  .thv-thread:hover { border-color: var(--th-primary-border, #BFDBFE); }
}

/* Notice variants. .thv-note itself is defined above. */
.thv-note--info   { border-left-color: var(--thv-blue); background: var(--thv-blue-soft); color: var(--thv-blue-dark); }
.thv-note--danger { border-left-color: var(--th-danger, #EF4444); background: #FEF2F2; color: #8A1C1C; }
.thv-note--ok     { border-left-color: #0B8A5A; background: #E9F7F1; color: #056445; }

/* Small stat tiles on the account landing. */
.thv-stats {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
  margin-top: 18px;
}
@media (min-width: 576px) { .thv-stats { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

.thv-stat {
  padding: 14px;
  border: 1px solid var(--thv-line);
  border-radius: var(--thv-r-tile);
  background: var(--thv-surface);
  text-align: center;
}
.thv-stat__n {
  display: block;
  font-size: 21px;
  font-weight: 800;
  letter-spacing: -.02em;
  color: var(--thv-navy);
  font-variant-numeric: tabular-nums;
}
.thv-stat__k {
  display: block;
  margin-top: 2px;
  font-size: 12px;
  color: var(--thv-ink-2);
}


/* =====================================================================
   28  ANIMATIONS
   =====================================================================
   Entrance animation is `.th-reveal` -> `.is-visible`, driven by the
   IntersectionObserver that is already in trnchub.js. It unobserves
   each element after it fires, so nothing stays subscribed and no
   element animates twice.

   Stagger is expressed as `--thv-i` on the element (0, 1, 2 …) and
   capped at 6 steps of 55ms. Capping is the point: without it, the
   eighth card in a rail waits 440ms and the visitor watches an empty
   grid fill itself.
   ===================================================================== */

/* THE HIDDEN STATE IS GATED ON `.th-js`, WHICH IS NOT OPTIONAL.
   -------------------------------------------------------------------
   `.th-reveal` starts at opacity 0 and is revealed by a class the
   JavaScript adds. Every section on this page carries it — so if the
   script never runs, an ungated rule would leave the visitor looking at
   a blank white page rather than at a page without animation.

   header.php sets `.th-js` on <html> from an inline script before the
   first paint, and the same script sweeps in anything still hidden a
   moment after load. With JavaScript off, the class is never added and
   the content is simply visible. There is no state in which the page
   is blank. */
.th-reveal {
  transition: opacity 420ms var(--thv-ease-out),
              transform 420ms var(--thv-ease-out);
  transition-delay: calc(min(var(--thv-i, 0), 6) * 55ms);
}
.th-js .th-reveal {
  opacity: 0;
  transform: translateY(16px);
  will-change: opacity, transform;
}
.th-reveal.is-visible {
  opacity: 1;
  transform: none;
  /* Released once the element has arrived: a permanent `will-change`
     keeps a composited layer alive for every card on the page. */
  will-change: auto;
}

/* Cards travel a little less than a section does. */
.th-js .thv-card.th-reveal,
.th-js .thv-job.th-reveal,
.th-js .thv-art.th-reveal { transform: translateY(12px); }

/* The hero is above the fold and never waits for an observer. */
.thv-hero__inner > * {
  animation: thv-rise 500ms var(--thv-ease-out) both;
}
.thv-hero__inner > *:nth-child(2) { animation-delay: 60ms; }
.thv-hero__inner > *:nth-child(3) { animation-delay: 110ms; }
.thv-hero__inner > *:nth-child(4) { animation-delay: 160ms; }
.thv-hero__inner > *:nth-child(5) { animation-delay: 210ms; }

@keyframes thv-rise {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: none; }
}


/* =====================================================================
   29  UTILITIES
   =====================================================================
   Nine, and each one earns its place. This is not a utility framework —
   Bootstrap is already loaded for that.
   ===================================================================== */

.thv-sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.thv-clamp-1 {
  display: -webkit-box;
  -webkit-line-clamp: 1;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.thv-clamp-2 { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }

.thv-stack   { display: grid; gap: var(--thv-gap); }
.thv-flow > * + * { margin-top: 10px; }
.thv-mt-0    { margin-top: 0 !important; }

/* The skip link. Off-screen until focused, then the first thing on the
   page — which is the only reason it is allowed to animate `top`. */
.th-skip-link {
  position: absolute;
  top: -60px;
  left: 16px;
  z-index: 2000;
  padding: 10px 16px;
  border-radius: 0 0 var(--thv-r-btn) var(--thv-r-btn);
  background: var(--thv-blue);
  color: #fff;
  font-size: 14px;
  font-weight: 650;
}
.th-skip-link:focus { top: 0; color: #fff; }


/* =====================================================================
   30  RESPONSIVE
   =====================================================================
   Almost every breakpoint in this file lives beside the component it
   changes, which is why this section is short. What is left here is the
   cross-component behaviour: what happens to the page as a whole.

   Tested at 360 / 375 / 390 / 412 / 576 / 768 / 992 / 1024 / 1200 /
   1440px. The acceptance test is `scrollWidth <= clientWidth` on the
   documentElement at every one of them.
   ===================================================================== */

/* Very narrow phones. Nothing is hidden; things get slightly tighter. */
@media (max-width: 359.98px) {
  :root { --thv-gutter: 12px; }
  .thv-town { width: 136px; }
  .thv-type { width: 148px; }
  .thv-h1   { font-size: 24px; }
  .th-logo__name  { display: none; }
}

/* Tablet: the hero centres, the rails start to breathe. */
@media (min-width: 768px) {
  .thv-head { margin-bottom: 20px; }
  .thv-head__sub { font-size: 14.5px; }
}

/* Desktop: hover states become available and rails become grids. */
@media (min-width: 1200px) {
  .thv-rail { padding-block: 0; }
}

/* --- Reduced motion --------------------------------------------------
   Everything non-essential stops. Reveal elements are made visible
   immediately rather than left at opacity 0 — an animation that never
   runs must not be able to hide content. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
  .th-reveal {
    opacity: 1 !important;
    transform: none !important;
    transition-delay: 0ms !important;
  }
  .thv-hero__inner > * { animation: none; }
  .thv-rail { scroll-snap-type: none; }
}

/* --- Forced colours / high contrast ---------------------------------- */
@media (forced-colors: active) {
  .thv-card,
  .thv-job,
  .thv-tile,
  .thv-type,
  .thv-pick { border: 1px solid CanvasText; }
  .thv-hero__scrim { display: none; }
}

/* --- Print ------------------------------------------------------------ */
@media print {
  .th-header,
  .th-tabbar,
  .thv-search,
  .thv-cta,
  .th-newsletter { display: none !important; }
  .th-reveal { opacity: 1; transform: none; }
  .thv-hero { min-height: 0; background: none; color: #000; }
}

/* =====================================================================
   Facet introduction  (includes/facet-intro.php)
   ---------------------------------------------------------------------
   The band between a town or category heading and its list of results.
   It carries the editor-written description of the town and the links
   into the same town in the other modules — the two things that stop a
   /jobs?city=girne page being a heading followed by cards.

   Sits inside .thv-page, above .thv-results, and is simply absent when
   there is nothing to put in it.
   ===================================================================== */
.thv-facetintro {
    padding: 20px 0 4px;
}

.thv-facetintro__body {
    max-width: 68ch;          /* a comfortable measure, not the full grid */
    color: var(--thv-text, #374151);
    font-size: 15px;
    line-height: 1.7;
}

.thv-facetintro__links {
    margin: 12px 0 0;
    font-size: 14px;
    color: var(--thv-text-muted, #6B7280);
}

.thv-facetintro__links a {
    color: var(--thv-primary, #2563EB);
    text-decoration: none;
    font-weight: 500;
}

.thv-facetintro__links a:hover,
.thv-facetintro__links a:focus-visible {
    text-decoration: underline;
}

@media (min-width: 992px) {
    .thv-facetintro { padding: 28px 0 8px; }
    .thv-facetintro__body { font-size: 16px; }
}

/* ---------------------------------------------------------------------
   "This listing does not state…"  (properties/detail.php)
   ---------------------------------------------------------------------
   Deliberately quiet: a neutral note, not a warning. The listing is not
   defective for omitting a floor area — most of them do — and styling
   this as an error would make every property on the site look broken.
   It exists so a reader knows what to ask on the phone.
   --------------------------------------------------------------------- */
.thv-notstated {
    display: flex;
    gap: .625rem;
    align-items: flex-start;
    margin: 1rem 0 0;
    padding: .75rem .875rem;
    border-radius: 8px;
    background: var(--thv-surface-2, #F9FAFB);
    color: var(--thv-text-muted, #6B7280);
    font-size: 14px;
    line-height: 1.55;
}

.thv-notstated i { margin-top: .15em; flex-shrink: 0; }
.thv-notstated strong { color: var(--thv-text, #374151); font-weight: 600; }

/* Live marketplace counts above a town's listings (facet-intro.php). */
.thv-facetintro__stats {
    margin: 0 0 .75rem;
    color: var(--thv-text-muted, #6B7280);
    font-size: 14px;
}

.thv-facetintro__stat {
    display: inline-block;
    color: var(--thv-text, #374151);
    font-weight: 600;
    white-space: nowrap;
}

/* ---------------------------------------------------------------------
   Published correction notice  (news/detail.php)
   ---------------------------------------------------------------------
   Sits above the body, not at the foot. A correction a reader has to
   scroll to find is not a correction — the person who needs it is the
   one who already read the original.
   --------------------------------------------------------------------- */
.thv-correction {
    display: flex;
    gap: .625rem;
    align-items: flex-start;
    margin: 0 0 1.5rem;
    padding: .875rem 1rem;
    border: 1px solid var(--thv-warning-border, #FDE68A);
    border-left: 3px solid var(--thv-warning, #D97706);
    border-radius: 8px;
    background: var(--thv-warning-bg, #FFFBEB);
    color: var(--thv-text, #374151);
    font-size: 14px;
    line-height: 1.6;
}

.thv-correction i { margin-top: .15em; flex-shrink: 0; color: var(--thv-warning, #D97706); }
.thv-correction time { white-space: nowrap; }
