kaddidlehopper 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. package/CONTEXT.md +139 -0
  2. package/README.md +47 -0
  3. package/add-ons/ai/README.md +34 -0
  4. package/add-ons/ai/assets/_dot_env.local.append +13 -0
  5. package/add-ons/ai/assets/src/components/AIAssistant.tsx +149 -0
  6. package/add-ons/ai/assets/src/lib/ai-hook.ts +21 -0
  7. package/add-ons/ai/assets/src/lib/weather-tools.ts +30 -0
  8. package/add-ons/ai/assets/src/routes/api.chat.ts +94 -0
  9. package/add-ons/ai/assets/src/routes/chat.css +175 -0
  10. package/add-ons/ai/assets/src/routes/chat.tsx +141 -0
  11. package/add-ons/ai/info.json +27 -0
  12. package/add-ons/ai/package.json +17 -0
  13. package/add-ons/ai/small-logo.svg +8 -0
  14. package/dist/cli.js +251 -0
  15. package/dist/index.js +33 -0
  16. package/dist/types/cli.d.ts +8 -0
  17. package/dist/types/index.d.ts +2 -0
  18. package/dist/types/types.d.ts +14 -0
  19. package/dist/types.js +1 -0
  20. package/examples/blog/README.md +60 -0
  21. package/examples/blog/assets/content/posts/beach.md +12 -0
  22. package/examples/blog/assets/content/posts/jungle.md.ejs +12 -0
  23. package/examples/blog/assets/content/posts/mountains.md.ejs +12 -0
  24. package/examples/blog/assets/content/posts/snorkeling.md.ejs +12 -0
  25. package/examples/blog/assets/content/posts/waterfall.md.ejs +12 -0
  26. package/examples/blog/assets/content-collections.ts +30 -0
  27. package/examples/blog/assets/public/beach.jpg +0 -0
  28. package/examples/blog/assets/public/jungle.jpg +0 -0
  29. package/examples/blog/assets/public/mountains.jpg +0 -0
  30. package/examples/blog/assets/public/snorkeling.jpg +0 -0
  31. package/examples/blog/assets/public/waterfall.jpg +0 -0
  32. package/examples/blog/assets/src/components/Header.tsx +52 -0
  33. package/examples/blog/assets/src/components/VacayAssistant.tsx +205 -0
  34. package/examples/blog/assets/src/components/blog-posts.tsx +78 -0
  35. package/examples/blog/assets/src/components/ui/card.tsx +92 -0
  36. package/examples/blog/assets/src/lib/blog-ai-hook.ts +25 -0
  37. package/examples/blog/assets/src/lib/blog-tools.ts +111 -0
  38. package/examples/blog/assets/src/lib/utils.ts +6 -0
  39. package/examples/blog/assets/src/routes/__root.tsx +57 -0
  40. package/examples/blog/assets/src/routes/api.blog-chat.ts +117 -0
  41. package/examples/blog/assets/src/routes/category.$category.tsx +19 -0
  42. package/examples/blog/assets/src/routes/index.tsx +19 -0
  43. package/examples/blog/assets/src/routes/posts.$slug.tsx +63 -0
  44. package/examples/blog/assets/src/styles.css +138 -0
  45. package/examples/blog/info.json +43 -0
  46. package/examples/blog/package.json +23 -0
  47. package/examples/events/README.md +110 -0
  48. package/examples/events/assets/content/speakers/andre-costa.md +22 -0
  49. package/examples/events/assets/content/speakers/hans-mueller.md.ejs +22 -0
  50. package/examples/events/assets/content/speakers/isabella-martinez.md.ejs +22 -0
  51. package/examples/events/assets/content/speakers/kenji-nakamura.md.ejs +22 -0
  52. package/examples/events/assets/content/speakers/marie-dubois.md.ejs +20 -0
  53. package/examples/events/assets/content/speakers/priya-sharma.md.ejs +22 -0
  54. package/examples/events/assets/content/talks/croissant-lamination-secrets.md +39 -0
  55. package/examples/events/assets/content/talks/french-macaron-mastery.md.ejs +39 -0
  56. package/examples/events/assets/content/talks/neapolitan-pizza-tradition-meets-innovation.md.ejs +39 -0
  57. package/examples/events/assets/content/talks/savory-breads-of-the-mediterranean.md.ejs +39 -0
  58. package/examples/events/assets/content/talks/sourdough-from-starter-to-masterpiece.md.ejs +36 -0
  59. package/examples/events/assets/content/talks/the-art-of-the-perfect-tart.md.ejs +32 -0
  60. package/examples/events/assets/content/talks/the-science-of-sugar.md.ejs +39 -0
  61. package/examples/events/assets/content/talks/umami-in-pastry-east-meets-west.md.ejs +39 -0
  62. package/examples/events/assets/content-collections.ts +56 -0
  63. package/examples/events/assets/public/background-1.jpg +0 -0
  64. package/examples/events/assets/public/background-2.jpg +0 -0
  65. package/examples/events/assets/public/background-3.jpg +0 -0
  66. package/examples/events/assets/public/background-4.jpg +0 -0
  67. package/examples/events/assets/public/conference-logo.png +0 -0
  68. package/examples/events/assets/public/favicon.ico +0 -0
  69. package/examples/events/assets/public/speakers/andre-costa.jpg +0 -0
  70. package/examples/events/assets/public/speakers/hans-mueller.jpg +0 -0
  71. package/examples/events/assets/public/speakers/isabella-martinez.jpg +0 -0
  72. package/examples/events/assets/public/speakers/kenji-nakamura.jpg +0 -0
  73. package/examples/events/assets/public/speakers/marie-dubois.jpg +0 -0
  74. package/examples/events/assets/public/speakers/priya-sharma.jpg +0 -0
  75. package/examples/events/assets/public/talks/croissant-lamination-secrets.jpg +0 -0
  76. package/examples/events/assets/public/talks/french-macaron-mastery.jpg +0 -0
  77. package/examples/events/assets/public/talks/neapolitan-pizza-tradition-meets-innovation.jpg +0 -0
  78. package/examples/events/assets/public/talks/savory-breads-of-the-mediterranean.jpg +0 -0
  79. package/examples/events/assets/public/talks/sourdough-from-starter-to-masterpiece.jpg +0 -0
  80. package/examples/events/assets/public/talks/the-art-of-the-perfect-tart.jpg +0 -0
  81. package/examples/events/assets/public/talks/the-science-of-sugar.jpg +0 -0
  82. package/examples/events/assets/public/talks/umami-in-pastry-east-meets-west.jpg +0 -0
  83. package/examples/events/assets/public/tanstack-circle-logo.png +0 -0
  84. package/examples/events/assets/public/tanstack-word-logo-white.svg +1 -0
  85. package/examples/events/assets/src/components/Header.tsx +59 -0
  86. package/examples/events/assets/src/components/HeaderNav.tsx +67 -0
  87. package/examples/events/assets/src/components/HeroCarousel.tsx +61 -0
  88. package/examples/events/assets/src/components/RemyAssistant.tsx +207 -0
  89. package/examples/events/assets/src/components/SpeakerCard.tsx +67 -0
  90. package/examples/events/assets/src/components/TalkCard.tsx +77 -0
  91. package/examples/events/assets/src/components/ui/card.tsx +92 -0
  92. package/examples/events/assets/src/lib/conference-ai-hook.ts +26 -0
  93. package/examples/events/assets/src/lib/conference-tools.ts +210 -0
  94. package/examples/events/assets/src/lib/model-selection.ts +1 -0
  95. package/examples/events/assets/src/lib/utils.ts +6 -0
  96. package/examples/events/assets/src/routes/__root.tsx +70 -0
  97. package/examples/events/assets/src/routes/api.remy-chat.ts +119 -0
  98. package/examples/events/assets/src/routes/index.tsx +192 -0
  99. package/examples/events/assets/src/routes/schedule.index.tsx +274 -0
  100. package/examples/events/assets/src/routes/speakers.$slug.tsx +122 -0
  101. package/examples/events/assets/src/routes/speakers.index.tsx +40 -0
  102. package/examples/events/assets/src/routes/talks.$slug.tsx +116 -0
  103. package/examples/events/assets/src/routes/talks.index.tsx +40 -0
  104. package/examples/events/assets/src/styles.css +182 -0
  105. package/examples/events/info.json +74 -0
  106. package/examples/events/package.json +23 -0
  107. package/examples/marketing/README.md +60 -0
  108. package/examples/marketing/assets/public/logo.png +0 -0
  109. package/examples/marketing/assets/public/motorcycle-adventure.jpg +0 -0
  110. package/examples/marketing/assets/public/motorcycle-cruiser.jpg +0 -0
  111. package/examples/marketing/assets/public/motorcycle-scooter.jpg +0 -0
  112. package/examples/marketing/assets/public/motorcycle-sport.jpg +0 -0
  113. package/examples/marketing/assets/public/motorcycle-supersport.jpg +0 -0
  114. package/examples/marketing/assets/src/components/Header.tsx +36 -0
  115. package/examples/marketing/assets/src/components/MotorcycleAIAssistant.tsx +162 -0
  116. package/examples/marketing/assets/src/components/MotorcycleRecommendation.tsx +53 -0
  117. package/examples/marketing/assets/src/data/motorcycles.ts.ejs +77 -0
  118. package/examples/marketing/assets/src/lib/motorcycle-ai-hook.ts +24 -0
  119. package/examples/marketing/assets/src/lib/motorcycle-tools.ts +42 -0
  120. package/examples/marketing/assets/src/routes/__root.tsx +57 -0
  121. package/examples/marketing/assets/src/routes/api.motorcycle-chat.ts +78 -0
  122. package/examples/marketing/assets/src/routes/index.tsx +72 -0
  123. package/examples/marketing/assets/src/routes/motorcycles/$motorcycleId.tsx +56 -0
  124. package/examples/marketing/assets/src/store/motorcycle-assistant.ts +3 -0
  125. package/examples/marketing/assets/src/styles.css +212 -0
  126. package/examples/marketing/info.json +38 -0
  127. package/examples/marketing/package.json +14 -0
  128. package/examples/resume/README.md +82 -0
  129. package/examples/resume/assets/content/education/code-school.md +17 -0
  130. package/examples/resume/assets/content/jobs/freelance.md.ejs +13 -0
  131. package/examples/resume/assets/content/jobs/initech-junior.md +20 -0
  132. package/examples/resume/assets/content/jobs/initech-lead.md.ejs +29 -0
  133. package/examples/resume/assets/content/jobs/initrode-senior.md.ejs +28 -0
  134. package/examples/resume/assets/content-collections.ts +36 -0
  135. package/examples/resume/assets/public/headshot-on-white.jpg +0 -0
  136. package/examples/resume/assets/src/components/Header.tsx +33 -0
  137. package/examples/resume/assets/src/components/ResumeAssistant.tsx +193 -0
  138. package/examples/resume/assets/src/components/ui/badge.tsx +46 -0
  139. package/examples/resume/assets/src/components/ui/card.tsx +92 -0
  140. package/examples/resume/assets/src/components/ui/checkbox.tsx +30 -0
  141. package/examples/resume/assets/src/components/ui/hover-card.tsx +44 -0
  142. package/examples/resume/assets/src/components/ui/separator.tsx +26 -0
  143. package/examples/resume/assets/src/lib/resume-ai-hook.ts +21 -0
  144. package/examples/resume/assets/src/lib/resume-tools.ts +165 -0
  145. package/examples/resume/assets/src/lib/utils.ts +6 -0
  146. package/examples/resume/assets/src/routes/api.resume-chat.ts +110 -0
  147. package/examples/resume/assets/src/routes/index.tsx +220 -0
  148. package/examples/resume/assets/src/styles.css +138 -0
  149. package/examples/resume/info.json +25 -0
  150. package/examples/resume/package.json +26 -0
  151. package/package.json +39 -0
  152. package/project/base/_dot_claude/skills/content-collections/SKILL.md +505 -0
  153. package/project/base/_dot_claude/skills/netlify-blobs/SKILL.md +410 -0
  154. package/project/base/_dot_claude/skills/netlify-db/SKILL.md +424 -0
  155. package/project/base/_dot_claude/skills/netlify-debugging/SKILL.md +419 -0
  156. package/project/base/_dot_claude/skills/netlify-forms/SKILL.md +243 -0
  157. package/project/base/_dot_claude/skills/netlify-functions/SKILL.md +372 -0
  158. package/project/base/_dot_claude/skills/tanstack-start-api-routes/SKILL.md +421 -0
  159. package/project/base/_dot_claude/skills/tanstack-start-loaders/SKILL.md +426 -0
  160. package/project/base/_dot_claude/skills/tanstack-start-project-setup/SKILL.md +493 -0
  161. package/project/base/_dot_claude/skills/tanstack-start-routes/SKILL.md +430 -0
  162. package/project/base/_dot_claude/skills/tanstack-start-server-functions/SKILL.md +445 -0
  163. package/project/base/_dot_claude/skills/tanstack-start-typesafe-routing/SKILL.md +494 -0
  164. package/project/base/_dot_gitignore +8 -0
  165. package/project/base/netlify.toml +7 -0
  166. package/project/base/package.json +33 -0
  167. package/project/base/public/favicon.ico +0 -0
  168. package/project/base/public/tanstack-circle-logo.png +0 -0
  169. package/project/base/public/tanstack-word-logo-white.svg +1 -0
  170. package/project/base/src/components/Header.tsx +17 -0
  171. package/project/base/src/components/HeaderNav.tsx.ejs +179 -0
  172. package/project/base/src/router.tsx +15 -0
  173. package/project/base/src/routes/__root.tsx +57 -0
  174. package/project/base/src/routes/index.tsx +48 -0
  175. package/project/base/src/styles.css +15 -0
  176. package/project/base/tsconfig.json +28 -0
  177. package/project/base/vite.config.ts.ejs +25 -0
  178. package/project/packages.json +22 -0
  179. package/scripts/check-outdated-packages.js +421 -0
  180. package/src/cli.ts +343 -0
  181. package/src/index.ts +49 -0
  182. package/src/types.ts +15 -0
  183. package/tsconfig.json +17 -0
