44 Commits

Author SHA1 Message Date
418fef1a15 Remove email obfuscation from contact page
The base64 atob() pattern is trivially decoded by scrapers, making it
ineffective. The contact form is the preferred channel anyway.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 19:36:54 -04:00
01cad11472 Fix ALTCHA widget not loading or submitting
Two bugs prevented form submission entirely:
1. `challengeurl` attribute was renamed to `challenge` in altcha v3 — the
   widget silently ignored the old name so it never fetched a challenge.
2. `altchaWidget.value` is not an exposed property on the v3 custom element;
   read the solved payload from the hidden `<input name="altcha">` the widget
   renders in light DOM instead.

Also clears the err-altcha error message at the start of each submit attempt
so it doesn't linger after the user completes verification and retries.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 10:44:05 -04:00
5e6b201336 Add Cache-Control: no-store to ALTCHA challenge endpoint 2026-06-12 09:20:17 -04:00
c503d6dd75 Easter egg: trigger on copyright symbol click only
Previously fired on 5 rapid taps anywhere on the page.
Now triggers with a single click on the © in the footer.
Added id="bpb-copyright" to the symbol span in nav.js.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 06:03:15 -04:00
2ebbe4fbe0 Fix ALTCHA CDN path for v3 widget
v3 moved the widget from dist/altcha.min.js to dist/main/altcha.min.js;
the old path served a cached v2 widget which expected a 'challenge'
string field and threw split-on-undefined against v3's parameters format.
Pin to @3.1.0 to prevent future surprise upgrades.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 06:01:45 -04:00
c00f2de338 Upgrade main-site to Node 20 for Web Crypto globals
Node 18 requires --experimental-global-webcrypto for crypto.subtle /
crypto.getRandomValues as globals; Node 20 LTS exposes them by default,
which altcha/lib needs for createChallenge.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 05:56:40 -04:00
9cac3a8e8a Fix altcha require path for Node 18 compatibility
Node 18 enforces the package exports map; the deep path
'altcha/dist/lib/index.umd.cjs' is not exported, but 'altcha/lib' is.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-11 14:43:53 -04:00
0652339539 Add ALTCHA widget to main page contact form
The widget was only on the /contact/ page; main page form was unprotected.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-11 14:25:27 -04:00
a89788f531 Add ALTCHA proof-of-work spam protection to contact form
- Server: /api/altcha generates a SHA-256 challenge (v3 API); /api/contact
  verifies the widget payload before processing the submission
- Widget: added <altcha-widget> from CDN above the submit button
- contact-form.js: blocks submission if altcha value is missing and
  appends it to FormData
- docker-compose.yml: passes ALTCHA_HMAC_KEY env var to main-site container
- package.json: added altcha@3.1.0

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-11 14:23:42 -04:00
2e5f253580 Polish: meta/OG tags, JSON-LD, 404 pages, typo fix
- Unique title + meta description on every main-site page
- OpenGraph + Twitter card tags sitewide (hero image on homepage, logo elsewhere)
- LocalBusiness JSON-LD on homepage for Google rich results
- Custom 404 page on main-site (branded, links home + contact)
- Custom not-found page on estore
- Fix typo: "Delivery avalable" → "Delivery available"

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 00:47:26 -04:00
0d57760df1 Contact form: set min date to today on event date picker 2026-06-07 00:35:32 -04:00
77318fb477 Contact form: split name into first/last, add ntfy notification
- First and last name are now separate required fields
- Server combines them into a full name for emails
- Sends a push notification to NTFY_URL on new inquiry (fire-and-forget)
- NTFY_URL env var wired through docker-compose

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 00:32:06 -04:00
cd18bd3937 Contact form: add emoji to subject, show event date instead of type 2026-06-07 00:27:50 -04:00
d026bc8217 Fix balloon animation: use Web Animations API instead of CSS custom properties in keyframes 2026-06-06 21:38:40 -04:00
75b20e6ca2 Add debug logging to easter egg trigger 2026-06-06 21:29:51 -04:00
181195dbbc Fix easter egg trigger: use touchstart on mobile, 5 taps in 3s
click events are unreliable on mobile due to scroll handling. Use
touchstart (fires immediately) for mobile and click for desktop with
deduplication. Lower threshold to 5 taps, widen window to 3 seconds.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 21:23:05 -04:00
066364d2b7 Fix easter egg trigger: 7 quick taps on non-interactive area
Long press on the logo link was unreliable. Switch to detecting 7 rapid
taps on any non-link/button area within 2 seconds — works on mobile and
desktop without conflicting with navigation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 21:15:26 -04:00
2002d7f35a Add balloon easter egg — long press logo to trigger
Hold the logo for 0.6s (works on mobile touch and desktop mouse) to launch
22 balloon silhouettes floating up from the bottom. Balloons drift with a
slight sway, have a shine highlight, and naturally disappear under the navbar.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 21:07:06 -04:00
5900ce817e Fix dark mode: add data-theme=light to all main-site HTML pages
Bulma 1.0 follows system dark mode preference if no theme is set.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 21:01:38 -04:00
aee1f10179 Sync native contact form to main-site, replace iframe
Replaces the third-party iframe form on both the homepage and contact page
with the self-hosted form: drag-and-drop photo upload, honeypot, rate
limiting, inline validation, auto-reply email. Adds multer/sharp/nodemailer
dependencies and the /api/contact endpoint to server.js.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 20:32:05 -04:00
53a2ca03e7 fix: force Bulma light theme on admin page 2026-05-21 12:07:22 -04:00
55055ae9bc fix: light theme for all admin modals 2026-05-21 12:02:56 -04:00
1435964f6f feat: drag-to-select and full-header click in admin gallery
- Click anywhere on the top bar of a photo card to toggle selection
  (replaces the tiny checkbox)
