/* Minimal base. Deliberately small: Phase 0 has one page, and a design system
   built before there is anything to design is a guess that has to be undone. */
:root {
  /* Tells the browser our palette follows the user's preference, so it renders
     NATIVE controls to match. Without it Chrome draws radios, checkboxes, text
     fields and scrollbars in light styling on top of a dark page — and an
     unchecked radio then appears as a filled white circle, which on a required
     field reads as "already selected". One line, and it fixes every form in the
     product, not only the newest one. */
  color-scheme: light dark;

  --ink: #1b2430;
  --muted: #5a6672;
  --line: #dde3ea;
  --bg: #ffffff;
}
@media (prefers-color-scheme: dark) {
  :root { --ink: #e8edf2; --muted: #9aa7b4; --line: #2b3542; --bg: #141a21; }
}
* { box-sizing: border-box; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 16px/1.55 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}
.wrap { max-width: 42rem; margin: 0 auto; padding: 2.5rem 1.25rem; }
h1 { font-size: 1.6rem; margin: 0 0 .5rem; }
p { color: var(--muted); margin: 0 0 1rem; }

/* ─────────────────────────────────────────────────────────────────────────
   Phase 1: the owner interface.

   Still deliberately small. This is not the Guest Portal design system — that
   arrives with the guest-facing portal, which is the surface a design system
   is actually for. What is here is enough structure to use the product and to
   see what the product is: readable forms, a legible table, and states that
   are distinguishable without colour alone.
   ───────────────────────────────────────────────────────────────────────── */
:root {
  --warn-bg: #fff7e6; --warn-line: #d19a2a; --warn-ink: #6b4708;
  --accent: #1f6feb;
  --accent-ink: #ffffff;
  --ok-bg: #e7f5ec; --ok-line: #58a97a; --ok-ink: #14532d;
  --err-bg: #fdecec; --err-line: #d05353; --err-ink: #7f1d1d;
  --danger: #b42318;
}
@media (prefers-color-scheme: dark) {
  :root {
    --accent: #4c8dff;
    --ok-bg: #16281d; --ok-line: #3f7f5c; --ok-ink: #b7e4c7;
    --warn-bg: #2b2312; --warn-line: #b98a2f; --warn-ink: #f2d99b;
    --err-bg: #2a1618; --err-line: #a34a4a; --err-ink: #f7c9c9;
    --danger: #f27a72;
  }
}

.wrap { max-width: 56rem; }
h2 { font-size: 1.15rem; margin: 2rem 0 .5rem; }
a { color: var(--accent); }

/* Navigation ─────────────────────────────────────────────────────────── */
.topnav {
  display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;
  padding-bottom: 1rem; margin-bottom: 1.5rem; border-bottom: 1px solid var(--line);
}
.topnav .brand { font-weight: 650; text-decoration: none; color: var(--ink); }
.topnav .org { color: var(--muted); font-size: .9rem; margin-right: auto; }
.topnav a { text-decoration: none; font-size: .95rem; }

/* Landing ────────────────────────────────────────────────────────────── */
.hero { margin-bottom: 2rem; }
.lede { font-size: 1.1rem; }

/* Forms ──────────────────────────────────────────────────────────────── */
.stack { display: grid; gap: 1.1rem; max-width: 30rem; }
.stack label, .row label { display: grid; gap: .3rem; font-weight: 550; font-size: .95rem; }
.stack small, .row small { font-weight: 400; color: var(--muted); font-size: .85rem; }
/* minmax(min(9rem, 100%), 1fr), not minmax(9rem, 1fr). With auto-fit, a bare
   9rem track floor makes the grid's MIN-CONTENT the sum of its tracks — three
   fields came to 464px — and `max-width` caps the maximum, not the minimum. In
   a grid parent that is what pushed the whole property editor 120px off a
   360px screen, in the pre-existing fields rather than in any one row. */
.row { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(9rem, 100%), 1fr)); gap: 1rem; max-width: 30rem; }
input, select {
  font: inherit; padding: .55rem .65rem; border: 1px solid var(--line);
  border-radius: 6px; background: var(--bg); color: var(--ink); width: 100%;
}
input:focus-visible, select:focus-visible, button:focus-visible, a:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
}
button {
  font: inherit; font-weight: 550; padding: .6rem 1.1rem; border: 1px solid transparent;
  border-radius: 6px; background: var(--accent); color: var(--accent-ink); cursor: pointer;
}
button:disabled { opacity: .5; cursor: default; }
.inline { display: inline; }
/* A submit button that reads as a link. Used where the action must be a POST —
   sign out, switch organization — because a GET that changes state is a link an
   attacker can put in an <img> tag on another site. */
.linkish {
  background: none; border: none; color: var(--accent); padding: 0;
  font-size: .95rem; font-weight: 400; cursor: pointer; text-decoration: underline;
}
.linkish:disabled { color: var(--muted); text-decoration: none; }

.actions { display: flex; gap: .75rem; flex-wrap: wrap; margin: 1.25rem 0; }
.button {
  display: inline-block; text-decoration: none; font-weight: 550;
  padding: .6rem 1.1rem; border-radius: 6px; background: var(--accent); color: var(--accent-ink);
}
.button-quiet { background: none; color: var(--accent); border: 1px solid var(--line); }
.button-danger { background: none; color: var(--danger); border: 1px solid var(--danger); }
.danger { margin-top: 2.5rem; padding-top: 1.25rem; border-top: 1px solid var(--line); }

/* Notices ────────────────────────────────────────────────────────────── */
/* A left border as well as a background, so the two states are distinguishable
   without relying on colour. */
.notice { padding: .8rem 1rem; border-radius: 6px; border-left: 4px solid; margin: 0 0 1.5rem; }
.notice-ok { background: var(--ok-bg); border-color: var(--ok-line); color: var(--ok-ink); }
.notice-error { background: var(--err-bg); border-color: var(--err-line); color: var(--err-ink); }
/* A warning is not an error: nothing has failed, but something the owner
   believes is in force is not. Amber rather than red, and it uses the existing
   warn tokens from the guest callout palette. */
.notice-warn { background: var(--warn-bg, #fff7e6); border-color: var(--warn-line, #d19a2a); color: var(--warn-ink, #6b4708); }
.notice ul { margin: 0; padding-left: 1.2rem; }
.notice p { color: inherit; }

/* Data ───────────────────────────────────────────────────────────────── */
.facts { display: grid; grid-template-columns: max-content 1fr; gap: .4rem 1.25rem; margin: 1.5rem 0; }
.facts dt { color: var(--muted); font-size: .9rem; }
.facts dd { margin: 0; }

.table { width: 100%; border-collapse: collapse; margin: 1rem 0; }
.table th, .table td { text-align: left; padding: .6rem .5rem; border-bottom: 1px solid var(--line); }
.table th { font-size: .82rem; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); }

.list { list-style: none; padding: 0; margin: 0; display: grid; gap: .4rem; }
.codes { list-style: none; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr)); gap: .35rem; }

