/* auth.css — /p auth page (login / signup / X-OAuth setup / credentials modal).
   Design source: Figma 186:5, 187:2, 187:41, 338:14. Uses tokens.css. */

* { box-sizing: border-box; }
/* Background lives on body (paints instantly at first CSS load) instead of
   .auth-page, whose height depends on --navbar-h from the nav bar — that bar
   is mounted asynchronously by nav.js (awaits getProfile()), so before it
   lands .auth-page is shorter than the viewport and left a plain-white gap
   at the bottom until it mounted (2026-07-08 fix). */
body {
  margin: 0;
  min-height: 100vh;
  min-height: 100dvh;
  background: var(--space-gradient), var(--bg-primary);
  background-repeat: no-repeat;
  background-attachment: fixed;
}

.auth-page {
  /* Explicit stacking context (2026-07-10, starfield ask) — required to
     paint above the fixed z-index:0 starfield layers (tokens.css); plain
     static-position content paints BEHIND a positioned z-index:0 sibling
     per CSS stacking rules. */
  position: relative;
  z-index: 1;
  /* The global nav (position:sticky, --navbar-h tall) sits above this as a
     sibling in normal flow, so 100vh here would push total page height past
     the viewport and force an unwanted scrollbar even when content is short. */
  min-height: calc(100vh - var(--navbar-total-h));
  min-height: calc(100dvh - var(--navbar-total-h));
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 20px;
  padding: 24px 16px 20px;
  color: var(--text-primary);
  font-family: var(--font-thai);
}

.auth-card {
  width: 100%;
  max-width: 390px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 22px 20px;
  background: var(--bg-secondary);
  border: 1px solid var(--border);
  border-radius: var(--radius-3xl);
  box-shadow: 0 20px 50px rgba(var(--shadow-rgb), calc(0.4 * var(--shadow-mult)));
  animation: auth-card-in 0.35s ease both;
  /* margin: auto 0 (2026-07-10) — centers the card vertically within the
     space above the footer (auto margins consume free space before
     justify-content applies), instead of sitting flush at the top. The
     footer itself keeps the normal gap:20px above it, no longer margin-top:
     auto — that pinned it to the very bottom, but decoupled it from the
     card in a way that broke the centered-as-a-group look this page wants. */
  margin: auto 0;
}
/* translateZ(0) appended to both keyframe steps (2026-07-27, not a separate
   `transform` rule on `.auth-modal__card` — this `animation`'s own keyframe
   values govern `transform` for the animation's duration and, with `both`
   fill, forever after, so a static rule would be silently overridden) — same
   iOS backdrop-filter compositing fix as .emoji-icon (p/tokens.css): this
   card is a DOM child of `.auth-modal`, which has `backdrop-filter`, and
   carries several raw inline-SVG icons (ICON_X_SVG close button, etc.) not
   covered by that rule. */
@keyframes auth-card-in {
  from { opacity: 0; transform: translateY(10px) translateZ(0); }
  to   { opacity: 1; transform: translateY(0) translateZ(0); }
}

.auth-head { display: flex; flex-direction: column; gap: 2px; }
.auth-head--center { align-items: center; text-align: center; }
.auth-title { font-size: 18px; font-weight: 700; }
.auth-title--lg { font-size: 22px; }
.auth-sub   { font-size: 11px; color: var(--text-secondary); }
.auth-sub--md { font-size: 13px; }

/* Numbered-timeline step list — openInfoModal() (modals.js), one-time
   feature intros. A connecting line down the badge column + a title/desc
   split per row (instead of one flat icon+sentence line) gives the list
   some visual rhythm and a scannable hierarchy. */
