/**
 * PWA-specific layer.
 * The shared plugin stylesheets (style.css / premium.css) power the look;
 * style.css leans on Telegram's theme variables which only exist inside
 * Telegram, so the block below supplies them for the browser/PWA context -
 * mapped straight onto the premium design tokens.
 */
:root {
  --tg-theme-bg-color: #0a0a0a;
  --tg-theme-secondary-bg-color: #141414;
  --tg-theme-text-color: #ffffff;
  --tg-theme-hint-color: #a0a0a0;
  --tg-theme-link-color: #faa000;
  --tg-theme-button-color: #faa000;
  --tg-theme-button-text-color: #1a1a1a;
}

body {
  background: #0a0a0a;
  min-height: 100vh;
}

/* Interactive elements shouldn't be selectable text. Without this, a
   quick double-tap on a nav item, tab, or card (completely normal when
   navigating fast) can register as a text-selection gesture instead of
   a tap - which is what was popping up Chrome/Android's "Search for
   this" sheet instead of just navigating. */
[onclick], button, .zws-nav-item, .tab, .link-btn {
  -webkit-user-select: none;
  -moz-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}

/* App header */
.app-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--space-xl);
}

.app-topbar .brand {
  font-size: var(--fs-lg);
  font-weight: 800;
  letter-spacing: 0.5px;
}

.app-topbar .brand .gold { color: var(--gold); }

.app-topbar .brand-logo {
  height: 32px;
  width: auto;
  display: block;
}

.app-topbar .link-btn {
  background: none;
  border: none;
  color: var(--text-secondary);
  font-size: var(--fs-sm);
  font-weight: 600;
  cursor: pointer;
  padding: 6px 0;
}

/* Login screen */
.login-hero {
  text-align: center;
  margin: 48px 0 32px;
}

.login-hero .logo-horizontal {
  width: min(230px, 64%);
  height: auto;
  margin-bottom: 14px;
}

.login-hero h1 {
  font-size: var(--fs-xl);
  font-weight: 800;
  margin: 0 0 6px;
}

.login-hero p {
  font-size: var(--fs-sm);
  color: var(--text-secondary);
  margin: 0;
}

.field {
  margin-bottom: var(--space-md);
}

.field label {
  display: block;
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--text-secondary);
  margin-bottom: 8px;
}

.field input {
  width: 100%;
  background: var(--surface-dark);
  border: 1.5px solid var(--border-subtle);
  border-radius: var(--radius-md, 12px);
  padding: 14px 16px;
  font-size: var(--fs-lg);
  font-weight: 600;
  color: var(--text-primary);
  letter-spacing: 1px;
  transition: border-color 0.2s ease;
  box-sizing: border-box;
}

.field input:focus { outline: none; border-color: var(--gold); }
.field .hint { font-size: var(--fs-xs); color: var(--text-muted); margin-top: 6px; }
.field .hint.error { color: var(--error-red); font-weight: 600; }
.field input.invalid { border-color: var(--error-red); }

.form-error {
  font-size: var(--fs-sm);
  color: var(--error-red);
  font-weight: 600;
  text-align: center;
  min-height: 20px;
  margin: var(--space-sm) 0;
}

.login-footer {
  text-align: center;
  font-size: var(--fs-sm);
  color: var(--text-secondary);
  margin-top: var(--space-xl);
}

.login-footer a { color: var(--gold); text-decoration: none; font-weight: 600; }

/* Home sections */
.section-title {
  font-size: var(--fs-sm);
  font-weight: 700;
  color: var(--text-secondary);
  text-transform: uppercase;
  letter-spacing: 0.6px;
  margin: var(--space-xl) 0 var(--space-sm);
  border-left: 3px solid var(--gold);
  padding-left: 10px;
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
}

