const { useState, useRef, useEffect } = React;

const RARITY = {
  common: { label: "Common", color: "#8b9daf", glow: "rgba(139,157,175,0.4)", bg: "linear-gradient(135deg, #2a3040 0%, #1e2530 100%)" },
  uncommon: { label: "Uncommon", color: "#4ade80", glow: "rgba(74,222,128,0.4)", bg: "linear-gradient(135deg, #1a3028 0%, #162420 100%)" },
  rare: { label: "Rare", color: "#60a5fa", glow: "rgba(96,165,250,0.5)", bg: "linear-gradient(135deg, #1a2540 0%, #141e35 100%)" },
  epic: { label: "Epic", color: "#c084fc", glow: "rgba(192,132,252,0.5)", bg: "linear-gradient(135deg, #2a1a40 0%, #201530 100%)" },
  legendary: { label: "Legendary", color: "#fbbf24", glow: "rgba(251,191,36,0.6)", bg: "linear-gradient(135deg, #3a2a10 0%, #2a1e0a 100%)" },
};

const CATEGORIES = {
  all: "All",
  linear: "Linear",
  conv: "Convolution",
  norm: "Normalization",
  attention: "Attention",
  activation: "Activation",
  regularization: "Regularization",
  pooling: "Pooling",
  recurrent: "Recurrent",
  embedding: "Embedding",
};

