// app.jsx — Codexa landing page

const { useState, useEffect, useRef, useLayoutEffect, useMemo, useCallback } = React;

/* ────────────────────────────────────────────────────────────
   Hooks
   ──────────────────────────────────────────────────────────── */

function useScrollY() {
  const [y, setY] = useState(0);
  useEffect(() => {
    let raf = 0;
    const onScroll = () => {
      cancelAnimationFrame(raf);
      raf = requestAnimationFrame(() => setY(window.scrollY));
    };
    window.addEventListener('scroll', onScroll, { passive: true });
    return () => { window.removeEventListener('scroll', onScroll); cancelAnimationFrame(raf); };
  }, []);
  return y;
}

function useInView(ref, threshold = 0.15) {
  const [seen, setSeen] = useState(false);
  useEffect(() => {
    if (!ref.current || seen) return;
    const io = new IntersectionObserver(([e]) => {
      if (e.isIntersecting) { setSeen(true); io.disconnect(); }
    }, { threshold });
    io.observe(ref.current);
    return () => io.disconnect();
  }, [ref, threshold, seen]);
  return seen;
}

/* ────────────────────────────────────────────────────────────
   Styles object (UNIQUE NAME)
   ──────────────────────────────────────────────────────────── */
const codexaStyles = `
:root { color-scheme: dark; }

/* ===== Layout ===== */
.cdx-wrap { width: 100%; max-width: 1280px; margin: 0 auto; padding: 0 32px; }
@media (max-width: 720px) { .cdx-wrap { padding: 0 20px; } }

/* ===== Nav ===== */
.cdx-nav {
  position: fixed; top: 14px; left: 50%; transform: translateX(-50%);
  z-index: 50; width: calc(100% - 32px); max-width: 1180px;
  display: flex; align-items: center; justify-content: space-between;
  padding: 10px 14px 10px 18px;
  background: rgba(15, 12, 28, 0.55);
  border: 1px solid var(--glass-border);
  border-radius: 999px;
  backdrop-filter: blur(var(--glass-blur)) saturate(140%);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(140%);
  transition: background .3s ease, border-color .3s ease;
}
.cdx-nav.solid { background: rgba(10, 8, 20, 0.82); }
.cdx-nav-logo { display:flex; align-items:center; gap:10px; font-family:'Space Grotesk'; font-weight:600; letter-spacing:-.02em; font-size:18px; }
.cdx-nav-logo .mark {
  width: 32px; height: 32px;
  background-image: url('assets/codexa-icon.png');
  background-size: contain; background-position: center; background-repeat: no-repeat;
  filter: drop-shadow(0 0 12px rgba(168,85,247,.5));
}

.cdx-nav-links { display:flex; align-items:center; gap: 6px; }
.cdx-nav-links a {
  color: var(--fg-dim); text-decoration: none; font-size: 14px;
  padding: 8px 14px; border-radius: 999px;
  transition: color .2s, background .2s;
}
.cdx-nav-links a:hover { color: var(--fg); background: rgba(255,255,255,.05); }
@media (max-width: 820px) { .cdx-nav-links { display:none; } }

.cdx-btn {
  display:inline-flex; align-items:center; gap:8px; cursor:pointer;
  font-family:'Inter'; font-weight:500; font-size:14px;
  padding: 10px 18px; border-radius: 999px;
  border: 1px solid transparent;
  text-decoration: none; transition: transform .15s ease, box-shadow .25s ease, background .2s ease;
}
.cdx-btn-primary {
  color:#fff;
  background: linear-gradient(135deg, var(--accent-1), var(--accent-2));
  box-shadow: 0 8px 24px -8px rgba(168,85,247,.6), inset 0 1px 0 rgba(255,255,255,.25);
}
.cdx-btn-primary:hover { transform: translateY(-1px); box-shadow: 0 12px 32px -8px rgba(168,85,247,.85), inset 0 1px 0 rgba(255,255,255,.3); }
.cdx-btn-ghost { color: var(--fg); background: rgba(255,255,255,.04); border-color: var(--glass-border); }
.cdx-btn-ghost:hover { background: rgba(255,255,255,.08); }

/* ===== Hero ===== */
.cdx-hero {
  position: relative; min-height: 100vh;
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
}
.cdx-hero-bg {
  position: absolute; inset: 0; z-index: 0;
}
.cdx-hero-video {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover;
  opacity: .55;
  filter: saturate(1.2) contrast(1.05);
}
.cdx-hero-bg::after {
  content:""; position:absolute; inset:0;
  background:
    radial-gradient(ellipse at center, transparent 0%, rgba(7,6,13,.85) 80%),
    linear-gradient(to bottom, transparent 40%, #07060d 100%);
}
.cdx-hero-bg::before {
  content:""; position:absolute; inset:0; z-index:1;
  background-image:
    linear-gradient(rgba(255,255,255,.04) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255,255,255,.04) 1px, transparent 1px);
  background-size: 64px 64px;
  mask-image: radial-gradient(ellipse at center, #000 0%, transparent 70%);
  -webkit-mask-image: radial-gradient(ellipse at center, #000 0%, transparent 70%);
}

.cdx-hero-content {
  position: relative; z-index: 2; text-align: center;
  max-width: 920px; padding: 120px 32px 60px;
}
.cdx-hero-logo {
  display:flex; justify-content:center; margin-bottom: 32px;
  position: relative;
}
.cdx-hero-logo img {
  width: 140px; height: 140px; object-fit: contain;
  filter: drop-shadow(0 0 40px rgba(168,85,247,.55)) drop-shadow(0 0 80px rgba(59,130,246,.35));
  animation: hero-logo-float 5s ease-in-out infinite;
}
@keyframes hero-logo-float {
  0%, 100% { transform: translateY(0) rotate(-1deg); }
  50% { transform: translateY(-10px) rotate(1deg); }
}
.cdx-hero-logo::after {
  content: ""; position: absolute; left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  width: 220px; height: 220px; border-radius: 50%;
  background: radial-gradient(circle, rgba(168,85,247,.4) 0%, transparent 60%);
  z-index: -1; animation: pulse-glow 4s ease-in-out infinite;
}
@keyframes pulse-glow {
  0%, 100% { opacity: .5; transform: translate(-50%, -50%) scale(1); }
  50% { opacity: 1; transform: translate(-50%, -50%) scale(1.15); }
}
@media (max-width: 720px) {
  .cdx-hero-logo img { width: 100px; height: 100px; }
  .cdx-hero-logo::after { width: 160px; height: 160px; }
}
.cdx-eyebrow {
  display: inline-flex; align-items:center; gap: 8px;
  font-family: 'JetBrains Mono'; font-size: 12px; letter-spacing: .12em; text-transform: uppercase;
  color: var(--fg-dim);
  padding: 6px 14px; border-radius: 999px;
  background: rgba(255,255,255,.04);
  border: 1px solid var(--glass-border);
  margin-bottom: 28px;
}
.cdx-eyebrow .dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--accent-1); box-shadow: 0 0 12px var(--accent-1);
  animation: pulse 2s ease-in-out infinite;
}
@keyframes pulse { 0%,100% { opacity:1; } 50% { opacity:.4; } }

.cdx-hero h1 {
  font-family: 'Space Grotesk'; font-weight: 500;
  font-size: clamp(44px, 7vw, 96px);
  line-height: 0.98; letter-spacing: -0.04em;
  margin: 0 0 24px;
  color: #fff;
}
.cdx-hero h1 em {
  font-style: normal;
  background: linear-gradient(120deg, var(--accent-1) 0%, var(--accent-2) 50%, var(--accent-3) 100%);
  background-size: 200% 200%;
  -webkit-background-clip: text; background-clip: text;
  color: transparent;
  animation: gradient-shift 8s ease infinite;
}
@keyframes gradient-shift { 0%,100% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } }

.cdx-hero p.lead {
  font-size: clamp(16px, 1.4vw, 19px); line-height: 1.55;
  color: var(--fg-dim); max-width: 600px; margin: 0 auto 36px;
}
.cdx-hero-cta { display:inline-flex; gap: 12px; flex-wrap: wrap; justify-content:center; }

.cdx-hero-scroll-cue {
  position: absolute; bottom: 28px; left: 50%; transform: translateX(-50%);
  z-index: 3; color: var(--fg-mute); font-family: 'JetBrains Mono'; font-size: 11px;
  letter-spacing: .15em; text-transform: uppercase; display:flex; flex-direction:column; align-items:center; gap:10px;
  animation: float 2.5s ease-in-out infinite;
}
@keyframes float { 0%,100% { transform: translate(-50%, 0); } 50% { transform: translate(-50%, 6px); } }
.cdx-hero-scroll-cue .line { width: 1px; height: 32px; background: linear-gradient(to bottom, transparent, var(--fg-dim)); }

/* ===== Hero variants ===== */
.cdx-hero-grid {
  position:absolute; inset:0; z-index:0;
  background:
    radial-gradient(circle at 20% 30%, rgba(168,85,247,.3), transparent 40%),
    radial-gradient(circle at 80% 70%, rgba(59,130,246,.3), transparent 40%),
    radial-gradient(circle at 50% 50%, rgba(217,70,239,.15), transparent 50%);
}
.cdx-hero-orb {
  position: absolute; inset: 0; z-index:0; display:grid; place-items:center;
  pointer-events: none;
}
.cdx-orb-svg {
  width: min(80vmin, 700px); height: min(80vmin, 700px);
  animation: orb-rotate 30s linear infinite;
  filter: drop-shadow(0 0 40px rgba(168,85,247,.5));
}
@keyframes orb-rotate { to { transform: rotate(360deg); } }

/* ===== Scroll-pinned video section ===== */
.cdx-scroll-video {
  position: relative; height: 300vh;
}
.cdx-scroll-video-sticky {
  position: sticky; top: 0; height: 100vh; overflow: hidden;
  display: flex; align-items: center; justify-content: center;
}
.cdx-scroll-video-sticky video {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; opacity: .8;
  filter: contrast(1.1) saturate(1.2);
}
.cdx-scroll-video-sticky::after {
  content:""; position:absolute; inset:0;
  background: linear-gradient(to bottom, rgba(7,6,13,.4) 0%, transparent 30%, transparent 70%, rgba(7,6,13,1) 100%);
}
.cdx-scroll-video-overlay {
  position: relative; z-index: 3; text-align: center;
  padding: 0 32px; max-width: 900px;
  mix-blend-mode: difference;
}
.cdx-scroll-video-overlay h2 {
  font-family: 'Space Grotesk'; font-weight: 500;
  font-size: clamp(48px, 9vw, 140px);
  letter-spacing: -0.05em; line-height: 0.92;
  margin: 0; color: #fff;
}
.cdx-scroll-progress {
  position: absolute; bottom: 40px; left: 50%; transform: translateX(-50%);
  z-index: 4; width: min(80%, 400px);
  font-family: 'JetBrains Mono'; font-size: 11px; letter-spacing: .12em;
  text-transform: uppercase; color: var(--fg-mute);
}
.cdx-scroll-progress-bar {
  margin-top: 8px; height: 2px; background: rgba(255,255,255,.1); border-radius: 2px;
  overflow: hidden; position: relative;
}
.cdx-scroll-progress-bar-fill {
  position: absolute; left: 0; top: 0; bottom: 0;
  background: linear-gradient(to right, var(--accent-1), var(--accent-2), var(--accent-3));
}

/* ===== Section header ===== */
.cdx-section { position: relative; padding: 140px 0; }
.cdx-section-header { margin-bottom: 64px; max-width: 720px; }
.cdx-section-tag {
  font-family: 'JetBrains Mono'; font-size: 12px; letter-spacing: .15em;
  text-transform: uppercase; color: var(--accent-1);
  display: flex; align-items: center; gap: 12px; margin-bottom: 20px;
}
.cdx-section-tag::before {
  content:""; width: 28px; height: 1px; background: var(--accent-1);
}
.cdx-section h2 {
  font-family: 'Space Grotesk'; font-weight: 500;
  font-size: clamp(36px, 5vw, 64px); line-height: 1.02; letter-spacing: -0.03em;
  margin: 0 0 20px; color: #fff;
}
.cdx-section h2 em {
  font-style: normal;
  background: linear-gradient(120deg, var(--accent-1), var(--accent-3));
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
.cdx-section .sub {
  font-size: 17px; line-height: 1.55; color: var(--fg-dim); max-width: 560px;
}

/* ===== About ===== */
.cdx-about-grid {
  display: grid; grid-template-columns: 1.1fr 1fr; gap: 80px; align-items: start;
}
@media (max-width: 900px) { .cdx-about-grid { grid-template-columns: 1fr; gap: 48px; } }
.cdx-about-stats { display:grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-top: 48px; }
.cdx-stat {
  padding: 24px; border-radius: 16px;
  background: var(--glass-bg); border: 1px solid var(--glass-border);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
}
.cdx-stat-num { font-family: 'Space Grotesk'; font-size: 44px; font-weight: 500; letter-spacing: -.02em; color: #fff; line-height: 1; }
.cdx-stat-num em { font-style: normal;
  background: linear-gradient(135deg, var(--accent-1), var(--accent-2));
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
.cdx-stat-label { font-size: 13px; color: var(--fg-dim); margin-top: 8px; font-family:'JetBrains Mono'; letter-spacing:.05em; }

.cdx-about-visual {
  position: relative; aspect-ratio: 4/5;
  border-radius: 24px; overflow: hidden;
  border: 1px solid var(--glass-border);
  background:
    radial-gradient(ellipse at top left, rgba(168,85,247,.4), transparent 50%),
    radial-gradient(ellipse at bottom right, rgba(59,130,246,.4), transparent 50%),
    linear-gradient(135deg, #14102a, #0a0818);
}
.cdx-about-visual::before {
  content:""; position:absolute; inset:0;
  background-image:
    linear-gradient(rgba(255,255,255,.06) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255,255,255,.06) 1px, transparent 1px);
  background-size: 32px 32px;
  mask-image: radial-gradient(circle at center, #000 0%, transparent 65%);
  -webkit-mask-image: radial-gradient(circle at center, #000 0%, transparent 65%);
}
.cdx-about-code {
  position: absolute; inset: 32px;
  font-family: 'JetBrains Mono'; font-size: 13px; line-height: 1.7;
  color: var(--fg-dim); display:flex; flex-direction:column; gap: 4px;
}
.cdx-about-code .ln-num { color: var(--fg-mute); margin-right: 16px; user-select:none; }
.cdx-about-code .kw { color: var(--accent-1); }
.cdx-about-code .str { color: #7dd3fc; }
.cdx-about-code .com { color: var(--fg-mute); font-style: italic; }
.cdx-about-code .fn { color: var(--accent-3); }

/* ===== Services ===== */
.cdx-services-grid {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px;
}
@media (max-width: 900px) { .cdx-services-grid { grid-template-columns: 1fr; } }
.cdx-service {
  position: relative; padding: 32px; border-radius: 20px;
  background: var(--glass-bg);
  border: 1px solid var(--glass-border);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
  overflow: hidden;
  transition: transform .3s ease, border-color .3s ease;
}
.cdx-service:hover { transform: translateY(-4px); border-color: rgba(168,85,247,.4); }
.cdx-service::before {
  content:""; position:absolute; top: -50%; right: -50%;
  width: 200px; height: 200px; border-radius: 50%;
  background: radial-gradient(circle, var(--accent-1) 0%, transparent 60%);
  opacity: .15; transition: opacity .3s;
}
.cdx-service:hover::before { opacity: .3; }
.cdx-service-icon {
  width: 48px; height: 48px; border-radius: 12px;
  background: linear-gradient(135deg, rgba(168,85,247,.2), rgba(59,130,246,.2));
  border: 1px solid var(--glass-border);
  display: grid; place-items: center; margin-bottom: 24px;
  position: relative;
}
.cdx-service h3 {
  font-family: 'Space Grotesk'; font-size: 22px; font-weight: 500;
  margin: 0 0 12px; color: #fff; letter-spacing: -0.02em;
}
.cdx-service p { font-size: 14.5px; line-height: 1.55; color: var(--fg-dim); margin: 0 0 20px; }
.cdx-service-tags { display:flex; flex-wrap:wrap; gap: 6px; }
.cdx-service-tags span {
  font-family:'JetBrains Mono'; font-size: 11px; padding: 4px 10px;
  border-radius: 999px; background: rgba(255,255,255,.04);
  border: 1px solid var(--glass-border); color: var(--fg-dim);
}

/* ===== Projects ===== */
.cdx-projects { position: relative; }
.cdx-projects-grid {
  display:grid; grid-template-columns: repeat(3, 1fr); gap: 24px;
}
@media (max-width: 900px) { .cdx-projects-grid { grid-template-columns: 1fr; } }
.cdx-projects-stack { display: flex; flex-direction: column; gap: 24px; }
.cdx-projects-horizontal {
  display: flex; gap: 24px; overflow-x: auto; padding-bottom: 32px;
  scroll-snap-type: x mandatory;
  scrollbar-width: thin; scrollbar-color: rgba(168,85,247,.4) transparent;
}
.cdx-projects-horizontal::-webkit-scrollbar { height: 6px; }
.cdx-projects-horizontal::-webkit-scrollbar-thumb { background: rgba(168,85,247,.4); border-radius: 3px; }
.cdx-projects-horizontal .cdx-project { min-width: 480px; scroll-snap-align: start; }
@media (max-width: 720px) {
  .cdx-projects-horizontal .cdx-project { min-width: 88vw; }
}

.cdx-project {
  position: relative; border-radius: 24px; overflow: hidden;
  background: var(--glass-bg);
  border: 1px solid var(--glass-border);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
  transition: transform .4s cubic-bezier(.2,.8,.2,1), border-color .3s;
  display: flex; flex-direction: column;
}
.cdx-project:hover { transform: translateY(-6px); border-color: rgba(168,85,247,.35); }
.cdx-project-stack-row {
  display: grid; grid-template-columns: 280px 1fr; gap: 0;
}
@media (max-width: 720px) {
  .cdx-project-stack-row { grid-template-columns: 1fr; }
}
.cdx-project-visual {
  position: relative; aspect-ratio: 16/10;
  display: grid; place-items: center;
  overflow: hidden;
  border-bottom: 1px solid var(--glass-border);
}
.cdx-projects-stack .cdx-project-visual { aspect-ratio: auto; height: 100%; min-height: 220px; border-bottom: none; border-right: 1px solid var(--glass-border); }
@media (max-width: 720px) {
  .cdx-projects-stack .cdx-project-visual { border-right: none; border-bottom: 1px solid var(--glass-border); min-height: 200px; }
}
.cdx-project-visual img {
  max-width: 80%; max-height: 80%; width: auto; height: auto; object-fit: contain;
  filter: drop-shadow(0 12px 32px rgba(0,0,0,.5));
  transition: transform .5s ease;
}
.cdx-projects-stack .cdx-project-visual img,
.cdx-projects-horizontal .cdx-project-visual img { max-width: 85%; max-height: 85%; }
.cdx-project:hover .cdx-project-visual img { transform: scale(1.06); }
.cdx-project-visual::before {
  content:""; position:absolute; inset:0;
  background-image:
    linear-gradient(rgba(255,255,255,.04) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255,255,255,.04) 1px, transparent 1px);
  background-size: 24px 24px;
}
.cdx-project-body { padding: 24px 28px 28px; display:flex; flex-direction:column; gap: 12px; flex:1; }
.cdx-project-meta {
  display:flex; justify-content: space-between; align-items: center;
  font-family: 'JetBrains Mono'; font-size: 11px; letter-spacing: .1em; text-transform: uppercase;
  color: var(--fg-mute);
}
.cdx-project-meta .url { color: var(--accent-1); }
.cdx-project h3 {
  font-family: 'Space Grotesk'; font-size: 26px; font-weight: 500;
  letter-spacing: -.02em; margin: 0; color: #fff;
}
.cdx-project p { font-size: 14.5px; line-height: 1.55; color: var(--fg-dim); margin: 0; flex:1; }
.cdx-project-tags { display:flex; flex-wrap:wrap; gap: 6px; }
.cdx-project-tags span {
  font-family:'JetBrains Mono'; font-size: 11px; padding: 4px 10px;
  border-radius: 999px; background: rgba(255,255,255,.04);
  border: 1px solid var(--glass-border); color: var(--fg-dim);
}
.cdx-project-link {
  display:inline-flex; align-items: center; gap: 8px;
  color: #fff; text-decoration: none; font-size: 14px; font-weight: 500;
  margin-top: 8px;
}
.cdx-project-link::after { content: "→"; transition: transform .2s; }
.cdx-project-link:hover::after { transform: translateX(4px); }

/* Custom visual gradients per project */
.proj-laborix .cdx-project-visual { background: radial-gradient(ellipse at center, rgba(249,115,22,.35), rgba(15,7,4,.6)); }
.proj-oneticket .cdx-project-visual { background: radial-gradient(ellipse at center, rgba(168,85,247,.35), rgba(10,5,20,.6)); }
.proj-lascanas .cdx-project-visual { background: radial-gradient(ellipse at center, rgba(245,158,11,.3), rgba(20,15,5,.6)); }

/* ===== Contact ===== */
.cdx-contact-grid {
  display: grid; grid-template-columns: 1fr 1.2fr; gap: 60px; align-items: start;
}
@media (max-width: 900px) { .cdx-contact-grid { grid-template-columns: 1fr; gap: 40px; } }
.cdx-contact-info { padding-top: 12px; }
.cdx-contact-detail {
  display: flex; gap: 16px; padding: 18px 0;
  border-bottom: 1px solid var(--glass-border);
}
.cdx-contact-detail:last-child { border-bottom: none; }
.cdx-contact-detail-icon {
  width: 40px; height: 40px; border-radius: 10px;
  background: rgba(255,255,255,.04); border: 1px solid var(--glass-border);
  display:grid; place-items:center; flex-shrink: 0;
}
.cdx-contact-detail h4 { font-family:'JetBrains Mono'; font-size: 11px; letter-spacing:.1em; text-transform: uppercase; color: var(--fg-mute); margin: 0 0 4px; font-weight: 500; }
.cdx-contact-detail p { font-size: 15px; color: var(--fg); margin: 0; }

.cdx-form {
  padding: 32px; border-radius: 24px;
  background: var(--glass-bg);
  border: 1px solid var(--glass-border);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
}
.cdx-form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
@media (max-width: 600px) { .cdx-form-row { grid-template-columns: 1fr; } }
.cdx-field { margin-bottom: 18px; }
.cdx-field label {
  display:block; font-family:'JetBrains Mono'; font-size: 11px;
  letter-spacing:.1em; text-transform: uppercase;
  color: var(--fg-dim); margin-bottom: 8px;
}
.cdx-field input, .cdx-field textarea, .cdx-field select {
  width: 100%; padding: 14px 16px;
  background: rgba(0,0,0,.3);
  border: 1px solid var(--glass-border);
  border-radius: 12px;
  color: var(--fg); font-family: 'Inter'; font-size: 15px;
  transition: border-color .2s, background .2s;
  appearance: none;
}
.cdx-field input:focus, .cdx-field textarea:focus, .cdx-field select:focus {
  outline: none; border-color: var(--accent-1);
  background: rgba(168,85,247,.06);
  box-shadow: 0 0 0 3px rgba(168,85,247,.15);
}
.cdx-field textarea { resize: vertical; min-height: 120px; font-family: 'Inter'; }
.cdx-field input::placeholder, .cdx-field textarea::placeholder { color: var(--fg-mute); }
.cdx-form-submit { display:flex; align-items:center; justify-content:space-between; gap: 16px; margin-top: 24px; }
.cdx-form-submit .note { font-family:'JetBrains Mono'; font-size: 11px; color: var(--fg-mute); }

.cdx-form-success {
  padding: 64px 32px; text-align: center;
}
.cdx-form-success .icon {
  width: 64px; height: 64px; border-radius: 50%; margin: 0 auto 24px;
  background: linear-gradient(135deg, var(--accent-1), var(--accent-2));
  display:grid; place-items: center; font-size: 28px;
}
.cdx-form-success h3 { font-family: 'Space Grotesk'; font-size: 28px; margin: 0 0 8px; color: #fff; letter-spacing:-.02em; }
.cdx-form-success p { font-size: 15px; color: var(--fg-dim); margin: 0; }

/* ===== Footer ===== */
.cdx-footer {
  position: relative; padding: 80px 0 32px;
  border-top: 1px solid var(--glass-border);
  margin-top: 40px;
}
.cdx-footer-grid {
  display: grid; grid-template-columns: 2fr 1fr 1fr 1fr; gap: 48px;
  padding-bottom: 60px;
}
@media (max-width: 900px) { .cdx-footer-grid { grid-template-columns: 1fr 1fr; gap: 32px; } }
@media (max-width: 540px) { .cdx-footer-grid { grid-template-columns: 1fr; } }
.cdx-footer-brand h4 { font-family:'Space Grotesk'; font-size: 22px; margin: 16px 0 12px; color: #fff; }
.cdx-footer-brand p { color: var(--fg-dim); font-size: 14px; max-width: 320px; }
.cdx-footer-col h5 { font-family:'JetBrains Mono'; font-size: 11px; letter-spacing:.12em; text-transform: uppercase; color: var(--fg-mute); margin: 8px 0 16px; font-weight: 600; }
.cdx-footer-col a { display:block; color: var(--fg-dim); text-decoration: none; font-size: 14px; padding: 6px 0; transition: color .2s; }
.cdx-footer-col a:hover { color: var(--fg); }
.cdx-footer-bottom {
  padding-top: 32px; border-top: 1px solid var(--glass-border);
  display:flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 16px;
  font-family:'JetBrains Mono'; font-size: 12px; color: var(--fg-mute);
}
.cdx-socials { display:flex; gap: 10px; }
.cdx-socials a {
  width: 36px; height: 36px; border-radius: 10px;
  background: rgba(255,255,255,.04); border: 1px solid var(--glass-border);
  display: grid; place-items: center;
  transition: background .2s, transform .15s;
}
.cdx-socials a:hover { background: rgba(168,85,247,.15); transform: translateY(-2px); }

/* Marquee strip */
.cdx-marquee {
  overflow: hidden; padding: 48px 0;
  border-top: 1px solid var(--glass-border);
  border-bottom: 1px solid var(--glass-border);
  background: rgba(0,0,0,.25);
  position: relative;
}
.cdx-marquee::before, .cdx-marquee::after {
  content:""; position:absolute; top:0; bottom:0; width: 160px; z-index: 2; pointer-events: none;
}
.cdx-marquee::before { left: 0; background: linear-gradient(to right, #07060d, transparent); }
.cdx-marquee::after { right: 0; background: linear-gradient(to left, #07060d, transparent); }
.cdx-marquee-label {
  position: relative; z-index: 3; text-align: center;
  font-family: 'JetBrains Mono'; font-size: 11px; letter-spacing: .2em;
  text-transform: uppercase; color: var(--fg-mute);
  margin-bottom: 32px;
}
.cdx-marquee-label::before, .cdx-marquee-label::after {
  content:""; display:inline-block; width: 40px; height:1px;
  background: var(--glass-border); vertical-align: middle; margin: 0 16px;
}
.cdx-marquee-track {
  display: flex; gap: 80px; align-items: center;
  animation: marquee 28s linear infinite;
  width: max-content;
}
@keyframes marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }
.cdx-marquee-logo {
  display: flex; align-items: center; justify-content: center;
  height: 80px; flex-shrink: 0;
  opacity: .55; transition: opacity .3s, filter .4s;
}
.cdx-marquee-logo:hover { opacity: 1; }
.cdx-marquee-logo img {
  height: 100%; width: auto; max-width: 220px; object-fit: contain;
  transition: filter .4s;
}
.cdx-marquee.bw .cdx-marquee-logo img {
  filter: grayscale(100%) brightness(1.6) contrast(1.1);
}
.cdx-marquee.bw .cdx-marquee-logo { opacity: .75; }
.cdx-marquee.bw .cdx-marquee-logo:hover img {
  filter: grayscale(0%) brightness(1) contrast(1);
}

/* Reveal animation */
.reveal { opacity: 0; transform: translateY(24px); transition: opacity .8s ease, transform .8s ease; }
.reveal.in { opacity: 1; transform: translateY(0); }

/* Tweaks panel adjustments for dark mode */
.twk-panel { color-scheme: light; }

/* ===== Custom cursor ===== */
body.cdx-custom-cursor-on,
body.cdx-custom-cursor-on * { cursor: none !important; }
.cdx-cursor-dot, .cdx-cursor-ring {
  position: fixed; top: 0; left: 0; pointer-events: none;
  z-index: 99999; will-change: transform;
}
.cdx-cursor-dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: #fff;
  box-shadow: 0 0 12px rgba(255,255,255,.8);
  mix-blend-mode: difference;
}
.cdx-cursor-ring {
  width: 36px; height: 36px; border-radius: 50%;
  border: 1px solid rgba(168,85,247,.6);
  box-shadow: 0 0 24px rgba(168,85,247,.4);
  transition: width .2s ease, height .2s ease, background .2s ease, border-color .2s ease;
}
.cdx-cursor-ring.hover {
  width: 60px; height: 60px;
  background: rgba(168,85,247,.12);
  border-color: rgba(217,70,239,.7);
  box-shadow: 0 0 40px rgba(217,70,239,.5);
}

/* ===== Scroll progress (top bar) ===== */
.cdx-scroll-progress-bar-top {
  position: fixed; top: 0; left: 0; right: 0; height: 2px;
  z-index: 100; background: transparent; pointer-events: none;
}
.cdx-scroll-progress-bar-fill-top {
  height: 100%; width: 0%;
  background: linear-gradient(to right, var(--accent-1), var(--accent-2), var(--accent-3));
  box-shadow: 0 0 12px var(--accent-1);
  transition: width .08s linear;
}

/* ===== Typewriter ===== */
.cdx-typewriter { display: inline; }
.cdx-typewriter-caret {
  display: inline-block; width: 3px; height: 0.85em;
  background: var(--accent-1); margin-left: 6px;
  vertical-align: -0.08em; box-shadow: 0 0 10px var(--accent-1);
  animation: caret-blink 0.9s infinite;
}
@keyframes caret-blink { 0%,49% { opacity: 1; } 50%,100% { opacity: 0; } }

/* ===== Aurora background ===== */
.cdx-aurora {
  position: absolute; inset: 0; overflow: hidden; z-index: 0;
  background: radial-gradient(ellipse at center, #100726 0%, #07060d 80%);
}
.cdx-aurora-blob {
  position: absolute; border-radius: 50%; filter: blur(80px);
  opacity: .55; mix-blend-mode: screen;
  will-change: transform;
}
.cdx-aurora-blob.b1 {
  width: 60vw; height: 60vw; left: -10vw; top: -20vw;
  background: radial-gradient(circle, var(--accent-1) 0%, transparent 60%);
  animation: aurora-1 22s ease-in-out infinite;
}
.cdx-aurora-blob.b2 {
  width: 55vw; height: 55vw; right: -15vw; top: -10vw;
  background: radial-gradient(circle, var(--accent-2) 0%, transparent 60%);
  animation: aurora-2 28s ease-in-out infinite;
}
.cdx-aurora-blob.b3 {
  width: 50vw; height: 50vw; left: 20vw; bottom: -15vw;
  background: radial-gradient(circle, var(--accent-3) 0%, transparent 60%);
  animation: aurora-3 25s ease-in-out infinite;
}
.cdx-aurora-blob.b4 {
  width: 45vw; height: 45vw; right: 5vw; bottom: 5vw;
  background: radial-gradient(circle, var(--accent-1) 0%, transparent 60%);
  opacity: .35;
  animation: aurora-1 30s ease-in-out infinite reverse;
}
@keyframes aurora-1 {
  0%, 100% { transform: translate(0, 0) scale(1); }
  33% { transform: translate(20vw, 10vh) scale(1.1); }
  66% { transform: translate(-10vw, 20vh) scale(0.95); }
}
@keyframes aurora-2 {
  0%, 100% { transform: translate(0, 0) scale(1); }
  50% { transform: translate(-25vw, 15vh) scale(1.15); }
}
@keyframes aurora-3 {
  0%, 100% { transform: translate(0, 0) scale(1); }
  40% { transform: translate(15vw, -10vh) scale(1.05); }
  80% { transform: translate(-20vw, -15vh) scale(0.9); }
}
.cdx-aurora-grain {
  position: absolute; inset: 0;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='200' height='200'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/></filter><rect width='100%25' height='100%25' filter='url(%23n)' opacity='.5'/></svg>");
  opacity: .06; mix-blend-mode: overlay; pointer-events: none;
}

/* ===== AI Chat ===== */
.cdx-chat-fab {
  position: fixed; bottom: 24px; left: 24px; z-index: 200;
  width: 56px; height: 56px; border-radius: 50%;
  background: linear-gradient(135deg, var(--accent-1), var(--accent-2));
  border: none; color: #fff;
  display: grid; place-items: center;
  cursor: pointer;
  box-shadow: 0 8px 32px -8px rgba(168,85,247,.7), inset 0 1px 0 rgba(255,255,255,.3);
  transition: transform .2s ease, box-shadow .3s ease;
}
.cdx-chat-fab:hover { transform: scale(1.06) translateY(-2px); box-shadow: 0 12px 40px -8px rgba(168,85,247,.9); }
.cdx-chat-fab.open { background: rgba(20,15,30,.95); }
.cdx-chat-label {
  position: fixed; bottom: 32px; left: 90px; z-index: 999;
  background: rgba(168,85,247,.15); border: 1px solid rgba(168,85,247,.4);
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  color: #e6e3f2; font-family: 'Inter', sans-serif; font-size: 13px;
  padding: 8px 14px; border-radius: 20px; cursor: pointer; white-space: nowrap;
  animation: label-in .4s cubic-bezier(.2,.8,.2,1);
}
.cdx-chat-label:hover { background: rgba(168,85,247,.25); border-color: rgba(168,85,247,.7); }
@keyframes label-in {
  from { opacity: 0; transform: translateX(-12px); }
  to   { opacity: 1; transform: translateX(0); }
}
.cdx-chat-fab-pulse {
  position: absolute; inset: -4px; border-radius: 50%;
  border: 2px solid var(--accent-1);
  animation: chat-pulse 2s ease-out infinite;
}
@keyframes chat-pulse {
  0% { transform: scale(.9); opacity: 1; }
  100% { transform: scale(1.6); opacity: 0; }
}
.cdx-chat-panel {
  position: fixed; bottom: 96px; left: 24px; z-index: 199;
  width: 380px; max-width: calc(100vw - 48px);
  height: 540px; max-height: calc(100vh - 130px);
  display: flex; flex-direction: column;
  background: rgba(15, 12, 28, 0.92);
  border: 1px solid var(--glass-border);
  border-radius: 20px; overflow: hidden;
  backdrop-filter: blur(28px) saturate(160%);
  -webkit-backdrop-filter: blur(28px) saturate(160%);
  box-shadow: 0 24px 60px -12px rgba(0,0,0,.6), 0 0 80px -20px rgba(168,85,247,.3);
  animation: chat-slide-in .3s cubic-bezier(.2,.8,.2,1);
}
@keyframes chat-slide-in {
  from { opacity: 0; transform: translateY(12px) scale(.96); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}
.cdx-chat-header {
  padding: 16px 18px; border-bottom: 1px solid var(--glass-border);
  display: flex; align-items: center; gap: 12px;
  background: linear-gradient(to bottom, rgba(168,85,247,.08), transparent);
}
.cdx-chat-avatar {
  width: 40px; height: 40px; border-radius: 50%;
  background: rgba(255,255,255,.05);
  border: 1px solid var(--glass-border);
  display: grid; place-items: center; overflow: hidden;
  position: relative;
}
.cdx-chat-avatar img { width: 70%; height: 70%; object-fit: contain; }
.cdx-chat-avatar::after {
  content: ""; position: absolute; bottom: -2px; right: -2px;
  width: 12px; height: 12px; border-radius: 50%;
  background: #10b981; border: 2px solid rgba(15,12,28,1);
}
.cdx-chat-title { font-family: 'Space Grotesk'; font-size: 16px; font-weight: 500; color: #fff; }
.cdx-chat-status { font-family: 'JetBrains Mono'; font-size: 10px; color: var(--fg-mute); display:flex; align-items:center; gap: 6px; letter-spacing: .05em; }
.cdx-chat-status-dot { width: 6px; height: 6px; border-radius: 50%; background: #10b981; box-shadow: 0 0 8px #10b981; }
.cdx-chat-messages {
  flex: 1; overflow-y: auto; padding: 18px;
  display: flex; flex-direction: column; gap: 12px;
  scrollbar-width: thin; scrollbar-color: rgba(168,85,247,.3) transparent;
}
.cdx-chat-messages::-webkit-scrollbar { width: 4px; }
.cdx-chat-messages::-webkit-scrollbar-thumb { background: rgba(168,85,247,.3); border-radius: 2px; }
.cdx-chat-msg {
  max-width: 85%; padding: 10px 14px;
  border-radius: 14px; font-size: 14px; line-height: 1.45;
  animation: msg-in .25s ease;
}
@keyframes msg-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } }
.cdx-chat-msg-user {
  align-self: flex-end;
  background: linear-gradient(135deg, var(--accent-1), var(--accent-2));
  color: #fff;
  border-bottom-right-radius: 4px;
}
.cdx-chat-msg-assistant {
  align-self: flex-start;
  background: rgba(255,255,255,.06);
  border: 1px solid var(--glass-border);
  color: var(--fg);
  border-bottom-left-radius: 4px;
}
.cdx-chat-typing { padding: 14px 16px; display: flex; gap: 4px; }
.cdx-chat-typing span {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--accent-1);
  animation: typing-bounce 1.4s ease-in-out infinite;
}
.cdx-chat-typing span:nth-child(2) { animation-delay: .2s; }
.cdx-chat-typing span:nth-child(3) { animation-delay: .4s; }
@keyframes typing-bounce {
  0%, 60%, 100% { transform: translateY(0); opacity: .4; }
  30% { transform: translateY(-6px); opacity: 1; }
}
.cdx-chat-input-row {
  padding: 12px; display: flex; gap: 8px;
  border-top: 1px solid var(--glass-border);
  background: rgba(0,0,0,.2);
}
.cdx-chat-input-row input {
  flex: 1; padding: 10px 14px;
  background: rgba(0,0,0,.3);
  border: 1px solid var(--glass-border);
  border-radius: 10px;
  color: var(--fg); font-family: 'Inter'; font-size: 14px;
}
.cdx-chat-input-row input:focus { outline: none; border-color: var(--accent-1); }
.cdx-chat-input-row button {
  width: 40px; height: 40px; border-radius: 10px;
  background: linear-gradient(135deg, var(--accent-1), var(--accent-2));
  border: none; color: #fff; cursor: pointer;
  display: grid; place-items: center;
  transition: opacity .2s, transform .15s;
}
.cdx-chat-input-row button:disabled { opacity: .4; cursor: not-allowed; }
.cdx-chat-input-row button:not(:disabled):hover { transform: scale(1.05); }
.cdx-chat-footer {
  padding: 8px 14px; font-family: 'JetBrains Mono'; font-size: 9px;
  color: var(--fg-mute); text-align: center; letter-spacing: .05em;
  border-top: 1px solid var(--glass-border);
}

@media (max-width: 480px) {
  .cdx-chat-panel { width: calc(100vw - 24px); left: 12px; bottom: 88px; }
  .cdx-chat-fab { bottom: 16px; left: 16px; }
}
`;

