/* NexBiz client. Mobile-first: the phone layout is the real one, and the
   desktop view is that same layout in a frame. */

:root {
  --gutter: 16px;
  --bg: #f4f5f8;
  --surface: #ffffff;
  --surface-2: #f8f9fc;
  --ink: #15171c;
  --ink-2: #4a4f5c;
  --ink-3: #868d9d;
  --line: #e4e7ee;
  --brand: #1f6f5c;
  --brand-ink: #ffffff;
  --brand-soft: #e6f2ee;
  --accent: #b4532a;
  --accent-soft: #fbeee7;
  --warn: #9a6a06;
  --warn-soft: #fdf3dd;
  --danger: #a5342f;
  --danger-soft: #fbeceb;
  --radius: 14px;
  --radius-sm: 10px;
  --shadow: 0 1px 2px rgba(16, 20, 30, .06), 0 8px 24px rgba(16, 20, 30, .06);
  --tap: 44px;
}

/* The dark palette is written twice on purpose: the media query serves viewers
   on the system setting, and the attribute block lets the in-app toggle win in
   either direction. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #0d0f13;
    --surface: #161920;
    --surface-2: #1d2027;
    --ink: #f2f4f8;
    --ink-2: #b9c0cd;
    --ink-3: #828a99;
    --line: #272b34;
    --brand: #4fb79b;
    --brand-ink: #06231c;
    --brand-soft: #16302a;
    --accent: #e08a62;
    --accent-soft: #2e201a;
    --warn: #e0b659;
    --warn-soft: #2d2619;
    --danger: #e98a84;
    --danger-soft: #2e1c1b;
    --shadow: 0 1px 2px rgba(0, 0, 0, .5), 0 8px 24px rgba(0, 0, 0, .35);
    color-scheme: dark;
  }
}

:root[data-theme="dark"] {
    --bg: #0d0f13;
    --surface: #161920;
    --surface-2: #1d2027;
    --ink: #f2f4f8;
    --ink-2: #b9c0cd;
    --ink-3: #828a99;
    --line: #272b34;
    --brand: #4fb79b;
    --brand-ink: #06231c;
    --brand-soft: #16302a;
    --accent: #e08a62;
    --accent-soft: #2e201a;
    --warn: #e0b659;
    --warn-soft: #2d2619;
    --danger: #e98a84;
    --danger-soft: #2e1c1b;
    --shadow: 0 1px 2px rgba(0, 0, 0, .5), 0 8px 24px rgba(0, 0, 0, .35);
    color-scheme: dark;
}

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

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  background: var(--bg);
  color: var(--ink);
  font: 15px/1.45 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
  -webkit-font-smoothing: antialiased;
}

/* An app shell, not a document: the column is exactly the viewport, and
   `main` is what scrolls. That is what lets the tab bar sit on the bottom
   edge and a screen's action bar sit directly above it — before this, `main`
   grew with its content, the page scrolled instead, and a sticky action bar
   had no scrollport to stick to, so it landed in the middle of the page. */
#app { height: 100%; display: flex; flex-direction: column; }

/* The band behind the sticky header, so nothing scrolls into view under a
   phone's status bar. */
body::before {
  content: "";
  position: fixed;
  inset: 0 0 auto;
  height: env(safe-area-inset-top, 0px);
  z-index: 30;
  background: var(--bg);
}

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

header.topbar {
  position: sticky; top: env(safe-area-inset-top, 0px); z-index: 20;
  display: flex; align-items: center; gap: 10px;
  padding: 14px max(16px, env(safe-area-inset-right, 0px)) 10px
           max(16px, env(safe-area-inset-left, 0px));
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--line);
}
header.topbar h1 { margin: 0; font-size: 17px; font-weight: 650; letter-spacing: -.2px; flex: 1; }
header.topbar .sub { font-size: 12px; color: var(--ink-3); font-weight: 450; }

main {
  flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain;
  padding-block: 14px 20px;
  padding-left: max(var(--gutter), env(safe-area-inset-left, 0px));
  padding-right: max(var(--gutter), env(safe-area-inset-right, 0px));
}

