/* nav.css — Shared /p navbar + dropdown menu. One navbar for every /p page.
   Design source: Figma 158:3 (guest) / 158:5 (member + dropdown). Uses tokens.css. */

.ppw-nav {
  position: sticky;
  top: 0;
  z-index: 1000;   /* outrank any in-page z-index (e.g. expanded ticket cards) */
  /* Safe-area aware since 2026-08-22 (viewport-fit=cover, see p/tokens.css).
     The bar now grows by the top inset and pads that growth away, so its
     CONTENT box stays exactly --navbar-h tall while its border box reaches
     up under the status bar / Dynamic Island — without this the logo and
     the bell/hamburger row render underneath the clock, worst in
     Add-to-Home-Screen standalone mode where the top inset is ~59px rather
     than 0. box-sizing is set explicitly rather than inherited: every /p
     page declares its own `* { box-sizing: border-box }` but p/tokens.css
     (the one stylesheet they all share) does not, and neither does this
     file, so height + padding must not depend on the page happening to
     carry that rule. Side padding absorbs the landscape insets so the
     controls clear the notch when the phone is rotated. */
  box-sizing: border-box;
  height: var(--navbar-total-h);
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--safe-top) calc(16px + var(--safe-right)) 0 calc(16px + var(--safe-left));
  /* Glass elevation (2026-07-10) — translucent + blurred instead of a flat
     fill, so page content shows through faintly as it scrolls underneath
     (position:sticky already gives this the right stacking context). Alpha
     approximates --navbar-bg (#080818); CSS custom properties can't carry
     their own alpha without color-mix(), so this is hardcoded to match. */
  background: rgba(5, 5, 5, 0.72);
  backdrop-filter: blur(14px) saturate(1.3);
  -webkit-backdrop-filter: blur(14px) saturate(1.3);
  border-bottom: 1px solid transparent;
  box-shadow: 0 4px 24px rgba(var(--shadow-rgb), calc(0.3 * var(--shadow-mult)));
  font-family: var(--font-thai);
  /* 2026-07-27 — same iOS backdrop-filter compositing fix as .emoji-icon
     (see p/tokens.css). Only .emoji-icon content (the CARDS dropdown) was
     protected by that rule; the raw inline-SVG icons this navbar also
     carries directly (.ppw-nav__lang-icon, .ppw-nav__login-icon) sit
     unprotected inside this same blurred stacking context, so the
     container itself is promoted too. Safe on a `position: sticky` element
     — this only breaks sticky when applied to an ANCESTOR of the sticky
     element, not the sticky element itself. */
  transform: translateZ(0);
}
/* Gradient hairline replacing the old flat border-bottom — thin gold→teal
   glow instead of plain gray, echoed on the two dropdowns below for
   continuity. */
.ppw-nav::after {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: -1px;
  height: 1px;
  background: linear-gradient(90deg, rgba(var(--accent-gold-rgb),0.55), rgba(var(--accent-teal-rgb),0.55), rgba(var(--accent-gold-rgb),0.55));
  opacity: 0.7;
  pointer-events: none;
}

/* Brand */
.ppw-nav__brand { display: flex; flex-direction: column; gap: 2px; }
.ppw-nav__brand-title { font-size: 16px; font-weight: 700; color: var(--text-primary); line-height: 1; text-decoration: none; transition: text-shadow 0.2s ease; }
/* White glow instead of a color swap (2026-07-10 fix) — gold matched the
   "#JASPERConcert" tag directly beneath it, reading as a clash rather
   than a hover state. Staying on --text-primary keeps it a neutral, always-
   legible affordance instead of competing with the tag's own accent color. */
@media (hover: hover) { a.ppw-nav__brand-title:hover { text-shadow: 0 0 12px rgba(var(--surface-rgb), 0.55); } }
.ppw-nav__brand-tag   { font-size: 11px; color: var(--accent-gold-text); line-height: 1; }
/* Darker gold specifically for THIS plain-text use (2026-08-08 fix, revised
   same day) — the shared --accent-gold token is tuned to ~3.2:1 on purpose
   (see LIGHT_MODE_COLOR_BIBLE.md §5's 3-round history — 4.5:1 read as "too
   brown", the whole point of that saga was landing on 3.2-3.5:1 as the
   accepted vibrant-but-readable floor). First pass here wrongly reached for
   strict WCAG 4.5:1 (#814908) for this one selector, inconsistent with that
   established floor and reported as "หม่น อึมครึม" (dull/muddy) — corrected
   to #9C5809, same 3.2-3.5:1 target as the rest of the palette (measures
   ~3.5:1 against this navbar), same hue/saturation as --accent-gold, just
   less lightness. Not touching --accent-gold itself — this stays scoped to
   this one plain-text selector. */
:root[data-theme="light"] .ppw-nav__brand-tag { color: var(--accent-gold-text); }

/* Right cluster */
.ppw-nav__right { display: flex; align-items: center; gap: 8px; }

/* Dropdown header row — "เมนู" label (left) + theme/language toggles (right) */
.ppw-nav__dropdown-header { display: flex; align-items: center; justify-content: space-between; }
.ppw-nav__header-actions { display: flex; align-items: center; gap: 8px; }

