@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@500;600;700&family=Inter:wght@400;500;600;700&family=Barlow+Condensed:wght@600;700&family=Caveat:wght@600;700&display=swap');

:root {
  --bg: #0a0b0f;
  --bg-panel: #14161d;
  --bg-panel-2: #1a1d26;
  --border: rgba(255, 255, 255, 0.09);
  --border-strong: rgba(255, 255, 255, 0.16);
  --text: #f5f6f8;
  --text-dim: #9aa1b0;
  --accent: #6366f1;
  --accent-2: #22d3ee;
  --gold: #f2b705;
  --success: #34d399;
  --danger: #f0555a;
  --radius: 16px;
  --font: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-display: "Space Grotesk", var(--font);
  --font-numeric: "Barlow Condensed", var(--font);
  --font-script: "Caveat", cursive;
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
}

* { box-sizing: border-box; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

html { color-scheme: dark; }

body {
  margin: 0;
  font-family: var(--font);
  background: var(--bg);
  color: var(--text);
  line-height: 1.6;
  font-size: 16px;
}

h1, h2, h3 { font-family: var(--font-display); letter-spacing: -0.01em; }

a { color: var(--accent-2); }

.icon { width: 1em; height: 1em; display: inline-block; vertical-align: -0.125em; stroke: currentColor; fill: none; stroke-width: 1.75; stroke-linecap: round; stroke-linejoin: round; }

.container {
  max-width: 1040px;
  margin: 0 auto;
  padding: 0 24px;
}

/* Animated mesh background */
.mesh-bg {
  position: fixed;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  background: var(--bg);
}
.mesh-bg::before,
.mesh-bg::after {
  content: "";
  position: absolute;
  width: 60vmax;
  height: 60vmax;
  border-radius: 50%;
  filter: blur(90px);
  opacity: 0.28;
  will-change: transform;
  animation: drift 22s ease-in-out infinite alternate;
}
.mesh-bg::before {
  background: var(--accent);
  top: -20%;
  left: -10%;
}
.mesh-bg::after {
  background: var(--gold);
  bottom: -25%;
  right: -15%;
  opacity: 0.16;
  animation-duration: 28s;
  animation-delay: -6s;
}
@keyframes drift {
  from { transform: translate(0, 0) scale(1); }
  to { transform: translate(6%, 8%) scale(1.15); }
}

header.site {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 18px 24px;
  background: rgba(10, 11, 15, 0.7);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--border);
}

.logo {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.15rem;
  letter-spacing: 0.01em;
  color: var(--text);
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.logo span { color: var(--accent-2); }

nav.site { display: flex; align-items: center; gap: 8px; }
nav.site a {
  color: var(--text-dim);
  text-decoration: none;
  padding: 8px 14px;
  border-radius: 999px;
  font-size: 0.92rem;
  font-weight: 500;
  transition: color 0.2s var(--ease-out), background 0.2s var(--ease-out);
}
nav.site a:hover { color: var(--text); background: rgba(255, 255, 255, 0.06); }

/* Hero */
.hero {
  padding: 120px 24px 72px;
  text-align: center;
  position: relative;
}
.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 16px;
  border-radius: 999px;
  border: 1px solid var(--border-strong);
  background: rgba(255, 255, 255, 0.04);
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--accent-2);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  margin-bottom: 24px;
}
.hero h1 {
  font-size: clamp(2.2rem, 5.5vw, 3.6rem);
  font-weight: 700;
  margin: 0 0 20px;
  line-height: 1.1;
}
.hero p.lead {
  color: var(--text-dim);
  font-size: 1.2rem;
  max-width: 620px;
  margin: 0 auto 36px;
}
.hero-actions { display: flex; gap: 14px; justify-content: center; flex-wrap: wrap; }

.btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 13px 26px;
  border-radius: 999px;
  background: linear-gradient(135deg, var(--accent), #818cf8);
  color: #fff;
  text-decoration: none;
  font-weight: 600;
  font-size: 0.98rem;
  border: none;
  cursor: pointer;
  transition: transform 0.2s var(--ease-out), box-shadow 0.2s var(--ease-out);
  box-shadow: 0 8px 24px rgba(99, 102, 241, 0.28);
}
.btn:hover { transform: translateY(-2px); box-shadow: 0 12px 28px rgba(99, 102, 241, 0.38); }
.btn:active { transform: translateY(0); }
.btn:focus-visible { outline: 2px solid var(--accent-2); outline-offset: 3px; }
.btn.secondary {
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--border-strong);
  color: var(--text);
  box-shadow: none;
}
.btn.secondary:hover { background: rgba(255, 255, 255, 0.08); box-shadow: none; }