.sub-card {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.sub-card .sub-status { font-size: var(--fs-lg); font-weight: 800; }
.sub-card .sub-status.active { color: var(--live-green); }
.sub-card .sub-status.expired { color: var(--error-red); }
.sub-card .sub-detail { font-size: var(--fs-xs); color: var(--text-muted); margin-top: 2px; }
.sub-card .btn-gold { width: auto; padding: 10px 16px; text-decoration: none; font-size: var(--fs-sm); }

.match-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 12px 0;
  border-bottom: 1px solid var(--border-subtle);
  font-size: var(--fs-sm);
}

.match-row:last-child { border-bottom: none; }
.match-row .teams { flex: 1; min-width: 0; }
.match-row .team { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; padding: 2px 0; }
.match-row .score { font-family: 'SF Mono','Courier New',monospace; font-weight: 800; text-align: right; }
.match-row .score .live { color: var(--live-green); }
.match-row .meta { font-size: var(--fs-xs); color: var(--text-muted); text-align: right; }

.empty-note {
  font-size: var(--fs-sm);
  color: var(--text-muted);
  text-align: center;
  padding: var(--space-lg) 0;
}

/* ============================================================
   HOME - "waking up" motion layer (subtle & premium)
   All motion is opt-out via prefers-reduced-motion at the end.
   ============================================================ */

/* Staggered entrance: each .wake element rises 8px and fades in.
   The inline --i on each element multiplies the delay so sections
   arrive in sequence rather than all at once - the app "waking up". */
.wake {
  opacity: 0;
  transform: translateY(8px);
  animation: wakeIn 0.5s cubic-bezier(0.22, 1, 0.36, 1) forwards;
  animation-delay: calc(var(--i, 0) * 70ms);
  will-change: opacity, transform;
}

@keyframes wakeIn {
  to { opacity: 1; transform: translateY(0); }
}

/* Hero greeting - the first human line the eye lands on */
.home-hero {
  margin: 4px 0 var(--space-lg);
}
.home-hero .greeting {
  font-size: var(--fs-xl);
  font-weight: 800;
  line-height: 1.15;
  letter-spacing: -0.3px;
}
.home-hero .greeting .accent { color: var(--gold); }
.home-hero .subline {
  font-size: var(--fs-sm);
  color: var(--text-secondary);
  margin-top: 3px;
}

/* Live badge - a quiet pulse that says "happening now" */
.live-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
/* When matches are actually live, the whole banner gently breathes to
   draw the eye. Applied via .is-live so a quiet "Live now" heading with
   no matches stays still (a pulsing empty banner would mislead). */
.live-badge.is-live {
  animation: liveBreathe 2s ease-in-out infinite;
}
@keyframes liveBreathe {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.55; }
}
.live-badge .dot {
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--live-green);
  position: relative;
  flex: 0 0 auto;
}
.live-badge .dot::after {
  content: '';
  position: absolute;
  inset: -3px;
  border-radius: 50%;
  background: var(--live-green);
  opacity: 0.35;
  animation: livePulse 1.8s ease-out infinite;
}
@keyframes livePulse {
  0%   { transform: scale(0.6); opacity: 0.5; }
  70%  { transform: scale(1.9); opacity: 0; }
  100% { transform: scale(1.9); opacity: 0; }
}

/* A live match row gets a faint green edge so it reads apart at a glance */
.match-row.is-live {
  border-left: 2px solid var(--live-green);
  padding-left: 10px;
  margin-left: -12px;
}

/* Goal flash - fires when a score changes on refresh. The row breathes
   gold once; the changed number pops. This is the emotional beat. */
.match-row.goal-flash {
  animation: goalFlash 1.6s ease-out;
}
.premium-card.goal-flash {
  animation: goalFlashCard 1.6s ease-out;
}
@keyframes goalFlashCard {
  0%   { background: rgba(250, 160, 0, 0.22); }
  100% { background: var(--surface-card); }
}
@keyframes goalFlash {
  0%   { background: rgba(250, 160, 0, 0.22); }
  100% { background: transparent; }
}
.score.score-pop {
  animation: scorePop 0.5s cubic-bezier(0.22, 1, 0.36, 1);
}
.fx-score.score-pop {
  animation: scorePop 0.5s cubic-bezier(0.22, 1, 0.36, 1);
}
@keyframes scorePop {
  0%   { transform: scale(1); }
  40%  { transform: scale(1.28); color: var(--gold); }
  100% { transform: scale(1); }
}

