/* ==========================================================================
   ClicClac — design system
   --------------------------------------------------------------------------
   Palette "persiana": the bottle green of Mediterranean window shutters, the
   brass of door hardware, whitewashed plaster. The product replaces a key, so
   the identity is borrowed from the doorway rather than from SaaS blue.

   Type rule, applied everywhere: anything the machine produces — codes, dates,
   hours, prices, statuses, links — is monospaced. Anything we say to a person
   is sans. That split is the whole typographic system.

   No webfonts on purpose: nothing loads from a third party, so there is no
   external request to explain to a European host and nothing to block render.
   ========================================================================== */

/* --- tokens ------------------------------------------------------------- */

/* Text colours (--ink, --muted, --brand, --brass, --alert) keep about 7:1
   against every surface they sit on, in both themes: much of the app's text is
   small, and small text at the bare 4.5:1 minimum reads badly. */
:root {
  --bg: #fbfaf8;
  --surface: #ffffff;
  --soft: #f2f0ea;
  --line: #e4dfd3;
  /* The edge of a control (a field, a ghost button): 3:1 against the page
     and the panel in both themes, so an empty field is findable in sunlight. */
  --line-strong: #968e7d;
  --ink: #14231c;
  --muted: #45524b;
  --brand: #1e5b44;
  --brand-hover: #164534;
  --brand-tint: #e8f0eb;
  --brand-ink: #ffffff;
  --brass: #6b4c16;
  --brass-mark: #b8892f;
  --brass-tint: #f7efdd;
  /* Something that can't wait: a guest due within 24 h with no check-in. */
  --alert: #9a3412;
  --alert-tint: #fdece4;
  /* What is behind the door when it opens. Warmer and lighter
     than --brass-tint, which is a surface, not a light. */
  --brand-glow: #ffe7b0;

  /* The handset body in the guest mock. Dark in both themes, because a phone
     is a dark object and that is what sells the frame as a device. */
  --device: #16211c;
  --device-trim: rgba(255, 255, 255, 0.28);

  --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif;
  --font-mono: ui-monospace, "SF Mono", "Cascadia Mono", "Segoe UI Mono",
    "Roboto Mono", Menlo, Consolas, monospace;

  --r-sm: 0.4rem;
  --r: 0.7rem;
  --r-lg: 1.1rem;

  --shadow-sm: 0 1px 2px rgba(20, 35, 28, 0.06);
  --shadow: 0 1px 2px rgba(20, 35, 28, 0.05), 0 10px 30px -14px rgba(20, 35, 28, 0.25);
  --shadow-lg: 0 2px 4px rgba(20, 35, 28, 0.05), 0 24px 60px -24px rgba(20, 35, 28, 0.32);

  --wrap: 68rem;
  color-scheme: light;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0f1613;
    --surface: #151e1a;
    --soft: #1a2420;
    --line: #28352e;
    --line-strong: #5c6d63;
    --ink: #e9f0eb;
    --muted: #b3c0b8;
    --brand: #5cbf98;
    --brand-hover: #7ed3b1;
    --brand-tint: #16281f;
    --brand-ink: #0f1613;
    --brass: #d5a85c;
    --brass-mark: #d5a85c;
    --brass-tint: #26200f;
    --alert: #ff8a5b;
    --alert-tint: #2c160e;
    /* Brighter here: in a dark room the doorway is the light. */
    --brand-glow: #d9a94e;

    --device: #050907;
    --device-trim: rgba(255, 255, 255, 0.22);

    --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
    --shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 10px 30px -14px rgba(0, 0, 0, 0.7);
    --shadow-lg: 0 2px 4px rgba(0, 0, 0, 0.4), 0 24px 60px -24px rgba(0, 0, 0, 0.8);
    color-scheme: dark;
  }
}

/* --- base --------------------------------------------------------------- */

* {
  box-sizing: border-box;
}

/* Scripts hide things with the attribute and expect them gone, whatever
   `display` their class sets (a .btn is inline-flex). */
[hidden] {
  display: none !important;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: 1rem;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

a {
  color: var(--brand);
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

/* A POST drawn as a link (log out). The form steps aside, so the button sits
   among the links as one of them, and the link rules below name it too. */
.link-form {
  display: contents;
}

.link-button {
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  text-align: inherit;
  color: var(--brand);
  cursor: pointer;
}

.link-button:hover {
  text-decoration: underline;
}

img,
svg {
  max-width: 100%;
}

:focus-visible {
  outline: 2px solid var(--brass-mark);
  outline-offset: 2px;
  border-radius: 2px;
}

/* Read out, not shown. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--brand);
  color: var(--brand-ink);
  padding: 0.7rem 1.1rem;
  border-radius: 0 0 var(--r) 0;
  z-index: 50;
}

.skip:focus {
  left: 0;
}

.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin: 0 auto;
  padding: 0 1.25rem;
}

/* --- typography --------------------------------------------------------- */

h1,
h2,
h3 {
  margin: 0;
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1.1;
  text-wrap: balance;
}

h1 {
  font-size: clamp(2.15rem, 1.35rem + 3.4vw, 3.9rem);
  letter-spacing: -0.038em;
}

h2 {
  font-size: clamp(1.6rem, 1.2rem + 1.6vw, 2.35rem);
}

h3 {
  font-size: 1.06rem;
  letter-spacing: -0.02em;
  line-height: 1.35;
}

p {
  margin: 0;
}

/* Eyebrows are labels the machine could have printed: mono, tracked, small. */
.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--brass);
}

.eyebrow::before {
  content: "";
  width: 1.6rem;
  height: 1px;
  background: var(--brass-mark);
  opacity: 0.7;
}

.lede {
  font-size: clamp(1.03rem, 0.97rem + 0.3vw, 1.2rem);
  line-height: 1.55;
  color: var(--muted);
  max-width: 42ch;
  text-wrap: pretty;
}

.section__head {
  max-width: 44ch;
  margin-bottom: 2.5rem;
}

.section__head h2 {
  margin-top: 0.9rem;
}

.section__head .lede {
  margin-top: 0.9rem;
}

/* --- rhythm ------------------------------------------------------------- */

.section {
  padding: clamp(3.5rem, 2rem + 6vw, 6.5rem) 0;
}

.section--tint {
  background: var(--soft);
  border-block: 1px solid var(--line);
}

/* --- buttons ------------------------------------------------------------ */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  min-height: 2.9rem;
  padding: 0.7rem 1.4rem;
  border: 1px solid transparent;
  border-radius: var(--r);
  background: var(--brand);
  color: var(--brand-ink);
  font: inherit;
  font-weight: 600;
  letter-spacing: -0.01em;
  cursor: pointer;
  transition: background-color 0.15s ease, transform 0.15s ease, border-color 0.15s ease;
}

.btn:hover {
  background: var(--brand-hover);
  text-decoration: none;
}

.btn:active {
  transform: translateY(1px);
}

.btn--ghost {
  background: transparent;
  color: var(--ink);
  border-color: var(--line-strong);
}

.btn--ghost:hover {
  background: var(--surface);
  border-color: var(--ink);
}

.btn--block {
  width: 100%;
}

.btn--sm {
  min-height: 2.2rem;
  padding: 0.35rem 0.8rem;
  font-size: 0.85rem;
}

.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-top: 2rem;
}

/* --- header ------------------------------------------------------------- */

/* A link to #access lands below the sticky header, not under it. */
html {
  scroll-padding-top: 6rem;
}

.site-header {
  position: sticky;
  top: 0;
  z-index: 20;
  /* Opaque first: if color-mix isn't understood the header still has a
     background instead of letting the page scroll through it. */
  background: var(--bg);
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter: saturate(1.6) blur(10px);
  border-bottom: 1px solid var(--line);
}

.site-header__inner {
  display: flex;
  align-items: center;
  gap: 1.5rem;
  min-height: 4.25rem;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  color: var(--ink);
  font-weight: 700;
  font-size: 1.08rem;
  letter-spacing: -0.035em;
}

.brand:hover {
  text-decoration: none;
}

.brand__mark {
  display: block;
  width: 1.65rem;
  height: 1.65rem;
  color: var(--brand);
  flex: none;
}

/* The door handle — the only brass in the mark, and the part the product
   makes redundant. Set here because presentation attributes can't take var(). */
.brand__handle {
  fill: var(--brass-mark);
}

/* --- the door opens ------------------------------------------------------
   The mark does the one thing a door does, but only while the name is under
   the cursor: a logo that loops on its own is one nobody can read past.
   Paused, the keyframes hold frame zero, which is the mark exactly as it was
   before it could move — so nothing about the still logo has changed. */

.brand__leaf,
.brand__room {
  animation-duration: 2.4s;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
  animation-play-state: paused;
}

.brand__leaf {
  animation-name: brand-swing, brand-tint;
  transform-box: view-box;
  transform-origin: 5px 0;
  /* Transparent rather than --bg: the mark sits on the header in one place
     and the footer in another, and it must not carry a ground of its own. */
  fill: transparent;
  stroke: currentColor;
  stroke-width: 1.35;
  stroke-opacity: 0;
}

.brand__room {
  animation-name: brand-glow;
  fill: var(--brand-glow);
  opacity: 0;
}

.brand:hover .brand__leaf,
.brand:hover .brand__room,
.brand:focus-visible .brand__leaf,
.brand:focus-visible .brand__room {
  animation-play-state: running;
}

/* Swinging away from the viewer, the slab reads in elevation as narrowing
   towards its hinge. A tenth of it stays in sight so it never disappears.
   Out of the latch quickly, then shut again fast: the clic, then the clac. */
@keyframes brand-swing {
  0%, 12% {
    transform: scaleX(1);
    animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
  }
  45%, 70% {
    transform: scaleX(0.1);
    animation-timing-function: cubic-bezier(0.7, 0, 0.84, 0);
  }
  82%, 100% {
    transform: scaleX(1);
  }
}

/* The slab only takes on colour once it is turning; shut, it is the outline. */
@keyframes brand-tint {
  0%, 8% {
    fill: transparent;
    stroke-opacity: 0;
  }
  20%, 70% {
    fill: var(--brand-tint);
    stroke-opacity: 1;
  }
  82%, 100% {
    fill: transparent;
    stroke-opacity: 0;
  }
}