/* Theme + language toggle pills (theme sits before language). */
.ppw-nav__lang-btn, .ppw-nav__theme-btn {
  box-sizing: border-box;
  display: flex; align-items: center; justify-content: center; gap: 4px;
  padding: 5px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-3xl);
  background: transparent;
  color: var(--text-secondary);
  font: 600 11px var(--font-thai);
  cursor: pointer;
  transition: border-color 0.2s ease, box-shadow 0.2s ease, color 0.2s ease;
}
@media (hover: hover) { .ppw-nav__lang-btn:hover, .ppw-nav__theme-btn:hover { border-color: rgba(var(--accent-teal-rgb),0.5); box-shadow: 0 0 12px rgba(var(--accent-teal-rgb),0.22); color: var(--text-primary); } }
/* Theme is icon-only (no "TH"/"EN"-style label) — sized to just fit the
   icon instead of inheriting the language pill's wider text padding. */
.ppw-nav__theme-btn { padding: 6px; border-radius: var(--radius-full); }
.ppw-nav__lang-icon, .ppw-nav__theme-icon { display: flex; font-size: 11px; line-height: 1; }

/* Guest login pill */
.ppw-nav__login {
  display: flex; align-items: center; gap: 6px;
  height: 36px; box-sizing: border-box;
  padding: 0 14px;
  border: 1px solid rgba(var(--surface-rgb), 0.18);
  border-radius: var(--radius-3xl);
  background: transparent;
  color: var(--text-primary);
  font: 600 13px var(--font-thai);
  cursor: pointer;
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
@media (hover: hover) { .ppw-nav__login:hover { border-color: rgba(var(--accent-teal-rgb),0.5); box-shadow: 0 0 12px rgba(var(--accent-teal-rgb),0.22); } }
.ppw-nav__login-icon { display: flex; font-size: 13px; color: var(--accent-teal); }
/* Very narrow screens: icon-only login so the label never wraps to two lines. */
@media (max-width: 400px) {
  .ppw-nav__login { width: 36px; padding: 0; justify-content: center; border-radius: var(--radius-md); }
  .ppw-nav__login-label { display: none; }
}
/* Get Tickets (nav-space.js, shown only while /p is closed) reuses the login pill as an <a>. */
.ppw-nav__tickets { text-decoration: none; white-space: nowrap; background: var(--accent-gold); border-color: var(--accent-gold); color: var(--gold-btn-text); }
@media (hover: hover) { .ppw-nav__tickets:hover { border-color: var(--accent-gold-text); box-shadow: 0 0 12px rgba(var(--accent-gold-rgb), 0.45); } }

/* Member avatar */
.ppw-nav__avatar-wrap { position: relative; }
.ppw-nav__avatar {
  width: 34px; height: 34px;
  display: flex; align-items: center; justify-content: center;
  background: var(--bg-elevated);
  border: 2px solid var(--accent-teal);
  border-radius: var(--radius-full);
  font-size: 16px;
  cursor: pointer;
  transition: box-shadow 0.2s ease, transform 0.15s ease;
}
@media (hover: hover) { .ppw-nav__avatar:hover { box-shadow: 0 0 0 4px rgba(var(--accent-teal-rgb),0.16), 0 0 16px rgba(var(--accent-teal-rgb),0.35); transform: translateY(-1px); } }
.ppw-nav__avatar[aria-expanded="true"] { box-shadow: 0 0 0 3px rgba(var(--accent-teal-rgb),0.22); }

/* Avatar dropdown — myJASPER / แก้ไขโปรไฟล์ / ออกจากระบบ. Anchored under the
   avatar itself (not the navbar), so it never fights the hamburger
   dropdown's own right-edge anchoring. Sized at 90% of its original spec
   (2026-07-08 ask) — dimensions below are the already-scaled values. */
.ppw-nav__avatar-dropdown {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  width: 171px;
  display: flex; flex-direction: column; gap: 2px;
  /* Padding/radius/offset unified with .ppw-nav__dropdown below (2026-07-10
     ask — the two previously used different scales: 7px/11px/+7px here vs
     12px/12px/+8px there, which read as inconsistent when opened side by
     side even though each was internally fine). */
  padding: 12px;
  /* Glass elevation (2026-07-10) — background itself is set below in the
     shared gradient-border rule (alpha approximates --dropdown-bg #0E0E22). */
  backdrop-filter: blur(20px) saturate(1.3);
  -webkit-backdrop-filter: blur(20px) saturate(1.3);
  will-change: backdrop-filter, transform;
  border-radius: var(--radius-xl);
  box-shadow: 0 12px 28px rgba(var(--shadow-rgb), calc(0.45 * var(--shadow-mult)));
  z-index: 1001;
}
.ppw-nav__avatar-dropdown[hidden] { display: none; }
.ppw-nav__avatar-item {
  display: flex; align-items: center; gap: 9px;
  padding: 8px 9px;
  border: none;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-primary);
  font: 600 12px var(--font-thai);
  text-align: left;
  cursor: pointer;
  transition: background 0.15s ease;
}
@media (hover: hover) { .ppw-nav__avatar-item:hover { background: rgba(var(--surface-rgb), 0.12); } }
@media (hover: hover) { .ppw-nav__avatar-item--gold:hover { background: rgba(var(--accent-gold-rgb),0.22); } }
@media (hover: hover) { .ppw-nav__avatar-item--danger:hover { background: rgba(var(--like-red-rgb), 0.22); color: var(--like-red); } }
.ppw-nav__avatar-item-icon { font-size: 13px; width: 16px; text-align: center; }
.ppw-nav__avatar-divider { height: 0; margin: 4px 2px; border-top: 1px solid rgba(var(--surface-rgb), 0.08); }