/* ────────────────────────────────────────────────────────────
   Icons
   ──────────────────────────────────────────────────────────── */
const Icon = {
  arrow: (p) => <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" {...p}><path d="M5 12h14M13 5l7 7-7 7"/></svg>,
  web: (p) => <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="url(#g1)" strokeWidth="1.5" {...p}>
    <defs><linearGradient id="g1" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stopColor="var(--accent-1)"/><stop offset="1" stopColor="var(--accent-2)"/></linearGradient></defs>
    <circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18"/></svg>,
  app: (p) => <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="url(#g2)" strokeWidth="1.5" {...p}>
    <defs><linearGradient id="g2" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stopColor="var(--accent-1)"/><stop offset="1" stopColor="var(--accent-3)"/></linearGradient></defs>
    <rect x="7" y="2" width="10" height="20" rx="2"/><line x1="11" y1="18" x2="13" y2="18"/></svg>,
  code: (p) => <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="url(#g3)" strokeWidth="1.5" {...p}>
    <defs><linearGradient id="g3" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stopColor="var(--accent-2)"/><stop offset="1" stopColor="var(--accent-3)"/></linearGradient></defs>
    <polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>,
  mail: (p) => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" {...p}><path d="M4 6h16v12H4z"/><path d="M4 7l8 6 8-6"/></svg>,
  phone: (p) => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" {...p}><path d="M22 16.92v3a2 2 0 01-2.18 2 19.79 19.79 0 01-8.63-3.07 19.5 19.5 0 01-6-6 19.79 19.79 0 01-3.07-8.67A2 2 0 014.11 2h3a2 2 0 012 1.72c.13.96.37 1.9.7 2.81a2 2 0 01-.45 2.11L8.09 9.91a16 16 0 006 6l1.27-1.27a2 2 0 012.11-.45c.91.33 1.85.57 2.81.7A2 2 0 0122 16.92z"/></svg>,
  pin: (p) => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" {...p}><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0118 0z"/><circle cx="12" cy="10" r="3"/></svg>,
  ig: (p) => <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" {...p}><rect x="2" y="2" width="20" height="20" rx="5"/><circle cx="12" cy="12" r="4"/><circle cx="17.5" cy="6.5" r="1" fill="currentColor"/></svg>,
  wa: (p) => <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" {...p}><path d="M17.5 14.4c-.3-.1-1.8-.9-2-1s-.5-.1-.7.2-.8 1-.9 1.2c-.2.2-.3.2-.6.1-.3-.1-1.3-.5-2.4-1.5-.9-.8-1.5-1.8-1.7-2.1-.2-.3 0-.5.1-.6.1-.1.3-.4.4-.5.1-.2.2-.3.3-.5.1-.2 0-.4 0-.5 0-.1-.7-1.7-.9-2.3-.2-.6-.5-.5-.7-.5h-.6c-.2 0-.5.1-.8.4s-1 1-1 2.5 1.1 2.9 1.2 3c.1.2 2.1 3.3 5.2 4.6.7.3 1.3.5 1.7.6.7.2 1.4.2 1.9.1.6-.1 1.8-.7 2-1.4.2-.7.2-1.3.2-1.4-.1-.1-.3-.2-.7-.4zM12 2A10 10 0 002 12c0 1.8.5 3.5 1.3 5L2 22l5.2-1.4A10 10 0 1012 2z"/></svg>,
  li: (p) => <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" {...p}><path d="M19 3H5a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2V5a2 2 0 00-2-2zM8.4 17.3H6V10h2.4v7.3zM7.2 9a1.4 1.4 0 110-2.8 1.4 1.4 0 010 2.8zm10.6 8.3h-2.4v-3.6c0-1 0-2.2-1.3-2.2s-1.6 1-1.6 2.1v3.7H10V10h2.3v1h0a2.5 2.5 0 012.3-1.3c2.5 0 2.9 1.6 2.9 3.7v4z"/></svg>,
  gh: (p) => <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" {...p}><path d="M12 2a10 10 0 00-3.2 19.5c.5.1.7-.2.7-.5v-1.7c-2.8.6-3.4-1.4-3.4-1.4-.4-1.2-1.1-1.5-1.1-1.5-.9-.6.1-.6.1-.6 1 .1 1.5 1 1.5 1 .9 1.6 2.4 1.1 3 .9.1-.7.4-1.1.7-1.4-2.2-.3-4.6-1.1-4.6-5a3.9 3.9 0 011-2.7c-.1-.3-.5-1.3.1-2.7 0 0 .8-.3 2.7 1a9.5 9.5 0 015 0c1.9-1.3 2.7-1 2.7-1 .5 1.4.2 2.4.1 2.7a3.9 3.9 0 011 2.7c0 3.9-2.4 4.7-4.6 5 .4.3.7.9.7 1.9v2.7c0 .3.2.6.7.5A10 10 0 0012 2z"/></svg>,
  spark: () => <span style={{fontSize:18}}>✦</span>,
};

