/*
 * RentRadar — Poziv.
 *
 * Phase 7, direction 13 of the landing studies (design/landing/13-poziv.html),
 * with the electric blue swapped for brown: this rents flats and houses, and
 * the accent should say timber and clay rather than dashboard.
 *
 * The language is the command palette — quiet warm-neutral surfaces, one brown
 * accent, monospace for anything that is a number or a machine fact, keyboard
 * affordances made visible. Restraint everywhere except the one accent, because
 * this is a page people scan while anxious about a flat.
 *
 * Two surfaces, deliberately different:
 *   - the signed-out landing (`.lp`) commits to the dark look it was designed
 *     in and ignores the theme, the way a poster does;
 *   - the app behind the login honours system / light / black from /settings,
 *     because that is where people actually spend time.
 *
 * `data-theme` is rendered server-side from a cookie in layout.tsx, so the
 * first paint is already correct and there is no flash of the wrong theme.
 */

/* Registering the type is what lets --typed animate smoothly instead of
   snapping between keyframes; an unregistered custom property is interpolated
   discretely. Used by the landing's self-typing sentence. */
@property --typed {
  syntax: "<length>";
  inherits: true;
  initial-value: 0ch;
}

/* ── Tokens ──────────────────────────────────────────────────────────────
   Every colour is OKLCH and lives here; nothing downstream writes a raw
   colour. The muted steps (--ink-3 especially) are measured values, not
   taste: each clears 4.5:1 against the surface it is used on, in both
   themes. Changing one means re-checking it. */

:root {
  color-scheme: light dark;

  --paper:      oklch(98.2% 0.006 80);
  --paper-2:    oklch(96% 0.008 78);
  --paper-3:    oklch(93% 0.010 76);
  --ink:        oklch(22% 0.014 60);
  --ink-2:      oklch(44% 0.012 60);
  --ink-3:      oklch(50% 0.012 60);
  /* --line and --line-soft are decoration: row rules and section dividers,
     which carry no meaning on their own and are exempt from the 3:1 floor.
     --line-strong is the opposite — it draws the *edge of a control* (input,
     button, toggle), which has to be identifiable, so it is held at ≥3:1
     against --paper in both themes. Using --line for an input border was the
     bug this split fixes: the field boundary sat at 1.33:1. */
  --line:       oklch(88.5% 0.008 70);
  --line-soft:  oklch(93% 0.007 72);
  --line-strong: oklch(60% 0.012 60);

  /* Brown, not blue: the product rents flats and houses. Measured, not picked —
     --accent carries white text at 7.06:1, --accent-ink reads on --accent-bg at
     7.26:1, and --accent on --paper is 6.93:1. */
  --accent:     oklch(46% 0.078 55);
  --accent-ink: oklch(42% 0.075 55);
  --accent-bg:  oklch(94% 0.030 68);
  --accent-fg:  oklch(99% 0.004 80);

  --ok:         oklch(45% 0.13 155);
  --ok-bg:      oklch(94.5% 0.045 155);
  --warn:       oklch(50% 0.13 62);
  --warn-bg:    oklch(95% 0.055 75);
  --info:       oklch(48% 0.13 250);
  --info-bg:    oklch(94.5% 0.04 250);
  --danger:     oklch(50% 0.19 25);
  --danger-bg:  oklch(95% 0.05 25);

  --shadow-1: 0 1px 2px oklch(20% 0.02 250 / .05);
  --shadow-2: 0 1px 2px oklch(20% 0.02 250 / .04), 0 10px 28px -14px oklch(20% 0.02 250 / .18);

  /* 8px grid. Everything spatial is a multiple of --sp. */
  --sp: 8px;
  --r-sm: 6px;
  --r: 10px;
  --r-lg: 16px;

  --sans: "Segoe UI Variable Text", "Segoe UI", Inter, system-ui, -apple-system, sans-serif;
  --serif: "Iowan Old Style", "Palatino Linotype", Palatino, "Book Antiqua", Georgia, serif;
  --mono: ui-monospace, "Cascadia Mono", "SF Mono", Consolas, monospace;

  --measure: 68ch;
  --page: 1180px;
}

/* Nordic Black. Written twice on purpose: one copy answers the system
   preference, the other an explicit choice, and plain CSS cannot share a
   declaration block across a media query and a bare selector. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --paper:      oklch(15% 0.006 60);
    --paper-2:    oklch(18.5% 0.007 60);
    --paper-3:    oklch(23% 0.008 60);
    --ink:        oklch(96% 0.005 70);
    --ink-2:      oklch(76% 0.008 60);
    --ink-3:      oklch(64% 0.009 60);
    --line:       oklch(30% 0.009 60);
    --line-soft:  oklch(24% 0.008 60);
    /* control edges must stay identifiable: 3.06:1 on --paper, measured */
    --line-strong: oklch(50% 0.010 60);

    --accent:     oklch(76% 0.085 68);
    --accent-ink: oklch(80% 0.080 68);
    --accent-bg:  oklch(28% 0.030 68);
    --accent-fg:  oklch(20% 0.035 68);

    --ok:         oklch(78% 0.15 155);
    --ok-bg:      oklch(24% 0.05 155);
    --warn:       oklch(80% 0.13 70);
    --warn-bg:    oklch(25% 0.055 68);
    --info:       oklch(76% 0.12 250);
    --info-bg:    oklch(24% 0.05 250);
    --danger:     oklch(74% 0.16 25);
    --danger-bg:  oklch(25% 0.07 25);

    --shadow-1: 0 1px 2px oklch(0% 0 0 / .35);
    --shadow-2: 0 1px 2px oklch(0% 0 0 / .3), 0 10px 28px -14px oklch(0% 0 0 / .6);
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;
  --paper:      oklch(15% 0.006 60);
  --paper-2:    oklch(18.5% 0.007 60);
  --paper-3:    oklch(23% 0.008 60);
  --ink:        oklch(96% 0.005 70);
  --ink-2:      oklch(76% 0.008 60);
  --ink-3:      oklch(64% 0.009 60);
  --line:       oklch(30% 0.009 60);
  --line-soft:  oklch(24% 0.008 60);
  /* This copy never declared --line-strong, so the explicit black theme
     inherited the light one (62%) and drew control edges brighter than the
     system-dark copy (50%) — same theme, two different borders. Now stated. */
  --line-strong: oklch(50% 0.010 60);

  --accent:     oklch(76% 0.085 68);
  --accent-ink: oklch(80% 0.080 68);
  --accent-bg:  oklch(28% 0.030 68);
  --accent-fg:  oklch(20% 0.035 68);

  --ok:         oklch(78% 0.15 155);
  --ok-bg:      oklch(24% 0.05 155);
  --warn:       oklch(80% 0.13 70);
  --warn-bg:    oklch(25% 0.055 68);
  --info:       oklch(76% 0.12 250);
  --info-bg:    oklch(24% 0.05 250);
  --danger:     oklch(74% 0.16 25);
  --danger-bg:  oklch(25% 0.07 25);

  --shadow-1: 0 1px 2px oklch(0% 0 0 / .35);
  --shadow-2: 0 1px 2px oklch(0% 0 0 / .3), 0 10px 28px -14px oklch(0% 0 0 / .6);
}

:root[data-theme="light"] { color-scheme: light; }

/* ── 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(--sans);
  font-size: 15px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

a { color: inherit; }

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

::selection { background: var(--accent-bg); color: var(--accent-ink); }

/* Keyboard users land here first; it stays out of the way until focused. */
.skip {
  position: absolute; left: calc(var(--sp) * 2); top: -100px; z-index: 100;
  background: var(--accent); color: var(--accent-fg);
  padding: calc(var(--sp) * 1.5) calc(var(--sp) * 2);
  border-radius: var(--r); font-weight: 600; text-decoration: none;
  transition: top .18s ease;
}
.skip:focus { top: calc(var(--sp) * 1.5); }