/* Staggered entrance for newly-rendered list content on a genuine first
   load - fade + slight rise, timed via an inline animation-delay set per
   element by the page's JS (loop index * a small increment, capped so
   long lists don't drag out the reveal). Not applied on background
   refresh ticks - only ever added when a page is populating for the
   first time. Generic (not scoped to one row class) so it can be reused
   anywhere a list of cards/rows renders. */
.zws-row-rise {
  opacity: 0;
  animation: zwsRowRise 0.55s cubic-bezier(0.22, 1, 0.36, 1) forwards;
}
@keyframes zwsRowRise {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ============================================================
   BOTTOM NAV - persistent 4-tab bar (app-shell.js)
   ============================================================ */
#zwsBottomNav {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 500;
  display: flex;
  background: var(--surface-dark);
  border-top: 1px solid var(--border-subtle);
  padding: 6px 4px calc(6px + env(safe-area-inset-bottom, 0px));
}
.zws-nav-item {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 6px 0;
  color: var(--text-secondary);
  font-size: 11px;
  font-weight: 600;
  cursor: pointer;
  text-decoration: none;
}
.zws-nav-item .zws-nav-icon { font-size: 20px; }
.zws-nav-item.active { color: var(--gold); }
.zws-nav-icon-wrap { position: relative; display: inline-block; transform-origin: 50% 100%; }
.zws-nav-badge {
  position: absolute;
  top: -4px;
  right: -9px;
  min-width: 15px;
  height: 15px;
  padding: 0 3px;
  border-radius: 999px;
  background: var(--error-red);
  color: #fff;
  font-size: 9px;
  font-weight: 700;
  line-height: 15px;
  text-align: center;
  display: none;
  align-items: center;
  justify-content: center;
}
body.has-bottom-nav { padding-bottom: calc(72px + env(safe-area-inset-bottom, 0px)); }

/* Tap feedback on bottom nav icons - squish on impact, two decreasing
   bounces, then settle. Triggered by app-shell.js toggling
   .zws-nav-bounce on the icon wrap on click (see zwsBuildBottomNav). */
@keyframes zwsNavBounce {
  0%   { transform: scale(1, 1) translateY(0); }
  8%   { transform: scale(1.3, 0.7) translateY(0); }
  22%  { transform: scale(0.8, 1.2) translateY(-8px); }
  38%  { transform: scale(1.2, 0.8) translateY(0); }
  52%  { transform: scale(0.88, 1.12) translateY(-3px); }
  66%  { transform: scale(1.1, 0.9) translateY(0); }
  80%  { transform: scale(0.96, 1.04) translateY(-1px); }
  100% { transform: scale(1, 1) translateY(0); }
}
.zws-nav-icon-wrap.zws-nav-bounce {
  animation: zwsNavBounce 0.6s cubic-bezier(.36,.07,.19,.97);
}

/* Menu icon tap feedback (nav-menu.js) - each icon reacts with its own
   analogy instead of one generic effect for all six. Triggered by
   zwsMenuBounce() toggling .zws-mi-go on the relevant .zws-mi-wrap. */
