/* gohome.css — /p/gohome (Star Buddy). Design source: GOHOME_REQUIREMENTS.md.
   Space theme only — reuses p/tokens.css custom properties, no new colors. */

/* Every top-level /p page sets its own body background+color (no shared base
   stylesheet does this) — same block as find.css/my.css/tickets.css etc. */
body { margin: 0; background: var(--space-gradient), var(--bg-primary); background-repeat: no-repeat; background-attachment: fixed; color: var(--text-primary); font-family: var(--font-thai); }

.gh-page {
  max-width: 480px;
  margin: 0 auto;
  padding: 16px 16px 48px;
  display: flex;
  flex-direction: column;
  gap: 22px;
}
/* Several cards below use <a> for full-card click targets — reset the
   browser's default link styling (blue + underline) so they read as cards,
   not links. */
.gh-page a { color: inherit; text-decoration: none; }
/* ...except .tk-back (tickets.css), which is an intentionally muted nav
   link — needs higher specificity to win back its color from the reset above. */
.gh-page .tk-back { color: var(--text-secondary); }

/* Phase-disabled state (§2 phase_config.gohome_interactive) — same pattern as find.css */
.gh-disabled {
  margin: 60px auto;
  /* width, not just max-width (2026-08-05 fix, "กล่องข้อความชิดขอบจอ") —
     this card is appended straight to #gohome-root, which has no padding
     of its own (unlike .gh-page), so on any screen narrower than the
     340px cap the card's own visible background/border sat flush against
     the physical screen edge with zero gutter. Guarantees a 16px minimum
     side gutter while still capping+centering at 340px on wider screens. */
  width: calc(100% - 32px);
  max-width: 340px;
  text-align: center;
  padding: 32px 20px;
  background: var(--bg-secondary);
  border: 1px solid var(--border);
  border-radius: 16px;
}
.gh-disabled__icon { font-size: 32px; margin-bottom: 10px; }
.gh-disabled__title { font-size: 16px; font-weight: 700; margin-bottom: 6px; }
.gh-disabled__subtext { font-size: 12px; color: var(--text-secondary); line-height: 1.5; }
.gh-disabled__chip { margin-top: 14px; display: inline-block; padding: 5px 12px; border-radius: 999px; background: rgba(var(--surface-rgb),0.06); font-size: 11px; color: var(--text-secondary); }

/* Not-logged-in gate (§5.1) — same visual pattern as .my-empty-tickets
   (my.css) so the "you need to log in / add a ticket" moment looks
   consistent across /p pages. */
.gh-auth-gate {
  display: flex; flex-direction: column; align-items: center; gap: 14px;
  text-align: center; padding: 48px 36px;
  /* width, not just max-width — same edge-gutter fix as .gh-disabled above. */
  width: calc(100% - 32px); max-width: 420px; margin: 60px auto;
  background:
    radial-gradient(circle at 50% 0%, rgba(var(--accent-gold-rgb),0.16), transparent 60%),
    var(--bg-secondary);
  border: 1px solid var(--border);
  border-radius: 22px;
  box-shadow: 0 24px 60px rgba(var(--shadow-rgb), calc(0.35 * var(--shadow-mult)));
}
.gh-auth-gate__icon {
  width: 84px; height: 84px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%; font-size: 42px;
  background: rgba(var(--accent-gold-rgb),0.14);
  box-shadow: 0 0 34px rgba(var(--accent-gold-rgb),0.28);
}
.gh-auth-gate__title { font-size: 21px; font-weight: 800; color: var(--text-primary); }
.gh-auth-gate__subtext { font-size: 14px; color: var(--text-secondary); line-height: 1.6; }
/* Big, full-width, trailing arrow (2026-08-05 ask, "คล้ายที่ popup
   เพิ่มบัตรอ่ะ") — same visual language as modals.js's showNoTicketModal()
   .noticket-modal__btn-primary (auth.css). */