/* ── Header ──────────────────────────────────────────────────────────── */

.site-header {
  position: sticky; top: 0; z-index: 20;
  background: color-mix(in oklab, var(--paper) 84%, transparent);
  backdrop-filter: saturate(160%) blur(12px);
  border-bottom: 1px solid var(--line-soft);
}

.hdr-in {
  max-width: var(--page); margin: 0 auto;
  padding: 0 calc(var(--sp) * 3);
  min-height: 64px;
  display: flex; align-items: center; gap: calc(var(--sp) * 3);
  flex-wrap: wrap;
}

.brand {
  display: flex; align-items: center; gap: calc(var(--sp) * 1.25);
  text-decoration: none; font-weight: 650; letter-spacing: -.02em; font-size: 16px;
  padding: calc(var(--sp) * 1.5) 0;
}
.brand .mark {
  width: 26px; height: 26px; border-radius: 50%; flex: none;
  background:
    radial-gradient(circle at 50% 50%, var(--accent) 0 22%, transparent 23%),
    radial-gradient(circle at 50% 50%, transparent 0 44%, color-mix(in oklab, var(--accent) 45%, transparent) 45% 50%, transparent 51%),
    radial-gradient(circle at 50% 50%, transparent 0 74%, color-mix(in oklab, var(--accent) 25%, transparent) 75% 80%, transparent 81%);
}

.nav { display: flex; gap: calc(var(--sp) * .5); margin-right: auto; flex-wrap: wrap; }
.nav a {
  text-decoration: none; color: var(--ink-2); font-size: 14px; font-weight: 500;
  padding: calc(var(--sp) * .875) calc(var(--sp) * 1.5);
  border-radius: 999px;
  transition: background .18s ease, color .18s ease;
  white-space: nowrap;
}
.nav a:hover { background: var(--paper-2); color: var(--ink); }
.nav a[aria-current="page"] { color: var(--accent-ink); background: var(--accent-bg); font-weight: 600; }

.hdr-end { display: flex; align-items: center; gap: calc(var(--sp) * 1.5); flex-wrap: wrap; }
.who { font-size: 13px; color: var(--ink-3); }

/* Language flags. Buttons, not links: switching locale is a state change on
   this browser, and it must work without JavaScript. */
.flags { display: flex; gap: calc(var(--sp) * .5); }
.flag {
  display: grid; place-items: center;
  width: 36px; height: 26px; padding: 0;
  border: 1px solid var(--line-strong); border-radius: 4px;
  background: transparent; cursor: pointer; overflow: hidden;
  transition: border-color .18s ease, transform .14s ease, opacity .18s ease;
  opacity: .65;
}
.flag:hover { opacity: 1; transform: translateY(-1px); }
.flag svg { display: block; width: 30px; height: 22px; }
.flag[aria-pressed="true"] {
  opacity: 1; border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent-bg);
}
/* On a touch screen the button grows to the 44×44 minimum while the flag
   itself stays the same size, rather than overlapping its neighbour's hit
   area — two 44px targets 4px apart do not fit at the drawn size. */
@media (pointer: coarse) {
  .flags { gap: var(--sp); }
  .flag { width: 44px; height: 44px; }
  /* .btn-sm is 29px tall, which is fine for a mouse and not for a thumb. */
  .btn { min-height: 44px; }
}

/* ── Layout ──────────────────────────────────────────────────────────── */

.site-main {
  max-width: var(--page); margin: 0 auto;
  padding: calc(var(--sp) * 6) calc(var(--sp) * 3) calc(var(--sp) * 12);
}

.site-footer {
  max-width: var(--page); margin: 0 auto;
  padding: calc(var(--sp) * 3);
  border-top: 1px solid var(--line-soft);
  display: flex; justify-content: space-between; gap: calc(var(--sp) * 2);
  flex-wrap: wrap;
  font-size: 12.5px; color: var(--ink-3);
}

.cols {
  display: grid; grid-template-columns: minmax(0, 1fr) 316px;
  gap: calc(var(--sp) * 7); align-items: start;
}

/* ── Typography ──────────────────────────────────────────────────────── */

h1 {
  font-family: var(--serif); font-weight: 400; letter-spacing: -.022em;
  font-size: clamp(30px, 4.2vw, 44px); line-height: 1.12;
  margin: 0 0 var(--sp);
}
h2 {
  font-size: 13px; letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink-3); font-weight: 650; margin: calc(var(--sp) * 5) 0 0;
}
h3 {
  font-family: var(--serif); font-weight: 400; font-size: 20px;
  letter-spacing: -.015em; margin: calc(var(--sp) * 3) 0 var(--sp);
}

.page-head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: calc(var(--sp) * 4); flex-wrap: wrap;
  margin-bottom: calc(var(--sp) * 4);
}
.lead { color: var(--ink-2); margin: 0; max-width: var(--measure); }
.muted { color: var(--ink-3); font-size: 13px; }
.err { color: var(--danger); }

.section-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: calc(var(--sp) * 2);
  padding-bottom: calc(var(--sp) * 1.75);
  border-bottom: 1px solid var(--line);
  margin-bottom: calc(var(--sp) * .5);
}
.section-head h2 { margin: 0; }
.section-head h3 { margin: 0; }
.section-head p { margin: 0; font-size: 13px; color: var(--ink-3); }

code, .mono { font-family: var(--mono); font-size: .9em; }
code {
  background: var(--paper-2); border: 1px solid var(--line-soft);
  border-radius: var(--r-sm); padding: 1px 5px;
}

/* ── Live indicator ──────────────────────────────────────────────────── */

.live {
  display: inline-flex; align-items: center; gap: var(--sp);
  font-size: 13px; font-weight: 550;
  color: var(--ok); background: var(--ok-bg);
  padding: calc(var(--sp) * .75) calc(var(--sp) * 1.75);
  border-radius: 999px;
  /* nowrap keeps "v živo" on one line, but the crawl-health variant carries a
     whole sentence ("Zadnji nov oglas: … · skupaj 96 oglasov") and pushed the
     admin page 38px wider than a 390px phone. Wrap when there is no room. */
  white-space: nowrap; max-width: 100%; flex-wrap: wrap;
}
@media (max-width: 560px) { .live { white-space: normal; border-radius: var(--r); } }
.live.is-lost { color: var(--warn); background: var(--warn-bg); }
.live.is-connecting { color: var(--ink-3); background: var(--paper-2); }
.live .dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; position: relative; flex: none; }
.live.is-live .dot::after {
  content: ""; position: absolute; inset: -4px; border-radius: 50%;
  border: 1px solid currentColor; opacity: .5;
  animation: ring 2.4s ease-out infinite;
}
@keyframes ring {
  from { transform: scale(.6); opacity: .6; }
  to { transform: scale(1.5); opacity: 0; }
}

/* ── Stats strip ─────────────────────────────────────────────────────── */

.stats {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 1px; background: var(--line-soft);
  border-block: 1px solid var(--line-soft);
  margin: calc(var(--sp) * 5) 0 0;
}
.stat { background: var(--paper); padding: calc(var(--sp) * 2.5) calc(var(--sp) * 3); }
.stat dt {
  font-size: 12px; letter-spacing: .04em; text-transform: uppercase;
  color: var(--ink-3); margin: 0 0 calc(var(--sp) * .75); font-weight: 600;
}
.stat dd {
  margin: 0; font-family: var(--serif); font-size: 30px; line-height: 1;
  letter-spacing: -.02em;
}
.stat dd small {
  font-family: var(--sans); font-size: 13px; color: var(--ink-3);
  letter-spacing: 0; margin-left: 4px;
}
.stat .trend { font-size: 12px; color: var(--ink-3); margin: var(--sp) 0 0; display: block; }

