diff --git a/index.html b/index.html index 0f3ec40..a23e4ca 100644 --- a/index.html +++ b/index.html @@ -4,24 +4,28 @@ - Posadev - Evento Anual de Comunidades + Posadev 2026 — comunidades.join() - + - + - - + + - - + + + + + + diff --git a/public/media-kit/posadev-duck.png b/public/media-kit/posadev-duck.png new file mode 100644 index 0000000..97bfe73 Binary files /dev/null and b/public/media-kit/posadev-duck.png differ diff --git a/src/App.tsx b/src/App.tsx index 6618977..69e30a6 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -30,7 +30,7 @@ const App = () => {
-
+
} /> } /> diff --git a/src/components/BecomeSponsor.tsx b/src/components/BecomeSponsor.tsx index 3a0a198..c4d89c1 100644 --- a/src/components/BecomeSponsor.tsx +++ b/src/components/BecomeSponsor.tsx @@ -1,125 +1,284 @@ import React from 'react'; -import {Download, CheckCircle, Building, Mail, MessageSquare} from 'lucide-react'; -import {useToast} from '@/hooks/use-toast'; -import becomeSponsorImage from '@/img/become_sponsor.jpeg'; -import DuckIcon from "@/components/ui/duckIcon.tsx"; - -const BecomeSponsor = () => { - const {toast} = useToast(); - - const handleBrochureClick = () => { - toast({ - title: "Descargando brochure...", - description: "El documento se está descargando a tu dispositivo.", - }); - }; - - return ( -
- {/* Section header */} -
-

- Únete como Patrocinador -

-

- Conecta con la comunidad tech más vibrante y posiciona tu marca junto a los mejores talentos del - desarrollo +import { useToast } from '@/hooks/use-toast'; +import Win from '@/components/Win'; + +/* ── Shared tokens ──────────────────────────────────────────────── */ +const INK = 'var(--ink)'; +const PINK = 'var(--pink)'; +const PAPER = 'var(--paper)'; +const PAPER2 = 'var(--paper-2)'; + +const REASONS = [ + { + icon: '$', + title: 'Visibilidad de marca', + text: 'Expón tu marca ante cientos de desarrolladores y profesionales de la tecnología.', + }, + { + icon: '⌘', + title: 'Networking real', + text: 'Expande tus redes de comunidades de desarrolladores.' + }, + { + icon: '♥', + title: 'Impacto en la comunidad', + text: 'Contribuye al crecimiento de las comunidades de la región.', + }, +]; + +/* ── Brochure CTA ────────────────────────────────────────────────── */ +const BrochureCta = () => { + const { toast } = useToast(); + return ( +

+ {/* Scanline overlay */} +
+ +
+

+ Descarga el brochure +

+

+ Tiers, beneficios, métricas de la edición pasada y plazos de entrega + de materiales en un PDF de 12 páginas. +

+ toast({ title: 'Descargando brochure...', description: 'El documento se está descargando.' })} + style={{ + display: 'inline-flex', alignItems: 'center', gap: 10, + height: 42, padding: '0 18px', + background: PAPER, border: `2px solid ${INK}`, + boxShadow: `3px 3px 0 ${INK}`, textDecoration: 'none', + cursor: 'pointer', transition: 'transform 80ms, box-shadow 80ms', + }} + onMouseEnter={e => { const el = e.currentTarget as HTMLAnchorElement; el.style.transform = 'translate(3px,3px)'; el.style.boxShadow = 'none'; }} + onMouseLeave={e => { const el = e.currentTarget as HTMLAnchorElement; el.style.transform = ''; el.style.boxShadow = `3px 3px 0 ${INK}`; }} + > + + Descargar brochure + + + ↓ + + +
+
+ ); +}; + +/* ── Main component ──────────────────────────────────────────────── */ +const BecomeSponsor = () => ( + + Edición IX · 2026 + brochure · 12 pp + + } + > +
+ + {/* ══ Left column ═══════════════════════════════════════════ */} +
+ + {/* Eyebrow */} +
+ ▸ Invitación · cupo limitado de marcas +
+ + {/* H2 */} +

+ Únete como
+ patrocinador. +

+ + {/* Subhead */} +

+

+ + {/* Reasons list */} +
+ {REASONS.map(({ icon, title, text }, i) => ( +
0 ? `1px solid ${INK}` : 'none', + padding: '14px 16px', gap: 16, + }}> + {/* Icon tile */} +
+ {icon} +
+ {/* Text */} +
+
+ {title} +
+

+ {text}

-
+
-
- {/* Benefits section */} -
-
-

- ¿Por qué patrocinar Posadev? -

-
- - {/* Lista de beneficios */} -
    -
  • - - -
    -

    Visibilidad de marca

    -

    - Expón tu marca ante cientos de desarrolladores y profesionales de la tecnología

    -
    -
  • - -
  • - - -
    -

    Networking

    -

    Expande tus redes de comunidades de desarrolladores

    -
    -
  • -
  • - - -
    -

    Impacto social

    -

    Contribuye al crecimiento de las comunidades de la - región

    -
    -
  • -
- - {/* Brochure download */} - -
- - {/* Sponsor Image */} -
-

- Conviértete en Patrocinador -

- -
- Conviértete en Patrocinador de Posadev -
-

- ¿Interesado en patrocinar nuestro evento? -

- - -
-
-
-
-
- ); -} -export default BecomeSponsor \ No newline at end of file + ))} + + + {/* Brochure CTA — pushed to bottom */} +
+ +
+ + + {/* ══ Right column — poster card ════════════════════════════ */} +
+ + {/* Art zone */} +
+ {/* Meta line */} +
+
+ + {/* Duck — overflows card intentionally */} + Pato mascota de Posadev con lentes + + {/* Shout sticker */} +
+ + // si aún no recibiste el brochure + + + Become a
+ + *sponsor* + +
+ {/* Triangle tail — downward, bottom-left */} +
+
+
+
+ + {/* Poster footer */} +
+

+ ¿Interesado en patrocinar nuestro evento?{' '} + Escríbenos y te mandamos paquetes y disponibilidad. +

