/*
  Palette sampled directly from design-reference/*.png (pixel-verified, not
  guessed) -- see katapts-54g.11 close notes for the sampling method.
  Contrast checked against WCAG AA (4.5:1 normal text, 3:1 large/bold text
  and graphical objects):
  - body text #1e2022 on #ffffff        -> ~16.3:1
  - muted text #4f4f4f on #ffffff       -> ~8.2:1
  - brand-primary #4c4107 vs white text -> ~10.2:1
  - accent-green #174f3c vs white text  -> ~9.5:1
  - accent-gold #ba8748 is reserved for icons/fills and large (>=19px) bold
    text (~3.2:1, meets the 3:1 large-text/graphical-object threshold);
    --color-link is a darkened gold (~4.7:1) for body-copy-sized text so
    inline links stay AA-compliant at any size.
  - navy #232a5f vs white text          -> ~13.4:1

  Focus ring: two concentric rings (dark + amber) so the *outer* ring
  (the one touching the surrounding page) always has >=3:1 contrast
  against it, on both light backgrounds (outer = dark, ~10.6:1 vs white)
  and dark brand-primary/navy surfaces (outer = amber, ~6.2:1 vs #4c4107).
  Neither single color clears 3:1 against both on its own, so any dark
  "island" sitting on a light page (the header brand badge, the header
  CTA button) swaps which ring is drawn outermost, same as the fully-dark
  footer.
*/
:root {
  --color-bg: #ffffff;
  --color-text: #1e2022;
  --color-text-muted: #4f4f4f;
  --color-border: #d8d8d8;

  --color-brand-primary: #4c4107;
  --color-brand-primary-hover: #3a3205;

  --color-brand-accent-green: #174f3c;
  --color-brand-accent-green-hover: #123e2f;

  --color-brand-accent-gold: #ba8748;
  --color-link: #966c39;
  --color-link-hover: #835f31;

  --color-navy: #232a5f;
  --color-neutral: #606060;
  --color-neutral-hover: #4a4a4a;

  --font-serif: "Lora", Georgia, "Times New Roman", serif;
  --font-sans: "Montserrat", "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

  --focus-a: #ffbf47; /* inner ring, 3px */
  --focus-b: #1a1a1a; /* outer ring, 6px - touches surrounding background */
}

/* Dark surfaces sitting on the (light) page need the ring order swapped so
   the *outer* ring still contrasts against them -- see :root comment above. */
.site-footer,
.site-header__brand,
.btn--header,
.investment-band,
.app-band {
  --focus-a: #1a1a1a;
  --focus-b: #ffbf47;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  background: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-serif);
  line-height: 1.6;
}

h1,
h2,
h3 {
  font-family: var(--font-serif);
  font-weight: 500;
  line-height: 1.25;
}

main h1,
main h2 {
  color: var(--color-brand-accent-green);
  font-weight: 500;
  padding-bottom: 1.25rem;
  border-bottom: 1px solid var(--color-border);
  margin: 0 0 1.5rem;
}

a {
  color: var(--color-link);
  text-decoration: underline;
}

a:hover {
  color: var(--color-link-hover);
}

/* Visually hidden until focused, so keyboard users can jump past the nav */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--color-brand-primary);
  color: #ffffff;
  padding: 0.75rem 1rem;
  z-index: 100;
  --focus-a: #1a1a1a;
  --focus-b: #ffbf47;
}

.skip-link:focus {
  left: 0.5rem;
  top: 0.5rem;
}

/*
  The live site's header is a WHITE bar with dark nav text -- the brand
  color only appears as the logo badge on the left and the "Pay Rent"
  button on the right, not as a full-width header background. (The
  rejected first attempt at this issue painted the whole header bar
  brand-primary with white nav text, which is why it read as "mixing
  palettes" -- the header and footer are not the same component.)
*/
.site-header {
  background: var(--color-bg);
  color: var(--color-text);
  box-shadow: 0 6px 12px -2px rgba(0, 0, 0, 0.12);
  position: relative;
  z-index: 5;
}

.site-header__inner {
  max-width: 100rem;
  margin: 0 auto;
  padding: 1rem 1.5rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  flex-wrap: wrap;
}

.site-header__brand {
  background: var(--color-brand-primary);
  display: inline-flex;
  align-items: center;
  padding: 0.35rem 0.5rem;
}

.site-header__brand-logo {
  display: block;
  height: clamp(3.5rem, 6vw, 5.5rem);
  width: auto;
}

.site-header__brand:hover {
  background: var(--color-brand-primary-hover);
  text-decoration: underline;
}

.site-nav {
  flex: 1 1 auto;
  display: flex;
  justify-content: center;
}