/* ────────────────────────────────────────────────────────────
   Components
   ──────────────────────────────────────────────────────────── */

function Logo({ size = 'sm' }) {
  if (size === 'lg') {
    return (
      <div className="cdx-nav-logo" style={{ gap: 14, fontSize: 22 }}>
        <div className="mark" style={{ width: 44, height: 44 }} />
        <span style={{ fontWeight: 600, letterSpacing: '-0.03em' }}>codexa</span>
      </div>
    );
  }
  return (
    <div className="cdx-nav-logo">
      <div className="mark" />
      <span>codexa</span>
    </div>
  );
}

function Nav() {
  const y = useScrollY();
  return (
    <nav className={"cdx-nav " + (y > 40 ? "solid" : "")}>
      <Logo />
      <div className="cdx-nav-links">
        <a href="#about">Nosotros</a>
        <a href="#services">Servicios</a>
        <a href="#projects">Proyectos</a>
        <a href="#contact">Contacto</a>
      </div>
      <a href="#contact" className="cdx-btn cdx-btn-primary">
        Iniciar proyecto <Icon.arrow />
      </a>
    </nav>
  );
}

function HeroOrbVariant() {
  return (
    <>
      <div className="cdx-hero-grid"></div>
      <div className="cdx-hero-orb">
        <svg className="cdx-orb-svg" viewBox="-100 -100 200 200">
          <defs>
            <linearGradient id="orbGrad" x1="0" y1="0" x2="1" y2="1">
              <stop offset="0" stopColor="var(--accent-1)" stopOpacity="0.8"/>
              <stop offset="0.5" stopColor="var(--accent-2)" stopOpacity="0.5"/>
              <stop offset="1" stopColor="var(--accent-3)" stopOpacity="0.8"/>
            </linearGradient>
            <radialGradient id="orbCore">
              <stop offset="0" stopColor="var(--accent-1)" stopOpacity="0.4"/>
              <stop offset="1" stopColor="transparent"/>
            </radialGradient>
          </defs>
          <circle r="80" fill="url(#orbCore)"/>
          {[...Array(6)].map((_, i) => (
            <ellipse key={i} rx="80" ry={20 + i * 8} fill="none" stroke="url(#orbGrad)" strokeWidth="0.5" strokeOpacity={0.4 - i * 0.05}
              transform={`rotate(${i * 30})`} />
          ))}
        </svg>
      </div>
    </>
  );
}

