@cahyo-dimas/freeday 1.53.0 → 2.0.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 (145) hide show
  1. package/CHANGELOG.md +657 -535
  2. package/COMPONENTS.md +153 -135
  3. package/README.id.md +46 -45
  4. package/README.md +46 -43
  5. package/USAGE.md +39 -35
  6. package/adapters/blazor/FdyAppShell.razor +50 -0
  7. package/adapters/blazor/FdyAppShell.razor.cs +114 -0
  8. package/adapters/blazor/FdyCfl.razor.cs +1 -1
  9. package/adapters/blazor/FdyChart.razor +1 -1
  10. package/adapters/blazor/FdyChart.razor.cs +3 -3
  11. package/adapters/blazor/FdyChartSeries.cs +1 -1
  12. package/adapters/blazor/FdyCombo.razor.cs +2 -2
  13. package/adapters/blazor/FdyModal.razor +1 -1
  14. package/adapters/blazor/FdyTable.razor.cs +7 -7
  15. package/adapters/blazor/FdyTableFilter.razor +1 -1
  16. package/adapters/blazor/FdyTableFooter.razor +1 -1
  17. package/adapters/blazor/FdyTableFooter.razor.cs +4 -4
  18. package/adapters/blazor/Freeday.Blazor.csproj +1 -1
  19. package/adapters/blazor/FreedayComponentBase.cs +1 -1
  20. package/adapters/blazor/TableModel.cs +3 -3
  21. package/adapters/blazor/TableTypes.cs +3 -3
  22. package/adapters/blazor/freeday-blazor.js +5 -5
  23. package/adapters/core/app-shell.d.ts +30 -0
  24. package/adapters/core/app-shell.js +134 -0
  25. package/adapters/core/table-model.d.ts +2 -2
  26. package/adapters/core/table-model.js +8 -8
  27. package/adapters/react/components/FdyAppShell.tsx +159 -0
  28. package/adapters/react/components/FdyAutocomplete.tsx +1 -1
  29. package/adapters/react/components/FdyCascade.tsx +1 -1
  30. package/adapters/react/components/FdyCfl.tsx +11 -11
  31. package/adapters/react/components/FdyDateRange.tsx +2 -2
  32. package/adapters/react/components/FdyDatepicker.tsx +5 -5
  33. package/adapters/react/components/FdyDrawer.tsx +1 -1
  34. package/adapters/react/components/FdyModal.tsx +2 -2
  35. package/adapters/react/components/FdyTable.tsx +7 -7
  36. package/adapters/react/components/FdyTableFilter.tsx +1 -1
  37. package/adapters/react/components/FdyTableFooter.tsx +5 -5
  38. package/adapters/react/index.d.ts +2 -1
  39. package/adapters/react/index.js +1 -0
  40. package/adapters/react/useFreeday.js +3 -3
  41. package/adapters/vue/components/FdyAppShell.vue +160 -0
  42. package/adapters/vue/components/FdyAutocomplete.vue +1 -1
  43. package/adapters/vue/components/FdyCascade.vue +1 -1
  44. package/adapters/vue/components/FdyCfl.vue +8 -8
  45. package/adapters/vue/components/FdyChart.vue +2 -2
  46. package/adapters/vue/components/FdyCombo.vue +1 -1
  47. package/adapters/vue/components/FdyDateRange.vue +2 -2
  48. package/adapters/vue/components/FdyDatepicker.vue +5 -5
  49. package/adapters/vue/components/FdyDrawer.vue +3 -3
  50. package/adapters/vue/components/FdyModal.vue +3 -3
  51. package/adapters/vue/components/FdyTable.vue +7 -7
  52. package/adapters/vue/components/FdyTableFilter.vue +1 -1
  53. package/adapters/vue/components/FdyTableFooter.vue +5 -5
  54. package/adapters/vue/index.d.ts +3 -2
  55. package/adapters/vue/index.js +1 -0
  56. package/adapters/vue/useFreeday.js +3 -3
  57. package/dist/asset.d.ts +3 -3
  58. package/dist/freeday-app-shell.js +49 -9
  59. package/dist/freeday-autocomplete.js +2 -2
  60. package/dist/freeday-breakpoint.js +1 -1
  61. package/dist/freeday-carousel.js +5 -5
  62. package/dist/freeday-cascade.js +7 -7
  63. package/dist/freeday-cfl.js +9 -9
  64. package/dist/freeday-chart.js +11 -11
  65. package/dist/freeday-chip.js +3 -3
  66. package/dist/freeday-datepicker.js +11 -7
  67. package/dist/freeday-datetime.js +5 -5
  68. package/dist/freeday-drawer.js +1 -1
  69. package/dist/freeday-form.js +14 -14
  70. package/dist/freeday-mask.js +6 -6
  71. package/dist/freeday-menu.js +1 -1
  72. package/dist/freeday-number.js +3 -3
  73. package/dist/freeday-popover.js +3 -3
  74. package/dist/freeday-rating.js +2 -2
  75. package/dist/freeday-select.js +5 -5
  76. package/dist/freeday-slider.js +2 -2
  77. package/dist/freeday-stepper.js +7 -7
  78. package/dist/freeday-table.js +15 -15
  79. package/dist/freeday-tabs.js +1 -1
  80. package/dist/freeday-timepicker.js +2 -2
  81. package/dist/freeday-toast.js +8 -8
  82. package/dist/freeday-tree.js +3 -3
  83. package/dist/freeday-upload.js +25 -25
  84. package/dist/freeday.bundle.css +176 -176
  85. package/dist/freeday.css +170 -170
  86. package/dist/freeday.d.ts +2 -2
  87. package/dist/freeday.js +202 -158
  88. package/dist/freeday.tokens.css +5 -5
  89. package/docs/agent-onboarding.md +42 -42
  90. package/docs/getting-started.md +35 -35
  91. package/docs/integrations.md +18 -18
  92. package/docs/reference-screen.html +14 -14
  93. package/package.json +5 -4
  94. package/src/base.css +6 -6
  95. package/src/components/accordion.css +4 -4
  96. package/src/components/alert.css +1 -1
  97. package/src/components/app-shell.css +10 -10
  98. package/src/components/appbar.css +3 -3
  99. package/src/components/autocomplete.css +1 -1
  100. package/src/components/avatar.css +3 -3
  101. package/src/components/badge.css +5 -5
  102. package/src/components/breadcrumb.css +1 -1
  103. package/src/components/breakpoints.css +4 -4
  104. package/src/components/button.css +13 -13
  105. package/src/components/card.css +8 -8
  106. package/src/components/carousel.css +2 -2
  107. package/src/components/cascade.css +2 -2
  108. package/src/components/cfl.css +4 -4
  109. package/src/components/chart.css +3 -3
  110. package/src/components/chip.css +2 -2
  111. package/src/components/combo.css +3 -3
  112. package/src/components/composition.css +7 -7
  113. package/src/components/datepicker.css +6 -6
  114. package/src/components/datetimepicker.css +1 -1
  115. package/src/components/description-list.css +1 -1
  116. package/src/components/divider.css +1 -1
  117. package/src/components/drawer.css +1 -1
  118. package/src/components/file-upload.css +5 -5
  119. package/src/components/filterbar.css +7 -7
  120. package/src/components/form-grid.css +2 -2
  121. package/src/components/input-group.css +6 -6
  122. package/src/components/input.css +6 -6
  123. package/src/components/kbd.css +1 -1
  124. package/src/components/list.css +6 -6
  125. package/src/components/menu.css +3 -3
  126. package/src/components/modal.css +1 -1
  127. package/src/components/pagination.css +1 -1
  128. package/src/components/progress.css +1 -1
  129. package/src/components/rating.css +2 -2
  130. package/src/components/selection.css +4 -4
  131. package/src/components/skeleton.css +1 -1
  132. package/src/components/slider.css +1 -1
  133. package/src/components/spinner.css +1 -1
  134. package/src/components/states.css +1 -1
  135. package/src/components/stepper.css +2 -2
  136. package/src/components/table.css +15 -15
  137. package/src/components/tabs.css +3 -3
  138. package/src/components/timeline.css +1 -1
  139. package/src/components/timepicker.css +1 -1
  140. package/src/components/toast.css +3 -3
  141. package/src/components/tooltip.css +1 -1
  142. package/src/components/tree.css +2 -2
  143. package/tokens/breakpoints.d.ts +2 -2
  144. package/tokens/breakpoints.mjs +3 -3
  145. package/tokens/tokens.json +1 -1