/* Hamburger */
.ppw-nav__menu-btn {
  width: 36px; height: 36px;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  padding: 8px;
  border: 1px solid rgba(var(--surface-rgb), 0.12);
  border-radius: var(--radius-md);
  background: transparent;
  cursor: pointer;
  transition: border-color 0.2s ease, box-shadow 0.2s ease, background 0.2s ease;
}
@media (hover: hover) { .ppw-nav__menu-btn:hover { border-color: rgba(var(--accent-gold-rgb),0.4); box-shadow: 0 0 12px rgba(var(--accent-gold-rgb),0.22); } }
.ppw-nav__menu-btn[aria-expanded="true"] { background: rgba(var(--surface-rgb), 0.08); border-color: rgba(var(--surface-rgb), 0.28); }
.ppw-nav__menu-btn span { display: block; width: 15px; height: 3px; border-radius: var(--radius-xs); background: var(--text-primary); }

/* Dropdown — anchored under the hamburger button (right side of the navbar) */
.ppw-nav__dropdown {
  position: absolute;
  top: calc(100% + 8px);
  right: 16px;
  left: auto;
  width: 400px; /* widened 2026-08-05 (was 330px) to fit icon-beside-text cards */
  max-width: calc(100vw - 32px);
  display: flex; flex-direction: column; gap: 10px;
  padding: 12px;
  /* Glass elevation (2026-07-10) — background itself is set below in the
     shared gradient-border rule. */
  backdrop-filter: blur(20px) saturate(1.3);
  -webkit-backdrop-filter: blur(20px) saturate(1.3);
  will-change: backdrop-filter, transform;
  border-radius: var(--radius-xl);
  box-shadow: 0 12px 28px rgba(var(--shadow-rgb), calc(0.45 * var(--shadow-mult)));
}
.ppw-nav__dropdown[hidden] { display: none; }
.ppw-nav__dropdown-label { font-size: 12.65px; line-height: 1.25; color: var(--text-secondary); }

/* Full-perimeter gradient border for both dropdowns (2026-07-10 fix — the
   previous version only drew the gradient across the top edge via ::before,
   with a plain flat-gray border on the other 3 sides, which read as
   mismatched/awkward). Standard two-layer-background trick: a transparent
   border plus one background painted into the padding-box (the glass fill)
   and a second painted into the border-box (the gradient ring), so the
   gradient wraps seamlessly with no visible seam. conic-gradient (not
   linear) is required for the color to flow continuously around all 4
   corners instead of just left-to-right. */