function HeroGradientVariant() {
  return (
    <div className="cdx-hero-bg">
      <div style={{
        position:'absolute', inset:0,
        background: `
          radial-gradient(circle at 20% 30%, rgba(168,85,247,.4), transparent 40%),
          radial-gradient(circle at 80% 20%, rgba(59,130,246,.4), transparent 40%),
          radial-gradient(circle at 50% 80%, rgba(217,70,239,.3), transparent 50%)
        `
      }}/>
    </div>
  );
}

function HeroVideo() {
  const ref = useRef(null);
  // Skip the white flash at the very start of the video file (~0.4s)
  const START_OFFSET = 0.4;
  useEffect(() => {
    const v = ref.current;
    if (!v) return;
    const seekStart = () => {
      try { v.currentTime = START_OFFSET; } catch(e) {}
    };
    const onLoaded = () => { seekStart(); v.play().catch(() => {}); };
    const onEnded = () => { seekStart(); v.play().catch(() => {}); };
    v.addEventListener('loadedmetadata', onLoaded);
    v.addEventListener('ended', onEnded);
    if (v.readyState >= 1) onLoaded();
    return () => {
      v.removeEventListener('loadedmetadata', onLoaded);
      v.removeEventListener('ended', onEnded);
    };
  }, []);
  return (
    <div className="cdx-hero-bg">
      <video ref={ref} className="cdx-hero-video" muted playsInline preload="auto">
        <source src="assets/hero-video.mp4" type="video/mp4" />
      </video>
    </div>
  );
}