.mono { font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace; font-size: .92em; }
.breakable { overflow-wrap: anywhere; }
.quiet { color: var(--muted); font-size: .92rem; }

/* Present for screen readers, absent visually — used where a second address
   line needs a label but a visible one would be noise. */
.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ─────────────────────────────────────────────────────────────────────────
   Phase 2: the welcome guide.

   Two audiences in one stylesheet. The `.g-` prefix is the guest-facing guide,
   which a member of the public reads on a phone, probably on hotel wifi,
   probably one-handed — so it is wider-spaced and larger-typed than the owner
   interface. Everything else here is the owner's editor.
   ───────────────────────────────────────────────────────────────────────── */

/* ── The owner's guide editor ──────────────────────────────────────────── */
.crumbs { font-size: .9rem; margin: -.5rem 0 1.5rem; }
.stack-wide { max-width: 46rem; }
textarea {
  font: .95rem/1.6 ui-monospace, SFMono-Regular, Menlo, monospace;
  padding: .7rem; border: 1px solid var(--line); border-radius: 6px;
  background: var(--bg); color: var(--ink); width: 100%; resize: vertical;
}
.button-strong { background: var(--ok-line); }

.guide-list { list-style: none; padding: 0; margin: 1rem 0; display: grid; gap: .25rem; }
.guide-list-quiet { opacity: .72; }
.guide-row {
  display: flex; flex-wrap: wrap; gap: .75rem; align-items: center;
  justify-content: space-between; padding: .7rem .25rem; border-bottom: 1px solid var(--line);
}
.guide-row-main { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; }
.guide-row-title { font-weight: 550; text-decoration: none; }
.guide-row-actions { display: flex; align-items: center; gap: .8rem; font-size: .9rem; }

/* Badges carry a word as well as a colour: "published" and "not published"
   must be distinguishable without seeing the difference between green and
   amber. */
.badge {
  display: inline-block; font-size: .72rem; font-weight: 600; letter-spacing: .03em;
  text-transform: uppercase; padding: .15rem .45rem; border-radius: 4px; border: 1px solid;
}
.badge-ok { background: var(--ok-bg); border-color: var(--ok-line); color: var(--ok-ink); }
.badge-draft { background: transparent; border-color: var(--muted); color: var(--muted); }
.badge-quiet { background: transparent; border-color: var(--line); color: var(--muted); }
.badge-error { background: var(--err-bg); border-color: var(--err-line); color: var(--err-ink); }

.syntax { margin: 2.5rem 0; border-top: 1px solid var(--line); padding-top: 1rem; }
.syntax summary { cursor: pointer; font-weight: 550; }
.syntax-list { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: .35rem 1.25rem; margin: 1rem 0; }
.syntax-list dt { overflow-wrap: anywhere; font-size: .88rem; }
.syntax-list dd { margin: 0; color: var(--muted); font-size: .88rem; }

.hash { white-space: nowrap; }
.preview-section { padding: .25rem 0; }
.preview-draft { border-left: 3px solid var(--muted); padding-left: 1rem; }
.guest-embed { margin-top: 1.5rem; }

/* ── The guest-facing guide ────────────────────────────────────────────── */
body.guest { background: var(--bg); }
.g-wrap { max-width: 40rem; margin: 0 auto; padding: 2.5rem 1.25rem 4rem; }

.g-header { margin-bottom: 2rem; }
.g-eyebrow {
  text-transform: uppercase; letter-spacing: .12em; font-size: .72rem;
  color: var(--muted); margin: 0 0 .35rem;
}
.g-property { font-size: 1.9rem; line-height: 1.2; margin: 0 0 .35rem; }
.g-unit { color: var(--muted); font-weight: 400; }
.g-where { color: var(--muted); margin: 0; }

.g-toc {
  margin: 0 0 2.5rem; padding: 1rem 1.25rem;
  border: 1px solid var(--line); border-radius: 8px;
}
.g-toc ul { list-style: none; padding: 0; margin: 0; display: grid; gap: .4rem; }
.g-toc a { text-decoration: none; }
.g-toc a:hover { text-decoration: underline; }

.g-section { margin: 0 0 2.75rem; scroll-margin-top: 1rem; }
.g-title { font-size: 1.25rem; margin: 0 0 .75rem; padding-bottom: .4rem; border-bottom: 1px solid var(--line); }
.g-heading { font-size: 1rem; margin: 1.5rem 0 .4rem; }
.g-text { color: var(--ink); margin: 0 0 .9rem; }
.g-list { margin: 0 0 1rem; padding-left: 1.3rem; display: grid; gap: .3rem; }

.g-callout {
  margin: 1.25rem 0; padding: .85rem 1rem; border-radius: 6px;
  border-left: 4px solid var(--muted); background: color-mix(in srgb, var(--muted) 8%, transparent);
}
.g-callout p { margin: 0; color: var(--ink); }
.g-callout-warning { border-left-color: var(--err-line); background: var(--err-bg); }
.g-callout-warning p { color: var(--err-ink); }
.g-callout-important { border-left-color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, transparent); }

.g-contact { margin: 1rem 0; padding: .85rem 1rem; border: 1px solid var(--line); border-radius: 6px; }
.g-contact-name { font-weight: 600; margin: 0 0 .2rem; color: var(--ink); }
.g-contact-line { margin: 0; color: var(--ink); }
.g-quiet { color: var(--muted); font-weight: 400; }

.g-linkline { margin: .75rem 0; }
.g-link { font-weight: 550; }

.g-figure { margin: 1.5rem 0; }
.g-image { width: 100%; height: auto; border-radius: 8px; display: block; }
.g-figure figcaption { color: var(--muted); font-size: .88rem; margin-top: .5rem; }

.g-facts { display: grid; grid-template-columns: max-content 1fr; gap: .35rem 1.25rem; margin: 1rem 0; }
.g-facts dt { color: var(--muted); font-size: .9rem; }
.g-facts dd { margin: 0; }

.g-footer {
  margin-top: 3.5rem; padding-top: 1.25rem; border-top: 1px solid var(--line);
  color: var(--muted); font-size: .85rem;
}
.g-footer p { margin: 0; color: inherit; }