.site-header__cta {
  flex: 0 0 auto;
}

.nav-list {
  list-style: none;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 1.75rem;
  margin: 0;
  padding: 0;
}

.nav-list a {
  font-family: var(--font-sans);
  color: var(--color-text);
  text-decoration: none;
  font-weight: 600;
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.nav-list a:hover {
  color: var(--color-link);
  text-decoration: underline;
  text-underline-offset: 0.3em;
}

.nav-list a[aria-current="page"] {
  color: var(--color-link);
  text-decoration: underline;
  text-underline-offset: 0.3em;
}

/* Dropdown submenus, built on <details>/<summary> so they're keyboard-
   operable (Tab + Enter/Space) as a fallback. Mouse users get hover-open/
   close on top of that to match the live site: nav.js sets/clears the
   native [open] attribute on mouseenter/mouseleave, the same attribute
   the browser itself toggles on click, so showing/hiding always goes
   through the one code path the browser already handles correctly. (A
   pure-CSS version of this -- revealing .nav-submenu via a :hover/
   :focus-within rule on an ancestor -- looks right in devtools but does
   not actually repaint in Chromium: <details> content is hidden with
   content-visibility, and toggling that property via an ancestor
   pseudo-class match doesn't reliably unlock/repaint it. Real attribute
   mutation, as click already did natively, avoids that bug entirely.) */
.nav-list__dropdown {
  position: relative;
}

/* .nav-submenu sits `margin-top: 0.5rem` below the summary for a visible
   gap -- but that gap is dead space no element covers, so a mouse moving
   straight down from the summary into the submenu crosses pixels that
   hit-test to whatever's behind the nav (not .nav-list__dropdown or any
   descendant), firing mouseleave and closing the dropdown before the
   pointer ever reaches the menu. This invisible pseudo-element bridges
   that gap: generated content is hit-tested as part of its host element,
   so hovering it still counts as hovering .nav-list__dropdown, keeping
   nav.js's mouseenter/mouseleave listeners from seeing a leave event
   while crossing the gap. See katapts nav hover bug report 2026-09-16. */
.nav-list__dropdown::before {
  content: "";
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  height: 0.5rem;
}

.nav-list__dropdown summary {
  display: inline-flex;
  align-items: center;
  font-family: var(--font-sans);
  color: var(--color-text);
  font-weight: 600;
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  cursor: pointer;
  list-style: none;
}

.nav-list__dropdown summary::-webkit-details-marker {
  display: none;
}

.nav-list__dropdown summary::marker {
  content: "";
}

.nav-list__dropdown summary::after {
  content: "▾";
  display: inline-block;
  margin-left: 0.35em;
  font-size: 0.9em;
  vertical-align: middle;
}

.nav-list__dropdown summary:hover {
  color: var(--color-link);
  text-decoration: underline;
}

.nav-list__dropdown[open] summary {
  color: var(--color-link);
  text-decoration: underline;
}

.nav-submenu {
  position: absolute;
  top: 100%;
  left: 0;
  z-index: 10;
  min-width: 12rem;
  margin: 0.5rem 0 0;
  padding: 0.5rem 0;
  list-style: none;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: 0.25rem;
  box-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.2);
}

.nav-submenu li {
  white-space: nowrap;
}

.nav-submenu a {
  display: block;
  padding: 0.5rem 1rem;
  color: var(--color-text);
  font-weight: 500;
  text-transform: none;
  letter-spacing: normal;
  text-decoration: none;
}

.nav-submenu a:hover {
  background: #f0efe9;
  color: var(--color-link);
  text-decoration: underline;
}

main {
  max-width: 75rem;
  margin: 0 auto;
  padding: 2.5rem 1.5rem;
}

/* Escapes main's reading-width container for full-viewport-width sections
   (hero photos, the home page's icon row) -- the live site keeps body text
   in a readable column but lets these sections span edge to edge. Usage:
   <section class="full-bleed"> as a direct child of <main>. */
.full-bleed {
  width: 100vw;
  margin-left: calc(50% - 50vw);
  margin-right: calc(50% - 50vw);
}

main section {
  margin-top: 2.5rem;
}

.tagline {
  font-family: var(--font-sans);
  font-size: 1.1rem;
  font-weight: 600;
  color: var(--color-text-muted);
}

/*
  Contact page's Phone/Address/Email icon row -- design-reference/contact_us/
  shows three centered columns (icon, bold label, value), not the plain
  definition list this used to be. Reuses the same icon paths as the footer
  (site-footer__icon) at a larger size in the gold accent color, so the two
  don't drift into different icon sets for the same information.
*/
.contact-info {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 2.5rem;
  margin: 1.5rem 0 2.5rem;
  padding: 0;
  text-align: center;
}