+ + {/* Mail pill */} +
+ {/* Envelope icon */} + + + sponsors@posadev.org + +
+ + {/* Primary CTA */} + + ▸ Contáctanos + +
+
+ +
+ +); + +export default BecomeSponsor; \ No newline at end of file diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx index ed32f85..283de5b 100644 --- a/src/components/Footer.tsx +++ b/src/components/Footer.tsx @@ -1,97 +1,32 @@ import React from 'react'; -import {MapPin, Mail, CandyCane, CalendarHeart, Map} from 'lucide-react'; -import {useNavigate} from "react-router-dom"; -import {scrollToTop} from "@/lib/utils.ts"; -import DuckFace from "@/components/ui/duckFace.tsx"; +import { useNavigate } from 'react-router-dom'; const Footer = () => { - const currentYear = new Date().getFullYear(); - const navigate = useNavigate(); + const navigate = useNavigate(); - return ( - - ); + + + + // comunidades.join() + + + ); }; -export default Footer; +export default Footer; \ No newline at end of file diff --git a/src/components/Gallery.tsx b/src/components/Gallery.tsx index f6a7f55..0dd97b2 100644 --- a/src/components/Gallery.tsx +++ b/src/components/Gallery.tsx @@ -1,5 +1,5 @@ -import React, {useState} from 'react'; -import {X, ChevronLeft, ChevronRight, Heart} from 'lucide-react'; +import React, { useState } from 'react'; +import { X, ChevronLeft, ChevronRight, Heart } from 'lucide-react'; import posadevFila from '@/img/gallery/posadev-fila.webp'; import posadevReferentes from '@/img/gallery/IMG_7461.webp'; import posadevAsistencia from '@/img/gallery/PXL_20241208_011903817.webp'; @@ -8,202 +8,115 @@ import voluntarios from '@/img/gallery/voluntarios.webp'; import hazzim from '@/img/gallery/Hazzim.webp'; import dinamicas from '@/img/gallery/Dinamicas.webp'; import gabriel from '@/img/gallery/Gabriel.webp'; -import posadev from '@/img/gallery/posadev.webp' -import dinamicaSponsor from '@/img/gallery/DinamicaSponsor.png' -import invocacionDemo from '@/img/gallery/invocacionDemo.png' -import tamales from '@/img/gallery/tamales.png' -import Photo from "@/components/Photo.tsx"; -import Carousel, {GridConfig} from "@/components/Carousel.tsx"; -import {IImage} from "@/types/types.ts"; -import {useIsMobile} from "@/hooks/use-mobile.tsx"; - +import posadev from '@/img/gallery/posadev.webp'; +import dinamicaSponsor from '@/img/gallery/DinamicaSponsor.png'; +import invocacionDemo from '@/img/gallery/invocacionDemo.png'; +import tamales from '@/img/gallery/tamales.png'; +import Photo from '@/components/Photo'; +import Carousel, { GridConfig } from '@/components/Carousel'; +import { IImage } from '@/types/types'; +import { useIsMobile } from '@/hooks/use-mobile'; +import Win from '@/components/Win'; const Gallery = () => { - const [selectedImage, setSelectedImage] = useState(null); - const gridLg: GridConfig = {cols: 3, rows: 2, itemsPerSlide: 6}; - const gridMd: GridConfig = {cols: 2, rows: 2, itemsPerSlide: 4}; - const gridSm: GridConfig = {cols: 1, rows: 1, itemsPerSlide: 1}; + const [selectedImage, setSelectedImage] = useState(null); + const gridLg: GridConfig = { cols: 3, rows: 2, itemsPerSlide: 6 }; + const gridMd: GridConfig = { cols: 2, rows: 2, itemsPerSlide: 4 }; + const gridSm: GridConfig = { cols: 1, rows: 1, itemsPerSlide: 1 }; - const images = React.useMemo(() => [ - { - id: 11, - src: invocacionDemo, - alt: "Invocación de demo", - title: "Invocación de demo" - }, - { - id: 12, - src: tamales, - alt: "Tamales", - title: "Y llegaron los tamales" - }, + const images = React.useMemo(() => [ + { id: 11, src: invocacionDemo, alt: "Invocación de demo", title: "Invocación de demo" }, + { id: 12, src: tamales, alt: "Tamales", title: "Y llegaron los tamales" }, + { id: 8, src: dinamicas, alt: "Dinamicas con sponsors", title: "Dinamicas con sponsors" }, + { id: 9, src: posadev, alt: "Posadev 2019", title: "Posadev 2019" }, + { id: 7, src: hazzim, alt: "Hazzim organizador 2024", title: "Hazzim Organizador" }, + { id: 2, src: voluntarios, alt: "Voluntarios Posadev", title: "Voluntarios" }, + { id: 1, src: gabriel, alt: "Gabriel speaker 2024", title: "Gabriel speaker" }, + { id: 3, src: posadevTecnologia, alt: "Tecnología", title: "Tecnología" }, + { id: 4, src: posadevFila, alt: "Posadev - Participantes", title: "Participantes" }, + { id: 5, src: posadevReferentes, alt: "Posadev - Referentes", title: "Referentes" }, + { id: 6, src: posadevAsistencia, alt: "Posadev - Asistencia", title: "Asistencia" }, + { id: 10, src: dinamicaSponsor, alt: "Dinamica con Sponsor", title: "Dinamica con Sponsor" }, + ], []); - { - id: 8, - src: dinamicas, - alt: "Dinamicas con sponsors", - title: "Dinamicas con sponsors" - }, - { - id: 9, - src: posadev, - alt: "Posadev 2019", - title: "Posadev 2019" - }, - { - id: 7, - src: hazzim, - alt: "Hazzim organizador Posadev 2024", - title: "Hazzim Organizador" - }, - { - id: 2, - src: voluntarios, - alt: "Voluntarios Posadev", - title: "Voluntarios" - }, - { - id: 1, - src: gabriel, - alt: "Gabriel speaker Posadev 2024", - title: "Gabriel speaker" - }, - { - id: 3, - src: posadevTecnologia, - alt: "Tecnología", - title: "Tecnología" - }, - { - id: 4, - src: posadevFila, - alt: "Posadev - Participantes", - title: "Participantes" - }, - { - id: 5, - src: posadevReferentes, - alt: "Posadev - Referentes", - title: "Referentes" - }, - { - id: 6, - src: posadevAsistencia, - alt: "Posadev - Asistencia", - title: "Asistencia" - }, - { - id: 10, - src: dinamicaSponsor, - alt: "Dinamica con Sponsor", - title: "Dinamica con Sponsor" - } - ], []); - const renderPhoto = React.useCallback( - (image: IImage, index: number) => ( - - ), []); + const renderPhoto = React.useCallback( + (image: IImage, index: number) => ( + + ), []); - const closeLightbox = () => { - setSelectedImage(null); - document.body.style.overflow = 'unset'; - }; + const closeLightbox = () => { setSelectedImage(null); document.body.style.overflow = 'unset'; }; + const nextImage = () => { if (selectedImage !== null) setSelectedImage((selectedImage + 1) % images.length); }; + const prevImage = () => { if (selectedImage !== null) setSelectedImage(selectedImage === 0 ? images.length - 1 : selectedImage - 1); }; - const nextImage = () => { - if (selectedImage !== null) { - setSelectedImage((selectedImage + 1) % images.length); - } + React.useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + if (selectedImage !== null) { + if (e.key === 'Escape') closeLightbox(); + if (e.key === 'ArrowRight') nextImage(); + if (e.key === 'ArrowLeft') prevImage(); + } }; + window.addEventListener('keydown', handleKeyDown); + return () => window.removeEventListener('keydown', handleKeyDown); + }, [selectedImage]); - const prevImage = () => { - if (selectedImage !== null) { - setSelectedImage(selectedImage === 0 ? images.length - 1 : selectedImage - 1); + return ( +
+ + {images.length} fotos + ediciones 2019–2024 + } - }; + > +
+

Galería de
momentos.

+ +
- // Manejar teclas del teclado - React.useEffect(() => { - const handleKeyDown = (e: KeyboardEvent) => { - if (selectedImage !== null) { - if (e.key === 'Escape') closeLightbox(); - if (e.key === 'ArrowRight') nextImage(); - if (e.key === 'ArrowLeft') prevImage(); - } - }; + +
- window.addEventListener('keydown', handleKeyDown); - return () => window.removeEventListener('keydown', handleKeyDown); - }, [selectedImage]); - - return ( -
- {/* Section header */} - -

- Revive los mejores momentos

-
- {/* Gallery grid */} - + + + +
+ {images[selectedImage].alt} - {/* Lightbox */} - {selectedImage !== null && ( -
- {/* Close button */} - - - {/* Navigation buttons */} - - - - {/* Image */} -
- {images[selectedImage].alt} -
- - {/* Image info */} -
-

- {images[selectedImage].title} -

-

- {selectedImage + 1} de {images.length} -

-
-
- )} -
- ); +
+
+

{images[selectedImage].title}

+

{selectedImage + 1} de {images.length}

+
+ + )} + + ); }; -export default Gallery; +export default Gallery; \ No newline at end of file diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 185d08e..38a71b1 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -1,134 +1,65 @@ -import React, {useEffect, useState} from 'react'; -import {Image, Menu, UserStar, X, BarChart3, Download} from 'lucide-react'; -import posadevLogo from '/media-kit/posadev-logo.png'; -import {useLocation, useNavigate} from "react-router-dom"; -import {cn} from "@/lib/utils.ts"; +import React, { useState, useEffect } from 'react'; +import { useNavigate } from 'react-router-dom'; const Header = () => { - const [isMenuOpen, setIsMenuOpen] = useState(false); - const [isScrolled, setIsScrolled] = useState(false); - const navigate = useNavigate(); - const location = useLocation(); + const [time, setTime] = useState(''); + const [menuOpen, setMenuOpen] = useState(false); + const navigate = useNavigate(); - useEffect(() => { - const handleScroll = () => { - setIsScrolled(window.scrollY > 20); - }; - window.addEventListener('scroll', handleScroll); - return () => window.removeEventListener('scroll', handleScroll); - }, []); - - const navigateMenu = (path: string) => { - setIsMenuOpen(false); - navigate(path); - } - - const isActive = (path: string) => { - return location.pathname + location.hash === path; + useEffect(() => { + const tick = () => { + const d = new Date(); + setTime(`${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`); }; + tick(); + const id = setInterval(tick, 1000); + return () => clearInterval(id); + }, []); - return ( -
-
-
- navigateMenu('/#inicio')}> - Posadev Logo - - - - - {/* Mobile menu button */} - -
+ const go = (path: string) => { + setMenuOpen(false); + navigate(path); + }; - {/* Mobile Navigation */} - {isMenuOpen && ( -
- -
- )} -
-
- ); + return ( + <> +
+ + + + + + + + +
+ + + ); }; -export default Header; +export default Header; \ No newline at end of file diff --git a/src/components/Hero.tsx b/src/components/Hero.tsx index e173dd6..31ff105 100644 --- a/src/components/Hero.tsx +++ b/src/components/Hero.tsx @@ -1,42 +1,75 @@ import React from 'react'; -import posadevLogo from '/media-kit/posadev-logo.png'; +import posadevLogo from '/media-kit/posadev_logo_blanco.png'; +import Win from '@/components/Win'; -const Hero = () => { - return ( -
-
-
-
- Logo de Posadev - - Posadev 2025 – El evento anual de comunidades tecnológicas - -
-

- El evento anual que reúne a la comunidades tecnológicas -

-
-
-
-
- Más información - - - -
-
- ); -}; +const Hero = () => ( +
+ + 1 ítem · Sáb 28.11.2026 + Cupo 350 · $500 MXN + + } + > + {/* Eyebrow */} +
+
-export default Hero; + {/* Logo + COMUNIDADES.JOIN() en la misma caja rosa con scanlines */} +
+ POSADEV 2026 +
+ + {/* Descripción en color rosa */} +