@@ -1,11 +1,11 @@
1
- /* Freeday — Combobox (custom styled select, WAI-ARIA APG select-only) */
1
+ /* Freeday. Combobox (custom styled select, WAI-ARIA APG select-only) */
2
2
  .fdy-combo{position:relative;display:block;max-width:22rem;}
3
3
  .fdy-combo__button{display:inline-flex;align-items:center;justify-content:space-between;gap:var(--space-2);width:100%;height:var(--control-h);padding:0 var(--space-3);text-align:left;cursor:pointer;font-family:var(--font-body);font-size:var(--text-sm);color:var(--color-text);background:var(--color-surface);border:1.5px solid var(--color-control-border);border-radius:var(--radius-md);box-shadow:inset 0 1px 2px rgba(16,14,30,.06);transition:border-color var(--dur-fast) var(--ease-standard),box-shadow var(--dur-fast) var(--ease-standard);}
4
4
  .fdy-combo__button:hover{border-color:var(--color-text-muted);}
5
5
  .fdy-combo__button:focus-visible{outline:none;border-color:var(--color-primary);box-shadow:inset 0 1px 2px rgba(16,14,30,.04),0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
6
6
  .fdy-combo__button.is-open{border-color:var(--color-primary);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
7
7
  .fdy-combo__button:disabled,.fdy-combo__button[aria-disabled="true"]{opacity:.5;cursor:not-allowed;}
8
- /* Read-only: stays focusable & shows its value at full contrast but can't open — muted surface,
8
+ /* Read-only: stays focusable & shows its value at full contrast but can't open, muted surface,
9
9
  softened border, default cursor, no focus-ring escalation (matches input[readonly], #39). */
10
10
  .fdy-combo__button[aria-readonly="true"]{background:var(--color-surface-2);border-color:var(--color-border);cursor:default;}
11
11
  .fdy-combo__button[aria-readonly="true"]:hover{border-color:var(--color-border);}
@@ -29,7 +29,7 @@
29
29
  .fdy-combo__option.is-highlighted[aria-selected="true"]{box-shadow:inset 0 0 0 2px color-mix(in srgb,var(--color-primary) 55%,transparent);}
30
30
  /* The tick is CSS, not content (#004 §2). Inside role="option" it joined the option's ACCESSIBLE
31
31
  NAME, so the selected option announced "✓August" while every other announced "August": the state
32
- was read twice — once as aria-selected, once as decoration — and a test asking for an option by
32
+ was read twice, once as aria-selected, once as decoration, and a test asking for an option by
33
33
  its own label stopped matching the one that was selected. The `/ ""` is alt text: the glyph is
34
34
  painted, the accessibility tree gets nothing. Same reasoning as .fdy-label--required (1.29.0).
35
35
  The empty box still reserves its width, so labels stay aligned whether selected or not. */
@@ -1,8 +1,8 @@
1
- /* Freeday — Page composition & type roles.
1
+ /* Freeday. Page composition & type roles.
2
2
  *
3
3
  * The kit stops at the component; these primitives encode *how a page is assembled* so screens
4
4
  * built by different people cohere. They carry the usage doctrine (see USAGE.md) in markup: a
5
- * named type role gets used, a doc gets skimmed. Everything here is token-driven layout — no new
5
+ * named type role gets used, a doc gets skimmed. Everything here is token-driven layout, no new
6
6
  * colours, no new shadows. Pair with .fdy-app (the shell) and the components inside.
7
7
  */
8
8
 
@@ -11,7 +11,7 @@
11
11
  * page title (the mistake that collapses three levels into one grey mush). */
12
12
  /* --color-text-muted, not -subtle: an eyebrow is a small label naming the thing below it, i.e.
13
13
  prose. -subtle is gated at 3.0 on purpose (placeholders, separators, decorative glyphs) and
14
- measures 4.41 on the page ground — axe reports it as a serious color-contrast failure. Same
14
+ measures 4.41 on the page ground, axe reports it as a serious color-contrast failure. Same
15
15
  argument, same ink, same 4.41 as .fdy-stat__label in 1.30.0. */
16
16
  .fdy-eyebrow{font-family:var(--font-body);font-size:var(--text-xs);font-weight:var(--weight-semibold);letter-spacing:var(--tracking-wide);text-transform:uppercase;color:var(--color-text-muted);margin:0;}
17
17
  .fdy-title-page{font-family:var(--font-display);font-size:var(--text-3xl);font-weight:var(--weight-bold);letter-spacing:var(--tracking-tighter);line-height:var(--leading-tight);color:var(--color-text);margin:0;text-wrap:balance;}
@@ -45,19 +45,19 @@
45
45
  .fdy-stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(11rem,1fr));gap:var(--space-5);}
46
46
  .fdy-stat{display:flex;flex-direction:column;gap:var(--space-1);min-width:0;}
47
47
  /* --color-text-muted, not -subtle: this is a LABEL, i.e. text, and -subtle is deliberately gated at
48
- 3.0 (placeholders, dividers, decorative glyphs). On surface-2 -subtle measures 4.41 — under AA
48
+ 3.0 (placeholders, dividers, decorative glyphs). On surface-2 -subtle measures 4.41, under AA
49
49
  for text, and a stat label is often the only thing naming the number above it. */
50
50
  .fdy-stat__label{font-size:var(--text-xs);font-weight:var(--weight-semibold);letter-spacing:var(--tracking-wide);text-transform:uppercase;color:var(--color-text-muted);}
51
51
  .fdy-stat__value{font-family:var(--font-display);font-size:var(--text-3xl);font-weight:var(--weight-bold);letter-spacing:var(--tracking-tighter);line-height:1;color:var(--color-text);font-variant-numeric:tabular-nums;}
52
52
  .fdy-stat__value small{font-size:var(--text-lg);font-weight:var(--weight-semibold);color:var(--color-text-muted);}
53
53
  .fdy-stat__meta{font-size:var(--text-sm);color:var(--color-text-muted);display:flex;align-items:center;gap:var(--space-2);}
54
- /* A stat value is display type sized for a COUNT — "1,284" — and a back office puts money in it.
54
+ /* A stat value is display type sized for a COUNT, "1,284", and a back office puts money in it.
55
55
  "IDR 300,000.00" needs 224px at --text-3xl and the grid's own track is 11rem, so it wrapped
56
56
  between the currency and the number: two lines of 31px where the design says one. Measured, not
57
57
  guessed (#020).
58
58
  The value shrinks only when its own column is too narrow to hold it, so a wide dashboard keeps
59
59
  exactly the type it has today. Scoped to a stat INSIDE .fdy-stats, where the width comes from the
60
- grid track — `container-type:inline-size` on a standalone .fdy-stat would stop it sizing to its
60
+ grid track, `container-type:inline-size` on a standalone .fdy-stat would stop it sizing to its
61
61
  own content. The unconditional rule above stays as the fallback wherever @container is missing. */