.contact-info__item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  width: 16rem;
}

.contact-info__icon {
  width: 3rem;
  height: 3rem;
  color: var(--color-brand-accent-gold);
}

.contact-info__label {
  font-family: var(--font-serif);
  font-weight: 700;
  font-size: 1.15rem;
  color: var(--color-text);
  /* Overrides the global `main h1, main h2` rule -- these are small
     column headings, not section titles, so no green color or border. */
  border-bottom: none;
  padding-bottom: 0;
  margin: 0;
}

.contact-info__item p {
  margin: 0;
}

/*
  Contact form (katapts-54g.6) -- Netlify Forms handles submission (see the
  HTML comment in content/contact.njk), so no server code lives here. Field
  layout (First/Last Name side by side, Email and Message full width,
  required-field asterisks) matches design-reference/contact_us/*.png.
*/
.contact-form {
  max-width: 48rem;
  margin: 0;
}

.contact-form__hint {
  font-size: 0.9rem;
  color: var(--color-text-muted);
  margin: 0 0 1.5rem;
}

.contact-form__row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0 1.5rem;
}

@media (max-width: 32rem) {
  .contact-form__row {
    grid-template-columns: 1fr;
  }
}

.contact-form__field {
  margin-bottom: 1.5rem;
}

.contact-form__field label {
  display: block;
  font-family: var(--font-sans);
  font-weight: 700;
  font-size: 0.9rem;
  margin-bottom: 0.4rem;
}

.contact-form__field input,
.contact-form__field textarea {
  width: 100%;
  box-sizing: border-box;
  padding: 0.65rem 0.75rem;
  border: 1px solid var(--color-border);
  border-radius: 4px;
  font-family: var(--font-serif);
  font-size: 1rem;
  color: var(--color-text);
  background: #ffffff;
}

.contact-form__field textarea {
  resize: vertical;
}

.contact-form__status {
  max-width: 32rem;
  margin: 1.5rem 0 0;
  padding: 1rem 1.25rem;
  border: 1px solid;
  border-radius: 4px;
  font-family: var(--font-sans);
  font-weight: 600;
}

/* Success reuses the already-contrast-checked brand green (~9.5:1 vs
   white, see :root comment); error introduces one new color, checked the
   same way: #8b1a1a on white -> ~9.3:1. */
.contact-form__status--success {
  color: var(--color-brand-accent-green);
  border-color: var(--color-brand-accent-green);
  background: #eef5f2;
}

.contact-form__status--error {
  color: #8b1a1a;
  border-color: #8b1a1a;
  background: #fbecec;
}

/*
  Button family -- the live site uses several distinct button styles, not
  one shared component (design-reference/*.png). Only --header is wired
  into a template today; the rest are defined here so the design system
  is ready for the issues that build the pages that need them (hero CTA,
  form submit, filter/list actions) instead of each retrofitting its own
  button styling.
*/
.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: 0.95rem;
  line-height: 1.2;
  padding: 0.7rem 1.35rem;
  border-radius: 4px;
  border: 2px solid transparent;
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
}

.btn__icon {
  width: 1.1em;
  height: 1.1em;
  flex-shrink: 0;
}

/* Header/footer CTA (e.g. "Pay Rent"): solid brand-primary fill. */
.btn--header {
  background: var(--color-brand-primary);
  color: #ffffff;
}

.btn--header:hover {
  background: var(--color-brand-primary-hover);
  color: #ffffff;
}

/* On the dark footer, the solid-fill CTA inverts so it still stands out. */
.site-footer .btn--header {
  background: #ffffff;
  color: var(--color-brand-primary);
}

.site-footer .btn--header:hover {
  background: #f0efe9;
  color: var(--color-brand-primary);
}

/* Hero CTA (e.g. "Find a Home"): gold fill, white border, uppercase,
   sized/weighted so its ~3.2:1 contrast clears the WCAG large-text
   threshold (raw gold fails the 4.5:1 required for smaller text). */
.btn--hero {
  background: var(--color-brand-accent-gold);
  color: #ffffff;
  border-color: #ffffff;
  border-radius: 0;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 700;
  font-size: 1.2rem;
  padding: 0.85rem 1.75rem;
}

.btn--hero:hover {
  background: #a47838;
  color: #ffffff;
}

/* Primary action (e.g. "Apply Filters"): solid forest-green fill. */
.btn--primary {
  background: var(--color-brand-accent-green);
  color: #ffffff;
}

.btn--primary:hover {
  background: var(--color-brand-accent-green-hover);
  color: #ffffff;
}

/* Neutral/secondary action (e.g. "Clear Filters", "View Details"). */
.btn--secondary {
  background: var(--color-neutral);
  color: #ffffff;
}

