/* gig.io: small supplements on top of Tailwind CDN */
button { cursor: pointer; }

/* Rating stars & scores read as gold-yellow (per request) rather than amber-
   orange. Kept deep enough that the score text beside the star stays legible
   on white; loaded after tw.css so these win. */
.text-amber-500 { color: #eab308 !important; }
.text-amber-400 { color: #eab308 !important; }

/* Shared magic numbers, declared once and reused (see MOBILE ADAPTATION).
   --mobile-nav-h must match the rendered height of the sm:hidden bottom nav,
   which is what `pb-16` (4rem) on <main> already reserves. */
:root {
  --mobile-nav-h: 4rem;
  --chat-chrome: 160px;   /* header + page padding subtracted from the chat shell */
}

/* ── hover states (the prebuilt tw.css ships no hover: utilities, so every
   button referencing one was inert). Re-declare the ones templates use. ── */
.hover\:bg-accent-dark:hover { background-color: #4d7c0f !important; }
.hover\:bg-brand-light:hover { background-color: #13395f !important; }
.hover\:bg-emerald-700:hover { background-color: #047857 !important; }
.hover\:brightness-95:hover { filter: brightness(.95); }

/* ── primary call-to-action: the "book / post / subscribe" actions. Skin only
   (colour, depth, motion); each button keeps its own padding / radius / width.
   Layered over the old `bg-accent text-brand` utilities, so it wins via
   specificity. Two looks by theme:
     • light: lime with navy text
     • dark:  deeper lime with white text, which holds on the navy surfaces */
.btn-primary,
a.btn-primary,
button.btn-primary {
  background: #a3e635 !important;   /* accent lime */
  color: #0a2540 !important;        /* brand navy */
  border: none;
  box-shadow: 0 1px 2px rgba(10, 37, 64, .10), 0 6px 16px rgba(163, 230, 53, .28);
}
.btn-primary svg { color: #0a2540; }
.btn-primary:hover {
  background: #4d7c0f !important;   /* accent-dark */
  transform: translateY(-1px);
  box-shadow: 0 2px 4px rgba(10, 37, 64, .12), 0 10px 24px rgba(77, 124, 15, .30);
}
.btn-primary:active { transform: translateY(0); box-shadow: 0 1px 2px rgba(10, 37, 64, .16); }
.btn-primary:disabled, .btn-primary[disabled] {
  background: #d9f99d !important; color: #0a2540 !important;
  box-shadow: none; transform: none; cursor: not-allowed;
}

/* dark theme: deeper lime + white text */
html.dark .btn-primary { background: #4d7c0f !important; color: #fff !important; }
html.dark .btn-primary svg { color: #fff; }
html.dark .btn-primary:hover { background: #3f6212 !important; }
html.dark .btn-primary:disabled, html.dark .btn-primary[disabled] {
  background: #1a2e05 !important; color: #bef264 !important;
}

/* cookie-consent banner: pin to the bottom, but lift above the mobile bottom
   nav (sm:hidden, ~3.25rem tall) on small screens so the two never overlap. */
/* Cookie consent: a clear corner card (never blocks the page; only an explicit
   choice dismisses it, so clicking elsewhere never closes it). */
#cookie-banner {
  position: fixed; left: 1rem; bottom: 1rem; z-index: 80;
  width: min(92vw, 25rem); background: #fff; color: #0f172a;
  border: 1px solid #e2e8f0; border-radius: 16px;
  box-shadow: 0 18px 50px rgba(2, 6, 23, .28); padding: 18px;
  animation: cc-in .25s ease-out;
}
/* --mobile: lift above the bottom nav AND clear the notched-device home
   indicator (the viewport is `viewport-fit=cover`, so we must inset ourselves). */
@media (max-width: 639.98px) { #cookie-banner { left: .75rem; right: .75rem; width: auto; bottom: calc(var(--mobile-nav-h) + env(safe-area-inset-bottom)); } }
html.dark #cookie-banner { background: #111a2e; border-color: #26344f; color: #e2e8f0; }
@keyframes cc-in { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
.cc-head { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.cc-head h2 { font-size: 1rem; font-weight: 800; }
.cc-text { font-size: .82rem; color: #64748b; line-height: 1.5; margin-bottom: 14px; }
html.dark .cc-text { color: #94a3b8; }
.cc-text a { color: #4d7c0f; font-weight: 600; white-space: nowrap; }
.cc-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.cc-btn { font-size: .82rem; font-weight: 700; border-radius: 10px; padding: .55rem .95rem; cursor: pointer; border: 1px solid transparent; }
.cc-ghost { background: #f1f5f9; color: #475569; }
.cc-ghost:hover { background: #e2e8f0; }
html.dark .cc-ghost { background: #1b2740; color: #cbd5e1; }
.cc-link { background: transparent; color: #4d7c0f; padding: .55rem .5rem; }
.cc-link:hover { text-decoration: underline; }
/* category list */
.cc-cats { display: flex; flex-direction: column; gap: 12px; margin: 4px 0 14px; max-height: 46vh; overflow: auto; }
.cc-cat { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.cc-cat-h { font-weight: 700; font-size: .85rem; }
.cc-cat-b { font-size: .75rem; color: #64748b; line-height: 1.45; margin-top: 2px; }
html.dark .cc-cat-b { color: #94a3b8; }
.cc-always { font-size: .72rem; font-weight: 700; color: #4d7c0f; white-space: nowrap; padding-top: 3px; }
.cc-none { font-size: .72rem; font-weight: 600; color: #94a3b8; white-space: nowrap; padding-top: 3px; }
.cc-disabled { opacity: .6; }
/* toggle switch */
.cc-switch { position: relative; display: inline-block; width: 42px; height: 24px; flex: none; }
.cc-switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.cc-slider { position: absolute; inset: 0; background: #cbd5e1; border-radius: 999px; transition: background .2s; cursor: pointer; }
.cc-slider::before { content: ""; position: absolute; width: 18px; height: 18px; left: 3px; top: 3px; background: #fff; border-radius: 999px; transition: transform .2s; box-shadow: 0 1px 3px rgba(0,0,0,.25); }
.cc-switch input:checked + .cc-slider { background: #4d7c0f; }
.cc-switch input:checked + .cc-slider::before { transform: translateX(18px); }
.cc-switch input:focus-visible + .cc-slider { outline: 2px solid #4d7c0f; outline-offset: 2px; }

/* profile avatar: always a crisp circle (never an oval when the nav is tight),
   with a themed ring for contrast against the header */
.avatar {
  width: 2.25rem; height: 2.25rem; flex: 0 0 auto; border-radius: 9999px;
  object-fit: cover; box-shadow: 0 0 0 2px rgba(255, 255, 255, .35);
}
html.dark .avatar { box-shadow: 0 0 0 2px rgba(148, 163, 184, .45); }
/* initials stay dark-navy on the accent circle in both themes (high contrast) */
.avatar-initial { color: #0a2540 !important; }

/* hide the native <details> disclosure marker so summaries align with nav links */
.no-marker { list-style: none; }
.no-marker::-webkit-details-marker { display: none; }
.no-marker::marker { content: ""; }

/* ── dark theme ───────────────────────────────────────────────────────────────
   The prebuilt Tailwind CSS has no dark: variants, so we override the common
   colour utilities under html.dark instead. Brand/accent colours stay as-is. */
.theme-sun { display: none; }
html.dark .theme-moon { display: none; }
html.dark .theme-sun { display: inline-block; }
.theme-sun-label { display: none; }
html.dark .theme-moon-label { display: none; }
html.dark .theme-sun-label { display: inline; }

/* Both themes have to pin this, not just dark. The meta tag declares the page
   supports "light dark", so with no declaration here the browser falls back to
   the visitor's OS preference, and someone on a dark desktop got dark date and
   time pickers, select popups and scrollbars over the light theme. The app's
   own theme decides, never the OS. */
html { color-scheme: light; }
html.dark { color-scheme: dark; }
html.dark body { background-color: #0b1220; color: #e2e8f0; }

html.dark .bg-white { background-color: #111a2e !important; }
html.dark .bg-slate-50 { background-color: #0f1830 !important; }
html.dark .bg-slate-100 { background-color: #1b2740 !important; }
html.dark .bg-slate-200 { background-color: #243150 !important; }

html.dark .text-slate-900, html.dark .text-slate-800, html.dark .text-brand { color: #e8eef7 !important; }
html.dark .text-slate-700, html.dark .text-slate-600 { color: #cbd5e1 !important; }
html.dark .text-slate-500, html.dark .text-slate-400 { color: #94a3b8 !important; }
html.dark .text-slate-300 { color: #64748b !important; }
/* accent-dark is deliberately deep (Alexandrite on Cloud Dancer); on dark surfaces
   that's too dim, so lift to a lighter teal tint for links/labels/icons. */
html.dark .text-accent-dark { color: #a3e635 !important; }

html.dark .border, html.dark .border-t, html.dark .border-b, html.dark .border-l, html.dark .border-r,
html.dark .border-slate-100, html.dark .border-slate-200, html.dark .border-slate-300 { border-color: #26344f !important; }
html.dark .divide-slate-100 > :not([hidden]) ~ :not([hidden]) { border-color: #26344f !important; }

html.dark .hover\:bg-slate-50:hover, html.dark .hover\:bg-slate-100:hover { background-color: #1b2740 !important; }

/* form controls + custom dropdowns + popups */
html.dark .field, html.dark input, html.dark textarea, html.dark select,
html.dark .nicesel-btn { background-color: #0f1830 !important; border-color: #2a3a5a !important; color: #e2e8f0; }
html.dark input::placeholder, html.dark textarea::placeholder { color: #64748b; }
html.dark .nicesel-menu, html.dark .mapboxgl-popup-content { background: #111a2e !important; border-color: #26344f !important; color: #e2e8f0; }
/* The popup darkens, but .map-card sets its own slate-900 text, so the title
   landed near-black on a near-black panel. The side list has the same colours
   on the same dark surface. The pin itself stays a white pill, since it sits on map
   tiles, not on an app surface. */
html.dark .map-card { color: #e2e8f0; }
html.dark .map-card .meta, html.dark .map-li .meta { color: #94a3b8; }
html.dark .map-card .p, html.dark .map-li .p { color: #a3e635; }
html.dark .map-card .p .ic { background: #2a3310; color: #bef264; }
html.dark .map-li { border-bottom-color: #26344f; }
html.dark .map-li:hover, html.dark .map-li.is-active { background: #1b2740; }
html.dark .nicesel-opt { color: #cbd5e1; }
html.dark .nicesel-opt:hover, html.dark .nicesel-opt.active { background: #1b2740; }
html.dark .nicesel-opt[aria-selected="true"] { background: #2a3310; color: #bef264; }

html.dark .shadow, html.dark .shadow-sm, html.dark .shadow-md, html.dark .shadow-lg, html.dark .shadow-xl, html.dark .shadow-2xl {
  box-shadow: 0 8px 28px rgba(0, 0, 0, .5) !important;
}

/* soft button ("Use my location") + slider tracks in dark mode */
html.dark .btn-soft { background: #0f1830; border-color: #2a3a5a; color: #e2e8f0; }
html.dark .btn-soft:hover { background: #1b2740; border-color: #33456b; }
html.dark .price-range .pr-track, html.dark .range1 .pr-track { background: #4a5e85; }
html.dark .price-range .pr-fill, html.dark .range1 .pr-fill { background: #a3e635; }
html.dark .price-range input[type="range"]::-webkit-slider-thumb,
html.dark .range1 input[type="range"]::-webkit-slider-thumb { background: #e8eef7; border-color: #a3e635; }
html.dark .price-range input[type="range"]::-moz-range-thumb,
html.dark .range1 input[type="range"]::-moz-range-thumb { background: #e8eef7; border-color: #a3e635; }

a, button { transition: color .15s ease, background-color .15s ease, box-shadow .15s ease, transform .12s ease; }
img { -webkit-user-drag: none; }
.line-clamp-1 { display:-webkit-box; -webkit-line-clamp:1; -webkit-box-orient:vertical; overflow:hidden; }
html { scroll-behavior: smooth; }
::-webkit-scrollbar { height: 8px; width: 8px; }
::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 4px; }
/* higher specificity + !important so these beat mapbox-gl.css (loaded after) */
.mapboxgl-popup .mapboxgl-popup-content { border-radius: 16px !important; padding: 0 !important; overflow: hidden !important; border: 1px solid #e2e8f0; box-shadow: 0 10px 34px rgba(2,6,23,.28); }
.mapboxgl-popup .mapboxgl-popup-content img { border-radius: 0; }
/* visible, legible close button (dark pill over the image) */
.mapboxgl-popup-close-button {
  font-size: 0; line-height: 0; width: 26px; height: 26px; top: 8px; right: 8px;
  border-radius: 999px; background: rgba(10,37,64,.65); color: #fff; display: grid; place-items: center;
}
.mapboxgl-popup-close-button::before { content: "\00d7"; font-size: 18px; line-height: 1; color:#fff; }
.mapboxgl-popup-close-button:hover { background: rgba(10,37,64,.9); }

/* map price markers: icon chip + price */
.map-pin { display:inline-flex; align-items:center; gap:6px; background:#fff; border:1px solid #e2e8f0;
  border-radius:999px; padding:3px 11px 3px 3px; box-shadow:0 2px 8px rgba(2,6,23,.18); cursor:pointer;
  font-size:12px; font-weight:700; color:#0a2540; white-space:nowrap; transition:transform .1s, box-shadow .1s; line-height:1; }
.map-pin:hover { transform:translateY(-1px); }
.map-pin.is-active { border-color:#a3e635; box-shadow:0 0 0 2px #a3e635, 0 2px 8px rgba(2,6,23,.18); }
.map-pin .ic { width:24px; height:24px; border-radius:999px; background:#0a2540; color:#a3e635; display:inline-flex; align-items:center; justify-content:center; flex:0 0 auto; }
.map-pin .ic svg { width:14px; height:14px; display:block; }
.map-pin .pr { color:#4d7c0f; display:inline-flex; align-items:center; }

/* popup card */
.map-card { width:220px; display:block; text-decoration:none; color:#0f172a; }
.map-card img { width:100%; height:120px; object-fit:cover; display:block; background:#f1f5f9; }
.map-card .body { padding:9px 11px; }
.map-card .t { font-weight:600; font-size:13px; }
.map-card .meta { font-size:11px; color:#64748b; margin-top:1px; }
.map-card .p { color:#4d7c0f; font-weight:700; font-size:12px; margin-top:4px; display:flex; align-items:center; gap:5px; }
.map-card .p .ic { width:18px;height:18px;border-radius:999px;background:#f4fbe4;display:grid;place-items:center;color:#4d7c0f; }

/* cluster bubble (declutters overlapping markers) */
.map-cluster { display:flex; align-items:center; justify-content:center; min-width:34px; height:34px; padding:0 9px;
  border-radius:999px; background:#0a2540; color:#fff; font-weight:700; font-size:13px; border:3px solid #fff;
  box-shadow:0 2px 10px rgba(2,6,23,.30); cursor:pointer; }
.map-cluster:hover { background:#4d7c0f; }

/* hide the Mapbox logo + attribution visually, but keep them in the DOM
   (attribution must remain present for data-attribution compliance, e.g. Japan) */
.mapboxgl-ctrl-logo, .mapboxgl-ctrl-attrib { display:none !important; }

/* mobile: show the in-view list as a slide-up bottom sheet */
@media (max-width: 639.98px) {
  /* dvh, not vh: on mobile browsers `vh` is the *expanded* viewport, so a 72vh
     sheet is taller than the visible area whenever the URL bar is showing. */
  .map-sheet { position:fixed; left:0; right:0; bottom:0; top:auto; height:72dvh; z-index:50;
    border-top-left-radius:18px; border-top-right-radius:18px; box-shadow:0 -14px 40px rgba(2,6,23,.28);
    transform:translateY(100%); transition:transform .26s ease; }
  .map-sheet.sheet-open { transform:translateY(0); }
  .map-sheet::before { content:""; position:absolute; top:7px; left:50%; transform:translateX(-50%);
    width:38px; height:4px; border-radius:999px; background:#cbd5e1; }
  body.sheet-lock { overflow:hidden; }
}
.map-card .p .ic svg { width:11px;height:11px; }

/* side list rows (larger, richer service boxes) */
.map-li { display:flex; gap:12px; padding:14px 16px; border-bottom:1px solid #f1f5f9; cursor:pointer; }
.map-li:hover, .map-li.is-active { background:#f9fee8; }
.map-li img, .map-li .map-li-noimg { width:80px; height:80px; border-radius:12px; object-fit:cover; flex:0 0 auto; background:#f1f5f9; }
.map-li .t { font-weight:600; font-size:15px; line-height:1.3; }
.map-li .meta { font-size:12px; color:#64748b; margin-top:2px; }
.map-li .p { color:#4d7c0f; font-weight:800; font-size:14px; margin-top:4px; }

/* mobile bottom-sheet open state (beats Tailwind's .hidden) */
.sheet-open { display: flex !important; }

/* fancy upload dropzone */
.dz { border: 2px dashed #cbd5e1; border-radius: 14px; padding: 18px; background: #f8fafc; cursor: pointer; transition: border-color .15s, background .15s; }
.dz:hover { border-color: #4d7c0f; background: #f9fee8; }
.dz.dz-over { border-color: #4d7c0f; background: #f4fbe4; }
.dz-prompt { display: flex; flex-direction: column; align-items: center; gap: 4px; text-align: center; pointer-events: none; }
.dz-previews { display: grid; grid-template-columns: repeat(auto-fill, minmax(84px, 1fr)); gap: 8px; margin-top: 12px; }
.dz-previews:empty { margin-top: 0; }
.dz-item { position: relative; aspect-ratio: 1/1; border-radius: 10px; overflow: hidden; background: #e2e8f0; }
.dz-item img { width: 100%; height: 100%; object-fit: cover; display: block; }
.dz-item button { position: absolute; top: 3px; right: 3px; width: 20px; height: 20px; border-radius: 999px; background: rgba(10,37,64,.75); color: #fff; font-size: 13px; line-height: 1; display: grid; place-items: center; }
.dz-item button:hover { background: #e11d48; }

/* The dropzone never got dark styles, so it sat as a white slab in the middle
   of the dark review, listing, album and avatar forms. Its prompt text already
   had dark overrides; only the surface was missing. */
html.dark .dz { background: #0f1830; border-color: #2a3a5a; }
html.dark .dz:hover { background: #111a2e; border-color: #a3e635; }
html.dark .dz.dz-over { background: #2a3310; border-color: #a3e635; }
html.dark .dz-item { background: #1b2740; }

/* search filters: sidebar on wide desktop; smooth bottom-sheet below xl */
@media (max-width: 1279.98px) {
  .filters-panel {
    position: fixed; left: 0; right: 0; bottom: 0; top: auto; margin: 0 auto;
    width: 100%; max-width: 560px; max-height: 85dvh; z-index: 70; overflow-y: auto;
    border-radius: 20px 20px 0 0;            /* rounded top, flush bottom (no lower border) */
    transform: translateY(100%); transition: transform .24s ease; display: block;
  }
  .filters-panel.filters-open { transform: translateY(0); box-shadow: 0 -12px 40px rgba(2,6,23,.28); }
}
/* on desktop the sticky sidebar scrolls on its own, so a long filter list
   (e.g. reaching "Verified businesses" at the bottom) never forces you to
   scroll the whole page. top-24 = 6rem, leave ~1.5rem breathing room. */
@media (min-width: 1280px) {
  #filters.filters-panel {
    max-height: calc(100vh - 7.5rem);
    overflow-y: auto;
    overscroll-behavior: contain;   /* keep the scroll inside, don't chain to the page */
    scrollbar-gutter: stable;       /* reserve the bar's space so content doesn't shift */
    scrollbar-width: thin;
    border-radius: 1.25rem;         /* a touch rounder so it reads as one contained card */
  }
  #filters.filters-panel::-webkit-scrollbar { width: 6px; }
  #filters.filters-panel::-webkit-scrollbar-thumb { background: #e2e8f0; border-radius: 999px; }
  #filters.filters-panel::-webkit-scrollbar-thumb:hover { background: #cbd5e1; }
  #filters.filters-panel::-webkit-scrollbar-track { background: transparent; }
}

/* categories mega-menu: rotate the chevron when open, subtle drop-in */
#cat-btn[aria-expanded="true"] .chev { transform: rotate(90deg); }
#cat-panel:not(.hidden) { animation: catdrop .14s ease-out; }
@keyframes catdrop { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }

/* horizontally-scrollable chip rows without a visible scrollbar */
.no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
.no-scrollbar::-webkit-scrollbar { display: none; }

/* visible keyboard focus for accessibility */
a:focus-visible, button:focus-visible, input:focus-visible,
select:focus-visible, textarea:focus-visible, summary:focus-visible {
  outline: 2px solid #4d7c0f;
  outline-offset: 2px;
  border-radius: 4px;
}

/* dual-thumb price range slider (search filters) */
.price-range { position: relative; height: 26px; cursor: pointer; }
.price-range .pr-track,
.price-range .pr-fill { position: absolute; top: 11px; height: 4px; border-radius: 999px; }
.price-range .pr-track { left: 0; right: 0; background: #e2e8f0; }
.price-range .pr-fill { background: #4d7c0f; }
/* both range inputs overlap; only their thumbs are interactive, so a click on
   the bare track falls through to the container (handled in filters.js) */
.price-range input[type="range"] {
  position: absolute; top: 0; left: 0; width: 100%; height: 26px; margin: 0; z-index: 3;
  -webkit-appearance: none; appearance: none; background: transparent; pointer-events: none;
}
.price-range input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none; pointer-events: auto;
  width: 20px; height: 20px; border-radius: 999px; background: #fff;
  border: 2px solid #4d7c0f; box-shadow: 0 1px 3px rgba(2, 6, 23, .25); cursor: grab;
}
.price-range input[type="range"]::-moz-range-thumb {
  pointer-events: auto; width: 20px; height: 20px; border-radius: 999px; background: #fff;
  border: 2px solid #4d7c0f; box-shadow: 0 1px 3px rgba(2, 6, 23, .25); cursor: grab;
}
.price-range input[type="range"]::-webkit-slider-runnable-track { background: transparent; border: none; }
.price-range input[type="range"]::-moz-range-track { background: transparent; border: none; }
.price-range input[type="range"]:focus { outline: none; }
.price-range input[type="range"]:focus-visible::-webkit-slider-thumb { outline: 2px solid #4d7c0f; outline-offset: 2px; }

/* single-thumb range (radius/distance), same look as the price slider */
.range1 { position: relative; height: 26px; }
.range1 .pr-track,
.range1 .pr-fill { position: absolute; top: 11px; height: 4px; border-radius: 999px; }
.range1 .pr-track { left: 0; right: 0; background: #e2e8f0; }
.range1 .pr-fill { left: 0; background: #4d7c0f; }
.range1 input[type="range"] {
  position: absolute; top: 0; left: 0; width: 100%; height: 26px; margin: 0;
  -webkit-appearance: none; appearance: none; background: transparent;
}
.range1 input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 18px; height: 18px; border-radius: 999px; background: #fff;
  border: 2px solid #4d7c0f; box-shadow: 0 1px 3px rgba(2, 6, 23, .25); cursor: pointer;
}
.range1 input[type="range"]::-moz-range-thumb {
  width: 18px; height: 18px; border-radius: 999px; background: #fff;
  border: 2px solid #4d7c0f; box-shadow: 0 1px 3px rgba(2, 6, 23, .25); cursor: pointer;
}
.range1 input[type="range"]::-webkit-slider-runnable-track { background: transparent; border: none; }
.range1 input[type="range"]::-moz-range-track { background: transparent; border: none; }
.range1 input[type="range"]:focus { outline: none; }

/* polished form fields (daisyUI-style; the build can't add the daisyUI plugin
   here, so this hand-rolls the same look) */
.field {
  width: 100%;
  background: #fff;
  border: 1px solid #e2e8f0;          /* slate-200 */
  border-radius: 0.6rem;
  padding: 0.55rem 0.75rem;
  font-size: 0.875rem;
  line-height: 1.25rem;
  color: #0f172a;                     /* slate-900 */
  transition: border-color .15s ease, box-shadow .15s ease, background-color .15s ease;
}
.field::placeholder { color: #94a3b8; }
.field:hover { border-color: #cbd5e1; }
.field:focus, .field:focus-visible {
  outline: none;
  border-color: #4d7c0f;              /* accent */
  box-shadow: 0 0 0 3px rgba(77, 124, 15, .16);
  background: #fff;
}
/* selects: hide native arrow, draw our own chevron */
select.field {
  -webkit-appearance: none; -moz-appearance: none; appearance: none;
  padding-right: 2rem;
  cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='none' stroke='%2394a3b8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6l4 4 4-4'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.65rem center;
}
select.field::-ms-expand { display: none; }
/* custom dropdown (daisyUI-style) that enhances native .field selects on
   pointer devices; the native select stays in the DOM for form submit + change
   events, and on touch the native picker is kept (it's better there). */
.nicesel { position: relative; }
.nicesel > select { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.nicesel-btn {
  width: 100%; display: flex; align-items: center; justify-content: space-between; gap: .5rem;
  background: #fff; border: 1px solid #e2e8f0; border-radius: .6rem; padding: .55rem .75rem;
  font-size: .875rem; color: #0f172a; text-align: left; cursor: pointer;
  transition: border-color .15s ease, box-shadow .15s ease;
}
.nicesel-btn:hover { border-color: #cbd5e1; }
.nicesel-btn:disabled { opacity: .55; cursor: not-allowed; }
.nicesel-btn:disabled:hover { border-color: #e2e8f0; }
.nicesel.open .nicesel-btn { border-color: #4d7c0f; box-shadow: 0 0 0 3px rgba(77, 124, 15, .16); }
.nicesel-btn .nicesel-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nicesel-btn svg { flex: none; width: 1rem; height: 1rem; color: #94a3b8; transition: transform .15s ease; }
.nicesel.open .nicesel-btn svg { transform: rotate(180deg); }
.nicesel-menu {
  position: absolute; z-index: 60; left: 0; right: 0; margin-top: .35rem;
  background: #fff; border: 1px solid #e2e8f0; border-radius: .75rem;
  box-shadow: 0 14px 36px rgba(2, 6, 23, .18); padding: .3rem; max-height: 18rem; overflow: auto;
}
.nicesel-menu[hidden] { display: none; }
.nicesel.up .nicesel-menu { top: auto; bottom: 100%; margin-top: 0; margin-bottom: .35rem; }

/* ── date picker (datepicker.js) ────────────────────────────────────────────
   The native calendar popup is drawn by the browser outside the page, so it
   ignores color-scheme on several platforms and kept showing up light over the
   dark theme. This panel is ours, so it follows the app. The input itself stays
   a real <input type="date">; only its built-in indicator is swapped for our
   button. Touch devices never load this and keep the OS picker. */
.dp { position: relative; }
.dp > input[type="date"] { width: 100%; padding-right: 2.25rem; }
.dp > input[type="date"]::-webkit-calendar-picker-indicator { display: none; }
.dp-btn {
  position: absolute; right: .5rem; top: 50%; transform: translateY(-50%);
  display: grid; place-items: center; width: 1.75rem; height: 1.75rem;
  border-radius: .45rem; color: #64748b; background: transparent; cursor: pointer;
}
.dp-btn svg { width: 1.05rem; height: 1.05rem; }
.dp-btn:hover { background: #f1f5f9; color: #0f172a; }
.dp.open .dp-btn { color: #4d7c0f; background: #f4fbe4; }

.dp-panel {
  /* centred on the field rather than pinned to an edge; datepicker.js nudges
     it back inside the viewport when the field sits near the screen edge */
  position: absolute; z-index: 60; left: 50%; transform: translateX(-50%);
  margin-top: .35rem; width: 17.5rem; padding: .6rem;
  background: #fff; border: 1px solid #e2e8f0; border-radius: .9rem;
  box-shadow: 0 14px 36px rgba(2, 6, 23, .18);
  overflow: auto; /* datepicker.js caps max-height when the viewport is short */
}
.dp-panel[hidden] { display: none; }
.dp.up .dp-panel { top: auto; bottom: 100%; margin-top: 0; margin-bottom: .35rem; }

.dp-head { display: flex; align-items: center; justify-content: space-between; gap: .25rem; margin-bottom: .4rem; }
.dp-title { font-weight: 700; font-size: .85rem; text-transform: capitalize; }
.dp-nav {
  display: grid; place-items: center; width: 1.75rem; height: 1.75rem;
  border-radius: .45rem; color: #64748b; cursor: pointer;
}
.dp-nav svg { width: 1rem; height: 1rem; }
.dp-nav:hover { background: #f1f5f9; color: #0f172a; }

.dp-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
.dp-dow {
  display: grid; place-items: center; height: 1.75rem;
  font-size: .68rem; font-weight: 700; text-transform: uppercase; color: #94a3b8;
}
.dp-day {
  display: grid; place-items: center; height: 2rem; border-radius: .5rem;
  font-size: .8rem; color: #334155; cursor: pointer;
}
.dp-day:hover:not(:disabled) { background: #f1f5f9; }
.dp-day:disabled { color: #cbd5e1; cursor: default; }
.dp-out { color: #94a3b8; }
.dp-out:disabled { color: #e2e8f0; }
.dp-today { font-weight: 800; box-shadow: inset 0 0 0 1px #4d7c0f; }
.dp-sel, .dp-sel:hover:not(:disabled) { background: #a3e635 !important; color: #0a2540 !important; font-weight: 700; }
.dp-day.dp-focus:focus-visible { outline: 2px solid #4d7c0f; outline-offset: 1px; }

.dp-foot { display: flex; justify-content: space-between; margin-top: .45rem; padding-top: .45rem; border-top: 1px solid #f1f5f9; }
.dp-link { font-size: .78rem; font-weight: 700; color: #4d7c0f; padding: .25rem .4rem; border-radius: .4rem; cursor: pointer; }
.dp-link:hover { background: #f4fbe4; }

html.dark .dp-panel { background: #111a2e; border-color: #26344f; }
html.dark .dp-btn { color: #94a3b8; }
html.dark .dp-btn:hover, html.dark .dp-nav:hover { background: #1b2740; color: #e2e8f0; }
html.dark .dp.open .dp-btn { color: #a3e635; background: #2a3310; }
html.dark .dp-nav { color: #94a3b8; }
html.dark .dp-day { color: #cbd5e1; }
html.dark .dp-day:hover:not(:disabled) { background: #1b2740; }
html.dark .dp-day:disabled { color: #33456b; }
html.dark .dp-out { color: #64748b; }
html.dark .dp-out:disabled { color: #2a3a5a; }
html.dark .dp-today { box-shadow: inset 0 0 0 1px #a3e635; }
html.dark .dp-foot { border-top-color: #26344f; }
html.dark .dp-link { color: #a3e635; }
html.dark .dp-link:hover { background: #2a3310; }

/* ── time picker (timepicker.js) ────────────────────────────────────────────
   Same reasoning as the date picker: the browser's clock popup is outside the
   page and cannot be themed. Two scrolling columns, hours labelled by locale so
   a 12-hour locale reads "9 AM" without needing a third meridiem column. */
.tp { position: relative; display: inline-block; }
.tp > input[type="time"] { padding-right: 2rem; }
.tp > input[type="time"]::-webkit-calendar-picker-indicator { display: none; }
.tp-btn {
  position: absolute; right: .35rem; top: 50%; transform: translateY(-50%);
  display: grid; place-items: center; width: 1.6rem; height: 1.6rem;
  border-radius: .4rem; color: #64748b; background: transparent; cursor: pointer;
}
.tp-btn svg { width: 1rem; height: 1rem; }
.tp-btn:hover { background: #f1f5f9; color: #0f172a; }
.tp.open .tp-btn { color: #4d7c0f; background: #f4fbe4; }

.tp-panel {
  /* centred on the field; see .dp-panel */
  position: absolute; z-index: 60; left: 50%; transform: translateX(-50%);
  margin-top: .35rem; padding: .45rem;
  background: #fff; border: 1px solid #e2e8f0; border-radius: .8rem;
  box-shadow: 0 14px 36px rgba(2, 6, 23, .18); overflow: hidden;
  display: flex; flex-direction: column;
}
.tp-panel[hidden] { display: none; }
.tp.up .tp-panel { top: auto; bottom: 100%; margin-top: 0; margin-bottom: .35rem; }

.tp-cols { display: flex; gap: .35rem; min-height: 0; overflow: hidden; }
.tp-col { display: flex; flex-direction: column; gap: 1px; overflow-y: auto; min-width: 4.25rem; max-height: 13rem; }
.tp-colhead {
  position: sticky; top: 0; background: #fff; padding: .15rem .3rem .3rem;
  font-size: .62rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: #94a3b8;
}
.tp-opt {
  padding: .35rem .5rem; border-radius: .45rem; font-size: .82rem; color: #334155;
  text-align: center; cursor: pointer; white-space: nowrap;
}
.tp-opt:hover { background: #f1f5f9; }
.tp-sel, .tp-sel:hover { background: #a3e635; color: #0a2540; font-weight: 700; }
.tp-foot { display: flex; justify-content: flex-end; margin-top: .35rem; padding-top: .35rem; border-top: 1px solid #f1f5f9; }
.tp-link { font-size: .75rem; font-weight: 700; color: #4d7c0f; padding: .2rem .4rem; border-radius: .4rem; cursor: pointer; }
.tp-link:hover { background: #f4fbe4; }

html.dark .tp-panel { background: #111a2e; border-color: #26344f; }
html.dark .tp-colhead { background: #111a2e; }
html.dark .tp-btn { color: #94a3b8; }
html.dark .tp-btn:hover { background: #1b2740; color: #e2e8f0; }
html.dark .tp.open .tp-btn { color: #a3e635; background: #2a3310; }
html.dark .tp-opt { color: #cbd5e1; }
html.dark .tp-opt:hover { background: #1b2740; }
html.dark .tp-foot { border-top-color: #26344f; }
html.dark .tp-link { color: #a3e635; }
html.dark .tp-link:hover { background: #2a3310; }
.nicesel-opt {
  padding: .5rem .6rem; border-radius: .5rem; font-size: .875rem; color: #334155;
  cursor: pointer; white-space: nowrap;
}
.nicesel-opt:hover, .nicesel-opt.active { background: #f1f5f9; }
.nicesel-opt[aria-selected="true"] { background: #ecfdf5; color: #065f46; font-weight: 600; }
.nicesel-group { padding: .5rem .6rem .2rem; font-size: .68rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: #94a3b8; }

/* loading skeletons (shimmer) */
.skeleton { position: relative; overflow: hidden; background: #eef2f7; border-radius: 8px; }
.skeleton::after {
  content: ""; position: absolute; inset: 0; transform: translateX(-100%);
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .65), transparent);
  animation: gig-shimmer 1.3s infinite;
}
@keyframes gig-shimmer { 100% { transform: translateX(100%); } }
.skel-row { display: flex; gap: 10px; padding: 10px 14px; align-items: center; }
.skel-row .s-img { width: 54px; height: 54px; border-radius: 10px; flex: none; }
.skel-row .s-line { height: 11px; }

/* soft pill button (e.g. "Use my location") */
.btn-soft {
  width: 100%;
  border: 1px solid #e2e8f0;
  border-radius: 0.6rem;
  padding: 0.55rem 0.75rem;
  font-size: 0.875rem;
  font-weight: 600;
  background: #fff;
  transition: border-color .15s ease, background-color .15s ease;
  cursor: pointer;
}
.btn-soft:hover { background: #f8fafc; border-color: #cbd5e1; }

/* ══════════════════════════════════════════════════════════════════════════
   MOBILE ADAPTATION
   ──────────────────────────────────────────────────────────────────────────
   Desktop is the source of truth: every rule here is scoped to a small-screen
   media query, so wide-viewport rendering is untouched.

   Breakpoints are declared ONCE here and reused. They mirror the boundaries
   the prebuilt tw.css already uses, so CSS and utility classes agree:
       phone   →  max-width: 639.98px   (below Tailwind `sm`)
       tablet− →  max-width: 1279.98px  (below Tailwind `xl`, used by filters)

   tw.css is generated from the templates by `make css`, and is now in sync
   with them (`make css-check` enforces that). The rules below are still
   hand-written rather than utility classes, because they express things
   Tailwind cannot: env() safe-area insets, dvh, :has(), and hit-area
   pseudo-elements.
   ══════════════════════════════════════════════════════════════════════════ */

/* Chat shell: unchanged on desktop (was an inline `calc(100vh - 160px)`);
   the mobile override lives in the phone block below. */
.chat-shell { height: calc(100vh - var(--chat-chrome)); }

/* Wide data: a scroll container is inert until its content actually exceeds
   it, so these are safe at every width (the tables fit on desktop). */
.table-scroll, .an-scroll, .sched-scroll { overflow-x: auto; }

@media (max-width: 639.98px) {
  /* ── Safe-area insets (notched devices) ──────────────────────────────────
     The page opts into `viewport-fit=cover`, which draws under the home
     indicator, so anything pinned to the bottom must inset itself or its tap
     targets fall partly under the indicator. */
  .mobile-nav { padding-bottom: env(safe-area-inset-bottom); }
  /* keep the page's bottom padding clear of nav + indicator */
  #main.pb-16 { padding-bottom: calc(var(--mobile-nav-h) + env(safe-area-inset-bottom)); }

  /* ── iOS zoom-on-focus ───────────────────────────────────────────────────
     Safari zooms the viewport whenever a focused control's text is under 16px.
     Every form control in the app inherits .field (14px), so this hit all of
     them. Buttons and non-text inputs are excluded, since they never trigger it. */
  .field,
  .nicesel-btn,
  select,
  textarea,
  input:not([type="range"]):not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="submit"]):not([type="button"]) {
    font-size: 16px;
  }
  /* A `text-sm`/`text-xs` utility on a control outranks the bare element
     selector above, so match its specificity. Deliberately not !important:
     that would also shrink any control intentionally set larger than 16px. */
  input.text-sm, input.text-xs,
  textarea.text-sm, textarea.text-xs,
  select.text-sm, select.text-xs { font-size: 16px; }

  /* ── Dynamic viewport height ─────────────────────────────────────────────
     Same reasoning as .map-sheet: `vh` ignores the collapsing URL bar, so
     these panels rendered taller than the screen and their lower edge (and in
     chat's case the composer) sat below the fold. */
  #cat-types, #cat-services { max-height: 70dvh; }
  #lightbox-img { max-height: 90dvh; }
  /* desktop formula + the bottom nav this page reserves on mobile */
  .chat-shell { height: calc(100dvh - var(--chat-chrome) - var(--mobile-nav-h) - env(safe-area-inset-bottom)); }

  /* ── Dashboard header ────────────────────────────────────────────────────
     The title and the five action buttons shared one non-wrapping row: 667px
     of content in a 343px box. Because <body> is overflow-x-hidden that was
     *clipped rather than scrollable*, so Schedule and Invite (which have no
     other entry point on mobile) were unreachable. Let both levels wrap. */
  .dash-header { flex-wrap: wrap; row-gap: .75rem; }
  .dash-actions { flex-wrap: wrap; }

  /* ── Rows that could not fit a 320px screen ──────────────────────────────
     Each of these was measured overflowing its own container at 320px. */

  /* Search/category: the results column is a grid item, so min-width:auto
     stopped it shrinking and pushed the page 30px wide. */
  .search-grid > * { min-width: 0; }

  /* Account avatar row: 64px avatar + dropzone + button overflowed by 40px.
     Give the dropzone its own line instead of crushing it to ~30px. */
  .avatar-form { flex-wrap: wrap; row-gap: .75rem; }
  .avatar-form [data-dropzone] { flex-basis: 100%; min-width: 0; }

  /* Booking rows: the middle column was squeezed to 79px while its own
     status+price line needed 108px. Drop the actions to their own line so
     the title/date/status column gets the full width. */
  .booking-row { flex-wrap: wrap; row-gap: .5rem; }
  .booking-actions { flex-basis: 100%; }

  /* Search results toolbar: Filters + map + save + the 9.5rem sort select add
     up to 334px in a 288px column, and the row could not wrap. Letting it wrap
     drops the sort control to its own line and keeps its fixed width intact. */
  .results-tools { flex-wrap: wrap; row-gap: .5rem; }

  /* ── User-supplied strings ───────────────────────────────────────────────
     Titles, search queries, emails, pasted URLs and referral links are single
     unbreakable tokens that otherwise set a min-content width wider than the
     screen. `anywhere` (not `break-word`) is required: only it affects
     intrinsic sizing, which is what forces the overflow. */
  h1, h2, code, .max-w-\[75\%\] { overflow-wrap: anywhere; }

  /* ── Wide data: scroll instead of crush ──────────────────────────────────
     Requirement is readability, not fitting everything on screen. Each of
     these keeps a legible minimum size and scrolls horizontally inside its
     own card, so the page itself never scrolls sideways. */

  /* Admin tables: 4 numeric columns needing 311px in a 288px card. */
  .table-scroll > table { min-width: max-content; }

  /* Analytics chart: 13 week columns shared ~240px, i.e. ~16px per bar with
     the date labels clipped to a character or two. Give every column a real
     width and let the chart scroll. `!important` is required because the
     flex sizing is an inline style on each column. */
  .an-chart { min-width: max-content; }
  .an-col { flex: 0 0 26px !important; }

  /* Day schedule: a fixed 48px hour gutter left ~208px of track, so three
     concurrent bookings became ~50px slivers with their titles fully
     ellipsed. Hold the track at a readable width and scroll. */
  .sched-grid { min-width: 34rem; }

  /* ── Forms ───────────────────────────────────────────────────────────────
     Side-by-side field pairs/triples stack on phones. The price row was the
     worst: price + currency + unit each got ~72px, and the subcategory select
     needed 174px in a 112px cell, pushing the page 44px wide. */
  .form-grid { grid-template-columns: 1fr; }

  /* "from <time> to <time>": two native time controls plus two labels
     cannot share one 288px line. */
  .time-row { flex-wrap: wrap; row-gap: .5rem; }

  /* Schedule's add-block form: its own children could not shrink. */
  .sched-form > * { min-width: 0; }

  /* ── Map "show list" button ──────────────────────────────────────────────
     Its left-1/2 / -translate-x-1/2 / z-20 utilities now come from tw.css, so
     only the vertical position is overridden here. The authored bottom-5
     (1.25rem) puts the button inside the fixed bottom nav's band, and the nav
     is z-40, so it covered the button and swallowed its taps, confirmed by
     hit-testing, which returned the nav. Clear the nav and the home indicator.
     The button is sm:hidden, so this can only ever affect phones. */
  #list-toggle {
    bottom: calc(var(--mobile-nav-h) + env(safe-area-inset-bottom) + .5rem);
  }
  /* The consent notice is also bottom-anchored and, at 295px tall on a 320px
     screen, lands squarely on this button. It is z-80 and must stay on top, so
     lift the button over it until the visitor has chosen; the offset is sized
     for the tallest (narrowest-screen) banner, and the button drops back to
     its normal place the moment the notice goes away. Browsers without :has()
     simply keep the previous behaviour. */
  body:has(#cookie-banner) #list-toggle {
    bottom: calc(var(--mobile-nav-h) + env(safe-area-inset-bottom) + 19rem);
  }

  /* ── Menu rows ───────────────────────────────────────────────────────────
     The header menus stack their rows with no gap, so the hit-area trick used
     elsewhere would make each row steal its neighbour's taps. Give them real
     height instead: 12px + 20px + 12px = 44px. Phone-only, so the desktop
     menu keeps its tighter rhythm. */
  .menu-panel a,
  .menu-panel button {
    padding-top: .75rem;
    padding-bottom: .75rem;
  }
  /* taller rows can outgrow a short phone, so let the menu scroll rather than
     run off the bottom of the screen */
  .menu-panel {
    max-height: calc(100dvh - 5rem);
    overflow-y: auto;
  }

  /* ── Body-copy floor: 14px ───────────────────────────────────────────────
     Read as applying to prose, not chrome. The cookie notice is the only
     body copy in the app below the floor (13.12px); it is also the one block
     of text a first-time visitor must actually read. Interface chrome (the
     11px bottom-nav labels, 12px card meta and badges) keeps its designed
     size, since raising those would change the visual identity rather than
     fix a readability problem. */
  .cc-text { font-size: 14px; }
}

/* ── Touch targets ─────────────────────────────────────────────────────────
   Grow the *hit area* to 44x44 with a centred pseudo-element rather than
   padding, so nothing moves and the design is untouched.

   Applied only to controls with clear space around them. An expanded target
   that overlapped its neighbour would silently steal that neighbour's taps --
   worse than a small target, and actively dangerous next to a destructive
   control. Deliberately NOT applied to: the profile dropdown rows (36px tall,
   stacked with no gap), footer link lists (4px apart), and the paired
   cover/delete buttons on media thumbnails, where the delete is destructive.
   Those are noted as a known gap rather than made hazardous.
   `pointer: coarse` so mouse users are unaffected. */
@media (max-width: 639.98px) and (pointer: coarse) {
  /* #list-toggle is deliberately absent from the position:relative list: it is
     already position:absolute, so it establishes its own containing block, and
     forcing relative would detach it from the map and undo the centring. */
  #near-me, #cat-btn, summary.no-marker, .cc-btn { position: relative; }
  #near-me::after, #cat-btn::after, summary.no-marker::after, .cc-btn::after,
  #list-toggle::after {
    content: "";
    position: absolute; left: 50%; top: 50%;
    transform: translate(-50%, -50%);
    width: 100%; height: 100%;
    min-width: 44px; min-height: 44px;
  }
}

/* ── Bouncing Bison chrome ──────────────────────────────────────────────────
   Page ground is Cloud Dancer, not slate. On the ink header/footer the accent
   hover/active state is Khaki: the kit's contrast table puts Lava Falls at
   2.35:1 on Ink, which is unreadable for small text. */
/* the page ground is slate again, so no override is needed */

/* Lockup sizes live here, not in utility classes: tw.css is prebuilt and has no
   w-44 / w-auto / h-auto, so those would silently do nothing and leave the mark
   at whatever width its column happened to be. The kit's minimum legible widths
   are 130px horizontal and 140px stacked. */
/* Listing sidebar. The booking card used to be the sticky element on its own,
   with the report block sitting after it in the sidebar's normal flow, so
   scrolling slid the report up and behind the pinned card. The whole sidebar
   sticks instead, and the two move as one.

   align-self matters: a grid item stretches to the row height by default, and a
   full-height item has no room left to stick in. Written out here rather than
   as lg:sticky / lg:self-start because tw.css is prebuilt and carries neither,
   so those classes would do nothing at all. Desktop only: below the two-column
   breakpoint the sidebar stacks under the listing, where pinning it would just
   trap it on screen. */
@media (min-width: 1024px) {
  .svc-side { position: sticky; top: 6rem; align-self: start; }
}

/* ── bison head mark ────────────────────────────────────────────────────────
   One silhouette, one echo. The head is currentColor so it comes out ink on a
   light surface and cloud on a dark one from a single asset, and the echo is
   the original lime. The counters are painted with whatever sits behind the
   mark rather than punched through, otherwise the echo would show as a lime
   eye where the two layers overlap. */
.bb-mark { height: 38px; width: 38px; display: block; flex: none; }
.bb-mark .bb-echo { fill: #a3e635; }
.bb-mark .bb-head { fill: currentColor; }
.bb-mark .bb-cut { fill: var(--bb-mark-bg, #ffffff); }
header .bb-mark { --bb-mark-bg: #0a2540; } /* the header is navy in both themes */

.brand-lockup-head { height: 36px; width: auto; max-width: 100%; }
.brand-lockup-foot { width: 176px; height: auto; max-width: 100%; }
/* footer mark: same asset as the header, larger, on the same indigo ground */
.brand-mark-foot .bb-mark { width: 64px; height: 64px; --bb-mark-bg: #0a2540; }

/* At 320px the 160px horizontal lockup squeezes the category button and wraps
   "Sign up" onto two lines, so swap to the icon mark rather than shrink the
   lockup under the kit's 130px legibility floor. The cutoff sits just below
   360px: every common phone from there up (360, 375, 390, 414) keeps the full
   wordmark. display:none keeps the hidden one out of the accessibility tree,
   so the name is announced once. */
.brand-mark-head { display: none; height: 34px; width: auto; }
@media (max-width: 359px) {
  .brand-lockup-head { display: none; }
  .brand-mark-head { display: block; }
}
/* lime on indigo clears 5:1, so the chrome uses the accent directly */

/* The footer is Ink in BOTH themes, so the global dark rule that dims
   .text-slate-300 to slate-500 lands on an already-dark surface and drags the
   link list down to 3.75:1. Hold slate-300 inside the footer. */
html.dark footer.text-slate-300 { color: #cbd5e1 !important; }

/* Same shape of problem in the consent sheet: Alexandrite is a link colour
   picked for the Cloud Dancer ground and reads at 2.5:1 on the dark panel.
   Lift it to the teal tint the rest of dark mode already uses. */
html.dark .cc-text a, html.dark .cc-link, html.dark .cc-always { color: #a3e635 !important; }
html.dark .cc-switch input:checked + .cc-slider { background: #4d7c0f; }
html.dark .cc-switch input:focus-visible + .cc-slider { outline-color: #a3e635; }

/* Serati (SIL OFL 1.1, GGBotNet): the identity's display face, italic cut.
   The logo SVGs are outlined, so nothing in the mark depends on this loading.

   The face covers ASCII and nothing else: no ă â î ș ț, no umlauts, no é or ß,
   not even @ or €. A browser resolves a missing glyph per character, so putting
   Serati on a heading that carries Romanian or German text splits the word
   across two faces mid-word: "Dog grooming in Zalău" renders as ZAL + Ă + U in
   two different typefaces. So it is opt-in through .bb-display, only for the
   English locale, and never on user-entered titles (service, album, profile).
   unicode-range leaves anything outside the covered range to the fallback. */
@font-face { font-family: "Serati"; src: url("/static/fonts/Serati-Regular.woff2") format("woff2"); font-weight: 400; font-style: normal; font-display: swap; unicode-range: U+0020-003F, U+0041-007E; }
@font-face { font-family: "Serati"; src: url("/static/fonts/Serati-Italic.woff2") format("woff2"); font-weight: 400; font-style: italic; font-display: swap; unicode-range: U+0020-003F, U+0041-007E; }
html[lang="en"] .bb-display { font-family: "Serati", Impact, "Arial Black", sans-serif; font-style: italic; font-weight: 400 !important; letter-spacing: .03em; }

/* Listing header: on a phone the title gets the full width and the price sits
   under it, instead of a narrow title column squeezed beside a price that
   wraps onto three lines. From 640px up (Tailwind's sm) they go side by side
   again, price on the right. The price never wraps ("4500 RON / day"). */
.svc-head { display: flex; flex-direction: column; gap: .25rem; }
.svc-head h1 { min-width: 0; overflow-wrap: anywhere; }
.svc-price { text-align: left; }
@media (min-width: 640px) {
  .svc-head { flex-direction: row; align-items: flex-start; justify-content: space-between; gap: 1rem; }
  .svc-price { text-align: right; flex: none; }
}

/* Blog article body. The posts are plain HTML fragments, so their typography
   lives here instead of as utility classes on every tag. */
.blog-prose { color: #334155; line-height: 1.7; }
.blog-prose > * + * { margin-top: 1rem; }
.blog-prose h2 { font-size: 1.35rem; font-weight: 700; color: #0f172a; margin-top: 2rem; }
.blog-prose h3 { font-size: 1.1rem; font-weight: 700; color: #0f172a; margin-top: 1.5rem; }
.blog-prose ul, .blog-prose ol { padding-left: 1.4rem; }
.blog-prose ul { list-style: disc; }
.blog-prose ol { list-style: decimal; }
.blog-prose li + li { margin-top: .35rem; }
.blog-prose a { color: #4d7c0f; text-decoration: underline; text-underline-offset: 2px; }
html.dark .blog-prose { color: #cbd5e1; }
html.dark .blog-prose h2, html.dark .blog-prose h3 { color: #e8eef7; }
html.dark .blog-prose a { color: #a3e635; }

/* Bulleted lists on the legal and privacy pages. */
.legal-list{list-style:disc;padding-left:1.25rem}
