/* ---- tokens.css ---- */
/* tokens.css — the design system for twinfallsroomsforrent.com
 *
 * Every colour pair below carries its computed contrast ratio in a comment. None of them were
 * chosen by eye. The generator recomputes them at build time and fails the build if any drops
 * below its target, so a token cannot be edited into an inaccessible value without the build saying so.
 *
 * The palette is deliberately not the luxury-real-estate one. That corpus reads expensive because
 * it is selling a two million dollar house. This business rents a clean room for $65 a night to
 * someone who needs it this week. The brief is legible, calm and unmistakably affordable, so the
 * type is large and light but the ground is a plain warm bone and the accent is a working clay
 * rather than an old gold.
 *
 * Two rules learned the hard way and encoded here:
 *   1. ONE accent token cannot be both ink and fill. An accent dark enough to read as text on a
 *      light ground is too dark to put dark text on top of. There are two tokens, not one.
 *   2. Any token re-declared later in a file beats an earlier theme block at equal specificity.
 *      `:root` and `[data-theme="dark"]` are both (0,1,0), so source order decides. Nothing below
 *      this file's theme blocks re-declares a palette token.
 */

:root {
  color-scheme: light dark;

  /* ---- Grounds. Bone, not white. Two hex digits of difference, and it is the difference
     between a page that looks printed and a page that looks like a default stylesheet. ---- */
  --ground: #f7f5f0;
  --ground-raised: #fffdf8;
  --ground-sunken: #ede9e1;

  /* ---- Ink. Charcoal with a little blue in it, never #000. ---- */
  --ink: #1c2430;          /* 14.34:1 on ground, 15.37:1 on raised, 12.90:1 on sunken */
  --ink-muted: #4c5768;    /*  6.71:1 on ground,  7.20:1 on raised */
  --ink-faint: #677181;    /*  4.53:1 on ground — the darkest this can go and still be "faint" */

  /* ---- One accent, in the two roles it actually has to play. ---- */
  --accent-ink: #8a3a16;         /* 7.15:1 on ground, 7.66:1 on raised. Use for TEXT and rules. */
  --accent-fill: #f0c9a8;        /* ink on it: 10.13:1. Use as a BACKGROUND under --ink. */
  --accent-fill-strong: #e0a878; /* ink on it:  7.47:1 */
  --accent-edge: #b1855f;        /* 3.02:1 on ground. The border of a filled control. */

  /* ---- Lines. A decorative divider has no contrast obligation. A line that IS the only
     boundary of a control does, and that is a different token. ---- */
  --line: #dcd6cc;         /* decorative only, 1.33:1, never the sole boundary of a control */
  --line-strong: #918d87;  /* 3.03:1 on ground — a border that defines a non-interactive component */
  /* SC 1.4.11 wants 3:1 for the boundary of a CONTROL. --line-strong sits at exactly 3.03 against
     the page ground, which left no margin and measured 2.71 to 2.73 against the raised and sunken
     surfaces buttons actually sit on. This token is darker on purpose: 4.6:1 on the ground. */
  --control-edge: #6f6a63;

  /* ---- Status. ---- */
  --ok: #1f5e3d;    /* 7.07:1 on ground */
  --warn: #8a5a00;  /* 5.44:1 on ground */

  /* ---- The primary button is ink, not accent. The accent already carries eyebrows, links,
     rules and hover states; spending it on every button too spends the whole chromatic budget.
     Ink means "do this". Accent means "notice this". 14.34:1 either way. ---- */
  --btn-primary-bg: var(--ink);
  --btn-primary-fg: var(--ground);
  --btn-secondary-bg: transparent;
  --btn-secondary-fg: var(--ink);
  --btn-secondary-border: var(--control-edge);

  /* ---- Type. Large and light, with a wide gap between display leading and body leading.
     A four-rem heading at weight 300 has presence. A two-rem heading at 700 just shouts. ---- */
  --font-display: "Newsreader", "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, serif;
  --font-body: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

  --step--1: clamp(0.875rem, 0.84rem + 0.17vw, 0.95rem);
  --step-0: clamp(1.0625rem, 1.02rem + 0.22vw, 1.1875rem);
  --step-1: clamp(1.3rem, 1.22rem + 0.4vw, 1.55rem);
  --step-2: clamp(1.6rem, 1.45rem + 0.75vw, 2.1rem);
  --step-3: clamp(2rem, 1.72rem + 1.4vw, 2.9rem);
  --step-4: clamp(2.5rem, 2rem + 2.5vw, 4rem);
  --step-5: clamp(3rem, 2.2rem + 4vw, 5.25rem);

  --lh-display: 1.06;
  --lh-heading: 1.15;
  --lh-body: 1.55;   /* the gap between 1.06 and 1.55 is the point, and it is deliberately wide */
  --lh-tight: 1.3;

  --weight-display: 380;  /* variable axis. Never 700. Newsreader reads lighter than the
                             previous face at the same number, so this is 340 re-measured. */
  --weight-heading: 400;
  --weight-body: 400;
  --weight-strong: 600;

  --measure: 68ch;

  /* ---- Space. The large token is the one that costs nothing and is where self-built sites
     lose most obviously. It feels wrong while you are building it. Trust the token. ---- */
  --space-3xs: 0.25rem;
  --space-2xs: 0.5rem;
  --space-xs: 0.75rem;
  --space-s: 1rem;
  --space-m: 1.5rem;
  --space-l: 2.5rem;
  --space-xl: 4rem;
  --space-2xl: clamp(5rem, 4rem + 5vw, 8.5rem);
  --space-3xl: clamp(7rem, 5rem + 9vw, 13rem);

  --width-prose: 44rem;
  --width-content: 72rem;
  --width-wide: 84rem;
  --gutter: clamp(1.25rem, 0.9rem + 1.8vw, 2.5rem);

  --radius-s: 3px;
  --radius-m: 6px;
  --radius-l: 12px;

  /* ---- Motion. Three curves, one duration. Nothing bounces. ---- */
  --ease-standard: cubic-bezier(0.645, 0.045, 0.355, 1);
  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
  --ease-in-out-strong: cubic-bezier(0.835, 0, 0.19, 1);
  --dur: 0.3s;
  --dur-fast: 0.2s;

  --shadow-s: 0 1px 2px rgb(28 36 48 / 0.06), 0 1px 3px rgb(28 36 48 / 0.05);
  --shadow-m: 0 2px 6px rgb(28 36 48 / 0.07), 0 8px 24px rgb(28 36 48 / 0.06);

  /* A hero photograph cannot be measured for contrast from a computed style, so text never sits
     directly on one. This scrim is what makes the hero measurable instead of unresolvable. */
  --scrim: linear-gradient(to right, rgb(18 22 28 / 0.82) 0%, rgb(18 22 28 / 0.72) 45%, rgb(18 22 28 / 0.35) 100%);
  --scrim-ink: #f7f5f0;   /* on the 0.82 end of the scrim: 12.6:1 against #12161C composited */
}

/* Dark palette. Redefined under BOTH the media query and the explicit attribute, because the
   default "system" setting stamps no attribute at all and only prefers-color-scheme separates
   the two. Guarded with :not([data-theme="light"]) so an explicit light choice still wins. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --ground: #12161c;
    --ground-raised: #1a2028;
    --ground-sunken: #0c0f14;
    --ink: #ede9e1;            /* 14.99:1 on ground */
    --ink-muted: #b6bec9;      /*  9.68:1 on ground */
    --ink-faint: #939ca9;      /*  6.54:1 on ground */
    --accent-ink: #f0a87a;     /*  9.15:1 on ground, 8.26:1 on raised */
    --accent-fill: #5a2a12;    /* ink on it: 9.77:1 */
    --accent-fill-strong: #7a3a1a; /* ink on it: 7.09:1 */
    --accent-edge: #9e4b22;    /*  3.01:1 on ground */
    --line: #2a323c;
    --line-strong: #546478;    /*  3.00:1 on ground */
    --control-edge: #8a97a6;   /*  6.1:1 on ground, 5.5:1 raised, 6.5:1 sunken */
    --ok: #6fd19b;             /*  9.74:1 on ground */
    --warn: #e0b25c;           /*  9.24:1 on ground */
    --shadow-s: 0 1px 2px rgb(0 0 0 / 0.4), 0 1px 3px rgb(0 0 0 / 0.3);
    --shadow-m: 0 2px 6px rgb(0 0 0 / 0.45), 0 8px 24px rgb(0 0 0 / 0.35);
  }
}

:root[data-theme="dark"] {
  --ground: #12161c;
  --ground-raised: #1a2028;
  --ground-sunken: #0c0f14;
  --ink: #ede9e1;
  --ink-muted: #b6bec9;
  --ink-faint: #939ca9;
  --accent-ink: #f0a87a;
  --accent-fill: #5a2a12;
  --accent-fill-strong: #7a3a1a;
  --accent-edge: #9e4b22;
  --line: #2a323c;
  --line-strong: #546478;
  --control-edge: #8a97a6;
  --ok: #6fd19b;
  --warn: #e0b25c;
  --shadow-s: 0 1px 2px rgb(0 0 0 / 0.4), 0 1px 3px rgb(0 0 0 / 0.3);
  --shadow-m: 0 2px 6px rgb(0 0 0 / 0.45), 0 8px 24px rgb(0 0 0 / 0.35);
}