const CARDS = [
  {
    id: "linear",
    name: "Linear",
    aka: "Dense / Fully Connected",
    category: "linear",
    rarity: "common",
    year: 1958,
    icon: "⟶",
    stats: { power: 5, speed: 9, versatility: 8, efficiency: 7 },
    flavor: "The workhorse. Every journey through a network begins and ends here.",
    description: "Applies a learned affine transformation y = xW^T + b. The fundamental building block mapping inputs to outputs through learned weights.",
    paper: "Rosenblatt, 1958",
    code: `nn.Linear(in_features=768, out_features=256)`,
    params: "in × out + out (bias)",
    complexity: "O(in × out)",
  },
  {
    id: "conv2d",
    name: "Conv2D",
    aka: "2D Convolution",
    category: "conv",
    rarity: "common",
    year: 1989,
    icon: "▦",
    stats: { power: 7, speed: 7, versatility: 6, efficiency: 8 },
    flavor: "Slides across pixels, finding edges, textures, and meaning in the noise.",
    description: "Applies learnable filters that slide across 2D input, computing local dot products. Exploits spatial locality and translation equivariance.",
    paper: "LeCun et al., 1989",
    code: `nn.Conv2d(in_channels=3, out_channels=64,\n  kernel_size=3, padding=1)`,
    params: "out × in × k × k + out",
    complexity: "O(out × in × k² × H × W)",
  },
  {
    id: "conv1d",
    name: "Conv1D",
    aka: "1D Convolution / Temporal Conv",
    category: "conv",
    rarity: "common",
    year: 1989,
    icon: "▬",
    stats: { power: 5, speed: 8, versatility: 6, efficiency: 8 },
    flavor: "Time is just another dimension to conquer.",
    description: "1D convolution over temporal or sequential data. Useful for audio, text, and time series where local patterns matter.",
    paper: "LeCun et al., 1989",
    code: `nn.Conv1d(in_channels=128, out_channels=256,\n  kernel_size=3, padding=1)`,
    params: "out × in × k + out",
    complexity: "O(out × in × k × L)",
  },
  {
    id: "depthwise_conv",
    name: "Depthwise Conv",
    aka: "Depthwise Separable",
    category: "conv",
    rarity: "uncommon",
    year: 2017,
    icon: "▤",
    stats: { power: 5, speed: 9, versatility: 5, efficiency: 10 },
    flavor: "Why mix channels when you can save 90% of the compute?",
    description: "Applies a single filter per input channel, then uses 1×1 pointwise conv to mix channels. Dramatically reduces parameters and FLOPs.",
    paper: "Howard et al., MobileNets 2017",
    code: `nn.Conv2d(channels, channels,\n  kernel_size=3, groups=channels)\n# + nn.Conv2d(channels, out, 1)`,
    params: "in × k² + in × out",
    complexity: "O(in × k² × H × W + in × out × H × W)",
  },
  {
    id: "dilated_conv",
    name: "Dilated Conv",
    aka: "Atrous Convolution",
    category: "conv",
    rarity: "uncommon",
    year: 2016,
    icon: "▥",
    stats: { power: 6, speed: 7, versatility: 6, efficiency: 7 },
    flavor: "See farther without growing larger — gaps become your superpower.",
    description: "Inserts gaps (dilation) between kernel elements to exponentially expand receptive field without increasing parameters. Key in WaveNet and DeepLab.",
    paper: "Yu & Koltun, 2016",
    code: `nn.Conv1d(256, 256, kernel_size=3,\n  dilation=4, padding=4)`,
    params: "Same as standard conv",
    complexity: "Same FLOPs, larger receptive field",
  },
  {
    id: "transposed_conv",
    name: "Transposed Conv",
    aka: "Deconvolution / Upconv",
    category: "conv",
    rarity: "uncommon",
    year: 2014,
    icon: "▧",
    stats: { power: 6, speed: 6, versatility: 5, efficiency: 5 },
    flavor: "What was compressed shall be restored — pixels reborn from features.",
    description: "Learnable upsampling that reverses the spatial reduction of convolution. Used in generators (GANs), decoders, and segmentation networks.",
    paper: "Zeiler et al., 2010 / Radford et al., DCGAN 2015",
    code: `nn.ConvTranspose2d(256, 128,\n  kernel_size=4, stride=2, padding=1)`,
    params: "in × out × k × k + out",
    complexity: "O(in × out × k² × H_out × W_out)",
  },
  {
    id: "batchnorm",
    name: "BatchNorm",
    aka: "Batch Normalization",
    category: "norm",
    rarity: "common",
    year: 2015,
    icon: "≋",
    stats: { power: 4, speed: 8, versatility: 7, efficiency: 9 },
    flavor: "Taming the internal covariate shift, one mini-batch at a time.",
    description: "Normalizes activations across the batch dimension, then applies learned scale (γ) and shift (β). Stabilizes and accelerates training.",
    paper: "Ioffe & Szegedy, 2015",
    code: `nn.BatchNorm2d(num_features=256)`,
    params: "2 × features (γ, β)",
    complexity: "O(N × features)",
  },
  {
    id: "layernorm",
    name: "LayerNorm",
    aka: "Layer Normalization",
    category: "norm",
    rarity: "uncommon",
    year: 2016,
    icon: "≡",
    stats: { power: 5, speed: 7, versatility: 9, efficiency: 8 },
    flavor: "Batch-size agnostic. The Transformer's best friend.",
    description: "Normalizes across the feature dimension for each sample independently. Essential in Transformers since it doesn't depend on batch statistics.",
    paper: "Ba et al., 2016",
    code: `nn.LayerNorm(normalized_shape=768)`,
    params: "2 × features (γ, β)",
    complexity: "O(features)",
  },
  {
    id: "groupnorm",
    name: "GroupNorm",
    aka: "Group Normalization",
    category: "norm",
    rarity: "uncommon",
    year: 2018,
    icon: "≣",
    stats: { power: 4, speed: 7, versatility: 7, efficiency: 7 },
    flavor: "When your batch is too small for BatchNorm but too big for InstanceNorm.",
    description: "Divides channels into groups and normalizes within each group. Works well with small batch sizes where BatchNorm degrades.",
    paper: "Wu & He, 2018",
    code: `nn.GroupNorm(num_groups=32,\n  num_channels=256)`,
    params: "2 × channels",
    complexity: "O(channels × spatial)",
  },
  {
    id: "rmsnorm",
    name: "RMSNorm",
    aka: "Root Mean Square Norm",
    category: "norm",
    rarity: "rare",
    year: 2019,
    icon: "√",
    stats: { power: 5, speed: 9, versatility: 7, efficiency: 9 },
    flavor: "LayerNorm's faster sibling — skip the mean, keep the magic.",
    description: "Simplifies LayerNorm by removing the mean-centering step, normalizing by RMS only. Used in LLaMA, Gemma, and modern LLMs for its speed advantage.",
    paper: "Zhang & Sennrich, 2019",
    code: `# PyTorch 2.4+\nnn.RMSNorm(normalized_shape=4096)`,
    params: "features (γ only)",
    complexity: "O(features)",
  },
  {
    id: "mha",
    name: "Multi-Head Attention",
    aka: "MHA / Self-Attention",
    category: "attention",
    rarity: "epic",
    year: 2017,
    icon: "◈",
    stats: { power: 10, speed: 4, versatility: 10, efficiency: 3 },
    flavor: "Attention is all you need. No, really. That's the whole paper.",
    description: "Projects input into Q, K, V across multiple heads, computes scaled dot-product attention in parallel, then concatenates. The engine of the Transformer revolution.",
    paper: "Vaswani et al., 2017",
    code: `nn.MultiheadAttention(\n  embed_dim=768, num_heads=12)`,
    params: "4 × d² (Q,K,V,O projections)",
    complexity: "O(n² × d) — quadratic in sequence length",
  },
  {
    id: "gqa",
    name: "Grouped Query Attention",
    aka: "GQA",
    category: "attention",
    rarity: "rare",
    year: 2023,
    icon: "◇",
    stats: { power: 9, speed: 7, versatility: 8, efficiency: 7 },
    flavor: "Share some keys, keep your queries — the sweet spot between MHA and MQA.",
    description: "Groups query heads to share key-value heads. Interpolates between MHA (all unique) and MQA (one shared KV). Used in LLaMA 2, Mistral.",
    paper: "Ainslie et al., 2023",
    code: `# num_kv_heads < num_heads\nGQA(dim=4096, num_heads=32,\n  num_kv_heads=8)`,
    params: "(heads + 2×kv_groups) × d × d_head",
    complexity: "O(n² × d) but smaller KV cache",
  },
  {
    id: "flash_attn",
    name: "Flash Attention",
    aka: "IO-Aware Attention",
    category: "attention",
    rarity: "legendary",
    year: 2022,
    icon: "⚡",
    stats: { power: 10, speed: 10, versatility: 8, efficiency: 10 },
    flavor: "Same math, 10× faster. Memory hierarchy is the final boss.",
    description: "Exact attention computed via tiling and kernel fusion to minimize HBM reads/writes. No approximation — just better hardware utilization. Changed what sequence lengths are practical.",
    paper: "Dao et al., 2022",
    code: `from flash_attn import flash_attn_func\nflash_attn_func(q, k, v, causal=True)`,
    params: "Same as standard attention",
    complexity: "O(n² × d) compute, O(n) memory",
  },
  {
    id: "cross_attn",
    name: "Cross Attention",
    aka: "Encoder-Decoder Attention",
    category: "attention",
    rarity: "rare",
    year: 2017,
    icon: "✦",
    stats: { power: 8, speed: 5, versatility: 9, efficiency: 5 },
    flavor: "Two worlds collide — queries from one, keys from another.",
    description: "Queries come from one sequence, keys and values from another. Bridges encoder and decoder in seq2seq, or fuses modalities (text + image) in multimodal models.",
    paper: "Vaswani et al., 2017",
    code: `nn.MultiheadAttention(embed_dim=768,\n  num_heads=12)  # Q from dec, KV from enc`,
    params: "4 × d²",
    complexity: "O(n_q × n_kv × d)",
  },
  {
    id: "relu",
    name: "ReLU",
    aka: "Rectified Linear Unit",
    category: "activation",
    rarity: "common",
    year: 2010,
    icon: "⌐",
    stats: { power: 4, speed: 10, versatility: 7, efficiency: 10 },
    flavor: "If it's negative, it's dead to me.",
    description: "f(x) = max(0, x). Sparse activation, cheap to compute, and broke the vanishing gradient curse. The activation that launched deep learning.",
    paper: "Nair & Hinton, 2010",
    code: `nn.ReLU()`,
    params: "0",
    complexity: "O(1) per element",
  },
  {
    id: "gelu",
    name: "GELU",
    aka: "Gaussian Error Linear Unit",
    category: "activation",
    rarity: "uncommon",
    year: 2016,
    icon: "∿",
    stats: { power: 6, speed: 8, versatility: 8, efficiency: 7 },
    flavor: "Smooth like a Gaussian, sharp like a ReLU. The best of both worlds.",
    description: "x × Φ(x) — weighs inputs by how likely they are to be positive under a Gaussian. Default in BERT, GPT, and most modern Transformers.",
    paper: "Hendrycks & Gimpel, 2016",
    code: `nn.GELU()`,
    params: "0",
    complexity: "O(1) per element (with approx.)",
  },
  {
    id: "swiglu",
    name: "SwiGLU",
    aka: "Swish-Gated Linear Unit",
    category: "activation",
    rarity: "rare",
    year: 2020,
    icon: "⟿",
    stats: { power: 8, speed: 7, versatility: 7, efficiency: 6 },
    flavor: "The secret spice in LLaMA's recipe.",
    description: "Combines Swish activation with a gating mechanism: SwiGLU(x) = Swish(xW₁) ⊙ xW₂. Empirically outperforms ReLU/GELU FFNs in LLMs.",
    paper: "Shazeer, 2020 (GLU Variants)",
    code: `# Typically in FFN block:\ngate = F.silu(self.w1(x))\nout = gate * self.w2(x)`,
    params: "3 × d × d_ff (vs 2 × for standard FFN)",
    complexity: "O(d × d_ff)",
  },
  {
    id: "softmax",
    name: "Softmax",
    aka: "Normalized Exponential",
    category: "activation",
    rarity: "common",
    year: 1959,
    icon: "℮",
    stats: { power: 5, speed: 8, versatility: 9, efficiency: 8 },
    flavor: "Turning raw logits into probabilities since 1959.",
    description: "Exponentiates and normalizes a vector to produce a probability distribution. Used in classification heads and as the core of attention score normalization.",
    paper: "Luce, 1959 / Bridle, 1990",
    code: `nn.Softmax(dim=-1)`,
    params: "0",
    complexity: "O(n)",
  },
  {
    id: "dropout",
    name: "Dropout",
    aka: "Inverted Dropout",
    category: "regularization",
    rarity: "common",
    year: 2014,
    icon: "◌",
    stats: { power: 3, speed: 9, versatility: 8, efficiency: 9 },
    flavor: "Randomly killing neurons so the survivors grow stronger.",
    description: "Randomly zeroes elements with probability p during training, scales by 1/(1-p). Forces redundancy and prevents co-adaptation of features.",
    paper: "Srivastava et al., 2014",
    code: `nn.Dropout(p=0.1)`,
    params: "0",
    complexity: "O(1) per element",
  },
  {
    id: "maxpool",
    name: "MaxPool2D",
    aka: "Max Pooling",
    category: "pooling",
    rarity: "common",
    year: 1989,
    icon: "⬆",
    stats: { power: 3, speed: 10, versatility: 5, efficiency: 10 },
    flavor: "Only the strongest activations survive. Natural selection for features.",
    description: "Takes the maximum value in each local window, reducing spatial dimensions. Provides translation invariance and reduces computation in deeper layers.",
    paper: "LeCun et al., 1989",
    code: `nn.MaxPool2d(kernel_size=2, stride=2)`,
    params: "0",
    complexity: "O(k² × H × W)",
  },
  {
    id: "adaptiveavgpool",
    name: "Adaptive AvgPool",
    aka: "Global Average Pooling",
    category: "pooling",
    rarity: "common",
    year: 2013,
    icon: "⊜",
    stats: { power: 3, speed: 10, versatility: 7, efficiency: 10 },
    flavor: "Any spatial size in, fixed size out. The great equalizer.",
    description: "Pools to a target output size regardless of input dimensions. Global average pooling (output_size=1) replaces flattening + FC layers in modern CNNs.",
    paper: "Lin et al., NiN 2013",
    code: `nn.AdaptiveAvgPool2d(output_size=1)`,
    params: "0",
    complexity: "O(H × W × C)",
  },
  {
    id: "lstm",
    name: "LSTM",
    aka: "Long Short-Term Memory",
    category: "recurrent",
    rarity: "rare",
    year: 1997,
    icon: "⟲",
    stats: { power: 7, speed: 3, versatility: 7, efficiency: 4 },
    flavor: "Gates of memory — forget, remember, output. The OG sequence modeler.",
    description: "Recurrent cell with forget, input, and output gates controlling information flow through a cell state. Solved vanishing gradients for sequences. Dominated NLP pre-Transformer.",
    paper: "Hochreiter & Schmidhuber, 1997",
    code: `nn.LSTM(input_size=256,\n  hidden_size=512, num_layers=2)`,
    params: "4 × (in × hid + hid² + hid) per layer",
    complexity: "O(T × hidden²) — sequential",
  },
  {
    id: "embedding",
    name: "Embedding",
    aka: "Lookup Table / Token Embedding",
    category: "embedding",
    rarity: "common",
    year: 2003,
    icon: "⊞",
    stats: { power: 4, speed: 10, versatility: 9, efficiency: 6 },
    flavor: "From integer to vector — every token gets its own corner of latent space.",
    description: "A learnable lookup table mapping discrete indices (tokens, categories) to dense vectors. The entry point to virtually every NLP model.",
    paper: "Bengio et al., 2003",
    code: `nn.Embedding(\n  num_embeddings=50257, embedding_dim=768)`,
    params: "vocab_size × dim",
    complexity: "O(1) lookup per token",
  },
  {
    id: "rope",
    name: "RoPE",
    aka: "Rotary Position Embedding",
    category: "embedding",
    rarity: "epic",
    year: 2021,
    icon: "⟳",
    stats: { power: 8, speed: 8, versatility: 8, efficiency: 9 },
    flavor: "Rotate your queries and keys — relative position encoded in the angle.",
    description: "Encodes position by rotating Q and K vectors in 2D subspaces at frequencies determined by position. Enables relative position awareness and length extrapolation. Used in LLaMA, Mistral, most modern LLMs.",
    paper: "Su et al., 2021",
    code: `# Applied to Q, K before attention\ndef apply_rope(x, freqs):\n  x_ = x.reshape(..., -1, 2)\n  return rotate(x_, freqs)`,
    params: "0 (computed, not learned)",
    complexity: "O(d) per position",
  },
  {
    id: "moe",
    name: "Mixture of Experts",
    aka: "MoE / Sparse MoE",
    category: "linear",
    rarity: "legendary",
    year: 2017,
    icon: "❖",
    stats: { power: 10, speed: 6, versatility: 9, efficiency: 8 },
    flavor: "Why use one FFN when you can route to the expert who knows best?",
    description: "Replaces dense FFN with N expert networks + a learned router that selects top-k experts per token. Scales parameters without scaling FLOPs proportionally. Powers Mixtral, GPT-4 (rumored), Switch Transformer.",
    paper: "Shazeer et al., 2017",
    code: `# Simplified routing\nscores = router(x)  # (batch, n_experts)\ntop_k = scores.topk(2)\nout = sum(w_i * expert_i(x))`,
    params: "n_experts × expert_params + router",
    complexity: "O(k × expert_cost) per token",
  },
  {
    id: "kv_cache",
    name: "KV Cache",
    aka: "Key-Value Cache",
    category: "attention",
    rarity: "epic",
    year: 2017,
    icon: "⟐",
    stats: { power: 7, speed: 10, versatility: 6, efficiency: 8 },
    flavor: "Compute once, attend forever. The trick that makes autoregressive generation bearable.",
    description: "Caches computed key and value tensors from previous tokens during autoregressive generation so they don't need recomputation. Turns O(n²) generation into O(n) per step.",
    paper: "Implicit in Vaswani et al., 2017",
    code: `# During generation:\nif past_kv is not None:\n  k = cat([past_k, new_k], dim=-2)\n  v = cat([past_v, new_v], dim=-2)`,
    params: "2 × layers × n × d_head × n_kv_heads",
    complexity: "O(n × d) per new token (vs O(n² × d))",
  },
];