.grid-3 {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20px;
  margin: 32px 0;
}
@media (max-width: 700px) {
  .grid-3 { grid-template-columns: 1fr; }
}

.card {
  background: linear-gradient(180deg, var(--bg-panel-2), var(--bg-panel));
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 26px;
  transition: transform 0.25s var(--ease-out), border-color 0.25s var(--ease-out), box-shadow 0.25s var(--ease-out);
}
.card:hover { transform: translateY(-4px); border-color: var(--border-strong); box-shadow: 0 16px 32px rgba(0, 0, 0, 0.35); }
.card .icon-badge {
  width: 44px;
  height: 44px;
  border-radius: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(99, 102, 241, 0.14);
  color: var(--accent-2);
  margin-bottom: 16px;
  font-size: 1.3rem;
}
.card h3 { margin: 0 0 8px; font-size: 1.1rem; }
.card p { color: var(--text-dim); margin-bottom: 0; font-size: 0.95rem; }

/* Reveal-on-scroll targets (progressively enhanced by GSAP; visible by default) */
[data-reveal] { opacity: 1; }
.js-anim [data-reveal] { opacity: 0; }

footer.site {
  text-align: center;
  color: var(--text-dim);
  font-size: 0.85rem;
  padding: 48px 24px 64px;
  border-top: 1px solid var(--border);
  margin-top: 40px;
}

/* Certificate page */
.cert-wrap {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}
.cert {
  width: 100%;
  max-width: 460px;
  background: linear-gradient(180deg, var(--bg-panel-2), var(--bg-panel));
  border: 1px solid var(--border-strong);
  border-radius: 24px;
  padding: 36px;
  position: relative;
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.45);
}
.cert::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 24px;
  padding: 1px;
  background: linear-gradient(135deg, rgba(99, 102, 241, 0.5), rgba(242, 183, 5, 0.35), transparent 60%);
  -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  pointer-events: none;
}
.cert-top { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.cert-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 13px;
  border-radius: 999px;
  font-size: 0.8rem;
  font-weight: 600;
}
.cert-badge.authentic { background: rgba(52, 211, 153, 0.15); color: var(--success); }
.cert-badge.verified { background: rgba(99, 102, 241, 0.18); color: #a5a8ff; }
.cert-badge.revoked { background: rgba(240, 85, 90, 0.15); color: var(--danger); }
.cert-seal { color: var(--gold); font-size: 1.5rem; line-height: 1; }

.cert h2 { margin: 20px 0 4px; font-size: 1.7rem; }
.cert .subtitle {
  color: var(--text-dim);
  margin-bottom: 26px;
  font-family: var(--font-numeric);
  font-size: 1.3rem;
  font-weight: 600;
  letter-spacing: 0.02em;
}

.cert-row {
  display: flex;
  justify-content: space-between;
  padding: 11px 0;
  border-bottom: 1px solid var(--border);
  font-size: 0.92rem;
}
.cert-row:last-child { border-bottom: none; }
.cert-row .label { color: var(--text-dim); }
.cert-row .value { font-weight: 600; text-align: right; }

.cert-sig {
  margin-top: 22px;
  display: flex;
  align-items: center;
  gap: 6px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.78rem;
  color: var(--text-dim);
  word-break: break-all;
}

.state-msg {
  text-align: center;
  color: var(--text-dim);
  padding: 60px 24px;
}

/* Brief "NFC erkannt" moment shown while a Player Card loads — the tap-a-
   tag instant is the whole point of NFC, worth its own beat rather than
   jumping straight to plain loading text. */
.nfc-detect {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  padding: 100px 24px;
  color: var(--text-dim);
  font-size: 0.9rem;
}
.nfc-detect-ring {
  width: 60px;
  height: 60px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--accent-2);
  border: 2px solid var(--accent-2);
  animation: nfc-pulse 1.4s ease-out infinite;
}
@keyframes nfc-pulse {
  0% { box-shadow: 0 0 0 0 rgba(34, 211, 238, 0.4); }
  70% { box-shadow: 0 0 0 14px rgba(34, 211, 238, 0); }
  100% { box-shadow: 0 0 0 0 rgba(34, 211, 238, 0); }
}
@media (prefers-reduced-motion: reduce) {
  .nfc-detect-ring { animation: none; }
}

/* Player Card — --team-accent/--team-accent-2 default to the playerID brand
   colors but are overridden inline per card from the club's own colors
   (clubs.primary_color/secondary_color), so the same template re-skins
   itself per team instead of needing a per-sport/per-league variant. */