/* ── Phase 4: acknowledgements ─────────────────────────────────────────────
   The guest block is deliberately heavier than the rest of the guide. It is
   the one part of the page a guest is asked to act on, and it should not read
   as another paragraph of welcome text. */

.g-ack { border: 2px solid var(--accent); border-radius: 10px; padding: 1.25rem 1.25rem .5rem; }
.g-ack > .g-title { border-bottom-color: var(--accent); }

.g-ack-item {
  margin: 0 0 1.5rem; padding: 1rem; border: 1px solid var(--line);
  border-radius: 8px; background: color-mix(in srgb, var(--muted) 5%, transparent);
}
.g-ack-title { font-size: 1.05rem; margin: 0 0 .2rem; }
.g-ack-version { font-size: .8rem; margin: 0 0 .75rem; }
.g-ack-body { margin-bottom: 1rem; }
.g-ack-body :last-child { margin-bottom: 0; }

/* The tick target is the whole sentence, not a 13px square.
   Scoped past `.stack label`, which is a grid and would otherwise stack the
   box above its own sentence. */
.stack label.g-ack-check, .g-ack-check {
  display: flex; gap: .7rem; align-items: flex-start; cursor: pointer;
  grid-template-columns: none;
  padding: .7rem; border-radius: 6px; border: 1px solid var(--accent);
  background: var(--bg); font-weight: 550;
}
.g-ack-check input { margin-top: .2rem; width: 1.15rem; height: 1.15rem; flex: none; }
.g-ack-sign input { max-width: 24rem; }

/* Set by /assets/stay-read.js when the measured read time is under the floor.
   A hint, never a block: with no JavaScript this attribute never appears and
   the form works exactly the same. */
.g-ack-item[data-read="short"] { border-left: 4px solid var(--accent); }

.ack-status { margin: 2rem 0; padding: 1rem 1.25rem; border: 1px solid var(--line); border-radius: 8px; }
.ack-status h2 { margin-top: 0; }
.ack-config { margin: 2.5rem 0; padding: 1rem 1.25rem; border: 1px solid var(--line); border-radius: 8px; }
.ack-config h2 { margin-top: 0; }
/* `.stack label` is a grid, which would stack the box above its own label.
   Scoped the same way so it actually wins. */
.stack label.checkline, .checkline {
  display: flex; gap: .6rem; align-items: center; grid-template-columns: none;
}
.checkline input { width: 1.1rem; height: 1.1rem; flex: none; }
/* The wording is a sentence, not a slug — give it the room a sentence needs. */
.ack-config .stack { max-width: 44rem; }

/* ── Phase 5: access release ───────────────────────────────────────────────
   MOBILE FIRST, and meant literally: these rules are written for a phone held
   one-handed in a rental car park at dusk, and the desktop case is what happens
   to the same rules on a wider screen. No hover-dependent affordance appears
   anywhere in this block — there is nothing to reveal, nothing to expand and
   nothing that needs a pointer, because the credential is already on the page
   when the server decided it may be.

   The released card is deliberately the loudest thing in the document. A guest
   who has just parked wants one number, large, in one glance. */

.g-access { border: 2px solid var(--accent); border-radius: 10px; padding: 1.25rem 1rem; }
.g-access > .g-title { border-bottom-color: var(--accent); }

.g-access-card {
  margin: 0 0 1rem; padding: 1rem;
  border: 1px solid var(--line); border-radius: 8px;
  background: color-mix(in srgb, var(--muted) 5%, transparent);
}
.g-access-card:last-child { margin-bottom: 0; }

.g-access-label {
  font-size: .95rem; font-weight: 600; letter-spacing: .04em; text-transform: uppercase;
  color: var(--muted); margin: 0 0 .35rem;
}

/* The number itself. `ch`-based sizing rather than a fixed rem so a six-digit
   code and a 63-character Wi-Fi passphrase both stay on the screen: clamp()
   gives the short one the full poster treatment and lets the long one fall back
   to something still comfortably readable.

   `overflow-wrap: anywhere` is what keeps a long passphrase from pushing the
   page sideways — no horizontal scrolling, on any width. */
.g-access-secret {
  margin: .1rem 0 .6rem;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: clamp(1.9rem, 11vw, 3rem);
  line-height: 1.15;
  font-weight: 650;
  letter-spacing: .06em;
  color: var(--ink);
  overflow-wrap: anywhere;
  /* Selectable and long-press-copyable on a phone, which is the only "copy
     button" this page needs. */
  user-select: all;
}
.g-access-card.is-open { border-color: var(--accent); border-left-width: 5px; background: var(--bg); }

.g-access-note { margin: 0; color: var(--ink); font-size: 1.02rem; }

.g-access-card.is-locked { border-style: dashed; }
.g-access-locked { margin: 0; color: var(--muted); font-size: 1.02rem; }

/* Room for a thumb. 44px is the accessibility floor for a touch target and
   applies to every control on the guest surface, not only this block. */
@media (max-width: 34rem) {
  .g-wrap { padding: 1.5rem 1rem 3rem; }
  .g-access { padding: 1rem .85rem; }
  .g-access-card { padding: .85rem; }
  body.guest button,
  body.guest .button,
  body.guest input:not([type="checkbox"]),
  body.guest select { min-height: 44px; font-size: 1rem; }
  body.guest .g-ack-check { padding: .9rem .8rem; }
  /* The section index is a list of 9-11 links a thumb has to hit. At the
     default line-height each one is an 18px target, which is under every
     guideline going and is felt on a phone before it is measured. */
  body.guest .g-toc ul { gap: 0; }
  body.guest .g-toc a { display: block; padding: .6rem 0; }
  body.guest .g-ack-check input { width: 1.4rem; height: 1.4rem; }
  .g-property { font-size: 1.6rem; }
}

/* Nothing on the guest surface may push the viewport sideways. Owner-authored
   text, a long passphrase and a wide pasted URL all end up here. */
body.guest { overflow-x: hidden; }
body.guest .g-wrap { max-width: 40rem; }

/* ── Phase 6: invitations, setup, and the accessibility pass ───────────────
   Written after an explicit review of the guest journey. Three of these rules
   fix real findings rather than polish: the focus ring was invisible on the
   dark theme, the locked access state was carried by a dashed border alone,
   and the guest surface had no skip mechanism for a keyboard user landing on a
   long welcome guide. */

/* FINDING 1. The default focus ring is a thin dark outline that vanishes
   against --bg in dark mode, so a keyboard user could not see where they were.
   Two layers so it is visible on any background the page uses. */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
  border-radius: 4px;
}
/* Never remove the ring without replacing it. Kept explicit so a future
   `outline: none` in a component reads as the mistake it is. */