@keyframes brand-glow {
  0%, 12% {
    opacity: 0;
  }
  45%, 70% {
    opacity: 1;
  }
  82%, 100% {
    opacity: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .brand__leaf,
  .brand__room {
    animation: none;
  }
}

.nav {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 1.5rem;
  font-size: 0.94rem;
}

.nav a,
.nav .link-button {
  color: var(--muted);
}

.nav a:hover,
.nav .link-button:hover {
  color: var(--ink);
  text-decoration: none;
}

.nav .btn {
  color: var(--brand-ink);
}

.nav .btn--ghost {
  color: var(--ink);
}

.nav a[aria-current] {
  color: var(--ink);
}

/* Settings in the header: a <details> like the phone menu, dropping a panel
   with the profile, each apartment and payments. It works without JavaScript;
   the script in base.html only closes it on an outside click or Escape. */
.dropdown {
  position: relative;
}

.dropdown > summary {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  list-style: none;
  color: var(--muted);
  cursor: pointer;
}

.dropdown > summary::-webkit-details-marker {
  display: none;
}

.dropdown > summary:hover,
.dropdown[open] > summary,
.dropdown > summary[aria-current] {
  color: var(--ink);
}

.dropdown__chevron {
  transition: transform 0.15s ease;
}

.dropdown[open] .dropdown__chevron {
  transform: rotate(180deg);
}

.dropdown__panel {
  position: absolute;
  top: calc(100% + 0.9rem);
  left: 50%;
  z-index: 20;
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  min-width: 15rem;
  max-width: 20rem;
  padding: 0.5rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r);
  box-shadow: var(--shadow);
  translate: -50% 0;
}

.dropdown__panel a {
  padding: 0.5rem 0.65rem;
  border-radius: var(--r-sm);
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.dropdown__panel a:hover {
  background: var(--soft);
  text-decoration: none;
}

.dropdown__panel a[aria-current],
.menu__panel .nav-group a[aria-current] {
  background: var(--brand-tint);
  color: var(--brand);
  font-weight: 600;
}

/* The account switcher: every account, a check on the one in use. The check
   keeps its room on every row, so the names line up. */
.dropdown__panel a.account-link,
.menu__panel a.account-link {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.account-link span {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

.account-link__check {
  flex: none;
  visibility: hidden;
}

/* The role this person has in a shared account, pushed to the right. */
.account-link__role {
  flex: none;
  margin-left: auto;
  padding-left: 0.75rem;
  font-size: 0.78rem;
  font-weight: 400;
  color: var(--muted);
}

/* Inside the drop-down, a section's links stack like the panel's own. */
.dropdown__panel .nav-group {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}

.dropdown__panel .nav-group__label:first-child {
  margin-top: 0.2rem;
}

/* The view switcher's label is the account in view: long names are cut,
   never pushed onto two lines. */
.view > summary {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}

.view__name {
  max-width: 12rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.view > summary .tag {
  padding: 0.05rem 0.4rem;
  font-size: 0.66rem;
}

/* Someone else's account: a quiet bar under the header, on every page. */
.tag--shared {
  background: var(--brand-tint);
  border-color: var(--brand);
  color: var(--brand);
}

.shared-bar {
  background: var(--soft);
  border-bottom: 1px solid var(--line);
  font-size: 0.88rem;
}

.shared-bar__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 1rem;
  padding-top: 0.65rem;
  padding-bottom: 0.65rem;
}

.shared-bar p {
  flex: 1 1 22rem;
  margin: 0;
  color: var(--ink);
  text-wrap: pretty;
}

.shared-bar a {
  font-weight: 600;
  white-space: nowrap;
}

.account-link[aria-current] .account-link__check {
  visibility: visible;
}

/* Shared by the drop-down and the phone menu (settings_links in base.html). */
.nav-group__label {
  margin: 0.55rem 0.65rem 0.15rem;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
}

.nav-group__sep {
  margin: 0.35rem 0;
  border: 0;
  border-top: 1px solid var(--line);
}

a.nav-group__add {
  color: var(--brand);
}

.menu__panel .nav-group a {
  white-space: nowrap;
}

.menu__panel .nav-group {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  margin-left: 0.6rem;
  padding-left: 0.5rem;
  border-left: 1px solid var(--line);
}

/* A language code is a value, not a sentence, so it is mono. Three of them
   make a segmented control rather than a toggle. */
.lang {
  display: inline-flex;
  flex: none;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  overflow: hidden;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  font-weight: 500;
  letter-spacing: 0.07em;
}

.lang a {
  padding: 0.28rem 0.44rem;
  color: var(--muted);
}

.lang a + a {
  border-left: 1px solid var(--line-strong);
}

.lang a:hover {
  background: var(--soft);
  color: var(--ink);
  text-decoration: none;
}

.lang a[aria-current] {
  background: var(--brand);
  color: var(--brand-ink);
}

/* In the mobile panel the languages are full names in the normal list. */
.menu__panel a[aria-current] {
  color: var(--brand);
  font-weight: 600;
}

/* Mobile disclosure menu — a <details>, no JavaScript involved. */
.menu {
  margin-left: auto;
  display: none;
  position: relative;
}

.menu > summary {
  list-style: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--r);
  cursor: pointer;
  color: var(--ink);
}

.menu > summary::-webkit-details-marker {
  display: none;
}

.menu__panel {
  position: absolute;
  right: 0;
  top: calc(100% + 0.6rem);
  min-width: 15rem;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  padding: 0.6rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r);
  box-shadow: var(--shadow);
}

.menu__panel a,
.menu__panel .link-button {
  padding: 0.6rem;
  border-radius: var(--r-sm);
  color: var(--ink);
}

.menu__panel a:hover,
.menu__panel .link-button:hover {
  background: var(--soft);
  text-decoration: none;
}

/* A button in the panel keeps a button's colours: the link rules above
   would put ink on green. */
.menu__panel .btn {
  margin-top: 0.35rem;
  color: var(--brand-ink);
}

.menu__panel .btn:hover {
  background: var(--brand-hover);
}

/* Raised from 46rem when French arrived: "Comment ça marche" plus a
   three-segment language control no longer fit beside the wordmark, and the
   nav was wrapping into the logo before the menu took over. */
@media (max-width: 54rem) {
  .nav {
    display: none;
  }
  /* A guest's page has no menu to fall back on, and three codes fit
     beside the wordmark on any phone. */
  .nav--lang {
    display: flex;
  }
  .menu {
    display: block;
  }
}

/* --- hero --------------------------------------------------------------- */

.hero {
  padding: clamp(2.75rem, 1.5rem + 5vw, 5.5rem) 0 clamp(3rem, 2rem + 5vw, 6rem);
}

.hero__grid {
  display: grid;
  gap: clamp(2.5rem, 1rem + 6vw, 4.5rem);
  align-items: center;
  grid-template-columns: 1fr;
}

@media (min-width: 60rem) {
  .hero__grid {
    grid-template-columns: 1.12fr 0.88fr;
  }
}

/* The largest type on the site: nothing sits above it, and it has ten seconds. */
.hero__title {
  font-size: clamp(2.5rem, 1.25rem + 5.2vw, 4.6rem);
  line-height: 1.02;
}

.hero__sub {
  margin-top: 1.35rem;
  font-size: clamp(1.05rem, 0.98rem + 0.4vw, 1.28rem);
  max-width: 40ch;
}

/* The three ways in: what the guest is sent depends on what is on the door. */
.hero__ways {
  margin-top: 1rem;
  font-family: var(--font-mono);
  font-size: 0.82rem;
  letter-spacing: 0.03em;
  color: var(--brand);
  text-wrap: balance;
}

/* Where the visitor's flats are: which country's rules the page explains. */
.country-pick {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 0.75rem;
  margin-top: 1.1rem;
  font-size: 0.9rem;
  color: var(--muted);
}

.country-pick .lang a {
  padding: 0.3rem 0.6rem;
}

.hero__trust {
  margin-top: 1.6rem;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  letter-spacing: 0.03em;
  color: var(--muted);
}

/* --- the signature: the traveller's record ---------------------------------
   What the law wants from every stay, filled in by the guest. Laid out like
   the form it replaces: a label, a value, a line. */

.record {
  margin: 0;
  max-width: 26rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}

@media (min-width: 60rem) {
  .record,
  .record-caption {
    margin-left: auto;
  }
}

.record__top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 1rem 1.4rem;
  border-bottom: 1px solid var(--line);
  font-family: var(--font-mono);
  font-size: 0.78rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
}

.record__state {
  padding: 0.2rem 0.6rem;
  border-radius: 999px;
  background: var(--brand-tint);
  color: var(--brand);
  letter-spacing: 0.06em;
}

.record__rows {
  margin: 0;
  padding: 0.35rem 1.4rem;
}

.record__rows div {
  display: grid;
  grid-template-columns: 7.5rem 1fr;
  gap: 1rem;
  padding: 0.6rem 0;
  border-bottom: 1px solid var(--line);
}

.record__rows div:last-child {
  border-bottom: 0;
}

.record__rows dt {
  font-size: 0.88rem;
  color: var(--muted);
}

.record__rows dd {
  margin: 0;
  font-weight: 500;
}

.record__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem 1rem;
  padding: 0.9rem 1.4rem;
  border-top: 1px solid var(--line);
  background: var(--soft);
  font-size: 0.85rem;
}

.record__done {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-weight: 500;
  color: var(--brand);
}

.record-caption {
  margin-top: 1.1rem;
  max-width: 26rem;
  font-size: 0.9rem;
  color: var(--muted);
  text-wrap: pretty;
}

/* Not built yet. Dashed and brass wherever it appears, so a host never reads
   an upcoming feature as one that already files their records. */
.soon {
  display: inline-block;
  padding: 0.15rem 0.55rem;
  border: 1px dashed var(--brass-mark);
  border-radius: 999px;
  background: var(--brass-tint);
  color: var(--brass);
  font-family: var(--font-mono);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: none;
  white-space: nowrap;
  vertical-align: 0.1em;
}

.eyebrow .soon,
.timeline__when .soon {
  margin-left: 0.4rem;
}

/* --- the law, in three figures -------------------------------------------- */

/* What the law asks, and beside it the record that answers it. */
.law-head {
  display: grid;
  gap: 2.5rem;
  align-items: center;
}

.law-head .section__head {
  margin-bottom: 0;
}

@media (min-width: 60rem) {
  .law-head {
    grid-template-columns: 1fr 26rem;
  }
}

.law {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: 1rem;
  margin: 2rem 0 0;
  padding: 0;
  list-style: none;
}

.law__fact {
  padding: 1.4rem 1.4rem 1.5rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
}

.law__figure {
  font-size: clamp(2rem, 1.6rem + 1.6vw, 2.75rem);
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.03em;
  color: var(--brand);
}

.law__body {
  margin-top: 0.7rem;
  font-size: 0.95rem;
  text-wrap: pretty;
}

.law__source {
  margin-top: 1.5rem;
  max-width: 48rem;
  font-size: 0.9rem;
  color: var(--muted);
}

.law__source a {
  text-decoration: underline;
}

/* --- before / after ----------------------------------------------------- */

.compare {
  display: grid;
  gap: 1.25rem;
  grid-template-columns: 1fr;
}

@media (min-width: 46rem) {
  .compare {
    grid-template-columns: 1fr 1fr;
  }
}