/* ---- fonts.css ---- */
/* fonts.css — self-hosted, no third-party font CDN, no preconnect to anyone.
 *
 * Both faces are VARIABLE fonts. Inter covers weight 300 to 700 in one file and Newsreader covers
 * 300 to 600, so the light display weights the design calls for cost zero additional bytes.
 *
 * The display face was Fraunces and was changed. Fraunces' lowercase f carries a descending hooked
 * tail that is part of the typeface, and at 84px at weight 340 it read as a rendering fault rather
 * than a choice. Newsreader is a conventional editorial serif with a normal f, and it is the same
 * face apprised.news uses, which aligns the two brands.
 *
 * Before adding a font file, check whether the one on disk already carries the weight you want.
 * Both of these are variable and already did.
 *
 * Both subsets are declared with their unicode-range. The `latin` subset alone covers every glyph
 * the Spanish copy needs, verified against á é í ó ú ü ñ Á É Í Ó Ú Ü Ñ ¿ ¡ « » ·, so `latin-ext`
 * is declared as a safety net and never actually downloads for these pages. Real cost: 137 KB.
 *
 * font-display: swap, because a motel guest looking for a phone number at 11pm should get text
 * immediately in a fallback rather than a blank block while a webfont arrives.
 */

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url('/assets/fonts/inter-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304,
    U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url('/assets/fonts/inter-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308,
    U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113,
    U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Newsreader';
  font-style: normal;
  font-weight: 300 600;
  font-display: swap;
  src: url('/assets/fonts/newsreader-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304,
    U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Newsreader';
  font-style: normal;
  font-weight: 300 600;
  font-display: swap;
  src: url('/assets/fonts/newsreader-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308,
    U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113,
    U+2C60-2C7F, U+A720-A7FF;
}

/* ---- site.css ---- */
/* site.css — component layer. Loads after tokens.css and fonts.css.
 *
 * Every colour here reads a token. There are no literal hex values below this comment, which is
 * the property that makes the palette auditable: a contrast sweep over the tokens is a sweep over
 * the whole site. The failure this avoids is a stylesheet where 64 rules hardcode colours and a
 * token override silently loses to them on specificity.
 *
 * There are no inline style attributes and no <style> blocks anywhere in the built HTML, so the
 * Content-Security-Policy can say style-src 'self' with no 'unsafe-inline'.
 */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* Anything scrolled to by a fragment clears the sticky header. Without this, following a
     skip link or an in-page anchor parks the target underneath the header, which fails
     WCAG 2.2 SC 2.4.11 Focus Not Obscured (Minimum). */
  scroll-padding-top: 6rem;
  /* The floating help button is always present and cannot be dismissed, so a fragment target
     landing under it would be entirely obscured. SC 2.4.11 Focus Not Obscured (Minimum). */
  scroll-padding-bottom: 7rem;
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--step-0);
  font-weight: var(--weight-body);
  line-height: var(--lh-body);
  font-synthesis-weight: none;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
}

img, picture, svg, video { max-width: 100%; height: auto; display: block; }

/* ---- Type ------------------------------------------------------------------------------- */

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: var(--weight-display);
  line-height: var(--lh-heading);
  margin: 0 0 var(--space-s);
  text-wrap: balance;
  letter-spacing: -0.005em;
}
h1 { font-size: var(--step-4); line-height: var(--lh-display); }
h2 { font-size: var(--step-3); margin-top: var(--space-xl); }
h3 { font-size: var(--step-2); margin-top: var(--space-l); font-weight: var(--weight-heading); }
h4 { font-size: var(--step-1); margin-top: var(--space-m); font-weight: var(--weight-heading); }

p, li { max-width: var(--measure); }
p { margin: 0 0 var(--space-s); }
strong, b { font-weight: var(--weight-strong); }
small { font-size: var(--step--1); color: var(--ink-muted); }

.lede { font-size: var(--step-1); color: var(--ink-muted); line-height: var(--lh-tight); max-width: 54ch; }
.eyebrow {
  font-family: var(--font-body);
  font-size: var(--step--1);
  font-weight: var(--weight-strong);
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--accent-ink);
  margin: 0 0 var(--space-2xs);
}

a { color: var(--accent-ink); text-decoration-thickness: 1px; text-underline-offset: 0.18em; }
a:hover { text-decoration-thickness: 2px; }

/* One visible focus style, on every focusable thing, in both themes. */
:focus-visible {
  outline: 3px solid var(--accent-ink);
  outline-offset: 2px;
  border-radius: var(--radius-s);
}

/* ---- Layout ----------------------------------------------------------------------------- */

.wrap { width: min(100% - var(--gutter) * 2, var(--width-content)); margin-inline: auto; }
/* Above roughly a 1600px viewport the fixed content width leaves an enormous empty band on either
   side and the page reads as a narrow strip. Let the container grow, and let the card grids use
   the extra width, rather than pinning everything to one measure at every screen size. */
@media (min-width: 100rem) {
  .wrap { width: min(100% - var(--gutter) * 2, var(--width-wide)); }
  .wrap-wide { width: min(100% - var(--gutter) * 2, 96rem); }
  /* The .hero-body width override deliberately does NOT live here. `.wrap` and `.hero-body` are
     both single-class selectors at (0,1,0) and .hero-body is declared later in this file, so a
     max-width set here would silently lose to it. The override sits at the end of the file. */
}
.wrap-wide { width: min(100% - var(--gutter) * 2, var(--width-wide)); margin-inline: auto; }
.wrap-prose { width: min(100% - var(--gutter) * 2, var(--width-prose)); margin-inline: auto; }

/* The large spacing token, used between sections. It looks like a mistake while you are building
   it and correct the moment the page is read on a real screen. */
.section { padding-block: var(--space-2xl); }
.section-tight { padding-block: var(--space-xl); }
.section-sunken { background: var(--ground-sunken); }

.grid { display: grid; gap: var(--space-m); }
.grid-2 { grid-template-columns: repeat(auto-fill, minmax(min(20rem, 100%), 1fr)); }
.grid-3 { grid-template-columns: repeat(auto-fill, minmax(min(17rem, 100%), 1fr)); }
.grid-4 { grid-template-columns: repeat(auto-fill, minmax(min(13rem, 100%), 1fr)); }
.stack > * + * { margin-top: var(--space-s); }
.cluster { display: flex; flex-wrap: wrap; gap: var(--space-s); align-items: center; }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}

.skip-link {
  position: absolute; left: var(--space-s); top: -100px; z-index: 200;
  background: var(--ink); color: var(--ground);
  padding: var(--space-xs) var(--space-m); border-radius: 0 0 var(--radius-m) var(--radius-m);
  text-decoration: none; font-weight: var(--weight-strong);
  transition: top var(--dur-fast) var(--ease-out);
}
.skip-link:focus { top: 0; }

/* ---- Header ----------------------------------------------------------------------------- */

.site-header {
  position: sticky; top: 0; z-index: 100;
  background: color-mix(in srgb, var(--ground) 92%, transparent);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--line);
}
.header-inner { display: flex; align-items: center; gap: var(--space-m); padding-block: var(--space-xs); }
.brand { display: flex; align-items: center; gap: var(--space-2xs); text-decoration: none; color: var(--ink); flex-shrink: 0; }
.brand img { width: 40px; height: 40px; }
.brand-name { font-family: var(--font-display); font-size: var(--step-1); font-weight: var(--weight-heading); line-height: 1; }
.brand-sub { display: block; font-family: var(--font-body); font-size: var(--step--1); color: var(--ink-muted); line-height: 1.2; }

.site-nav { margin-left: auto; }
.site-nav ul { display: flex; flex-wrap: wrap; gap: var(--space-2xs); list-style: none; margin: 0; padding: 0; }
.site-nav a {
  display: block; padding: var(--space-2xs) var(--space-xs);
  color: var(--ink); text-decoration: none; border-radius: var(--radius-s);
  /* SC 2.5.8 Target Size (Minimum) wants 24x24 CSS pixels. */
  min-height: 44px; display: flex; align-items: center;
}
.site-nav a:hover { background: var(--ground-sunken); }
.site-nav a[aria-current='page'] { box-shadow: inset 0 -2px 0 var(--accent-ink); font-weight: var(--weight-strong); }

.header-actions { display: flex; gap: var(--space-2xs); align-items: center; }

/* ---- Buttons ---------------------------------------------------------------------------- */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2xs);
  min-height: 44px; min-width: 44px;
  padding: var(--space-xs) var(--space-m);
  border-radius: var(--radius-m);
  font-family: var(--font-body); font-size: var(--step-0); font-weight: var(--weight-strong);
  text-decoration: none; cursor: pointer;
  border: 1px solid transparent;
  transition: background var(--dur) var(--ease-standard), color var(--dur) var(--ease-standard),
    border-color var(--dur) var(--ease-standard), transform var(--dur-fast) var(--ease-out);
}
/* Primary is INK, not accent. 14.34:1 in light, 14.99:1 in dark. The accent is already carrying
   eyebrows, links, rules and hover states; putting it on every button too would spend the entire
   one-accent budget the design rests on. */
.btn-primary { background: var(--btn-primary-bg); color: var(--btn-primary-fg); }
.btn-primary:hover { background: var(--accent-ink); color: var(--ground); }
.btn-secondary { background: var(--btn-secondary-bg); color: var(--btn-secondary-fg); border-color: var(--control-edge); }
.btn-secondary:hover { background: var(--ground-sunken); }
/* Booking goes to the hub (tfrooms.com). The warm fill is the one action on the page that is not
   ink-on-ground, so it reads as THE button: ink on --accent-fill-strong is 7.47:1 light, 7.09:1 dark. */
.btn-book { background: var(--accent-fill-strong); color: var(--ink); border-color: var(--accent-edge); }
.btn-book:hover { background: var(--accent-ink); color: var(--ground); border-color: var(--accent-ink); }
.btn-book svg { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 2; }
.btn:active { transform: translateY(1px); }
.btn-sm { min-height: 36px; padding: var(--space-3xs) var(--space-s); font-size: var(--step--1); }
/* A phone number must never wrap. It rendered as three stacked lines in the header at 156x81px,
   which is both ugly and unreadable as a number. */
.btn { white-space: nowrap; }
.header-actions .btn span { white-space: nowrap; }

.icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px;
  background: transparent; color: var(--ink);
  border: 1px solid var(--control-edge); border-radius: var(--radius-m); cursor: pointer;
}
.icon-btn:hover { background: var(--ground-sunken); }
.icon-btn svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 1.75; }

/* ---- Hero ------------------------------------------------------------------------------- */

/* Text never sits directly on a photograph. A computed style cannot report the pixel colour of a
   background image, so contrast over one is unmeasurable rather than merely unknown. The scrim
   makes it measurable, and the measured value is 12.6:1. */
.hero { position: relative; isolation: isolate; background: var(--ground-sunken); }
.hero-media { position: absolute; inset: 0; z-index: -2; overflow: hidden; }
/* The SAME <picture> height-chain trap that bit the property cards, in a second place, and this
   one shipped. A percentage height on the img resolves against its parent, which is the <picture>,
   and the reset gives that height:auto. An indeterminate percentage falls back to auto, so the img
   sized to its own 3:2 ratio: measured 3825x2550 inside a 752px-tall hero. overflow:hidden then
   cropped it from the TOP, which is why the hero showed nothing but sky. object-fit never got a
   chance to run, because the box it was fitting into was the wrong box.
   The picture has to carry the height for the img's 100% to mean anything. */
.hero-media picture { display: block; width: 100%; height: 100%; }
/* Biased above centre on purpose. The photograph is 3:2 and the hero band is up to 5:1 on a wide
   screen, so only about 29% of the image height survives the crop. Dead centre lands half on sky.
   58% was tried and lands entirely on the valley floor, which reads as a golf course rather than a
   canyon. 44% catches the basalt walls on both sides framing the river and the town, which is the
   part that says Twin Falls. Checked at 3840, 1440, 1280, 768 and 390 wide. */
.hero-media img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 44%; }
.hero-body { padding-block: var(--space-2xl); max-width: 46rem; }

/* The scrim lives on the BODY as a real background, not on a ::before pseudo-element.
   A contrast checker reads computed styles off elements and cannot see a pseudo-element at all,
   so a scrim drawn that way makes hero text permanently unmeasurable: the checker reports the
   text sitting on the section's own ground and invents a failure. Putting a translucent panel on
   the element the text is actually in means the checker composites it correctly and the number is
   real. It is also more robust, because it does not depend on what the photograph happens to
   look like behind any given line of type. */
.hero.has-media .hero-body {
  background: rgb(18 22 28 / 0.88);
  padding-inline: var(--gutter);
  border-radius: var(--radius-l);
  margin-block: var(--space-l);
}
.hero.has-media .hero-body,
.hero.has-media h1,
.hero.has-media .lede,
.hero.has-media dt,
.hero.has-media dd { color: var(--scrim-ink); }
/* NOT --accent-fill. That token is a FILL: light in the light theme and dark in the dark theme.
   Used as ink over the scrim it measured 1.27:1 in light and 1.62:1 in dark. This is the exact
   "one token cannot be both ink and fill" trap, and the fix is to use a colour chosen for ink. */
