
/* ---------------------------------------------------------------- tokens */
:root {
  /* Cool greys, deliberately not cream. Paper is a shade darker than the
     cards so the grid reads as objects laid on a surface. */
  --paper:#eceef1; --surface:#ffffff; --ink:#0e1218; --muted:#5c6673; --line:#d3d8de;
  --signal:#1b34c8; --on-signal:#ffffff; --signal-wash:#e7eaff;
  --ok:#0d6b45; --danger:#a4172c;

  --display:'Archivo',ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
  --body:'Archivo',ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
  --data:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,"Liberation Mono",monospace;

  --r:3px;              /* squared. pills and 10px cards are the default look. */
  --page:1180px;
  --gap:clamp(20px,4vw,34px);
}
/* Tells the browser to render NATIVE controls — the date picker's calendar
   icon, checkboxes, select arrows, the file button, scrollbars — in the
   scheme that is actually active. Without it, swapping the custom properties
   above only restyles what we draw ourselves: in dark mode the date input's
   picker icon stayed near-black on a near-black field and looked like it did
   not exist, and the checkboxes rendered as stark white squares. */
:root { color-scheme: light dark; }

@media (prefers-color-scheme:dark) {
  :root {
    --paper:#0d1014; --surface:#161b21; --ink:#e8ebee; --muted:#97a1ad; --line:#2a313a;
    --signal:#8ea0ff; --on-signal:#0d1014; --signal-wash:#1b2233;
    --ok:#4fbf8e; --danger:#ff8494;
  }
}

/* ------------------------------------------------------------------ base */
*,*::before,*::after { box-sizing:border-box; }
html { -webkit-text-size-adjust:100%; }
body {
  margin:0; background:var(--paper); color:var(--ink);
  font-family:var(--body); font-size:1rem; line-height:1.55;
  -webkit-font-smoothing:antialiased; overflow-x:hidden;
}
a { color:var(--signal); text-underline-offset:.16em; }
img { max-width:100%; display:block; }
:focus-visible { outline:3px solid var(--signal); outline-offset:2px; border-radius:var(--r); }
@media (prefers-reduced-motion:reduce) {
  *,*::before,*::after { transition-duration:.01ms !important; animation-duration:.01ms !important; }
}

.wrap { max-width:var(--page); margin-inline:auto; padding-inline:20px; }
main { padding:var(--gap) 0 72px; }

/* --------------------------------------------------------------- display */
/* One family throughout, separated by weight and width rather than by mixing
   a serif in. Archivo carries a width axis; headings push it wide so they
   read as signage, body stays at normal width for long German compounds. */
h1,h2,h3 {
  font-family:var(--display); font-weight:700; margin:0;
  letter-spacing:-.025em; line-height:1.08; text-wrap:balance;
  font-stretch:112%;
}
h1 { font-size:clamp(2rem,5.2vw,3.35rem); margin-bottom:14px; }
h2 { font-size:clamp(1.25rem,2.4vw,1.65rem); margin:52px 0 18px; }
h3 { font-size:1.05rem; font-stretch:100%; letter-spacing:-.01em; }
p { margin:0 0 1rem; }
.lede { color:var(--muted); font-size:1.1rem; max-width:56ch; margin:0 0 30px; }

/* Small uppercase mono, used for every label, eyebrow and unit in the UI.
   This is the thread that ties header, filters, cards and detail together. */
label,.tag,.eyebrow,.langs,.pager,.count {
  font-family:var(--data); font-size:.72rem; text-transform:uppercase; letter-spacing:.09em;
}

/* ---------------------------------------------------------- header/footer */
header.site { background:var(--surface); border-bottom:1px solid var(--line); }
header.site .wrap { display:flex; align-items:center; gap:24px; min-height:62px; }
.brand {
  font-family:var(--display); font-weight:700; font-stretch:118%;
  font-size:1.22rem; letter-spacing:-.03em; text-decoration:none; color:var(--ink);
}
.langs { margin-left:auto; display:flex; gap:3px; }
/* Codes come out of the markup lowercase (they are the hreflang values), so
   the uppercasing is presentational and belongs here. */
