Commit graph

400 commits

Author SHA1 Message Date
Natalie
49b5dec89b feat(quinn.www): SSO-gated /admin dev view; Theme Lab gated to it
Replace the ?theme-viewer opt-in with a hidden /admin route: the Theme Lab now
mounts ONLY on /admin (never on public pages). /admin is SSO-gated at the nginx
edge (auth_request to quinn SSO :3025 — unauth redirects to sso.transquinnftw.com)
and declared outside the route registry so it is absent from the sitemap; the
page sets noindex. Authenticated dev surface to preview themes without the full
admin panel. One-click set-as-site-default save is the next addition.
2026-06-24 04:05:26 -04:00
Natalie
91f6dc2adb fix(promo-banner): make entire banner clickable, not just the pill
The whole bar is now a single anchor (CardLink) wrapping artwork, copy and
the CTA pill, so clicking anywhere navigates — eliminating the ~80px dead
zone that was the real cause of near-zero promo CTR. The pill becomes an
aria-hidden visual affordance; dismiss (X) stays outside the link so it
never triggers navigation. One folded aria-label + focus-visible outline
keep it accessible; hover lift now driven from the parent anchor. Thumb
bumped 44->48px so the image creative reads as the hero.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 22:31:39 -04:00
Natalie
30d9a082ae fix(banners): show website logo as placeholder banner image when no platform-provided imgSrc
- On /banners, if a VerifiedProfile has empty imgSrc (no banner from user/platform), render the site logo (/icon-512.png) as a branded visual placeholder inside linked or static frame.
- Introduced LogoPreview, LogoPlaceholder, LogoImg styled components for contained square logo with padding/bg.
- Removed redundant text-only ProfileLink (the logo image now provides the visual + click target when href present).
- Updated comments and logic in BannerItem.
- Always provides an image slot now for uniform card layout (real banner or site logo).
- Typecheck clean; e2e smoke test for /banners passes.
- Fallback only affects UI rendering (data can still omit imgSrc); matches request for transquinnftw.com/banners.
- Uses existing static icon from manifest/public for the 'logo of the website'.
2026-06-23 06:53:41 -04:00
Natalie
bcd2d96a1f feat(quinn-admin): move default theme selector from hardcoded quinn.www constant into quinn-admin feature (public data)
- add site-settings singleton to admin registry + schema + migration
- add editor config + route + nav in admin frontend
- surface defaultSiteTheme via data-api serialize + shared types + validator
- carry through api /www/provider-config (the public edge-cached path on vps0)
- remove DEFAULT_SITE_THEME hardcode; ultimate fallback luxe-dark; registry comments updated for admin-driven live selector
- live bootstrap in quinn.www root + data hook to pick admin default without quinn.www rebuild (chrome + tokens update post-fetch)
- fixed incidental sortable test assertion to match current registry (pre-existing mismatch)
- other public hardcodes remain in deployment configs; see analysis