nav.tabs {
  position: sticky; bottom: 0; z-index: 20;
  display: grid; grid-template-columns: repeat(5, 1fr);
  background: color-mix(in srgb, var(--surface) 97%, transparent);
  backdrop-filter: blur(14px);
  border-top: 1px solid var(--line);
  padding-bottom: env(safe-area-inset-bottom, 0px);
  padding-left: env(safe-area-inset-left, 0px);
  padding-right: env(safe-area-inset-right, 0px);
}
nav.tabs button {
  appearance: none; border: 0; background: none; cursor: pointer;
  min-height: var(--tap); padding: 8px 2px 10px;
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  color: var(--ink-3); font-size: 10.5px; font-weight: 550; letter-spacing: .1px;
}
nav.tabs button svg { width: 21px; height: 21px; stroke-width: 1.7; }
nav.tabs button[aria-current="page"] { color: var(--brand); }
nav.tabs .badge {
  position: absolute; transform: translate(14px, -4px);
  background: var(--accent); color: #fff; border-radius: 9px;
  font-size: 9.5px; font-weight: 700; padding: 1px 5px; line-height: 1.4;
}

/* ---------- Primitives ---------- */

.card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 14px; margin-bottom: 12px;
  box-shadow: var(--shadow);
}
.card.flat { box-shadow: none; }
.card.tap { cursor: pointer; }
.card.tap:active { transform: scale(.995); }

h2.section { font-size: 13px; font-weight: 650; color: var(--ink-2);
  text-transform: uppercase; letter-spacing: .6px; margin: 20px 2px 10px; }
h3 { margin: 0 0 4px; font-size: 15.5px; font-weight: 640; letter-spacing: -.1px; }
p { margin: 0 0 8px; color: var(--ink-2); }
p:last-child { margin-bottom: 0; }
.muted { color: var(--ink-3); font-size: 13px; }
.row { display: flex; gap: 10px; align-items: center; }
.row.between { justify-content: space-between; }
.row.wrap { flex-wrap: wrap; }
.grow { flex: 1; min-width: 0; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.stack > * + * { margin-top: 10px; }

button, .btn {
  appearance: none; font: inherit; font-weight: 600; cursor: pointer;
  border-radius: var(--radius-sm); border: 1px solid var(--line);
  background: var(--surface); color: var(--ink);
  min-height: 40px; padding: 9px 14px;
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  text-decoration: none;
}
button:active { transform: scale(.98); }
/* The display above outranks the browser's own [hidden] rule, so a button
   given the attribute would otherwise stay on screen. */
button[hidden], .btn[hidden] { display: none; }
button:disabled { opacity: .5; cursor: default; }
.btn-primary { background: var(--brand); border-color: var(--brand); color: var(--brand-ink); }
.btn-ghost { background: transparent; border-color: transparent; color: var(--brand); padding: 8px; }
.btn-danger { background: var(--danger-soft); border-color: transparent; color: var(--danger); }
.btn-block { width: 100%; }
.btn-sm { min-height: 34px; padding: 6px 11px; font-size: 13px; }

input, select, textarea {
  font: inherit; width: 100%; color: var(--ink);
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-sm); padding: 11px 12px; min-height: 42px;
}
input:focus, select, textarea:focus { outline: 2px solid var(--brand); outline-offset: -1px; }
textarea { min-height: 84px; resize: vertical; }
label { display: block; font-size: 12.5px; font-weight: 600; color: var(--ink-2); margin: 0 0 5px 2px; }
.field + .field { margin-top: 12px; }

.pill {
  display: inline-flex; align-items: center; gap: 5px;
  border-radius: 999px; padding: 3px 9px;
  font-size: 11.5px; font-weight: 600; white-space: nowrap;
  background: var(--surface-2); color: var(--ink-2); border: 1px solid var(--line);
}
.pill.brand { background: var(--brand-soft); color: var(--brand); border-color: transparent; }
.pill.accent { background: var(--accent-soft); color: var(--accent); border-color: transparent; }
.pill.warn { background: var(--warn-soft); color: var(--warn); border-color: transparent; }
.pill.danger { background: var(--danger-soft); color: var(--danger); border-color: transparent; }

.chips { display: flex; gap: 8px; overflow-x: auto; padding: 2px 0 6px; scrollbar-width: none; }
.chips::-webkit-scrollbar { display: none; }
.chips button { min-height: 34px; padding: 6px 12px; border-radius: 999px; font-size: 13px;
  white-space: nowrap; }
.chips button[aria-pressed="true"] { background: var(--brand); border-color: var(--brand);
  color: var(--brand-ink); }

.searchbar { position: relative; }
.searchbar input { padding-left: 38px; min-height: 46px; border-radius: 12px; }
.searchbar svg { position: absolute; left: 12px; top: 13px; width: 19px; height: 19px;
  stroke: var(--ink-3); }