.langs a {
  color:var(--muted); text-decoration:none; padding:5px 8px; min-width:34px; text-align:center;
  border:1px solid transparent; border-radius:var(--r);
}
.langs a:hover { color:var(--ink); border-color:var(--line); }
.langs a[aria-current="true"] { color:var(--on-signal); background:var(--signal); border-color:var(--signal); }
footer.site {
  border-top:1px solid var(--line); padding:26px 0; color:var(--muted); font-size:.85rem;
}
.skip { position:absolute; left:-9999px; }
.skip:focus { left:12px; top:12px; background:var(--surface); padding:9px 14px; border-radius:var(--r); z-index:10; }

/* ------------------------------------------------------- ask (the hero) */
/* The natural-language box is the product, so it gets the weight a hero
   image would get elsewhere: full width, a signal spine down the left, and
   type large enough to type a sentence into without squinting. */
form.ask {
  display:flex; gap:0; flex-wrap:wrap; margin:0 0 20px;
  background:var(--surface); border:1px solid var(--line); border-left:4px solid var(--signal);
  border-radius:var(--r);
}
form.ask input[type=search] {
  flex:1 1 300px; min-width:0; padding:17px 18px; font-size:1.08rem;
  border:0; background:transparent; border-radius:0;
}
form.ask input[type=search]:focus { outline:0; box-shadow:inset 0 0 0 2px var(--signal); }
form.ask button { border-radius:0; padding:15px 26px; border:0; border-left:1px solid var(--line); }
form.ask .hint {
  flex-basis:100%; margin:0; padding:9px 18px; color:var(--muted);
  font-family:var(--data); font-size:.76rem; border-top:1px solid var(--line);
}
form.ask[aria-busy=true] { opacity:.6; }

/* ------------------------------------------- the query line (signature) */
/* Every constraint the parser inferred, shown as a formula the reader can
   take apart one term at a time. This is the whole promise of the product:
   you can always see what it thinks you asked for, and disagree. */
.interp { margin:0 0 26px; }
.interp .note {
  font-family:var(--data); font-size:.78rem; color:var(--muted); margin:0 0 10px;
  padding-left:11px; border-left:2px solid var(--danger);
}
.chips { list-style:none; display:flex; gap:7px; flex-wrap:wrap; padding:0; margin:0; align-items:center; }
.chip {
  display:inline-flex; gap:9px; align-items:center; text-decoration:none;
  font-family:var(--data); font-size:.82rem; letter-spacing:-.01em;
  background:var(--signal-wash); color:var(--signal);
  border:1px solid transparent; border-radius:var(--r); padding:6px 10px;
}
.chip span { opacity:.55; font-size:.95em; }
.chip:hover { border-color:var(--signal); }
.chip:hover span { opacity:1; }

/* --------------------------------------------------------------- filters */
form.filters {
  background:var(--surface); border:1px solid var(--line); border-radius:var(--r);
  padding:20px; margin-bottom:30px;
}
.fields { display:grid; grid-template-columns:repeat(auto-fit,minmax(158px,1fr)); gap:16px; }
label { display:block; color:var(--muted); margin-bottom:6px; }
input,select {
  width:100%; padding:9px 10px; font:inherit; font-size:.95rem;
  color:var(--ink); background:var(--paper);
  border:1px solid var(--line); border-radius:var(--r);
}
input:focus,select:focus { outline:2px solid var(--signal); outline-offset:0; border-color:var(--signal); }
.checks { display:flex; gap:20px; align-items:center; margin-top:16px; flex-wrap:wrap; }
.checks label {
  display:flex; align-items:center; gap:8px; margin:0; color:var(--ink);
  font-family:var(--body); font-size:.93rem; text-transform:none; letter-spacing:0;
}
.checks input { width:auto; accent-color:var(--signal); }
.actions { display:flex; gap:10px; margin-top:18px; }

button,.btn {
  font-family:var(--body); font-size:.95rem; font-weight:600; cursor:pointer;
  padding:10px 20px; border-radius:var(--r); text-decoration:none; display:inline-block;
  border:1px solid var(--signal); background:var(--signal); color:var(--on-signal);
}
button:hover,.btn:hover { filter:brightness(1.12); }
.btn.secondary { background:transparent; color:var(--ink); border-color:var(--line); }
.btn.secondary:hover { border-color:var(--ink); filter:none; }

