@nebutra/tokens 0.1.0 → 0.1.2

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 (57) hide show
  1. package/.turbo/turbo-build.log +8 -0
  2. package/.turbo/turbo-typecheck.log +4 -0
  3. package/AGENTS.md +5 -2
  4. package/CHANGELOG.md +33 -0
  5. package/DESIGN.md +30 -6
  6. package/LICENSE +21 -676
  7. package/README.md +41 -8
  8. package/brands/gsap/brand.json +180 -0
  9. package/brands/linear/brand.json +202 -0
  10. package/brands/notion/brand.json +189 -0
  11. package/brands/raycast/brand.json +167 -0
  12. package/brands/stripe/brand.json +183 -0
  13. package/brands/vanta/brand.json +198 -0
  14. package/brands/vercel/brand.json +207 -0
  15. package/package.json +18 -9
  16. package/recipe.css +114 -0
  17. package/scripts/compile-brand-run.ts +79 -0
  18. package/scripts/compile-brand.mjs +25 -0
  19. package/scripts/emit-skins-run.ts +62 -0
  20. package/scripts/emit-skins.mjs +31 -0
  21. package/scripts/sync-styles.mjs +29 -0
  22. package/skins/README.md +151 -0
  23. package/skins/gsap.css +247 -0
  24. package/skins/linear.css +282 -0
  25. package/skins/notion.css +248 -0
  26. package/skins/raycast.css +272 -0
  27. package/skins/stripe.css +248 -0
  28. package/skins/vanta.css +255 -0
  29. package/skins/vercel.css +283 -0
  30. package/src/brand-package/__tests__/compile-refero.test.ts +427 -0
  31. package/src/brand-package/__tests__/emit-css.test.ts +130 -0
  32. package/src/brand-package/apply-brand.ts +92 -0
  33. package/src/brand-package/compile-helpers.ts +170 -0
  34. package/src/brand-package/compile-refero.ts +69 -0
  35. package/src/brand-package/emit-css.ts +468 -0
  36. package/src/brand-package/hex-to-hsl.ts +100 -0
  37. package/src/brand-package/index.ts +71 -0
  38. package/src/brand-package/infer-recipe.ts +198 -0
  39. package/src/brand-package/normalize.ts +295 -0
  40. package/src/brand-package/presets/context.ts +17 -0
  41. package/src/brand-package/presets/generic.ts +214 -0
  42. package/src/brand-package/presets/gsap.ts +146 -0
  43. package/src/brand-package/presets/index.ts +31 -0
  44. package/src/brand-package/presets/linear.ts +183 -0
  45. package/src/brand-package/presets/notion.ts +208 -0
  46. package/src/brand-package/presets/raycast.ts +138 -0
  47. package/src/brand-package/presets/recipe.ts +61 -0
  48. package/src/brand-package/presets/stripe.ts +207 -0
  49. package/src/brand-package/presets/vanta.ts +220 -0
  50. package/src/brand-package/presets/vercel.ts +187 -0
  51. package/src/brand-package/types.ts +285 -0
  52. package/src/brand-package/use-brand.ts +207 -0
  53. package/src/brand-package/validate.ts +108 -0
  54. package/src/index.ts +31 -1
  55. package/src/theme-provider.tsx +21 -6
  56. package/styles.css +157 -79
  57. package/turbo.json +15 -0
package/styles.css CHANGED
@@ -69,13 +69,10 @@
69
69
  --nebutra-gray-800: #262626;
70
70
  --nebutra-gray-900: #171717;
71
71
  --nebutra-gray-950: #0a0a0a;
72
- --brand-tertiary: #8b5cf6; /** Tertiary accent — data viz, infrastructure tags */
73
- --status-danger: #ef4444;
74
- --status-warning: #f59e0b;
75
- --status-success: #22c55e; /** Tailwind green-500 — VI manual canonical success color (replaces drifted #10b981 emerald). */
76
- --container-text: 896px; /** Reading-focused (hero copy, FAQ) */
77
- --container-content: 1152px; /** Pricing, blog */
78
- --container-wide: 1400px; /** Feature bento, navbar */
72
+ --breakpoint-xs: 26.25rem; /** 420px — narrow-phone grid flip */
73
+ --breakpoint-tight: 67.5rem; /** 1080px — dense dashboard header horizontal */
74
+ --breakpoint-shell: 73.75rem; /** 1180px — three-pane shell threshold */
75
+ --breakpoint-3xl: 112.5rem; /** 1800px — 4K / Studio Display friendliness */
79
76
  --radius-none: 0;
