@livenetworks/ashlar 2.1.13 → 2.2.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 (101) hide show
  1. package/components/COMPONENTS.md +80 -80
  2. package/components/ln-api-connector/ln-api-connector.schema.json +105 -105
  3. package/components/ln-chart/ln-chart-dev.scss +0 -4
  4. package/components/ln-core/README.md +21 -21
  5. package/components/ln-core/ln-core.schema.json +120 -120
  6. package/components/ln-core/query.js +113 -113
  7. package/components/ln-data-coordinator/README.md +2 -0
  8. package/components/ln-data-coordinator/ln-data-coordinator.js +1 -1
  9. package/components/ln-data-coordinator/ln-data-coordinator.schema.json +196 -196
  10. package/components/ln-data-coordinator/src/ln-data-coordinator.js +457 -432
  11. package/components/ln-data-store/ln-data-store.schema.json +92 -92
  12. package/components/ln-date/ln-date.js +1 -1
  13. package/components/ln-date/src/ln-date.js +1 -1
  14. package/components/ln-debug/ln-debug.js +1 -1
  15. package/components/ln-debug/ln-debug.schema.json +28 -28
  16. package/components/ln-debug/src/generated-attributes.js +304 -302
  17. package/components/ln-dropdown/ln-dropdown.schema.json +90 -90
  18. package/components/ln-external-links/README.md +19 -19
  19. package/components/ln-external-links/ln-external-links.js +1 -1
  20. package/components/ln-external-links/ln-external-links.schema.json +9 -0
  21. package/components/ln-external-links/ln-external-links.scss +12 -0
  22. package/components/ln-external-links/src/ln-external-links.js +3 -2
  23. package/components/ln-filter/README.md +2 -1
  24. package/components/ln-filter/ln-filter.js +1 -1
  25. package/components/ln-filter/ln-filter.schema.json +157 -169
  26. package/components/ln-filter/src/ln-filter.js +1 -3
  27. package/components/ln-form/ln-form.schema.json +57 -57
  28. package/components/ln-key/ln-key.js +1 -1
  29. package/components/ln-key/ln-key.schema.json +8 -0
  30. package/components/ln-key/src/ln-key.js +8 -5
  31. package/components/ln-obfuscator/ln-obfuscator.js +1 -1
  32. package/components/ln-obfuscator/ln-obfuscator.schema.json +6 -0
  33. package/components/ln-obfuscator/src/ln-obfuscator.js +2 -2
  34. package/components/ln-options/ln-options.schema.json +47 -47
  35. package/components/ln-popover/ln-popover.schema.json +82 -82
  36. package/components/ln-progress/ln-progress.schema.json +0 -13
  37. package/components/ln-search/ln-search.schema.json +111 -111
  38. package/components/ln-sort/README.md +1 -0
  39. package/components/ln-sort/ln-sort.js +1 -1
  40. package/components/ln-sort/ln-sort.schema.json +100 -112
  41. package/components/ln-sort/src/sort-model.js +1 -6
  42. package/components/ln-sortable/ln-sortable.schema.json +38 -38
  43. package/components/ln-sortable/ln-sortable.scss +25 -25
  44. package/components/ln-table/README.md +8 -1
  45. package/components/ln-table/ln-table.js +1 -1
  46. package/components/ln-table/ln-table.schema.json +195 -195
  47. package/components/ln-table/src/ln-table.js +43 -7
  48. package/components/ln-table-coordinator/ln-table-coordinator.js +1 -1
  49. package/components/ln-table-coordinator/ln-table-coordinator.schema.json +18 -0
  50. package/components/ln-table-coordinator/src/ln-table-coordinator.js +104 -2
  51. package/components/ln-toast/README.md +1 -1
  52. package/components/ln-toast/ln-toast.js +1 -1
  53. package/components/ln-toast/ln-toast.schema.json +6 -0
  54. package/components/ln-toast/src/ln-toast.js +1 -1
  55. package/components/ln-validate/README.md +6 -6
  56. package/components/ln-validate/ln-validate-dev.scss +0 -5
  57. package/components/ln-validate/ln-validate.js +1 -1
  58. package/components/ln-validate/ln-validate.schema.json +0 -6
  59. package/components/ln-validate/src/ln-validate.js +48 -29
  60. package/ln-ashlar.html-data.json +1824 -1814
  61. package/package.json +95 -95
  62. package/theme/README.md +7 -6
  63. package/theme/base/_global.scss +2 -2
  64. package/theme/base/_reset.scss +11 -0
  65. package/theme/components/_app-shell.scss +0 -8
  66. package/theme/components/_form.scss +6 -6
  67. package/theme/components/_ln-table.scss +6 -3
  68. package/theme/components/_scrollbar.scss +5 -4
  69. package/theme/components/_sortable.scss +19 -19
  70. package/theme/components/_stat-card.scss +1 -0
  71. package/theme/components/_table.scss +10 -33
  72. package/theme/config/_density.scss +236 -104
  73. package/theme/config/_palette.scss +71 -49
  74. package/theme/config/_theme.scss +27 -31
  75. package/theme/config/_tokens.scss +28 -26
  76. package/theme/config/mixins/_app-shell.scss +17 -21
  77. package/theme/config/mixins/_btn.scss +4 -2
  78. package/theme/config/mixins/_card.scss +87 -67
  79. package/theme/config/mixins/_colors.scss +2 -1
  80. package/theme/config/mixins/_display.scss +12 -0
  81. package/theme/config/mixins/_footer.scss +3 -1
  82. package/theme/config/mixins/_form.scss +48 -16
  83. package/theme/config/mixins/_layout.scss +9 -0
  84. package/theme/config/mixins/_ln-table.scss +22 -15
  85. package/theme/config/mixins/_modal.scss +3 -0
  86. package/theme/config/mixins/_nav.scss +27 -4
  87. package/theme/config/mixins/_sidebar.scss +2 -1
  88. package/theme/config/mixins/_stat-card.scss +33 -2
  89. package/theme/config/mixins/_status-badge.scss +1 -0
  90. package/theme/config/mixins/_table.scss +81 -39
  91. package/theme/config/mixins/_tabs.scss +31 -0
  92. package/theme/config/mixins/_toast.scss +0 -1
  93. package/theme/designs/relaxed/_card.scss +12 -0
  94. package/theme/designs/relaxed/_layout.scss +24 -0
  95. package/theme/ln-ashlar-core.scss +1 -0
  96. package/theme/ln-ashlar-dev.scss +0 -1
  97. package/theme/ln-ashlar-relaxed.scss +67 -0
  98. package/theme/tailwind/components.css +561 -24
  99. package/theme/utilities/_utilities.scss +1 -11
  100. package/web-types.json +2351 -2337
  101. package/components/ln-progress/ln-progress-dev.scss +0 -8
@@ -1,90 +1,90 @@
1
- {
2
- "$comment": "This file is partly generated. The $comment, component, generator, and each attribute's direction, sources, type, values, fallback, and description keys are owned by the generator and synchronized from JS source code. All other keys are preserved and safe to hand-author. Regenerate with `npm run sync:ln-schemas`.",
3
- "component": "ln-dropdown",
4
- "generator": "scripts/sync-ln-schemas.mjs",
5
- "attributes": {
6
- "data-ln-debug": {
7
- "direction": null,
8
- "sources": [
9
- "ln-dropdown-dev.scss"
10
- ]
11
- },
12
- "data-ln-dropdown": {
13
- "direction": null,
14
- "sources": [
15
- "ln-dropdown-dev.scss",
16
- "ln-dropdown.scss",
17
- "src/ln-dropdown.js"
18
- ],
19
- "type": "marker",
20
- "description": "Initializes the dropdown container"
21
- },
22
- "data-ln-dropdown-menu": {
23
- "direction": "runtime",
24
- "sources": [
25
- "ln-dropdown.scss",
26
- "src/ln-dropdown.js"
27
- ],
28
- "type": "marker",
29
- "description": "Dropdown menu element containing items"
30
- },
31
- "data-ln-dropdown-placement": {
32
- "direction": "both",
33
- "sources": [
34
- "src/ln-dropdown.js"
35
- ],
36
- "type": "enum",
37
- "values": [
38
- "top",
39
- "top-start",
40
- "top-end",
41
- "bottom",
42
- "bottom-start",
43
- "bottom-end",
44
- "left",
45
- "left-start",
46
- "left-end",
47
- "right",
48
- "right-start",
49
- "right-end"
50
- ],
51
- "description": "Active calculated placement applied at runtime"
52
- },
53
- "data-ln-dropdown-position": {
54
- "direction": null,
55
- "sources": [
56
- "src/ln-dropdown.js"
57
- ],
58
- "type": "enum",
59
- "values": [
60
- "top",
61
- "top-start",
62
- "top-end",
63
- "bottom",
64
- "bottom-start",
65
- "bottom-end",
66
- "left",
67
- "left-start",
68
- "left-end",
69
- "right",
70
- "right-start",
71
- "right-end"
72
- ],
73
- "description": "Preferred positioning anchor"
74
- },
75
- "data-ln-toggle": {
76
- "direction": "both",
77
- "sources": [
78
- "ln-dropdown-dev.scss",
79
- "src/ln-dropdown.js"
80
- ]
81
- },
82
- "data-ln-toggle-for": {
83
- "direction": "author",
84
- "sources": [
85
- "ln-dropdown-dev.scss",
86
- "src/ln-dropdown.js"
87
- ]
88
- }
89
- }
90
- }
1
+ {
2
+ "$comment": "This file is partly generated. The $comment, component, generator, and each attribute's direction, sources, type, values, fallback, and description keys are owned by the generator and synchronized from JS source code. All other keys are preserved and safe to hand-author. Regenerate with `npm run sync:ln-schemas`.",
3
+ "component": "ln-dropdown",
4
+ "generator": "scripts/sync-ln-schemas.mjs",
5
+ "attributes": {
6
+ "data-ln-debug": {
7
+ "direction": null,
8
+ "sources": [
9
+ "ln-dropdown-dev.scss"
10
+ ]
11
+ },
12
+ "data-ln-dropdown": {
13
+ "direction": null,
14
+ "sources": [
15
+ "ln-dropdown-dev.scss",
16
+ "ln-dropdown.scss",
17
+ "src/ln-dropdown.js"
18
+ ],
19
+ "type": "marker",
20
+ "description": "Initializes the dropdown container"
21
+ },
22
+ "data-ln-dropdown-menu": {
23
+ "direction": "runtime",
24
+ "sources": [
25
+ "ln-dropdown.scss",
26
+ "src/ln-dropdown.js"
27
+ ],
28
+ "type": "marker",
29
+ "description": "Dropdown menu element containing items"
30
+ },
31
+ "data-ln-dropdown-placement": {
32
+ "direction": "both",
33
+ "sources": [
34
+ "src/ln-dropdown.js"
35
+ ],
36
+ "type": "enum",
37
+ "values": [
38
+ "top",
39
+ "top-start",
40
+ "top-end",
41
+ "bottom",
42
+ "bottom-start",
43
+ "bottom-end",
44
+ "left",
45
+ "left-start",
46
+ "left-end",
47
+ "right",
48
+ "right-start",
49
+ "right-end"
50
+ ],
51
+ "description": "Active calculated placement applied at runtime"
52
+ },
53
+ "data-ln-dropdown-position": {
54
+ "direction": null,
55
+ "sources": [
56
+ "src/ln-dropdown.js"
57
+ ],
58
+ "type": "enum",
59
+ "values": [
60
+ "top",
61
+ "top-start",
62
+ "top-end",
63
+ "bottom",
64
+ "bottom-start",
65
+ "bottom-end",
66
+ "left",
67
+ "left-start",
68
+ "left-end",
69
+ "right",
70
+ "right-start",
71
+ "right-end"
72
+ ],
73
+ "description": "Preferred positioning anchor"
74
+ },
75
+ "data-ln-toggle": {
76
+ "direction": "both",
77
+ "sources": [
78
+ "ln-dropdown-dev.scss",
79
+ "src/ln-dropdown.js"
80
+ ]
81
+ },
82
+ "data-ln-toggle-for": {
83
+ "direction": "author",
84
+ "sources": [
85
+ "ln-dropdown-dev.scss",
86
+ "src/ln-dropdown.js"
87
+ ]
88
+ }
89
+ }
90
+ }
@@ -2,7 +2,7 @@
2
2
 
