/* App chrome — ported from Plant Hire App.dc.html's inline <style> block.
   The design canvas wrapped each screen in a device-frame mockup (.blueprint,
   .frame, .cap captions); those were presentation-only and are dropped here.
   Everything else — .ph, .nav, .chip, .kv, .cta — is the real app shell,
   with env(safe-area-inset-*) replacing the mockup's fixed notch/home-bar padding.

   This file is the MOBILE baseline (single column, bottom tab bar). The
   desktop dashboard layout lives in desktop.css and overrides from 1024px up;
   nothing here should need a max-width query. */

html, body { height: 100%; margin: 0; background: var(--color-bg); }
/* Any author rule that sets `display` outranks the UA stylesheet's
   [hidden] { display: none }, which silently defeats el.hidden = true.
   Restate it so the attribute keeps working wherever it is used. */
[hidden] { display: none !important; }
#app { min-height: 100dvh; display: flex; flex-direction: column; }
a { text-decoration: none; color: inherit; }

.screen {
  flex: 1; display: flex; flex-direction: column;
  padding-top: env(safe-area-inset-top);
}

.ph {
  position: relative; min-height: 100%; display: flex; flex-direction: column;
  background: var(--color-bg); font-family: var(--font-body); color: var(--color-text);
  font-size: 13px; line-height: 1.35;
}
.ph .h1 { font-family: var(--font-heading); font-weight: 600; letter-spacing: .01em; }
.pad { padding: 0 16px; }
.row { display: flex; align-items: center; gap: 10px; }
.hair { border: 1px solid var(--color-divider); }
.lbl { font-family: var(--font-heading); font-size: 11px; letter-spacing: .14em; text-transform: uppercase; color: var(--color-neutral-600); }

/* ---- Mode switch (top bar) --------------------------------------------
   order:-1 lifts this above every other child of .ph, so the nav helper can
   emit it without each screen having to place it. Touch targets are sized for
   a gloved hand on a site: 60px tall, half the bar wide, nothing else near. */
.topbar {
  order: -1; position: sticky; top: 0; z-index: 30;
  background: var(--color-bg); border-bottom: 1px solid var(--color-divider);
  padding: calc(8px + env(safe-area-inset-top)) 16px 8px;
}
.mode-switch { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; }
.mode-option {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 2px; min-height: 60px; padding: 8px 10px; text-align: center;
  border: 2px solid var(--color-neutral-300); background: var(--color-neutral-100);
  color: var(--color-neutral-700); cursor: pointer;
  transition: background .12s ease, color .12s ease, border-color .12s ease;
}
.mode-option .mode-label {
  font-family: var(--font-heading); font-weight: 600; font-size: 16px;
  letter-spacing: .06em; text-transform: uppercase; line-height: 1.1;
}
.mode-option .mode-hint { font-size: 11px; line-height: 1.2; opacity: .8; }
/* The active mode is a filled dark block against outlined grey - readable at a
   glance in sunlight, and not reliant on colour alone. */
.mode-option.on {
  background: var(--color-accent-900); border-color: var(--color-accent-900); color: #fff;
}
.mode-option.on .mode-hint { opacity: .75; }
.mode-option:not(.on):active { background: var(--color-neutral-200); }

/* ---- One-tap status selector ------------------------------------------ */
.status-picker { display: grid; grid-template-columns: repeat(3, 1fr); gap: 4px; margin-top: 10px; }
.status-option {
  min-height: 44px; padding: 8px 4px; cursor: pointer;
  font-family: var(--font-heading); font-weight: 600; font-size: 12px;
  letter-spacing: .04em; text-transform: uppercase; line-height: 1.15;
  color: var(--color-neutral-700); background: var(--color-neutral-100);
  border: 1px solid var(--color-neutral-300);
  transition: background .12s ease, color .12s ease, border-color .12s ease;
}
.status-option.on {
  background: var(--color-accent-900); border-color: var(--color-accent-900); color: #fff;
}
.status-option:not(.on):active { background: var(--color-neutral-200); }
/* Saving is a whole-group state: the tap already won, this is just feedback. */
.status-picker.saving { opacity: .55; pointer-events: none; }

.field-optional {
  font-family: var(--font-body); font-size: 11px; font-weight: 400;
  text-transform: none; letter-spacing: 0; color: var(--color-neutral-600);
  border: 1px solid var(--color-neutral-300); padding: 1px 6px; margin-left: 6px;
}