80
77
  --radius-sm: 0.25rem;
81
78
  --radius-md: 0.375rem;
@@ -102,20 +99,43 @@
102
99
  --leading-heading: 1.15;
103
100
  --ring-hairline: inset 0 0 0 1px rgb(255 255 255 / 0.04); /** Etched-glass hairline (light mode value, dark mode override in themes/dark.json) */
104
101
  --overlay-faint: rgb(0 0 0 / 0.06); /** Light-mode faint overlay (6% black) */
105
- --font-sans: "Geist", "Noto Sans SC", "PingFang SC", "Microsoft YaHei", -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
102
+ --font-sans:
103
+ var(--font-geist-sans), "Geist", "Noto Sans SC", "PingFang SC", "Microsoft YaHei",
104
+ -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
106
105
  --font-cn: "Noto Sans SC", "PingFang SC", "Microsoft YaHei", "vivo Sans", sans-serif;
107
- --font-display: "Geist", "Noto Sans SC", sans-serif;
108
- --font-heading: "Geist", "Noto Sans SC", sans-serif;
109
- --font-mono: "Geist Mono", "Fira Code", ui-monospace, Consolas, "Courier New", monospace;
106
+ --font-display: var(--font-geist-sans), "Geist", "Noto Sans SC", sans-serif;
107
+ --font-heading: var(--font-geist-sans), "Geist", "Noto Sans SC", sans-serif;
108
+ --font-mono:
109
+ var(--font-geist-mono), "Geist Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
110
110
  --transition-duration: 200ms; /** Default transition duration — 200ms flow rail. */
111
111
  --transition-easing: ease-out; /** ease-out keyword form (CSS-friendly). Equivalent to cubic-bezier(0,0,0.2,1). */
112
112
  --brand-primary: #0033fe; /** Brand primary — 云毓蓝 */
113
113
  --brand-accent: #0bf1c3; /** Brand accent — 云毓青 */
114
114
  --brand-tertiary: #8b5cf6;