.btn--secondary:hover {
  background: var(--color-neutral-hover);
  color: #ffffff;
}

/* Plain (e.g. form "Submit"): deliberately the least-styled button. */
.btn--plain {
  background: #ffffff;
  color: var(--color-text);
  border-color: var(--color-border);
}

.btn--plain:hover {
  background: #f7f7f5;
  color: var(--color-text);
}

/* AppFolio's own portal-login buttons use a generic navy that isn't part
   of the Katica brand palette elsewhere on the site -- reserved for that
   one context, not a general-purpose accent. */
.btn--portal {
  background: var(--color-navy);
  color: #ffffff;
}

.btn--portal:hover {
  background: #1b2049;
  color: #ffffff;
}

/*
  Hero patterns -- not wired into any page yet (that's later, content-level
  work), defined here so those pages consume one system instead of each
  inventing its own. Full-bleed single-panel (home): photo background with
  a light desaturating grade, NOT a heavy dark overlay -- stays bright and
  legible, with centered white content. Split two-panel (residents/owners):
  photo+overlay on the left, a plain panel on the right.
*/
.hero {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 28rem;
  padding: 2rem 1.5rem;
  background-size: cover;
  background-position: center;
  text-align: center;
}

.hero::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    180deg,
    rgba(30, 42, 50, 0.35),
    rgba(30, 42, 50, 0.15)
  );
}

.hero__content {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.5rem;
}

.hero__title {
  font-family: var(--font-serif);
  color: #ffffff;
  font-size: clamp(1.75rem, 4vw, 2.75rem);
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
  margin: 0;
  /* Overrides the global `main h1` rule's green color + bottom border --
     wrong on a photo hero, see main h1/h2 rule above. */
  border-bottom: none;
  padding-bottom: 0;
}

.hero__blurb {
  color: #ffffff;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
  max-width: 26rem;
  margin: 1rem 0 0;
}

/* Full-bleed photo with two inset overlay boxes forming a single centered
   band -- measured pixel-for-pixel off design-reference/residents+owners
   screenshots (both at the same 1844x1052 capture): the band sits inset
   17.5% from the hero's left/right edges and 20% from its top/bottom,
   split 50/50 between the dark text box and the plain icon box, so the
   photo stays visible on all four sides of the band rather than the boxes
   filling the hero edge to edge. See katapts-qoh close notes.

   The photo itself lives on .hero--split (full-bleed background-image, set
   inline per page) rather than on .hero__panel--photo, because vertical
   percentage padding/inset always resolves against the containing block's
   WIDTH, not height (CSS spec quirk). .hero__panel--photo used to be the
   full-bleed element with padding positioning its text -- at a 1400px-wide
   viewport that computed 20% top/bottom padding as 280px+280px=560px,
   overflowing the ~544px-tall hero by 16px and misaligning it against
   .hero__panel--plain next to it. Now .hero__panel--photo is just the box
   (top/bottom/left/right percentages, same technique as .hero__panel--plain
   below), so its height always matches. See katapts screenshot bug report
   2026-09-16. */
.hero--split {
  position: relative;
  display: block;
  min-height: 34rem;
  padding: 0;
  overflow: hidden;
  background-size: cover;
  /* --hero-bg-position lets a page tune which part of a non-16:9 source
     photo shows through the wide hero band -- e.g. hero-owners.jpg is a
     square desk-flatlay photo (coffee cup up top, phone down at the
     bottom) and only the vertical position matters here since cover
     already matches the band's full width. Default unchanged (plain
     center) for photos already shot/cropped for a wide band. */
  background-position: var(--hero-bg-position, center);
}

.hero--split .hero__panel {
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.hero--split .hero__panel--photo {
  position: absolute;
  top: 20%;
  bottom: 20%;
  left: 17.5%;
  right: 50%;
  z-index: 1;
  color: #ffffff;
  text-align: left;
  padding: 1.5rem;
}

.hero--split .hero__panel--photo::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  /* --hero-overlay-alpha lets a page opt into a stronger wash when its own
     photo has bright highlights the default 0.45 doesn't reliably darken
     enough for AA text contrast (measured per-pixel, not eyeballed --
     see katapts-54g.7 close notes). Default unchanged so existing pages
     (Owner Portal) keep their already-reviewed look. */
  background: rgba(30, 42, 50, var(--hero-overlay-alpha, 0.45));
}

.hero--split .hero__panel--photo > * {
  position: relative;
  z-index: 1;
}

.hero--split .hero__panel--plain {
  position: absolute;
  top: 20%;
  bottom: 20%;
  left: 50%;
  right: 17.5%;
  z-index: 1;
  padding: 1.5rem;
  background: #f4f2ec;
  align-items: center;
  text-align: center;
  gap: 1.25rem;
}

