// theme.jsx — Paleta "Mitla" + tipografías + motivos zapotecos de la etiqueta de mezcal.
// Exporta a window: T, Micro, Greca, StepDiamond, AgaveMark, Reveal, Rule, injectThemeCSS.
const T = {
bg: '#ece3cf', // crema
paper: '#efe7d4', // papel
card: '#f3ecdb', // tarjeta (un punto más clara)
ink: '#2c4632', // verde agave oscuro
soft: '#5a6e54', // verde suave
accent: '#b1542e', // terracota
deep: '#22351f', // verde profundo (pie / overlays)
line: 'rgba(44,70,50,0.22)',
display: "'Cormorant Garamond', serif",
micro: "'Barlow Semi Condensed', sans-serif",
};
// textura de papel sutil (idéntica a la etiqueta)
const GRAIN = "url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.045'/%3E%3C/svg%3E\")";
function injectThemeCSS() {
if (document.getElementById('__inviteTheme')) return;
const s = document.createElement('style');
s.id = '__inviteTheme';
s.textContent = `
*{margin:0;padding:0;box-sizing:border-box;}
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%;}
body{background:${T.bg};color:${T.ink};font-family:${T.micro};
background-image:${GRAIN};font-weight:500;
-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;overflow-x:hidden;}
::selection{background:${T.accent}33;}
a{color:inherit;}
.serif{font-family:${T.display};}
.ital{font-family:${T.display};font-style:italic;}
.micro{font-family:${T.micro};text-transform:uppercase;letter-spacing:.24em;font-weight:600;}
/* scroll reveal */
.rv{opacity:0;transform:translateY(26px);transition:opacity 1s cubic-bezier(.2,.7,.2,1),transform 1s cubic-bezier(.2,.7,.2,1);will-change:opacity,transform;}
.rv.in{opacity:1;transform:none;}
@media (prefers-reduced-motion:reduce){.rv{opacity:1!important;transform:none!important;transition:none!important;}}
/* image-slot look */
image-slot{--is-bg:${T.card};--is-fg:${T.soft};--is-border:${T.line};display:block;}
.lnk{text-decoration:none;border-bottom:1px solid ${T.accent}66;transition:border-color .3s,color .3s;}
.lnk:hover{border-color:${T.accent};color:${T.accent};}
input,select,textarea,button{font-family:${T.micro};}
`;
document.head.appendChild(s);
}
function Micro({ children, size = 11, ls = '.24em', color = T.soft, weight = 600, as = 'div', style }) {
const Tag = as;
return (
{children}
);
}
// ——— Greca escalonada de Mitla (banda tileable) ———
let __gid = 0;
function Greca({ color = T.ink, strokeWidth = 1.5, step = 6, ups = 3, pad = 3, double = true, opacity = 0.85, style }) {
const id = React.useMemo(() => `gr-${__gid++}`, []);
const h = ups * step;
const period = 2 * ups * step;
let d = `M0 ${h}`, x = 0, y = h;
for (let i = 0; i < ups; i++) { x += step; d += ` L${x} ${y}`; y -= step; d += ` L${x} ${y}`; }
for (let i = 0; i < ups; i++) { x += step; d += ` L${x} ${y}`; y += step; d += ` L${x} ${y}`; }
const totalH = h + pad * 2;
return (
);
}
function StepDiamond({ size = 22, color = T.accent, stroke = 1.6, fill = 'none' }) {
const s = size / 6;
const pts = [[3,0],[4,1],[5,2],[6,3],[5,4],[4,5],[3,6],[2,5],[1,4],[0,3],[1,2],[2,1]]
.map(([a,b]) => `${a*s},${b*s}`).join(' ');
return (
);
}
function AgaveMark({ size = 84, color = T.ink, blades = 11, sw = 1.4 }) {
const cx = size / 2, baseY = size * 0.92, spread = 78, arr = [];
for (let i = 0; i < blades; i++) {
const t = blades === 1 ? 0.5 : i / (blades - 1);
const ang = (-spread + t * spread * 2) * Math.PI / 180;
const lenFactor = 0.62 + 0.38 * (1 - Math.abs(t - 0.5) * 2) ** 0.9;
const len = size * 0.82 * lenFactor;
const tipX = cx + Math.sin(ang) * len, tipY = baseY - Math.cos(ang) * len;
const w = size * 0.045 * (0.7 + 0.3 * (1 - Math.abs(t - 0.5) * 2));
const perp = ang + Math.PI / 2;
const bx1 = cx + Math.cos(perp) * w, by1 = baseY + Math.sin(perp) * w;
const bx2 = cx - Math.cos(perp) * w, by2 = baseY - Math.sin(perp) * w;
arr.push();
}
return ;
}
// divider con rombo escalonado al centro
function Rule({ width = 220, color = T.line, diamond = T.accent, dSize = 20, style }) {
return (
);
}
// Reveal por scroll (IntersectionObserver compartido)
let __io;
function getIO() {
if (__io) return __io;
__io = new IntersectionObserver((entries) => {
entries.forEach((e) => { if (e.isIntersecting) { e.target.classList.add('in'); __io.unobserve(e.target); } });
}, { threshold: 0.08, rootMargin: '0px 0px -6% 0px' });
return __io;
}
// Red de seguridad: si por cualquier razón el observer no dispara (viewport raro,
// JS lento, etc.), revela todo lo pendiente para que nunca quede contenido oculto.
if (typeof window !== 'undefined' && !window.__rvSafety) {
window.__rvSafety = true;
const reveal = () => document.querySelectorAll('.rv:not(.in)').forEach((el) => el.classList.add('in'));
setTimeout(reveal, 2000);
window.addEventListener('load', () => setTimeout(reveal, 1500));
}
function Reveal({ children, delay = 0, as = 'div', className = '', style }) {
const ref = React.useRef(null);
React.useEffect(() => {
const el = ref.current; if (!el) return;
el.style.transitionDelay = (delay || 0) + 'ms';
getIO().observe(el);
}, [delay]);
const Tag = as;
return {children};
}
Object.assign(window, { T, GRAIN, injectThemeCSS, Micro, Greca, StepDiamond, AgaveMark, Rule, Reveal });