62
62
  @supports (container-type:inline-size){
63
63
  .fdy-stats>.fdy-stat{container-type:inline-size;}
@@ -70,7 +70,7 @@
70
70
  /* Icon box (#001 §4). The kit ships no icons on purpose, but every icon slot it DOES ship
71
71
  (.fdy-btn__icon, .fdy-nav__icon, .fdy-state__icon) assumes something sensible inside, so a
72
72
  standalone icon had no contract and every app invented one. 1em keeps the glyph matched to the
73
- text beside it at every scale — bring your own paths, use our box. Works on the <svg> itself or
73
+ text beside it at every scale, bring your own paths, use our box. Works on the <svg> itself or
74
74
  on a wrapper around it. */
75
75
  .fdy-icon{display:inline-block;width:1em;height:1em;flex:none;vertical-align:-0.125em;}
76
76
  .fdy-icon>svg{display:block;width:100%;height:100%;}
@@ -1,10 +1,10 @@
1
- /* Freeday — Date picker (input-styled trigger + calendar popover).
1
+ /* Freeday. Date picker (input-styled trigger + calendar popover).
2
2
  * Built by freeday-datepicker.js from an empty [data-fdy-datepicker] wrapper. */
3
3
  .fdy-datepicker{position:relative;display:inline-block;width:100%;max-width:14rem;}
4
4
  /* Inside a labelled field, a picker fills the field like every other control.
5
5
  The 14rem/11rem caps above suit a picker standing on its own; in a form grid
6
- they leave the date box visibly short of the combo beside it — 224px against
7
- 319px in a two-column layout — and the row reads as ragged. The field's own
6
+ they leave the date box visibly short of the combo beside it. 224px against
7
+ 319px in a two-column layout, and the row reads as ragged. The field's own
8
8
  max-width is what should govern there. */
9
9
  .fdy-field>.fdy-datepicker,.fdy-field>.fdy-timepicker,.fdy-field>.fdy-daterange{max-width:none;}
10
10
  .fdy-datepicker__trigger{display:inline-flex;align-items:center;gap:var(--space-2);width:100%;height:var(--control-h);padding:0 var(--space-3);text-align:left;cursor:pointer;font-family:var(--font-body);font-size:var(--text-sm);color:var(--color-text);background:var(--color-surface);border:1.5px solid var(--color-control-border);border-radius:var(--radius-md);box-shadow:inset 0 1px 2px rgba(16,14,30,.06);transition:border-color var(--dur-fast) var(--ease-standard),box-shadow var(--dur-fast) var(--ease-standard);}
@@ -50,12 +50,12 @@
50
50
  .fdy-cal__nav:hover{background:var(--color-surface-2);color:var(--color-text);}
51
51
  .fdy-cal__nav:focus-visible{outline:none;box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
52
52
  .fdy-cal__grid{display:grid;grid-template-columns:repeat(7,2rem);gap:2px;}
53
- /* Month grid — 3×4 over the same track width as the 7-column day grid, so the panel does not resize
53
+ /* Month grid. 3×4 over the same track width as the 7-column day grid, so the panel does not resize
54
54
  when you drill and the pointer does not have to travel.
55
55
 
56
56
  AFTER `.fdy-cal__grid`, and that is the whole rule: the element carries both classes, they weigh
57
57
  the same, so source order decides. Declared earlier, the modifier lost to the base it modifies and
58
- twelve months rendered seven across — the layout the comment above described, never once applied. */
58
+ twelve months rendered seven across, the layout the comment above described, never once applied. */
59
59
  .fdy-cal__grid--months,.fdy-cal__grid--years{grid-template-columns:repeat(3,1fr);gap:var(--space-1);}
60
60
  .fdy-cal__dow{display:flex;align-items:center;justify-content:center;height:1.75rem;font-size:var(--text-xs);font-weight:var(--weight-semibold);color:var(--color-text-muted);text-transform:capitalize;}
61
61
  .fdy-cal__day{display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;border:0;border-radius:var(--radius-sm);background:transparent;color:var(--color-text);font-size:var(--text-sm);font-variant-numeric:tabular-nums;cursor:pointer;}
@@ -73,7 +73,7 @@
73
73
  /* Responsive: each picker may shrink (min-width:0) & wrap so the range never overflows a
74
74
  narrow row/phone; inline-flex still hugs to ~23rem at wider widths (look unchanged). */
75
75
  .fdy-daterange{display:inline-flex;flex-wrap:wrap;align-items:center;gap:var(--space-2);}
76
- /* Each picker shrinks from an 11rem basis but not below ~7rem — enough for "dd/mm/yyyy" + the icon,
76
+ /* Each picker shrinks from an 11rem basis but not below ~7rem, enough for "dd/mm/yyyy" + the icon,
77
77
  so a squeezed range compresses gracefully (and wraps) instead of clipping the date. */
78
78
  .fdy-daterange .fdy-datepicker{flex:0 1 11rem;min-width:7rem;}
79
79
  .fdy-daterange__sep{flex:none;color:var(--color-text-subtle);}
@@ -1,4 +1,4 @@
1
- /* Freeday — Datetime picker (composed: date picker + time picker → one value).
1
+ /* Freeday. Datetime picker (composed: date picker + time picker → one value).
2
2
  * A thin composer (freeday-datetime.js) links a child [data-fdy-datepicker] and
3
3
  * [data-fdy-timepicker] and emits a single combined event. Icon variants come from
4
4
  * the child pickers (data-fdy-no-icon / <template data-fdy-icon>). */
@@ -1,4 +1,4 @@
1
- /* Freeday — Description list (key-value detail view) */
1
+ /* Freeday. Description list (key-value detail view) */
2
2
  .fdy-dl{display:grid;grid-template-columns:auto 1fr;gap:var(--space-2) var(--space-5);margin:0;}
3
3
  .fdy-dl dt{color:var(--color-text-muted);font-size:var(--text-sm);}
4
4
  .fdy-dl dd{margin:0;color:var(--color-text);font-size:var(--text-sm);font-weight:var(--weight-medium);}
@@ -1,3 +1,3 @@
1
- /* Freeday — Divider */
1
+ /* Freeday. Divider */
2
2
  .fdy-divider{border:0;border-top:var(--bw) solid var(--color-border);margin:var(--space-4) 0;}
3
3
  .fdy-divider--vertical{border-top:0;border-left:var(--bw) solid var(--color-border);margin:0 var(--space-4);align-self:stretch;}
@@ -1,4 +1,4 @@
1
- /* Freeday — Drawer (temporary side panel on native <dialog>: focus-trap / Esc /
1
+ /* Freeday. Drawer (temporary side panel on native <dialog>: focus-trap / Esc /
2
2
  * backdrop / return-focus are free). Anchors left (default) or right.
3
3
  * Enhanced by freeday-drawer.js (open trigger + backdrop-click close). */
4
4
  /* display is gated behind [open] so the native <dialog> stays hidden until opened.
@@ -1,4 +1,4 @@
1
- /* Freeday — File upload (drop target + explicit per-file states).
1
+ /* Freeday. File upload (drop target + explicit per-file states).
2
2
  * Enhanced by freeday-upload.js; states: idle · drag-over · uploading · done · error. */
3
3
  .fdy-dropzone{display:flex;flex-direction:column;align-items:center;gap:var(--space-2);padding:var(--space-6);text-align:center;border:2px dashed var(--color-control-border);border-radius:var(--radius-lg);background:var(--color-surface-2);color:var(--color-text-muted);cursor:pointer;transition:border-color var(--dur-fast) var(--ease-standard),background var(--dur-fast) var(--ease-standard),color var(--dur-fast) var(--ease-standard);}
4
4
  .fdy-dropzone:hover{border-color:var(--color-text-muted);}
@@ -10,17 +10,17 @@
10
10
  .fdy-dropzone__icon svg{display:block;width:2rem;height:2rem;}
11
11
  .fdy-dropzone__title{font-size:inherit;font-weight:var(--weight-medium);color:var(--color-text);margin:0;}
12
12
  .fdy-dropzone__hint{font-size:var(--text-xs);}
13
- /* Wide (horizontal) layout — icon beside the copy instead of stacked, for full-width
13
+ /* Wide (horizontal) layout, icon beside the copy instead of stacked, for full-width
14
14
  * sections. Wrap the title + hint in .fdy-dropzone__text. */
15
15
  .fdy-dropzone--row{flex-direction:row;align-items:center;justify-content:center;text-align:left;gap:var(--space-4);}
16
16
  .fdy-dropzone__text{display:flex;flex-direction:column;gap:var(--space-1);}
17
17
 
18
18
  /* File rows (list of added/uploaded/rejected files) */
19
- /* Symmetric on purpose. The list carried margin-top and nothing below, so whatever follows it —
20
- an "Add files" button, in every upload UI there is — sat flush against the last row: measured
19
+ /* Symmetric on purpose. The list carried margin-top and nothing below, so whatever follows it,
20
+ an "Add files" button, in every upload UI there is, sat flush against the last row: measured
21
21
  0px. A block that claims space on one side only is not spacing, it is a lean. */
22
22
  .fdy-filelist{display:flex;flex-direction:column;gap:var(--space-2);margin-top:var(--space-3);margin-bottom:var(--space-3);}
23
- /* Nothing after it, nothing to separate from — so the room is not spent. */
23
+ /* Nothing after it, nothing to separate from, so the room is not spent. */
24
24
  .fdy-filelist:last-child{margin-bottom:0;}
25
25
  /* Side-by-side rows on wide screens (reflow to one column when narrow). */
26
26
  .fdy-filelist--grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(16rem,1fr));}