3
3
  > Runs globally across `document.body` watching all `<a>` and `<area>` elements. When an element's `href` host
4
4
  > differs from `window.location.host`, it sets `target="_blank"`, merges `rel="noopener noreferrer"`,
5
- > appends a `<span class="sr-only">(opens in new tab)</span>`, and marks it with `data-ln-external-link="processed"` to prevent redundant mutations.
5
+ > appends a `<span data-ln-external-links-hint>(opens in new tab)</span>`, and marks it with `data-ln-external-link="processed"` to prevent redundant mutations.
6
6
 
7
7
  ## Markup anatomy
8
8
 
@@ -20,7 +20,7 @@ story:
20
20
  rel="noopener noreferrer"
21
21
  data-ln-external-link="processed">
22
22
  Read more
23
- <span class="sr-only">(opens in new tab)</span>
23
+ <span data-ln-external-links-hint>(opens in new tab)</span>
24
24
  </a>
25
25
 
26
26
  <!-- Internal links — no change at all -->
@@ -34,7 +34,7 @@ Four edits happen on each external link, in this order:
34
34
 
35
35
  1. `target="_blank"` — opens in a new tab.
36
36
  2. `rel="noopener noreferrer"` — security + privacy.
37
- 3. A `<span class="sr-only">(opens in new tab)</span>` is appended
37
+ 3. A `<span data-ln-external-links-hint>(opens in new tab)</span>` is appended
38
38
  inside the anchor as the last child. Sighted users see no
39
39
  change; assistive tech announces "Read more, opens in new tab"
40
40
  when the link receives focus. WCAG 2.4.4 ("Link Purpose")
@@ -51,15 +51,15 @@ Four edits happen on each external link, in this order:
51
51
  | Has this link been processed? | `data-ln-external-link="processed"` on the `<a>` / `<area>` | ln-external-links (writes once, reads on every pass) |
52
52
  | Should it open in a new tab? | `target="_blank"` on the link | ln-external-links (writes) |
53
53
  | Security / privacy posture | `rel="noopener noreferrer"` on the link | ln-external-links (writes — merges with prior `rel`) |
54
- | Screen-reader hint | `<span class="sr-only">` appended inside the link | ln-external-links (creates and appends) |
54
+ | Screen-reader hint | `<span data-ln-external-links-hint>` appended inside the link | ln-external-links (creates and appends) |
55
55
  | Click event delivery | `document.body` click delegate | ln-external-links (one listener for the whole page) |
56
56
  | Decoration of new DOM | `MutationObserver` on `document.body`, `childList: true, subtree: true, attributes: true, attributeFilter: ['href']` | ln-external-links |
57
57
 
58
58
  ## States & visual feedback
59
59
 
60
60
  There is no visual state. Decoration is invisible to sighted users
61
- (target/rel are non-rendering attributes, the sr-only span is hidden
62
- by the utility class). The "before/after" is purely the four mutations
61
+ (target/rel are non-rendering attributes, the hint span is hidden
62
+ by the rule shipped in `ln-ashlar-core.css`). The "before/after" is purely the four mutations
63
63
  listed above.
64
64
 
65
65
  | Trigger | What JS does | What the user sees |
@@ -85,6 +85,7 @@ that doubles as an opt-out hatch.
85
85
  | Attribute | On | Direction | Description |
86
86
  |---|---|---|---|
87
87
  | `data-ln-external-link="processed"` | `<a>` / `<area>` | written by component, read on each pass | Idempotency marker. Set after a link is decorated. The processing function early-returns on links that already carry it. **Pre-setting it manually** in your markup makes the component skip the link entirely — the unofficial opt-out path. |
88
+ | `data-ln-external-links-hint` | `<span>` | written | Marks the visually hidden "(opens in new tab)" hint span appended inside a processed link. |
88
89
  | `target` | `<a>` / `<area>` | written | Set to `_blank` on every external link. **Overwrites** any pre-existing `target` value. |
89
90
  | `rel` | `<a>` / `<area>` | written | Merged: `noopener` and `noreferrer` are added if not already present. Pre-existing tokens (`me`, `author`, `license`, `nofollow`, `ugc`, `sponsored`) survive on the link. |
90
91
 
@@ -99,7 +100,7 @@ itself.
99
100
 
100
101
  | Event | Bubbles | Cancelable | `detail` | Dispatched when | Common consumer |
101
102
  |---|---|---|---|---|---|
102
- | `ln-external-links:processed` | yes | no | `{ link: HTMLAnchorElement, href: string }` | A link finishes decoration (after `target`, `rel`, sr-only span, and the marker attribute are all written) | Auditing / debug logging; verifying decoration coverage |
103
+ | `ln-external-links:processed` | yes | no | `{ link: HTMLAnchorElement, href: string }` | A link finishes decoration (after `target`, `rel`, hint span, and the marker attribute are all written) | Auditing / debug logging; verifying decoration coverage |
103
104
  | `ln-external-links:clicked` | yes | no | `{ link, href: string, text: string }` | The user clicks anywhere inside a processed external link. `text` is `link.textContent || link.title || ''` — the visible label, falling back to the title attribute, falling back to empty string | Analytics / outbound-link tracking |
104
105
 
105
106
  The `:clicked` event is a **notification, not a hook**. The component does not call `preventDefault()` and does not honor a consumer's `preventDefault()` on the CustomEvent. To intercept navigation, attach a click listener on the link or a parent in the **capture phase**, and `preventDefault()` the platform `click` event. The demo page shows a working interstitial.
@@ -222,7 +223,7 @@ feature.
222
223
  The component **merges** `noopener` and `noreferrer` into any pre-existing
223
224
  `rel` token list. Authoring `rel="me"` on a personal-website link in your
224
225
  bio is enough — no pre-marking required. After decoration, the link
225
- carries `rel="me noopener noreferrer"` and the auto sr-only hint span:
226
+ carries `rel="me noopener noreferrer"` and the auto hint span:
226
227
 
227
228
  ```html
228
229
  <!-- Before (your authored HTML) -->
@@ -234,13 +235,13 @@ carries `rel="me noopener noreferrer"` and the auto sr-only hint span:
234
235
  target="_blank"
235
236
  data-ln-external-link="processed">
236
237
  Live Networks
237
- <span class="sr-only">(opens in new tab)</span>
238
+ <span data-ln-external-links-hint>(opens in new tab)</span>
238
239
  </a>
239
240
  ```
240
241
 
241
242
  ## Common mistakes
242
243
 
243
- ### Mistake 1 — Reading `link.textContent` for analytics and getting the sr-only hint string
244
+ ### Mistake 1 — Reading `link.textContent` for analytics and getting the hint string
244
245
 
245
246
  ```js
246
247
  document.addEventListener('ln-external-links:clicked', function (e) {
@@ -252,7 +253,7 @@ document.addEventListener('ln-external-links:clicked', function (e) {
252
253
  `detail.text` is `link.textContent || link.title || ''`, evaluated at
253
254
  click time *after* the hint span has been appended.
254
255
  `link.textContent` returns the concatenated text of all descendants,
255
- including the sr-only span. If your analytics dashboard collects
256
+ including the hint span. If your analytics dashboard collects
256
257
  `event_label`, you'll see the hint suffix on every entry.
257
258
 
258
259
  Two workarounds:
@@ -265,7 +266,7 @@ const cleanLabel = label.replace(/\s*\(opens in new tab\)\s*$/, '');
265
266
  // The hint is always the LAST child of the link.
266
267
  const link = e.detail.link;
267
268
  const labelOnly = Array.prototype.filter.call(link.childNodes, function (n) {
268
- return !(n.nodeType === 1 && n.classList.contains('sr-only'));
269
+ return !(n.nodeType === 1 && n.hasAttribute && n.hasAttribute('data-ln-external-links-hint'));
269
270
  }).map(function (n) { return n.textContent; }).join('').trim();
270
271
  ```