/* ----------------------------------------------------------------- cards */
.count { color:var(--muted); margin:0 0 16px; }
.grid {
  display:grid; grid-template-columns:repeat(auto-fill,minmax(268px,1fr));
  gap:var(--gap); list-style:none; padding:0; margin:0;
}
.card {
  background:var(--surface); border:1px solid var(--line); border-radius:var(--r); overflow:hidden;
}
.card:hover { border-color:var(--signal); }
.card a.cardlink { text-decoration:none; display:block; color:inherit; }
.card .photo,.gallery .photo {
  aspect-ratio:4/3; background:var(--paper); display:flex; align-items:center; justify-content:center;
  color:var(--muted); font-family:var(--data); font-size:.72rem; text-align:center; padding:16px;
}
/* Photo-less cards are not an edge case here: ~44% of the catalogue has no
   image at source, and on some searches it is three cards in four. Centring an
   apology in the 4:3 field made a normal listing look like a broken one, so
   the field carries the advertiser's own description instead — left-aligned
   and set as running text, because that is what tells a reader it is content
   rather than a failed asset. --paper, not --signal-wash: a washed accent
   means "chip" in this stylesheet (see the note below), and a card is not one. */
.card blockquote.photo {
  display:block; margin:0; text-align:left; align-items:stretch;
  padding:17px 18px 17px 16px; overflow:hidden;
  font-family:var(--body); font-size:.9rem; line-height:1.5; color:var(--ink);
  border-left:3px solid var(--line);
  /* Eight lines is what it takes to fill the 4:3 at card width. Fewer left a
     visible void under the text, which reads as another kind of missing
     content — the point is a field that looks occupied, not a shorter apology.
     The server truncates as well; this catches languages that set taller than
     the character budget predicts. */
  display:-webkit-box; -webkit-line-clamp:8; -webkit-box-orient:vertical;
}
/* A card with neither photo nor description renders no field above the body at
   all — see the note in ListingCard. There is deliberately no rule here. */
/* One column: there is no neighbouring card to line up with, so a short
   description should not hold open a full 4:3 of empty field. The ratio is
   kept above this width because a ragged row of differently-sized text blocks
   costs more scanning than the whitespace does. */
@media (max-width:600px) { .card blockquote.photo { aspect-ratio:auto; } }
.card img.photo { width:100%; aspect-ratio:4/3; object-fit:cover; display:block; }
.card .body { padding:15px 16px 17px; }
.card .price {
  font-family:var(--display); font-weight:700; font-stretch:108%; font-size:1.22rem;
  letter-spacing:-.02em; font-variant-numeric:tabular-nums;
}
.card .where { color:var(--muted); font-size:.9rem; margin-top:1px; }
/* The data line. Monospace and tabular so rooms and area sit in the same
   column from card to card — that is what makes a grid scannable. */
.card .facts {
  font-family:var(--data); font-variant-numeric:tabular-nums;
  font-size:.82rem; color:var(--ink); margin-top:9px;
}
.tags { display:flex; gap:5px; margin-top:11px; flex-wrap:wrap; }
.tag {
  border:1px solid var(--line); border-radius:var(--r);
  padding:2px 7px; color:var(--muted); line-height:1.6;
}

.empty {
  background:var(--surface); border:1px solid var(--line); border-radius:var(--r);
  padding:44px 24px; text-align:center; color:var(--muted);
}
.empty p:last-child { margin:0; }
.pager {
  display:flex; gap:14px; align-items:center; justify-content:center; margin-top:46px; color:var(--muted);
}
.pager .btn { font-family:var(--body); font-size:.9rem; text-transform:none; letter-spacing:0; }