.compare__col {
  padding: 1.6rem;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
}

.compare__col--after {
  border-color: var(--brand);
  background: var(--brand-tint);
}

.compare__label {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
}

.compare__col--after .compare__label {
  color: var(--brand);
}

/* The "without" side is the host's phone at midnight. Bubbles rather than
   bullet points, because every host has had this exact conversation. */
.thread {
  display: grid;
  gap: 0.7rem;
  margin-top: 1.25rem;
}

.bubble {
  justify-self: start;
  max-width: 90%;
  padding: 0.7rem 0.95rem;
  background: var(--bg);
  border: 1px solid var(--line);
  /* squared-off bottom left: the tail, without drawing one */
  border-radius: 1rem 1rem 1rem 0.25rem;
  font-size: 0.94rem;
  line-height: 1.45;
  text-wrap: pretty;
}

.bubble time {
  display: block;
  margin-top: 0.35rem;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.06em;
  color: var(--muted);
}

/* The "with" side is one email, sent while the host was asleep. */
.mailcard {
  margin-top: 1.25rem;
  padding: 1.1rem 1.2rem;
  background: var(--surface);
  border: 1px solid var(--brand);
  border-radius: var(--r);
  box-shadow: var(--shadow-sm);
}

.mailcard__subject {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  font-weight: 600;
  letter-spacing: -0.015em;
}

.mailcard__subject svg {
  flex: none;
  color: var(--brand);
}

.mailcard__code {
  margin-top: 0.85rem;
  font-family: var(--font-mono);
  font-size: clamp(1.35rem, 1.1rem + 1vw, 1.7rem);
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.3em;
  color: var(--ink);
}

.mailcard__meta {
  margin-top: 0.55rem;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--muted);
}

.quiet {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  margin-top: 1.15rem;
  font-family: var(--font-mono);
  font-size: 0.8rem;
  color: var(--brand);
}

.quiet svg {
  flex: none;
}

/* In the hero the two halves stack. The messages are the setup, small and
   bare; the email is the answer, and its code is the page's second-largest
   type. The email is the card here, so the card inside it goes flat. */
.compare--hero {
  grid-template-columns: 1fr;
  gap: 1.5rem;
  max-width: 30rem;
}

@media (min-width: 60rem) {
  .compare--hero {
    margin-left: auto;
  }
}

.compare--hero .compare__col:not(.compare__col--after) {
  padding: 0;
  border: 0;
  background: none;
}

.compare--hero .thread {
  gap: 0.5rem;
  margin-top: 0.8rem;
}

.compare--hero .bubble {
  background: var(--surface);
  font-size: 0.9rem;
}

.compare--hero .compare__col--after {
  padding: clamp(1.4rem, 1rem + 2vw, 2.1rem);
  box-shadow: var(--shadow-lg);
}

.compare--hero .mailcard {
  margin-top: 1rem;
  padding: 0;
  border: 0;
  background: none;
  box-shadow: none;
}

/* On a phone the columns stack, and the answer has to be on the first
   screen: the email comes first, the messages it spares the host after it.
   The way to the law is in the menu there, so the one button is the one
   that matters. */
@media (max-width: 59.99rem) {
  .compare--hero .compare__col--after {
    order: -1;
  }
}

@media (max-width: 34rem) {
  .hero .btn--ghost {
    display: none;
  }
}

.compare--hero .mailcard__code {
  margin-top: 0.6rem;
  font-size: clamp(3rem, 1.6rem + 5vw, 4.75rem);
  line-height: 1.1;
  letter-spacing: 0.1em;
  color: var(--brand);
  white-space: nowrap;
}

/* --- scroll reveal -------------------------------------------------------
   The messages should arrive as the reader scrolls, the way they arrived on
   the host's phone. The hidden state is applied by script, never in the base
   stylesheet — without JavaScript everything is simply already visible. */

.reveal > * {
  opacity: 0;
  transform: translateY(0.7rem);
  transition: opacity 0.5s ease, transform 0.5s cubic-bezier(0.2, 0.7, 0.3, 1);
}

.reveal--in > * {
  opacity: 1;
  transform: none;
}

/* One message at a time, slow enough to read as a sequence. */
.reveal > *:nth-child(1) { transition-delay: 0.05s; }
.reveal > *:nth-child(2) { transition-delay: 0.3s; }
.reveal > *:nth-child(3) { transition-delay: 0.55s; }

/* The reply lands after the last message, not alongside it. */
[data-reveal="reply"].reveal > *:nth-child(1) { transition-delay: 0.85s; }
[data-reveal="reply"].reveal > *:nth-child(2) { transition-delay: 1.05s; }

@media (prefers-reduced-motion: reduce) {
  .reveal > * {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* --- the life of one booking -------------------------------------------
   A rail with five events. Only the first is the host's, and colouring it
   differently is the argument: everything after it happens without them. */

.timeline {
  position: relative;
  display: grid;
  gap: 1.6rem;
  margin-top: 2rem;
}

.timeline::before {
  content: "";
  position: absolute;
  left: 0.55rem;
  top: 0.6rem;
  bottom: 0.6rem;
  width: 2px;
  margin-left: -1px;
  background: var(--line);
}

.timeline__item {
  position: relative;
  padding-left: 2.1rem;
}

.timeline__item::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.1rem;
  width: 1.1rem;
  height: 1.1rem;
  border-radius: 50%;
  background: var(--brand);
  border: 3px solid var(--bg);
}

/* The host's own step: hollow and brass, like the handle in the mark. */
.timeline__item--you::before {
  background: var(--bg);
  border: 3px solid var(--brass-mark);
}

.timeline__when {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
}

.timeline__item--you .timeline__when {
  color: var(--brass);
}

.timeline__item--soon::before {
  background: var(--bg);
  border: 2px dashed var(--brass-mark);
}

.timeline__item h3 {
  margin: 0.4rem 0 0.35rem;
}

.timeline__item p {
  font-size: 0.93rem;
  color: var(--muted);
  text-wrap: pretty;
}

@media (min-width: 56rem) {
  .timeline {
    grid-template-columns: repeat(5, 1fr);
    gap: 1.5rem;
  }
  .timeline::before {
    left: 0;
    right: 0;
    top: 0.55rem;
    bottom: auto;
    width: auto;
    height: 2px;
    margin-left: 0;
  }
  .timeline__item {
    padding-left: 0;
    padding-top: 2rem;
  }
  .timeline__item::before {
    top: 0;
  }
}

.timeline__legend {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.5rem;
  margin-top: 2.25rem;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}

.timeline__key {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
}

.timeline__key::before {
  content: "";
  width: 0.7rem;
  height: 0.7rem;
  border-radius: 50%;
  background: var(--brand);
}

.timeline__key--you::before {
  background: transparent;
  border: 2px solid var(--brass-mark);
}

.timeline__key--soon::before {
  background: transparent;
  border: 2px dashed var(--brass-mark);
}

/* --- the host's side ----------------------------------------------------
   Mirrors the hero on purpose: prose on one side, the artifact on the other.
   Up there it's the guest's code; down here it's the host's screen. */

.showcase {
  display: grid;
  gap: clamp(2.5rem, 1rem + 5vw, 4rem);
  align-items: center;
  grid-template-columns: 1fr;
}

@media (min-width: 58rem) {
  .showcase {
    grid-template-columns: 0.95fr 1.05fr;
  }
}

/* Two guarantees, as a definition list rather than bullets — the label is
   the claim and the body is the detail, which a bullet can't show. */
.assure {
  margin: 2rem 0 0;
  display: grid;
  gap: 1.1rem;
}

.assure dt {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--brass);
}

.assure dd {
  margin: 0.35rem 0 0;
  font-size: 0.95rem;
  color: var(--muted);
  max-width: 46ch;
  text-wrap: pretty;
}

.mock {
  padding: 1.1rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-lg);
}

.mock .next {
  padding: 1.15rem;
}

.mock__label {
  margin: 1.4rem 0 0.6rem;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
}

.mock__log {
  list-style: none;
  margin: 0;
  padding: 0;
  border-top: 1px solid var(--line);
}

.mock__log li {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem 0.75rem;
  padding: 0.7rem 0.15rem;
  border-bottom: 1px solid var(--line);
  font-size: 0.83rem;
}

.mock__log .num {
  flex: 1 1 9rem;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  color: var(--muted);
}

.mock__when {
  flex: none;
  width: 100%;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--muted);
}

@media (min-width: 26rem) {
  .mock__when {
    width: auto;
  }
}

/* --- pricing ------------------------------------------------------------ */

/* Cards must stretch to the tallest, or .plan__body's flex:1 has no room to
   work and the three CTAs end up at three different heights. */
.plans {
  display: grid;
  gap: 1.25rem;
  grid-template-columns: 1fr;
}

@media (min-width: 52rem) {
  .plans {
    grid-template-columns: repeat(3, 1fr);
  }
}

.plan {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.8rem;
  padding: 1.75rem 1.5rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
}

.plan--featured {
  border-color: var(--brand);
  box-shadow: var(--shadow);
}

/* A price is a number the business set, so it reads as data. Price and period
   stack, so "9 €" and "Hablamos" line up across the row either way. */
.plan__price {
  margin-top: 0.2rem;
  font-family: var(--font-mono);
  font-size: clamp(1.65rem, 1.25rem + 1.1vw, 2.1rem);
  font-weight: 500;
  line-height: 1.1;
  letter-spacing: -0.03em;
  color: var(--ink);
}

.plan__period {
  display: block;
  margin-top: 0.35rem;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}

.plan__body {
  color: var(--muted);
  font-size: 0.95rem;
  flex: 1;
  text-wrap: pretty;
}

.plan .btn {
  margin-top: 0.4rem;
}

/* --- faq ---------------------------------------------------------------- */

/* Separate cards rather than hairline rows: five distinct things you can
   open, not one undifferentiated list. */
.faq {
  display: grid;
  gap: 0.7rem;
  max-width: 46rem;
}

.faq__item {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r);
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.faq__item:hover {
  border-color: var(--line-strong);
}

.faq__item[open] {
  border-color: var(--brand);
  box-shadow: var(--shadow-sm);
}

.faq__item > summary {
  list-style: none;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1.25rem;
  padding: 1.05rem 1.2rem;
  cursor: pointer;
}

.faq__item > summary::-webkit-details-marker {
  display: none;
}

/* The topic is what makes the stack scannable without opening anything. */
.faq__topic {
  display: block;
  margin-bottom: 0.3rem;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--brass);
}

.faq__q {
  display: block;
  font-weight: 600;
  letter-spacing: -0.015em;
  text-wrap: pretty;
}

/* A plus that becomes a minus: the vertical stroke turns and fades out. */
.faq__sign {
  position: relative;
  flex: none;
  width: 1rem;
  height: 1rem;
  margin-top: 0.3rem;
}

