@jjlmoya/utils-pets 1.20.0 → 1.22.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 (42) hide show
  1. package/package.json +1 -1
  2. package/src/category/index.ts +2 -1
  3. package/src/entries.ts +4 -1
  4. package/src/index.ts +1 -0
  5. package/src/tests/i18n_coverage.test.ts +5 -2
  6. package/src/tests/locale_completeness.test.ts +1 -1
  7. package/src/tests/qa-test-helpers.ts +32 -0
  8. package/src/tests/qa_bibliography_links.test.ts +40 -0
  9. package/src/tests/qa_claim_evidence.test.ts +69 -0
  10. package/src/tests/qa_logic_reference_coverage.test.ts +46 -0
  11. package/src/tests/qa_runtime_i18n.test.ts +100 -0
  12. package/src/tests/tool_validation.test.ts +3 -3
  13. package/src/tool/petCarrierCrateSizePlanner/bibliography.astro +6 -0
  14. package/src/tool/petCarrierCrateSizePlanner/bibliography.ts +12 -0
  15. package/src/tool/petCarrierCrateSizePlanner/component.astro +89 -0
  16. package/src/tool/petCarrierCrateSizePlanner/controller.ts +128 -0
  17. package/src/tool/petCarrierCrateSizePlanner/dom-views.ts +96 -0
  18. package/src/tool/petCarrierCrateSizePlanner/entry.ts +27 -0
  19. package/src/tool/petCarrierCrateSizePlanner/evaluator.ts +14 -0
  20. package/src/tool/petCarrierCrateSizePlanner/i18n/de.ts +171 -0
  21. package/src/tool/petCarrierCrateSizePlanner/i18n/en.ts +171 -0
  22. package/src/tool/petCarrierCrateSizePlanner/i18n/es.ts +171 -0
  23. package/src/tool/petCarrierCrateSizePlanner/i18n/fr.ts +171 -0
  24. package/src/tool/petCarrierCrateSizePlanner/i18n/id.ts +171 -0
  25. package/src/tool/petCarrierCrateSizePlanner/i18n/it.ts +171 -0
  26. package/src/tool/petCarrierCrateSizePlanner/i18n/ja.ts +171 -0
  27. package/src/tool/petCarrierCrateSizePlanner/i18n/ko.ts +171 -0
  28. package/src/tool/petCarrierCrateSizePlanner/i18n/nl.ts +171 -0
  29. package/src/tool/petCarrierCrateSizePlanner/i18n/pl.ts +171 -0
  30. package/src/tool/petCarrierCrateSizePlanner/i18n/pt.ts +171 -0
  31. package/src/tool/petCarrierCrateSizePlanner/i18n/ru.ts +171 -0
  32. package/src/tool/petCarrierCrateSizePlanner/i18n/sv.ts +171 -0
  33. package/src/tool/petCarrierCrateSizePlanner/i18n/tr.ts +171 -0
  34. package/src/tool/petCarrierCrateSizePlanner/i18n/zh.ts +171 -0
  35. package/src/tool/petCarrierCrateSizePlanner/index.ts +12 -0
  36. package/src/tool/petCarrierCrateSizePlanner/logic.test.ts +49 -0
  37. package/src/tool/petCarrierCrateSizePlanner/logic.ts +77 -0
  38. package/src/tool/petCarrierCrateSizePlanner/pet-carrier-crate-size-planner.css +639 -0
  39. package/src/tool/petCarrierCrateSizePlanner/seo.astro +14 -0
  40. package/src/tool/petCarrierCrateSizePlanner/storage.ts +31 -0
  41. package/src/tool/petCarrierCrateSizePlanner/ui.ts +59 -0
  42. package/src/tools.ts +3 -0