.quick { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.quick button {
  flex-direction: column; align-items: flex-start; gap: 2px; text-align: left;
  min-height: 74px; padding: 12px; border-radius: var(--radius);
  background: var(--surface); box-shadow: var(--shadow);
}
.quick .t { font-weight: 650; font-size: 14px; }
.quick .d { font-size: 11.5px; color: var(--ink-3); font-weight: 450; }

.stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.stat { background: var(--surface-2); border-radius: var(--radius-sm); padding: 10px; }
.stat .n { font-size: 19px; font-weight: 700; letter-spacing: -.4px; }
.stat .l { font-size: 10.5px; color: var(--ink-3); text-transform: uppercase;
  letter-spacing: .4px; font-weight: 600; }

.meter { height: 6px; border-radius: 99px; background: var(--surface-2); overflow: hidden; }
.meter > i { display: block; height: 100%; background: var(--brand); }

.avatar {
  width: 38px; height: 38px; border-radius: 50%; flex: none;
  background: var(--brand-soft); color: var(--brand);
  display: grid; place-items: center; font-weight: 700; font-size: 14px;
}
.avatar.lg { width: 56px; height: 56px; font-size: 20px; }

.timeline { list-style: none; margin: 0; padding: 0 0 0 18px; border-left: 2px solid var(--line); }
.timeline li { position: relative; padding: 0 0 14px 12px; }
.timeline li::before {
  content: ""; position: absolute; left: -23px; top: 4px;
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--brand); box-shadow: 0 0 0 3px var(--bg);
}
.timeline .when { font-size: 11.5px; color: var(--ink-3); }

.empty { text-align: center; padding: 34px 18px; color: var(--ink-3); }
.empty .big { font-size: 30px; margin-bottom: 8px; }

.banner { border-radius: var(--radius-sm); padding: 11px 12px; font-size: 13px; margin-bottom: 12px; }
.banner.warn { background: var(--warn-soft); color: var(--warn); }
.banner.danger { background: var(--danger-soft); color: var(--danger); }
.banner.brand { background: var(--brand-soft); color: var(--brand); }

.sheet-backdrop {
  position: fixed; inset: 0; z-index: 60; background: rgba(8, 10, 14, .45);
  display: flex; align-items: flex-end; justify-content: center;
}
.sheet {
  background: var(--surface); width: 100%; max-width: 420px;
  border-radius: 18px 18px 0 0; padding: 16px 16px calc(16px + env(safe-area-inset-bottom));
  max-height: 88vh; overflow-y: auto;
}
@media (min-width: 760px) {
  .sheet-backdrop { position: absolute; }
  .sheet { border-radius: 18px; margin-bottom: 0; }
}
.sheet h3 { font-size: 17px; margin-bottom: 10px; }

.toast {
  position: fixed; left: 50%; bottom: 96px; transform: translateX(-50%);
  background: var(--ink); color: var(--bg); padding: 10px 16px; border-radius: 99px;
  font-size: 13px; font-weight: 550; z-index: 80; max-width: 88%; text-align: center;
}
@media (min-width: 760px) { .toast { position: absolute; } }

.skeleton { background: var(--surface-2); border-radius: var(--radius); height: 72px;
  margin-bottom: 12px; animation: pulse 1.3s ease-in-out infinite; }
@keyframes pulse { 50% { opacity: .55; } }

.sponsored-label { font-size: 10px; font-weight: 700; letter-spacing: .5px;
  text-transform: uppercase; color: var(--ink-3); }

/* Feature lists on a plan card: a tick and a line, not a bullet. */
.ticks { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.ticks li { display: flex; gap: 7px; align-items: flex-start; font-size: 13px; }
.ticks li svg { flex: none; margin-top: 2px; color: var(--brand); }

.list { list-style: none; margin: 0; padding: 0; }
.list li { padding: 11px 0; border-bottom: 1px solid var(--line); }
.list li:last-child { border-bottom: 0; }

a { color: var(--brand); }

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .01ms !important; animation-iteration-count: 1 !important;
      transition-duration: .01ms !important; }
  button:active, .card.tap:active { transform: none; }
}

/* Visible keyboard focus, which the default outline removal would otherwise lose. */
:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; border-radius: 6px; }

/* ---------------------------------------------------------------------------
   Directory components: the icon grid, rails, listing cards, photo grid and
   the sticky action bar. Sized for a thumb on a phone first.
   --------------------------------------------------------------------------- */