.hero__panel-icon {
  width: 4.5rem;
  height: 4.5rem;
  color: var(--color-text);
}

.hero__panel-heading {
  font-family: var(--font-serif);
  font-weight: 500;
  font-size: 1.5rem;
  color: var(--color-text);
  /* Overrides the global `main h1, main h2` rule -- this small heading has
     no green color or bottom border on the live site. */
  border-bottom: none;
  padding-bottom: 0;
  margin: 0;
}

@media (max-width: 45rem) {
  .hero--split {
    min-height: 40rem;
  }

  .hero--split .hero__panel--photo {
    padding: 8% 8% 55% 8%;
  }

  .hero--split .hero__panel--photo::before {
    top: 8%;
    bottom: 55%;
    left: 8%;
    right: 8%;
  }

  .hero--split .hero__panel--plain {
    top: 59%;
    bottom: 8%;
    left: 8%;
    right: 8%;
    padding: 1rem;
  }
}

/* Home page's 4-icon quick-link row: solid brand-primary circle badges,
   white icon glyphs, label below each -- sampled layout from
   design-reference/home/*.png. */
.quick-links {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 3rem;
  padding: 3rem 1.5rem;
  text-decoration: none;
}

.quick-links__item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.75rem;
  text-decoration: none;
  color: var(--color-text-muted);
  font-family: var(--font-serif);
  width: 11rem;
  text-align: center;
}

.quick-links__icon {
  width: 4.5rem;
  height: 4.5rem;
  border-radius: 50%;
  background: var(--color-brand-primary);
  color: #ffffff;
  display: flex;
  align-items: center;
  justify-content: center;
}

.quick-links__icon svg {
  width: 1.75rem;
  height: 1.75rem;
}

.site-footer {
  background: var(--color-brand-primary);
  color: #ffffff;
  padding: 3rem 1.5rem 2rem;
  font-size: 0.95rem;
}

.site-footer__inner {
  max-width: 75rem;
  margin: 0 auto;
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 2.5rem;
  text-align: left;
}

.site-footer__heading {
  font-family: var(--font-sans);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-size: 1rem;
  margin: 0 0 1rem;
}

.site-footer__line {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin: 0.6rem 0;
}

.site-footer__icon {
  width: 1.1rem;
  height: 1.1rem;
  flex-shrink: 0;
}

.portal-links {
  display: flex;
  gap: 3rem;
  text-align: center;
}

.portal-links__label {
  margin: 0 0 0.25rem;
  font-size: 0.85rem;
}

.portal-links__login {
  font-family: var(--font-serif);
  font-size: 1.4rem;
}

.site-footer__bottom {
  max-width: 75rem;
  margin: 2rem auto 0;
  padding-top: 1.5rem;
  border-top: 1px solid rgba(255, 255, 255, 0.25);
  text-align: left;
}

.site-footer__sitemap a {
  font-weight: 700;
}

.site-footer__copyright {
  margin: 0.5rem 0 0;
  font-size: 0.85rem;
}

.site-footer a {
  color: #ffffff;
}

/*
  Owner Portal ("BACKED BY..." eyebrow, feature list, closing band) --
  sampled from design-reference/owners/*.png. The reference shows a single
  vertical feature list next to a device-mockup image, NOT a 2x2 icon grid
  (unlike what katapts-54g.8's own ticket text assumed) -- see that issue's
  close notes.
*/
.section-intro {
  text-align: center;
  max-width: 44rem;
  /* margin-inline (not the `margin: 0 auto` shorthand) so this doesn't
     zero out the `main section { margin-top: 2.5rem }` spacing between
     this and the hero above it -- a class selector beats that two-element
     selector, so the shorthand was silently collapsing the hero straight
     into this section with no gap. See katapts hero/spacing bug report
     2026-09-16. */
  margin-inline: auto;
}

.section-intro__eyebrow {
  font-family: var(--font-serif);
  font-variant: small-caps;
  letter-spacing: 0.06em;
  font-size: 1.4rem;
  font-weight: 500;
  color: var(--color-text);
  text-align: center;
  /* Overrides the global `main h1, main h2` rule -- centered, no green
     color or bottom border here. */
  border-bottom: none;
  padding-bottom: 0;
  margin: 0 0 1rem;
}

.section-intro__subtitle {
  color: var(--color-text-muted);
  font-size: 1.1rem;
  margin: 0;
}

.feature-showcase {
  display: flex;
  align-items: flex-start;
  justify-content: center;
  gap: 3rem;
  flex-wrap: wrap;
}