.auth-info-list { display: flex; flex-direction: column; margin: 8px 0 22px; }
.auth-info-list__item { display: flex; align-items: flex-start; gap: 12px; padding: 8px 0; position: relative; }
.auth-info-list__item:not(:last-child)::before {
  content: ''; position: absolute; left: 16px; top: 40px; bottom: -4px; width: 1px;
  background: linear-gradient(rgba(var(--accent-gold-rgb),0.35), rgba(var(--accent-teal-rgb),0.15));
}
.auth-info-list__badge {
  flex: none; width: 33px; height: 33px; border-radius: var(--radius-full);
  display: flex; align-items: center; justify-content: center;
  background: rgba(var(--accent-gold-rgb),0.1);
  border: 1px solid rgba(var(--accent-gold-rgb),0.3);
  font-size: 15px; position: relative; z-index: 1;
}
.auth-info-list__item.is-highlight .auth-info-list__badge {
  background: rgba(var(--accent-gold-rgb),0.22); border-color: var(--accent-gold);
  box-shadow: 0 0 0 3px rgba(var(--accent-gold-rgb),0.12);
}
.auth-info-list__body { flex: 1; padding-top: 3px; }
.auth-info-list__title { font-size: 13px; font-weight: 700; margin-bottom: 2px; }
.auth-info-list__item.is-highlight .auth-info-list__title { color: var(--accent-gold-text); }
.auth-info-list__desc { font-size: 12px; color: var(--text-secondary); line-height: 1.5; }