+ // + Una conferencia de software organizada por y para + developers — un día entero, cuatro tracks, una piñata. +

+ + {/* Meta grid */} +
+
+
Cuándo
+
Sáb 28 Nov · 9:00 —
21:00
+
+
+
Dónde
+
Holiday Inn Guadalajara Expo
+
+
+
Cuánto
+
$500 MXN
+
+
+
CFP
+
Cierra 25 sep
+
+
+ + {/* CTAs */} + +
+
+); + +export default Hero; \ No newline at end of file diff --git a/src/components/Sponsors.tsx b/src/components/Sponsors.tsx index e18bfff..52353eb 100644 --- a/src/components/Sponsors.tsx +++ b/src/components/Sponsors.tsx @@ -1,49 +1,30 @@ -import React from "react"; -import background from '@/img/background_blue.png' -import {sponsors} from "@/data/sponsors.ts"; -import Lights from "@/components/ui/lights.tsx"; -import SponsorsByTier from "@/components/sponsors/SponsorsByTier.tsx"; +import React from 'react'; +import { sponsors } from '@/data/sponsors'; +import SponsorsByTier from '@/components/sponsors/SponsorsByTier'; +import Win from '@/components/Win'; const Sponsors = () => { + const paidCount = sponsors.filter(s => s.isPaid).length; - - return ( -
- {/* SVG de luces */} -
- - - - - -
- {/* Fondo y texto */} -
-
-

- Patrocinadores del 2025 -

-
- -
-
-
- - - - - -
-
- - ) + return ( + + {paidCount} patrocinadores + Edición 2025 + + } + > +
+

Patrocinadores
del 2025.

+

+ Empresas que apostaron por Posadev en la edicion anterior. +

+
+ +
+ ); }; export default Sponsors; \ No newline at end of file diff --git a/src/components/Win.tsx b/src/components/Win.tsx new file mode 100644 index 0000000..e095a8d --- /dev/null +++ b/src/components/Win.tsx @@ -0,0 +1,37 @@ +import React from 'react'; +import { cn } from '@/lib/utils'; + +interface WinProps { + title: string; + footer?: React.ReactNode; + dark?: boolean; + showScrollbar?: boolean; + className?: string; + bodyClassName?: string; + children: React.ReactNode; +} + +const Win: React.FC = ({ + title, footer, dark, showScrollbar, className, bodyClassName, children +}) => ( +
+
+
+
+ {children} +
+ {footer &&
{footer}
} + {showScrollbar && ( + +); + +export default Win; \ No newline at end of file diff --git a/src/index.css b/src/index.css index 6f3bfd8..51497e1 100644 --- a/src/index.css +++ b/src/index.css @@ -1,20 +1,49 @@ @tailwind base; @tailwind components; @tailwind utilities; -/* Definition of the design system. All colors, gradients, fonts, etc should be defined here. -All colors MUST be HSL. -*/ + +/* ═══════════════════════════════════════════════════════════════ + Posadev 2026 — Retro Pixel Design System + Pink Christmas desktop · bordered windows · chunky typography + ═══════════════════════════════════════════════════════════════ */ @layer base { :root { + /* ── Retro design tokens ── */ + --pink: #ff0a55; + --pink-2: #ff3d76; + --pink-dk: #c20842; + --pink-shadow: #7a0428; + --xmas-green: #0e5c3a; + --xmas-green-dk: #082f1d; + --paper: #fef6e8; + --paper-2: #f6e9cf; + --ink: #0a0a0a; + --ink-2: #2a2a2a; + --win-shadow: 6px 6px 0 var(--ink); + --win-shadow-sm: 3px 3px 0 var(--ink); + + /* ── Tailwind token compatibility ── */ --background: 0 0% 100%; --foreground: 222.2 84% 4.9%; - --card: 0 0% 100%; --card-foreground: 222.2 84% 4.9%; - --popover: 0 0% 100%; --popover-foreground: 222.2 84% 4.9%; + --primary: 340 97% 50%; + --primary-foreground: 210 40% 98%; + --secondary: 217 75% 43%; + --secondary-foreground: 222.2 47.4% 11.2%; + --muted: 210 40% 96.1%; + --muted-foreground: 215.4 16.3% 46.9%; + --accent: 210 40% 96.1%; + --accent-foreground: 222.2 47.4% 11.2%; + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 210 40% 98%; + --border: 214.3 31.8% 91.4%; + --input: 214.3 31.8% 91.4%; + --ring: 222.2 84% 4.9%; + --radius: 0rem; --primary-100: 341, 100%, 91%, 1; --primary-200: 340, 100%, 83%, 1; @@ -24,12 +53,11 @@ All colors MUST be HSL. --primary-600: 340, 99%, 43%, 1; --primary-700: 340, 100%, 38%, 1; --primary-800: 340, 99%, 32%, 1; - --primary-900: 340, 98% 26%, 1; + --primary-900: 340, 98%, 26%, 1; --primary-foreground: 210, 40%, 98%, 1; - --secondary-100: 217, 100%, 86%, 1; --secondary-200: 217, 100%, 77%, 1; - --secondary-300: 217, 100%, 64%, 1%; + --secondary-300: 217, 100%, 64%, 1; --secondary-400: 217, 63%, 51%, 1; --secondary-500: 217, 75%, 43%, 1; --secondary-600: 217, 76%, 37%, 1; @@ -37,18 +65,16 @@ All colors MUST be HSL. --secondary-800: 217, 88%, 24%, 1; --secondary-900: 217, 86%, 18%, 1; --secondary-foreground: 222.2 47.4% 11.2%; - - --alternative-100: 167, 100%, 86%, 1; - --alternative-200: 167, 84%, 76%, 1; - --alternative-300: 167, 82%, 50%, 1; - --alternative-400: 167, 100%, 39%, 1; - --alternative-500: 168, 98%, 26%, 1; - --alternative-600: 168, 99%, 20%, 1; - --alternative-700: 168, 99%, 15%, 1; - --alternative-800: 168, 100%, 10%, 1; - --alternative-900: 168, 98%, 7%, 1; + --alternative-100: 167, 100%, 86%, 1; + --alternative-200: 167, 84%, 76%, 1; + --alternative-300: 167, 82%, 50%, 1; + --alternative-400: 167, 100%, 39%, 1; + --alternative-500: 168, 98%, 26%, 1; + --alternative-600: 168, 99%, 20%, 1; + --alternative-700: 168, 99%, 15%, 1; + --alternative-800: 168, 100%, 10%, 1; + --alternative-900: 168, 98%, 7%, 1; --alternative-foreground: 340 100% 91%; - --grey-100: 240, 100%, 100%, 1; --grey-200: 0, 70%, 93%, 1; --grey-300: 0, 2%, 87%, 1; @@ -59,23 +85,6 @@ All colors MUST be HSL. --grey-800: 0, 1%, 26%, 1; --grey-900: 0, 0%, 13%, 1; --grey-foreground: 222.2 84% 4.9%; - - - --muted: 210 40% 96.1%; - --muted-foreground: 215.4 16.3% 46.9%; - - --accent: 210 40% 96.1%; - --accent-foreground: 222.2 47.4% 11.2%; - - --destructive: 0 84.2% 60.2%; - --destructive-foreground: 210 40% 98%; - - --border: 214.3 31.8% 91.4%; - --input: 214.3 31.8% 91.4%; - --ring: 222.2 84% 4.9%; - - --radius: 0.5rem; - --sidebar-background: 0 0% 98%; --sidebar-foreground: 240 5.3% 26.1%; --sidebar-primary: 240 5.9% 10%; @@ -84,113 +93,547 @@ All colors MUST be HSL. --sidebar-accent-foreground: 240 5.9% 10%; --sidebar-border: 220 13% 91%; --sidebar-ring: 217.2 91.2% 59.8%; + } - ::-webkit-scrollbar { - width: 12px; - } + * { + box-sizing: border-box; + @apply border-border; + } - ::-webkit-scrollbar-thumb { - background: repeating-linear-gradient( - 135deg, - #ffffff 0 8px, - #FB0454 8px 16px - ); - border-radius: 9999px; - } + html, body { + margin: 0; + padding: 0; + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; + overflow-x: hidden; + } + body { + font-family: "Space Grotesk", ui-sans-serif, system-ui, sans-serif; + font-size: 16px; + line-height: 1.5; + color: var(--ink); + /* Green Christmas desktop */ + background-color: var(--xmas-green); + background-image: + url("data:image/svg+xml;utf8,"), + repeating-linear-gradient(0deg, transparent 0 1px, rgba(8,46,28,.55) 1px 2px), + repeating-linear-gradient(90deg, transparent 0 1px, rgba(8,46,28,.3) 1px 2px); + background-size: 320px 320px, auto, auto; + min-height: 100vh; } + + h1, h2, h3, h4 { margin: 0; font-weight: 600; line-height: 1; } + p { margin: 0; } + a { color: inherit; text-decoration: none; } + a, button, [role="button"], input, label { cursor: pointer; } + img { display: block; max-width: 100%; } + button { font: inherit; color: inherit; background: transparent; border: 0; } } -.candy-text { - background: repeating-linear-gradient(135deg, #ffffff 0 8px, #FB0454 8px 16px); - -webkit-background-clip: text; - background-clip: text; - color: transparent; - background-size: 200% auto; - animation: candyFlow 2s linear infinite; +/* ── Win component ───────────────────────────────────────────────── */ +.win { + background: var(--paper); + border: 2px solid var(--ink); + box-shadow: var(--win-shadow); + position: relative; + color: var(--ink); } -@layer base { +.win-title { + height: 22px; + border-bottom: 2px solid var(--ink); + background: repeating-linear-gradient(0deg, var(--ink) 0 1px, var(--paper) 1px 3px); + display: flex; + align-items: center; + padding: 0 8px; + position: relative; + user-select: none; +} - * { - @apply border-border; - } +.win-close { + width: 13px; height: 13px; + border: 2px solid var(--ink); + background: var(--paper); + margin-right: auto; + cursor: default; + display: inline-block; + flex-shrink: 0; +} +.win-close:hover { background: var(--ink); } + +.win-zoom { + width: 13px; height: 13px; + border: 2px solid var(--ink); + background: var(--paper); + margin-left: 8px; + position: relative; + display: inline-block; + flex-shrink: 0; +} +.win-zoom::before { + content: ""; position: absolute; inset: 2px; + border: 1.5px solid var(--ink); +} - body { - @apply bg-background text-foreground; - } +.win-name { + position: absolute; left: 50%; transform: translateX(-50%); + background: var(--paper); padding: 0 14px; + font-family: "Pixelify Sans", monospace; font-weight: 600; font-size: 14px; + line-height: 22px; white-space: nowrap; max-width: 80%; + overflow: hidden; text-overflow: ellipsis; +} - a, - button, - [role="button"], - [role="link"], - input, - input[type="button"], - label { - cursor: pointer; - } +.win-body { padding: 24px 28px 28px; } +@media (max-width: 600px) { .win-body { padding: 16px 14px 20px; } } + +.win-foot { + border-top: 2px solid var(--ink); padding: 6px 12px; font-size: 13px; + background: var(--paper-2); display: flex; justify-content: space-between; + font-family: "Space Grotesk", sans-serif; font-weight: 500; letter-spacing: .01em; } -.gradient-bg { - background: linear-gradient( - 125deg, - #6C0224 0%, - #6C0224 50%, - #FB0454 80% +/* Dark variant */ +.win.dark { background: var(--ink); color: var(--paper); } +.win.dark .win-title { + background: repeating-linear-gradient(0deg, var(--paper) 0 1px, var(--ink) 1px 3px); + border-bottom-color: var(--paper); +} +.win.dark .win-name { background: var(--ink); color: var(--paper); } +.win.dark .win-close, .win.dark .win-zoom { background: var(--ink); border-color: var(--paper); } +.win.dark .win-zoom::before { border-color: var(--paper); } +.win.dark .win-foot { background: #1a1a1a; color: var(--paper); border-top-color: var(--paper); } + +/* Fake scrollbar */ +.win-scrollbar { + position: absolute; right: -2px; top: 22px; bottom: 0; width: 16px; + border-left: 2px solid var(--ink); + background: repeating-linear-gradient(45deg, var(--paper) 0 2px, var(--paper-2) 2px 4px); + display: flex; flex-direction: column; align-items: stretch; +} +.win-arr { + height: 16px; border-bottom: 2px solid var(--ink); background: var(--paper); + display: flex; align-items: center; justify-content: center; font-size: 10px; line-height: 1; +} +.win-arr.dn { border-bottom: 0; border-top: 2px solid var(--ink); margin-top: auto; } +.win-thumb { + width: 100%; height: 60px; background: var(--paper); + border-top: 2px solid var(--ink); border-bottom: 2px solid var(--ink); margin-top: 24px; +} + +/* ── Menubar ─────────────────────────────────────────────────────── */ +.menubar { + position: sticky; top: 0; left: 0; right: 0; height: 28px; + background: var(--paper); border-bottom: 2px solid var(--ink); + display: flex; align-items: center; padding: 0 10px; + z-index: 100; gap: 0; + font-family: "Pixelify Sans", monospace; font-size: 16px; font-weight: 500; +} + +.menubar-apple { + margin-right: 14px; display: inline-flex; align-items: center; flex-shrink: 0; +} + +.menubar-star { + display: inline-block; width: 14px; height: 14px; + background: conic-gradient(from 0deg, var(--ink) 0 12.5%, transparent 0 50%, var(--ink) 0 62.5%, transparent 0 100%); + transform: rotate(45deg); +} + +.menubar-item { + padding: 0 10px; line-height: 28px; + font-family: "Pixelify Sans", monospace; font-size: 16px; font-weight: 500; + background: transparent; border: 0; cursor: default; white-space: nowrap; + height: 28px; display: flex; align-items: center; +} +.menubar-item:hover { background: var(--ink); color: var(--paper); } + +.menubar-right { + margin-left: auto; display: flex; align-items: center; gap: 12px; + font-size: 14px; color: var(--ink-2); white-space: nowrap; +} + +.menubar-dot { + display: inline-block; width: 8px; height: 8px; background: var(--pink); flex-shrink: 0; +} + +.menubar-hamburger { + margin-left: auto; display: none; width: 22px; height: 16px; + flex-direction: column; justify-content: space-between; + cursor: pointer; background: transparent; border: 0; padding: 0; flex-shrink: 0; +} +.menubar-hamburger span { display: block; height: 2px; background: var(--ink); width: 100%; } + +/* Mobile nav */ +.mobile-nav { + display: none; background: var(--paper); border-bottom: 2px solid var(--ink); + padding: 8px 12px 12px; flex-direction: column; gap: 6px; + position: sticky; top: 28px; z-index: 99; +} +.mobile-nav.open { display: flex; } + +.mobile-nav-item { + font-family: "Pixelify Sans", monospace; font-size: 15px; font-weight: 500; + padding: 8px 12px; border: 2px solid var(--ink); background: var(--paper); + display: block; text-align: left; cursor: pointer; width: 100%; +} +.mobile-nav-item:hover { background: var(--ink); color: var(--paper); } +.mobile-nav-item.primary { background: var(--pink); color: var(--paper); border-color: var(--ink); } + +@media (max-width: 768px) { + .menubar-right { display: none; } + .menubar-hamburger { display: flex; } + .menubar-item { display: none; } +} + +/* ── Desktop layout ──────────────────────────────────────────────── */ +.desktop { + min-height: calc(100vh - 28px); + padding: 32px 20px 72px; + overflow-x: hidden; +} + +.desktop-wrap { + max-width: 1180px; margin: 0 auto; + display: flex; flex-direction: column; gap: 36px; +} + +/* ── Stripe / ticker ─────────────────────────────────────────────── */ +.stripe { + overflow: hidden; border: 2px solid var(--ink); + background: var(--ink); color: var(--paper); + height: 34px; display: flex; align-items: center; +} + +.stripe-track { + display: flex; align-items: center; white-space: nowrap; + animation: marquee 22s linear infinite; + font-family: "Press Start 2P", monospace; font-size: 9px; letter-spacing: .1em; +} +.stripe-track > span { display: inline-flex; align-items: center; } +.stripe-item { padding: 0 20px; } +.stripe-star { display: inline-block; width: 6px; height: 6px; background: var(--pink); margin: 0 10px; } + +@keyframes marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } } + +/* ── Buttons ─────────────────────────────────────────────────────── */ +.retro-btn { + display: inline-flex; align-items: center; gap: 8px; + height: 42px; padding: 0 20px; + font-family: "Pixelify Sans", monospace; font-size: 16px; font-weight: 600; + border: 2px solid var(--ink); box-shadow: var(--win-shadow-sm); + cursor: pointer; text-decoration: none; color: var(--ink); white-space: nowrap; + transition: transform 80ms, box-shadow 80ms; +} +.retro-btn:hover { transform: translate(3px, 3px); box-shadow: none; } +.retro-btn.primary { background: var(--pink); color: var(--paper); } +.retro-btn.ghost { background: var(--paper); } +.retro-btn.dark-ghost { background: var(--paper); color: var(--pink); border-color: var(--paper); } +.retro-btn.dark-ghost:hover { background: var(--ink); color: var(--paper); border-color: var(--paper); } + +/* ── Section headings ────────────────────────────────────────────── */ +.sec-head { margin-bottom: 24px; } +.sec-head h2 { + font-family: "Pixelify Sans", monospace; font-weight: 700; + font-size: clamp(36px, 5.5vw, 64px); line-height: 1.05; + color: var(--ink); margin-bottom: 14px; +} +.sec-head h2 em { font-style: normal; color: var(--pink); } +.sec-sub { + font-family: "Space Grotesk", sans-serif; font-size: 16px; + color: var(--ink-2); line-height: 1.6; max-width: 560px; +} + +/* ── Hero ────────────────────────────────────────────────────────── */ +.hero-eyebrow { + display: flex; align-items: center; gap: 12px; + font-family: "Press Start 2P", monospace; font-size: 9px; letter-spacing: .08em; + color: var(--ink-2); margin-bottom: 18px; flex-wrap: wrap; +} +.hero-eyebrow-dot { width: 10px; height: 10px; background: var(--pink); display: inline-block; flex-shrink: 0; } +.hero-eyebrow-div { width: 20px; height: 2px; background: var(--ink); flex-shrink: 0; } + +.hero-logo { + border: 2px solid var(--ink); box-shadow: var(--win-shadow); + background: var(--pink); position: relative; overflow: hidden; margin-bottom: 20px; +} +/* Scanline overlay — covers both the image and the join text */ +.hero-logo::before { + content: ""; position: absolute; inset: 0; pointer-events: none; z-index: 1; + background: repeating-linear-gradient( + 0deg, + transparent 0 3px, + rgba(0, 0, 0, 0.18) 3px 4px ); - @media (max-width: 768px) { - background: linear-gradient( - 110deg, - #6C0224 10%, - #6C0224 35%, - #FB0454 80% - ); - } +} +.hero-logo img { display: block; width: 100%; height: auto; position: relative; } + +/* COMUNIDADES.JOIN() inside the pink box */ +.hero-logo-join { + position: relative; + text-align: center; + padding: 10px 20px 22px; + font-family: "Silkscreen", "Press Start 2P", ui-monospace, monospace; + font-size: clamp(11px, 2vw, 22px); + letter-spacing: 0.18em; + color: var(--ink); + text-transform: uppercase; + line-height: 1.4; } +.hero-tag { + font-family: "Pixelify Sans", monospace; font-size: clamp(14px, 1.8vw, 18px); + color: var(--pink-dk); line-height: 1.55; max-width: 640px; margin-bottom: 8px; + font-weight: 500; +} +.hero-tag-code { + font-family: "Pixelify Sans", monospace; font-size: inherit; color: var(--pink); font-weight: 700; +} -.gradient-text { - background: linear-gradient(135deg, #CF0F47, #FF0B55); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; +.hero-meta { + display: grid; grid-template-columns: repeat(4, 1fr); + border: 2px solid var(--ink); margin-top: 28px; background: var(--ink); +} +.hero-meta-cell { background: var(--paper); padding: 14px 16px; } +.hero-meta-cell + .hero-meta-cell { border-left: 2px solid var(--ink); } +.hero-meta-key { + font-family: "Press Start 2P", monospace; font-size: 8px; letter-spacing: .1em; + color: var(--ink-2); text-transform: uppercase; margin-bottom: 8px; +} +.hero-meta-val { + font-family: "Space Grotesk", sans-serif; font-weight: 700; font-size: 19px; + line-height: 1.2; letter-spacing: -.01em; +} +.hero-meta-val em { font-style: normal; color: var(--pink); } +.hero-ctas { display: flex; gap: 12px; margin-top: 28px; flex-wrap: wrap; } + +@media (max-width: 720px) { + .hero-meta { grid-template-columns: repeat(2, 1fr); } + .hero-meta-cell:nth-child(2) { border-left: 2px solid var(--ink); } + .hero-meta-cell:nth-child(3) { border-top: 2px solid var(--ink); border-left: 0; } + .hero-meta-cell:nth-child(4) { border-top: 2px solid var(--ink); } } -.hover-scale { - transition: transform 0.3s ease, box-shadow 0.3s ease; +/* ── Hero primary button square icon ────────────────────────────── */ +.hero-btn-square { + display: inline-block; + width: 10px; height: 10px; + background: var(--paper); + flex-shrink: 0; } -.hover-scale:hover { - transform: translateY(-5px) scale(1.02); - box-shadow: 0 20px 40px rgba(207, 15, 71, 0.3); +@keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } } + + +/* ── Estadisticas responsive grids ──────────────────────────────── */ + +/* Section header: title + description */ +.stats-intro { + display: grid; grid-template-columns: 1fr 1fr; + gap: 32px; margin-bottom: 28px; align-items: start; } -@media (max-width: 640px) { - .hover-scale { - transform: translateY(-5px) scale(1.02); - box-shadow: 0 2.5px 5px rgba(207, 15, 71, 0.3); - } +/* 4-cell KPI strip — uses ink bg + 2px gap as borders */ +.stats-kpi { + display: grid; grid-template-columns: repeat(4, 1fr); + gap: 2px; border: 2px solid var(--ink); + background: var(--ink); margin-bottom: 28px; +} +.stats-kpi-cell { padding: 16px 18px; min-width: 0; } - .keen-slider__slide { - will-change: transform; - transform: translateZ(0); - backface-visibility: hidden; - contain: layout paint; - } +/* 2-column chart rows */ +.stats-2col { + display: grid; grid-template-columns: 1fr 1fr; gap: 20px; +} +.stats-2col + .stats-2col { margin-top: 20px; } +/* Bar chart scroll wrapper */ +.stats-bar-outer { + border: 2px solid var(--ink); background: var(--paper); + box-shadow: var(--win-shadow-sm); padding: 20px 24px 28px; margin-bottom: 28px; + overflow-x: auto; } +.stats-bar-inner { min-width: 420px; } -/* Lightbox overlay */ -.lightbox-overlay { - background: rgba(0, 0, 0, 0.9); - backdrop-filter: blur(10px); +@media (max-width: 700px) { + .stats-intro { grid-template-columns: 1fr; gap: 16px; } + .stats-kpi { grid-template-columns: repeat(2, 1fr); } + .stats-2col { grid-template-columns: 1fr; } } -#root { - display: flex; - flex-direction: column; - min-height: 100vh; + +/* ── Stats / numeralia (legacy classes kept) ─────────────────────── */ +.stats-grid { + display: grid; grid-template-columns: repeat(4, 1fr); + border: 2px solid var(--ink); background: var(--ink); margin-top: 20px; +} +.stat-cell { background: var(--paper); padding: 20px 16px; min-width: 0; } +.stat-cell + .stat-cell { border-left: 2px solid var(--ink); } +.stat-n { + font-family: "Pixelify Sans", monospace; font-weight: 700; + font-size: clamp(40px, 5vw, 60px); line-height: 1; color: var(--ink); margin-bottom: 8px; +} +.stat-n em { font-style: normal; color: var(--pink); } +.stat-l { + font-family: "Press Start 2P", monospace; font-size: 8px; letter-spacing: .1em; + color: var(--ink-2); text-transform: uppercase; line-height: 1.6; +} + +@media (max-width: 600px) { + .stats-grid { grid-template-columns: repeat(2, 1fr); } + .stat-cell:nth-child(2) { border-left: 2px solid var(--ink); } + .stat-cell:nth-child(3) { border-top: 2px solid var(--ink); border-left: 0; } + .stat-cell:nth-child(4) { border-top: 2px solid var(--ink); } +} + +/* ── Patrocinio / BecomeSponsor ──────────────────────────────────── */ +.patron-grid { + display: grid; grid-template-columns: 1.1fr 1fr; + gap: 34px; align-items: stretch; } +/* Poster card — right column */ +.patron-poster { + border: 2px solid var(--ink); box-shadow: var(--win-shadow); + background: #0a0a0a; + display: flex; flex-direction: column; + min-height: 560px; + /* overflow NOT hidden — duck is allowed to spill */ +} + +/* Art zone — pink with halftone */ +.patron-art { + flex: 1; + background: var(--pink); + background-image: + radial-gradient(circle, rgba(122,4,40,.65) 1.5px, transparent 1.5px), + radial-gradient(circle, rgba(255,255,255,.10) 2px, transparent 2px); + background-size: 10px 10px, 14px 14px; + background-position: 0 0, 7px 7px; + padding: 20px 22px 28px; + position: relative; + display: flex; flex-direction: column; justify-content: flex-end; + min-height: 340px; +} + +/* Duck — overflows card */ +.patron-duck { + position: absolute; + right: -70px; top: -50px; + width: 480px; height: auto; + transform: rotate(6deg); + filter: drop-shadow(5px 5px 0 black); + image-rendering: pixelated; + z-index: 1; + pointer-events: none; +} + +/* Shout sticker */ +.patron-sticker { + position: relative; + z-index: 3; + display: inline-flex; flex-direction: column; align-self: flex-start; + background: var(--paper); + border: 3px solid var(--ink); + box-shadow: 6px 6px 0 var(--ink); + padding: 12px 16px 16px; + max-width: 78%; + transform: rotate(-3deg); + margin-bottom: 28px; +} + +/* CTA button in poster footer */ +.patron-cta { + display: inline-flex; align-items: center; + align-self: flex-start; + height: 44px; padding: 0 18px; + background: var(--pink); + border: 2px solid var(--paper); + box-shadow: 4px 4px 0 var(--paper); + font-family: "Pixelify Sans", monospace; font-weight: 700; font-size: 16px; + color: var(--paper); text-decoration: none; cursor: pointer; + transition: transform 80ms, box-shadow 80ms; +} +.patron-cta:hover { + transform: translate(-1px, -1px); + box-shadow: 5px 5px 0 var(--paper); +} + +@media (max-width: 900px) { + .patron-grid { grid-template-columns: 1fr; } + .patron-duck { width: 360px; right: -50px; top: -40px; } + .patron-poster { min-height: 440px; } +} + +/* ── Sponsors section ────────────────────────────────────────────── */ +.tier-block { margin-bottom: 24px; } +.tier-label { + font-family: "Press Start 2P", monospace; font-size: 9px; letter-spacing: .1em; + text-transform: uppercase; color: var(--ink-2); margin-bottom: 10px; + display: flex; align-items: center; gap: 10px; +} +.tier-star { display: inline-block; width: 8px; height: 8px; background: var(--pink); flex-shrink: 0; } +.tier-line { flex: 1; height: 2px; background: var(--ink); } +.tier-cards { display: flex; flex-wrap: wrap; gap: 10px; } + +.retro-sp-card { + border: 2px solid var(--ink); background: var(--paper); box-shadow: var(--win-shadow-sm); + padding: 10px 18px; font-family: "Pixelify Sans", monospace; font-weight: 600; + font-size: 17px; display: flex; align-items: center; justify-content: center; +} +.tier-gold .retro-sp-card { font-size: 21px; padding: 14px 24px; } +.tier-gold .tier-star { background: #d6a23a; width: 10px; height: 10px; } +.tier-silver .tier-star { background: #aaa; } +.tier-bronze .tier-star { background: #cd7f32; } + +/* ── Gallery ─────────────────────────────────────────────────────── */ +.gallery-label { + font-family: "Press Start 2P", monospace; font-size: 9px; letter-spacing: .1em; + color: var(--ink-2); margin-bottom: 6px; +} + +/* ── Footer ──────────────────────────────────────────────────────── */ +.site-footer { + background: var(--paper); border-top: 2px solid var(--ink); + padding: 16px 28px; display: flex; align-items: center; + flex-wrap: wrap; gap: 14px; justify-content: space-between; + font-family: "Space Grotesk", sans-serif; font-size: 13px; color: var(--ink-2); +} +.site-footer-logo { + font-family: "Pixelify Sans", monospace; font-weight: 700; + font-size: 18px; color: var(--ink); +} +.site-footer-logo span { color: var(--pink); } +.site-footer-links { display: flex; gap: 16px; flex-wrap: wrap; align-items: center; } +.site-footer-links a { + font-family: "Press Start 2P", monospace; font-size: 8px; letter-spacing: .04em; + color: var(--ink-2); text-decoration: underline; +} +.site-footer-links a:hover { color: var(--pink); } +.site-footer-copy { font-size: 12px; color: var(--ink-2); } + +/* ── Lightbox ────────────────────────────────────────────────────── */ +.lightbox-overlay { background: rgba(0, 0, 0, 0.9); backdrop-filter: blur(10px); } + + +/* ── Root shell ──────────────────────────────────────────────────── */ +#root { display: flex; flex-direction: column; min-height: 100vh; } + +/* ── Tailwind utility backcompat ─────────────────────────────────── */ +.gradient-bg { + background: linear-gradient(125deg, #6C0224 0%, #6C0224 50%, #FB0454 80%); +} +.gradient-text { + background: linear-gradient(135deg, #CF0F47, #FF0B55); + -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; +} +.hover-scale { transition: transform .3s ease, box-shadow .3s ease; } +.hover-scale:hover { transform: translateY(-5px) scale(1.02); box-shadow: 0 20px 40px rgba(207,15,71,.3); } +.candy-text { + background: repeating-linear-gradient(135deg, #ffffff 0 8px, #FB0454 8px 16px); + -webkit-background-clip: text; background-clip: text; color: transparent; + background-size: 200% auto; animation: candyFlow 2s linear infinite; +} +@keyframes candyFlow { 0% { background-position: 0% 0%; } 100% { background-position: 200% 0%; } } @keyframes caneBounce { 0% { transform: translateY(0) rotate(-20deg); } 30% { transform: translateY(-8px) rotate(-18deg); } @@ -200,24 +643,10 @@ All colors MUST be HSL. @layer utilities { .bg-candy-stripes { - background-image: repeating-linear-gradient( - 45deg, - #FF0B55 0 10px, - white 10px 20px - ); + background-image: repeating-linear-gradient(45deg, #FF0B55 0 10px, white 10px 20px); } .gpu-optimized { - will-change: transform; - transform: translateZ(0); - backface-visibility: hidden; - contain: layout paint; - - img { - object-fit: cover; - max-width: 100%; - image-rendering: optimizeSpeed; - will-change: transform; - transform: translateZ(0); - } + will-change: transform; transform: translateZ(0); + backface-visibility: hidden; contain: layout paint; } } \ No newline at end of file diff --git a/src/pages/Estadisticas.tsx b/src/pages/Estadisticas.tsx index c107455..bfcfaaa 100644 --- a/src/pages/Estadisticas.tsx +++ b/src/pages/Estadisticas.tsx @@ -1,450 +1,336 @@ import React from 'react'; -import { - ChartContainer, - ChartTooltip, - ChartTooltipContent, -} from "@/components/ui/chart"; -import { - BarChart, - Bar, - LineChart, - Line, - PieChart, - Pie, - Cell, - XAxis, - YAxis, - CartesianGrid, - Legend, -} from "recharts"; -import Gradient from "@/components/Gradient.tsx"; +import Win from '@/components/Win'; -const Estadisticas = () => { - // Datos de género - const datosGenero = [ - { response: "Hombre", count: 340, percentage: 77.27, color: "#FF6B9D" }, - { response: "Mujer", count: 95, percentage: 21.59, color: "#C44569" }, - { response: "No binario", count: 3, percentage: 0.68, color: "#F8B500" }, - { response: "Prefiero no decir", count: 2, percentage: 0.45, color: "#4ECDC4" }, - ]; +/* ── Data ─────────────────────────────────────────────────────────── */ +const ATTENDANCE = [ + { year: "'17", count: 302 }, + { year: "'18", count: 247 }, + { year: "'19", count: 343 }, + { year: "'20", count: 1000, virtual: true }, + { year: "'21", count: 1000, virtual: true }, + { year: "'22", count: 259 }, + { year: "'23", count: 316 }, + { year: "'24", count: 350 }, + { year: "'25", count: 440 }, +]; - // Datos de edad - const datosEdad = [ - { age_range: "< 17", count: 3, percentage: 0.68, color: "#FF6B9D" }, - { age_range: "18-24", count: 174, percentage: 39.55, color: "#C44569" }, - { age_range: "25-34", count: 162, percentage: 36.82, color: "#F8B500" }, - { age_range: "35-44", count: 73, percentage: 16.59, color: "#4ECDC4" }, - { age_range: "45-54", count: 25, percentage: 5.68, color: "#95E1D3" }, - { age_range: "> 55", count: 2, percentage: 0.45, color: "#FFA07A" }, - { age_range: "prefiero no decir", count: 1, percentage: 0.23, color: "#98D8C8" }, - ]; +const EXPERIENCE = [ + { label: '1 - 3 años', pct: 51 }, + { label: '4 - 6 años', pct: 18 }, + { label: '7+ años', pct: 30 }, +]; - // Datos de experiencia - const datosExperiencia = [ - { experience_years: "1 - 3", count: 226, percentage: 51.36, color: "#FF6B9D" }, - { experience_years: "4 - 6", count: 81, percentage: 18.41, color: "#C44569" }, - { experience_years: "> 7", count: 133, percentage: 30.23, color: "#F8B500" }, - ]; +const INTERESTS = [ + { label: 'Backend Developer', pct: 22 }, + { label: 'Full Stack Developer', pct: 19 }, + { label: 'Software Dev (General)', pct: 6 }, + { label: 'Frontend Developer', pct: 6 }, + { label: 'DevOps / SRE', pct: 6 }, + { label: 'QA / Testing', pct: 5 }, + { label: 'Mobile Developer', pct: 4 }, + { label: 'Otros', pct: 32 }, +]; - // Datos de especialidades - const datosEspecialidades = [ - { specialty_group: "Backend Developer", count: 95, percentage: 21.59, color: "#FF6B9D" }, - { specialty_group: "Full Stack Developer", count: 83, percentage: 18.86, color: "#C44569" }, - { specialty_group: "Software Developer (General)", count: 28, percentage: 6.36, color: "#F8B500" }, - { specialty_group: "Frontend Developer", count: 27, percentage: 6.14, color: "#4ECDC4" }, - { specialty_group: "DevOps/SRE", count: 25, percentage: 5.68, color: "#95E1D3" }, - { specialty_group: "QA/Testing", count: 21, percentage: 4.77, color: "#FFA07A" }, - { specialty_group: "Mobile Developer", count: 16, percentage: 3.64, color: "#98D8C8" }, - { specialty_group: "Programming Languages", count: 15, percentage: 3.41, color: "#FFD700" }, - { specialty_group: "UX/UI Design", count: 14, percentage: 3.18, color: "#87CEEB" }, - { specialty_group: "Data Science/Analytics", count: 12, percentage: 2.73, color: "#DDA0DD" }, - { specialty_group: "Student", count: 11, percentage: 2.50, color: "#F0E68C" }, - { specialty_group: "AI/Machine Learning", count: 9, percentage: 2.05, color: "#20B2AA" }, - { specialty_group: "Project Management", count: 8, percentage: 1.82, color: "#FF6347" }, - { specialty_group: "Software Engineer", count: 7, percentage: 1.59, color: "#4682B4" }, - { specialty_group: "No Specialty/Other", count: 6, percentage: 1.36, color: "#32CD32" }, - { specialty_group: "Cloud/Infrastructure", count: 4, percentage: 0.91, color: "#FF1493" }, - { specialty_group: "Cybersecurity", count: 4, percentage: 0.91, color: "#00CED1" }, - { specialty_group: "Fotografo", count: 2, percentage: 0.45, color: "#FF8C00" }, - { specialty_group: "Software Architect", count: 2, percentage: 0.45, color: "#9370DB" }, - { specialty_group: "Tech Recruiting", count: 2, percentage: 0.45, color: "#3CB371" }, - ]; +const GENDER = [ + { label: 'Hombre', pct: 77, color: '#FF0A55' }, + { label: 'Mujer', pct: 22, color: '#0E5C3A' }, + { label: 'No binario / NS', pct: 1, color: '#D4AF37' }, +]; - // Datos de tecnologías - const datosTecnologias = [ - { technology_name: "JavaScript", mention_count: 56, unique_attendees: 56, percentage_of_mentions: 18.54, color: "#FF6B9D" }, - { technology_name: "Java", mention_count: 49, unique_attendees: 49, percentage_of_mentions: 16.23, color: "#C44569" }, - { technology_name: "Python", mention_count: 43, unique_attendees: 43, percentage_of_mentions: 14.24, color: "#F8B500" }, - { technology_name: "Node.js", mention_count: 32, unique_attendees: 32, percentage_of_mentions: 10.60, color: "#4ECDC4" }, - { technology_name: "React", mention_count: 16, unique_attendees: 16, percentage_of_mentions: 5.30, color: "#95E1D3" }, - { technology_name: "C#", mention_count: 12, unique_attendees: 12, percentage_of_mentions: 3.97, color: "#FFA07A" }, - { technology_name: "C++", mention_count: 10, unique_attendees: 10, percentage_of_mentions: 3.31, color: "#98D8C8" }, - { technology_name: "PHP", mention_count: 10, unique_attendees: 10, percentage_of_mentions: 3.31, color: "#FFD700" }, - { technology_name: "TypeScript", mention_count: 8, unique_attendees: 8, percentage_of_mentions: 2.65, color: "#87CEEB" }, - { technology_name: "Android", mention_count: 7, unique_attendees: 7, percentage_of_mentions: 2.32, color: "#DDA0DD" }, - { technology_name: "React Native", mention_count: 7, unique_attendees: 7, percentage_of_mentions: 2.32, color: "#F0E68C" }, - { technology_name: "Angular", mention_count: 6, unique_attendees: 6, percentage_of_mentions: 1.99, color: "#20B2AA" }, - { technology_name: "C", mention_count: 6, unique_attendees: 6, percentage_of_mentions: 1.99, color: "#FF6347" }, - { technology_name: "Linux", mention_count: 6, unique_attendees: 6, percentage_of_mentions: 1.99, color: "#4682B4" }, - { technology_name: "HTML/CSS", mention_count: 5, unique_attendees: 5, percentage_of_mentions: 1.66, color: "#32CD32" }, - { technology_name: "iOS", mention_count: 5, unique_attendees: 5, percentage_of_mentions: 1.66, color: "#FF1493" }, - { technology_name: "SQL/SQL Server", mention_count: 4, unique_attendees: 4, percentage_of_mentions: 1.32, color: "#00CED1" }, - { technology_name: "AWS", mention_count: 3, unique_attendees: 3, percentage_of_mentions: 0.99, color: "#FF8C00" }, - { technology_name: "Go", mention_count: 3, unique_attendees: 3, percentage_of_mentions: 0.99, color: "#9370DB" }, - { technology_name: "MongoDB", mention_count: 3, unique_attendees: 3, percentage_of_mentions: 0.99, color: "#3CB371" }, - { technology_name: "Kotlin", mention_count: 2, unique_attendees: 2, percentage_of_mentions: 0.66, color: "#8B4513" }, - { technology_name: "Ruby", mention_count: 2, unique_attendees: 2, percentage_of_mentions: 0.66, color: "#CD5C5C" }, - { technology_name: "Azure", mention_count: 1, unique_attendees: 1, percentage_of_mentions: 0.33, color: "#4169E1" }, - { technology_name: "Mulesoft", mention_count: 1, unique_attendees: 1, percentage_of_mentions: 0.33, color: "#FF69B4" }, - { technology_name: "PostgreSQL", mention_count: 1, unique_attendees: 1, percentage_of_mentions: 0.33, color: "#00FA9A" }, - { technology_name: "Rust", mention_count: 1, unique_attendees: 1, percentage_of_mentions: 0.33, color: "#FFD700" }, - { technology_name: "Salesforce", mention_count: 1, unique_attendees: 1, percentage_of_mentions: 0.33, color: "#00BFFF" }, - { technology_name: "SAP", mention_count: 1, unique_attendees: 1, percentage_of_mentions: 0.33, color: "#FF6347" }, - { technology_name: "Spring", mention_count: 1, unique_attendees: 1, percentage_of_mentions: 0.33, color: "#9370DB" }, - ]; +const AGE = [ + { label: '18-24', pct: 40, color: '#FF0A55' }, + { label: '25-34', pct: 37, color: '#0E5C3A' }, + { label: '35-44', pct: 17, color: '#D4AF37' }, + { label: '45+', pct: 6, color: '#7A0428' }, +]; - const chartConfig = { - count: { - label: "Cantidad", - color: "#FF6B9D", - }, - percentage: { - label: "Porcentaje", - color: "#C44569", - }, - }; +const MAX_ATT = Math.max(...ATTENDANCE.map(d => d.count)); +const CHART_H = 220; - // Formatear el tooltip para mostrar count y percentage - const customTooltip = ({ active, payload }: any) => { - if (active && payload && payload.length) { - const data = payload[0].payload; - const label = data.response || data.age_range; - return ( -
-

{label}

-

Cantidad: {data.count}

-

Porcentaje: {data.percentage}%

-
- ); - } - return null; - }; +/* ── Pixel pie chart (SVG, crispEdges) ───────────────────────────── */ +interface PieSlice { label: string; pct: number; color: string } - // Tooltip específico para edad - const customTooltipEdad = ({ active, payload }: any) => { - if (active && payload && payload.length) { - const data = payload[0].payload; - return ( -
-

{data.age_range}

-

Cantidad: {data.count}

-

Porcentaje: {data.percentage}%

-
- ); - } - return null; - }; +function polarToXY(cx: number, cy: number, r: number, angleDeg: number) { + const rad = ((angleDeg - 90) * Math.PI) / 180; + return { x: cx + r * Math.cos(rad), y: cy + r * Math.sin(rad) }; +} - // Tooltip específico para experiencia - const customTooltipExperiencia = ({ active, payload }: any) => { - if (active && payload && payload.length) { - const data = payload[0].payload; - return ( -
-