:where(a, button, input, select, textarea):focus:not(:focus-visible) { outline: none; }

/* FINDING 2. The locked access card said "locked" only through a dashed
   border. The word is now in the markup; this is what it looks like. */
.g-access-state {
  display: inline-block;
  margin-left: .5rem;
  padding: .1rem .45rem;
  font-size: .7rem;
  font-weight: 700;
  letter-spacing: .06em;
  border: 1px solid var(--muted);
  border-radius: 4px;
  color: var(--muted);
  vertical-align: middle;
}

/* FINDING 3. A keyboard or screen-reader user landing on /stay had to walk the
   whole welcome guide to reach anything actionable. The skip link is the first
   focusable thing on the page and is invisible until focused. */
.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 10;
  padding: .75rem 1rem; background: var(--accent); color: var(--accent-ink);
  text-decoration: none; border-radius: 0 0 6px 0; font-weight: 600;
}
.skip-link:focus { left: 0; }

/* The setup panel. Neutral markers, text state, no progress bar — see
   src/web/views/setup.js for why. */
.setup {
  margin: 0 0 2rem; padding: 1.25rem 1.25rem .5rem;
  border: 1px solid var(--accent); border-radius: 10px;
  background: color-mix(in srgb, var(--accent) 6%, transparent);
}
.setup h2 { margin: 0 0 .35rem; font-size: 1.1rem; }
.setup-list { list-style: none; margin: 1rem 0; padding: 0; display: grid; gap: .85rem; }
.setup-step { display: flex; gap: .75rem; align-items: baseline; }
.setup-marker {
  flex: none; width: 1.4rem; text-align: center; font-weight: 700;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
.setup-body { display: grid; gap: .1rem; }
.setup-label { font-weight: 600; }
/* The state as words. Colour is a reinforcement, never the message. */
.setup-state { font-size: .82rem; font-weight: 600; letter-spacing: .03em; text-transform: uppercase; }
.setup-done   .setup-state { color: var(--ok-line); }
.setup-todo   .setup-state { color: var(--accent); }
.setup-skipped .setup-state,
.setup-skipped .setup-marker { color: var(--muted); }
.setup-hint { font-size: .9rem; }

/* Invitation status: a badge plus a sentence, never a badge alone. */
.status-line { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; margin: 0 0 1rem; }
.status-line .badge { flex: none; }

/* Owner surface on a phone. Editing a guide is still nicer on a desktop, but
   sending an invitation and reading the checklist must work one-handed. */
@media (max-width: 34rem) {
  .wrap { padding: 1.25rem 1rem 3rem; }
  .topnav { gap: .6rem; font-size: .95rem; }
  .topnav .org { margin-right: 0; width: 100%; }
  .actions { display: flex; flex-wrap: wrap; gap: .5rem; }
  .actions .button { flex: 1 1 auto; text-align: center; min-height: 44px; line-height: 1.4; }
  button, .button, input:not([type="checkbox"]), select, textarea { min-height: 44px; font-size: 1rem; }
  /* A table of six columns cannot be made narrow; let it scroll INSIDE its own
     box rather than pushing the page sideways. */
  .table, .grid { display: block; overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .setup { padding: 1rem .85rem .5rem; }
  .facts { grid-template-columns: 1fr; gap: .1rem 0; }
  .facts dt { margin-top: .6rem; }
}

/* Nothing on the owner surface may push the viewport sideways either. */
body { overflow-x: hidden; }

/* ── Internal admin ────────────────────────────────────────────────────────
   Guest Portal's back office reuses this stylesheet rather than getting a
   second one. What marks it as a consequential place is a badge in the nav and
   plain language on the form, not a different skin. */
.topnav .admin-badge {
  background: var(--accent); color: var(--accent-ink);
  padding: .1rem .45rem; border-radius: 4px; font-size: .78rem;
  font-weight: 620; letter-spacing: .02em; margin-right: auto;
}
table.grid { border-collapse: collapse; width: 100%; margin: 0 0 1.5rem; font-size: .95rem; }
table.grid th, table.grid td {
  text-align: left; padding: .5rem .6rem; border-bottom: 1px solid var(--line);
  vertical-align: top;
}
table.grid thead th { font-size: .82rem; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); }
table.grid tbody th[scope="row"] { width: 12rem; font-weight: 560; }
.muted { color: var(--muted); }

/* The public pricing figure, when a price is actually published. */
.site .price-line { margin: 0 0 1rem; }
.site .price-amount { font-size: clamp(2rem, 1.5rem + 2vw, 2.75rem); font-weight: 680; color: var(--ink-strong); letter-spacing: -0.02em; }
.site .price-unit { color: var(--muted); margin-left: .4rem; }

/* A checkbox and its label on one line. `.row` is a multi-column grid meant for
   side-by-side FIELDS, and using it here put the box and its text in separate
   9rem grid tracks — which is what made the two checkboxes on the pricing form
   line up with each other and with nothing else. */
.checkrow {
  display: flex; align-items: flex-start; gap: .6rem;
  font-weight: 550; font-size: .95rem; max-width: 30rem;
}
.checkrow input[type="checkbox"] {
  width: 1.1rem; height: 1.1rem; margin: .15rem 0 0; flex: none;
}

/* ─────────────────────────────────────────────────────────────────────────
   The public prospect demos.

   These pages reuse the guest surface above wholesale — .g-section, .g-access,
   .g-ack, .g-facts and the 44px touch targets are all inherited, which is the
   point of rendering the demo through the real markup. What is added here is
   only the three things a real stay page must never have: a banner saying this
   is not real, a way back out to the marketing site, and a hero image.

   .btn in site.css is scoped to .site, so the exit bar gets its own small
   button rules rather than pulling a second stylesheet onto a guest page.
   ───────────────────────────────────────────────────────────────────────── */
.demo-flag {
  margin: 0; padding: .6rem 1.25rem;
  background: var(--ok-bg); color: var(--ok-ink);
  border-bottom: 1px solid var(--ok-line);
  font-size: .85rem; line-height: 1.45; text-align: center;
}
.demo-flag strong { font-weight: 650; }

.g-ack-check.is-done {
  display: block; margin: 0; padding: .75rem .8rem;
  border: 1px solid var(--ok-line); border-radius: 8px;
  background: var(--ok-bg); color: var(--ok-ink); font-size: .9rem;
}

.g-footer-note { font-size: .85rem; margin: 2rem 0 0; }

.demo-exit {
  margin-top: 3rem; padding-top: 2rem; border-top: 2px solid var(--line);
}
.demo-actions {
  display: flex; flex-wrap: wrap; gap: .6rem; margin: 1.25rem 0 0;
}
.demo .btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 44px; padding: .6rem 1.15rem; border-radius: 8px;
  border: 1px solid transparent; text-decoration: none;
  font-weight: 600; font-size: .95rem;
}
.demo .btn-primary { background: var(--accent); color: var(--accent-ink); }
.demo .btn-secondary { background: transparent; color: var(--ink); border-color: var(--line); }
.demo .btn-secondary:hover { border-color: var(--muted); }