.faq__sign::before,
.faq__sign::after {
  content: "";
  position: absolute;
  background: var(--brass-mark);
  border-radius: 1px;
  transition: transform 0.2s ease, opacity 0.2s ease;
}

.faq__sign::before {
  left: 0;
  right: 0;
  top: 50%;
  height: 2px;
  margin-top: -1px;
}

.faq__sign::after {
  top: 0;
  bottom: 0;
  left: 50%;
  width: 2px;
  margin-left: -1px;
}

.faq__item[open] .faq__sign::after {
  transform: rotate(90deg);
  opacity: 0;
}

.faq__answer {
  margin: 0;
  padding: 0 1.2rem 1.25rem;
  color: var(--muted);
  text-wrap: pretty;
}

/* The measure belongs to the prose. Anything shown alongside it, like the
   three handsets, gets the full width of the card. */
.faq__answer p {
  max-width: 62ch;
}

.faq__more {
  margin-top: 1.75rem;
  font-size: 0.92rem;
  color: var(--muted);
}

/* --- the guest's three screens -----------------------------------------
   Everything the guest ever sees, on the device they see it on. Schematic
   rather than a fake screenshot, and it runs to three handsets because the
   code lands in an email: the confirmation page cannot show it, the Nuki
   code doesn't exist until the pipeline runs a few minutes later. */

/* All three at once, never a sideways scroll: three equal columns once there
   is room for them, one stacked column when there isn't. */
.phones {
  display: grid;
  gap: 0.9rem;
  grid-template-columns: 1fr;
  margin-top: 1.4rem;
}

@media (min-width: 40rem) {
  .phones {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

.phone-step {
  display: flex;
  flex-direction: column;
  margin: 0;
  min-width: 0;
  max-width: 15rem;
  margin-inline: auto;
}

@media (min-width: 40rem) {
  .phone-step {
    max-width: none;
    margin-inline: 0;
  }
}

.phone-step figcaption {
  /* Two lines are reserved whether or not they're used, so a caption that
     wraps can't make its handset shorter than the two beside it. */
  min-height: 3.2em;
  margin-top: 0.7rem;
  font-family: var(--font-mono);
  font-size: 0.62rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--muted);
  text-wrap: pretty;
}

.phone-step__index {
  color: var(--brass);
}

/* flex:1 makes every handset as tall as the tallest of the three, which is
   the form. The screens then absorb the difference from the inside. */
.phone {
  display: flex;
  flex-direction: column;
  flex: 1;
  padding: 0.45rem;
  background: var(--device);
  border-radius: 1.45rem;
  box-shadow:
    0 0 0 1px rgba(0, 0, 0, 0.25),
    var(--shadow);
}

/* Earpiece slot and home indicator: two bars are all it takes to read as a
   handset, and they cost no markup. */
.phone::before,
.phone::after {
  content: "";
  flex: none;
  border-radius: 999px;
  background: var(--device-trim);
  margin-inline: auto;
}

.phone::before {
  width: 2.4rem;
  height: 0.22rem;
  margin-block: 0.3rem 0.45rem;
}

.phone::after {
  width: 3rem;
  height: 0.2rem;
  margin-block: 0.5rem 0.3rem;
}

.phone__screen {
  display: flex;
  flex-direction: column;
  flex: 1;
  padding: 0.85rem 0.8rem 0.95rem;
  background: var(--surface);
  border-radius: 1.05rem;
}

.phone__title {
  font-size: 0.95rem;
  font-weight: 700;
  letter-spacing: -0.025em;
  color: var(--ink);
}

.phone__meta {
  margin-top: 0.25rem;
  font-family: var(--font-mono);
  font-size: 0.58rem;
  letter-spacing: 0.02em;
  color: var(--muted);
}

.phone__field {
  margin-top: 0.45rem;
  padding: 0.42rem 0.5rem;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 0.35rem;
  font-size: 0.62rem;
  color: var(--muted);
}

.phone__btn {
  margin-top: 0.7rem;
  padding: 0.48rem;
  background: var(--brand);
  border-radius: 0.35rem;
  color: var(--brand-ink);
  font-size: 0.65rem;
  font-weight: 600;
  text-align: center;
}

/* Screen two: the confirmation. It promises the code rather than showing it,
   which is exactly what the real page does. */
/* The shortest screen of the three: auto margins park it in the middle of
   whatever height the form screen set. */
.phone__done {
  margin-block: auto;
  padding: 0.6rem 0.2rem;
  text-align: center;
}

.phone__done svg {
  width: 1.6rem;
  height: 1.6rem;
  color: var(--brand);
}

.phone__done b {
  display: block;
  margin-top: 0.6rem;
  font-size: 0.85rem;
  letter-spacing: -0.02em;
  color: var(--ink);
}

.phone__done span {
  display: block;
  margin-top: 0.4rem;
  font-size: 0.6rem;
  line-height: 1.45;
  color: var(--muted);
  text-wrap: pretty;
}

/* Screen three: the email, which is where the code actually turns up. */
.phone__mail {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  padding-bottom: 0.5rem;
  border-bottom: 1px solid var(--line);
  font-family: var(--font-mono);
  font-size: 0.55rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

.phone__mail svg {
  flex: none;
  color: var(--brand);
}

.phone__subject {
  margin-top: 0.6rem;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--ink);
}

/* Header and subject stay at the top like a real mail app; the code and its
   window take the slack between them and the bottom. */
.phone__code {
  margin-top: auto;
  padding: 0.55rem 0.3rem;
  background: var(--soft);
  border-radius: 0.4rem;
  font-family: var(--font-mono);
  font-size: 1.15rem;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.22em;
  text-align: center;
  /* the tracking is on the right of each glyph, so nudge back to centre */
  text-indent: 0.22em;
  color: var(--ink);
}

.phone__window {
  margin-top: 0.5rem;
  margin-bottom: auto;
  font-family: var(--font-mono);
  font-size: 0.58rem;
  text-align: center;
  color: var(--muted);
}

/* --- launch page ---------------------------------------------------------
   The one page that stands on the green itself rather than on plaster: it is
   the "coming soon" slide, and until we open it is the front door. The ground
   is fixed per theme instead of following --brand, because --brand turns into
   a light mint in the dark and a whole screen of that is not a shutter. */

.launch {
  --launch-ground: #1e5b44;
  --launch-ink: #fbfaf8;
  --launch-leaf: #2f7a5d;
  --launch-glow: #ffe7b0;
  --launch-on-glow: #14231c;

  background: var(--launch-ground);
  color: var(--launch-ink);
  padding: clamp(2.75rem, 1.5rem + 6vw, 5.5rem) 0 clamp(3rem, 2rem + 6vw, 5.5rem);
  /* The step you cross, in the same brass as the handle. */
  border-bottom: 0.35rem solid var(--brass-mark);
  /* The closing button sends people back here; keep the sticky header off it. */
  scroll-margin-top: 4.25rem;
}

@media (prefers-color-scheme: dark) {
  .launch {
    --launch-ground: #12261d;
    --launch-ink: #e9f0eb;
    --launch-leaf: #1f4534;
    --launch-glow: #f0cd84;
  }
}

/* Words left, film right, each centred on the other. Stacked on a phone,
   words first: the film is the proof, not the pitch. */
.launch__grid {
  display: grid;
  gap: clamp(2.25rem, 1rem + 5vw, 4rem);
  align-items: center;
}

@media (min-width: 60rem) {
  .launch__grid {
    grid-template-columns: 0.9fr 1.1fr;
  }
}

.doorway {
  display: block;
  width: clamp(3.25rem, 2.6rem + 2.5vw, 4.25rem);
  height: clamp(3.25rem, 2.6rem + 2.5vw, 4.25rem);
  overflow: visible;
  color: var(--launch-ink);
}

.doorway__leaf {
  /* brand-tint, the header mark's keyframes, fills the turning slab with
     --brand-tint; on this ground that has to be a green, not a pale one. */
  --brand-tint: var(--launch-leaf);
  transform-box: view-box;
  transform-origin: 5px 0;
  fill: transparent;
  stroke: currentColor;
  stroke-width: 1.35;
  stroke-opacity: 0;
}

.doorway__handle {
  fill: var(--brass-mark);
  stroke: none;
}

.doorway__room {
  fill: var(--launch-glow);
  opacity: 0;
}

.doorway__ping {
  transform-box: view-box;
  transform-origin: 15.6px 15px;
  stroke: var(--launch-glow);
  stroke-width: 0.5;
  opacity: 0;
}

/* Once, as the page arrives, and then it is a logo again: the header's rule
   that a mark which loops on its own is one nobody can read past. */
@media (prefers-reduced-motion: no-preference) {
  .doorway__leaf {
    animation: brand-swing 2.4s linear 0.6s both, brand-tint 2.4s linear 0.6s both;
  }
  .doorway__room {
    animation: brand-glow 2.4s linear 0.6s both;
  }
  .doorway__ping {
    animation: doorway-ping 2.4s linear 0.6s both;
  }
}

@keyframes doorway-ping {
  0%, 10% {
    transform: scale(1);
    opacity: 0.9;
  }
  26%, 100% {
    transform: scale(3.4);
    opacity: 0;
  }
}

/* For someone who has just joined, the door is open and stays open. The
   resting values are the open door, so with motion off it simply is. */
.doorway--open .doorway__leaf {
  transform: scaleX(0.1);
  fill: var(--launch-leaf);
  stroke-opacity: 1;
  animation: none;
}

.doorway--open .doorway__room {
  opacity: 1;
  animation: none;
}

.doorway--open .doorway__ping {
  animation: none;
}

@media (prefers-reduced-motion: no-preference) {
  .doorway--open .doorway__leaf {
    animation: doorway-open 0.8s cubic-bezier(0.16, 1, 0.3, 1) 0.3s backwards;
  }
  .doorway--open .doorway__room {
    animation: doorway-light 0.8s ease-out 0.3s backwards;
  }
}

@keyframes doorway-open {
  from {
    transform: scaleX(1);
    fill: transparent;
    stroke-opacity: 0;
  }
}

@keyframes doorway-light {
  from {
    opacity: 0;
  }
}

.launch__soon {
  margin-top: 1.6rem;
  font-family: var(--font-mono);
  font-size: 0.76rem;
  font-weight: 500;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--launch-glow);
}

/* What it is, then when: one line each. */
.launch__soon span {
  display: block;
  text-wrap: balance;
}

/* On a phone the mark stands beside those two lines instead of over them:
   the height it gives back is what puts the film's button on the first screen. */
@media (max-width: 33.99rem) {
  .launch {
    padding-top: 2rem;
  }

  .launch__grid {
    gap: 1.25rem;
  }

  .launch__head {
    display: flex;
    align-items: center;
    gap: 0.9rem;
  }

  .launch__head .doorway {
    flex: none;
    width: 2.6rem;
    height: 2.6rem;
  }

  .launch__head .launch__soon {
    margin-top: 0;
    letter-spacing: 0.12em;
  }
}