/* ---- Screen header ---------------------------------------------------- */
.page-head { padding-top: 56px; padding-bottom: 4px; }
.page-title { font-family: var(--font-heading); font-weight: 600; font-size: 24px; letter-spacing: .01em; margin: 0; }
.page-sub { font-size: 12px; color: var(--color-neutral-700); }
.page-head-actions { display: flex; gap: 8px; align-items: center; margin-top: 10px; }
.page-head-actions .btn { font-size: 12px; }

/* Onboarding blocks. These screens have no nav, so desktop.css turns the
   stack into a centred auth panel; the classes exist so it can do that
   without fighting inline padding. */
.auth-body { padding-top: 74px; }
.auth-head { padding-top: 56px; padding-bottom: 12px; border-bottom: 1px solid var(--color-divider); }
.auth-form { padding-top: 14px; }

/* ---- Content primitives ------------------------------------------------ */
/* .feed is the equipment stream: one column on mobile, a card grid on desktop. */
.feed { display: flex; flex-direction: column; gap: 10px; }
.card {
  border: 1px solid var(--color-divider); background: var(--color-bg);
  padding: 10px; display: flex; flex-direction: column;
}
.card-link { cursor: pointer; }
.card-title { font-family: var(--font-heading); font-weight: 600; font-size: 15px; letter-spacing: .01em; }
.card-meta { font-size: 12px; color: var(--color-neutral-700); }
.card-note { font-size: 11px; color: var(--color-neutral-600); margin-top: 4px; }
.card-tags { display: flex; align-items: center; gap: 5px; margin-top: 5px; flex-wrap: wrap; }
.card-foot { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-top: 6px; }
.card-price { font-family: var(--font-heading); font-weight: 600; font-size: 15px; }
.card-price small { font-size: 11px; color: var(--color-neutral-600); font-family: var(--font-body); font-weight: 400; }

/* Machine photos. A fixed aspect slot is rendered whether or not a listing has
   a photo, so a mixed feed still lines up in the desktop grid. */
.card-photo {
  display: block; width: 100%; aspect-ratio: 16 / 10; object-fit: cover;
  background: var(--color-neutral-200); border: 1px solid var(--color-divider);
  margin-bottom: 10px;
}
.card-photo-empty {
  display: flex; align-items: center; justify-content: center;
  font-family: var(--font-heading); font-size: 11px; letter-spacing: .14em;
  text-transform: uppercase; color: var(--color-neutral-500);
}
.detail-photo { margin-bottom: 14px; }
.detail-photo .card-photo { aspect-ratio: 16 / 9; margin-bottom: 0; }
.photo-preview {
  display: block; width: 100%; max-width: 260px; aspect-ratio: 16 / 10;
  object-fit: cover; margin-top: 8px; border: 1px solid var(--color-divider);
}

.stat-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.stat { border: 1px solid var(--color-divider); padding: 14px; }
.stat-value { font-family: var(--font-heading); font-weight: 600; font-size: 26px; line-height: 1.1; }

.chips { display: flex; gap: 7px; flex-wrap: wrap; }
/* .panel is a bordered surface on desktop only (see desktop.css) — it has no
   mobile styling at all, deliberately, so it can be combined with .form-grid. */
.form-grid { display: flex; flex-direction: column; gap: 12px; }

/* ---- Navigation -------------------------------------------------------- */
/* Mobile: bottom tab bar. The brand block, icons and footer are desktop-only. */
.nav {
  margin-top: auto; position: sticky; bottom: 0;
  border-top: 1px solid var(--color-divider); background: var(--color-bg);
  padding: 8px 0 calc(16px + env(safe-area-inset-bottom)); text-align: center;
}
.nav-brand, .nav-foot, .nav-icon { display: none; }
/* Sub-screens opt into a desktop-only sidebar without gaining a phone tab bar. */
.nav-aside { display: none; }
.nav-links { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; }
.nav-link {
  font-family: var(--font-heading); font-size: 11px; letter-spacing: .08em;
  text-transform: uppercase; color: var(--color-neutral-500); display: block;
}
.nav-link.on { color: var(--color-accent-700); }