@media (max-width: 30rem) {
  /* One full-width target per row beats three cramped ones on a 360px phone. */
  .demo-actions { flex-direction: column; }
  .demo-actions .btn { width: 100%; }
}
/* The Sample badge on a released demo credential. The real card's state badge
   is neutral; this one has to read as "not real" at a glance, because a
   screenshot of a door code travels further than the page around it. */
.g-access-state.is-sample {
  background: var(--ok-bg); color: var(--ok-ink); border-color: var(--ok-line);
}

/* ─────────────────────────────────────────────────────────────────────────
   Property branding — Phase 9C.

   --brand is set by a NONCED <style> element on the guest surface when the
   property has an accent, and is otherwise the product's own accent. It is used
   ONLY as a background behind white text: services/properties.js refuses any
   value below 4.5:1 against white, and that single check holds in both light
   and dark mode precisely because the background is ours rather than the
   theme's. It is never a text, link or border colour.
   ───────────────────────────────────────────────────────────────────────── */
.guest { --brand: var(--accent); }

.g-hero { margin: 0 0 1.75rem; }
.g-hero img {
  width: 100%; height: auto; display: block; border-radius: 10px;
  /* A wide crop, so a portrait photo cannot push the whole page off-screen. */
  aspect-ratio: 16 / 9; object-fit: cover;
  background: var(--line);
}

.g-tagline {
  margin: .35rem 0 0; color: var(--muted); font-size: 1.02rem; line-height: 1.45;
  text-wrap: pretty;
}

/* The one place the property's colour is used on the guest surface, and it is
   a background behind white. */
.guest button[type="submit"],
.guest .button {
  background: var(--brand); color: #fff; border-color: var(--brand);
}
.guest button[type="submit"]:hover, .guest .button:hover { filter: brightness(1.08); }
/* Focus stays visible whatever the owner chose: the ring is drawn in the
   surface's own ink, not in --brand, so a dark accent cannot swallow it. */
.guest button[type="submit"]:focus-visible,
.guest .button:focus-visible {
  outline: 3px solid var(--ink); outline-offset: 2px;
}

@media (max-width: 34rem) {
  .g-hero { margin: 0 0 1.25rem; }
  .g-hero img { border-radius: 8px; aspect-ratio: 3 / 2; }
}

/* The owner's branding fieldsets. The rest of the property form is a plain
   stack of labels; these two are grouped because they answer one question each
   ("what do guests see", "when do they arrive"). */