/* ── Feed ────────────────────────────────────────────────────────────── */

.feed { list-style: none; margin: 0; padding: 0; }
.feed-item {
  display: grid; grid-template-columns: 108px minmax(0, 1fr) auto;
  gap: calc(var(--sp) * 2.5); align-items: center;
  padding: calc(var(--sp) * 2.25) calc(var(--sp) * 1.5) calc(var(--sp) * 2.25) 0;
  border-bottom: 1px solid var(--line-soft);
  transition: background .2s ease;
}
.feed-item:hover { background: var(--paper-2); }

.thumb {
  position: relative; width: 108px; height: 76px; flex: none;
  border-radius: var(--r-sm); overflow: hidden; display: block;
  box-shadow: inset 0 0 0 1px oklch(50% 0.02 60 / .12);
}
/* In the feed the icon fills the thumbnail rather than sitting in its own
   44px tile, so the badges keep somewhere to land. */
.thumb .picon { width: 100%; height: 100%; border-radius: var(--r-sm); border: 0; }
.thumb .picon svg { width: 40px; height: 40px; }
.thumb-badge {
  position: absolute; top: 5px; left: 5px;
  font-size: 10.5px; font-weight: 650; letter-spacing: .015em;
  padding: 2px 7px; border-radius: 999px;
  background: var(--paper); color: var(--ok);
  box-shadow: 0 1px 3px oklch(20% 0.02 250 / .2);
}
.thumb-badge.is-repost { color: var(--warn); }
.thumb-badge.is-bumped { color: var(--info); }
.thumb-count {
  position: absolute; bottom: 5px; right: 5px;
  font-size: 10px; font-weight: 650; padding: 1px 6px; border-radius: 999px;
  background: var(--paper); color: var(--ink-2);
  box-shadow: 0 1px 3px oklch(20% 0.02 250 / .2);
}

.title-link {
  font-size: 16px; font-weight: 600; letter-spacing: -.011em;
  text-decoration: none; display: inline-block;
}
.title-link:hover { color: var(--accent-ink); text-decoration: underline; text-underline-offset: 3px; }

.meta {
  margin: calc(var(--sp) * .625) 0 0; font-size: 13px; color: var(--ink-2);
  display: flex; flex-wrap: wrap; gap: calc(var(--sp) * .75) calc(var(--sp) * 1.25);
  align-items: center;
}
.meta .sep { color: var(--ink-3); opacity: .5; }
.meta time { font-variant-numeric: tabular-nums; color: var(--ink-3); }

.price { text-align: right; white-space: nowrap; }
/* Mono, not serif: in this direction anything that is a number or a machine
   fact is monospaced, which also keeps the rent column optically aligned. */
.price b { font-family: var(--mono); font-size: 19px; font-weight: 700; letter-spacing: -.03em; }
.price span { display: block; font-size: 12.5px; color: var(--ink-3); margin-top: 3px; }
.price .trend-row {
  display: flex; align-items: center; justify-content: flex-end;
  gap: calc(var(--sp) * .75); margin-top: calc(var(--sp) * .5);
}

/* ── Property icons ──────────────────────────────────────────────────────
   One glyph per property type (PropertyIcon.tsx), replacing the painted
   scenes. At thumbnail size a drawn landscape turns to mush, whereas a pitched
   roof, a roller door or a shop awning says "house" / "garage" / "commercial"
   instantly — which is the only job an icon this size has. Photographs from
   the listings are still never shown (PLAN.md §0); this is our own geometry.

   Strokes are currentColor, so one rule themes the whole set. Each type sets
   only `--picon-h`; the chroma is held low on purpose, so eight recognisable
   tints still read as earth tones next to the brown accent rather than as a
   paint chart. */

.picon {
  --picon-h: 60;
  display: grid; place-items: center; flex: none;
  width: 44px; height: 44px; border-radius: 11px;
  background: oklch(93% 0.030 var(--picon-h));
  color: oklch(38% 0.070 var(--picon-h));
  border: 1px solid oklch(86% 0.038 var(--picon-h));
}
.picon svg { width: 24px; height: 24px; display: block; }
.picon .s { fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.picon .f { fill: currentColor; stroke: none; }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .picon {
    background: oklch(27% 0.028 var(--picon-h));
    color: oklch(82% 0.065 var(--picon-h));
    border-color: oklch(35% 0.032 var(--picon-h));
  }
}
:root[data-theme="dark"] .picon {
  background: oklch(27% 0.028 var(--picon-h));
  color: oklch(82% 0.065 var(--picon-h));
  border-color: oklch(35% 0.032 var(--picon-h));
}

.picon-stanovanje       { --picon-h: 250; }
.picon-hisa             { --picon-h: 145; }
.picon-vikend           { --picon-h: 195; }
.picon-poslovni-prostor { --picon-h: 300; }
.picon-garaza           { --picon-h: 265; }
.picon-posest           { --picon-h: 125; }
.picon-kmetijsko        { --picon-h: 85; }
.picon-drugo            { --picon-h: 40; }

/* ── Pills and states ────────────────────────────────────────────────── */

.pill {
  font-size: 11.5px; font-weight: 600; padding: 2px 9px; border-radius: 999px;
  background: var(--accent-bg); color: var(--accent-ink); white-space: nowrap;
}
.pill.is-neutral { background: var(--paper-3); color: var(--ink-2); }
.pill.is-ok { background: var(--ok-bg); color: var(--ok); }
.pill.is-warn { background: var(--warn-bg); color: var(--warn); }
.pill.is-danger { background: var(--danger-bg); color: var(--danger); }

.state {
  font-size: 12px; font-weight: 600; display: inline-flex; align-items: center;
  gap: calc(var(--sp) * .625); color: var(--ok);
}
.state.is-off { color: var(--ink-3); }
.state i { width: 6px; height: 6px; border-radius: 50%; background: currentColor; display: block; }

/* ── Tables ──────────────────────────────────────────────────────────── */

.table-wrap { overflow-x: auto; margin-top: calc(var(--sp) * 2); }
table { border-collapse: collapse; width: 100%; }
thead th {
  text-align: left; font-size: 12px; letter-spacing: .05em; text-transform: uppercase;
  color: var(--ink-3); font-weight: 650;
  padding: 0 calc(var(--sp) * 1.5) calc(var(--sp) * 1.25);
  border-bottom: 1px solid var(--line); white-space: nowrap;
}
tbody td, tbody th {
  text-align: left; vertical-align: top; font-weight: inherit;
  padding: calc(var(--sp) * 1.5);
  border-bottom: 1px solid var(--line-soft);
}
tbody th { color: var(--ink-3); font-weight: 600; white-space: nowrap; }
tbody tr:hover { background: var(--paper-2); }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }
/* First and last cells sit flush with the section rules above them. */
thead th:first-child, tbody td:first-child, tbody th:first-child { padding-left: 0; }
thead th:last-child, tbody td:last-child { padding-right: 0; }

/* ── Panels, notices, empty states ───────────────────────────────────── */

.panel {
  border: 1px solid var(--line-soft); border-radius: var(--r-lg);
  padding: calc(var(--sp) * 3); background: var(--paper);
  box-shadow: var(--shadow-1);
}

.notice {
  border-radius: var(--r); background: var(--paper-2);
  border: 1px solid var(--line-soft);
  padding: calc(var(--sp) * 2);
  font-size: 13px; color: var(--ink-2); line-height: 1.55;
  margin: calc(var(--sp) * 2) 0 0;
}
.notice b { color: var(--ink); }
.notice.is-warn { background: var(--warn-bg); border-color: transparent; color: var(--warn); }
.notice.is-danger { background: var(--danger-bg); border-color: transparent; color: var(--danger); }

