@ofidj/generator-fidj 1.0.1 → 1.0.3

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 (26) hide show
  1. package/README.md +58 -14
  2. package/bin/create-fidj.cjs +9 -1
  3. package/generators/app/index.js +57 -15
  4. package/generators/app/templates/typescript/README.md +11 -2
  5. package/generators/app/templates/typescript/app.config.json +5 -1
  6. package/generators/app/templates/typescript/public/fidj-logo.png +0 -0
  7. package/generators/app/templates/typescript/public/fonts/IBMPlexMono-400-latin-ext.woff2 +0 -0
  8. package/generators/app/templates/typescript/public/fonts/IBMPlexMono-400-latin.woff2 +0 -0
  9. package/generators/app/templates/typescript/public/fonts/IBMPlexMono-500-latin-ext.woff2 +0 -0
  10. package/generators/app/templates/typescript/public/fonts/IBMPlexMono-500-latin.woff2 +0 -0
  11. package/generators/app/templates/typescript/public/fonts/IBMPlexSans-400_600-latin-ext.woff2 +0 -0
  12. package/generators/app/templates/typescript/public/fonts/IBMPlexSans-400_600-latin.woff2 +0 -0
  13. package/generators/app/templates/typescript/public/fonts/InstrumentSerif-400-latin-ext.woff2 +0 -0
  14. package/generators/app/templates/typescript/public/fonts/InstrumentSerif-400-latin.woff2 +0 -0
  15. package/generators/app/templates/typescript/scripts/build.mjs +3 -0
  16. package/generators/app/templates/typescript/scripts/render-content.mjs +2 -2
  17. package/generators/app/templates/typescript/server/index.ts +10 -1
  18. package/generators/app/templates/typescript/src/content.ts +112 -37
  19. package/generators/app/templates/typescript/src/fonts.css +92 -0
  20. package/generators/app/templates/typescript/src/main.ts +17 -9
  21. package/generators/app/templates/typescript/src/service-agreement.ts +40 -0
  22. package/generators/app/templates/typescript/src/style.css +640 -363
  23. package/generators/app/templates/typescript/src/tokens.css +56 -0
  24. package/lib/app-module.cjs +6 -1
  25. package/lib/scaffold.cjs +72 -3
  26. package/package.json +2 -6
@@ -1,193 +1,252 @@
1
+ @import "./tokens.css";
2
+ @import "./fonts.css";
3
+
4
+ /* The generated shell. Flat surfaces, 1px rules, one accent — the look is
5
+ * carried by type and by borders, never by elevation or rounding. Values come
6
+ * from tokens.css; nothing below may introduce a literal color. */
7
+
1
8
  :root {
2
- font-family:
3
- Inter,
4
- ui-sans-serif,
5
- system-ui,
6
- -apple-system,
7
- sans-serif;
8
- color: #1e4037;
9
- background: #f3f6f0;
9
+ font-family: var(--fidj-font-sans);
10
+ color: var(--fidj-text);
11
+ background: var(--fidj-paper);
10
12
  font-synthesis: none;
11
- line-height: 1.5;
13
+ line-height: 1.55;
14
+ -webkit-font-smoothing: antialiased;
12
15
  }
13
16
  * {
14
17
  box-sizing: border-box;
15
18
  }
16
19
  body {
17
20
  margin: 0;
21
+ background: var(--fidj-paper);
18
22
  }
19
23
  button,
20
24
  input,