/* ---------------------------------------------------------------- detail */
.detail { display:grid; grid-template-columns:minmax(0,1.7fr) minmax(268px,1fr); gap:44px; align-items:start; }
@media (max-width:860px) { .detail { grid-template-columns:1fr; gap:28px; } }
.gallery { display:grid; gap:10px; }
.gallery img { width:100%; border-radius:var(--r); background:var(--paper); }
.sidebar {
  background:var(--surface); border:1px solid var(--line); border-top:4px solid var(--signal);
  border-radius:var(--r); padding:22px; position:sticky; top:22px;
}
.sidebar .price {
  font-family:var(--display); font-weight:700; font-stretch:112%;
  font-size:2rem; letter-spacing:-.03em; font-variant-numeric:tabular-nums; line-height:1.1;
}
.sidebar .per {
  font-family:var(--data); font-size:.72rem; text-transform:uppercase; letter-spacing:.09em;
  color:var(--muted); margin:4px 0 20px;
}
.sidebar .btn { width:100%; text-align:center; }
.note { color:var(--muted); font-size:.82rem; margin-top:12px; }

dl.facts { display:grid; grid-template-columns:auto 1fr; gap:0; margin:0 0 30px; }
dl.facts dt,dl.facts dd { padding:9px 0; border-top:1px solid var(--line); }
dl.facts dt {
  font-family:var(--data); font-size:.72rem; text-transform:uppercase; letter-spacing:.09em;
  color:var(--muted); padding-right:26px;
}
dl.facts dd { margin:0; font-size:.95rem; font-variant-numeric:tabular-nums; }
/* The map sizes off the column rather than a fixed height, so it stays a
   readable window on a phone instead of a letterbox slit. */
figure.map { margin:0 0 30px; }
figure.map iframe {
  display:block; width:100%; aspect-ratio:16/10; max-height:420px;
  border:1px solid var(--line); border-radius:var(--r); background:var(--paper);
}
@media (max-width:620px) { figure.map iframe { aspect-ratio:4/3; } }
figure.map figcaption { margin-top:9px; color:var(--muted); font-size:.82rem; }

.desc { white-space:pre-wrap; max-width:68ch; }
.banner {
  background:var(--surface); border:1px solid var(--line); border-left:4px solid var(--danger);
  border-radius:var(--r); padding:16px 18px; margin-bottom:28px;
}
.banner p:last-child { margin:0; }

/* -------------------------------------------------------- recent searches */
/* Sits below the ask box and left-aligned, reading as an aside to the form
   rather than an overlay on top of it. Outlined like a filter chip, never
   filled with --signal: a solid accent block means "button that acts" in this
   design system, and these are shortcuts back to a query, not the primary
   action. The label is what makes the row legible at all — unlabelled boxes
   of old query text are what this replaced. */
.recent {
  display:flex; align-items:center; gap:9px; flex-wrap:wrap; margin:0 0 22px;
}
.recent[hidden] { display:none; }
.recent-label {
  font-family:var(--data); font-size:.72rem; font-weight:400; text-transform:uppercase;
  letter-spacing:.09em; color:var(--muted); margin:0;
}
.recent-list { list-style:none; display:flex; gap:7px; flex-wrap:wrap; padding:0; margin:0; align-items:center; }
.recent-item {
  font-family:var(--data); font-size:.82rem; font-weight:400; letter-spacing:-.01em;
  background:var(--signal-wash); color:var(--signal);
  border:1px solid transparent; border-radius:var(--r); padding:6px 10px;
}
.recent-item:hover { border-color:var(--signal); filter:none; }
.recent-clear {
  font-family:var(--data); font-size:.76rem; font-weight:400; letter-spacing:-.01em;
  background:transparent; color:var(--muted); border:0; padding:6px 4px;
  text-decoration:underline; text-underline-offset:3px;
}
.recent-clear:hover { color:var(--ink); filter:none; }

/* ---------------------------------------------------------------- place */
/* The statistics block is the substance of a landing page, so it gets the
   card treatment the listing grid uses rather than reading as a caption. */
.stats {
  background:var(--surface); border:1px solid var(--line); border-left:4px solid var(--signal);
  border-radius:var(--r); padding:20px; margin:0 0 30px;
}
.stat { display:flex; flex-direction:column; gap:2px; margin-bottom:16px; }
.stat-label {
  font-family:var(--data); font-size:.72rem; text-transform:uppercase; letter-spacing:.09em; color:var(--muted);
}
.stat-value {
  font-family:var(--display); font-weight:700; font-stretch:112%; font-size:2rem;
  letter-spacing:-.03em; font-variant-numeric:tabular-nums; line-height:1.1;
}
dl.bands {
  display:grid; grid-template-columns:auto 1fr; gap:0; margin:0 0 14px; max-width:340px;
}
dl.bands dt, dl.bands dd { padding:7px 0; border-top:1px solid var(--line); }
dl.bands dt {
  font-family:var(--data); font-size:.72rem; text-transform:uppercase; letter-spacing:.09em;
  color:var(--muted); padding-right:24px;
}
dl.bands dd { margin:0; font-variant-numeric:tabular-nums; text-align:right; }
/* The "see all rentals in {place}" pointer on a faceted search page: it is a
   navigational aside, not part of the result copy, so it sits apart from the
   chips above it and reads at caption size. */