{data.experience_years} años

-

Cantidad: {data.count}

-

Porcentaje: {data.percentage}%

-
- ); - } - return null; - }; - - // Tooltip específico para especialidades - const customTooltipEspecialidades = ({ active, payload }: any) => { - if (active && payload && payload.length) { - const data = payload[0].payload; - return ( -
-

{data.specialty_group}

-

Cantidad: {data.count}

-

Porcentaje: {data.percentage}%

-
- ); - } - return null; - }; +const PixelPie = ({ data, size = 160 }: { data: PieSlice[]; size?: number }) => { + const cx = size / 2; + const cy = size / 2; + const r = size / 2 - 4; - // Tooltip específico para tecnologías - const customTooltipTecnologias = ({ active, payload }: any) => { - if (active && payload && payload.length) { - const data = payload[0].payload; - return ( -
-

{data.technology_name}

-

Menciones: {data.mention_count}

-

Asistentes únicos: {data.unique_attendees}

-

Porcentaje: {data.percentage_of_mentions}%

-
- ); - } - return null; - }; + let cumAngle = 0; + const slices = data.map(d => { + const startAngle = cumAngle; + const sweep = (d.pct / 100) * 360; + cumAngle += sweep; + return { ...d, startAngle, sweep }; + }); return ( - -
-

- Numeralia Posadev 2025 -

+ + {/* Pixel-grid overlay pattern */} + + + + + + {/* Scanline pattern per color */} + {slices.map((s, i) => ( + + + + + ))} + -
- {/* Gráfica de Pastel */} -
-

- Identidad de Género -

-
- - - `${response}: ${percentage}%`} - outerRadius={140} - fill="#8884d8" - dataKey="count" - > - {datosGenero.map((entry, index) => ( - - ))} - - - - -
-
+ {slices.map((s, i) => { + if (s.sweep >= 360) { + return ( + + + + + ); + } + const start = polarToXY(cx, cy, r, s.startAngle); + const end = polarToXY(cx, cy, r, s.startAngle + s.sweep); + const large = s.sweep > 180 ? 1 : 0; + const d = [ + `M ${cx} ${cy}`, + `L ${start.x.toFixed(1)} ${start.y.toFixed(1)}`, + `A ${r} ${r} 0 ${large} 1 ${end.x.toFixed(1)} ${end.y.toFixed(1)}`, + 'Z', + ].join(' '); + + return ( + + + + + ); + })} + + {/* Center hole — donut style */} + + + ); +}; + +/* ── Legend ──────────────────────────────────────────────────────── */ +const PieLegend = ({ data }: { data: PieSlice[] }) => ( +
+ {data.map(d => ( +
+
+ + {d.label} + + + {d.pct}% + +
+ ))} +
+); + +/* ── Horizontal bar ──────────────────────────────────────────────── */ +const HBar = ({ label, pct }: { label: string; pct: number }) => ( +
+
+ {label} +
+
+
+
+
+ {pct}% +
+
+); - {/* Gráfica de Barras */} -
-