@@ -1,4 +1,4 @@
1
- /* Freeday — Filter bar (a consistent row of filters: search + selects + actions).
1
+ /* Freeday. Filter bar (a consistent row of filters: search + selects + actions).
2
2
  * Solves the "ragged filter row": apps improvised flex rows with ad-hoc
3
3
  * flex/min-width, so grow fields ballooned while selects stayed tiny and no two
4
4
  * screens matched. Here every field snaps to a shared width rhythm; one field may
@@ -7,7 +7,7 @@
7
7
  .fdy-filterbar{display:flex;flex-wrap:wrap;align-items:flex-end;gap:var(--space-3) var(--space-4);}
8
8
  /* Composed with a table toolbar (one element, both classes): the fields decide the alignment.
9
9
  * `.fdy-table-toolbar` sets align-items:center (right for bare buttons) at equal specificity, and
10
- * being later in the bundle it would otherwise win — floating labelled fields' neighbours out of line.
10
+ * being later in the bundle it would otherwise win, floating labelled fields' neighbours out of line.
11
11
  * This 2-class selector (0,2,0) settles it toward the filterbar's flex-end regardless of source order. */
12
12
  .fdy-table-toolbar.fdy-filterbar{align-items:flex-end;}
13
13
  /* Fields are direct children. Drop the standalone field's 22rem cap; default to the
@@ -15,7 +15,7 @@
15
15
  .fdy-filterbar>.fdy-field{max-width:none;flex:0 0 auto;width:12rem;}
16
16
  /* Controls fill their field's column (drop the standalone input-group/combo caps). */
17
17
  .fdy-filterbar>.fdy-field .fdy-input-group,.fdy-filterbar>.fdy-field .fdy-combo{max-width:none;width:100%;}
18
- /* Column-width rhythm — pick one per field so bars across the app line up. */
18
+ /* Column-width rhythm, pick one per field so bars across the app line up. */
19
19
  .fdy-filterbar>.fdy-field--w-sm{width:9rem;}
20
20
  .fdy-filterbar>.fdy-field--w-lg{width:15rem;}
21
21
  .fdy-filterbar>.fdy-field--w-xl{width:18rem;}
@@ -25,11 +25,11 @@
25
25
  .fdy-filterbar>.fdy-field--w-grow{flex:1 1 16rem;width:auto;min-width:12rem;}
26
26
  /* Trailing actions (buttons) pushed to the end, aligned with the controls' baseline.
27
27
  * Wrap note: a wrapped bar breaks flex lines on each item's *flex-basis* (the grow field's
28
- * `flex:1 1 16rem` reserves 16rem for line-breaking), NOT its rendered width — so the grow basis,
28
+ * `flex:1 1 16rem` reserves 16rem for line-breaking), NOT its rendered width, so the grow basis,
29
29
  * not the visible width, decides the wrap point. Margins can't keep the actions off a lonely wrapped
30
30
  * row; to avoid stranded actions, tune the --w-grow basis or drop a field so the bar fits one row. */
31
- /* A control that carries NO label of its own — a checkbox, a switch, a lone
32
- button — in a bar whose `align-items:flex-end` exists to line up LABELLED
31
+ /* A control that carries NO label of its own, a checkbox, a switch, a lone
32
+ button, in a bar whose `align-items:flex-end` exists to line up LABELLED
33
33
  fields. Flex-end puts such a control's bottom on the inputs' bottom, so a
34
34
  20px checkbox sits well below the 32px input's centre line and reads as
35
35
  dropped. Giving it the height of a control lands its box exactly where an
@@ -37,7 +37,7 @@
37
37
  line. Raised from IDU_EMATE_APPL_WEB (#014). */
38
38
  .fdy-filterbar>.fdy-check,.fdy-filterbar>.fdy-radio,.fdy-filterbar>.fdy-switch,.fdy-filterbar>.fdy-btn{min-height:var(--control-h);}
39
39
  .fdy-filterbar__actions{display:flex;align-items:center;gap:var(--space-2);margin-left:auto;}
40
- /* Opt-in: sit the actions inline right after the last field instead of at the far edge — so when the
40
+ /* Opt-in: sit the actions inline right after the last field instead of at the far edge, so when the
41
41
  * bar wraps they read as attached to the filters rather than shoved to the right of a lonely row. */
42
42
  .fdy-filterbar--actions-inline .fdy-filterbar__actions{margin-left:0;}
43
43
  /* Stack on narrow screens so nothing overflows the row. */
@@ -1,8 +1,8 @@
1
- /* Freeday — Form grid (document/header fields laid out in aligned columns).
1
+ /* Freeday. Form grid (document/header fields laid out in aligned columns).
2
2
  * The two-dimensional counterpart to the single-column .fdy-field stack and the
3
3
  * one-row .fdy-filterbar: related fields sit in columns that reflow to one on narrow
4
4
  * screens. Fields are the standard .fdy-field; a field with --full spans the whole row
5
- * (e.g. an address or note). Zero JS — pure layout over .fdy-field. */
5
+ * (e.g. an address or note). Zero JS, pure layout over .fdy-field. */
6
6
  .fdy-form-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(14rem,1fr));gap:var(--space-4) var(--space-5);}
7
7
  .fdy-form-grid>.fdy-field{max-width:none;}
8
8
  .fdy-form-grid>.fdy-field--full{grid-column:1/-1;}
@@ -1,4 +1,4 @@
1
- /* Freeday — Input group (prefix/suffix addons, e.g. Rp / % / icon) */
1
+ /* Freeday. Input group (prefix/suffix addons, e.g. Rp / % / icon) */
2
2
  .fdy-input-group{display:flex;align-items:stretch;height:var(--control-h);max-width:22rem;background:var(--color-surface);border:1.5px solid var(--color-control-border);border-radius:var(--radius-md);box-shadow:inset 0 1px 2px rgba(16,14,30,.06);overflow:hidden;transition:border-color var(--dur-fast) var(--ease-standard),box-shadow var(--dur-fast) var(--ease-standard);}
3
3
  .fdy-input-group:hover{border-color:var(--color-text-muted);}