/* Half a column wide and a full sentence long, so a size under the h1's. */
.launch__title {
  margin-top: 0.8rem;
  font-size: clamp(2rem, 1.35rem + 2.5vw, 3.1rem);
}

.launch__lines {
  /* Also an <output> (the confirmation), which is inline by default. */
  display: block;
  margin-top: 1.25rem;
  font-size: clamp(1.05rem, 0.98rem + 0.35vw, 1.22rem);
  line-height: 1.5;
  color: var(--launch-ink);
  color: color-mix(in srgb, var(--launch-ink) 84%, transparent);
}

.launch__lines span {
  display: block;
}

.launch__lines .launch__you {
  color: var(--launch-glow);
}

.launch__form {
  position: relative;
  display: grid;
  gap: 0.6rem;
  max-width: 29rem;
  margin-top: 2rem;
}

@media (min-width: 34rem) {
  .launch__form {
    grid-template-columns: 1fr auto;
  }
}

.launch__form input[type="email"] {
  border-color: transparent;
}

/* In the dark the field is as dark as the ground: it needs its edge back. */
@media (prefers-color-scheme: dark) {
  .launch__form input[type="email"] {
    border-color: var(--line-strong);
  }
}

.launch__form input[type="email"]:focus {
  border-color: var(--launch-glow);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--launch-glow) 35%, transparent);
}

/* The green button would vanish on a green ground, so here the action takes
   the colour of the light behind the door. */
.launch__btn {
  background: var(--launch-glow);
  color: var(--launch-on-glow);
}

.launch__btn:hover {
  background: #fff3d6;
}

.launch__trap {
  position: absolute;
  left: -9999px;
}

.launch__error {
  margin-top: 0.85rem;
  font-size: 0.92rem;
  font-weight: 600;
  color: var(--launch-glow);
}

/* Said to a person, so sans, not the mono of machine output. It takes its
   colour from wherever it sits: the hero's ink, or the closing doorway's. */
.launch__note {
  max-width: 29rem;
  margin-top: 1rem;
  font-size: 0.8rem;
  line-height: 1.5;
  color: color-mix(in srgb, currentColor 88%, transparent);
  text-wrap: pretty;
}

.launch__note a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

/* What joining gets you: the one line in the hero that is an offer. */
.launch__perk {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  margin-top: 1.6rem;
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--launch-glow);
}

.launch__perk svg {
  flex: none;
  width: 1.15rem;
  height: 1.15rem;
}

.launch__perk + .launch__form {
  margin-top: 0.9rem;
}

/* The film is light-grounded in every theme. On the green that makes it the
   lit room: no border, just the shadow it throws. */
.film {
  position: relative;
  aspect-ratio: 16 / 9;
  background: #f6f4ee;
  border-radius: var(--r-lg);
  overflow: hidden;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.18), 0 30px 70px -28px rgba(0, 0, 0, 0.6);
}

.film video {
  display: block;
  width: 100%;
  height: 100%;
}

/* The whole poster is the button, so there is no small target to aim at.
   What shows is one pill in the corner: a disc in the middle would sit on
   the door code, which is the thing the poster is there to show. */
.film__play {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: flex-end;
  justify-content: flex-start;
  padding: clamp(0.8rem, 0.4rem + 1.6vw, 1.4rem);
  border: 0;
  background: transparent;
  font: inherit;
  cursor: pointer;
}

.film__play[hidden] {
  display: none;
}

.film__pill {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.4rem 1rem 0.4rem 0.4rem;
  border-radius: 999px;
  background: #1e5b44;
  color: #fbfaf8;
  font-weight: 600;
  font-size: 0.92rem;
  box-shadow: 0 10px 30px -8px rgba(20, 35, 28, 0.55);
  transition: transform 0.2s ease, background-color 0.2s ease;
}

.film__play:hover .film__pill {
  background: #164534;
  transform: translateY(-1px);
}

.film__disc {
  display: grid;
  place-items: center;
  width: 2.2rem;
  height: 2.2rem;
  border-radius: 50%;
  background: #ffe7b0;
  color: #14231c;
}

.film__disc svg {
  width: 46%;
  height: 46%;
  fill: currentColor;
}

.film__length {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  font-weight: 400;
  opacity: 0.75;
}

/* On a phone the poster is small enough that a full-size pill would lie
   across its caption. */
@media (max-width: 34rem) {
  .film__play {
    padding: 0.55rem;
  }

  .film__pill {
    gap: 0.45rem;
    padding: 0.28rem 0.75rem 0.28rem 0.28rem;
    font-size: 0.78rem;
  }

  .film__disc {
    width: 1.6rem;
    height: 1.6rem;
  }
}

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

/* The same film on the landing's plaster: there it is an object on the page,
   so it takes the showcase shadow and a hairline instead of the green's. */
.filmband {
  display: grid;
  gap: clamp(1.5rem, 1rem + 2vw, 2.5rem);
  justify-items: center;
  text-align: center;
}

.filmband .film {
  width: min(100%, 54rem);
  border: 1px solid var(--line);
  box-shadow: var(--shadow-lg);
}

/* --- the open doorway -----------------------------------------------------
   The closing block is a small scene in thin lines: the doorway of a flat,
   an open arch on a floor line, a lamp over it, a plant beside it. Flat
   fills, one weight of line, brass for the lamp and the pot. There is no
   door in it: an arch with the light on behind it is already an open one.

   Everything takes the theme's own tokens, so it follows light and dark like
   the rest of the site. */

.scene {
  --scene-line: 1.5px;
  --lamp: clamp(5.5rem, 4rem + 6vw, 8rem);

  position: relative;
  max-width: 47rem;
  margin: 0 auto;
  /* Room for the lamp above the arch and the plant beside it. The lamp is
     about two thirds as tall as it is wide, and wants air under it. */
  padding: calc(var(--lamp) * 0.74 + 1rem) 18% 0;
}

/* The floor: one line under everything. */
.scene::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: var(--scene-line);
  background: var(--ink);
}

.scene__line {
  fill: none;
  stroke: var(--ink);
  stroke-width: 1.5;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
}

.scene__brass {
  fill: var(--brass-mark);
}

.scene__leaf-dark {
  fill: var(--brand);
}

.scene__leaf-light {
  fill: color-mix(in srgb, var(--brand) 62%, var(--brass-tint));
}

/* Hung over the middle of the arch, and large enough to light it. */
.scene__lamp {
  position: absolute;
  top: 0;
  left: 50%;
  width: var(--lamp);
  transform: translateX(-50%);
  overflow: visible;
  pointer-events: none;
}

.scene__plant {
  position: absolute;
  left: 1%;
  bottom: 0;
  width: clamp(4.75rem, 14%, 6.5rem);
}

/* The opening: a thin outline round the lit room, standing on the floor. */
.scene__room {
  /* Radii this large are scaled down to half the width, whatever the width
     is: a semicircle on every screen. */
  border-radius: 9999px 9999px 0 0;
  border: var(--scene-line) solid var(--ink);
  border-bottom: 0;
  background: var(--brass-tint);
  /* The title has to clear the curve of the arch, so the top padding grows
     with the door. */
  padding: clamp(5rem, 2.5rem + 11vw, 7.5rem) clamp(1.5rem, 0.75rem + 3vw, 2.5rem)
    clamp(1.75rem, 1.25rem + 2vw, 2.5rem);
  /* Warms from the grey of the wall when the lamp comes on (below). */
  transition: background-color 0.9s ease 0.2s;
}

/* On a phone the arch takes the whole width, and the plant goes. */
@media (max-width: 40rem) {
  .scene {
    padding-inline: 0;
  }

  .scene__plant {
    display: none;
  }
}

/* The lamp comes on when the reader reaches the doorway. Lit is the resting
   state, so a page without the script has its light on; the script puts the
   scene in the dark first (--off) and switches it on (--on) when it scrolls
   into view. Three things change: the shade takes its brass, the bulb and
   its beam appear with the stutter of a lamp catching, and the room behind
   the arch warms from the grey of the wall to its own colour. */

.scene__bulb {
  fill: var(--brand-glow);
}

.scene__beam {
  fill: url(#scene-beam);
}

.scene__beam-near {
  stop-color: var(--brand-glow);
  stop-opacity: 0.55;
}

.scene__beam-far {
  stop-color: var(--brand-glow);
  stop-opacity: 0;
}

/* The beam falls behind what is written in the doorway, not over it. */
.scene__room > * {
  position: relative;
  z-index: 1;
}

.scene__lamp .scene__brass {
  transition: fill 0.35s ease;
}

.scene--off .scene__lamp .scene__brass {
  fill: var(--line-strong);
}

.scene--off .scene__bulb,
.scene--off .scene__beam {
  opacity: 0;
}

.scene--off .scene__room {
  background: var(--soft);
}

.scene--on .scene__bulb {
  animation: lamp-on 0.9s ease-out backwards;
}

.scene--on .scene__beam {
  animation: lamp-on 0.9s ease-out backwards;
}

@keyframes lamp-on {
  0% { opacity: 0; }
  28% { opacity: 1; }
  44% { opacity: 0.3; }
  62%, 100% { opacity: 1; }
}

/* What is written in the doorway arrives once the light is on. */
.scene__room.reveal > *:nth-child(1) { transition-delay: 0.55s; }
.scene__room.reveal > *:nth-child(2) { transition-delay: 0.75s; }
.scene__room.reveal > *:nth-child(3) { transition-delay: 0.95s; }
.scene__room.reveal > *:nth-child(4) { transition-delay: 1.1s; }

/* The three drawings do their one thing when their benefit is reached, not
   when the page loads: the booking nobody typed in arrives, the code lands
   digit by digit and its validity runs. */
.benefits .codecard__bar i {
  animation: none;
}

@media (prefers-reduced-motion: no-preference) {
  .reveal--in .mini-rows__new {
    animation: row-in 0.5s cubic-bezier(0.2, 0.7, 0.3, 1) 0.75s backwards;
  }

  .reveal--in .mini-code__digits span {
    animation: digit-in 0.5s cubic-bezier(0.2, 0.7, 0.3, 1) backwards;
  }
  .reveal--in .mini-code__digits span:nth-child(1) { animation-delay: 0.45s; }
  .reveal--in .mini-code__digits span:nth-child(2) { animation-delay: 0.54s; }
  .reveal--in .mini-code__digits span:nth-child(3) { animation-delay: 0.63s; }
  .reveal--in .mini-code__digits span:nth-child(4) { animation-delay: 0.72s; }
  .reveal--in .mini-code__digits span:nth-child(5) { animation-delay: 0.81s; }
  .reveal--in .mini-code__digits span:nth-child(6) { animation-delay: 0.9s; }

  .reveal--in .mini-code .codecard__bar i {
    animation: bar-fill 1s cubic-bezier(0.3, 0.8, 0.4, 1) 1.05s backwards;
  }
}

@keyframes row-in {
  from {
    opacity: 0;
    transform: translateY(0.5rem);
  }
}

.scene h2 {
  max-width: 13ch;
  margin: 0 auto;
  text-align: center;
  font-size: clamp(1.4rem, 1.1rem + 1.4vw, 1.9rem);
}

/* The landing's doorway: a longer title than the pre-launch page's, one
   sentence under it, then the button and what it costs to press it. */
.scene h2:has(+ .scene__sub) {
  max-width: 17ch;
}

.scene__sub {
  max-width: 32ch;
  margin: 1rem auto 0;
  text-align: center;
  color: var(--muted);
  text-wrap: pretty;
}

.scene__cta {
  margin-top: 1.5rem;
  text-align: center;
}

.scene__trust {
  margin-top: 1rem;
  text-align: center;
  text-wrap: balance;
}

/* What happens after joining: a rail down the side with three brass marks,
   the landing page's timeline in small. */
.steps {
  list-style: none;
  margin: 1.9rem 0 0;
  padding: 0;
}

.steps li {
  position: relative;
  padding: 0 0 1.15rem 1.4rem;
  border-left: 2px solid var(--line-strong);
}

/* The rail stops at the last mark instead of running on past it. */
.steps li:last-child {
  padding-bottom: 0;
  border-left-color: transparent;
}

.steps li::before {
  content: "";
  position: absolute;
  top: 0.15rem;
  left: -0.42rem;
  width: 0.7rem;
  height: 0.7rem;
  border-radius: 50%;
  background: var(--brass-mark);
  box-shadow: 0 0 0 3px var(--brass-tint);
}

.steps__when {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--brass);
}