.feature-showcase__image {
  display: block;
  flex: 0 0 auto;
  max-width: 21rem;
  height: auto;
}

.feature-list {
  list-style: none;
  margin: 0;
  padding: 0;
  flex: 1 1 24rem;
  max-width: 32rem;
  display: flex;
  flex-direction: column;
  gap: 2rem;
}

.feature-list__item {
  display: flex;
  gap: 1.25rem;
}

.feature-list__icon {
  flex: 0 0 auto;
  width: 2.25rem;
  height: 2.25rem;
  color: #2f6690;
}

.feature-list__label {
  font-family: var(--font-serif);
  font-variant: small-caps;
  letter-spacing: 0.04em;
  font-weight: 700;
  font-size: 1.1rem;
  color: var(--color-text);
  margin: 0 0 0.5rem;
}

.investment-band {
  background: var(--color-text-muted);
  color: #ffffff;
  text-align: center;
  padding: 3.5rem 1.5rem;
}

.investment-band__title {
  font-family: var(--font-serif);
  font-weight: 500;
  color: #ffffff;
  font-size: clamp(1.75rem, 4vw, 2.5rem);
  /* Overrides the global `main h1, main h2` rule -- white on this dark
     band, no green color or bottom border. */
  border-bottom: none;
  padding-bottom: 0;
  margin: 0 0 1rem;
}

.investment-band__subtitle {
  font-size: 1.1rem;
  margin: 0 0 2rem;
}

/*
  Resident Portal 2x2 feature grid + "Your Portal, Your Way" app band --
  sampled from design-reference/residents/*.png. Unlike the Owner Portal's
  single vertical feature-list (katapts-54g.8), the resident page's features
  render as two columns of two items next to the device-mockup image, and
  the closing band is a phone mockup + App Store/Google Play badges rather
  than a plain CTA button.
*/
.feature-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 2.5rem 3rem;
  flex: 1 1 28rem;
  max-width: 42rem;
  margin: 0;
}

.feature-grid__item {
  display: flex;
  gap: 1.1rem;
}

@media (max-width: 32rem) {
  .feature-grid {
    grid-template-columns: 1fr;
  }
}

/* Dark band background reuses --color-text-muted, already contrast-checked
   against white text (~8.2:1, see :root comment) rather than introducing a
   new gray. */
.app-band {
  background: var(--color-text-muted);
  color: #ffffff;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 3rem;
  padding: 3.5rem 1.5rem;
}

.app-band__phone {
  display: block;
  flex: 0 0 auto;
  width: 14rem;
  max-width: 70vw;
  height: auto;
}

.app-band__content {
  flex: 1 1 26rem;
  max-width: 34rem;
}

.app-band__eyebrow {
  font-family: var(--font-serif);
  font-variant: small-caps;
  letter-spacing: 0.06em;
  font-size: 1.4rem;
  font-weight: 500;
  color: #ffffff;
  /* Overrides the global `main h1, main h2` rule -- white on this dark
     band, no green color or bottom border. */
  border-bottom: none;
  padding-bottom: 0;
  margin: 0 0 1rem;
}

.app-band__lede {
  font-size: 1.05rem;
  margin: 0 0 1rem;
}

.app-band__emphasis {
  font-weight: 700;
  margin: 0 0 1.75rem;
}

.app-band__download-label {
  font-family: var(--font-sans);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-size: 0.9rem;
  font-weight: 600;
  margin: 0 0 1rem;
}

.app-store-badges {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem;
}

.app-store-badges a {
  display: inline-block;
  line-height: 0;
}

.app-store-badges img {
  display: block;
  height: 2.5rem;
  width: auto;
}

/*
  /properties/ card grid (katapts-54g.4) -- design-reference/properties/*.png
  shows a plain 3-column/2-row grid of photo + bold name + gray 2-line
  address, with no visible button/CTA on the cards: the whole card is one
  link (the <a> wraps the photo and both text lines), not a bordered card
  with a separate action element. Photos have no border/shadow, matching
  the reference exactly.
*/
.property-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 2.5rem 2rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