4
4
  .fdy-input-group:focus-within{border-color:var(--color-primary);box-shadow:inset 0 1px 2px rgba(16,14,30,.04),0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
@@ -11,23 +11,23 @@
11
11
  .fdy-input-group .fdy-input:focus{box-shadow:none;}
12
12
  .fdy-input-group__addon{display:inline-flex;align-items:center;padding:0 var(--space-3);background:var(--color-surface-2);color:var(--color-text-muted);font-size:var(--text-sm);white-space:nowrap;border-right:var(--bw) solid var(--color-border);}
13
13
  .fdy-input-group__addon:last-child{border-right:0;border-left:var(--bw) solid var(--color-border);}
14
- /* Borderless leading icon (e.g. search magnifier) — self-contained like __btn so it
14
+ /* Borderless leading icon (e.g. search magnifier), self-contained like __btn so it
15
15
  centers standalone (no base .fdy-input-group__addon required). */
16
16
  .fdy-input-group__addon--icon{display:inline-flex;align-items:center;background:transparent;border:0;padding:0 0 0 var(--space-3);color:var(--color-text-subtle);}
17
17
  .fdy-input-group__addon--icon svg{display:block;width:1rem;height:1rem;}
18
- /* The leading icon already provides the left inset — don't let the input add its full left padding on top. */
18
+ /* The leading icon already provides the left inset, don't let the input add its full left padding on top. */
19
19
  .fdy-input-group__addon--icon + .fdy-input{padding-left:var(--space-2);}
20
- /* Trailing action button (e.g. choose-from-list magnifier) — a real, focusable control. */
20
+ /* Trailing action button (e.g. choose-from-list magnifier), a real, focusable control. */
21
21
  .fdy-input-group__btn{display:inline-flex;align-items:center;justify-content:center;flex:none;width:var(--control-h);align-self:stretch;border:0;border-left:var(--bw) solid var(--color-border);background:var(--color-surface-2);color:var(--color-text-muted);cursor:pointer;transition:background var(--dur-fast) var(--ease-standard),color var(--dur-fast) var(--ease-standard);}
22
22
  .fdy-input-group__btn:hover{background:var(--color-surface-3);color:var(--color-text);}
23
23
  .fdy-input-group__btn:focus-visible{outline:none;color:var(--color-text);box-shadow:inset 0 0 0 2px var(--color-primary);}
24
24
  .fdy-input-group__btn svg{display:block;width:1.1rem;height:1.1rem;}
25
25
  /* Leading action button (the "−" of a number stepper): the divider belongs on the other side. */
26
26
  .fdy-input-group__btn:first-child{border-left:0;border-right:var(--bw) solid var(--color-border);}
27
- /* At a bound, or on a disabled/read-only field, the button must look as inert as it acts — same dim
27
+ /* At a bound, or on a disabled/read-only field, the button must look as inert as it acts, same dim
28
28
  + not-allowed as .fdy-input:disabled. :hover still matches a disabled button, so it is withdrawn. */
29
29
  .fdy-input-group__btn:disabled{opacity:.5;cursor:not-allowed;}
30
30
  .fdy-input-group__btn:disabled:hover{background:var(--color-surface-2);color:var(--color-text-muted);}
31
- /* Error state must win over the neutralizing rules above — promote to the wrapper. */
31
+ /* Error state must win over the neutralizing rules above, promote to the wrapper. */
32
32
  .fdy-input-group:has(.fdy-input--error),.fdy-input-group:has(.fdy-combo--error),.fdy-input-group:has([aria-invalid="true"]){border-color:var(--color-danger);}
33
33
  .fdy-input-group:has(.fdy-input--error):focus-within,.fdy-input-group:has(.fdy-combo--error):focus-within,.fdy-input-group:has([aria-invalid="true"]):focus-within{border-color:var(--color-danger);box-shadow:inset 0 1px 2px rgba(16,14,30,.04),0 0 0 3px color-mix(in srgb,var(--color-danger) 26%,transparent);}
@@ -1,10 +1,10 @@
1
- /* Freeday — Field & Input */
1
+ /* Freeday. Field & Input */
2
2
  .fdy-field{display:flex;flex-direction:column;gap:var(--space-2);max-width:22rem;}
3
- /* A grouped field is legitimately a <fieldset> (it needs a <legend>) — e.g. a radio/checkbox group.
3
+ /* A grouped field is legitimately a <fieldset> (it needs a <legend>), e.g. a radio/checkbox group.
4
4
  Neutralise the UA groove border / padding / min-inline-size so it lays out identically to the
5
5
  <div> form (min-inline-size:min-content would otherwise block the filterbar column widths). */
6
6
  fieldset.fdy-field{border:0;margin:0;padding:0;min-inline-size:0;}
7
- /* The rendered legend is laid out outside the fieldset's flex flow, so `gap` never reaches it —
7
+ /* The rendered legend is laid out outside the fieldset's flex flow, so `gap` never reaches it,
8
8
  measured 0px between legend and first control. This is the spacing contract a grouped field
9
9
  needs; it matches .fdy-field's own gap so the group reads as one rhythm. */
10
10
  fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
@@ -22,17 +22,17 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
22
22
  .fdy-input[readonly]:hover,.fdy-textarea[readonly]:hover{border-color:var(--color-border);}
23
23
  .fdy-input[readonly]:focus-visible,.fdy-textarea[readonly]:focus-visible{border-color:var(--color-border-strong);box-shadow:none;}
24
24
  /* Numbers (#44): the user agent draws its own spin buttons inside the field, in OS widget colours no
25
- theme reaches — on a dark surface they read as a light-grey artefact glued to a themed field, and
25
+ theme reaches, on a dark surface they read as a light-grey artefact glued to a themed field, and
26
26
  it is the only unthemed control on the page. Removed so .fdy-input looks the same whatever its
27
27
  type. `appearance:textfield` is the Firefox half, the pseudo-element the Blink/WebKit half; both
28
- are needed. This trades away the mouse-only increment — arrow keys on a focused number input still
28
+ are needed. This trades away the mouse-only increment, arrow keys on a focused number input still
29
29
  work, and [data-fdy-number] gives the affordance back on the kit's terms. */
30
30
  .fdy-input[type="number"]{appearance:textfield;-webkit-appearance:textfield;}
31
31
  .fdy-input[type="number"]::-webkit-outer-spin-button,.fdy-input[type="number"]::-webkit-inner-spin-button{appearance:none;-webkit-appearance:none;margin:0;}
32
32
  .fdy-input--error,.fdy-input[aria-invalid="true"],.fdy-textarea[aria-invalid="true"]{border-color:var(--color-danger);}
33
33
  .fdy-input--error:focus,.fdy-input[aria-invalid="true"]:focus,.fdy-textarea[aria-invalid="true"]:focus{border-color:var(--color-danger);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-danger) 26%,transparent);}
34
34
  /* Required marker. The control already carries `required`, so the asterisk is decoration: the
35
- `/ ""` is CSS alt text — the glyph is painted but exposes NOTHING to the accessibility tree, so a
35
+ `/ ""` is CSS alt text, the glyph is painted but exposes NOTHING to the accessibility tree, so a
36
36
  screen reader does not read "star" after every label. That is stricter than a <span> an app has to
37
37
  remember to mark aria-hidden, and it cannot be forgotten. */
38
38
  .fdy-label--required::after{content:"*" / "";margin-left:var(--space-1);color:var(--color-danger);}
@@ -1,2 +1,2 @@
1
- /* Freeday — Keyboard key */
1
+ /* Freeday. Keyboard key */
2
2
  .fdy-kbd{display:inline-flex;align-items:center;justify-content:center;min-width:1.5rem;height:1.5rem;padding:0 var(--space-2);font-family:var(--font-mono);font-size:var(--text-xs);color:var(--color-text);background:var(--color-surface-2);border:var(--bw) solid var(--color-border-strong);border-bottom-width:2px;border-radius:var(--radius-sm);}
@@ -1,17 +1,17 @@
1
- /* Freeday — List: the FLAT row container.
1
+ /* Freeday. List: the FLAT row container.
2
2
  *
3
3
  * USAGE.md §3 puts list rows among the surfaces that should be flat, but until now the only
4
- * container the kit shipped was .fdy-card — which carries --shadow-lift (a real 34px lift). A
4
+ * container the kit shipped was .fdy-card, which carries --shadow-lift (a real 34px lift). A
5
5
  * responsive table that becomes a list below `md` therefore had to choose between a stack of
6
6
  * shadowed cards (against the doctrine) or a hand-built box, which needs a colour and so escapes
7
7
  * the token system. This is that missing container: one flat surface, hairline dividers, no shadow.
8
8
  *
9
9
  * Not to be confused with .fdy-list-reset (base.css), which only strips UA bullets/indent.
10
10
  * Works on <ul>/<ol> (list-style is reset here) or on plain <div>s. */