.place-link { margin:0 0 var(--gap); font-size:.9rem; }

/* Shared with .count/.lede: same muted-caption treatment for the honest-data
   fallback ("no listing here quotes a price yet.") and the furnished/temporary line. */
.ta-muted { color:var(--muted); }

/* ---------------------------------------------------------------- alerts */
/* The subscribe block sits directly under the chips, so it gets the same
   card treatment as .stats rather than looking like a second filter form. */
.alerts {
  background:var(--surface); border:1px solid var(--line); border-left:4px solid var(--signal);
  border-radius:var(--r); padding:20px; margin:0 0 30px;
}
.alerts h2 { margin:0 0 6px; font-size:1.05rem; font-stretch:100%; letter-spacing:-.01em; }
.alerts .lede { margin:0 0 16px; font-size:.95rem; }
.alerts .row { display:flex; gap:16px; flex-wrap:wrap; align-items:flex-end; margin-bottom:14px; }
.alerts .row > div { flex:1 1 220px; }
.alerts .cadence { display:flex; gap:18px; flex-wrap:wrap; margin-bottom:16px; }
.alerts .cadence label {
  display:flex; align-items:center; gap:8px; margin:0; color:var(--ink);
  font-family:var(--body); font-size:.93rem; text-transform:none; letter-spacing:0;
}
.alerts .cadence input { width:auto; accent-color:var(--signal); }

/* Manage page: one card per saved search, chips reused verbatim from search
   so what's being watched always matches what search would show for it. */
.watchlist { list-style:none; padding:0; margin:0 0 30px; display:flex; flex-direction:column; gap:16px; }
.watchlist li {
  background:var(--surface); border:1px solid var(--line); border-radius:var(--r); padding:18px;
}
.watchlist .meta { color:var(--muted); font-size:.85rem; margin:10px 0 14px; }
.watchlist .actions { display:flex; gap:10px; }
.watchlist .actions form { margin:0; }

/* ---------------------------------------------------------------- narrow */
@media (max-width:620px) {
  /* The ask form stacks, so the button owns a full row and the divider moves
     from its left edge to its top. */
  form.ask button { width:100%; border-left:0; border-top:1px solid var(--line); }

  h2 { margin-top:40px; }
  .sidebar { position:static; }
}

/* The geo "near you" hint. Injected after load, so it must not shift the
   search box: it sits below the form and reserves no space when absent. */
.nearby { margin: .5rem 0 0; }
.nearby[hidden] { display: none; }
.nearby-link {
  display: inline-flex; align-items: baseline; gap: .4rem;
  padding: .25rem .55rem;
  border: 1px solid var(--line); border-radius: var(--r);
  background: var(--surface); color: var(--ink);
  font-size: .875rem; text-decoration: none;
}
.nearby-link:hover { border-color: var(--signal); color: var(--signal); }
.nearby-link span { font-family: var(--data); color: var(--muted); }

/* Direct listings. Every value from an existing :root token. */
.sell-points { list-style: none; padding: 0; margin: 0 0 1.5rem; }
.sell-points li { color: var(--muted); padding: .15rem 0; }
/* A single column of 1100px-wide inputs pushes the date field's picker icon
   an inch away from the text it belongs to, which is part of why it went
   unnoticed. Form fields want a readable measure, not the full page. */