@media (max-width: 48rem) {
  .property-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (max-width: 32rem) {
  .property-grid {
    grid-template-columns: 1fr;
  }
}

.property-card__link {
  display: block;
  color: var(--color-text);
  text-decoration: none;
}

.property-card__link:hover .property-card__name {
  color: var(--color-link);
  text-decoration: underline;
}

.property-card__photo {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 387 / 229;
  object-fit: cover;
}

.property-card__name {
  display: block;
  margin-top: 1rem;
  font-family: var(--font-serif);
  font-weight: 700;
  font-size: 1.15rem;
  text-align: center;
}

.property-card__address {
  display: block;
  margin-top: 0.35rem;
  color: var(--color-text-muted);
  font-size: 0.95rem;
  text-align: center;
  line-height: 1.5;
}

/*
  /vacancies/ page (katapts-wr9.2) -- design-reference/vacancies/*.png shows
  a two-column layout: an always-visible map on the left (~40% width) and a
  grid of listing cards on the right, no toggle/hidden state. Listing data
  loads client-side from the katapts-wr9.1 Netlify Function (see
  assets/js/vacancies.js), so the map/list/status regions below start in a
  loading state and are filled in (or swapped for an empty/error message)
  once that fetch resolves.
*/
.vacancies-layout {
  display: grid;
  grid-template-columns: 40% 1fr;
  gap: 2rem;
  align-items: start;
}

@media (max-width: 48rem) {
  .vacancies-layout {
    grid-template-columns: 1fr;
  }
}

.vacancies-map-pane {
  position: sticky;
  top: 1.5rem;
}

.vacancies-map {
  width: 100%;
  height: 32rem;
  border: 1px solid var(--color-border);
  border-radius: 4px;
  background: #f4f2ec;
}

@media (max-width: 48rem) {
  .vacancies-map-pane {
    position: static;
  }

  .vacancies-map {
    height: 20rem;
  }
}

.vacancies-status {
  color: var(--color-text-muted);
  font-family: var(--font-sans);
}

.vacancies-status--error {
  color: #8b1a1a;
}

.vacancy-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
  gap: 1.75rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

.vacancy-card {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--color-border);
  border-radius: 4px;
  overflow: hidden;
  background: #ffffff;
  /* Markers use the same 3px amber ring on click-to-highlight, so the card
     needs its own visible (but distinct) highlight state to match -- see
     .vacancy-card--highlighted below and assets/js/vacancies.js. */
  transition: box-shadow 0.2s ease;
}

.vacancy-card--highlighted {
  box-shadow: 0 0 0 3px var(--focus-a), 0 0 0 6px var(--focus-b);
}

.vacancy-card__photo {
  display: block;
  width: 100%;
  height: 12rem;
  object-fit: cover;
  background: #f4f2ec;
}

.vacancy-card__body {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  padding: 1.1rem 1.25rem 1.25rem;
}

.vacancy-card__address {
  font-family: var(--font-serif);
  font-weight: 700;
  font-size: 1.05rem;
  margin: 0 0 0.6rem;
}

.vacancy-card__rent {
  font-family: var(--font-sans);
  font-weight: 700;
  font-size: 1.25rem;
  color: var(--color-brand-accent-green);
  margin: 0 0 0.6rem;
}

.vacancy-card__rent-period {
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: 0.75rem;
  color: var(--color-text-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.vacancy-card__specs {
  color: var(--color-text-muted);
  font-family: var(--font-sans);
  font-size: 0.9rem;
  margin: 0 0 1rem;
}

.vacancy-card__link {
  margin-top: auto;
  align-self: flex-start;
}

.vacancies-empty {
  border: 1px solid var(--color-border);
  border-radius: 4px;
  background: #f4f2ec;
  padding: 2rem 1.5rem;
  text-align: center;
  color: var(--color-text-muted);
  font-family: var(--font-sans);
}

.vacancies-empty strong {
  display: block;
  font-family: var(--font-serif);
  font-size: 1.2rem;
  color: var(--color-text);
  margin-bottom: 0.5rem;
}

/*
  /vacancies/<listing_id>/ route (katapts-wr9.3) -- content/vacancy-detail.njk
  + assets/js/vacancy-detail.js. design-reference/listing/*.png shows a photo
  gallery (one large photo + a grid of smaller ones) above a two-column body:
  a narrow sidebar (price, availability, beds/bath/sqft, Contact Us, Terms
  list) and a wider main column (address, unit title, building/neighborhood
  description, map). Photo count varies per listing (confirmed live: as few
  as 1, as many as 8), so the gallery grid is sized to flow any count rather
  than assuming a fixed layout.
*/
.vacancy-detail-back {
  margin: 0 0 1rem;
  font-family: var(--font-sans);
}

.listing-detail__gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
  grid-auto-rows: 8rem;
  gap: 0.5rem;
  margin-bottom: 2rem;
}

.listing-detail__gallery-item {
  display: block;
  width: 100%;
  height: 100%;
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
}

.listing-detail__gallery-item--large {
  grid-column: span 2;
  grid-row: span 2;
}

.listing-detail__gallery-photo {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 4px;
  background: #f4f2ec;
}

@media (max-width: 32rem) {
  .listing-detail__gallery {
    grid-template-columns: repeat(2, 1fr);
    grid-auto-rows: 7rem;
  }
}

/*
  Full-photo viewer for the gallery above -- the grid only ever shows
  cropped (object-fit: cover) thumbnails, so this is the only way to see an
  uncropped photo at a readable size. A plain fixed-position overlay rather
  than a library: one image, prev/next, Escape/arrow-key and click-outside
  to close is the whole feature (see assets/js/vacancy-detail.js).
*/
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.9);
}