/* Flip container — tap the card to see its reverse (barcode, HMAC hash,
   a digital-signature flourish). Front/back are both full .player-card
   elements stacked with 3D transforms; the front sits in normal flow (so
   it defines the container's height) and the back is absolutely
   positioned over it, pre-rotated 180deg, so only one face is ever
   "right side up" at a time. */
.player-card-flip {
  /* Defaults live here, not on .player-card — the flip wrapper is where
     the per-card inline theme override (--team-accent/-2, set from the
     club's colors) actually lands now, and a redeclaration on the child
     .player-card would shadow whatever it inherits, same-specificity own-
     property beating inherited regardless of the parent's inline style.
     Broke exactly this way when the flip wrapper was introduced: every
     card silently fell back to the site's own indigo/cyan instead of its
     club's colors. */
  --team-accent: var(--accent);
  --team-accent-2: var(--accent-2);
  width: 100%;
  max-width: 400px;
  perspective: 1800px;
}
.player-card-flip-inner {
  position: relative;
  width: 100%;
  cursor: pointer;
  transition: transform 0.7s cubic-bezier(0.4, 0.2, 0.2, 1);
  transform-style: preserve-3d;
}
.player-card-flip.is-flipped .player-card-flip-inner { transform: rotateY(180deg); }
@media (prefers-reduced-motion: reduce) {
  .player-card-flip-inner { transition: none; }
}

/* Subtle hover/focus tilt — a dedicated perspective wrapper around the
   card (perspective only affects children, so the tilted element must be
   a *child* of the element establishing it — it cannot tilt itself)
   rotates the card a few degrees on hover/focus, mimicking a physical
   card catching the light. CSS-only (:hover/:focus-within), no pointer-
   position tracking. Works around both .player-card-flip (live card) and
   bare .player-card (this marketing mockup, which doesn't use the flip). */
.player-card-tilt {
  width: 100%;
  max-width: 400px;
  margin: 0 auto;
  perspective: 1400px;
}
.player-card-tilt > .player-card-flip,
.player-card-tilt > .player-card {
  transition: transform 0.4s var(--ease-out);
  transform: rotateX(0deg) rotateY(0deg);
  will-change: transform;
}
.player-card-tilt:hover > .player-card-flip,
.player-card-tilt:hover > .player-card,
.player-card-tilt:focus-within > .player-card-flip,
.player-card-tilt:focus-within > .player-card {
  transform: rotateX(6deg) rotateY(-9deg) scale(1.015);
}
@media (prefers-reduced-motion: reduce) {
  .player-card-tilt:hover > .player-card-flip,
  .player-card-tilt:hover > .player-card,
  .player-card-tilt:focus-within > .player-card-flip,
  .player-card-tilt:focus-within > .player-card {
    transform: none;
  }
}
/* Glare sweep — brightens on the same hover/focus that drives the tilt, so
   the card reads as "catching the light" rather than just rotating. A real
   element (not a pseudo — .player-card's ::before/::after are already the
   border gradient and credential sweep), painted last/topmost via an
   explicit z-index so DOM-order stacking nuances elsewhere on the card
   can't bury it. */
.player-card-glare {
  position: absolute;
  inset: 0;
  z-index: 5;
  border-radius: inherit;
  background: radial-gradient(circle at 30% 15%, rgba(255, 255, 255, 0.35), transparent 55%);
  opacity: 0;
  transition: opacity 0.4s var(--ease-out);
  mix-blend-mode: overlay;
  pointer-events: none;
}
.player-card-tilt:hover .player-card-glare,
.player-card-tilt:focus-within .player-card-glare {
  opacity: 1;
}
@media (prefers-reduced-motion: reduce) {
  .player-card-glare { transition: none; }
}

.player-card-front, .player-card-back {
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
}
/* .player-card.player-card-back (both classes, not just .player-card-back
   alone) — needs to out-specificity .player-card's own "position:
   relative", which shares the same single-class specificity and comes
   later in the cascade, so it was winning and leaving the back face in
   normal flow below the front instead of overlaid on top of it. */
.player-card.player-card-back {
  position: absolute;
  inset: 0;
  transform: rotateY(180deg);
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}
.player-card {
  width: 100%;
  max-width: 400px;
  background: linear-gradient(165deg, var(--bg-panel-2), var(--bg-panel) 60%);
  border: 1px solid var(--border-strong);
  border-radius: 24px;
  padding: 22px 30px 20px 22px;
  position: relative;
  box-shadow: 0 28px 70px rgba(0, 0, 0, 0.5);
  overflow: hidden;
}
/* Foil finish for PLAYER-original cards — a slow-drifting diagonal sheen so
   the rarer "this exact one was the player's own" instance reads as
   visually distinct from a FAN copy at a glance, no label-reading needed.
   A real element, not a pseudo-element — both ::before (border gradient)
   and ::after (diagonal sweep) on .player-card are already spoken for. */
