  /* ============================================================
     TOKENS — canonical, single source of truth. Loaded from
     tokens.css (must stay the first rule in this file — @import
     is only valid before any other CSS rule). See
     docs/DESIGN_SYSTEM_V1.md for the full token reference and why
     this file lives at this specific path.
  ============================================================ */
  @import url("tokens.css");
  @media (prefers-reduced-motion: reduce){
    *{ scroll-behavior:auto !important; transition:none !important; animation:none !important; }
  }
  /* Scroll-reveal — progressive enhancement. .reveal elements are fully
     visible by default; JS adds .js-reveal to <html> only once it's ready
     to manage visibility, so a JS failure/disabled-JS never leaves content
     stuck invisible. Under reduced-motion the sitewide rule above already
     kills the transition, so .is-visible just snaps in instead of animating. */
  .js-reveal .reveal{ opacity:0; transform:translateY(24px); transition:opacity .6s ease, transform .6s ease; }
  .js-reveal .reveal.is-visible{ opacity:1; transform:translateY(0); }
  html{ scroll-behavior:smooth; scroll-padding-top:118px; }
  *{ box-sizing:border-box; margin:0; padding:0; }
  body{ background-color:var(--paper); background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='ttxPaperGrain'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23ttxPaperGrain)' opacity='0.035'/%3E%3C/svg%3E"); color:var(--ink); font-family:var(--font-body); line-height:1.55; -webkit-font-smoothing:antialiased; overflow-x:hidden; }
  a{ color:inherit; text-decoration:none; }
  a:focus-visible, button:focus-visible, summary:focus-visible{ outline:2px solid var(--turquoise-dark); outline-offset:3px; }
  header:not(.is-scrolled) .logo:focus-visible, header:not(.is-scrolled) .icon-btn:focus-visible, .hero-content a:focus-visible, #nightlife a:focus-visible, footer a:focus-visible{ outline-color:#fff; }
  h1,h2,h3{ font-family:var(--font-display); font-weight:500; color:var(--ink); line-height:1.15; }
  .accent{ color:var(--mango); }
  .wrap{ max-width:520px; margin:0 auto; padding:0 20px; }
  img{ display:block; max-width:100%; }

  .ph{ position:relative; width:100%; height:100%; display:flex; align-items:flex-end; justify-content:flex-start;
    background:repeating-linear-gradient(135deg, var(--ph-a,#e4ede9), var(--ph-a,#e4ede9) 10px, var(--ph-b,#d3e2dc) 10px, var(--ph-b,#d3e2dc) 20px);
    border:1.5px dashed rgba(23,48,42,0.22); }
  .ph-label{ font-family:var(--font-mono); font-size:10px; letter-spacing:.04em; text-transform:uppercase; color:var(--ink);
    background:rgba(255,255,255,.82); padding:6px 10px; margin:10px; border-radius:8px; line-height:1.4; max-width:calc(100% - 20px); }
  /* Venue Type Badges V1 — reused verbatim from the pre-existing,
     previously-unused .pending-badge rule (same file, same styling,
     just renamed for what it's actually for now). One venue type per
     card, top-left pill overlay, Space Mono/uppercase/subtle. */
  .venue-type-badge{ position:absolute; top:10px; left:10px; background:rgba(23,48,42,.72); color:#fff; font-family:var(--font-mono); font-size:9px; letter-spacing:.04em; text-transform:uppercase; padding:5px 9px; border-radius:6px; z-index:2; }
  .ph.tone-island{ --ph-a:#DCEFEC; --ph-b:#BFE1DC; }
  .ph.tone-market{ --ph-a:#F3E6D2; --ph-b:#E7D0AC; }
  .ph.tone-night{ --ph-a:#233F37; --ph-b:#17302A; }
  .ph.tone-night .ph-label{ background:rgba(255,255,255,.14); color:#fff; }
  .ph.tone-reel{ --ph-a:#17302A; --ph-b:#233F37; }
  .ph.tone-reel .ph-label{ background:rgba(255,255,255,.14); color:#fff; }
  img.is-photo{ width:100%; height:100%; object-fit:cover; display:block; opacity:0; transition:opacity .35s ease; }
  img.is-photo.is-loaded{ opacity:1; }
  /* Skeleton shimmer while a card photo loads — scoped to the card image
     containers specifically (the numerous small thumbnails), not the hero
     or the nightlife banners, which are few, large, and usually already
     decoded by the time they're visible. True blur-up (a real low-res
     preview per photo) would need a generated placeholder for every one of
     the 90+ images on this site — this shimmer gets the "something is
     loading here" signal without that per-image preprocessing step. */
  .disco-card-img, .area-card-img, .venue-card-img, .mini-card-img, .promo-card-img, .reel-tile{ background-image:linear-gradient(90deg, var(--sand) 0%, #fff 50%, var(--sand) 100%); background-size:200% 100%; animation:img-shimmer 1.4s ease-in-out infinite; }
  .disco-card-img.is-loaded, .area-card-img.is-loaded, .venue-card-img.is-loaded, .mini-card-img.is-loaded, .promo-card-img.is-loaded, .reel-tile.is-loaded{ animation:none; background-image:none; }
  @keyframes img-shimmer{ 0%{ background-position:200% 0; } 100%{ background-position:-200% 0; } }

  /* ---------- CLUB FALLBACK — branded placeholder for Beach Club cards
     without a real venue photo yet (Café del Mar, Catch, Kudo). Same tone-island
     stripe as the sitewide "photo needed" state, but centers the venue name
     as a Fraunces wordmark (no "photo pending"-style label — this should read
     as an intentional branded editorial tile, not a construction placeholder)
     instead of a corner-anchored pending chip. No new colors, no photography,
     no claim that the background is a real venue photo. ---------- */
  /* Flat fill instead of .ph's diagonal stripe — the stripe pattern reads
     as "under construction," which fights the "intentional editorial tile"
     framing these fallbacks are meant to have. */
  .club-fallback{ background:var(--ph-a); border:none; flex-direction:column; align-items:center; justify-content:center; text-align:center; gap:8px; padding:20px; }
  .club-fallback-name{ font-family:var(--font-display); font-weight:600; font-size:20px; line-height:1.25; letter-spacing:-.005em; color:var(--turquoise-dark); }
  /* Small mango dash under the wordmark — same accent language as .eyebrow::before
     elsewhere on the page, so the fallback reads as designed, not default. */
  .club-fallback-accent{ width:28px; height:2px; background:var(--mango); border-radius:1px; }

  /* Dark counterpart of CLUB FALLBACK for Nightlife venue-cards without a
     usable photo — same wordmark construction on the existing tone-night
     stripe instead of tone-island, white text instead of turquoise-dark. */
  .venue-fallback{ background:var(--ph-a); border:none; flex-direction:column; align-items:center; justify-content:center; text-align:center; gap:6px; padding:20px; }
  .venue-fallback-name{ font-family:var(--font-display); font-weight:600; font-size:19px; line-height:1.25; letter-spacing:-.005em; color:#fff; }

  .eyebrow{ font-family:var(--font-body); font-weight:600; font-size:12px; letter-spacing:.04em; text-transform:uppercase; color:var(--turquoise-dark); display:flex; align-items:center; gap:8px; }
  .eyebrow::before{ content:''; width:14px; height:1px; background:var(--mango); }
  .eyebrow.on-dark{ color:rgba(255,255,255,.82); }

  section{ padding:36px 0; }
  .section-head{ margin-bottom:var(--space-sm); }
  .section-head .htext h2{ font-family:var(--font-display); font-weight:600; font-size:var(--fs-h2); letter-spacing:-.005em; }
  .section-head .htext .eyebrow + h2{ margin-top:6px; }
  .section-head .htext p{ font-family:var(--font-body); font-size:var(--fs-small); color:var(--muted); margin-top:6px; max-width:46ch; }
  .section-head.on-dark .htext h2{ color:#fff; }
  .section-head.on-dark .htext p{ color:rgba(255,255,255,.72); }
  .text-link{ font-family:var(--font-body); font-weight:600; font-size:var(--fs-small); color:var(--turquoise-dark); }
  /* Solid pill, same visual weight as the site's booking CTAs — this is
     the payoff action for selecting a live area, so it should read as
     clearly clickable, not blend in as another text link. */
  .guide-cta{ display:inline-flex; align-items:center; justify-content:center; gap:6px; height:44px; padding:0 20px; margin-top:14px; border-radius:var(--radius-button); background:var(--turquoise-dark); color:#fff; font-family:var(--font-body); font-weight:600; font-size:13.5px; box-shadow:inset 0 1px 0 rgba(255,255,255,.2), 0 3px 8px rgba(34,29,24,.10); transition:filter .15s ease, transform .15s ease; }
  .guide-cta:active{ transform:translateY(1px) scale(.98); }
  .bg-white{ background-color:var(--paper); background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='ttxPaperGrain'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23ttxPaperGrain)' opacity='0.035'/%3E%3C/svg%3E"); }
  .bg-sand{ background-color:var(--sand); background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='ttxSandGrain'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23ttxSandGrain)' opacity='0.02'/%3E%3C/svg%3E"); }
  .bg-ink{ background:var(--ink); color:rgba(255,255,255,.85); }

  /* ---------- Visual Polish V3 — controlled prototype (Homepage + Weather
     only). Mirrors index.html's identical rules — see that file's comment
     for the full rationale. Two background watermarks, layered on top of
     each section's existing paper-grain, sized/positioned to read as a
     large, sparse, partially-cropped corner accent. ---------- */
  #things-to-do{ background-color:var(--paper); background-image:var(--motif-wave), url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='ttxPaperGrain'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23ttxPaperGrain)' opacity='0.035'/%3E%3C/svg%3E"); background-repeat:no-repeat, repeat; background-position:right -40px bottom -60px, 0 0; background-size:340px auto, 200px 200px; }
  /* Visual Approval + Finish V3 — Phase 10: mirrors index.html's
     identical rule (see that file's comment) — Areas switched from
     paper to sand purely for section rhythm against the two adjacent
     paper sections.

     Targeted Visual/UX Correction Pass — Section 8: the --motif-island
     watermark layer is removed now that the real interactive map lives
     in this section (mirrors index.html's identical change). The
     --sand tint + grain texture stays. */
  #areas{ background-color:var(--sand); background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='ttxSandGrain'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23ttxSandGrain)' opacity='0.02'/%3E%3C/svg%3E"); background-repeat:repeat; }
  @media (max-width:639px){
    #things-to-do{ background-size:220px auto, 200px 200px; background-position:right -30px bottom -30px, 0 0; }
  }
  /* Warm counterpart to bg-ink, using the existing (previously unused)
     --mango-light token — no new color introduced. Pairs Nightlife (dark,
     evening) with Beach Clubs (warm, sunset) as two related "going out"
     sections without merging their carousels or darkening Beach Clubs. */
  .bg-sunset{ background:var(--mango-light); }

  /* Nightlife → Beach Clubs is the one seam in the page meant to read as a
     pair, so it gets a smaller-than-usual gap; every other section keeps
     the normal padding above. Values are half of each breakpoint's base
     section padding (36/48/72), tightened further at desktop below. */
  #nightlife{ padding-bottom:18px; }
  #beach-clubs{ padding-top:18px; }
  /* Night Markets and Reels share the same bg-sand background with no
     visual break between them, so full section padding on both sides
     doubles up into a dead gap that reads as "empty," not as a section
     boundary — same fix as nightlife/beach-clubs above. */
  #night-markets{ padding-bottom:18px; }
  #reels{ padding-top:18px; }

  /* ---------- HEADER — sticky, compacts once scrolled. ---------- */
  header{ position:fixed; top:0; left:0; right:0; z-index:60; background:linear-gradient(180deg, rgba(15,20,17,.5) 0%, rgba(15,20,17,0) 100%); transition:background .2s ease, box-shadow .2s ease; }
  header.is-scrolled{ background:var(--paper); box-shadow:0 2px 14px rgba(34,29,24,.08); }
  header.is-scrolled .logo, header.is-scrolled .icon-btn{ color:var(--ink); }
  /* Grid instead of flex so the compact-on-scroll height change animates via
     grid-template-rows (compositor-friendly) rather than the `height`
     property, which forces layout on every frame. Visual result is identical
     to the flex+height version: logo/links/burger arranged in 3 columns,
     row height shrinks 60px -> 50px once scrolled. */
  nav.topnav{ display:grid; grid-template-columns:auto 1fr auto; column-gap:24px; grid-template-rows:60px; align-items:center; padding:0 20px; max-width:520px; margin:0 auto; transition:grid-template-rows .2s ease; }
  header.is-scrolled nav.topnav{ grid-template-rows:50px; }
  nav.topnav .logo{ grid-column:1; }
  nav.topnav .nav-links{ grid-column:2; justify-self:center; }
  nav.topnav .icon-btn{ grid-column:3; }
  .logo{ font-family:var(--font-body); font-size:18px; font-weight:700; color:#fff; letter-spacing:-.01em; transition:color .2s ease; }
  .logo span{ color:var(--mango); }
  .icon-btn{ width:var(--tap); height:var(--tap); display:flex; align-items:center; justify-content:center; color:#fff; background:none; border:none; cursor:pointer; }
  .icon-btn svg{ width:21px; height:21px; }
  .nav-links{ display:none; }

  /* ---------- MOBILE MENU — a real panel, not decoration. Anchored to the
     sticky header (which is a positioning context), so it always sits
     right below it regardless of scroll position. ---------- */
  .mobile-menu{ position:absolute; top:100%; left:0; right:0; background:var(--surface); box-shadow:0 12px 24px rgba(34,29,24,.14); border-top:1px solid var(--line); z-index:65; }
  .mobile-menu[hidden]{ display:none; }
  .mobile-menu ul{ list-style:none; max-width:520px; margin:0 auto; padding:8px 20px 14px; }
  .mobile-menu a{ display:flex; align-items:center; min-height:var(--tap); font-family:var(--font-body); font-weight:600; font-size:15px; color:var(--ink); border-bottom:1px solid var(--line); }
  .mobile-menu li:last-child a{ border-bottom:none; }
  .mobile-menu-label{ display:block; font-family:var(--font-mono); font-size:10px; font-weight:700; text-transform:uppercase; letter-spacing:.05em; color:var(--muted); padding:12px 0 4px; }
  .mobile-menu li:first-child.mobile-menu-label{ padding-top:4px; }

  /* ---------- HERO — tagline + H1 + one supporting line, photo does the rest.
     Taller than a bare vh band so the photo can carry on past the text and
     dissolve into the page instead of hard-cutting into the chip row. ---------- */
  .hero{ position:relative; height:var(--hero-page-h); min-height:var(--hero-page-min); max-height:var(--hero-page-max); overflow:hidden; display:flex; }
  [data-ttx-page-type="homepage"] .hero{ position:relative; height:var(--hero-landing-h); min-height:var(--hero-landing-min); max-height:var(--hero-landing-max); overflow:hidden; display:flex; }
  /* Mobile crop is deliberately tighter than a plain cover-fit: the source
     lagoon photo has so much vertical range that a bare object-position pan
     leaves the boats/turquoise water tiny and distant. The scale(1.8) brings
     the lagoon mouth close enough to read immediately as boats-in-turquoise-
     water, centered high in the frame (object-position 70%) so it sits in
     the clear zone above the text/scrim rather than fighting it. Uniform
     scale only — never stretched. */
  .hero img{ position:absolute; inset:0; width:100%; height:100%; object-fit:cover; object-position:center 70%; transform:scale(1.8) translateY(var(--parallax-y, 0px));
    -webkit-mask-image:linear-gradient(to bottom, #000 0%, #000 82%, rgba(0,0,0,.85) 88%, rgba(0,0,0,.55) 93%, rgba(0,0,0,.25) 97%, transparent 100%);
    mask-image:linear-gradient(to bottom, #000 0%, #000 82%, rgba(0,0,0,.85) 88%, rgba(0,0,0,.55) 93%, rgba(0,0,0,.25) 97%, transparent 100%); }
  /* Scrim is concentrated tightly around the text zone only (roughly the
     bottom third) and drops off quickly above it, so the boats/water sitting
     higher in frame stay bright and clear rather than getting dulled by a
     scrim meant for the text below them. It also stays light right at the
     very bottom (0%) so the fade mask below dissolves natural photo tones
     into the page instead of a dark-navy-into-cream band.
     The fade mask itself only touches the bottom ~18% of the hero (82%→100%,
     eased across several stops) — an earlier version faded the whole bottom
     HALF of the image, which read as a visible hazy/foggy band instead of a
     natural disappearance. Keeping the affected zone short is what keeps the
     photo crisp everywhere else and the dissolve genuinely subtle. Same mask
     shape reused on .hero img above so photo + scrim dissolve together. */
  /* Text is vertically centered now (see .hero-content), so the scrim is a
     tight band around the middle only — not a bottom fade. Top and bottom
     of the photo stay crisp; darkening is scoped to where the text
     actually sits, not spread across most of the image. */
  .hero-scrim{ position:absolute; inset:0; background:linear-gradient(0deg, rgba(9,16,14,.12) 0%, rgba(9,16,14,.22) 25%, rgba(9,16,14,.5) 38%, rgba(9,16,14,.55) 50%, rgba(9,16,14,.5) 62%, rgba(9,16,14,.22) 75%, rgba(9,16,14,.12) 100%);
    -webkit-mask-image:linear-gradient(to bottom, #000 0%, #000 82%, rgba(0,0,0,.85) 88%, rgba(0,0,0,.55) 93%, rgba(0,0,0,.25) 97%, transparent 100%);
    mask-image:linear-gradient(to bottom, #000 0%, #000 82%, rgba(0,0,0,.85) 88%, rgba(0,0,0,.55) 93%, rgba(0,0,0,.25) 97%, transparent 100%); }
  .hero-content{ position:relative; width:100%; display:flex; flex-direction:column; justify-content:center; padding:0 20px; color:#fff; max-width:520px; margin:0 auto; }
  /* padding-top clears the fixed overlay header. .hero-content is
     bottom-justified, so without it the eyebrow/H1 ride up underneath the
     logo once the hero is compact — visible as the logo overlapping the
     eyebrow on every page. Reserving the header height caps how far the
     content can rise instead of relying on the hero being tall enough. */
  .hero-content{ padding-top:60px; }
  .hero-tagline{ font-family:var(--font-body); font-weight:600; font-size:14px; line-height:1.3; letter-spacing:.01em; color:rgba(255,255,255,.94); display:flex; align-items:center; gap:8px; margin-bottom:10px; text-shadow:0 1px 6px rgba(0,0,0,.55); }
  .hero-tagline::before{ content:''; width:14px; height:1px; background:var(--mango); flex-shrink:0; }
  /* color is explicit here (not inherited from .hero-content) because the
     sitewide h1,h2,h3{color:var(--ink)} rule sets color directly on every
     h1 element — an explicit value always wins over inheritance regardless
     of the parent rule's specificity, so without this the title rendered as
     dark ink on the dark photo instead of the intended light color. */
  .hero-content h1{ font-family:var(--font-display); font-weight:700; font-size:var(--fs-h1); line-height:1.08; letter-spacing:-.01em; color:var(--paper); text-shadow:0 2px 12px rgba(0,0,0,.32); }
  .hero-sub{ font-family:var(--font-body); font-weight:400; font-size:14px; line-height:1.45; color:rgba(255,255,255,.9); margin-top:10px; text-shadow:0 1px 5px rgba(0,0,0,.32); }

  /* ---------- SOCIAL PROOF — Instagram trust card. Was a single plain
     text line + button (flagged as "too plain/system-like"), then a
     count-free redesign (no approved follower number existed yet). Now
     shows the owner-approved count — see .social-proof-count below and
     ttx_social_config() (functions.php): instagramFollowersLabel is the
     single source for the "54.5K+" figure, never hardcoded here. If that
     value is ever unset again, fall back to a count-free line rather than
     guessing a number. Compact editorial card: icon + count/headline/
     subcopy on one side, CTA on the other — cream section backdrop
     (--paper) with a white card (--surface) on top, subtle turquoise-
     tinted border, --radius-sm (14px). No Instagram gradient, no
     glassmorphism, no oversized icon. ---------- */
  /* Visual Polish V2 — overlaps the hero's bottom edge instead of sitting
     flush below it (see index.html's mirrored comment for the full
     rationale). Targeted Visual/UX Correction Pass — increased overlap
     to ~40% of the card's own height; mirrors index.html exactly. */
  .social-proof{ background:var(--paper); padding:0 0 4px; position:relative; z-index:5; margin-top:-68px; }
  .social-proof-inner{ display:flex; flex-direction:column; align-items:stretch; gap:14px; background:var(--surface); border:1px solid var(--turquoise-light); border-radius:var(--radius-sm); padding:16px; box-shadow:0 10px 28px rgba(23,48,42,.14); }
  .social-proof-main{ display:flex; align-items:flex-start; gap:10px; min-width:0; }
  /* Instagram social anchor. The glyph was a 20px turquoise outline that
     read as a generic UI icon rather than Instagram; it is now 26px (+30%)
     and carries Instagram's own brand colour so the block is recognisable
     at a glance. Scoped deliberately to the ICON ONLY — the surrounding
     card, border, text and Follow button all stay in the ThaiExplores
     palette, so this reads as a trust signal, not an Instagram-branded
     component. aria-hidden on the span is unchanged; the accessible name
     still comes from the adjacent link text. */
  .social-proof-icon{ flex-shrink:0; width:34px; height:34px; display:flex; align-items:center; justify-content:center; color:#E1306C; }
  .social-proof-icon svg{ width:26px; height:26px; }
  .social-proof-copy{ display:flex; flex-direction:column; gap:2px; min-width:0; }
  /* Follower count + unit — the count carries the visual weight now
     (bold, larger, --ink), TRAVELERS demoted to the small Space Mono
     utility treatment the old single-style eyebrow used to have. */
  /* Visual Approval + Finish V3 — Phase 4: mirrors index.html's identical
     rule (see that file's comment). */
  .social-proof-count::before{ content:''; width:8px; height:2px; background:var(--mango); border-radius:1px; align-self:center; }
  .social-proof-count{ display:flex; align-items:baseline; gap:5px; }
  .social-proof-number{ font-family:var(--font-body); font-weight:700; font-size:15px; color:var(--ink); }
  .social-proof-unit{ font-family:var(--font-mono); font-size:9.5px; font-weight:700; letter-spacing:.05em; text-transform:uppercase; color:var(--turquoise-dark); }
  .social-proof-headline{ font-family:var(--font-body); font-weight:700; font-size:13.5px; color:var(--ink); line-height:1.35; }
  .social-proof-sub{ font-family:var(--font-body); font-size:11.5px; color:var(--muted); line-height:1.3; }
  .social-proof-cta{ display:inline-flex; align-items:center; justify-content:center; gap:6px; flex-shrink:0; width:100%; height:40px; padding:0 18px; border-radius:var(--radius-button); border:1.5px solid var(--turquoise-dark); color:var(--turquoise-dark); font-family:var(--font-body); font-weight:600; font-size:12.5px; white-space:nowrap; transition:background .15s ease, color .15s ease; }
  .social-proof-cta:active{ transform:scale(.96); }

  /* ---------- QUICK DISCOVERY — sticky category row, stays reachable while
     scrolling instead of disappearing after the hero. Deliberately restrained
     (smaller type, lighter border, tighter gap/padding) so it reads as a
     supporting nav rather than competing with the hero above it — tested
     moving this above the hero entirely, but that pushed the photo down and
     made the first screen feel like a generic app header instead of an
     immersive travel hero, so the structural order stayed the same and only
     the chip styling itself was toned down. Touch target stays 44px
     (Apple's own minimum), never shrunk below "comfortable". ---------- */
  .discover{ position:sticky; top:50px; z-index:50; background:var(--paper); padding:9px 0; border-bottom:1px solid var(--line); }
  .chip-row{ display:flex; gap:8px; overflow-x:auto; padding:2px 20px; margin:0; scrollbar-width:none; }
  .chip-row::-webkit-scrollbar{ display:none; }
  .chip{ flex:0 0 auto; font-family:var(--font-body); font-size:12.5px; font-weight:600; padding:0 14px; min-height:44px; display:inline-flex; align-items:center;
    border-radius:var(--radius-button); background:var(--surface); border:1px solid var(--line); color:var(--ink); white-space:nowrap; transition:background .15s ease, color .15s ease, border-color .15s ease; }
  .chip.is-active{ background:var(--turquoise-dark); color:#fff; border-color:var(--turquoise-dark); }

  /* Things to Do category filter — same visual language as the sticky
     .chip-row above, but scoped locally (own class, own JS) so filtering
     Things to Do never touches the sitewide section-nav chips. */
  .disco-filter{ display:flex; gap:8px; overflow-x:auto; padding:2px 0 2px; margin-top:14px; scrollbar-width:none; }
  .disco-filter::-webkit-scrollbar{ display:none; }
  .disco-filter-chip{ flex:0 0 auto; font-family:var(--font-body); font-size:12.5px; font-weight:600; padding:0 14px; min-height:40px; display:inline-flex; align-items:center; border-radius:var(--radius-button); background:var(--surface); border:1px solid var(--line); color:var(--ink); white-space:nowrap; cursor:pointer; transition:background .15s ease, color .15s ease, border-color .15s ease; }
  .disco-filter-chip.is-active{ background:var(--turquoise-dark); color:#fff; border-color:var(--turquoise-dark); }

  /* ---------- SWIPE ROW (shared by every carousel section) ---------- */
  /* Margin/padding are deliberately asymmetric (right-only negative margin,
     matched to left+right padding) so the row still bleeds past the right
     edge for the next-card peek without changing card widths, which are
     percentages of this row's content box. scroll-padding-left matches that
     same left padding so mandatory snapping settles with card #1 sitting at
     the normal page inset instead of collapsing flush against the edge.
     Mobile card widths (area/disco 72%, venue 70%, mini 64%) are deliberately
     narrower than a plain "one full card per screen" fit — most traffic is
     mobile, and at ~78-80% the next card barely peeked in, so the row read as
     a single static card instead of an obviously swipeable one. Any new
     carousel/card type added later should follow this same ~64-72% mobile
     range, not default back to 78%+. */
  .swipe-row{ display:flex; gap:var(--gap-carousel); overflow-x:auto; padding:2px 20px 10px; margin:0; scroll-snap-type:x mandatory; scroll-padding-left:20px; scrollbar-width:none; }
  .swipe-row::-webkit-scrollbar{ display:none; }

  /* ---------- WHERE TO STAY — boxed swipe cards, all current Areas once.
     Narrower (58% vs the 72% every other card uses) and a taller 4:5 crop,
     so a narrower footprint still shows most of a portrait phone photo
     without the card reading as oversized the way a 72%-wide 4:5 card did.
     Title + description sit on the photo (scrim overlay); the pros/cons
     tags stay off the photo in a plain white strip below — they're pills
     the reader compares at a glance, and that scan works better on a flat
     surface than sitting on top of a busy travel photo. */
  /* display:flex column (+ flex:1 on -body below) so .swipe-row's default
     flex stretch — which equalizes every card's outer height to the tallest
     in the row — lands as extra breathing room inside the white body
     instead of an uneven gap under the tag row on cards with fewer tags. */
  .area-card{ flex:0 0 58%; scroll-snap-align:start; scroll-snap-stop:always; scroll-margin-left:20px; background:var(--surface); border-radius:var(--radius-card); overflow:hidden; box-shadow:var(--shadow-card); transition:transform .15s ease, box-shadow .15s ease; display:flex; flex-direction:column; }
  /* Tap/hover feedback is scoped to a.area-card only — most area cards are
     plain divs now (no working destination yet), and a div with no click
     handler shouldn't visually pretend to be tappable. */
  a.area-card:active{ transform:scale(1.02); }
  .area-card-img{ position:relative; aspect-ratio:4/5; overflow:hidden; }
  .area-card-scrim{ position:absolute; inset:0; background:linear-gradient(0deg, rgba(23,42,36,.8) 0%, rgba(23,42,36,.32) 28%, rgba(23,42,36,0) 48%); pointer-events:none; }
  .area-card-content{ position:absolute; left:14px; right:14px; bottom:12px; }
  .area-card-title{ font-family:var(--font-display); font-weight:700; font-size:var(--fs-title-std); line-height:1.3; color:var(--paper); margin:0 0 3px; text-shadow:0 1px 4px rgba(0,0,0,.2); }
  .area-card-desc{ font-family:var(--font-body); font-size:var(--fs-small); color:rgba(255,255,255,.85); line-height:1.4; margin:0; }
  .area-card-body{ padding:12px 15px 14px; flex:1; }
  /* Area highlight — names one standout landmark per area, overlaid on the
     photo (same corner slot as .disco-badge) rather than mixed into
     .area-tags below, so it reads as "this is the one thing to know about
     this area" instead of just another pill. Only areas with one genuinely
     obvious landmark get this — not every area. Dark translucent chip, not
     mango/turquoise, so it doesn't read as a CTA or duplicate .disco-badge's
     "commercial data" meaning. */
  .area-highlight{ position:absolute; top:10px; left:10px; background:var(--mango); color:#fff; font-family:var(--font-body); font-weight:700; font-size:var(--fs-micro); letter-spacing:.02em; padding:5px 10px; border-radius:var(--radius-tag); }
  /* Pills keep their own solid fill, so they stay readable sitting directly
     on the scrim/photo without needing a white backing panel. */
  .area-tags{ display:flex; flex-wrap:wrap; gap:6px; }
  .area-tag{ display:inline-block; font-family:var(--font-body); font-size:var(--fs-label); font-weight:600; padding:4px 10px; border-radius:var(--radius-tag); background:var(--turquoise-light); color:var(--turquoise-dark); }
  .area-tag.is-con{ background:var(--terracotta-light); color:var(--terracotta-dark); }

  /* ---------- DISCOVERY CARD — Things to Do. Bookable items get a mango CTA
     pill with an honest pending note; free/self-guided items are informative
     only (no CTA) until real guide pages exist — never a link that 404s. ---------- */
  /* display:flex + column here (and on -body below) so the row's default
     flex stretch — which already equalizes every card's outer height —
     carries down into the body instead of leaving dead space under it;
     the CTA then sits at the same level on every card via margin-top:auto. */
  /* Visual Approval + Finish V3 — Phase 3: mirrors index.html's identical
     rule. Only front-page.php uses .disco-card in this shared file
     (template-areas.php doesn't), so this stays homepage-scoped in
     practice despite living in the shared stylesheet. */
  .disco-card{ flex:0 0 62%; scroll-snap-align:start; scroll-snap-stop:always; scroll-margin-left:20px; background:var(--surface-warm); border-radius:var(--radius-card); overflow:hidden; box-shadow:inset 0 1px 0 rgba(255,255,255,.5), 0 1px 2px rgba(34,29,24,.08), 0 10px 22px rgba(34,29,24,.08); transition:transform .15s ease, box-shadow .15s ease; display:flex; flex-direction:column; }
  .disco-card:active{ transform:scale(1.02); }
  .disco-card-img{ aspect-ratio:4/5; position:relative; overflow:hidden; }
  .disco-card-body{ padding:14px 16px 16px; display:flex; flex-direction:column; flex:1; }
  .disco-card-body h3{ font-family:var(--font-display); font-weight:700; font-size:var(--fs-title-std); line-height:1.4; margin-bottom:4px; display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:2; overflow:hidden; min-height:2.8em; }
  .disco-card-body p{ font-size:var(--fs-small); color:var(--muted); line-height:1.45; margin-bottom:10px; display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:3; overflow:hidden; min-height:54px; }
  /* Booking CTA design. Still-pending cards render this as a non-navigating
     span (no href to fake) with an explicit pending note underneath — never
     a link that looks live but 404s. Cards with a real affiliate link render
     it as an <a> instead, which is why cursor:pointer is scoped to a.disco-cta
     only — the span variant should stay visibly non-interactive. */
  .disco-cta{ display:inline-flex; align-items:center; justify-content:center; gap:6px; background:var(--mango); color:#fff; font-family:var(--font-body); font-weight:600; font-size:12.5px; height:38px; padding:0 16px; border-radius:var(--radius-button); cursor:default; margin-top:auto; align-self:flex-start; box-shadow:inset 0 1px 0 rgba(255,255,255,.2), 0 3px 8px rgba(34,29,24,.10); transition:filter .15s ease, transform .15s ease; }
  a.disco-cta{ cursor:pointer; }
  .disco-cta-note{ display:block; margin-top:6px; font-family:var(--font-mono); font-size:9px; letter-spacing:.03em; text-transform:uppercase; color:var(--muted); }
  /* Ready for real commercial data (Bestseller / Popular / Sale / % off). Unused until verified. */
  .disco-badge{ position:absolute; top:10px; left:10px; background:var(--turquoise-dark); color:#fff; font-family:var(--font-body); font-weight:700; font-size:var(--fs-micro); letter-spacing:.02em; padding:5px 10px; border-radius:var(--radius-tag); }

  /* ---------- MINI CARD — Top Night Markets only: photo, name, one line.
     Discovery only, no CTA until real guides/booking links exist. Same
     width/ratio/radius family as .area-card now (see VENUE CARD below for
     the same reasoning) — kept as its own class only because Night
     Markets' body layout (name + one metadata line) is genuinely simpler
     than the other card types, not because its proportions differ. ---------- */
  .mini-card{ flex:0 0 58%; scroll-snap-align:start; scroll-snap-stop:always; scroll-margin-left:20px; background:var(--surface); border-radius:var(--radius-card); overflow:hidden; box-shadow:var(--shadow-card); transition:transform .15s ease, box-shadow .15s ease; display:flex; flex-direction:column; }
  .mini-card:active{ transform:scale(1.02); }
  .mini-card-img{ aspect-ratio:4/5; position:relative; overflow:hidden; }
  .mini-card-body{ padding:11px 13px 13px; flex:1; display:flex; flex-direction:column; }
  .mini-card-body h3{ font-family:var(--font-display); font-weight:700; font-size:var(--fs-title-std); line-height:1.4; margin-bottom:2px; display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:2; overflow:hidden; min-height:2.8em; }
  /* Compact area/day metadata — quieter than the market name (smaller, muted)
     so it reads as supporting info, never competing with the h3. Public Sans,
     matching venue-card-meta/season-months — no monospace on user-facing metadata. */
  .mini-card-meta{ display:block; font-family:var(--font-body); font-weight:600; font-size:12px; color:var(--muted); margin-bottom:4px; }
  .mini-card-body p{ font-size:var(--fs-label); color:var(--muted); line-height:1.45; display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:3; overflow:hidden; min-height:4.35em; }

  /* ---------- VENUE CARD — shared "going out" card family for Nightlife and
     Beach Clubs: image -> body (name, location/type, short description).
     Width, radius, spacing, title position and carousel behavior are
     identical between the two sections — mood comes entirely from the
     .is-night modifier (dark card surface, light text) rather than a
     different component, and text always sits on a flat card body instead
     of over a photo, which is what actually fixes Nightlife's readability.
     Kept as its own class instead of reusing .mini-card so Night Markets'
     smaller sizing/radius stays untouched. ---------- */
  .venue-card{ flex:0 0 58%; scroll-snap-align:start; scroll-snap-stop:always; scroll-margin-left:20px; background:var(--surface); border-radius:var(--radius-card); overflow:hidden; box-shadow:var(--shadow-card); transition:transform .15s ease, box-shadow .15s ease; display:flex; flex-direction:column; }
  .venue-card:active{ transform:scale(1.02); }
  .venue-card-img{ aspect-ratio:4/5; position:relative; overflow:hidden; }
  .venue-card-body{ padding:11px 13px 13px; display:flex; flex-direction:column; flex:1; }
  .venue-card-body h3{ font-family:var(--font-display); font-weight:700; font-size:var(--fs-title-std); line-height:1.3; margin-bottom:3px; display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:2; overflow:hidden; min-height:2.6em; }
  .venue-card-meta{ display:block; font-family:var(--font-body); font-weight:600; font-size:12px; color:var(--muted); margin-bottom:4px; }
  .venue-card-body p{ font-size:var(--fs-label); color:var(--muted); line-height:1.45; display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:3; overflow:hidden; min-height:48px; }
  /* SECONDARY, text-only exploration link — deliberately not a button
     (unlike .guide-cta/.area-hub-cta elsewhere) so it never competes with
     .venue-card-cta's booking CTA below it in the same card. Same quiet
     underline-on-hover treatment as .reel-handle. Only rendered for
     hotels whose area has a real, live guide page — see the hotel cards
     below. */
  .venue-card-explore{ font-family:var(--font-body); font-weight:600; font-size:11.5px; color:var(--turquoise-dark); display:inline-block; align-self:flex-start; margin-bottom:8px; border-bottom:1px solid transparent; }
  .venue-card-explore:hover{ border-bottom-color:var(--turquoise-dark); }
  /* Only venues with a real, tracked affiliate link get this — same
     turquoise-dark pill family as .promo-cta/.hotel-cta/.s-card-cta, so a
     booking CTA looks the same everywhere on the site regardless of
     which carousel it's in. Most venue-cards have no CTA at all (no real
     link yet), which is why this isn't baked into .venue-card-body.
     margin-top:auto (with .venue-card-body as a flex column above) pins
     it to the same bottom baseline across cards regardless of how much
     title/description text precedes it — same pattern as .disco-cta. */
  .venue-card-cta{ margin-top:auto; display:inline-flex; align-items:center; justify-content:center; font-family:var(--font-body); font-weight:600; font-size:12.5px; color:#fff; background:var(--mango); padding:0 16px; height:38px; border-radius:var(--radius-button); align-self:flex-start; box-shadow:inset 0 1px 0 rgba(255,255,255,.2), 0 3px 8px rgba(34,29,24,.10); transition:filter .15s ease, transform .15s ease; }
  /* Reserved action slot: a card without a .venue-card-cta keeps the same
     overall height as a footered sibling in another row, so the family's
     geometry no longer depends on whether that particular card happens to
     have a booking link. Matches the .venue-card-cta box exactly (38px). */
  .venue-card-body:not(:has(.venue-card-cta))::after{ content:''; display:block; margin-top:auto; height:38px; }
  /* One body height for the whole family. Hotel cards carry an extra
     .venue-card-explore link that made their row 27px taller than the
     Nightlife/Beach Club rows built from the same component; the shared
     min-height absorbs that difference into the CTA's existing margin-top:auto
     instead, so every .venue-card is the same height in every section.
     201px = the tallest configuration (2-line title, meta, 3-line clamped
     description, explore link, CTA) and is width-independent because every
     text zone above is clamped. */
  .venue-card-body{ min-height:201px; }

  /* Standalone commercial CTA below a carousel (not a card inside it) —
     same solid-mango pill family as .disco-cta/.venue-card-cta, sized like
     .area-hub-cta. Hidden entirely (see front-page.php) until a real
     Phuket-wide affiliate URL exists — never shown pointing nowhere. */
  .hotel-viewall-row{ text-align:center; padding:18px 20px 0; }
  .hotel-viewall-card{ display:inline-flex; align-items:center; justify-content:center; gap:6px; height:44px; padding:0 22px; border-radius:var(--radius-button); background:var(--mango); color:#fff; font-family:var(--font-body); font-weight:600; font-size:13.5px; transition:filter .15s ease; }
  .hotel-viewall-card:active{ transform:scale(.96); }

  /* Night mood — a subtle white-alpha overlay on the section's own --ink
     (not a new color) for a slightly raised dark card, plus light text.
     Everything above this is untouched, so Nightlife stays the same
     component as Beach Clubs, just recolored. */
  .venue-card.is-night{ background:rgba(255,255,255,.08); box-shadow:none; border:1px solid rgba(255,255,255,.08); }
  .venue-card.is-night .venue-card-body h3{ color:#fff; }
  .venue-card.is-night .venue-card-meta{ color:rgba(255,255,255,.65); }
  .venue-card.is-night .venue-card-body p{ color:rgba(255,255,255,.78); }
  /* Visual Polish V2 — Nightlife cards have no CTA and no explore link, so
     the shared 201px min-height (sized for the tallest Hotel-card
     configuration: 2-line title + meta + 3-line description + explore
     link + CTA) left a large empty dark void below the text on every
     Nightlife card. Scoped to .is-night only — Hotels/Beach Clubs keep
     the shared height since their longer card bodies actually need it. */
  .venue-card.is-night .venue-card-body{ min-height:0; }
  .venue-card.is-night .venue-card-body h3{ min-height:1.3em; }
  .venue-card.is-night .venue-card-body p{ min-height:0; }

  /* ---------- SEASON & WEATHER — one signature diagonal-split composition,
     not two generic cards. Andaman Teal (High Season) transitions diagonally
     into Deep Palm Ink (Low Season) within one continuous frame — both are
     already-established tokens (not a bespoke one-off color), so the two
     sides read as one coherent family rather than an outlier brown intruding
     on the palette. The diagonal is a background identity, not a literal
     clip shape — content stays in two simple, fully-readable zones so
     nothing has to fit inside a shrinking wedge at 390px. Each zone carries
     a light ambient motion layer (sun-glow / cloud-and-rain drift) that is
     purely decorative, positioned away from body text, and fully covered
     by the sitewide prefers-reduced-motion rule. See DESIGN.md "Motion". ---------- */
  .season-diagonal{ position:relative; border-radius:var(--radius-card); overflow:hidden; background:linear-gradient(165deg, var(--turquoise) 0%, var(--turquoise) 46%, var(--ink) 54%, var(--ink) 100%); box-shadow:0 8px 24px rgba(34,29,24,.1); }
  .season-zone{ position:relative; overflow:hidden; padding:22px; }
  .season-zone.is-high{ padding-bottom:26px; }
  .season-zone.is-low{ padding-top:8px; }
  .season-zone-head{ position:relative; z-index:1; display:flex; align-items:center; gap:10px; margin-bottom:12px; }
  .season-icon{ width:34px; height:34px; border-radius:50%; display:flex; align-items:center; justify-content:center; flex-shrink:0; background:rgba(255,255,255,.16); }
  .season-icon svg{ width:18px; height:18px; stroke:#fff; }
  .season-name{ font-family:var(--font-display); font-weight:700; font-size:15px; color:#fff; }
  .season-months{ font-family:var(--font-body); font-weight:600; font-size:12px; color:rgba(255,255,255,.75); }
  .season-list{ position:relative; z-index:1; list-style:none; }
  .season-list li{ display:flex; align-items:flex-start; gap:8px; font-size:13.5px; color:#fff; padding:4px 0; line-height:1.4; }
  .season-list li::before{ content:'\2713'; flex-shrink:0; font-weight:700; color:#fff; width:16px; }
  .season-list li.is-con{ color:rgba(255,255,255,.82); }
  .season-list li.is-con::before{ content:'\2212'; color:rgba(255,255,255,.82); }

  /* Current-month indicator — reuses the existing "active chip" pill
     language (translucent fill + hairline border) rather than inventing a
     new visual pattern for "this is the current state." */
  .season-current-badge{ position:relative; z-index:1; display:inline-flex; align-items:center; gap:6px; font-family:var(--font-body); font-weight:600; font-size:12px; color:#fff; background:rgba(255,255,255,.16); border:1px solid rgba(255,255,255,.4); padding:5px 10px; border-radius:var(--radius-button); margin-bottom:12px; }
  .season-current-badge::before{ content:''; width:6px; height:6px; border-radius:50%; background:#fff; flex-shrink:0; }

  /* Ambient decoration — transform/opacity only, purely decorative (aria-hidden),
     kept clear of the text column, disabled entirely under reduced-motion via
     the sitewide `*{animation:none!important}` rule. */
  .season-decor{ position:absolute; inset:0; overflow:hidden; pointer-events:none; z-index:0; }
  .sun-glow{ position:absolute; top:-30px; right:-20px; width:150px; height:150px; border-radius:50%; background:radial-gradient(circle, rgba(255,255,255,.38) 0%, rgba(255,255,255,.10) 55%, transparent 76%); animation:sunBreathe 7s ease-in-out infinite; }
  @keyframes sunBreathe{ 0%,100%{ opacity:.75; transform:scale(.94); } 50%{ opacity:1; transform:scale(1.06); } }
  .cloud{ position:absolute; border-radius:50%; background:rgba(255,255,255,.09); filter:blur(7px); }
  .cloud-1{ width:120px; height:52px; top:6px; right:-24px; animation:cloudDrift1 65s ease-in-out infinite; }
  .cloud-2{ width:86px; height:40px; bottom:14px; right:36px; animation:cloudDrift2 80s ease-in-out infinite; }
  @keyframes cloudDrift1{ 0%,100%{ transform:translateX(0); } 50%{ transform:translateX(-26px); } }
  @keyframes cloudDrift2{ 0%,100%{ transform:translateX(0); } 50%{ transform:translateX(20px); } }
  .rain-texture{ position:absolute; inset:-10% -10%; background-image:repeating-linear-gradient(115deg, rgba(255,255,255,.06) 0px, rgba(255,255,255,.06) 1px, transparent 1px, transparent 16px); animation:rainDrift 16s linear infinite; opacity:.55; }
  @keyframes rainDrift{ 0%{ background-position:0 0; } 100%{ background-position:32px 64px; } }

  /* ---------- NIGHTLIFE — dark section, organized as location groups rather
     than one flat list of venues: Bangla Road/Patong (dominant, most venues)
     and Old Town after dark (smaller, quieter scene). Venue cards inside
     both groups use .venue-card.is-night (see VENUE CARD above) — same
     component as Beach Clubs, recolored for mood, so individual venues stay
     visually consistent across the whole "going out" family. ---------- */

  /* Nightlife area feature — identical construction and dimensions for both
     group headers (Bangla Road, Old Town): same banner height/aspect ratio,
     radius, internal padding, label placement, title size and description
     placement at every breakpoint. Mood differs only through photography and
     color (neon night vs. golden dusk), never through size — an earlier
     version scaled Old Town down (.is-secondary) and that read as "these are
     different things" instead of "two variants of the same header".
     Deliberately a different construction from the venue cards below
     (image+overlay, not image-then-body) so an area header can't be mistaken
     for "one more venue". */
  /* Full-bleed editorial chapter opener below ~1024px — no longer nested
     in .wrap (moved to a direct section child in markup), no side margins,
     no rounded corners, so it reads as a photo the chapter opens with,
     not a rounded card like the venue items underneath. Re-contained
     within the editorial column at desktop (see media query below). */
  .nightlife-feature{ display:block; position:relative; overflow:hidden; aspect-ratio:16/9; margin-bottom:14px; }
  .nightlife-feature img{ width:100%; height:100%; object-fit:cover; display:block; }
  .nightlife-feature-scrim{ position:absolute; inset:0; background:linear-gradient(0deg, rgba(9,16,14,.85) 0%, rgba(9,16,14,.2) 55%, rgba(9,16,14,.05) 100%); }
  .nightlife-feature-content{ position:absolute; left:20px; right:20px; bottom:18px; color:#fff; }
  .nightlife-feature-tag{ font-family:var(--font-body); font-weight:600; font-size:11px; letter-spacing:.06em; text-transform:uppercase; color:rgba(255,255,255,.72); display:block; margin-bottom:4px; }
  /* 24px, up from the sitewide card-title size (~15-19px elsewhere) — this
     banner is a chapter opener (Bangla Road / Old Town after dark), not a
     card, so its title earns real weight without becoming a hero. */
  .nightlife-feature-content h3{ font-family:var(--font-display); font-weight:700; font-size:24px; color:#fff; margin-bottom:4px; }
  .nightlife-feature-content p{ font-family:var(--font-body); font-size:13px; color:rgba(255,255,255,.85); max-width:38ch; line-height:1.4; }

  /* Editorial-chapter treatment, scoped to this section only — not a
     sitewide .section-head change. Reuses the existing .eyebrow component
     (its own mango dash already serves as the "subtle divider") and just
     gives the h2 more size/room before the photo-banner chapter beneath
     it, so the heading reads as an opener rather than a card label. */
  #nightlife .section-head{ margin-bottom:28px; }
  #nightlife .section-head .htext h2{ font-size:clamp(26px, 20px + 2vw, 38px); }

  /* Group cue — sits between the image header and its venue carousel, e.g.
     "What to visit on Bangla Road". Deliberately much quieter than the
     header's own title (12.5px vs 22px) so the hierarchy reads as: location
     title (strong) -> short context (in the banner) -> small group cue ->
     venue carousel, never four equally-loud elements stacked in the banner
     itself. */
  .nightlife-group-cue{ display:block; font-family:var(--font-body); font-weight:600; font-size:12.5px; color:rgba(255,255,255,.7); margin:14px 0 10px; }

  /* ---------- LIVE WEBCAM — honest not-connected state, ready to connect later. ---------- */

  /* ---------- REELS — real thumbnails, linking straight to Instagram. Each
     thumbnail already carries its own title graphic, so the card doesn't
     repeat it — only a small handle badge at the bottom (short scrim, just
     enough for legibility) and a subtle corner play badge so it still reads
     as a video, not a static photo. ---------- */
  .reel-row{ display:flex; gap:10px; overflow-x:auto; padding:2px 20px 6px; margin:0; scroll-snap-type:x mandatory; scroll-padding-left:20px; scrollbar-width:none; }
  .reel-row::-webkit-scrollbar{ display:none; }
  .reel-tile{ display:block; flex:0 0 118px; scroll-snap-align:start; scroll-snap-stop:always; scroll-margin-left:20px; aspect-ratio:9/16; border-radius:var(--radius-sm); overflow:hidden; position:relative; background:var(--ink); transition:transform .1s ease; }
  .reel-tile:active{ transform:scale(1.02); }
  .reel-tile img{ width:100%; height:100%; object-fit:cover; display:block; }
  .reel-tile-scrim{ position:absolute; inset:0; background:linear-gradient(0deg, rgba(9,16,14,.7) 0%, rgba(9,16,14,0) 24%); }
  .reel-play{ position:absolute; top:8px; right:8px; width:22px; height:22px; display:flex; align-items:center; justify-content:center; background:rgba(9,16,14,.4); border-radius:50%; }
  .reel-play svg{ width:11px; height:11px; fill:#fff; opacity:.95; }
  .reel-tile-info{ position:absolute; left:10px; right:10px; bottom:8px; }
  .reel-tile-handle{ display:block; font-family:var(--font-body); font-weight:600; font-size:10px; color:rgba(255,255,255,.85); }
  .reel-handle{ font-family:var(--font-body); font-weight:600; font-size:13px; color:var(--turquoise-dark); margin-top:10px; display:inline-block; border-bottom:1px solid transparent; }
  .reel-handle:hover{ border-bottom-color:var(--turquoise-dark); }

  /* ---------- FAQ — native details/summary, works with zero JS. ---------- */
  /* Season "learn more" — same zero-JS <details> pattern as the FAQ below,
     but framed as a centered pill button rather than a list row, since
     it's a single optional expansion sitting under a finished card, not
     one item in a list of many. */
  .season-more{ margin-top:16px; text-align:center; }
  .season-more summary{ display:inline-flex; align-items:center; gap:6px; font-family:var(--font-body); font-weight:600; font-size:13px; color:var(--turquoise-dark); cursor:pointer; list-style:none; padding:10px 18px; border:1px solid var(--line); border-radius:var(--radius-button); min-height:var(--tap); }
  .season-more summary::-webkit-details-marker{ display:none; }
  .season-more summary::after{ content:'+'; font-size:16px; }
  .season-more[open] summary::after{ content:'\2212'; }
  .season-more-body{ text-align:left; max-width:60ch; margin:16px auto 0; }
  .season-more-body p{ font-size:13.5px; color:var(--muted); line-height:1.6; margin-bottom:12px; }

  /* ---------- WEATHER DATE CHECKER V1 — decision tool inside the existing
     "When to visit" section (the site's canonical Weather nav target,
     #season — no new URL). Card language matches .map-detail/.finder-
     result-item elsewhere: surface/radius/shadow, Fraunces heading,
     Public Sans body/controls, Space Mono for the small eyebrow/metadata
     only. Three positive/neutral result colors (turquoise/mango/
     terracotta, all already in the palette) — no red, no icons, no
     gradients. See docs/WEATHER_DATE_CHECKER_V1.md. ---------- */
  .weather-checker{ background:var(--surface); border-radius:var(--radius-card); box-shadow:var(--shadow-card); padding:22px 18px; margin-top:20px; }
  .wc-eyebrow{ font-family:var(--font-mono); font-size:10px; text-transform:uppercase; letter-spacing:.05em; color:var(--turquoise-dark); display:block; margin-bottom:6px; }
  .weather-checker h3{ font-family:var(--font-display); font-weight:700; font-size:19px; color:var(--ink); margin-bottom:6px; line-height:1.25; }
  .wc-intro{ font-family:var(--font-body); font-size:13.5px; color:var(--muted); line-height:1.5; margin-bottom:18px; }
  .wc-form{ display:flex; flex-direction:column; gap:12px; }
  .wc-field{ display:flex; flex-direction:column; gap:4px; min-width:0; }
  .wc-field label{ font-family:var(--font-body); font-weight:600; font-size:12.5px; color:var(--ink); }
  .wc-field input[type=date]{ font-family:var(--font-body); font-size:15px; color:var(--ink); padding:11px 12px; border-radius:var(--radius-sm); border:1.5px solid var(--line); background:var(--paper); min-height:var(--tap); width:100%; min-width:0; max-width:100%; box-sizing:border-box; }
  .wc-field input[type=date]:focus-visible{ outline:2px solid var(--turquoise-dark); outline-offset:1px; border-color:var(--turquoise-dark); }
  .wc-submit{ display:inline-flex; align-items:center; justify-content:center; gap:6px; height:var(--tap); border-radius:var(--radius-button); background:var(--turquoise-dark); color:#fff; font-family:var(--font-body); font-weight:600; font-size:14.5px; margin-top:4px; border:none; cursor:pointer; box-shadow:inset 0 1px 0 rgba(255,255,255,.2), 0 3px 8px rgba(34,29,24,.10); transition:filter .15s ease, transform .15s ease; }
  .wc-submit:disabled{ opacity:.6; cursor:default; }
  .wc-submit:active{ transform:translateY(1px) scale(.98); }
  @media (min-width:1024px){ .wc-submit:hover{ filter:brightness(1.04); transform:translateY(-1px); } }
  .wc-loading{ font-family:var(--font-body); font-size:13px; color:var(--muted); text-align:center; padding:14px 0 0; }
  .wc-error{ font-family:var(--font-body); font-size:13px; color:var(--terracotta-dark); background:var(--terracotta-light); border-radius:var(--radius-sm); padding:12px 14px; margin-top:14px; }
  .wc-result{ margin-top:20px; padding-top:20px; border-top:1px solid var(--line); }
  .wc-result-eyebrow{ font-family:var(--font-mono); font-size:10px; text-transform:uppercase; letter-spacing:.05em; color:var(--turquoise-dark); display:block; margin-bottom:6px; }
  .wc-result-label{ font-family:var(--font-display); font-weight:700; font-size:19px; margin-bottom:8px; }
  .wc-label--GREAT_TIME{ color:var(--turquoise-dark); }
  .wc-label--STILL_GOOD_MIXED{ color:var(--mango-dark); }
  .wc-label--WET_SEASON_FEEL{ color:var(--terracotta-dark); }
  .wc-result-body{ font-family:var(--font-body); font-size:14px; color:var(--ink); line-height:1.55; margin-bottom:16px; }
  .wc-grid{ display:grid; grid-template-columns:1fr 1fr; gap:10px; margin-bottom:14px; }
  .wc-cell{ background:var(--paper); border-radius:var(--radius-sm); padding:10px 12px; display:flex; flex-direction:column; gap:2px; }
  .wc-cell-label{ font-family:var(--font-body); font-weight:600; font-size:11.5px; color:var(--muted); }
  .wc-cell-value{ font-family:var(--font-body); font-weight:700; font-size:14px; color:var(--ink); }
  .wc-forecast-note{ font-family:var(--font-body); font-size:12px; color:var(--muted); line-height:1.5; margin-bottom:14px; font-style:italic; }
  /* ---------- WEATHER V2 — "how does your month compare" chart. Reuses
     the engine's own wetnessScore (already 0-100, used directly as bar
     height — no second metric) and the same three result colors as
     .wc-label--* above, just as a background instead of a text color.
     Selected month(s) get a border + bold label, not color alone (WCAG
     1.4.1) — the chart's full text equivalent lives in the container's
     aria-label; the bars/axis themselves are aria-hidden. See
     docs/WEATHER_DATE_CHECKER_V1.md. ---------- */
  .wc-compare{ margin-top:18px; padding-top:18px; border-top:1px solid var(--line); }
  .wc-compare-eyebrow{ font-family:var(--font-mono); font-size:10px; text-transform:uppercase; letter-spacing:.05em; color:var(--turquoise-dark); display:block; margin-bottom:6px; }
  .wc-compare-heading{ font-family:var(--font-display); font-weight:700; font-size:15px; color:var(--ink); margin-bottom:14px; }
  .wc-compare-bars{ display:flex; align-items:flex-end; gap:4px; }
  .wc-compare-col{ flex:1; min-width:0; display:flex; flex-direction:column; align-items:center; gap:6px; }
  .wc-compare-bar-track{ width:100%; max-width:18px; height:56px; display:flex; align-items:flex-end; background:var(--paper); border-radius:3px; border:2px solid transparent; overflow:hidden; }
  .wc-compare-bar{ width:100%; min-height:3px; border-radius:2px; background:var(--muted); }
  .wc-compare-bar--GREAT_TIME{ background:var(--turquoise-dark); }
  .wc-compare-bar--STILL_GOOD_MIXED{ background:var(--mango-dark); }
  .wc-compare-bar--WET_SEASON_FEEL{ background:var(--terracotta-dark); }
  .wc-compare-month{ font-family:var(--font-mono); font-size:9px; text-transform:uppercase; letter-spacing:.02em; color:var(--muted); }
  .wc-compare-col--selected .wc-compare-bar-track{ border-color:var(--ink); }
  .wc-compare-col--selected .wc-compare-month{ color:var(--ink); font-weight:700; }
  .wc-compare-axis{ display:flex; justify-content:space-between; font-family:var(--font-mono); font-size:9px; text-transform:uppercase; letter-spacing:.04em; color:var(--muted); margin-top:8px; }
  .wc-compare-sentence{ font-family:var(--font-body); font-size:13px; color:var(--ink); font-weight:600; margin-top:14px; }
  .wc-compare-basis{ font-family:var(--font-body); font-size:11.5px; color:var(--muted); font-style:italic; margin-top:2px; }
  .wc-sr-only{ position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0,0,0,0); white-space:nowrap; border:0; }
  .wc-details summary{ display:inline-flex; align-items:center; gap:6px; font-family:var(--font-body); font-weight:600; font-size:13px; color:var(--turquoise-dark); cursor:pointer; list-style:none; padding:8px 0; min-height:var(--tap); }
  .wc-details summary::-webkit-details-marker{ display:none; }
  .wc-details-body{ margin-top:4px; }
  .wc-details-meta{ font-family:var(--font-mono); font-size:10px; text-transform:uppercase; letter-spacing:.04em; color:var(--muted); margin-bottom:10px; }
  .wc-details-body dl{ display:flex; flex-direction:column; gap:8px; margin-bottom:12px; }
  .wc-details-body dl > div{ display:flex; justify-content:space-between; gap:10px; font-family:var(--font-body); font-size:13px; }
  .wc-details-body dt{ color:var(--muted); }
  .wc-details-body dd{ color:var(--ink); font-weight:600; text-align:right; }
  .wc-details-note{ font-family:var(--font-body); font-size:12px; color:var(--muted); line-height:1.5; margin-bottom:8px; }
  .wc-details-disclaimer{ font-family:var(--font-body); font-size:11.5px; color:var(--muted); font-style:italic; }

  .faq-item{ border-bottom:1px solid var(--line); padding:14px 0; }
  .faq-item summary{ font-family:var(--font-body); font-weight:600; font-size:14.5px; cursor:pointer; list-style:none; display:flex; justify-content:space-between; align-items:center; gap:10px; min-height:var(--tap); }
  .faq-item summary::-webkit-details-marker{ display:none; }
  .faq-item summary::after{ content:'+'; font-size:20px; color:var(--mango); flex-shrink:0; }
  .faq-item[open] summary::after{ content:'\2212'; }
  .faq-item p{ font-size:13px; color:var(--muted); margin-top:8px; line-height:1.55; padding-right:26px; }

  /* ---------- FOOTER ---------- */
  footer{ padding:36px 20px 40px; text-align:center; background:var(--ink); color:rgba(255,255,255,.7); }
  footer .logo2{ font-family:var(--font-body); font-size:17px; font-weight:700; color:#fff; }
  footer .logo2 span{ color:var(--mango); }
  footer .f-links{ display:flex; flex-wrap:wrap; justify-content:center; gap:14px; margin:16px 0; font-size:12px; }
  /* Global footer follow CTA — deliberately quieter than .f-links (smaller
     type, muted handle) so it never outweighs the primary nav above it. */
  footer .f-follow{ margin:4px auto 0; }
  footer .f-follow-title{ font-family:var(--font-body); font-weight:700; font-size:12.5px; color:#fff; }
  footer .f-follow-sub{ font-family:var(--font-body); font-size:11px; color:rgba(255,255,255,.55); margin-top:3px; }
  footer .f-follow-cta{ display:inline-flex; align-items:center; gap:8px; margin-top:8px; font-family:var(--font-body); font-weight:600; font-size:11.5px; color:rgba(255,255,255,.9); }
  footer .f-follow-handle{ font-family:var(--font-mono); font-size:10px; color:rgba(255,255,255,.6); }
  footer .f-bottom{ font-family:var(--font-mono); font-size:10px; color:rgba(255,255,255,.4); margin-top:10px; }

  /* ============================================================
     RESPONSIVE
  ============================================================ */

  /* ---------- TABLET (>=768px) ---------- */
  @media (min-width:768px){
    .wrap{ max-width:680px; padding:0 32px; }
    section{ padding:48px 0; }
    .social-proof{ padding:0 0 8px; margin-top:-52px; }
    .social-proof-inner{ flex-direction:row; align-items:center; justify-content:space-between; gap:20px; padding:0 24px; min-height:80px; }
    .social-proof-main{ align-items:center; }
    .social-proof-cta{ width:auto; }
    #nightlife{ padding-bottom:24px; }
    #beach-clubs{ padding-top:24px; }
    #night-markets{ padding-bottom:24px; }
    #reels{ padding-top:24px; }
    /* Left edge still aligns with the heading above it (via margin-left,
       matching .wrap's inset), but the right side is deliberately left
       loose instead of centered — so the last card bleeds past the column
       into the page margin as a "there's more" cue instead of hard-stopping
       flush at the same edge as the heading. scroll-snap-type stays
       "mandatory" (tried "proximity" for smoother mouse/trackpad scroll,
       but it lets the row rest mid-card at an unaligned, half-cut position
       — worse than the minor jerkiness mandatory trades for). */
    .swipe-row, .reel-row{ margin-left:max(32px, calc(50vw - 340px + 32px)); margin-right:8px; padding:2px 0 10px; scroll-padding-left:0; scroll-snap-type:x mandatory; }
    .area-card, .disco-card, .mini-card, .venue-card, .reel-tile{ scroll-margin-left:0; }
    .chip-row{ max-width:680px; margin:0 auto; padding:2px 32px; }
    nav.topnav{ max-width:680px; padding:0 32px; }
    .mobile-menu ul{ max-width:680px; padding:8px 32px 14px; }

    .hero{ height:var(--hero-page-h-md); min-height:var(--hero-page-min-md); max-height:var(--hero-page-max-md); }
    [data-ttx-page-type="homepage"] .hero{ height:var(--hero-landing-h-md); min-height:var(--hero-landing-min-md); max-height:var(--hero-landing-max-md); }
    /* Centered + tightened (scale 1.25) on the wide lagoon shot so both
       cliffs frame the cove and the boats/turquoise water read clearly —
       dead-center at 1x cropped into almost pure pale water with no boats
       prominent. Uniform scale only, never stretched. */
    .hero img{ object-position:center 50%; transform:scale(1.25) translateY(var(--parallax-y, 0px)); }
    .hero-scrim{ background:linear-gradient(0deg, rgba(9,16,14,.1) 0%, rgba(9,16,14,.2) 25%, rgba(9,16,14,.45) 38%, rgba(9,16,14,.5) 50%, rgba(9,16,14,.45) 62%, rgba(9,16,14,.2) 75%, rgba(9,16,14,.1) 100%); }
    .hero-content{ max-width:680px; padding:0 32px; }
    .hero-tagline{ font-size:15px; }
    .hero-sub{ font-size:15px; max-width:44ch; }

    .area-card{ flex:0 0 34%; }
    .disco-card{ flex:0 0 36%; }
    .mini-card{ flex:0 0 34%; }
    .venue-card{ flex:0 0 34%; }
    .reel-row{ display:grid; grid-template-columns:repeat(4,1fr); gap:14px; overflow:visible; }
    .reel-tile{ flex:none; }

    /* Wider frame reads as a left/right diagonal split instead of a top/bottom one. */
    .season-diagonal{ display:flex; background:linear-gradient(115deg, var(--turquoise) 0%, var(--turquoise) 46%, var(--ink) 54%, var(--ink) 100%); }
    .season-zone{ flex:1; padding:28px 30px; }
    .season-zone.is-high, .season-zone.is-low{ padding:28px 30px; }

    /* Wider/shorter banner proportions so both features stay a compact intro
       strip instead of growing into a near-square block — applies equally to
       Bangla and Old Town, so they stay the same size at this breakpoint too. */
    .nightlife-feature{ aspect-ratio:21/9; }
    .nightlife-feature-content h3{ font-size:30px; }
  }

  /* ---------- DESKTOP (>=1024px) ---------- */
  @media (min-width:1024px){
    html{ scroll-padding-top:90px; }
    .wrap{ max-width:var(--container-content); padding:0 var(--gutter-desktop); }
    section{ padding:72px 0; }
    #nightlife{ padding-bottom:36px; }
    #beach-clubs{ padding-top:36px; }
    #night-markets{ padding-bottom:36px; }
    #reels{ padding-top:36px; }
    /* Desktop Layout Consistency Audit V1 — CAROUSEL STANDARD: CONTAINED.
       Was asymmetric (margin-right:8px fixed while margin-left grew with
       viewport) — at wide viewports the row kept bleeding almost to the
       screen edge with no bound, showing an ever-larger, uncontrolled
       "peek" instead of the deliberate small one the surrounding comments
       described. Symmetric now: first and last card both sit inside
       --container-content at every width, matching the area-guide
       template's carousels (which were already correct) and guaranteeing
       no accidental clipping. Desktop already shows hover-revealed
       .carousel-arrow prev/next buttons, so the "there's more" affordance
       doesn't depend on a visible peek the way it does on mobile. */
    .swipe-row, .reel-row{ margin-left:var(--carousel-inset); margin-right:var(--carousel-inset); padding:2px 0 10px; scroll-padding-left:0; scroll-snap-type:x mandatory; }

    nav.topnav{ max-width:var(--container-content); padding:0 var(--gutter-desktop); }
    .nav-links{ display:flex; align-items:center; gap:26px; }
    .nav-links a{ font-family:var(--font-body); font-size:13.5px; font-weight:600; color:#fff; transition:color .2s ease; }
    .nav-links a:hover, .nav-links a:focus-visible, .nav-links a.is-active{ color:var(--mango); }
    header.is-scrolled .nav-links a{ color:var(--ink); }
    .icon-btn, .mobile-menu{ display:none; }
    .discover{ display:none; }

    /* ---------- DESKTOP "PLAN" DROPDOWN — same trigger/panel pattern as
       the mobile menu (button + aria-expanded + hidden panel). ---------- */
    .nav-plan{ position:relative; }
    .nav-plan-trigger{ display:flex; align-items:center; gap:5px; background:none; border:none; cursor:pointer; padding:0; font-family:var(--font-body); font-size:13.5px; font-weight:600; color:#fff; transition:color .2s ease; }
    .nav-plan-trigger svg{ width:13px; height:13px; transition:transform .15s ease; }
    .nav-plan-trigger[aria-expanded="true"] svg{ transform:rotate(180deg); }
    .nav-plan-trigger:hover, .nav-plan-trigger:focus-visible{ color:var(--mango); }
    header.is-scrolled .nav-plan-trigger{ color:var(--ink); }
    .nav-plan-menu{ position:absolute; top:100%; left:50%; transform:translateX(-50%); margin-top:14px; min-width:210px; background:var(--surface); border-radius:var(--radius-sm); box-shadow:0 12px 28px rgba(23,48,42,.18); padding:6px; display:flex; flex-direction:column; z-index:70; }
    .nav-plan-menu[hidden]{ display:none; }
    .nav-plan-menu a{ font-family:var(--font-body); font-size:13.5px; font-weight:600; color:var(--ink) !important; padding:10px 12px; border-radius:8px; transition:background .15s ease; }
    .nav-plan-menu a:hover, .nav-plan-menu a:focus-visible{ background:var(--sand); color:var(--ink) !important; }

    .hero{ height:var(--hero-page-h-lg); min-height:var(--hero-page-min-lg); max-height:var(--hero-page-max-lg); }
    [data-ttx-page-type="homepage"] .hero{ height:var(--hero-landing-h-lg); min-height:var(--hero-landing-min-lg); max-height:var(--hero-landing-max-lg); }
    .hero-content{ max-width:var(--container-content); padding:0 var(--gutter-desktop); }
    .hero-tagline{ font-size:16px; }
    .hero-sub{ font-size:16px; max-width:52ch; }

    /* Desktop Layout Consistency Audit V1 — CARD SIZE TIERS. Was
       flex-basis percentages within 1 point of each other (19–20%) for
       every card family regardless of role, which read as arbitrary
       rather than deliberate, and drifted further apart at wide
       viewports once the row itself was unbounded (see the carousel fix
       above). Fixed pixel widths instead, in three tiers, now that the
       row's own width is capped by --container-content and so no longer
       grows past ~1084px content width at any viewport — meaning these
       produce the same card size at 1024*, 1280, 1440 and 1600 alike.
       (*At exactly 1024–1179px viewport the row is narrower than 1084px,
       since --container-content hasn't started centering yet — cards
       stay the same fixed width regardless, just fewer are visible.)
       COMPACT — catalogue/quick discovery (Night Markets).
       STANDARD — general recommendations (Popular Phuket Experiences;
       .area-card is included here too even though the homepage's own
       area-card carousel was removed in an earlier batch in favor of
       the interactive map — .zone-grid .area-card on the Areas Hub is a
       CSS Grid item and ignores flex-basis entirely, so this is a no-op
       there, kept only so .area-card has a sane value if it's ever used
       as a swipe-row card again).
       FEATURED — high-value commercial (Hotels/Nightlife/Beach Clubs —
       one shared .venue-card component per docs/CONTENT_ARCHITECTURE.md
       §9; do not re-differentiate hotels from nightlife/beach-clubs
       within this family, only the family itself sits in a bigger tier
       than Experiences). 260px so four cards plus gaps total 1082px and
       fill the 1084px rail — at 280px only three fitted and the row
       stopped 216px short of the rail, stranding it mid-section. */
    .mini-card{ flex:0 0 200px; }
    .area-card, .disco-card{ flex:0 0 240px; }
    .venue-card{ flex:0 0 260px; }
    .reel-row{ grid-template-columns:repeat(4,140px); gap:18px; justify-content:flex-start; }

    .season-zone.is-high, .season-zone.is-low{ padding:34px 36px; }
    .season-list li{ font-size:14px; }

    .weather-checker{ padding:28px 30px; }
    .wc-form{ flex-direction:row; align-items:flex-end; gap:14px; }
    .wc-field{ flex:1; min-width:0; }
    .wc-submit{ flex-shrink:0; padding:0 22px; margin-top:0; }
    .wc-grid{ grid-template-columns:repeat(4,1fr); }
    .wc-compare-bar-track{ max-width:26px; height:64px; }

    /* Even wider frame — keeps the feature a banner rather than a slab
       once the page column reaches full desktop width. Re-contained to
       the editorial max-width here (unlike the full-bleed mobile/tablet
       treatment above) but deliberately spans the *outer* 1180px, not
       the padded 1084px text column .wrap itself uses — genuinely wider
       than a normal card, still inside the page's own max-width. */
    .nightlife-feature{ aspect-ratio:26/9; max-width:var(--container-content); margin-left:auto; margin-right:auto; border-radius:var(--radius-card); }
  }

  /* ---------- WIDE DESKTOP (>=1280px) — map stage scale only. Scoped to
     the two sections that actually contain the map (#area-map on the
     homepage — renamed from #areas by the Homepage Map Decision, Option C
     — and #map on the Areas Hub); nothing else on the page widens.
     ---------- */
  @media (min-width:1280px){
    /* Desktop Layout System V2 — the map section keeps --container-content
       like every other section. It previously widened to --container-wide
       here, which pushed the map's left edge 50-70px off the sitewide rail
       (48px vs 98px at 1280, 108px vs 178px at 1440) and, because the
       component was fit-content-centred, added dead space rather than
       removing it. The component now fills the rail (see .map-component
       above), so only the map stage itself grows here — the island keeps
       its own 180:384 proportions, no path or viewBox change. */
    #area-map .map-media-row, #map .map-media-row{ height:760px; }
  }

  /* ---------- DESKTOP HOVER — narrow, explicit exception to the sitewide
     "touch surface, no hover choreography" rule (DESIGN.md, Elevation &
     Depth). Gated strictly to fine-pointer devices that can actually hover,
     so touch devices never see it regardless of viewport width. ---------- */
  @media (hover:hover) and (pointer:fine){
    .area-card, .disco-card, .mini-card, .venue-card, .reel-tile{ transition:transform .15s ease, box-shadow .15s ease; }
    a.area-card:hover, .disco-card:hover, .mini-card:hover, .venue-card:hover, .reel-tile:hover{ transform:scale(1.02); box-shadow:var(--shadow-card-hover); }
    .area-card-img img.is-photo, .disco-card-img img.is-photo, .mini-card-img img.is-photo, .venue-card-img img.is-photo{ transition:transform .2s ease; }
    a.area-card:hover .area-card-img img.is-photo, .disco-card:hover .disco-card-img img.is-photo, .mini-card:hover .mini-card-img img.is-photo, .venue-card:hover .venue-card-img img.is-photo{ transform:scale(1.03); }

    /* Mouse click-drag scroll — these rows hide their scrollbar, so without
       this a mouse-only desktop user has no way to move a carousel at all
       (no touch, no trackpad-style horizontal wheel by default). */
    .swipe-row, .reel-row{ cursor:grab; }
    .swipe-row.is-dragging, .reel-row.is-dragging{ cursor:grabbing; scroll-snap-type:none; user-select:none; }

    /* Shared CTA hover — one behavior for every filled/outline button
       regardless of tier, so "how a button responds" never varies, only
       its color does. */
    a.disco-cta:hover, .venue-card-cta:hover, .guide-cta:hover, .promo-cta:hover{ filter:brightness(1.04); transform:translateY(-1px); }
    .area-hub-cta:hover{ background:var(--turquoise-light); transform:translateY(-1px); }
  }

  /* ---------- CAROUSEL ARROWS — injected by JS as siblings of each
     .swipe-row/.reel-row, wrapped in .carousel-shell. Desktop-only: on
     touch/tablet the native swipe gesture is already the primary way to
     move these rows, and a floating button just adds clutter. ---------- */
  .carousel-shell{ position:relative; }
  .carousel-arrow{ display:none; }
  @media (min-width:1024px){
    .carousel-shell.has-no-scroll .carousel-arrow{ display:none !important; }
    .carousel-arrow{
      display:flex; align-items:center; justify-content:center;
      position:absolute; top:42%; transform:translateY(-50%);
      width:44px; height:44px; border-radius:50%; border:none;
      background:var(--surface); box-shadow:0 4px 16px rgba(23,48,42,.18);
      color:var(--ink); cursor:pointer; z-index:5;
      transition:opacity .15s ease, background .15s ease;
    }
    .carousel-arrow:hover{ background:var(--turquoise-light); color:var(--turquoise-dark); }
    .carousel-arrow svg{ width:20px; height:20px; }
    /* Desktop Visual QA V1 fix-up — was a flat -8px, positioned relative to
       .carousel-shell. The shell is intentionally full-bleed (the row's own
       margin is what creates the inset — see --carousel-inset above), so a
       shell-relative -8px floated the arrows out past the row's actual edge
       and, at 1024–1179px where the shell touches the true viewport edge,
       past the page itself (the ~8px overflow this fixes). Same token the
       row's margin uses, offset by half the arrow's own 44px width so it
       straddles the row's edge exactly as -8px was meant to. Always ≥48px,
       so the arrow can never reach the viewport edge at any width. */
    .carousel-arrow-prev{ left:calc(var(--carousel-inset) - 22px); }
    .carousel-arrow-next{ right:calc(var(--carousel-inset) - 22px); }
    /* .promo-swipe carousels (template-areas.php's "Popular Phuket
       Experiences" / "Top Hotels in Phuket") are never full-bleed like
       .swipe-row/.reel-row are — they sit inside .wrap on this page too
       — so the viewport-relative --carousel-inset token above measures
       the wrong thing here and lands the arrows mid-row instead of at
       its edge. Straddle the shell's own edge directly instead (same
       "half in, half out" idea, just relative to the row itself, not
       the viewport). Carousel Arrow Geometry V2. */
    .carousel-shell:has(> .promo-swipe) .carousel-arrow-prev{ left:-22px; }
    .carousel-shell:has(> .promo-swipe) .carousel-arrow-next{ right:-22px; }
    .carousel-arrow.is-disabled{ opacity:0; pointer-events:none; }
  }

/* ---------- EXPLORE PHUKET BY AREA — the full interactive map
   component (map + area list + selected-area preview), same one used
   on the Areas Hub (phuket-zones.html), embedded directly on the
   homepage instead of a stripped-down teaser. "Where to Stay" above
   stays focused on accommodation decisions (photos, who it's for,
   pros/cons); this component is the geographic discovery tool — map,
   names, location — so the two no longer show the same thing twice. */
.map-component{ display:flex; flex-direction:column; background:var(--surface); border-radius:var(--radius-card); box-shadow:var(--shadow-card); overflow:hidden; }

/* Mobile/tablet only (<1024px): side-label pills flank the map instead
   of a separate "Select an area" horizontal carousel below it — the
   island's own aspect ratio (180:384, tall/narrow) always left a lot
   of unused width beside it at these sizes, room enough for real tap
   targets. .map-media keeps a fixed aspect-ratio instead of a fixed
   height so it fills whatever width is left after the two label
   columns, at any viewport from 320 to 430px. Desktop (1024px+) is
   unchanged: hides this row's two label columns and treats
   .map-media-row as the old fixed-width .map-media slot. */
.map-media-row{ display:flex; padding:4px 0; }
.map-media{ position:relative; width:100%; flex:1 1 auto; min-width:0; aspect-ratio:180/384; max-height:480px; margin:0 auto; }
/* Targeted Visual/UX Correction Pass — homepage compact-map mobile cap;
   mirrors index.html and the desktop #areas .map-media-row override
   above. */
#areas .map-media{ max-height:400px; }
.map-svg{ position:absolute; inset:0; width:100%; height:100%; }
.map-svg .island{ fill:url(#island-gradient); stroke:var(--turquoise-dark); stroke-width:2; }
.bg-area{ fill:var(--ink); fill-opacity:.03; stroke:var(--ink); stroke-opacity:.16; stroke-width:.75; pointer-events:none; clip-path:url(#island-clip); }
/* One consistent color for every tourist-area polygon — matches the
   same simplification on phuket-zones.html (no legend ever explained
   the old per-zone hues, so they just read as visual noise). */
.area-shape{ cursor:pointer; fill:var(--turquoise-dark); stroke:var(--turquoise-dark); stroke-width:2; fill-opacity:.32; transition:fill-opacity .15s ease, stroke-width .15s ease, filter .15s ease; -webkit-tap-highlight-color:transparent; clip-path:url(#island-clip); }
.area-shape:hover, .area-shape:focus-visible{ fill-opacity:.5; stroke-width:2.5; }
.area-shape:focus-visible{ outline:2px solid var(--turquoise-dark); outline-offset:2px; }
.area-shape.is-active{ fill-opacity:.7; stroke-width:3.5; filter:drop-shadow(0 2px 5px rgba(11,110,104,.35)); }
.map-media.has-selection .area-shape:not(.is-active){ fill-opacity:.12; }
.area-label{ font-family:var(--font-body); font-weight:600; font-size:8px; fill:var(--ink); opacity:.88; pointer-events:none; paint-order:stroke; stroke:var(--paper); stroke-width:2.6px; stroke-linejoin:round; letter-spacing:.01em; text-anchor:middle; }

.map-tooltip{ position:absolute; z-index:5; pointer-events:none; background:var(--ink); color:#fff; font-family:var(--font-body); font-size:12px; font-weight:600; padding:6px 10px; border-radius:8px; white-space:nowrap; opacity:0; transform:translate(-50%,-8px); transition:opacity .12s ease; }
.map-tooltip.is-visible{ opacity:1; }

.map-side{ display:flex; flex-direction:column; border-top:1px solid var(--line); }

/* Superseded by .map-side-labels below 1024px (see comment above
   .map-media-row) — kept in the DOM/CSS rather than deleted, since
   .area-chips-row is still the tablet+desktop selector's underlying
   data-driven render target if this concept is ever reverted. */
.area-chips-wrap{ display:none; padding:14px 16px 4px; }
.area-chips-label{ font-family:var(--font-mono); font-size:10px; text-transform:uppercase; letter-spacing:.05em; color:var(--muted); display:block; margin-bottom:8px; }
.area-chips-row{ display:flex; gap:8px; overflow-x:auto; scrollbar-width:none; padding-bottom:2px; scroll-padding-left:16px; }
.area-chips-row::-webkit-scrollbar{ display:none; }
.area-chip{ flex:0 0 auto; font-family:var(--font-body); font-size:12.5px; font-weight:600; padding:0 15px; height:38px; display:inline-flex; align-items:center; border-radius:var(--radius-button); background:var(--surface); border:1px solid var(--line); color:var(--ink); white-space:nowrap; cursor:pointer; transition:background .15s ease, color .15s ease, border-color .15s ease; }
.area-chip.is-active{ background:var(--turquoise-dark); color:#fff; border-color:var(--turquoise-dark); }

.area-grid{ display:none; }
.area-grid-btn{ display:flex; flex-direction:column; align-items:flex-start; gap:1px; padding:9px 12px; border-radius:var(--radius-sm); border:1px solid var(--line); background:var(--surface); cursor:pointer; text-align:left; min-height:44px; justify-content:center; transition:background .15s ease, border-color .15s ease; }
.area-grid-btn:hover{ background:var(--sand); }
.area-grid-btn.is-active{ background:var(--turquoise-light); border-color:var(--turquoise-dark); }
.area-grid-name{ font-family:var(--font-body); font-weight:600; font-size:12.5px; color:var(--ink); line-height:1.25; }
.area-grid-btn.is-active .area-grid-name{ color:var(--turquoise-dark); }
.area-grid-zone{ font-family:var(--font-body); font-size:9.5px; color:var(--muted); }

/* Card treatment — previously just a top border with the photo and
   text floating loose on the section background. Same surface/shadow
   language as the site's other travel cards (hotel/venue cards), so
   the selected-area preview reads as one component, not two stacked
   pieces. Photo keeps its own radius/crop untouched. */
.map-detail{ background:var(--surface); border-radius:var(--radius-card); box-shadow:0 4px 14px rgba(34,29,24,.06); margin:16px; overflow:hidden; }
/* Empty state. Desktop Layout V2 gave the panel a real column width, which
   left a single centred line floating in it. It now carries the two things
   that actually help the decision — what tapping an area reveals, and where
   to go if you don't know which to tap — using journeys that already exist
   (the Areas Hub's Area Finder, and the hub itself from the homepage). No
   new functionality, no decorative filler. On mobile the panel is short and
   these simply stack under the prompt. */
.map-panel-empty{ display:flex; flex-direction:column; align-items:center; justify-content:center; gap:10px; padding:20px 18px; text-align:center; font-family:var(--font-body); font-size:12px; color:var(--muted); }
.map-panel-empty svg{ width:22px; height:22px; flex-shrink:0; stroke:var(--muted); }
.map-panel-empty-title{ font-family:var(--font-body); font-weight:600; font-size:13.5px; color:var(--ink); }
.map-panel-empty-hint{ max-width:34ch; line-height:1.5; }
.map-panel-empty-cta{ font-family:var(--font-body); font-weight:600; font-size:12px; color:var(--turquoise-dark); display:inline-flex; align-items:center; min-height:var(--tap); }
/* Visual Polish V2 — Phase 8: fills the default map-panel empty state
   with already-established area positioning (see index.html's mirrored
   comment) instead of leaving the desktop panel mostly blank. */
.map-quickpicks{ list-style:none; display:flex; flex-direction:column; gap:6px; width:100%; max-width:220px; text-align:left; margin:2px 0 0; padding-top:10px; border-top:1px solid var(--line); }
.map-quickpicks li{ display:flex; align-items:baseline; justify-content:space-between; gap:10px; font-size:11.5px; line-height:1.4; }
.map-quickpicks strong{ font-family:var(--font-body); font-weight:700; font-size:12px; color:var(--ink); flex-shrink:0; }
.map-quickpicks span{ color:var(--muted); text-align:right; }
/* Visual QA + Carousel Pass — Homepage Map Decision, Option C. The
   #areas section's own quick-read list (see index.html's mirrored
   comment for the full rationale) — more prominent than .map-quickpicks
   since this is now the section's main content, not a fallback empty
   state. Row layout at tablet+ lives in the 768px media query below. */
.areas-teaser-list{ list-style:none; display:flex; flex-direction:column; gap:12px; margin:20px 0 0; padding:16px 0 0; border-top:1px solid var(--line); max-width:440px; }
.areas-teaser-list li{ display:flex; align-items:baseline; justify-content:space-between; gap:14px; font-size:13.5px; }
.areas-teaser-list strong{ font-family:var(--font-display); font-weight:700; font-size:14.5px; color:var(--ink); }
.areas-teaser-list span{ color:var(--muted); text-align:right; }
.areas-teaser-ctas{ display:flex; align-items:center; gap:16px; flex-wrap:wrap; margin-top:20px; }
/* Areas Map Info Panel Cleanup V1 — text-only panel, no photo/video
   slot any more (previously a .map-panel-media 4:5 image block; the
   .ph/.ph-label placeholder pattern itself is still used elsewhere,
   e.g. .area-card-img, so it isn't touched — only this panel's own
   media markup/CSS is gone). Sized naturally to its own content now,
   never a fixed portrait height, so switching areas can't leave a
   tall blank gap under short copy. */
.map-panel-body{ padding:16px; min-width:0; }
.map-panel-zone{ font-family:var(--font-mono); font-size:9.5px; text-transform:uppercase; letter-spacing:.05em; color:var(--turquoise-dark); display:block; margin-bottom:3px; }
.map-panel-body h3{ font-family:var(--font-display); font-weight:700; font-size:var(--fs-title-std); margin-bottom:4px; }
.map-panel-body p.desc{ font-size:12px; color:var(--muted); line-height:1.45; margin-bottom:10px; display:-webkit-box; -webkit-line-clamp:3; -webkit-box-orient:vertical; overflow:hidden; }
.map-panel-section-label{ font-family:var(--font-mono); font-size:9px; text-transform:uppercase; letter-spacing:.05em; color:var(--turquoise-dark); display:block; margin-bottom:5px; }
.map-tags{ display:flex; flex-wrap:wrap; gap:6px; margin-bottom:10px; }
/* Top spots — up to 3 named places, plain compact rows (never full
   cards, no images) per this batch's spec. */
.map-panel-spots{ list-style:none; margin:0 0 10px; padding:0; display:flex; flex-direction:column; gap:4px; }
.map-panel-spots li{ font-family:var(--font-body); font-size:11.5px; color:var(--ink); line-height:1.4; padding-left:13px; position:relative; }
.map-panel-spots li::before{ content:''; position:absolute; left:1px; top:7px; width:5px; height:5px; border-radius:50%; background:var(--mango); }
.map-panel-note{ font-size:11.5px; color:var(--muted); line-height:1.4; margin-bottom:10px; display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:2; overflow:hidden; }
.map-panel-note strong{ color:var(--ink); font-weight:600; }
.map-panel-body .text-link{ display:inline-flex; align-items:center; gap:5px; }
/* Desktop-only balance photo (Phase 2G) — see main.js's renderPanel().
   Hidden by default so mobile/tablet stays exactly the text-only panel
   from Info Panel Cleanup V1 (never regressed); re-shown only inside the
   1024px desktop block below. */
.map-panel-photo{ display:none; }
.map-panel-photo img{ width:100%; height:100%; object-fit:cover; display:block; }
/* Outline pill, deliberately quiet — sits above the map, under the
   section intro, so it's found without competing with the venue/
   hotel booking CTAs elsewhere on the page (solid turquoise fill). */
.area-hub-cta{ display:inline-flex; align-items:center; justify-content:center; gap:6px; height:44px; padding:0 18px; margin:14px 0 18px; border-radius:var(--radius-button); border:1.5px solid var(--turquoise-dark); background:var(--paper); color:var(--turquoise-dark); font-family:var(--font-body); font-weight:600; font-size:13.5px; box-shadow:inset 0 1px 0 rgba(255,255,255,.4), 0 2px 6px rgba(34,29,24,.06); transition:background .15s ease, transform .15s ease; }
a.disco-cta:active, .venue-card-cta:active, .promo-cta:active, .area-hub-cta:active{ transform:translateY(1px) scale(.98); }

@media (min-width:1024px){
  /* UX Polish V1 — approved orientation flip: info panel left, map
     right (was map left / info right). row-reverse only changes
     visual order, not DOM order — TTX_SELECT_AREA, the Finder's map
     fallback, and every id/selector are untouched. Map stays the
     larger, dominant element; only which side it renders on changes. */
  .map-component{ flex-direction:row-reverse; align-items:stretch; }
  /* Refinement pass: %-based sizing didn't actually grow the map — see
     index.html for the full measurement/rationale. Column now sized to
     the map's true shape (no wasted space), grown via height instead. */
  .map-media-row{ flex:1 1 auto; height:660px; align-self:center; padding:0; gap:0; }
  /* Targeted Visual/UX Correction Pass — the homepage's map instance
     (#areas) is deliberately shorter than the Areas Hub's own 660px:
     "a compact / attractive version" per the brief, with the hub page
     (/areas/, template-areas.php) staying the deeper, full-height
     experience. Higher specificity than the base rule above, so only
     #areas is affected. Mirrors index.html exactly. */
  #areas .map-media-row{ height:520px; }
  .map-media{ height:100%; width:auto; aspect-ratio:180/384; max-height:none; margin:0 auto; }
  /* Info Panel Cleanup V1 — now that the panel never carries a photo,
     it no longer needs (or should take) an even flex:1 share of the
     row: at this component's actual width, that left a wide, mostly
     empty column beside a narrow map. Fixed, content-sized width
     instead (chips/list/CTA all read comfortably at ~280px), and the
     whole component hugs map+panel rather than stretching to the
     section's full width — map dominant, panel narrow, no dead
     space. The map's own box (~340px, height-driven off its actual
     north–south island shape) can't literally reach "75–80% width"
     without either an unreadably thin panel or an absurdly tall map
     — see this batch's report for the math; this is the closest
     large-map/narrow-panel balance achievable without either. */
  .map-component{ width:auto; max-width:100%; margin:0; }
  /* Desktop Map Containment + Portrait Photo Fix — was two separate fixed
     widths at two breakpoints (240px here, a redundant 280px override
     further down at >=1280px) — a fragile stepped total, not a resilient
     one. One fluid rule instead: clamp(280px, 24vw, 320px) sits at 280px
     from 1024px up to ~1167px, scales smoothly to 320px by ~1333px, and
     holds at 320px beyond that — matching the requested "info panel:
     approximately 280-320px" without a breakpoint-specific override. */
  .map-side{ flex:0 1 clamp(340px, 38%, 440px); min-width:320px; max-width:440px; align-self:stretch; margin-top:0; border-top:none; border-left:none; border-right:1px solid var(--line); justify-content:center; }

  .map-detail{ margin:0; }
  .map-panel-body p.desc{ -webkit-line-clamp:3; }
  /* Desktop-only balance photo (Phase 2G) — the info column can otherwise
     read shorter than the tall map beside it; a small photo at the bottom
     helps fill that height without stretching content artificially. Not a
     hero — capped, own radius, never distorted. Portrait 3:4 (was 16:9) —
     a landscape crop inside a ~280-320px column produced a short, squeezed
     image; 3:4 matches the site's normal card-photo language (hotel/venue/
     area cards are all portrait) and reads naturally in a narrow column. */
  .map-panel-photo{ display:block; margin-top:12px; aspect-ratio:3/4; border-radius:var(--radius-sm); overflow:hidden; }
}

/* ---------- AREAS HUB placeholder tone (Kata Noi's card, no photo yet) ---------- */
.ph.tone-area{ --ph-a:#DCEFEC; --ph-b:#BFE1DC; }

/* ---------- BREADCRUMB — template-areas.php only so far ---------- */
.crumb{ font-family:var(--font-mono); font-size:10.5px; color:var(--muted); padding:14px 0 0; display:flex; gap:6px; flex-wrap:wrap; }
.crumb a{ color:var(--turquoise-dark); display:inline-block; padding:6px 0; margin:-6px 0; }

/* ============================================================
   AREAS HUB ZONE GRID — template-areas.php only. Deliberately scoped
   under .zone-grid rather than reusing the bare .area-card family:
   main.css's unscoped .area-card/.area-card-* already style the
   homepage's swipe-row "Where to Stay" carousel (flex-basis,
   scroll-snap, white text baked into the photo overlay). The zone
   grid's card is structurally different — a plain grid item, desc in
   a separate white body panel below the photo — so it needs its own
   rules, not a redefinition of the carousel's. Same visual language
   (radius, shadow) as the rest of the theme, just not the same DOM
   shape.
============================================================ */
.zone-grid{ display:grid; grid-template-columns:repeat(2, 1fr); gap:14px; }
.zone-grid .area-card{ background:var(--surface); border-radius:var(--radius-card); overflow:hidden; box-shadow:0 4px 16px rgba(34,29,24,.07); display:flex; flex-direction:column; transition:transform .15s ease, box-shadow .15s ease; }
.zone-grid .area-card-img{ position:relative; aspect-ratio:4/5; overflow:hidden; }
.zone-grid .area-card-img .ph{ align-items:flex-start; }
.zone-grid .area-card-scrim{ position:absolute; inset:0; background:linear-gradient(0deg, rgba(23,42,36,.8) 0%, rgba(23,42,36,.32) 28%, rgba(23,42,36,0) 48%); pointer-events:none; }
.zone-grid .area-card-content{ position:absolute; left:12px; right:12px; bottom:10px; }
.zone-grid .area-card-zone{ display:block; font-family:var(--font-mono); font-size:9px; text-transform:uppercase; letter-spacing:.05em; color:rgba(255,255,255,.75); margin-bottom:2px; }
.zone-grid .area-card-title{ font-family:var(--font-display); font-weight:700; font-size:var(--fs-title-std); line-height:1.25; color:var(--paper); margin:0; text-shadow:0 1px 4px rgba(0,0,0,.2); display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:2; overflow:hidden; min-height:2.5em; }
.zone-grid .area-card-body{ padding:11px 13px 13px; flex:1; display:flex; flex-direction:column; gap:8px; }
/* Clamped + min-height: every card's description occupies the same
   space regardless of copy length — see phuket-zones.html for the full
   rationale (this is the WP-side mirror of that same fix). */
.zone-grid .area-card-desc{ font-size:11.5px; color:var(--muted); line-height:1.45; display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:4; overflow:hidden; min-height:67px; }
/* .zone-grid .area-card-footer (not .area-card-bestfor) now carries
   margin-top:auto — see below. */
.zone-grid .area-card-bestfor-label{ display:block; font-family:var(--font-mono); font-size:9px; text-transform:uppercase; letter-spacing:.05em; color:var(--turquoise-dark); margin-bottom:5px; }
/* Fixed two-row tag zone. Tag counts differ per area (1-4 wrapped rows
   were driving an 80px height spread across otherwise identical cards);
   the zone now always occupies two rows and clips beyond that, so tag
   count never changes card geometry. */
.zone-grid .area-card-tags{ display:flex; flex-wrap:wrap; gap:5px; height:calc(2 * 22px + 5px); min-height:calc(2 * 22px + 5px); overflow:hidden; align-content:flex-start; }
.zone-grid .area-card-tags .area-tag{ font-size:10px; padding:3px 8px; }
/* Groups tags + CTA as one unit pinned to the card's bottom edge —
   works whether a card has both, just one, or neither. */
.zone-grid .area-card-footer{ margin-top:auto; display:flex; flex-direction:column; gap:8px; }
/* Reserved slot: an area card without a guide CTA keeps the footer the
   same height as one with it (30px button + 8px gap), so CTA presence
   never changes card geometry. */
.zone-grid .area-card-footer:not(:has(.area-card-cta))::after{ content:''; display:block; height:30px; }
/* margin-top removed — .area-card-footer's own gap:8px now provides
   the spacing from the tags block above it. */
.zone-grid .area-card-cta{ font-family:var(--font-body); font-weight:600; font-size:11.5px; color:var(--turquoise-dark); align-self:flex-start; display:inline-flex; align-items:center; height:30px; padding:0 12px; border-radius:var(--radius-button); border:1.5px solid var(--turquoise-dark); transition:background .15s ease; }
.zone-grid .area-card-cta:active{ transform:scale(.96); }
.zone-grid-empty{ grid-column:1 / -1; text-align:center; padding:30px 10px; font-size:13px; color:var(--muted); }

@media (hover:hover) and (pointer:fine){
  .zone-grid .area-card:hover{ transform:scale(1.015); box-shadow:0 8px 22px rgba(34,29,24,.12); }
  .zone-grid .area-card-cta:hover{ background:var(--turquoise-light); }
  .promo-cta:hover{ filter:brightness(.93); }
}

/* ---------- AREA GUIDE SUMMARY — plain prose, short SEO section ---------- */
.area-guide-copy p{ font-family:var(--font-body); font-size:14px; color:var(--ink); line-height:1.65; max-width:68ch; margin-bottom:14px; }
.area-guide-copy p:last-child{ margin-bottom:0; }

/* ============================================================
   PROMO CAROUSELS — template-areas.php's Tours & Hotels rows. Same
   card language as .zone-grid .area-card (radius, shadow, tags) but a
   swipe row, since these are affiliate CTAs, not internal nav.
============================================================ */
/* scroll-padding-left matches the 4px padding that gives card shadows and
   the hover scale room to breathe. Without it, scroll-snap aligns the first
   card to the container's start edge and silently consumes that padding
   (scrollLeft settles at 4), landing the first card 4px left of the section
   heading — the "carousel starting edges vary between pages" drift, since
   .swipe-row rows have no such padding and sit exactly on the rail. */
.promo-swipe{ display:flex; gap:var(--gap-carousel); overflow-x:auto; scroll-snap-type:x mandatory; padding:4px 4px 14px; margin:0 -4px; scroll-padding-left:4px; scrollbar-width:none; -webkit-overflow-scrolling:touch; }
.promo-swipe::-webkit-scrollbar{ display:none; }
/* One width for the family — was 78% here against #tours' 62%, so the
   Hotels row's cards were 273px on mobile against Tours' 217px. */
.promo-card{ flex:0 0 62%; scroll-snap-align:start; background:var(--surface); border-radius:var(--radius-card); overflow:hidden; box-shadow:0 4px 16px rgba(34,29,24,.07); display:flex; flex-direction:column; }
.promo-card-img{ position:relative; aspect-ratio:4/3; overflow:hidden; }
.promo-card-body{ padding:14px 15px 16px; display:flex; flex-direction:column; gap:6px; flex:1; }
.promo-card-eyebrow{ font-family:var(--font-mono); font-size:9.5px; text-transform:uppercase; letter-spacing:.05em; color:var(--turquoise-dark); }
/* font-family corrected to Fraunces (var(--font-display)) — this was the
   one card-title rule in the theme still using Public Sans, out of step
   with every other card family (disco/venue/hotel/nearby all use
   Fraunces) and with this same class's own copies in things-to-do.html
   and phuket-zones.html, both already correct. */
.promo-card-body h3{ font-family:var(--font-display); font-weight:700; font-size:15px; line-height:1.25; display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:2; overflow:hidden; min-height:2.5em; }
/* Clamped like #tours' own override already was — the base rule was
   unclamped, so the #hotels row's descriptions free-sized (35-52px)
   while #tours' were a fixed 54px, giving one component two heights. */
.promo-card-desc{ font-size:12px; color:var(--muted); line-height:1.45; display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:3; overflow:hidden; min-height:54px; }
.promo-card-meta{ font-family:var(--font-mono); font-size:10.5px; color:var(--muted); margin-top:-2px; }
.promo-cta{ display:inline-flex; align-items:center; justify-content:center; gap:6px; font-family:var(--font-body); font-weight:600; font-size:12.5px; color:#fff; background:var(--mango); padding:0 16px; height:38px; border-radius:var(--radius-button); margin-top:auto; align-self:flex-start; box-shadow:inset 0 1px 0 rgba(255,255,255,.2), 0 3px 8px rgba(34,29,24,.10); transition:filter .15s ease, transform .15s ease; }
.promo-cta:active{ transform:scale(.96); }
.promo-swipe-hint{ font-family:var(--font-mono); font-size:10px; color:var(--muted); margin-top:-6px; margin-bottom:14px; }
/* Curated-hotel corner badge — same treatment as the homepage Top
   Hotels' .area-highlight (not shared here since /areas/ has no other
   use for that class), and a taller portrait crop scoped to just this
   section since the hotel photos are portrait-shot, unlike the 4:3
   Tours & Activities promo-cards above it on the same page. */
#hotels .promo-card-img{ aspect-ratio:4/5; }
.promo-badge{ position:absolute; top:10px; left:10px; z-index:1; background:var(--mango); color:#fff; font-family:var(--font-body); font-weight:700; font-size:var(--fs-micro); letter-spacing:.02em; padding:5px 10px; border-radius:var(--radius-tag); }

/* Tours & Activities aligned to the homepage/Things-to-Do curated card
   pattern (.disco-card) — same curated-activity purpose, several
   identical entities (Maya Bay, Phang Nga Bay, Andamanda). Scoped to
   #tours only (ID beats the shared .promo-card class) so Hotels' own
   portrait-card styling above is completely untouched. */
#tours .promo-card{ flex:0 0 62%; box-shadow:var(--shadow-card); transition:transform .15s ease, box-shadow .15s ease; }
#tours .promo-card:active{ transform:scale(1.02); }
#tours .promo-card-img{ aspect-ratio:4/5; }
#tours .promo-card-body{ padding:14px 16px 16px; gap:0; }
#tours .promo-card-eyebrow{ margin-bottom:4px; }
#tours .promo-card-body h3{ line-height:1.4; margin-bottom:4px; }
#tours .promo-card-desc{ font-size:var(--fs-small); margin-bottom:10px; display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:3; overflow:hidden; min-height:54px; }
/* Short intro + 4-item quick list + closing line for the Area Guide
   summary — was two dense prose paragraphs (155 words, read like an
   article on mobile). */
.area-guide-list{ list-style:none; max-width:68ch; margin-bottom:16px; display:flex; flex-direction:column; gap:10px; }
.area-guide-list li{ font-family:var(--font-body); font-size:14px; color:var(--ink); line-height:1.5; }
.area-guide-list strong{ font-weight:700; }

/* Areas Hub's own grid/carousel breakpoints, matching the theme's
   existing 768/1024 tablet/desktop system (phuket-zones.html used
   640/960 as a standalone page; this maps those same 2/3/4-column and
   card-width steps onto main.css's shared breakpoints instead of
   introducing new ones). */
@media (min-width:768px){
  .zone-grid{ grid-template-columns:repeat(3, 1fr); }
  .promo-card{ flex-basis:44%; }
  #tours .promo-card{ flex-basis:36%; }
  /* Wider cards from here up have more room per line, so 3 clamped
     lines already show as much as 4 did on a narrow phone. */
  .zone-grid .area-card-desc{ -webkit-line-clamp:3; min-height:50px; }
  .areas-teaser-list{ flex-direction:row; max-width:none; gap:28px; }
  .areas-teaser-list li{ flex-direction:column; align-items:flex-start; gap:3px; flex:1; }
  .areas-teaser-list span{ text-align:left; }
}
@media (min-width:1024px){
  .zone-grid{ grid-template-columns:repeat(4, 1fr); }
  /* Desktop Layout Consistency Audit V1 — STANDARD tier, same fixed
     width as .disco-card/.s-card/.nearby-card sitewide (see main.css's
     card-tier comment in the 1024px block above for the full rationale). */
  .promo-card, #tours .promo-card{ flex:0 0 240px; }
}
@media (hover:hover) and (pointer:fine){
  #tours .promo-card:hover{ transform:scale(1.015); box-shadow:var(--shadow-card-hover); }
}

/* ============================================================
   AREA FINDER v1 — chips-based preference matcher. Same design system
   as everywhere else: chip treatment matches .disco-filter-chip exactly
   (same size/spacing/active state), CTA hierarchy matches .guide-cta
   (internal action, turquoise) and .promo-cta (affiliate hotel links,
   mango) rather than inventing a third color. No gradients, no
   glassmorphism, no large animation. Kept in sync by hand with the
   identical block in phuket-zones.html's inline <style> — same
   convention already used for shortDescription/tags/etc in
   area-data.js vs the map's own AREA_DATA.
============================================================ */
.finder-card{ background:var(--surface); border-radius:var(--radius-card); box-shadow:var(--shadow-card); padding:20px 18px 22px; }
.finder-intro{ font-family:var(--font-body); font-size:var(--fs-small); color:var(--muted); margin-top:6px; margin-bottom:18px; max-width:52ch; }
.finder-group-label{ font-family:var(--font-mono); font-size:10px; text-transform:uppercase; letter-spacing:.05em; color:var(--turquoise-dark); display:block; margin-bottom:4px; }
.finder-helper{ font-family:var(--font-body); font-size:11.5px; color:var(--muted); margin-bottom:10px; }
.finder-chips-row{ display:flex; flex-wrap:wrap; gap:8px; margin-bottom:20px; }
.finder-chip{ font-family:var(--font-body); font-size:12.5px; font-weight:600; padding:0 14px; min-height:40px; display:inline-flex; align-items:center; border-radius:var(--radius-button); background:var(--surface); border:1px solid var(--line); color:var(--ink); white-space:nowrap; cursor:pointer; transition:background .15s ease, color .15s ease, border-color .15s ease, opacity .15s ease; }
.finder-chip.is-active{ background:var(--turquoise-dark); color:#fff; border-color:var(--turquoise-dark); }
.finder-chip:disabled{ opacity:.4; cursor:not-allowed; }
.finder-nightlife-row{ display:flex; gap:6px; background:var(--sand); border-radius:var(--radius-button); padding:4px; margin-bottom:20px; }
.finder-nl-option{ flex:1 1 0; font-family:var(--font-body); font-size:11.5px; font-weight:600; min-height:38px; border-radius:calc(var(--radius-button) - 3px); background:transparent; border:none; color:var(--ink); cursor:pointer; transition:background .15s ease, color .15s ease; }
.finder-nl-option.is-active{ background:var(--turquoise-dark); color:#fff; }
.finder-cta{ display:flex; align-items:center; justify-content:center; width:100%; height:48px; border:none; border-radius:var(--radius-button); background:var(--turquoise-dark); color:#fff; font-family:var(--font-body); font-weight:700; font-size:14.5px; cursor:pointer; box-shadow:inset 0 1px 0 rgba(255,255,255,.2), 0 3px 8px rgba(34,29,24,.10); transition:filter .15s ease, transform .15s ease; }
.finder-cta:disabled{ opacity:.4; cursor:not-allowed; }
@media (hover:hover) and (pointer:fine){ .finder-cta:not(:disabled):hover{ filter:brightness(1.04); transform:translateY(-1px); } }
.finder-cta:not(:disabled):active{ transform:translateY(1px) scale(.98); }

/* Compact, quiet, easy to reopen — never another large card. Two short
   lines of actual info plus a text-only "Change" link, not a
   run-on sentence. */
.finder-summary{ display:flex; flex-wrap:wrap; align-items:center; justify-content:space-between; gap:10px 16px; background:var(--surface); border-radius:var(--radius-sm); box-shadow:var(--shadow-card); padding:12px 16px; }
.finder-summary[hidden]{ display:none; }
.finder-summary-text{ display:flex; flex-direction:column; gap:2px; min-width:0; }
.finder-summary-label{ font-family:var(--font-mono); font-size:9px; text-transform:uppercase; letter-spacing:.05em; color:var(--turquoise-dark); }
.finder-summary-picks{ font-family:var(--font-body); font-size:12.5px; font-weight:600; color:var(--ink); }
.finder-summary-nightlife{ font-family:var(--font-body); font-size:11px; color:var(--muted); }
.finder-change-btn{ font-family:var(--font-body); font-weight:600; font-size:12px; color:var(--turquoise-dark); background:none; border:none; cursor:pointer; padding:6px 0; flex-shrink:0; }

/* Result Presentation V2 — one coherent recommendation module (single
   surface/shadow), not three equally-heavy floating cards — internal
   items separated by a hairline. Deliberately NO weight tapering by
   rank any more: all three results render at the same size/weight so
   they read as three comparable options, not "the answer plus two
   runners-up" — see docs note in area-finder-ui.js. scroll-margin-top
   clears the fixed header on the post-submit scroll. */
.finder-results-title{ font-family:var(--font-mono); font-size:11px; text-transform:uppercase; letter-spacing:.06em; color:var(--turquoise-dark); margin:22px 0 4px; scroll-margin-top:70px; }
.finder-results-sub{ font-family:var(--font-body); font-size:12.5px; color:var(--muted); line-height:1.5; margin:0 0 14px; }
.finder-results-module{ background:var(--surface); border-radius:var(--radius-card); box-shadow:var(--shadow-card); overflow:hidden; }
.finder-result-item{ padding:16px 18px; }
.finder-result-item + .finder-result-item{ border-top:1px solid var(--line); }
.finder-result-head{ display:flex; flex-direction:column; align-items:flex-start; gap:4px; margin-bottom:6px; }
.finder-result-head h3{ font-family:var(--font-display); font-weight:700; font-size:var(--fs-title-std); }
/* One badge style for every result — a trait-derived positioning label
   ("Best for beach & space", "Best overall balance"), not a rank word,
   so no single result reads as visually or semantically "the winner". */
.finder-result-badge{ font-family:var(--font-body); font-weight:700; font-size:9.5px; letter-spacing:.02em; text-transform:uppercase; padding:3px 9px; border-radius:var(--radius-tag); background:var(--turquoise-light); color:var(--turquoise-dark); white-space:nowrap; }
/* Compact chips, not a mono metadata line — Public Sans, quiet fill,
   never the visually dominant element on the card. */
.finder-result-matched{ display:flex; flex-wrap:wrap; gap:5px; margin-bottom:8px; }
.finder-matched-chip{ font-family:var(--font-body); font-size:10.5px; font-weight:600; color:var(--turquoise-dark); background:var(--turquoise-light); padding:3px 9px; border-radius:var(--radius-tag); }
.finder-result-desc{ font-size:12.5px; color:var(--muted); line-height:1.5; margin-bottom:10px; }
.finder-fit-label, .finder-caveat-label{ font-family:var(--font-mono); font-size:9px; text-transform:uppercase; letter-spacing:.05em; display:block; margin-bottom:2px; }
.finder-fit-label{ color:var(--turquoise-dark); }
.finder-caveat-label{ color:var(--muted); }
.finder-result-fit, .finder-result-caveat{ margin-bottom:10px; }
.finder-result-fit p, .finder-result-caveat p{ font-size:13px; color:var(--ink); line-height:1.5; }
.finder-hotels{ display:flex; flex-wrap:wrap; align-items:center; gap:7px; margin-bottom:10px; }
.finder-hotels-label{ font-family:var(--font-mono); font-size:9.5px; text-transform:uppercase; letter-spacing:.05em; color:var(--muted); width:100%; margin-bottom:1px; }
.finder-hotel-link{ font-family:var(--font-body); font-weight:600; font-size:12px; color:#fff; background:var(--mango); padding:0 12px; height:30px; display:inline-flex; align-items:center; border-radius:var(--radius-button); }
.finder-hotel-link.is-info{ background:none; color:var(--muted); font-style:italic; font-weight:400; height:auto; padding:0; }
.finder-explore-cta{ display:inline-flex; align-items:center; justify-content:center; gap:6px; height:40px; padding:0 18px; border-radius:var(--radius-button); background:var(--turquoise-dark); color:#fff; font-family:var(--font-body); font-weight:600; font-size:13px; border:none; cursor:pointer; }
.finder-explore-cta.is-map-link{ background:var(--surface); color:var(--turquoise-dark); border:1.5px solid var(--turquoise-dark); }