/* Fields */
.auth-field { display: flex; flex-direction: column; gap: 6px; }
.auth-label { font-size: 12px; font-weight: 600; }
.auth-input {
  height: 44px;
  padding: 0 12px;
  background: var(--bg-primary);
  border: 1px solid rgba(var(--surface-rgb),0.1);
  border-radius: var(--radius-lg);
  color: var(--text-primary);
  font: 400 13px var(--font-thai);
  outline: none;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.auth-input::placeholder { color: var(--text-secondary); }
.auth-input:focus { border-color: var(--focus-ring); box-shadow: 0 0 0 3px rgba(var(--accent-gold-rgb),0.12); }
.auth-hint { font-size: 10px; color: var(--text-secondary); }
.auth-input--locked {
  display: flex; align-items: center; justify-content: space-between;
  border-color: rgba(var(--surface-rgb),0.05);
  font-weight: 600;
}
.auth-label__req { color: var(--like-red); }
.auth-input.is-invalid { border-color: var(--like-red); }
.auth-input.is-invalid:focus { box-shadow: 0 0 0 3px rgba(var(--like-red-rgb), 0.15); }
.auth-field-error { font-size: 10px; color: var(--like-red); font-weight: 600; }

/* Country picker (User Country Selection System, 2026-07-09) */
.cty-picker { position: relative; }
.cty-picker.is-invalid .auth-input { border-color: var(--like-red); }
.cty-picker__input { width: 100%; cursor: text; }
.cty-picker.has-flag .cty-picker__input { padding-left: 36px; }
.cty-picker__flag {
  position: absolute; left: 12px; top: 50%; transform: translateY(-50%);
  line-height: 0; pointer-events: none;
}
.cty-list {
  position: absolute; top: calc(100% + 4px); left: 0; right: 0;
  max-height: 220px; overflow-y: auto; z-index: 20;
  background: var(--bg-elevated); border: 1px solid rgba(var(--surface-rgb),0.1);
  border-radius: var(--radius-lg); padding: 4px;
  box-shadow: 0 12px 28px rgba(var(--shadow-rgb), calc(0.45 * var(--shadow-mult)));
}
.cty-option {
  display: flex; align-items: center; justify-content: space-between; gap: 8px; width: 100%; text-align: left; padding: 8px 10px;
  background: none; border: none; border-radius: var(--radius-sm);
  color: var(--text-primary); font: 400 13px var(--font-thai);
  cursor: pointer;
}
.cty-option.is-selected { background: rgba(var(--surface-rgb),0.06); }
@media (hover: hover) { .cty-option:hover { background: rgba(var(--surface-rgb),0.06); } }
.cty-list__empty { padding: 8px 10px; color: var(--text-secondary); font-size: 12px; }

.auth-field-row { display: flex; gap: 10px; }
.auth-field-row .auth-btn { width: auto; flex: 1; }

/* Step indicator (2-step signup) */
.auth-steps { display: flex; gap: 6px; }
.auth-steps__seg { flex: 1; height: 4px; border-radius: var(--radius-xs); background: rgba(var(--surface-rgb),0.1); }
.auth-steps__seg.is-active, .auth-steps__seg.is-done { background: var(--accent-gold); }
:root[data-theme="light"] .auth-steps__seg.is-active,
:root[data-theme="light"] .auth-steps__seg.is-done { background: var(--brand-gold); }
.auth-steps__label { font-size: 11px; color: var(--text-secondary); font-weight: 600; }

/* Buttons */
.auth-btn {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  border: none; border-radius: var(--radius-xl);
  font: 600 14px var(--font-thai);
  cursor: pointer;
  width: 100%;
  transition: transform 0.12s ease, filter 0.12s ease, background 0.12s ease, box-shadow 0.12s ease;
}
.auth-btn:active { transform: scale(0.98); }
.auth-btn:disabled { opacity: 0.6; cursor: default; transform: none; }
.auth-btn--primary { background: var(--accent-gold); color: var(--gold-btn-text); padding: 15px; }
@media (hover: hover) { .auth-btn--primary:hover:not(:disabled) { filter: brightness(1.08); box-shadow: 0 4px 16px rgba(var(--accent-gold-rgb),0.25); } }
/* Keeps dark mode's vivid gold + dark text in light mode too (2026-08-08 ask)
   — solid fill + in-box text has self-contained contrast, independent of
   the page background, so light mode's muted gold isn't needed here. */
:root[data-theme="light"] .auth-btn--primary { background: var(--brand-gold); color: var(--brand-gold-ink); }
/* X's own brand buttons are black/white, not gold — using gold here made it
   read as the same action as the (also gold) primary Sign-up/Next button. */
.auth-btn--x {
  background: #000; color: var(--white); padding: 18px; font-size: 16px;
  box-shadow: 0 6px 20px rgba(var(--shadow-rgb), calc(0.35 * var(--shadow-mult)));
}
@media (hover: hover) { .auth-btn--x:hover:not(:disabled) { filter: brightness(1.3); box-shadow: 0 8px 24px rgba(var(--shadow-rgb), calc(0.45 * var(--shadow-mult))); } }
.auth-btn--ghost {
  background: rgba(var(--surface-rgb),0.06);
  border: 1px solid rgba(var(--surface-rgb),0.15);
  color: var(--text-primary);
  padding: 13px; font-size: 13px;
}
@media (hover: hover) { .auth-btn--ghost:hover:not(:disabled) { background: rgba(var(--surface-rgb),0.1); border-color: rgba(var(--surface-rgb),0.25); } }
.auth-btn--outline {
  background: rgba(var(--surface-rgb),0.12);
  border: 1px solid rgba(var(--surface-rgb),0.5);
  color: var(--text-primary);
  padding: 13px; font-size: 13px;
}
@media (hover: hover) { .auth-btn--outline:hover:not(:disabled) { background: rgba(var(--surface-rgb),0.18); } }
.auth-btn__x { font-family: 'Inter', sans-serif; font-weight: 700; font-size: 20px; }

.auth-divider {
  display: flex; align-items: center; gap: 10px;
  font-size: 11px;
  color: var(--text-secondary);
}
.auth-divider::before, .auth-divider::after { content: ''; flex: 1; height: 0; border-top: 1px solid var(--border); }
.auth-note-recommend { font-size: 11px; color: var(--text-secondary); text-align: center; }

.auth-amber {
  padding: 10px 12px;
  border-radius: var(--radius-lg);
  background: rgba(254, 172, 104, 0.1);
  color: #FEAC68;
  font-size: 10px; font-weight: 600;
}
/* 2026-08-08 fix — badge-pattern bug (literal text ≈ its own translucent
   tint), measured 1.61:1 in light mode. Same hue, darkened to ~3.3:1. */
:root[data-theme="light"] .auth-amber { color: #BA5501; }

.auth-foot { font-size: 11px; color: var(--text-secondary); text-align: center; }
.auth-link { color: var(--accent-gold-text); cursor: pointer; text-decoration: none; }

/* Emoji + color pickers */
.auth-identity-picker { display: flex; flex-direction: column; gap: 20px; }
.auth-picker { display: flex; flex-direction: column; gap: 8px; }
/* Grid (not flex-wrap) so every row's columns line up evenly across the full
   width instead of ragged-left — each circle grows/shrinks with its column
   via width:100%+aspect-ratio (2026-07-09 realignment). */
.auth-grid { display: grid; gap: 8px; }
.auth-grid--emoji { grid-template-columns: repeat(auto-fill, minmax(42px, 1fr)); }
.auth-grid--color { grid-template-columns: repeat(auto-fill, minmax(32px, 1fr)); }

.auth-emoji {
  width: 100%; aspect-ratio: 1;
  display: flex; align-items: center; justify-content: center;
  background: var(--bg-elevated);
  border: 2px solid transparent;
  border-radius: var(--radius-full);
  font-size: 18px;
  cursor: pointer;
  transition: border-color 0.12s ease, transform 0.12s ease;
}
@media (hover: hover) { .auth-emoji:hover { transform: translateY(-1px); } }
.auth-emoji.is-selected { border-color: var(--accent-teal); background: rgba(var(--accent-teal-rgb),0.14); }

.auth-swatch {
  width: 100%; aspect-ratio: 1;
  border: 2px solid transparent;
  border-radius: var(--radius-full);
  cursor: pointer;
  transition: border-color 0.12s ease, transform 0.12s ease;
}
@media (hover: hover) { .auth-swatch:hover { transform: translateY(-1px); } }
.auth-swatch.is-selected { border-color: var(--white); }

.auth-toggle {
  align-self: flex-start;
  padding: 6px 10px;
  border: 1px solid rgba(var(--surface-rgb),0.12);
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--text-secondary);
  font: 600 11px var(--font-thai);
  cursor: pointer;
  transition: background 0.12s ease;
}
@media (hover: hover) { .auth-toggle:hover { background: rgba(var(--surface-rgb),0.06); } }

.auth-error {
  display: flex; align-items: center; gap: 6px;
  padding: 9px 12px;
  border-radius: var(--radius-lg);
  background: rgba(var(--like-red-rgb), 0.12);
  font-size: 12px; font-weight: 600;
  color: var(--like-red);
}
.auth-error::before {
  /* Flat warning icon (2026-10-26) — same path as ICON_WARN_SVG in p/icons.js,
     drawn as a mask so it takes the error's currentColor instead of the
     OS-native ⚠️ glyph the old `content` rendered. */
  content: ''; flex: none; width: 12px; height: 12px;
  background: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0.56 0.06 22.88 22.88'%3E%3Cpath fill-rule='evenodd' d='M12 2 1 21h22zm-1 7h2v6h-2zm0 8h2v2h-2z'/%3E%3C/svg%3E") center / contain no-repeat;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0.56 0.06 22.88 22.88'%3E%3Cpath fill-rule='evenodd' d='M12 2 1 21h22zm-1 7h2v6h-2zm0 8h2v2h-2z'/%3E%3C/svg%3E") center / contain no-repeat;
}
/* Duplicate-seat error (2026-07-08): 2 lines instead of 1 — main message at
   the box's normal 12px, then a smaller dispute-instructions line below. */
.auth-error--seat { align-items: flex-start; }
.auth-error__sub { display: block; margin-top: 4px; font-size: 10px; font-weight: 500; opacity: 0.85; }
/* A class-based `display` always beats the UA [hidden] rule's specificity —
   without this, .auth-error stayed visible (as an empty red bar + icon)
   even while hidden=true, e.g. above the signup button before any error. */
.auth-error[hidden] { display: none; }

/* Credentials modal — appended directly to <body> (outside .auth-page), so
   it doesn't inherit .auth-page's `color: var(--text-primary)`; without an
   explicit color here, text falls back to the browser default black. */
.auth-modal {
  position: fixed; inset: 0;
  display: flex; align-items: center; justify-content: center;
  /* Safe-area padding (2026-08-22) — the scrim is inset:0 so it now covers
     the status bar and home indicator too; padding it keeps the CARD inside
     the safe area without touching any of the card's own vh-based max-heights. */
  padding: calc(30px + var(--safe-top)) calc(30px + var(--safe-right)) calc(30px + var(--safe-bottom)) calc(30px + var(--safe-left));
  background: rgba(var(--black-rgb), 0.85);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  color: var(--text-primary);
  font-family: var(--font-thai);
  /* Above .ppw-nav (nav.css, z-index:1000) — every popup built on this shell
     (modals.js's Edit Profile/Ticket/Verify/Add/Sale/Password/X-conflict,
     market.js's report modal) must sit above the sticky navbar, not under it. */
  z-index: 1100;
  animation: auth-modal-in 0.15s ease both;
}
@keyframes auth-modal-in { from { opacity: 0; } to { opacity: 1; } }
.auth-modal[hidden] { display: none; }
.auth-modal__card {
  position: relative;
  width: 100%; max-width: 340px;
  display: flex; flex-direction: column; gap: 14px;
  padding: 24px 20px 20px;
  /* Was var(--bg-elevated) — this shell originally had its own literal
     #0E0F1F (distinct from --bg-elevated's #1C1E35), which an earlier
     light-mode conversion pass mistakenly collapsed into --bg-elevated
     instead of giving it its own token, silently lightening every popup's
     dark-mode background sitewide (2026-08-08 fix — see --modal-bg in
     tokens.css/style.css for the full story). */
  background: var(--modal-bg);
  border: 1px solid rgba(var(--surface-rgb), 0.1);
  border-radius: var(--radius-3xl);
  /* Layered shadow stack (2026-07-27, Feel Lab ref) — tight/dark + medium +
     wide/faint, instead of one flat shadow; used by every modals.js popup
     + market.js's report modal via this shared class. */
  box-shadow:
    0 2px 5px rgba(var(--black-rgb), 0.35),
    0 10px 24px rgba(var(--black-rgb), 0.32),
    0 28px 60px rgba(var(--black-rgb), 0.3);
  animation: auth-card-in 0.45s var(--ease-spring) both;
  /* Overflow scrolls inside the card instead of the card just overflowing
     .auth-modal uncontrolled (2026-07-08 fix) — the shared neutral scrollbar
     (tokens.css) applies automatically, no extra styling needed here. */
  max-height: calc(100vh - 60px);
  max-height: calc(100dvh - 60px);
  overflow-y: auto;
}
.auth-modal__icon {
  width: 44px; height: 44px; margin: 0 auto 2px;
  display: flex; align-items: center; justify-content: center;
  border-radius: var(--radius-full);
  background: rgba(var(--accent-gold-rgb), 0.14);
  border: 2px solid var(--accent-gold);
  font-size: 18px;
  color: var(--accent-gold-text);
}
.auth-modal__head { display: flex; align-items: flex-start; gap: 8px; }
.auth-modal__head-txt { flex: 1; display: flex; flex-direction: column; gap: 2px; }
.auth-modal__close {
  position: absolute; top: 12px; right: 12px;
  width: 28px; height: 28px;
  display: flex; align-items: center; justify-content: center;
  background: rgba(var(--surface-rgb), 0.06);
  border: none; border-radius: var(--radius-md);
  color: var(--text-secondary); font-size: 13px; cursor: pointer;
  transition: background 0.12s ease;
}
@media (hover: hover) { .auth-modal__close:hover { background: rgba(var(--surface-rgb), 0.12); } }

/* ── Standard "no ticket on file" nudge (modals.js showNoTicketModal(),
   2026-08-05 — promoted from a homesafe.js one-off into the shared /p
   component every ticket-gated feature should reuse). Custom layout, not
   the generic .auth-modal__actions confirm/cancel row: centered icon badge,
   full-width gradient primary button, de-emphasized text-link skip below
   it. Originally built to match HOMESAFE_FIGMA_NODE_MAP.md's
   Modal=NoTicketNudge mockup. */
.noticket-modal .auth-modal__card { align-items: center; text-align: center; }
/* Plain, gentle fade for both backdrop and card (2026-08-11 — 2 earlier
   passes both read as flicker: 0.05s backdrop snapped in as a flash, and a
   scale/spring "pop" on the card looked like a jitter). The screen is
   already locked from the instant this modal mounts regardless of how slow
   its opacity fade is — .auth-modal is a fixed, full-viewport element from
   frame one, so nothing behind it is clickable during the fade either way;
   the motion here is purely cosmetic polish, not what does the locking.
   Scoped to just this modal — every other /p popup keeps the shared,
   slightly snappier auth-modal-in/auth-card-in. */
.noticket-modal { animation-duration: 0.2s; }
@keyframes noticket-card-in {
  from { opacity: 0; transform: translateY(14px) translateZ(0); }
  to   { opacity: 1; transform: translateY(0) translateZ(0); }
}
.noticket-modal .auth-modal__card { animation: noticket-card-in 0.28s ease-out both; }
.noticket-modal__icon {
  width: 64px; height: 64px; border-radius: var(--radius-full);
  display: flex; align-items: center; justify-content: center;
  background: rgba(var(--accent-gold-rgb), 0.14);
  box-shadow: 0 0 28px rgba(var(--accent-gold-rgb), 0.22);
  font-size: 28px;
  margin: 4px 0 2px;
}
.noticket-modal__title { font-size: 17px; }
.noticket-modal__sub { font-size: 13px; line-height: 1.6; }
.noticket-modal__btn-primary {
  background: linear-gradient(135deg, var(--cta-grad-hi), var(--cta-grad-lo));
  /* Fixed dark text, overriding .auth-btn--primary's inherited
     var(--gold-btn-text) (2026-08-08 fix) — that token is white in light
     mode, correct for .auth-btn--primary's own solid var(--accent-gold)
     background, but THIS button overrides to a fixed light gradient that
     stays light in both themes, so it needs the fixed dark text back
     (white measured 1.72:1 here — a regression the gold-btn-text token
     introduced on this one button since it shares the base class). */
  color: var(--brand-gold-ink);
  width: 100%; margin-top: 4px;
  display: flex; align-items: center; justify-content: center; gap: 8px;
  font-size: 16px;
}
.noticket-modal__btn-arrow { display: inline-flex; font-size: 15px; }
.noticket-modal__skip {
  background: none; border: none; cursor: pointer;
  color: var(--text-secondary); font: 500 12px var(--font-thai);
  padding: 4px; margin-top: 2px;
}
@media (hover: hover) { .noticket-modal__skip:hover { color: var(--text-primary); } }

/* "ยินดีต้อนรับสู่ myJASPER" welcome card — shown at /p for a logged-in member
   with 0 tickets (2026-08-11 ask, moved from my.js's old renderNoTicketsState()
   in p/my.css; copied verbatim here so /p doesn't need to load my.css). */
.my-empty-wrap {
  min-height: calc(100vh - var(--navbar-total-h));
  min-height: calc(100dvh - var(--navbar-total-h));
  display: flex; align-items: center; justify-content: center;
  padding: 24px;
}
.my-empty-tickets {
  display: flex; flex-direction: column; align-items: center; gap: 14px;
  text-align: center; padding: 48px 36px; max-width: 420px;
  background:
    radial-gradient(circle at 50% 0%, rgba(var(--accent-gold-rgb),0.16), transparent 60%),
    var(--bg-secondary);
  border: 1px solid var(--border);
  border-radius: var(--radius-5xl);
  box-shadow: 0 24px 60px rgba(var(--shadow-rgb), calc(0.35 * var(--shadow-mult)));
}
.my-empty-tickets__icon {
  width: 84px; height: 84px;
  display: flex; align-items: center; justify-content: center;
  border-radius: var(--radius-full);
  font-size: 42px;
  background: rgba(var(--accent-gold-rgb),0.14);
  box-shadow: 0 0 34px rgba(var(--accent-gold-rgb),0.28);
}
.my-empty-tickets__title { font-size: 21px; font-weight: 800; color: var(--text-primary); }
.my-empty-tickets__msg { font-size: 14px; color: var(--text-secondary); line-height: 1.6; }
.my-empty-tickets__cta {
  margin-top: 6px;
  display: inline-flex; align-items: center; gap: 6px;
  padding: 13px 26px;
  border: none; border-radius: var(--radius-xl);
  background: linear-gradient(120deg, var(--cta-grad-hi), var(--cta-grad-lo));
  color: var(--brand-gold-ink);
  font: 700 14px var(--font-thai);
  text-decoration: none;
  cursor: pointer;
  box-shadow: 0 10px 24px rgba(var(--accent-gold-rgb),0.3), inset 0 1px 0 rgba(255, 255, 255, .18);
  transition: filter 0.15s ease;
}
@media (hover: hover) { .my-empty-tickets__cta:hover { filter: brightness(1.06); } }
.auth-input-wrap.is-invalid { border-color: var(--like-red); }
.auth-input-row { display: flex; align-items: center; gap: 8px; }
.auth-input-row .auth-input { flex: 1; border: none; background: transparent; height: auto; padding: 0; }
.auth-input-wrap {
  /* Same box as a plain .auth-input (2026-10-23) — it used a lighter 5%
     surface and taller padding, so a password field with the eye button
     looked like a different control next to the signup form's other inputs. */
  display: flex; align-items: center; gap: 8px;
  height: 44px;
  padding: 0 12px;
  background: var(--bg-primary);
  border: 1px solid rgba(var(--surface-rgb),0.1);
  border-radius: var(--radius-lg);
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.auth-input-wrap:focus-within { border-color: var(--focus-ring); box-shadow: 0 0 0 3px rgba(var(--accent-gold-rgb),0.12); }
/* The inner .auth-input already has its own border/background — without this
   override it nests a second frame inside .auth-input-wrap's frame. The focus
   glow above lives on the wrap instead, so suppress the inner input's own. */
/* min-width: 0 — an <input>'s default min width is its intrinsic ~20-char
   size, so on a narrow card it refused to shrink and pushed the eye button
   out past the frame (2026-10-23). */
.auth-input-wrap .auth-input { flex: 1; min-width: 0; border: none; background: transparent; height: auto; padding: 0; }
.auth-input-wrap .auth-eye { flex: none; }
.auth-input-wrap .auth-input:focus { box-shadow: none; }
.auth-eye {
  background: none; border: none; cursor: pointer; font-size: 13px;
  transition: transform 0.12s ease;
}
@media (hover: hover) { .auth-eye:hover { transform: scale(1.1); } }

/* Page titles (2026-10-14, owner ask): every feature page's own title is one
   size, with a flat icon in front tinted like that page's nav card
   (--nav-ic-*). Hero-style headings (vote, gohome) and title-less pages
   (my, find) deliberately don't use this. Doubled class beats page CSS. */
.page-title.page-title { font-size: 24px; font-weight: 700; }
.page-title__icon.page-title__icon { font-size: 24px; display: inline-flex; vertical-align: -0.15em; }
.page-title__icon.page-title__icon--md {  color: var(--nav-ic-md); }
.page-title__icon.page-title__icon--fanbene {  color: var(--nav-ic-fanbene); }
.page-title__icon.page-title__icon--giveaway {  color: var(--nav-ic-giveaway); }
.page-title__icon.page-title__icon--market {  color: var(--nav-ic-find); }
.page-title__icon.page-title__icon--sponsor {  color: var(--nav-ic-md); }
.page-title__icon.page-title__icon--map {  color: var(--nav-ic-directory-map); }
.page-title__icon.page-title__icon--notify {  color: var(--nav-ic-guide); }
.page-title__icon.page-title__icon--help {  color: var(--nav-ic-guide); }
.page-title__icon.page-title__icon--tickets {  color: var(--nav-ic-my); }
.page-title__icon.page-title__icon--homesafe {  color: var(--nav-ic-homesafe); }

/* ── Bias picker / "เมนของฉัน" row (2026-10-22) — p/bias-picker.js ─────────
   Read-only row (/p/my): five circles fit one row at 320px, never wrap.
   Picker (signup, Edit Profile): two rows, 3 + 2 centred, bigger photos. */
.bias-picker { display: flex; flex-direction: column; gap: 12px; }
.bias-row { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 8px; }
.bias-item {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  min-width: 0; padding: 0; background: none; border: 0;
  color: var(--text-secondary); font: inherit; cursor: default;
}
button.bias-item { cursor: pointer; }
.bias-row--pick { display: flex; flex-wrap: wrap; justify-content: center; gap: 14px 12px; }
.bias-row--pick .bias-item { flex: 0 0 calc((100% - 24px) / 3); }
.bias-row--pick .bias-item__img { max-width: 96px; }
.bias-row--pick .bias-item__name { font-size: 13px; }
.bias-item__img {
  width: 100%; max-width: 64px; aspect-ratio: 1; object-fit: cover;
  border-radius: var(--radius-full);
  border: 2px solid transparent;
  background: var(--bg-elevated);
  transition: border-color 0.12s ease, opacity 0.12s ease, transform 0.12s ease;
}
.bias-item__name {
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: 11px; font-weight: 600; letter-spacing: 0.02em;
}
button.bias-item .bias-item__img { opacity: 0.45; }
button.bias-item.is-selected .bias-item__img { opacity: 1; border-color: var(--accent-gold); }
button.bias-item.is-selected .bias-item__name { color: var(--text-primary); }
@media (hover: hover) { button.bias-item:hover .bias-item__img { transform: translateY(-1px); } }
.bias-row--static .bias-item { color: var(--text-primary); }
.bias-row--static .bias-item__name { font-weight: 400; }
/* Very narrow screens: keep one row and scroll sideways instead of shrinking
   the circles below 56px. */
.bias-row--static {
  grid-template-columns: repeat(5, minmax(56px, 1fr));
  overflow-x: auto; overscroll-behavior-x: contain; scrollbar-width: none;
}
.bias-row--static::-webkit-scrollbar { display: none; }
.bias-scroll { position: relative; }
.bias-scroll__hint {
  position: absolute; top: 0; bottom: 0; width: 28px;
  display: flex; align-items: center;
  pointer-events: none; opacity: 0; transition: opacity 0.15s ease;
}
.bias-scroll__hint::after {
  content: ''; width: 7px; height: 7px;
  border: solid var(--text-primary); border-width: 0 2px 2px 0;
}
.bias-scroll__hint--left { left: 0; justify-content: flex-start; background: linear-gradient(to right, var(--bg-primary), transparent); }
.bias-scroll__hint--left::after { transform: rotate(135deg); margin-left: 4px; }
.bias-scroll__hint--right { right: 0; justify-content: flex-end; background: linear-gradient(to left, var(--bg-primary), transparent); }
.bias-scroll__hint--right::after { transform: rotate(-45deg); margin-right: 4px; }
.bias-scroll.has-left .bias-scroll__hint--left,
.bias-scroll.has-right .bias-scroll__hint--right { opacity: 1; }
.auth-btn.bias-all { width: auto; align-self: center; padding: 7px 18px; font-size: 13px; border-radius: var(--radius-md); }
.bias-note { font-size: 10.5px; line-height: 1.55; color: var(--text-secondary); }