- Cantidad por Género -

- - - - - - - - - +/* ── Chart panel wrapper ─────────────────────────────────────────── */ +const ChartPanel = ({ title, children }: { title: string; children: React.ReactNode }) => ( +
+
+ {title} +
+ {children} +
+); + +/* ── Main component ──────────────────────────────────────────────── */ +const Estadisticas = () => ( +
+ + 4,257+ asistentes · 2017–2025 + fuente: registros históricos + + } + > + + {/* ── Header ─────────────────────────────────────────────── */} +
+

+ Quiero saber
los números. +

+

+ Datos históricos de Posadev 2017–2025 en Guadalajara. + 9 ediciones, más de 4,257 asistentes: esto es quiénes son, + cuántos años llevan en la industria y qué tecnologías usan. +

+
+ + {/* ── Stats grid ─────────────────────────────────────────── */} +
+
+
+ Asistentes acumulados +
+
+ 4,257+ +
+
+ Suma histórica 2017–2025 (incl. 2 ediciones virtuales).
- - {/* Sección de Distribución por Edad */} -
- {/* Gráfica de Pastel - Edad */} -
-

- Distribución por Edad -

-
- - - `${age_range}: ${percentage}%`} - outerRadius={140} - fill="#8884d8" - dataKey="count" - > - {datosEdad.map((entry, index) => ( - - ))} - - - - + {[ + { label: 'Asistentes 2025', n: '440', sub: 'Edición 2025 presencial · GDL.' }, + { label: 'Ediciones', n: '9', sub: '2017–2025 · 2 ediciones virtuales.' }, + { label: 'Tecnologías top', n: 'JS', sub: 'JavaScript · Java · Python.' }, + ].map(({ label, n, sub }) => ( +
+
+ {label} +
+
+ {n} +
+
+ {sub}
+ ))} +
- {/* Gráfica de Línea - Edad */} -
-