.player-card-foil-sheen {
  position: absolute;
  inset: 0;
  z-index: 3;
  border-radius: 24px;
  background: linear-gradient(
    115deg,
    transparent 20%,
    rgba(255, 255, 255, 0.14) 34%,
    rgba(255, 255, 255, 0.32) 40%,
    rgba(255, 255, 255, 0.14) 46%,
    transparent 60%
  );
  background-size: 220% 220%;
  background-position: 0% 0%;
  mix-blend-mode: overlay;
  pointer-events: none;
  animation: foil-drift 6s ease-in-out infinite;
}
@keyframes foil-drift {
  0%, 100% { background-position: 0% 0%; }
  50% { background-position: 100% 100%; }
}
@media (prefers-reduced-motion: reduce) {
  .player-card-foil-sheen { animation: none; background-position: 40% 40%; }
}

/* FAN vs PLAYER rim — both variants get the identical layout/typography
   (no "cheaper" version), only the card's material finish differs so a
   "Spieler-Original" (PLAYER) reads as the rarer piece at a glance next to
   a FAN edition. PLAYER pairs with the existing .player-card-foil-sheen
   element for a brighter, more metallic border; FAN gets a calmer, matte
   border. Two-class selector (.player-card.player-card-owner-*), same
   pattern already used for .player-card.player-card-back. */
.player-card.player-card-owner-player::before {
  opacity: 0.9;
  background: linear-gradient(
    140deg,
    #ffffff 0%,
    var(--team-accent) 30%,
    var(--team-accent-2) 65%,
    rgba(255, 255, 255, 0.65) 100%
  );
}
.player-card.player-card-owner-player {
  box-shadow: 0 28px 70px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(255, 255, 255, 0.06) inset;
}
.player-card.player-card-owner-fan::before {
  opacity: 0.4;
  filter: saturate(0.7);
}

/* Seasonal design variant ribbon (item 16) — a sellable premium look for a
   special occasion (playoffs, club anniversary) layered onto the same
   underlying card, not a separate template. */