.gh-auth-gate__cta {
  margin-top: 6px;
  width: 100%;
  display: flex; align-items: center; justify-content: center; gap: 8px;
  padding: 16px 26px;
  border-radius: 12px;
  /* Fixed gradient, not theme-following (2026-08-08 fix) — same drift-from-
     reference bug as p/homesafe.css's identical .hs-auth-gate__cta. */
  background: linear-gradient(120deg, #ffb852, #ed8420);
  color: #12121A;
  font: 700 16px var(--font-thai);
  text-decoration: none;
  cursor: pointer;
  box-shadow: 0 10px 24px rgba(var(--accent-gold-rgb),0.3);
  transition: filter 0.15s ease;
}
.gh-auth-gate__cta:hover { filter: brightness(1.06); }
.gh-auth-gate__cta-arrow { display: inline-flex; font-size: 15px; }

/* Search box (§9) */
.gh-search { position: relative; }
.gh-search__input {
  width: 100%;
  padding: 13px 14px;
  border-radius: 12px;
  border: 1px solid var(--border);
  background: var(--bg-secondary);
  color: var(--text-primary);
  font: 500 14px var(--font-thai);
}
.gh-search__input::placeholder { color: var(--text-secondary); }
.gh-search__input:focus { outline: none; border-color: var(--accent-teal); }
/* Home hero search icon (2026-07-15 ask) — magnifying glass inside the field. */
.gh-search--icon .gh-search__input { padding-left: 40px; }
.gh-search__icon {
  position: absolute; left: 14px; top: 50%; transform: translateY(-50%);
  color: var(--text-secondary); font-size: 15px;
  display: flex; align-items: center; pointer-events: none;
}
.gh-search__results {
  position: absolute;
  left: 0; right: 0; top: calc(100% + 6px);
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 8px;
  z-index: 20;
  max-height: 320px;
  overflow-y: auto;
  box-shadow: 0 12px 28px rgba(var(--shadow-rgb), calc(0.4 * var(--shadow-mult)));
}
.gh-search__group-label { font-size: 11px; font-weight: 700; color: var(--text-secondary); padding: 6px 8px 2px; }
.gh-search__result {
  display: flex; align-items: center; gap: 8px;
  width: 100%; text-align: left;
  padding: 9px 8px; border-radius: 8px;
  background: none; border: none; color: var(--text-primary);
  font: 500 13px var(--font-thai); cursor: pointer;
}
.gh-search__result:hover { background: rgba(var(--surface-rgb),0.06); }
.gh-search__result-sub { color: var(--text-secondary); font-size: 11px; margin-left: auto; }
.gh-search__result-text { display: flex; flex-direction: column; gap: 1px; }
.gh-search__result-en { font-size: 10px; color: var(--text-secondary); }
.gh-search__empty { padding: 14px 8px; font-size: 12px; color: var(--text-secondary); text-align: center; }

/* Home hero (2026-07-15 redesign, replaces the boxed-search-in-a-box pass)
   — greeting + search as one booking-site-style block: a gradient backdrop
   the search field sits on, not a card nested inside another card. */
.gh-home-hero {
  padding: 26px 18px 20px; border-radius: 20px;
  border: 1px solid var(--border);
  background:
    radial-gradient(ellipse 460px 220px at 50% 0%, rgba(var(--accent-teal-rgb),0.12), transparent 70%),
    var(--bg-secondary);
  text-align: center;
}
.gh-home-hero__title { font-size: 21px; font-weight: 800; margin-bottom: 16px; }
.gh-home-hero .gh-search__input { min-height: 50px; text-align: left; }

/* Clear separation between hero / wallet / browse (2026-07-15 ask) */
.gh-home-divider { height: 0; border-top: 1px solid var(--border); margin: 2px 0; }

/* Cross-link to Safe Landing (2026-08-05 ask) */
.gh-safelanding-link {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 16px;
  border-radius: 16px;
  border: 1px solid var(--border);
  background: rgba(79, 209, 135, 0.06);
  text-decoration: none;
  color: inherit;
}
.gh-safelanding-link:hover { border-color: rgba(79, 209, 135, 0.35); }
.gh-safelanding-link__icon { font-size: 22px; flex-shrink: 0; }
.gh-safelanding-link__text { flex: 1; min-width: 0; }
.gh-safelanding-link__title { font: 700 14px var(--font-thai); color: var(--text-primary); }
.gh-safelanding-link__sub { font-size: 11.5px; color: var(--text-secondary); line-height: 1.5; margin-top: 2px; }
.gh-safelanding-link__arrow { width: 16px; height: 16px; flex-shrink: 0; color: var(--text-secondary); display: inline-flex; }
.gh-safelanding-link__arrow svg { width: 100%; height: 100%; }

/* Section shared */
.gh-section-title { font-size: 18px; font-weight: 700; margin-bottom: 10px; } /* 15px + 20% (2026-07-16 ask) */
.gh-section-sub { font-size: 11px; color: var(--text-secondary); margin-top: -6px; margin-bottom: 10px; }
.gh-browse-note { font-size: 10px; color: var(--text-secondary); opacity: 0.75; line-height: 1.5; margin-top: -6px; margin-bottom: 10px; }

/* Section B — wallet (status-ordered stack, one card per round) */
.gh-wallet { display: flex; flex-direction: column; gap: 10px; }
.gh-wallet-card {
  border-radius: 14px;
  border: 1px solid var(--border);
  background: var(--bg-secondary);
  padding: 14px;
}
/* Generic "plan another round" placeholder (2026-07-16 ask) — the only card
   class left that renders this (the old per-round "+ หาเพื่อน" card was
   replaced by renderWalletPlaceholder(), gohome.js). Small/compact, back to
   the original row layout (2026-07-16 follow-up — the bigger centered
   treatment below is now reserved for the zero-plan --empty state only). */
.gh-wallet-card--plus {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  cursor: pointer; border-style: dashed;
  transition: opacity 0.15s ease, border-color 0.15s ease;
}
.gh-wallet-card--plus:hover { border-color: var(--accent-gold); }
/* Faded variant (has at least one real plan already) — secondary to the
   planned card(s) above it, so this one stays dimmed. */
.gh-wallet-card--faded { opacity: 0.3; }
.gh-wallet-card--faded:hover { opacity: 0.6; }
.gh-wallet-card--plus .gh-wallet-round { font-size: 13px; font-weight: 400; } /* was 15px — too big (2026-07-16 ask) */
.gh-wallet-card--plus .gh-wallet-cta { color: var(--accent-gold); font-size: 13px; font-weight: 700; }
/* Zero-plan state only (2026-07-16 ask) — centered, generously padded, and
   a real pill CTA, since it's the only card on the page in this state and
   should read as inviting rather than a dense row like every other card. */
.gh-wallet-card--empty {
  flex-direction: column; text-align: center;
  gap: 12px; padding: 28px 20px;
}
.gh-wallet-card--empty .gh-wallet-round { color: var(--text-secondary); line-height: 1.5; } /* slightly faded (2026-07-16 ask) */
.gh-wallet-card--empty .gh-wallet-cta {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 8px 20px; border-radius: 999px;
  background: rgba(var(--accent-gold-rgb),0.14); border: 1px solid rgba(var(--accent-gold-rgb),0.4);
}
.gh-wallet-card--empty:hover .gh-wallet-cta { background: rgba(var(--accent-gold-rgb),0.24); }
/* 2026-08-08 fix — same badge-pattern issue as elsewhere (text = accent-gold
   sitting on a translucent tint of itself), measured 2.46:1 in light mode.
   Same value already established for this exact role (nav tag/CTA, see
   LIGHT_MODE_COLOR_BIBLE.md §4 item 12) — not a new derivation. */
:root[data-theme="light"] .gh-wallet-card--plus .gh-wallet-cta { color: #9C5809; }

/* Locked/passed — grayscale (no color left at all) so it never reads as
   "just dim" the way the not-booked-yet card above does; those two states
   mean opposite things ("hurry, still time" vs. "too late now"). */
.gh-wallet-card--locked { display: flex; align-items: center; justify-content: space-between; gap: 10px; opacity: 0.6; filter: grayscale(0.7); cursor: pointer; }
.gh-wallet-card--locked .gh-wallet-round { font-size: 15px; font-weight: 700; }
.gh-wallet-card--locked .gh-wallet-badge { font-size: 11px; color: var(--text-secondary); }

.gh-wallet-card--planned { border-top: 2px solid var(--accent-teal); padding: 0; overflow: hidden; }
.gh-wallet-card--planned.is-success { border-top-color: var(--accent-gold); }
.gh-wallet-round { font-size: 16px; font-weight: 700; }
/* Date half of "รอบ N · date" smaller/lighter than the round number
   (2026-07-16 ask) — was one uniform text node. */
.gh-wallet-round__date { font-size: 12px; font-weight: 400; color: var(--text-secondary); }
.gh-wallet-dest { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--text-secondary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-top: 4px; }

/* Peek + expand (2026-07-15 redesign) — collapsed by default, same visual
   weight as a myPPW ticket's peek row, so this section doesn't dominate the
   page just by always showing every detail. */
.gh-wallet-peek {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 14px; cursor: pointer;
}
.gh-wallet-peek__main { min-width: 0; }
.gh-wallet-peek__right { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }
.gh-wallet-peek__success { font-size: 15px; }
.gh-wallet-chevron { color: var(--text-secondary); font-size: 18px; transition: transform 0.15s ease; }
.gh-wallet-card--planned.is-open .gh-wallet-chevron { transform: rotate(90deg); color: var(--accent-teal); }

.gh-wallet-detail {
  max-height: 0; opacity: 0; overflow: hidden;
  padding: 0 14px; transition: max-height 0.2s ease, opacity 0.2s ease, padding 0.2s ease;
}
.gh-wallet-card--planned.is-open .gh-wallet-detail {
  max-height: 260px; opacity: 1; padding: 0 14px 14px;
}
.gh-wallet-badge--role { display: inline-block; font-size: 10px; padding: 2px 7px; border-radius: 999px; background: rgba(var(--surface-rgb),0.08); color: var(--text-secondary); white-space: nowrap; }
.gh-wallet-badge--success { font-size: 11px; color: var(--accent-gold); font-weight: 700; margin-top: 6px; }
.gh-wallet-row2 { display: flex; gap: 8px; margin-top: 10px; }
.gh-wallet-action {
  flex: 1; min-height: 44px;
  border-radius: 10px; border: 1px solid var(--border);
  background: rgba(var(--surface-rgb),0.04);
  color: var(--text-primary); font: 600 12px var(--font-thai);
  cursor: pointer; white-space: nowrap;
}
.gh-wallet-action--danger { color: var(--like-red); border-color: rgba(var(--like-red-rgb),0.4); }
.gh-wallet-action--revert { color: var(--accent-teal); border-color: rgba(var(--accent-teal-rgb),0.4); }
.gh-wallet-action:disabled { opacity: 0.5; cursor: default; }

.gh-wallet-links { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--border); }
.gh-wallet-view-route { font: 700 12px var(--font-thai); color: var(--accent-teal); }
/* Now a <button> (was an <a>, 2026-07-16 popup change) — needs its own
   box-model reset since .gh-wallet-view-route's <a> never needed one. */
.gh-wallet-edit { font: 600 12px var(--font-thai); color: var(--text-secondary); background: none; border: none; padding: 0; cursor: pointer; }
.gh-wallet-edit:hover { color: var(--accent-teal); }

/* Section A — browse (destination cards) */
.gh-browse-filter { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 12px; }
.gh-browse-filter__group { flex: 1 1 140px; display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.gh-browse-filter__label { font-size: 11px; color: var(--text-secondary); }
.gh-browse-filter__select {
  width: 100%; min-height: 44px; padding: 0 10px;
  border-radius: 10px; border: 1px solid var(--border);
  background: var(--bg-secondary); color: var(--text-primary);
  font: 600 12px var(--font-thai);
}
.gh-dest-grid { display: flex; flex-direction: column; gap: 10px; }
/* --line-color set inline per card (the destination's primary transit
   line) — falls back to the plain border when a destination has no line
   nearby, so cards without transit data look exactly as before. */
.gh-dest-card {
  --line-color: var(--border);
  position: relative;
  border-radius: 14px; border: 1px solid var(--border);
  border-left: 3px solid var(--line-color);
  background: var(--bg-secondary); padding: 14px;
  cursor: pointer; display: flex; flex-direction: column; gap: 6px;
}
.gh-dest-card:hover { border-color: var(--accent-teal); border-left-color: var(--line-color); }
.gh-dest-card__cat { font-size: 10px; color: var(--text-secondary); }
.gh-dest-card__popular {
  position: absolute; top: 10px; right: 12px;
  font-size: 9px; font-weight: 700; color: var(--accent-gold);
  display: inline-flex; align-items: center; gap: 3px;
}
.gh-dest-card__name { font-size: 14px; font-weight: 700; }
.gh-dest-card__name-en { font-size: 10px; color: var(--text-secondary); margin-top: 2px; }
/* Line badges (2026-07-16 ask) — small colored pills replacing the old
   run-on "จุดตัด X x Y" sentence; --line-color is set inline per badge. */
.gh-line-badges { display: flex; flex-wrap: wrap; gap: 4px; }
.gh-line-badge {
  font-size: 9px; font-weight: 700; padding: 2px 7px; border-radius: 999px;
  background: color-mix(in srgb, var(--line-color) 22%, transparent);
  border: 1px solid color-mix(in srgb, var(--line-color) 45%, transparent);
  color: var(--line-color); white-space: nowrap;
}
/* Darkened text for light mode (2026-08-08 fix) — --line-color is each
   transit line's real official brand color (BTS/MRT), set inline per badge
   in JS (p/data-p.js), with no light/dark variant of its own — several
   (MRT yellow/pink, BTS Sukhumvit/Silom) measured as low as 1.37:1 in
   light mode since the badge tints its own background with the same
   color. color-mix() darkens toward black while preserving hue (so the
   line is still recognizably "the yellow/pink/green line") without
   needing a second hardcoded color per line. */
:root[data-theme="light"] .gh-line-badge { color: color-mix(in srgb, var(--line-color) 55%, black); }
/* Shared pill chip look — post-wizard group/sub/method pickers
   (gohome-post.js), the wants-gender modal's "ทุกเพศ" chip, and the
   edit-plan popup's method/role rows (gohome-shared.js's renderChipRow) all
   still use this. Accidentally deleted in the 2026-07-16 browse-card
   decluttering pass even though the comment below claimed it stayed —
   restored (2026-07-16 fix) after it was reported rendering as plain white
   browser buttons everywhere else that reused the class. */
.gh-dest-chip {
  font-family: var(--font-thai); /* buttons don't inherit font-family by default */
  font-size: 11px; padding: 4px 9px; border-radius: 999px;
  background: rgba(var(--surface-rgb),0.06); border: 1px solid transparent;
  color: var(--text-secondary); cursor: pointer;
  white-space: nowrap; flex-shrink: 0;
}
.gh-dest-chip.is-selected { border-color: var(--accent-teal); color: var(--accent-teal); background: rgba(var(--accent-teal-rgb),0.1); }
.gh-dest-chip:hover { border-color: var(--accent-teal); color: var(--accent-teal); }
/* Stat row (2026-07-16 ask, redesigned again — a pill "chunk" for the total
   still read as just another chip) — a horizontal divider line separates
   this row from the destination info above; within the row, total sits
   plain left, per-method breakdown plain right. No pill/background on
   either side — that's what made it read as chip clutter in the first
   place. Reuses neither .gh-dest-chip (still the pill style for
   post-wizard pickers). */
.gh-dest-card__methods {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 6px 10px;
  /* margin-top: auto (2026-07-16 ask) — pins the divider+stats row flush to
     the card's bottom edge regardless of how much content sits above it
     (name/badges vary in length card to card). .gh-dest-card is already a
     column flex box, and .gh-dest-grid's CSS Grid stretches every card in a
     row to the tallest one's height by default, so this actually has room
     to push down into on cards shorter than their row-mates. */
  margin-top: auto; padding-top: 10px; border-top: 1px solid var(--border);
}
/* margin-left:auto + justify-content:flex-end (2026-07-16 ask) — when the
   card is too narrow for both on one line, this block wraps onto its own
   full-width row and still hugs the right edge instead of sliding left
   (the old shrink-to-fit approach left it stranded mid-card on 2-col
   desktop widths). */
.gh-dest-card__methods-list { display: flex; align-items: center; justify-content: flex-end; gap: 10px; flex-wrap: wrap; margin-left: auto; }
.gh-dest-stat {
  font-family: var(--font-thai); font-size: 12px; font-weight: 400;
  color: var(--text-secondary); background: none; border: none; padding: 0;
  cursor: pointer; white-space: nowrap;
}
.gh-dest-stat:hover { color: var(--accent-teal); }
.gh-dest-stat--total {
  display: inline-flex; align-items: center; gap: 5px;
  font-family: var(--font-thai); font-size: 12px; font-weight: 700;
  color: var(--accent-teal); white-space: nowrap;
}
.gh-dest-stat--total__icon { display: inline-flex; font-size: 11px; }
/* Shared inline SVG-icon-in-text spot (2026-07-16 ask) — replaces a plain
   👥 emoji wherever "อยากได้เพื่อน..." text appears (route card, wizard
   review, edit-plan popup); fill="currentColor" on the SVG itself means it
   always matches whatever color the surrounding text already has. */
.gh-icon-inline { display: inline-flex; vertical-align: -2px; }

/* Empty state (shared shape, §5.1/§8) */
.gh-empty {
  text-align: center; padding: 26px 16px;
  border: 1px dashed var(--border); border-radius: 14px;
  color: var(--text-secondary); font-size: 12px;
}

/* BottomSheet (§15, MOBILE_STRATEGY §B3 item 8 — first build in this codebase) */
/* Body scroll-lock while any sheet is open (bottom-sheet.js) — prevents the
   background page from scrolling under the fixed overlay, which on mobile
   can shift the browser's dynamic toolbar and add to the sheet's perceived
   jump at the bottom edge. */
.gh-sheet-lock { overflow: hidden; }
.gh-sheet-overlay {
  position: fixed; inset: 0; z-index: 300;
  background: rgba(4,4,14,0.65);
  backdrop-filter: blur(2px);
  display: flex; align-items: flex-end; justify-content: center;
  opacity: 0; transition: opacity 0.16s ease;
}
.gh-sheet-overlay.is-open { opacity: 1; }
.gh-sheet {
  position: relative;
  width: 100%; max-width: 480px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-bottom: none;
  border-radius: 22px 22px 0 0;
  box-shadow: 0 -16px 48px rgba(var(--shadow-rgb), calc(0.5 * var(--shadow-mult)));
  /* translateZ(0) appended (2026-07-27) — same iOS backdrop-filter
     compositing fix as .emoji-icon (p/tokens.css): this sheet is a DOM
     child of .gh-sheet-overlay, which has backdrop-filter, and carries raw
     inline-SVG + emoji icon content not fully covered by that rule alone.
     Combined into the same `transform` as the existing translateY (not a
     separate declaration) so it isn't silently dropped — same requirement
     applies to the .is-open override below. */
  transform: translateY(100%) translateZ(0);
  /* Fast, plain-ease CLOSE by default — the spring only applies to the OPEN
     transition below. A symmetric spring on both directions was a 2026-07-27
     regression: closing took the full 450ms, so a chained openBottomSheet()
     call right after (gohome.js's method→round step flow) appended the next
     sheet's overlay while the previous one was still mid-exit, stacking two
     dim backdrops for most of that window. Closing fast avoids the overlap;
     opening keeps the springy settle-with-overshoot feel. */
  transition: transform 0.2s ease;
}
.gh-sheet-overlay.is-open .gh-sheet { transform: translateY(0) translateZ(0); transition: transform 0.45s var(--ease-spring); }
/* Scrollable inner wrapper (2026-08-01) — max-height/overflow-y/padding used
   to live directly on .gh-sheet; moved here so .gh-sheet itself stays
   overflow:visible and doesn't clip .gh-sheet__skirt below. */
.gh-sheet__scroll {
  padding: 12px 20px calc(24px + env(safe-area-inset-bottom, 0px));
  max-height: 74vh; overflow-y: auto;
  background: radial-gradient(ellipse 420px 200px at 50% 0%, rgba(var(--accent-teal-rgb),0.08), transparent 70%);
  border-radius: inherit;
}
/* Skirt (2026-08-01) — the open spring above overshoots translateY past 0
   before settling, which briefly pulls the sheet's bottom edge up and away
   from the viewport's bottom edge (measured ~14px gap at the overshoot
   peak). This extends the sheet's own background below its bottom edge so
   that gap still reads as "more sheet, off-screen" rather than a hole
   showing the dim backdrop through — the sheet stays visually rooted to
   the bottom of the screen through the whole bounce. */
.gh-sheet__skirt {
  position: absolute; left: 0; right: 0; top: 100%;
  height: 40px;
  background: var(--bg-elevated);
  border-left: 1px solid var(--border);
  border-right: 1px solid var(--border);
  pointer-events: none;
}
.gh-sheet__handle { width: 36px; height: 4px; border-radius: 999px; background: rgba(var(--surface-rgb),0.18); margin: 4px auto 14px; }

.gh-sheet__dots { display: flex; align-items: center; justify-content: center; gap: 6px; margin-bottom: 12px; }
.gh-sheet__dot { width: 6px; height: 6px; border-radius: 999px; background: rgba(var(--surface-rgb),0.15); transition: background 0.15s ease, width 0.15s ease; }
.gh-sheet__dot.is-active { background: var(--accent-teal); width: 16px; }
.gh-sheet__dots-count { font: 700 10px var(--font-thai); color: var(--text-secondary); margin-left: 4px; }

.gh-sheet__title-row { display: flex; align-items: center; justify-content: center; min-height: 26px; }
.gh-sheet__title { font-size: 17px; font-weight: 800; text-align: center; }
/* Bottom, plain text (2026-07-16 ask) — matches .gh-post-back's style in the
   post wizard exactly, instead of the pill-chip pinned to the title row this
   used to be. */
.gh-sheet__back {
  display: flex; align-items: center; justify-content: center; gap: 4px;
  margin: 4px auto 0;
  background: none; border: none;
  color: var(--text-secondary);
  font: 600 11px var(--font-thai); cursor: pointer; padding: 6px 4px;
}
.gh-sheet__back:hover { color: var(--accent-gold); }
.gh-sheet__subtitle { font-size: 13px; color: var(--text-secondary); text-align: center; margin-top: 6px; }

.gh-sheet__options { display: flex; gap: 10px; margin-top: 20px; }
.gh-sheet__options--list { flex-direction: column; }
.gh-sheet__options--grid { display: grid; grid-template-columns: repeat(3, 1fr); }

.gh-sheet__chip {
  border-radius: 14px; border: 1px solid var(--border);
  background: rgba(var(--surface-rgb),0.035); color: var(--text-primary);
  font-family: var(--font-thai); /* buttons don't inherit font-family by default */
  cursor: pointer; transition: border-color 0.12s ease, background 0.12s ease, transform 0.08s ease;
}
.gh-sheet__chip:hover { border-color: var(--accent-gold); background: rgba(var(--accent-gold-rgb),0.08); }
.gh-sheet__chip:active { transform: scale(0.98); }
.gh-sheet__chip.is-default { border-color: var(--accent-gold); background: rgba(var(--accent-gold-rgb),0.1); }

.gh-sheet__chip--list {
  display: flex; align-items: center; gap: 12px;
  width: 100%; min-height: 56px; padding: 0 14px; text-align: left;
}
.gh-sheet__chip-icon {
  width: 38px; height: 38px; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%; font-size: 18px;
  background: rgba(var(--surface-rgb),0.06);
}
.gh-sheet__chip.is-default .gh-sheet__chip-icon { background: rgba(var(--accent-gold-rgb),0.16); }
/* Content-sized + self-centered by default (right for the grid layout,
   where the parent's own justify-content:center should govern); the list
   layout opts into flex:1 separately so it can grow to fill the row. */
.gh-sheet__chip-text { display: flex; flex-direction: column; justify-content: center; gap: 2px; min-width: 0; }
.gh-sheet__chip--list .gh-sheet__chip-text { flex: 1; }
.gh-sheet__chip-label { font: 600 14px var(--font-thai); }
.gh-sheet__chip-tag { font-size: 10px; font-weight: 700; color: var(--accent-gold); }
.gh-sheet__chip-chevron { color: var(--text-secondary); font-size: 16px; flex-shrink: 0; }

.gh-sheet__chip--grid {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
  min-height: 64px; padding: 10px 6px;
}
.gh-sheet__chip--grid .gh-sheet__chip-label { font: 700 14px var(--font-thai); white-space: nowrap; }
/* Toned down (2026-07-16 ask) — was a solid gold pill, read as too loud
   next to the plain date label it's a footnote to. Plain small text now. */
.gh-sheet__chip--grid .gh-sheet__chip-tag {
  font-size: 8px; font-weight: 600; color: var(--accent-gold);
  padding: 0; background: none; border-radius: 0;
}

/* ── /p/gohome/post — Add flow (§5.2), step wizard ─────────────────────── */
.gh-post-page { padding-bottom: 110px; } /* clears the fixed footer */

/* Slim top bar — back-to-Home link + progress, one row, nothing else
   competing with the step's own heading below (2026-07-15 restructure:
   previously a big static page title sat above the step title too). */
.gh-post-topbar { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.gh-post-topbar .tk-back { margin: 0; }
.gh-post-dots { margin: 0; }

/* The step's own question is the page's one real heading. */
.gh-post-heading { font-size: 22px; font-weight: 800; margin-top: 18px; }
.gh-post-heading__sub { font-size: 12px; color: var(--text-secondary); margin-top: 4px; }
/* Divider before the wants-gender step's specific-gender grid — sits well
   below the "ทุกเพศ" button above it, not flush against it like a heading
   subtitle normally would be. */
.gh-post-wants-divider { margin-top: 22px; }
.gh-post-step-options { margin-top: 16px; }

/* Destination step */
.gh-post-dest-search-wrap { position: relative; margin-top: 16px; }
.gh-post-dest-search { margin: 0; }
.gh-post-dest-dropdown {
  position: absolute; left: 0; right: 0; top: calc(100% + 6px); z-index: 20;
  background: var(--bg-elevated); border: 1px solid var(--border); border-radius: 12px;
  padding: 6px; max-height: 260px; overflow-y: auto;
  box-shadow: 0 12px 28px rgba(var(--shadow-rgb), calc(0.4 * var(--shadow-mult)));
}
.gh-post-dest-dropdown__result {
  display: block; width: 100%; text-align: left;
  padding: 10px 10px; border-radius: 8px;
  background: none; border: none; color: var(--text-primary);
  font: 500 13px var(--font-thai); cursor: pointer;
}
.gh-post-dest-dropdown__result:hover { background: rgba(var(--surface-rgb),0.06); }
.gh-post-dest-dropdown__text { display: flex; flex-direction: column; gap: 1px; }
.gh-post-dest-dropdown__en { font-size: 8.5px; color: var(--text-secondary); }
.gh-post-subs-label { font-size: 12px; color: var(--text-secondary); margin: 16px 0 8px; }
/* Names the two chip rows below (2026-07-15 ask) — previously one generic
   "หรือเลือกจากรายการ" line covered both groups, giving no cue that they're
   two different kinds of chip (area vs. exact destination). */
.gh-post-field-label { font-size: 11px; font-weight: 700; color: var(--text-secondary); margin: 14px 0 6px; }
.gh-post-groups { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }
.gh-post-group-chip.is-selected { border-color: var(--accent-teal); color: var(--accent-teal); background: rgba(var(--accent-teal-rgb),0.1); }
.gh-post-subs { display: flex; flex-wrap: wrap; gap: 8px; }
/* Thai name + small English name stacked (2026-07-15 ask) — overrides the
   shared .gh-dest-chip's single-line nowrap layout for this chip only. */
.gh-post-sub-chip { display: flex; flex-direction: column; align-items: flex-start; gap: 1px; white-space: normal; padding: 6px 10px; }
.gh-post-sub-chip__en { font-size: 7.65px; color: var(--text-secondary); }
.gh-post-sub-chip.is-selected { border-color: var(--accent-gold); color: var(--accent-gold); background: rgba(var(--accent-gold-rgb),0.12); }
/* Destination chips select gold, but were inheriting the generic teal
   .gh-dest-chip:hover below — hover now matches its own selected color. */
.gh-post-sub-chip:hover { border-color: var(--accent-gold); color: var(--accent-gold); }
.gh-post-subs__empty { font-size: 12px; color: var(--text-secondary); padding: 8px 0; }

/* Upcountry branch (§10 addendum, 2026-08-09) — province search reuses the
   Bangkok destination step's own .gh-post-dest-search-wrap/__dropdown
   classes directly (2026-08-09 redesign, typeahead combobox replacing the
   plain <select>); nothing new needed here for that part. */
.gh-post-dest-detail-input {
  width: 100%; min-height: 46px; margin-top: 4px; padding: 0 14px;
  border-radius: 12px; border: 1px solid var(--border);
  background: var(--bg-secondary); color: var(--text-primary);
  font: 600 13px var(--font-thai);
}
.gh-post-dest-detail-input::placeholder { color: var(--text-secondary); font-weight: 400; }
.gh-post-dest-detail-input:focus { outline: none; border-color: var(--accent-teal); }

/* Round step */
.gh-post-everyday {
  width: 100%; min-height: 48px; margin-top: 12px;
  border-radius: 14px; border: 1px dashed var(--border);
  background: rgba(var(--surface-rgb),0.03); color: var(--text-primary);
  font: 600 13px var(--font-thai); cursor: pointer;
}
.gh-post-everyday.is-selected { border-style: solid; border-color: var(--accent-gold); color: var(--accent-gold); background: rgba(var(--accent-gold-rgb),0.1); }

/* Review step */
.gh-post-summary { display: flex; flex-direction: column; gap: 10px; padding: 16px; border-radius: 14px; background: var(--bg-secondary); border: 1px solid var(--border); }
.gh-post-summary__row { font-size: 14px; font-weight: 600; }
/* อำเภอ/ตำบล (§10 addendum, 2026-08-09) — nested inside the destination
   row, not a sibling row, so it reads as a detail of the province above it
   rather than an equally-weighted fact of its own. */
.gh-post-summary__row-sub { margin-top: 2px; font-size: 11px; font-weight: 400; color: var(--text-secondary); }
.gh-post-notice { margin-top: 14px; font-size: 12px; color: var(--text-secondary); line-height: 1.6; text-align: center; }

/* Footer (fixed, always reachable) */
.gh-post-footer {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 50;
  display: flex; flex-direction: column; align-items: center; gap: 10px; /* was 4px (2026-07-16 ask) — read as cramped against the primary button above */
  padding: 12px 16px calc(14px + env(safe-area-inset-bottom, 0px));
  background: linear-gradient(to top, var(--bg-primary) 60%, transparent);
}
.gh-post-footer .auth-btn { max-width: 480px; }
/* Self-contained — deliberately NOT reusing .gh-sheet__back, whose
   position:absolute (meant for the BottomSheet's title row) previously
   made this pin to the fixed footer's top-left corner instead of sitting
   centered under the primary button. */
/* Plain text, not a chip (2026-07-16 ask) — was pill-styled (background +
   border), read as too heavy next to the primary "ถัดไป" button above it. */
.gh-post-back {
  display: inline-flex; align-items: center; gap: 4px;
  background: none; border: none;
  padding: 6px 4px; /* was 0 — plain text still needs its own tap-target breathing room (2026-07-16 ask) */
  color: var(--text-secondary); font: 600 11px var(--font-thai);
  cursor: pointer;
}
.gh-post-back:hover { color: var(--accent-gold); }

/* Success toast (§5.2 step 8) — 2026-07-15 restyle: was a solid gold block,
   which read as an alert rather than a confirmation. Now the same dark
   elevated + teal-glow language as .gh-sheet/.gh-modal, so a "this worked"
   moment looks like the rest of the theme instead of a one-off orange box. */
.gh-post-toast {
  position: fixed; left: 50%; bottom: 90px; z-index: 400;
  transform: translate(-50%, 12px); opacity: 0;
  max-width: 320px; text-align: center;
  padding: 14px 20px; border-radius: 14px;
  background:
    radial-gradient(ellipse 240px 100px at 50% 0%, rgba(var(--accent-teal-rgb),0.14), transparent 70%),
    var(--bg-elevated);
  border: 1px solid var(--accent-teal);
  color: var(--text-primary);
  font: 700 13px var(--font-thai);
  box-shadow: 0 12px 32px rgba(var(--shadow-rgb), calc(0.4 * var(--shadow-mult)));
  transition: transform 0.2s ease, opacity 0.2s ease;
}

/* Unified popup visual language (2026-07-15 polish) — every Star Buddy
   modal built on the shared .auth-modal shell (conflict/report/wants-gender/
   join-confirm) gets the sheet's own rounded-corner + soft-teal-glow look
   instead of the shell's generic square-ish card, so sheets and modals read
   as one design language despite being two different container mechanics. */
.gh-modal .auth-modal__card {
  border-radius: 22px;
  /* Base layer was var(--bg-elevated) — corrected to var(--modal-bg)
     (2026-08-08 fix, see that token's definition for the full story). */
  background:
    radial-gradient(ellipse 420px 200px at 50% 0%, rgba(var(--accent-teal-rgb),0.08), transparent 70%),
    var(--modal-bg);
}

/* Conflict-check modal body card (§5.2 step 5) — the old plan gets its own
   small card instead of being crammed into the modal's bold title. */
.gh-conflict-card {
  margin-top: 14px; padding: 12px 14px; border-radius: 12px;
  background: rgba(var(--surface-rgb),0.04); border: 1px solid var(--border);
  display: flex; flex-direction: column; gap: 4px; text-align: left;
}
.gh-conflict-card__label { font-size: 10px; color: var(--text-secondary); }
.gh-conflict-card__value { font-size: 13px; font-weight: 700; }
.gh-conflict-card__note {
  margin-top: 12px; padding: 10px 12px; border-radius: 10px;
  background: rgba(var(--accent-gold-rgb),0.1); border: 1px solid rgba(var(--accent-gold-rgb),0.3);
  font-size: 12px; line-height: 1.5; color: var(--accent-gold); text-align: left;
}
.gh-conflict-question { margin-top: 12px; font-size: 13px; color: var(--text-secondary); }

/* ── /p/gohome/post — Edit flow, single page (2026-07-15 redesign,
   card-ized 2026-07-15 polish) — each topic now sits in its own bordered
   card (same language as .gh-wallet-card) instead of a bare label+chips row
   stacked directly on the page background, which read as one long messy list. */
/* Round context, emphasized (2026-07-15 ask — was 12px/secondary-color and
   easy to miss; this is the one fact anchoring which plan you're editing). */
.gh-edit-round-badge {
  display: inline-flex;
  padding: 6px 14px; border-radius: 999px;
  border: 1px solid var(--accent-teal);
  background: rgba(var(--accent-teal-rgb),0.12);
  color: var(--accent-teal);
  font: 700 14px var(--font-thai);
}
/* No margin-top on .gh-edit-card/.gh-edit-actions below (2026-07-16 fix) —
   both now live inside the edit-plan popup's .auth-modal__card, which is
   `display:flex; flex-direction:column; gap:14px` (auth.css). Keeping the
   old page layout's own margin-top on top of that gap was stacking two
   spacings into one big gap between every section instead of one. */
.gh-edit-card {
  padding: 16px;
  border-radius: 16px; border: 1px solid var(--border);
  background: var(--bg-secondary);
}
.gh-edit-card > *:not(:first-child) { margin-top: 10px; }
/* Role sits nested under the method card — a subtle indent + top divider
   marks it as "detail of the choice above," not a new topic. */
.gh-edit-field--nested {
  margin-top: 14px; padding-top: 12px;
  border-top: 1px dashed var(--border);
}
.gh-edit-field__label { font-size: 12px; font-weight: 700; color: var(--text-secondary); margin-bottom: 8px; }
.gh-edit-chip-row { display: flex; flex-wrap: wrap; gap: 8px; }
.gh-edit-dest-body { display: flex; flex-direction: column; gap: 8px; }
.gh-edit-select {
  width: 100%; min-height: 46px; padding: 0 12px;
  border-radius: 12px; border: 1px solid var(--border);
  background: var(--bg-secondary); color: var(--text-primary);
  font: 600 13px var(--font-thai);
}
.gh-edit-actions { display: flex; flex-direction: column; align-items: center; gap: 10px; }
.gh-edit-actions .auth-btn { width: 100%; }
/* Edit-plan popup (2026-07-16 ask) — a bit wider than the default 340px
   modal shell so the destination dropdowns and chip rows have room. */
.gh-edit-modal .auth-modal__card { max-width: 400px; }
.gh-edit-modal .gh-edit-card:first-of-type { margin-top: 4px; }
.gh-edit-modal .gh-edit-round-badge { margin-top: 4px; }

/* ── /p/gohome/route — Route Page (§5.3) ───────────────────────────────── */
.gh-route-page { padding-bottom: 24px; }
/* Disclaimer (2026-07-15 ask) — same shape/spirit as Marketplace's own
   .mk-disclaimer (market.css), teal instead of gold since this isn't a
   money-risk warning, just a "we're only a middleman" notice. */
.gh-route-disclaimer {
  display: flex; gap: 10px; align-items: center;
  padding: 12px 16px; border-radius: 12px;
  background: rgba(var(--accent-teal-rgb),0.08);
  border: 1px solid rgba(var(--accent-teal-rgb),0.3);
}
.gh-route-disclaimer__icon { font-size: 14px; flex-shrink: 0; }
.gh-route-disclaimer span:last-child { font-size: 11px; color: var(--text-primary); line-height: 1.5; }
.gh-route-header { display: flex; flex-direction: column; gap: 10px; }
.gh-route-header .tk-back { margin: 0; }
.gh-route-header__title { font-size: 19px; font-weight: 800; line-height: 1.4; }
.gh-route-header__title-en { margin-top: -6px; font-size: 12px; color: var(--text-secondary); }
.gh-route-header .gh-line-badges { margin-top: 2px; }
.gh-route-header__locked { margin-top: 4px; font-size: 12px; color: var(--text-secondary); }

/* Round/method switcher (2026-07-15 ask) — jump to a different round or
   travel method for the same destination without leaving the page.
   2026-07-15 polish: boxed + gold-accented, visually distinct from the
   filter chips below (both used to share .gh-route-filter-chip, teal
   on-state — two different jobs, "change what page you're viewing" vs
   "narrow the current list", reading as the identical control). */
.gh-route-switcher {
  display: flex; flex-direction: column; gap: 8px;
  padding: 10px 12px; border-radius: 14px;
  border: 1px solid var(--border);
  background: rgba(var(--accent-gold-rgb),0.04);
}
/* Label above, chips below (2026-07-15 ask — was inline beside the chips). */
.gh-route-switcher__row { display: flex; flex-direction: column; gap: 6px; }
.gh-route-switcher__label { font-size: 11px; font-weight: 700; color: var(--text-secondary); }
.gh-route-switcher__chips { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
/* Small on purpose (2026-07-15 ask) — this only switches which round/method
   you're viewing, not a primary action; at 40px it read as more important
   than it is. */
.gh-route-switcher-chip {
  display: inline-flex; align-items: center; gap: 4px; /* used as <a> — anchors default inline */
  /* gap (2026-07-16 fix) — flexbox trims the leading space of the text run
     that follows the emoji <img>, so the space character alone (appended in
     JS as a separate text node) wasn't enough; icon and label ran together. */
  min-height: 28px; padding: 0 10px;
  border-radius: 999px; border: 1px solid var(--border);
  background: rgba(var(--surface-rgb),0.04); color: var(--text-secondary);
  font: 600 11px var(--font-thai); cursor: pointer; white-space: nowrap;
}
/* Round = gold, method = teal (2026-07-15 ask) — the two rows are visually
   distinct controls, not just one long list of switcher chips. */
.gh-route-switcher-chip--round.is-on { border-color: var(--accent-gold); color: var(--accent-gold); background: rgba(var(--accent-gold-rgb),0.12); }
.gh-route-switcher-chip--method.is-on { border-color: var(--accent-teal); color: var(--accent-teal); background: rgba(var(--accent-teal-rgb),0.12); }
/* Upcountry's "หาเพื่อนนั่งรถทัวร์กลับด้วยกัน" method label (2026-08-09
   fix) is far longer than any other method label this chip was designed
   around — nowrap forced a pill wider than the mobile viewport. Method
   chips specifically now wrap onto 2 lines within a capped width instead;
   round chips (always a short date) keep the original nowrap. */
.gh-route-switcher-chip--method { white-space: normal; text-align: center; max-width: 200px; line-height: 1.3; }

/* Sticky freeze header (2026-07-15 ask) — condensed context once the real
   title scrolls out of view, pinned right under the navbar. */
.gh-route-sticky {
  position: fixed; left: 0; right: 0; top: var(--navbar-h); z-index: 900;
  display: flex; justify-content: center;
  padding: 10px 16px;
  /* 2026-08-09 fix ("ทำไมมันเป็นของ light mode") — was rgba(var(--surface-rgb), 0.7).
     --surface-rgb is meant for subtle 0.02-0.2 alpha raised-surface TINT
     overlays (white in dark mode, ink in light mode); at 0.7 alpha in dark
     mode it rendered as a near-solid WHITE bar, i.e. it always looked like
     light mode regardless of theme. CSS vars can't carry their own alpha
     without color-mix(), so — same fix/reasoning as nav.css's .ppw-nav —
     hardcoded to approximate --bg-elevated (dark default) with an explicit
     light-mode override below. */
  background:
    radial-gradient(ellipse 420px 120px at 50% 0%, rgba(var(--accent-teal-rgb),0.14), transparent 70%),
    rgba(28, 30, 53, 0.82);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--border);
  box-shadow: 0 8px 20px rgba(var(--shadow-rgb), calc(0.3 * var(--shadow-mult)));
  /* translateZ(0) appended (2026-07-27) — same iOS backdrop-filter
     compositing fix as .emoji-icon (p/tokens.css): this bar has its own
     backdrop-filter AND carries a raw inline-SVG icon (arrowIcon(), the
     back button) not covered by that rule. Combined into this same
     `transform` (not a separate declaration) so it isn't silently dropped —
     same requirement on the .is-visible override below. */
  opacity: 0; pointer-events: none; transform: translateY(-6px) translateZ(0);
  transition: opacity 0.15s ease, transform 0.15s ease;
}
/* pointer-events must flip back to auto here — the bar's own back link
   (added 2026-07-15) needs to be clickable once visible, not just seen. */
.gh-route-sticky.is-visible { opacity: 1; pointer-events: auto; transform: translateY(0) translateZ(0); }
:root[data-theme="light"] .gh-route-sticky {
  background:
    radial-gradient(ellipse 420px 120px at 50% 0%, rgba(var(--accent-teal-rgb),0.14), transparent 70%),
    rgba(241, 237, 249, 0.82);
}
/* position:relative + the back button pulled out via position:absolute
   (2026-07-16 fix) — was a flex row with the 24px back button + 10px gap
   counted as real flex space, so .gh-route-sticky__text's own text-align:
   center only centered within the *leftover* width, skewing it visibly
   right of the bar's true center. Absolute-positioning the button removes
   it from that flex-space math entirely; .gh-route-sticky__text's
   horizontal padding (below) keeps its own centered text clear of the
   button on narrow screens instead. */
.gh-route-sticky__inner { max-width: 480px; width: 100%; position: relative; display: flex; align-items: center; justify-content: center; }
/* Lives outside .gh-page (see JS comment), so the page-wide `a { text-decoration: none }`
   reset doesn't reach it — needed its own (2026-07-16 fix, was underlined). */
.gh-route-sticky__back {
  position: absolute; left: 0; top: 50%; transform: translateY(-50%);
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px;
  background: rgba(var(--surface-rgb),0.06); border: 1px solid var(--border); border-radius: 999px;
  color: var(--text-secondary); font: 700 12px var(--font-thai); text-decoration: none;
}
.gh-route-sticky__back:hover { color: var(--accent-teal); border-color: var(--accent-teal); }
/* Two-line split (2026-07-16 ask) — name bold/prominent, date+method
   smaller/lighter/faded on their own line below, each truncating
   independently instead of one nowrap line fighting over the space. */
.gh-route-sticky__text { flex: 1; min-width: 0; padding: 0 34px; text-align: center; overflow: hidden; }
/* Desktop (2026-07-16 fix — was stuck at the mobile 480px width and
   two-line layout on every screen size): bar widens to match the real
   desktop page width (.gh-route-page's own 1100px, §889), and name+date+
   method go back to a single line since there's room for it. */
@media (min-width: 768px) {
  .gh-route-sticky__inner { max-width: 1100px; }
  .gh-route-sticky__text { display: flex; align-items: baseline; justify-content: center; gap: 6px; }
  .gh-route-sticky__name { flex-shrink: 1; min-width: 0; }
  .gh-route-sticky__meta { flex-shrink: 0; margin-top: 0; }
}
.gh-route-sticky__name { display: block; font: 700 13px var(--font-thai); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.gh-route-sticky__meta { display: block; font: 400 10px var(--font-thai); color: var(--text-secondary); opacity: 0.8; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-top: 1px; }

.gh-route-filters { display: flex; flex-wrap: wrap; gap: 8px; }
/* Sort mode (every route page) + province filter (upcountry aggregate only)
   — <select> versions of the toggle chips below, sized to match exactly
   (2026-08-09 ask) and living in the same continuous .gh-route-filters row,
   not a separate one above it (2026-08-09 fix — was split into its own row
   at first). */
.gh-route-filter-select {
  min-height: 28px; padding: 0 10px;
  border-radius: 999px; border: 1px solid var(--border);
  background: rgba(var(--surface-rgb),0.04); color: var(--text-secondary);
  font: 600 11px var(--font-thai); cursor: pointer;
}
.gh-route-filter-chip {
  display: inline-flex; align-items: center; gap: 4px; /* also a <button> here */
  /* Matched to .gh-route-switcher-chip's slim dimensions (2026-07-16 ask —
     "too big" next to the round/method chips above), well under the
     MOBILE_STRATEGY §B2 44px tap-target floor by explicit request. */
  min-height: 28px; padding: 0 10px;
  border-radius: 999px; border: 1px solid var(--border);
  background: rgba(var(--surface-rgb),0.04); color: var(--text-secondary);
  font: 600 11px var(--font-thai); cursor: pointer; white-space: nowrap;
}
.gh-route-filter-chip.is-on { border-color: var(--accent-teal); color: var(--accent-teal); background: rgba(var(--accent-teal-rgb),0.1); }
/* Trailing count, e.g. "(3)" (2026-07-16 ask) — regular weight, not the
   chip's own 600 semi-bold. */
.gh-route-filter-chip__count { font-weight: 400; }

/* Selector prefixed with .gh-page (2 classes, 2026-08-09 fix) — same bug/
   fix as .gh-route-card__dm-pill below: outranks the page-wide
   `.gh-page a { color: inherit }` reset (class+tag beats a single class),
   which was silently forcing this button's text back to inherited white
   (unreadable on the gold gradient) once it became an <a> in aggregate
   mode. Harmless no-op for the <button> case, which was never affected by
   that reset in the first place. */
.gh-page .gh-route-join {
  /* display:flex added (2026-08-09 fix) — this was relying on <button>'s
     native block-ish default for width:100% to work, which silently broke
     when the upcountry aggregate page's join button became an <a> (links to
     the post wizard instead of running an in-page action): anchors default
     to display:inline, where width:100% has no effect at all, shrinking the
     button down to its text width instead of filling the sidebar like every
     other route page's join button. */
  display: flex; align-items: center; justify-content: center; gap: 6px;
  width: 100%; min-height: 48px;
  border-radius: 14px; border: none;
  /* Fixed gradient, not theme-following (2026-08-08 fix) — same drift bug. */
  background: linear-gradient(120deg, #ffb852, #ed8420);
  color: #12121A; font: 700 14px var(--font-thai); cursor: pointer;
  text-decoration: none; /* only relevant when rendered as <a> */
  box-shadow: 0 10px 24px rgba(var(--accent-gold-rgb),0.25);
}

/* Compact single-row card (2026-07-15 redesign) — was a stacked 3-row card
   with a full-width DM button (~110-140px tall); a route with 5-10+ people
   turned into an endless scroll for not much real information. Now one
   ~56px row: avatar, name+meta, a small round DM button. */
.gh-route-list { display: flex; flex-direction: column; gap: 10px; } /* was 6px — cards read as touching on mobile (2026-07-16 ask) */
.gh-route-card {
  border-radius: 12px; border: 1px solid var(--border);
  background: var(--bg-secondary); padding: 8px 10px;
  display: flex; flex-direction: column; gap: 6px;
}
/* Wraps everything except the dest-strip (2026-08-09 fix) — .gh-route-list
   becomes a 2-col CSS grid at >=1024px (default align-items:stretch), so a
   card next to a taller sibling gets stretched to match the row height. A
   plain justify-content:center directly on .gh-route-card was tried first
   and rejected twice over: (1) the dest-strip bleeds flush to the card's
   top edge via a negative margin assuming flex-start, so centering the
   whole card pushed the strip away from that edge too; (2) centering the
   whole body as one piece (top + report-row/actions together) was also
   rejected — report-row/actions must stay pinned flush to the card's
   bottom edge always, only .top should float to the middle of whatever
   space is left. See .gh-route-card__top-area below for how that's split. */
/* flex:1 here too, not just on .top-area below (2026-08-09 fix) — .card's
   own children (this wrapper + the dest-strip) don't automatically grow to
   fill a stretched .card just by .card being taller; .top-area's flex:1
   only matters once ITS parent (this wrapper) is already claiming .card's
   full leftover height to hand down. Without this, the leftover stretched
   space sat unclaimed below the whole body instead of being distributed
   at all — report-row wasn't actually flush at the bottom either. */
.gh-route-card__body { flex: 1; display: flex; flex-direction: column; gap: 6px; min-width: 0; }
/* Only this wrapper grows/centers (2026-08-09 fix) — `flex:1` claims 100%
   of the card's leftover stretched height, `justify-content:center` then
   centers its one child (.top) within it. report-row/actions are body's
   OTHER children, siblings of this wrapper rather than inside it, so they
   end up sitting immediately after wherever this wrapper's growth stops —
   which, since it already claimed all the leftover space, is always flush
   against the card's bottom edge. */
.gh-route-card__top-area { flex: 1; display: flex; flex-direction: column; justify-content: center; min-width: 0; }
.gh-route-card--own { position: relative; border: 2px solid var(--accent-gold); background: rgba(var(--accent-gold-rgb),0.06); }
/* "ข้อมูลของฉัน" corner badge (2026-07-16 ask) — small, doesn't compete with
   the centered content below it. */
.gh-route-card__mine {
  position: absolute; top: -8px; right: 10px;
  font-size: 9px; font-weight: 700; color: var(--gold-btn-text);
  padding: 2px 8px; border-radius: 999px; background: var(--accent-gold);
}
/* Keeps dark mode's vivid gold + dark text in light mode too (2026-08-08 ask)
   — solid fill + in-box text has self-contained contrast, independent of
   the page background, so light mode's muted gold isn't needed here. */
:root[data-theme="light"] .gh-route-card__mine { background: #F4A140; color: #1a1300; }
/* Own card fully centered (2026-07-16 ask) — this is the viewer's own
   selection, not a list row to scan against others, so it reads better as
   a centered summary than left-aligned like every other row. Scoped to
   --own only; the DM pill and report row never render on own cards
   (renderCard's isOwn checks), so there's no off-axis content to worry
   about centering around. */
.gh-route-card--own .gh-route-card__top { flex-direction: column; align-items: center; text-align: center; }
.gh-route-card--own .gh-route-card__info { align-items: center; }
.gh-route-card--own .gh-route-card__name,
.gh-route-card--own .gh-route-card__meta { justify-content: center; }
.gh-route-card--own .gh-route-card__actions { justify-content: center; }
.gh-route-card--success { opacity: 0.65; }
/* Own card stays full-strength even once matched (2026-07-16 ask) — the
   dimming is meant to sink *other people's* matched cards visually, not the
   viewer's own status summary, which should always read clearly. */
.gh-route-card--own.gh-route-card--success { opacity: 1; }
.gh-route-card--locked { opacity: 0.55; }
/* Driver highlight (2026-07-16 ask) — the role badge alone (both driver and
   passenger rendered the same teal, .gh-route-card__badge--role below) gave
   no at-a-glance way to spot who's offering a ride while scanning a long
   list. Only drivers get the accent — they're the scarce/sought-after role;
   passengers keep the plain border so this stays a signal, not decoration.
   Not combined with .gh-route-card--own's border-left (own uses a full
   2px border instead), so the two never fight over the same edge. */
.gh-route-card--driver:not(.gh-route-card--own) { border-left: 3px solid var(--accent-teal); }
.gh-route-card__top { display: flex; align-items: center; gap: 10px; min-height: 40px; }
.gh-route-card__avatar {
  width: 34px; height: 34px; border-radius: 50%; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center; font-size: 16px;
}
.gh-route-card__info { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.gh-route-card__name { font-size: 13px; font-weight: 700; display: flex; align-items: center; gap: 5px; min-width: 0; }
.gh-route-card__username { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.gh-route-card__meta { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; font-size: 11px; color: var(--text-secondary); }
/* Status line (matched/ended) — the one fact most likely to change a
   viewer's decision, so it gets its own line instead of blending into the
   secondary details below (2026-07-15 hierarchy pass). */
.gh-route-card__meta--status { font-size: 11px; }
/* Secondary line (handle/zone/role/wants) — everything else, dimmed down
   one notch so it doesn't compete with the name/status above. */
.gh-route-card__meta--secondary { opacity: 0.82; }
.gh-route-card__badge { font-size: 10px; padding: 1px 7px; border-radius: 999px; background: rgba(var(--surface-rgb),0.08); color: var(--text-secondary); white-space: nowrap; }
.gh-route-card__badge--role { background: rgba(var(--accent-teal-rgb),0.12); color: var(--accent-teal); }
/* Province + อำเภอ/ตำบล header strip (§10 addendum, redesigned 2026-08-09,
   "Option A" chosen after a visual mockup comparison) — a thin gold strip
   across the top of the card, bled to the card's own edges via negative
   margins matching .gh-route-card's padding (8px 10px). Leads ahead of
   avatar/name since it's the fact that differentiates one card from
   another on the aggregate page, not secondary detail buried in the badge
   row. อำเภอ/ตำบล is free text (up to 120 chars, not required) — it's the
   part that can run long, so it truncates with an ellipsis inside a
   min-width:0 flex item (the `title` attribute set in JS carries the full
   text on hover); the province itself never truncates, it's always one of
   the fixed 76 names. */
.gh-route-card__dest-strip {
  display: flex; align-items: baseline; gap: 6px; min-width: 0;
  padding: 5px 10px; margin: -8px -10px 0;
  background: rgba(var(--accent-gold-rgb),0.1);
  border-bottom: 1px solid rgba(var(--accent-gold-rgb),0.22);
  border-radius: 12px 12px 0 0;
}
.gh-route-card__dest-strip__main { flex-shrink: 0; font: 700 12px var(--font-thai); color: var(--accent-gold); }
.gh-route-card__dest-strip__sub {
  flex-shrink: 1; min-width: 0;
  font-size: 10.5px; color: var(--text-secondary);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* อำเภอ/ตำบล alone, no province repeat (normal single-province page — the
   page header already names the province) — plain small line, not the
   strip treatment above; same ellipsis handling since it's the same free-
   text field. */
.gh-route-card__dest-sub {
  margin-top: 1px; font-size: 11px; font-weight: 400; color: var(--text-secondary);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%;
}
/* Standalone variant (normal single-province page, อำเภอ/ตำบล only — the
   province itself is already named in the page header) — no bold headline
   pairing, so it reads at the same weight as the wizard's own sub-line. */
.gh-route-card__dest-sub--standalone { margin: 2px 0; }
.gh-route-card__status { font-weight: 700; }
.gh-route-card__status--success { color: var(--accent-gold); }
.gh-route-card__status--locked { color: var(--text-secondary); }
/* "ทักเลย" pill with the real X mark (2026-07-15 redesign — matches
   Marketplace's listing-card DM button, market.css .mk-card__dm, scaled
   down to fit this card's compact row).
   Selector prefixed with .gh-page (2 classes) — needed to outrank the
   page-wide `.gh-page a { color: inherit }` reset (1 class + 1 tag is still
   higher specificity than a single class alone), which was silently forcing
   this pill's text back to inherited white-on-white and making it unreadable. */
.gh-page .gh-route-card__dm-pill {
  flex-shrink: 0; display: inline-flex; align-items: center; gap: 5px;
  padding: 7px 11px; border-radius: 8px;
  /* color was var(--bg-primary) (2026-08-08 fix) — same bug as
     p/market.css's .mk-card__dm (this pill was explicitly copied from it).
     Fixed background is always white; text needs to match with a fixed
     dark literal, not follow the theme. */
  background: #fff; color: #07071A;
  text-decoration: none; font: 700 11px var(--font-thai);
}
.gh-route-card__dm-pill:hover { background: rgba(255, 255, 255, 0.85); }
.gh-route-card__dm-pill svg { width: 11px; height: 11px; }

/* Bottom row (2026-07-15 redesign) — posted date left, report button right,
   its own line below the card body, fully separate from both the data row
   and the DM pill, report styled as a real small button instead of a
   barely-visible text link. */
/* margin-top: auto removed (2026-08-09 fix) — used to pin this row flush to
   the card's bottom edge itself (2026-07-16 ask); now redundant and
   actively wrong, since .gh-route-card__top-area's flex:1 already claims
   all the stretched card's leftover height, which has the same end result
   (this row ends up flush at the bottom regardless) without also eating
   into .top's own centering — the two rules briefly fought during that
   pass (auto-margin always wins over a sibling's justify-content, so .top
   never got its centering space at all) before this was found and removed. */
.gh-route-card__report-row { padding-top: 8px; border-top: 1px solid var(--border); display: flex; align-items: center; justify-content: space-between; gap: 8px; }
/* 15% smaller again (2026-07-15 ask) — was 30px/10px pad/11px font. */
.gh-route-card__report-btn {
  min-height: 26px; padding: 0 8px;
  border-radius: 7px; border: 1px solid var(--border);
  background: rgba(var(--surface-rgb),0.03); color: var(--text-secondary);
  font: 600 9px var(--font-thai); cursor: pointer;
}
.gh-route-card__report-btn:hover { color: var(--like-red); border-color: rgba(var(--like-red-rgb),0.4); }

/* Posted date, left side (2026-07-15 ask, originally same size as
   Marketplace's "ประกาศขายเมื่อ" line at 8.1px; enlarged 10% 2026-07-16 ask). */
.gh-route-card__posted { font-size: 8.91px; color: var(--text-secondary); }

/* Own-card mini actions (2026-07-15 ask, widened same pass) — smaller than
   the full-size .auth-btn/.gh-wallet-action used on Home so the route list
   stays compact, but no longer a 28px sliver that's easy to mis-tap. */
.gh-route-card__actions { display: flex; gap: 6px; }
.gh-route-card__mini-btn {
  flex: 1; min-height: 36px; padding: 0 8px;
  border-radius: 8px; border: 1px solid var(--border);
  background: rgba(var(--surface-rgb),0.04); color: var(--text-primary);
  font: 600 11px var(--font-thai); cursor: pointer;
}
.gh-route-card__mini-btn--danger { color: var(--like-red); border-color: rgba(var(--like-red-rgb),0.4); }
.gh-route-card__mini-btn--revert { color: var(--accent-teal); border-color: rgba(var(--accent-teal-rgb),0.4); }

.gh-route-empty { text-align: center; padding: 32px 16px; border: 1px dashed var(--border); border-radius: 14px; color: var(--text-secondary); font-size: 13px; }

/* Report modal (openReportModal, gohome-shared.js) */
.gh-report-reasons { display: flex; flex-direction: column; gap: 8px; margin-top: 14px; }
.gh-report-reason {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 12px; border-radius: 10px; border: 1px solid var(--border);
  background: rgba(var(--surface-rgb),0.03); font-size: 13px; cursor: pointer;
}
.gh-report-reason input { accent-color: var(--accent-gold); width: 16px; height: 16px; flex-shrink: 0; }
.gh-report-reason.is-selected { border-color: var(--accent-gold); background: rgba(var(--accent-gold-rgb),0.08); }
.gh-report-detail {
  width: 100%; margin-top: 12px; padding: 10px 12px;
  border-radius: 10px; border: 1px solid var(--border);
  background: rgba(var(--surface-rgb),0.03); color: var(--text-primary);
  font: 500 13px var(--font-thai); resize: vertical;
}
.gh-report-detail::placeholder { color: var(--text-secondary); }

/* Wants-gender modal (openWantsGenderModal, gohome-shared.js) */
.gh-wants-gender-mode { display: flex; gap: 8px; margin-top: 14px; }
.gh-wants-gender-checks { display: flex; flex-direction: column; gap: 8px; margin-top: 12px; }
/* Edit Plan popup's wants-gender field (openEditPlanModal, gohome-shared.js)
   — chips instead of a checkbox list (2026-07-16 ask), own class since the
   checkbox modal above is untouched and still needs its column layout. */
.gh-wants-gender-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }

/* Gender badge on a route card (§ new ask) */
.gh-route-card__gender { font-size: 12px; }
.gh-route-card__wants { font-size: 10px; color: var(--text-secondary); flex-basis: 100%; }

/* ══════════════════════════════════════════════════════════════════════
   Desktop layout (2026-07-15 ask) — same breakpoint/sidebar+bento pattern
   as myPPW (my.css, MOBILE_STRATEGY §B2: 768px). Star Buddy had no desktop
   treatment at all before this — every page just centered the 480px mobile
   column on a wide viewport, wasting most of the screen. Wizard pages are
   deliberately left alone (a step-by-step flow doesn't benefit from width);
   only Home, Route, and the Edit page get real desktop layouts.
   ══════════════════════════════════════════════════════════════════════ */

/* ── Home — sidebar (search + wallet) + main (browse) ─────────────────── */
.gh-home-sidebar, .gh-home-main { display: contents; }
@media (min-width: 768px) {
  .gh-home-page {
    max-width: 1200px;
    display: grid;
    grid-template-columns: minmax(300px, 34%) 1fr;
    align-items: start;
    gap: 28px;
  }
  .gh-home-sidebar { display: flex; flex-direction: column; gap: 22px; position: sticky; top: calc(var(--navbar-h) + 20px); }
  .gh-home-main { display: flex; flex-direction: column; gap: 22px; }
  /* Columns already separate the two groups visually — this divider (kept
     for mobile stacking) would just be a stray line floating in column 1. */
  .gh-home-divider--section { display: none; }
  /* 2 per row at every desktop width (2026-07-16 ask) — was 3 at 1100px+,
     which crowded cards that already carry a lot of info per line badges. */
  .gh-dest-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; }
}

/* ── Route — sidebar (switcher/disclaimer/search/filters/join) + main (list) ── */
.gh-route-sidebar, .gh-route-main { display: contents; }
/* Mobile default for the bento wrapper (2026-07-16 fix) — only had a
   @media(min-width:768px) grid rule below; with no display/gap of its own
   below that breakpoint it fell back to a plain block <div>, and since its
   `display:contents` children (switcher/disclaimer/search/filters/list) get
   promoted straight into it, they stacked with zero gap between them —
   every "box" on the route page read as touching. */
.gh-route-bento { display: flex; flex-direction: column; gap: 14px; }
@media (min-width: 768px) {
  .gh-route-page { max-width: 1100px; }
  .gh-route-bento {
    display: grid;
    grid-template-columns: minmax(260px, 30%) 1fr;
    align-items: start;
    gap: 24px;
  }
  .gh-route-sidebar { display: flex; flex-direction: column; gap: 14px; position: sticky; top: calc(var(--navbar-h) + 20px); }
  .gh-route-main { display: flex; flex-direction: column; gap: 14px; }
}
@media (min-width: 1024px) {
  .gh-route-list { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; }
}

/* ── Edit plan — 2-column card grid (wizard itself stays single-column,
   .gh-edit-page is its own modifier so this width increase never touches
   the wizard, which shares .gh-post-page with this page) ── */
@media (min-width: 768px) {
  .gh-edit-page { max-width: 720px; }
  .gh-edit-grid {
    display: grid; grid-template-columns: 1fr 1fr; gap: 14px; align-items: start;
  }
  .gh-edit-grid > .gh-edit-card { margin-top: 0; } /* grid gap replaces the stacked margin-top */
  .gh-edit-card--wide { grid-column: 1 / -1; }
}
.gh-post-toast.is-open { transform: translate(-50%, 0); opacity: 1; }

/* Legal footer (2026-08-05 fix — was missing entirely on this page) — same
   spacing convention as find.css's #find-root > .lg-footer-link. Lives
   inside .gh-gate-shell now (gate/disabled states only — see below), not a
   direct #gohome-root child. */
.gh-gate-shell .lg-footer-link { margin-top: 20px; padding-bottom: 20px; }
/* Logged-in state footer (2026-08-09 fix, "legal ชิดขอบล่าง") — main.js
   appends buildLegalFooter() straight to #gohome-root as a sibling of
   .gh-home-page (so renderAll()'s page.innerHTML='' can't wipe it), but
   that placement was never given the same spacing as the gate-shell case
   above — same #root > .lg-footer-link fallback convention as find.css/
   market.css. */
#gohome-root > .lg-footer-link { margin-top: 20px; padding-bottom: 20px; }

/* Gate/disabled-state shell (2026-08-05 fix, "ทำไมกล่องไม่อยู่กลางหน้า...
   แถบ legal ไม่ชิดขอบ") — fills the viewport, vertically centers the card
   in the available space, footer trailing right after it. Scoped to this
   wrapper only (renderAuthGate()/renderDisabledState() in gohome.js), never
   applied to #gohome-root itself, so the normal .gh-page layout is
   untouched by it. */
.gh-gate-shell {
  min-height: calc(100vh - var(--navbar-h));
  min-height: calc(100dvh - var(--navbar-h));
  display: flex;
  flex-direction: column;
  align-items: center;
}
.gh-gate-shell .gh-auth-gate,
.gh-gate-shell .gh-disabled { margin-top: auto; margin-bottom: auto; }
