Zoe09 Claude Sonnet 4.6 commited on
Commit
a54c42a
Β·
1 Parent(s): b00cb71

Extract inline CSS to static/style.css + add mobile responsive rules

Browse files

- Move 350-line inline css_code string to static/style.css
- app.py reads the file at startup (sprint task #11)
- Add @media breakpoints for 768px and 480px (sprint task #16)
- Add ::before/::after pulse-ring pseudo-elements for recording UX

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Files changed (2) hide show
  1. app.py +2 -150
  2. static/style.css +179 -0
app.py CHANGED
@@ -178,156 +178,8 @@ narrative_subtitles = [
178
  "And that was when she realized, she was not alone in the valley."
179
  ]
180
 
181
- css_code = """
182
- @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Playfair+Display:ital,wght@0,400;0,600;0,700;1,400&display=swap');
183
- /* Main layouts and colors matching VoiceBook cream and amber theme */
184
- body, .gradio-container {
185
- background-color: #FAF7F2 !important;
186
- font-family: 'Inter', system-ui, sans-serif !important;
187
- }
188
- .gradio-container {
189
- max-width: 1200px !important;
190
- margin: 0 auto !important;
191
- }
192
- /* Serif headers */
193
- .serif-header {
194
- font-family: 'Playfair Display', Georgia, serif !important;
195
- color: #1c1c19 !important;
196
- font-weight: 700 !important;
197
- }
198
- .cream-bg {
199
- background-color: #FAF7F2 !important;
200
- }
201
- .card-container {
202
- background-color: #f6f3ee !important;
203
- border: 1px solid #ebdccb !important;
204
- border-radius: 16px !important;
205
- padding: 24px !important;
206
- box-shadow: 0 2px 8px rgba(148, 74, 0, 0.03) !important;
207
- }
208
- .accent-border {
209
- border-color: #ddc1b0 !important;
210
- }
211
- .highlight-orange {
212
- color: #f5841f !important;
213
- }
214
- .highlight-amber {
215
- color: #944a00 !important;
216
- }
217
- .dark-card-container {
218
- background-color: #122116 !important;
219
- color: #FAF7F2 !important;
220
- border: 1px solid #1e3422 !important;
221
- border-radius: 16px !important;
222
- padding: 24px !important;
223
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15) !important;
224
- }
225
- /* Customized HTML grids matching identical React typography */
226
- .hero-spotlight {
227
- background: linear-gradient(135deg, #1e3422 0%, #121f15 100%) !important;
228
- color: #FFFFFF !important;
229
- border-radius: 24px !important;
230
- padding: 32px !important;
231
- position: relative;
232
- overflow: hidden;
233
- }
234
- .badge-featured {
235
- background: rgba(245, 132, 31, 0.2);
236
- color: #f5841f;
237
- border: 1px solid rgba(245, 132, 31, 0.3);
238
- border-radius: 9999px;
239
- padding: 4px 10px;
240
- font-size: 11px;
241
- font-weight: 700;
242
- text-transform: uppercase;
243
- display: inline-block;
244
- }
245
- .book-shelf-grid {
246
- display: grid;
247
- grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
248
- gap: 20px;
249
- }
250
- .shelf-card {
251
- background: #f6f3ee;
252
- border: 1px solid #eadacc;
253
- border-radius: 16px;
254
- padding: 16px;
255
- display: flex;
256
- gap: 16px;
257
- transition: all 0.3s ease;
258
- cursor: pointer;
259
- }
260
- .shelf-card:hover {
261
- border-color: #f5841f;
262
- transform: translateY(-2px);
263
- box-shadow: 0 4px 12px rgba(148, 74, 0, 0.06);
264
- }
265
- .cover-image {
266
- width: 80px;
267
- height: 110px;
268
- object-fit: cover;
269
- border-radius: 8px;
270
- box-shadow: 0 2px 6px rgba(0,0,0,0.1);
271
- }
272
- .progress-bar-bg {
273
- width: 100%;
274
- height: 6px;
275
- background: #ebdccb;
276
- border-radius: 999px;
277
- overflow: hidden;
278
- }
279
- .progress-bar-fill {
280
- height: 100%;
281
- background: #f5841f;
282
- }
283
- /* Pulsing rings for microphone recording */
284
- @keyframes pulse-ring {
285
- 0% { transform: scale(0.85); opacity: 0.6; }
286
- 50% { transform: scale(1.15); opacity: 0.25; }
287
- 100% { transform: scale(1.4); opacity: 0; }
288
- }
289
- .pulse-circle {
290
- position: relative;
291
- width: 120px;
292
- height: 120px;
293
- border-radius: 50%;
294
- background: #944a00;
295
- color: white;
296
- display: flex;
297
- flex-direction: column;
298
- align-items: center;
299
- justify-content: center;
300
- margin: 0 auto;
301
- }
302
- .recording-active {
303
- background: #ef4444 !important;
304
- }
305
- /* Subtitle dialog ticker */
306
- .dialog-synced {
307
- background: rgba(0,0,0,0.15);
308
- border: 1px solid rgba(255,166,0,0.1);
309
- color: #ffd3a6;
310
- font-family: 'Playfair Display', Georgia, serif;
311
- font-size: 1.25rem;
312
- font-style: italic;
313
- padding: 24px;
314
- border-radius: 16px;
315
- text-align: center;
316
- min-height: 100px;
317
- display: flex;
318
- align-items: center;
319
- justify-content: center;
320
- }
321
- /* Tab button custom styles */
322
- .tabs .tab-nav button {
323
- border-bottom: 2px solid transparent !important;
324
- }
325
- .tabs .tab-nav button.selected {
326
- color: #944a00 !important;
327
- border-bottom-color: #944a00 !important;
328
- font-weight: 700 !important;
329
- }
330
- """
331
 
332
  def generate_dashboard_html(books_list):
333
  featured = books_list[0]
 
178
  "And that was when she realized, she was not alone in the valley."
179
  ]
180
 
181
+ with open(os.path.join(os.path.dirname(__file__), "static", "style.css"), encoding="utf-8") as _f:
182
+ css_code = _f.read()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
183
 
184
  def generate_dashboard_html(books_list):
185
  featured = books_list[0]
static/style.css ADDED
@@ -0,0 +1,179 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Playfair+Display:ital,wght@0,400;0,600;0,700;1,400&display=swap');
2
+
3
+ /* ── Base ── */
4
+ body, .gradio-container {
5
+ background-color: #FAF7F2 !important;
6
+ font-family: 'Inter', system-ui, sans-serif !important;
7
+ }
8
+ .gradio-container {
9
+ max-width: 1200px !important;
10
+ margin: 0 auto !important;
11
+ }
12
+
13
+ /* ── Typography ── */
14
+ .serif-header {
15
+ font-family: 'Playfair Display', Georgia, serif !important;
16
+ color: #1c1c19 !important;
17
+ font-weight: 700 !important;
18
+ }
19
+ .cream-bg { background-color: #FAF7F2 !important; }
20
+ .highlight-orange { color: #f5841f !important; }
21
+ .highlight-amber { color: #944a00 !important; }
22
+
23
+ /* ── Cards ── */
24
+ .card-container {
25
+ background-color: #f6f3ee !important;
26
+ border: 1px solid #ebdccb !important;
27
+ border-radius: 16px !important;
28
+ padding: 24px !important;
29
+ box-shadow: 0 2px 8px rgba(148, 74, 0, 0.03) !important;
30
+ }
31
+ .accent-border { border-color: #ddc1b0 !important; }
32
+
33
+ .dark-card-container {
34
+ background-color: #122116 !important;
35
+ color: #FAF7F2 !important;
36
+ border: 1px solid #1e3422 !important;
37
+ border-radius: 16px !important;
38
+ padding: 24px !important;
39
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15) !important;
40
+ }
41
+
42
+ /* ── Hero ── */
43
+ .hero-spotlight {
44
+ background: linear-gradient(135deg, #1e3422 0%, #121f15 100%) !important;
45
+ color: #FFFFFF !important;
46
+ border-radius: 24px !important;
47
+ padding: 32px !important;
48
+ position: relative;
49
+ overflow: hidden;
50
+ }
51
+ .badge-featured {
52
+ background: rgba(245, 132, 31, 0.2);
53
+ color: #f5841f;
54
+ border: 1px solid rgba(245, 132, 31, 0.3);
55
+ border-radius: 9999px;
56
+ padding: 4px 10px;
57
+ font-size: 11px;
58
+ font-weight: 700;
59
+ text-transform: uppercase;
60
+ display: inline-block;
61
+ }
62
+
63
+ /* ── Book grid & cards ── */
64
+ .book-shelf-grid {
65
+ display: grid;
66
+ grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
67
+ gap: 20px;
68
+ }
69
+ .shelf-card {
70
+ background: #f6f3ee;
71
+ border: 1px solid #eadacc;
72
+ border-radius: 16px;
73
+ padding: 16px;
74
+ display: flex;
75
+ gap: 16px;
76
+ transition: all 0.3s ease;
77
+ cursor: pointer;
78
+ }
79
+ .shelf-card:hover {
80
+ border-color: #f5841f;
81
+ transform: translateY(-2px);
82
+ box-shadow: 0 4px 12px rgba(148, 74, 0, 0.06);
83
+ }
84
+ .cover-image {
85
+ width: 80px;
86
+ height: 110px;
87
+ object-fit: cover;
88
+ border-radius: 8px;
89
+ box-shadow: 0 2px 6px rgba(0,0,0,0.1);
90
+ }
91
+
92
+ /* ── Progress bar ── */
93
+ .progress-bar-bg {
94
+ width: 100%;
95
+ height: 6px;
96
+ background: #ebdccb;
97
+ border-radius: 999px;
98
+ overflow: hidden;
99
+ }
100
+ .progress-bar-fill {
101
+ height: 100%;
102
+ background: #f5841f;
103
+ }
104
+
105
+ /* ── Recording UX ── */
106
+ @keyframes pulse-ring {
107
+ 0% { transform: scale(0.85); opacity: 0.6; }
108
+ 50% { transform: scale(1.15); opacity: 0.25; }
109
+ 100% { transform: scale(1.4); opacity: 0; }
110
+ }
111
+ @keyframes pulse {
112
+ 0%, 100% { opacity: 1; }
113
+ 50% { opacity: 0.4; }
114
+ }
115
+ .pulse-circle {
116
+ position: relative;
117
+ width: 120px;
118
+ height: 120px;
119
+ border-radius: 50%;
120
+ background: #944a00;
121
+ color: white;
122
+ display: flex;
123
+ flex-direction: column;
124
+ align-items: center;
125
+ justify-content: center;
126
+ margin: 0 auto;
127
+ }
128
+ .pulse-circle::before,
129
+ .pulse-circle::after {
130
+ content: '';
131
+ position: absolute;
132
+ inset: 0;
133
+ border-radius: 50%;
134
+ border: 3px solid #944a00;
135
+ animation: pulse-ring 1.8s ease-out infinite;
136
+ }
137
+ .pulse-circle::after { animation-delay: 0.6s; }
138
+ .recording-active { background: #ef4444 !important; }
139
+ .recording-active::before,
140
+ .recording-active::after { border-color: #ef4444; }
141
+
142
+ /* ── Subtitle / dialog ticker ── */
143
+ .dialog-synced {
144
+ background: rgba(0,0,0,0.15);
145
+ border: 1px solid rgba(255,166,0,0.1);
146
+ color: #ffd3a6;
147
+ font-family: 'Playfair Display', Georgia, serif;
148
+ font-size: 1.25rem;
149
+ font-style: italic;
150
+ padding: 24px;
151
+ border-radius: 16px;
152
+ text-align: center;
153
+ min-height: 100px;
154
+ display: flex;
155
+ align-items: center;
156
+ justify-content: center;
157
+ }
158
+
159
+ /* ── Tabs ── */
160
+ .tabs .tab-nav button {
161
+ border-bottom: 2px solid transparent !important;
162
+ }
163
+ .tabs .tab-nav button.selected {
164
+ color: #944a00 !important;
165
+ border-bottom-color: #944a00 !important;
166
+ font-weight: 700 !important;
167
+ }
168
+
169
+ /* ── Mobile responsive ── */
170
+ @media (max-width: 768px) {
171
+ .gradio-container { max-width: 100% !important; padding: 0 12px !important; }
172
+ .book-shelf-grid { grid-template-columns: 1fr !important; }
173
+ .hero-spotlight { flex-direction: column !important; padding: 20px !important; }
174
+ .shelf-card { flex-direction: column; }
175
+ .card-container, .dark-card-container { padding: 16px !important; }
176
+ }
177
+ @media (max-width: 480px) {
178
+ .book-shelf-grid { grid-template-columns: 1fr !important; gap: 12px; }
179
+ }