115
- --brand-gradient: linear-gradient(135deg, #0033fe 0%, #0bf1c3 100%); /** Brand gradient — Logo standard 135° */
116
- --brand-gradient-reverse: linear-gradient(135deg, #0bf1c3 0%, #0033fe 100%);
117
- --brand-gradient-vertical: linear-gradient(180deg, #0033fe 0%, #0bf1c3 100%);
118
- --brand-gradient-radial: radial-gradient(circle, #0bf1c3 0%, #0033fe 100%);
115
+ --brand-gradient-start: hsl(
116
+ var(--primary)
117
+ ); /** Legacy alias of --primary (shadcn product blue). Prefer bg-primary / Button default. */
118
+ --brand-gradient-end: hsl(
119
+ var(--primary)
120
+ ); /** Legacy alias of --primary. Prefer hover:bg-primary/90. */
121
+ --brand-gradient: hsl(
122
+ var(--primary)
123
+ ); /** Legacy --brand-gradient. Same color as --primary — not a second palette. App CTAs should use Button / bg-primary. */
124
+ --brand-gradient-reverse: hsl(var(--primary)); /** Legacy alias of --primary. */
125
+ --brand-gradient-vertical: hsl(var(--primary)); /** Legacy alias of --primary. */
126
+ --brand-gradient-radial: hsl(var(--primary)); /** Legacy alias of --primary. */
127
+ --brand-gradient-logo: linear-gradient(
128
+ 135deg,
129
+ #0033fe 0%,
130
+ #00a2e9 50%,
131
+ #0bf1c3 100%
132
+ ); /** Logo/VI gradient (云毓蓝→云毓青). Mid stop #00a2e9 is the OKLab perceptual midpoint (avoids sRGB mid-path soft spots). Brand assets only — not product CTAs. */
133
+ --brand-gradient-logo-reverse: linear-gradient(
134
+ 135deg,
135
+ #0bf1c3 0%,
136
+ #00a2e9 50%,
137
+ #0033fe 100%
138
+ ); /** Logo/VI reverse gradient with same OKLab mid stop. */
119
139
  --status-danger: #ef4444;
120
140
  --status-warning: #f59e0b;
121
141
  --status-success: #22c55e;
@@ -259,6 +279,8 @@
259
279
  --gradient-brand-reverse: var(--brand-gradient-reverse);
260
280
  --gradient-brand-radial: var(--brand-gradient-radial);
261
281
  --gradient-brand-glow: var(--brand-gradient-radial);
282
+ --gradient-brand-logo: var(--brand-gradient-logo);
283
+ --gradient-brand-logo-reverse: var(--brand-gradient-logo-reverse);
262
284
  --gradient-section: var(--brand-gradient-vertical);
263
285
  --gradient-glow: var(--brand-gradient-radial);
264
286
  }
@@ -342,13 +364,10 @@
342
364
  --nebutra-gray-800: #262626;
343
365
  --nebutra-gray-900: #171717;
344
366
  --nebutra-gray-950: #0a0a0a;
345
- --brand-tertiary: #8b5cf6; /** Tertiary accent — data viz, infrastructure tags */
346
- --status-danger: #ef4444;
347
- --status-warning: #f59e0b;
348
- --status-success: #22c55e; /** Tailwind green-500 — VI manual canonical success color (replaces drifted #10b981 emerald). */
349
- --container-text: 896px; /** Reading-focused (hero copy, FAQ) */
350
- --container-content: 1152px; /** Pricing, blog */
351
- --container-wide: 1400px; /** Feature bento, navbar */
367
+ --breakpoint-xs: 26.25rem; /** 420px — narrow-phone grid flip */
368
+ --breakpoint-tight: 67.5rem; /** 1080px — dense dashboard header horizontal */
369
+ --breakpoint-shell: 73.75rem; /** 1180px — three-pane shell threshold */
370
+ --breakpoint-3xl: 112.5rem; /** 1800px — 4K / Studio Display friendliness */
352
371
  --radius-none: 0;
353
372
  --radius-sm: 0.25rem;
354
373
  --radius-md: 0.375rem;
@@ -374,21 +393,46 @@
374
393
  --leading-display: 1.05;
375
394
  --leading-heading: 1.15;
376
395
  --ring-hairline: inset 0 0 0 1px rgb(255 255 255 / 0.06); /** Hairline override — more visible on dark background */
377
- --overlay-faint: rgb(255 255 255 / 0.06); /** Dark-mode faint overlay (6% white) — inverted from light */
378
- --font-sans: "Geist", "Noto Sans SC", "PingFang SC", "Microsoft YaHei", -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
396
+ --overlay-faint: rgb(
397
+ 255 255 255 / 0.06
398
+ ); /** Dark-mode faint overlay (6% white) — inverted from light */
399
+ --font-sans:
400
+ var(--font-geist-sans), "Geist", "Noto Sans SC", "PingFang SC", "Microsoft YaHei",
401
+ -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
379
402
  --font-cn: "Noto Sans SC", "PingFang SC", "Microsoft YaHei", "vivo Sans", sans-serif;
380
- --font-display: "Geist", "Noto Sans SC", sans-serif;
381
- --font-heading: "Geist", "Noto Sans SC", sans-serif;
382
- --font-mono: "Geist Mono", "Fira Code", ui-monospace, Consolas, "Courier New", monospace;
403
+ --font-display: var(--font-geist-sans), "Geist", "Noto Sans SC", sans-serif;
404
+ --font-heading: var(--font-geist-sans), "Geist", "Noto Sans SC", sans-serif;
405
+ --font-mono:
406
+ var(--font-geist-mono), "Geist Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
383
407
  --transition-duration: 200ms; /** Default transition duration — 200ms flow rail. */
384
408
  --transition-easing: ease-out; /** ease-out keyword form (CSS-friendly). Equivalent to cubic-bezier(0,0,0.2,1). */
385
409
  --brand-primary: #5c7cfa; /** Dark-mode brand primary — visible against dark surfaces (uses --blue-9 = nebutra-blue-400). */
386
410
  --brand-accent: #1af7c8; /** Dark-mode brand accent — visible against dark surfaces (uses --cyan-9 = nebutra-cyan-400). */
387
411
  --brand-tertiary: #8b5cf6;
388
- --brand-gradient: linear-gradient(135deg, #0033fe 0%, #0bf1c3 100%); /** Brand gradient — Logo standard 135° */
389
- --brand-gradient-reverse: linear-gradient(135deg, #0bf1c3 0%, #0033fe 100%);
390
- --brand-gradient-vertical: linear-gradient(180deg, #0033fe 0%, #0bf1c3 100%);
391
- --brand-gradient-radial: radial-gradient(circle, #0bf1c3 0%, #0033fe 100%);
412
+ --brand-gradient-start: hsl(
413
+ var(--primary)
414
+ ); /** Legacy alias of --primary (shadcn product blue). Prefer bg-primary / Button default. */
415
+ --brand-gradient-end: hsl(
416
+ var(--primary)
417
+ ); /** Legacy alias of --primary. Prefer hover:bg-primary/90. */
418
+ --brand-gradient: hsl(
419
+ var(--primary)
420
+ ); /** Legacy --brand-gradient. Same color as --primary — not a second palette. App CTAs should use Button / bg-primary. */
421
+ --brand-gradient-reverse: hsl(var(--primary)); /** Legacy alias of --primary. */
422
+ --brand-gradient-vertical: hsl(var(--primary)); /** Legacy alias of --primary. */
423
+ --brand-gradient-radial: hsl(var(--primary)); /** Legacy alias of --primary. */
424
+ --brand-gradient-logo: linear-gradient(
425
+ 135deg,
426
+ #0033fe 0%,
427
+ #00a2e9 50%,
428
+ #0bf1c3 100%
429
+ ); /** Logo/VI gradient (云毓蓝→云毓青). Mid stop #00a2e9 is the OKLab perceptual midpoint (avoids sRGB mid-path soft spots). Brand assets only — not product CTAs. */
430
+ --brand-gradient-logo-reverse: linear-gradient(
431
+ 135deg,
432
+ #0bf1c3 0%,
433
+ #00a2e9 50%,
434
+ #0033fe 100%
435
+ ); /** Logo/VI reverse gradient with same OKLab mid stop. */
392
436
  --status-danger: #ef4444;
393
437
  --status-warning: #f59e0b;
394
438
  --status-success: #22c55e;
@@ -438,39 +482,39 @@
438
482
  --cyan-10: #4dfcd4;
439
483
  --cyan-11: #80ffe0;
440
484
  --cyan-12: #b3ffec;
441
- --background: 222 47% 4%;
442
- --foreground: 210 40% 98%;
443
- --card: 222 47% 7%;
444
- --card-foreground: 210 40% 98%;
445
- --popover: 222 47% 7%;
446
- --popover-foreground: 210 40% 98%;
485
+ --background: 222 14% 9%;
486
+ --foreground: 210 18% 96%;
487
+ --card: 222 12% 13%;
488
+ --card-foreground: 210 18% 96%;
489
+ --popover: 222 12% 13%;
490
+ --popover-foreground: 210 18% 96%;
447
491
  --primary: 228 90% 72%;
448
- --primary-foreground: 222 47% 4%;
449
- --secondary: 217 33% 17%;
450
- --secondary-foreground: 210 40% 98%;
451
- --muted: 217 33% 17%;
452
- --muted-foreground: 215 20% 65%;
453
- --accent: 228 100% 12%;
454
- --accent-foreground: 228 95% 67%;
455
- --destructive: 0 63% 31%;
456
- --destructive-foreground: 210 40% 98%;
457
- --success: 142 76% 36%;
458
- --success-foreground: 222 47% 4%;
459
- --warning: 38 80% 45%;
460
- --warning-foreground: 222 47% 4%;
461
- --info: 228 95% 67%;
462
- --info-foreground: 222 47% 4%;
463
- --border: 0 0% 14.9%;
464
- --input: 0 0% 14.9%;
492
+ --primary-foreground: 222 14% 9%;
493
+ --secondary: 222 10% 18%;
494
+ --secondary-foreground: 210 18% 96%;
495
+ --muted: 222 10% 17%;
496
+ --muted-foreground: 220 8% 65%;
497
+ --accent: 228 38% 22%;
498
+ --accent-foreground: 228 95% 80%;
499
+ --destructive: 0 63% 38%;
500
+ --destructive-foreground: 210 18% 96%;
501
+ --success: 142 60% 42%;
502
+ --success-foreground: 222 14% 9%;
503
+ --warning: 38 80% 52%;
504
+ --warning-foreground: 222 14% 9%;
505
+ --info: 228 95% 72%;
506
+ --info-foreground: 222 14% 9%;
507
+ --border: 220 8% 22%;
508
+ --input: 220 8% 22%;
465
509
  --ring: 0 0% 88%; /** Focus ring (dark mode) — neutral light gray. Mirrors light-mode neutral foreground choice. */
466
- --sidebar: 222 47% 6%;
467
- --sidebar-foreground: 210 40% 96%;
468
- --sidebar-primary: 228 95% 67%;
469
- --sidebar-primary-foreground: 222 47% 4%;
470
- --sidebar-accent: 228 100% 12%;
471
- --sidebar-accent-foreground: 228 95% 67%;
472
- --sidebar-border: 0 0% 14.9%;
473
- --sidebar-ring: 228 95% 67%;
510
+ --sidebar: 222 16% 8%;
511
+ --sidebar-foreground: 210 18% 94%;
512
+ --sidebar-primary: 228 95% 72%;
513
+ --sidebar-primary-foreground: 222 14% 9%;
514
+ --sidebar-accent: 228 38% 22%;
515
+ --sidebar-accent-foreground: 228 95% 80%;
516
+ --sidebar-border: 220 8% 18%;
517
+ --sidebar-ring: 228 95% 72%;
474
518
  --chart-1: 228 95% 67%;
475
519
  --chart-2: 168 96% 54%;
476
520
  --chart-3: 228 100% 82%;
@@ -526,6 +570,8 @@
526
570
  --gradient-brand-reverse: var(--brand-gradient-reverse);
527
571
  --gradient-brand-radial: var(--brand-gradient-radial);
528
572
  --gradient-brand-glow: var(--brand-gradient-radial);
573
+ --gradient-brand-logo: var(--brand-gradient-logo);
574
+ --gradient-brand-logo-reverse: var(--brand-gradient-logo-reverse);
529
575
  --gradient-section: var(--brand-gradient-vertical);
530
576
  --gradient-glow: var(--brand-gradient-radial);
531
577
  }
@@ -743,11 +789,14 @@
743
789
  --duration-cinematic: 500ms;
744
790
 
745
791
  /* Font family — Geist primary stack with CJK fallbacks */
746
- --font-sans: "Geist", "Noto Sans SC", "PingFang SC", "Microsoft YaHei", -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
792
+ --font-sans:
793
+ var(--font-geist-sans), "Geist", "Noto Sans SC", "PingFang SC", "Microsoft YaHei",
794
+ -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
747
795
  --font-cn: "Noto Sans SC", "PingFang SC", "Microsoft YaHei", "vivo Sans", sans-serif;
748
- --font-display: "Geist", "Noto Sans SC", sans-serif;
749
- --font-heading: "Geist", "Noto Sans SC", sans-serif;
750
- --font-mono: "Geist Mono", "Fira Code", ui-monospace, Consolas, "Courier New", monospace;
796
+ --font-display: var(--font-geist-sans), "Geist", "Noto Sans SC", sans-serif;
797
+ --font-heading: var(--font-geist-sans), "Geist", "Noto Sans SC", sans-serif;
798
+ --font-mono:
799
+ var(--font-geist-mono), "Geist Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
751
800
  }
752
801
 
753
802
  /* ============================================
@@ -755,15 +804,39 @@
755
804
  This file is concatenated into styles.generated.css after the @theme block.
756
805
  ============================================ */
757
806
 
758
- *,
759
- *::before,
760
- *::after {
761
- /* Token-aware default border — resolves via cascade:
762
- :root → light (hsl 240 5.9% 90% ≈ #e4e4e7)
763
- .dark → dark (hsl 0 0% 14.9% ≈ #262626)
764
- Previously hardcoded to the light value, which leaked a near-white border
765
- into every element in dark mode regardless of component. See ADR 2026-05-11. */
766
- border-color: hsl(var(--border));
807
+ /* ─── Edge tokens (2026-05 perf governance) ─────────────────
808
+ Pre-computed alpha values for borders / hairlines / outer glows.
809
+ Replaces ~70 runtime `color-mix(foreground, transparent N%)` calls per
810
+ page that re-evaluated on every paint. Mode-aware via CSS cascade.
811
+ Lives in static/ so it survives Style Dictionary rebuilds.
812
+ ─────────────────────────────────────────────────────────── */
813
+ :root {
814
+ --edge-faint: rgb(0 0 0 / 0.04);
815
+ --edge-soft: rgb(0 0 0 / 0.07);
816
+ --edge-medium: rgb(0 0 0 / 0.12);
817
+ --halo-faint: rgb(0 0 0 / 0.03);
818
+ }
819
+ .dark {
820
+ --edge-faint: rgb(255 255 255 / 0.04);
821
+ --edge-soft: rgb(255 255 255 / 0.06);
822
+ --edge-medium: rgb(255 255 255 / 0.1);
823
+ --halo-faint: rgb(255 255 255 / 0.03);
824
+ }
825
+
826
+ @layer base {
827
+ *,
828
+ *::before,
829
+ *::after {
830
+ /* Token-aware default border — resolves via cascade:
831
+ :root → light (hsl 240 5.9% 90% ≈ #e4e4e7)
832
+ .dark → dark (hsl 0 0% 14.9% ≈ #262626)
833
+ Previously hardcoded to the light value, which leaked a near-white border
834
+ into every element in dark mode regardless of component. See ADR 2026-05-11.
835
+
836
+ Keep this in Tailwind's base layer so component utilities such as
837
+ border-r-transparent can override it. */
838
+ border-color: hsl(var(--border));
839
+ }
767
840
  }
768
841
 
769
842
  body {
@@ -779,7 +852,9 @@ html {
779
852
  scroll-behavior: smooth;
780
853
  }
781
854
 
782
- :focus-visible {
855
+ :focus-visible:not(
856
+ :where(input, textarea, select, [cmdk-input], [role="textbox"], [contenteditable="true"])
857
+ ) {
783
858
  outline: 2px solid hsl(var(--ring) / 0.5);
784
859
  outline-offset: 2px;
785
860
  border-radius: inherit;
@@ -1379,10 +1454,13 @@ html {
1379
1454
  }
1380
1455
 
1381
1456
  /* ─── Reduced Motion ─────────────────────────────────────── */
1457
+ /* Honors OS preference EXCEPT for functional progress indicators
1458
+ * (spinners / pulses / pings). Per WCAG 2.3.3, those are excluded
1459
+ * from the "animation from interactions" reduction requirement. */
1382
1460
  @media (prefers-reduced-motion: reduce) {
1383
- *,
1384
- *::before,
1385
- *::after {
1461
+ *:not(.animate-spin):not(.animate-pulse):not(.animate-ping),
1462
+ *:not(.animate-spin):not(.animate-pulse):not(.animate-ping)::before,
1463
+ *:not(.animate-spin):not(.animate-pulse):not(.animate-ping)::after {
1386
1464
  animation-duration: 0.01ms;
1387
1465
  animation-iteration-count: 1;
1388
1466
  transition-duration: 0.01ms;
package/turbo.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "extends": ["//"],
3
+ "tasks": {
4
+ "build": {
5
+ "dependsOn": ["@nebutra/design-tokens#build"],
6
+ "inputs": [
7
+ "$TURBO_EXTENDS$",
8
+ "../design-tokens/build/css/styles.generated.css",
9
+ "brands/**",
10
+ "scripts/**"
11
+ ],
12
+ "outputs": ["styles.css", "skins/**"]
13
+ }
14
+ }
15
+ }