function Hero({ variant, showVideo, typewriter }) {
  return (
    <section className="cdx-hero" id="top">
      {variant === 'video' && showVideo && <HeroVideo />}
      {(variant === 'video' && !showVideo) && <HeroGradientVariant />}
      {variant === 'gradient' && <HeroGradientVariant />}
      {variant === 'orb' && <HeroOrbVariant />}
      {variant === 'aurora' && <AuroraBackground />}

      <div className="cdx-hero-content">
        <div className="cdx-hero-logo">
          <img src="assets/codexa-icon.png" alt="Codexa" />
        </div>
        <span className="cdx-eyebrow"><span className="dot"></span>Software · Solutions · Impact</span>
        <h1>
          Construimos<br/>
          <em>{typewriter ? <Typewriter text="software a medida" speed={70} startDelay={400} /> : 'software a medida'}</em><br/>
          que mueve negocios.
        </h1>
        <p className="lead">
          Somos un estudio de desarrollo enfocado en webs, aplicaciones y plataformas
          que combinan diseño, ingeniería e identidad de marca.
        </p>
        <div className="cdx-hero-cta">
          <a href="#contact" className="cdx-btn cdx-btn-primary">Empezar un proyecto <Icon.arrow /></a>
          <a href="#projects" className="cdx-btn cdx-btn-ghost">Ver nuestro trabajo</a>
        </div>
      </div>

      <div className="cdx-hero-scroll-cue">
        Scroll
        <div className="line"></div>
      </div>
    </section>
  );
}