.empty {
  border: 1px dashed var(--line); border-radius: var(--r-lg);
  padding: calc(var(--sp) * 6) calc(var(--sp) * 3);
  text-align: center; margin-top: calc(var(--sp) * 3);
}
.empty p { margin: 0 auto; max-width: 46ch; color: var(--ink-2); }
.empty p + p { margin-top: var(--sp); }

/* ── Buttons ─────────────────────────────────────────────────────────── */

.btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--sp);
  font: inherit; font-size: 14px; font-weight: 600; line-height: 1.2;
  padding: calc(var(--sp) * 1.25) calc(var(--sp) * 2);
  border-radius: var(--r); border: 1px solid var(--line-strong);
  background: var(--paper); color: var(--ink);
  cursor: pointer; text-decoration: none; white-space: nowrap;
  transition: border-color .18s ease, background .18s ease, transform .14s ease, color .18s ease;
}
.btn:hover { border-color: var(--ink-3); }
.btn:active { transform: translateY(1px); }
.btn[disabled] { opacity: .55; cursor: not-allowed; }

.btn-primary {
  background: var(--accent); color: var(--accent-fg); border-color: transparent;
  box-shadow: var(--shadow-1);
}
.btn-primary:hover { filter: brightness(1.08); border-color: transparent; }

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

.btn-danger { color: var(--danger); border-color: var(--danger-bg); background: var(--danger-bg); }
.btn-danger:hover { border-color: var(--danger); }

.btn-sm { font-size: 12.5px; padding: calc(var(--sp) * .75) calc(var(--sp) * 1.25); }

.btn-link {
  background: none; border: 0; padding: 0; font: inherit; color: var(--accent-ink);
  font-weight: 600; cursor: pointer; text-decoration: none;
}
.btn-link:hover { text-decoration: underline; text-underline-offset: 3px; }

.actions { display: flex; gap: var(--sp); align-items: center; flex-wrap: wrap; margin-top: calc(var(--sp) * 3); }

/* ── Forms ───────────────────────────────────────────────────────────── */

fieldset {
  border: 0; border-top: 1px solid var(--line-soft);
  margin: 0; padding: calc(var(--sp) * 3) 0;
}
fieldset:first-of-type { border-top: 0; padding-top: 0; }
legend {
  font-size: 13px; letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink-3); font-weight: 650; padding: 0;
}

label { display: inline-block; font-size: 13.5px; color: var(--ink-2); }
label.check {
  display: inline-flex; align-items: center; gap: calc(var(--sp) * .875);
  cursor: pointer; padding: calc(var(--sp) * .5) 0;
}
label.check:hover { color: var(--ink); }

input[type="text"], input[type="email"], input[type="password"], input[type="number"], select {
  font: inherit; font-size: 14px;
  color: var(--ink); background: var(--paper);
  border: 1px solid var(--line-strong); border-radius: var(--r-sm);
  padding: calc(var(--sp) * 1.125) calc(var(--sp) * 1.5);
  width: 100%; max-width: 100%;
  transition: border-color .18s ease, box-shadow .18s ease;
}
input:hover, select:hover { border-color: var(--ink-3); }
input:focus-visible, select:focus-visible {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-bg);
}
input[readonly] { background: var(--paper-2); color: var(--ink-2); }
input::placeholder { color: var(--ink-3); opacity: .8; }
input[type="checkbox"] { accent-color: var(--accent); width: 16px; height: 16px; flex: none; }

.field { display: flex; flex-direction: column; gap: calc(var(--sp) * .75); }
.field > span { font-size: 12.5px; color: var(--ink-3); font-weight: 600; }
.field-sm { max-width: 8.5rem; }
.field-md { max-width: 18rem; }
.field-lg { max-width: 26rem; }

.row {
  display: flex; gap: calc(var(--sp) * 2); align-items: flex-end; flex-wrap: wrap;
  margin-top: calc(var(--sp) * 1.5);
}
.grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: calc(var(--sp) * .5) calc(var(--sp) * 2);
  margin-top: calc(var(--sp) * 1.5);
}
.hint { font-size: 12.5px; color: var(--ink-3); margin: calc(var(--sp) * 1.5) 0 0; max-width: var(--measure); }

/* ── Keyword field with suggestions ──────────────────────────────────────
   A popover rather than an inline block: the two keyword fields sit side by
   side in a `.row`, and a list that pushed the layout down would move the other
   field every time you typed a letter. Absolute, so only the paper moves. */

.kw { position: relative; max-width: 18rem; flex: 1 1 18rem; }
.kw .field { max-width: none; }
.kw-pop {
  position: absolute; z-index: 20; top: calc(100% + 6px); left: 0; right: 0;
  background: var(--paper-2); border: 1px solid var(--line);
  border-radius: 10px; box-shadow: var(--shadow-2);
  padding: calc(var(--sp) * 1.25);
}
.kw-head {
  display: flex; flex-direction: column; gap: 2px;
  margin: 0 0 calc(var(--sp) * 1.25);
  font-size: 11.5px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase;
  color: var(--ink-3);
}
.kw-head span { font-weight: 400; letter-spacing: 0; text-transform: none; font-size: 12px; }
.kw-list {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: calc(var(--sp) * .75);
}
/* Chips, matching the ones the landing resolves the demo sentence into — the
   same gesture on both sides of the login. */
.kw-opt {
  font: inherit; font-size: 12.5px; cursor: pointer;
  background: var(--paper-3); color: var(--ink-2);
  border: 1px solid var(--line); border-radius: 999px;
  padding: 4px 11px;
}
.kw-opt:hover { border-color: var(--line-strong); color: var(--ink); }
/* Keyboard highlight has to read as strongly as hover does, since it is the
   one the arrow keys move and Enter takes. */
.kw-opt.is-active {
  background: var(--accent-bg); border-color: var(--accent); color: var(--accent-ink);
}
.kw-empty { margin: 0; font-size: 12.5px; color: var(--ink-3); }

/* The name field is the wide one, and its suggestions carry a category badge,
   so its popover follows the field rather than the 18rem keyword column. */
.kw-wide { max-width: 26rem; flex: none; }
.kw-opt-badged { display: inline-flex; align-items: baseline; gap: 7px; }
.kw-opt-badged b {
  font-family: var(--mono, ui-monospace, monospace);
  font-size: 10.5px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink-3);
}
.kw-opt-badged.is-active b { color: var(--accent-ink); }

/* ── Choice cards (settings) ─────────────────────────────────────────── */

.choices {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: calc(var(--sp) * 1.5); margin-top: calc(var(--sp) * 2);
}
.choice {
  display: flex; flex-direction: column; gap: calc(var(--sp) * .5);
  align-items: flex-start; text-align: left;
  padding: calc(var(--sp) * 2); border: 1px solid var(--line-strong);
  border-radius: var(--r-lg); background: var(--paper);
  font: inherit; cursor: pointer; width: 100%;
  transition: border-color .18s ease, background .18s ease, transform .14s ease;
}
.choice:hover { border-color: var(--ink-3); transform: translateY(-1px); }
.choice[aria-pressed="true"] {
  border-color: var(--accent); background: var(--accent-bg);
}
.choice .choice-name { font-size: 15px; font-weight: 620; color: var(--ink); }
.choice[aria-pressed="true"] .choice-name { color: var(--accent-ink); }
.choice .choice-hint { font-size: 12.5px; color: var(--ink-3); }
/* On a selected card the surface is --accent-bg, not --paper, and the muted
   step lands at 4.35:1 there in the black theme. Selected also wants slightly
   more presence, so the hint steps up rather than the token being loosened. */
.choice[aria-pressed="true"] .choice-hint { color: var(--ink-2); }
.choice .choice-mark { font-size: 11.5px; font-weight: 650; color: var(--accent-ink); }

