diff --git a/public/css/style.css b/public/css/style.css index 93dee54..7fb0b6b 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -798,6 +798,182 @@ section { color: var(--muted); } +.footer-right { + display: flex; + align-items: center; + gap: 0.6rem; +} + +.footer-sep { + color: var(--border); +} + +.footer a { + color: var(--dim); + transition: color 0.2s; +} + +.footer a:hover { + color: var(--accent); +} + +/* ============================================= + COOKIE CONSENT BANNER + ============================================= */ +.cookie-banner { + position: fixed; + bottom: 0; + left: 0; + right: 0; + z-index: 200; + display: flex; + align-items: center; + justify-content: space-between; + gap: 1.5rem; + padding: 1.25rem 2.5rem; + background: var(--dark); + border-top: 1px solid var(--border); + flex-wrap: wrap; +} + +.cookie-banner[hidden] { display: none; } + +.cookie-text { + font-size: 0.78rem; + color: var(--dim); + line-height: 1.6; + flex: 1; + min-width: 0; +} + +.cookie-text a { + color: var(--accent); + text-decoration: underline; + margin-left: 0.4rem; +} + +.cookie-actions { + display: flex; + gap: 0.75rem; + flex-shrink: 0; +} + +.cookie-btn { + font-family: var(--font-mono); + font-size: 0.72rem; + letter-spacing: 0.1em; + text-transform: uppercase; + padding: 0.55rem 1.25rem; + border: 1px solid var(--border); + background: transparent; + color: var(--dim); + cursor: pointer; + transition: color 0.2s, border-color 0.2s, background 0.2s; +} + +.cookie-btn:hover { + color: var(--text); + border-color: var(--muted); +} + +.cookie-btn-accept { + background: var(--accent); + color: var(--black); + border-color: var(--accent); +} + +.cookie-btn-accept:hover { + background: var(--white); + border-color: var(--white); + color: var(--black); +} + +/* ============================================= + PRIVACY POLICY PAGE + ============================================= */ +.policy-wrap { + max-width: 760px; + margin: 0 auto; + padding: 7rem 2.5rem 6rem; +} + +.policy-wrap h1 { + font-family: var(--font-display); + font-size: clamp(3rem, 6vw, 5.5rem); + line-height: 1; + color: var(--white); + letter-spacing: 0.02em; + margin-bottom: 0.5rem; +} + +.policy-effective { + font-size: 0.72rem; + letter-spacing: 0.15em; + text-transform: uppercase; + color: var(--dim); + margin-bottom: 3.5rem; +} + +.policy-section { + margin-bottom: 2.5rem; + border-top: 1px solid var(--border); + padding-top: 2rem; +} + +.policy-section h2 { + font-family: var(--font-mono); + font-size: 0.75rem; + font-weight: 500; + letter-spacing: 0.2em; + text-transform: uppercase; + color: var(--accent); + margin-bottom: 1rem; +} + +.policy-section p, +.policy-section li { + font-size: 0.88rem; + color: var(--dim); + line-height: 1.9; + margin-bottom: 0.8rem; +} + +.policy-section ul { + list-style: none; + padding: 0; +} + +.policy-section ul li::before { + content: '→ '; + color: var(--accent); +} + +.policy-section a { + color: var(--accent); + text-decoration: underline; +} + +.policy-section a:hover { + color: var(--white); +} + +.policy-back { + display: inline-block; + font-size: 0.75rem; + letter-spacing: 0.12em; + text-transform: uppercase; + color: var(--dim); + border-bottom: 1px solid var(--border); + padding-bottom: 0.2rem; + margin-bottom: 3rem; + transition: color 0.2s, border-color 0.2s; +} + +.policy-back:hover { + color: var(--accent); + border-color: var(--accent); +} + /* ============================================= ANIMATIONS ============================================= */ @@ -878,8 +1054,28 @@ section { text-align: center; } + .footer-right { + justify-content: center; + } + .projects-footer { flex-direction: column; align-items: flex-start; } + + .cookie-banner { + padding: 1.25rem 1.5rem; + flex-direction: column; + align-items: flex-start; + gap: 1rem; + } + + .cookie-actions { + width: 100%; + justify-content: flex-end; + } + + .policy-wrap { + padding: 6rem 1.5rem 4rem; + } } diff --git a/public/index.html b/public/index.html index ad6b120..6a8bdbe 100644 --- a/public/index.html +++ b/public/index.html @@ -11,14 +11,13 @@ - + @@ -243,10 +242,25 @@