.searchbar:has(.city-chip) input { padding-right: 112px; }
.searchbar .city-chip {
  position: absolute; right: 6px; top: 6px;
  min-height: 34px; padding: 5px 11px; border-radius: 9px;
  background: var(--surface-2); border: 1px solid var(--line);
  font-size: 12.5px; font-weight: 600; color: var(--ink-2); max-width: 40%;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.suggest { padding: 6px; margin-top: -4px; }
.suggest-row {
  width: 100%; justify-content: flex-start; text-align: left;
  border: 0; background: none; min-height: 42px; padding: 8px 6px; border-radius: 8px;
  font-weight: 500;
}
.suggest-row:hover { background: var(--surface-2); }
.suggest-icon { width: 24px; flex: none; text-align: center; }

.icon-grid {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 4px;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 12px 6px; box-shadow: var(--shadow); margin-bottom: 14px;
}
.icon-tile {
  border: 0; background: none; min-height: 74px; padding: 8px 4px;
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  font-size: 11px; font-weight: 600; color: var(--ink-2); line-height: 1.25;
}
.icon-tile .glyph {
  width: 42px; height: 42px; border-radius: 12px; display: grid; place-items: center;
  background: var(--surface-2); font-size: 20px;
}
.icon-tile .glyph.more { background: var(--brand-soft); color: var(--brand); }
.icon-tile .label { text-align: center; }

.rail {
  display: flex; gap: 10px; overflow-x: auto; padding-bottom: 8px;
  scroll-snap-type: x proximity; scrollbar-width: none; margin: 0 -2px 4px;
}
.rail::-webkit-scrollbar { display: none; }
.rail-card {
  flex: 0 0 136px; scroll-snap-align: start; border: 0; background: none; padding: 0;
  display: flex; flex-direction: column; gap: 5px; text-align: left;
  min-height: 0; align-items: stretch;
}
.rail-card.narrow { flex-basis: 112px; }
.rail-card.wide { flex-basis: 162px; }
.rail-title { font-size: 13px; font-weight: 620; color: var(--ink); line-height: 1.3; }
.rail-note { font-size: 11.5px; color: var(--ink-3); font-weight: 500; }
.rail-price { font-size: 13.5px; font-weight: 700; color: var(--ink); }

.tile {
  width: 100%; display: grid; place-items: center; overflow: hidden;
  color: #fff; font-weight: 700; font-size: 19px; letter-spacing: .5px;
  text-shadow: 0 1px 3px rgba(0, 0, 0, .25);
}

/* The photograph a tile stands in for: same box, so a list of items with and
   without one does not jump about. */
.photo-img {
  width: 100%; display: block; object-fit: cover;
  background: var(--surface-2);
}

/* --- the member catalogue ------------------------------------------------
   A service and a B2B product are the same shape on the page: a photograph,
   what it is, what it costs, and the controls. One grid for both is what
   makes the two lists read as one catalogue rather than two tables that
   happen to sit above each other. The photograph column is a fixed 64px, so
   a row with a picture and a row without line up down the left edge. */
.cat-list { list-style: none; margin: 0; padding: 0; }

.cat-item {
  display: grid;
  grid-template-columns: 64px minmax(0, 1fr);
  gap: 0 14px; align-items: center;
  padding: 14px 0; border-bottom: 1px solid var(--line);
}
.cat-item:first-child { padding-top: 2px; }
.cat-item:last-child { padding-bottom: 2px; border-bottom: 0; }

.cat-item .tile, .cat-item .photo-img {
  width: 64px; height: 64px; border-radius: 12px;
  display: block; align-self: start;
}
.cat-item .photo-img { border: 1px solid var(--line); }

.cat-body { min-width: 0; display: grid; gap: 4px; }
.cat-name { font-size: 14.5px; font-weight: 640; letter-spacing: -.1px; }
.cat-note {
  font-size: 12.5px; line-height: 1.45; color: var(--ink-3);
  /* Two lines, then an ellipsis: a long description must not push the
     controls of the row below it off the fold. */
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.cat-meta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 1px; }
.cat-price { font-size: 13.5px; font-weight: 700; color: var(--ink); }
.cat-price span { font-weight: 500; color: var(--ink-3); }

/* On a phone the controls sit under the text, indented to the same column;
   once there is room they move out to the end of the row. */
.cat-actions { grid-column: 2; display: flex; align-items: center; gap: 8px; margin-top: 10px; }
.cat-actions .btn-sm { flex: 1; }
@media (min-width: 560px) {
  .cat-item { grid-template-columns: 64px minmax(0, 1fr) auto; }
  .cat-actions { grid-column: 3; margin-top: 0; }
  .cat-actions .btn-sm { flex: none; min-width: 66px; }
}

/* A row whose only control is a single icon keeps it at the end of the line
   at every width: it is narrow enough to fit, and a lone cross on a row of
   its own reads as if it belonged to the item below. */
.cat-item.compact { grid-template-columns: 64px minmax(0, 1fr) auto; }
.cat-item.compact .cat-actions { grid-column: 3; margin-top: 0; }

.cat-empty { color: var(--ink-3); font-size: 13px; padding: 4px 0 10px; }
.cat-foot { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--line); }
.cat-list:empty + .cat-foot,
.cat-list > .cat-empty:only-child ~ .cat-foot { border-top: 0; }