11
- /* position:relative — containing block for out-of-flow row content (see base.css). */
11
+ /* position:relative, containing block for out-of-flow row content (see base.css). */
12
12
  .fdy-list{position:relative;list-style:none;margin:0;padding:0;background:var(--color-surface);border:var(--bw) solid var(--color-border);border-radius:var(--radius-lg);overflow:hidden;}
13
13
  /* A row that hosts a stretched target has to be the anchor itself (#008 §1). Without this the
14
- overlay resolves against .fdy-list — which IS positioned — so EVERY row's target covers the whole
14
+ overlay resolves against .fdy-list, which IS positioned, so EVERY row's target covers the whole
15
15
  list and the last one in the DOM wins: measured, a click on row one opens row two, under row one's
16
16
  name, which looks exactly like working software. Opt-in via :has(), so a row that doesn't use the
17
17
  pattern is untouched and nothing an app absolutely positions inside a plain row starts resolving
@@ -20,7 +20,7 @@
20
20
  .fdy-list__row{display:flex;align-items:center;gap:var(--space-3);padding:var(--space-4) var(--space-5);min-width:0;}
21
21
  /* Divider between rows. Two shapes are supported and both are load-bearing: rows as direct children
22
22
  * of the list, and rows wrapped in <li> (the semantic shape, where the ADJACENT siblings are the
23
- * <li>s — a bare `.fdy-list__row + .fdy-list__row` silently matches nothing there). */
23
+ * <li>s, a bare `.fdy-list__row + .fdy-list__row` silently matches nothing there). */
24
24
  .fdy-list__row + .fdy-list__row,
25
25
  .fdy-list > * + * > .fdy-list__row{border-top:var(--bw) solid var(--color-border-muted);}
26
26
  /* A row that is itself the control: render it as <button>/<a> and the UA box is reset without
@@ -29,7 +29,7 @@
29
29
  .fdy-list__row--button:hover,.fdy-list__row--interactive:hover{background:var(--color-surface-2);}
30
30
  .fdy-list__row--button:focus-visible{outline:none;box-shadow:inset 0 0 0 2px var(--color-primary);}
31
31
  .fdy-list__row--interactive{cursor:pointer;}
32
- /* Disabled row. The reset above adopts the UA button box, but not its disabled state — so a row
32
+ /* Disabled row. The reset above adopts the UA button box, but not its disabled state, so a row
33
33
  disabled mid-flight (a redirect in progress, say) kept lighting up and kept a pointer cursor:
34
34
  a control answering the pointer while refusing input. Same contract as every other disabled
35
35
  control in the kit (dim + not-allowed), hover withdrawn, both the native and the aria- form. */
@@ -1,4 +1,4 @@
1
- /* Freeday — Menu (action popup) + Split button. Enhanced by freeday-menu.js. */
1
+ /* Freeday. Menu (action popup) + Split button. Enhanced by freeday-menu.js. */
2
2
  .fdy-menu-wrap{position:relative;display:inline-flex;}
3
3
  .fdy-menu{position:absolute;top:calc(100% + var(--space-1));left:0;z-index:130;min-width:11rem;margin:0;padding:var(--space-1);list-style:none;background:var(--color-surface);color:var(--color-text);border:var(--bw) solid var(--color-border-strong);border-radius:var(--radius-md);box-shadow:var(--shadow-3);}
4
4
  .fdy-menu[hidden]{display:none;}
@@ -6,7 +6,7 @@
6
6
  .fdy-menu__item{display:flex;align-items:center;gap:var(--space-2);width:100%;padding:var(--space-2) var(--space-3);border:0;background:transparent;border-radius:var(--radius-sm);font-family:var(--font-body);font-size:var(--text-sm);color:var(--color-text);text-align:left;cursor:pointer;white-space:nowrap;}
7
7
  .fdy-menu__item:hover,.fdy-menu__item.is-highlighted{background:var(--color-surface-3);}
8
8
  /* Focus is not hover. The enhancer moves real DOM focus (freeday-menu.js `.focus()`), so this is
9
- the only thing a keyboard user has to go on — with the hover fill alone, arrowing through the
9
+ the only thing a keyboard user has to go on, with the hover fill alone, arrowing through the
10
10
  menu is invisible. Same ring as .fdy-nav__item, which got this right. Reported twice. */
11
11
  .fdy-menu__item:focus-visible{outline:none;background:var(--color-surface-3);box-shadow:0 0 0 2px color-mix(in srgb,var(--color-primary) 45%,transparent);}
12
12
  .fdy-menu__item:disabled{opacity:.5;cursor:not-allowed;background:transparent;}
@@ -15,7 +15,7 @@
15
15
  .fdy-menu__sep{height:0;margin:var(--space-1) 0;border:0;border-top:var(--bw) solid var(--color-border-muted);}
16
16
  @media (prefers-reduced-motion:no-preference){.fdy-menu:not([hidden]){animation:fdy-combo-in var(--dur-fast) var(--ease-standard);}}
17
17
 
18
- /* Split button — main action + attached menu toggle (caret) */
18
+ /* Split button, main action + attached menu toggle (caret) */
19
19
  .fdy-btn-split{position:relative;display:inline-flex;border-radius:var(--radius-md);transition:transform var(--dur-fast) var(--ease-standard),box-shadow var(--dur-fast) var(--ease-standard);}
20
20
  /* One control: the two halves must stay coplanar. Neutralize each segment's own
21
21
  lift + drop-shadow (they would misalign the seam / glow a single half); the
@@ -1,4 +1,4 @@
1
- /* Freeday — Modal (native <dialog>) */
1
+ /* Freeday. Modal (native <dialog>) */
2
2
  .fdy-modal{width:min(32rem,calc(100vw - var(--space-8)));max-height:calc(100vh - var(--space-16));padding:0;border:var(--bw) solid var(--color-border);border-radius:var(--radius-xl);background:var(--color-surface);color:var(--color-text);box-shadow:var(--shadow-lift-hover);overflow:hidden;flex-direction:column;opacity:0;transform:translateY(8px) scale(.98);transition:opacity var(--dur-base) var(--ease-spring),transform var(--dur-base) var(--ease-spring),overlay var(--dur-base) allow-discrete,display var(--dur-base) allow-discrete;}
3
3
  .fdy-modal--sm{width:min(24rem,calc(100vw - var(--space-8)));}
4
4
  .fdy-modal--md{width:min(32rem,calc(100vw - var(--space-8)));}
@@ -1,4 +1,4 @@
1
- /* Freeday — Pagination */
1
+ /* Freeday. Pagination */
2
2
  .fdy-pagination__list{display:flex;align-items:center;gap:var(--space-1);list-style:none;margin:0;padding:0;}
3
3
  .fdy-pagination__link{display:inline-flex;align-items:center;justify-content:center;min-width:2.25rem;height:2.25rem;padding:0 var(--space-2);border-radius:var(--radius-md);font-size:var(--text-sm);font-weight:var(--weight-medium);color:var(--color-text);background:transparent;border:var(--bw) solid transparent;text-decoration:none;cursor:pointer;transition:background var(--dur-fast) var(--ease-standard),border-color var(--dur-fast) var(--ease-standard);}
4
4
  .fdy-pagination__link:hover{background:var(--color-surface-2);border-color:var(--color-border);}
@@ -1,4 +1,4 @@
1
- /* Freeday — Progress */
1
+ /* Freeday. Progress */
2
2
  .fdy-progress{height:.5rem;border-radius:var(--radius-full);background:var(--color-surface-3);overflow:hidden;}
3
3
  .fdy-progress__bar{height:100%;border-radius:inherit;background:var(--color-primary);transition:width var(--dur-base) var(--ease-standard);}
4
4
  .fdy-progress--indeterminate{position:relative;}
@@ -1,4 +1,4 @@
1
- /* Freeday — Rating (star rating). Interactive = native radio group (arrow keys, form-assoc);
1
+ /* Freeday. Rating (star rating). Interactive = native radio group (arrow keys, form-assoc);
2
2
  * filled vs empty differ by fill-vs-outline (not colour alone). Enhanced by freeday-rating.js. */
3
3
  .fdy-rating{display:inline-flex;gap:var(--space-1);}