.player-card-ribbon {
  position: absolute;
  top: 16px;
  left: -38px;
  transform: rotate(-45deg);
  width: 150px;
  text-align: center;
  padding: 4px 0;
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  z-index: 4;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.35);
  pointer-events: none;
}
.player-card-ribbon.variant-playoff { background: linear-gradient(90deg, #38bdf8, #0ea5e9); color: #04121c; }
.player-card-ribbon.variant-anniversary { background: linear-gradient(90deg, #f2b705, #d4af37); color: #241900; }

.player-card::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 24px;
  padding: 1px;
  background: linear-gradient(140deg, var(--team-accent) 0%, var(--team-accent-2) 45%, transparent 85%);
  opacity: 0.65;
  -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  pointer-events: none;
}
/* Credential-badge sweep — a soft diagonal wash of the team's own colors
   behind the header, evoking an access-badge card without copying any
   specific product's layout, logo, or trade dress. */
.player-card::after {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 175px;
  background: linear-gradient(120deg, var(--team-accent) -20%, transparent 65%);
  opacity: 0.24;
  border-radius: 24px 24px 0 0;
  pointer-events: none;
}
/* Vertical accent strip along the right edge — an original ID-strip
   treatment (own gradient, own tick pattern, own position) evoking the
   credential-card genre without reproducing any specific card's barcode,
   logo, or layout. Purely decorative, not a real/scannable barcode. */
.player-card-vstrip {
  position: absolute;
  top: 0; right: 0; bottom: 0;
  width: 11px;
  background: linear-gradient(180deg, var(--team-accent), var(--team-accent-2));
  opacity: 0.6;
}
/* Authentic-reading barcode texture — irregular bar widths over a long,
   non-obvious repeat cycle (a real barcode never reads as a neat 3-bar
   loop), alternating dark and light bars for real contrast against the
   color gradient underneath rather than just gaps in one dark tone. */
.player-card-vstrip::after {
  content: "";
  position: absolute;
  inset: 0;
  background-image: repeating-linear-gradient(
    0deg,
    rgba(0, 0, 0, 0.55) 0 2px, transparent 2px 5px,
    rgba(255, 255, 255, 0.45) 5px 6px, transparent 6px 11px,
    rgba(0, 0, 0, 0.55) 11px 15px, transparent 15px 18px,
    rgba(255, 255, 255, 0.45) 18px 19px, transparent 19px 23px,
    rgba(0, 0, 0, 0.55) 23px 24px, transparent 24px 29px,
    rgba(255, 255, 255, 0.45) 29px 32px, transparent 32px 36px,
    rgba(0, 0, 0, 0.55) 36px 38px, transparent 38px 44px,
    rgba(255, 255, 255, 0.45) 44px 45px, transparent 45px 50px
  );
}
.player-card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 14px;
  position: relative;
}
.player-card-photo {
  width: 104px;
  height: 104px;
  border-radius: 18px;
  object-fit: cover;
  background: var(--bg-panel-2);
  border: 2px solid var(--border-strong);
  flex-shrink: 0;
}
.player-card-photo-placeholder {
  width: 104px;
  height: 104px;
  border-radius: 18px;
  background: var(--bg-panel-2);
  border: 2px solid var(--border-strong);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-dim);
  flex-shrink: 0;
}
.player-card-crest-col {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 8px;
}
/* Crest medallion — a light plate behind the logo (most real club crests
   pulled from a league site are dark/monochrome artwork on a transparent
   PNG, e.g. EC Jakling's is a plain black silhouette; flat on the card's
   dark panel it reads as invisible) reframed as an actual medallion
   instead of a flat circle stuck on top: a thin two-tone ring in the
   team's own colors (ties the crest into the same accent language as the
   vstrip/subband/bottombar rather than sitting apart from it), a glass
   highlight arc for dimensionality, and a two-layer shadow (tight contact
   + soft ambient) instead of one flat drop shadow. Fixes every logo
   regardless of its own colors — no per-logo luminance detection needed
   (fragile: a club's primary_color is not reliably the same as its crest
   image's actual pixel colors, proven wrong for EC Jakling, whose brand
   color is gold but whose crest PNG is pure black). */
.player-card-crest-badge {
  width: 136px;
  height: 136px;
  border-radius: 50%;
  flex-shrink: 0;
  box-sizing: border-box;
  padding: 3px;
  position: relative;
  background: conic-gradient(from 215deg, var(--team-accent), var(--team-accent-2), var(--team-accent));
  box-shadow:
    0 10px 22px rgba(0, 0, 0, 0.42),
    0 2px 6px rgba(0, 0, 0, 0.35);
}
/* Ambient glow behind the medallion, in the club's own accent — reads as
   the badge casting color onto the card rather than sitting on top of it.
   Contained (small inset/blur) so it doesn't clip against .player-card's
   overflow:hidden edge when the badge sits near the card corner. */
.player-card-crest-badge::before {
  content: "";
  position: absolute;
  inset: -10px;
  border-radius: 50%;
  background: radial-gradient(circle, var(--team-accent) 0%, transparent 70%);
  opacity: 0.45;
  filter: blur(11px);
  z-index: -1;
  pointer-events: none;
}
.player-card-crest-badge::after {
  content: "";
  position: absolute;
  inset: 3px;
  border-radius: 50%;
  background: radial-gradient(circle at 32% 24%, rgba(255, 255, 255, 0.55), transparent 45%);
  pointer-events: none;
  z-index: 1;
}
/* Logo plate — replaces a flat opaque white disc (read as a cheap sticker)
   with a pearlescent glass tinted by the club's own colors: a bright
   radial highlight blended over the team gradient via soft-light stays
   light enough to keep a pure-black silhouette logo (e.g. EC Jakling's
   crest) legible, while never being neutral/generic white — every club's
   badge reads as its own color, not the same plate reused. Inset shadows
   (contact shade + top sheen + hairline rim) give it a pressed-medallion
   dimensionality a flat gradient fill can't. */
.player-card-club-logo {
  width: 100%;
  height: 100%;
  object-fit: contain;
  box-sizing: border-box;
  padding: 16px;
  border-radius: 50%;
  display: block;
  background:
    radial-gradient(circle at 32% 26%, rgba(255, 255, 255, 0.98) 0%, rgba(255, 255, 255, 0.86) 48%, rgba(255, 255, 255, 0.74) 100%),
    linear-gradient(155deg, var(--team-accent), var(--team-accent-2));
  background-blend-mode: soft-light;
  box-shadow:
    inset 0 2px 5px rgba(0, 0, 0, 0.22),
    inset 0 -3px 6px rgba(255, 255, 255, 0.45),
    inset 0 0 0 1px rgba(255, 255, 255, 0.4);
}
.player-card-crest-fallback {
  width: 100%;
  height: 100%;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--team-accent);
  background: radial-gradient(circle at 38% 30%, var(--bg-panel-2), var(--bg-panel) 75%);
}
.player-card-crest-fallback .icon { width: 56px; height: 56px; }
/* Wappen-Wasserzeichen — large, near-invisible logo bleeding off the card
   behind the title/name zone, additive to (not a replacement for) the
   crest medallion badge above: lets the card surface itself carry club
   identity instead of only the small header badge. First child in the DOM
   (see index.html) so it paints behind the vstrip/text content that
   already rely on DOM-order stacking (see the .player-card-vstrip and
   text position:relative comments below/above). */