.steps__what {
  margin-top: 0.3rem;
  font-size: 0.95rem;
  font-weight: 600;
  line-height: 1.4;
  text-wrap: pretty;
}

/* The same form, with the site's ordinary field and its ordinary button:
   the hero's pale button is for a green ground, and this is not one. */
.scene .launch__form {
  max-width: none;
  margin-top: 1.75rem;
  /* A doorway is narrow: the field and its button stack at every width. */
  grid-template-columns: 1fr;
}

.scene .launch__form input[type="email"] {
  border-color: var(--line-strong);
}

.scene .launch__form input[type="email"]:focus {
  border-color: var(--brand);
  box-shadow: 0 0 0 3px var(--brand-tint);
}

.scene .launch__btn {
  background: var(--brand);
  color: var(--brand-ink);
}

.scene .launch__btn:hover {
  background: var(--brand-hover);
}

.scene .launch__note {
  max-width: none;
  color: var(--muted);
}

.benefits__title {
  max-width: 22ch;
  margin: 0 auto;
  text-align: center;
}

.benefits {
  list-style: none;
  margin: clamp(2.25rem, 1.5rem + 3vw, 3.5rem) 0 0;
  padding: 0;
  display: grid;
  gap: 2.25rem clamp(1.5rem, 0.5rem + 3vw, 3rem);
  text-align: center;
}

@media (min-width: 50rem) {
  .benefits {
    grid-template-columns: repeat(3, 1fr);
  }
}

/* One frame for the three, the same size, so the row reads as a set. */
.benefit__art {
  display: grid;
  place-items: center;
  height: 9.5rem;
  padding: 1rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--muted);
  text-align: left;
}

.benefit__art p {
  max-width: none;
  margin: 0;
  color: inherit;
}

.mini-tag {
  padding: 0.1rem 0.45rem;
  border-radius: 999px;
  background: var(--soft);
  color: var(--ink);
  font-size: 0.66rem;
}

.mini-rows {
  display: grid;
  gap: 0.4rem;
  width: min(100%, 13.5rem);
}

.mini-rows p {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.38rem 0.55rem;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
}

/* The newest booking: nobody typed it in. */
.mini-rows .mini-rows__new {
  border-color: var(--brand);
  background: var(--brand-tint);
  color: var(--brand);
}

.mini-code {
  width: min(100%, 13.5rem);
}

.mini-code__digits {
  display: flex;
  gap: 0.25rem;
  margin-bottom: 0.65rem;
}

.mini-code__digits span {
  flex: 1;
  padding: 0.45rem 0;
  border-radius: var(--r-sm);
  background: var(--soft);
  color: var(--ink);
  font-size: 1.15rem;
  font-weight: 500;
  line-height: 1;
  text-align: center;
}

.mini-code p {
  margin-top: 0.5rem;
  text-align: right;
}

.mini-form {
  display: grid;
  gap: 0.4rem;
  width: min(100%, 13.5rem);
}

.mini-form__flags {
  display: flex;
  gap: 0.25rem;
}

.mini-form__flags span {
  flex: 1;
  padding: 0.22rem 0;
  border-radius: var(--r-sm);
  background: var(--soft);
  font-size: 0.66rem;
  text-align: center;
}

.mini-form .mini-form__field {
  padding: 0.38rem 0.55rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  background: var(--bg);
}

.benefits h3 {
  margin-top: 1.25rem;
  font-size: 1.15rem;
}

/* Only the sentence under each title, not the text inside the drawings. */
.benefits > li > p {
  max-width: 34ch;
  margin: 0.5rem auto 0;
  color: var(--muted);
  text-wrap: pretty;
}

/* --- app pages: panels, forms ------------------------------------------- */

/* Top and bottom only: the side gutter is .wrap's, and a padding shorthand
   here would zero it on every page that is both. */
.page {
  padding-block: clamp(2.25rem, 1.5rem + 3vw, 3.75rem) clamp(3rem, 2rem + 4vw, 5rem);
}

.page__head {
  margin-bottom: 2.25rem;
}

.page__head h1 {
  font-size: clamp(1.7rem, 1.4rem + 1.3vw, 2.3rem);
}

.page__head .lede {
  margin-top: 0.6rem;
}

.narrow {
  max-width: 34rem;
  margin-inline: auto;
}

.panel {
  padding: clamp(1.5rem, 1rem + 2vw, 2.25rem);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
}

.fieldset {
  border: 0;
  margin: 0;
  padding: 0;
}

.fieldset + .fieldset {
  margin-top: 2rem;
  padding-top: 2rem;
  border-top: 1px solid var(--line);
}

.fieldset__legend {
  font-weight: 700;
  letter-spacing: -0.02em;
  font-size: 1.02rem;
}

.fieldset__help {
  margin-top: 0.3rem;
  margin-bottom: 1.2rem;
  font-size: 0.88rem;
  color: var(--muted);
  max-width: 52ch;
  text-wrap: pretty;
}

.field {
  display: block;
}

.field + .field {
  margin-top: 1.1rem;
}

.field__label {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  margin-bottom: 0.4rem;
  font-size: 0.88rem;
  font-weight: 600;
  letter-spacing: -0.005em;
}

.field__optional {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  font-weight: 400;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

.field__help {
  margin-top: 0.4rem;
  font-size: 0.83rem;
  color: var(--muted);
}

/* Two notes under one field (a calendar's sync, then what it lacks): two lines. */
.field__help + .field__help {
  display: block;
}

input,
select,
textarea {
  width: 100%;
  min-height: 2.9rem;
  padding: 0.65rem 0.8rem;
  font: inherit;
  color: var(--ink);
  background: var(--bg);
  border: 1px solid var(--line-strong);
  border-radius: var(--r);
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

/* Shown, not editable (a guest's proved email): it reads as settled. */
input[readonly] {
  color: var(--muted);
}

/* The browser's default placeholder grey is too faint to read. */
::placeholder {
  color: var(--muted);
  opacity: 1;
}

input:hover,
select:hover {
  border-color: var(--muted);
}

input:focus,
select:focus,
textarea:focus {
  outline: none;
  border-color: var(--brand);
  border-radius: var(--r); /* not the 2px of the global :focus-visible */
  box-shadow: 0 0 0 3px var(--brand-tint);
}

/* URLs, tokens and IDs are machine values — show them as such. */
.field input[type="url"],
.field input[name$="_url"],
.field input[name$="token"],
.field input[name$="_id"] {
  font-family: var(--font-mono);
  font-size: 0.88rem;
}

/* "+ Add another": a step below the last field it adds to. */
.add-more {
  margin-top: 0.75rem;
}

/* A secret and the button that shows it, side by side. */
.secret {
  display: flex;
  gap: 0.5rem;
}

.secret input {
  flex: 1;
  min-width: 0;
}

/* An eye; struck through while the value shows. Square, as tall as the input. */
.eye {
  flex: none;
  display: inline-grid;
  place-items: center;
  width: 2.9rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--r);
  background: var(--bg);
  color: var(--muted);
  cursor: pointer;
}

.eye:hover,
.eye[aria-pressed="true"] {
  border-color: var(--brand);
  color: var(--brand);
}

.eye:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
}

.eye svg {
  width: 1.25rem;
  height: 1.25rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.eye:not([aria-pressed="true"]) .eye__slash {
  display: none;
}

/* Half a phone is too narrow for a value and its eye, or a date and its
   picker: that row stacks. */
@media (max-width: 30rem) {
  .field--row:has(.secret),
  .field--row:has(input[type="date"]) {
    grid-template-columns: 1fr;
  }
}

.field--row {
  display: grid;
  gap: 1.1rem;
  grid-template-columns: 1fr 1fr;
}

.field--row .field + .field {
  margin-top: 0;
}

/* A row is fields too: what follows it keeps the same distance. */
.field--row + .field,
.field + .field--row {
  margin-top: 1.1rem;
}

.form-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem;
  margin-top: 1.75rem;
}

.note {
  padding: 0.9rem 1.05rem;
  background: var(--soft);
  border: 1px solid var(--line);
  border-radius: var(--r);
  color: var(--muted);
  font-size: 0.9rem;
  text-wrap: pretty;
}

.note--ok {
  background: var(--brand-tint);
  border-color: var(--brand);
  color: var(--brand);
}

/* A form sent back. It leads to the first thing to fix, so its link has to
   read as one on the tint. */
.note--alert {
  background: var(--alert-tint);
  border-color: var(--alert);
  color: var(--alert);
  font-weight: 600;
}

.note--alert a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

/* What is wrong with one answer, under it: before the help, in the alarm colour. */
.field__error {
  display: block;
  margin-top: 0.4rem;
  font-size: 0.88rem;
  font-weight: 600;
  color: var(--alert);
  text-wrap: pretty;
}

.field__error + .field__help {
  display: block;
}

/* It stays the alarm colour under the cursor and while it is being corrected. */
[aria-invalid="true"],
[aria-invalid="true"]:hover,
[aria-invalid="true"]:focus {
  border-color: var(--alert);
}