4
4
  .fdy-rating__star{display:inline-flex;line-height:0;cursor:pointer;}
@@ -11,6 +11,6 @@
11
11
  .fdy-rating--disabled .fdy-rating__star{cursor:not-allowed;}
12
12
  .fdy-rating--disabled .fdy-rating__star svg{opacity:.5;}
13
13
  /* Invalid: tint the (outline) empty stars danger so a required-but-empty rating reads as an error,
14
- paired with a help/error message — never colour alone. */
14
+ paired with a help/error message, never colour alone. */
15
15
  .fdy-rating--error .fdy-rating__star svg{stroke:var(--color-danger);}
16
16
  .fdy-rating--sm .fdy-rating__star svg{width:1.1rem;height:1.1rem;}
@@ -1,10 +1,10 @@
1
- /* Freeday — Selection controls */
1
+ /* Freeday. Selection controls */
2
2
  /* `flex:none` on every box, track and standalone checkbox is load-bearing, not tidiness.
3
3
  `.fdy-check` is an inline-flex row of [box, label], and a flex item with a WIDTH is still
4
- shrinkable — so as soon as the label wrapped to a second line the box gave up horizontal space
4
+ shrinkable, so as soon as the label wrapped to a second line the box gave up horizontal space
5
5
  to it and rendered an 18px-tall rectangle 13px wide. Squashed, off-square, and different from
6
6
  every sibling whose label happened to be short: a form of five checkboxes showed three sizes.
7
- Nothing in the declarations looked wrong, because the width was never overridden — it was
7
+ Nothing in the declarations looked wrong, because the width was never overridden, it was
8
8
  negotiated away. */
9
9
  .fdy-check,.fdy-radio,.fdy-switch{display:inline-flex;align-items:center;gap:var(--space-2);font-size:var(--text-sm);color:var(--color-text);cursor:pointer;}
10
10
  .fdy-check input,.fdy-radio input{appearance:none;-webkit-appearance:none;flex:none;width:var(--control-box);height:var(--control-box);margin:0;border:1.5px solid var(--color-control-border);background:var(--color-surface);box-shadow:inset 0 1px 2px var(--color-shadow-inset);cursor:pointer;display:grid;place-content:center;transition:background var(--dur-fast) var(--ease-standard),border-color var(--dur-fast) var(--ease-standard);}
@@ -22,7 +22,7 @@
22
22
  .fdy-switch input:checked{background:var(--color-primary);}
23
23
  .fdy-switch input:checked::before{transform:translateX(calc(var(--control-switch-w) - var(--control-switch-h)));}
24
24
 
25
- /* Standalone checkbox (e.g. table row/select-all) — supports :indeterminate */
25
+ /* Standalone checkbox (e.g. table row/select-all), supports :indeterminate */
26
26
  .fdy-checkbox{appearance:none;-webkit-appearance:none;flex:none;width:var(--control-box);height:var(--control-box);margin:0;border:1.5px solid var(--color-control-border);border-radius:var(--radius-sm);background:var(--color-surface);box-shadow:inset 0 1px 2px var(--color-shadow-inset);cursor:pointer;display:grid;place-content:center;transition:background var(--dur-fast) var(--ease-standard),border-color var(--dur-fast) var(--ease-standard);}
27
27
  .fdy-checkbox::before{content:"";width:.62rem;height:.62rem;transform:scale(0);transition:transform var(--dur-fast) var(--ease-spring);box-shadow:inset 1rem 1rem var(--color-on-primary);clip-path:polygon(14% 44%,0 65%,50% 100%,100% 16%,80% 0,43% 62%);}
28
28
  .fdy-checkbox:checked,.fdy-checkbox:indeterminate{background:var(--color-primary);border-color:var(--color-primary);}
@@ -1,4 +1,4 @@
1
- /* Freeday — Skeleton */
1
+ /* Freeday. Skeleton */
2
2
  .fdy-skeleton{display:block;border-radius:var(--radius-sm);background:var(--color-surface-3);background-image:linear-gradient(90deg,transparent,color-mix(in srgb,var(--color-surface) 60%,transparent),transparent);background-size:200% 100%;background-repeat:no-repeat;animation:fdy-skeleton 1.4s ease-in-out infinite;}
3
3
  .fdy-skeleton--text{height:.8rem;margin:.3rem 0;}
4
4
  .fdy-skeleton--title{height:1.2rem;width:40%;}
@@ -1,4 +1,4 @@
1
- /* Freeday — Slider (native <input type="range"> themed via accent-color: keeps native
1
+ /* Freeday. Slider (native <input type="range"> themed via accent-color: keeps native
2
2
  * keyboard + ARIA, filled track and thumb come free). Optional value output + scale.
3
3
  * freeday-slider.js just mirrors the value into an <output for="…">. */
4
4
  .fdy-slider{width:100%;max-width:22rem;height:1.5rem;accent-color:var(--color-primary);cursor:pointer;}
@@ -1,4 +1,4 @@
1
- /* Freeday — Spinner */
1
+ /* Freeday. Spinner */
2
2
  .fdy-spinner{display:inline-block;width:1.5rem;height:1.5rem;border:2.5px solid var(--color-border-strong);border-top-color:var(--color-primary);border-radius:var(--radius-full);animation:fdy-spin .7s linear infinite;}
3
3
  .fdy-spinner--sm{width:1rem;height:1rem;border-width:2px;}
4
4
  .fdy-spinner--lg{width:2.5rem;height:2.5rem;border-width:3px;}
@@ -1,4 +1,4 @@
1
- /* Freeday — States (empty / loading / error placeholders) */
1
+ /* Freeday. States (empty / loading / error placeholders) */
2
2
  .fdy-state{display:flex;flex-direction:column;align-items:center;text-align:center;gap:var(--space-2);padding:var(--space-10) var(--space-6);}
3
3
  .fdy-state__icon{display:inline-flex;align-items:center;justify-content:center;width:2.5rem;height:2.5rem;color:var(--color-text-subtle);}
4
4
  .fdy-state__icon svg{width:100%;height:100%;}
@@ -1,4 +1,4 @@
1
- /* Freeday — Stepper / Wizard (progress header + optional panels).
1
+ /* Freeday. Stepper / Wizard (progress header + optional panels).
2
2
  * Enhanced by freeday-stepper.js for prev/next navigation. */
3
3
  .fdy-stepper{display:flex;list-style:none;margin:0;padding:0;}
4
4
  .fdy-step{flex:1;position:relative;display:flex;flex-direction:column;align-items:center;text-align:center;min-width:0;}
@@ -8,7 +8,7 @@
8
8
  .fdy-step__btn{position:relative;z-index:1;display:flex;flex-direction:column;align-items:center;gap:var(--space-2);padding:0 var(--space-2);background:none;border:0;font:inherit;color:inherit;cursor:pointer;max-width:100%;}
9
9
  .fdy-step__btn:disabled{cursor:default;}
10
10
  /* The marker carries the lift itself, NOT `__btn`. The connector above is position:absolute with
11
- z-index:0, and a positioned box paints after in-flow inline content in the same stacking context —
11
+ z-index:0, and a positioned box paints after in-flow inline content in the same stacking context,
12
12
  so a marker that is merely inline (an indicator-only stepper with no `__btn`) gets painted THROUGH
13
13
  by the connector line. Lifting the part that must always exist makes that assembly impossible;
14
14
  `__btn`'s own z-index stays, harmlessly, for the navigable case. */
@@ -1,5 +1,5 @@
1
- /* Freeday — Table */
2
- /* position:relative on every scroller/clipper — it makes the box the containing block for its own
1
+ /* Freeday. Table */
2
+ /* position:relative on every scroller/clipper, it makes the box the containing block for its own
3
3
  absolutely positioned content (.fdy-visually-hidden above all), which `overflow` alone does NOT
4
4
  clip. Without it a hidden label in a wide table scrolls the whole page. See base.css. */
5
5
  .fdy-table-wrap{position:relative;overflow-x:auto;border:var(--bw) solid var(--color-border);border-radius:var(--radius-lg);box-shadow:var(--shadow-1);background:var(--color-surface);}