/* The photograph field inside a catalogue sheet: the preview at the size it
   will be seen at, and the controls beside it rather than under it. */
.img-picker {
  display: grid; grid-template-columns: 96px minmax(0, 1fr);
  gap: 12px; align-items: start;
}
.img-picker-preview img {
  width: 96px; height: 96px; border-radius: 12px;
  object-fit: cover; display: block; border: 1px solid var(--line);
}
.img-empty {
  width: 96px; height: 96px; border-radius: 12px;
  display: grid; place-items: center; text-align: center; padding: 8px;
  background: var(--surface-2); border: 1px dashed var(--line);
  color: var(--ink-3); font-size: 11.5px; line-height: 1.35;
}
.img-picker-controls { display: grid; gap: 8px; justify-items: stretch; }
.img-picker-controls .muted { margin: 0; font-size: 12px; line-height: 1.45; }
.img-picker-controls input[type="file"] { font-size: 12.5px; padding: 9px 10px; min-height: 0; }
.img-picker-controls button { justify-self: start; }

/* Home carousel promos. Named apart from .banner (the inline alert) so the two
   never style each other. */
.promos { margin-bottom: 4px; }
.promo {
  flex: 0 0 min(280px, 82%); scroll-snap-align: start;
  display: flex; flex-direction: column; align-items: flex-start; gap: 3px;
  text-align: left; min-height: 84px; padding: 13px 14px; border: 0; border-radius: var(--radius);
  background: color-mix(in srgb, var(--promo-accent) 14%, var(--surface));
  border-left: 3px solid var(--promo-accent);
}
.promo strong { font-size: 14.5px; color: var(--ink); }
.promo span { font-size: 12px; color: var(--ink-2); font-weight: 500; }
.promo .sponsored-label { color: var(--ink-3); }

h2.section .section-note {
  float: right; text-transform: none; letter-spacing: 0;
  font-size: 11.5px; font-weight: 500; color: var(--ink-3);
}