.p-group {
  border: 1px solid var(--line); border-radius: 10px;
  padding: 1rem 1.1rem 1.25rem; margin: 1.5rem 0;
  /* ⚠️ BOTH, and the second is the one that matters. A <fieldset> has a UA
     `min-inline-size: min-content` that plain `min-width: 0` does not always
     defeat, so a fixed-width child inside it stops the whole FORM from
     shrinking — which showed up as the property editor overflowing by 120px at
     360, in the pre-existing fields rather than in the new ones. */
  min-width: 0; min-inline-size: 0;
}
.p-group legend { font-weight: 650; padding: 0 .4rem; }
.p-preview { margin: .75rem 0 1rem; }
.p-preview img {
  /* min() rather than a bare max-width: on a narrow screen 100% wins, so the
     preview can never be wider than the column it sits in. */
  width: 100%; max-width: min(28rem, 100%); height: auto; display: block;
  aspect-ratio: 16 / 9; object-fit: cover;
  border-radius: 8px; border: 1px solid var(--line); background: var(--line);
}
.p-times { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
@media (max-width: 34rem) { .p-times { grid-template-columns: 1fr; gap: .5rem; } }

/* ── The cover-image control ───────────────────────────────────────────────
   Upload is the normal path; the externally-hosted URL is a <details> beside
   it. One primary control, not two competing ones. */
.cover-field { margin: 0 0 1.5rem; }
.cover-label { font-weight: 600; color: var(--ink); margin: 0 0 .5rem; }
.cover-actions { margin: .75rem 0 .25rem; }
.cover-actions .button { min-height: 44px; }
.cover-status { margin: .25rem 0; font-size: .9rem; min-height: 1.2em; color: var(--muted); }
.cover-status.is-ok { color: var(--ok-ink); font-weight: 600; }
.cover-status.is-error { color: var(--err-ink); font-weight: 600; }
.cover-status.is-busy { color: var(--muted); }
.cover-hint { font-size: .85rem; margin: .25rem 0 0; }
.cover-remove { margin: .5rem 0 0; }
.cover-external { margin: 1rem 0 0; }
.cover-external summary { cursor: pointer; color: var(--accent); font-size: .9rem; padding: .5rem 0; }
.cover-external summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.cover-external label { margin-top: .5rem; }

/* The access-credential field. Visible by default with a Hide control — the
   owner is copying a door code from a note and the system never shows it back,
   so reading what you typed matters more than hiding it in your own home. */
.secret-field { margin: 0 0 1.25rem; }
.secret-field label { margin: 0; }
.secret-actions { margin: .35rem 0 0; }
.secret-note { display: block; margin: .4rem 0 0; }

/* ── Choice cards ──────────────────────────────────────────────────────────
   The homeowner-facing replacement for a <select>. "How do guests get inside?"
   is a question with five answers, and five answers hidden behind a dropdown is
   an admin form; five cards is a decision.

   ⚠️ THESE ARE REAL RADIOS. Every card is a <label> wrapping
   <input type="radio">, so keyboard arrows, form submission, validation and
   screen readers all work exactly as they always did — this is CSS over a
   standard control, not a div pretending to be one. The input is clipped rather
   than `display:none`, because a hidden input is removed from the accessibility
   tree and unfocusable.

   Borrowed from site.css deliberately: the 12px radius and the roomier rhythm,
   so the product stops downgrading visually at the moment somebody signs up.
   Not borrowed: the marketing bands and hero scale, which do not belong here. */

.choice-set {
  border: 0;
  margin: 0 0 1.25rem;
  padding: 0;
  min-width: 0;                 /* a fieldset defaults to min-content: overflow */
}
.choice-set > legend {
  padding: 0;
  margin-bottom: .6rem;
  font-weight: 600;
  font-size: 1.05rem;
  color: var(--ink);
}
.choice-set > .hint {
  margin: -.35rem 0 .75rem;
  color: var(--muted);
  font-size: .9rem;
}

.choice-grid {
  display: grid;
  /* min() so a narrow screen cannot be forced wider by the track floor — the
     same overflow that pushed the property editor off a 360px phone. */
  grid-template-columns: repeat(auto-fit, minmax(min(13rem, 100%), 1fr));
  gap: .75rem;
}

.choice { display: block; cursor: pointer; }

/* Clipped, not hidden: still focusable, still announced. */
.choice > input {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden; white-space: nowrap;
  clip: rect(0 0 0 0); clip-path: inset(50%);
}

.choice-body {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: .3rem;
  height: 100%;
  padding: 1rem 2.35rem 1rem 1rem;   /* right padding leaves room for the tick */
  min-height: 3.5rem;                /* a comfortable target on a phone */
  border: 1.5px solid var(--line);
  border-radius: 12px;
  background: var(--bg);
  transition: border-color .12s ease, box-shadow .12s ease, transform .12s ease;
}
@media (prefers-reduced-motion: reduce) {
  .choice-body { transition: none; }
}

.choice-icon { color: var(--accent); line-height: 0; }
.choice-title { font-weight: 600; color: var(--ink); }
.choice-desc { color: var(--muted); font-size: .87rem; line-height: 1.35; }

.choice:hover .choice-body {
  border-color: var(--accent);
  box-shadow: 0 2px 10px rgb(0 0 0 / .07);
}

/* ⚠️ FOCUS IS ON THE CARD, NOT THE CLIPPED INPUT. Without this the focus ring
   would be drawn around a 1px box somewhere off-screen and keyboard users would
   have no idea where they are. */
.choice > input:focus-visible + .choice-body {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}

.choice > input:checked + .choice-body {
  border-color: var(--accent);
  border-width: 2px;
  padding: calc(1rem - .5px) calc(2.35rem - .5px) calc(1rem - .5px) calc(1rem - .5px);
  background: color-mix(in srgb, var(--accent) 7%, var(--bg));
}

/* ⚠️ SELECTED IS NOT SIGNALLED BY COLOUR ALONE. A tick is drawn on the chosen
   card, so the state survives greyscale, high contrast, and colour blindness.
   The border also thickens, which is a third, non-colour signal. */
.choice > input:checked + .choice-body::after {
  content: "";
  position: absolute;
  top: .85rem; right: .85rem;
  width: 1.1rem; height: 1.1rem;
  border-radius: 50%;
  background: var(--accent);
  /* A checkmark punched out of the dot, drawn with a mask so it needs no glyph
     and no second element. */
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round' d='M4 8.4l2.6 2.6L12 5.6'/%3E%3C/svg%3E") center / 100% no-repeat,
           linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round' d='M4 8.4l2.6 2.6L12 5.6'/%3E%3C/svg%3E") center / 100% no-repeat,
       linear-gradient(#000 0 0);
  mask-composite: exclude;
}

/* Fallback for engines without mask-composite: a plain filled dot still reads
   as "this one", and the thicker border still carries the state. */
@supports not (mask-composite: exclude) {
  .choice > input:checked + .choice-body::after { -webkit-mask: none; mask: none; }
}

/* The "More" group. <details> so it works with JavaScript off. */
.choice-more { margin-top: .85rem; }
.choice-more > summary {
  cursor: pointer;
  padding: .5rem .1rem;
  font-weight: 600;
  color: var(--accent);
}
.choice-more > summary:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
.choice-more[open] > summary { margin-bottom: .6rem; }

/* Revealed only when its choice is selected. Server-rendered `hidden` is the
   default, so with JavaScript off the owner sees every follow-up field rather
   than none — visible and answerable beats hidden and unreachable. */
.choice-reveal { margin: .25rem 0 1.25rem; }

/* The structured parking fact, led into the owner's own prose. */
.g-parking-line { margin: 0 0 .6rem; }

/* ═══════════════════════════════════════════════════════════════════════════
   Guide setup cards
   ─────────────────────────────────────────────────────────────────────────
   Six expandable modules instead of a section picker. Built on <details name>,
   so the accordion, the keyboard behaviour and the expanded/collapsed state
   announced to assistive technology are the browser's, not ours — there is no
   JavaScript in this feature at all.
   ═══════════════════════════════════════════════════════════════════════════ */

.setup-cards { display: grid; gap: .75rem; margin: 1.5rem 0; }

.setup-card {
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--bg);
  overflow: hidden;
  transition: border-color .15s ease, box-shadow .15s ease;
}
.setup-card[open] {
  border-color: color-mix(in srgb, var(--accent) 45%, var(--line));
  box-shadow: 0 1px 3px rgb(0 0 0 / .06), 0 8px 24px rgb(0 0 0 / .05);
}

/* The whole header is the control: a big, obvious mobile target. */
.setup-summary {
  display: flex; align-items: center; gap: .85rem;
  padding: 1rem 1.1rem;
  cursor: pointer;
  list-style: none;
  min-height: 3.5rem;
}
.setup-summary::-webkit-details-marker { display: none; }
.setup-summary:hover { background: color-mix(in srgb, var(--accent) 4%, transparent); }
/* ⚠️ Focus lands on <summary>, so this IS the keyboard affordance. */
.setup-card > .setup-summary:focus-visible {
  outline: 2px solid var(--accent); outline-offset: -2px;
}

.setup-icon {
  flex: 0 0 auto; display: grid; place-items: center;
  width: 2.5rem; height: 2.5rem; border-radius: 10px;
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  color: var(--accent);
}
.setup-icon svg { width: 1.35rem; height: 1.35rem; }

.setup-heading { flex: 1 1 auto; min-width: 0; display: grid; gap: .15rem; }
.setup-title { font-weight: 600; color: var(--ink); }
.setup-blurb {
  font-size: .85rem; color: var(--muted); line-height: 1.35;
  /* Two lines at most on a narrow phone, then it stops. */
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}

/* A chevron that turns. Decoration only — the state is on <details>. */
.setup-summary::after {
  content: ""; flex: 0 0 auto;
  width: .55rem; height: .55rem; margin-left: .1rem;
  border-right: 2px solid var(--muted); border-bottom: 2px solid var(--muted);
  transform: rotate(45deg); transition: transform .18s ease;
}
.setup-card[open] > .setup-summary::after { transform: rotate(225deg); }

/* ⚠️ TEXT, NOT ONLY COLOUR. The dot is decorative; the word carries the state. */
.card-badge {
  flex: 0 0 auto; display: inline-flex; align-items: center; gap: .35rem;
  font-size: .75rem; font-weight: 600; white-space: nowrap;
  padding: .25rem .6rem; border-radius: 999px;
  border: 1px solid var(--line); color: var(--muted); background: var(--bg);
}
.card-dot { width: .45rem; height: .45rem; border-radius: 50%; background: currentColor; }
.card-badge-ok   { color: var(--ok-ink);   background: var(--ok-bg);   border-color: var(--ok-line); }
.card-badge-warn { color: var(--warn-ink); background: var(--warn-bg); border-color: var(--warn-line); }
.card-badge-off  { color: var(--muted);    background: color-mix(in srgb, var(--muted) 8%, transparent); }

.setup-body { padding: 0 1.1rem 1.2rem; border-top: 1px solid var(--line); }
.setup-body > :first-child { margin-top: 1rem; }
.setup-body label { display: block; margin: 0 0 .9rem; font-weight: 550; }
.setup-body textarea, .setup-body input[type="text"],
.setup-body input[type="time"], .setup-body input[type="password"], .setup-body input:not([type]) {
  width: 100%; margin-top: .35rem;
  /* 16px stops iOS zooming the page on focus. */
  font-size: 1rem; padding: .6rem .7rem;
  border: 1px solid var(--line); border-radius: 9px;
  background: var(--bg); color: var(--ink);
}
.setup-body input[type="time"] { width: auto; min-width: 9rem; }

.setup-include {
  margin-top: 1.25rem; padding-top: 1rem;
  border-top: 1px dashed var(--line);
}
.setup-include .checkline { font-weight: 550; margin-bottom: .5rem; }
.checkline-warn {
  background: var(--warn-bg); border: 1px solid var(--warn-line);
  color: var(--warn-ink); border-radius: 9px; padding: .7rem .8rem;
  font-weight: 500; font-size: .9rem;
}

.secret-field {
  border: 1px solid var(--line); border-radius: 10px;
  padding: .85rem .9rem; margin-bottom: .9rem;
  background: color-mix(in srgb, var(--muted) 4%, transparent);
}
.secret-field > :last-child { margin-bottom: 0; }
.secret-state { margin: 0 0 .35rem; display: grid; gap: .2rem; }
.secret-replace summary { cursor: pointer; color: var(--accent); font-weight: 550; }
.secret-replace[open] summary { margin-bottom: .6rem; }

.setup-more-title { margin-top: 2rem; }
.setup-extra { margin-top: 2rem; }
.setup-advanced {
  margin-top: 2rem; border-top: 1px solid var(--line); padding-top: 1rem;
}
.setup-advanced summary { cursor: pointer; font-weight: 600; color: var(--ink); }
.setup-advanced[open] summary { margin-bottom: 1rem; }
.notice-quiet {
  background: color-mix(in srgb, var(--muted) 7%, transparent);
  border: 1px solid var(--line); border-radius: 9px;
  padding: .7rem .8rem; font-size: .9rem; color: var(--muted);
}
.lede { font-size: 1.05rem; color: var(--muted); max-width: 46ch; }
ul.plain { list-style: none; padding: 0; }

@media (max-width: 480px) {
  .setup-summary { padding: .85rem .9rem; gap: .7rem; }
  .setup-body { padding: 0 .9rem 1rem; }
  .setup-icon { width: 2.2rem; height: 2.2rem; }
  /* The badge drops under the title rather than squeezing it. */
  .setup-summary { flex-wrap: wrap; }
  .setup-heading { flex: 1 1 60%; }
}

@media (prefers-reduced-motion: reduce) {
  .setup-card, .setup-summary::after { transition: none; }
}

/* A secret being typed: readable by default, with an added Hide/Show.
   The button is appended by location.js — with no script there is no button
   and the field is simply readable, which is the point. */
.secret-row { display: flex; gap: .5rem; align-items: stretch; margin-top: .35rem; }
.secret-row .secret-input { flex: 1 1 auto; margin-top: 0; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.secret-toggle {
  flex: 0 0 auto; min-width: 4.5rem;
  padding: .6rem .8rem; font-size: .9rem; font-weight: 600;
  border: 1px solid var(--line); border-radius: 9px;
  background: var(--bg); color: var(--accent); cursor: pointer;
}
.secret-toggle:hover { background: color-mix(in srgb, var(--accent) 8%, transparent); }
.secret-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.secret-label { display: block; }

/* ── "Where is your property?" ───────────────────────────────────────────────
   The address-first setup step. One question, one big field, readable results.

   ⚠️ Every interactive thing here is at least 44px tall and stays inside a
   320px viewport. The narrowest phone still in real use is 320px, and the
   product's own history is that a grid track floor pushed the property editor
   off a 360px screen — so widths are min()-clamped rather than assumed. */
.ask { font-size: 1.75rem; line-height: 1.2; margin: 0 0 .35rem; }
.ask-sub { font-size: 1.02rem; margin: 0 0 1.5rem; }

.place-search {
  display: flex;
  gap: .5rem;
  flex-wrap: wrap;              /* the button drops below on a 320px screen */
  max-width: 34rem;
  margin: 0 0 1.25rem;
}
.place-search input[type="search"] {
  flex: 1 1 14rem;
  min-width: 0;                 /* without it the flex item refuses to shrink */
  font-size: 1.05rem;
  padding: .85rem 1rem;
  min-height: 3rem;
  border: 1.5px solid var(--line);
  border-radius: 12px;
  background: var(--bg);
  color: var(--ink);
}
.place-search input[type="search"]:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 1px;
  border-color: var(--accent);
}
.place-search .button { min-height: 3rem; padding-inline: 1.25rem; }

/* ⚠️ NO MARGIN ON THE EMPTY FORM. The suggestion form is always in the markup —
   the client script fills it, so it has to exist before there is anything in it
   — and with a bottom margin it reserved 1.5rem of nothing on the empty search
   page, which read as a hole between the field and the fallback. The spacing
   comes from whatever is actually rendered instead. */
.place-list { display: grid; gap: .5rem; max-width: 34rem; margin: 0; }
.place-list [data-place-results] { display: grid; gap: .5rem; margin-bottom: 1.5rem; }
.place-list [data-place-results]:empty { display: none; margin-bottom: 0; }

/* A suggestion. A real submit button, so the keyboard and the screen reader
   get it for free — and so a click is a form submission rather than a
   JavaScript handler that has to exist. */
.place {
  display: flex;
  flex-direction: column;
  gap: .15rem;
  width: 100%;
  text-align: left;
  padding: .85rem 1rem;
  min-height: 3.25rem;
  border: 1.5px solid var(--line);
  border-radius: 12px;
  background: var(--bg);
  color: var(--ink);
  font: inherit;
  cursor: pointer;
  transition: border-color .12s ease, box-shadow .12s ease, transform .12s ease;
}
.place:hover { border-color: var(--accent); transform: translateY(-1px); }
.place:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { .place { transition: none; } }
.place-title { font-weight: 600; }
.place-sub { color: var(--muted); font-size: .9rem; }

/* The confirmation card. Deliberately quiet — it is a thing to READ and agree
   with, not another control to operate. */
.place-card {
  max-width: 34rem;
  padding: 1.25rem;
  margin: 0 0 1.5rem;
  border: 1.5px solid var(--line);
  border-left: 4px solid var(--accent);
  border-radius: 12px;
  background: var(--bg);
}
.place-card-name { font-size: 1.2rem; font-weight: 650; color: var(--ink); margin: 0 0 .25rem; }
.place-card-line { color: var(--ink); margin: 0 0 .15rem; }
.place-card-time { margin: .75rem 0 0; color: var(--muted); }
.place-card-time strong { color: var(--ink); }
.place-card-unknown { color: var(--muted); font-size: .92rem; }

.place-manual { margin: 1.5rem 0; }
/* ⚠️ `display: flex` REMOVES THE DEFAULT DISCLOSURE TRIANGLE, and without it
   this read as a bold line of text rather than a control — on the no-results
   page, the message says "enter it yourself below" and pointed straight at
   something that did not look clickable. The chevron is drawn back explicitly,
   and the accent colour says "this does something". Found by looking at the
   page, not by any assertion. */
.place-manual > summary {
  cursor: pointer;
  font-weight: 550;
  color: var(--accent);
  padding: .6rem 0;             /* a tappable summary, not a 16px line of text */
  min-height: 2.75rem;
  display: flex;
  align-items: center;
  gap: .55rem;
  list-style: none;             /* Firefox keeps its own marker without this */
}
.place-manual > summary::-webkit-details-marker { display: none; }
.place-manual > summary::before {
  content: '';
  flex: none;
  width: .45rem;
  height: .45rem;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(-45deg);    /* points right when closed, down when open */
  transition: transform .12s ease;
}
.place-manual[open] > summary::before { transform: rotate(45deg); }
@media (prefers-reduced-motion: reduce) {
  .place-manual > summary::before { transition: none; }
}
.place-manual > summary:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

/* ⚠️ VENDOR ATTRIBUTION — a licence obligation, not a credit line. Google
   requires it wherever Places data is displayed without a Google Map, and this
   product has no map. Quiet, but never hidden: it must be legible, so it takes
   the muted colour rather than a smaller size that would push it under the
   accessible floor. */
.place-attrib {
  max-width: 34rem;
  margin: -.75rem 0 1.5rem;
  color: var(--muted);
  font-size: .82rem;
}

/* A control that is not available YET — not one that can never work. It stays
   readable rather than fading to near-invisible: an owner has to be able to see
   what it says in order to understand what the note underneath is about. */
.button:disabled,
.button[disabled] {
  opacity: .55;
  cursor: not-allowed;
  border-style: dashed;
}

/* ── "Add a section" ────────────────────────────────────────────────────────
   One tap per kind. This replaced a picker + a required title field + a submit
   button at the foot of a long page, which then navigated somewhere else — for
   an intention the owner had already expressed completely by choosing a kind. */
.setup-add { margin: 2rem 0 0; }
.setup-add-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(11rem, 100%), 1fr));
  gap: .6rem;
  margin: .75rem 0 0;
}
.setup-add-card {
  display: flex;
  text-decoration: none;    /* it is an anchor, but it reads as a card */
  align-items: center;
  gap: .6rem;
  width: 100%;
  min-height: 3.25rem;      /* a comfortable target on a phone */
  padding: .8rem .9rem;
  text-align: left;
  border: 1.5px dashed var(--line);   /* dashed: this is a slot, not a thing yet */
  border-radius: 12px;
  background: var(--bg);
  color: var(--ink);
  font: inherit;
  font-weight: 550;
  cursor: pointer;
  transition: border-color .12s ease, transform .12s ease;
}
.setup-add-card:hover { border-color: var(--accent); transform: translateY(-1px); }
.setup-add-card:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { .setup-add-card { transition: none; } }
.setup-add-icon { color: var(--accent); line-height: 0; flex: none; }