- Distribución por Rango de Edad -

- - - - - - - - - + {/* ── Attendance bar chart ────────────────────────────────── */} +
+
+
+
+ Asistencia por edición
-
- - {/* Sección de Años de Experiencia */} -
- {/* Gráfica de Pastel - Experiencia */} -
-
-

- Años de Experiencia -

-
- - - `${experience_years} años: ${percentage}%`} - outerRadius={140} - fill="#8884d8" - dataKey="count" - > - {datosExperiencia.map((entry, index) => ( - - ))} - - - - -
-
+
+ {[ + { label: 'PRESENCIAL', green: false }, + { label: 'VIRTUAL', green: true }, + ].map(({ label, green }) => ( + + + {label} + + ))}
- - {/* Sección de Especialidades */} -
- {/* Gráfica de Pastel - Especialidades */} -
-

- Especialidades -

-
- - - { - // Solo mostrar etiquetas para porcentajes mayores a 2% - if (percentage > 3) { - return `${specialty_group}: ${percentage}%`; - } - return ''; - }} - outerRadius={150} - fill="#8884d8" - dataKey="count" - > - {datosEspecialidades.map((entry, index) => ( - - ))} - - - - -
+
+ {[250, 500, 750, 1000].map(v => ( +
+ ))} +
+ {ATTENDANCE.map(({ year, count, virtual: isVirtual }) => { + const h = Math.round((count / MAX_ATT) * CHART_H); + return ( +
+
{isVirtual ? '1k+' : count}
+
+
+ ); + })} +
+
+ {ATTENDANCE.map(({ year }) => ( +
{year}
+ ))}
+
+
- {/* Sección de Tecnologías */} -
- {/* Gráfica de Barras - Tecnologías */} -
-

- Tecnologías -

- - b.mention_count - a.mention_count)} - layout="vertical" - margin={{ top: 5, right: 8, left: 80, bottom: 5 }} - > - - - - - - {datosTecnologias.map((entry, index) => ( - - ))} - - - + {/* ── Pie charts row ──────────────────────────────────────── */} +
+ +
+ +
-
+ + +
+ + +
+
+
+ + {/* ── Horizontal bars row ─────────────────────────────────── */} +
+ + {EXPERIENCE.map(d => )} + + + {INTERESTS.map(d => )} +
- - ); -}; -export default Estadisticas; + +
+); +export default Estadisticas; \ No newline at end of file diff --git a/src/pages/Index.tsx b/src/pages/Index.tsx index 680cd14..23877a0 100644 --- a/src/pages/Index.tsx +++ b/src/pages/Index.tsx @@ -1,42 +1,71 @@ -import React, {useEffect} from 'react'; +import React, { useEffect } from 'react'; import Hero from '@/components/Hero'; import Gallery from '@/components/Gallery'; -import {useLocation} from "react-router-dom"; -import BecomeSponsor from "@/components/BecomeSponsor.tsx"; -import Sponsors from "@/components/Sponsors.tsx"; -import Estadisticas from "@/pages/Estadisticas.tsx"; +import { useLocation } from 'react-router-dom'; +import BecomeSponsor from '@/components/BecomeSponsor'; +import Sponsors from '@/components/Sponsors'; +import Estadisticas from '@/pages/Estadisticas'; + +const STRIPE_ITEMS = [ + 'POSADEV 2026', + '1 DÍA · 4 TRACKS', + '30+ CHARLAS', + '6 COMUNIDADES', + '$500 MXN', + 'COMIDA INCLUIDA', + 'HOLIDAY INN GUADALAJARA EXPO', + 'SÁB 28 NOV', +]; + +const Stripe = () => { + const row = ( + + {STRIPE_ITEMS.map((item, i) => ( + + {item} + + ))} + + ); + return ( + + ); +}; const Index = () => { - const location = useLocation(); - useEffect(() => { - if (location.hash) { - const element = document.querySelector(location.hash); - if (element) { - setTimeout(() => { - const headerOffset = 80; - const elementPosition = element.getBoundingClientRect().top + window.scrollY; - const offsetPosition = elementPosition - headerOffset; + const location = useLocation(); - window.scrollTo({ - top: offsetPosition, - behavior: "smooth", - }); - }, 100); - } - } - }, [location]); + useEffect(() => { + if (location.hash) { + const element = document.querySelector(location.hash); + if (element) { + setTimeout(() => { + const elementPosition = element.getBoundingClientRect().top + window.scrollY; + window.scrollTo({ top: elementPosition - 60, behavior: 'smooth' }); + }, 100); + } + } + }, [location]); - return ( - <> - - -
- - -
- - - ); + return ( +
+
+ + + +
+ +
+ +
+
+ +
+
+ ); }; -export default Index; +export default Index; \ No newline at end of file diff --git a/tailwind.config.ts b/tailwind.config.ts index d1d5664..107af1d 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -19,6 +19,13 @@ export default { } }, extend: { + fontFamily: { + pixel: ['"Pixelify Sans"', 'ui-sans-serif', 'monospace'], + grotesk: ['"Space Grotesk"', 'ui-sans-serif', 'system-ui', 'sans-serif'], + vt: ['"VT323"', 'ui-monospace', 'monospace'], + silk: ['"Silkscreen"', 'ui-monospace', 'monospace'], + tiny: ['"Press Start 2P"', 'ui-monospace', 'monospace'], + }, backgroundImage: { 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))', },