function ScrollVideoSection({ scrollSpeed }) {
  const wrapRef = useRef(null);
  const videoRef = useRef(null);
  const [progress, setProgress] = useState(0);
  const [duration, setDuration] = useState(0);

  useEffect(() => {
    const v = videoRef.current;
    if (!v) return;
    const onMeta = () => setDuration(v.duration || 0);
    v.addEventListener('loadedmetadata', onMeta);
    if (v.readyState >= 1) onMeta();
    return () => v.removeEventListener('loadedmetadata', onMeta);
  }, []);

  useEffect(() => {
    let raf = 0;
    const update = () => {
      raf = requestAnimationFrame(update);
      const el = wrapRef.current;
      const v = videoRef.current;
      if (!el || !v || !v.duration) return;
      const rect = el.getBoundingClientRect();
      const total = rect.height - window.innerHeight;
      const scrolled = Math.max(0, Math.min(total, -rect.top));
      const p = total > 0 ? scrolled / total : 0;
      setProgress(p);
      // Map scroll progress to video time, multiplied by scrollSpeed (clamped to valid range)
      const target = Math.max(0, Math.min(v.duration - 0.05, p * v.duration * scrollSpeed));
      // Only update when meaningfully different to avoid jitter
      if (Math.abs(v.currentTime - target) > 0.03) {
        try { v.currentTime = target; } catch(e) {}
      }
    };
    raf = requestAnimationFrame(update);
    return () => cancelAnimationFrame(raf);
  }, [scrollSpeed]);

  return (
    <section className="cdx-scroll-video" ref={wrapRef}>
      <div className="cdx-scroll-video-sticky">
        <video ref={videoRef} muted playsInline preload="auto">
          <source src="assets/hero-video.mp4" type="video/mp4" />
        </video>
        <div className="cdx-scroll-video-overlay">
          <h2>
            Diseñamos.<br/>
            <em style={{
              background: 'linear-gradient(120deg, var(--accent-1), var(--accent-2), var(--accent-3))',
              WebkitBackgroundClip: 'text', backgroundClip: 'text', color: 'transparent',
              fontStyle: 'normal'
            }}>Programamos.</em><br/>
            Hacemos crecer.
          </h2>
        </div>
        <div className="cdx-scroll-progress">
          <div style={{display:'flex', justifyContent:'space-between'}}>
            <span>Scroll · {Math.round(progress * 100).toString().padStart(2,'0')}%</span>
            <span>{(progress * (duration || 0)).toFixed(2)}s / {(duration || 0).toFixed(2)}s</span>
          </div>
          <div className="cdx-scroll-progress-bar">
            <div className="cdx-scroll-progress-bar-fill" style={{width: `${progress * 100}%`}}/>
          </div>
        </div>
      </div>
    </section>
  );
}

function Reveal({ children, delay = 0 }) {
  const ref = useRef(null);
  const seen = useInView(ref);
  return (
    <div ref={ref} className={"reveal " + (seen ? "in" : "")} style={{ transitionDelay: delay + 'ms' }}>
      {children}
    </div>
  );
}