const StatBar = ({ label, value, color }) => (
  <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 3 }}>
    <span style={{ fontSize: 10, width: 60, color: "#8899aa", fontFamily: "'JetBrains Mono', monospace", textTransform: "uppercase", letterSpacing: 1 }}>{label}</span>
    <div style={{ flex: 1, height: 4, background: "rgba(255,255,255,0.06)", borderRadius: 2, overflow: "hidden" }}>
      <div style={{
        width: `${value * 10}%`, height: "100%", borderRadius: 2,
        background: `linear-gradient(90deg, ${color}88, ${color})`,
        transition: "width 0.6s cubic-bezier(0.22,1,0.36,1)",
      }} />
    </div>
    <span style={{ fontSize: 10, width: 16, textAlign: "right", color: color, fontFamily: "'JetBrains Mono', monospace", fontWeight: 700 }}>{value}</span>
  </div>
);

const Card = ({ card, onClick, index }) => {
  const rarity = RARITY[card.rarity];
  const cardRef = useRef(null);
  const [tilt, setTilt] = useState({ x: 0, y: 0 });
  const [hovering, setHovering] = useState(false);

  const handleMouseMove = (e) => {
    if (!cardRef.current) return;
    const rect = cardRef.current.getBoundingClientRect();
    const x = (e.clientX - rect.left) / rect.width - 0.5;
    const y = (e.clientY - rect.top) / rect.height - 0.5;
    setTilt({ x: y * -15, y: x * 15 });
  };

  const handleMouseLeave = () => {
    setTilt({ x: 0, y: 0 });
    setHovering(false);
  };

  const isHolo = card.rarity === "legendary" || card.rarity === "epic";

  return (
    <div
      ref={cardRef}
      onClick={() => onClick(card)}
      onMouseMove={handleMouseMove}
      onMouseEnter={() => setHovering(true)}
      onMouseLeave={handleMouseLeave}
      style={{
        width: 220, minHeight: 320, borderRadius: 16, cursor: "pointer",
        background: rarity.bg,
        border: `1px solid ${rarity.color}33`,
        padding: 0, position: "relative", overflow: "hidden",
        transform: `perspective(800px) rotateX(${tilt.x}deg) rotateY(${tilt.y}deg) scale(${hovering ? 1.04 : 1})`,
        transition: hovering ? "transform 0.1s ease-out" : "transform 0.4s cubic-bezier(0.22,1,0.36,1)",
        boxShadow: hovering ? `0 20px 60px ${rarity.glow}, 0 0 30px ${rarity.glow}` : `0 4px 20px rgba(0,0,0,0.4)`,
        animation: `cardEntry 0.5s cubic-bezier(0.22,1,0.36,1) ${index * 0.04}s both`,
      }}
    >
      {isHolo && (
        <div style={{
          position: "absolute", inset: 0, zIndex: 1, pointerEvents: "none", opacity: hovering ? 0.3 : 0.1,
          background: `linear-gradient(${135 + tilt.y * 5}deg, 
            transparent 20%, ${rarity.color}44 35%, transparent 40%,
            transparent 55%, ${rarity.color}33 65%, transparent 75%)`,
          transition: "opacity 0.3s",
        }} />
      )}

      <div style={{ padding: "16px 16px 12px", position: "relative", zIndex: 2 }}>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", marginBottom: 8 }}>
          <span style={{
            fontSize: 9, fontWeight: 700, letterSpacing: 1.5, textTransform: "uppercase",
            color: rarity.color, fontFamily: "'JetBrains Mono', monospace",
          }}>{rarity.label}</span>
          <span style={{
            fontSize: 9, color: "#556677", fontFamily: "'JetBrains Mono', monospace",
          }}>{card.year}</span>
        </div>

        <div style={{
          fontSize: 44, textAlign: "center", margin: "8px 0 4px",
          filter: `drop-shadow(0 0 12px ${rarity.glow})`,
          lineHeight: 1,
        }}>{card.icon}</div>

        <h3 style={{
          margin: "8px 0 2px", fontSize: 18, fontWeight: 800, color: "#e8eef4",
          fontFamily: "'Space Mono', monospace", textAlign: "center", letterSpacing: -0.5,
        }}>{card.name}</h3>
        <p style={{
          margin: 0, fontSize: 9, color: "#667788", textAlign: "center",
          fontFamily: "'JetBrains Mono', monospace", letterSpacing: 0.5,
        }}>{card.aka}</p>

        <p style={{
          margin: "12px 0 14px", fontSize: 11, color: "#8899aa", lineHeight: 1.5,
          fontStyle: "italic", textAlign: "center", minHeight: 32,
        }}>"{card.flavor}"</p>

        <div style={{ marginTop: 4 }}>
          <StatBar label="PWR" value={card.stats.power} color={rarity.color} />
          <StatBar label="SPD" value={card.stats.speed} color={rarity.color} />
          <StatBar label="VERS" value={card.stats.versatility} color={rarity.color} />
          <StatBar label="EFF" value={card.stats.efficiency} color={rarity.color} />
        </div>
      </div>

      <div style={{
        position: "absolute", bottom: 0, left: 0, right: 0, height: 4,
        background: `linear-gradient(90deg, transparent, ${rarity.color}, transparent)`,
        opacity: 0.5,
      }} />
    </div>
  );
};