- Drag across the grid to rubber-band select multiple photos at once
- Selected cards show a blue ring + tinted header + solid checkmark icon
- Cards swept during drag show a green ring preview before releasing
- Fixed innerHTML += perf issue (now builds all cards then sets once)
- Thumbnails used in grid so page loads faster

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 11:49:31 -04:00
7fce1632be feat: editable tag presets, next/prev modal nav, needs-tagging filter
- Restructured presets as single-tag accumulators (click multiple to build up tags)
- Added 6 new tags: bridal-shower, cocktail, signature, indoor, outdoor, mitzvah
- Fixed organic/garland alias conflict
- Presets stored in data/presets.json with full CRUD API (add, edit, delete from admin)
- Edit modal shows photo thumbnail, prev/next navigation, preset buttons
- Keyboard shortcuts: Ctrl+Enter to save, arrow keys to navigate, Esc to close
- "Needs tagging" filter in manage view shows only uncategorized/low-tag photos

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 11:42:32 -04:00
0e4461e957 fix: reseed sets createdAt from filename timestamp or file mtime
Photos reseeded from disk now sort by their original upload time
instead of all getting the same insertion timestamp.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 11:29:11 -04:00
633f1e2380 fix: reseed script now skips -sm and -md variant files
Previously all .webp files were indexed including thumbnails and medium
variants, causing each photo to appear three times in the gallery.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 15:48:06 -04:00
2723a6d954 feat: replace text watermark with BPB logo SVG overlay on gallery uploads
Loads bpb-watermark.svg at startup, converts black fills to semi-transparent
white (35% opacity), and composites it centered at 45% image width over every
uploaded photo. Removes the old diagonal "BEACH PARTY BALLOONS" text overlay.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 15:39:03 -04:00
92cf44e5f5 fix: resolve gallery CORS failure and simplify API routing
- gallery backend: replace origin whitelist with wildcard CORS — NPMplus
  was stripping the Allow-Origin header; wildcard passes through reliably
  and is appropriate for a public photo gallery
- gallery.js: hardcode photobackend.beachpartyballoons.com as the API base
  (NPMplus already routes this subdomain) and remove dead port fallbacks
- nginx.conf: add /photos and /uploads proxy routes to gallery-backend
  (kept for direct-nginx access; NPMplus handles external traffic)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 14:53:13 -04:00
4a135a7919 fix: route gallery API through nginx, send Square receipts, unblock order completion
- nginx: add /photos and /uploads proxy routes to gallery-backend so the
  browser can reach the gallery API without needing direct port access
- gallery.js: drop hardcoded port/subdomain fallbacks; use same-origin path
  via the new nginx routes
- square.ts: pass buyerEmailAddress to createPayment so Square auto-sends
  a payment receipt to the customer on capture
- square.ts: create fulfillments in RESERVED state (was PROPOSED) so staff
  can mark orders complete/filled directly from the Square dashboard