/* A miniature of each theme, so the choice is visible rather than described. */
.swatch {
  display: flex; width: 100%; height: 34px; border-radius: var(--r-sm);
  overflow: hidden; border: 1px solid var(--line); margin-bottom: calc(var(--sp) * .5);
}
.swatch i { display: block; height: 100%; }

/* ── Alarm list (sidebar) ────────────────────────────────────────────── */

.alarm {
  display: block; text-decoration: none;
  padding: calc(var(--sp) * 2) 0;
  border-bottom: 1px solid var(--line-soft);
}
.alarm-top { display: flex; align-items: center; justify-content: space-between; gap: calc(var(--sp) * 1.5); }
.alarm-name { font-weight: 600; font-size: 14.5px; letter-spacing: -.008em; }
.alarm:hover .alarm-name { color: var(--accent-ink); }
.alarm-crit { font-size: 12.5px; color: var(--ink-2); margin: calc(var(--sp) * .75) 0 0; line-height: 1.5; }
.alarm-sent { font-size: 12px; color: var(--ink-3); margin: calc(var(--sp) * .75) 0 0; font-variant-numeric: tabular-nums; }

/* ── Auth pages ──────────────────────────────────────────────────────── */

/* Login, signup and reset are single-purpose pages; a narrow column keeps the
   eye on the one form rather than the full-width page chrome. */
.auth-page { max-width: 26rem; margin: 0 auto; padding-top: calc(var(--sp) * 4); }
.auth-page h1 { font-size: clamp(26px, 4vw, 34px); }
.auth-form { display: grid; gap: calc(var(--sp) * 2); margin-top: calc(var(--sp) * 4); }
.auth-form .field { max-width: none; }
.auth-form .actions { margin-top: 0; }
.auth-form .btn { width: 100%; }

/* ── Side panels (feed sidebar) ──────────────────────────────────────── */

/* Stacked, not side by side: at 316px each panel is already narrow, and the
   alarms list has no fixed height — putting them in a row would leave one of
   them padded out with empty space. */
.side-stack { display: grid; gap: calc(var(--sp) * 5); align-content: start; }
.side-panel > .section-head { margin-bottom: 0; }
.side-panel .hint { margin-top: calc(var(--sp) * 2); }
.panel-cta { width: 100%; margin-top: calc(var(--sp) * 2.5); }
.side-panel .alarm:last-of-type { border-bottom: 0; }

/* ── Hourly histogram ────────────────────────────────────────────────── */

.hours {
  display: flex; align-items: flex-end; gap: 2px;
  height: 76px; margin-top: calc(var(--sp) * 2.5);
}
.hour {
  flex: 1; display: block; min-width: 0;
  background: var(--line);
  border-radius: 2px 2px 0 0;
  transition: background .18s ease;
}
.hour:hover { background: var(--ink-3); }
/* The busiest two hours are the point of the chart, so they are the only
   thing that carries colour. */
.hour.is-peak { background: var(--accent); }
.hour.is-peak:hover { background: var(--accent-ink); }

.hours-axis {
  display: flex; justify-content: space-between;
  margin-top: calc(var(--sp) * .75);
  font-size: 11px; font-variant-numeric: tabular-nums; color: var(--ink-3);
}

/* ── Paywall ─────────────────────────────────────────────────────────────
   The blur is the visual half only. Nothing real is rendered behind it — the
   pages never query listings or filters without an entitlement — so this is a
   skeleton being blurred, not data. Worth stating loudly, because a blur over
   real rows looks identical and is not a paywall at all. */

.locked { position: relative; margin-top: calc(var(--sp) * 3); }
.locked-behind {
  filter: blur(7px) saturate(.55);
  opacity: .55;
  pointer-events: none;
  user-select: none;
  /* Contain the blur so it does not bleed over neighbouring sections. */
  overflow: hidden;
  min-height: 320px;
}
.locked-card {
  position: absolute; inset: 0; margin: auto;
  width: min(440px, calc(100% - var(--sp) * 4)); height: fit-content;
  display: flex; flex-direction: column; align-items: center; text-align: center;
  gap: calc(var(--sp) * 1.5);
  padding: calc(var(--sp) * 4);
  background: color-mix(in oklab, var(--paper) 88%, transparent);
  backdrop-filter: blur(6px) saturate(140%);
  border: 1px solid var(--line); border-radius: var(--r-lg);
  box-shadow: var(--shadow-2);
}
.locked-card h2 { margin: 0; font-size: 19px; font-weight: 660; letter-spacing: -.02em; }
.locked-card p { margin: 0; font-size: 14.5px; color: var(--ink-2); max-width: 40ch; }
.locked-note { font-size: 12.5px; color: var(--ink-3); }
.locked-icon {
  width: 38px; height: 38px; border-radius: 50%;
  background: var(--accent-bg); position: relative; flex: none;
}
/* A padlock, drawn rather than shipped as an icon font. */
.locked-icon::before {
  content: ""; position: absolute; left: 11px; top: 17px; width: 16px; height: 12px;
  border-radius: 2px; background: var(--accent-ink);
}
.locked-icon::after {
  content: ""; position: absolute; left: 14px; top: 10px; width: 10px; height: 9px;
  border: 2px solid var(--accent-ink); border-bottom: 0;
  border-radius: 5px 5px 0 0;
}

/* Skeleton rows: shaped like the real thing, carrying none of its content. */
.skel { pointer-events: none; }
.skel-line {
  display: block; height: 12px; border-radius: 999px;
  background: var(--paper-3); margin: 6px 0;
}
.skel-line.skel-sm { height: 9px; opacity: .75; }
.skel-line.skel-price { width: 74px; height: 16px; margin-left: auto; }
.skel-box { background: var(--paper-3); border-radius: var(--r-sm); }

/* ── Billing ─────────────────────────────────────────────────────────────*/

.billing-box {
  display: grid; grid-template-columns: auto 1fr auto;
  gap: calc(var(--sp) * 3); align-items: center;
  padding: calc(var(--sp) * 3);
  border: 1px solid var(--line-soft); border-radius: var(--r-lg);
  background: var(--paper-2);
  margin-top: calc(var(--sp) * 2);
}
.billing-price { display: flex; align-items: baseline; gap: calc(var(--sp) * .75); }
.billing-price b {
  font-family: var(--mono); font-size: clamp(24px, 3vw, 32px);
  font-weight: 700; letter-spacing: -.04em; color: var(--ink);
}
.billing-price span { font-size: 13px; color: var(--ink-3); }
.billing-meta { min-width: 0; }
.billing-note { margin: 0; font-size: 14px; color: var(--ink-2); }
.billing-meta .hint { margin-top: calc(var(--sp) * .5); }
.billing-meta .notice { margin-top: calc(var(--sp) * 1.5); }
.billing-actions { display: flex; gap: var(--sp); align-items: center; flex-wrap: wrap; }

@media (max-width: 720px) {
  .billing-box { grid-template-columns: 1fr; gap: calc(var(--sp) * 2); }
  .billing-actions { width: 100%; }
  .billing-actions .btn { width: 100%; }
  .locked-card { padding: calc(var(--sp) * 3); }
}

/* ── Landing, signed out ─────────────────────────────────────────────────
   The one surface that ignores the theme. A landing page is an impression and
   it was designed dark; letting a light system preference repaint it would
   hand back a page nobody designed. The override sits on <body> so the header
   and footer come along, and it only exists while there is no session. */