const DetailModal = ({ card, onClose }) => {
  const rarity = RARITY[card.rarity];
  const [visible, setVisible] = useState(false);

  useEffect(() => {
    requestAnimationFrame(() => setVisible(true));
  }, []);

  const handleClose = () => {
    setVisible(false);
    setTimeout(onClose, 250);
  };

  return (
    <div onClick={handleClose} style={{
      position: "fixed", inset: 0, zIndex: 1000, display: "flex", alignItems: "center", justifyContent: "center",
      background: visible ? "rgba(0,0,0,0.8)" : "rgba(0,0,0,0)",
      backdropFilter: visible ? "blur(10px)" : "blur(0px)",
      transition: "all 0.3s ease", padding: 20,
    }}>
      <div onClick={(e) => e.stopPropagation()} style={{
        width: "100%", maxWidth: 560, maxHeight: "90vh", overflowY: "auto",
        background: "#0e1218", borderRadius: 20,
        border: `1px solid ${rarity.color}44`,
        boxShadow: `0 40px 100px rgba(0,0,0,0.8), 0 0 60px ${rarity.glow}`,
        transform: visible ? "scale(1) translateY(0)" : "scale(0.9) translateY(30px)",
        opacity: visible ? 1 : 0,
        transition: "all 0.35s cubic-bezier(0.22,1,0.36,1)",
      }}>
        <div style={{
          padding: "28px 28px 0", display: "flex", justifyContent: "space-between", alignItems: "center",
        }}>
          <div>
            <span style={{
              fontSize: 10, fontWeight: 700, letterSpacing: 2, textTransform: "uppercase",
              color: rarity.color, fontFamily: "'JetBrains Mono', monospace",
            }}>{rarity.label} · {card.category.toUpperCase()}</span>
            <h2 style={{
              margin: "4px 0 0", fontSize: 28, fontWeight: 800, color: "#e8eef4",
              fontFamily: "'Space Mono', monospace",
            }}>{card.icon} {card.name}</h2>
            <p style={{ margin: "2px 0 0", fontSize: 12, color: "#667788", fontFamily: "'JetBrains Mono', monospace" }}>{card.aka}</p>
          </div>
          <button onClick={handleClose} style={{
            width: 36, height: 36, borderRadius: 10, border: "1px solid #ffffff15",
            background: "#ffffff08", color: "#8899aa", fontSize: 18, cursor: "pointer",
            display: "flex", alignItems: "center", justifyContent: "center",
          }}>×</button>
        </div>

        <div style={{ padding: "20px 28px 28px" }}>
          <p style={{ fontSize: 14, color: "#b0bec5", lineHeight: 1.7, margin: "0 0 20px" }}>
            {card.description}
          </p>

          <div style={{
            display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10, marginBottom: 20,
          }}>
            {Object.entries(card.stats).map(([key, val]) => (
              <div key={key} style={{
                background: "rgba(255,255,255,0.03)", borderRadius: 10, padding: "10px 14px",
                border: "1px solid rgba(255,255,255,0.04)",
              }}>
                <div style={{ fontSize: 9, color: "#556677", textTransform: "uppercase", letterSpacing: 1.5, fontFamily: "'JetBrains Mono', monospace", marginBottom: 4 }}>
                  {key}
                </div>
                <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
                  <div style={{ flex: 1, height: 4, background: "rgba(255,255,255,0.06)", borderRadius: 2, overflow: "hidden" }}>
                    <div style={{
                      width: `${val * 10}%`, height: "100%", borderRadius: 2,
                      background: `linear-gradient(90deg, ${rarity.color}88, ${rarity.color})`,
                    }} />
                  </div>
                  <span style={{ fontSize: 14, fontWeight: 800, color: rarity.color, fontFamily: "'JetBrains Mono', monospace" }}>{val}</span>
                </div>
              </div>
            ))}
          </div>

          <div style={{ marginBottom: 16 }}>
            <div style={{ fontSize: 10, color: "#556677", textTransform: "uppercase", letterSpacing: 1.5, fontFamily: "'JetBrains Mono', monospace", marginBottom: 8 }}>
              Implementation
            </div>
            <pre style={{
              background: "#080c12", borderRadius: 12, padding: 16, margin: 0,
              border: "1px solid rgba(255,255,255,0.05)",
              fontSize: 12, lineHeight: 1.6, color: "#a3e635", overflowX: "auto",
              fontFamily: "'JetBrains Mono', monospace",
            }}>{card.code}</pre>
          </div>

          <div style={{
            display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10,
          }}>
            <div style={{
              background: "rgba(255,255,255,0.03)", borderRadius: 10, padding: "12px 14px",
              border: "1px solid rgba(255,255,255,0.04)",
            }}>
              <div style={{ fontSize: 9, color: "#556677", textTransform: "uppercase", letterSpacing: 1.5, fontFamily: "'JetBrains Mono', monospace", marginBottom: 4 }}>
                Parameters
              </div>
              <div style={{ fontSize: 12, color: "#b0bec5", fontFamily: "'JetBrains Mono', monospace" }}>{card.params}</div>
            </div>
            <div style={{
              background: "rgba(255,255,255,0.03)", borderRadius: 10, padding: "12px 14px",
              border: "1px solid rgba(255,255,255,0.04)",
            }}>
              <div style={{ fontSize: 9, color: "#556677", textTransform: "uppercase", letterSpacing: 1.5, fontFamily: "'JetBrains Mono', monospace", marginBottom: 4 }}>
                Complexity
              </div>
              <div style={{ fontSize: 12, color: "#b0bec5", fontFamily: "'JetBrains Mono', monospace" }}>{card.complexity}</div>
            </div>
          </div>

          <div style={{
            marginTop: 16, padding: "10px 14px", borderRadius: 10,
            background: "rgba(255,255,255,0.02)", border: "1px solid rgba(255,255,255,0.04)",
            display: "flex", alignItems: "center", gap: 8,
          }}>
            <span style={{ fontSize: 9, color: "#556677", textTransform: "uppercase", letterSpacing: 1.5, fontFamily: "'JetBrains Mono', monospace" }}>Paper</span>
            <span style={{ fontSize: 12, color: "#8899aa" }}>{card.paper}</span>
          </div>
        </div>
      </div>
    </div>
  );
};