- CartDrawer: merge Custom Vinyl into the Shape Balloon line item (one fewer
  Square line item per vinyl order); show modifier price deltas in cart

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 14:32:00 -04:00
5777788127 fix: point all Plausible scripts to self-hosted metrics instance
Replace plausible.io with metrics.beachpartyballoons.com across all
main-site pages and estore layout.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 13:15:41 -04:00
8e283a4dff fix: update Green Tea hex to #b2ddc3, remove Pastel Magenta
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 10:50:16 -04:00
5643153a05 feat: add Magenta (#a01357) to Pinks & Reds color family
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 10:41:50 -04:00
07ae012aa3 feat: add Green Tea (#8a9f7f) to Greens color family
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 10:39:34 -04:00
57cc5840b9 feat: obfuscate email with click-to-reveal across all pages
Replace all bare info@ occurrences with a click-to-reveal pattern:
- New EmailLink React component (base64 decode on click, never in DOM pre-click)
- privacy, terms, refund pages use EmailLink
- contact/index.html uses a vanilla JS button with the same pattern
- PaymentForm mailto builder uses atob() to keep email out of source literals

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 10:23:50 -04:00
3330c47af2 fix: secure admin API endpoints with Bearer token auth
- main-site/server.js: add requireAuth middleware to POST /api/update-status
- gallery-backend/routes/photos.js: add requireAuth to upload, delete, and update routes
- admin/admin.js: send Authorization: Bearer header on all mutating requests (fetch + XHR upload); handle 401 on update-status and photo save
- docker-compose.yml: pass ADMIN_PASSWORD to gallery-backend; remove MongoDB public port mapping (27017:27017)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 08:30:58 -04:00
6705293e50 fix/feat: hex conflict, scroll-to-top, search all, admin error emails
- Fix Chrome Rose Gold hex (#B76E79 → #C17F87) so it no longer
  conflicts with Classic Rose Gold; image still used for display
- ScrollToTop hides when cart drawer is open and uses z-index 98
  (below the drawer); uses drawerOpen from CartContext
- Search now switches to All tab automatically so results span every
  item, not just the active category
- Add sendAdminErrorAlert() to notify.ts; checkout route emails
  admin@beachpartyballoons.com on unexpected server errors and on
  critical calendar-write failures; card decline errors are not
  forwarded (customers can self-resolve those)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 14:19:29 -04:00
01c908e919 fix: color picker selection keyed on name instead of hex
Classic Rose Gold and Chrome Rose Gold share the same hex (#B76E79),
so clicking one would deselect the other. Switched all selection
checks (toggle, remove, highlight) to use color.name which is unique.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 14:08:57 -04:00
0576677523 feat: scroll-to-top button in estore; fix JS/CSS cache headers on main site
- Add ScrollToTop component matching main site's green Top button
  (appears after 130px scroll, same styling and font)
- Fix main-site server.js: JS/CSS now use max-age=3600 + must-revalidate
  instead of 30d immutable — changes reach users within 1 hour instead
  of being stuck in browser cache for a month
- Images/fonts keep 30d immutable (safe, as they are content-addressed)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 14:41:42 -04:00
f4b1f7722e Fix data dir permissions and legal doc links
- Dockerfile: create /app/data owned by nextjs before USER switch so fresh
  deployments work without manual chown. Existing servers need:
    sudo chown -R 1001:1001 estore/data
- nav.js: fix footer legal links to point to /shop/privacy|terms|refund
  (pages live in estore, not main site)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 13:28:20 -04:00
215a8f2e3f Add Plausible Analytics to color page and estore
Both were missing tracking. All pages now report to beachpartyballoons.com
in Plausible.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 21:25:06 -04:00
50680a323f Major overhaul: shared nav, admin improvements, email enhancements, routing fixes
Navigation & layout
- Replace per-page hardcoded nav/footer with shared nav.js (client-side injection)
- Add nginx reverse proxy back to docker-compose for clean localhost routing
- Rename /color-picker/ to /color/ across nav, directory, and references

eStore admin
- Add variation hiding controls (mirrors existing modifier hiding)
- Add delivery rate editor (base fee + per-mile per tier, persisted to data/)
- Fix all missing BASE prefix on fetch calls (admin PATCH/DELETE, availability, slots, colors)
- Mount estore/data/ as a Docker volume so admin config survives rebuilds

Booking & calendar
- Set pickup calendar events to TRANSPARENT (free) so they don't block delivery slots
- Skip CANCELLED events in busy-time calculation
- Re-check slot availability at checkout before charging (409 on conflict)

Phone & email validation
- Auto-format phone as (XXX) XXX-XXXX as user types
- Require exactly 10 digits; tighten email regex

Confirmation emails (store alert + customer)
- Full item detail per line: name, price, add-ons, colors, note
- Charges breakdown: subtotal, delivery fee, tax, total
- Delivery window: simplified M/D/YY h:mm – h:mm AM/PM format
- .ics calendar attachment on customer confirmation

Delivery rates
- Extract configurable rates to delivery-rates.ts (server-only, no fs in client bundle)
- calcDelivery() accepts optional rates param; delivery-quote route passes configured rates

Content
- Change all "40+ latex colors" references to "70+"

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 21:14:06 -04:00
c984c14085 Remove terms page — now lives in estore footer 2026-04-13 19:22:36 -04:00
f58ae2c5f7 Add 'main-site/color-picker/' from commit '248d73a619ea4fbdca711a516f464cd0a505bfae'
git-subtree-dir: main-site/color-picker
git-subtree-mainline: 21ebb9667b34023f8d563bf8fa2abf7f838f51d7
git-subtree-split: 248d73a619ea4fbdca711a516f464cd0a505bfae
2026-04-13 19:22:30 -04:00
746868d720 Add 'main-site/' from commit '5cefb4d1618bc54ae0e86830421a8c911900302c'
git-subtree-dir: main-site
git-subtree-mainline: 4d1daa39101c0a85ca6d916f1c31139faf39632a
git-subtree-split: 5cefb4d1618bc54ae0e86830421a8c911900302c
2026-04-13 19:22:17 -04:00