271
272
 
@@ -323,10 +324,9 @@ This self-contained script executes immediately and sets up the necessary hooks
323
324
  - **`@mixin ln-icon`** is unused here — there's no icon decoration on
324
325
  external links by default. If you want a "↗" indicator, add it in
325
326
  project SCSS via `a[data-ln-external-link="processed"]::after`.
326
- - **`.sr-only`** (`theme/utilities/_utilities.scss`) — the
327
- visually-hidden utility used by the appended hint span. If your
328
- project does not include ln-ashlar utilities, supply your own
329
- `.sr-only` definition or the hint will be visible.
327
+ - **`[data-ln-external-links-hint]`** (`components/ln-external-links/ln-external-links.scss`) — the
328
+ visually-hidden rule for the appended hint span. It ships in
329
+ `ln-ashlar-core.css`; no `.sr-only` utility is needed.
330
330
  - **[`ln-modal`](../ln-modal/README.md)** — the demo page composes
331
331
  `ln-modal` with this component to show a "leaving the site"
332
332
  interstitial. The composition is project code, not library code;
@@ -359,7 +359,7 @@ Both the click delegate and the observer are wrapped in `guardBody` (`ln-core`):
359
359
  2. `!_isExternalLink(link)` → return.
360
360
  3. `target = '_blank'` (clobbers any pre-existing value).
361
361
  4. `rel` tokens merged: `noopener` + `noreferrer` added, existing tokens (`me`, `nofollow`, etc.) survive.
362
- 5. `<span class="sr-only">(opens in new tab)</span>` appended as last child.
362
+ 5. `<span data-ln-external-links-hint>(opens in new tab)</span>` appended as last child.
363
363
  6. `data-ln-external-link="processed"` written (the marker for future passes).
364
364
  7. `ln-external-links:processed` dispatched (bubbles, not cancelable).
365
365
 