function NNCardCollection() {
  const [selectedCard, setSelectedCard] = useState(null);
  const [filter, setFilter] = useState("all");
  const [rarityFilter, setRarityFilter] = useState("all");
  const [search, setSearch] = useState("");

  const filtered = CARDS.filter((c) => {
    if (filter !== "all" && c.category !== filter) return false;
    if (rarityFilter !== "all" && c.rarity !== rarityFilter) return false;
    if (search && !c.name.toLowerCase().includes(search.toLowerCase()) && !c.aka.toLowerCase().includes(search.toLowerCase())) return false;
    return true;
  });

  const rarityCounts = {};
  CARDS.forEach(c => { rarityCounts[c.rarity] = (rarityCounts[c.rarity] || 0) + 1; });

  return (
    <div style={{
      minHeight: "100vh", background: "#0a0e14",
      fontFamily: "'DM Sans', -apple-system, sans-serif", color: "#e8eef4",
    }}>
      <style>{`
        @import url('https://fonts.googleapis.com/css2?family=Space+Mono:wght@400;700&family=JetBrains+Mono:wght@400;500;700&family=DM+Sans:wght@400;500;700&display=swap');
        @keyframes cardEntry {
          from { opacity: 0; transform: translateY(30px) scale(0.95); }
          to { opacity: 1; transform: translateY(0) scale(1); }
        }
        ::-webkit-scrollbar { width: 6px; }
        ::-webkit-scrollbar-track { background: transparent; }
        ::-webkit-scrollbar-thumb { background: #ffffff15; border-radius: 3px; }
        * { box-sizing: border-box; }
      `}</style>

      <div style={{ maxWidth: 1200, margin: "0 auto", padding: "40px 24px" }}>
        <div style={{ textAlign: "center", marginBottom: 40 }}>
          <div style={{
            fontSize: 10, letterSpacing: 4, textTransform: "uppercase", color: "#445566",
            fontFamily: "'JetBrains Mono', monospace", marginBottom: 8,
          }}>Collection · {CARDS.length} Cards</div>
          <h1 style={{
            fontSize: 42, fontWeight: 800, margin: "0 0 8px",
            fontFamily: "'Space Mono', monospace",
            color: "#e8eef4",
            letterSpacing: -2,
          }}>Neural Network Cards</h1>
          <p style={{ fontSize: 14, color: "#556677", margin: 0, maxWidth: 500, marginLeft: "auto", marginRight: "auto" }}>
            Every neural network component, ranked by power, speed, versatility, and efficiency. Click any card to inspect.
          </p>
        </div>

        <div style={{ marginBottom: 12, display: "flex", justifyContent: "center" }}>
          <input
            type="text" placeholder="Search components..." value={search}
            onChange={(e) => setSearch(e.target.value)}
            style={{
              width: "100%", maxWidth: 360, padding: "10px 16px", borderRadius: 12,
              background: "rgba(255,255,255,0.04)", border: "1px solid rgba(255,255,255,0.08)",
              color: "#e8eef4", fontSize: 13, outline: "none",
              fontFamily: "'JetBrains Mono', monospace",
            }}
          />
        </div>

        <div style={{ display: "flex", flexWrap: "wrap", gap: 6, justifyContent: "center", marginBottom: 10 }}>
          {Object.entries(CATEGORIES).map(([key, label]) => (
            <button key={key} onClick={() => setFilter(key)} style={{
              padding: "6px 14px", borderRadius: 8, border: "1px solid",
              borderColor: filter === key ? "#e8eef488" : "rgba(255,255,255,0.08)",
              background: filter === key ? "rgba(255,255,255,0.1)" : "rgba(255,255,255,0.02)",
              color: filter === key ? "#e8eef4" : "#556677",
              fontSize: 11, cursor: "pointer", fontFamily: "'JetBrains Mono', monospace",
              fontWeight: filter === key ? 700 : 400, letterSpacing: 0.5,
              transition: "all 0.2s",
            }}>{label}</button>
          ))}
        </div>

        <div style={{ display: "flex", flexWrap: "wrap", gap: 6, justifyContent: "center", marginBottom: 32 }}>
          <button onClick={() => setRarityFilter("all")} style={{
            padding: "5px 12px", borderRadius: 8, border: "1px solid",
            borderColor: rarityFilter === "all" ? "#e8eef488" : "rgba(255,255,255,0.08)",
            background: rarityFilter === "all" ? "rgba(255,255,255,0.1)" : "rgba(255,255,255,0.02)",
            color: rarityFilter === "all" ? "#e8eef4" : "#556677",
            fontSize: 10, cursor: "pointer", fontFamily: "'JetBrains Mono', monospace",
            fontWeight: rarityFilter === "all" ? 700 : 400,
          }}>All Rarities</button>
          {Object.entries(RARITY).map(([key, r]) => (
            <button key={key} onClick={() => setRarityFilter(key)} style={{
              padding: "5px 12px", borderRadius: 8, border: "1px solid",
              borderColor: rarityFilter === key ? `${r.color}88` : "rgba(255,255,255,0.08)",
              background: rarityFilter === key ? `${r.color}18` : "rgba(255,255,255,0.02)",
              color: rarityFilter === key ? r.color : "#556677",
              fontSize: 10, cursor: "pointer", fontFamily: "'JetBrains Mono', monospace",
              fontWeight: rarityFilter === key ? 700 : 400,
            }}>{r.label} ({rarityCounts[key] || 0})</button>
          ))}
        </div>

        <div style={{
          display: "flex", flexWrap: "wrap", gap: 20, justifyContent: "center",
        }}>
          {filtered.map((card, i) => (
            <Card key={card.id} card={card} onClick={setSelectedCard} index={i} />
          ))}
        </div>

        {filtered.length === 0 && (
          <div style={{ textAlign: "center", padding: "60px 20px", color: "#334455" }}>
            <div style={{ fontSize: 40, marginBottom: 12 }}>∅</div>
            <div style={{ fontSize: 14, fontFamily: "'JetBrains Mono', monospace" }}>No cards match your filters</div>
          </div>
        )}
      </div>

      {selectedCard && <DetailModal card={selectedCard} onClose={() => setSelectedCard(null)} />}
    </div>
  );
}

const rootElement = document.getElementById("nn-cards-root");

if (rootElement) {
  ReactDOM.createRoot(rootElement).render(<NNCardCollection />);
}