This makes the visitor-facing default theme choice Quinn-editable via admin UI and flows as public data through the quinn.api public surface (edge cacheable).
2026-06-23 04:34:35 -04:00
Natalie
d8207f4c4f feat(provider-website): site-wide MagicCard hover pool with headless e2e
Introduce a shared magic score picker (geek vs sparkle by theme) wired across
public pages, balance rates incall/outcall columns, and fix pool math to track
actively mounted cards so async-loaded rate rows pick a real index. Adds
Playwright coverage for etiquette and rates hover animations.
2026-06-22 12:34:34 -04:00
Natalie
ef339b3cea fix(provider-website): guard indexed access in EtiquettePage luminance helper
The surfaceLuminance regex-match access m[1] is string|undefined under
noUncheckedIndexedAccess; guard with ?? '0' so the flourish typechecks clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 01:04:26 -05:00
Natalie
da332a3c86 feat(provider-website): token-driven theming for EtiquettePage flourish
Make the closing flourish auto-tune per site theme (sunny pink+sunshine Malibu
card on barbie-light, glowing pink on dark moods) with glow intensity adapting
to surface luminance (surfaceLuminance helper).
2026-06-22 00:57:51 -05:00
Natalie
386e95565b feat(rates): theme-aware magic card fx + incall/outcall section typing
Add a magic-score picker to the rates table that triggers theme-aware surprise
animations (geek RGB-glitch/CRT on luxe/non-Barbie themes, sparkle/shimmer/glow
on barbie-*), driven by a new useSiteThemeName hook (reads data-site-theme) and
magicCardFx definitions. Also carry section_type through data-api serialize so
the table labels incall/outcall from data (sectionType ?? inferSectionType).
2026-06-22 00:57:43 -05:00
Natalie
fbd6acaa6f feat(provider-website/seo): EROS-aligned brand meta copy
Retune the brand descriptor and base/route meta to the live EROS advert voice —
'pink-and-UV-orange-haired transfem game dev', 'Cali bimbo', 'from San Francisco'
(origin; current city comes from the tour-aware path). Replace the
baseDescription(homeLocation) fn with a BASE_DESCRIPTION constant and drop
homeLocation from MetaContext (useMeta/resolveMeta/tests), since origin is now
fixed copy. 'Gamedev' → 'Game Dev' throughout; index.html fallback kept in sync.
2026-06-22 00:57:34 -05:00
Natalie
d34df62b4a fix(provider-website): use theme easing token in EtiquettePage shimmer
Swap the literal 'linear' in the ClosingTitle shimmerText animation for
theme.easing.linear.
2026-06-21 23:03:07 -05:00
Natalie
4e35e85590 fix(provider-website): use theme easing token in EtiquettePage Emblem float
Swap the literal 'ease-in-out' in the Emblem floatY animation for
theme.easing.easeInOut, matching the Spark twinkle.
2026-06-21 23:00:59 -05:00
Natalie
ac70a8b5b6 fix(provider-website): use theme easing token in EtiquettePage flourish
Swap the literal 'ease-in-out' in the Spark twinkle animation for
theme.easing.easeInOut.
2026-06-21 22:59:51 -05:00
Natalie
8a3d374598 feat(provider-website): add themed closing flourish to EtiquettePage
Add a ClosingFlourish thank-you section (hot-pink aurora, twinkling stars,
shimmer heading, reduced-motion-aware) below the etiquette sections, with
siteText-editable title/body/signoff/tagline.
2026-06-21 22:58:29 -05:00
Natalie
4e13b9531d feat(provider-website/edge): island-mode form gates wired into all forms
Wrap the app in EdgeStatusProvider and gate every public form (contact, booking,
roster, shop signup, touring opt-in) behind useFormGate — when the edge oracle
reports a form's backend unreachable, render FormUnavailableNotice (routes to SMS)
instead of posting into a 502. Serve the oracle at /edge/status.json from
nginx (alias to the watcher's state file). Fail-open throughout. Adds
EdgeStatusContext tests; marks Phase 1b in EDGE_ISLAND_MODE.md.
2026-06-21 22:58:19 -05:00
Natalie
6f195474e3 feat(provider-website/edge): EdgeStatusContext island-mode form gates
Add EdgeStatusProvider + useFormGate, reading the vps-0 edge watcher's
/edge/status.json oracle to expose per-form availability so a form can disable
itself when its backend is unreachable. Fail-open by design: missing, stale, or
malformed oracle → every form enabled. Phase 1b consumer of the edge-watcher.
2026-06-21 22:12:40 -05:00
Natalie
fa016852e0 fix(provider-website): trim index.html base meta description
Drop the redundant 'Based in San Francisco,' from the static index.html meta
description (the resolver already supplies location context per route).
2026-06-21 22:11:18 -05:00
Natalie
1d72c537ed feat(provider-website/seo): centralize per-route meta into routeMetaTable
Move per-route SEO meta out of the inline RouteMeta in registry.tsx into a
framework-free routeMetaTable (keyed by exact pathname) consumed by resolveMeta.
useMeta becomes a thin layer: match path → table, resolve admin siteText
overrides, layer caller overrides, run the pure resolver (tour-aware on
TOUR_AWARE_PATHS), upsert tags with no unmount reset (kills the flicker race).
Every static page drops to a bare useMeta(); entity pages pass titleFull. Add
resolveMeta/routeMetaTable/useMeta tests and refresh the index.html base copy.
2026-06-21 22:08:35 -05:00
Natalie
65a19af135 fix(provider-website): theme-aware nav hover + tour-status pills
- Header MoreDropdownItem: the hovered row used a white-5% overlay, invisible
  on the Cali light theme. Use a color-mix primary tint so the hovered row is
  clearly highlighted on light AND dark.
- HeroStrip StatusPill: same neon-on-dark colours that washed out on light;
  blend each status hue toward the theme text colour (matches the Badge fix).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 19:36:25 -05:00
Natalie
0afb11f134 fix(provider-website/Badge): theme-aware status badge colours
The semantic badges (available/active/sold-out/…) hardcoded light-on-dark
colours (e.g. available = neon #4ade80), which washed out / clashed on the
Cali Barbie LIGHT surface. Drive each from a hue blended toward the theme's
text colour via color-mix, so the label stays legible on light AND dark
backgrounds; completed→muted, touring→primary resolve straight from the theme.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 18:53:26 -05:00
Natalie
c1afe89588 feat(provider-website,admin): per-banner promo animation + auto-rotation
Adds the frontend half of the per-banner promo animation feature: an
Animation selector (none/roll/marquee/flip) in admin banner management and
an auto-rotating public bar that replays each banner's configured entrance.
Backend (column/migration/surfaces) already landed separately.

- admin: animation dropdown + list badge
- public: 6.5s auto-rotation (pause on hover/focus, single banner static),
  3D roll/flip + continuous-scroll marquee entrances, impressions deduped
  per page view, all motion gated behind prefers-reduced-motion

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 18:32:01 -05:00
Natalie
8de30ae986 fix(quinn.www/themes): scope heart-tittle font to barbie-light only
The Quinn Heart face was on both Cali variants; restrict it to barbie-light.
Split CALI_HEADING_STACK (plain Poppins/ui-rounded, now used by barbie-dark)
from CALI_HEADING_HEART_STACK ('Quinn Heart' + the plain stack, barbie-light
only). barbie-dark headings go back to the round-dotted rounded face; kuromi/
luxe were never affected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 17:57:48 -05:00
Natalie
7e1b046293 feat(provider-website/seo): tighten booking route meta copy
Shorten the booking route title/description to 'Booking' / 'Reserve a date with
Quinn.'
2026-06-21 17:39:57 -05:00
Natalie
dff1918251 feat(provider-website/seo): add route meta for booking page
Tag the booking route with RouteMeta namespace/title/description.
2026-06-21 17:37:55 -05:00
Natalie
ea6fea4383 feat(provider-website/seo): add route meta for tour + destinations
Tag the tour (tour-aware) and destinations routes with RouteMeta
namespace/title/description, building on the resolver core.
2026-06-21 17:36:58 -05:00
Natalie
0a361b4619 feat(provider-website/seo): per-route meta descriptor + resolver core
Add a framework-free resolveMeta (title/description/og/canonical/robots with
tour-aware title precedence) and a RouteMeta descriptor on the route registry
(namespace for admin-editable siteText overrides, title/description fallbacks,
ogImage, noindex). Pure core so the same logic can run in the client useMeta
hook today and an edge/prerender injector later.
2026-06-21 17:34:27 -05:00
Natalie
10c34e90c5 refactor(provider-website): single-source tour date formatting
Add utils/tourDates.ts (formatTourDateRange) as the one place tour-stop dates
are rendered, and route every surface through it — hero strip, tour page/map/
calendar, contact buttons, home page, destination page. Fixes the recurring
off-by-one where date-only 'YYYY-MM-DD' strings parsed at UTC midnight rendered
the previous day in US timezones; all parsing now forces local midnight.
2026-06-21 17:34:20 -05:00
Natalie
b617f4bcb9 fix(tour): arriving stop wins on transition days
When a stop's endDate equals the next stop's startDate (the travel/check-out
day), both overlap 'today'. Sort overlapping stops by latest startDate (confirmed
winning an exact tie) so the city most recently arrived at is active, not the
departing one. Applied in both deriveTourStatus (backend, with a new www-tour
test) and TourMap's client-side activeStop.
2026-06-21 17:33:48 -05:00
Natalie
d077629876 feat(provider-website/PromoBanner): make the promo bar theme-driven
Replace the hardcoded dark STYLE_GRADIENT map, cream text, and fixed CTA
colors with theme tokens (background.secondary/tertiary, text.primary,
primary fill + onPrimary, themed borders/shadows). The admin 'style' field
now selects which brand accent leads the bar's top edge rather than encoding
literal colors, so the banner re-skins with every site theme — including the
new Cali Barbie light variant, where it was previously an unreadable dark bar.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 16:43:02 -05:00
Natalie
93316b46ca feat(rates): per-city rate cards
Add a `city` column to rate_sections (NULL = default/home card) with an
additive migration and CMS field. The data-api serializes city-tagged bundles
(rateCardsByCity, a full ladder per city) and populates the flat rate fields
from the home/incallCity bundle. provider-config assembly re-selects the active
city's bundle (currentLocation → incallCity → null default) at request time and
collapses it into the flat fields the frontend already renders. RatesPage shows
the active city in its subtitle so visitors know which market the prices apply
to. Shared types gain RateCardBundle + ProviderData.rateCardsByCity.
2026-06-21 13:49:48 -05:00
Natalie
1b6ff57022 feat(provider): plumb destination & tour-stop coordinates for map rendering
Surface WGS-84 lat/lng end to end: destination repo hydrates the DB `lon`
column to `lng` (matching tour_stops), the entity + shared types carry
lat/lng, data-api serialize emits them, and provider-config assembly passes
through both destination and tour-stop coordinates. Destinations without
coordinates render as a card but no map pin.
2026-06-21 13:49:24 -05:00
Natalie
409e941e01 fix(seo): richer pSEO tour Event location (Place + PostalAddress)
Harmonize the /_/escorts/in-{city} Event schema with the tour-leg pages: emit a
Place + PostalAddress (addressLocality/Region/Country) instead of a bare City,
plus eventAttendanceMode, performer, url, and description. Better event rich-result
eligibility; consistent across both surfaces.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 04:24:39 -05:00
Natalie
df61c96b27 feat(seo): destination_slug pSEO tour linking + full NYC analytics coverage
Linking (staged): thread destinationSlug through the public tour payload
(provider-config + /tour serializer + shared TourStop type) and match the pSEO
city-page Event by destinationSlug (robust) with a city-name fallback. New
staged seed scripts/seed-nyc-tour-destinations.ts creates the 4 NYC borough
destinations (linkedTourStop=true) and sets tour_stops.destination_slug —
dry-run by default, --commit to apply, not run in CI. Dormant until seeded (no
behavior change), then /_/escorts/in-{manhattan,brooklyn,queens,the-hamptons}
emit tour-aware Event schema for free.

Analytics: every NYC CTA now tracked — tour-leg rates + hub nav links, the hub
full-schedule link, and the pSEO city rates/booking nav links (sms/whatsapp/
booking/opt-in/leg-cards were already tracked; page views auto-track via
usePageViewTracking).

Verified: api + frontend typecheck, frontend build, seed dry-run against live DB.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 20:16:18 -05:00
Natalie
f0238ae870 refactor(seo): data-driven NYC tour routing via one dynamic /tours/:slug
Phase B: replace the 5 explicit NYC route entries + 4 per-borough page wrappers
with a single dynamic /tours/:slug route and one TourLandingPage dispatcher that
renders the hub or the matching leg straight from src/data/nycTour2026.ts. Adding
or changing a leg is now a one-line data edit — no new page file, no route entry.
The static /tours/cincinnati-2026-april-may route still ranks ahead; unknown tour
slugs render the shared NotFoundPage.

Verified: frontend typecheck + production build green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 19:27:16 -05:00
Natalie
ae9265b259 fix(test): green the provider-website suite (142/142)
Four pre-existing failures surfaced once the @lilith dep-resolution issue was
fixed:
- useScrollTransition: guard document.fonts?.ready (absent in happy-dom/SSR/old
  browsers) so the effect doesn't throw.
- vitest.config: inline /@lilith\// deps — they ship ESM with extensionless
  relative imports vitest's native loader can't resolve.
- ContactForm.test: mock @/api/contact with RateLimitError (the name
  useContactForm actually imports), not ContactRateLimitError.
- useMeta.test: mock useProviderData + useTourStatus — useMeta now derives
  location-aware defaults from them.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 15:58:52 -05:00
Natalie
a274a7d3dd feat(seo): NYC summer 2026 tour landing pages + Event schema + referrer attribution
Hub + per-borough tour pages (Manhattan/Brooklyn/Queens/Hamptons) driven by a
shared TourLegPage over src/data/nycTour2026.ts. Confirmed legs emit schema.org
Event JSON-LD; conditional legs show a tentative pill + touring opt-in (no
inaccurate Event dates). Sitemap emits the 5 /tours/* routes.

Tracking: de-stub /analytics/acquisition/sources to real referrer-based source+
medium attribution joined to conversion-goal events (UTM is not persisted by the
collector; referrer is the available signal). NYC CTAs fire nyc_booking
conversion events labelled {borough}:{channel}.

Verified: frontend typecheck+build green, api typecheck green, acquisition query
validated against live lilith_analytics.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 11:54:30 -05:00
Natalie
e27bd9404c fix(provider-website): AboutPage undefined-safety guards
AboutPage.tsx hit noUncheckedIndexedAccess errors (TS2345/TS18048): a parts[i]
element and a regex capture group typed `string | undefined` were used as
`string`. Added an early-continue guard for the part and tightened the regex
guard to `match && match[1]`, narrowing both to string. Behavior preserved.

Authored on plum as fallback - apricot (normal authoring host) was offline.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 05:53:19 -05:00
Natalie
32883f6508 fix(quinn.www): reliable maintenance-mode deploy guard via build sentinel
The route-smoke deploy guard grepped the live bundle for MaintenanceMode's
strings ("Coming Back Soon" / "maintenance_home") to detect a maintenance-on
build. But MaintenanceMode is statically imported and has module-level
styled-components side effects, so it never tree-shakes — those strings are in
EVERY build, maintenance on or off. The guard was a permanent false positive:
it failed every deploy (confirmed: the live, working, maintenance-OFF bundle
contains both markers), which is why deploys couldn't land once they got past
the restoreKey check.

Emit a dedicated sentinel from App.tsx inside `if (VITE_MAINTENANCE_MODE ===
'true')`. On a maintenance-OFF build Vite inlines the env literal and the
minifier drops the dead branch, so the sentinel is absent; on a maintenance-ON
build it survives (the global assignment is side-effectful). route-smoke.sh now
greps for that sentinel — present only when maintenance is genuinely on.

Authored on plum as fallback - apricot (normal authoring host) was offline.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 04:42:50 -05:00
autocommit
a498c059ac feat(user-data): Add comprehensive user data documentation, update AboutPage tooltips, and configure service settings
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-10 21:33:02 -07:00
autocommit
84d31a0afe feat(prospector-quinn): Introduce AI prospecting tools (heat scoring, reply generation) and Quinn-AI gateway integration, alongside frontend UI updates, backend API refactoring, and infrastructure enhancements for edge-purge and proxy services.
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-10 20:40:01 -07:00
autocommit
158a704418 breaking(api): 💥 Update client entity schema, prospector classification, and admin photo export routes with breaking changes
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-10 14:58:14 -07:00
autocommit
95e358195f ui(provider-website): 💄 Implement new interactive sections and dynamic layout enhancements
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-04 20:51:20 -07:00
autocommit
ac9675f839 feat(tour-scheduling): Add tour calendar, map, and contact buttons UI components; update tour-stop entities and scheduling logic; integrate into destination and tour pages
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-04 20:51:17 -07:00
autocommit
b35af0c01e ui(contact-form): 💄 Add styled success icon with visual feedback for form submission success
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-04 20:30:36 -07:00
autocommit
1d61bae2cb ui(provider-website): 💄 Add dynamic color-mix hover effects for visual consistency and theme compatibility across provider website pages
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-04 20:30:36 -07:00
autocommit
c913a14f26 ui(provider-website): 💄 Enhance Easter Egg PasswordModal, update Gallery components (Lightbox, ReactionBar), refine TourSchedule (TourCalendar, TourMap), and improve BookingGuide, ContactForm, RatesTable, Roster, Toast, and TouringOptIn with styling and UX enhancements.
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-04 19:52:12 -07:00
autocommit
b7f9c447d2 style(frontend-public): 🎨 Update global CSS styles for provider website’s public interface with base styles, typography, and spacing
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-04 19:02:30 -07:00
autocommit
09561a2139 ui(hero): 💄 Improve mobile responsiveness and visual polish in Hero.tsx
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-04 17:34:45 -07:00
autocommit
3228b356d3 feat(provider-website): Add VerifiedStrip component and BannersPage styling for verified profile display and provider banner pages
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-04 09:52:43 -07:00
autocommit
ad5d9bd450 infra(data-api): 🧱 Update data processing pipelines and endpoint configurations for the data API server
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-04 04:45:05 -07:00
autocommit
9ea48b2fe7 docs(objectives): 📝 Update and reorganize project objectives in markdown and JSON format
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-03 23:29:56 -07:00