.chip {
  font-family: var(--font-heading); font-size: 12px; letter-spacing: .06em;
  text-transform: uppercase; padding: 5px 9px; border: 1px solid var(--color-divider);
  white-space: nowrap; cursor: pointer;
}
.chip.on { background: var(--color-accent-900); color: #fff; border-color: var(--color-accent-900); }

.kv { display: flex; justify-content: space-between; gap: 12px; padding: 7px 0; border-bottom: 1px solid var(--color-divider); }
.kv span:first-child { color: var(--color-neutral-700); }
.kv span:last-child { font-variant-numeric: tabular-nums; }

.cta {
  position: sticky; bottom: 0; margin-top: auto; background: var(--color-accent-900);
  color: #fff; padding: 12px 16px calc(12px + env(safe-area-inset-bottom));
  display: flex; gap: 10px; align-items: center; justify-content: center; cursor: pointer;
  border: 0; width: 100%; font: inherit;
}
.cta-label { font-family: var(--font-heading); font-weight: 600; font-size: 15px; letter-spacing: .08em; text-transform: uppercase; }

.empty-note { text-align: center; color: var(--color-neutral-600); font-size: 13px; padding-top: 40px; }
.form-error { color: #b3261e; font-size: 12px; }

#toast {
  position: fixed; left: 50%; bottom: calc(96px + env(safe-area-inset-bottom));
  transform: translateX(-50%); background: var(--color-neutral-900); color: #fff;
  padding: 10px 16px; border-radius: 6px; font-size: 13px; z-index: 999;
  opacity: 0; transition: opacity .2s; pointer-events: none; max-width: 80vw; text-align: center;
}

/* Map field (js/screens/mapField.js). Leaflet is loaded from a CDN and may not
   arrive at all, so .map-canvas-empty styles the honest fallback rather than
   leaving an empty box that reads as a broken map. */
.map-field { margin-top: 6px; }
.map-canvas {
  width: 100%; border: 1px solid var(--color-divider); background: var(--color-neutral-100);
}
.map-canvas-empty {
  display: flex; align-items: center; justify-content: center; text-align: center;
  padding: 16px; font-size: 12px; color: var(--color-neutral-600);
}
.map-hint { font-size: 11px; color: var(--color-neutral-600); margin-top: 5px; }

/* A CSS pin, so no marker image is fetched - leaflet.css points its default
   icon at a relative path that 404s when Leaflet is served from a CDN. */
.map-pin { display: block; }
.map-pin-dot {
  display: block; width: 18px; height: 18px; border-radius: 50%;
  background: var(--color-accent); border: 3px solid var(--color-surface);
  box-shadow: 0 0 0 1px var(--color-neutral-500);
}

.card-edit {
  font-size: 11px; text-transform: uppercase; letter-spacing: .05em;
  color: var(--color-neutral-700); text-decoration: underline; white-space: nowrap;
}

/* Distance is a straight line from a possibly-approximate origin, so it is
   shown quietly next to the location rather than as a headline figure. */
.card-distance { font-size: 11px; color: var(--color-neutral-600); }

/* Filter panel. Collapsed by default - the keyword box and the category chips
   are the fast path, and most sessions never open this. */
.filter-panel {
  border: 1px solid var(--color-divider); border-top: none;
  padding: 14px; background: var(--color-surface);
}
.field-note { font-size: 11px; color: var(--color-neutral-600); margin-top: 5px; }

/* Fleet tiles are links into the matching filtered listings, so they have to
   read as pressable - a bare count looked like a dead end. */
a.stat { display: block; text-decoration: none; color: inherit; cursor: pointer; }
a.stat:hover, a.stat:focus-visible { border-color: var(--color-accent); }

/* A removed machine is still the owner's, but it must not read as live at a
   glance - it is off the marketplace until they put it back. */
.card-removed { opacity: .72; }
.card-removed .card-photo { filter: grayscale(1); }

/* Photo strip. CSS scroll-snap rather than a carousel library - this project
   carries no UI dependencies, and swiping is what a phone user expects. */
.photo-strip {
  display: flex; gap: 6px; overflow-x: auto; scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch; padding-bottom: 4px;
}
.photo-strip-item {
  flex: 0 0 88%; scroll-snap-align: center; aspect-ratio: 16 / 10;
  object-fit: cover; border: 1px solid var(--color-divider);
}
@media (min-width: 768px) { .photo-strip-item { flex-basis: 46%; } }

/* Gallery management on the edit screen. */
.gallery-manage { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
.gallery-thumb { margin: 0; width: 108px; border: 1px solid var(--color-divider); }
.gallery-thumb img { display: block; width: 100%; aspect-ratio: 4 / 3; object-fit: cover; }
.gallery-thumb figcaption { display: flex; gap: 2px; padding: 4px; }
.gallery-thumb button {
  flex: 1; font-size: 10px; padding: 3px 2px; cursor: pointer;
  border: 1px solid var(--color-divider); background: var(--color-surface);
}
.gallery-thumb button[disabled] { opacity: .4; cursor: default; }

/* Trust signals. Each figure is labelled and separate on purpose - they are
   not folded into one score, because the data behind them does not support
   that kind of precision. */
.trust-panel { display: flex; flex-wrap: wrap; gap: 6px; margin: 10px 0 4px; }
.trust-item {
  font-size: 11px; padding: 3px 8px; border: 1px solid var(--color-divider);
  color: var(--color-neutral-700);
}
.trust-verified { color: var(--color-accent); border-color: var(--color-accent); }

/* ── Launch UX pass ─────────────────────────────────────────────────── */

/* Category chips: one row, swipe sideways. Wrapped, fifteen of them stacked
   five rows deep and pushed the first machine below the fold. */
.chips-scroll {
  flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch;
  scrollbar-width: none; padding-bottom: 2px;
}
.chips-scroll::-webkit-scrollbar { display: none; }
.chips-scroll .chip { white-space: nowrap; flex: 0 0 auto; }

/* The results toolbar: count · location · sort · filters. */
.results-bar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.results-count { font-size: 12px; color: var(--color-neutral-700); margin-right: auto; }
.input-compact { width: auto; padding: 6px 8px; font-size: 12px; }
.results-bar .chip { cursor: pointer; }

/* The money bar: WhatsApp and Call never scroll away. WhatsApp wears its own
   green because recognisability IS the affordance; Call stays in the app's
   palette. Deliberate exception to the token rule, matching the brand colour
   of the service being opened. */
.cta-bar {
  position: sticky; bottom: 0; display: flex; z-index: 4;
}
.cta-action {
  flex: 1; text-align: center; padding: 16px 10px; font-family: var(--font-heading);
  font-weight: 600; font-size: 14px; letter-spacing: .08em; text-transform: uppercase;
  text-decoration: none; color: #fff;
}
.cta-whatsapp { background: #1faa53; }
.cta-call { background: var(--color-neutral-900); }

/* Signed-out top bar: one honest line and a way in. */
.topbar-signedout {
  display: flex; align-items: center; gap: 10px; padding: 10px 14px;
  background: var(--color-surface); border-bottom: 1px solid var(--color-divider);
}
.topbar-note { font-size: 11px; color: var(--color-neutral-700); flex: 1; }
.topbar-signin { white-space: nowrap; }

/* Section headers inside the machine form: same fields, grouped so nineteen
   of them read as five short steps instead of one wall. */
.form-section {
  font-size: 12px; font-family: var(--font-heading); font-weight: 600;
  text-transform: uppercase; letter-spacing: .06em; color: var(--color-neutral-700);
  border-bottom: 1px solid var(--color-divider); padding-bottom: 6px;
  margin-top: 14px;
}

/* Category silhouette where no photo exists yet: same stroke language as the
   nav icons, quiet accent tint, honest caption. Replaces the flat grey
   "NO PHOTO" box that read as a broken app rather than a young listing. */
.card-photo-art {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 4px; background: var(--color-neutral-100); color: var(--color-neutral-500);
}
.card-photo-art svg { width: 46%; max-width: 200px; height: auto; }
.card-photo-note {
  font-size: 10px; text-transform: uppercase; letter-spacing: .08em;
  color: var(--color-neutral-600);
}

/* One quiet line under the trust chips explaining the availability loop -
   the differentiator a first-time hirer would otherwise not know is special. */
.trust-explain { font-size: 11px; color: var(--color-neutral-600); margin: 2px 0 8px; line-height: 1.5; }

/* ── Website pass ──────────────────────────────────────────────────── */

/* Footer links inside the app. On mobile it sits at the end of the scroll
   above the tab bar; desktop restyles it into a proper site footer. */
.site-links {
  margin-top: 26px; padding-top: 18px; padding-bottom: 18px;
  border-top: 1px solid var(--color-divider);
}
.site-links nav { display: flex; gap: 16px; flex-wrap: wrap; }
.site-links a {
  font-size: 12px; color: var(--color-neutral-700); text-decoration: none;
  text-transform: uppercase; letter-spacing: .06em; font-family: var(--font-heading);
}
.site-links a:hover { color: var(--color-accent-700); text-decoration: underline; }
.site-links-note { font-size: 11px; color: var(--color-neutral-600); margin-top: 10px; }

/* Blog */
.blog-card .card-title { font-size: 17px; }
.blog-excerpt { margin-top: 6px; line-height: 1.55; }
.blog-body { font-size: 15px; line-height: 1.7; }
.blog-body p { margin: 0 0 14px; }
.blog-video { aspect-ratio: 16 / 9; margin-bottom: 16px; }
.blog-video iframe { width: 100%; height: 100%; border: 0; }

/* The brand block became a link (logo = way home); keep it looking identical.
   No display property here - the mobile bar hides the brand and the desktop
   sidebar shows it, and those existing rules must keep the final say. */
a.nav-brand { text-decoration: none; color: inherit; }