.player-card-watermark {
  position: absolute;
  top: 120px;
  right: -36px;
  width: 240px;
  height: 240px;
  object-fit: contain;
  opacity: 0.06;
  filter: grayscale(1) brightness(1.6);
  mix-blend-mode: overlay;
  pointer-events: none;
}
.player-card-titleband {
  font-family: var(--font-display);
  font-size: 1.02rem;
  letter-spacing: 0.01em;
  margin-bottom: 7px;
  position: relative;
}
.player-card-titleband .strong { font-weight: 700; color: var(--text); }
.player-card-titleband .light { font-weight: 500; color: var(--text-dim); }
/* Scrim-plate, not a team-color fill: color-mix() keeps --bg (near-black)
   as the dominant component (80%+) regardless of how light or dark the
   given team-accent is, so text painted in the fixed --text/--text-dim
   tokens stays WCAG AA-safe on every club's colors — a robust, common
   trading-card technique, not a fallback. The team color still shows up
   as a border/tint, just never as the text's own background field. */
.player-card-subband {
  display: inline-block;
  font-size: 0.64rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text);
  background: color-mix(in srgb, var(--bg) 80%, var(--team-accent) 20%);
  border: 1px solid color-mix(in srgb, var(--team-accent) 50%, transparent);
  padding: 3px 10px;
  border-radius: 4px;
  margin-bottom: 14px;
  position: relative;
}
.player-card-name {
  font-family: var(--font-display);
  font-size: clamp(1.65rem, 7vw, 2.15rem);
  font-weight: 700;
  line-height: 1.08;
  letter-spacing: -0.01em;
  text-wrap: balance;
  margin: 0 0 3px;
  position: relative;
}
.player-card-meta {
  color: var(--text-dim);
  font-family: var(--font-numeric);
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  margin-bottom: 3px;
  position: relative;
}
.player-card-vitals {
  color: var(--text-dim);
  font-size: 0.72rem;
  margin-bottom: 14px;
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.player-card-divider {
  height: 1px;
  background: linear-gradient(90deg, var(--border-strong), transparent 75%);
  margin: 0 0 16px;
  position: relative;
}
.stat-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 8px;
  margin-bottom: 20px;
}
.stat-tile {
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid var(--border);
  border-top: 2px solid var(--team-accent);
  border-radius: 12px;
  padding: 10px 4px;
  text-align: center;
}
.stat-tile .num {
  font-family: var(--font-numeric);
  font-variant-numeric: tabular-nums;
  font-size: 1.4rem;
  font-weight: 700;
  color: var(--text);
  line-height: 1.1;
}
.stat-tile .label {
  font-size: 0.68rem;
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.stat-note {
  text-align: center;
  color: var(--text-dim);
  font-size: 0.78rem;
  margin: -10px 0 20px;
}
.cert-badge-block {
  display: flex;
  align-items: center;
  gap: 14px;
  position: relative;
  background: linear-gradient(135deg, rgba(242, 183, 5, 0.14), rgba(242, 183, 5, 0.02) 65%);
  border: 1px solid rgba(242, 183, 5, 0.35);
  border-radius: 16px;
  padding: 14px 16px;
  overflow: hidden;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06);
}
.cert-badge-block::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(115deg, transparent 30%, rgba(255, 255, 255, 0.09) 45%, transparent 60%);
  pointer-events: none;
}
.cert-badge-block .cert-badge-icon {
  width: 42px;
  height: 42px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: radial-gradient(circle at 35% 30%, rgba(242, 183, 5, 0.4), rgba(242, 183, 5, 0.05) 72%);
  border: 1px solid rgba(242, 183, 5, 0.55);
  color: var(--gold);
  flex-shrink: 0;
}
.cert-badge-block .cert-number {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-weight: 700;
  font-size: 0.98rem;
  letter-spacing: 0.03em;
  color: var(--text);
}
.cert-badge-block .cert-label {
  font-size: 0.72rem;
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.cert-badge-block.revoked {
  background: linear-gradient(135deg, rgba(240, 85, 90, 0.14), rgba(240, 85, 90, 0.02) 65%);
  border-color: rgba(240, 85, 90, 0.35);
}
.cert-badge-block.revoked .cert-badge-icon {
  background: radial-gradient(circle at 35% 30%, rgba(240, 85, 90, 0.4), rgba(240, 85, 90, 0.05) 72%);
  border-color: rgba(240, 85, 90, 0.55);
  color: var(--danger);
}
.nfc-pill {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 0.7rem;
  color: var(--accent-2);
  margin-top: 8px;
}
.fulfillment-chain {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 16px;
  font-size: 0.72rem;
}
.fulfillment-step {
  flex: 1;
  padding: 6px 10px;
  border-radius: 8px;
  border: 1px solid var(--border);
  color: var(--text-dim);
  text-align: center;
  position: relative;
}
.fulfillment-step.done {
  border-color: var(--accent-2);
  color: var(--accent-2);
}
.fulfillment-step span {
  display: block;
  font-size: 0.62rem;
  opacity: 0.8;
  margin-top: 2px;
}
.fulfillment-step:not(:last-child)::after {
  content: '→';
  position: absolute;
  right: -13px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--text-dim);
}
.player-card-badges {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 10px;
}
.player-card-badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 0.66rem;
  font-weight: 600;
  padding: 4px 9px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--team-accent, var(--accent-2)) 18%, transparent);
  border: 1px solid color-mix(in srgb, var(--team-accent, var(--accent-2)) 45%, transparent);
  color: var(--text);
  white-space: nowrap;
}
.install-hint {
  text-align: center;
  margin-top: 16px;
}
.install-hint button {
  background: none;
  border: none;
  color: var(--accent-2);
  font-size: 0.82rem;
  cursor: pointer;
  text-decoration: underline;
  padding: 4px;
}
.player-card-footnote {
  font-size: 0.68rem;
  font-style: italic;
  color: var(--text-dim);
  opacity: 0.75;
  margin: 16px 0 14px;
  position: relative;
}
/* Bottom identity bar — team color, own wordmark, distinct from and much
   slimmer than any reference card's bottom bar. */