.post-form { max-width: 34rem; }
.post-form .field { margin-bottom: 1rem; }
.post-form label { display: block; font-size: .875rem; color: var(--muted); margin-bottom: .25rem; }
.post-form input[type="text"], .post-form input[type="email"], .post-form input[type="date"],
.post-form select, .post-form textarea, .relay input, .relay textarea {
  width: 100%; padding: .45rem .6rem;
  border: 1px solid var(--line); border-radius: var(--r);
  background: var(--surface); color: var(--ink); font: inherit;
}
/* The browser draws this icon at about 12px, which is a small target for
   something that is the only affordance telling you a picker exists at all.
   Scaled up and given breathing room, with a pointer cursor so it reads as
   clickable rather than decorative. */
.post-form input[type="date"]::-webkit-calendar-picker-indicator {
  width: 20px; height: 20px; padding: 2px; margin-left: 4px;
  cursor: pointer; opacity: .85;
}
.post-form input[type="date"]::-webkit-calendar-picker-indicator:hover { opacity: 1; }
/* Comfortable hit areas: 16px checkboxes are hard to hit accurately on a
   phone, and these two sit side by side. */
.post-form .checks input[type="checkbox"] { width: 18px; height: 18px; cursor: pointer; }
.post-form .checks label { display: inline-flex; align-items: center; gap: .5rem; margin-right: 1.25rem; cursor: pointer; }
.field-error { color: var(--signal); font-size: .8125rem; margin: .25rem 0 0; }
.direct-badge {
  border: 1px solid var(--line); border-left: 3px solid var(--signal);
  border-radius: var(--r); padding: .5rem .7rem; margin: 0 0 .75rem;
  font-size: .875rem; background: var(--surface);
}
.direct-badge.compact { padding: .2rem .4rem; font-size: .75rem; }
.relay { margin-top: 1rem; }
.relay .field { margin-bottom: .75rem; }
.report { margin-top: .75rem; }
.report .linkish {
  background: none; border: 0; padding: 0;
  color: var(--muted); font: inherit; text-decoration: underline; cursor: pointer;
}
.managed { list-style: none; padding: 0; }
.managed li { border-top: 1px solid var(--line); padding: .75rem 0; }
.advertise-strip { margin: 1rem 0 0; font-size: .875rem; }
.advertise-strip a { color: var(--signal); }

/* The posting CTA. Sits in the header on every browsing page because a
   would-be advertiser arrives as a reader — they are browsing flats, not
   looking for a form. margin-left:auto puts it and the language switcher
   together on the right; the switcher's own auto-margin then stops applying. */
.post-cta {
  margin-left:auto; white-space:nowrap;
  font-family:var(--data); font-size:.72rem; text-transform:uppercase; letter-spacing:.09em;
  background:var(--signal); color:#fff; text-decoration:none;
  padding:8px 14px; border-radius:var(--r);
}
.post-cta:hover { filter:brightness(1.12); }
header.site .post-cta + .langs { margin-left:12px; }

/* The home-page block. Was a one-line footnote nobody found. */
.advertise-block {
  border:1px solid var(--line); border-left:3px solid var(--signal);
  border-radius:var(--r); background:var(--surface);
  padding:18px 20px; margin:28px 0;
}
.advertise-block h2 { margin:0 0 6px; font-size:1.05rem; }
.advertise-block p { margin:0 0 12px; color:var(--muted); font-size:.9rem; }
.advertise-block .btn { display:inline-block; }
@media (max-width:520px) {
  .post-cta { padding:7px 10px; font-size:.66rem; }
  header.site .wrap { gap:10px; }
}

/* Fullscreen map page. Exists because the old "open the full map" link went to
   openstreetmap.org itself, whose welcome panel and event banners cover a
   third of the viewport with chrome we cannot dismiss from a link. Same tiles,
   our page: nothing on it but the map and a way back. */
.map-full { position:fixed; inset:0; display:flex; flex-direction:column; background:var(--paper); }
.map-full header { display:flex; align-items:center; gap:16px; padding:10px 16px; background:var(--surface); border-bottom:1px solid var(--line); }
.map-full header .brand { font-family:var(--display); font-weight:700; font-size:1.05rem; color:var(--ink); text-decoration:none; }
.map-full header .back { color:var(--signal); text-decoration:none; font-size:.875rem; }
.map-full header .osm { margin-left:auto; color:var(--muted); font-size:.75rem; }
.map-full iframe { flex:1; width:100%; border:0; }