function AboutSection() {
  return (
    <section className="cdx-section" id="about">
      <div className="cdx-wrap">
        <div className="cdx-about-grid">
          <Reveal>
            <div className="cdx-section-header" style={{ marginBottom: 0 }}>
              <div className="cdx-section-tag">/ Sobre nosotros</div>
              <h2>Pensamos en producto, <em>entregamos software.</em></h2>
              <p className="sub">
                Codexa es un estudio digital que combina diseño, desarrollo e identidad
                para lanzar plataformas que se ven bien, funcionan rápido y crecen con tu negocio.
                Trabajamos con equipos chicos, marcas y emprendimientos que buscan algo serio.
              </p>
            </div>
            <div className="cdx-about-stats">
              <div className="cdx-stat">
                <div className="cdx-stat-num"><em>+15</em></div>
                <div className="cdx-stat-label">Proyectos entregados</div>
              </div>
              <div className="cdx-stat">
                <div className="cdx-stat-num"><em>3</em></div>
                <div className="cdx-stat-label">Productos en vivo</div>
              </div>
              <div className="cdx-stat">
                <div className="cdx-stat-num"><em>100%</em></div>
                <div className="cdx-stat-label">A medida</div>
              </div>
              <div className="cdx-stat">
                <div className="cdx-stat-num"><em>24/7</em></div>
                <div className="cdx-stat-label">Soporte continuo</div>
              </div>
            </div>
          </Reveal>

          <Reveal delay={150}>
            <div className="cdx-about-visual">
              <div className="cdx-about-code">
                <div><span className="ln-num">01</span><span className="com">// codexa.studio</span></div>
                <div><span className="ln-num">02</span><span className="kw">const</span> codexa = <span className="fn">build</span>({'{'}</div>
                <div><span className="ln-num">03</span>{'  '}mission: <span className="str">"software con propósito"</span>,</div>
                <div><span className="ln-num">04</span>{'  '}stack: [<span className="str">"react"</span>, <span className="str">"node"</span>, <span className="str">"figma"</span>],</div>
                <div><span className="ln-num">05</span>{'  '}values: {'{'}</div>
                <div><span className="ln-num">06</span>{'    '}design: <span className="str">"obsesivo"</span>,</div>
                <div><span className="ln-num">07</span>{'    '}code:   <span className="str">"prolijo"</span>,</div>
                <div><span className="ln-num">08</span>{'    '}delivery: <span className="str">"a tiempo"</span></div>
                <div><span className="ln-num">09</span>{'  '}{'}'},</div>
                <div><span className="ln-num">10</span>{'  '}ready: <span className="kw">true</span></div>
                <div><span className="ln-num">11</span>{'}'});</div>
                <div><span className="ln-num">12</span></div>
                <div><span className="ln-num">13</span><span className="fn">codexa</span>.<span className="fn">ship</span>(); <span className="com">// ✓</span></div>
              </div>
            </div>
          </Reveal>
        </div>
      </div>
    </section>
  );
}

function ServicesSection() {
  const services = [
    {
      icon: <Icon.web/>, title: "Sitios web a medida",
      desc: "Landings, portfolios y sitios corporativos pensados para convertir. Diseño único, performance impecable.",
      tags: ["Next.js", "Astro", "WordPress", "SEO"]
    },
    {
      icon: <Icon.app/>, title: "Aplicaciones web",
      desc: "Plataformas SaaS, dashboards y herramientas internas. Lógica robusta, UX simple para tus usuarios.",
      tags: ["React", "Node", "Postgres", "Auth"]
    },
    {
      icon: <Icon.code/>, title: "Software a medida",
      desc: "Sistemas hechos a la medida del negocio: control de stock, gestión, eventos, ticketing, lo que necesites.",
      tags: ["APIs", "Integraciones", "MercadoPago", "Stripe"]
    }
  ];

  return (
    <section className="cdx-section" id="services" style={{ paddingTop: 80 }}>
      <div className="cdx-wrap">
        <Reveal>
          <div className="cdx-section-header">
            <div className="cdx-section-tag">/ Servicios</div>
            <h2>Tres formas de <em>construir contigo.</em></h2>
            <p className="sub">
              Desde una landing simple hasta una plataforma compleja. Elegimos el stack
              según el problema, no al revés.
            </p>
          </div>
        </Reveal>

        <div className="cdx-services-grid">
          {services.map((s, i) => (
            <Reveal key={s.title} delay={i * 120}>
              <div className="cdx-service">
                <div className="cdx-service-icon">{s.icon}</div>
                <h3>{s.title}</h3>
                <p>{s.desc}</p>
                <div className="cdx-service-tags">
                  {s.tags.map(t => <span key={t}>{t}</span>)}
                </div>
              </div>
            </Reveal>
          ))}
        </div>
      </div>
    </section>
  );
}

const PROJECTS = [
  {
    key: "laborix", className: "proj-laborix",
    img: "assets/laborix-logo.png",
    name: "Laborix", url: "laborix.online",
    year: "2025",
    desc: "Plataforma de control de horarios y pagos para empresas. Gestión de empleados, registro horario, liquidaciones automáticas.",
    tags: ["SaaS", "Dashboard", "RR.HH.", "MercadoPago"]
  },
  {
    key: "oneticket", className: "proj-oneticket",
    img: "assets/oneticket-logo.png",
    name: "OneTicket", url: "oneticket.world",
    year: "2025",
    desc: "Plataforma de venta y gestión de tickets para eventos. Compra online, control de acceso por QR y panel para productores.",
    tags: ["Eventos", "Ticketing", "QR", "Pagos online"]
  },
  {
    key: "lascanas", className: "proj-lascanas",
    img: "assets/lascanas-logo.png",
    name: "Las Cañas Expo Car", url: "laexpocar.com",
    year: "2024",
    desc: "Sitio oficial del evento Expo Car de Las Cañas. Información, agenda, registro de expositores y venta de entradas.",
    tags: ["Evento", "Branding", "Landing", "Registro"]
  }
];

function ProjectCard({ p, layout, tiltEnabled }) {
  const tiltRef = useTilt(tiltEnabled && layout !== 'stack', 6);
  if (layout === 'stack') {
    return (
      <div className={"cdx-project " + p.className}>
        <div className="cdx-project-stack-row">
          <div className="cdx-project-visual">
            <img src={p.img} alt={p.name + " logo"} />
          </div>
          <div className="cdx-project-body">
            <div className="cdx-project-meta">
              <span>Caso · {p.year}</span>
              <span className="url">{p.url} ↗</span>
            </div>
            <h3>{p.name}</h3>
            <p>{p.desc}</p>
            <div className="cdx-project-tags">
              {p.tags.map(t => <span key={t}>{t}</span>)}
            </div>
            <a className="cdx-project-link" href={`https://${p.url}`} target="_blank" rel="noopener">Ver proyecto</a>
          </div>
        </div>
      </div>
    );
  }
  return (
    <div ref={tiltRef} className={"cdx-project " + p.className} style={{ transformStyle: 'preserve-3d' }}>
      <div className="cdx-project-visual">
        <img src={p.img} alt={p.name + " logo"} />
      </div>
      <div className="cdx-project-body">
        <div className="cdx-project-meta">
          <span>Caso · {p.year}</span>
          <span className="url">{p.url} ↗</span>
        </div>
        <h3>{p.name}</h3>
        <p>{p.desc}</p>
        <div className="cdx-project-tags">
          {p.tags.map(t => <span key={t}>{t}</span>)}
        </div>
        <a className="cdx-project-link" href={`https://${p.url}`} target="_blank" rel="noopener">Ver proyecto</a>
      </div>
    </div>
  );
}

function ProjectsSection({ layout, tiltEnabled }) {
  const cls = layout === 'horizontal' ? 'cdx-projects-horizontal'
    : layout === 'stack' ? 'cdx-projects-stack'
    : 'cdx-projects-grid';
  return (
    <section className="cdx-section" id="projects">
      <div className="cdx-wrap">
        <Reveal>
          <div className="cdx-section-header">
            <div className="cdx-section-tag">/ Proyectos</div>
            <h2>Algunos de los <em>productos que construimos.</em></h2>
            <p className="sub">
              Tres casos reales, en producción, usados por clientes y usuarios todos los días.
            </p>
          </div>
        </Reveal>
        <div className={cls}>
          {PROJECTS.map((p, i) => (
            <Reveal key={p.key} delay={i * 100}>
              <ProjectCard p={p} layout={layout} tiltEnabled={tiltEnabled} />
            </Reveal>
          ))}
        </div>
      </div>
    </section>
  );
}

function Marquee({ mode }) {
  const logos = [
    { src: 'assets/codexa-icon.png', alt: 'Codexa' },
    { src: 'assets/laborix-logo.png', alt: 'Laborix' },
    { src: 'assets/oneticket-logo.png', alt: 'OneTicket' },
    { src: 'assets/lascanas-logo.png', alt: 'Las Cañas Expo Car' }
  ];
  const loop = [...logos, ...logos, ...logos];
  return (
    <div className={"cdx-marquee " + (mode === 'bw' ? 'bw' : 'color')}>
      <div className="cdx-marquee-label">
        <span>Productos en producción</span>
      </div>
      <div className="cdx-marquee-track">
        {loop.map((logo, i) => (
          <div className="cdx-marquee-logo" key={i}>
            <img src={logo.src} alt={logo.alt} />
          </div>
        ))}
      </div>
    </div>
  );
}