.hero.has-media .eyebrow { color: #f0c9a8; }
.hero.has-media .btn-secondary { color: var(--scrim-ink); border-color: var(--scrim-ink); }
.hero.has-media .btn-secondary:hover { background: rgb(255 255 255 / 0.16); }
/* The primary button is --ink, which is near-black. Against the near-black scrim its edge measured
   1.17:1, i.e. invisible. On a dark panel the loudest control has to be the light one. */
.hero.has-media .btn-primary { background: var(--scrim-ink); color: #12161c; border-color: var(--scrim-ink); }
.hero.has-media .btn-primary:hover { background: #f0c9a8; color: #12161c; border-color: #f0c9a8; }
.hero-plain { background: var(--ground-sunken); }
/* A hero with no photograph still needs presence, or it reads as an accidental grey strip. */
.hero-plain .hero-body { padding-block: var(--space-2xl); }
.hero-plain::before { display: none; }

.rate-strip { display: flex; flex-wrap: wrap; gap: var(--space-m); margin-top: var(--space-m); }
.rate-strip div { min-width: 8rem; }
.rate-strip dt { font-size: var(--step--1); text-transform: uppercase; letter-spacing: 0.08em; opacity: 0.85; }
.rate-strip dd { margin: 0; font-family: var(--font-display); font-size: var(--step-2); font-weight: var(--weight-heading); }

/* ---- Cards ------------------------------------------------------------------------------ */

.card {
  background: var(--ground-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius-l);
  overflow: hidden;
  display: flex; flex-direction: column;
  transition: border-color var(--dur) var(--ease-standard), box-shadow var(--dur) var(--ease-standard);
}
.card:hover { border-color: var(--line-strong); box-shadow: var(--shadow-m); }
.card-media { aspect-ratio: 4 / 3; background: var(--ground-sunken); position: relative; overflow: hidden; }
/* The <picture> element sits between .card-media and the <img>. A percentage height on the img
   resolves against ITS parent, which is the <picture>, and the reset gives that `height: auto`.
   An indeterminate percentage height falls back to auto, so every card image sized to its own
   aspect ratio instead of the 4:3 box and the cards came out different heights. The picture has
   to carry the height for the img's 100% to mean anything. */
.card-media picture { display: block; width: 100%; height: 100%; }
/* The property's own mark, sat on the card image. Every property has one, so this is the fastest
   way for a returning guest to find the place they stayed. */
/* The twelve marks range from 550x200 to 350x500. A single max-height makes the wide ones large
   and the tall ones look like stamps, so each sits in a FIXED BOX and is contained inside it.
   Every logo then reads at the same optical weight regardless of its aspect ratio. */
/* Specificity matters here and cost a render. `.card-media img` is (0,1,1) and a bare `.card-logo`
   is (0,1,0), so the logo inherited width:100%, height:100% and object-fit:cover from the card
   image rule and covered the photograph entirely. The selector below is (0,2,1) so it wins. */
.card-media img.card-logo,
.card-logo {
  position: absolute; left: var(--space-2xs); bottom: var(--space-2xs);
  width: 104px; height: 60px;
  object-fit: contain; object-position: left center;
  background: rgb(255 253 248 / 0.95);
  border: 1px solid rgb(28 36 48 / 0.10);
  border-radius: var(--radius-s);
  padding: 6px 8px;
  box-shadow: 0 1px 3px rgb(28 36 48 / 0.12);
}
/* On a card with no photograph the mark is the whole visual, so it gets more room and no plate. */
.card-body img.card-logo-bare,
.card-logo-bare {
  width: 100%; max-width: 180px; height: 76px;
  object-fit: contain; object-position: left center;
  margin-bottom: var(--space-2xs);
}
/* The hero mark is contained too, so a tall logo cannot tower over a wide one. */
.property-logo, .property-logo img {
  height: 84px; width: auto; max-width: 260px;
  object-fit: contain; object-position: left center;
}
.card-media img { width: 100%; height: 100%; object-fit: cover; }
.card-body { padding: var(--space-m); flex: 1; display: flex; flex-direction: column; gap: var(--space-2xs); }
.card-body h3 { margin: 0; font-size: var(--step-1); }
.card-sub { font-size: var(--step--1); color: var(--ink-muted); margin: 0; }
.card-foot { margin-top: auto; padding-top: var(--space-s); display: flex; justify-content: space-between; align-items: baseline; gap: var(--space-2xs); }
.price { font-family: var(--font-display); font-size: var(--step-1); color: var(--accent-ink); }
.price small { color: var(--ink-muted); font-family: var(--font-body); }

/* A card whose whole surface is a link, without nesting interactive elements. */
.card-link { position: absolute; inset: 0; }
.card { position: relative; }

.badge {
  display: inline-block; padding: 2px var(--space-2xs);
  background: var(--accent-fill); color: var(--ink);
  border: 1px solid var(--accent-edge);
  border-radius: 999px; font-size: var(--step--1); font-weight: var(--weight-strong);
}

/* ---- Tables ----------------------------------------------------------------------------- */

.table-scroll { overflow-x: auto; margin-block: var(--space-m); }
table { border-collapse: collapse; width: 100%; font-size: var(--step-0); }
caption { text-align: left; color: var(--ink-muted); font-size: var(--step--1); padding-bottom: var(--space-2xs); }
th, td { text-align: left; padding: var(--space-xs) var(--space-s); border-bottom: 1px solid var(--line); }
thead th { border-bottom: 2px solid var(--line-strong); font-weight: var(--weight-strong); white-space: nowrap; }
tbody tr:last-child td { border-bottom: none; }

/* ---- FAQ -------------------------------------------------------------------------------- */

.faq details {
  border-bottom: 1px solid var(--line);
  padding-block: var(--space-s);
}
.faq summary {
  cursor: pointer; font-family: var(--font-display); font-size: var(--step-1);
  font-weight: var(--weight-heading); list-style: none;
  display: flex; justify-content: space-between; gap: var(--space-s); align-items: baseline;
  min-height: 44px;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after { content: '+'; color: var(--accent-ink); font-size: var(--step-2); line-height: 1; }
.faq details[open] summary::after { content: '\2212'; }
.faq details > *:not(summary) { margin-top: var(--space-xs); }

/* ---- Amenity grid ----------------------------------------------------------------------- */

.amenities { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-2xs);
  grid-template-columns: repeat(auto-fit, minmax(min(14rem, 100%), 1fr)); }
.amenities li { display: flex; gap: var(--space-2xs); align-items: flex-start; max-width: none; }
.amenities svg { width: 18px; height: 18px; flex-shrink: 0; margin-top: 0.25em; stroke: var(--accent-ink); fill: none; stroke-width: 1.75; }

/* ---- Forms ------------------------------------------------------------------------------ */

.field { display: flex; flex-direction: column; gap: var(--space-3xs); margin-bottom: var(--space-s); }
.field label { font-weight: var(--weight-strong); font-size: var(--step-0); }
.field .hint { font-size: var(--step--1); color: var(--ink-muted); }
.field input, .field select, .field textarea {
  font: inherit; color: var(--ink); background: var(--ground-raised);
  border: 1px solid var(--control-edge); border-radius: var(--radius-m);
  padding: var(--space-xs) var(--space-s); min-height: 44px; width: 100%;
}
.field input:focus-visible, .field select:focus-visible, .field textarea:focus-visible { border-color: var(--accent-ink); }
.field-error { color: var(--warn); font-size: var(--step--1); font-weight: var(--weight-strong); }
.honeypot { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.honeypot input { width: 1px; height: 1px; min-height: 0; padding: 0; border: 0; }

/* ---- Footer ----------------------------------------------------------------------------- */

.site-footer { background: var(--ground-sunken); border-top: 1px solid var(--line); padding-block: var(--space-xl) var(--space-l); margin-top: var(--space-2xl); }
.footer-grid { display: grid; gap: var(--space-l); grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr)); }
.site-footer h2 { font-size: var(--step-0); font-family: var(--font-body); font-weight: var(--weight-strong);
  text-transform: uppercase; letter-spacing: 0.08em; color: var(--ink-muted); margin: 0 0 var(--space-2xs); }
.site-footer ul { list-style: none; margin: 0; padding: 0; }
.site-footer li { margin-bottom: var(--space-3xs); }
.site-footer a { color: var(--ink); text-decoration: none; }
.site-footer a:hover { text-decoration: underline; }
.footer-bottom { margin-top: var(--space-l); padding-top: var(--space-s); border-top: 1px solid var(--line);
  display: flex; flex-wrap: wrap; gap: var(--space-s); justify-content: space-between; font-size: var(--step--1); color: var(--ink-muted); }

/* ---- Floating help button --------------------------------------------------------------- */

/* SC 3.2.6 Consistent Help: this appears in the same place in the same order on every page.
   SC 2.4.11 Focus Not Obscured: it is anchored bottom-right and the panel is dismissible, so it
   never covers a focused element that the user tabbed to. */
.help-fab {
  position: fixed; right: var(--space-m); bottom: var(--space-m); z-index: 90;
  display: inline-flex; align-items: center; gap: var(--space-2xs);
  min-height: 52px; padding: var(--space-xs) var(--space-m);
  background: var(--ink); color: var(--ground);
  border: 1px solid var(--ink); border-radius: 999px;
  font-family: var(--font-body); font-weight: var(--weight-strong); font-size: var(--step-0);
  box-shadow: var(--shadow-m); cursor: pointer;
}
.help-fab:hover { background: var(--accent-ink); border-color: var(--accent-ink); }
.help-fab { text-decoration: none; bottom: calc(var(--space-m) + env(safe-area-inset-bottom, 0px)); }
.help-fab:hover, .help-fab:focus-visible { color: var(--ground); }
.help-fab svg { width: 22px; height: 22px; stroke: currentColor; fill: none; stroke-width: 1.75; }

.help-panel {
  position: fixed; right: var(--space-m); bottom: calc(var(--space-m) + 64px); z-index: 95;
  width: min(22rem, calc(100vw - var(--space-m) * 2));
  background: var(--ground-raised); color: var(--ink);
  border: 1px solid var(--control-edge); border-radius: var(--radius-l);
  box-shadow: var(--shadow-m); padding: var(--space-m);
}
.help-panel[hidden] { display: none; }
/* The panel must not exceed the viewport. At 320x256 it was clipped off the TOP with no scroll
   container, so the title and the first action were unreachable. Cap it against the space between
   the FAB and the top of the screen and let it scroll inside that. */
.help-panel {
  max-height: calc(100dvh - var(--space-m) * 2 - 64px);
  overflow-y: auto;
  overscroll-behavior: contain;
}
/* The close button is absolutely positioned in the top-right of the panel and its rectangle
   intersected the title at every viewport measured, so the first line of the heading ran
   underneath a 44px button. Reserve the gutter. */
.help-head { display: flex; align-items: flex-start; gap: var(--space-2xs); margin-bottom: var(--space-2xs); }
.help-panel h2 { font-size: var(--step-1); margin: 0; flex: 1 1 auto; min-width: 0; }
.help-head .help-close { flex: 0 0 auto; }
.help-panel p { font-size: var(--step--1); color: var(--ink-muted); margin-bottom: var(--space-s); }
.help-panel .btn { width: 100%; margin-bottom: var(--space-2xs); }
.help-close { position: static; }

@media (max-width: 30rem) {
  .help-fab { right: var(--space-s); bottom: calc(var(--space-s) + env(safe-area-inset-bottom, 0px)); }
  .help-fab .help-fab-label { display: none; }
  .help-fab { min-width: 52px; justify-content: center; padding: var(--space-xs); }
}

/* ---- Reveal on scroll ------------------------------------------------------------------- */

/* The whole block sits inside @supports. A browser that has not confirmed it can run the
   animation never sees the opacity: 0 rule at all and renders everything normally. A progressive
   enhancement that can hide your content is not an enhancement. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .reveal {
      animation: reveal-in linear both;
      animation-timeline: view();
      animation-range: entry 10% cover 26%;
    }
    @keyframes reveal-in {
      from { opacity: 0; transform: translateY(14px); }
      to { opacity: 1; transform: none; }
    }
  }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}

/* ---- Print ------------------------------------------------------------------------------ */

@media print {
  .site-header, .site-footer, .help-fab, .help-panel, .skip-link { display: none !important; }
  body { background: #fff; color: #000; }
  a::after { content: ' (' attr(href) ')'; font-size: 0.85em; color: #444; }
}

/* ---- Review and maintenance blocks ------------------------------------------------------ */

.review-block {
  background: var(--ground-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius-l);
  padding: var(--space-l);
  margin-block: var(--space-l);
}
.review-band { background: var(--accent-fill); border-color: var(--accent-edge); }
.review-inner {
  display: flex; flex-wrap: wrap; gap: var(--space-l);
  align-items: center; justify-content: space-between;
}
.review-copy { flex: 1 1 22rem; }
.review-copy h2 { margin-top: 0; }
.review-qr { margin: 0; text-align: center; flex: 0 0 auto; }
.review-qr img {
  width: 176px; height: 176px;
  background: var(--ground);
  border: 1px solid var(--control-edge);
  border-radius: var(--radius-m);
  padding: var(--space-2xs);
}
.review-qr figcaption {
  font-size: var(--step--1); color: var(--ink-muted);
  max-width: 20ch; margin: var(--space-2xs) auto 0; line-height: var(--lh-tight);
}
/* On the accent band the muted ink token is tuned for the bone ground, so restate the pair that
   was actually measured: --ink on --accent-fill is 10.13:1. */
.review-band .review-qr figcaption, .review-band p { color: var(--ink); }

.callout {
  background: var(--ground-sunken);
  border-left: 4px solid var(--accent-ink);
  border-radius: 0 var(--radius-m) var(--radius-m) 0;
  padding: var(--space-m) var(--space-l);
}
.callout h2 { margin-top: 0; font-size: var(--step-1); }

.report-form { max-width: 34rem; }

.help-divider {
  margin: var(--space-s) 0 var(--space-2xs);
  padding-top: var(--space-s);
  border-top: 1px solid var(--line);
  font-size: var(--step--1); color: var(--ink-muted);
}
.help-email {
  display: block; margin-top: var(--space-2xs);
  font-size: var(--step--1); color: var(--ink-muted);
  min-height: 24px; line-height: 24px;
}

@media print {
  /* The QR codes are the one thing worth printing off these pages: front-desk staff tape them
     inside room doors. Keep them, drop the rest of the block's chrome. */
  .review-block { border: none; background: none; padding: 0; break-inside: avoid; }
  .review-qr img { border: 1px solid #000; }
}

.property-logo { margin-bottom: var(--space-s); }
/* On a photographic hero the mark sits on the dark scrim, so it needs a lift to stay legible
   against whatever the photograph happens to be doing behind it. */
.hero.has-media .property-logo { filter: drop-shadow(0 1px 4px rgb(0 0 0 / 0.5)); }
@media (min-width: 48rem) { .property-logo, .property-logo img { height: 104px; max-width: 320px; } }


/* ---- Wide-viewport overrides -------------------------------------------------------------- */
/* These sit at the END of the file on purpose. Every selector here is a single class at (0,1,0),
   the same specificity as the rules it overrides, so source order is the only thing deciding the
   winner. Putting these earlier makes them silently ineffective, which is exactly what happened
   to the first attempt: the hero stayed pinned at 46rem on a 3840px screen. */
@media (min-width: 100rem) {
  .hero-body { max-width: 60rem; }
  .hero-body h1 { font-size: var(--step-5); }
  .wrap-prose { width: min(100% - var(--gutter) * 2, 52rem); }
}

/* ---- Editorial separators ------------------------------------------------------------------
 *
 * Borrowed from apprised.news and re-coloured for this brand. The vocabulary there is: squared
 * corners rather than rounded, hairline rules doing the separating, a thin brand-coloured border
 * on a callout rather than a filled panel, and a triangle that rotates when a disclosure opens.
 * Apprised uses a teal accent; this uses the rust already in the palette.
 *
 * The point of it is definition. Cards floating on a ground with nothing between them read as
 * unfinished; a page with rules, notches and squared edges reads as edited.
 */

/* A section rule with a notch in it. The notch is the accent, the rule is the hairline, and the
   whole thing is one element with two pseudo-elements rather than an image. */
.rule {
  position: relative;
  border: 0;
  border-top: 1px solid var(--line-strong);
  margin: var(--space-xl) 0 var(--space-l);
  height: 0;
}
.rule::before {
  content: '';
  position: absolute;
  top: -1px; left: 0;
  width: 4rem; height: 0;
  border-top: 3px solid var(--accent-ink);
}
/* The triangle. A CSS border triangle rather than a glyph, so it inherits the accent and needs
   no font. Sits on the rule, pointing down into the section it introduces. */
.rule::after {
  content: '';
  position: absolute;
  top: 0; left: 1.25rem;
  width: 0; height: 0;
  border-left: 6px solid transparent;
  border-right: 6px solid transparent;
  border-top: 7px solid var(--accent-ink);
}

/* Section headings get the same notch without needing a separate element. */
.section-tight > h2:first-child,
.section > h2:first-child {
  position: relative;
  padding-top: var(--space-m);
}
.section-tight > h2:first-child::before,
.section > h2:first-child::before {
  content: '';
  position: absolute;
  top: 0; left: 0;
  width: 3rem; height: 3px;
  background: var(--accent-ink);
}

/* Definition rows: hairline-separated, label left, value right. This is the shape apprised uses
   for its source lists, and it is the right shape for "how far to" and for a rate line. */
.deflist { margin: var(--space-m) 0; border-top: 1px solid var(--line); }
.deflist > div {
  display: flex; flex-wrap: wrap; gap: var(--space-2xs);
  justify-content: space-between; align-items: baseline;
  padding: var(--space-xs) 0;
  border-bottom: 1px solid var(--line);
}
.deflist dt { color: var(--ink-muted); font-size: var(--step--1); margin: 0; }
.deflist dd { margin: 0; font-weight: var(--weight-strong); font-variant-numeric: tabular-nums; }

/* Squared, not rounded. Editorial surfaces get a hairline and a hard corner. */
.card, .review-block, .callout, .table-scroll table { border-radius: 0; }
.card { border-color: var(--line-strong); }

/* The callout keeps its accent bar but loses the rounded side, matching the apprised treatment
   of a bordered analysis block rather than a filled tint. */
.callout {
  background: var(--ground-raised);
  border: 1px solid var(--line-strong);
  border-left: 3px solid var(--accent-ink);
  border-radius: 0;
}

/* A status pill with a horizontal wash rather than a flat fill. Subtle, and it reads as a label
   rather than a button, which matters because it is not clickable. */
.pill {
  display: inline-block;
  padding: 2px var(--space-2xs);
  font-size: var(--step--1);
  font-weight: var(--weight-strong);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent-ink);
  border: 1px solid var(--accent-edge);
  border-radius: 0;
  background: linear-gradient(90deg, color-mix(in srgb, var(--accent-fill) 55%, transparent), transparent);
}

/* The FAQ disclosure marker becomes a triangle that rotates, replacing the +/− pair. */
.faq summary::after { content: none; }
.faq summary::before {
  content: '';
  flex: 0 0 auto;
  width: 0; height: 0;
  margin-top: 0.55em;
  border-top: 6px solid transparent;
  border-bottom: 6px solid transparent;
  border-left: 8px solid var(--accent-ink);
  transition: transform var(--dur-fast) var(--ease-out);
  transform-origin: 25% 50%;
}
.faq details[open] summary::before { transform: rotate(90deg); }
.faq summary { gap: var(--space-xs); }
.faq details { border-bottom: 1px solid var(--line); }
.faq details[open] { border-bottom-color: var(--line-strong); }

/* Banded sections get a hairline top and bottom rather than only a colour change, so the band
   has an edge even where the two grounds are close in value. */
.section-sunken { border-block: 1px solid var(--line); }

@media (prefers-reduced-motion: reduce) {
  .faq summary::before { transition: none; }
}


/* ---- Vertical rhythm correction --------------------------------------------------------------
 *
 * The gaps between sections were compounding. `.section-tight` pays 4rem of padding top AND
 * bottom, and `h2` separately carries `margin-top: var(--space-xl)`, another 4rem. Between two
 * sections that is up to 12rem of nothing, which reads as an unfinished page rather than a
 * generous one. The large spacing token is worth trusting between MAJOR bands, not between every
 * heading and the paragraph above it.
 *
 * Now that sections carry a visible notch, they no longer need distance to be legible as separate.
 */
.section-tight > h2:first-child,
.section > h2:first-child,
.wrap-prose > .section-tight > h2:first-child { margin-top: 0; }

.section-tight { padding-block: var(--space-l); }
.section { padding-block: var(--space-xl); }

/* A heading that follows body copy still needs air, just not four rems of it. */
h2 { margin-top: var(--space-l); }
h3 { margin-top: var(--space-m); }

/* The first section after a hero should sit close to it. */
.hero + .wrap > .section-tight:first-child,
.hero + .wrap-prose > .section-tight:first-child { padding-top: var(--space-m); }

/* A table caption directly under a heading that says the same thing is noise. Keep the caption
   for screen readers and drop it visually where the heading already carries it. */
.table-scroll table > caption { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }

/* ---- The map ---------------------------------------------------------------------------------
 *
 * Leaflet ships its own stylesheet; this is only the parts that belong to this brand: the pins,
 * the legend, the popup and the index beneath. The container carries an explicit aspect ratio so
 * the page does not reflow when tiles arrive, which is the usual cause of a map scoring badly on
 * layout shift.
 *
 * The pin is a CSS shape with two letters in it rather than an image, so it inherits the palette,
 * survives dark mode, and costs no request. Leaflet renders it through a divIcon.
 */
.map-section { margin-block: var(--space-xl); }

.property-map {
  width: 100%;
  aspect-ratio: 16 / 9;
  min-height: 20rem;
  max-height: 34rem;
  margin: var(--space-m) 0 var(--space-s);
  border: 1px solid var(--line-strong);
  background: var(--ground-sunken);
  z-index: 0; /* Leaflet panes carry z-index up to 800; keep the whole map under the header. */
}
@media (max-width: 40rem) { .property-map { aspect-ratio: 4 / 5; max-height: none; } }

.map-pin {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px; height: 28px;
  border-radius: 50%;
  font: var(--weight-strong) 11px/1 var(--font-body);
  letter-spacing: 0.02em;
  color: #ffffff;
  border: 2px solid #ffffff;
  box-shadow: 0 1px 4px rgb(0 0 0 / 0.45);
}
/* Both pin colours are checked against white text: the rust reads 7.15:1 and the slate 8.09:1. */
.map-pin--motel { background: #8a3a16; }
.map-pin--extended { background: #2f4858; }
.map-pin--landmark {
  background: var(--ground-raised);
  color: var(--ink-muted);
  border-color: var(--line-strong);
  width: 18px; height: 18px;
  font-size: 9px;
}

.map-legend {
  display: flex; flex-wrap: wrap; gap: var(--space-s);
  list-style: none; margin: 0; padding: 0;
  font-size: var(--step--1);
}
.map-legend li { display: flex; align-items: center; gap: var(--space-3xs); }

.map-fallback { font-size: var(--step--1); color: var(--ink-muted); margin: 0 0 var(--space-s); }

.map-index {
  list-style: none; margin: 0; padding: 0;
  display: grid;
  /* A column gap, and a track wide enough that a name and its address stay together. Without the
     gap, the address of one property sits hard against the name of the next and the eye pairs the
     wrong two. 22rem is the widest name plus the longest address at the body size. */
  column-gap: var(--space-xl);
  row-gap: 0;
  grid-template-columns: repeat(auto-fill, minmax(min(22rem, 100%), 1fr));
  border-top: 1px solid var(--line);
}
.map-index li {
  display: flex; flex-wrap: wrap; justify-content: space-between; align-items: baseline;
  gap: var(--space-2xs);
  padding: var(--space-2xs) 0;
  border-bottom: 1px solid var(--line);
}
.map-index li span { color: var(--ink-muted); font-size: var(--step--1); }
.map-index-btn {
  background: none; border: 0; padding: 0;
  font: inherit; font-weight: var(--weight-strong);
  color: var(--accent-ink);
  text-align: left;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.map-index-btn:hover { text-decoration-thickness: 2px; }

/* Leaflet's popup is a white box by default; give it the site's surface so it does not glare in
   dark mode, and square the corners to match everything else. */
.leaflet-popup-content-wrapper,
.leaflet-popup-tip {
  background: var(--ground-raised);
  color: var(--ink);
  border-radius: 0;
  box-shadow: 0 6px 24px rgb(0 0 0 / 0.28);
}
.leaflet-popup-content { margin: var(--space-xs) var(--space-s); font-family: var(--font-body); }
.leaflet-container { font-family: var(--font-body); background: var(--ground-sunken); }
.leaflet-container a { color: var(--accent-ink); }
.map-popup { display: grid; gap: 2px; }
.map-popup-addr, .map-popup-rate { color: var(--ink-muted); font-size: var(--step--1); }
.map-popup-links { display: flex; gap: var(--space-xs); margin-top: var(--space-3xs); }
/* Leaflet writes its controls with a light background; the attribution needs to stay legible. */
.leaflet-control-attribution {
  background: rgb(255 255 255 / 0.85) !important;
  color: #23272e !important;
}
.leaflet-control-attribution a { color: #1c4f8a !important; }
.leaflet-bar a {
  background: var(--ground-raised);
  color: var(--ink);
  border-bottom-color: var(--line-strong);
}
.leaflet-bar a:hover { background: var(--ground-sunken); color: var(--ink); }

/* ---- Footer social and payment ---------------------------------------------------------------
 *
 * Two rows below the link columns: where to find the business, and what it takes at the desk.
 * The card marks are real images at their native 100x63 and are the only raster images in the
 * footer; everything else is a path that inherits currentColor.
 */
.footer-meta {
  display: grid;
  gap: var(--space-l);
  grid-template-columns: repeat(auto-fit, minmax(min(18rem, 100%), 1fr));
  margin-top: var(--space-l);
  padding-top: var(--space-m);
  border-top: 1px solid var(--line);
}
.social-row { display: flex; flex-wrap: wrap; gap: var(--space-2xs); }
.social-row li { margin: 0; }
.social-row a {
  display: inline-flex; align-items: center; gap: var(--space-3xs);
  padding: var(--space-3xs) var(--space-2xs);
  border: 1px solid var(--line-strong);
  color: var(--ink);
  font-size: var(--step--1);
  min-height: 2.75rem; /* 2.5.8: a 44px target, which an icon-sized link otherwise misses */
  box-sizing: border-box;
}
.social-row a:hover { background: var(--ground-raised); text-decoration: none; }
.social-row a:hover span { text-decoration: underline; }
.social-row svg { width: 18px; height: 18px; fill: currentColor; flex: 0 0 auto; }

.pay-row { display: flex; flex-wrap: wrap; gap: var(--space-2xs); align-items: center; }
.pay-row li { margin: 0; }
.pay-row img {
  display: block;
  width: 50px; height: 32px;
  object-fit: contain;
  background: #ffffff;
  border: 1px solid var(--line);
  padding: 2px;
}
.pay-note { font-size: var(--step--2); color: var(--ink-muted); margin: var(--space-2xs) 0 0; max-width: 34rem; }

@media print {
  .property-map, .map-legend, .social-row, .pay-row { display: none !important; }
}

/* ---- Photo attachment -------------------------------------------------------------------------
 *
 * The dashed edge says "drop or choose" without a JavaScript drop zone, which would be a control
 * a keyboard cannot reach. The list below it is written by app.js and is a live region, so a
 * screen reader hears what was attached rather than only seeing it.
 */
.file-field input[type="file"] {
  width: 100%;
  padding: var(--space-2xs);
  border: 1px dashed var(--line-strong);
  background: var(--ground-raised);
  color: var(--ink);
  font: inherit;
}
.file-field input[type="file"]::file-selector-button {
  font: inherit;
  font-weight: var(--weight-strong);
  padding: var(--space-3xs) var(--space-xs);
  margin-right: var(--space-xs);
  border: 1px solid var(--accent-ink);
  background: var(--accent-ink);
  color: #ffffff;
  cursor: pointer;
}
.file-list { list-style: none; margin: var(--space-2xs) 0 0; padding: 0; font-size: var(--step--1); }
.file-list li {
  display: flex; justify-content: space-between; gap: var(--space-xs);
  padding: var(--space-3xs) 0;
  border-bottom: 1px solid var(--line);
}
/* --warn is the palette's alert colour and is already contrast-checked in both themes (5.44:1 on
   the light ground, 9.24:1 on the dark one). The word "too large" carries the meaning as well as
   the hue, so the state is not signalled by colour alone (1.4.1). */
.file-list li.over { color: var(--warn); font-weight: var(--weight-strong); }

/* ---- The property's own domain, on the card ---------------------------------------------------
 *
 * Quieter than the heading link on purpose. The card has one primary target, its property page on
 * this site; the domain is a secondary route for someone who wants the property's own website. Two
 * links of equal weight on one card is what makes a reader hesitate.
 */
.card-own {
  font-size: var(--step--1);
  color: var(--ink-muted);
  text-decoration: underline;
  text-decoration-color: var(--line-strong);
  text-underline-offset: 3px;
  white-space: nowrap;
}
.card-own:hover { color: var(--accent-ink); text-decoration-color: currentColor; }
.card-foot { display: flex; flex-wrap: wrap; gap: var(--space-2xs); justify-content: space-between; align-items: baseline; }

/* ---- Small screens -----------------------------------------------------------------------
 *
 * The header had NO small-screen rule at all. Measured on the deployed site at a true 390px
 * layout viewport with puppeteer: 290px of horizontal overflow, a header 327px tall, and the five
 * nav links wrapped onto five separate rows. .header-inner is a nowrap flex row holding a 199px
 * brand, the nav, and 305px of actions; the actions cannot shrink because .btn is white-space:
 * nowrap, so they simply ran off the right edge.
 *
 * The fix needs no JavaScript and no hamburger. Below 60rem the header wraps and the nav becomes
 * one horizontally scrollable strip on its own row, which keeps every link reachable and visible
 * rather than hiding them behind a control that has to be built, labelled and focus-managed.
 */
@media (max-width: 60rem) {
  .header-inner { flex-wrap: wrap; row-gap: var(--space-2xs); }
  /* min-width: 0 is load-bearing. A flex item defaults to min-width: auto, which refuses to shrink
     below its content's min-content size, so the strip sized itself to all five links (607px) and
     pushed the page 238px wide even though the ul already had overflow-x: auto. The overflow rule
     cannot do anything until the box is allowed to be narrower than its contents. */
  .site-nav { order: 3; flex: 1 0 100%; min-width: 0; margin-left: 0; }
  .site-nav ul {
    flex-wrap: nowrap;
    min-width: 0;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    /* The strip is scrolled, so it must not also drag the page sideways. */
    scrollbar-width: none;
    -ms-overflow-style: none;
  }
  .site-nav ul::-webkit-scrollbar { display: none; }
  .site-nav a { white-space: nowrap; }
  .header-actions { margin-left: auto; }
}

@media (max-width: 34rem) {
  /* The wrapped header is about 150px tall, and a sticky bar that deep eats a fifth of a phone
     screen on every scroll. It is released here. Booking stays one tap away: the floating button is
     fixed to the viewport on every page and goes straight to the booking form on tfrooms.com. */
  .site-header { position: static; }

  /* Brand drops to one line. "New Leaf Properties" is still in the footer of every page. */
  .brand-sub { display: none; }

  /* Actions get their own full-width row and spread across it, so the language switch, the theme
     toggle and the phone number all stay visible rather than one of them being cut off. */
  .header-actions { order: 4; flex: 1 0 100%; justify-content: space-between; margin-left: 0; flex-wrap: wrap; row-gap: var(--space-2xs); }
  .header-actions .btn-book { order: -1; flex: 1 0 100%; }

  /* The hero was 810px tall, almost the whole viewport, because --space-2xl is a clamp that only
     bottoms out at 5rem. A phone does not need 83px of padding above and below the headline. */
  .hero-body { padding-block: var(--space-l); }

  /* The three rates were breaking 2 + 1 because each cell reserved 8rem. They fit on one line at
     390px once the reservation is dropped and the figure comes down one step. */
  .rate-strip { gap: var(--space-s); }
  .rate-strip div { min-width: 0; flex: 1 1 auto; }
  .rate-strip dd { font-size: var(--step-1); }

  /* Two buttons side by side at 390px leaves each too narrow to read; stacked they are full width
     and comfortably over the 44px target. */
  .hero-body .cluster { display: grid; gap: var(--space-2xs); }
  .hero-body .cluster .btn { width: 100%; }
}

/* ---- The property's own site, at the top of a property page -----------------------------------
 *
 * This block used to sit seventh on the page, below the FAQ. The domain is the identifier a
 * returning guest is most likely to already recognise from a card, a sign or a receipt, so it now
 * leads. The link is set slightly apart from body copy because it is a URL and reads as one.
 */
.own-site { border-left: 3px solid var(--accent-ink); padding-left: var(--space-s); }
.own-site h2 { margin-top: 0; }
.own-site-url {
  font-weight: var(--weight-strong);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 2px;
}

/* --- The New Leaf Properties wordmark in the footer. -------------------------------------------
   The artwork is black on white and the footer ground follows the theme, so the mark sits on its
   own white chip rather than on the theme surface. Sized by height with contain, because the file
   is 4:1 and the chip must not stretch it. */
.footer-wordmark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-block: var(--space-m, 1rem) var(--space-s, .5rem);
  padding: 8px 12px;
  background: #ffffff;
  border-radius: var(--radius, 6px);
}
.footer-wordmark picture {
  display: flex;
  align-items: center;
  justify-content: center;
}
.footer-wordmark img {
  display: block;
  width: auto;
  height: 36px;
  max-width: 100%;
  object-fit: contain;
}

/* ---- Narrow-screen tables --------------------------------------------------------------------
   The Spanish rate tables have a min-content width of about 635px ("Tipo de habitación", "Por
   noche"), and on a 360px phone that let the whole PAGE scroll sideways by 69px even though the
   table already sits in a .table-scroll container. Measured on every /es/propiedades/ page at 360
   and 390; the English pages were unaffected because their column headings are shorter.

   Fixed layout makes the table honour its container instead of its longest heading, and the cells
   wrap rather than forcing a column wider than the phone. Scoped to narrow screens so the
   auto-sized columns are untouched on a desktop. */
@media (max-width: 760px) {
  .table-scroll > table { table-layout: fixed; width: 100%; }
  .table-scroll th,
  .table-scroll td { overflow-wrap: anywhere; }
}

/* ==== v31 New Leaf Properties LLC lockup ==== */
/* Same lockup, same reasoning as the hub: a 4:1 mark needs a wide slot, and black type needs a
   white chip anywhere the header is dark.

   26px, not the hub's 34. This header puts the mark beside two lines of brand text and a five item
   nav that is allowed to wrap. At 34px the mark is 145 wide, the nav lost 105px and wrapped to a
   second row, and the header grew from 70px to 124px tall at every desktop width. At 26px the mark
   is 111 wide and the header measures 70px with a single nav row at 1440 and 1920, which is what
   it did before the mark changed. Measured against the live site, not guessed. */
.site-header .brand img { width: auto; height: 26px; max-width: none; object-fit: contain; flex: 0 0 auto; }
@media (prefers-color-scheme: dark) {
  .site-header .brand img { background: #fff; border-radius: 6px; padding: 3px 5px; }
}
@media (max-width: 560px) {
  .site-header .brand img { height: 22px; }
}

/* ---- WebKit form fields (New Leaf fleet v124f) ------------------------------------------ */

/* Owner report on the fleet (iPhone Safari and the Chrome app, both WebKit): date fields ran past
   the card and stood taller than the text fields. The UA sheet gives them a min-width and centres
   the value. This site has no date field today; the rules are here so the next form cannot regress.
   One rule per engine pseudo-element, so an engine that does not know a name drops only that rule. */
input[type="date"], input[type="time"], input[type="datetime-local"], input[type="month"], input[type="week"] {
  display: block; width: 100%; max-width: 100%; min-width: 0; box-sizing: border-box;
  -webkit-appearance: none; appearance: none; text-align: left;
}
input[type="date"]::-webkit-date-and-time-value, input[type="time"]::-webkit-date-and-time-value, input[type="datetime-local"]::-webkit-date-and-time-value, input[type="month"]::-webkit-date-and-time-value, input[type="week"]::-webkit-date-and-time-value { margin: 0; text-align: left; min-height: 1.2em; min-height: 1lh; }
input[type="date"]::-webkit-datetime-edit, input[type="time"]::-webkit-datetime-edit, input[type="datetime-local"]::-webkit-datetime-edit, input[type="month"]::-webkit-datetime-edit, input[type="week"]::-webkit-datetime-edit { padding: 0; }
input[type="date"]::-webkit-datetime-edit-fields-wrapper, input[type="time"]::-webkit-datetime-edit-fields-wrapper, input[type="datetime-local"]::-webkit-datetime-edit-fields-wrapper, input[type="month"]::-webkit-datetime-edit-fields-wrapper, input[type="week"]::-webkit-datetime-edit-fields-wrapper { padding: 0; }
input::-webkit-datetime-edit-year-field { padding-block: 0; }
input::-webkit-datetime-edit-month-field { padding-block: 0; }
input::-webkit-datetime-edit-week-field { padding-block: 0; }
input::-webkit-datetime-edit-day-field { padding-block: 0; }
input::-webkit-datetime-edit-hour-field { padding-block: 0; }
input::-webkit-datetime-edit-minute-field { padding-block: 0; }
input::-webkit-datetime-edit-second-field { padding-block: 0; }
input::-webkit-datetime-edit-millisecond-field { padding-block: 0; }
input::-webkit-datetime-edit-meridiem-field { padding-block: 0; }
input::-webkit-datetime-edit-ampm-field { padding-block: 0; }
input[type="date"]::-webkit-calendar-picker-indicator, input[type="time"]::-webkit-calendar-picker-indicator, input[type="datetime-local"]::-webkit-calendar-picker-indicator, input[type="month"]::-webkit-calendar-picker-indicator, input[type="week"]::-webkit-calendar-picker-indicator { padding-block: 0; margin-block: 0; cursor: pointer; }
form fieldset { min-width: 0; }
form input, form select, form textarea { max-width: 100%; }

/* ---- Utility bar + office clock (New Leaf fleet v123 chip, ported 2026-09-23) ------------ */

/* An inverse strip above the header in both themes: --ink on --ground is the site's own highest-contrast
   pair, so the bar is --ground on --ink (and flips with the theme). It holds the office-hours chip and the
   sentence that says bookings are made on tfrooms.com. */
.util-bar { background: var(--ink); color: var(--ground); font-size: var(--step--1); }
.util-inner { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: var(--space-3xs) var(--space-s); padding-block: var(--space-3xs); }
.util-book { margin: 0; }
.util-book a { display: inline-flex; align-items: center; }
.util-book a { color: inherit; font-weight: var(--weight-strong); text-underline-offset: 0.2em; white-space: nowrap; }
.util-book a:hover { text-decoration-thickness: 2px; }
svg.ext { display: inline-block; width: 0.9em; height: 0.9em; margin-left: 0.25em; vertical-align: -0.05em;
  stroke: currentColor; fill: none; stroke-width: 2.25; stroke-linecap: round; stroke-linejoin: round; }
.btn svg.ext { width: 14px; height: 14px; margin-left: 0; }

/* The chip. The words say open or closed; the dot is decoration (aria-hidden). tabular-nums keeps the
   count from wobbling each second. */
.office-clock { display: inline-flex; align-items: center; gap: 0.45rem; padding: 0.1rem 0.6rem;
  border: 1px solid color-mix(in srgb, currentColor 40%, transparent); border-radius: 6px;
  font-weight: var(--weight-strong); line-height: 1.4; white-space: nowrap; font-variant-numeric: tabular-nums; }
.office-clock .oc-dot { flex: none; width: 8px; height: 8px; border-radius: 50%; background: currentColor; opacity: 0.5; }
.office-clock[data-open="true"] .oc-dot { background: #2fbf71; opacity: 1; }
.office-clock[data-open="false"] .oc-dot { background: #ef5b4c; opacity: 1; }
@media (min-width: 601px) {
  /* the chip reserves the widest ticking text, so the static hours and the live count take the same room and
     the bar never changes height when the clock starts (app.js reads this min-width for its fit step) */
  .office-clock { min-width: 30.5em; justify-content: center; }
  .office-clock:lang(es) { min-width: 33em; }
}
@media (max-width: 600px) {
  .util-inner { justify-content: center; text-align: center; }
  .office-clock { max-width: 100%; white-space: normal; }
  .office-clock .oc-text { min-width: 0; }
  .office-clock .oc-detail { display: none; }
  .office-clock .oc-word, .office-clock .oc-verb, .office-clock .oc-count { white-space: nowrap; }
  .office-clock[data-open="unknown"] .oc-text { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
}
@media (max-width: 360px) {
  .office-clock .oc-word, .office-clock .oc-verb, .office-clock .oc-count { white-space: normal; }
}

/* the floating button's second line names the site it opens */
.help-fab-label { display: inline-flex; flex-direction: column; line-height: 1.2; text-align: left; }
.help-fab-sub { font-size: 0.75em; font-weight: var(--weight-body, 400); opacity: 0.9; }

/* ---- Property card extras: Book + QR codes (as on tfrooms.com/locations, 2026-09-23) ------ */
.card-book { margin: var(--space-xs) 0 0; }
.card-book .btn { width: 100%; white-space: normal; }
.card-qr { display: flex; flex-wrap: wrap; gap: var(--space-s); margin-top: var(--space-s); padding-top: var(--space-xs); border-top: 1px solid var(--line); }
.card-qr figure { margin: 0; display: flex; flex-direction: column; align-items: center; gap: var(--space-3xs); }
/* QR modules stay dark-on-light in both themes, or a phone camera cannot read them. */
.card-qr img { width: 96px; height: 96px; background: #f7f5f0; padding: 4px; border-radius: 4px; }
.card-qr figcaption { font-size: var(--step--1); }

/* ---- Accessibility widget (New Leaf fleet parity, 2026-09-23) --------------------------- */

/* The floating stack sits above the booking button on the right, column order top to bottom:
   back to top (appears after scrolling), text size, contrast, accessibility options. */
.float-stack { position: fixed; right: var(--space-m); z-index: 91;
  bottom: calc(var(--space-m) + 76px + env(safe-area-inset-bottom, 0px));
  display: flex; flex-direction: column; gap: 0.6rem; align-items: center; }
.float-btn { width: 48px; height: 48px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center;
  background: var(--ground-raised); color: var(--ink); border: 2px solid var(--control-edge);
  box-shadow: var(--shadow-m); cursor: pointer; padding: 0; font: inherit; }
.float-btn:hover { border-color: var(--accent-ink); color: var(--accent-ink); }
.float-btn[hidden] { display: none; }
.float-btn svg { width: 22px; height: 22px; stroke: currentColor; fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.float-btn svg .fill { fill: currentColor; stroke: none; }
.float-btn--text { font-weight: var(--weight-strong); font-size: 1rem; }
.float-btn--main { background: var(--ink); color: var(--ground); border-color: var(--ink); }
.float-btn--main:hover { background: var(--accent-ink); border-color: var(--accent-ink); color: var(--ground); }
.float-btn[aria-pressed="true"] { background: var(--ink); color: var(--ground); border-color: var(--ink); }
@media (max-width: 30rem) {
  .float-stack { right: var(--space-s); bottom: calc(var(--space-s) + 64px + env(safe-area-inset-bottom, 0px)); gap: 0.5rem; }
  .float-btn { width: 44px; height: 44px; }
}

/* The options panel: a native modal dialog slid in from the right. */
.a11y-dialog { position: fixed; inset: 0 0 0 auto; margin: 0; padding: 0; border: 0; height: 100%; max-height: 100%;
  width: min(24rem, 100%); max-width: 100%; background: var(--ground-raised); color: var(--ink);
  border-left: 1px solid var(--line-strong); box-shadow: var(--shadow-m); }
.a11y-dialog::backdrop { background: rgb(0 0 0 / 0.5); }
.a11y-inner { padding: var(--space-m); overflow-y: auto; height: 100%; box-sizing: border-box; }
.a11y-head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-s); margin-bottom: var(--space-s); }
.a11y-head h2 { font-size: var(--step-1); margin: 0; }
.a11y-group { padding-block: var(--space-s); border-bottom: 1px solid var(--line); }
.a11y-group:last-child { border-bottom: 0; }
.a11y-label { display: block; font-size: var(--step--1); font-weight: var(--weight-strong); color: var(--ink-muted); margin-bottom: var(--space-2xs); }
.a11y-sizes { display: flex; flex-wrap: wrap; gap: 6px; }
.a11y-sizes button { min-height: 44px; min-width: 44px; padding: 0.4rem 0.8rem; border-radius: var(--radius-s);
  background: var(--ground); color: var(--ink); border: 1px solid var(--control-edge); font: inherit; font-size: var(--step--1); cursor: pointer; }
.a11y-sizes button[aria-checked="true"] { background: var(--ink); color: var(--ground); border-color: var(--ink); font-weight: var(--weight-strong); }
.a11y-row { display: flex; align-items: center; justify-content: space-between; gap: var(--space-s); min-height: 48px; }
.a11y-switch { position: relative; flex: none; width: 52px; height: 30px; padding: 0; border-radius: 15px; cursor: pointer;
  background: var(--ground-sunken); border: 2px solid var(--control-edge); }
.a11y-switch::after { content: ''; position: absolute; top: 3px; left: 3px; width: 20px; height: 20px; border-radius: 50%;
  background: var(--ink-muted); transition: transform var(--dur-fast, 150ms) ease; }
.a11y-switch[aria-checked="true"] { background: var(--ink); border-color: var(--ink); }
.a11y-switch[aria-checked="true"]::after { transform: translateX(22px); background: var(--ground); }
.a11y-statement p { font-size: var(--step--1); margin: 0 0 var(--space-2xs); }

/* Text AND icon size. rem-based type follows the root size; the px-sized icons are raised too. */
html[data-text-size="14"] { font-size: 87.5%; }
html[data-text-size="18"] { font-size: 112.5%; }
html[data-text-size="20"] { font-size: 125%; }
html[data-text-size="18"] :is(.btn, .icon-btn, .help-fab, .float-btn) svg { width: 1.3em; height: 1.3em; }
html[data-text-size="20"] :is(.btn, .icon-btn, .help-fab, .float-btn) svg { width: 1.45em; height: 1.45em; }
html[data-text-size="18"] :is(.icon-btn, .float-btn) { width: 3.1rem; height: 3.1rem; }
html[data-text-size="20"] :is(.icon-btn, .float-btn) { width: 3.25rem; height: 3.25rem; }

/* High contrast: pure black on white (white on black in dark mode), hard edges, underlined links. */
html.a11y-hc { --ground: #ffffff; --ground-raised: #ffffff; --ground-sunken: #eeeeee; --ink: #000000; --ink-muted: #000000; --ink-faint: #1a1a1a;
  --line: #000000; --line-strong: #000000; --control-edge: #000000; --accent-ink: #6b1a00; --accent-fill: #ffffff; --accent-fill-strong: #ffd28a;
  --accent-edge: #000000; --btn-primary-bg: #000000; --btn-primary-fg: #ffffff; --btn-secondary-bg: #ffffff; --btn-secondary-fg: #000000;
  --btn-secondary-border: #000000; --ok: #005a2b; --warn: #6b3a00; --shadow-s: none; --shadow-m: 0 0 0 2px #000000; }
html.a11y-hc[data-theme="dark"] { --ground: #000000; --ground-raised: #000000; --ground-sunken: #1a1a1a; --ink: #ffffff; --ink-muted: #ffffff; --ink-faint: #e6e6e6;
  --line: #ffffff; --line-strong: #ffffff; --control-edge: #ffffff; --accent-ink: #ffc896; --accent-fill: #000000; --accent-fill-strong: #5a2600;
  --accent-edge: #ffffff; --btn-primary-bg: #ffffff; --btn-primary-fg: #000000; --btn-secondary-bg: #000000; --btn-secondary-fg: #ffffff;
  --btn-secondary-border: #ffffff; --ok: #8df0b8; --warn: #ffd28a; --shadow-m: 0 0 0 2px #ffffff; }
@media (prefers-color-scheme: dark) {
  html.a11y-hc:not([data-theme="light"]) { --ground: #000000; --ground-raised: #000000; --ground-sunken: #1a1a1a; --ink: #ffffff; --ink-muted: #ffffff; --ink-faint: #e6e6e6;
    --line: #ffffff; --line-strong: #ffffff; --control-edge: #ffffff; --accent-ink: #ffc896; --accent-fill: #000000; --accent-fill-strong: #5a2600;
    --accent-edge: #ffffff; --btn-primary-bg: #ffffff; --btn-primary-fg: #000000; --btn-secondary-bg: #000000; --btn-secondary-fg: #ffffff;
    --btn-secondary-border: #ffffff; --ok: #8df0b8; --warn: #ffd28a; --shadow-m: 0 0 0 2px #ffffff; }
}
html.a11y-hc a { text-decoration: underline; text-decoration-thickness: 2px; }
html.a11y-hc :is(.card, .btn, table, th, td) { border-width: 2px; border-color: var(--ink); }

/* Reduce motion: no animation, no transition, no smooth scroll; revealed content is simply there. */
html.a11y-rm, html.a11y-rm *, html.a11y-rm *::before, html.a11y-rm *::after {
  animation: none !important; transition: none !important; scroll-behavior: auto !important; }
html.a11y-rm .reveal { opacity: 1 !important; transform: none !important; }

/* Highlight links: every link underlined and outlined so it cannot be mistaken for plain text. */
html.a11y-links a:not(.btn) { text-decoration: underline !important; text-decoration-thickness: 2px !important;
  outline: 2px solid var(--accent-ink); outline-offset: 2px; border-radius: 2px; }
html.a11y-links a.btn { outline: 3px solid var(--accent-ink); outline-offset: 2px; }

/* Reading guide: a highlighted band that follows the pointer, for keeping your place in a line. */
.reading-guide { display: none; position: fixed; left: 0; right: 0; top: 0; height: 2.8em; z-index: 90; pointer-events: none;
  background: rgb(255 214 102 / 0.22); border-block: 2px solid rgb(0 0 0 / 0.55); }
html.a11y-rg .reading-guide { display: block; }
html[data-theme="dark"] .reading-guide { border-block-color: rgb(255 255 255 / 0.6); }

/* ---- Layout corrections, 2026-09-23 (Josh's phone screenshots) ------------------------------ */

/* 1. Floating buttons: one centred column above the message button, with the same right edge, and a
      small toggle on top that hides them all (the toggle then sits alone in the corner). */
.float-stack { width: 48px; align-items: center; gap: 0.55rem; }
.float-btn--toggle { width: 34px; height: 34px; border-width: 1px; box-shadow: var(--shadow-s, none); }
.float-btn--toggle svg { width: 16px; height: 16px; }
.float-btn--toggle .i-show { display: none; }
html.float-hidden .float-btn--toggle .i-hide { display: none; }
html.float-hidden .float-btn--toggle .i-show { display: block; }
html.float-hidden .float-stack > :not(#float-toggle),
html.float-hidden .help-fab { display: none !important; }
html.float-hidden .float-stack { bottom: calc(var(--space-m) + env(safe-area-inset-bottom, 0px)); }
.help-fab { font: inherit; font-weight: var(--weight-strong); }
@media (max-width: 30rem) {
  .help-fab { min-width: 48px; min-height: 48px; width: 48px; height: 48px; padding: 0; }
  .float-stack { right: var(--space-s); bottom: calc(var(--space-s) + 58px + env(safe-area-inset-bottom, 0px)); }
  html.float-hidden .float-stack { bottom: calc(var(--space-s) + env(safe-area-inset-bottom, 0px)); }
}

/* 2. The message panel (shares the accessibility panel's slide-in shell). */
.msg-lede { font-size: var(--step--1); color: var(--ink-muted); margin: 0 0 var(--space-s); }
.msg-form .field { margin-bottom: var(--space-xs); }
.msg-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(9.5rem, 100%), 1fr)); gap: 0 var(--space-xs); }
.msg-send { width: 100%; margin-top: var(--space-2xs); }
.msg-done { padding: var(--space-s); border: 1px solid var(--line-strong); border-radius: var(--radius-m); background: var(--ground-sunken); }
.msg-done h3 { margin: 0 0 var(--space-2xs); font-size: var(--step-1); }
.msg-done p { margin: 0; }
.msg-alt, .msg-privacy { font-size: var(--step--1); color: var(--ink-muted); margin: var(--space-s) 0 0; }
.msg-privacy { margin-top: var(--space-2xs); }
.msg-form textarea { resize: vertical; min-height: 4.5rem; }

/* 3. The home rates table lies flat on a phone: room and property count on one line, the weekly and
      nightly price on the next. No cell is squeezed into breaking a word in half. */
@media (max-width: 40rem) {
  .rate-table, .rate-table tbody { display: block; width: 100%; }
  .table-scroll > table.rate-table { table-layout: auto; }
  .rate-table thead { display: none; }
  .rate-table tr { display: grid; grid-template-columns: 1fr auto; column-gap: var(--space-s); row-gap: 0.2rem;
    padding: var(--space-xs) 0; border-bottom: 1px solid var(--line); }
  .rate-table tbody tr:last-child { border-bottom: 0; }
  .rate-table th, .rate-table td { display: block; padding: 0; border: 0; overflow-wrap: normal; }
  .rate-table th[scope="row"] { grid-column: 1; grid-row: 1; }
  .rate-table .rt-where { grid-column: 2; grid-row: 1; text-align: right; color: var(--ink-muted); white-space: nowrap; }
  .rate-table .rt-week { grid-column: 1; grid-row: 2; }
  .rate-table .rt-night { grid-column: 2; grid-row: 2; text-align: right; white-space: nowrap; }
  .rate-table .rt-week br { display: none; }
  .rate-table .rt-week .rate-perday { display: block; font-size: var(--step--1); color: var(--ink-muted); }
  .rate-table .rt-flat { grid-column: 1 / -1; }
  .rate-table td[data-label]::before { content: attr(data-label) " "; color: var(--ink-muted); font-size: var(--step--1); }
}

/* 4. Less empty space between sections, most of all on a phone. */
.section-tight { padding-block: clamp(1.25rem, 0.8rem + 1.8vw, 2.5rem); }
@media (max-width: 40rem) {
  .section-tight > h2:first-child, .section > h2:first-child { padding-top: var(--space-s); }
  h2 { margin-top: var(--space-m); }
  h3 { margin-top: var(--space-s); }
  .grid { gap: var(--space-s); }
}

/* 5. The fade-in finishes while a section's top edge is still low on the screen, so a tall section
      (twelve cards, a long guide) is never left washed out while you read it. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .reveal { animation-range: entry 0% entry 35%; }
  }
}