.rating-chip {
  background: #157a4a; color: #fff; border-radius: 5px;
  padding: 2px 6px; font-size: 12px; font-weight: 700;
}
.rating-chip.lg { font-size: 14px; padding: 3px 8px; }
.stars { color: #e8a33d; letter-spacing: -1px; }
.open { color: #157a4a; font-weight: 600; font-size: 13.5px; }
.closed { color: var(--danger); font-weight: 600; font-size: 13.5px; }
.price { font-size: 16px; font-weight: 700; margin: 4px 0; }
.price.lg { font-size: 21px; }
.price .muted { font-size: 13px; font-weight: 500; }

.action-row {
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 6px;
  margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--line);
}
.action {
  display: flex; flex-direction: column; align-items: center; gap: 5px;
  border: 0; background: none; min-height: 60px; padding: 4px;
  font-size: 11.5px; font-weight: 600; color: var(--ink-2); text-decoration: none;
}
.action .ico {
  width: 40px; height: 40px; border-radius: 11px; display: grid; place-items: center;
  background: var(--surface-2); color: var(--ink-2);
}
.action .ico.call { background: var(--brand); color: var(--brand-ink); }
.action .ico.wa { background: #25d366; color: #fff; font-size: 17px; }

.photo-grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-bottom: 14px;
}
.photo { position: relative; }
.photo.tall { grid-row: span 2; }
.photo-label {
  position: absolute; left: 8px; bottom: 8px;
  background: rgba(10, 12, 16, .62); color: #fff; backdrop-filter: blur(3px);
  border-radius: 6px; padding: 2px 7px; font-size: 11px; font-weight: 600;
}

.listing .sponsored-label { margin-bottom: 6px; }

/* The action bar on a listing, a product or an event. It sits on the bottom
   edge of the scrolling area — directly above the tab bar — and stays there
   while the page scrolls under it, so "Call" is always one reach away. The
   negative margins pull it out of `main`'s side padding so it spans the full
   width; the last-child margin gives the content above it room to clear. */
.sticky-cta {
  position: sticky; bottom: -20px; z-index: 15;
  display: flex; gap: 8px; margin: 16px -16px -20px;
  padding: 10px max(16px, env(safe-area-inset-left, 0px))
           calc(10px + env(safe-area-inset-bottom, 0px))
           max(16px, env(safe-area-inset-right, 0px));
  background: color-mix(in srgb, var(--surface) 94%, transparent);
  backdrop-filter: blur(10px); border-top: 1px solid var(--line);
}
/* Three actions across a phone: keep each on one line rather than letting
   "Get Best Price" wrap and make the whole bar twice as tall. */
.sticky-cta .btn, .sticky-cta button {
  white-space: nowrap; font-size: 13.5px; padding-inline: 10px; min-width: 0;
}
.sticky-cta .wa { background: #25d366; border-color: #25d366; color: #06301a; }

.badge-dot {
  position: absolute; transform: translate(10px, -14px);
  background: var(--accent); color: #fff; border-radius: 9px;
  font-size: 9.5px; font-weight: 700; padding: 1px 5px;
}

.or {
  display: flex; align-items: center; gap: 10px; margin: 16px 0 6px;
  color: var(--ink-3); font-size: 12px; font-weight: 600;
}
.or::before, .or::after { content: ""; flex: 1; height: 1px; background: var(--line); }

.btn.google { font-weight: 650; }
.btn.google .g {
  width: 20px; height: 20px; border-radius: 50%; display: grid; place-items: center;
  background: #fff; color: #4285f4; font-weight: 800; font-size: 13px;
  border: 1px solid var(--line);
}

.stat.tap { cursor: pointer; }

/* --------------------------------------------------------------- events */

.event-art { position: relative; }
.event-badge {
  position: absolute; left: 10px; top: 10px;
  background: rgba(10, 12, 16, .66); backdrop-filter: blur(4px); color: #fff;
  border-radius: 7px; padding: 3px 8px; font-size: 11px; font-weight: 700;
  letter-spacing: .3px;
}
.event-badge.paid { background: var(--accent); color: #fff; }
.event-badge.approval { background: var(--warn); color: #241a05; }
.event-card { padding-top: 12px; }

.ticket {
  border-style: dashed; border-width: 1.5px; border-color: var(--brand);
  background: var(--brand-soft);
}
.ticket-code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 22px; font-weight: 700; letter-spacing: 1px; color: var(--brand);
}

/* ---------------------------------------------------------------- zones */

.zone-row {
  width: 100%; display: flex; align-items: center; gap: 10px; text-align: left;
  min-height: 52px; padding: 10px; border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface); margin-bottom: 8px; font-weight: 500;
}
.zone-row.current { border-color: var(--brand); background: var(--brand-soft); }
.zone-dot { width: 10px; height: 10px; border-radius: 50%; flex: none; }
.zone-count { display: block; font-size: 11.5px; }

/* --------------------------------------------------------- theme toggle */

.theme-row { display: flex; gap: 6px; }
.theme-row button {
  flex: 1; min-height: 38px; font-size: 13px; border-radius: var(--radius-sm);
}
.theme-row button[aria-pressed="true"] {
  background: var(--brand); border-color: var(--brand); color: var(--brand-ink);
}

/* ------------------------------------------------------ zone map editor */

.editor {
  display: grid; gap: 14px; grid-template-columns: 1fr; margin-bottom: 14px;
}
@media (min-width: 1000px) { .editor { grid-template-columns: 1.6fr 1fr; } }
.editor-canvas {
  position: relative; background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); overflow: hidden; box-shadow: var(--shadow);
}
.editor-canvas canvas { width: 100%; height: auto; display: block; cursor: crosshair; }
.editor-hint {
  position: absolute; left: 12px; top: 12px;
  background: color-mix(in srgb, var(--surface) 88%, transparent); backdrop-filter: blur(6px);
  border: 1px solid var(--line); border-radius: 8px; padding: 5px 10px;
  font-size: 12px; font-weight: 600; color: var(--ink-2);
}
.editor-side {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 14px; box-shadow: var(--shadow);
}

/* ---------- Advertisement slots ---------- */

/* Three positions, three shapes. An advert is always marked, and never
   dressed as a search result: the label is drawn by the server, not inferred
   here, so a card cannot lose it by being rendered somewhere new. */

.ad { cursor: pointer; }
.ad:active { transform: scale(.995); }

.ad-mid {
  display: flex; align-items: center; gap: 14px;
  margin: 14px 0 4px; padding: 15px 16px; border-radius: var(--radius);
  background: color-mix(in srgb, var(--promo-accent) 13%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--promo-accent) 26%, transparent);
}
.ad-mid strong { display: block; font-size: 15.5px; margin-top: 2px; }
.ad-mid p { margin: 4px 0 0; font-size: 13px; color: var(--ink-2); }
.ad-mid .by { display: block; margin-top: 7px; font-size: 11px; color: var(--ink-3); }
.ad-mid .ad-cta {
  flex: none; padding: 9px 14px; border-radius: 9px; font-size: 13px; font-weight: 650;
  background: var(--promo-accent); color: #fff; white-space: nowrap;
}