body.is-landing {
  color-scheme: dark;
  /* The room is placed against the document rather than the viewport, so it
     scrolls away with the page instead of following it. This is what makes
     that possible. */
  position: relative;
  /* One number, because two things depend on it and they must not drift: the
     height of the scene, and how far down the page the content starts. */
  /* 36.2vw is the artwork's own aspect ratio (1440×520), and it is left
     uncapped on purpose: any cap makes the band shorter than the drawing at
     wide widths and the foot of it — floor, rug, vacuum — is cropped away.
     The scene is meant to be seen whole at every width. */
  --room-h: 36.2vw;
  /* How far into the scene the page starts. 1 = not at all: the drawing
     finishes, then the page begins on flat --paper below it. Nothing is
     painted over the room. */
  --room-enter: 1;
  /* …and then the interface is pulled back up by this much, into the scene. */
  --room-lift: 900px;
  --paper:      oklch(15% 0.006 60);
  --paper-2:    oklch(18.5% 0.007 60);
  --paper-3:    oklch(23% 0.008 60);
  --ink:        oklch(96% 0.005 70);
  --ink-2:      oklch(76% 0.008 60);
  --ink-3:      oklch(64% 0.009 60);
  --line:       oklch(30% 0.009 60);
  --line-soft:  oklch(24% 0.008 60);
  --line-strong: oklch(50% 0.010 60);
  --accent:     oklch(76% 0.085 68);
  --accent-ink: oklch(80% 0.080 68);
  --accent-bg:  oklch(28% 0.030 68);
  --accent-fg:  oklch(20% 0.035 68);
  --ok:         oklch(80% 0.155 152);
  --ok-bg:      oklch(26% 0.05 152);
  --warn:       oklch(82% 0.140 72);
  --warn-bg:    oklch(27% 0.055 68);
  --info:       oklch(78% 0.10 250);
  --info-bg:    oklch(26% 0.05 250);
  --shadow-1: 0 1px 2px oklch(0% 0 0 / .35);
  --shadow-2: 0 1px 2px oklch(0% 0 0 / .3), 0 10px 28px -14px oklch(0% 0 0 / .6);
}
/* Signed out there is no nav, so the brand needs something to push against. */
.nav-spacer { margin-right: auto; }
/* Content starts in the lower part of the scene — over the floor, which is
   flat and dark — and never in the lit upper part. Measured overlap is the
   reason: with the band simply behind the page, the hero headline landed on
   the bulb and the pool of light under the shade. The lamp is the one thing in
   this drawing that is bright, so it is the one thing type must not cross. */
body.is-landing .site-main {
  max-width: none;
  /* --room-lift pulls the whole signed-out interface back up into the band.
     The room itself does not move: only where the page starts against it.
     Margin rather than padding, because past a lift of --room-h the offset
     goes negative and the interface has to keep climbing. */
  padding: 0;
  margin-top: calc(var(--room-h) * var(--room-enter) - var(--room-lift, 0px));
}
body.is-landing .site-footer { border-top-color: var(--line-soft); }

.lp { position: relative; }

/* ── The room ────────────────────────────────────────────────────────────
   A lit room instead of a grid. It is a band at the *top* of the document, so
   the page opens in the room and then leaves it behind: absolute rather than
   fixed, which is the whole difference between a scene you scroll out of and a
   wallpaper that follows you down the page.

   The band ends on the floor, and the floor is painted in --paper — the page
   background itself — so there is no seam to notice and nothing below the band
   that has to be kept in sync with it.

   The cost of moving it up is that the type now sits over a lit scene rather
   than over flat paper, so the scrim below is load-bearing rather than
   decorative, and the contrast over it is measured. */

.room {
  position: absolute; top: 0; left: 0; right: 0;
  height: var(--room-h);
  z-index: -1; pointer-events: none; overflow: hidden;
}
.room-art { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }

.r-wall     { fill: oklch(19% 0.010 60); }
/* Not a colour of its own: the floor *is* the page background, which is what
   lets the scene end without an edge. It was oklch(15.5% 0.012 50) before, half
   a percent of lightness away from --paper — a distinction nobody could see and
   the only thing standing between the room and the rest of the page. */
.r-floor    { fill: var(--paper); }
.r-skirting { fill: oklch(23% 0.012 58); }
/* The rug had all but vanished once the scrim ran to full --paper at the foot
   of the band: it was two lightness steps above the floor, and the scrim ate
   both. Lifted, and the scrim's lower ramp was pulled back to leave it room. */
.r-rug      { fill: oklch(25% 0.030 45); }

.r-sofa-back { fill: oklch(29% 0.028 46); }
.r-sofa-seat { fill: oklch(33% 0.032 46); }
.r-sofa-arm  { fill: oklch(31% 0.030 46); }
.r-cushion   { fill: oklch(36% 0.036 48); }
.r-pillow    { fill: oklch(44% 0.060 58); }
.r-leg       { fill: oklch(24% 0.024 52); }

.r-tabletop { fill: oklch(38% 0.045 55); }
.r-shelf    { fill: oklch(33% 0.038 55); }

.r-lamp-base { fill: oklch(36% 0.040 58); }
.r-lamp-stem { fill: oklch(42% 0.048 62); }
.r-shade     { fill: oklch(58% 0.095 72); }
.r-shade-lip { fill: oklch(76% 0.130 78); }
/* The bulb and the spill are the only bright things in the scene. */
.r-bulb      { fill: oklch(92% 0.140 84); opacity: .85; }
.r-spill     { fill: oklch(84% 0.120 80); opacity: .13; }

.r-book      { fill: oklch(40% 0.070 30); }
.r-book-2    { fill: oklch(36% 0.055 200); }
.r-mug       { fill: oklch(46% 0.050 60); }
.r-mug-handle { fill: none; stroke: oklch(46% 0.050 60); stroke-width: 3; }

.r-pot  { fill: oklch(38% 0.060 40); }
.r-leaf { fill: oklch(32% 0.050 148); }

/* The robot vacuum. Light enough to read through the scrim at the depth it
   drives — it is on the floor, which is the most veiled part of the band, so a
   furniture-dark body would simply not be there. */
/* Every value here is a contrast ceiling, not a taste call. The vacuum drives
   the length of the floor, so at some point in its crossing it is behind every
   line of type on the page — including the 12.5px form labels, which need
   4.5:1. A lit-up robot measured 3.29:1 under `E-naslov`, and its indicator
   would have been far worse: the sampler only missed it because a moving 2px
   light is easy to miss. Hence a dark machine with a dim amber dot, which is
   also what a robot vacuum looks like in an unlit room. */
.r-vac-shadow { fill: oklch(9% 0.005 60); opacity: .55; }
.r-vac-body   { fill: oklch(40% 0.010 250); }
.r-vac-deck   { fill: oklch(28% 0.010 250); }
.r-vac-eye    { fill: oklch(44% 0.070 84); }

/* Mud on the rug. Dark and matte — it is dirt seen in an unlit room, so it
   reads as an absence of rug rather than as a colour of its own. */
.r-mud > * { fill: oklch(17% 0.030 42); }
/* Both the mud and the vacuum are moved and turned by transforms whose origin
   has to be the shape itself, not the middle of the 1440×520 artwork — which is
   what `transform-box: view-box` would give them. */
.r-mud, .r-vac { transform-box: fill-box; transform-origin: center; }

/* Hidden by default, and the animation is what reveals it. That way reduced
   motion — where the global rule runs every animation once, instantly, to its
   end state — leaves no vacuum on the floor rather than a parked one, and no
   mud either: nobody is coming to clean it. */
.r-vac { opacity: 0; }
.r-mud { opacity: 0; }
@media (prefers-reduced-motion: no-preference) {
  /* The route and the spots come from the same clock, or the machine cleans
     where the mud is not. `--vac-cycle` is written onto `.room` by the same
     pass that wrote the keyframes. */
  .r-vac {
    animation: rr-vac-run var(--vac-cycle, 40s) linear infinite;
  }
  .r-mud {
    animation-duration: var(--vac-cycle, 40s);
    animation-timing-function: ease-out;
    animation-iteration-count: infinite;
    /* animation-name is per spot — rr-mud-0…4, set inline. */
  }
}

/* The lamp's light, drawn inside the scene so it cannot drift off the lamp.
   Two parts, because one gradient cannot be both: a wide pool on the wall that
   falls off slowly, and a small hot bloom at the bulb. */