21
- textarea {
25
+ textarea,
26
+ select {
22
27
  font: inherit;
23
28
  }
24
29
  a {
25
30
  color: inherit;
31
+ text-decoration: none;
32
+ }
33
+ a:hover {
34
+ color: var(--fidj-accent);
35
+ }
36
+ ::placeholder {
37
+ color: var(--fidj-placeholder);
38
+ }
39
+
40
+ /* Type scale. Display serif for page titles, mono for every label and eyebrow,
41
+ * sans for everything a person actually reads. */
42
+ h1 {
43
+ font-family: var(--fidj-font-display);
44
+ font-weight: 400;
45
+ font-size: clamp(32px, 4vw, 44px);
46
+ line-height: 1.08;
47
+ letter-spacing: -0.01em;
48
+ margin: 10px 0 20px;
49
+ text-wrap: balance;
50
+ }
51
+ h2 {
52
+ font-size: 15px;
53
+ font-weight: 600;
54
+ letter-spacing: -0.005em;
55
+ margin: 0 0 12px;
26
56
  }
57
+ h3 {
58
+ margin: 12px 0 8px;
59
+ font-size: 15px;
60
+ font-weight: 600;
61
+ }
62
+ p {
63
+ color: var(--fidj-text-faint);
64
+ text-wrap: pretty;
65
+ }
66
+ small {
67
+ font-size: 12.5px;
68
+ color: var(--fidj-text-faint);
69
+ }
70
+ .eyebrow {
71
+ font-family: var(--fidj-font-mono);
72
+ font-size: 11px;
73
+ font-weight: 400;
74
+ letter-spacing: 0.12em;
75
+ text-transform: uppercase;
76
+ color: var(--fidj-text-faint);
77
+ margin: 0;
78
+ }
79
+
27
80
  .topbar {
28
- max-width: 1180px;
29
- margin: auto;
30
- padding: 25px 32px;
81
+ background: var(--fidj-ink);
82
+ color: var(--fidj-on-ink);
83
+ padding: 0 clamp(20px, 4vw, 56px);
31
84
  display: flex;
32
85
  justify-content: space-between;
33
86
  align-items: center;
34
87
  gap: 20px;
35
- border-bottom: 1px solid #dce4d9;
88
+ height: 60px;
36
89
  }
37
90
  .brand {
38
91
  display: flex;
39
92
  align-items: center;
40
93
  gap: 12px;
41
- font-weight: 700;
42
- text-decoration: none;
43
- font-size: 19px;
94
+ font-weight: 600;
95
+ font-size: 16px;
96
+ letter-spacing: 0.02em;
97
+ }
98
+ .brand img {
99
+ height: 20px;
100
+ width: auto;
101
+ max-width: 80px;
102
+ object-fit: contain;
103
+ display: block;
104
+ }
105
+ .brand:hover {
106
+ color: var(--fidj-on-ink);
44
107
  }
45
108
  .app-symbol {
46
- background: #dce8d4;
47
- border-radius: 12px;
48
- padding: 4px 13px;
49
- font-size: 30px;
109
+ font-size: 15px;
110
+ color: var(--fidj-accent);
50
111
  }
51
112
  .fidj-brand {
52
113
  display: flex;
53
114
  align-items: center;
54
115
  gap: 10px;
55
- font-size: 12px;
56
- text-decoration: none;
116
+ font-family: var(--fidj-font-mono);
117
+ font-size: 11px;
118
+ letter-spacing: 0.08em;
119
+ text-transform: uppercase;
120
+ color: var(--fidj-on-ink-faint);
121
+ }
122
+ .fidj-brand:hover {
123
+ color: var(--fidj-on-ink);
57
124
  }
58
125
  .fidj-brand img {
59
- width: 28px;
60
- height: 28px;
61
- image-rendering: pixelated;
126
+ width: 18px;
127
+ height: 18px;
62
128
  }
63
129
  main {
64
- max-width: 1180px;
130
+ max-width: var(--fidj-shell-max);
65
131
  margin: auto;
66
- padding: 38px 32px;
67
- }
68
- h1 {
69
- font-size: 48px;
70
- line-height: 1.13;
71
- letter-spacing: -0.045em;
72
- margin: 10px 0 24px;
73
- font-weight: 650;
74
- }
75
- h2 {
76
- font-size: 22px;
77
- letter-spacing: -0.025em;
78
- margin: 0 0 12px;
79
- }
80
- h3 {
81
- margin: 12px 0 8px;
82
- font-size: 19px;
83
- }
84
- p {
85
- color: #5d7166;
86
- }
87
- .eyebrow {
88
- font-size: 11px;
89
- letter-spacing: 0.14em;
90
- font-weight: 700;
91
- color: #527864;
92
- }
93
- .welcome {
94
- display: grid;
95
- grid-template-columns: 1.1fr 1fr;
96
- align-items: center;
97
- gap: 90px;
98
- margin: 35px 0;
99
- }
100
- .welcome h1 {
101
- font-size: 68px;
102
- }
103
- .welcome > div > p {
104
- max-width: 390px;
132
+ padding: clamp(32px, 5vw, 56px) clamp(20px, 4vw, 56px) 80px;
105
133
  }
134
+
106
135
  .card {
107
- background: white;
108
- border: 1px solid #dce5da;
109
- border-radius: 20px;
110
- padding: 30px;
111
- box-shadow: 0 6px 20px #233b2904;
136
+ background: var(--fidj-surface);
137
+ border: 1px solid var(--fidj-line);
138
+ border-radius: var(--fidj-radius);
139
+ padding: 24px;
112
140
  }
113
141
  .promise {
114
142
  display: flex;
115
- gap: 14px;
143
+ gap: 12px;
116
144
  align-items: center;
117
145
  font-size: 13px;
118
- color: #617268;
119
- margin-top: 35px;
146
+ color: var(--fidj-text-faint);
147
+ border-top: 1px solid var(--fidj-line-soft);
148
+ padding-top: 20px;
149
+ margin-top: 32px;
120
150
  }
121
151
  .promise img {
122
- width: 38px;
123
- height: 38px;
124
- image-rendering: pixelated;
152
+ width: 22px;
153
+ height: 22px;
125
154
  }
155
+
126
156
  label {
127
157
  display: block;
128
- font-size: 13px;
129
- font-weight: 600;
130
- margin: 18px 0 8px;
158
+ font-family: var(--fidj-font-mono);
159
+ font-size: 11px;
160
+ letter-spacing: 0.1em;
161
+ text-transform: uppercase;
162
+ color: var(--fidj-text-faint);
163
+ margin: 18px 0 7px;
131
164
  }
132
165
  input,
133
- textarea {
166
+ textarea,
167
+ select {
134
168
  display: block;
135
169
  width: 100%;
136
- padding: 12px;
137
- border: 1px solid #cfdbd0;
138
- border-radius: 8px;
139
- background: #fcfdfb;
140
- color: #173c30;
170
+ padding: 13px 14px;
171
+ border: 1px solid var(--fidj-line-strong);
172
+ border-radius: var(--fidj-radius);
173
+ background: var(--fidj-surface);
174
+ color: var(--fidj-text);
175
+ font-size: 15px;
141
176
  }
142
177
  input:focus,
143
178
  textarea:focus,
144
- button:focus-visible {
145
- outline: 2px solid #567d65;
146
- outline-offset: 3px;
179
+ select:focus {
180
+ outline: none;
181
+ border-color: var(--fidj-ink);
182
+ box-shadow: var(--fidj-focus-ring);
147
183
  }
148
184
  button {
149
- border: 1px solid #c8d7c8;
150
- color: #245344;
151
- background: white;
152
- padding: 10px 15px;
153
- border-radius: 8px;
185
+ border: 1px solid var(--fidj-line-strong);
186
+ border-radius: var(--fidj-radius);
187
+ color: var(--fidj-text);
188
+ background: transparent;
189
+ padding: 11px 16px;
154
190
  cursor: pointer;
155
- font-size: 13px;
156
- font-weight: 600;
191
+ font-size: 13.5px;
192
+ font-weight: 500;
157
193
  }
158
- button:hover {
159
- background: #edf4e8;
194
+ button:hover:not(:disabled) {
195
+ border-color: var(--fidj-ink);
160
196
  }
161
197
  button:disabled {
162
- opacity: 0.5;
198
+ opacity: 0.45;
163
199
  cursor: not-allowed;
164
200
  }
165
201
  .primary {
166
- background: #245d49;
167
- color: white;
168
- border-color: #245d49;
202
+ background: var(--fidj-ink);
203
+ color: var(--fidj-paper);
204
+ border-color: var(--fidj-ink);
169
205
  display: block;
170
206
  width: 100%;
171
- margin: 22px 0 12px;
207
+ padding: 14px 18px;
208
+ font-size: 15px;
209
+ font-weight: 600;
210
+ margin: 20px 0 10px;
211
+ }
212
+ .primary:hover:not(:disabled) {
213
+ background: var(--fidj-ink-raised);
214
+ border-color: var(--fidj-ink-raised);
215
+ }
216
+ .danger {
217
+ color: var(--fidj-danger);
218
+ border-color: var(--fidj-danger-line);
172
219
  }
173
- .primary:hover {
174
- background: #194b3a;
220
+ .danger:hover:not(:disabled) {
221
+ border-color: var(--fidj-danger);
222
+ background: var(--fidj-danger-tint);
175
223
  }
224
+
176
225
  .demo {
177
- background: #f0f4eb;
178
- border-radius: 12px;
179
- padding: 15px;
226
+ background: var(--fidj-surface-sunk);
227
+ border: 1px solid var(--fidj-line-soft);
228
+ border-radius: var(--fidj-radius);
229
+ padding: 16px;
180
230
  margin-top: 24px;
181
- font-size: 12px;
231
+ font-size: 12.5px;
182
232
  }
183
- .demo button {
184
- margin: 4px 5px 0 0;
233
+ .demo strong {
234
+ font-family: var(--fidj-font-mono);
185
235
  font-size: 11px;
186
- padding: 8px;
236
+ letter-spacing: 0.1em;
237
+ text-transform: uppercase;
238
+ color: var(--fidj-text-faint);
239
+ font-weight: 400;
240
+ }
241
+ .demo button {
242
+ margin: 4px 6px 0 0;
243
+ font-size: 12px;
244
+ padding: 8px 10px;
187
245
  }
188
246
  .demo p {
189
- margin: 6px 0 12px;
247
+ margin: 8px 0 12px;
190
248
  }
249
+
191
250
  .page-heading,
192
251
  .section-heading {
193
252
  display: flex;
@@ -195,45 +254,71 @@ button:disabled {
195
254
  align-items: center;
196
255
  gap: 20px;
197
256
  }
198
- .roles {
257
+ .section-heading {
258
+ align-items: baseline;
259
+ border-bottom: 1px solid var(--fidj-line-soft);
260
+ padding-bottom: 10px;
261
+ margin-bottom: 16px;
262
+ }
263
+ .section-heading span {
264
+ font-family: var(--fidj-font-mono);
199
265
  font-size: 11px;
200
- border: 1px solid #cbd8c9;
201
- border-radius: 20px;
202
- padding: 5px 10px;
266
+ letter-spacing: 0.1em;
267
+ color: var(--fidj-text-faint);
268
+ }
269
+ .roles {
270
+ font-family: var(--fidj-font-mono);
271
+ font-size: 10.5px;
272
+ letter-spacing: 0.06em;
273
+ text-transform: uppercase;
274
+ color: var(--fidj-text-faint);
275
+ border: 1px solid var(--fidj-line);
276
+ border-radius: var(--fidj-radius);
277
+ padding: 5px 9px;
203
278
  margin-left: 8px;
204
279
  }
280
+
205
281
  nav {
206
282
  display: flex;
207
- gap: 8px;
208
- border-bottom: 1px solid #dce4d9;
209
- margin: 10px 0 30px;
210
- padding-bottom: 15px;
283
+ flex-wrap: wrap;
284
+ gap: 2px;
285
+ border-bottom: 1px solid var(--fidj-line-soft);
286
+ margin: 10px 0 32px;
211
287
  }
212
288
  nav button {
213
- border-color: transparent;
289
+ border: 0;
290
+ border-bottom: 2px solid transparent;
291
+ border-radius: 0;
214
292
  background: transparent;
293
+ padding: 16px 14px;
294
+ font-size: 14px;
295
+ color: var(--fidj-text-faint);
296
+ }
297
+ nav button:hover:not(:disabled) {
298
+ color: var(--fidj-text);
215
299
  }
216
300
  nav button.selected {
217
- background: #dfeada;
301
+ color: var(--fidj-text);
302
+ border-bottom-color: var(--fidj-ink);
218
303
  }
219
304
  nav button:last-child {
220
305
  margin-left: auto;
221
306
  }
307
+
222
308
  .workspace {
223
309
  display: grid;
224
310
  grid-template-columns: 1.2fr 1fr;
225
- gap: 28px;
311
+ gap: 24px;
226
312
  align-items: start;
227
313
  }
228
314
  .privacy-grid {
229
315
  display: grid;
230
316
  grid-template-columns: 1fr 1fr;
231
- gap: 28px;
317
+ gap: 24px;
232
318
  align-items: start;
233
319
  }
234
- .section-heading span {
235
- font-size: 12px;
236
- color: #647867;
320
+ .privacy-grid button {
321
+ margin-top: 16px;
237
322
  }
238
323
  .note {
239
324
  margin-top: 16px;
@@ -241,44 +326,55 @@ nav button:last-child {
241
326
  .note p {
242
327
  white-space: pre-wrap;
243
328
  overflow-wrap: anywhere;
329
+ color: var(--fidj-text-muted);
244
330
  }
245
331
  .empty {
246
332
  text-align: center;
247
- padding: 60px 28px;
333
+ padding: 56px 28px;
248
334
  }
249
335
  .empty > span {
250
- font-size: 45px;
251
- color: #9ab696;
252
- }
253
- small {
254
- font-size: 12px;
255
- color: #6b7d70;
336
+ font-size: 28px;
337
+ color: var(--fidj-accent);
256
338
  }
339
+
257
340
  .notice,
258
341
  .error {
259
- border-radius: 10px;
260
- padding: 15px 20px;
342
+ border: 1px solid var(--fidj-line);
343
+ border-left: 2px solid var(--fidj-ok);
344
+ border-radius: var(--fidj-radius);
345
+ background: var(--fidj-surface);
346
+ font-size: 13.5px;
347
+ color: var(--fidj-text-muted);
348
+ padding: 14px 18px;
261
349
  margin-bottom: 24px;
262
350
  }
263
- .notice {
264
- background: #e0edd8;
265
- }
266
351
  .error {
267
- background: #fdebe7;
268
- color: #99362d;
352
+ border-left-color: var(--fidj-danger);
353
+ background: var(--fidj-danger-tint);
354
+ color: var(--fidj-danger-strong);
269
355
  }
356
+
270
357
  .agreement {
271
- background: #f0f5ec;
272
- border-radius: 10px;
358
+ background: var(--fidj-surface-sunk);
359
+ border: 1px solid var(--fidj-line-soft);
360
+ border-radius: var(--fidj-radius);
273
361
  padding: 16px;
274
362
  font-size: 13px;
275
363
  margin: 24px 0;
276
364
  }
365
+ .agreement strong {
366
+ font-size: 13.5px;
367
+ }
277
368
  .agreement span {
278
369
  float: right;
370
+ font-family: var(--fidj-font-mono);
371
+ font-size: 10.5px;
372
+ letter-spacing: 0.06em;
373
+ text-transform: uppercase;
374
+ color: var(--fidj-text-faint);
279
375
  }
280
376
  .agreement p {
281
- font-size: 12px;
377
+ font-size: 12.5px;
282
378
  }
283
379
  .toggle {
284
380
  display: flex;
@@ -286,7 +382,13 @@ small {
286
382
  justify-content: space-between;
287
383
  gap: 20px;
288
384
  padding: 18px 0;
289
- border-bottom: 1px solid #e9eee5;
385
+ margin: 0;
386
+ border-bottom: 1px solid var(--fidj-line-soft);
387
+ font-family: var(--fidj-font-sans);
388
+ font-size: 14px;
389
+ letter-spacing: 0;
390
+ text-transform: none;
391
+ color: var(--fidj-text);
290
392
  }
291
393
  .toggle small {
292
394
  display: block;
@@ -294,72 +396,392 @@ small {
294
396
  margin-top: 5px;
295
397
  }
296
398
  .toggle input {
297
- width: 20px;
298
- height: 20px;
299
- accent-color: #245d49;
300
- }
301
- .privacy-grid button {
302
- margin-top: 15px;
303
- }
304
- .danger {
305
- color: #a2392d;
306
- border-color: #d7b2a9;
399
+ width: 18px;
400
+ height: 18px;
401
+ accent-color: var(--fidj-ink);
307
402
  }
308
403
  .fineprint {
309
404
  font-size: 12px;
405
+ color: var(--fidj-text-faint);
310
406
  margin-top: 24px;
311
407
  }
312
408
  hr {
313
409
  border: 0;
314
- border-top: 1px solid #e3eade;
410
+ border-top: 1px solid var(--fidj-line-soft);
315
411
  margin: 28px 0;
316
412
  }
317
413
  footer {
318
414
  text-align: center;
319
- font-size: 11px;
320
- color: #7c8b7f;
321
- padding: 40px 0 10px;
415
+ font-family: var(--fidj-font-mono);
416
+ font-size: 10.5px;
417
+ letter-spacing: 0.06em;
418
+ color: var(--fidj-text-faint);
419
+ border-top: 1px solid var(--fidj-line-soft);
420
+ padding: 28px 0 10px;
322
421
  }
422
+
323
423
  @media (max-width: 760px) {
324
424
  main {
325
- padding: 25px 18px;
425
+ padding: 28px 18px 56px;
326
426
  }
327
427
  .topbar {
328
- padding: 18px;
428
+ padding: 0 18px;
329
429
  }
330
- .welcome,
331
430
  .workspace,
332
431
  .privacy-grid {
333
432
  grid-template-columns: 1fr;
334
- gap: 24px;
335
- }
336
- .welcome {
337
- margin-top: 10px;
338
- }
339
- .welcome h1 {
340
- font-size: 48px;
341
- }
342
- .welcome > div {
343
- padding: 10px;
344
433
  }
345
434
  .card {
346
- padding: 22px;
347
- }
348
- .page-heading h1 {
349
- font-size: 36px;
435
+ padding: 20px;
350
436
  }
351
437
  .fidj-brand {
352
438
  font-size: 0;
439
+ gap: 0;
353
440
  }
354
441
  .roles {
355
442
  display: inline-block;
356
- margin: 8px 0;
443
+ margin: 8px 0 0;
357
444
  }
358
445
  .page-heading {
359
446
  align-items: flex-start;
360
447
  }
361
448
  }
362
449
 
450
+ /* The sign-in entry: ink panel carrying the app's identity, paper panel
451
+ * carrying the form. It stacks below 860px, ink first. It is full-bleed, so
452
+ * the shell's own header, padding and footer step aside while it is shown. */
453
+ body.signin-view .topbar,
454
+ body.signin-view main > footer {
455
+ display: none;
456
+ }
457
+ body.signin-view main {
458
+ max-width: none;
459
+ margin: 0;
460
+ padding: 0;
461
+ }
462
+ /* The sign-in entry: ink panel carrying the app's identity, paper panel
463
+ * carrying the form. It stacks below 860px, ink first. */
464
+ .signin-shell {
465
+ display: flex;
466
+ flex-wrap: wrap;
467
+ align-items: stretch;
468
+ min-height: 100vh;
469
+ width: 100%;
470
+ background: var(--fidj-paper);
471
+ }
472
+ .signin-form [role="alert"],
473
+ .signin-form [role="status"] {
474
+ margin: 0 0 20px;
475
+ }
476
+ .signin-intro {
477
+ flex: 1.35 1 520px;
478
+ background: var(--fidj-ink);
479
+ color: var(--fidj-on-ink);
480
+ padding: 56px clamp(32px, 5vw, 80px) 44px;
481
+ display: flex;
482
+ flex-direction: column;
483
+ gap: 52px;
484
+ justify-content: space-between;
485
+ }
486
+ .signin-masthead {
487
+ display: flex;
488
+ align-items: center;
489
+ gap: 12px;
490
+ }
491
+ /* The app's own mark, beside its name. An app that supplied none borrows the
492
+ * Fidj mark, which keeps the corner from being blank without pretending the
493
+ * app is Fidj — the trust line below always names the provider explicitly. */
494
+ .signin-masthead .app-mark {
495
+ height: 22px;
496
+ width: auto;
497
+ max-width: 88px;
498
+ object-fit: contain;
499
+ display: block;
500
+ flex: none;
501
+ }
502
+ .signin-masthead strong {
503
+ font-size: 17px;
504
+ font-weight: 600;
505
+ letter-spacing: 0.02em;
506
+ }
507
+ .signin-identity {
508
+ display: flex;
509
+ flex-direction: column;
510
+ gap: 28px;
511
+ max-width: 620px;
512
+ }
513
+ /* An app that supplies no highlights would leave the panel hollow under
514
+ * space-between; centre its identity in the space instead. */
515
+ .signin-intro.is-plain .signin-identity {
516
+ margin-block: auto;
517
+ }
518
+ .signin-intro h1 {
519
+ margin: 0;
520
+ font-size: clamp(40px, 5.2vw, 68px);
521
+ line-height: 1.03;
522
+ letter-spacing: -0.015em;
523
+ }
524
+ .signin-description {
525
+ margin: 0;
526
+ font-size: 17px;
527
+ line-height: 1.6;
528
+ color: var(--fidj-on-ink-muted);
529
+ max-width: 52ch;
530
+ }
531
+ /* One hairline grid: the 1px gap is the parent background showing through. */
532
+ .signin-highlights {
533
+ display: grid;
534
+ grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
535
+ gap: 1px;
536
+ background: var(--fidj-ink-raised);
537
+ border: 1px solid var(--fidj-ink-raised);
538
+ }
539
+ .signin-highlights article {
540
+ background: var(--fidj-ink);
541
+ padding: 22px 20px 24px;
542
+ display: flex;
543
+ flex-direction: column;
544
+ gap: 9px;
545
+ }
546
+ .signin-highlights .eyebrow {
547
+ color: var(--fidj-accent);
548
+ letter-spacing: 0.12em;
549
+ }
550
+ .signin-highlights h2 {
551
+ margin: 0;
552
+ font-size: 15px;
553
+ }
554
+ .signin-highlights p {
555
+ margin: 0;
556
+ font-size: 13.5px;
557
+ line-height: 1.55;
558
+ color: var(--fidj-on-ink-faint);
559
+ }
560
+ .signin-trust {
561
+ display: flex;
562
+ flex-direction: column;
563
+ gap: 12px;
564
+ border-top: 1px solid var(--fidj-line-soft);
565
+ padding-top: 20px;
566
+ }
567
+ .signin-trust-head {
568
+ display: flex;
569
+ align-items: center;
570
+ gap: 10px;
571
+ }
572
+ .signin-trust .signin-logo {
573
+ width: 18px;
574
+ height: 18px;
575
+ flex: none;
576
+ }
577
+ .signin-trust strong {
578
+ font-size: 13px;
579
+ color: var(--fidj-text);
580
+ }
581
+ .signin-trust p:not(.signin-trust-head) {
582
+ display: flex;
583
+ align-items: baseline;
584
+ gap: 10px;
585
+ margin: 0;
586
+ font-size: 13px;
587
+ line-height: 1.55;
588
+ color: var(--fidj-text-muted);
589
+ }
590
+ .signin-trust p:not(.signin-trust-head)::before {
591
+ content: "";
592
+ width: 5px;
593
+ height: 5px;
594
+ border-radius: 50%;
595
+ background: var(--fidj-accent);
596
+ flex: none;
597
+ transform: translateY(-2px);
598
+ }
599
+
600
+ .signin-form {
601
+ flex: 1 1 420px;
602
+ background: var(--fidj-paper);
603
+ padding: 56px clamp(28px, 4vw, 64px) 44px;
604
+ display: flex;
605
+ flex-direction: column;
606
+ gap: 28px;
607
+ }
608
+ .signin-form > * {
609
+ max-width: 400px;
610
+ width: 100%;
611
+ margin-inline: auto;
612
+ }
613
+ .signin-form h2 {
614
+ font-family: var(--fidj-font-display);
615
+ font-weight: 400;
616
+ font-size: 34px;
617
+ line-height: 1.1;
618
+ letter-spacing: -0.01em;
619
+ margin: 0 0 20px;
620
+ }
621
+ .signin-form > p {
622
+ margin: 0;
623
+ font-size: 14px;
624
+ }
625
+ .signin-form input {
626
+ min-height: 48px;
627
+ }
628
+ .signin-form form > button {
629
+ min-height: 48px;
630
+ width: 100%;
631
+ margin-top: 12px;
632
+ }
633
+ .signin-form .secondary {
634
+ background: transparent;
635
+ margin-top: 8px;
636
+ font-weight: 500;
637
+ }
638
+ /* The label row carries its own escape hatch, as the mockup does: the reset
639
+ * link sits with the field it is about, not adrift under the form. */
640
+ .field-head {
641
+ display: flex;
642
+ align-items: center;
643
+ justify-content: space-between;
644
+ gap: 12px;
645
+ margin: 18px 0 7px;
646
+ }
647
+ .field-head label {
648
+ margin: 0;
649
+ }
650
+ .field-head a {
651
+ font-size: 12px;
652
+ color: var(--fidj-text-faint);
653
+ text-decoration: underline;
654
+ text-underline-offset: 3px;
655
+ }
656
+ .password-field {
657
+ position: relative;
658
+ display: block;
659
+ }
660
+ .password-field input {
661
+ padding-right: 68px;
662
+ }
663
+ .password-field button {
664
+ position: absolute;
665
+ right: 5px;
666
+ top: 50%;
667
+ transform: translateY(-50%);
668
+ border: 0;
669
+ background: none;
670
+ font-family: var(--fidj-font-mono);
671
+ font-size: 11px;
672
+ letter-spacing: 0.06em;
673
+ text-transform: uppercase;
674
+ color: var(--fidj-text-faint);
675
+ padding: 8px;
676
+ }
677
+ .password-field button:hover:not(:disabled) {
678
+ color: var(--fidj-text);
679
+ }
680
+ .signin-divider {
681
+ display: flex;
682
+ align-items: center;
683
+ gap: 12px;
684
+ font-family: var(--fidj-font-mono);
685
+ font-size: 10.5px;
686
+ letter-spacing: 0.1em;
687
+ text-transform: uppercase;
688
+ color: var(--fidj-text-faint);
689
+ margin: 0;
690
+ }
691
+ .signin-divider::before,
692
+ .signin-divider::after {
693
+ content: "";
694
+ height: 1px;
695
+ flex: 1;
696
+ background: var(--fidj-line);
697
+ }
698
+ .anonymous-entry {
699
+ display: flex;
700
+ justify-content: space-between;
701
+ align-items: center;
702
+ width: 100%;
703
+ min-height: 46px;
704
+ }
705
+ .signin-form .signin-footnote {
706
+ font-size: 12px;
707
+ text-align: center;
708
+ margin: 0;
709
+ }
710
+ .signin-badges {
711
+ margin-top: auto;
712
+ padding-top: 24px;
713
+ display: flex;
714
+ flex-wrap: wrap;
715
+ gap: 8px;
716
+ }
717
+ .signin-badges span {
718
+ font-family: var(--fidj-font-mono);
719
+ font-size: 10.5px;
720
+ letter-spacing: 0.06em;
721
+ text-transform: uppercase;
722
+ color: var(--fidj-text-faint);
723
+ border: 1px solid var(--fidj-line);
724
+ padding: 5px 9px;
725
+ }
726
+
727
+ @media (max-width: 860px) {
728
+ .signin-intro {
729
+ gap: 32px;
730
+ padding: 36px 24px 32px;
731
+ }
732
+ .signin-form {
733
+ padding: 36px 24px 32px;
734
+ }
735
+ .signin-description {
736
+ font-size: 15px;
737
+ }
738
+ }
739
+
740
+ .public-content {
741
+ text-align: center;
742
+ margin: 2rem 0;
743
+ }
744
+ .public-content img {
745
+ display: inline-block;
746
+ width: min(100%, 520px);
747
+ height: auto;
748
+ }
749
+ .public-content h1 {
750
+ font-size: clamp(2rem, 5vw, 3.5rem);
751
+ }
752
+ .content-account {
753
+ max-width: 680px;
754
+ margin: 2rem auto;
755
+ }
756
+ .content-account button {
757
+ margin: 0.4rem 0.4rem 0.4rem 0;
758
+ }
759
+ .content-account input:not([type="checkbox"]) {
760
+ width: 100%;
761
+ }
762
+ .content-nav {
763
+ display: flex;
764
+ align-items: center;
765
+ gap: 10px;
766
+ }
767
+ .content-nav #exit {
768
+ margin-left: auto;
769
+ }
770
+ .signin-logo {
771
+ width: 22px;
772
+ height: 22px;
773
+ }
774
+
775
+ button:focus-visible,
776
+ a:focus-visible,
777
+ input:focus-visible,
778
+ summary:focus-visible {
779
+ outline: 2px solid var(--fidj-ink);
780
+ outline-offset: 3px;
781
+ }
782
+
783
+ /* The personal site layout mleweb builds from --content. Same system, wider
784
+ * measure: the supplied HTML is developer-authored and styled, not sanitised. */
363
785
  .site-nav {
364
786
  border: 0;
365
787
  margin: 0;
@@ -368,13 +790,10 @@ footer {
368
790
  gap: 24px;
369
791
  font-size: 13px;
370
792
  }
371
- .site-nav a {
372
- text-decoration: none;
373
- }
374
793
  .workspace-link {
375
- border: 1px solid #cad8c8;
794
+ border: 1px solid var(--fidj-line-strong);
795
+ border-radius: var(--fidj-radius);
376
796
  padding: 9px 14px;
377
- border-radius: 8px;
378
797
  }
379
798
  .personal-hero {
380
799
  display: grid;
@@ -394,14 +813,13 @@ footer {
394
813
  image-rendering: pixelated;
395
814
  }
396
815
  .personal-intro h1 {
397
- font-size: 66px;
398
- line-height: 1.08;
816
+ font-size: clamp(44px, 6vw, 66px);
817
+ line-height: 1.05;
399
818
  max-width: 480px;
400
819
  }
401
820
  .personal-tagline {
402
- font-size: 21px;
403
- color: #274b3b;
404
- font-weight: 600;
821
+ font-size: 19px;
822
+ color: var(--fidj-text-muted);
405
823
  }
406
824
  .personal-intro > p {
407
825
  max-width: 440px;
@@ -414,14 +832,17 @@ footer {
414
832
  }
415
833
  .site-primary {
416
834
  display: inline-block;
417
- padding: 12px 19px;
418
- border-radius: 9px;
419
- background: #9d302b;
420
- color: white;
421
- text-decoration: none;
835
+ padding: 13px 19px;
836
+ border-radius: var(--fidj-radius);
837
+ background: var(--fidj-ink);
838
+ color: var(--fidj-paper);
422
839
  font-weight: 600;
423
840
  font-size: 14px;
424
841
  }
842
+ .site-primary:hover {
843
+ background: var(--fidj-ink-raised);
844
+ color: var(--fidj-paper);
845
+ }
425
846
  .personal-contact {
426
847
  font-size: 13px;
427
848
  line-height: 2;
@@ -431,15 +852,17 @@ footer {
431
852
  display: grid;
432
853
  grid-template-columns: 0.7fr 1.3fr;
433
854
  gap: 70px;
434
- border-top: 1px solid #dce4d9;
855
+ border-top: 1px solid var(--fidj-line-soft);
435
856
  padding: 60px 0;
436
857
  }
437
858
  .resume-intro h2 {
859
+ font-family: var(--fidj-font-display);
860
+ font-weight: 400;
438
861
  font-size: 34px;
439
862
  }
440
863
  .resume-timeline article {
441
864
  padding: 0 0 26px 24px;
442
- border-left: 2px solid #d6e0d2;
865
+ border-left: 1px solid var(--fidj-line);
443
866
  }
444
867
  .resume-timeline h3 {
445
868
  margin: 4px 0 8px;
@@ -448,10 +871,12 @@ footer {
448
871
  font-weight: 400;
449
872
  }
450
873
  .resume-period {
451
- font-size: 12px;
874
+ font-family: var(--fidj-font-mono);
875
+ font-size: 11px;
876
+ letter-spacing: 0.08em;
877
+ text-transform: uppercase;
452
878
  margin: 0;
453
- color: #7e6252;
454
- font-weight: 600;
879
+ color: var(--fidj-text-faint);
455
880
  }
456
881
  .resume-timeline article > p:last-child {
457
882
  margin-bottom: 0;
@@ -466,174 +891,26 @@ footer {
466
891
  margin-bottom: 0;
467
892
  }
468
893
 
469
- .public-content {
470
- text-align: center;
471
- margin: 2rem 0;
472
- }
473
- .public-content img {
474
- display: inline-block;
475
- width: min(100%, 520px);
476
- height: auto;
477
- }
478
- .public-content h1 {
479
- font-size: clamp(2rem, 5vw, 3.5rem);
480
- }
481
- .content-account {
482
- max-width: 680px;
483
- margin: 2rem auto;
484
- }
485
- .content-account button {
486
- margin: 0.4rem 0.4rem 0.4rem 0;
487
- }
488
- .content-account input:not([type="checkbox"]) {
489
- width: 100%;
490
- }
491
-
492
- .signin-logo {
493
- width: 56px;
494
- height: 56px;
495
- image-rendering: pixelated;
496
- }
497
-
498
- .signin-shell {
499
- display: grid;
500
- grid-template-columns: 1fr 1fr;
501
- background: white;
502
- border: 1px solid #dce5da;
503
- border-radius: 24px;
504
- overflow: hidden;
505
- margin: 34px auto;
506
- box-shadow: 0 18px 70px #23413208;
507
- }
508
- .signin-shell > [role="alert"],
509
- .signin-shell > [role="status"] {
510
- grid-column: 1 / -1;
511
- margin: 16px 24px 0;
512
- }
513
- .signin-intro {
514
- padding: 64px 52px;
515
- background: #eaf0e5;
516
- display: flex;
517
- flex-direction: column;
518
- align-items: flex-start;
519
- }
520
- .signin-intro h1 {
521
- max-width: 390px;
522
- font-size: clamp(36px, 4.3vw, 58px);
523
- margin: 24px 0;
524
- }
525
- .signin-description {
526
- font-size: 17px;
527
- max-width: 330px;
528
- margin: 0 0 64px;
529
- }
530
- .signin-trust {
531
- display: flex;
532
- gap: 16px;
533
- align-items: center;
534
- margin-top: auto;
535
- }
536
- .signin-trust .signin-logo {
537
- width: 36px;
538
- height: 36px;
539
- }
540
- .signin-trust strong {
541
- font-size: 13px;
542
- }
543
- .signin-trust p {
544
- font-size: 12px;
545
- margin: 4px 0 0;
546
- }
547
- .signin-form {
548
- padding: 56px 52px;
549
- }
550
- .signin-form h2 {
551
- font-size: 28px;
552
- margin-bottom: 8px;
553
- }
554
- .signin-form > p {
555
- margin: 0 0 28px;
556
- }
557
- .signin-form input {
558
- width: 100%;
559
- min-height: 48px;
560
- }
561
- .signin-form form button {
562
- width: 100%;
563
- min-height: 48px;
564
- margin-top: 12px;
565
- }
566
- .signin-form .secondary {
567
- background: white;
568
- }
569
- .signin-divider {
570
- display: flex;
571
- align-items: center;
572
- gap: 12px;
573
- color: #738277;
574
- font-size: 12px;
575
- margin: 24px 0 18px;
576
- }
577
- .signin-divider::before,
578
- .signin-divider::after {
579
- content: "";
580
- height: 1px;
581
- flex: 1;
582
- background: #e1e7de;
583
- }
584
- .anonymous-entry {
585
- display: flex;
586
- justify-content: space-between;
587
- align-items: center;
588
- width: 100%;
589
- min-height: 46px;
590
- background: #f5f7f2;
591
- }
592
- .signin-form .signin-footnote {
593
- font-size: 12px;
594
- text-align: center;
595
- margin: 12px 0 0;
596
- }
597
- .content-nav {
598
- display: flex;
599
- align-items: center;
600
- gap: 10px;
601
- }
602
- .content-nav #exit {
603
- margin-left: auto;
604
- }
605
- button:focus-visible,
606
- a:focus-visible,
607
- input:focus-visible,
608
- summary:focus-visible {
609
- outline: 3px solid #4b7e68;
610
- outline-offset: 4px;
611
- }
612
- @media (max-width: 720px) {
613
- .signin-shell {
894
+ @media (max-width: 760px) {
895
+ .personal-hero,
896
+ .resume-section {
614
897
  grid-template-columns: 1fr;
615
- margin: 0;
616
- }
617
- .signin-intro {
618
- padding: 28px;
898
+ gap: 28px;
899
+ min-height: 0;
619
900
  }
620
- .signin-intro h1 {
621
- margin: 12px 0;
901
+ .resume-section {
902
+ padding: 36px 0;
622
903
  }
623
- .signin-description {
624
- font-size: 15px;
625
- margin-bottom: 24px;
626
- }
627
- .signin-form {
628
- padding: 28px;
629
- }
630
- .content-nav {
904
+ .personal-footer,
905
+ .site-links {
631
906
  flex-wrap: wrap;
632
- }
633
- main {
634
- padding: 24px 16px;
635
- }
636
- .topbar {
637
- padding: 18px 16px;
907
+ gap: 16px;
638
908
  }
639
909
  }
910
+
911
+ .signin-agreement { margin-block: 1rem; }
912
+ .agreement-choice { display: flex; align-items: flex-start; gap: 0.75rem; font-family: var(--fidj-font-sans); font-size: 14px; letter-spacing: normal; text-transform: none; line-height: 1.45; color: var(--fidj-text-muted); }
913
+ .agreement-choice input[type="checkbox"] { width: 1.15rem; height: 1.15rem; min-height: 0; flex: 0 0 auto; margin: 0.15rem 0 0; padding: 0; accent-color: var(--fidj-ink); }
914
+ .signin-agreement button { margin-block: 0.5rem; padding: 0; border: 0; background: transparent; text-decoration: underline; }
915
+ #agreement-dialog { color: var(--fidj-text); background: var(--fidj-paper); border: 1px solid var(--fidj-line); border-radius: var(--fidj-radius); padding: 1.5rem; max-width: min(40rem, calc(100vw - 2rem)); max-height: 80vh; overflow: auto; }
916
+ #agreement-text { white-space: pre-wrap; overflow-wrap: anywhere; }