Ready to see what AI can do for you?

© 2026 LookAtWhatAiCanDo, LLC + + + + diff --git a/public/js/app.js b/public/js/app.js index 2ac01d0..130db02 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -4,6 +4,11 @@ generated at deploy time by GitHub Actions. ============================================= */ +// Returns true if the user has consented to tracking and gtag is available, false otherwise. +function canTrack() { + return window.analyticsConsented === true && typeof gtag === 'function'; +} + function timeAgo(dateString) { if (!dateString) return null; const date = new Date(dateString); @@ -55,7 +60,7 @@ function renderProjects(projects, fromCache = false) { // Track project card clicks card.addEventListener('click', () => { - if (typeof gtag !== 'undefined') { + if (canTrack()) { gtag('event', 'project_click', { 'project_name': p.name, 'project_url': p.url @@ -117,11 +122,10 @@ loadProjects(); // ── Analytics Event Tracking ────────────────────────────────────────────────── (function initAnalyticsTracking() { - if (typeof gtag === 'undefined') return; - // Track CTA button clicks document.querySelectorAll('.btn-primary, .btn-ghost').forEach(btn => { btn.addEventListener('click', () => { + if (!canTrack()) return; gtag('event', 'cta_click', { 'button_text': btn.textContent.trim(), 'button_href': btn.getAttribute('href') @@ -133,6 +137,7 @@ loadProjects(); const emailLink = document.querySelector('a[href^="mailto:"]'); if (emailLink) { emailLink.addEventListener('click', () => { + if (!canTrack()) return; gtag('event', 'contact_email_click', { 'contact_method': 'email_link' }); @@ -142,6 +147,7 @@ loadProjects(); // Track social media link clicks document.querySelectorAll('.social-link').forEach(link => { link.addEventListener('click', () => { + if (!canTrack()) return; gtag('event', 'social_click', { 'platform': link.querySelector('.social-name')?.textContent || 'unknown', 'url': link.getAttribute('href') diff --git a/public/js/consent.js b/public/js/consent.js new file mode 100644 index 0000000..303a260 --- /dev/null +++ b/public/js/consent.js @@ -0,0 +1,65 @@ +/* ============================================= + consent.js — LookAtWhatAiCanDo + Cookie consent management. + - Checks localStorage for a stored decision. + - Shows/hides the consent banner accordingly. + - On accept: upgrades GA4 consent and fires config. + - On decline: stores decision and hides banner. + ============================================= */ + +(function initConsent() { + var STORAGE_KEY = 'cookie_consent'; + var GA_ID = 'G-QNSWDZWZ74'; + + function enableAnalytics() { + if (typeof gtag !== 'function') return; + gtag('consent', 'update', { 'analytics_storage': 'granted' }); + gtag('config', GA_ID); + window.analyticsConsented = true; + } + + function showBanner() { + var banner = document.getElementById('cookie-banner'); + if (banner) banner.removeAttribute('hidden'); + } + + function hideBanner() { + var banner = document.getElementById('cookie-banner'); + if (banner) banner.setAttribute('hidden', ''); + } + + // Apply any previously stored decision immediately + var stored = null; + try { + stored = localStorage.getItem(STORAGE_KEY); + } catch (e) { + // Storage unavailable — treat as no decision + } + if (stored === 'accepted') { + enableAnalytics(); + } else if (!stored) { + // No decision yet — show the banner + showBanner(); + } + // 'declined' — do nothing (analytics stays denied) + + // Wire up banner buttons + var acceptBtn = document.getElementById('cookie-accept'); + var declineBtn = document.getElementById('cookie-decline'); + + if (acceptBtn) { + acceptBtn.addEventListener('click', function () { + var saved = false; + try { localStorage.setItem(STORAGE_KEY, 'accepted'); saved = true; } catch (e) {} + if (saved) enableAnalytics(); + hideBanner(); + }); + } + + if (declineBtn) { + declineBtn.addEventListener('click', function () { + try { localStorage.setItem(STORAGE_KEY, 'declined'); } catch (e) {} + hideBanner(); + }); + } +}()); diff --git a/public/privacy.html b/public/privacy.html new file mode 100644 index 0000000..1262848 --- /dev/null +++ b/public/privacy.html @@ -0,0 +1,174 @@ + + + + + + Privacy Policy — LookAtWhatAiCanDo + + + + + + + + + + + +
+ + + + +
+ ← Back to Home + +
// Legal
+