@@ -10,7 +10,7 @@
10
10
  .fdy-table tbody tr:last-child td{border-bottom:0;}
11
11
  .fdy-table tbody tr{transition:background-color var(--dur-fast) var(--ease-standard);}
12
12
  .fdy-table tbody tr:hover{background:var(--color-surface-2);}
13
- /* Monospace data — ids, codes, IPs, timestamps. Alignment-neutral so it works in any cell
13
+ /* Monospace data, ids, codes, IPs, timestamps. Alignment-neutral so it works in any cell
14
14
  or inline (`<span class="fdy-mono">`), not just numeric columns. */
15
15
  .fdy-mono{font-family:var(--font-mono);font-variant-numeric:tabular-nums;}
16
16
  /* Numeric cells are mono + right-aligned. Kept self-contained (not composed from .fdy-mono)
@@ -37,7 +37,7 @@
37
37
  .fdy-table__selcol{width:1px;white-space:nowrap;}
38
38
  .fdy-table tbody tr[aria-selected="true"],.fdy-table tbody tr[aria-selected="true"]:hover{background:var(--color-primary-soft);}
39
39
 
40
- /* Frozen header row and/or first column — a rate matrix, a timetable, any grid read
40
+ /* Frozen header row and/or first column, a rate matrix, a timetable, any grid read
41
41
  against two axes at once.
42
42
 
43
43
  This modifier exists because `position:sticky` alone is NOT enough. With
@@ -51,12 +51,12 @@
51
51
  /* Freezing the HEADER ROW is its own modifier because it needs a vertical
52
52
  scrollport (`.fdy-table-scroll--frozen`). A table that only freezes columns
53
53
  scrolls with the PAGE, and `top:0` against the page sticks the header under
54
- the viewport edge — over the app's own top bar — which is worse than not
54
+ the viewport edge, over the app's own top bar, which is worse than not
55
55
  freezing it. Ask for the axis you actually have. */
56
56
  .fdy-table--sticky-head thead th{position:sticky;top:0;z-index:2;border-bottom:var(--bw) solid var(--color-border);}
57
57
 
58
58
  /* Freezing a COLUMN: put `.fdy-table__freeze` on every cell of it, header
59
- included, and give each frozen column its own `--fdy-freeze-left` — 0 for the
59
+ included, and give each frozen column its own `--fdy-freeze-left`. 0 for the
60
60
  first, the summed widths of the frozen columns left of it after that. One
61
61
  frozen column needs no variable at all. Mark the last one `--edge` so the
62
62
  reader can see where the frozen block ends.
@@ -65,12 +65,12 @@
65
65
  widths, which CSS cannot sum, and hard-coding them here would only work for
66
66
  tables whose columns are all the same width. */
67
67
  .fdy-table--sticky .fdy-table__freeze{position:sticky;left:var(--fdy-freeze-left,0);}
68
- /* A frozen body cell slides OVER the data, so it must be OPAQUE — otherwise the
68
+ /* A frozen body cell slides OVER the data, so it must be OPAQUE, otherwise the
69
69
  cells beneath show through it. A `th` already carries the header tint from the
70
70
  base rule, and that tint is usually the whole point: it is what marks a column
71
71
  as identity rather than data. So only a `td` is given a surface here. A table
72
- that wants its frozen `th` to read as a body row instead — a rate matrix, where
73
- a hovered row should band unbroken across the freeze line — says so itself. */
72
+ that wants its frozen `th` to read as a body row instead, a rate matrix, where
73
+ a hovered row should band unbroken across the freeze line, says so itself. */
74
74
  .fdy-table--sticky tbody .fdy-table__freeze{z-index:1;}
75
75
  .fdy-table--sticky tbody td.fdy-table__freeze{background:var(--color-surface);}
76
76
  .fdy-table--sticky tbody tr:hover td.fdy-table__freeze{background:var(--color-surface-2);}
@@ -93,19 +93,19 @@
93
93
  .fdy-table-footer{display:flex;align-items:center;justify-content:space-between;gap:var(--space-3);flex-wrap:wrap;padding:var(--space-3) var(--space-4);border-top:var(--bw) solid var(--color-border);}
94
94
  .fdy-table-footer__info{font-size:var(--text-sm);color:var(--color-text-muted);}
95
95
  /* Rows per page. `margin-left:auto` eats the free space, so the size control and the pager travel
96
- together on the right while the range stays left — no wrapper element, which is what keeps the
96
+ together on the right while the range stays left, no wrapper element, which is what keeps the
97
97
  footer's DOM byte-identical for the tables that don't offer one (#008). The control is the kit's
98
98
  own `.fdy-combo`, never a native <select>: an OS menu is unthemeable, and on macOS it drops a
99
- dark panel into a light page — which is the reason FdyCombo exists at all. Sized to its content:
99
+ dark panel into a light page, which is the reason FdyCombo exists at all. Sized to its content:
100
100
  `.fdy-combo` is display:block at max-width 22rem, which in a footer is a bar-wide box for two
101
- digits. Its HEIGHT is left alone on purpose — `--control-h`, the same as every other control on
101
+ digits. Its HEIGHT is left alone on purpose, `--control-h`, the same as every other control on
102
102
  the page. It briefly carried 2.25rem to match the pager links beside it, which made the one
103
103
  combobox in a compact app 4px taller than all the others; a pager link is a nav button, and the
104
104
  thing this must agree with is the form controls. */
105
105
  .fdy-table-footer__size{display:inline-flex;align-items:center;gap:var(--space-2);margin-left:auto;font-size:var(--text-sm);color:var(--color-text-muted);}
106
106
  .fdy-table-footer__size .fdy-combo{width:5.5rem;}
107
107
 
108
- /* Column filter — funnel button injected (enhancer) or rendered (FdyTable) into a filterable <th> */
108
+ /* Column filter, funnel button injected (enhancer) or rendered (FdyTable) into a filterable <th> */
109
109
  .fdy-table__filterwrap{display:inline-flex;align-items:center;vertical-align:middle;}
110
110
  .fdy-table__filterbtn{appearance:none;display:inline-flex;align-items:center;justify-content:center;width:1.5rem;height:1.5rem;margin-left:var(--space-1);padding:0;vertical-align:middle;border:0;border-radius:var(--radius-sm);background:transparent;color:var(--color-text-subtle);cursor:pointer;}
111
111
  .fdy-table__filterbtn:hover{background:var(--color-surface-3);color:var(--color-text);}
@@ -122,7 +122,7 @@
122
122
  .fdy-filter__range .fdy-input{width:6rem;}
123
123
  .fdy-filter__foot{display:flex;justify-content:space-between;gap:var(--space-2);margin-top:var(--space-3);}
124
124
 
125
- /* Bulk-action bar — appears above the table body once rows are selected */
125
+ /* Bulk-action bar, appears above the table body once rows are selected */
126
126
  .fdy-table-bulkbar{display:flex;align-items:center;gap:var(--space-3);padding:var(--space-2) var(--space-4);background:var(--color-primary-soft);border-bottom:var(--bw) solid var(--color-border);color:var(--color-primary-strong);font-size:var(--text-sm);}
127
127
  .fdy-table-bulkbar[hidden]{display:none;}
128
128
  .fdy-table-bulkbar__count{font-weight:var(--weight-semibold);white-space:nowrap;}
@@ -130,7 +130,7 @@
130
130
  .fdy-table-bulkbar__actions{display:flex;gap:var(--space-2);}
131
131
 
132
132
  /* Controls inside a data row. `.fdy-input` and friends are width:100%, which in an auto-layout
133
- table means they contribute NO intrinsic width — the column shrinks to whatever the header text
133
+ table means they contribute NO intrinsic width, the column shrinks to whatever the header text
134
134
  needs and the control collapses with it. A NOTE column ends up a box too narrow to read what you
135
135
  typed into it. The floor is per-control rather than on the column, because the table does not
136
136
  know which of its columns hold controls. */