Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@
<button id="enterSystemBtn" class="enter-system-btn">
[ ENTER_SYSTEM ]
</button>
<button id="skipIntroBtn" class="enter-system-btn" style="margin-left:10px;font-size:0.8rem;opacity:0.7;">
[ SKIP_INTRO ]
</button>
</div>
</div>

Expand Down Expand Up @@ -501,6 +504,12 @@ <h1 class="main-title glitch" data-text="KAITOARTZ">KAITOARTZ</h1>
</div>

<form id="contactForm" class="contact-form">
<!-- Honeypot anti-spam field (hidden from real users) -->
<div aria-hidden="true" style="position:absolute;left:-9999px;top:-9999px;">
<label for="contactWebsite">Website</label>
<input type="text" id="contactWebsite" name="_gotcha" tabindex="-1" autocomplete="off">
</div>

<div class="form-group">
<label for="contactName" data-i18n="contact.name">NAME_IDENTIFIER:</label>
<input type="text" id="contactName" name="name" required placeholder="John_Doe" data-i18n="placeholder.name">
Expand All @@ -526,6 +535,11 @@ <h1 class="main-title glitch" data-text="KAITOARTZ">KAITOARTZ</h1>
</button>

<div class="form-status" id="formStatus"></div>

<p class="form-privacy-notice" style="margin-top:10px;font-size:0.75rem;color:var(--text-dim);opacity:0.7;">
Al enviar, tus datos se procesan vía <a href="https://formspree.io/legal/privacy-policy/" target="_blank" rel="noopener noreferrer" style="color:var(--accent-color);">Formspree</a>.
By submitting, your data is processed via <a href="https://formspree.io/legal/privacy-policy/" target="_blank" rel="noopener noreferrer" style="color:var(--accent-color);">Formspree</a>.
</p>
</form>
</section>

Expand Down
9 changes: 9 additions & 0 deletions public/sitemap.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://kaitoartz.github.io/</loc>
<lastmod>2025-06-01</lastmod>
<changefreq>monthly</changefreq>
<priority>1.0</priority>
</url>
</urlset>
9 changes: 9 additions & 0 deletions sitemap.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://kaitoartz.github.io/</loc>
<lastmod>2025-06-01</lastmod>
<changefreq>monthly</changefreq>
<priority>1.0</priority>
</url>
</urlset>
49 changes: 46 additions & 3 deletions src/scripts/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -916,8 +916,9 @@ class HyperScrollIntro {
}

initLenis() {
// Disable Lenis on mobile for performance and better native feel
if (typeof Lenis !== 'undefined' && !performanceManager.hardware.isMobile) {
// Disable Lenis on mobile/touch devices for performance and better native feel
const isTouchDevice = performanceManager.hardware.isMobile || ('ontouchstart' in window) || (navigator.maxTouchPoints > 0);
if (typeof Lenis !== 'undefined' && !isTouchDevice) {
// Usamos un wrapper específico si es posible, pero para el efecto global
// el demo usa window. Solo controlamos que no afecte al dashboard después
this.lenis = new Lenis({
Expand Down Expand Up @@ -968,6 +969,25 @@ class HyperScrollIntro {
if (btn) {
btn.addEventListener('click', () => this.warpAndEnter());
}

// Recruiter Mode: Skip intro button for quick access to portfolio
const skipBtn = document.getElementById('skipIntroBtn');
if (skipBtn) {
skipBtn.addEventListener('click', () => this.skipIntro());
}
}

skipIntro() {
if (this.state.warping || this.state.fading) return;
this.state.warping = true;

// Load body content synchronously if needed
if (typeof this.loadBodyContentSynchronously === 'function') {
this.loadBodyContentSynchronously();
}

// Skip directly to end without warp animation
this.endIntro();
}

async warpAndEnter() {
Expand Down Expand Up @@ -2906,6 +2926,9 @@ class AudioVisualizer {
this.animationId = requestAnimationFrame(this.draw);

this.analyser.getByteFrequencyData(this.dataArray);

// Skip rendering when there is no audio data (silence)
if (!this.dataArray.some(v => v > 0)) return;

const ctx = this.ctx;
const width = this.canvas.width;
Expand Down Expand Up @@ -3141,7 +3164,7 @@ class MatrixRain {
} else if (performanceManager.hardware.isMobile) {
scale = 1;
} else {
scale = Math.min(window.devicePixelRatio, 2);
scale = Math.min(window.devicePixelRatio, 1.5);
}

this.logicalWidth = window.innerWidth;
Expand Down Expand Up @@ -3289,6 +3312,8 @@ class ParallaxManager {
}

// ========== CONTACT FORM MANAGER ==========
const SUBMIT_COOLDOWN_MS = 30000;

class ContactFormManager {
constructor() {
this.form = null;
Expand All @@ -3297,6 +3322,7 @@ class ContactFormManager {
this.messageInput = null;
this.submitBtn = null;
this.statusDiv = null;
this.lastSubmitTime = 0;
}

init() {
Expand All @@ -3306,6 +3332,7 @@ class ContactFormManager {
this.nameInput = document.getElementById('contactName');
this.emailInput = document.getElementById('contactEmail');
this.messageInput = document.getElementById('contactMessage');
this.honeypotInput = document.getElementById('contactWebsite');
this.submitBtn = document.getElementById('submitBtn');
this.statusDiv = document.getElementById('formStatus');

Expand Down Expand Up @@ -3373,11 +3400,27 @@ class ContactFormManager {
async handleSubmit(e) {
e.preventDefault();

// Anti-spam: honeypot check
if (this.honeypotInput && this.honeypotInput.value) {
this.showStatus('TRANSMISSION_SUCCESSFUL ✓', 'success');
this.form.reset();
return;
}

// Rate limiting: 30 seconds between submissions
const now = Date.now();
if (now - this.lastSubmitTime < SUBMIT_COOLDOWN_MS) {
this.showStatus('RATE_LIMIT: WAIT BEFORE RETRANSMITTING', 'error');
return;
}

if (!this.validateAll()) {
this.showStatus('VALIDATION_ERROR: CHECK ALL FIELDS', 'error');
return;
}

this.lastSubmitTime = now;

this.submitBtn.disabled = true;
this.submitBtn.classList.add('transmitting');
this.showStatus('TRANSMITTING_DATA...', 'transmitting');
Expand Down