.ppw-nav__dropdown,
.ppw-nav__avatar-dropdown {
  border: 1px solid transparent;
  background-clip: padding-box, border-box;
  background-image:
    linear-gradient(rgba(17, 17, 17, 0.97), rgba(17, 17, 17, 0.97)),
    conic-gradient(from 180deg, rgba(var(--accent-gold-rgb),0.55), rgba(var(--accent-teal-rgb),0.55), rgba(var(--accent-gold-rgb),0.55));
  background-origin: border-box;
  /* Short-viewport fix (2026-07-28): neither dropdown had a height cap, so on
     short/landscape mobile screens the CARDS grid (9 cards) could run past
     the bottom of the viewport with no way to reach the clipped cards —
     dropdown top is pinned at navbar-h + 8px below the sticky navbar (see
     `top: calc(100% + 8px)` below), so that's what the cap is measured
     against; -24px leaves breathing room above the viewport's bottom edge. */
  max-height: calc(100vh - var(--navbar-total-h) - 32px);
  max-height: calc(100dvh - var(--navbar-total-h) - 32px);
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* Notification bell (2026-08-01) — same footprint/border/hover language as
   .ppw-nav__menu-btn above (deliberately, per design ask: "ต้องออกแบบให้เป็น
   ทางเดียวกัน อย่าทำให้เด่นจนเกินไป" — same chrome, not a louder variant). */
.ppw-nav__bell-btn {
  position: relative;
  width: 36px; height: 36px;
  display: flex; align-items: center; justify-content: center;
  padding: 8px;
  border: 1px solid rgba(var(--surface-rgb), 0.12);
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--text-primary);
  font-size: 15px;
  cursor: pointer;
  transition: border-color 0.2s ease, box-shadow 0.2s ease, background 0.2s ease;
}
@media (hover: hover) { .ppw-nav__bell-btn:hover { border-color: rgba(var(--accent-gold-rgb),0.4); box-shadow: 0 0 12px rgba(var(--accent-gold-rgb),0.22); } }
.ppw-nav__bell-btn[aria-expanded="true"] { background: rgba(var(--surface-rgb), 0.08); border-color: rgba(var(--surface-rgb), 0.28); }
.ppw-nav__bell-icon { display: flex; }
/* Dot only — no count (2026-07-31 design ask: enough to say "something's
   new" without the visual weight of a numbered pill). */
.ppw-nav__bell-dot {
  position: absolute;
  top: 6px; right: 6px;
  width: 7px; height: 7px;
  border-radius: var(--radius-full);
  background: var(--accent-gold);
  box-shadow: 0 0 4px rgba(var(--accent-gold-rgb),0.7);
}
:root[data-theme="light"] .ppw-nav__bell-dot { background: var(--brand-gold); }
.ppw-nav__bell-dot[hidden] { display: none; }

/* Notification dropdown — reuses .ppw-nav__dropdown's glass panel/sizing/
   responsive rules verbatim (same width/max-width/max-height/short-viewport
   behavior as the hamburger dropdown, per design ask); only the inner list
   below is bell-specific. */
/* --accent-teal-text, not --accent-teal (2026-08-18): the plain teal's light
   value is tuned for stripes/borders and measured 2.77:1 as 11px/600 text on
   this dropdown's surface. See the token's own comment in p/tokens.css. */
.ppw-notify-markall {
  border: none;
  background: transparent;
  color: var(--accent-teal-text);
  font: 600 12px var(--font-thai);
  cursor: pointer;
  padding: 0;
}
@media (hover: hover) { .ppw-notify-markall:hover { text-decoration: underline; } }
.ppw-notify-list { display: flex; flex-direction: column; gap: 6px; }
.ppw-notify-empty { padding: 14px 2px; font-size: 13px; color: var(--text-secondary); text-align: center; }

/* Notification card (2026-08-01 restyle — "ดูไม่สวย/basic เกินไป" ask):
   platform-notification-style card, not a bare text row — its own filled
   surface + left accent stripe (priority color) + hover lift, so the list
   reads as a stack of distinct cards rather than plain list items. */
.ppw-notify-card {
  position: relative;
  border-radius: var(--radius-lg);
  background: rgba(var(--surface-rgb), 0.035);
  border: 1px solid rgba(var(--surface-rgb), 0.07);
  overflow: hidden;
  transition: background 0.15s ease, border-color 0.15s ease;
}
.ppw-notify-card::before {
  content: '';
  position: absolute;
  left: 0; top: 9px; bottom: 9px;
  width: 3px;
  border-radius: 0 var(--radius-xs) var(--radius-xs) 0;
  background: var(--accent-teal);
  opacity: 0.35;
}
:root[data-theme="light"] .ppw-notify-card::before { background: var(--brand-teal); }
/* Pinned cards get an orange accent stripe + border so they read as visually
   distinct from general announcements at a glance (2026-08-19 ask). Urgent
   still wins over pinned when both apply — same red as everywhere else. */
.ppw-notify-card.is-pinned::before { background: var(--accent-gold); }
.ppw-notify-card.is-urgent::before { background: var(--status-like); }
.ppw-notify-card.is-unread::before { opacity: 0.9; }
.ppw-notify-card.is-pinned { border-color: rgba(var(--accent-gold-rgb), 0.3); }
@media (hover: hover) { .ppw-notify-card:hover { background: rgba(var(--surface-rgb), 0.06); border-color: rgba(var(--surface-rgb), 0.14); } }
@media (hover: hover) { .ppw-notify-card.is-pinned:hover { border-color: rgba(var(--accent-gold-rgb), 0.45); } }
/* Expanded-state border (2026-08-11 ask — "พอไม่มีขอบแล้วงง", the collapsed
   hover-only border change gave no lasting cue for which card was currently
   open) — matches each card's OWN accent-stripe color (teal normally, red
   when urgent), not a universal gold, per revised 2026-08-11 ask ("เอาขอบสี
   ตรงกับการ์ดนั้นๆ"). */
.ppw-notify-card.is-expanded { border-color: rgba(var(--accent-teal-rgb), 0.6); background: rgba(var(--surface-rgb), 0.06); }
.ppw-notify-card.is-urgent.is-expanded { border-color: rgba(var(--like-red-rgb), 0.6); }

.ppw-notify-card__hit {
  width: 100%;
  display: flex; flex-direction: column; gap: 3px;
  padding: 10px 12px 10px 16px;
  border: none;
  background: transparent;
  /* <button> doesn't inherit font-family from its ancestors per the browser
     UA stylesheet (2026-08-01 fix — __time/__preview below were silently
     falling back to Arial instead of --font-thai because of this). */
  font-family: var(--font-thai);
  text-align: left;
  cursor: pointer;
}
.ppw-notify-card__top { display: flex; align-items: baseline; gap: 8px; }
/* Pinned indicator (2026-08-11 ask — "ให้มีไอคอนหมุดด้านหน้าประกาศนั้นๆ") —
   FontAwesome thumbtack glyph (ICON_PIN_SVG, p/icons.js), gold to match the
   unread dot's accent rather than either identity color (teal/red) used by
   the card border/stripe, since "pinned" is a separate concept from those. */
.ppw-notify-card__pin { flex: none; align-self: center; font-size: 11px; color: var(--accent-gold-text); display: flex; }
:root[data-theme="light"] .ppw-notify-card__pin { color: var(--brand-gold); }
/* Pin doubles as its own card's unread indicator (2026-08-11 ask — "ไม่ต้อง
   มีจุด แต่ใช้สีหมุดแทน") — gold above is the unread state; once read it
   dims to the exact same treatment as a read title (`.is-read-dim`), not a
   separate color scheme. No JS toggling needed — same class the title
   already reacts to. */
.ppw-notify-card.is-read-dim .ppw-notify-card__pin { color: var(--text-primary); opacity: 0.72; }
/* Dot only ever renders on general (unpinned) cards — pinned cards use the
   pin icon as their unread indicator instead (see above). Its color matches
   the card's own accent stripe/border (2026-08-19 ask): teal normally, red
   when urgent — never gold, since gold now means "pinned", not "unread". */
.ppw-notify-card__unread-dot { width: 6px; height: 6px; border-radius: var(--radius-full); background: var(--accent-teal); flex: none; align-self: center; }
:root[data-theme="light"] .ppw-notify-card__unread-dot { background: var(--brand-teal); }
.ppw-notify-card.is-urgent .ppw-notify-card__unread-dot { background: var(--status-like); }
.ppw-notify-card__title { flex: 1; min-width: 0; font: 700 14px var(--font-thai); line-height: 1.35; color: var(--text-primary); }
/* Dimming is driven by `.is-read-dim`, NOT simply "not unread" (2026-08-11
   ask) — the card just clicked open stays full-brightness until attention
   moves elsewhere (another card opens, or the dropdown/page is reloaded);
   see createNotifyBell()'s justReadId tracking in announcements-client.js. */
.ppw-notify-card.is-read-dim .ppw-notify-card__title { font-weight: 600; opacity: 0.72; }
.ppw-notify-card__time { flex: none; font-size: 11px; color: var(--text-secondary); opacity: 0.65; }
/* One-line clamp + ellipsis — the "โชว์แค่บรรทัดเดียวแล้วมี wrap ..." ask.
   `-webkit-line-clamp` (not plain overflow/nowrap) so it degrades to a
   normal single-line ellipsis in engines without line-clamp support too. */
.ppw-notify-card__preview {
  font-size: 12.5px;
  line-height: 1.4;
  color: var(--text-secondary);
  display: -webkit-box;
  -webkit-line-clamp: 1;
  -webkit-box-orient: vertical;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ppw-notify-card__hit.is-expanded .ppw-notify-card__preview { display: none; }

.ppw-notify-card__body { padding: 0 12px 12px 16px; font-size: 13px; }
.ppw-notify-card__body[hidden] { display: none; }
.ppw-notify-card__footer {
  margin: 10px -12px -12px -16px;
  padding: 8px 12px 8px 16px;
  background: rgba(var(--surface-rgb), 0.025);
  border-top: 1px solid rgba(var(--surface-rgb), 0.05);
}
.ppw-notify-item__links { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.ppw-notify-card__footer .ppw-notify-item__links { margin-top: 0; }
.ppw-notify-link-btn {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 10px;
  border: 1px solid rgba(var(--surface-rgb), 0.18);
  border-radius: var(--radius-lg);
  color: var(--text-primary);
  font: 600 12px var(--font-thai);
  text-decoration: none;
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
@media (hover: hover) { .ppw-notify-link-btn:hover { border-color: rgba(var(--accent-teal-rgb),0.5); box-shadow: 0 0 10px rgba(var(--accent-teal-rgb),0.22); } }
.ppw-notify-link-btn__arrow { display: inline-flex; font-size: 11px; flex: none; }
.ppw-notify-viewall {
  text-align: center;
  padding-top: 6px;
  border-top: 1px solid rgba(var(--surface-rgb), 0.08);
  color: var(--accent-gold-text);
  font: 600 12.5px var(--font-thai);
  text-decoration: none;
}
@media (hover: hover) { .ppw-notify-viewall:hover { text-decoration: underline; } }

/* New-announcement toast (2026-08-11) — one-time nudge fired from
   announcements-client.js's createNotifyBell() the first time a visitor's
   browser sees a newer announcement than it's ever shown a toast for (its
   own localStorage flag, independent of the dropdown's read/unread state).
   Deliberately quiet: same glass/gradient-border language as the dropdowns
   above, fixed corner placement instead of a banner, auto-dismissing, and
   closable — never blocks page content or demands interaction. */
.ppw-notify-toast {
  position: fixed;
  right: calc(16px + var(--safe-right));
  bottom: calc(16px + var(--safe-bottom) + var(--inapp-bottom));
  z-index: 1002; /* above both dropdowns (1001) and the navbar (1000) */
  width: 300px;
  max-width: calc(100vw - 32px);
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 12px 14px;
  border: 1px solid transparent;
  border-radius: var(--radius-xl);
  background-clip: padding-box, border-box;
  background-image:
    linear-gradient(rgba(17, 17, 17, 0.97), rgba(17, 17, 17, 0.97)),
    conic-gradient(from 180deg, rgba(var(--accent-gold-rgb),0.55), rgba(var(--accent-teal-rgb),0.55), rgba(var(--accent-gold-rgb),0.55));
  background-origin: border-box;
  backdrop-filter: blur(20px) saturate(1.3);
  -webkit-backdrop-filter: blur(20px) saturate(1.3);
  box-shadow: 0 12px 28px rgba(var(--shadow-rgb), calc(0.45 * var(--shadow-mult)));
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 0.2s ease, transform 0.2s ease;
  pointer-events: none;
}
.ppw-notify-toast[hidden] { display: none; }
.ppw-notify-toast.is-visible { opacity: 1; transform: translateY(0); pointer-events: auto; }
.ppw-notify-toast__hit {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: flex-start;
  gap: 10px;
  border: none;
  background: transparent;
  padding: 0;
  cursor: pointer;
  text-align: left;
  font-family: var(--font-thai);
}
.ppw-notify-toast__icon {
  flex: none;
  width: 30px; height: 30px;
  display: flex; align-items: center; justify-content: center;
  border-radius: var(--radius-full);
  background: rgba(var(--accent-gold-rgb), 0.14);
  color: var(--accent-gold-text);
  font-size: 15px;
}
.ppw-notify-toast__body { flex: 1; min-width: 0; }
.ppw-notify-toast__top { display: flex; align-items: center; gap: 6px; }
.ppw-notify-toast__title { font: 600 11.5px var(--font-thai); color: var(--text-secondary); }
.ppw-notify-toast__dot { flex: none; width: 5px; height: 5px; border-radius: var(--radius-full); background: var(--accent-teal); }
:root[data-theme="light"] .ppw-notify-toast__dot { background: var(--brand-teal); }
/* Full announcement title, no clamp/ellipsis (2026-08-11 ask) — this is the
   actual content of the nudge, so it wraps to as many lines as it needs
   instead of being cut off. */
.ppw-notify-toast__preview {
  margin-top: 3px;
  font: 700 14px var(--font-thai);
  color: var(--text-primary);
  line-height: 1.35;
}
.ppw-notify-toast__close {
  flex: none;
  border: none;
  background: transparent;
  padding: 2px;
  color: var(--text-secondary);
  font-size: 14px;
  line-height: 0;
  cursor: pointer;
}
@media (hover: hover) { .ppw-notify-toast__close:hover { color: var(--text-primary); } }
.ppw-notify-toast__bar {
  position: absolute;
  left: 12px; right: 12px; bottom: 0;
  height: 2px;
  border-radius: var(--radius-xs);
  overflow: hidden;
  background: rgba(var(--surface-rgb), 0.1);
}
.ppw-notify-toast__bar-fill {
  width: 100%; height: 100%;
  background: var(--accent-gold);
  transform-origin: left;
  transform: scaleX(1);
}
.ppw-notify-toast.is-visible .ppw-notify-toast__bar-fill { transition: transform 5.2s linear; transform: scaleX(0); }
@media (max-width: 480px) {
  /* bottom re-stated with both insets (2026-08-27) — this override dropped
     var(--safe-bottom) when it was written, so the toast ignored the home
     indicator on every phone, the same trap find.css's own breakpoint fell
     into. --inapp-bottom keeps it clear of an in-app browser's controls and
     is 0 in a real browser. */
  .ppw-notify-toast { right: 12px; left: 12px; bottom: calc(12px + var(--safe-bottom) + var(--inapp-bottom)); width: auto; max-width: none; }
}

/* Limited-markdown renderer (p/markdown.js) — duplicated from sponsor.css
   (2026-07-24 original) rather than imported, since nav.css loads on pages
   (main site, most of /p) that never load sponsor.css. */
.sp-markdown { font-size: 13px; color: var(--text-primary); line-height: 1.6; }
.sp-markdown__p { margin: 4px 0; }
.sp-markdown__p:first-child { margin-top: 0; }
.sp-markdown__list { margin: 4px 0; padding-left: 16px; }
.sp-markdown a { color: var(--accent-teal); }
/* Blank line in source body_md = paragraph break (2026-08-09 fix) — without
   this, a blank line between sections collapsed to the same 4px gap as any
   two consecutive lines, so admin-authored paragraph breaks silently vanished
   on the public-facing render even though the admin textarea still showed them. */
.sp-markdown__p--gap, .sp-markdown__list--gap { margin-top: 14px; }

/* Back to 2 cards per row (2026-08-05 follow-up ask — "ยังเอา 2 คอลัมน์
   เหมือนเดิม") — cards are icon-beside-text rows now (was icon-on-top), but
   the grid itself stays 2-per-row; the wider dropdown (400px, was 330px)
   gives each column enough room for that row layout. */
.ppw-nav__grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }

.ppw-nav__card {
  position: relative;
  box-sizing: border-box;
  min-width: 0; /* 2026-08-05 fix — a plain 1fr grid track's implicit min
    width is its content's min-content size, not 0; a card with an
    unbreakable long word (e.g. "Marketplace") was silently forcing its
    whole column wider than the other, so unrelated cards in the narrower
    column (e.g. "Find My JASPER") wrapped mid-word even though the dropdown
    itself had plenty of room. This lets both grid columns split evenly. */
  display: flex; flex-direction: row; align-items: center; gap: 12px;
  padding: 15px 12px; /* taller (2026-08-05 ask) — was 10px 12px */
  /* min-height (2026-08-05 fix, "ปรับความสูงของเมนู...เท่ากับตัว Fan Forge")
     — CSS grid sizes each row by its own tallest cell, so a card whose sub
     text wraps to 2 lines (e.g. Fan Forge's "รวม Fan Project และ Giveaway")
     made only ITS row taller, leaving every other row visibly shorter.
     74px = padding(15*2) + a 2-line sub column, so every regular card now
     reserves that height whether its own sub wraps or not — uniform row
     height sitewide. Not applied to .ppw-nav__card--inline (below), which
     is deliberately compact by design. */
  min-height: 74px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: rgba(var(--surface-rgb), 0.04);
  text-decoration: none;
  cursor: pointer;
  transition: border-color 0.2s ease, background 0.2s ease, box-shadow 0.2s ease;
  /* Stagger-in on open (2026-07-21) — replays each time the dropdown goes
     display:none → flex, since CSS animations restart on that transition.
     --i (set per-card in JS) staggers the delay; capped so the whole grid
     settles within ~300ms. */
  animation: ppw-nav-card-in 0.22s ease backwards;
  animation-delay: calc(var(--i, 0) * 40ms);
}
@keyframes ppw-nav-card-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}
@media (hover: hover) { .ppw-nav__card:not(.is-disabled):hover { border-color: rgba(var(--accent-gold-rgb),0.7);
  background: rgba(var(--accent-gold-rgb),0.1);
  box-shadow: 0 0 18px rgba(var(--accent-gold-rgb),0.25);
  transform: translateY(-1px); } }
.ppw-nav__card.is-active {
  background: rgba(var(--surface-rgb), 0.06);
  border: 1px solid var(--accent-gold);
  border-left-width: 3px;
}
.ppw-nav__card.is-disabled { opacity: 0.4; pointer-events: none; }

.ppw-nav__card-icon {
  width: 34px; height: 34px;
  flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  border-radius: var(--radius-md);
  background: rgba(var(--accent-teal-rgb),0.18);
  font-size: 16px;
}
/* Flat icons (2026-10-14): currentColor SVGs, so the colour comes from here. */
.ppw-nav__card-icon { color: var(--accent-teal-text); }
.ppw-nav__card.is-active .ppw-nav__card-icon,
.ppw-nav__card--gold .ppw-nav__card-icon { background: rgba(var(--accent-gold-rgb),0.18); color: var(--accent-gold-text); }
/* Per-card colour by feature group (2026-10-14, owner ask) — one hue family
   per group, a different shade per card: red = info/tickets, gold = shop/
   perks, steel blue = friends/travel, rose = fan activities. Tokens live in
   p/tokens.css + theme.json (--nav-ic-<card key>). */
.ppw-nav__card[data-card-key="guide"] { --nav-ic: var(--nav-ic-guide); }
.ppw-nav__card[data-card-key="my"] { --nav-ic: var(--nav-ic-my); }
.ppw-nav__card[data-card-key="home"] { --nav-ic: var(--nav-ic-home); }
.ppw-nav__card[data-card-key="ttm"] { --nav-ic: var(--nav-ic-ttm); }
.ppw-nav__card[data-card-key="md"] { --nav-ic: var(--nav-ic-md); }
.ppw-nav__card[data-card-key="fanbene"] { --nav-ic: var(--nav-ic-fanbene); }
.ppw-nav__card[data-card-key="find"] { --nav-ic: var(--nav-ic-find); }
.ppw-nav__card[data-card-key="gohome"] { --nav-ic: var(--nav-ic-gohome); }
.ppw-nav__card[data-card-key="directory-map"] { --nav-ic: var(--nav-ic-directory-map); }
.ppw-nav__card[data-card-key="homesafe"] { --nav-ic: var(--nav-ic-homesafe); }
.ppw-nav__card[data-card-key="vote"] { --nav-ic: var(--nav-ic-vote); }
.ppw-nav__card[data-card-key="giveaway"] { --nav-ic: var(--nav-ic-giveaway); }
.ppw-nav__card[data-card-key="impact-3d"] { --nav-ic: var(--nav-ic-directory-map); }
.ppw-nav__card[data-card-key="sponsor"] { --nav-ic: var(--nav-ic-giveaway); }
.ppw-nav__card[data-card-key] .ppw-nav__card-icon {
  color: var(--nav-ic, var(--accent-teal-text));
  background: color-mix(in srgb, var(--nav-ic, var(--accent-teal-text)) 16%, transparent);
}
.ppw-nav__card-icon svg, .ppw-nav__avatar-item-icon svg { display: block; }

/* Title+sub column beside the icon (2026-08-05 layout change). */
.ppw-nav__card-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }

/* line-height: 1.25 (2026-07-09) — these otherwise fall back to Noto Sans
   Thai's own default line-height ("normal"), which runs unusually tall
   (~1.6x) and made even single-line card text look overly spaced out. */
.ppw-nav__card-title { display: flex; align-items: center; gap: 6px; font-size: 13px; line-height: 1.25; font-weight: 600; color: var(--text-primary); }
.ppw-nav__card-sub   { font-size: 10.5px; line-height: 1.25; color: var(--text-secondary); }
/* Top-right corner of the card (2026-07-09) — absolute, so it no longer
   pushes the title/sub text down as an extra flex row. */
.ppw-nav__card-badge {
  position: absolute;
  top: 8px;
  right: 8px;
  padding: 2px 6px;
  border-radius: var(--radius-sm);
  background: rgba(var(--surface-rgb), 0.1);
  font-size: 9px; line-height: 1.25; font-weight: 400;
  color: var(--text-secondary);
}

/* Compact icon+label-on-one-line variant (2026-07-10) — used by "back to
   main page" / "buy live ticket", which need no sub-text and read better as
   a single-line button than a full vertical card. */
/* Narrower/shorter than the regular cards (2026-08-05 ask) — "กลับหน้าหลัก"/
   "ซื้อบัตรไลฟ์" are single-line, self-explanatory actions, not full feature
   cards, so they shouldn't grow with the taller .ppw-nav__card padding above. */
/* min-height: auto (2026-08-05 fix) — the regular-card min-height added
   above is a plain `.ppw-nav__card` rule, so this inline variant inherited
   it too, undoing its own "stay compact" padding straight after this rule
   was added. */
.ppw-nav__card--inline { flex-direction: row; align-items: center; gap: 10px; padding: 9px 10px; min-height: auto; }
.ppw-nav__card--inline .ppw-nav__card-icon { width: 28px; height: 28px; font-size: 13px; }

/* Spans both grid columns (2026-07-11 ask, main-site lite nav's "buy live
   ticket" card) — generic modifier so any full-row card can opt in without
   its own separate grid. */
.ppw-nav__card--full { grid-column: 1 / -1; }

/* Stack group (2026-07-25 ask): cards sharing a `stack` key (e.g. "back to
   home" + "buy live ticket") render into one shared grid cell, split
   top/bottom, so together they match the row height of the card beside
   them (Fan Forge) instead of each claiming a full grid cell. The wrap
   div is itself the grid item, so it inherits the row's stretch height;
   `flex: 1` on each stacked card then splits that height evenly. */
.ppw-nav__card-stack { display: flex; flex-direction: column; gap: 8px; }
.ppw-nav__card--stacked { flex: 1; padding: 8px 10px; }
.ppw-nav__card--stacked .ppw-nav__card-icon { width: 22px; height: 22px; font-size: 12px; }
.ppw-nav__card--stacked .ppw-nav__card-title { font-size: 12px; }

/* Short-viewport fix, part 2 (2026-07-28): the max-height + overflow-y auto
   above (shared .ppw-nav__dropdown rule) already guarantees every card is
   reachable, but on short/landscape phones that meant near-constant
   scrolling just to see the whole grid. Shrinking the grid's own metrics
   below ~600px tall (comfortably above common landscape-phone heights like
   360–430px) fits more rows on screen before scroll ever kicks in, so most
   short-screen visitors never need to scroll at all. */
@media (max-height: 600px) {
  .ppw-nav__dropdown { gap: 6px; padding: 10px; }
  .ppw-nav__grid { gap: 6px; }
  .ppw-nav__card { padding: 8px 10px; gap: 5px; }
  .ppw-nav__card-icon { width: 22px; height: 22px; font-size: 12px; }
  .ppw-nav__card-title { font-size: 12px; }
  .ppw-nav__card-sub { font-size: 9px; }
}

/* ─── Light mode (2026-08-06) ───────────────────────────────────────────
   .ppw-nav's glass background and the two dropdowns' glass-fill layer are
   hardcoded rgba (see comments above — CSS vars can't carry their own alpha
   without color-mix()), so they don't move with --navbar-bg/--dropdown-bg
   automatically and need their own explicit theme branches. Dark is always
   the default regardless of OS color-scheme — light only ever applies when
   theme-toggle.js sets data-theme explicitly. */
:root[data-theme="light"] .ppw-nav { background: rgba(211, 203, 227, 0.72); }
:root[data-theme="light"] .ppw-nav__dropdown,
:root[data-theme="light"] .ppw-nav__avatar-dropdown {
  background-image:
    linear-gradient(rgba(219, 212, 233, 0.94), rgba(219, 212, 233, 0.94)),
    conic-gradient(from 180deg, rgba(var(--accent-gold-rgb),0.55), rgba(var(--accent-teal-rgb),0.55), rgba(var(--accent-gold-rgb),0.55));
}
:root[data-theme="dark"] .ppw-nav { background: rgba(5, 5, 5, 0.72); }
:root[data-theme="dark"] .ppw-nav__dropdown,
:root[data-theme="dark"] .ppw-nav__avatar-dropdown {
  background-image:
    linear-gradient(rgba(17, 17, 17, 0.97), rgba(17, 17, 17, 0.97)),
    conic-gradient(from 180deg, rgba(var(--accent-gold-rgb),0.55), rgba(var(--accent-teal-rgb),0.55), rgba(var(--accent-gold-rgb),0.55));
}

/* Theme-following nav glass (2026-10-12) — only for a theme whose motif is
   red-fog (events/jasper-temptation-2026). The rgba literals above stay the
   default so PPW is unchanged; this branch derives the glass from
   --navbar-bg / --dropdown-bg with color-mix(). Extra [data-project] keeps it
   above the :root[data-theme] branches regardless of source order. */
:root[data-motif-pattern="red-fog"][data-project] .ppw-nav {
  background: color-mix(in srgb, var(--navbar-bg) 72%, transparent);
  border-bottom-color: var(--border);
}
:root[data-motif-pattern="red-fog"][data-project] .ppw-nav__dropdown,
:root[data-motif-pattern="red-fog"][data-project] .ppw-nav__avatar-dropdown {
  background-image:
    linear-gradient(color-mix(in srgb, var(--dropdown-bg) 96%, transparent), color-mix(in srgb, var(--dropdown-bg) 96%, transparent)),
    conic-gradient(from 180deg, rgba(var(--accent-gold-rgb),0.55), rgba(var(--accent-teal-rgb),0.55), rgba(var(--accent-gold-rgb),0.55));
}
:root[data-motif-pattern="red-fog"][data-project] .ppw-nav__brand-tag { color: var(--accent-gold-text); }