.zws-mi-wrap { display: inline-flex; }
.zws-mi-bell { transform-origin: 50% 0%; }
.zws-mi-bell.zws-mi-go { animation: zwsMiBell 0.6s ease; }
@keyframes zwsMiBell {
  0%, 100% { transform: rotate(0deg); }
  15% { transform: rotate(-16deg); }
  30% { transform: rotate(13deg); }
  45% { transform: rotate(-10deg); }
  60% { transform: rotate(7deg); }
  75% { transform: rotate(-4deg); }
  90% { transform: rotate(2deg); }
}
.zws-mi-gear { transform-origin: 50% 50%; }
.zws-mi-gear.zws-mi-go { animation: zwsMiGear 1.1s ease-in-out; }
@keyframes zwsMiGear {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}
.zws-mi-help.zws-mi-go { animation: zwsMiHelp 0.8s ease; }
@keyframes zwsMiHelp {
  0%, 100% { transform: rotate(0deg); }
  25% { transform: rotate(-13deg); }
  50% { transform: rotate(0deg); }
  75% { transform: rotate(13deg); }
}
.zws-mi-heart.zws-mi-go { animation: zwsMiHeart 0.7s ease; }
@keyframes zwsMiHeart {
  0%, 100% { transform: scale(1); }
  15% { transform: scale(1.3); }
  30% { transform: scale(1); }
  45% { transform: scale(1.22); }
  60% { transform: scale(1); }
}
.zws-mi-hand { transform-origin: 50% 50%; }
.zws-mi-hand.zws-mi-go { animation: zwsMiHand 0.6s ease; }
@keyframes zwsMiHand {
  0%, 100% { transform: translateY(0); }
  25% { transform: translateY(-6px); }
  50% { transform: translateY(0); }
  75% { transform: translateY(-4px); }
}
.zws-mi-door.zws-mi-go { animation: zwsMiDoor 1s ease forwards; }
@keyframes zwsMiDoor {
  0%   { transform: scale(1); opacity: 1; }
  55%  { transform: scale(0.25); opacity: 0; }
  100% { transform: scale(0.25); opacity: 0; }
}

/* ============================================================
   FIXTURE TABS - Today/Recent/Upcoming + Live pill
   ============================================================ */
.zws-fixture-tabs-row {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 20px;
}
.zws-live-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: var(--live-green);
  color: #fff;
  border: none;
  border-radius: var(--radius-full);
  padding: 10px 16px;
  font-weight: 700;
  font-size: var(--fs-sm);
  white-space: nowrap;
  cursor: pointer;
}
.zws-live-pill.current { box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.4) inset; }
.zws-live-pill .zws-live-dot {
  width: 7px; height: 7px;
  border-radius: 50%;
  background: #fff;
  animation: blink 1s infinite;
}

/* ============================================================
   NOTIFICATIONS TOGGLE (nav-menu.js)
   ============================================================ */
.zws-toggle {
  position: relative;
  width: 44px; height: 26px;
  border-radius: 999px;
  /* Was var(--surface-dark) - identical to the menu sheet behind it, so
     the track had no visible fill at all, just a 6%-white border. This
     faint white fill gives it a real, visible pill shape at rest. */
  background: rgba(255, 255, 255, 0.10);
  border: 1px solid rgba(255, 255, 255, 0.16);
  flex-shrink: 0;
  cursor: pointer;
  transition: background 0.2s ease, border-color 0.2s ease;
}
.zws-toggle.on { background: var(--gold); border-color: var(--gold); }
.zws-toggle.disabled { opacity: 0.4; cursor: not-allowed; }
.zws-toggle .zws-toggle-knob {
  position: absolute;
  top: 2px; left: 2px;
  width: 20px; height: 20px;
  border-radius: 50%;
  background: #fff;
  transition: transform 0.2s ease;
}
.zws-toggle.on .zws-toggle-knob { transform: translateX(18px); }

/* ============================================================
   LIVE / HALF-TIME FIXTURE CARD ACCENTS
   Ambient color instead of just a text badge: a green edge + tint
   while a match is actually being played, shifting to red at half-time
   so a glance down the list shows what's paused vs in motion.
   ============================================================ */
.fixture-inplay {
  border-left: 3px solid var(--live-green) !important;
  background: rgba(0, 200, 83, 0.07) !important;
}
.fixture-halftime {
  border-left: 3px solid var(--error-red) !important;
  background: rgba(255, 82, 82, 0.07) !important;
}

/* Referenced by matches.html's status badge for in-play fixtures but
   never defined until now - reuses the same breathing pulse as the
   home page's live badge instead of introducing a second animation. */
.live-badge-pulse-green {
  animation: liveBreathe 2s ease-in-out infinite;
}