.r-halo  { fill: url(#rr-halo); }
.r-bloom { fill: url(#rr-bloom); }

/* Falls off fast on purpose. A wider, slower halo looked better on its own and
   measured worse: it reached the centre column, where the form labels sit, and
   took one of them to 3.29:1. Light near the lamp, dark where the type is. */
.r-halo-0 { stop-color: oklch(88% 0.130 84); stop-opacity: .32; }
.r-halo-1 { stop-color: oklch(84% 0.120 80); stop-opacity: .13; }
.r-halo-2 { stop-color: oklch(80% 0.110 76); stop-opacity: .03; }
.r-halo-3 { stop-color: oklch(80% 0.110 76); stop-opacity: 0; }

.r-bloom-0 { stop-color: oklch(96% 0.115 92); stop-opacity: .52; }
.r-bloom-1 { stop-color: oklch(92% 0.130 86); stop-opacity: .20; }
.r-bloom-2 { stop-color: oklch(90% 0.130 84); stop-opacity: 0; }
/* A second, slower pass that drifts across the wall, so the light moves
   rather than only pulsing in place. */
.room-sweep {
  position: absolute; right: 10%; bottom: 34%;
  width: min(38vw, 520px); aspect-ratio: 1.4;
  background: radial-gradient(ellipse, oklch(88% 0.120 86 / .16), transparent 62%);
  filter: blur(40px);
}
/* The bulb, its bloom and the pool on the wall breathe on one timing, because
   they are one light: staggering them reads as three separate things fading. */
@media (prefers-reduced-motion: no-preference) {
  .room-sweep { animation: room-drift 19s ease-in-out infinite alternate; }
  .r-bulb, .r-bloom { animation: room-bulb 7s ease-in-out infinite alternate; }
  .r-halo { animation: room-breathe 7s ease-in-out infinite alternate; transform-origin: 66% 47%; }
}
@keyframes room-breathe {
  from { opacity: .82; transform: scale(1); }
  to   { opacity: 1;   transform: scale(1.06); }
}
@keyframes room-drift {
  from { opacity: .55; transform: translate3d(0, 0, 0) scale(1); }
  to   { opacity: .95; transform: translate3d(-9vw, -3vh, 0) scale(1.16); }
}
@keyframes room-bulb { from { opacity: .74; } to { opacity: 1; } }

/* Keeps text legible over the lit half of the room, and ends the scene.
   Measured, not guessed: the scene is sampled through this scrim in the
   contrast check.

   Opaque at both ends and clear in the middle. The top end is the old job —
   the header and the first line of type sit there. The bottom end is what the
   move to the top of the page made necessary: the band now ends in the middle
   of the screen instead of at the bottom edge, so anything the band cuts
   through — the rug is the obvious one — would show a hard horizontal seam.
   Dissolving into --paper means the room fades out into the page as you
   scroll rather than being sliced off. */
.room-scrim {
  position: absolute; inset: 0; pointer-events: none;
  /* Clear through the middle: no page type crosses the band any more, so the
     scrim has only two jobs left. A light wash at the very top, where the
     header sits over the wall, and a fade into --paper at the foot, so the
     scene dissolves into the page instead of ending on a seam. */
  background: linear-gradient(180deg,
    color-mix(in oklab, var(--paper) 55%, transparent) 0%,
    color-mix(in oklab, var(--paper) 18%, transparent) 9%,
    transparent 20%,
    transparent 82%,
    color-mix(in oklab, var(--paper) 45%, transparent) 93%,
    var(--paper) 100%);
}

@media (max-width: 720px) {
  /* Still the artwork's own ratio — the scene stays whole on phones too, just
     smaller. A vh-based band cropped the floor off it. */
  body.is-landing { --room-h: 36.2vw; }
}

/* The top padding is small on purpose. It used to open a gap under a band the
   page started *inside* of; now the page starts below the room, and the room's
   own dark floor already reads as space. Anything more and the hero falls off
   the fold. */
.lp-hero { max-width: 900px; margin: 0 auto; padding: calc(var(--sp) * 3) calc(var(--sp) * 3) 0; text-align: center; }
.lp-pill {
  display: inline-flex; align-items: center; gap: calc(var(--sp) * 1.25);
  font-size: 12.5px; color: var(--ink-2); background: var(--paper-2);
  border: 1px solid var(--line); padding: 6px 14px; border-radius: 999px;
  margin: 0 0 calc(var(--sp) * 5);
}
.lp-pill i { width: 6px; height: 6px; border-radius: 50%; background: var(--ok); animation: lp-blink 2s ease-in-out infinite; }
@keyframes lp-blink { 50% { opacity: .25; } }

/* One line, always. The 17ch measure and `text-wrap: balance` were there to
   break a longer headline into two even lines; this one is meant to be read in
   a single sweep, so the wrap is off and the type shrinks with the viewport
   instead. The floor keeps it legible on a phone, where 5.6vw is tiny. */
.lp h1 {
  font-size: clamp(24px, 5.6vw, 58px); line-height: 1.04; letter-spacing: -.042em;
  font-weight: 680; margin: 0 auto calc(var(--sp) * 3); white-space: nowrap;
}
.lp-lead {
  color: var(--ink-2); font-size: clamp(15.5px, 1.4vw, 17.5px);
  max-width: 56ch; margin: 0 auto calc(var(--sp) * 6);
}

.lp-prompt {
  display: flex; align-items: center; gap: calc(var(--sp) * 1.75); text-align: left;
  background: var(--paper-2); border: 1px solid var(--line); border-radius: var(--r-lg);
  padding: 18px; position: relative;
  box-shadow: 0 24px 60px -34px oklch(0% 0 0 / .9), 0 0 60px -30px oklch(76% 0.085 68 / .45);
  transition: border-color .2s ease, box-shadow .2s ease;
}
.lp-prompt:hover, .lp-prompt:focus-within {
  border-color: oklch(76% 0.085 68 / .55);
  box-shadow: 0 24px 60px -34px oklch(0% 0 0 / .9), 0 0 70px -26px oklch(76% 0.085 68 / .65);
}
.lp-ico { flex: none; width: 20px; height: 20px; border-radius: 50%; border: 2px solid var(--accent); position: relative; }
.lp-ico::after {
  content: ""; position: absolute; left: 13px; top: 13px; width: 8px; height: 2px;
  background: var(--accent); transform: rotate(45deg); transform-origin: left;
}
/* The sentence types itself by clipping, and the caret is translated by the
   same length — one variable drives both, so they cannot drift apart. Both are
   composited properties; nothing here triggers layout. */
.lp-qwrap {
  --typed: 0ch; --chars: 45;
  position: relative; flex: 1; min-width: 0; overflow: hidden;
  font-family: var(--mono); font-size: 15.5px; color: var(--ink);
}
.lp-q { display: block; width: max-content; max-width: 100%; white-space: nowrap; clip-path: inset(0 calc(100% - var(--typed)) 0 0); }
.lp-caret { position: absolute; left: 0; top: 50%; width: 2px; height: 20px; background: var(--accent); translate: var(--typed) -50%; }
@media (prefers-reduced-motion: no-preference) {
  .lp-qwrap { animation: lp-type 13s steps(var(--chars), end) infinite; }
  .lp-caret { animation: lp-caret 1s steps(2, jump-none) infinite; }
}
@keyframes lp-type { 0% { --typed: 0ch; } 46%, 100% { --typed: calc(var(--chars) * 1ch); } }
@keyframes lp-caret { 50% { opacity: 0; } }
/* Reduced motion gets the finished sentence, not an empty box. */
@media (prefers-reduced-motion: reduce) { .lp-qwrap { --typed: calc(var(--chars) * 1ch); } }
.lp-go { flex: none; }

.lp-resolved {
  display: flex; flex-wrap: wrap; gap: var(--sp); justify-content: center;
  margin-top: calc(var(--sp) * 4); min-height: 34px;
}
.lp-chip {
  display: inline-flex; align-items: center; gap: var(--sp); font-size: 13px; color: var(--ink);
  background: var(--paper-3); border: 1px solid var(--line); padding: 6px 13px; border-radius: 999px;
}
.lp-chip b {
  font-family: var(--mono); font-size: 10.5px; letter-spacing: .08em;
  text-transform: uppercase; color: var(--accent-ink); font-weight: 500;
}
@media (prefers-reduced-motion: no-preference) {
  .lp-chip { opacity: 0; animation: lp-pop 13s ease-out infinite; }
  .lp-chip:nth-child(1) { animation-delay: 0s; }
  .lp-chip:nth-child(2) { animation-delay: .35s; }
  .lp-chip:nth-child(3) { animation-delay: .7s; }
  .lp-chip:nth-child(4) { animation-delay: 1.05s; }
}
@keyframes lp-pop {
  0%, 48% { opacity: 0; transform: translateY(7px) scale(.96); }
  53%, 92% { opacity: 1; transform: none; }
  97%, 100% { opacity: 0; transform: translateY(-4px) scale(.98); }
}


.lp-answer { max-width: 900px; margin: calc(var(--sp) * 12) auto 0; padding: 0 calc(var(--sp) * 3); }
.lp-answer-head { display: flex; align-items: center; gap: calc(var(--sp) * 1.25); margin-bottom: var(--sp); font-size: 13px; color: var(--ink-3); }
.lp-answer-head b { color: var(--ink-2); font-weight: 600; }
.lp-live { margin-left: auto; display: inline-flex; align-items: center; gap: 7px; font-family: var(--mono); font-size: 11px; color: var(--ok); }
.lp-live i { width: 6px; height: 6px; border-radius: 50%; background: var(--ok); animation: lp-blink 1.8s ease-in-out infinite; }

.lp-stream { border: 1px solid var(--line-soft); border-radius: var(--r-lg); overflow: hidden; background: var(--paper-2); }
.lp-hit {
  display: grid; grid-template-columns: 44px minmax(0, 1fr) auto; gap: calc(var(--sp) * 1.75);
  align-items: center; padding: 14px 16px; border-bottom: 1px solid var(--line-soft);
  transition: background .16s ease;
}
.lp-hit:last-child { border-bottom: 0; }
.lp-hit:hover { background: var(--paper-3); }
@media (prefers-reduced-motion: no-preference) {
  .lp-hit { opacity: 0; animation: lp-arrive 13s ease-out infinite; }
  .lp-hit:nth-child(1) { animation-delay: 1.5s; }
  .lp-hit:nth-child(2) { animation-delay: 1.85s; }
  .lp-hit:nth-child(3) { animation-delay: 2.2s; }
  .lp-hit:nth-child(4) { animation-delay: 2.55s; }
  .lp-hit:nth-child(5) { animation-delay: 2.9s; }
}
@keyframes lp-arrive {
  0%, 8% { opacity: 0; transform: translateY(10px); }
  13%, 92% { opacity: 1; transform: none; }
  97%, 100% { opacity: 0; transform: translateY(-5px); }
}
.lp-hit h3 { margin: 0 0 2px; font-size: 14.5px; font-weight: 600; letter-spacing: -.015em; }
.lp-hit p { margin: 0; font-size: 12.5px; color: var(--ink-3); font-family: var(--mono); }
.lp-tag {
  display: inline-block; font-family: var(--mono); font-size: 9.5px; letter-spacing: .1em;
  text-transform: uppercase; padding: 2px 6px; border-radius: 4px; margin-right: 7px; vertical-align: 1.5px;
}
.lp-tag.is-new { color: var(--ok); background: var(--ok-bg); }
.lp-tag.is-repost { color: var(--warn); background: var(--warn-bg); }
.lp-tag.is-bumped { color: var(--info); background: var(--info-bg); }
.lp-eur { text-align: right; font-family: var(--mono); font-size: 15px; font-weight: 700; letter-spacing: -.02em; white-space: nowrap; }
.lp-eur span { display: block; font-size: 10.5px; font-weight: 400; color: var(--ink-3); }

.lp-trio {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: calc(var(--sp) * 2);
  max-width: 900px; margin: calc(var(--sp) * 12) auto 0; padding: 0 calc(var(--sp) * 3) calc(var(--sp) * 10);
}
.lp-t { background: var(--paper-2); border: 1px solid var(--line-soft); border-radius: var(--r-lg); padding: 20px; }
.lp-t b { display: block; font-family: var(--mono); font-size: 10.5px; letter-spacing: .12em; color: var(--accent-ink); margin-bottom: 9px; }
.lp-t p { margin: 0; font-size: 14px; color: var(--ink-2); }

@media (max-width: 780px) {
  .lp-trio { grid-template-columns: 1fr; }
  .lp-hit { grid-template-columns: minmax(0, 1fr) auto; }
  .lp-hit .picon { display: none; }
  .lp-qwrap { font-size: 13px; }
}
@media (max-width: 560px) {
  .lp-hero { padding: calc(var(--sp) * 2) calc(var(--sp) * 2) 0; }
  .lp-answer, .lp-trio { padding-inline: calc(var(--sp) * 2); }
  .lp-prompt { padding: 14px; gap: var(--sp); flex-wrap: wrap; }
  .lp-go { width: 100%; }
}

/* ── Admin statistics ────────────────────────────────────────────────── */

.stats-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: calc(var(--sp) * 1.5);
  margin: var(--sp) 0 calc(var(--sp) * 2);
}

.stat-card {
  background: var(--paper-2);
  border: 1px solid var(--line-soft);
  border-radius: var(--r);
  padding: calc(var(--sp) * 1.75);
}

.stat-card b {
  display: block;
  font-size: 26px;
  line-height: 1.2;
  margin-bottom: calc(var(--sp) / 2);
}

.stat-card span { color: var(--ink-2); font-size: 13.5px; }

.stats-chart { margin: 0 0 calc(var(--sp) * 2); }

.stats-chart svg {
  display: block;
  max-width: 100%;
  height: auto;
}

.s-bar { shape-rendering: crispEdges; }
.s-visits { fill: var(--accent); }
.s-signups { fill: var(--info); }
.s-axis { fill: var(--ink-3); font-size: 10.5px; }

.stats-chart figcaption {
  display: flex;
  gap: calc(var(--sp) * 2);
  margin-top: var(--sp);
  font-size: 12.5px;
  color: var(--ink-2);
}

.s-key {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 2px;
  margin-right: calc(var(--sp) / 2);
  vertical-align: -1px;
}

.s-key-visits { background: var(--accent); }
.s-key-signups { background: var(--info); }

/* ── Responsive ──────────────────────────────────────────────────────── */

@media (max-width: 940px) {
  .cols { grid-template-columns: 1fr; gap: calc(var(--sp) * 5); }
}

@media (max-width: 720px) {
  .site-main { padding: calc(var(--sp) * 4) calc(var(--sp) * 2) calc(var(--sp) * 8); }
  .hdr-in { padding: 0 calc(var(--sp) * 2); gap: calc(var(--sp) * 2); }
  .site-footer { padding: calc(var(--sp) * 2); }
  .nav { order: 3; width: 100%; margin: 0 0 var(--sp); overflow-x: auto; }
  .hdr-end { margin-left: auto; }
  .feed-item { grid-template-columns: 76px minmax(0, 1fr); gap: calc(var(--sp) * 1.75); }
  .thumb { width: 76px; height: 58px; }
  .price { grid-column: 2; text-align: left; }
  .price .trend-row { justify-content: flex-start; }
  h2 { margin-top: calc(var(--sp) * 4); }
}

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