.player-card-bottombar {
  margin: 0 -30px -20px -22px;
  padding: 10px 22px 10px 22px;
  /* Same scrim-plate logic as .player-card-subband above — the fill stays
     dominated by --bg so --text on top is always readable; the team
     gradient now lives in the top border instead of the whole fill. */
  background: linear-gradient(
    90deg,
    color-mix(in srgb, var(--bg) 82%, var(--team-accent) 18%),
    color-mix(in srgb, var(--bg) 82%, var(--team-accent-2) 18%)
  );
  border-top: 1px solid color-mix(in srgb, var(--team-accent) 45%, transparent);
  color: var(--text);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  display: flex;
  align-items: center;
  justify-content: space-between;
  position: relative;
}

/* Card back face — original content, not a copy of any physical card's
   reverse: a seal, a cursive rendering of the player's own name standing
   in for a signature, the full HMAC hash, a big horizontal barcode band,
   and a plain-language note on what the NFC binding actually means. */
.player-card-back-seal {
  width: 72px;
  height: 72px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--team-accent);
  background: radial-gradient(circle at 35% 30%, rgba(255, 255, 255, 0.1), transparent 70%);
  border: 1.5px solid var(--team-accent);
  margin: 28px 0 6px;
  flex-shrink: 0;
}
.player-card-back-signature {
  font-family: var(--font-script);
  font-size: 2.1rem;
  font-weight: 700;
  color: var(--text);
  line-height: 1.1;
  margin: 4px 0 2px;
  max-width: 100%;
  overflow-wrap: break-word;
}
.player-card-back-signature-label {
  font-size: 0.62rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-dim);
  margin-bottom: 18px;
}
.player-card-back-hash {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.7rem;
  color: var(--text-dim);
  word-break: break-all;
  padding: 0 8px;
  margin-bottom: 20px;
}
.player-card-back-barcode {
  width: 100%;
  height: 34px;
  margin-bottom: 20px;
  background-image: repeating-linear-gradient(
    90deg,
    var(--text) 0 1px, transparent 1px 3px,
    var(--text) 3px 5px, transparent 5px 6px,
    var(--text) 6px 8px, transparent 8px 12px,
    var(--text) 12px 13px, transparent 13px 16px,
    var(--text) 16px 19px, transparent 19px 22px,
    var(--text) 22px 23px, transparent 23px 27px,
    var(--text) 27px 30px, transparent 30px 34px
  );
  opacity: 0.7;
}
.player-card-back-legal {
  font-size: 0.72rem;
  color: var(--text-dim);
  line-height: 1.5;
  padding: 0 6px;
  margin-bottom: auto;
}
.player-card-back-hint {
  font-size: 0.68rem;
  color: var(--team-accent-2);
  letter-spacing: 0.04em;
  margin-top: 16px;
  margin-bottom: 4px;
}