/* Respect the OS setting: no motion, just clean static content */
@media (prefers-reduced-motion: reduce) {
  .wake,
  .match-row.goal-flash,
  .premium-card.goal-flash,
  .zws-fixture-row.goal-flash,
  .score.score-pop,
  .fx-score.score-pop,
  .score-large.score-pop,
  .zws-row-rise,
  .live-badge .dot::after,
  .live-badge-pulse-green,
  .zws-live-pill .zws-live-dot,
  .zws-nav-icon-wrap.zws-nav-bounce,
  .zws-mi-bell.zws-mi-go,
  .zws-mi-gear.zws-mi-go,
  .zws-mi-help.zws-mi-go,
  .zws-mi-heart.zws-mi-go,
  .zws-mi-hand.zws-mi-go,
  .zws-mi-door.zws-mi-go,
  .zws-star.zws-star-pop,
  .zws-star.zws-star-shrink,
  .zws-spark.zws-spark-go,
  .zws-fixture-row.rc-flash,
  .zws-rc-badge.rc-pop,
  .zws-nav-badge.zws-badge-pop-in,
  .zws-nav-badge.zws-badge-bump {
    animation: none !important;
  }
  .zws-tab-indicator {
    transition: none !important;
  }
  .live-badge.is-live {
    animation: none !important;
    opacity: 1 !important;
  }
  .wake { opacity: 1; transform: none; }
  /* .zws-row-rise starts at opacity:0 as a base property (not just within
     the animation), so disabling the animation alone would leave content
     stuck invisible - restore it explicitly. */
  .zws-row-rise { opacity: 1; transform: none; }
}

/* ============================================================
   COMPACT FIXTURE ROW STYLES
   ============================================================ */

.zws-fixture-row {
    display: flex;
    align-items: center;
    gap: 0;
    padding: 10px 0;
    border-bottom: 1px solid var(--border-subtle);
    cursor: pointer;
    position: relative;
    min-height: 56px;
}
.zws-fixture-row:last-child { border-bottom: none; }
.zws-fixture-row:active { opacity: 0.7; }