[aria-invalid="true"]:focus {
  box-shadow: 0 0 0 3px var(--alert-tint);
}

.alt {
  margin-top: 1.5rem;
  font-size: 0.9rem;
  color: var(--muted);
  text-align: center;
}

/* --- next arrival ------------------------------------------------------- */

.next {
  display: grid;
  gap: 1.25rem;
  align-items: center;
  grid-template-columns: 1fr;
  padding: 1.5rem;
  border: 1px solid var(--brand);
  border-radius: var(--r-lg);
  background: var(--brand-tint);
}

@media (min-width: 44rem) {
  .next {
    grid-template-columns: 1fr auto;
  }
}

.next__when {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--brand);
}

.next__who {
  margin-top: 0.5rem;
  font-size: 1.3rem;
  font-weight: 700;
  letter-spacing: -0.025em;
}

.next__meta {
  margin-top: 0.3rem;
  font-family: var(--font-mono);
  font-size: 0.82rem;
  color: var(--muted);
}

/* A booking still waiting on its check-in: present, but not competing with
   the rows that have a real name in them. */
.pending {
  font-family: var(--font-mono);
  font-size: 0.82rem;
  color: var(--muted);
}

/* --- host dashboard ----------------------------------------------------- */

.dash-section {
  margin-top: 2.5rem;
}

.dash-section h2 {
  margin-bottom: 1rem;
  font-size: 1.35rem;
}

.section-head {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem 1rem;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 1rem;
}

.section-head h2 {
  margin-bottom: 0;
}

/* The next arrivals: a row that scrolls sideways and stops card by card.
   Five cards fill a wide screen and the sixth peeks at the edge, so it shows
   there is more; on a phone each keeps a readable width and the row swipes. */
.arrivals {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(11rem, 18%);
  gap: 0.75rem;
  margin: 0;
  padding: 0 0 0.5rem;
  list-style: none;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
}

.arrival {
  display: grid;
  gap: 0.2rem;
  align-content: start;
  justify-items: start;
  padding: 0.9rem 1rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-top: 3px solid var(--line-strong);
  border-radius: var(--r);
  font-size: 0.88rem;
  scroll-snap-align: start;
}

/* Where an apartment's "no check-in" tag jumps to. */
.arrival:target {
  outline: 2px solid var(--alert);
  outline-offset: 2px;
}

.arrival--checked_in {
  border-top-color: var(--brand);
}

.arrival--soon {
  border-top-color: var(--brass-mark);
}

/* Due within 24 h and nobody has checked in: the one thing on the page that
   should be impossible to miss. */
.arrival--urgent {
  border-color: var(--alert);
  border-top-color: var(--alert);
  background: var(--alert-tint);
  animation: urgent-pulse 2s ease-in-out infinite;
}

@keyframes urgent-pulse {
  50% {
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--alert) 25%, transparent);
  }
}

.arrival__when {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
}

.arrival--urgent .arrival__when,
.arrival--urgent .arrival__state {
  color: var(--alert);
  font-weight: 700;
}

.arrival--checked_in .arrival__state {
  color: var(--brand);
}

.arrival--soon .arrival__state {
  color: var(--brass);
}

.arrival__who {
  font-size: 1rem;
  font-weight: 700;
  letter-spacing: -0.02em;
}

.arrival__apartment {
  font-weight: 500;
}

.arrival__meta {
  font-size: 0.82rem;
  color: var(--muted);
}

.arrival__state {
  margin-top: 0.3rem;
  font-size: 0.85rem;
  color: var(--muted);
}

.arrival .copy {
  margin-top: 0.4rem;
}

/* Every apartment on one line. */
.apt-table td,
.apt-table th {
  padding: 0.6rem 0.9rem;
}

.apt-table__name {
  font-weight: 600;
  color: var(--ink);
}

.apt-table .tag + .tag,
.apt-status .tag + .tag {
  margin-left: 0.3rem;
}

/* Under an apartment's name on its own page: the same tags as its row. */
.apt-status {
  margin-top: 0.7rem;
}

.dot {
  display: inline-block;
  width: 0.55rem;
  height: 0.55rem;
  margin-right: 0.55rem;
  border-radius: 50%;
  vertical-align: 0.05em;
  background: var(--brand);
}

.dot--warn {
  background: var(--brass-mark);
}

.dot--urgent {
  background: var(--alert);
  animation: urgent-pulse 2s ease-in-out infinite;
}

.apt-table__ok {
  font-size: 0.85rem;
  color: var(--muted);
}

/* Its own scroll box, headings pinned. Two classes so it outlasts the phone
   layout's overflow: visible on .table-wrap. */
.table-wrap.table-wrap--scroll {
  max-height: 32rem;
  overflow-y: auto;
}

/* As a table it shows five rows under its headings, and the sixth cut short
   says there are more. (On a phone each row is a card: the box above.) */
@media (min-width: 46.01rem) {
  .table-wrap.table-wrap--scroll {
    max-height: calc(2.75rem + 5.4 * var(--row, 2.75rem));
  }

  /* Rows whose first line has a second under it (a guest and their address). */
  .table-wrap--two-line {
    --row: 4.56rem;
  }
}

/* The quieter line under a cell's main one. */
.cell-sub {
  display: block;
  font-size: 0.82rem;
  color: var(--muted);
}

.table-wrap--scroll thead th {
  position: sticky;
  top: 0;
  z-index: 1;
}

/* A row that opens something: its link stretches over the whole row. */
tr:has(.row-link) {
  position: relative;
}

tr:has(.row-link):hover td {
  background: var(--soft);
}

.row-link {
  color: var(--ink);
}

.row-link::after {
  content: "";
  position: absolute;
  inset: 0;
}

/* One sent email's subject, and beside it what the host can do with it
   (send it again). A long subject wraps beside the button, not under it. */
.mail-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
}

.mail-head form {
  flex: none;
  margin-top: 0.15rem;
}

.mail-notice {
  margin-bottom: 1.5rem;
}

/* One sent email, as the guest read it: plain text, line breaks kept. */
.mail-meta {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 0.35rem 1rem;
  margin: 0 0 1.5rem;
  font-size: 0.9rem;
}

.mail-meta dt {
  color: var(--muted);
}

.mail-meta dd {
  margin: 0;
}

.mail-body {
  margin: 0;
  padding: 1.25rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  font-family: inherit;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

@media (prefers-reduced-motion: reduce) {
  .arrival--urgent,
  .dot--urgent {
    animation: none;
  }

  .arrivals {
    scroll-behavior: auto;
  }
}

/* --- settings ----------------------------------------------------------- */

/* Under a calendar URL: whether its last sync worked. */
.sync::before {
  content: "●";
  margin-right: 0.4rem;
  font-size: 0.7em;
  vertical-align: 0.1em;
}

.sync--ok {
  color: var(--brand);
}

.sync--failing {
  color: var(--brass);
}

.back-link {
  display: inline-block;
  margin-bottom: 1rem;
  font-size: 0.9rem;
  color: var(--muted);
}

.back-link:hover {
  color: var(--ink);
  text-decoration: none;
}

/* What a saved flat still lacks, above its form: a word, then a tag per thing,
   each a link to where it is fixed. */
.todo {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 0.6rem;
  margin-bottom: 1.5rem;
}

.todo__label {
  font-size: 0.9rem;
  font-weight: 600;
}

/* Fields that belong to one answer of the group they sit in (a lock's, a key
   box's): a step below the question, like one more field. */
.fields {
  margin-top: 1.1rem;
}

.fields > .field__help {
  display: block;
}

/* A group that can wait, folded: its legend is what opens it. */
/* The whole line is the target, a thumb tall, without moving its neighbours. */
.more > summary {
  margin-block: -0.6rem;
  padding-block: 0.6rem;
  cursor: pointer;
}

.more[open] > summary {
  margin-bottom: 0.3rem;
}

/* A flat's guest link, once per platform: whose it is, the link with that
   platform's booking code after it, and the button that copies it. */
.links {
  display: grid;
  gap: 0.9rem;
  margin: 0 0 0.9rem;
  padding: 0;
  list-style: none;
}

.links__row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 0.15rem 0.9rem;
  align-items: center;
}

.links__platform {
  grid-column: 1 / -1;
  font-size: 0.88rem;
  font-weight: 600;
}

.links__row .code {
  min-width: 0;
  font-size: 0.88rem;
  overflow-wrap: anywhere;
}

/* On a phone the link takes the whole line, its button under it. */
@media (max-width: 34rem) {
  .links__row {
    grid-template-columns: 1fr;
  }

  .links__row .copy {
    justify-self: start;
  }
}

/* What the platform fills in: not part of the link, so it doesn't read as one. */
.links__token {
  padding: 0.05rem 0.4rem;
  border: 1px dashed var(--brass-mark);
  border-radius: var(--r-sm);
  background: var(--brass-tint);
  color: var(--brass);
  font-family: var(--font-sans);
  font-size: 0.82rem;
  letter-spacing: 0;
  white-space: nowrap;
}

/* Beside a calendar's link, as tall as it. */
.remove {
  flex: none;
  padding-inline: 0.9rem;
  font-size: 0.88rem;
}

/* Settings > My properties: one row per apartment, the whole row a link. */
.apartment-list {
  margin: 0;
  padding: 0;
  list-style: none;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  overflow: hidden;
}

.apartment-list li + li {
  border-top: 1px solid var(--line);
}

.apartment-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem 1rem;
  padding: 1rem 1.25rem;
  color: var(--ink);
}

.apartment-row:hover {
  background: var(--soft);
  text-decoration: none;
}

.apartment-row__main {
  display: grid;
  flex: 1 1 12rem;
  gap: 0.15rem;
  min-width: 0;
}

.apartment-row__name {
  font-weight: 700;
  letter-spacing: -0.02em;
}

.apartment-row__meta {
  font-size: 0.85rem;
  color: var(--muted);
}

.apartment-row__go {
  flex: none;
  color: var(--muted);
}

.plan-now {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  align-items: center;
  margin-top: 0.8rem;
}

.plan-now__name {
  font-size: 1.3rem;
  font-weight: 700;
  letter-spacing: -0.02em;
}

.plans-list {
  margin: 1.25rem 0 0;
  padding: 0;
  list-style: none;
  border: 1px solid var(--line);
  border-radius: var(--r);
}

.plans-list__item {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.75rem 1rem;
}

.plans-list__item + .plans-list__item {
  border-top: 1px solid var(--line);
}

.plans-list__item--current {
  background: var(--brand-tint);
  color: var(--brand);
  font-weight: 700;
}

.plans-list__period {
  font-size: 0.85em;
  color: var(--muted);
}

/* --- tables ------------------------------------------------------------- */

.table-wrap {
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
  overflow: hidden;
}

table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.92rem;
}