@@ -371,11 +371,11 @@ Watches `document.body` with `childList: true, subtree: true, attributes: true,
371
371
 
372
372
  ### Click delegation
373
373
 
374
- One delegate on `document.body`, resolving `e.target.closest('a, area')`. No `preventDefault()` — the browser's default action and all modifier-key semantics (Ctrl/Cmd/middle/Shift-click) pass through untouched. Firing of `:clicked` is gated on the `processed` marker, **not** a fresh `_isExternalLink` re-check — a link pre-marked `processed` in markup (the opt-out hatch) fires `:clicked` even if it's actually internal. `detail.text` reads `link.textContent`, which includes the appended sr-only span text.
374
+ One delegate on `document.body`, resolving `e.target.closest('a, area')`. No `preventDefault()` — the browser's default action and all modifier-key semantics (Ctrl/Cmd/middle/Shift-click) pass through untouched. Firing of `:clicked` is gated on the `processed` marker, **not** a fresh `_isExternalLink` re-check — a link pre-marked `processed` in markup (the opt-out hatch) fires `:clicked` even if it's actually internal. `detail.text` reads `link.textContent`, which includes the appended hint span text.
375
375
 
376
376
  ### Accessibility
377
377
 
378
- The sr-only hint has no i18n hook — the string is hard-coded English. Projects needing localization must patch the compiled bundle or pre-mark every link to skip the component.
378
+ The hint has no i18n hook — the string is hard-coded English. Projects needing localization must patch the compiled bundle or pre-mark every link to skip the component.
379
379
 
380
380
  ### Cross-component coordination
381
381
 
@@ -1 +1 @@
1
- (function(){"use strict";function T(){if(typeof window>"u")return!1;if(window.lnDebug===!0||window.lnCore&&window.lnCore._debugSink)return!0;if(typeof document<"u"){if(document.documentElement&&document.documentElement.hasAttribute("data-ln-debug"))return!0;if(document.body)return document.body.hasAttribute("data-ln-debug")||document.body.querySelector("[data-ln-debug]")!==null}return!1}function y(e){if(typeof window>"u")return!1;if(window.lnDebug===!0)return!0;if(window.lnCore&&window.lnCore._debugIsContained)return e?window.lnCore._debugIsContained(e):window.lnCore._debugSink!==null;if(e&&e.closest){const t=e.closest("[data-ln-debug]");return!(!t||typeof document<"u"&&t===document.documentElement)}return typeof document<"u"&&document.body?document.body.hasAttribute("data-ln-debug"):!1}if(typeof window<"u"&&(window.lnCore=window.lnCore||{},!window.lnCore._warnBound)){window.lnCore._warnBound=!0;const e=console.warn;console.warn=function(...t){if(typeof t[0]=="string"&&(t[0].startsWith("[ln-")||t[0].startsWith("[lnCore"))){let o=null;for(let l=1;l<t.length;l++)if(t[l]&&t[l].nodeType===1){o=t[l];break}if(!y(o))return}e.apply(console,t)}}function p(e,t,r){const o=r||{};window.lnCore._debugSink&&window.lnCore._debugSink("event",t,e,o),e.dispatchEvent(new CustomEvent(t,{bubbles:!0,detail:o}))}function w(e,t){if(!document.body){document.addEventListener("DOMContentLoaded",function(){w(e,t)}),console.warn("["+t+'] Script loaded before <body> — add "defer" to your <script> tag');return}e()}function M(e){const t=e.querySelector('input[name="_method"]');return((t&&t.value!==""?t.value:e.method)||"").toUpperCase()}function B(e,t){const r=!!(t&&t.typed),o=t&&t.exclude,l={},c=e.elements,i={};if(r)for(let s=0;s<c.length;s++){const n=c[s];n.name&&n.type==="checkbox"&&!n.disabled&&(i[n.name]=(i[n.name]||0)+1)}for(let s=0;s<c.length;s++){const n=c[s];if(!(!n.name||n.disabled||n.type==="file"||n.type==="submit"||n.type==="button")&&!(o&&n.matches&&n.matches(o)))if(n.type==="checkbox")r&&i[n.name]===1?l[n.name]=n.checked:(l[n.name]||(l[n.name]=[]),n.checked&&l[n.name].push(n.value));else if(n.type==="radio")n.checked&&(l[n.name]=n.value);else if(n.type==="select-multiple"){l[n.name]=[];for(let a=0;a<n.options.length;a++)n.options[a].selected&&l[n.name].push(n.options[a].value)}else if(r&&n.type==="hidden")l[n.name]=n.value;else if(r&&(n.type==="number"||n.type==="range")){const a=Number(n.value);l[n.name]=n.value===""||isNaN(a)?null:a}else l[n.name]=n.value}return l}function j(e){if(typeof e!="string")return!!e;const t=e.trim().toLowerCase();return t!=="false"&&t!=="0"&&t!==""&&t!=="off"&&t!=="no"}function W(e,t){const r=e.elements,o=[],l={};for(let c=0;c<r.length;c++){const i=r[c];i.name&&i.type==="checkbox"&&(l[i.name]=(l[i.name]||0)+1)}for(let c=0;c<r.length;c++){const i=r[c];if(i.type==="file"||i.type==="submit"||i.type==="button")continue;const s=i.getAttribute("data-ln-fill-as")||i.name;if(!s||!(s in t))continue;const n=t[s];if(i.type==="checkbox"){if(Array.isArray(n))i.checked=n.indexOf(i.value)!==-1;else if(l[i.name]>1){const a=String(n).split(",").map(function(u){return u.trim()});i.checked=a.indexOf(i.value)!==-1}else i.checked=j(n);o.push(i)}else if(i.type==="radio")i.checked=i.value===String(n),o.push(i);else if(i.type==="select-multiple"){if(Array.isArray(n))for(let a=0;a<i.options.length;a++)i.options[a].selected=n.indexOf(i.options[a].value)!==-1;o.push(i)}else i.value=n,i.tagName==="SELECT"&&n!=null&&i.setAttribute("data-ln-value",n),o.push(i)}return o}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.populateForm=W,window.lnCore.serializeForm=B,window.lnCore.resolveFormMethod=M);const A={};function U(e,t){A[e]=t}function V(e){return A[e]||{ingress:t=>t,egress:t=>t}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.registerDataMapper=U,window.lnCore.getDataMapper=V);function H(){typeof window>"u"||(window.lnCore=window.lnCore||{},!window.lnCore._localeObserverBound&&(window.lnCore._localeObserverBound=!0,w(function(){new MutationObserver(function(){p(document,"ln-core:locale-change",{})}).observe(document.documentElement,{attributes:!0,attributeFilter:["lang"],subtree:!0})},"ln-core")))}const x={};function J(e,t){if(!e||typeof t!="object")return;const r=e.toLowerCase().split("-")[0];x[r]=t}function K(e){if(!e)return null;const t=e.toLowerCase().split("-")[0];return x[t]||null}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.registerLocaleFallback=J,window.lnCore.getLocaleFallback=K,window.lnCore.ensureLocaleObserver=H);const b={};function P(e,t){b[e]||(b[e]=document.querySelector('[data-ln-template="'+e+'"]'));const r=b[e];return r?r.content.cloneNode(!0):(console.warn("["+(t||"ln-core")+'] Template "'+e+'" not found'),null)}function _(e,t){if(!e||!t)return e;const r=e.querySelectorAll("[data-ln-field]");for(let i=0;i<r.length;i++){const s=r[i],n=s.getAttribute("data-ln-field");t[n]!=null&&(s.textContent=t[n])}const o=e.querySelectorAll("[data-ln-attr]");for(let i=0;i<o.length;i++){const s=o[i],n=s.getAttribute("data-ln-attr").split(",");for(let a=0;a<n.length;a++){const u=n[a].trim().split(":");if(u.length!==2)continue;const d=u[0].trim(),f=u[1].trim();t[f]!=null&&s.setAttribute(d,t[f])}}const l=e.querySelectorAll("[data-ln-show]");for(let i=0;i<l.length;i++){const s=l[i],n=s.getAttribute("data-ln-show");n in t&&s.classList.toggle("hidden",!t[n])}const c=e.querySelectorAll("[data-ln-class]");for(let i=0;i<c.length;i++){const s=c[i],n=s.getAttribute("data-ln-class").split(",");for(let a=0;a<n.length;a++){const u=n[a].trim().split(":");if(u.length!==2)continue;const d=u[0].trim(),f=u[1].trim();f in t&&s.classList.toggle(d,!!t[f])}}return e}function G(e,t){e.matches&&e.matches("[data-ln-form], [data-ln-fillable]")&&(window.lnCore._debugSink&&window.lnCore._debugSink("event","ln-fill",e,t??null),e.dispatchEvent(new CustomEvent("ln-fill",{detail:t??null,bubbles:!0})));const r=e.querySelectorAll("[data-ln-form], [data-ln-fillable]");for(let o=0;o<r.length;o++)window.lnCore._debugSink&&window.lnCore._debugSink("event","ln-fill",r[o],t??null),r[o].dispatchEvent(new CustomEvent("ln-fill",{detail:t??null,bubbles:!0}));return e}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore._fillBound||(window.lnCore._fillBound=!0,document.addEventListener("ln-fill",function(e){if(!(!e.target.matches||!e.target.matches("[data-ln-fillable]")))if(e.detail)_(e.target,e.detail);else{const t=e.target.querySelectorAll("[data-ln-field]");for(let r=0;r<t.length;r++)t[r].textContent=""}})));function k(e,t){if(!e||!t)return e;const r=document.createTreeWalker(e,NodeFilter.SHOW_TEXT);for(;r.nextNode();){const c=r.currentNode;c.textContent.indexOf("{{")!==-1&&(c.textContent=c.textContent.replace(/\{\{\s*(\w+)\s*\}\}/g,function(i,s){return t[s]!==void 0?t[s]:""}))}const o=function(c,i){return t[i]!==void 0?t[i]:""},l=Array.from(e.querySelectorAll("*"));e.nodeType===1&&l.push(e);for(let c=0;c<l.length;c++){const i=l[c],s=i.attributes;for(let n=0;n<s.length;n++){const a=s[n];a.value.indexOf("{{")!==-1&&i.setAttribute(a.name,a.value.replace(/\{\{\s*(\w+)\s*\}\}/g,o))}}return e}function Q(e,t,r,o,l,c){const i={};for(let n=0;n<e.children.length;n++){const a=e.children[n],u=a.getAttribute("data-ln-render-key");u&&(i[u]=a)}const s=document.createDocumentFragment();for(let n=0;n<t.length;n++){const a=t[n],u=String(o(a));let d=i[u];if(d)l(d,a,n);else{const f=P(r,c);if(!f||(k(f,a),d=f.firstElementChild,!d))continue;d.setAttribute("data-ln-render-key",u),l(d,a,n)}s.appendChild(d)}e.textContent="",e.appendChild(s)}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.fillTemplate=k,window.lnCore.fill=_,window.lnCore.lnFill=G,window.lnCore.renderList=Q);function g(e,t,r){const o=e.getAttribute(t);return o===null?r:o}function S(e,t,r){const o=parseInt(e.getAttribute(t),10);return isNaN(o)?r:o}function v(e,t,r=!1){const o=e.getAttribute(t);if(o===null)return!!r;const l=o.trim().toLowerCase();return!(l==="false"||l==="0")}function E(e,t){return(e.getAttribute(t)||"").split(",").map(r=>r.trim()).filter(Boolean)}const m=new Map;function L(e,t){const r=(e?e.join("|"):"")+"::"+t;if(m.has(r))return m.get(r);const o=new Set(e||[]),l=function(c,i){const s=c.getAttribute(i);return s!==null&&o.has(s)?s:t};return m.set(r,l),l}function q(e,t,r){const o=parseFloat(e.getAttribute(t));return isNaN(o)?r:o}function D(e,t,r){const o=e.getAttribute(t);if(!o)return r;try{return JSON.parse(o)}catch{return r}}function z(e,t,r,o,l){if(!e||t===null)return!0;const c=o||"ln-component",i=e.type;if(i==="trigger"||i==="marker"||i==="string"||i==="list"||!i||t===""&&e.fallback!==void 0)return!0;const s=n=>{l?console.warn(n,l):console.warn(n)};if(i==="boolean"){const n=t.trim().toLowerCase();return n!==""&&n!=="true"&&n!=="false"&&n!=="1"&&n!=="0"?(s(`[${c}] Invalid value "${t}" for boolean attribute "${r}". Allowed: "true", "false", or presence-only.`),!1):!0}if(i==="enum"){const n=e.values||[];return n.includes(t)?!0:(s(`[${c}] Invalid value "${t}" for attribute "${r}". Allowed: ${n.join(", ")}. Fallback: "${e.fallback}".`),!1)}if(i==="integer"){if(!/^-?\d+$/.test(t))return s(`[${c}] Invalid integer "${t}" for attribute "${r}". Fallback: ${e.fallback}.`),!1;const n=parseInt(t,10);return e.min!==void 0&&n<e.min?(s(`[${c}] Value ${n} for attribute "${r}" is less than min (${e.min}).`),!1):e.max!==void 0&&n>e.max?(s(`[${c}] Value ${n} for attribute "${r}" is greater than max (${e.max}).`),!1):!0}if(i==="float")return isNaN(Number(t))?(s(`[${c}] Invalid float "${t}" for attribute "${r}". Fallback: ${e.fallback}.`),!1):!0;if(i==="json")try{return JSON.parse(t),!0}catch(n){return s(`[${c}] Invalid JSON for attribute "${r}": ${n.message}. Fallback: ${e.fallback}.`),!1}return!0}function X(e,t,r){for(const o in r){const[l,c,i]=r[o];Object.defineProperty(e,o,{get:function(){return l(t,c,i)},enumerable:!0,configurable:!0})}return e}function Y(e){const t={};for(const r in e){const o=e[r];if(!o||!o.prop)continue;let l=o.read;if(!l&&o.type)switch(o.type){case"enum":l=L(o.values,o.fallback);break;case"integer":l=S;break;case"float":l=q;break;case"boolean":l=v;break;case"list":l=E;break;case"json":l=D;break;case"string":default:l=g;break}l||(l=g),t[o.prop]=[l,r,o.fallback]}return t}function Z(e){if(e.onAttrChange&&!e.declared)return null;const t=new Set;if(e.effects)for(const r in e.effects)t.add(r);if(e.onAttrChange&&e.declared)for(const r of e.declared)t.add(r);return t}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.defineAttrs=X,window.lnCore.attrSpec=Y,window.lnCore.attrStr=g,window.lnCore.attrInt=S,window.lnCore.attrBool=v,window.lnCore.attrList=E,window.lnCore.attrEnum=L,window.lnCore.attrFloat=q,window.lnCore.attrJson=D);function h(e,t){if(!e[t.attribute]){window.lnCore._persistSink&&e.hasAttribute&&e.hasAttribute("data-ln-persist")&&window.lnCore._persistSink(e,t.selector);try{e[t.attribute]=new t.ComponentFn(e)}catch(r){console.error("["+t.attribute+"] init failed",e,r)}}}function ee(e,t,r,o){if(e.nodeType!==1)return;const c=t.indexOf("[")!==-1||t.indexOf(".")!==-1||t.indexOf("#")!==-1?t:"["+t+"]",i={selector:t,attribute:r,ComponentFn:o};if(e.matches&&e.matches(c)&&h(e,i),e.querySelectorAll){const s=e.querySelectorAll(c);for(let n=0;n<s.length;n++)h(s[n],i)}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore._bootHolds=window.lnCore._bootHolds||0,window.lnCore._bootQueue=window.lnCore._bootQueue||[]);function O(){window.lnCore=window.lnCore||{};const e=window.lnCore._attrRegistry=window.lnCore._attrRegistry||{byAttr:new Map,reactive:[],persist:[]};return e.byReactive=e.byReactive||new Map,e.reactiveWildcard=e.reactiveWildcard||[],e}function te(e){const t=O(),r=e.observed||[];for(let o=0;o<r.length;o++){const l=r[o];t.byAttr.has(l)||t.byAttr.set(l,[]),t.byAttr.get(l).push(e)}if(t.byDeclaredAttr=t.byDeclaredAttr||new Map,e.attributes)for(const o in e.attributes){t.byDeclaredAttr.has(o)||t.byDeclaredAttr.set(o,[]);const l=t.byDeclaredAttr.get(o);l.some(c=>c.componentTag===e.componentTag)||l.push({spec:e.attributes[o],componentTag:e.componentTag})}if(e.onAttrChange||e.effects){t.reactive.push(e);const o=Z(e);if(o===null)t.reactiveWildcard.push(e);else for(const l of o)t.byReactive.has(l)||t.byReactive.set(l,[]),t.byReactive.get(l).push(e)}e.persist&&t.persist.push(e)}function F(e,t,r,o){for(let l=0;l<e.length;l++){const c=e[l];if(!t[c.attribute])continue;const i=c.effects&&c.effects[r];i?i(t,r,o):c.onAttrChange&&(!c.declared||c.declared.has(r))&&c.onAttrChange(t,r,o)}}function ne(e){const t=e.attributeName,r=t.indexOf("data-ln-")===0,o=O(),l=o.byAttr.get(t),c=T()&&o.byDeclaredAttr&&o.byDeclaredAttr.has(t);if(!r&&!l&&!c)return;const i=e.target;if(e.oldValue!==i.getAttribute(t)){if(c&&y(i)){const s=o.byDeclaredAttr.get(t),n=i.getAttribute(t);for(let a=0;a<s.length;a++)z(s[a].spec,n,t,s[a].componentTag,i)}if(window.lnCore._debugSink&&(r||l)&&window.lnCore._debugSink("attr",t,i,{oldValue:e.oldValue,newValue:i.getAttribute(t)}),r){const s=o.byReactive.get(t);s&&F(s,i,t,e.oldValue),o.reactiveWildcard.length&&F(o.reactiveWildcard,i,t,e.oldValue)}if(l)for(let s=0;s<l.length;s++){const n=l[s];if(n.handler){n.handler(i,t,e.oldValue);continue}n.onAttributeChange&&i[n.attribute]?n.onAttributeChange(i,t):(ee(i,n.selector,n.attribute,n.ComponentFn),n.onInit&&n.onInit(i))}}}function re(){window.lnCore=window.lnCore||{},!window.lnCore._attrObserverBound&&(window.lnCore._attrObserverBound=!0,w(function(){new MutationObserver(function(t){for(let r=0;r<t.length;r++)try{ne(t[r])}catch(o){console.error("[ln-core] mutation handler failed",t[r].target,o)}}).observe(document.body,{attributes:!0,subtree:!0,attributeOldValue:!0})},"ln-core"))}function I(){return window.lnCore=window.lnCore||{},window.lnCore._lifecycleRegistry=window.lnCore._lifecycleRegistry||[]}function $(){return window.lnCore=window.lnCore||{},window.lnCore._subtreeRegistry=window.lnCore._subtreeRegistry||[]}function R(e,t){if(!document.contains(e))if(t.attribute){const r=e[t.attribute];if(r&&typeof r.destroy=="function")try{r.destroy()}catch(o){console.error("["+t.attribute+"] destroy failed",e,o)}delete e[t.attribute]}else t.onDestroy&&t.onDestroy(e)}function oe(e){const t=I();if(!t.length)return;const r=e.target;if(r){const l=r.nodeType===1?r:r.parentElement;if(l&&(l.isContentEditable||l.closest&&l.closest('[contenteditable]:not([contenteditable="false"])')))return}const o=$();if(e.target&&o.length)for(let l=0;l<o.length;l++){const c=o[l],i=c.query,s=e.target.nodeType===1?e.target.matches&&e.target.matches(i)?e.target:e.target.closest?e.target.closest(i):null:e.target.parentElement&&e.target.parentElement.closest?e.target.parentElement.closest(i):null;s&&c.onSubtreeChange(s,e)}for(let l=0;l<e.addedNodes.length;l++){const c=e.addedNodes[l];if(c.nodeType!==1)continue;const i=!!(c.firstElementChild||c.children&&c.children.length);for(let s=0;s<t.length;s++){const n=t[s],a=n.query;if(a&&(c.matches&&c.matches(a)&&(n.ComponentFn&&h(c,n),n.onInit&&n.onInit(c)),i&&c.querySelectorAll)){const u=c.querySelectorAll(a);for(let d=0;d<u.length;d++)n.ComponentFn&&h(u[d],n),n.onInit&&n.onInit(u[d])}}}for(let l=0;l<e.removedNodes.length;l++){const c=e.removedNodes[l];if(c.nodeType!==1||document.contains(c))continue;const i=!!(c.firstElementChild||c.children&&c.children.length);for(let s=0;s<t.length;s++){const n=t[s],a=n.query;if(a&&(c.matches&&c.matches(a)&&R(c,n),i&&c.querySelectorAll)){const u=c.querySelectorAll(a);for(let d=0;d<u.length;d++)R(u[d],n)}}}}function ie(){window.lnCore=window.lnCore||{},!window.lnCore._lifecycleObserverBound&&(window.lnCore._lifecycleObserverBound=!0,w(function(){const e=new MutationObserver(function(t){for(let r=0;r<t.length;r++){const o=t[r];if(o.type==="childList")try{oe(o)}catch(l){console.error("[ln-core] lifecycle handler failed",o.target,l)}}});window.lnCore._lifecycleObserver=e,e.observe(document.body,{childList:!0,subtree:!0})},"ln-core"))}function le(e,t){te({observed:e,handler:t}),re()}function se(e){I().push(e),e.onSubtreeChange&&e.query&&$().push(e),ie()}function N(e){return(e||"").replace(/^#/,"")}function C(e){const t=e===void 0?location.hash:e,r={},o=N(t);if(!o)return r;const l=o.split("&");for(let c=0;c<l.length;c++){const i=l[c];if(!i)continue;const s=i.indexOf(":"),n=s>-1?i.slice(0,s):i,a=s>-1?i.slice(s+1):"";if(n)try{r[n]=decodeURIComponent(a)}catch{r[n]=a}}return r}function ce(e){if(!e)return null;const t=C();return e in t?t[e]:null}function ae(e,t){if(!e)return;const r=C();t==null?delete r[e]:r[e]=String(t);const l=Object.keys(r).map(function(c){const i=r[c];return i===""?c:c+":"+encodeURIComponent(i)}).join("&");N(location.hash)!==l&&(location.hash=l)}function ue(e){return e.button===1||e.ctrlKey||e.metaKey||e.shiftKey?!1:(e.preventDefault(),!0)}function de(e,t){if(!e||!e.hasAttribute("data-ln-hash"))return null;const r=e.getAttribute("data-ln-hash");if(r&&r.trim()!=="")return r.trim();const o=e.getAttribute("data-ln-sort")||e.getAttribute("data-ln-search-for")||e.getAttribute("data-ln-search")||e.getAttribute("data-ln-filter")||e.id;return o?t?o+"-"+t:o:t||null}function fe(e,t){return!t||t==="none"||e===null||e===void 0?null:String(e)+"."+t}function we(e){return!e||typeof e!="string"?null:e.endsWith(".asc")?{fieldOrColumn:e.slice(0,-4),direction:"asc"}:e.endsWith(".desc")?{fieldOrColumn:e.slice(0,-5),direction:"desc"}:null}function he(e,t){return!e||!Array.isArray(t)||t.length===0?null:e+":"+t.map(encodeURIComponent).join(",")}function pe(e){if(!e||typeof e!="string")return null;const t=e.indexOf(":");if(t===-1)return null;const r=e.slice(0,t),o=e.slice(t+1),l=o?o.split(",").map(function(c){try{return decodeURIComponent(c)}catch{return c}}).filter(Boolean):[];return{key:r,values:l}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.hashParse=C,window.lnCore.hashGet=ce,window.lnCore.hashSet=ae,window.lnCore.hashLinkClick=ue,window.lnCore.resolveHashNamespace=de,window.lnCore.hashSortEncode=fe,window.lnCore.hashSortDecode=we,window.lnCore.hashFilterEncode=he,window.lnCore.hashFilterDecode=pe);function be(e){return(e||"").replace(/^\?/,"")}function ge(e){const t=e===void 0?typeof window<"u"?window.location.search:"":e,r={},o=be(t);if(!o)return r;const l=new URLSearchParams(o);for(const[c,i]of l.entries())r[c]=i;return r}function me(e){if(!e||typeof window>"u")return null;const t=new URLSearchParams(window.location.search);return t.has(e)?t.get(e):null}function Ce(e,t){if(!e||typeof window>"u")return;const r=new URL(window.location.href);if(t==null||t===""?r.searchParams.delete(e):r.searchParams.set(e,String(t)),r.search===window.location.search)return;const o=r.pathname+r.search+r.hash;window.history.replaceState(window.history.state,"",o)}function ye(e,t){if(!e||!e.hasAttribute("data-ln-query"))return null;const r=e.getAttribute("data-ln-query");if(r&&r.trim()!=="")return r.trim();const o=e.getAttribute("data-ln-sort")||e.getAttribute("data-ln-search-for")||e.getAttribute("data-ln-search")||e.getAttribute("data-ln-filter")||e.id;return o?t?o+"_"+t:o:t||null}function Ae(e,t){return!t||t==="none"||e===null||e===void 0?null:String(e)+"."+t}function xe(e,t){if(e&&typeof e=="string"){if(e.endsWith(".asc"))return{fieldOrColumn:e.slice(0,-4),direction:"asc"};if(e.endsWith(".desc"))return{fieldOrColumn:e.slice(0,-5),direction:"desc"}}if(t&&typeof t=="object"){const r=t.sort_field||t.sort_by;if(r){const o=t.sort_dir&&String(t.sort_dir).toLowerCase()==="desc"?"desc":"asc";return{fieldOrColumn:r,direction:o}}}return null}function _e(e){return!Array.isArray(e)||e.length===0?null:e.map(encodeURIComponent).join(",")}function ke(e){return!e||typeof e!="string"?[]:e.split(",").map(function(t){try{return decodeURIComponent(t.trim())}catch{return t.trim()}}).filter(Boolean)}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.queryParse=ge,window.lnCore.queryGet=me,window.lnCore.querySet=Ce,window.lnCore.resolveQueryParam=ye,window.lnCore.querySortEncode=Ae,window.lnCore.querySortDecode=xe,window.lnCore.queryFilterEncode=_e,window.lnCore.queryFilterDecode=ke);function Se(){return typeof crypto<"u"&&typeof crypto.randomUUID=="function"?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{const t=Math.random()*16|0;return(e==="x"?t:t&3|8).toString(16)})}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.uuid=Se),(function(){const e="lnExternalLinks";if(window[e]!==void 0)return;function t(s){return s.hostname&&s.hostname!==window.location.hostname}function r(s){if(s.getAttribute("data-ln-external-link")==="processed"||!t(s))return;s.target="_blank";const n=(s.rel||"").split(/\s+/).filter(Boolean);n.includes("noopener")||n.push("noopener"),n.includes("noreferrer")||n.push("noreferrer"),s.rel=n.join(" ");const a=document.createElement("span");a.className="sr-only",a.textContent="(opens in new tab)",s.appendChild(a),s.setAttribute("data-ln-external-link","processed"),p(s,"ln-external-links:processed",{link:s,href:s.href})}function o(s){s=s||document.body;for(const n of s.querySelectorAll("a, area"))r(n)}function l(){w(function(){document.body.addEventListener("click",function(s){const n=s.target.closest("a, area");n&&n.getAttribute("data-ln-external-link")==="processed"&&p(n,"ln-external-links:clicked",{link:n,href:n.href,text:n.textContent||n.title||""})})},"ln-external-links")}function c(){w(function(){se({onInit:function(s){if(s.matches&&(s.matches("a")||s.matches("area"))&&r(s),s.querySelectorAll)for(const n of s.querySelectorAll("a, area"))r(n)}}),le(["href"],function(s){s.matches&&(s.matches("a")||s.matches("area"))&&r(s)})},"ln-external-links")}function i(){l(),c(),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",function(){o()}):o()}window[e]={process:o},i()})()})();
1
+ (function(){"use strict";function N(){if(typeof window>"u")return!1;if(window.lnDebug===!0||window.lnCore&&window.lnCore._debugSink)return!0;if(typeof document<"u"){if(document.documentElement&&document.documentElement.hasAttribute("data-ln-debug"))return!0;if(document.body)return document.body.hasAttribute("data-ln-debug")||document.body.querySelector("[data-ln-debug]")!==null}return!1}function y(e){if(typeof window>"u")return!1;if(window.lnDebug===!0)return!0;if(window.lnCore&&window.lnCore._debugIsContained)return e?window.lnCore._debugIsContained(e):window.lnCore._debugSink!==null;if(e&&e.closest){const t=e.closest("[data-ln-debug]");return!(!t||typeof document<"u"&&t===document.documentElement)}return typeof document<"u"&&document.body?document.body.hasAttribute("data-ln-debug"):!1}if(typeof window<"u"&&(window.lnCore=window.lnCore||{},!window.lnCore._warnBound)){window.lnCore._warnBound=!0;const e=console.warn;console.warn=function(...t){if(typeof t[0]=="string"&&(t[0].startsWith("[ln-")||t[0].startsWith("[lnCore"))){let o=null;for(let l=1;l<t.length;l++)if(t[l]&&t[l].nodeType===1){o=t[l];break}if(!y(o))return}e.apply(console,t)}}function p(e,t,r){const o=r||{};window.lnCore._debugSink&&window.lnCore._debugSink("event",t,e,o),e.dispatchEvent(new CustomEvent(t,{bubbles:!0,detail:o}))}function w(e,t){if(!document.body){document.addEventListener("DOMContentLoaded",function(){w(e,t)}),console.warn("["+t+'] Script loaded before <body> — add "defer" to your <script> tag');return}e()}function M(e){const t=e.querySelector('input[name="_method"]');return((t&&t.value!==""?t.value:e.method)||"").toUpperCase()}function B(e,t){const r=!!(t&&t.typed),o=t&&t.exclude,l={},c=e.elements,i={};if(r)for(let s=0;s<c.length;s++){const n=c[s];n.name&&n.type==="checkbox"&&!n.disabled&&(i[n.name]=(i[n.name]||0)+1)}for(let s=0;s<c.length;s++){const n=c[s];if(!(!n.name||n.disabled||n.type==="file"||n.type==="submit"||n.type==="button")&&!(o&&n.matches&&n.matches(o)))if(n.type==="checkbox")r&&i[n.name]===1?l[n.name]=n.checked:(l[n.name]||(l[n.name]=[]),n.checked&&l[n.name].push(n.value));else if(n.type==="radio")n.checked&&(l[n.name]=n.value);else if(n.type==="select-multiple"){l[n.name]=[];for(let a=0;a<n.options.length;a++)n.options[a].selected&&l[n.name].push(n.options[a].value)}else if(r&&n.type==="hidden")l[n.name]=n.value;else if(r&&(n.type==="number"||n.type==="range")){const a=Number(n.value);l[n.name]=n.value===""||isNaN(a)?null:a}else l[n.name]=n.value}return l}function j(e){if(typeof e!="string")return!!e;const t=e.trim().toLowerCase();return t!=="false"&&t!=="0"&&t!==""&&t!=="off"&&t!=="no"}function W(e,t){const r=e.elements,o=[],l={};for(let c=0;c<r.length;c++){const i=r[c];i.name&&i.type==="checkbox"&&(l[i.name]=(l[i.name]||0)+1)}for(let c=0;c<r.length;c++){const i=r[c];if(i.type==="file"||i.type==="submit"||i.type==="button")continue;const s=i.getAttribute("data-ln-fill-as")||i.name;if(!s||!(s in t))continue;const n=t[s];if(i.type==="checkbox"){if(Array.isArray(n))i.checked=n.indexOf(i.value)!==-1;else if(l[i.name]>1){const a=String(n).split(",").map(function(u){return u.trim()});i.checked=a.indexOf(i.value)!==-1}else i.checked=j(n);o.push(i)}else if(i.type==="radio")i.checked=i.value===String(n),o.push(i);else if(i.type==="select-multiple"){if(Array.isArray(n))for(let a=0;a<i.options.length;a++)i.options[a].selected=n.indexOf(i.options[a].value)!==-1;o.push(i)}else i.value=n,i.tagName==="SELECT"&&n!=null&&i.setAttribute("data-ln-value",n),o.push(i)}return o}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.populateForm=W,window.lnCore.serializeForm=B,window.lnCore.resolveFormMethod=M);const A={};function U(e,t){A[e]=t}function V(e){return A[e]||{ingress:t=>t,egress:t=>t}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.registerDataMapper=U,window.lnCore.getDataMapper=V);function H(){typeof window>"u"||(window.lnCore=window.lnCore||{},!window.lnCore._localeObserverBound&&(window.lnCore._localeObserverBound=!0,w(function(){new MutationObserver(function(){p(document,"ln-core:locale-change",{})}).observe(document.documentElement,{attributes:!0,attributeFilter:["lang"],subtree:!0})},"ln-core")))}const x={};function J(e,t){if(!e||typeof t!="object")return;const r=e.toLowerCase().split("-")[0];x[r]=t}function K(e){if(!e)return null;const t=e.toLowerCase().split("-")[0];return x[t]||null}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.registerLocaleFallback=J,window.lnCore.getLocaleFallback=K,window.lnCore.ensureLocaleObserver=H);const b={};function P(e,t){b[e]||(b[e]=document.querySelector('[data-ln-template="'+e+'"]'));const r=b[e];return r?r.content.cloneNode(!0):(console.warn("["+(t||"ln-core")+'] Template "'+e+'" not found'),null)}function _(e,t){if(!e||!t)return e;const r=e.querySelectorAll("[data-ln-field]");for(let i=0;i<r.length;i++){const s=r[i],n=s.getAttribute("data-ln-field");t[n]!=null&&(s.textContent=t[n])}const o=e.querySelectorAll("[data-ln-attr]");for(let i=0;i<o.length;i++){const s=o[i],n=s.getAttribute("data-ln-attr").split(",");for(let a=0;a<n.length;a++){const u=n[a].trim().split(":");if(u.length!==2)continue;const d=u[0].trim(),f=u[1].trim();t[f]!=null&&s.setAttribute(d,t[f])}}const l=e.querySelectorAll("[data-ln-show]");for(let i=0;i<l.length;i++){const s=l[i],n=s.getAttribute("data-ln-show");n in t&&s.classList.toggle("hidden",!t[n])}const c=e.querySelectorAll("[data-ln-class]");for(let i=0;i<c.length;i++){const s=c[i],n=s.getAttribute("data-ln-class").split(",");for(let a=0;a<n.length;a++){const u=n[a].trim().split(":");if(u.length!==2)continue;const d=u[0].trim(),f=u[1].trim();f in t&&s.classList.toggle(d,!!t[f])}}return e}function G(e,t){e.matches&&e.matches("[data-ln-form], [data-ln-fillable]")&&(window.lnCore._debugSink&&window.lnCore._debugSink("event","ln-fill",e,t??null),e.dispatchEvent(new CustomEvent("ln-fill",{detail:t??null,bubbles:!0})));const r=e.querySelectorAll("[data-ln-form], [data-ln-fillable]");for(let o=0;o<r.length;o++)window.lnCore._debugSink&&window.lnCore._debugSink("event","ln-fill",r[o],t??null),r[o].dispatchEvent(new CustomEvent("ln-fill",{detail:t??null,bubbles:!0}));return e}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore._fillBound||(window.lnCore._fillBound=!0,document.addEventListener("ln-fill",function(e){if(!(!e.target.matches||!e.target.matches("[data-ln-fillable]")))if(e.detail)_(e.target,e.detail);else{const t=e.target.querySelectorAll("[data-ln-field]");for(let r=0;r<t.length;r++)t[r].textContent=""}})));function k(e,t){if(!e||!t)return e;const r=document.createTreeWalker(e,NodeFilter.SHOW_TEXT);for(;r.nextNode();){const c=r.currentNode;c.textContent.indexOf("{{")!==-1&&(c.textContent=c.textContent.replace(/\{\{\s*(\w+)\s*\}\}/g,function(i,s){return t[s]!==void 0?t[s]:""}))}const o=function(c,i){return t[i]!==void 0?t[i]:""},l=Array.from(e.querySelectorAll("*"));e.nodeType===1&&l.push(e);for(let c=0;c<l.length;c++){const i=l[c],s=i.attributes;for(let n=0;n<s.length;n++){const a=s[n];a.value.indexOf("{{")!==-1&&i.setAttribute(a.name,a.value.replace(/\{\{\s*(\w+)\s*\}\}/g,o))}}return e}function Q(e,t,r,o,l,c){const i={};for(let n=0;n<e.children.length;n++){const a=e.children[n],u=a.getAttribute("data-ln-render-key");u&&(i[u]=a)}const s=document.createDocumentFragment();for(let n=0;n<t.length;n++){const a=t[n],u=String(o(a));let d=i[u];if(d)l(d,a,n);else{const f=P(r,c);if(!f||(k(f,a),d=f.firstElementChild,!d))continue;d.setAttribute("data-ln-render-key",u),l(d,a,n)}s.appendChild(d)}e.textContent="",e.appendChild(s)}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.fillTemplate=k,window.lnCore.fill=_,window.lnCore.lnFill=G,window.lnCore.renderList=Q);function g(e,t,r){const o=e.getAttribute(t);return o===null?r:o}function S(e,t,r){const o=parseInt(e.getAttribute(t),10);return isNaN(o)?r:o}function v(e,t,r=!1){const o=e.getAttribute(t);if(o===null)return!!r;const l=o.trim().toLowerCase();return!(l==="false"||l==="0")}function E(e,t){return(e.getAttribute(t)||"").split(",").map(r=>r.trim()).filter(Boolean)}const m=new Map;function L(e,t){const r=(e?e.join("|"):"")+"::"+t;if(m.has(r))return m.get(r);const o=new Set(e||[]),l=function(c,i){const s=c.getAttribute(i);return s!==null&&o.has(s)?s:t};return m.set(r,l),l}function q(e,t,r){const o=parseFloat(e.getAttribute(t));return isNaN(o)?r:o}function D(e,t,r){const o=e.getAttribute(t);if(!o)return r;try{return JSON.parse(o)}catch{return r}}function z(e,t,r,o,l){if(!e||t===null)return!0;const c=o||"ln-component",i=e.type;if(i==="trigger"||i==="marker"||i==="string"||i==="list"||!i||t===""&&e.fallback!==void 0)return!0;const s=n=>{l?console.warn(n,l):console.warn(n)};if(i==="boolean"){const n=t.trim().toLowerCase();return n!==""&&n!=="true"&&n!=="false"&&n!=="1"&&n!=="0"?(s(`[${c}] Invalid value "${t}" for boolean attribute "${r}". Allowed: "true", "false", or presence-only.`),!1):!0}if(i==="enum"){const n=e.values||[];return n.includes(t)?!0:(s(`[${c}] Invalid value "${t}" for attribute "${r}". Allowed: ${n.join(", ")}. Fallback: "${e.fallback}".`),!1)}if(i==="integer"){if(!/^-?\d+$/.test(t))return s(`[${c}] Invalid integer "${t}" for attribute "${r}". Fallback: ${e.fallback}.`),!1;const n=parseInt(t,10);return e.min!==void 0&&n<e.min?(s(`[${c}] Value ${n} for attribute "${r}" is less than min (${e.min}).`),!1):e.max!==void 0&&n>e.max?(s(`[${c}] Value ${n} for attribute "${r}" is greater than max (${e.max}).`),!1):!0}if(i==="float")return isNaN(Number(t))?(s(`[${c}] Invalid float "${t}" for attribute "${r}". Fallback: ${e.fallback}.`),!1):!0;if(i==="json")try{return JSON.parse(t),!0}catch(n){return s(`[${c}] Invalid JSON for attribute "${r}": ${n.message}. Fallback: ${e.fallback}.`),!1}return!0}function X(e,t,r){for(const o in r){const[l,c,i]=r[o];Object.defineProperty(e,o,{get:function(){return l(t,c,i)},enumerable:!0,configurable:!0})}return e}function Y(e){const t={};for(const r in e){const o=e[r];if(!o||!o.prop)continue;let l=o.read;if(!l&&o.type)switch(o.type){case"enum":l=L(o.values,o.fallback);break;case"integer":l=S;break;case"float":l=q;break;case"boolean":l=v;break;case"list":l=E;break;case"json":l=D;break;case"string":default:l=g;break}l||(l=g),t[o.prop]=[l,r,o.fallback]}return t}function Z(e){if(e.onAttrChange&&!e.declared)return null;const t=new Set;if(e.effects)for(const r in e.effects)t.add(r);if(e.onAttrChange&&e.declared)for(const r of e.declared)t.add(r);return t}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.defineAttrs=X,window.lnCore.attrSpec=Y,window.lnCore.attrStr=g,window.lnCore.attrInt=S,window.lnCore.attrBool=v,window.lnCore.attrList=E,window.lnCore.attrEnum=L,window.lnCore.attrFloat=q,window.lnCore.attrJson=D);function h(e,t){if(!e[t.attribute]){window.lnCore._persistSink&&e.hasAttribute&&e.hasAttribute("data-ln-persist")&&window.lnCore._persistSink(e,t.selector);try{e[t.attribute]=new t.ComponentFn(e)}catch(r){console.error("["+t.attribute+"] init failed",e,r)}}}function ee(e,t,r,o){if(e.nodeType!==1)return;const c=t.indexOf("[")!==-1||t.indexOf(".")!==-1||t.indexOf("#")!==-1?t:"["+t+"]",i={selector:t,attribute:r,ComponentFn:o};if(e.matches&&e.matches(c)&&h(e,i),e.querySelectorAll){const s=e.querySelectorAll(c);for(let n=0;n<s.length;n++)h(s[n],i)}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore._bootHolds=window.lnCore._bootHolds||0,window.lnCore._bootQueue=window.lnCore._bootQueue||[]);function O(){window.lnCore=window.lnCore||{};const e=window.lnCore._attrRegistry=window.lnCore._attrRegistry||{byAttr:new Map,reactive:[],persist:[]};return e.byReactive=e.byReactive||new Map,e.reactiveWildcard=e.reactiveWildcard||[],e}function te(e){const t=O(),r=e.observed||[];for(let o=0;o<r.length;o++){const l=r[o];t.byAttr.has(l)||t.byAttr.set(l,[]),t.byAttr.get(l).push(e)}if(t.byDeclaredAttr=t.byDeclaredAttr||new Map,e.attributes)for(const o in e.attributes){t.byDeclaredAttr.has(o)||t.byDeclaredAttr.set(o,[]);const l=t.byDeclaredAttr.get(o);l.some(c=>c.componentTag===e.componentTag)||l.push({spec:e.attributes[o],componentTag:e.componentTag})}if(e.onAttrChange||e.effects){t.reactive.push(e);const o=Z(e);if(o===null)t.reactiveWildcard.push(e);else for(const l of o)t.byReactive.has(l)||t.byReactive.set(l,[]),t.byReactive.get(l).push(e)}e.persist&&t.persist.push(e)}function F(e,t,r,o){for(let l=0;l<e.length;l++){const c=e[l];if(!t[c.attribute])continue;const i=c.effects&&c.effects[r];i?i(t,r,o):c.onAttrChange&&(!c.declared||c.declared.has(r))&&c.onAttrChange(t,r,o)}}function ne(e){const t=e.attributeName,r=t.indexOf("data-ln-")===0,o=O(),l=o.byAttr.get(t),c=N()&&o.byDeclaredAttr&&o.byDeclaredAttr.has(t);if(!r&&!l&&!c)return;const i=e.target;if(e.oldValue!==i.getAttribute(t)){if(c&&y(i)){const s=o.byDeclaredAttr.get(t),n=i.getAttribute(t);for(let a=0;a<s.length;a++)z(s[a].spec,n,t,s[a].componentTag,i)}if(window.lnCore._debugSink&&(r||l)&&window.lnCore._debugSink("attr",t,i,{oldValue:e.oldValue,newValue:i.getAttribute(t)}),r){const s=o.byReactive.get(t);s&&F(s,i,t,e.oldValue),o.reactiveWildcard.length&&F(o.reactiveWildcard,i,t,e.oldValue)}if(l)for(let s=0;s<l.length;s++){const n=l[s];if(n.handler){n.handler(i,t,e.oldValue);continue}n.onAttributeChange&&i[n.attribute]?n.onAttributeChange(i,t):(ee(i,n.selector,n.attribute,n.ComponentFn),n.onInit&&n.onInit(i))}}}function re(){window.lnCore=window.lnCore||{},!window.lnCore._attrObserverBound&&(window.lnCore._attrObserverBound=!0,w(function(){new MutationObserver(function(t){for(let r=0;r<t.length;r++)try{ne(t[r])}catch(o){console.error("[ln-core] mutation handler failed",t[r].target,o)}}).observe(document.body,{attributes:!0,subtree:!0,attributeOldValue:!0})},"ln-core"))}function I(){return window.lnCore=window.lnCore||{},window.lnCore._lifecycleRegistry=window.lnCore._lifecycleRegistry||[]}function $(){return window.lnCore=window.lnCore||{},window.lnCore._subtreeRegistry=window.lnCore._subtreeRegistry||[]}function R(e,t){if(!document.contains(e))if(t.attribute){const r=e[t.attribute];if(r&&typeof r.destroy=="function")try{r.destroy()}catch(o){console.error("["+t.attribute+"] destroy failed",e,o)}delete e[t.attribute]}else t.onDestroy&&t.onDestroy(e)}function oe(e){const t=I();if(!t.length)return;const r=e.target;if(r){const l=r.nodeType===1?r:r.parentElement;if(l&&(l.isContentEditable||l.closest&&l.closest('[contenteditable]:not([contenteditable="false"])')))return}const o=$();if(e.target&&o.length)for(let l=0;l<o.length;l++){const c=o[l],i=c.query,s=e.target.nodeType===1?e.target.matches&&e.target.matches(i)?e.target:e.target.closest?e.target.closest(i):null:e.target.parentElement&&e.target.parentElement.closest?e.target.parentElement.closest(i):null;s&&c.onSubtreeChange(s,e)}for(let l=0;l<e.addedNodes.length;l++){const c=e.addedNodes[l];if(c.nodeType!==1)continue;const i=!!(c.firstElementChild||c.children&&c.children.length);for(let s=0;s<t.length;s++){const n=t[s],a=n.query;if(a&&(c.matches&&c.matches(a)&&(n.ComponentFn&&h(c,n),n.onInit&&n.onInit(c)),i&&c.querySelectorAll)){const u=c.querySelectorAll(a);for(let d=0;d<u.length;d++)n.ComponentFn&&h(u[d],n),n.onInit&&n.onInit(u[d])}}}for(let l=0;l<e.removedNodes.length;l++){const c=e.removedNodes[l];if(c.nodeType!==1||document.contains(c))continue;const i=!!(c.firstElementChild||c.children&&c.children.length);for(let s=0;s<t.length;s++){const n=t[s],a=n.query;if(a&&(c.matches&&c.matches(a)&&R(c,n),i&&c.querySelectorAll)){const u=c.querySelectorAll(a);for(let d=0;d<u.length;d++)R(u[d],n)}}}}function ie(){window.lnCore=window.lnCore||{},!window.lnCore._lifecycleObserverBound&&(window.lnCore._lifecycleObserverBound=!0,w(function(){const e=new MutationObserver(function(t){for(let r=0;r<t.length;r++){const o=t[r];if(o.type==="childList")try{oe(o)}catch(l){console.error("[ln-core] lifecycle handler failed",o.target,l)}}});window.lnCore._lifecycleObserver=e,e.observe(document.body,{childList:!0,subtree:!0})},"ln-core"))}function le(e,t){te({observed:e,handler:t}),re()}function se(e){I().push(e),e.onSubtreeChange&&e.query&&$().push(e),ie()}function T(e){return(e||"").replace(/^#/,"")}function C(e){const t=e===void 0?location.hash:e,r={},o=T(t);if(!o)return r;const l=o.split("&");for(let c=0;c<l.length;c++){const i=l[c];if(!i)continue;const s=i.indexOf(":"),n=s>-1?i.slice(0,s):i,a=s>-1?i.slice(s+1):"";if(n)try{r[n]=decodeURIComponent(a)}catch{r[n]=a}}return r}function ce(e){if(!e)return null;const t=C();return e in t?t[e]:null}function ae(e,t){if(!e)return;const r=C();t==null?delete r[e]:r[e]=String(t);const l=Object.keys(r).map(function(c){const i=r[c];return i===""?c:c+":"+encodeURIComponent(i)}).join("&");T(location.hash)!==l&&(location.hash=l)}function ue(e){return e.button===1||e.ctrlKey||e.metaKey||e.shiftKey?!1:(e.preventDefault(),!0)}function de(e,t){if(!e||!e.hasAttribute("data-ln-hash"))return null;const r=e.getAttribute("data-ln-hash");if(r&&r.trim()!=="")return r.trim();const o=e.getAttribute("data-ln-sort")||e.getAttribute("data-ln-search-for")||e.getAttribute("data-ln-search")||e.getAttribute("data-ln-filter")||e.id;return o?t?o+"-"+t:o:t||null}function fe(e,t){return!t||t==="none"||e===null||e===void 0?null:String(e)+"."+t}function we(e){return!e||typeof e!="string"?null:e.endsWith(".asc")?{fieldOrColumn:e.slice(0,-4),direction:"asc"}:e.endsWith(".desc")?{fieldOrColumn:e.slice(0,-5),direction:"desc"}:null}function he(e,t){return!e||!Array.isArray(t)||t.length===0?null:e+":"+t.map(encodeURIComponent).join(",")}function pe(e){if(!e||typeof e!="string")return null;const t=e.indexOf(":");if(t===-1)return null;const r=e.slice(0,t),o=e.slice(t+1),l=o?o.split(",").map(function(c){try{return decodeURIComponent(c)}catch{return c}}).filter(Boolean):[];return{key:r,values:l}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.hashParse=C,window.lnCore.hashGet=ce,window.lnCore.hashSet=ae,window.lnCore.hashLinkClick=ue,window.lnCore.resolveHashNamespace=de,window.lnCore.hashSortEncode=fe,window.lnCore.hashSortDecode=we,window.lnCore.hashFilterEncode=he,window.lnCore.hashFilterDecode=pe);function be(e){return(e||"").replace(/^\?/,"")}function ge(e){const t=e===void 0?typeof window<"u"?window.location.search:"":e,r={},o=be(t);if(!o)return r;const l=new URLSearchParams(o);for(const[c,i]of l.entries())r[c]=i;return r}function me(e){if(!e||typeof window>"u")return null;const t=new URLSearchParams(window.location.search);return t.has(e)?t.get(e):null}function Ce(e,t){if(!e||typeof window>"u")return;const r=new URL(window.location.href);if(t==null||t===""?r.searchParams.delete(e):r.searchParams.set(e,String(t)),r.search===window.location.search)return;const o=r.pathname+r.search+r.hash;window.history.replaceState(window.history.state,"",o)}function ye(e,t){if(!e||!e.hasAttribute("data-ln-query"))return null;const r=e.getAttribute("data-ln-query");if(r&&r.trim()!=="")return r.trim();const o=e.getAttribute("data-ln-sort")||e.getAttribute("data-ln-search-for")||e.getAttribute("data-ln-search")||e.getAttribute("data-ln-filter")||e.id;return o?t?o+"_"+t:o:t||null}function Ae(e,t){return!t||t==="none"||e===null||e===void 0?null:String(e)+"."+t}function xe(e,t){if(e&&typeof e=="string"){if(e.endsWith(".asc"))return{fieldOrColumn:e.slice(0,-4),direction:"asc"};if(e.endsWith(".desc"))return{fieldOrColumn:e.slice(0,-5),direction:"desc"}}if(t&&typeof t=="object"){const r=t.sort_field||t.sort_by;if(r){const o=t.sort_dir&&String(t.sort_dir).toLowerCase()==="desc"?"desc":"asc";return{fieldOrColumn:r,direction:o}}}return null}function _e(e){return!Array.isArray(e)||e.length===0?null:e.map(encodeURIComponent).join(",")}function ke(e){return!e||typeof e!="string"?[]:e.split(",").map(function(t){try{return decodeURIComponent(t.trim())}catch{return t.trim()}}).filter(Boolean)}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.queryParse=ge,window.lnCore.queryGet=me,window.lnCore.querySet=Ce,window.lnCore.resolveQueryParam=ye,window.lnCore.querySortEncode=Ae,window.lnCore.querySortDecode=xe,window.lnCore.queryFilterEncode=_e,window.lnCore.queryFilterDecode=ke);function Se(){return typeof crypto<"u"&&typeof crypto.randomUUID=="function"?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{const t=Math.random()*16|0;return(e==="x"?t:t&3|8).toString(16)})}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.uuid=Se),(function(){const e="lnExternalLinks";if(window[e]!==void 0)return;function t(s){return s.hostname&&s.hostname!==window.location.hostname}function r(s){if(s.getAttribute("data-ln-external-link")==="processed"||!t(s))return;s.target="_blank";const n=(s.rel||"").split(/\s+/).filter(Boolean);n.includes("noopener")||n.push("noopener"),n.includes("noreferrer")||n.push("noreferrer"),s.rel=n.join(" ");const a=document.createElement("span");a.setAttribute("data-ln-external-links-hint",""),a.textContent="(opens in new tab)",s.appendChild(a),s.setAttribute("data-ln-external-link","processed"),p(s,"ln-external-links:processed",{link:s,href:s.href})}function o(s){s=s||document.body;for(const n of s.querySelectorAll("a, area"))r(n)}function l(){w(function(){document.body.addEventListener("click",function(s){const n=s.target.closest("a, area");n&&n.getAttribute("data-ln-external-link")==="processed"&&p(n,"ln-external-links:clicked",{link:n,href:n.href,text:n.textContent||n.title||""})})},"ln-external-links")}function c(){w(function(){se({onInit:function(s){if(s.matches&&(s.matches("a")||s.matches("area"))&&r(s),s.querySelectorAll)for(const n of s.querySelectorAll("a, area"))r(n)}}),le(["href"],function(s){s.matches&&(s.matches("a")||s.matches("area"))&&r(s)})},"ln-external-links")}function i(){l(),c(),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",function(){o()}):o()}window[e]={process:o},i()})()})();
@@ -13,6 +13,15 @@
13
13
  "processed"
14
14
  ],
15
15
  "description": "Marker indicating link was identified and processed as external"
16
+ },
17
+ "data-ln-external-links-hint": {
18
+ "direction": "runtime",
19
+ "sources": [
20
+ "ln-external-links.scss",
21
+ "src/ln-external-links.js"
22
+ ],
23
+ "type": "marker",
24
+ "description": "Marks the visually hidden "
16
25
  }
17
26
  }
18
27
  }
@@ -0,0 +1,12 @@
1
+ // ==========================================================================
2
+ // ln-external-links — Functional Hiding Contract
3
+ // ==========================================================================
4
+
5
+ // The injected "(opens in new tab)" hint is a screen-reader-only span. This file
6
+ // ships in the CORE bundle and the theme `.sr-only` utility does not, so the
7
+ // hiding cannot be delegated to the theme.
8
+ @use '../../theme/config/mixins/display' as *;
9
+
10
+ [data-ln-external-links-hint] {
11
+ @include sr-only;
12
+ }
@@ -7,7 +7,8 @@ import { dispatch, guardBody, observeAttributes, observeLifecycle } from '../../
7
7
 
8
8
  // ─── Attribute Contract (SSOT) ──────────────────────────
9
9
  const ATTRIBUTES = {
10
- 'data-ln-external-link': { type: 'enum', values: ['processed'], description: 'Marker indicating link was identified and processed as external' }
10
+ 'data-ln-external-link': { type: 'enum', values: ['processed'], description: 'Marker indicating link was identified and processed as external' },
11
+ 'data-ln-external-links-hint': { type: 'marker', description: 'Marks the visually hidden "(opens in new tab)" hint span appended inside a processed link' }
11
12
  };
12
13
 
13
14
  function _isExternalLink(link) {
@@ -25,7 +26,7 @@ import { dispatch, guardBody, observeAttributes, observeLifecycle } from '../../
25
26
  link.rel = existing.join(' ');
26
27
 
27
28
  const hint = document.createElement('span');
28
- hint.className = 'sr-only';
29
+ hint.setAttribute('data-ln-external-links-hint', '');
29
30
  hint.textContent = '(opens in new tab)';
30
31
  link.appendChild(hint);
31
32
 
@@ -78,6 +78,7 @@ Filters plain `<table>` rows by Column Index `2` (Department) and saves state to
78
78
  | `data-ln-query` | Container root | Opt-in. Synchronizes active filter values to URL query parameter (e.g. `?category=design,dev` or `?status=active`). Value is parameter name (if omitted, falls back to `data-ln-filter-col` or `data-ln-filter-key`). Interoperable with browser back/forward history (`popstate`). |
79
79
  | `data-ln-filter-values` | Container root | *State*. Comma-joined, percent-encoded list of currently active filter values. Written by the component, read by `ln-persist` for restore/save. |
80
80
  | `data-ln-filter-hide` | Children of target | *State*. Automatically toggled on non-matching elements (`display: none !important`). |
81
+ | `data-ln-table-empty` | Table rows | Rows carrying this attribute are skipped when filtering plain-table rows. ln-table writes it on its empty and loading placeholder row; author it on a hand-written placeholder row in a plain table. |
81
82
 
82
83
 
83
84
  ---
@@ -102,7 +103,7 @@ The canonical composition for table per-column filters.
102
103
  ```html
103
104
  <th data-ln-table-filter-col="department">
104
105
  Department
105
- <button class="table-filter" type="button"
106
+ <button type="button"
106
107
  data-ln-table-col-filter
107
108
  data-ln-popover-for="filter-my-table-dept"
108
109
  aria-label="Filter department">