.ad-inline { border: 1px dashed var(--line); }
.ad-inline .sponsored-label { position: static; flex: none; }

/* Banner artwork. It sits behind the words under a gradient, because a
   headline printed straight onto a photograph is unreadable about half the
   time — and which half depends on the photograph, so it cannot be checked
   once and forgotten. A banner with no artwork keeps its accent colour. */
.ad-art {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; border-radius: inherit;
}
.promo.has-art, .ad-mid.has-art { position: relative; overflow: hidden; }
.promo.has-art::after, .ad-mid.has-art::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit;
  background: linear-gradient(100deg,
    color-mix(in srgb, var(--promo-accent) 88%, transparent) 0%,
    color-mix(in srgb, var(--promo-accent) 62%, transparent) 48%,
    transparent 92%);
}
.promo.has-art .ad-text, .ad-mid.has-art > .grow, .ad-mid.has-art > .ad-cta {
  position: relative; z-index: 1;
}
.promo .ad-text { display: flex; flex-direction: column; align-items: flex-start; gap: 3px; }
.promo.has-art .ad-text strong,
.promo.has-art .ad-text span,
.ad-mid.has-art strong,
.ad-mid.has-art p,
.ad-mid.has-art .by,
.ad-mid.has-art .sponsored-label { color: #fff; }
.ad-mid.has-art { border-color: transparent; }
.ad-mid.has-art .ad-cta { background: #fff; color: var(--promo-accent); }

.ad-thumb { width: 100%; height: 76px; object-fit: cover; border-radius: 10px; display: block; }

/* ---------- The website, on a wide screen ----------

   Same app, same markup, a different shape. On a phone the tab bar is on the
   bottom edge where a thumb reaches it; on a desktop that is wrong — nobody
   reaches for the bottom of a monitor — so the same five destinations become
   a horizontal nav under the header, and the content gets a centred column
   wide enough to put cards side by side.

   Nothing here is a separate build: one stylesheet, one set of components,
   reordered with flexbox and gridded where a list has room to breathe. */

@media (min-width: 760px) {
  #app { max-width: none; }

  /* The header and the nav become one bar at the top, in that order, with
     the content under them. Flex order does it; the markup is untouched. */
  header.topbar { order: 1; padding-inline: var(--gutter); }
  nav.tabs { order: 2; }
  .demo-strip { order: 3; }
  main { order: 4; }

  nav.tabs {
    position: static;
    display: flex; justify-content: flex-start; gap: 4px;
    padding: 0 var(--gutter);
    border-top: 0; border-bottom: 1px solid var(--line);
    background: var(--surface);
  }
  nav.tabs button {
    flex-direction: row; gap: 8px;
    padding: 13px 14px 12px; font-size: 14px; font-weight: 600;
    border-bottom: 2px solid transparent; border-radius: 0;
  }
  nav.tabs button svg { width: 18px; height: 18px; }
  nav.tabs button[aria-current="page"] { border-bottom-color: var(--brand); }
  nav.tabs button:hover { color: var(--ink); }

  header.topbar h1 { font-size: 20px; flex: 0 0 auto; }
  header.topbar .sub { font-size: 12.5px; }
  header.topbar > .grow { flex: 1; }

  main { padding-inline: var(--gutter); padding-block: 22px 40px; }

  /* The action bar stops spanning the window and becomes a panel at the foot
     of the column it belongs to. */
  .sticky-cta {
    margin-inline: 0; margin-bottom: 0; bottom: 0;
    border: 1px solid var(--line); border-bottom: 0;
    border-radius: var(--radius) var(--radius) 0 0;
    padding-inline: 14px;
  }
  .sticky-cta .btn, .sticky-cta button { font-size: 14px; padding-inline: 16px; }

  .icon-grid { grid-template-columns: repeat(6, 1fr); }
  .promos .promo { flex: 0 0 calc(50% - 6px); }
}

/* A column, centred, once there is room for one. */
@media (min-width: 760px) {
  :root { --gutter: 28px; }
  main > *, nav.tabs, header.topbar { margin-inline: auto; width: 100%; max-width: 1180px; }
  nav.tabs, header.topbar { max-width: none; }
  nav.tabs > *, header.topbar > * { max-width: none; }
}

@media (min-width: 1024px) {
  :root { --gutter: max(32px, calc((100% - 1180px) / 2)); }

  .icon-grid { grid-template-columns: repeat(8, 1fr); gap: 6px; padding: 16px 10px; }
  .promos .promo { flex: 0 0 calc(33.333% - 6px); }
  .searchbar input { min-height: 52px; font-size: 15.5px; }

  /* Lists that are a stack of cards on a phone have room for two columns.
     The wrapper is the only markup change this layout needed. */
  /* Cards in a row share a height: a grid of cards that all end at different
     places reads as a mistake, where a stack of them never does. */
  .grid-cards {
    display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0 16px; align-items: stretch;
  }
  .grid-cards > .card { display: flex; flex-direction: column; }
  .grid-cards > .card > .row:last-child, .grid-cards > .card > :last-child { margin-top: auto; }
  .grid-cards.wide { grid-template-columns: repeat(3, minmax(0, 1fr)); }

  /* The rails keep scrolling horizontally, but show more of themselves. */
  .rail-card { flex-basis: 184px; }
  .rail-card.wide { flex-basis: 248px; }
  .rail-card.narrow { flex-basis: 136px; }

  /* With a page this wide the action bar is a floating panel rather than a
     full-width bar — so it has to cast a shadow and sit clear of the edge,
     or it reads as a strip cut out of the text behind it. */
  .sticky-cta {
    max-width: 560px; margin-left: auto; margin-bottom: 18px;
    border: 1px solid var(--line); border-radius: var(--radius);
    background: var(--surface); backdrop-filter: none;
    box-shadow: 0 2px 6px rgba(0, 0, 0, .06), 0 14px 34px rgba(0, 0, 0, .12);
  }

  /* Controls stop stretching once the column is wide: a sort menu spanning
     1180px is a target nobody needs that big. */
  #sort, select { max-width: 280px; }
  /* A narrower child of a centred column would otherwise centre itself. */
  .searchbar { max-width: 780px; margin-left: 0; margin-right: auto; }

  /* The photo mosaic is a header, not the page. Capped so a listing opens on
     its details rather than on three storeys of placeholder. */
  /* Rows of actions and artwork stop stretching to the full column. */
  .action-row { max-width: 560px; }
  .event-art .tile { height: 150px !important; }

  .photo-grid { grid-template-columns: 2fr 1fr 1fr; grid-auto-rows: 148px; }
  /* The tiles carry an inline height for the phone; on a wide screen the grid
     rows decide instead, so each one fills the cell it was given. */
  .photo-grid .tile { height: 100% !important; }
}

@media (min-width: 1400px) {
  :root { --gutter: max(32px, calc((100% - 1320px) / 2)); }
  main > *, nav.tabs, header.topbar { max-width: 1320px; }
  nav.tabs, header.topbar { max-width: none; }
  .grid-cards { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .grid-cards.wide { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}


/* --- inside the native app ----------------------------------------------
   The shell sets data-native on <html> once the page is up. These are the
   small things that otherwise give a web page away on a phone: a long press
   on a tab raising selection handles, a drag on the header highlighting the
   title. Body text stays selectable — a phone number has to be copyable. */
html[data-native] header.topbar,
html[data-native] nav.tabs,
html[data-native] .demo-strip,
html[data-native] .icon-grid,
html[data-native] .chip,
html[data-native] button,
html[data-native] .btn,
html[data-native] .btn-sm {
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
}