/* Archive / restore. Separated from the delete "danger" block on purpose:
   archiving loses nothing and is reversible, deleting is neither, and a page
   that presents them identically teaches that they are the same weight. */
.archive-block {
  margin: 2.5rem 0 0;
  padding-top: 1.25rem;
  border-top: 1px solid var(--line);
}
.archive-block h2 { font-size: 1.05rem; margin: 0 0 .35rem; }

/* ⚠️ A TABLE MUST NOT CLIP WHAT IT CANNOT SCROLL TO. The properties table was
   395px of content in a 374px box on a 406px phone, with no scroll container —
   so the Status column was not merely off-screen, it was unreachable. The house
   rule for wide content is its own overflow-x container; this is that. */
.table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.table-scroll > .table { margin-block: 0; }

/* The guest address, while it is being decided. Quiet: it is something to
   glance at, not a field to fill in — the owner types a name, not a URL. */
.slug-line {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: .25rem .5rem;
  margin: -.35rem 0 .35rem;
  font-size: .85rem;
  color: var(--muted);
}
.slug-label { font-weight: 550; }
.slug-state { font-size: .82rem; }
.slug-state-ok { color: var(--ok-line); }
.slug-state-bad { color: var(--warn-ink); }
@media (prefers-color-scheme: dark) { .slug-state-bad { color: var(--warn-line); } }
.slug-change { margin: 0 0 1.1rem; }
.slug-change > summary {
  cursor: pointer;
  font-size: .85rem;
  color: var(--accent);
  padding: .45rem 0;
  min-height: 2.5rem;
  display: flex;
  align-items: center;
}
.slug-change > summary:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

/* ── Community items on the guide page (0028) ─────────────────────────────
   They borrow the setup-card shell so the association's items sit among the
   owner's own rather than under them — but they are NOT <details>, so the
   disclosure arrow and the hover affordance would both be promises the card
   cannot keep. */
.community-card > .setup-summary::after { content: none; }
.community-card > .setup-summary { cursor: default; }
.community-card > .setup-summary:hover { background: transparent; }