function ContactForm() {
  const [data, setData] = useState({ name:'', email:'', company:'', type:'web', message:'' });
  const [sent, setSent] = useState(false);
  const [submitting, setSubmitting] = useState(false);

  const set = (k) => (e) => setData(d => ({ ...d, [k]: e.target.value }));
  const submit = (e) => {
    e.preventDefault();
    if (submitting) return;
    setSubmitting(true);
    // simulated
    setTimeout(() => { setSent(true); setSubmitting(false); }, 700);
  };

  if (sent) return (
    <div className="cdx-form cdx-form-success">
      <div className="icon">✓</div>
      <h3>¡Mensaje enviado!</h3>
      <p>Te respondemos por mail dentro de las próximas 24h hábiles.</p>
    </div>
  );

  return (
    <form className="cdx-form" onSubmit={submit}>
      <div className="cdx-form-row">
        <div className="cdx-field">
          <label>Nombre</label>
          <input required value={data.name} onChange={set('name')} placeholder="Tu nombre" />
        </div>
        <div className="cdx-field">
          <label>Email</label>
          <input required type="email" value={data.email} onChange={set('email')} placeholder="tu@email.com" />
        </div>
      </div>
      <div className="cdx-form-row">
        <div className="cdx-field">
          <label>Empresa / Proyecto</label>
          <input value={data.company} onChange={set('company')} placeholder="Opcional" />
        </div>
        <div className="cdx-field">
          <label>Tipo de proyecto</label>
          <select value={data.type} onChange={set('type')}>
            <option value="web">Sitio web / landing</option>
            <option value="app">Aplicación web</option>
            <option value="saas">Plataforma SaaS</option>
            <option value="custom">Software a medida</option>
            <option value="other">Otro</option>
          </select>
        </div>
      </div>
      <div className="cdx-field">
        <label>Contanos sobre tu idea</label>
        <textarea required value={data.message} onChange={set('message')} placeholder="¿Qué querés construir? Plazos, alcance, referencias…"></textarea>
      </div>
      <div className="cdx-form-submit">
        <span className="note">Respondemos en menos de 24h</span>
        <button type="submit" className="cdx-btn cdx-btn-primary" disabled={submitting}>
          {submitting ? 'Enviando…' : 'Enviar mensaje'} <Icon.arrow />
        </button>
      </div>
    </form>
  );
}

function ContactSection() {
  return (
    <section className="cdx-section" id="contact">
      <div className="cdx-wrap">
        <Reveal>
          <div className="cdx-section-header">
            <div className="cdx-section-tag">/ Contacto</div>
            <h2>Contanos qué <em>querés construir.</em></h2>
            <p className="sub">
              Mandanos los detalles y armamos una propuesta en 2-3 días.
              Sin compromiso, sin formularios eternos.
            </p>
          </div>
        </Reveal>
        <div className="cdx-contact-grid">
          <Reveal>
            <div className="cdx-contact-info">
              <div className="cdx-contact-detail">
                <div className="cdx-contact-detail-icon"><Icon.mail/></div>
                <div>
                  <h4>Email</h4>
                  <p>codexa.studiouy@gmail.com</p>
                </div>
              </div>
              <div className="cdx-contact-detail">
                <div className="cdx-contact-detail-icon"><Icon.wa/></div>
                <div>
                  <h4>WhatsApp</h4>
                  <p>+598 91 086 359</p>
                </div>
              </div>
              <div className="cdx-contact-detail">
                <div className="cdx-contact-detail-icon"><Icon.pin/></div>
                <div>
                  <h4>Ubicación</h4>
                  <p>Uruguay · 100% remoto</p>
                </div>
              </div>
              <div className="cdx-contact-detail">
                <div className="cdx-contact-detail-icon"><Icon.ig/></div>
                <div>
                  <h4>Instagram</h4>
                  <p>@codexa.group</p>
                </div>
              </div>
            </div>
          </Reveal>
          <Reveal delay={150}>
            <ContactForm />
          </Reveal>
        </div>
      </div>
    </section>
  );
}

function Footer() {
  return (
    <footer className="cdx-footer">
      <div className="cdx-wrap">
        <div className="cdx-footer-grid">
          <div className="cdx-footer-brand">
            <Logo size="lg" />
            <h4>Software. Solutions. Impact.</h4>
            <p>Estudio de desarrollo y diseño digital. Construimos productos a medida con foco en marca, UX y performance.</p>
          </div>
          <div className="cdx-footer-col">
            <h5>Estudio</h5>
            <a href="#about">Nosotros</a>
            <a href="#services">Servicios</a>
            <a href="#projects">Proyectos</a>
            <a href="#contact">Contacto</a>
          </div>
          <div className="cdx-footer-col">
            <h5>Productos</h5>
            <a href="https://laborix.online" target="_blank" rel="noopener">Laborix ↗</a>
            <a href="https://oneticket.world" target="_blank" rel="noopener">OneTicket ↗</a>
            <a href="https://laexpocar.com" target="_blank" rel="noopener">Las Cañas Expo ↗</a>
          </div>
          <div className="cdx-footer-col">
            <h5>Legal</h5>
            <a href="#">Privacidad</a>
            <a href="#">Términos</a>
            <a href="#">Cookies</a>
          </div>
        </div>
        <div className="cdx-footer-bottom">
          <span>© 2026 Codexa Studio · Hecho con ✦ en Uruguay</span>
          <div className="cdx-socials">
            <a href="https://instagram.com/codexa.group" target="_blank" rel="noopener" aria-label="Instagram"><Icon.ig/></a>
            <a href="https://wa.me/59891086359" target="_blank" rel="noopener" aria-label="WhatsApp"><Icon.wa/></a>
            <a href="mailto:codexa.studiouy@gmail.com" aria-label="Email"><Icon.mail/></a>

          </div>
        </div>
      </div>
    </footer>
  );
}

/* ────────────────────────────────────────────────────────────
   App
   ──────────────────────────────────────────────────────────── */

function App() {
  const [t, setTweak] = useTweaks(window.TWEAK_DEFAULTS);

  // Apply palette + tokens as CSS vars on :root
  useLayoutEffect(() => {
    const root = document.documentElement;
    const [a1, a2, a3] = t.palette || ['#a855f7','#3b82f6','#d946ef'];
    root.style.setProperty('--accent-1', a1);
    root.style.setProperty('--accent-2', a2);
    root.style.setProperty('--accent-3', a3);
    root.style.setProperty('--glass-blur', `${t.glassIntensity}px`);
  }, [t.palette, t.glassIntensity]);

  // Hide boot loader once mounted
  useEffect(() => {
    const boot = document.getElementById('boot');
    if (boot) {
      setTimeout(() => boot.classList.add('hide'), 400);
      setTimeout(() => boot.remove(), 1200);
    }
  }, []);

  return (
    <>
      <style dangerouslySetInnerHTML={{__html: codexaStyles}} />
      {t.scrollProgress && <ScrollProgress />}
      {t.customCursor && <CustomCursor enabled={t.customCursor} />}
      <Nav />
      <Hero variant={t.heroVariant} showVideo={t.showHeroVideo} typewriter={t.typewriter} />
      <AboutSection />
      <Marquee mode={t.marqueeMode} />
      <ServicesSection />
      <ProjectsSection layout={t.projectsLayout} tiltEnabled={t.tilt} />
      <ContactSection />
      <Footer />
      <AIChat enabled={t.aiChat} />

      <TweaksPanel title="Tweaks · Codexa">
        <TweakSection label="Hero" />
        <TweakRadio
          label="Variante"
          value={t.heroVariant}
          options={[
            { value: 'video', label: 'Video' },
            { value: 'aurora', label: 'Aurora' },
            { value: 'gradient', label: 'Gradient' },
            { value: 'orb', label: 'Orb' }
          ]}
          onChange={(v) => setTweak('heroVariant', v)}
        />
        <TweakToggle
          label="Mostrar video en hero"
          value={t.showHeroVideo}
          onChange={(v) => setTweak('showHeroVideo', v)}
        />
        <TweakToggle
          label="Typewriter en el título"
          value={t.typewriter}
          onChange={(v) => setTweak('typewriter', v)}
        />

        <TweakSection label="Interacciones" />
        <TweakToggle
          label="Custom cursor"
          value={t.customCursor}
          onChange={(v) => setTweak('customCursor', v)}
        />
        <TweakToggle
          label="Tilt 3D en cards"
          value={t.tilt}
          onChange={(v) => setTweak('tilt', v)}
        />
        <TweakToggle
          label="Barra de progreso"
          value={t.scrollProgress}
          onChange={(v) => setTweak('scrollProgress', v)}
        />
        <TweakToggle
          label="Chat AI flotante"
          value={t.aiChat}
          onChange={(v) => setTweak('aiChat', v)}
        />

        <TweakSection label="Aspecto" />
        <TweakColor
          label="Paleta de acento"
          value={t.palette}
          options={[
            ['#a855f7','#3b82f6','#d946ef'],
            ['#22d3ee','#3b82f6','#a855f7'],
            ['#f472b6','#a855f7','#3b82f6'],
            ['#10b981','#22d3ee','#3b82f6'],
            ['#f97316','#ef4444','#d946ef']
          ]}
          onChange={(v) => setTweak('palette', v)}
        />
        <TweakSlider
          label="Intensidad glass / blur"
          value={t.glassIntensity}
          min={0} max={48} step={2} unit="px"
          onChange={(v) => setTweak('glassIntensity', v)}
        />

        <TweakSection label="Proyectos" />
        <TweakRadio
          label="Layout cards"
          value={t.projectsLayout}
          options={[
            { value: 'grid', label: 'Grid' },
            { value: 'horizontal', label: 'Scroll' },
            { value: 'stack', label: 'Stack' }
          ]}
          onChange={(v) => setTweak('projectsLayout', v)}
        />
        <TweakRadio
          label="Carrusel logos"
          value={t.marqueeMode}
          options={[
            { value: 'bw', label: 'B/N' },
            { value: 'color', label: 'Color' }
          ]}
          onChange={(v) => setTweak('marqueeMode', v)}
        />
      </TweaksPanel>
    </>
  );
}

ReactDOM.createRoot(document.getElementById('app')).render(<App />);