.lightbox[hidden] {
  display: none;
}

.lightbox__image {
  max-width: min(92vw, 60rem);
  max-height: 85vh;
  border-radius: 4px;
}

.lightbox__close,
.lightbox__prev,
.lightbox__next {
  position: absolute;
  width: 2.75rem;
  height: 2.75rem;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.6);
  color: #fff;
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
}

.lightbox__close:hover,
.lightbox__prev:hover,
.lightbox__next:hover {
  background: rgba(0, 0, 0, 0.8);
}

.lightbox__close {
  top: 1rem;
  right: 1rem;
}

.lightbox__prev,
.lightbox__next {
  top: 50%;
  transform: translateY(-50%);
}

.lightbox__prev {
  left: 1rem;
}

.lightbox__next {
  right: 1rem;
}

.lightbox__counter {
  position: absolute;
  bottom: 1rem;
  left: 50%;
  transform: translateX(-50%);
  margin: 0;
  padding: 0.25rem 0.75rem;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.6);
  color: #fff;
  font-family: var(--font-sans);
  font-size: 0.85rem;
}

@media (max-width: 32rem) {
  .lightbox__close,
  .lightbox__prev,
  .lightbox__next {
    width: 2.25rem;
    height: 2.25rem;
    font-size: 1.25rem;
  }
}

.listing-detail__body {
  display: grid;
  grid-template-columns: 20rem 1fr;
  gap: 2.5rem;
  align-items: start;
}

@media (max-width: 48rem) {
  .listing-detail__body {
    grid-template-columns: 1fr;
  }
}

.listing-detail__sidebar {
  border: 1px solid var(--color-border);
  border-radius: 4px;
  padding: 1.5rem;
  background: #f9f8f4;
}

.listing-detail__price {
  font-family: var(--font-sans);
  font-weight: 700;
  font-size: 1.75rem;
  color: var(--color-brand-accent-green);
  margin: 0 0 0.25rem;
}

.listing-detail__availability {
  font-family: var(--font-sans);
  color: var(--color-text-muted);
  margin: 0 0 1rem;
}

.listing-detail__specs {
  font-family: var(--font-sans);
  font-weight: 600;
  margin: 0 0 1.25rem;
}

.listing-detail__contact-btn {
  display: flex;
  justify-content: center;
  width: 100%;
  margin-bottom: 1.5rem;
}

.listing-detail__terms-heading {
  font-family: var(--font-sans);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-size: 0.9rem;
  color: var(--color-text);
  margin: 0 0 0.75rem;
}

.listing-detail__terms {
  list-style: none;
  margin: 0;
  padding: 0;
}

.listing-detail__terms-item {
  padding: 0.4rem 0;
  border-bottom: 1px solid var(--color-border);
  font-family: var(--font-serif);
}

.listing-detail__terms-item:last-child {
  border-bottom: none;
}

/* Overrides the global `main h1, main h2` rule -- this is the listing's
   name, not a section title, so no green color or bottom border here
   (design-reference/listing/*.png shows it in plain dark text). */
.listing-detail__address {
  color: var(--color-text);
  border-bottom: none;
  padding-bottom: 0;
  margin: 0 0 0.5rem;
}

.listing-detail__unit-title {
  font-family: var(--font-sans);
  font-weight: 600;
  color: var(--color-text-muted);
  margin: -0.5rem 0 1.5rem;
}

.listing-detail__description {
  line-height: 1.8;
  margin-bottom: 2rem;
}

.listing-detail__map-heading {
  font-size: 1.3rem;
}

.listing-detail__map {
  width: 100%;
  height: 22rem;
  border: 1px solid var(--color-border);
  border-radius: 4px;
  background: #f4f2ec;
}

/*
  Explicit, visible keyboard focus for every interactive element.
  A double ring (dark + amber) keeps 3:1+ contrast against both the
  light body background and dark surfaces (footer, brand badge, header
  CTA) -- see :root comment above for which ring order applies where.
*/
a:focus-visible,
button:focus-visible,
input:focus-visible,
summary:focus-visible,
[tabindex]:focus-visible {
  outline: 3px solid transparent;
  outline-offset: 2px;
  box-shadow:
    0 0 0 3px var(--focus-a),
    0 0 0 6px var(--focus-b);
  border-radius: 2px;
}