/* Live / HT accent bar on left edge */
.zws-fixture-row.live-accent::before {
    content: '';
    position: absolute;
    left: 0;
    top: 4px;
    bottom: 4px;
    width: 3px;
    border-radius: 0 2px 2px 0;
    background: linear-gradient(180deg, var(--live-green), #00a843);
}
.zws-fixture-row.ht-accent::before {
    content: '';
    position: absolute;
    left: 0;
    top: 4px;
    bottom: 4px;
    width: 3px;
    border-radius: 0 2px 2px 0;
    background: linear-gradient(180deg, var(--error-red), #cc3333);
}

/* Status/time column (left) */
.zws-fixture-status {
    min-width: 44px;
    text-align: center;
    flex-shrink: 0;
    align-self: center;
    padding-right: 8px;
}
.zws-fixture-status .status-live {
    font-size: var(--fs-xs);
    font-weight: 700;
    color: var(--live-green);
}
.zws-fixture-status .status-ht {
    font-size: var(--fs-xs);
    font-weight: 700;
    color: var(--error-red);
}
.zws-fixture-status .status-ft {
    font-size: var(--fs-xs);
    font-weight: 600;
    color: var(--text-muted);
}
.zws-fixture-status .status-time {
    font-size: var(--fs-xs);
    font-weight: 600;
    color: var(--text-secondary);
}
.zws-fixture-status .status-scheduled {
    font-size: var(--fs-xs);
    font-weight: 600;
    color: var(--gold);
}

/* Teams column (middle) */
.zws-fixture-teams {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding-right: 8px;
}
.zws-fixture-team {
    display: flex;
    align-items: center;
    gap: 8px;
}
.zws-fixture-team .team-name {
    font-size: var(--fs-sm);
    font-weight: 600;
    color: var(--text-primary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Score column (right) */
.zws-fixture-score {
    min-width: 28px;
    text-align: right;
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 6px;
}
.zws-fixture-score .score-num {
    font-size: var(--fs-lg);
    font-weight: 800;
    font-family: 'SF Mono', 'Courier New', monospace;
    line-height: 1;
    color: var(--text-primary);
}

/* ============================================================
   FIXTURE TABS - grayed out except Live (app-shell.js)
   Today/Recent/Upcoming no longer get the loud gold-pill "active"
   treatment - only the Live button stays visually loud, so it's the
   one thing that pulls the eye at the top of the fixture pages.
   ============================================================ */
.zws-fixture-tabs-row .tabs {
    background: transparent;
    padding: 0;
    gap: 4px;
}
.zws-fixture-tabs-row .tabs .tab {
    color: var(--text-muted);
    padding: 8px 14px;
}
.zws-fixture-tabs-row .tabs .tab.active {
    background: var(--surface-dark);
    border-color: transparent;
    color: var(--text-primary);
    font-weight: 700;
}

/* ============================================================
   FIXTURES VIEW AMBIENT BACKGROUND (matches/recent/upcoming/live)
   A photo sits fixed behind the page, confined to roughly the
   bottom quarter of the viewport, and fades to solid black as it
   reaches upward so it dissolves into the rest of the page rather
   than having a hard edge. Everything is scoped under
   body.zws-has-hero so this can never leak onto any other page.
   Cards keep their own solid var(--surface-card) background, so
   legibility is untouched no matter what's behind them.
   ============================================================ */
body.zws-has-hero .zws-hero-bg {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    height: 46vh;
    background-image: url('https://zwsoccer.com/wp-content/uploads/2026/07/soccer-fans.png');
    background-size: cover;
    background-position: center bottom;
    z-index: 0;
    pointer-events: none;
}
body.zws-has-hero .zws-hero-bg::after {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(
        to top,
        rgba(10, 10, 10, 0.45) 0%,
        rgba(10, 10, 10, 0.82) 40%,
        rgba(10, 10, 10, 0.97) 70%,
        rgba(10, 10, 10, 1) 100%
    );
}
body.zws-has-hero .container {
    position: relative;
    z-index: 1;
}

/* ============================================================
   COLLAPSIBLE LEAGUE HEADERS (matches/live/recent/upcoming)
   One consistent style used identically in all 4 views - flag + name,
   same font size/weight everywhere, no icon badge, no per-page variants.
   Default state is expanded. zwsToggleLeague() in utils.js flips the
   body's display and rotates the chevron; re-renders (30s auto-refresh)
   read the same zwsCollapsedLeagues Set so a collapsed league doesn't
   silently re-expand on the next tick.
   ============================================================ */
.zws-date-header {
    font-size: var(--fs-sm);
    font-weight: 700;
    color: var(--text-secondary);
    letter-spacing: 0.3px;
    padding: 10px 0 4px;
}
.zws-league-header {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 0 8px 10px;
    margin: 4px 0 0;
    cursor: pointer;
    border-left: 3px solid var(--gold);
    background: none;
    border-top: none;
    border-right: none;
    border-bottom: none;
    width: 100%;
    text-align: left;
    font: inherit;
    color: inherit;
    -webkit-appearance: none;
    -moz-appearance: none;
    appearance: none;
}
.zws-league-header .zws-league-flag {
    font-size: 18px;
    flex-shrink: 0;
}
.zws-league-header .name {
    flex: 1;
    min-width: 0;
    font-size: var(--fs-sm);
    font-weight: 700;
    color: var(--text-primary);
}
.zws-league-chevron {
    color: var(--text-muted);
    font-size: var(--fs-lg);
    flex-shrink: 0;
    display: inline-block;
    transform: rotate(90deg);
    transition: transform 0.2s ease;
}
.zws-league-chevron.collapsed {
    transform: rotate(0deg);
}
.zws-row-chevron {
    color: var(--text-muted);
    font-size: 24px;
    line-height: 1;
    flex-shrink: 0;
}

/* ============================================================
   FAVOURITE STAR - pop + sparkle burst on favouriting, soft shrink
   on un-favouriting (teams.html, team.html). Triggered by
   zwsAnimateStar()/zwsSparkleBurst() in utils.js.
   ============================================================ */
.zws-star-wrap { position: relative; display: inline-flex; }
.zws-star.zws-star-pop { animation: zwsStarPop 0.5s cubic-bezier(.34,1.56,.64,1); }
.zws-star.zws-star-shrink { animation: zwsStarShrink 0.35s ease; }
@keyframes zwsStarPop {
    0%   { transform: scale(1); }
    40%  { transform: scale(1.45); }
    65%  { transform: scale(0.88); }
    100% { transform: scale(1); }
}
@keyframes zwsStarShrink {
    0%   { transform: scale(1); }
    50%  { transform: scale(0.72); }
    100% { transform: scale(1); }
}
.zws-spark {
    position: absolute;
    top: 50%; left: 50%;
    width: 4px; height: 4px;
    border-radius: 50%;
    background: var(--gold);
    opacity: 0;
    transform: translate(-50%, -50%);
    pointer-events: none;
}
.zws-spark.zws-spark-go {
    animation: zwsSparkBurst 0.5s ease-out forwards;
}
@keyframes zwsSparkBurst {
    0%   { opacity: 1; transform: translate(-50%, -50%) translate(0, 0) scale(0.4); }
    100% { opacity: 0; transform: translate(-50%, -50%) translate(var(--dx), var(--dy)) scale(1); }
}

/* ============================================================
   TAB SLIDER - shared gold-outline indicator that slides between
   tabs instead of the active state snapping instantly. Used by
   standings.html (Table/Top Scorers), teams.html (Teams/Leagues),
   team.html (Club Info/Squad/Season Stats) - all share the same
   .tabs/.tab markup. Managed by zwsInitTabSlider()/
   zwsPlaceTabIndicator() in utils.js.
   ============================================================ */
.zws-tab-indicator {
    position: absolute;
    top: 6px; bottom: 6px;
    border: 1.5px solid var(--gold);
    border-radius: var(--radius-full);
    transition: left 0.32s cubic-bezier(.4,0,.2,1), width 0.32s cubic-bezier(.4,0,.2,1);
    pointer-events: none;
    z-index: 1;
}

/* ============================================================
   RED CARD FLASH - fires when a fixture's red card count goes up
   between refreshes (matches.html, live.html). Same family as the
   goal-flash below, red-tinted so it doesn't read as a goal.
   ============================================================ */
.zws-fixture-row.goal-flash {
    animation: goalFlash 1.6s ease-out;
}
.zws-fixture-row.rc-flash {
    animation: rcFlash 1.6s ease-out;
}
@keyframes rcFlash {
    0%   { background: rgba(255, 82, 82, 0.22); }
    100% { background: transparent; }
}
.zws-rc-badge { display: inline-block; }
.zws-rc-badge.rc-pop {
    animation: rcBadgePop 0.6s cubic-bezier(.34,1.56,.64,1);
}
@keyframes rcBadgePop {
    0%   { transform: scale(0) rotate(0deg); }
    40%  { transform: scale(1.5) rotate(-10deg); }
    65%  { transform: scale(0.9) rotate(6deg); }
    100% { transform: scale(1) rotate(0deg); }
}

/* ============================================================
   NEWS BADGE - pop-in the first time it appears, a smaller bump if
   the count rises again while already visible (app-shell.js).
   ============================================================ */
.zws-nav-badge.zws-badge-pop-in { animation: zwsBadgePopIn 0.5s cubic-bezier(.34,1.56,.64,1); }
.zws-nav-badge.zws-badge-bump { animation: zwsBadgeBump 0.35s ease; }
@keyframes zwsBadgePopIn {
    0%   { transform: scale(0); }
    50%  { transform: scale(1.3); }
    75%  { transform: scale(0.9); }
    100% { transform: scale(1); }
}
@keyframes zwsBadgeBump {
    0%   { transform: scale(1); }
    40%  { transform: scale(1.25); }
    100% { transform: scale(1); }
}