th,
td {
  text-align: left;
  padding: 0.85rem 1rem;
  border-bottom: 1px solid var(--line);
  vertical-align: middle;
}

tbody tr:last-child td {
  border-bottom: 0;
}

th {
  background: var(--soft);
  color: var(--muted);
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  white-space: nowrap;
}

/* Codes, dates and addresses are data. */
.num {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.code {
  font-family: var(--font-mono);
  font-weight: 500;
  letter-spacing: 0.06em;
}

/* A guest link has nowhere to break: on a phone it would push the page wide. */
.field .code {
  overflow-wrap: anywhere;
}

.tag {
  display: inline-block;
  padding: 0.2rem 0.55rem;
  border-radius: 999px;
  border: 1px solid var(--line-strong);
  background: var(--soft);
  font-family: var(--font-mono);
  font-size: 0.76rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

.tag--sent {
  background: var(--brand-tint);
  border-color: var(--brand);
  color: var(--brand);
}

/* Can't wait: a guest due within 24 h with no check-in. */
.tag--urgent {
  background: var(--alert-tint);
  border-color: var(--alert);
  color: var(--alert);
}

/* Something the host has to act on: no calendar yet, or one that fails. */
.tag--warn {
  background: var(--brass-tint);
  border-color: var(--brass-mark);
  color: var(--brass);
  text-decoration: none;
}


.copy {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.3rem 0.65rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-mono);
  font-size: 0.75rem;
  cursor: pointer;
  white-space: nowrap;
}

.copy:hover {
  border-color: var(--brand);
  color: var(--brand);
}

.copy[data-copied="1"] {
  border-color: var(--brand);
  background: var(--brand-tint);
  color: var(--brand);
}

/* Below 46rem a five-column table stops being readable, so each row becomes
   its own labelled card. The labels come from data-label on the cells. */
@media (max-width: 46rem) {
  .table-wrap {
    border: 0;
    background: transparent;
    overflow: visible;
  }

  .table-wrap table,
  .table-wrap tbody,
  .table-wrap tr,
  .table-wrap td {
    display: block;
    width: 100%;
  }

  .table-wrap thead {
    display: none;
  }

  .table-wrap tr {
    margin-bottom: 0.85rem;
    padding: 0.4rem 1rem;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r);
  }

  .table-wrap td {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 0.55rem 0;
    border-bottom: 1px solid var(--line);
  }

  .table-wrap tr td:last-child {
    border-bottom: 0;
  }

  .table-wrap td::before {
    content: attr(data-label);
    flex: none;
    font-family: var(--font-mono);
    font-size: 0.68rem;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--muted);
  }
}

/* --- guest check-in ----------------------------------------------------- */

.checkin {
  max-width: 28rem;
  margin: 0 auto;
  padding: clamp(2rem, 1.5rem + 3vw, 3.5rem) 0 4rem;
}

.checkin__booking {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.5rem 1rem;
  margin-top: 1.25rem;
  padding: 0.85rem 1.05rem;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--soft);
  font-family: var(--font-mono);
  font-size: 0.82rem;
}

.checkin__booking dt {
  font-size: 0.75rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
}

.checkin__booking dd {
  margin: 0.15rem 0 0;
  font-weight: 500;
}

.done {
  text-align: center;
  padding: 2rem 0;
}

.done__mark {
  width: 3.5rem;
  height: 3.5rem;
  margin: 0 auto 1.5rem;
  color: var(--brand);
}

/* A checkbox reads left to right, not label-above-field like the rest: the
   box is the answer and the words are the question. */

.field--check {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 0.6rem;
  padding: 0.2rem 0; /* the whole row is the tap target, not the box */
}

.field--check input {
  width: 1.15rem;
  height: 1.15rem;
  min-height: 0;
  margin: 0;
  accent-color: var(--brand);
}

.field--check .field__label {
  margin: 0;
  font-weight: 500;
}

/* A list of boxes to tick (a person's groups, a group's apartments): under
   their own label, closer together than separate questions. */

.checks {
  margin-top: 1.1rem;
}

.checks .field--check + .field--check {
  margin-top: 0.45rem;
}

/* A long list scrolls in its own window, like the dashboard's apartments:
   the first rows show, and the last one cut in half says there are more. */

.checks__search {
  margin-bottom: 0.5rem;
}

.checks__none {
  margin: 0.4rem 0;
  font-size: 0.88rem;
  color: var(--muted);
}

.checks__list {
  max-height: 14.5rem;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 0.4rem 0.8rem;
  background: var(--soft);
  border: 1px solid var(--line);
  border-radius: var(--r);
}

/* A role is chosen from cards that say what each one means. */

.roles {
  margin: 1.1rem 0 0;
  padding: 0;
  border: 0;
}

.role {
  display: flex;
  align-items: flex-start;
  gap: 0.65rem;
  padding: 0.7rem 0.85rem;
  border: 1px solid var(--line);
  border-radius: var(--r);
  cursor: pointer;
}

.role + .role {
  margin-top: 0.45rem;
}

.role:has(input:checked) {
  border-color: var(--brand);
  background: var(--brand-tint);
}

.role input {
  flex: none;
  width: 1.1rem;
  height: 1.1rem;
  min-height: 0;
  margin: 0.15rem 0 0;
  accent-color: var(--brand);
}

.role__text {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
}

.role__name {
  font-size: 0.92rem;
  font-weight: 600;
  color: var(--ink);
}

.role__help {
  font-size: 0.84rem;
  color: var(--muted);
}

/* Which apartments: every one, or those of some groups, whose list sits
   under its own choice. */
.scope__groups {
  margin: 0.6rem 0 0 1.75rem;
}

/* Under a person's name: what they can reach today. */
.person__sees {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem 0.6rem;
  margin: -0.5rem 0 0.5rem;
  font-size: 0.88rem;
  color: var(--muted);
}

.person__sees:last-child {
  margin-bottom: 0;
}

/* The privacy policy and the terms: long text, set to be read. */
.legal {
  max-width: 42rem;
  margin-inline: auto;
  color: var(--ink);
  font-size: 1rem;
  line-height: 1.65;
  overflow-wrap: anywhere;
}

.legal h1 {
  font-size: clamp(1.7rem, 1.4rem + 1.3vw, 2.3rem);
  margin-bottom: 1.25rem;
}

.legal h2 {
  margin: 2rem 0 0.6rem;
  font-size: 1.15rem;
}

.legal p,
.legal li {
  margin: 0 0 0.75rem;
  font-size: 1rem;
}

.legal ul {
  padding-left: 1.2rem;
}

.legal__draft {
  margin-bottom: 1.5rem;
  border-color: var(--brass-mark);
  background: var(--brass-tint);
  color: var(--brass);
}

.legal__updated {
  margin-top: 2.5rem;
  font-size: 0.85rem;
  color: var(--muted);
}

/* The activity log: what happened, then who, when and from where. */
.activity {
  max-height: 36rem;
  margin: 0;
  padding: 0;
  overflow-y: auto;
  list-style: none;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
}

.activity li {
  padding: 0.75rem 1rem;
}

.activity li + li {
  border-top: 1px solid var(--line);
}

.activity__what {
  margin: 0;
  color: var(--ink);
  font-size: 0.92rem;
  overflow-wrap: anywhere;
}

.activity__meta {
  margin: 0.2rem 0 0;
  font-size: 0.8rem;
  color: var(--muted);
  overflow-wrap: anywhere;
}

/* People and management groups: one panel each, stacked. */

.panel + .panel {
  margin-top: 1rem;
}

.panel__title {
  margin-bottom: 1rem;
  font-weight: 700;
  overflow-wrap: anywhere;
}

.panel__title span {
  font-weight: 400;
  color: var(--muted);
}

/* --- guest help ---------------------------------------------------------
   One card per check, on the tinted beige rather than white: this page is a
   list of things to try, not a form, and the markers sat outside the panel. */

.help {
  list-style: none;
  margin: 1.5rem 0 0;
  padding: 0;
  display: grid;
  gap: 0.75rem;
}

.help__step {
  display: flex;
  gap: 0.9rem;
  padding: 1.05rem 1.15rem;
  background: var(--soft);
  border: 1px solid var(--line);
  border-radius: var(--r);
}

.help__num {
  flex: none;
  width: 1.6rem;
  height: 1.6rem;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--brand);
  color: var(--brand-ink);
  font-size: 0.8rem;
  font-weight: 600;
}

.help__step p {
  margin: 0.3rem 0 0;
  color: var(--muted);
  font-size: 0.92rem;
  line-height: 1.55;
}

.help__action {
  margin-top: 0.8rem;
}

/* How the "send it again" went, under its button: a note's own colours. */
.help__step .help__notice {
  margin-top: 0.8rem;
  line-height: 1.45;
}

.help__step .help__notice.note--ok {
  color: var(--brand);
}

.help__step .help__notice.note--alert {
  color: var(--alert);
}

/* --- footer ------------------------------------------------------------- */

.site-footer {
  border-top: 1px solid var(--line);
  background: var(--soft);
  padding: 3rem 0 2.5rem;
}

.site-footer__grid {
  display: grid;
  gap: 2rem;
  grid-template-columns: 1fr;
}

@media (min-width: 46rem) {
  .site-footer__grid {
    grid-template-columns: 1.6fr 1fr 1fr;
  }
}

.site-footer h2 {
  font-size: 0.78rem;
  font-family: var(--font-mono);
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 0.9rem;
}

.site-footer ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.55rem;
  font-size: 0.92rem;
}

.site-footer__tag {
  margin-top: 0.9rem;
  max-width: 30ch;
  font-size: 0.92rem;
  color: var(--muted);
  text-wrap: pretty;
}

.site-footer__base {
  margin-top: 2.5rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--line);
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.5rem;
  align-items: center;
  font-family: var(--font-mono);
  font-size: 0.76rem;
  color: var(--muted);
}

/* --- phones and touch screens -------------------------------------------
   Last, so it wins. Every field at 16px: below that, iOS zooms the whole page
   in when one is tapped (a host pasting a calendar link, a guest typing a
   document number). Every control at least 44px tall, a thumb's width, where
   a mouse was happy with less: small buttons, the arrivals' "copy link" and
   arrows, the rows of boxes to tick, the way back. */
@media (max-width: 46rem), (pointer: coarse) {
  /* !important: a floor no field may go under, however specific its own
     rule (the monospace URL and token fields set a smaller size). */
  input,
  select,
  textarea {
    font-size: 16px !important;
  }

  .btn--sm,
  .copy {
    min-height: 2.75rem;
  }

  .copy {
    padding-inline: 0.9rem;
  }

  .field--check,
  .role {
    min-height: 2.75rem;
  }

  .back-link {
    padding-block: 0.65rem;
    margin-bottom: 0.4rem;
  }

  .nav--lang .lang a {
    padding: 0.75rem 0.85rem;
  }
}