/* Curated color-preset picker (admin) — swatch pairs pre-checked for
   contrast/harmony, so a new club starts from a good combination instead
   of two independently-chosen hex values that can clash (exactly what
   happened before this existed: EC Jakling shipped with a copy-pasted
   pair). "Eigene Farben" still opens the raw pickers for full control. */
.color-preset-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 8px;
  margin: 8px 0 14px;
}
.color-preset-swatch {
  height: 40px;
  border-radius: 8px;
  border: 2px solid transparent;
  cursor: pointer;
  padding: 0;
}
.color-preset-swatch.is-selected { border-color: var(--text); }
.color-preset-preview {
  margin: -4px 0 14px;
  padding: 6px 12px;
  border-radius: 4px;
  font-size: 0.64rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-align: center;
  color: #0a0b0f;
  transition: background 0.2s;
}

/* Public club page (/v/{code}) — items 15+19: a micro-landingpage that
   doubles as an embeddable gallery widget (an iframe just points at this
   URL, no separate embed mode needed). Only shows is_public cards — the
   full roster is never listed here, same closed-access rule as a single
   card's public preview. */
.club-page {
  --team-accent: var(--accent);
  --team-accent-2: var(--accent-2);
  width: 100%;
  max-width: 780px;
  margin: 0 auto;
}
.club-page-head {
  display: flex;
  align-items: center;
  gap: 20px;
  margin-bottom: 24px;
}
.club-page-stats {
  display: flex;
  gap: 28px;
  margin-bottom: 20px;
}
.club-page-stats strong {
  display: block;
  font-family: var(--font-numeric);
  font-size: 1.6rem;
  color: var(--team-accent-2);
}
.club-page-stats span {
  font-size: 0.78rem;
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.club-gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
  gap: 14px;
}
.club-gallery-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  text-decoration: none;
  color: var(--text);
  font-size: 0.78rem;
  text-align: center;
  padding: 12px 8px;
  border-radius: 14px;
  background: var(--bg-panel);
  border: 1px solid var(--border);
  transition: border-color 0.2s;
}
.club-gallery-card:hover { border-color: var(--team-accent); }
.club-gallery-card img, .club-gallery-card-placeholder {
  width: 64px;
  height: 64px;
  border-radius: 50%;
  object-fit: cover;
  background: var(--bg-panel-2);
  border: 2px solid var(--border-strong);
}

/* Forms */
.form-wrap {
  max-width: 440px;
  margin: 0 auto;
  padding: 100px 24px 60px;
}
.form-wrap h1 { font-size: 1.7rem; margin-bottom: 6px; }
label {
  display: block;
  margin: 18px 0 7px;
  font-size: 0.9rem;
  font-weight: 500;
  color: var(--text-dim);
}
input, select {
  width: 100%;
  padding: 12px 14px;
  border-radius: 10px;
  border: 1px solid var(--border-strong);
  background: rgba(255, 255, 255, 0.03);
  color: var(--text);
  font-size: 1rem;
  font-family: var(--font);
  transition: border-color 0.2s var(--ease-out), background 0.2s var(--ease-out);
}
input:focus, select:focus {
  outline: none;
  border-color: var(--accent);
  background: rgba(99, 102, 241, 0.06);
}
.msg { margin-top: 16px; font-size: 0.9rem; display: flex; align-items: center; gap: 6px; }
.msg.error { color: var(--danger); }
.msg.ok { color: var(--success); }

/* Admin */
.admin-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 20px;
  margin: 24px 0;
}
@media (max-width: 800px) {
  .admin-grid { grid-template-columns: 1fr; }
}
table { width: 100%; border-collapse: collapse; font-size: 0.88rem; }
th, td { text-align: left; padding: 10px; border-bottom: 1px solid var(--border); }
th { color: var(--text-dim); font-weight: 600; font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.04em; }
tbody tr { transition: background 0.15s var(--ease-out); }
tbody tr:hover { background: rgba(255, 255, 255, 0.03); }
.table-wrap { max-height: 340px; overflow-y: auto; border: 1px solid var(--border); border-radius: 12px; }
.badge-count {
  display: inline-block;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--text-dim);
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 3px 10px;
}
.type-badge {
  display: inline-block;
  font-size: 0.76rem;
  font-weight: 600;
  padding: 3px 9px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.05);
  color: var(--text-dim);
}
.type-badge-player {
  background: rgba(242, 183, 5, 0.14);
  color: var(--gold);
}

.section-title {
  font-size: 1.7rem;
  margin: 8px 0 4px;
}
.section-sub { color: var(--text-dim); margin: 0 0 8px; }