Privacy Policy

+

Effective date: March 28, 2026

+ +
+

01 — Who We Are

+

+ LookAtWhatAiCanDo, LLC ("we", "our", "us") operates the website at + lookatwhataicando.llc. + We are an AI consulting company. Questions about this policy can be sent to + hello@lookatwhataicando.llc. +

+
+ +
+

02 — What Data We Collect

+

This site does not collect personal information directly. When you consent to analytics, Google Analytics 4 may collect:

+ +

We do not collect your name, email address, or any other identifying information unless you contact us directly.

+
+ +
+

03 — Cookies

+

+ This site stores your consent choice in your browser's localStorage under the key + cookie_consent. This is a local browser storage entry — not an HTTP cookie — and + is never transmitted to our servers. +

+

+ If you accept analytics, Google Analytics 4 sets HTTP cookies (e.g. _ga, _ga_*) + to distinguish visitors and sessions. In our standard setup these are first-party cookies set on + this domain in connection with a service provided by Google, and data collected via these cookies is + subject to + Google's Privacy Policy. +

+

+ If you decline, no analytics cookies are set. +

+
+ +
+

04 — How We Use Your Data

+

Analytics data is used solely to understand how visitors use this site so we can improve it. We do not:

+ +
+ +
+

05 — Third-Party Services

+

+ We use Google Analytics 4 (Google LLC, USA). When analytics is enabled, data is transferred to + Google servers in the United States under Google's standard contractual clauses. For details, see + Google's Privacy Policy and + Google Analytics data safeguards. +

+

+ This site also loads fonts from Google Fonts. Google may log your IP address when + serving font files. See + Google Fonts privacy FAQ. +

+
+ +
+

06 — Data Retention

+

+ Google Analytics retains event-level data for 14 months by default. Aggregated reports may be retained + indefinitely. You can request deletion of your data at any time by contacting us. +

+
+ +
+

07 — Your Rights

+

Depending on your location, you may have the right to:

+ +

To exercise these rights, email hello@lookatwhataicando.llc.

+
+ +
+

08 — How to Opt Out or Change Your Choice

+

You can change your analytics consent at any time:

+ +
+ +
+

09 — Children's Privacy

+

+ This site is not directed at children under 13 (or under 16 in the EU/UK). We do not knowingly + collect data from children. +

+
+ +
+

10 — Changes to This Policy

+

+ We may update this policy from time to time. The effective date at the top of this page will be + updated when changes are made. Continued use of this site after changes constitutes acceptance of + the revised policy. +

+
+ +
+

11 — Contact

+

+ LookAtWhatAiCanDo, LLC
+ hello@lookatwhataicando.llc +

+
+
+ + + + + +