@@ -0,0 +1,192 @@
1
+ import { createFileRoute, Link } from '@tanstack/react-router'
2
+ import { ArrowRight, Calendar, MapPin, Users } from 'lucide-react'
3
+
4
+ import { allSpeakers, allTalks } from 'content-collections'
5
+
6
+ import SpeakerCard from '@/components/SpeakerCard'
7
+ import TalkCard from '@/components/TalkCard'
8
+ import RemyAssistant from '@/components/RemyAssistant'
9
+ import HeroCarousel from '@/components/HeroCarousel'
10
+
11
+ export const Route = createFileRoute('/')({
12
+ component: HomePage,
13
+ })
14
+
15
+ function HomePage() {
16
+ const featuredSpeakers = allSpeakers.slice(0, 3)
17
+ const featuredTalks = allTalks.slice(0, 4)
18
+
19
+ return (
20
+ <>
21
+ <RemyAssistant />
22
+
23
+ {/* Hero Section */}
24
+ <section className="relative min-h-[90vh] flex items-center justify-center px-6 overflow-hidden">
25
+ {/* Background carousel */}
26
+ <HeroCarousel />
27
+
28
+ <div className="relative max-w-5xl mx-auto text-center z-10">
29
+ {/* Event date badge */}
30
+ <div className="inline-flex items-center gap-2 px-4 py-2 mb-8 rounded-full bg-copper/10 border border-copper/30 text-copper-light text-sm font-medium">
31
+ <Calendar className="w-4 h-4" />
32
+ <span>March 15-17, 2026</span>
33
+ <span className="mx-2 text-copper/40">•</span>
34
+ <MapPin className="w-4 h-4" />
35
+ <span>Paris, France</span>
36
+ </div>
37
+
38
+ {/* Main title */}
39
+ <h1 className="font-display text-6xl md:text-8xl font-bold text-cream mb-6 leading-tight">
40
+ Haute
41
+ <span className="block text-gold italic">Pâtisserie</span>
42
+ </h1>
43
+
44
+ <p className="text-xl md:text-2xl text-cream/70 font-body max-w-3xl mx-auto mb-10 leading-relaxed">
45
+ Join the world's most celebrated pastry chefs and master bakers for three extraordinary days of masterclasses, demonstrations, and culinary inspiration.
46
+ </p>
47
+
48
+ {/* Stats */}
49
+ <div className="flex flex-wrap justify-center gap-8 mb-12">
50
+ <div className="text-center">
51
+ <div className="text-4xl font-display font-bold text-gold">{allSpeakers.length}</div>
52
+ <div className="text-cream/50 text-sm uppercase tracking-wider">Master Chefs</div>
53
+ </div>
54
+ <div className="text-center">
55
+ <div className="text-4xl font-display font-bold text-gold">{allTalks.length}</div>
56
+ <div className="text-cream/50 text-sm uppercase tracking-wider">Sessions</div>
57
+ </div>
58
+ <div className="text-center">
59
+ <div className="text-4xl font-display font-bold text-gold">3</div>
60
+ <div className="text-cream/50 text-sm uppercase tracking-wider">Days</div>
61
+ </div>
62
+ </div>
63
+
64
+ {/* CTA buttons */}
65
+ <div className="flex flex-wrap justify-center gap-4">
66
+ <Link
67
+ to="/speakers"
68
+ className="inline-flex items-center gap-2 px-8 py-4 rounded-full bg-gradient-to-r from-copper to-copper-dark text-charcoal font-semibold text-lg transition-all hover:shadow-lg hover:shadow-copper/30 hover:scale-[1.02]"
69
+ >
70
+ <Users className="w-5 h-5" />
71
+ Meet Our Speakers
72
+ </Link>
73
+ <Link
74
+ to="/talks"
75
+ className="inline-flex items-center gap-2 px-8 py-4 rounded-full border-2 border-gold/50 text-gold font-semibold text-lg transition-all hover:bg-gold/10 hover:border-gold"
76
+ >
77
+ View Sessions
78
+ <ArrowRight className="w-5 h-5" />
79
+ </Link>
80
+ </div>
81
+ </div>
82
+ </section>
83
+
84
+ {/* Featured Speakers Section */}
85
+ <section className="py-20 px-6">
86
+ <div className="max-w-7xl mx-auto">
87
+ <div className="flex items-end justify-between mb-12">
88
+ <div>
89
+ <h2 className="font-display text-4xl md:text-5xl font-bold text-cream mb-3">
90
+ Featured <span className="text-gold italic">Speakers</span>
91
+ </h2>
92
+ <p className="text-cream/60 text-lg font-body">
93
+ Learn from award-winning pastry chefs and master bakers
94
+ </p>
95
+ </div>
96
+ <Link
97
+ to="/speakers"
98
+ className="hidden md:inline-flex items-center gap-2 text-gold hover:text-gold/80 transition-colors font-medium"
99
+ >
100
+ View all speakers
101
+ <ArrowRight className="w-4 h-4" />
102
+ </Link>
103
+ </div>
104
+
105
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-8">
106
+ {featuredSpeakers.map((speaker) => (
107
+ <SpeakerCard key={speaker.slug} speaker={speaker} featured />
108
+ ))}
109
+ </div>
110
+
111
+ <div className="md:hidden mt-8 text-center">
112
+ <Link
113
+ to="/speakers"
114
+ className="inline-flex items-center gap-2 text-gold hover:text-gold/80 transition-colors font-medium"
115
+ >
116
+ View all speakers
117
+ <ArrowRight className="w-4 h-4" />
118
+ </Link>
119
+ </div>
120
+ </div>
121
+ </section>
122
+
123
+ {/* Divider */}
124
+ <div className="max-w-7xl mx-auto px-6">
125
+ <div className="h-px bg-gradient-to-r from-transparent via-border to-transparent" />
126
+ </div>
127
+
128
+ {/* Featured Sessions Section */}
129
+ <section className="py-20 px-6">
130
+ <div className="max-w-7xl mx-auto">
131
+ <div className="flex items-end justify-between mb-12">
132
+ <div>
133
+ <h2 className="font-display text-4xl md:text-5xl font-bold text-cream mb-3">
134
+ Featured <span className="text-gold italic">Sessions</span>
135
+ </h2>
136
+ <p className="text-cream/60 text-lg font-body">
137
+ Masterclasses and demonstrations to elevate your craft
138
+ </p>
139
+ </div>
140
+ <Link
141
+ to="/talks"
142
+ className="hidden md:inline-flex items-center gap-2 text-gold hover:text-gold/80 transition-colors font-medium"
143
+ >
144
+ View all sessions
145
+ <ArrowRight className="w-4 h-4" />
146
+ </Link>
147
+ </div>
148
+
149
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-8">
150
+ {featuredTalks.map((talk) => (
151
+ <TalkCard key={talk.slug} talk={talk} featured />
152
+ ))}
153
+ </div>
154
+
155
+ <div className="md:hidden mt-8 text-center">
156
+ <Link
157
+ to="/talks"
158
+ className="inline-flex items-center gap-2 text-gold hover:text-gold/80 transition-colors font-medium"
159
+ >
160
+ View all sessions
161
+ <ArrowRight className="w-4 h-4" />
162
+ </Link>
163
+ </div>
164
+ </div>
165
+ </section>
166
+
167
+ {/* CTA Section */}
168
+ <section className="py-20 px-6">
169
+ <div className="max-w-4xl mx-auto text-center">
170
+ <div className="relative p-12 rounded-3xl bg-gradient-to-br from-card to-charcoal border border-border/50 overflow-hidden">
171
+ {/* Decorative elements */}
172
+ <div className="absolute top-0 right-0 w-64 h-64 bg-copper/5 rounded-full blur-3xl" />
173
+ <div className="absolute bottom-0 left-0 w-48 h-48 bg-gold/5 rounded-full blur-3xl" />
174
+
175
+ <div className="relative">
176
+ <h2 className="font-display text-3xl md:text-4xl font-bold text-cream mb-4">
177
+ Ready to Elevate Your Craft?
178
+ </h2>
179
+ <p className="text-cream/60 text-lg font-body mb-8 max-w-2xl mx-auto">
180
+ Join us in Paris for an unforgettable experience with the world's finest pastry artisans.
181
+ </p>
182
+ <div className="inline-flex items-center gap-2 px-4 py-2 rounded-full bg-gold/10 border border-gold/30 text-gold text-sm font-medium">
183
+ <span>🥐</span>
184
+ <span>Registration opens January 2026</span>
185
+ </div>
186
+ </div>
187
+ </div>
188
+ </div>
189
+ </section>
190
+ </>
191
+ )
192
+ }
@@ -0,0 +1,274 @@
1
+ import { createFileRoute, Link } from '@tanstack/react-router'
2
+ import { Clock, Calendar, MapPin, ChevronRight } from 'lucide-react'
3
+ import { useState } from 'react'
4
+
5
+ import { allTalks, allSpeakers } from 'content-collections'
6
+
7
+ import RemyAssistant from '@/components/RemyAssistant'
8
+
9
+ export const Route = createFileRoute('/schedule/')({
10
+ component: SchedulePage,
11
+ })
12
+
13
+ // Helper to get speaker data by name
14
+ function getSpeakerByName(name: string) {
15
+ return allSpeakers.find(
16
+ (s) => s.name.toLowerCase() === name.toLowerCase()
17
+ )
18
+ }
19
+
20
+ // Define the conference schedule with time slots
21
+ const scheduleData = [
22
+ {
23
+ day: 1,
24
+ date: 'March 15, 2026',
25
+ dayName: 'Day One',
26
+ theme: 'French Foundations',
27
+ sessions: [
28
+ { time: '9:00 AM', talkSlug: 'french-macaron-mastery' },
29
+ { time: '11:30 AM', talkSlug: 'croissant-lamination-secrets' },
30
+ { time: '3:00 PM', talkSlug: 'the-science-of-sugar' },
31
+ ],
32
+ },
33
+ {
34
+ day: 2,
35
+ date: 'March 16, 2026',
36
+ dayName: 'Day Two',
37
+ theme: 'Global Traditions',
38
+ sessions: [
39
+ { time: '9:00 AM', talkSlug: 'sourdough-from-starter-to-masterpiece' },
40
+ { time: '11:30 AM', talkSlug: 'umami-in-pastry-east-meets-west' },
41
+ { time: '2:30 PM', talkSlug: 'savory-breads-of-the-mediterranean' },
42
+ ],
43
+ },
44
+ {
45
+ day: 3,
46
+ date: 'March 17, 2026',
47
+ dayName: 'Day Three',
48
+ theme: 'Artisan Mastery',
49
+ sessions: [
50
+ { time: '9:00 AM', talkSlug: 'the-art-of-the-perfect-tart' },
51
+ { time: '11:00 AM', talkSlug: 'neapolitan-pizza-tradition-meets-innovation' },
52
+ ],
53
+ },
54
+ ]
55
+
56
+ function SchedulePage() {
57
+ const [selectedDay, setSelectedDay] = useState(1)
58
+
59
+ const currentDayData = scheduleData.find((d) => d.day === selectedDay)!
60
+
61
+ return (
62
+ <>
63
+ <RemyAssistant />
64
+ <div className="min-h-screen">
65
+ {/* Hero section */}
66
+ <div className="relative py-16 px-6">
67
+ <div className="max-w-7xl mx-auto text-center">
68
+ <div className="inline-flex items-center gap-2 px-4 py-2 mb-6 rounded-full bg-copper/10 border border-copper/30 text-copper-light text-sm font-medium">
69
+ <Calendar className="w-4 h-4" />
70
+ <span>March 15-17, 2026</span>
71
+ <span className="mx-2 text-copper/40">•</span>
72
+ <MapPin className="w-4 h-4" />
73
+ <span>Paris, France</span>
74
+ </div>
75
+ <h1 className="font-display text-5xl md:text-6xl font-bold text-cream mb-4">
76
+ Conference <span className="text-gold italic">Schedule</span>
77
+ </h1>
78
+ <p className="text-xl text-cream/70 max-w-2xl mx-auto font-body">
79
+ Three days of masterclasses, demonstrations, and culinary inspiration from the world's finest pastry artisans.
80
+ </p>
81
+ </div>
82
+ </div>
83
+
84
+ {/* Day selector tabs */}
85
+ <div className="max-w-7xl mx-auto px-6 mb-12">
86
+ <div className="flex justify-center">
87
+ <div className="inline-flex bg-card/50 rounded-2xl p-2 border border-border/50">
88
+ {scheduleData.map((day) => (
89
+ <button
90
+ key={day.day}
91
+ onClick={() => setSelectedDay(day.day)}
92
+ className={`relative px-8 py-4 rounded-xl font-display font-semibold transition-all duration-300 ${
93
+ selectedDay === day.day
94
+ ? 'bg-gradient-to-br from-copper to-copper-dark text-charcoal shadow-lg shadow-copper/20'
95
+ : 'text-cream/70 hover:text-cream hover:bg-card'
96
+ }`}
97
+ >
98
+ <span className="block text-xs uppercase tracking-wider opacity-75">
99
+ {day.dayName}
100
+ </span>
101
+ <span className="block text-lg">{day.date.split(',')[0].split(' ').slice(0, 2).join(' ')}</span>
102
+ </button>
103
+ ))}
104
+ </div>
105
+ </div>
106
+ </div>
107
+
108
+ {/* Day theme header */}
109
+ <div className="max-w-7xl mx-auto px-6 mb-8">
110
+ <div className="text-center">
111
+ <h2 className="font-display text-3xl font-bold text-cream mb-2">
112
+ {currentDayData.dayName}: <span className="text-gold italic">{currentDayData.theme}</span>
113
+ </h2>
114
+ <p className="text-cream/50 font-body">{currentDayData.date}</p>
115
+ </div>
116
+ </div>
117
+
118
+ {/* Schedule timeline */}
119
+ <div className="max-w-5xl mx-auto px-6 pb-20">
120
+ <div className="relative">
121
+ {/* Timeline line */}
122
+ <div className="absolute left-8 md:left-12 top-0 bottom-0 w-px bg-gradient-to-b from-copper via-gold to-copper/30" />
123
+
124
+ {/* Sessions */}
125
+ <div className="space-y-8">
126
+ {currentDayData.sessions.map((session, index) => {
127
+ const talk = allTalks.find((t) => t.slug === session.talkSlug)
128
+ if (!talk) return null
129
+
130
+ const speaker = getSpeakerByName(talk.speaker)
131
+
132
+ return (
133
+ <Link
134
+ key={session.talkSlug}
135
+ to={`/talks/${talk.slug}`}
136
+ className="group block"
137
+ >
138
+ <div className="relative flex gap-6 md:gap-10">
139
+ {/* Time marker */}
140
+ <div className="flex-shrink-0 w-16 md:w-24 pt-6">
141
+ <div className="relative">
142
+ {/* Timeline dot */}
143
+ <div className="absolute -right-[13px] md:-right-[17px] top-0 w-6 h-6 rounded-full bg-charcoal border-2 border-gold flex items-center justify-center group-hover:border-copper group-hover:scale-110 transition-all">
144
+ <div className="w-2 h-2 rounded-full bg-gold group-hover:bg-copper transition-colors" />
145
+ </div>
146
+ <span className="block text-right text-sm md:text-base font-display font-semibold text-copper-light">
147
+ {session.time}
148
+ </span>
149
+ </div>
150
+ </div>
151
+
152
+ {/* Session card */}
153
+ <div
154
+ className="flex-1 relative overflow-hidden rounded-2xl bg-card border border-border/50
155
+ group-hover:border-gold/50 transition-all duration-300 group-hover:shadow-xl group-hover:shadow-gold/5
156
+ group-hover:-translate-y-1"
157
+ style={{
158
+ animationDelay: `${index * 100}ms`,
159
+ }}
160
+ >
161
+ {/* Background image with overlay */}
162
+ <div className="absolute inset-0">
163
+ <img
164
+ src={`/${talk.image}`}
165
+ alt={talk.title}
166
+ className="w-full h-full object-cover opacity-30 group-hover:opacity-40 group-hover:scale-105 transition-all duration-500"
167
+ />
168
+ <div className="absolute inset-0 bg-gradient-to-r from-charcoal via-charcoal/95 to-charcoal/80" />
169
+ </div>
170
+
171
+ {/* Content */}
172
+ <div className="relative p-6 md:p-8 flex flex-col md:flex-row gap-6 items-start">
173
+ {/* Speaker image */}
174
+ {speaker && (
175
+ <div className="flex-shrink-0">
176
+ <div className="relative w-20 h-20 md:w-24 md:h-24 rounded-xl overflow-hidden border-2 border-gold/30 group-hover:border-gold/60 transition-colors shadow-lg">
177
+ <img
178
+ src={`/${speaker.headshot}`}
179
+ alt={speaker.name}
180
+ className="w-full h-full object-cover"
181
+ />
182
+ <div className="absolute inset-0 bg-gradient-to-t from-charcoal/40 to-transparent" />
183
+ </div>
184
+ </div>
185
+ )}
186
+
187
+ {/* Talk info */}
188
+ <div className="flex-1 min-w-0">
189
+ {/* Topics */}
190
+ <div className="flex flex-wrap gap-2 mb-3">
191
+ {talk.topics.slice(0, 3).map((topic) => (
192
+ <span
193
+ key={topic}
194
+ className="px-2.5 py-0.5 text-xs font-medium tracking-wide uppercase bg-gold/10 text-gold border border-gold/20 rounded-full"
195
+ >
196
+ {topic}
197
+ </span>
198
+ ))}
199
+ </div>
200
+
201
+ {/* Title */}
202
+ <h3 className="font-display text-xl md:text-2xl font-semibold text-cream group-hover:text-gold transition-colors mb-2 leading-tight">
203
+ {talk.title}
204
+ </h3>
205
+
206
+ {/* Speaker & Duration */}
207
+ <div className="flex flex-wrap items-center gap-4 text-cream/60 text-sm mb-3">
208
+ <span className="font-medium text-copper-light">
209
+ {talk.speaker}
210
+ </span>
211
+ <div className="flex items-center gap-1.5">
212
+ <Clock className="w-3.5 h-3.5" />
213
+ <span>{talk.duration}</span>
214
+ </div>
215
+ </div>
216
+
217
+ {/* Speaker title if available */}
218
+ {speaker && (
219
+ <p className="text-cream/50 text-sm font-body">
220
+ {speaker.title} at {speaker.restaurant}
221
+ </p>
222
+ )}
223
+ </div>
224
+
225
+ {/* Arrow indicator */}
226
+ <div className="flex-shrink-0 self-center">
227
+ <div className="w-10 h-10 rounded-full bg-gold/10 border border-gold/20 flex items-center justify-center group-hover:bg-gold/20 group-hover:border-gold/40 transition-all">
228
+ <ChevronRight className="w-5 h-5 text-gold group-hover:translate-x-0.5 transition-transform" />
229
+ </div>
230
+ </div>
231
+ </div>
232
+ </div>
233
+ </div>
234
+ </Link>
235
+ )
236
+ })}
237
+ </div>
238
+ </div>
239
+ </div>
240
+
241
+ {/* Bottom CTA */}
242
+ <div className="max-w-4xl mx-auto px-6 pb-20">
243
+ <div className="relative p-8 md:p-12 rounded-3xl bg-gradient-to-br from-card to-charcoal border border-border/50 overflow-hidden text-center">
244
+ <div className="absolute top-0 right-0 w-64 h-64 bg-copper/5 rounded-full blur-3xl" />
245
+ <div className="absolute bottom-0 left-0 w-48 h-48 bg-gold/5 rounded-full blur-3xl" />
246
+
247
+ <div className="relative">
248
+ <h3 className="font-display text-2xl md:text-3xl font-bold text-cream mb-3">
249
+ Don't Miss a Single Session
250
+ </h3>
251
+ <p className="text-cream/60 font-body mb-6 max-w-xl mx-auto">
252
+ Each masterclass offers hands-on learning from the world's finest pastry artisans.
253
+ </p>
254
+ <div className="flex flex-wrap justify-center gap-4">
255
+ <Link
256
+ to="/talks"
257
+ className="inline-flex items-center gap-2 px-6 py-3 rounded-full bg-gradient-to-r from-copper to-copper-dark text-charcoal font-semibold transition-all hover:shadow-lg hover:shadow-copper/30 hover:scale-[1.02]"
258
+ >
259
+ Browse All Sessions
260
+ </Link>
261
+ <Link
262
+ to="/speakers"
263
+ className="inline-flex items-center gap-2 px-6 py-3 rounded-full border-2 border-gold/50 text-gold font-semibold transition-all hover:bg-gold/10 hover:border-gold"
264
+ >
265
+ Meet the Speakers
266
+ </Link>
267
+ </div>
268
+ </div>
269
+ </div>
270
+ </div>
271
+ </div>
272
+ </>
273
+ )
274
+ }
@@ -0,0 +1,122 @@
1
+ import { createFileRoute } from '@tanstack/react-router'
2
+ import { marked } from 'marked'
3
+ import { MapPin, Award, ArrowLeft } from 'lucide-react'
4
+ import { Link } from '@tanstack/react-router'
5
+
6
+ import { allSpeakers, allTalks } from 'content-collections'
7
+
8
+ import RemyAssistant from '@/components/RemyAssistant'
9
+ import TalkCard from '@/components/TalkCard'
10
+
11
+ export const Route = createFileRoute('/speakers/$slug')({
12
+ loader: async ({ params }) => {
13
+ const speaker = allSpeakers.find((s) => s.slug === params.slug)
14
+ if (!speaker) {
15
+ throw new Error('Speaker not found')
16
+ }
17
+ const speakerTalks = allTalks.filter((t) => t.speaker === speaker.name)
18
+ return { speaker, speakerTalks }
19
+ },
20
+ component: SpeakerDetailPage,
21
+ })
22
+
23
+ function SpeakerDetailPage() {
24
+ const { speaker, speakerTalks } = Route.useLoaderData()
25
+
26
+ return (
27
+ <div className="min-h-screen">
28
+ <RemyAssistant />
29
+
30
+ {/* Back navigation */}
31
+ <div className="max-w-7xl mx-auto px-6 py-4">
32
+ <Link
33
+ to="/speakers"
34
+ className="inline-flex items-center gap-2 text-cream/60 hover:text-gold transition-colors"
35
+ >
36
+ <ArrowLeft className="w-4 h-4" />
37
+ <span>All Speakers</span>
38
+ </Link>
39
+ </div>
40
+
41
+ {/* Hero section */}
42
+ <div className="relative py-12 px-6">
43
+ <div className="max-w-7xl mx-auto">
44
+ <div className="grid grid-cols-1 lg:grid-cols-3 gap-12">
45
+ {/* Photo */}
46
+ <div className="lg:col-span-1">
47
+ <div className="aspect-square rounded-2xl overflow-hidden border border-border/50">
48
+ <img
49
+ src={`/${speaker.headshot}`}
50
+ alt={speaker.name}
51
+ className="w-full h-full object-cover"
52
+ />
53
+ </div>
54
+ </div>
55
+
56
+ {/* Info */}
57
+ <div className="lg:col-span-2 flex flex-col justify-center">
58
+ {/* Specialty tag */}
59
+ <span className="inline-block w-fit px-4 py-1.5 text-sm font-medium tracking-wider uppercase bg-copper/20 text-copper-light rounded-full border border-copper/30 mb-4">
60
+ {speaker.specialty}
61
+ </span>
62
+
63
+ <h1 className="font-display text-5xl md:text-6xl font-bold text-cream mb-3">
64
+ {speaker.name}
65
+ </h1>
66
+
67
+ <p className="text-2xl text-gold font-display italic mb-4">
68
+ {speaker.title}
69
+ </p>
70
+
71
+ <div className="flex items-center gap-2 text-cream/60 text-lg mb-8">
72
+ <MapPin className="w-5 h-5 text-copper" />
73
+ <span>{speaker.restaurant}, {speaker.location}</span>
74
+ </div>
75
+
76
+ {/* Awards */}
77
+ {speaker.awards && speaker.awards.length > 0 && (
78
+ <div className="space-y-3">
79
+ <h3 className="text-sm font-medium tracking-wider uppercase text-cream/50">Awards & Recognition</h3>
80
+ <div className="flex flex-wrap gap-2">
81
+ {speaker.awards.map((award) => (
82
+ <span
83
+ key={award}
84
+ className="inline-flex items-center gap-2 px-3 py-1.5 text-sm bg-gold/10 text-gold/90 rounded-lg border border-gold/20"
85
+ >
86
+ <Award className="w-3.5 h-3.5" />
87
+ {award}
88
+ </span>
89
+ ))}
90
+ </div>
91
+ </div>
92
+ )}
93
+ </div>
94
+ </div>
95
+ </div>
96
+ </div>
97
+
98
+ {/* Bio section */}
99
+ <div className="max-w-4xl mx-auto px-6 py-12">
100
+ <div className="prose prose-lg max-w-none prose-invert prose-p:text-cream/80 prose-headings:text-cream prose-headings:font-display prose-strong:text-cream prose-a:text-gold font-body text-lg leading-relaxed">
101
+ <div
102
+ dangerouslySetInnerHTML={{ __html: marked(speaker.content) }}
103
+ />
104
+ </div>
105
+ </div>
106
+
107
+ {/* Speaker's talks */}
108
+ {speakerTalks.length > 0 && (
109
+ <div className="max-w-7xl mx-auto px-6 py-12">
110
+ <h2 className="font-display text-3xl font-bold text-cream mb-8">
111
+ Sessions by {speaker.name}
112
+ </h2>
113
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-8">
114
+ {speakerTalks.map((talk) => (
115
+ <TalkCard key={talk.slug} talk={talk} />
116
+ ))}
117
+ </div>
118
+ </div>
119
+ )}
120
+ </div>
121
+ )
122
+ }
@@ -0,0 +1,40 @@
1
+ import { createFileRoute } from '@tanstack/react-router'
2
+
3
+ import { allSpeakers } from 'content-collections'
4
+
5
+ import SpeakerCard from '@/components/SpeakerCard'
6
+ import RemyAssistant from '@/components/RemyAssistant'
7
+
8
+ export const Route = createFileRoute('/speakers/')({
9
+ component: SpeakersPage,
10
+ })
11
+
12
+ function SpeakersPage() {
13
+ return (
14
+ <>
15
+ <RemyAssistant />
16
+ <div className="min-h-screen">
17
+ {/* Hero section */}
18
+ <div className="relative py-16 px-6">
19
+ <div className="max-w-7xl mx-auto text-center">
20
+ <h1 className="font-display text-5xl md:text-6xl font-bold text-cream mb-4">
21
+ Our <span className="text-gold italic">Distinguished</span> Speakers
22
+ </h1>
23
+ <p className="text-xl text-cream/70 max-w-2xl mx-auto font-body">
24
+ Meet the world-renowned pastry chefs and master bakers who will share their expertise at Haute Pâtisserie 2026.
25
+ </p>
26
+ </div>
27
+ </div>
28
+
29
+ {/* Speakers grid */}
30
+ <div className="max-w-7xl mx-auto px-6 pb-20">
31
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
32
+ {allSpeakers.map((speaker) => (
33
+ <SpeakerCard key={speaker.slug} speaker={speaker} />
34
+ ))}
35
+ </div>
36
+ </div>
37
+ </div>
38
+ </>
39
+ )
40
+ }