@@ -0,0 +1,639 @@
1
+ .pet-carrier-crate-size-planner {
2
+ --n-ink: #14313a;
3
+ --n-muted: #557079;
4
+ --n-paper: #f4efe6;
5
+ --n-card: #fffdf8;
6
+ --n-line: #cfdad7;
7
+ --n-teal: #087c78;
8
+ --n-teal-dark: #07514f;
9
+ --n-coral: #d85f49;
10
+ --n-coral-soft: #f5d7ca;
11
+ --n-blueprint: #e1f0ed;
12
+ --n-result: #163d46;
13
+ --n-result-text: #fffdf8;
14
+ --n-result-muted: #b7d2d0;
15
+ --n-carrier: #fffdf8;
16
+ --n-grid: #fff;
17
+ --n-pet: #14313a;
18
+ --n-focus: #e9a391;
19
+ --n-status: #d7eee6;
20
+ --n-status-text: #07514f;
21
+ --n-adjusted: #f7cfc2;
22
+ --n-adjusted-text: #7d3025;
23
+ --n-error: #9b382b;
24
+
25
+ box-sizing: border-box;
26
+ max-width: 70rem;
27
+ margin: 2rem auto;
28
+ padding: clamp(1rem, 3vw, 2rem);
29
+ border: 1px solid var(--n-line);
30
+ border-radius: 1.5rem;
31
+ color: var(--n-ink);
32
+ background: var(--n-paper);
33
+ box-shadow: 0.45rem 0.45rem 0 rgba(18, 62, 59, 0.12);
34
+ }
35
+
36
+ .theme-dark .pet-carrier-crate-size-planner {
37
+ --n-ink: #f0faf7;
38
+ --n-muted: #b1c8c7;
39
+ --n-paper: #0b1e2a;
40
+ --n-card: #142d38;
41
+ --n-line: #36535c;
42
+ --n-teal: #32bcb1;
43
+ --n-teal-dark: #09252e;
44
+ --n-coral: #ff9277;
45
+ --n-coral-soft: #4a2b2c;
46
+ --n-blueprint: #24484d;
47
+ --n-result: #071b26;
48
+ --n-result-text: #f7fffc;
49
+ --n-result-muted: #c2dcda;
50
+ --n-carrier: #eef7f3;
51
+ --n-grid: #d9f2ee;
52
+ --n-pet: #092a34;
53
+ --n-focus: #ffb3a0;
54
+ --n-status: #b5e8db;
55
+ --n-status-text: #092a34;
56
+ --n-adjusted: #6b3834;
57
+ --n-adjusted-text: #fff4ef;
58
+ --n-error: #ffb09b;
59
+
60
+ border-color: var(--n-line);
61
+ box-shadow: 0.45rem 0.45rem 0 rgba(0, 0, 0, 0.28);
62
+ }
63
+
64
+ .pet-carrier-crate-size-planner .carrier-hero {
65
+ display: grid;
66
+ grid-template-columns: minmax(0, 1fr) auto;
67
+ gap: 1.5rem;
68
+ align-items: end;
69
+ padding: 0.25rem 0 1.5rem;
70
+ border-bottom: 1px solid var(--n-line);
71
+ }
72
+
73
+ .pet-carrier-crate-size-planner .carrier-hero-copy p,
74
+ .pet-carrier-crate-size-planner .carrier-panel-heading p,
75
+ .pet-carrier-crate-size-planner .result-heading p,
76
+ .pet-carrier-crate-size-planner .result-heading h2,
77
+ .pet-carrier-crate-size-planner .dimension-ledger p,
78
+ .pet-carrier-crate-size-planner .carrier-method,
79
+ .pet-carrier-crate-size-planner .carrier-note,
80
+ .pet-carrier-crate-size-planner .carrier-checklist p {
81
+ margin: 0;
82
+ }
83
+
84
+ .pet-carrier-crate-size-planner .carrier-kicker,
85
+ .pet-carrier-crate-size-planner .carrier-panel-heading p,
86
+ .pet-carrier-crate-size-planner .result-heading > .result-title-row p,
87
+ .pet-carrier-crate-size-planner .dimension-ledger > p {
88
+ color: var(--n-coral);
89
+ font-size: 0.7rem;
90
+ font-weight: 900;
91
+ letter-spacing: 0.13em;
92
+ line-height: 1.3;
93
+ text-transform: uppercase;
94
+ }
95
+
96
+ .pet-carrier-crate-size-planner .carrier-kicker {
97
+ margin-bottom: 0.35rem;
98
+ }
99
+
100
+ .pet-carrier-crate-size-planner .carrier-journey {
101
+ max-width: 42rem;
102
+ color: var(--n-muted);
103
+ font-size: 1rem;
104
+ line-height: 1.5;
105
+ }
106
+
107
+ .pet-carrier-crate-size-planner .unit-switch {
108
+ display: flex;
109
+ align-items: center;
110
+ gap: 0.45rem;
111
+ flex-wrap: wrap;
112
+ justify-content: flex-end;
113
+ }
114
+
115
+ .pet-carrier-crate-size-planner .unit-switch > span {
116
+ margin-right: 0.2rem;
117
+ color: var(--n-muted);
118
+ font-size: 0.72rem;
119
+ font-weight: 800;
120
+ letter-spacing: 0.05em;
121
+ text-transform: uppercase;
122
+ }
123
+
124
+ .pet-carrier-crate-size-planner button {
125
+ min-height: 2.7rem;
126
+ border: 1px solid var(--n-line);
127
+ border-radius: 0.7rem;
128
+ padding: 0.6rem 0.85rem;
129
+ color: var(--n-ink);
130
+ background: var(--n-card);
131
+ font: inherit;
132
+ font-weight: 700;
133
+ cursor: pointer;
134
+ transition: border-color 160ms ease, background 160ms ease, color 160ms ease, transform 160ms ease;
135
+ }
136
+
137
+ .pet-carrier-crate-size-planner button:hover,
138
+ .pet-carrier-crate-size-planner button:focus-visible {
139
+ border-color: var(--n-teal);
140
+ transform: translateY(-1px);
141
+ }
142
+
143
+ .pet-carrier-crate-size-planner button.is-active,
144
+ .pet-carrier-crate-size-planner [data-unit].is-active {
145
+ border-color: var(--n-teal);
146
+ color: var(--n-card);
147
+ background: var(--n-teal);
148
+ }
149
+
150
+ .pet-carrier-crate-size-planner .carrier-workbench {
151
+ display: grid;
152
+ grid-template-columns: minmax(17rem, 0.78fr) minmax(0, 1.22fr);
153
+ gap: 1rem;
154
+ min-width: 0;
155
+ margin-top: 1rem;
156
+ }
157
+
158
+ .pet-carrier-crate-size-planner .carrier-controls,
159
+ .pet-carrier-crate-size-planner .carrier-result {
160
+ min-width: 0;
161
+ padding: clamp(1rem, 2.5vw, 1.5rem);
162
+ border: 1px solid var(--n-line);
163
+ border-radius: 1.15rem;
164
+ }
165
+
166
+ .pet-carrier-crate-size-planner .carrier-controls {
167
+ background: var(--n-card);
168
+ }
169
+
170
+ .pet-carrier-crate-size-planner .carrier-result {
171
+ overflow: hidden;
172
+ color: var(--n-result-text);
173
+ background: var(--n-result);
174
+ border-color: var(--n-result);
175
+ }
176
+
177
+ .pet-carrier-crate-size-planner .carrier-panel-heading {
178
+ display: flex;
179
+ gap: 0.75rem;
180
+ align-items: flex-start;
181
+ margin-bottom: 1.35rem;
182
+ padding-bottom: 1rem;
183
+ border-bottom: 1px solid var(--n-line);
184
+ }
185
+
186
+ .pet-carrier-crate-size-planner .carrier-panel-heading h2 {
187
+ margin: 0.15rem 0 0;
188
+ color: var(--n-ink);
189
+ font-size: 1.35rem;
190
+ line-height: 1.1;
191
+ }
192
+
193
+ .pet-carrier-crate-size-planner .carrier-panel-heading p {
194
+ color: var(--n-muted);
195
+ font-size: 0.68rem;
196
+ }
197
+
198
+ .pet-carrier-crate-size-planner .carrier-step {
199
+ display: grid;
200
+ flex: 0 0 2.2rem;
201
+ place-items: center;
202
+ width: 2.2rem;
203
+ height: 2.2rem;
204
+ border-radius: 0.65rem;
205
+ color: var(--n-card);
206
+ background: var(--n-coral);
207
+ font-size: 0.75rem;
208
+ font-weight: 900;
209
+ }
210
+
211
+ .pet-carrier-crate-size-planner fieldset {
212
+ min-width: 0;
213
+ margin: 0 0 1.2rem;
214
+ padding: 0;
215
+ border: 0;
216
+ }
217
+
218
+ .pet-carrier-crate-size-planner legend {
219
+ margin-bottom: 0.55rem;
220
+ color: var(--n-ink);
221
+ font-weight: 800;
222
+ }
223
+
224
+ .pet-carrier-crate-size-planner .choice-row,
225
+ .pet-carrier-crate-size-planner .preset-strip {
226
+ display: flex;
227
+ gap: 0.55rem;
228
+ flex-wrap: wrap;
229
+ }
230
+
231
+ .pet-carrier-crate-size-planner .choice-row button {
232
+ display: flex;
233
+ flex: 1;
234
+ gap: 0.45rem;
235
+ align-items: center;
236
+ justify-content: center;
237
+ min-width: 7rem;
238
+ }
239
+
240
+ .pet-carrier-crate-size-planner .choice-row svg {
241
+ width: 1.1rem;
242
+ height: 1.1rem;
243
+ }
244
+
245
+ .pet-carrier-crate-size-planner .measurements {
246
+ display: grid;
247
+ gap: 0.6rem;
248
+ margin-bottom: 1rem;
249
+ padding-top: 0.15rem;
250
+ border-top: 1px solid var(--n-line);
251
+ }
252
+
253
+ .pet-carrier-crate-size-planner .measurements legend {
254
+ padding-top: 1rem;
255
+ }
256
+
257
+ .pet-carrier-crate-size-planner .measurements label {
258
+ display: grid;
259
+ grid-template-columns: minmax(0, 1fr) minmax(5rem, 6.25rem) auto;
260
+ gap: 0.55rem;
261
+ align-items: center;
262
+ }
263
+
264
+ .pet-carrier-crate-size-planner .measurements label > span {
265
+ font-size: 0.87rem;
266
+ line-height: 1.2;
267
+ }
268
+
269
+ .pet-carrier-crate-size-planner small {
270
+ display: block;
271
+ margin-top: 0.15rem;
272
+ color: var(--n-muted);
273
+ font-size: 0.72rem;
274
+ line-height: 1.25;
275
+ }
276
+
277
+ .pet-carrier-crate-size-planner input[type="number"] {
278
+ width: 100%;
279
+ box-sizing: border-box;
280
+ border: 1px solid var(--n-line);
281
+ border-radius: 0.65rem;
282
+ padding: 0.62rem 0.65rem;
283
+ color: var(--n-ink);
284
+ background: var(--n-card);
285
+ font: inherit;
286
+ font-variant-numeric: tabular-nums;
287
+ }
288
+
289
+ .pet-carrier-crate-size-planner input:focus-visible,
290
+ .pet-carrier-crate-size-planner button:focus-visible {
291
+ outline: 3px solid var(--n-focus);
292
+ outline-offset: 2px;
293
+ }
294
+
295
+ .pet-carrier-crate-size-planner .measurements b {
296
+ min-width: 1.7rem;
297
+ color: var(--n-muted);
298
+ font-size: 0.78rem;
299
+ }
300
+
301
+ .pet-carrier-crate-size-planner .snub-toggle {
302
+ display: flex;
303
+ gap: 0.65rem;
304
+ align-items: flex-start;
305
+ margin: 0.2rem 0 1.1rem;
306
+ padding: 0.8rem;
307
+ border: 1px solid var(--n-line);
308
+ border-radius: 0.8rem;
309
+ background: var(--n-paper);
310
+ }
311
+
312
+ .pet-carrier-crate-size-planner .snub-toggle input {
313
+ flex: 0 0 auto;
314
+ margin-top: 0.2rem;
315
+ accent-color: var(--n-coral);
316
+ }
317
+
318
+ .pet-carrier-crate-size-planner .snub-toggle strong {
319
+ font-size: 0.84rem;
320
+ }
321
+
322
+ .pet-carrier-crate-size-planner .preset-strip {
323
+ align-items: center;
324
+ padding-top: 1rem;
325
+ border-top: 1px solid var(--n-line);
326
+ }
327
+
328
+ .pet-carrier-crate-size-planner .preset-strip > span {
329
+ width: 100%;
330
+ color: var(--n-muted);
331
+ font-size: 0.72rem;
332
+ font-weight: 800;
333
+ letter-spacing: 0.04em;
334
+ text-transform: uppercase;
335
+ }
336
+
337
+ .pet-carrier-crate-size-planner .preset-strip button {
338
+ min-height: 2.3rem;
339
+ padding: 0.45rem 0.62rem;
340
+ font-size: 0.75rem;
341
+ }
342
+
343
+ .pet-carrier-crate-size-planner .carrier-error {
344
+ min-height: 1.25rem;
345
+ margin: 0.75rem 0 0;
346
+ color: var(--n-error);
347
+ font-size: 0.82rem;
348
+ }
349
+
350
+ .pet-carrier-crate-size-planner .result-title-row {
351
+ display: flex;
352
+ gap: 1rem;
353
+ align-items: flex-start;
354
+ justify-content: space-between;
355
+ }
356
+
357
+ .pet-carrier-crate-size-planner .result-heading > .result-title-row p {
358
+ color: #f19a7e;
359
+ }
360
+
361
+ .pet-carrier-crate-size-planner .result-heading h2 {
362
+ max-width: 18rem;
363
+ margin-top: 0.25rem;
364
+ font-size: clamp(1.8rem, 4vw, 2.65rem);
365
+ letter-spacing: -0.035em;
366
+ line-height: 0.98;
367
+ }
368
+
369
+ .pet-carrier-crate-size-planner .result-status {
370
+ display: inline-block;
371
+ flex: 0 0 auto;
372
+ margin-top: 0.1rem;
373
+ padding: 0.42rem 0.6rem;
374
+ border-radius: 0.55rem;
375
+ color: var(--n-status-text);
376
+ background: var(--n-status);
377
+ font-size: 0.72rem;
378
+ font-weight: 900;
379
+ white-space: nowrap;
380
+ }
381
+
382
+ .pet-carrier-crate-size-planner .result-status[data-tone="adjusted"] {
383
+ color: var(--n-adjusted-text);
384
+ background: var(--n-adjusted);
385
+ }
386
+
387
+ .pet-carrier-crate-size-planner .result-status-detail {
388
+ display: block;
389
+ max-width: 36rem;
390
+ margin-top: 0.75rem;
391
+ color: var(--n-result-muted);
392
+ font-size: 0.84rem;
393
+ line-height: 1.45;
394
+ }
395
+
396
+ .pet-carrier-crate-size-planner .carrier-scene {
397
+ min-height: 0;
398
+ margin: 1.2rem 0 1rem;
399
+ overflow: hidden;
400
+ border-radius: 0.85rem;
401
+ }
402
+
403
+ .pet-carrier-crate-size-planner .carrier-blueprint {
404
+ display: block;
405
+ width: 100%;
406
+ height: auto;
407
+ overflow: hidden;
408
+ }
409
+
410
+ .pet-carrier-crate-size-planner .blueprint-paper {
411
+ fill: var(--n-blueprint);
412
+ }
413
+
414
+ .pet-carrier-crate-size-planner .blueprint-grid {
415
+ stroke: var(--n-grid);
416
+ stroke-width: 1;
417
+ opacity: 0.7;
418
+ }
419
+
420
+ .pet-carrier-crate-size-planner .carrier-outline {
421
+ fill: var(--n-carrier);
422
+ stroke: var(--n-teal);
423
+ stroke-width: 3;
424
+ }
425
+
426
+ .pet-carrier-crate-size-planner .carrier-door {
427
+ fill: none;
428
+ stroke: var(--n-coral);
429
+ stroke-width: 3;
430
+ }
431
+
432
+ .pet-carrier-crate-size-planner .pet-icon {
433
+ display: block;
434
+ fill: var(--n-pet);
435
+ }
436
+
437
+ .pet-carrier-crate-size-planner .measure-line {
438
+ stroke: var(--n-coral);
439
+ stroke-width: 2;
440
+ marker-start: url('#measure-start');
441
+ marker-end: url('#measure-end');
442
+ }
443
+
444
+ .pet-carrier-crate-size-planner .measure-label {
445
+ fill: var(--n-ink);
446
+ font-size: 11px;
447
+ font-weight: 800;
448
+ text-anchor: middle;
449
+ }
450
+
451
+ .pet-carrier-crate-size-planner .blueprint-caption {
452
+ fill: var(--n-muted);
453
+ font-size: 10px;
454
+ letter-spacing: 0.08em;
455
+ text-transform: uppercase;
456
+ }
457
+
458
+ .pet-carrier-crate-size-planner .blueprint-badge {
459
+ fill: var(--n-coral);
460
+ font-size: 17px;
461
+ font-weight: 800;
462
+ }
463
+
464
+ .pet-carrier-crate-size-planner .dimension-ledger {
465
+ display: block;
466
+ min-width: 0;
467
+ padding: 1rem 0 0;
468
+ border-top: 1px solid rgba(216, 239, 232, 0.25);
469
+ }
470
+
471
+ .pet-carrier-crate-size-planner .dimension-ledger > p {
472
+ color: #9bc7ba;
473
+ }
474
+
475
+ .pet-carrier-crate-size-planner .dimension-primary,
476
+ .pet-carrier-crate-size-planner .dimension-context {
477
+ display: grid;
478
+ width: 100%;
479
+ box-sizing: border-box;
480
+ gap: 0.55rem;
481
+ }
482
+
483
+ .pet-carrier-crate-size-planner .dimension-primary {
484
+ grid-template-columns: repeat(3, minmax(0, 1fr));
485
+ margin-top: 0.8rem;
486
+ }
487
+
488
+ .pet-carrier-crate-size-planner .dimension-context {
489
+ grid-template-columns: repeat(2, minmax(0, 1fr));
490
+ margin-top: 0.75rem;
491
+ padding-top: 0.75rem;
492
+ border-top: 1px solid rgba(216, 239, 232, 0.25);
493
+ }
494
+
495
+ .pet-carrier-crate-size-planner .dimension-primary > div,
496
+ .pet-carrier-crate-size-planner .dimension-context > div {
497
+ min-width: 0;
498
+ display: block;
499
+ }
500
+
501
+ .pet-carrier-crate-size-planner .dimension-ledger span,
502
+ .pet-carrier-crate-size-planner .dimension-ledger strong {
503
+ display: block;
504
+ }
505
+
506
+ .pet-carrier-crate-size-planner .dimension-ledger span {
507
+ color: var(--n-result-muted);
508
+ font-size: 0.7rem;
509
+ font-weight: 700;
510
+ letter-spacing: 0.03em;
511
+ text-transform: uppercase;
512
+ }
513
+
514
+ .pet-carrier-crate-size-planner .dimension-primary strong {
515
+ margin-top: 0.18rem;
516
+ color: var(--n-result-text);
517
+ font-size: clamp(1.1rem, 2vw, 1.4rem);
518
+ line-height: 1;
519
+ }
520
+
521
+ .pet-carrier-crate-size-planner .dimension-context strong {
522
+ margin-top: 0.18rem;
523
+ color: var(--n-result-text);
524
+ font-size: 0.92rem;
525
+ }
526
+
527
+ .pet-carrier-crate-size-planner .carrier-checklist h3 {
528
+ margin: 1.25rem 0 0.7rem;
529
+ color: var(--n-result-text);
530
+ font-size: 1rem;
531
+ }
532
+
533
+ .pet-carrier-crate-size-planner .carrier-checklist p {
534
+ display: flex;
535
+ gap: 0.55rem;
536
+ color: var(--n-result-muted);
537
+ font-size: 0.82rem;
538
+ line-height: 1.4;
539
+ }
540
+
541
+ .pet-carrier-crate-size-planner .carrier-checklist p + p {
542
+ margin-top: 0.45rem;
543
+ }
544
+
545
+ .pet-carrier-crate-size-planner .carrier-checklist p span {
546
+ color: #f19a7e;
547
+ font-weight: 900;
548
+ }
549
+
550
+ .pet-carrier-crate-size-planner .carrier-note {
551
+ display: flex;
552
+ gap: 0.8rem;
553
+ margin-top: 1rem;
554
+ padding: 0.95rem 1rem;
555
+ border-left: 4px solid var(--n-coral);
556
+ border-radius: 0 0.75rem 0.75rem 0;
557
+ background: var(--n-coral-soft);
558
+ }
559
+
560
+ .pet-carrier-crate-size-planner .carrier-note strong {
561
+ flex: 0 0 auto;
562
+ color: var(--n-ink);
563
+ }
564
+
565
+ .pet-carrier-crate-size-planner .carrier-note span,
566
+ .pet-carrier-crate-size-planner .carrier-method {
567
+ color: var(--n-muted);
568
+ font-size: 0.82rem;
569
+ line-height: 1.5;
570
+ }
571
+
572
+ .pet-carrier-crate-size-planner .carrier-method {
573
+ margin-top: 0.9rem;
574
+ }
575
+
576
+ .pet-carrier-crate-size-planner .carrier-method strong {
577
+ color: var(--n-ink);
578
+ }
579
+
580
+ @media (max-width: 760px) {
581
+ .pet-carrier-crate-size-planner .carrier-hero,
582
+ .pet-carrier-crate-size-planner .carrier-workbench {
583
+ grid-template-columns: 1fr;
584
+ }
585
+
586
+ .pet-carrier-crate-size-planner .unit-switch {
587
+ justify-content: flex-start;
588
+ }
589
+
590
+ .pet-carrier-crate-size-planner .result-title-row {
591
+ display: block;
592
+ }
593
+
594
+ .pet-carrier-crate-size-planner .result-status {
595
+ margin-top: 0.8rem;
596
+ }
597
+
598
+ .pet-carrier-crate-size-planner .carrier-note {
599
+ display: block;
600
+ }
601
+
602
+ .pet-carrier-crate-size-planner .carrier-note strong {
603
+ display: block;
604
+ margin-bottom: 0.35rem;
605
+ }
606
+
607
+ .pet-carrier-crate-size-planner .dimension-primary {
608
+ grid-template-columns: repeat(2, minmax(0, 1fr));
609
+ }
610
+ }
611
+
612
+ @media (max-width: 430px) {
613
+ .pet-carrier-crate-size-planner {
614
+ margin: 1rem auto;
615
+ padding: 0.8rem;
616
+ border-radius: 1.1rem;
617
+ }
618
+
619
+ .pet-carrier-crate-size-planner .carrier-controls,
620
+ .pet-carrier-crate-size-planner .carrier-result {
621
+ padding: 0.9rem;
622
+ border-radius: 0.9rem;
623
+ }
624
+
625
+ .pet-carrier-crate-size-planner .measurements label {
626
+ grid-template-columns: minmax(0, 1fr) minmax(4.5rem, 5.5rem) auto;
627
+ gap: 0.4rem;
628
+ }
629
+
630
+ .pet-carrier-crate-size-planner .dimension-primary strong {
631
+ font-size: 1rem;
632
+ }
633
+ }
634
+
635
+ @media (prefers-reduced-motion: reduce) {
636
+ .pet-carrier-crate-size-planner button {
637
+ transition: none;
638
+ }
639
+ }
@@ -0,0 +1,14 @@
1
+ ---
2
+ import { SEORenderer } from '@jjlmoya/utils-shared';
3
+ import { petCarrierCrateSizePlanner } from './index';
4
+ import type { KnownLocale } from '../../types';
5
+
6
+ interface Props {
7
+ locale?: KnownLocale;
8
+ }
9
+
10
+ const { locale = 'en' } = Astro.props as Props;
11
+ const content = await petCarrierCrateSizePlanner.i18n[locale]?.();
12
+ ---
13
+
14
+ {content && <SEORenderer content={{ locale, sections: content.seo }} />}
@@ -0,0 +1,31 @@
1
+ import type { PetSpecies, TravelMode, UnitSystem } from './logic';
2
+
3
+ const STORAGE_KEY = 'jjlmoya-pet-carrier-crate-size';
4
+
5
+ export interface CarrierStorageState {
6
+ species: PetSpecies;
7
+ mode: TravelMode;
8
+ unit: UnitSystem;
9
+ noseToTail: number;
10
+ elbowHeight: number;
11
+ shoulderWidth: number;
12
+ standingHeight: number;
13
+ bedding: number;
14
+ weight: number;
15
+ snubNosed: boolean;
16
+ }
17
+
18
+ export function loadCarrierState(): Partial<CarrierStorageState> {
19
+ try {
20
+ const raw = window.localStorage.getItem(STORAGE_KEY);
21
+ return raw ? JSON.parse(raw) as Partial<CarrierStorageState> : {};
22
+ } catch {
23
+ return {};
24
+ }
25
+ }
26
+
27
+ export function saveCarrierState(state: CarrierStorageState): void {
28
+ try {
29
+ window.localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
30
+ } catch {}
31
+ }