{
  "version": 1,
  "base": "base",
  "modules": {
    "base": {
      "path": "/library/assets/base.css",
      "css": ":root{--motion-normal:300ms;--motion-ease:ease-in-out;--motion-distance:8px;color-scheme:light;background:var(--bg);--error:#b42336;--bg:#f7f7f2;--surface:#fff;--soft:#eeefe9;--ink:#202923;--muted:#5e685f;--line:#d9ddd4;--accent:#294cda;--accent-hover:#203cac;--tint:#e9edff;--on-accent:#fff;--radius:12px;--space:1.5rem;--page-gutter:32px;--content-width:1200px;--shadow:0 24px 70px #26322112;font-family:Inter,ui-sans-serif,-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif;font-size:16px;line-height:1.6}:root[data-theme=dark]{color-scheme:dark;--error:#ff9caa;--bg:#141916;--surface:#1d2420;--soft:#262f28;--ink:#edf1e9;--muted:#abb6ad;--line:#38433b;--accent:#a8b8ff;--accent-hover:#c5cfff;--tint:#283352;--on-accent:#152247;--shadow:0 24px 70px #0003}:root[data-palette=iris]{--accent:#7040bd;--accent-hover:#572c9c;--tint:#f0e8fc}:root[data-palette=grove]{--accent:#216b47;--accent-hover:#174f34;--tint:#e1f1e7}:root[data-theme=dark][data-palette=iris]{--accent:#cfb0ff;--accent-hover:#e0cdff;--tint:#3b2e4f;--on-accent:#302044}:root[data-theme=dark][data-palette=grove]{--accent:#94d8ae;--accent-hover:#b7e8ca;--tint:#253e31;--on-accent:#153322}:root[data-palette=rose]{--accent:#c21e47;--accent-hover:#9a1839;--tint:#fdf2f5;--on-accent:#ffffff;}:root[data-theme=dark][data-palette=rose]{--accent:#f1a7b9;--accent-hover:#f7cad5;--tint:#350813;--on-accent:#350813;}:root[data-palette=amber]{--accent:#815d08;--accent-hover:#604506;--tint:#fefaf1;--on-accent:#ffffff;}:root[data-theme=dark][data-palette=amber]{--accent:#f9de9f;--accent-hover:#fbebc5;--tint:#3a2904;--on-accent:#3a2904;}:root[data-palette=teal]{--accent:#1c6e6a;--accent-hover:#14524f;--tint:#f3fcfb;--on-accent:#ffffff;}:root[data-theme=dark][data-palette=teal]{--accent:#adebe8;--accent-hover:#cef3f1;--tint:#0c312f;--on-accent:#0c312f;}:root[data-palette=sky]{--accent:#1365a0;--accent-hover:#0e4e7b;--tint:#f1f8fd;--on-accent:#ffffff;}:root[data-theme=dark][data-palette=sky]{--accent:#a4d3f4;--accent-hover:#c8e4f9;--tint:#062337;--on-accent:#062337;}:root[data-palette=slate]{--accent:#5f6c81;--accent-hover:#4c5667;--tint:#f6f7f8;--on-accent:#ffffff;}:root[data-theme=dark][data-palette=slate]{--accent:#c4cad4;--accent-hover:#dcdfe5;--tint:#1a1d23;--on-accent:#1a1d23;}:root[data-palette=orange]{--accent:#a64c0c;--accent-hover:#803b0a;--tint:#fef6f1;--on-accent:#ffffff;}:root[data-theme=dark][data-palette=orange]{--accent:#f8c5a0;--accent-hover:#fbdcc6;--tint:#391a04;--on-accent:#391a04;}*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink)}a{color:var(--accent);text-underline-offset:4px}button,input,select,textarea{font:inherit}button,a,input,select,textarea{touch-action:manipulation}button{cursor:pointer}button:disabled{cursor:default;opacity:.55}img,svg{max-width:100%}h1,h2,h3,p{margin:0}h1,h2,h3{line-height:1.12;letter-spacing:-.045em}h1{font-size:clamp(2.8rem,6.3vw,5.5rem);font-weight:550}h2{font-size:clamp(2rem,3.5vw,3rem);font-weight:550}h3{font-size:1.25rem;font-weight:600}p{max-width:65ch}small{font-size:.8rem}button,a,input,select,textarea,summary,[tabindex]{outline-offset:5px}:focus-visible{outline:2px solid var(--accent)}main[tabindex=\"-1\"]:focus{outline:none}[hidden]{display:none!important}@media(prefers-reduced-motion:no-preference){:where(a,button,input,select,textarea){transition:background-color var(--motion-normal) var(--motion-ease),color var(--motion-normal) var(--motion-ease),border-color var(--motion-normal) var(--motion-ease),box-shadow var(--motion-normal) var(--motion-ease)}}@media(prefers-reduced-motion:reduce){*,::before,::after,::backdrop,::details-content{animation:none!important;transition:none!important;scroll-behavior:auto!important}}:where([data-motion=none],[data-motion=none] *),:where([data-motion=none],[data-motion=none] *)::before,:where([data-motion=none],[data-motion=none] *)::after,:where([data-motion=none],[data-motion=none] *)::backdrop,:where([data-motion=none],[data-motion=none] *)::details-content{animation:none!important;transition:none!important;scroll-behavior:auto!important}.container{width:min(var(--content-width),calc(100% - 2 * var(--page-gutter)));margin-inline:auto}.stack{display:flex;flex-direction:column;gap:var(--space)}.cluster{display:flex;align-items:center;gap:1rem;flex-wrap:wrap}.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,240px),1fr));gap:var(--space)}[data-canvas]{position:relative;isolation:isolate}[data-canvas]::before{content:\"\";position:absolute;inset:0;z-index:-1;pointer-events:none;border-radius:inherit;opacity:var(--canvas-opacity,.2);background-size:var(--canvas-step,24px) var(--canvas-step,24px);mask-image:linear-gradient(90deg,#000,transparent 35%,transparent 65%,#000)}[data-canvas=\"dots\"]::before{background-image:radial-gradient(var(--line) 1px,transparent 1px)}[data-canvas=\"grid\"]::before{background-image:linear-gradient(var(--line) 1px,transparent 1px),linear-gradient(90deg,var(--line) 1px,transparent 1px)}[data-ruler]::after{content:\"\";position:absolute;inset:0 24px auto;height:8px;border-bottom:1px solid var(--line);background:repeating-linear-gradient(90deg,var(--line) 0 1px,transparent 1px 24px);pointer-events:none}[data-finish=flat]{--finish-image:none;--finish-button:none;--finish-shadow:none;--finish-edge:color-mix(in srgb,var(--line) 60%,var(--surface));--finish-blur:none;--finish-surface:var(--surface);}[data-finish=gradient]{--finish-image:linear-gradient(145deg,color-mix(in srgb,var(--accent) 6%,transparent),color-mix(in srgb,var(--ink) 2%,transparent));--finish-button:linear-gradient(145deg,#ffffff08,#00000008);--finish-shadow:0 3px 12px #0000000a;--finish-edge:color-mix(in srgb,var(--line) 60%,var(--surface));--finish-blur:none;--finish-surface:var(--surface);}[data-finish=glass]{--finish-image:linear-gradient(145deg,#ffffff30,transparent 48%,#0000000a);--finish-button:linear-gradient(145deg,#ffffff30,transparent 48%,#0000000a);--finish-shadow:inset 0 .5px #ffffff26,0 8px 28px #0000001a;--finish-edge:color-mix(in srgb,var(--line) 45%,transparent);--finish-blur:none;--finish-surface:var(--surface);}@supports(backdrop-filter:blur(20px)){[data-finish=glass]{--finish-surface:color-mix(in srgb,var(--surface) 68%,transparent);--finish-blur:blur(20px) saturate(1.5)}}.surface{background-color:var(--finish-surface,var(--surface));backdrop-filter:var(--finish-blur,none);background-image:var(--finish-image,none);box-shadow:var(--finish-shadow,none);border-color:var(--finish-edge,var(--line))}.text-gradient{color:var(--accent);padding-inline-end:.08em;margin-inline-end:-.08em;box-decoration-break:clone;-webkit-box-decoration-break:clone}@supports(background-clip:text){.text-gradient{background:var(--paint-text,linear-gradient(120deg,var(--accent),color-mix(in srgb,var(--accent) 80%,var(--ink))));background-clip:text;-webkit-text-fill-color:transparent}}@media(forced-colors:active){.text-gradient{background:none;-webkit-text-fill-color:currentColor}}@media(prefers-reduced-transparency:reduce){[data-finish=glass]{--finish-surface:var(--surface);--finish-blur:none}}[data-guides]{border-inline:1px solid color-mix(in srgb,var(--line) 50%,transparent)}:is(.field-error,.color-picker-error,[data-range-error]){margin:0;color:var(--error);font-size:.85rem;font-weight:500}:is(.field-error,.color-picker-error,[data-range-error]):not(:empty){position:relative;padding-inline-start:24px}:is(.field-error,.color-picker-error,[data-range-error]):not(:empty)::before{content:\"!\"/\"\";position:absolute;inset-inline-start:0;top:.2em;width:16px;height:16px;display:grid;place-items:center;border:1.5px solid currentColor;border-radius:50%;font:700 12px/1 sans-serif;box-sizing:border-box}fieldset{min-inline-size:0;margin:0;padding:16px;border:1px solid var(--finish-edge,var(--line));border-radius:var(--radius)}legend{max-inline-size:100%;padding-inline:8px;color:var(--ink);font-size:.85rem;font-weight:600;line-height:1.5;overflow-wrap:anywhere}.muted{color:var(--muted)}.eyebrow{text-transform:uppercase;letter-spacing:.16em;font-size:.69rem;font-weight:700;color:var(--muted)}@media(max-width:760px){:root{--page-gutter:16px}}",
      "bytes": 8122,
      "hash": "3567038049f06a5f4345d2223ba1db36b6d04003fd054c2d53423d7629cb2137"
    },
    "link": {
      "path": "/library/assets/link.css",
      "css": ".link-underline{align-self:flex-start;overflow-wrap:anywhere;text-decoration:none;background:linear-gradient(currentColor,currentColor) no-repeat left bottom/100% 1px}.link-underline:dir(rtl){background-position:right bottom}.link-underline:hover{color:var(--accent-hover)}.link-underline:active{color:var(--ink)}.link-animated{background-size:0 1px}.link-animated:is(:hover,:focus-visible){background-size:100% 1px}.link-animated:focus-visible{transition:none}@media(prefers-reduced-motion:no-preference){.link-animated{transition:background-size var(--motion-normal) var(--motion-ease),color var(--motion-normal) var(--motion-ease)}}:root[data-palette=lime]{--accent:#517217;--accent-hover:#3c5511;--tint:#f9fcf2;--on-accent:#ffffff;}:root[data-theme=dark][data-palette=lime]{--accent:#d5eeaa;--accent-hover:#e6f5cc;--tint:#24330a;--on-accent:#24330a;}",
      "bytes": 854,
      "hash": "560df0b7ed475400ca95ccdb493d97553722a5a3d2dea2848e17ea6c8304dc64"
    },
    "panel": {
      "path": "/library/assets/panel.css",
      "css": ".panel{min-width:0;overflow-wrap:anywhere;background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);padding:var(--space)}.panel{box-shadow:var(--finish-shadow,none);border-color:var(--finish-edge,var(--line));background-color:var(--finish-surface,var(--surface));background-image:var(--finish-image,none);backdrop-filter:var(--finish-blur,none)}.panel-link{text-decoration:none;color:var(--ink)}.panel-link:hover{border-color:color-mix(in srgb,var(--accent) 40%,var(--finish-edge,var(--line)))}.panel>:not(dialog,[popover]):first-child{margin-top:0}.panel>:not(dialog,[popover]):last-child{margin-bottom:0}",
      "bytes": 631,
      "hash": "bc4a0231c3cb835db4b651e9659097093efc08ae3697e5695a9c86f3d3b13115"
    },
    "button": {
      "path": "/library/assets/button.css",
      "css": ".btn{display:inline-flex;align-items:center;justify-content:center;gap:.6rem;min-height:44px;padding:.65rem 1.1rem;border:1px solid transparent;border-radius:var(--control-radius,7px);background:var(--brand,var(--accent));color:var(--on-brand,var(--on-accent));text-decoration:none;font-size:.86rem;font-weight:600;max-width:100%;overflow-wrap:anywhere;text-align:center}.btn.compact{padding-inline:.6rem}.btn:not(:disabled):hover{background-color:var(--brand-hover,var(--accent-hover))}.btn.secondary{background:color-mix(in srgb,var(--surface) 82%,transparent);border-color:var(--line);color:var(--ink)}.btn.secondary:not(:disabled):hover{background-color:color-mix(in srgb,var(--ink) 6%,transparent)}.btn.ghost{background:transparent;color:var(--ink)}.btn.ghost:not(:disabled):hover{background-color:color-mix(in srgb,var(--ink) 6%,transparent)}.btn:not(.ghost){background-image:var(--finish-button,none);box-shadow:var(--finish-shadow,none)}.btn.secondary{background-color:color-mix(in srgb,var(--surface) 82%,transparent);backdrop-filter:var(--finish-blur,none);border-color:var(--finish-edge,var(--line))}.btn:not(:disabled):not(.ghost):hover{box-shadow:inset 0 0 0 1px color-mix(in srgb,currentColor 12%,transparent),var(--finish-shadow,0 0 transparent)}.btn-icon{width:44px;height:44px;flex:none;padding:0}.btn>svg{width:1.25em;height:1.25em;flex:none}.btn-large{min-height:52px;padding:.8rem 1.4rem}.btn-pill{border-radius:999px}.btn:not(:disabled):active{background-color:var(--brand-hover,var(--accent-hover))}.btn.secondary:not(:disabled):active,.btn.ghost:not(:disabled):active{background-color:color-mix(in srgb,var(--ink) 10%,transparent)}.btn-choice{flex-direction:column;gap:8px;padding:12px 8px;line-height:24px;white-space:nowrap;outline-offset:-2px}.btn-gradient:not(.secondary):not(.ghost){background-image:var(--paint-button,var(--finish-button,none));color:var(--on-paint-button,var(--on-brand,var(--on-accent)))}.btn-gradient:not(.secondary):not(.ghost):not(:disabled):is(:hover,:active){background-image:var(--paint-button-hover,var(--paint-button,var(--finish-button,none)))}",
      "bytes": 2101,
      "hash": "3123be2c70538f722b17f1ceecb87c6810860d0b04895b1756f6b56d14b704be"
    },
    "badge": {
      "path": "/library/assets/badge.css",
      "css": ".badge{display:inline-flex;max-width:100%;vertical-align:middle;overflow-wrap:anywhere;align-items:center;gap:.4rem;border-radius:1em;background:color-mix(in srgb,var(--accent) 8%,transparent);color:color-mix(in srgb,var(--accent) 80%,var(--ink));padding:.25em .75em;font-size:.75rem;font-weight:600;line-height:1.5;align-self:flex-start}.badge-neutral{background:color-mix(in srgb,var(--ink) 6%,transparent);color:var(--ink)}.badge-outline{background:transparent;box-shadow:inset 0 0 0 1px var(--line);color:var(--ink)}.badge-solid{background:var(--accent);color:var(--on-accent)}.badge-rounded{border-radius:5px}.badge-pill,.badge-count{border-radius:999px}.badge-count{min-width:2em;justify-content:center;font-variant-numeric:tabular-nums}.badge>svg{width:1em;height:1em;flex:none}",
      "bytes": 785,
      "hash": "4605b6e93cc2217af9f86eac83ce31cf7d44111da7cca1cee853bdcbf5e17e42"
    },
    "field": {
      "path": "/library/assets/field.css",
      "css": "input,select,textarea{border:1px solid var(--line);border-radius:var(--control-radius,7px);padding:.65rem .8rem;background:var(--surface);color:var(--ink);min-width:0;max-width:100%}select:not([multiple]):not([size]):not(.select-control>select){appearance:none;min-height:44px;font-size:1rem;padding-inline-end:44px;background-image:linear-gradient(45deg,transparent 40%,var(--muted) 40% 60%,transparent 60%),linear-gradient(135deg,transparent 40%,var(--muted) 40% 60%,transparent 60%);background-position:right 23px center,right 18px center;background-size:5px 5px;background-repeat:no-repeat}select:not([multiple]):not([size]):not(.select-control>select):dir(rtl){background-position:left 18px center,left 23px center}@media(forced-colors:active){select:not([multiple]):not([size]):not(.select-control>select){appearance:auto;background-image:none}}input:where(:not([type=checkbox],[type=radio],[type=range],[type=hidden],[type=button],[type=submit],[type=reset],[type=image],[type=color])),select,textarea{min-height:44px;font-size:1rem;line-height:1.4;border-color:var(--muted)}:is(input,textarea)::placeholder{color:var(--muted);opacity:1}:is(input,select,textarea):where(:disabled),:is(input,textarea):where([readonly]){background-color:var(--soft)}:is(input,select,textarea):where(:disabled){cursor:not-allowed}:is(input,select,textarea):where([aria-invalid=true]){border:2px dashed var(--error)}textarea{min-height:96px;resize:vertical}input:where([type=checkbox],[type=radio]){width:20px;height:20px;padding:0;margin:0;vertical-align:middle;accent-color:var(--brand,var(--accent))}input:where([type=range]){padding:0;min-height:44px;accent-color:var(--brand,var(--accent))}input:where([type=color]){width:44px;height:44px;padding:4px}button:where(:not([class])),input:where([type=button],[type=submit],[type=reset]),input::file-selector-button{min-height:44px;padding:10px 16px;border:1px solid var(--line);border-radius:var(--control-radius,8px);background:var(--surface);color:var(--ink);font:inherit;font-weight:600;cursor:pointer}:where(button:not([class]),input[type=button],input[type=submit],input[type=reset]):hover:not(:disabled){background:var(--soft)}label{font-size:.85rem;font-weight:550}label.stack{gap:.4rem}.field>p{margin:0}.field{display:flex;flex-direction:column;gap:8px;min-width:0;overflow-wrap:anywhere}.input{min-height:44px;font-size:1rem;border-color:var(--muted)}.input::placeholder{color:var(--muted);opacity:1}.input:disabled,.input[readonly]{background:var(--soft)}.input:disabled{cursor:not-allowed}.input[aria-invalid=true]{border-width:2px;border-style:dashed;border-color:var(--error)}.input-large{min-height:52px}",
      "bytes": 2657,
      "hash": "1f5471c564ed43b057c5a3162aada273c1c6d2afa3abca984e814044c0745133"
    },
    "table": {
      "path": "/library/assets/table.css",
      "css": "table{border-collapse:collapse;width:100%;font-size:.87rem;text-align:start}td,th{overflow-wrap:anywhere;vertical-align:top;padding:1rem .8rem;border-bottom:1px solid var(--line)}th{text-align:inherit;font-size:.72rem;color:var(--muted);font-weight:600}caption{text-align:start;margin-bottom:1rem;font-weight:600}.table-scroll{min-width:0;max-width:100%;overflow-x:auto}.table-scroll table{min-width:var(--table-min,28rem)}tbody th,tfoot th{font-size:inherit;color:inherit}.table-number{text-align:end;font-variant-numeric:tabular-nums;white-space:nowrap}.table-striped tbody tr:nth-child(even),tfoot{background:color-mix(in srgb,var(--ink) 4%,transparent)}.table-compact :is(td,th){padding:.5rem .8rem}",
      "bytes": 703,
      "hash": "eeadcd2b09d22f67266674d55e672014d4ca3242cfc5bf5b9987efdfacd703f6"
    },
    "disclosure": {
      "path": "/library/assets/disclosure.css",
      "css": "summary{display:flex;align-items:center;justify-content:space-between;gap:24px;cursor:pointer;user-select:none;-webkit-user-select:none;list-style:none;font-weight:500;padding:20px 0}summary::-webkit-details-marker{display:none}summary::after{content:\"\";flex:none;width:7px;height:7px;margin-inline-end:4px;border:solid var(--muted);border-width:0 1.5px 1.5px 0;transform:rotate(45deg)}details[open]>summary::after{transform:rotate(225deg)}summary:focus-visible{outline:2px solid var(--accent);outline-offset:4px;border-radius:4px}details{overflow-wrap:anywhere;border-bottom:1px solid var(--line)}details>p,details>.disclosure-content{color:var(--muted);margin:0;padding-block:0 20px;padding-inline-end:32px}@media(prefers-reduced-motion:no-preference){summary::after{transition:transform var(--motion-normal) var(--motion-ease)}@supports(interpolate-size:allow-keywords){details::details-content{interpolate-size:allow-keywords;block-size:0;overflow:clip;transition:content-visibility var(--motion-normal) allow-discrete,block-size var(--motion-normal) var(--motion-ease)}details[open]::details-content{block-size:auto}}}",
      "bytes": 1123,
      "hash": "7f02e39b06343af52dea61aa0d0abc77155ba32c4709684b36eb8576dea0bd70"
    },
    "chart": {
      "path": "/library/assets/chart.css",
      "css": "figure:has(.bar-chart){margin:0}.bar-chart{display:grid;gap:16px;margin:0}.bar-chart div{display:grid;grid-template-columns:minmax(4rem,auto) minmax(0,1fr) 3rem;align-items:center;gap:16px}.bar-chart dd{margin:0}.bar-chart meter{appearance:none;display:block;width:100%;height:12px;border:0;border-radius:999px;background:color-mix(in srgb,var(--ink) 6%,transparent);accent-color:var(--bar-color,var(--accent))}.bar-chart meter::-webkit-meter-bar{height:12px;border:0;border-radius:999px;background:color-mix(in srgb,var(--ink) 6%,transparent);box-shadow:none}.bar-chart meter::-webkit-meter-optimum-value,.bar-chart meter::-webkit-meter-suboptimum-value,.bar-chart meter::-webkit-meter-even-less-good-value{background:var(--bar-color,var(--accent));border-radius:999px}.bar-chart meter::-moz-meter-bar{background:var(--bar-color,var(--accent));border-radius:999px}",
      "bytes": 865,
      "hash": "e76f76e307a59d5b6caa5b39cf186593815e74d7b4deb80400c50400ef0f19c4"
    },
    "empty-state": {
      "path": "/library/assets/empty-state.css",
      "css": ".empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;padding:clamp(24px,6vw,64px);text-align:center;color:var(--ink);min-width:0}.empty-state>h2,.empty-state>h3,.empty-state>p{margin:0}.empty-state>p{max-width:36ch;color:var(--muted)}.empty-state>.cluster{justify-content:center;flex-wrap:wrap}.empty-state>h2,.empty-state>h3{font-size:1.5rem;line-height:1.3}.empty-state>.icon{width:48px;height:48px;flex:none;color:var(--muted)}",
      "bytes": 476,
      "hash": "39f12b8f018000cdf67150cccd5e6987350cdbdca123781a8aaac83cfa62ab2c"
    },
    "slider": {
      "path": "/library/assets/slider.css",
      "css": ".slider-field{display:grid;gap:4px;min-inline-size:0}.slider-heading{display:flex;justify-content:space-between;gap:16px}.slider-heading output{color:var(--muted);font-variant-numeric:tabular-nums}.slider-field input[type=range]{appearance:none;display:block;inline-size:100%;min-block-size:44px;margin:0;padding:0;border:0;border-radius:var(--control-radius,var(--radius));background:transparent;box-shadow:none;cursor:pointer;accent-color:var(--accent)}.slider-field input[type=range]::-webkit-slider-runnable-track{height:4px;border-radius:var(--control-radius,var(--radius));background:var(--slider-track,var(--line))}.slider-field input[type=range]::-moz-range-track{height:4px;border-radius:var(--control-radius,var(--radius));background:var(--slider-track,var(--line))}.slider-field input[type=range]::-webkit-slider-thumb{appearance:none;width:20px;height:20px;margin-top:-8px;border:2px solid var(--bg);border-radius:50%;background:var(--accent)}.slider-field input[type=range]::-moz-range-thumb{width:16px;height:16px;border:2px solid var(--bg);border-radius:50%;background:var(--accent)}.slider-field input[type=range]:hover{accent-color:var(--accent-hover)}.slider-field input[type=range]:hover::-webkit-slider-thumb{background:var(--accent-hover)}.slider-field input[type=range]:hover::-moz-range-thumb{background:var(--accent-hover)}.slider-field input[type=range]:focus-visible{outline:2px solid var(--accent);outline-offset:2px}.slider-field:has(:disabled){opacity:.55}.slider-field input:disabled{cursor:not-allowed}@media(forced-colors:active){.slider-field input[type=range]{appearance:auto}}",
      "bytes": 1611,
      "hash": "0869b29f12aa46aea5a3faa35cbe9779300515947c8b08156d779aa13fa1feb2",
      "javascript": "(()=>{document.querySelectorAll('.slider-field').forEach(field=>{const input=field.querySelector('input[type=range]'),output=field.querySelector('output');if(!input||!output)return;const sync=()=>{output.value=input.value+(output.dataset.unit||'');};input.addEventListener('input',sync);input.addEventListener('change',sync);input.form?.addEventListener('reset',()=>queueMicrotask(sync));sync();});})();\n",
      "javascriptPath": "/library/assets/slider.js",
      "javascriptBytes": 404
    },
    "spinner": {
      "path": "/library/assets/spinner.css",
      "css": ".spinner{display:inline-block;flex:none;inline-size:var(--spinner-size,20px);block-size:var(--spinner-size,20px);box-sizing:border-box;vertical-align:-.15em}.spinner::before{content:\"\";display:block;width:100%;height:100%;background:currentColor;mask:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%221.75%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Ccircle%20cx%3D%2212%22%20cy%3D%2212%22%20r%3D%229%22%2F%3E%3Cpath%20d%3D%22M12%207v5l3%202%22%2F%3E%3C%2Fsvg%3E\") center/contain no-repeat}.spinner-small{--spinner-size:16px}.spinner-large{--spinner-size:32px}@keyframes spinner-turn{to{transform:rotate(1turn)}}@media(prefers-reduced-motion:no-preference){.spinner{border:2px solid currentColor;border-inline-end-color:transparent;border-radius:50%;animation:spinner-turn calc(var(--motion-normal)*3) linear infinite}.spinner::before{display:none}}[data-motion=none] .spinner,.spinner[data-motion=none]{animation:none;border:0;transform:none}[data-motion=none] .spinner::before,.spinner[data-motion=none]::before{display:block}",
      "bytes": 1192,
      "hash": "25895f07027af92366a4ac9321af9787b54343f8b7c5250c530906c739c76512"
    },
    "toast": {
      "path": "/library/assets/toast.css",
      "css": ".toasts{position:fixed;inset-block-end:max(16px,env(safe-area-inset-bottom));inset-inline:0;margin-inline:auto;width:min(384px,calc(100vw - 32px));max-height:calc(100dvh - 32px);overflow:auto;padding:4px;z-index:1000;pointer-events:none}.toasts[data-position=top],.toasts[data-position^=\"top-\"]{inset-block-start:max(16px,env(safe-area-inset-top));inset-block-end:auto}.toast-list{display:grid;gap:8px}.toast{display:flex;align-items:flex-start;gap:12px;padding:16px;border:1px solid var(--finish-edge,var(--line));border-inline-start:3px solid var(--accent);border-radius:var(--radius);background:var(--finish-image,none) var(--finish-surface,var(--surface));backdrop-filter:var(--finish-blur,none);color:var(--ink);box-shadow:0 8px 24px #0002;pointer-events:auto;overflow-wrap:anywhere;animation:toast-in var(--motion-normal) var(--motion-ease)}.toast-body{flex:1;min-width:0;display:grid;gap:8px}.toast-body strong{font-size:1rem}.toast-body p{margin:0;color:var(--muted);font-size:.875rem;line-height:1.5}.toast-body .btn{justify-self:start}.toast-close{flex:none;width:44px;height:44px;display:grid;place-items:center;padding:0;border:0;border-radius:6px;background:transparent;color:var(--muted);font:inherit;font-size:24px;cursor:pointer}.toast-close:hover{background:var(--soft);color:var(--ink)}.toast-close:focus-visible{outline:2px solid var(--accent);outline-offset:2px}.toast-sr{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}@keyframes toast-in{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}@media(prefers-reduced-motion:reduce){.toast{animation:none}}[data-motion=none] .toast{animation:none}.toast-icon{flex:none;width:20px;height:20px;margin-block-start:2px;color:var(--accent)}.toast-icon svg{display:block;width:20px;height:20px}.toasts[data-position$=\"-start\"]{inset-inline-start:16px;inset-inline-end:auto}.toasts[data-position$=\"-center\"]{inset-inline:0;margin-inline:auto}.toast-body strong{line-height:1.5}.toast-close{margin-block:-8px;margin-inline-end:-8px}.toast:not(:has(p)):not(:has(.btn)){align-items:center}.toast-icon{width:32px;height:32px;margin-block-start:0;border-radius:50%;background:var(--soft);display:grid;place-items:center}.toasts[data-position^=\"bottom-\"]{inset-block-start:auto;inset-block-end:max(16px,env(safe-area-inset-bottom))}.toasts:is([data-position=top],[data-position$=\"-end\"]){inset-inline-start:auto;inset-inline-end:16px}",
      "bytes": 2453,
      "hash": "9c395894009a5136a1ffa5c0112be9665e12f4273823e08559b7402efde8240b",
      "javascript": "// Optional notifications. Application code owns the operation and its outcome.\n(()=>{\n const icons={\"success\": \"<svg aria-hidden=\\\"true\\\" focusable=\\\"false\\\" xmlns=\\\"http://www.w3.org/2000/svg\\\" viewBox=\\\"0 0 24 24\\\" fill=\\\"none\\\" stroke=\\\"currentColor\\\" stroke-width=\\\"1.75\\\" stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\"><circle cx=\\\"12\\\" cy=\\\"12\\\" r=\\\"9\\\"/><path d=\\\"m7 12 3 3 7-7\\\"/></svg>\\n\", \"info\": \"<svg aria-hidden=\\\"true\\\" focusable=\\\"false\\\" xmlns=\\\"http://www.w3.org/2000/svg\\\" viewBox=\\\"0 0 24 24\\\" fill=\\\"none\\\" stroke=\\\"currentColor\\\" stroke-width=\\\"1.75\\\" stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\"><circle cx=\\\"12\\\" cy=\\\"12\\\" r=\\\"9\\\"/><path d=\\\"M12 11v6m0-10v.1\\\"/></svg>\\n\", \"warning\": \"<svg aria-hidden=\\\"true\\\" focusable=\\\"false\\\" xmlns=\\\"http://www.w3.org/2000/svg\\\" viewBox=\\\"0 0 24 24\\\" fill=\\\"none\\\" stroke=\\\"currentColor\\\" stroke-width=\\\"1.75\\\" stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\"><path d=\\\"M10 5q2-3 4 0l7 13q2 3-2 3H5q-4 0-2-3L10 5ZM12 9v5m0 3v.1\\\"/></svg>\\n\", \"error\": \"<svg aria-hidden=\\\"true\\\" focusable=\\\"false\\\" xmlns=\\\"http://www.w3.org/2000/svg\\\" viewBox=\\\"0 0 24 24\\\" fill=\\\"none\\\" stroke=\\\"currentColor\\\" stroke-width=\\\"1.75\\\" stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\"><circle cx=\\\"12\\\" cy=\\\"12\\\" r=\\\"9\\\"/><path d=\\\"m9 9 6 6m0-6-6 6\\\"/></svg>\\n\"};\n document.querySelectorAll('[data-toasts]').forEach(root=>{\n  if(root.toast)return;\n  const make=(tag,cls,text)=>{const el=document.createElement(tag);el.className=cls;if(text)el.textContent=text;return el;};\n  const list=make('div','toast-list'),live=make('span','toast-sr'),urgent=make('span','toast-sr');\n  live.setAttribute('role','status');urgent.setAttribute('role','alert');\n  [live,urgent].forEach(el=>el.setAttribute('aria-atomic','true'));\n  root.tabIndex=-1;root.append(list,live,urgent);\n  const entries=new Map();let sequence=0;\n  const pending=new Set();let announcement;\n  const announce=item=>{pending.add(item);live.textContent=urgent.textContent='';clearTimeout(announcement);announcement=setTimeout(()=>{for(const [node,priority] of [[live,false],[urgent,true]])node.textContent=[...pending].filter(i=>entries.has(i.id)&&i.el&&!!i.options.urgent===priority).map(i=>i.options.title+(i.options.description?' '+i.options.description:'')).join('. ');pending.clear();},50);};\n  const paused=item=>document.hidden||item.hover||item.el?.contains(document.activeElement);\n  const stop=item=>{if(item.timer){clearTimeout(item.timer);item.timer=0;item.remaining=Math.max(0,item.remaining-(performance.now()-item.started));}};\n  const start=item=>{stop(item);if(item.el&&item.remaining>0&&!paused(item)){item.started=performance.now();item.timer=setTimeout(()=>dismiss(item.id),item.remaining);}};\n  const duration=options=>options.action||['error','loading'].includes(options.tone)?0:Number.isFinite(options.duration)&&options.duration>0?Math.max(6000,options.duration):0;\n  const dismiss=id=>{const item=entries.get(id);if(!item)return;stop(item);const focused=item.el?.contains(document.activeElement);item.el?.remove();entries.delete(id);pump();if(focused){const target=item.origin?.isConnected&&!item.origin.matches(':disabled')?item.origin:list.querySelector('button')||root;target?.focus({preventScroll:true});}root.dispatchEvent(new CustomEvent('toast-dismiss',{detail:{id}}));};\n  const render=item=>{\n   const o=item.options,el=make('div','toast'),body=make('div','toast-body'),title=make('strong','',o.title),description=make('p','',o.description),close=make('button','toast-close','\u00d7');\n   el.dataset.tone=['success','info','warning','error','loading'].includes(o.tone)?o.tone:'neutral';\n   close.type='button';close.setAttribute('aria-label',(root.dataset.dismiss||'Dismiss')+': '+o.title);close.addEventListener('click',()=>dismiss(item.id));\n   body.append(title);if(o.description)body.append(description);\n   if(o.action&&typeof o.action.onClick==='function'){const action=make('button','btn secondary',o.action.label||'Continue');action.type='button';action.addEventListener('click',()=>o.action.onClick({id:item.id,dismiss:()=>dismiss(item.id)}));body.append(action);}\n   if(o.tone==='loading'){const icon=make('span','toast-icon'),spinner=make('span','spinner');icon.setAttribute('aria-hidden','true');icon.append(spinner);el.append(icon);}\n   if(icons[o.tone]){const icon=make('span','toast-icon');icon.setAttribute('aria-hidden','true');icon.innerHTML=icons[o.tone];el.append(icon);}el.append(body,close);el.addEventListener('pointerenter',()=>{item.hover=true;stop(item);});el.addEventListener('pointerleave',()=>{item.hover=false;start(item);});el.addEventListener('focusin',()=>stop(item));el.addEventListener('focusout',()=>queueMicrotask(()=>start(item)));el.addEventListener('keydown',e=>{if(e.key==='Escape'){e.preventDefault();e.stopPropagation();dismiss(item.id);}});\n   item.el=el;list.append(el);announce(item);start(item);\n  };\n  const pump=()=>{let count=[...entries.values()].filter(item=>item.el).length;for(const item of entries.values()){if(count>=3)break;if(!item.el){render(item);count++;}}};\n  const show=options=>{if(!options||typeof options.title!=='string'||!options.title.trim()||entries.size>=20)return null;const id=++sequence,item={id,options:{...options},origin:document.activeElement,remaining:duration(options),timer:0};entries.set(id,item);pump();return id;};\n  const update=(id,options)=>{const item=entries.get(id);if(!item)return false;const next={...item.options,...options};if(typeof next.title!=='string'||!next.title.trim())return false;const focused=item.el?.contains(document.activeElement);stop(item);item.options=next;item.remaining=duration(next);if(item.el){const old=item.el;render(item);old.replaceWith(item.el);if(focused)item.el.querySelector('button')?.focus({preventScroll:true});}return true;};\n  root.toast={show,update,dismiss,clear:()=>{for(const [id,item] of entries)if(!item.el)entries.delete(id);for(const id of [...entries.keys()])dismiss(id);}};\n  document.addEventListener('visibilitychange',()=>entries.forEach(item=>document.hidden?stop(item):start(item)));\n  document.querySelectorAll('[data-toast-target]').forEach(button=>{if(button.dataset.toastTarget!==root.id)return;button.hidden=false;button.addEventListener('click',()=>show({title:button.dataset.toastTitle,description:button.dataset.toastDescription,tone:button.dataset.toastTone,duration:Number(button.dataset.toastDuration),urgent:button.hasAttribute('data-toast-urgent')}));});\n });\n})();\n",
      "javascriptPath": "/library/assets/toast.js",
      "javascriptBytes": 6467
    },
    "color-picker": {
      "path": "/library/assets/color-picker.css",
      "css": ".color-picker{display:flex;align-items:center;gap:8px;min-width:0;padding:4px;border:1px solid var(--muted);border-radius:var(--control-radius,8px);background:var(--surface);color:var(--ink)}.color-picker>.input{flex:1;width:100%;min-width:0;min-height:44px;border:0;background:transparent;padding:8px;font-family:ui-monospace,monospace;font-size:1rem}.color-picker:focus-within,.color-picker:has([aria-expanded=true]){outline:2px solid var(--accent);outline-offset:2px}.color-picker>.input:focus-visible{outline:none}.color-picker:has(.input[aria-invalid=true]){border-style:dashed;border-color:var(--error)}.color-picker:has(.input:disabled){opacity:.55}.color-picker button:disabled{cursor:not-allowed}.color-picker[data-empty] [data-color-swatch]::before{background:linear-gradient(135deg,transparent 47%,var(--muted) 48% 52%,transparent 53%)}.color-picker-popup{container-type:inline-size;position:fixed;inset:auto;margin:0;width:min(384px,calc(100vw - 16px));max-height:calc(100dvh - 16px);overflow:auto;padding:16px;border:1px solid var(--finish-edge,var(--line));border-radius:var(--radius);background:var(--finish-image,none) var(--finish-surface,var(--surface));backdrop-filter:var(--finish-blur,none);color:var(--ink);box-shadow:0 12px 32px #0002;z-index:1000}.color-picker-popup[open]{display:grid;gap:16px;animation:color-picker-in var(--motion-normal) var(--motion-ease)}.color-picker-heading{display:flex;align-items:center;justify-content:flex-start;gap:16px}.color-picker-area{height:auto;aspect-ratio:3/2;position:relative;border-radius:var(--control-radius,6px);background-image:linear-gradient(to top,#000,transparent),linear-gradient(to right,#fff,transparent);touch-action:none;cursor:crosshair}.color-picker-marker{position:absolute;box-sizing:border-box;width:20px;height:20px;border:2px solid var(--ink);background:var(--handle-color);box-shadow:0 0 0 1px var(--surface);transform:translate(-50%,-50%);border-radius:50%;pointer-events:none}.color-picker-channel{display:grid;grid-template-columns:80px minmax(0,1fr) 40px;align-items:center;gap:8px;font-size:14px}.color-picker-channel input{width:100%;min-width:0;height:28px;accent-color:var(--accent);direction:ltr}.color-picker-value{white-space:nowrap;direction:ltr;text-align:right;font-variant-numeric:tabular-nums;color:var(--muted)}.color-picker-hex{display:grid;grid-template-columns:80px 1fr;align-items:center;gap:8px}.color-picker-hex .input{min-width:0;width:100%;font-family:ui-monospace,monospace}.color-picker-popup :focus-visible,.color-picker button:focus-visible{outline:2px solid var(--accent);outline-offset:2px}@keyframes color-picker-in{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}@media(prefers-reduced-motion:reduce){.color-picker-popup[open]{animation:none}}.color-picker-popup[data-motion=none]{animation:none}@media(forced-colors:active){.color-picker-area,.color-picker [data-color-swatch]::before{forced-color-adjust:none}}@media(max-width:360px){.color-picker-channel{grid-template-columns:72px minmax(0,1fr) 36px;gap:4px}}.color-picker-error{margin-top:-8px}.color-picker-error[hidden]{display:none}.color-picker [data-color-swatch]{background:transparent}.color-picker-channel input[type=range]{appearance:none;background:transparent;border:0;padding:0;margin:0;height:28px;border-radius:var(--control-radius,4px);box-shadow:none}.color-picker-hue{--picker-track:linear-gradient(to right,#f00,#ff0,#0f0,#0ff,#00f,#f0f,#f00)}.color-picker-channel input::-webkit-slider-runnable-track{height:8px;background:transparent}.color-picker-channel input::-moz-range-track{height:8px;background:transparent}.color-picker-channel input::-webkit-slider-thumb{appearance:none;box-sizing:border-box;width:20px;height:20px;margin-top:-6px;border:2px solid var(--ink);border-radius:50%;background:var(--handle-color,var(--surface));box-shadow:0 0 0 1px var(--surface)}.color-picker-channel input::-moz-range-thumb{box-sizing:border-box;width:20px;height:20px;border:2px solid var(--ink);border-radius:50%;background:var(--handle-color,var(--surface));box-shadow:0 0 0 1px var(--surface)}@media(forced-colors:active){.color-picker-channel input{--picker-track:linear-gradient(CanvasText,CanvasText)}}.color-picker-view{min-height:44px;padding:8px;border:1px solid var(--line);border-radius:var(--control-radius,6px);color:var(--ink);background:var(--soft);cursor:pointer;margin-inline-start:auto}.color-picker-heading{gap:8px}.color-picker-palette{display:none;min-height:0;overflow:auto;scrollbar-gutter:stable;padding:4px;padding-inline-end:20px}.color-picker-palette-view[open]{grid-template-rows:auto minmax(0,1fr)}.color-picker-palette-view>:not(.color-picker-heading):not(.color-picker-palette){display:none}.color-picker-palette-view>.color-picker-palette{display:grid;gap:16px;align-content:start}.color-picker-family{display:grid;gap:8px;font-size:14px}.color-picker-scale{display:grid;grid-template-columns:repeat(11,minmax(24px,1fr));gap:2px;min-width:284px}.color-picker-scale button{width:24px;height:24px;min-width:24px;min-height:24px;aspect-ratio:1;padding:0;border:1px solid var(--muted);border-radius:50%;justify-self:center;background:var(--picked-color);cursor:pointer}.color-picker-scale button[aria-pressed=true]{outline:2px solid var(--accent);outline-offset:1px;box-shadow:inset 0 0 0 2px var(--surface)}.color-picker-rail{position:relative;display:flex;align-items:center;min-width:0}.color-picker-rail::before{content:\"\";position:absolute;inset-inline:0;top:50%;height:12px;transform:translateY(-50%);border-radius:999px;background:var(--picker-track,linear-gradient(var(--accent),var(--accent)))}.color-picker-rail input{position:relative;z-index:1}.color-picker-eyedropper{width:44px;display:grid;place-items:center}.color-picker-eyedropper svg{width:20px;height:20px}.color-picker-heading{position:relative;border-bottom:1px solid var(--line);gap:16px}.color-picker-mode{min-height:44px;padding:8px 4px;border:0;border-bottom:2px solid transparent;background:transparent;color:var(--muted);font:inherit;cursor:pointer}.color-picker-mode[aria-pressed=true]{color:var(--ink)}.color-picker-heading .color-picker-close{margin-inline-start:auto;margin-bottom:8px}.color-picker-channels{display:flex;align-items:center;gap:12px}.color-picker-sliders{flex:1;min-width:0;display:grid;gap:4px}.color-picker-sliders .color-picker-channel{grid-template-columns:minmax(0,1fr) 5ch;gap:12px}.color-picker-sliders .color-picker-channel>span:first-child{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.color-picker-hex{grid-template-columns:44px minmax(0,1fr)}.color-picker-value-row{display:flex;align-items:center;gap:12px}.color-picker-value-row .color-picker-hex{flex:1;min-width:0}.color-picker-value-row .color-picker-eyedropper{flex:none}.color-picker-value-row{gap:8px}.color-picker-value-row>.select-control{width:calc(9ch + 58px);flex:none}.color-picker-value-row>.input{width:0;min-width:0;flex:1;padding-inline:8px}.color-picker-value-row select{padding-inline:8px 28px}.color-picker-value-row>.select-control::after{inset-inline-end:12px}.color-picker [data-color-swatch]{flex:none;width:44px;height:44px;border:0;border-radius:var(--control-radius,6px);padding:6px;cursor:pointer;display:grid;place-items:center}.color-picker [data-color-swatch]::before{content:\"\";display:block;box-sizing:border-box;width:32px;height:32px;border:1px solid var(--muted);border-radius:var(--control-radius,4px);background:var(--picked-color)}.color-picker-channel[hidden]{display:none}.color-picker-basic .color-picker-heading,.color-picker-basic .color-picker-value-row{display:none}.color-picker-heading::after{content:\"\";position:absolute;left:0;bottom:-1px;width:1px;height:2px;background:var(--accent);transform-origin:left;transform:var(--indicator);transition:transform var(--motion-normal) var(--motion-ease);pointer-events:none}.color-picker[data-hue-slider]{flex-wrap:wrap}.color-picker-inline{flex:0 0 100%;grid-template-columns:minmax(0,1fr);padding-inline:8px}.color-picker-inline>span:first-child{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.color-picker-inline input[type=range]{height:44px}.color-picker[data-hue-slider]>.input{width:0}.color-picker[data-slider-only][data-color-ready]{padding:0;border:0;background:transparent}.color-picker[data-slider-only][data-color-ready]>[hidden]{display:none}.color-picker[data-slider-only][data-color-ready]:focus-within{outline:none}.color-picker-inline input:focus-visible{outline:2px solid var(--accent);outline-offset:2px}.color-picker:has(.input[readonly]) .color-picker-inline{opacity:.55}.color-picker[data-slider-swatch][data-color-ready]{flex-wrap:nowrap;padding:0;border:0;background:transparent}.color-picker[data-slider-swatch][data-color-ready]>.color-picker-inline{flex:1;min-width:0}.color-picker[data-slider-swatch][data-color-ready]>.input[hidden]{display:none}.color-picker[data-slider-swatch][data-color-ready]:focus-within,.color-picker[data-slider-swatch][data-color-ready]:has([aria-expanded=true]){outline:none}.color-picker[data-swatch-only][data-color-ready]{padding:0;border:0;background:transparent;flex:none}.color-picker[data-swatch-only][data-color-ready]>.input[hidden]{display:none}@media(max-width:400px){.color-picker-value-row{display:grid;grid-template-columns:minmax(0,1fr) auto}.color-picker-value-row>.select-control{grid-column:1/-1;width:100%}.color-picker-value-row>.input{width:100%}}@container(max-width:320px){.color-picker-scale{grid-template-columns:repeat(6,minmax(24px,1fr));min-width:0}}",
      "bytes": 9621,
      "hash": "48cbaec856b2a13bf7b01b3682f9656e86043d673e9f125fe9a2bc0f07eb3638",
      "javascript": "// Optional, dependency-free color selection. The named hex field remains the form value.\n(()=>{\n const valid=v=>/^#[0-9a-f]{6}$/i.test(v);\n const hsv=hex=>{const [r,g,b]=hex.slice(1).match(/../g).map(v=>parseInt(v,16)/255),max=Math.max(r,g,b),min=Math.min(r,g,b),d=max-min;return [d?((max===r?(g-b)/d+(g<b?6:0):max===g?(b-r)/d+2:(r-g)/d+4)*60):0,max?d/max*100:0,max*100];};\n const hex=([h,s,v])=>{s/=100;v/=100;const f=n=>{const k=(n+h/60)%6;return Math.round(255*(v-v*s*Math.max(0,Math.min(k,4-k,1)))).toString(16).padStart(2,'0');};return '#'+f(5)+f(3)+f(1);};\nconst toOKLCH=color=>{const rgb=color.slice(1,7).match(/../g).map(v=>parseInt(v,16)); const [r,g,b]=rgb.map(channel=>{const c=channel/255;return c<=.04045?c/12.92:((c+.055)/1.055)**2.4;}); const l=Math.cbrt(.4122214708*r+.5363325363*g+.0514459929*b); const m=Math.cbrt(.2119034982*r+.6806995451*g+.1073969566*b); const s=Math.cbrt(.0883024619*r+.2817188376*g+.6299787005*b); const light=.2104542553*l+.793617785*m-.0040720468*s; const a=1.9779984951*l-2.428592205*m+.4505937099*s; const axis=.0259040371*l+.7827717662*m-.808675766*s; const chroma=Math.hypot(a,axis),hue=chroma<.00001?0:(Math.atan2(axis,a)*180/Math.PI+360)%360; return `oklch(${+(light*100).toFixed(3)}% ${+chroma.toFixed(5)} ${+(hue%360).toFixed(2)})`; };\n const tokenColor=value=>{const m=/^var\\(--color-([a-z]+)-(\\d+)\\)$/.exec(value);return m?scales[m[1]]?.[m[2]]||null:null;};\n const colorToken=color=>{for(const [name,shades] of Object.entries(scales))for(const [shade,value] of Object.entries(shades))if(value===color.toLowerCase())return `var(--color-${name}-${shade})`;return color;};\n const fromOKLCH=value=>{if(!/^oklch\\([\\d.]+%?\\s+[\\d.]+\\s+[+-]?[\\d.]+(?:\\s*\\/\\s*[\\d.]+%?)?\\)$/.test(value)||!CSS.supports('color',value))return null;const c=document.createElement('canvas');c.width=c.height=1;const ctx=c.getContext('2d');ctx.fillStyle=value;ctx.fillRect(0,0,1,1);return '#'+[...ctx.getImageData(0,0,1,1).data].map(v=>v.toString(16).padStart(2,'0')).join('');};\n const formatColor=(color,format)=>{if(format==='token')return colorToken(color);if(format==='oklch')return valid(color)?toOKLCH(color):color;if(!valid(color)||(format==='hex'||format==='css'))return color;if(format==='hsb'){const [h,s,b]=hsv(color);return `${+h.toFixed(2)}, ${+s.toFixed(2)}%, ${+b.toFixed(2)}%`;}const rgb=color.slice(1).match(/../g).map(v=>parseInt(v,16));if(format==='rgb')return rgb.join(', ');const [h,s,v]=hsv(color),l=v/100*(1-s/200),sat=l===0||l===1?0:(v/100-l)/Math.min(l,1-l);return `${+h.toFixed(2)}, ${+(sat*100).toFixed(2)}%, ${+(l*100).toFixed(2)}%`;};\n const parseColor=(value,format)=>{value=value.trim();if(format==='token')return tokenColor(value)||parseColor(value,'hex');if(format==='oklch'){const c=fromOKLCH(value);return c?.endsWith('ff')?c.slice(0,7):null;}if(format==='css'){const type=/^(rgb|hsl)\\(/i.exec(value)?.[1].toLowerCase();return parseColor(value,type||'hex');}if(format==='hex')return valid(value)?value.toLowerCase():null;const body=value.replace(new RegExp('^'+format+'\\\\((.*)\\\\)$','i'),'$1'),parts=body.split(/\\s*,\\s*/);if(parts.length!==3)return null;const numbers=parts.map((p,i)=>{const pattern=(format==='hsl'||format==='hsb')&&i?/^\\d+(?:\\.\\d+)?%$/:/^\\d+(?:\\.\\d+)?$/;return pattern.test(p.trim())?Number(p.trim().replace('%','')):NaN;});if(numbers.some((n,i)=>!Number.isFinite(n)||n<0||n>(format==='rgb'?255:i?100:360)))return null;if(format==='rgb')return '#'+numbers.map(n=>Math.round(n).toString(16).padStart(2,'0')).join('');if(format==='hsb')return hex(numbers);const [h,s,l]=numbers,v=l+s*Math.min(l,100-l)/100;return hex([h%360,v?200*(1-l/v):0,v]);};\n const alphaValid=v=>/^#[0-9a-f]{6}(?:[0-9a-f]{2})?$/i.test(v);\n const alphaByte=a=>Math.round(a*255).toString(16).padStart(2,'0');\n const opaqueHex=value=>/^#[0-9a-f]{6}ff$/i.test(value)?value.slice(0,7):value;\n const formatAlpha=(color,format)=>{if(format==='token')return colorToken(opaqueHex(color));if(format==='oklch')return alphaValid(color)?toOKLCH(color).replace(')',` / ${color.length===9?+(parseInt(color.slice(7),16)/255).toFixed(4):1})`):color;if(!alphaValid(color)||format==='hex'||format==='css')return opaqueHex(color);return formatColor(color.slice(0,7),format)+', '+Math.round((color.length===9?parseInt(color.slice(7),16)/255:1)*10000)/100+'%';};\n const parseAlpha=(value,format)=>{value=value.trim();if(format==='oklch')return fromOKLCH(value);if(format==='token')return tokenColor(value)||parseAlpha(value,'hex');if(format==='hex'||(format==='css'&&value.startsWith('#')))return alphaValid(value)?value.toLowerCase():null;if(format==='css'){const match=/^(rgba?|hsla?)\\((.*)\\)$/i.exec(value);if(!match)return null;return parseAlpha(match[2],match[1].toLowerCase().startsWith('rgb')?'rgb':'hsl');}const parts=value.replace(/^(?:rgb|hsl)a?\\((.*)\\)$/i,'$1').split(',');if(parts.length===3)return parseColor(value,format);if(parts.length!==4)return null;const raw=parts.pop().trim();if(!/^\\d+(?:\\.\\d+)?%?$/.test(raw))return null;const a=parseFloat(raw)/(raw.endsWith('%')?100:1),color=parseColor(parts.join(','),format);return color&&a>=0&&a<=1?color+alphaByte(a):null;};\n const scales=Object.fromEntries(Object.entries({\"red\":\"fdf2f1fce5e4f8ccc9f4aaa4ee7b72e64337c824199f1d147b160f5b110b360a07\",\"orange\":\"fef6f1fdeee3fbdcc6f8c5a0f4a56cef7f2ed16010a64c0c803b0a5f2c07391a04\",\"amber\":\"fefaf1fdf5e2fbebc5f9de9ff6cc6af2b62cd3980da8790b815d086045063a2904\",\"yellow\":\"fefcf1fdfae3faf4c7f7eda1f2e36eedd731ceb812a4930e7f710b5e5408383205\",\"lime\":\"f9fcf2f2fae6e6f5ccd5eeaabee47ca3d94584ba2669941e5172173c551124330a\",\"green\":\"f3fcf6e7f8ecd0f1dab0e8c184db9f51cd7632ae58288a461f6b36174f280e2f18\",\"emerald\":\"f2fcf8e6f9f2cdf4e5abedd47de3bc47d7a028b88120926719714f12543b0b3223\",\"teal\":\"f3fcfbe7f9f8cef3f1adebe881dfdb4bd2cb2db4ad248f891c6e6a14524f0c312f\",\"cyan\":\"f2fbfde5f6fbcaeef6a7e2f177d2e93ebfe01fa1c119809a1363760e4958092c35\",\"sky\":\"f1f8fde4f2fcc8e4f9a4d3f472baee369ee7187fc91365a00e4e7b0b3a5b062337\",\"blue\":\"f1f5fde4eafbc9d5f8a5baf37394ed3866e51a48c715399e102c7a0c215a071436\",\"indigo\":\"f2f2fce6e6facdcdf4ababed7d7de34646d82727b91f1f931818721212540b0b32\",\"violet\":\"f6f2fceee5faddccf5c6aaeea77be58244da6425bb4f1d953d17732d11551b0a33\",\"purple\":\"f9f3fcf3e6f9e8cdf3d9acecc47fe1ab49d48c2bb66f2291561a70401353260c32\",\"fuchsia\":\"fdf2fcfbe5f9f6caf3f1a7ebe977dfe03ed2c11fb49a198f76136e580e52350931\",\"pink\":\"fdf2f8fae5f2f6cbe5efa9d3e679bbdc419ebe2380971c6674154e56103a340923\",\"rose\":\"fdf2f5fbe5eaf7cad5f1a7b9ea7693e13d66c21e479a183977132c580e20350813\",\"slate\":\"f6f7f8edeff2dcdfe5c4cad4a4adbc7e8aa05f6c814c56673b424f2b313b1a1d23\",\"stone\":\"f8f7f7f1f0eee3e1ded0cdc8b6b1aa989086797267605a524a463f37342f211f1c\",\"gray\":\"f7f7f8eff0f0dfe0e1cacbceaeafb28b8e926d6f7457585c4344473132351e1e20\",\"zinc\":\"f7f7f8efeff0e0dfe2cbc9cfaeacb48c89946d6b7657555e4341483230361e1d20\",\"neutral\":\"f7f7f7f0f0f0e0e0e0ccccccb0b0b08f8f8f7070705959594545453333331f1f1f\",\"taupe\":\"f8f7f6f2f0eee4e0ddd2ccc6b9afa69c8e817e6f6364594f4d443d39332d221e1b\",\"mauve\":\"f8f7f8f1eef1e3dee2d1c7cfb7a9b59985967a667761515f4b3f49382e36211c20\",\"mist\":\"f7f8f8eef1f1dee2e3c7cfd1a9b5b785969966777a515f613f494b2e36381c2021\",\"olive\":\"f8f8f6f1f2eee2e4ddcfd2c6b5b9a6969c81777e635f644f494d3d36392d20221b\"}).map(([name,colors])=>[name,Object.fromEntries(colors.match(/.{6}/g).map((hex,i)=>[[50,100,200,300,400,500,600,700,800,900,950][i],'#'+hex]))]));\n let active,sequence=0;\n document.querySelectorAll('[data-color-picker]').forEach(root=>{\n  if(root.hasAttribute('data-color-ready'))return;\n  const text=root.querySelector('.input'),button=root.querySelector('[data-color-swatch]');\n  if(!text||!button)return;\n  const alpha=root.hasAttribute('data-alpha'),isValid=alpha?alphaValid:valid,display=alpha?formatAlpha:formatColor,parse=alpha?parseAlpha:parseColor;\n  root.dataset.colorReady='';button.hidden=false;\n  let inlineRange,inlineRail,syncedColor;\n  let opacity=1,values=hsv(isValid(text.value)?text.value.slice(0,7):'#294cda'),panel,area,dot,entry,error,ranges=[],dragging=false,opener=button,palette,toggle,mode,showPalette,sampling,format;\n  const blocked=()=>text.matches(':disabled')||text.readOnly;\n  const make=(tag,cls,content)=>{const el=document.createElement(tag);if(cls)el.className=cls;if(content)el.textContent=content;return el;};\n  const close=(focus=false)=>{sampling?.abort();if(!panel?.open)return;panel.close();button.setAttribute('aria-expanded','false');if(active===close)active=null;if(focus&&!blocked())opener.focus();};\n  const paint=()=>{\n   button.style.setProperty('--picked-color',isValid(text.value)?text.value:'transparent');\n   root.toggleAttribute('data-empty',!text.value);\n   if(inlineRange){inlineRange.value=Math.round(values[0]);inlineRange.disabled=blocked();inlineRange.setAttribute('aria-valuetext',Math.round(values[0])+'\u00b0, '+text.value);inlineRail.style.setProperty('--handle-color',hex(values));}\n   if(!panel)return;\n   const tab=panel.classList.contains('color-picker-palette-view')?toggle:mode;tab.parentElement.style.setProperty('--indicator',`translateX(${tab.offsetLeft}px) scaleX(${tab.offsetWidth})`);\n   area.style.backgroundColor=`hsl(${values[0]} 100% 50%)`;\n   dot.style.setProperty('--handle-color',hex(values));\n   dot.style.left=values[1]+'%';dot.style.top=(100-values[2])+'%';\n   ranges.forEach((input,i)=>{input.value=Math.round(i===3?opacity*100:values[i]);input.parentElement.nextElementSibling.textContent=input.value+(i?'%':'\u00b0');});\n   ranges.forEach((input,i)=>input.style.setProperty('--handle-color',i===0?hex([values[0],100,100]):hex(values)));\n   ranges[1].parentElement.style.setProperty('--picker-track',`linear-gradient(to right,${hex([values[0],0,values[2]])},${hex([values[0],100,values[2]])})`);\n   ranges[2].parentElement.style.setProperty('--picker-track',`linear-gradient(to right,#000000,${hex([values[0],values[1],100])})`);\n   if(alpha)ranges[3].parentElement.style.setProperty('--picker-track',`linear-gradient(to right,${hex(values)}00,${hex(values)}),repeating-conic-gradient(#ccc 0% 25%,#fff 0% 50%) 0/8px 8px`);\n   if(document.activeElement!==entry){entry.value=display(text.value,format.value);entry.removeAttribute('aria-invalid');error.hidden=true;}\n   panel.querySelectorAll('[data-color]').forEach(b=>b.setAttribute('aria-pressed',String(b.dataset.color===text.value.toLowerCase())));\n  };\n  const sync=()=>{if(button.disabled!==blocked())button.disabled=blocked();if(blocked())close();if(isValid(text.value)&&text.value.toLowerCase()!==syncedColor){syncedColor=text.value.toLowerCase();const next=hsv(text.value.slice(0,7));if(next[1]===0)next[0]=values[0];if(next[2]===0)next[1]=values[1];values=next;if(alpha)opacity=text.value.length===9?parseInt(text.value.slice(7),16)/255:1;}paint();};\n  const commit=(value,change=true)=>{if(blocked())return;text.value=opaqueHex(alpha&&value.length===7?value+alphaByte(opacity):value);text.removeAttribute('aria-invalid');text.dispatchEvent(new Event('input',{bubbles:true}));if(change)text.dispatchEvent(new Event('change',{bubbles:true}));};\n  const position=()=>{if(!panel?.open)return;const r=button.getBoundingClientRect(),p=panel.getBoundingClientRect();panel.style.left=Math.max(8,Math.min(r.left,innerWidth-p.width-8))+'px';panel.style.top=Math.max(8,Math.min(r.bottom+8,innerHeight-p.height-8))+'px';};\n  const build=()=>{\n   panel=make('dialog','color-picker-popup');panel.setAttribute('aria-label',button.getAttribute('aria-label')||'Choose Color');panel.dir=getComputedStyle(root).direction;panel.lang=root.closest('[lang]')?.lang||document.documentElement.lang;\n   const heading=make('div','color-picker-heading'),dismiss=make('button','btn ghost btn-icon color-picker-close');dismiss.innerHTML='<svg class=\"icon\" aria-hidden=\"true\" focusable=\"false\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.75\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m6 6 12 12M18 6 6 18\"/></svg>';dismiss.type='button';dismiss.setAttribute('aria-label',root.dataset.close||'Close Color Picker');dismiss.addEventListener('click',()=>close(true));toggle=make('button','color-picker-view',root.dataset.palette||'Palette');toggle.type='button';toggle.setAttribute('aria-expanded','false');mode=make('button','color-picker-mode',root.dataset.color||'Color');mode.type='button';mode.setAttribute('aria-pressed','true');toggle.className='color-picker-mode';mode.addEventListener('click',()=>showPalette(false));heading.append(mode,toggle,dismiss);panel.append(heading);let eyedropper;const channels=make('div','color-picker-channels'),sliders=make('div','color-picker-sliders');channels.append(sliders);\n   if(typeof EyeDropper==='function'&&isSecureContext){eyedropper=make('button','color-picker-view color-picker-eyedropper');eyedropper.type='button';eyedropper.setAttribute('aria-label',root.dataset.eyedropper||'Eyedropper');eyedropper.innerHTML=\"<svg class=\\\"icon\\\" aria-hidden=\\\"true\\\" focusable=\\\"false\\\" xmlns=\\\"http://www.w3.org/2000/svg\\\" viewBox=\\\"0 0 24 24\\\" fill=\\\"none\\\" stroke=\\\"currentColor\\\" stroke-width=\\\"1.75\\\" stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\"><path d=\\\"m13 7 2-2a3.5 3.5 0 1 1 4 5l-2 1M11.5 6.5l6 6M13 8 4.5 16.5q-.5.5-.5 1.2V20h2.3q.7 0 1.2-.5L16 11M4 20l-1 1\\\"/></svg>\";eyedropper.style.marginInlineStart='0';eyedropper.addEventListener('click',async()=>{if(blocked()||sampling)return;const controller=new AbortController();sampling=controller;eyedropper.disabled=true;try{const result=await new EyeDropper().open({signal:controller.signal});if(!controller.signal.aborted&&panel.open&&!blocked()&&valid(result.sRGBHex)){commit(result.sRGBHex.toLowerCase());if(panel.classList.contains('color-picker-palette-view'))showPalette(false);}}catch(reason){if(reason.name!=='AbortError'){error.textContent=root.dataset.sampleError||'Could not sample a color. Try again or enter a hex value.';error.hidden=false;}}finally{sampling=null;eyedropper.disabled=false;if(panel.open&&!blocked())eyedropper.focus({preventScroll:true});}});}\n\n   showPalette=show=>{if(show){panel.style.height=panel.getBoundingClientRect().height+'px';if(!palette)buildPalette();}panel.classList.toggle('color-picker-palette-view',show);mode.setAttribute('aria-pressed',String(!show));toggle.setAttribute('aria-pressed',String(show));toggle.setAttribute('aria-expanded',String(show));if(!show)panel.style.height='';paint();if(show){const buttons=[...palette.querySelectorAll('button')],selected=buttons.find(b=>b.dataset.color===text.value.toLowerCase())||buttons[0];buttons.forEach(b=>b.tabIndex=b===selected?0:-1);selected.focus();}else toggle.focus();};\n   const buildPalette=()=>{palette=make('div','color-picker-palette');palette.setAttribute('role','group');palette.setAttribute('aria-label',root.dataset.palette||'Color palette');\n    Object.entries(scales).forEach(([family,shades])=>{const group=make('section','color-picker-family'),name=make('strong','',family[0].toUpperCase()+family.slice(1)),row=make('div','color-picker-scale');group.append(name,row);Object.entries(shades).forEach(([shade,color])=>{const b=make('button');b.type='button';b.tabIndex=-1;b.dataset.color=color;b.style.setProperty('--picked-color',color);b.setAttribute('aria-label',`${family} ${shade}, ${color}`);b.addEventListener('click',()=>{commit(color);showPalette(false);});row.append(b);});palette.append(group);});\n    palette.addEventListener('keydown',e=>{const buttons=[...palette.querySelectorAll('button')],index=buttons.indexOf(e.target);if(index<0)return;const rtl=panel.dir==='rtl',delta={ArrowRight:rtl?-1:1,ArrowLeft:rtl?1:-1,ArrowDown:11,ArrowUp:-11}[e.key];let next;if(delta)next=Math.max(0,Math.min(buttons.length-1,index+delta));else if(e.key==='Home')next=e.ctrlKey?0:Math.floor(index/11)*11;else if(e.key==='End')next=e.ctrlKey?buttons.length-1:Math.floor(index/11)*11+10;else return;e.preventDefault();buttons[index].tabIndex=-1;buttons[next].tabIndex=0;buttons[next].focus();});panel.append(palette);\n   };toggle.addEventListener('click',()=>showPalette(!panel.classList.contains('color-picker-palette-view')));\n   area=make('div','color-picker-area');area.setAttribute('aria-hidden','true');dot=make('span','color-picker-marker');area.append(dot);panel.append(area);\n   const move=e=>{const r=area.getBoundingClientRect();values[1]=Math.max(0,Math.min(100,(e.clientX-r.left)/r.width*100));values[2]=100-Math.max(0,Math.min(100,(e.clientY-r.top)/r.height*100));const saved=[...values];commit(hex(values),false);values=saved;paint();};\n   area.addEventListener('pointerdown',e=>{if(e.button!==0)return;dragging=true;area.setPointerCapture(e.pointerId);move(e);});area.addEventListener('pointermove',e=>{if(dragging)move(e);});\n   const end=()=>{if(!dragging)return;dragging=false;text.dispatchEvent(new Event('change',{bubbles:true}));};area.addEventListener('pointerup',end);area.addEventListener('pointercancel',end);\n   (alpha?['Hue','Saturation','Brightness','Opacity']:['Hue','Saturation','Brightness']).forEach((name,i)=>{const row=make('label','color-picker-channel'),label=make('span','',root.dataset[name.toLowerCase()]||name),input=make('input');input.type='range';if(!i)input.className='color-picker-hue';input.min=0;input.max=i?100:359;input.step=1;const output=make('span','color-picker-value');output.setAttribute('aria-hidden','true');const rail=make('span','color-picker-rail'+(!i?' color-picker-hue':''));rail.append(input);row.append(label,rail,output);ranges.push(input);input.addEventListener('input',()=>{if(i===3)opacity=Number(input.value)/100;else values[i]=Number(input.value);const saved=[...values];commit(hex(values),false);values=saved;paint();});input.addEventListener('change',()=>text.dispatchEvent(new Event('change',{bubbles:true})));sliders.append(row);});panel.append(channels);\n   if(root.hasAttribute('data-basic')){panel.classList.add('color-picker-basic');[ranges[1],ranges[2]].forEach(input=>input.closest('label').hidden=true);area.removeAttribute('aria-hidden');area.tabIndex=0;area.setAttribute('role','group');area.setAttribute('aria-label',root.dataset.areaLabel||'Saturation and brightness. Left and right adjust saturation; up and down adjust brightness.');area.addEventListener('keydown',e=>{const keys={ArrowLeft:[1,-1],ArrowRight:[1,1],ArrowUp:[2,1],ArrowDown:[2,-1]},key=keys[e.key];if(!key)return;e.preventDefault();values[key[0]]=Math.max(0,Math.min(100,values[key[0]]+key[1]*(e.shiftKey?10:1)));commit(hex(values));});}\n\n   const selectWrap=make('span','select-control');format=make('select');format.setAttribute('aria-label',root.dataset.formatLabel||'Color format');['hex','rgb','hsl','oklch','token'].forEach(value=>{const option=make('option','',value==='hex'?'Hex':value==='token'?'CSS token':value.toUpperCase());option.value=value;format.append(option);});selectWrap.append(format);\n   entry=make('input','input');entry.dir='ltr';entry.spellcheck=false;entry.maxLength=64;entry.required=true;entry.autocomplete='off';const valueRow=make('div','color-picker-value-row');valueRow.append(selectWrap,entry);if(eyedropper)valueRow.append(eyedropper);panel.append(valueRow);error=make('p','color-picker-error');error.id='color-picker-error-'+(++sequence);error.hidden=true;entry.setAttribute('aria-describedby',error.id);panel.append(error);\n   const setFormat=()=>{entry.setAttribute('aria-label',(root.dataset.valueLabel||'Color value')+' ('+format.value.toUpperCase()+')');entry.value=display(text.value,format.value);entry.removeAttribute('aria-invalid');error.hidden=true;};format.addEventListener('change',setFormat);setFormat();\n   entry.addEventListener('input',()=>{const color=parse(entry.value,format.value);error.hidden=!!color;error.textContent=format.value==='token'?'Choose a palette token or enter hex for a custom color.':format.value==='oklch'?'Enter oklch(lightness% chroma hue), with optional / opacity.':alpha?'Enter a valid color; optional opacity is 0\u2013100% (or 0\u20131).':(format.value==='hex'||format.value==='css')?(root.dataset.invalid||'Enter # followed by six hexadecimal digits.'):format.value==='rgb'?'Enter three values from 0 to 255, separated by commas.':'Enter hue from 0 to 360 and two percentages from 0% to 100%, separated by commas.';entry.setAttribute('aria-invalid',String(!color));if(color){if(alpha)opacity=color.length===9?parseInt(color.slice(7),16)/255:1;commit(color);}});\n   document.body.append(panel);panel.addEventListener('cancel',e=>{e.preventDefault();close(true);});panel.addEventListener('keydown',e=>{if(e.key==='Escape'){e.preventDefault();close(true);}});\n  };\n  button.setAttribute('aria-haspopup','dialog');button.setAttribute('aria-expanded','false');\n  const open=from=>{if(blocked()||panel?.open)return;opener=from;active?.();if(!panel)build();if(panel.classList.contains('color-picker-palette-view')){panel.classList.remove('color-picker-palette-view');panel.style.height='';mode.setAttribute('aria-pressed','true');toggle.setAttribute('aria-pressed','false');toggle.setAttribute('aria-expanded','false');}sync();panel.dataset.motion=root.closest('[data-motion]')?.dataset.motion||'';panel.show();button.setAttribute('aria-expanded','true');active=close;paint();position();if(from===text)text.focus({preventScroll:true});else ranges[0].focus();};\n  button.addEventListener('click',()=>{if(panel?.open)close(true);else open(button);});\n  text.addEventListener('click',()=>open(text));\n  text.addEventListener('keydown',e=>{if(e.key==='Escape'&&panel?.open){e.preventDefault();e.stopPropagation();close(true);}});\n  document.addEventListener('pointerdown',e=>{if(!sampling&&panel?.open&&!panel.contains(e.target)&&!root.contains(e.target))close();});\n  document.addEventListener('focusin',e=>{if(!sampling&&panel?.open&&!panel.contains(e.target)&&!root.contains(e.target))close();});\n  addEventListener('resize',()=>close());addEventListener('scroll',()=>{if(panel?.open)position();},true);\n  text.addEventListener('input',()=>{text.setAttribute('aria-invalid',String(!!(text.value||text.required)&&!isValid(text.value)));sync();});text.addEventListener('change',sync);text.addEventListener('blur',()=>{if(isValid(text.value))text.value=text.value.toLowerCase();sync();});\n  text.form?.addEventListener('reset',()=>setTimeout(()=>{close();text.removeAttribute('aria-invalid');sync();},0));root.addEventListener('color-picker-sync',sync);\n  new MutationObserver(sync).observe(root,{subtree:true,attributes:true,attributeFilter:['disabled','readonly','value']});\n  for(let parent=root.parentElement;parent;parent=parent.parentElement)if(parent.tagName==='FIELDSET')new MutationObserver(sync).observe(parent,{attributes:true,attributeFilter:['disabled']});\n  if(root.hasAttribute('data-hue-slider')){\n   const row=make('label','color-picker-channel color-picker-inline'),label=make('span','',root.dataset.hue||'Hue');\n   inlineRail=make('span','color-picker-rail color-picker-hue');inlineRange=make('input');inlineRange.type='range';inlineRange.min=0;inlineRange.max=359;inlineRange.step=1;inlineRange.setAttribute('aria-label',root.dataset.hue||'Hue');inlineRail.append(inlineRange);row.append(label,inlineRail);root.prepend(row);\n   inlineRange.addEventListener('input',()=>{if(blocked())return;values[0]=Number(inlineRange.value);const saved=[...values];commit(hex(values),false);values=saved;paint();});\n   inlineRange.addEventListener('change',()=>{if(!blocked())text.dispatchEvent(new Event('change',{bubbles:true}));});\n   if(root.hasAttribute('data-slider-only')||root.hasAttribute('data-slider-swatch')){text.hidden=true;button.hidden=root.hasAttribute('data-slider-only');text.addEventListener('invalid',e=>{e.preventDefault();inlineRange.focus();});}\n  }\n  if(root.hasAttribute('data-swatch-only')){text.hidden=true;text.addEventListener('invalid',e=>{e.preventDefault();button.focus();});}\n  sync();\n });\n})();\n",
      "javascriptPath": "/library/assets/color-picker.js",
      "javascriptBytes": 23669
    },
    "datepicker": {
      "path": "/library/assets/datepicker.css",
      "css": ".datepicker{position:relative;display:flex;min-width:0}.datepicker>.input{min-width:0;width:100%;flex:1}.datepicker>button,.datepicker-dialog button{min-height:44px;min-width:44px;border:1px solid var(--line);border-radius:var(--control-radius,7px);background:var(--surface);color:var(--ink);font:inherit;cursor:pointer;transition:background var(--motion-normal) var(--motion-ease),color var(--motion-normal) var(--motion-ease)}.datepicker>button{display:grid;place-items:center;padding:10px;flex:none}.datepicker .icon{width:20px;height:20px}.datepicker-dialog{width:min(368px,calc(100% - 24px));max-height:calc(100dvh - 24px);margin:auto;padding:16px;border:1px solid var(--finish-edge,var(--line));border-radius:var(--radius);background:var(--finish-image,none) var(--finish-surface,var(--surface));backdrop-filter:var(--finish-blur,none);color:var(--ink);overflow:auto;box-shadow:0 12px 32px #0002}.datepicker-dialog::backdrop{background:#0b151780}.datepicker-header{display:grid;grid-template-columns:44px minmax(0,1fr) 44px;align-items:center;gap:0;margin-bottom:16px}.datepicker-controls{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px;margin-bottom:16px}.datepicker-controls input,.datepicker-controls select{min-width:0;width:100%;padding:8px;font:inherit;font-size:1rem}.datepicker-dialog table{width:100%;table-layout:fixed;border-collapse:separate;border-spacing:2px;margin:0}.datepicker-dialog th{padding:4px 0 12px;font-size:.6875rem;color:var(--accent);font-weight:700;letter-spacing:.08em}.datepicker-dialog td{padding:0;border:0;text-align:center}.datepicker-dialog td button{display:block;width:100%;min-width:0;border-color:transparent;padding:0;font-size:1rem;font-weight:500;font-variant-numeric:tabular-nums}.datepicker-dialog [aria-selected=true]>button{background:var(--accent);color:var(--on-accent);border-color:var(--accent);font-weight:700}.datepicker-dialog [aria-current=date]{border-color:var(--muted)}.datepicker-dialog [aria-disabled=true]{color:var(--muted);cursor:not-allowed}.datepicker-dialog button:hover:not(:disabled):not([aria-disabled=true]),.datepicker>button:hover:not(:disabled){background:var(--soft);color:var(--ink)}.datepicker-dialog button:focus-visible,.datepicker>button:focus-visible{outline:2px solid var(--accent);outline-offset:2px}.datepicker button:disabled{opacity:.5;cursor:not-allowed}@keyframes datepicker-enter{from{opacity:0;transform:translateY(4px)}}@media(prefers-reduced-motion:no-preference){.datepicker-dialog[open]{animation:datepicker-enter var(--motion-normal) var(--motion-ease)}}@media(max-width:360px){.datepicker-dialog{padding:12px}.datepicker-controls{gap:8px}}@media(forced-colors:active){.datepicker-dialog [aria-selected=true]>button{border:2px solid Highlight}.datepicker-dialog [aria-disabled=true]{color:GrayText}}.datepicker-header:dir(rtl)>button{transform:rotate(180deg)}.datepicker[data-datepicker-ready]>.input{appearance:none}.datepicker[data-datepicker-ready]>.input::-webkit-calendar-picker-indicator{display:none}.datepicker-dialog[data-presentation=anchored]{position:fixed;inset:auto;margin:0;z-index:1000}.datepicker-header>button{display:grid;place-items:center;border-color:transparent;background:transparent}.datepicker-dialog>.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}.datepicker-dialog [data-adjacent]{color:var(--muted);font-size:.875rem;font-weight:400}.datepicker-dialog [aria-selected=true]>[data-adjacent]{color:var(--on-accent)}.datepicker-titles{display:flex;justify-content:center;gap:4px;min-width:0}.datepicker-titles button{background:transparent;border-color:transparent;padding:4px 8px;min-width:0;font-size:.9375rem;font-weight:600;white-space:nowrap}.datepicker-controls>button{min-height:64px;border-color:transparent}.datepicker-controls>[aria-pressed=true]{background:var(--tint);color:var(--accent)}.datepicker-dialog [aria-selected=true]>button:hover:not([aria-disabled=true]){background:var(--accent);color:var(--on-accent)}.datepicker-dialog [aria-selected=true]>button:focus-visible{outline-offset:2px}.datepicker-dialog [aria-selected=true]>[data-adjacent]{font-size:1rem;font-weight:700}@media(max-width:360px){.datepicker-header{margin-inline:-8px}.datepicker-titles button{font-size:.875rem}}.datepicker[data-datepicker-ready]>.input{padding-inline-end:52px;min-height:48px}.datepicker[data-datepicker-ready]>button{position:absolute;inset-block:2px;inset-inline-end:2px;width:44px;padding:10px;border:0;border-radius:max(0px,calc(var(--control-radius,7px) - 2px));background:transparent}.datepicker[data-datepicker-ready]>button:focus-visible{outline-offset:-2px}.datepicker-fields{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,240px),1fr));gap:24px;align-items:start}.datepicker-fields input[type=time]{min-height:48px}.datepicker[data-timepicker-ready]>.input{appearance:none;padding-inline-end:52px;min-height:48px}.datepicker[data-timepicker-ready]>.input::-webkit-calendar-picker-indicator{display:none}.datepicker[data-timepicker-ready]>button{position:absolute;inset-block:2px;inset-inline-end:2px;width:44px;border:0;border-radius:max(0px,calc(var(--control-radius,7px) - 2px));background:transparent;padding:10px;font-size:24px}.timepicker-columns{display:flex;gap:12px;margin-block:20px}.timepicker-part{display:flex;flex:1;min-width:0;flex-direction:column;gap:8px;text-align:center}.timepicker-part input{width:100%;height:48px;min-height:48px;padding:8px;text-align:center;font:inherit;font-size:1.25rem;font-variant-numeric:tabular-nums;appearance:textfield}.timepicker-part input::-webkit-inner-spin-button{appearance:none}.timepicker-part span{font-size:.75rem;color:var(--muted)}.timepicker-dialog .timepicker-done{width:100%;background:var(--accent);color:var(--on-accent);margin-top:16px}.timepicker-dialog p:empty{display:none}.timepicker-dialog [hidden]{display:none}.timepicker-part>button{border-color:transparent;background:transparent;font-size:1.25rem}.timepicker-dialog .timepicker-done:hover{background:var(--accent);color:var(--on-accent)}.timepicker-columns{margin-block:0}.timepicker-part>button{display:grid;place-items:center}.timepicker-part>button .icon{width:20px;height:20px}.timepicker-dialog>button{margin-top:16px}.timepicker-columns>.timepicker-period{flex:none;padding-top:52px}.timepicker-period>span{visibility:hidden}.timepicker-period>button{min-height:48px;padding-inline:8px;font-weight:600;font-size:1rem;border-color:var(--line)}[data-date-range]{border:0;padding:0;margin:0;min-width:0}[data-date-range]>legend{font-weight:600;margin-bottom:16px}[data-range-error]:empty{display:none}.datepicker-dialog [data-in-range]>button:not([aria-disabled=true]){background:var(--tint);color:var(--accent)}.datepicker-dialog [data-in-range][aria-selected=true]>button{background:var(--accent);color:var(--on-accent)}.timepicker-dialog button:disabled{opacity:.4;cursor:not-allowed}.datepicker-dialog td button[aria-disabled=true]{opacity:.35;font-weight:400}.datepicker-dialog td button[aria-disabled=true]:focus-visible{opacity:1}@media(forced-colors:active){.datepicker-dialog td button[aria-disabled=true]{opacity:1;color:GrayText}}.date-range-display{appearance:none;border:1px solid var(--muted);border-radius:var(--control-radius,7px);padding:.65rem .8rem;min-height:48px;background:var(--surface);color:var(--ink);font:inherit;display:flex;align-items:center;justify-content:space-between;gap:12px;width:100%;text-align:start;cursor:pointer}.date-range-display .icon{width:20px;height:20px;flex:none}[data-date-range] .datepicker-fields[hidden]{display:none}.datepicker-range-prompt{text-align:center;font-size:.8125rem;line-height:1.5;color:var(--muted);margin:12px 0 0}[data-date-range]{gap:8px}.timepicker-columns:has(+.field-error:not(:empty)){margin-bottom:8px}",
      "bytes": 7876,
      "hash": "6a8b7053f51b804cf3a5ae391dc17fcf670b53ceca7b5d52ddc965af95b74bb3",
      "javascript": "// Date-only arithmetic stays in UTC; never parse a localized display string.\n(()=>{\n  const day=86400000;\n  const date=(y,m,d)=>{const value=new Date(0);value.setUTCFullYear(y,m,d);return value;};\n  const iso=d=>`${String(d.getUTCFullYear()).padStart(4,'0')}-${String(d.getUTCMonth()+1).padStart(2,'0')}-${String(d.getUTCDate()).padStart(2,'0')}`;\n  const parse=value=>{\n    if(!/^\\d{4}-\\d{2}-\\d{2}$/.test(value||''))return null;\n    const [y,m,d]=value.split('-').map(Number), result=date(y,m-1,d);\n    return y>0&&iso(result)===value?result:null;\n  };\n  const month=(d,n)=>{const target=date(d.getUTCFullYear(),d.getUTCMonth()+n,1);return date(target.getUTCFullYear(),target.getUTCMonth(),Math.min(d.getUTCDate(),date(target.getUTCFullYear(),target.getUTCMonth()+1,0).getUTCDate()));};\n  const create=(tag,attrs={},text)=>{const el=document.createElement(tag);for(const [k,v] of Object.entries(attrs))el.setAttribute(k,v);if(text!==undefined)el.textContent=text;return el;};\n  let chooserSequence=0;\n  document.querySelectorAll('[data-datepicker]').forEach(group=>{\n    const input=group.querySelector('input[type=date]'), trigger=group.querySelector('[data-datepicker-open]');\n    if(!input||!trigger||group.hasAttribute('data-datepicker-ready')||typeof HTMLDialogElement==='undefined'||!HTMLDialogElement.prototype.showModal)return;\n    group.setAttribute('data-datepicker-ready','');\n    const range=group.closest('[data-date-range]');\n    const start=range?.querySelector('[data-range-start]'),end=range?.querySelector('[data-range-end]');\n    if(start&&end&&!range.hasAttribute('data-range-ready')){\n      range.setAttribute('data-range-ready','');\n      const validate=()=>{\n        const invalid=!!(parse(start.value)&&parse(end.value)&&start.value>end.value);\n        const message=invalid?(range.getAttribute('data-range-message')||'End date must be on or after start date.'):'';\n        end.setCustomValidity(message);\n        if(invalid)end.setAttribute('aria-invalid','true');else end.removeAttribute('aria-invalid');\n        const error=range.querySelector('[data-range-error]');if(error)error.textContent=message;\n      };\n      range.addEventListener('input',validate);range.addEventListener('change',validate);\n      start.form?.addEventListener('reset',()=>setTimeout(validate,0));validate();\n    }\n    const shared=!!(start&&end);let rangeStart=null;\n    const endGroup=shared?end.closest('[data-datepicker]'):null,endTrigger=endGroup?.querySelector('[data-datepicker-open]');\n    if(shared&&input===end)return;\n    if(endGroup)endGroup.setAttribute('data-datepicker-ready','');\n    const label=(key,fallback)=>group.getAttribute('data-'+key)||fallback;\n    let locale=group.closest('[lang]')?.lang||document.documentElement.lang||'en';\n    try{new Intl.DateTimeFormat(locale);}catch{locale='en';}\n    const format=options=>new Intl.DateTimeFormat(locale,{...options,timeZone:'UTC',calendar:'gregory'});\n    const number=new Intl.NumberFormat(locale,{useGrouping:false});\n    const full=format({dateStyle:'full'}), heading=format({month:'long',year:'numeric'});\n    const today=()=>{const now=new Date();return date(now.getFullYear(),now.getMonth(),now.getDate());};\n    let rangeDisplay=null;\n    if(shared){\n      rangeDisplay=create('button',{type:'button',class:'input date-range-display','aria-haspopup':'dialog'});\n      rangeDisplay.append(create('span'),trigger.querySelector('svg').cloneNode(true));\n      range.append(rangeDisplay);range.querySelector('.datepicker-fields').hidden=true;\n      [start,end].forEach(field=>field.addEventListener('invalid',event=>{event.preventDefault();rangeDisplay.focus();const error=range.querySelector('[data-range-error]');if(error)error.textContent=field.validationMessage;}));\n    }\n    const first=group.dataset.weekStart==='1'?1:0;\n    const dialog=create('dialog',{class:'datepicker-dialog','aria-label':label('title','Choose a date')});\n    const title=create('div',{class:'datepicker-titles'});\n    const announcement=create('span',{class:'sr-only','aria-live':'polite','aria-atomic':'true'});\n    const header=create('div',{class:'datepicker-header'}),controls=create('div',{class:'datepicker-controls'});\n    const button=(text,name)=>create('button',{type:'button','aria-label':name},text);\n    const previous=button('',label('previous','Previous Month')), next=button('',label('next','Next Month'));\n    for(const [control,rotation] of [[previous,90],[next,-90]]){\n      const svg=document.createElementNS('http://www.w3.org/2000/svg','svg');\n      for(const [key,value] of Object.entries({viewBox:'0 0 24 24',width:'20',height:'20',fill:'none',stroke:'currentColor','stroke-width':'1.75','stroke-linecap':'round','stroke-linejoin':'round','aria-hidden':'true',focusable:'false'}))svg.setAttribute(key,value);\n      const path=document.createElementNS('http://www.w3.org/2000/svg','path');path.setAttribute('d','m5 9 7 7 7-7');svg.append(path);svg.style.transform='rotate('+rotation+'deg)';control.append(svg);\n    }\n    const months=button('',label('month','Month')),year=button('',label('year','Year'));\n    title.append(months,year);\n    const rangePrompt=shared?create('p',{class:'muted datepicker-range-prompt','aria-live':'polite'}):null;\n    const grid=create('table',{role:'grid'}), head=create('thead'), row=create('tr'), body=create('tbody');\n    for(let i=0;i<7;i++){\n      const d=date(2024,0,7+(i+first)%7);\n      row.append(create('th',{scope:'col',abbr:format({weekday:'long'}).format(d)},format({weekday:'narrow'}).format(d)));\n    }\n    head.append(row);grid.append(head,body);\n    let chooserId;do{chooserId='datepicker-chooser-'+(++chooserSequence);}while(document.getElementById(chooserId));\n    controls.hidden=true;controls.id=chooserId;controls.setAttribute('role','group');\n    for(const control of [months,year]){control.setAttribute('aria-controls',chooserId);control.setAttribute('aria-expanded','false');}\n    header.append(previous,title,next);dialog.append(header,controls,grid,announcement);(shared?range:group).append(dialog);\n    let focusDate=today(),modal=false,returnFocus=true,view='days',yearStart=1,opener=trigger;\n    const setView=nextView=>{view=nextView;yearStart=Math.floor((focusDate.getUTCFullYear()-1)/12)*12+1;render();if(view==='days')body.querySelector('[tabindex=\"0\"]')?.focus();else controls.querySelector('[tabindex=\"0\"]')?.focus();};\n    months.addEventListener('click',()=>setView(view==='months'?'days':'months'));\n    year.addEventListener('click',()=>setView(view==='years'?'days':'years'));\n    const place=()=>{\n      if(!dialog.open||modal)return;\n      const r=(rangeDisplay||group).getBoundingClientRect(),w=dialog.offsetWidth,h=dialog.offsetHeight,pad=12;\n      const below=innerHeight-r.bottom-8,above=r.top-8;\n      const y=below<h&&above>below?r.top-h-8:r.bottom+8;\n      const x=getComputedStyle(group).direction==='rtl'?r.right-w:r.left;\n      dialog.style.left=Math.max(pad,Math.min(x,innerWidth-w-pad))+'px';\n      dialog.style.top=Math.max(pad,Math.min(y,innerHeight-h-pad))+'px';\n    };\n    const dismiss=(restore=true)=>{returnFocus=restore;dialog.close();};\n    const bounds=()=>{\n      const target=shared&&rangeStart?end:input;\n      let min=parse(target.min)||date(1,0,1),max=parse(target.max)||date(9999,11,31);\n      if(shared&&!rangeStart&&parse(end.max))max=new Date(Math.min(+max,+parse(end.max)));\n      if(shared&&rangeStart)min=new Date(Math.max(+min,+parse(rangeStart)));\n      return [min,max];\n    };\n    const clamp=d=>{const [min,max]=bounds();return new Date(Math.max(+min,Math.min(+max,+d)));};\n    const available=d=>{\n      const target=shared&&rangeStart?end:input;\n      const [min,max]=bounds(), step=Number(target.step), base=parse(target.min)||parse(target.getAttribute('value'))||date(1970,0,1);\n      const interval=target.step==='any'?1:step>0?step:1;\n      return d>=min&&d<=max&&Math.abs((d-base)/day/interval-Math.round((d-base)/day/interval))<1e-7;\n    };\n    const sync=()=>{\n      trigger.disabled=input.matches(':disabled')||input.readOnly||!!(shared&&(end.matches(':disabled')||end.readOnly));\n      if(endTrigger)endTrigger.disabled=trigger.disabled;\n      if(rangeDisplay){\n        rangeDisplay.disabled=trigger.disabled;\n        const a=parse(start.value),b=parse(end.value),short=format({month:'short',day:'numeric',year:'numeric'});\n        const text=rangeStart?short.format(parse(rangeStart))+' \u2013 \u2026':a&&b?(short.formatRange&&a<=b?short.formatRange(a,b):short.format(a)+' \u2013 '+short.format(b)):a?short.format(a)+' \u2013 \u2026':label('range-placeholder','Choose dates');\n        rangeDisplay.querySelector('span').textContent=text;\n        rangeDisplay.setAttribute('aria-label',label('range-label','Date range')+': '+text);\n        const described=end.getAttribute('aria-describedby');if(described)rangeDisplay.setAttribute('aria-describedby',described);\n      }\n      const selected=parse(input.value);\n      trigger.setAttribute('aria-label',label('title','Choose a date')+(selected?': '+full.format(selected):''));\n      if(trigger.disabled&&dialog.open)dialog.close();\n    };\n    const render=(focus=false)=>{\n      const y=focusDate.getUTCFullYear(),m=focusDate.getUTCMonth(), [min,max]=bounds();\n      months.textContent=format({month:'long'}).format(focusDate);year.textContent=number.format(y);\n      months.setAttribute('aria-label',label('month','Month')+': '+months.textContent);year.setAttribute('aria-label',label('year','Year')+': '+year.textContent);\n      months.setAttribute('aria-expanded',String(view==='months'));year.setAttribute('aria-expanded',String(view==='years'));\n      announcement.textContent=view==='years'?number.format(yearStart)+' \u2013 '+number.format(Math.min(9999,yearStart+11)):heading.format(focusDate);grid.setAttribute('aria-label',heading.format(focusDate));\n      controls.hidden=view==='days';grid.hidden=view!=='days';\n      const unit=view==='days'?'Month':view==='months'?'Year':'Years';\n      previous.setAttribute('aria-label',label('previous-'+unit.toLowerCase(),'Previous '+unit));next.setAttribute('aria-label',label('next-'+unit.toLowerCase(),'Next '+unit));\n      previous.disabled=view==='years'?yearStart<=min.getUTCFullYear():view==='months'?y<=min.getUTCFullYear():date(y,m,1)<=date(min.getUTCFullYear(),min.getUTCMonth(),1);\n      next.disabled=view==='years'?yearStart+11>=max.getUTCFullYear():view==='months'?y>=max.getUTCFullYear():date(y,m,1)>=date(max.getUTCFullYear(),max.getUTCMonth(),1);\n      if(view!=='days'){\n        controls.replaceChildren();controls.setAttribute('aria-label',view==='months'?label('month','Month'):label('year','Year'));\n        for(let i=0;i<12;i++){\n          const value=view==='months'?i:yearStart+i;if(value>9999)break;\n          const start=view==='months'?date(y,i,1):date(value,0,1),end=view==='months'?date(y,i+1,0):date(value,11,31);\n          const b=button(view==='months'?format({month:'short'}).format(start):number.format(value),view==='months'?format({month:'long'}).format(start):number.format(value));\n          b.dataset.choice=value;b.tabIndex=value===(view==='months'?m:y)?0:-1;b.setAttribute('aria-pressed',String(b.tabIndex===0));\n          if(end<min||start>max)b.setAttribute('aria-disabled','true');controls.append(b);\n        }\n        if(!controls.querySelector('[tabindex=\"0\"]'))controls.children[0].tabIndex=0;\n        place();return;\n      }\n      const current=iso(today());\n      body.replaceChildren();let tr;\n      const offset=(date(y,m,1).getUTCDay()-first+7)%7,count=date(y,m+1,0).getUTCDate();\n      for(let i=0;i<42;i++){\n        if(i%7===0){tr=create('tr');body.append(tr);}\n        const cell=create('td');tr.append(cell);const n=i-offset+1;\n        const d=date(y,m,n), value=iso(d), selected=shared?(rangeStart?value===rangeStart:value===start.value||value===end.value):value===input.value;\n        if(!parse(value)){cell.textContent=number.format(d.getUTCDate());continue;}\n        cell.setAttribute('aria-selected',String(selected));\n        if(!rangeStart&&parse(start?.value)&&parse(end?.value)&&value>=start.value&&value<=end.value)cell.setAttribute('data-in-range','');\n        const b=button(number.format(d.getUTCDate()),full.format(d));\n        if(n<1||n>count)b.setAttribute('data-adjacent','');b.dataset.date=value;b.tabIndex=value===iso(focusDate)?0:-1;\n        if(!available(d))b.setAttribute('aria-disabled','true');\n        if(value===current)b.setAttribute('aria-current','date');\n        cell.append(b);\n      }\n      place();\n      if(focus)body.querySelector('[tabindex=\"0\"]')?.focus();\n    };\n    const move=d=>{focusDate=clamp(d);render(true);};\n    trigger.setAttribute('aria-haspopup','dialog');\n    trigger.hidden=false;sync();\n    const open=source=>{\n      sync();if(trigger.disabled||dialog.open)return;opener=source;\n      returnFocus=true;view='days';controls.hidden=true;rangeStart=null;\n      if(rangePrompt)rangePrompt.textContent=label('start-prompt','Choose a start date.');\n      focusDate=clamp(parse(input.value)||today());render();\n      modal=innerWidth<480;dialog.dataset.presentation=modal?'modal':'anchored';\n      dialog.style.left='';dialog.style.top='';\n      if(modal)dialog.showModal();else dialog.show();place();\n      if(modal||source===trigger||source===endTrigger||source===rangeDisplay)body.querySelector('[tabindex=\"0\"]')?.focus();else source.focus();\n    };\n    if(rangeDisplay)rangeDisplay.addEventListener('click',()=>dialog.open?dismiss():open(rangeDisplay));\n    if(shared&&endTrigger){\n      endTrigger.hidden=false;endTrigger.setAttribute('aria-haspopup','dialog');\n      endTrigger.addEventListener('click',()=>dialog.open?dismiss():open(endTrigger));\n      end.addEventListener('click',event=>{event.preventDefault();open(end);});\n      end.addEventListener('keydown',event=>{if(event.key==='Escape'&&dialog.open){event.preventDefault();dismiss();}});\n      dialog.append(rangePrompt);\n    }\n    trigger.addEventListener('click',()=>{if(dialog.open)dismiss();else open(trigger);});\n    input.addEventListener('click',event=>{event.preventDefault();open(input);});\n    input.addEventListener('keydown',event=>{if(event.key==='Escape'&&dialog.open){event.preventDefault();dismiss();}});\n    const outside=event=>{const r=dialog.getBoundingClientRect();return event.target===dialog&&(event.clientX<r.left||event.clientX>r.right||event.clientY<r.top||event.clientY>r.bottom);};\n    let backdropPress=false;\n    dialog.addEventListener('pointerdown',event=>{backdropPress=outside(event);});\n    dialog.addEventListener('click',event=>{if(backdropPress&&outside(event))dialog.close();backdropPress=false;});\n    dialog.addEventListener('close',()=>{rangeStart=null;sync();if(returnFocus&&!trigger.disabled)opener.focus();returnFocus=true;});\n    dialog.addEventListener('keydown',event=>{if(event.key==='Escape'&&!modal){event.preventDefault();dismiss();}});\n    document.addEventListener('pointerdown',event=>{if(dialog.open&&!modal&&!dialog.contains(event.target)&&!trigger.contains(event.target)&&event.target!==input&&!endGroup?.contains(event.target)&&event.target!==rangeDisplay&&!rangeDisplay?.contains(event.target))dismiss(false);});\n    document.addEventListener('focusin',event=>{if(dialog.open&&!modal&&!dialog.contains(event.target)&&!trigger.contains(event.target)&&event.target!==input&&!endGroup?.contains(event.target)&&event.target!==rangeDisplay&&!rangeDisplay?.contains(event.target))dismiss(false);});\n    addEventListener('resize',()=>{if(dialog.open)dismiss();});\n    document.addEventListener('scroll',place,true);\n    const page=direction=>{\n      if(view==='years')yearStart=Math.max(1,Math.min(9997,yearStart+direction*12));\n      else focusDate=clamp(month(focusDate,direction*(view==='months'?12:1)));\n      render();\n    };\n    previous.addEventListener('click',()=>{page(-1);if(previous.disabled)(view==='days'?body:controls).querySelector('[tabindex=\"0\"]')?.focus();});\n    next.addEventListener('click',()=>{page(1);if(next.disabled)(view==='days'?body:controls).querySelector('[tabindex=\"0\"]')?.focus();});\n    controls.addEventListener('click',event=>{\n      const b=event.target.closest('[data-choice]');if(!b||b.getAttribute('aria-disabled')==='true')return;\n      const value=Number(b.dataset.choice),wasYear=view==='years';\n      focusDate=clamp(month(focusDate,wasYear?(value-focusDate.getUTCFullYear())*12:value-focusDate.getUTCMonth()));setView('days');\n    });\n    controls.addEventListener('keydown',event=>{\n      const b=event.target.closest('[data-choice]');if(!b)return;\n      const buttons=[...controls.children],i=buttons.indexOf(b),rtl=getComputedStyle(group).direction==='rtl';\n      const delta={ArrowLeft:rtl?1:-1,ArrowRight:rtl?-1:1,ArrowUp:-3,ArrowDown:3,Home:-i,End:buttons.length-1-i};\n      if(event.key in delta){event.preventDefault();const nextIndex=Math.max(0,Math.min(buttons.length-1,i+delta[event.key]));buttons.forEach((el,n)=>el.tabIndex=n===nextIndex?0:-1);buttons[nextIndex].focus();}\n      else if(['PageUp','PageDown'].includes(event.key)){event.preventDefault();page(event.key==='PageUp'?-1:1);controls.querySelector('[tabindex=\"0\"]')?.focus();}\n    });\n    body.addEventListener('click',event=>{\n      const b=event.target.closest('[data-date]');if(!b||input.matches(':disabled')||input.readOnly||!available(parse(b.dataset.date)))return;\n      if(shared){\n        if(end.matches(':disabled')||end.readOnly)return;\n        if(!rangeStart){rangeStart=b.dataset.date;focusDate=parse(rangeStart);sync();render(true);rangePrompt.textContent=label('end-prompt','Choose an end date.');return;}\n        const changed=[start.value!==rangeStart,end.value!==b.dataset.date];start.value=rangeStart;end.value=b.dataset.date;rangeStart=null;\n        [start,end].forEach((field,i)=>{if(changed[i]){field.dispatchEvent(new Event('input',{bubbles:true}));field.dispatchEvent(new Event('change',{bubbles:true}));}});sync();dialog.close();return;\n      }\n      if(input.value!==b.dataset.date){input.value=b.dataset.date;input.dispatchEvent(new Event('input',{bubbles:true}));input.dispatchEvent(new Event('change',{bubbles:true}));}sync();dialog.close();\n    });\n    body.addEventListener('keydown',event=>{\n      const b=event.target.closest('[data-date]');if(!b)return;\n      const d=parse(b.dataset.date), rtl=getComputedStyle(group).direction==='rtl';\n      const delta={ArrowLeft:rtl?1:-1,ArrowRight:rtl?-1:1,ArrowUp:-7,ArrowDown:7,Home:-((d.getUTCDay()-first+7)%7),End:6-((d.getUTCDay()-first+7)%7)};\n      if(event.key in delta){event.preventDefault();move(new Date(+d+delta[event.key]*day));}\n      else if(['PageUp','PageDown'].includes(event.key)){event.preventDefault();move(month(d,(event.key==='PageUp'?-1:1)*(event.shiftKey?12:1)));}\n    });\n    input.addEventListener('change',sync);\n    range?.addEventListener('input',()=>{sync();if(dialog.open)render();});\n    input.form?.addEventListener('reset',()=>{if(dialog.open)dialog.close();setTimeout(sync,0);});\n    const observer=new MutationObserver(()=>{sync();if(dialog.open){focusDate=clamp(focusDate);render();}});\n    observer.observe(input,{attributes:true,attributeFilter:['disabled','readonly','min','max','step','value']});\n    if(shared)observer.observe(end,{attributes:true,attributeFilter:['disabled','readonly','min','max','step','value']});\n    for(let parent=input.parentElement;parent;parent=parent.parentElement)if(parent.tagName==='FIELDSET')observer.observe(parent,{attributes:true,attributeFilter:['disabled']});\n  });\n})();\n\n// Progressive time entry; native input remains the submitted source of truth.\n(()=>{\n  const make=(tag,attrs={},text)=>{const el=document.createElement(tag);for(const [key,value] of Object.entries(attrs))el.setAttribute(key,value);if(text!==undefined)el.textContent=text;return el;};\n  document.querySelectorAll('[data-timepicker]').forEach(group=>{\n    const input=group.querySelector('input[type=time]'),trigger=group.querySelector('button');\n    if(!input||!trigger||group.hasAttribute('data-timepicker-ready')||!globalThis.HTMLDialogElement?.prototype.showModal)return;\n    const label=(key,fallback)=>group.getAttribute('data-'+key)||fallback;\n    const dialog=make('dialog',{class:'datepicker-dialog timepicker-dialog','aria-label':label('title','Choose a time')});\n    const fields=make('div',{class:'timepicker-columns'}),error=make('p',{class:'field-error','aria-live':'polite'});\n    const parts=[];let opener=input,modal=false,restore=true;\n    const hour12=group.dataset.hourFormat==='12';\n    const display=input.cloneNode();display.type='text';display.removeAttribute('name');display.removeAttribute('min');display.removeAttribute('max');display.removeAttribute('step');display.removeAttribute('value');display.autocomplete='off';\n    let nativeId=(input.id||'time')+'-native';while(document.getElementById(nativeId))nativeId+='-';input.id=nativeId;input.hidden=true;group.insertBefore(display,input);\n    const formatValue=()=>{\n      if(!input.value)return '';\n      const [h,m,seconds]=input.value.split(':'),hour=Number(h);\n      return (hour12?String(hour%12||12).padStart(2,'0'):h)+':'+m+(seconds!==undefined?':'+seconds:'')+(hour12?(hour>=12?' PM':' AM'):'');\n    };\n    const refresh=()=>{display.value=formatValue();display.disabled=input.matches(':disabled');display.readOnly=input.readOnly;display.required=input.required;display.setCustomValidity('');};refresh();\n    display.addEventListener('input',()=>{\n      const raw=display.value.trim(),match=raw.match(hour12?/^(\\d{1,2}):(\\d{2})(?::(\\d{2}))?\\s*(AM|PM)$/i:/^(\\d{1,2}):(\\d{2})(?::(\\d{2}))?$/);\n      let value='';\n      if(match){let hour=Number(match[1]);if(hour12&&hour>=1&&hour<=12)hour=hour%12+(match[4].toUpperCase()==='PM'?12:0);else if(hour12)hour=99;if(hour<24&&Number(match[2])<60&&Number(match[3]||0)<60)value=String(hour).padStart(2,'0')+':'+match[2]+(match[3]!==undefined?':'+match[3]:'');}\n      const probe=input.cloneNode();probe.setCustomValidity('');probe.value=value;\n      const message=raw&&(!value||!probe.validity.valid)?label('invalid',hour12?'Enter a valid time, such as 09:30 AM, within the allowed range.':'Enter a valid time, such as 23:30, within the allowed range.'):'';\n      display.setCustomValidity(message);input.setCustomValidity(message);error.textContent=message;\n      if(message)return;\n      input.value=value;input.dispatchEvent(new Event('input',{bubbles:true}));input.dispatchEvent(new Event('change',{bubbles:true}));\n    });\n    display.addEventListener('blur',()=>{if(display.validity.valid)refresh();});\n    input.addEventListener('invalid',event=>{event.preventDefault();display.setCustomValidity(input.validationMessage);display.reportValidity();});\n\n    for(const [key,max] of [['hour',hour12?12:23],['minute',59],['second',59]]){\n      const wrap=make('label',{class:'timepicker-part'}),name=label(key,key[0].toUpperCase()+key.slice(1)),field=make('input',{type:'number',min:key==='hour'&&hour12?1:0,max,step:1,'aria-label':name});\n      const up=make('button',{type:'button','aria-label':label('increase','Increase')+' '+name}),down=make('button',{type:'button','aria-label':label('decrease','Decrease')+' '+name});\n      for(const [button,upward] of [[up,true],[down,false]]){const svg=document.createElementNS('http://www.w3.org/2000/svg','svg');for(const [k,v] of Object.entries({viewBox:'0 0 24 24',fill:'none',stroke:'currentColor','stroke-width':'1.75','stroke-linecap':'round','stroke-linejoin':'round','aria-hidden':'true',focusable:'false',class:'icon'}))svg.setAttribute(k,v);const path=document.createElementNS('http://www.w3.org/2000/svg','path');path.setAttribute('d','m5 9 7 7 7-7');if(upward)svg.style.transform='rotate(180deg)';svg.append(path);button.append(svg);}\n      // Buttons are siblings of the label: no nested interactive label controls.\n      const column=make('div',{class:'timepicker-part'});wrap.append(make('span',{},name),field);column.append(up,wrap,down);fields.append(column);parts.push({column,field,up,down});\n      for(const [button,delta] of [[up,1],[down,-1]])button.addEventListener('click',()=>{if(button.disabled)return;field.value=String(nextValue(field,delta)).padStart(2,'0');commit();});\n    }\n    const period=make('button',{type:'button','aria-label':label('period','Toggle AM or PM')},'AM');const syncPeriod=()=>period.setAttribute('aria-label',label('period','Toggle AM or PM')+': '+period.textContent);period.hidden=!hour12;period.addEventListener('click',()=>{if(period.disabled)return;period.textContent=period.textContent==='AM'?'PM':'AM';syncPeriod();commit();});\n    const periodColumn=make('div',{class:'timepicker-part timepicker-period'}),periodLabel=make('span',{'aria-hidden':'true'},'AM/PM');periodColumn.hidden=!hour12;periodColumn.append(periodLabel,period);fields.append(periodColumn);dialog.append(fields,error);group.after(dialog);\n    const unavailable=()=>input.matches(':disabled')||input.readOnly;\n    const nextValue=(field,delta)=>{const min=Number(field.min),max=Number(field.max),next=Number(field.value)+delta;return next>max?min:next<min?max:next;};\n    const candidate=(values,pm=period.textContent==='PM')=>{\n      if(values.some((value,i)=>value===''||!Number.isInteger(Number(value))||Number(value)<Number(parts[i].field.min)||Number(value)>Number(parts[i].field.max)))return '';\n      const numbers=values.map(Number);if(hour12)numbers[0]=numbers[0]%12+(pm?12:0);\n      return numbers.map(n=>String(n).padStart(2,'0')).join(':');\n    };\n    const allowed=value=>{if(!value||unavailable())return false;const probe=input.cloneNode();probe.setCustomValidity('');probe.value=value;return probe.validity.valid;};\n    const updateControls=()=>{\n      const values=parts.filter(p=>!p.column.hidden).map(p=>p.field.value);\n      parts.forEach(({field,up,down},i)=>{for(const [button,delta] of [[up,1],[down,-1]]){const next=[...values];next[i]=String(nextValue(field,delta));button.disabled=!allowed(candidate(next));}});\n      period.disabled=!allowed(candidate(values,period.textContent!=='PM'));\n    };\n    const sync=()=>{display.disabled=input.matches(':disabled');display.readOnly=input.readOnly;display.required=input.required;trigger.disabled=unavailable();if(trigger.disabled&&dialog.open)dialog.close();else if(dialog.open)updateControls();};\n    const place=()=>{if(!dialog.open||modal)return;const r=group.getBoundingClientRect(),d=dialog.getBoundingClientRect();dialog.style.left=Math.max(12,Math.min(r.left,innerWidth-d.width-12))+'px';dialog.style.top=Math.max(12,Math.min(r.bottom+8,innerHeight-d.height-12))+'px';};\n    const open=source=>{\n      sync();if(unavailable()||dialog.open)return;opener=source;restore=true;\n      const values=(input.value||'09:00').split(':').map(Number);parts.forEach((p,i)=>p.field.value=String(values[i]||0).padStart(2,'0'));\n      if(hour12){parts[0].field.value=String(values[0]%12||12).padStart(2,'0');period.textContent=values[0]>=12?'PM':'AM';syncPeriod();}\n      parts[2].column.hidden=!(input.step==='any'||Number(input.step||60)%60!==0||input.value.split(':').length===3);\n      updateControls();error.textContent='';modal=innerWidth<480;dialog.dataset.presentation=modal?'modal':'anchored';dialog.style.left='';dialog.style.top='';\n      modal?dialog.showModal():dialog.show();place();if(modal||source===trigger)parts[0].field.focus();else source.focus();\n    };\n    trigger.hidden=false;group.setAttribute('data-timepicker-ready','');trigger.setAttribute('aria-haspopup','dialog');sync();\n    trigger.addEventListener('click',()=>dialog.open?dialog.close():open(trigger));display.addEventListener('click',()=>open(display));\n    const commit=()=>{\n      updateControls();if(unavailable())return;\n      const shown=parts.filter(p=>!p.column.hidden);if(shown.some(p=>p.field.value===''||!p.field.checkValidity())){error.textContent=label('invalid','Enter a valid time.');return;}\n      let hour=Number(parts[0].field.value);if(hour12)hour=hour%12+(period.textContent==='PM'?12:0);\n      const value=[hour,Number(parts[1].field.value),...(parts[2].column.hidden?[]:[Number(parts[2].field.value)])].map(n=>String(n).padStart(2,'0')).join(':');\n      const probe=input.cloneNode();probe.setCustomValidity('');probe.value=value;\n      if(!probe.checkValidity()){error.textContent=label('invalid','Choose a time within the allowed range and interval.');return;}\n      input.setCustomValidity('');display.setCustomValidity('');if(input.value!==value){input.value=value;input.dispatchEvent(new Event('input',{bubbles:true}));input.dispatchEvent(new Event('change',{bubbles:true}));}refresh();error.textContent='';\n    };\n    parts.forEach(({field})=>{field.addEventListener('input',commit);field.addEventListener('blur',()=>{if(field.value!==''&&field.checkValidity())field.value=String(Number(field.value)).padStart(2,'0');});});\n    const escape=e=>{if(e.key==='Escape'&&dialog.open){e.preventDefault();dialog.close();}};dialog.addEventListener('keydown',escape);display.addEventListener('keydown',escape);\n    dialog.addEventListener('close',()=>{if(restore&&!unavailable())opener.focus();restore=true;});\n    const outside=e=>{const r=dialog.getBoundingClientRect();return e.target===dialog&&(e.clientX<r.left||e.clientX>r.right||e.clientY<r.top||e.clientY>r.bottom);};let backdrop=false;\n    dialog.addEventListener('pointerdown',e=>backdrop=outside(e));dialog.addEventListener('click',e=>{if(backdrop&&outside(e))dialog.close();backdrop=false;});\n    document.addEventListener('pointerdown',e=>{if(dialog.open&&!modal&&!dialog.contains(e.target)&&!group.contains(e.target)){restore=false;dialog.close();}});\n    document.addEventListener('focusin',e=>{if(dialog.open&&!modal&&!dialog.contains(e.target)&&!group.contains(e.target)){restore=false;dialog.close();}});\n    document.addEventListener('scroll',place,true);addEventListener('resize',()=>{if(dialog.open)dialog.close();});input.form?.addEventListener('reset',()=>{if(dialog.open)dialog.close();setTimeout(()=>{input.setCustomValidity('');refresh();error.textContent='';},0);});\n    const observer=new MutationObserver(sync);observer.observe(input,{attributes:true,attributeFilter:['disabled','readonly','required','min','max','step','value']});for(let p=input.parentElement;p;p=p.parentElement)if(p.tagName==='FIELDSET')observer.observe(p,{attributes:true,attributeFilter:['disabled']});\n  });\n})();\n",
      "javascriptPath": "/library/assets/datepicker.js",
      "javascriptBytes": 30276
    },
    "password": {
      "path": "/library/assets/password.css",
      "css": ".password{position:relative;display:flex;align-items:stretch;min-width:0}.password>.input{flex:1;width:100%;min-width:0}.password>button{flex:none;display:grid;place-items:center;width:44px;min-height:44px;padding:10px;border:0;background:transparent;color:var(--ink);font:inherit;font-size:1rem}.password>button:hover:not(:disabled){background:color-mix(in srgb,var(--ink) 6%,transparent)}.password>button:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}.password .icon{width:20px;height:20px}.password-eye-off{display:none}.password>input[type=text]~button .password-eye{display:none}.password>input[type=text]~button .password-eye-off{display:block}.password[data-password-ready]>.input{padding-inline-end:52px;min-height:48px}.password[data-password-ready]>button{position:absolute;inset-block:2px;inset-inline-end:2px;border:0;border-radius:max(0px,calc(var(--control-radius,7px) - 2px));background:transparent;cursor:pointer}.password[data-password-ready]>button:hover:not(:disabled){background:color-mix(in srgb,var(--ink) 6%,transparent)}.password>button:disabled{cursor:not-allowed;opacity:.5}",
      "bytes": 1120,
      "hash": "eaf00b2c3b7a77541a9c28283f7fe5442c219b29cae0763d2f9c1e61ab21a51e",
      "javascript": "// Enhance the existing input; never copy, store, or transmit its value.\n(()=>{\n  document.querySelectorAll('[data-password]').forEach(group=>{\n    const input=group.querySelector('input[type=password]');\n    const button=group.querySelector('[data-password-toggle]');\n    if(!input||!button||group.hasAttribute('data-password-ready'))return;\n    group.setAttribute('data-password-ready','');\n    const mask=()=>{input.type='password';button.setAttribute('aria-label',button.dataset.show);};\n    const sync=()=>{button.disabled=input.matches(':disabled');if(button.disabled)mask();};\n    button.hidden=false;sync();\n    button.addEventListener('click',()=>{\n      if(input.matches(':disabled'))return;\n      const start=input.selectionStart,end=input.selectionEnd,direction=input.selectionDirection;\n      const show=input.type==='password';\n      input.type=show?'text':'password';\n      button.setAttribute('aria-label',show?button.dataset.hide:button.dataset.show);\n      if(start!==null)input.setSelectionRange(start,end,direction);\n    });\n    input.form?.addEventListener('reset',mask);\n    addEventListener('pagehide',mask);\n    const observer=new MutationObserver(sync);\n    observer.observe(input,{attributes:true,attributeFilter:['disabled']});\n    for(let parent=input.parentElement;parent;parent=parent.parentElement){\n      if(parent.tagName==='FIELDSET')observer.observe(parent,{attributes:true,attributeFilter:['disabled']});\n    }\n  });\n})();\n",
      "javascriptPath": "/library/assets/password.js",
      "javascriptBytes": 1459
    },
    "switch": {
      "path": "/library/assets/switch.css",
      "css": ".switch{display:flex;align-items:flex-start;gap:12px;min-height:44px;padding-block:10px;cursor:pointer;line-height:1.5}.switch>input[type=checkbox]{appearance:none;flex:none;display:grid;align-items:center;justify-items:start;width:44px;height:24px;min-height:0;font-size:inherit;margin:calc(.75em - 12px) 0 0;padding:2px;border:2px solid var(--muted);border-radius:999px;background:var(--surface);cursor:inherit}.switch>input::before{content:\"\";width:16px;height:16px;border-radius:50%;background:var(--muted);transform:translateX(0)}.switch>input:checked{border-color:var(--accent);background:var(--accent)}.switch>input:checked::before{background:var(--on-accent);transform:translateX(20px)}.switch>input:dir(rtl):checked::before{transform:translateX(-20px)}.switch>input:focus-visible{outline:2px solid var(--accent);outline-offset:3px}.switch:has(>input:disabled){opacity:.55;cursor:not-allowed}.switch>span{min-width:0;overflow-wrap:anywhere}@media(prefers-reduced-motion:no-preference){.switch>input{transition:background-color var(--motion-normal) var(--motion-ease),border-color var(--motion-normal) var(--motion-ease)}.switch>input::before{transition:transform var(--motion-normal) var(--motion-ease),background-color var(--motion-normal) var(--motion-ease)}}@media(forced-colors:active){.switch>input[type=checkbox]{appearance:auto}.switch>input::before{display:none}}",
      "bytes": 1379,
      "hash": "15dcf12b76cf1408fb4c943fedd192adcd5a178c4643d18cc7d9b4ccdb663c73"
    },
    "textarea": {
      "path": "/library/assets/textarea.css",
      "css": ".textarea{display:block;width:100%;min-height:96px;resize:vertical;font-size:1rem;line-height:1.5;text-align:start}.textarea:focus-visible{outline:2px solid var(--accent);outline-offset:3px}.textarea[readonly]{cursor:text}",
      "bytes": 222,
      "hash": "3b5c858cff36fc7a76a2ba8bd04924eb7676242a01f04bd3e1d4ac958c3d128e"
    },
    "radio": {
      "path": "/library/assets/radio.css",
      "css": ".radio{display:flex;align-items:flex-start;gap:12px;min-height:44px;padding-block:10px;cursor:pointer;line-height:1.5}.radio>input[type=radio]{appearance:none;flex:none;display:grid;place-content:center;width:20px;height:20px;min-height:0;padding:0;font-size:inherit;margin:calc(.75em - 10px) 0 0;border:1.5px solid var(--muted);border-radius:50%;background:var(--surface);color:var(--on-accent);cursor:inherit}.radio>input::before{content:\"\";width:8px;height:8px;background:currentColor;border-radius:50%;opacity:0}.radio>input:checked{background:var(--accent);border-color:var(--accent)}.radio>input:checked::before{opacity:1}.radio:hover>input:not(:disabled){border-color:var(--accent)}.radio>input:focus-visible{outline:2px solid var(--accent);outline-offset:3px}.radio:has(>input:disabled){opacity:.55;cursor:not-allowed}.radio>input[aria-invalid=true]{border-style:dashed;border-color:var(--error)}.radio>span{min-width:0;overflow-wrap:anywhere}.radio small{display:block;color:var(--muted);font-weight:400}.radio-group{border:0;padding:0;margin:0;min-width:0}.radio-group>legend{padding:0;margin-bottom:8px;font-weight:600}@media(prefers-reduced-motion:no-preference){.radio>input{transition:background-color var(--motion-normal) var(--motion-ease),border-color var(--motion-normal) var(--motion-ease)}.radio>input::before{transition:opacity var(--motion-normal) var(--motion-ease)}}@media(forced-colors:active){.radio>input[type=radio]{appearance:auto}.radio>input::before{display:none}}.radio-group>.field-error{margin-top:8px}",
      "bytes": 1536,
      "hash": "fd67af37d73eb6906a2b794d5e1ca0be74550f7bf0e436bc389b5a571805ca6b"
    },
    "checkbox": {
      "path": "/library/assets/checkbox.css",
      "css": ".checkbox{display:flex;align-items:flex-start;gap:12px;min-height:44px;padding-block:10px;cursor:pointer;line-height:1.5}.checkbox>input[type=checkbox]{appearance:none;flex:none;display:grid;place-content:center;width:20px;height:20px;min-height:0;padding:0;font-size:inherit;margin:calc(.75em - 10px) 0 0;border:1.5px solid var(--muted);border-radius:min(var(--control-radius,5px),5px);background:var(--surface);color:var(--on-accent);cursor:inherit}.checkbox>input::before{content:\"\";width:12px;height:12px;background:currentColor;clip-path:polygon(14% 44%,0 59%,38% 96%,100% 22%,84% 8%,36% 67%);opacity:0}.checkbox>input:checked,.checkbox>input:indeterminate{background:var(--accent);border-color:var(--accent)}.checkbox>input:checked::before,.checkbox>input:indeterminate::before{opacity:1}.checkbox>input:indeterminate::before{clip-path:inset(5px 0 round 1px)}.checkbox:hover>input:not(:disabled){border-color:var(--accent)}.checkbox>input:focus-visible{outline:2px solid var(--accent);outline-offset:3px}.checkbox:has(>input:disabled){opacity:.55;cursor:not-allowed}.checkbox>input[aria-invalid=true]{border-style:dashed;border-color:var(--error)}.checkbox>span{min-width:0;overflow-wrap:anywhere}.checkbox small{display:block;color:var(--muted);font-weight:400}.checkbox-group{border:0;padding:0;margin:0;min-width:0}.checkbox-group>legend{padding:0;margin-bottom:8px;font-weight:600}@media(prefers-reduced-motion:no-preference){.checkbox>input{transition:background-color var(--motion-normal) var(--motion-ease),border-color var(--motion-normal) var(--motion-ease)}.checkbox>input::before{transition:opacity var(--motion-normal) var(--motion-ease)}}@media(forced-colors:active){.checkbox>input[type=checkbox]{appearance:auto}.checkbox>input::before{display:none}}",
      "bytes": 1771,
      "hash": "70729e98c68e6ce1040eadb938de80eff295e02a93e699c0bc833067bec84e99"
    },
    "example": {
      "path": "/library/assets/example.css",
      "css": ".example{min-width:0}.example-frame{border:1px solid var(--line);border-radius:var(--radius);background:var(--bg);overflow:visible}.example-preview{display:grid;place-items:center;min-height:144px;padding:24px;border-radius:var(--radius);min-width:0}.example-preview>.example-content{width:100%;min-width:0}.example-preview[data-width=narrow]>.example-content{max-width:480px}.example-preview[data-width=compact]>.example-content{width:auto;align-items:center}.example-preview[data-width=compact]>.example-content>.cluster{justify-content:center}.example-frame>details{border-top:1px solid var(--line);border-bottom:0;padding:0 16px}.example-frame>details>summary{min-height:44px}.example-code{display:grid;gap:16px;padding-bottom:24px;min-width:0}.example-code pre{margin:0;padding:16px;max-height:480px;overflow:auto;background:var(--soft);border:1px solid var(--line);border-radius:var(--radius);font-size:.85rem;line-height:1.6}.example-toolbar{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}.example-frame>.library-preview{border:0;border-radius:var(--radius) var(--radius) 0 0}@media(max-width:480px){.example-preview{padding:16px}}",
      "bytes": 1177,
      "hash": "480457bcd58ede5b2c2dad6dc1eb5d7ab03c069027a4e08938be45294c81ddf0"
    },
    "select": {
      "path": "/library/assets/select.css",
      "css": ".select-control{position:relative;display:block;min-width:0;width:100%}.select-control>select{appearance:none;display:block;width:100%;min-width:0;min-height:44px;margin:0;padding:10px 14px;padding-inline-end:44px;border:1px solid var(--line);border-radius:var(--control-radius,8px);background:var(--surface);color:var(--ink);font:inherit;font-size:1rem;line-height:1.4;cursor:pointer;text-overflow:ellipsis;background-image:linear-gradient(45deg,transparent 40%,var(--muted) 40% 60%,transparent 60%),linear-gradient(135deg,transparent 40%,var(--muted) 40% 60%,transparent 60%);background-position:right 23px center,right 18px center;background-size:5px 5px;background-repeat:no-repeat}.select-control>select:dir(rtl){background-position:left 18px center,left 23px center}.select-control>select:hover:not(:disabled){border-color:var(--muted)}.select-control>select:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-color:var(--accent)}.select-control>select:disabled{opacity:.55;cursor:not-allowed}.select-control>select[aria-invalid=true]{border-color:var(--accent);border-style:dashed;border-color:var(--error)}.select-control option,.select-control optgroup{background:var(--surface);color:var(--ink)}@media(prefers-reduced-motion:no-preference){.select-control>select{transition:background-color var(--motion-normal) var(--motion-ease),border-color var(--motion-normal) var(--motion-ease),color var(--motion-normal) var(--motion-ease)}}@media(forced-colors:active){.select-control>select{appearance:auto;background-image:none}}.select-control[data-select-ready]>select{position:absolute;width:1px;height:1px;min-height:0;padding:0;border:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap;opacity:0;pointer-events:none}.select-trigger{display:block;width:100%;min-height:44px;padding:10px 44px 10px 14px;border:1px solid var(--line);border-radius:var(--control-radius,8px);background-color:var(--surface);color:var(--ink);font:inherit;font-size:1rem;line-height:1.4;text-align:start;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;background-image:linear-gradient(45deg,transparent 40%,var(--muted) 40% 60%,transparent 60%),linear-gradient(135deg,transparent 40%,var(--muted) 40% 60%,transparent 60%);background-position:right 23px center,right 18px center;background-size:5px 5px;background-repeat:no-repeat}.select-trigger:dir(rtl){padding:10px 14px 10px 44px;background-position:left 18px center,left 23px center}.select-trigger:hover:not(:disabled){border-color:var(--muted)}.select-trigger:focus-visible{outline:2px solid var(--accent);outline-offset:2px}.select-trigger[aria-invalid=true]{border:2px dashed var(--error)}.select-trigger:disabled{cursor:not-allowed;opacity:.55}.select-menu{pointer-events:none;position:fixed;inset:auto;margin:0;padding:6px;border:1px solid var(--line);border-radius:var(--radius,12px);background:var(--surface);color:var(--ink);box-shadow:0 8px 28px #0002;overflow:hidden;box-sizing:border-box;max-width:calc(100vw - 16px);font:inherit}.select-menu:popover-open{pointer-events:auto;display:flex;flex-direction:column;gap:4px}.select-options{overflow:auto;overscroll-behavior:contain;min-height:0;scroll-padding:4px}.select-option{position:relative;min-height:44px;padding:10px 36px 10px 12px;border-radius:max(4px,calc(var(--radius,12px) - 6px));line-height:1.5;overflow-wrap:anywhere;cursor:pointer}.select-option:dir(rtl){padding:10px 12px 10px 36px}.select-option:hover:not([aria-disabled=true]),.select-option[data-active]{background:color-mix(in srgb,var(--ink) 7%,transparent)}.select-option[data-active]{outline:2px solid var(--accent);outline-offset:-2px}.select-option[aria-selected=true]{font-weight:600}.select-option[aria-selected=true]::after{content:\"\";position:absolute;inset-inline-end:14px;top:17px;width:9px;height:5px;border-left:2px solid currentColor;border-bottom:2px solid currentColor;transform:rotate(-45deg)}.select-option[aria-disabled=true]{opacity:.5;cursor:not-allowed}.select-group{padding:8px 12px 4px;font-size:.8rem;font-weight:600;color:var(--muted)}.select-search{flex:none;width:100%;min-height:44px;padding:10px 12px;border:1px solid var(--line);border-radius:var(--control-radius,8px);background:var(--surface);color:var(--ink);font:inherit}.select-empty{padding:12px;color:var(--muted);font-size:.875rem}.select-search[hidden],.select-empty[hidden]{display:none}@media(prefers-reduced-motion:no-preference){.select-menu{opacity:0;transform:translateY(-4px);transition:opacity var(--motion-normal,300ms) var(--motion-ease,ease),transform var(--motion-normal,300ms) var(--motion-ease,ease),display var(--motion-normal,300ms) allow-discrete,overlay var(--motion-normal,300ms) allow-discrete}.select-menu:popover-open{opacity:1;transform:none}@starting-style{.select-menu:popover-open{opacity:0;transform:translateY(-4px)}}}[data-motion=none] .select-menu{transition:none;transform:none}@media(forced-colors:active){.select-menu,.select-trigger{border-color:ButtonText}.select-option[data-active]{outline-color:Highlight}.select-option[aria-selected=true]{color:Highlight}}",
      "bytes": 5091,
      "hash": "7b6e6492e42164ddaf949fd9f706421e07bac60785193632d97a4807d6a6f4ef",
      "javascript": "// Progressive single-select enhancement. The native select owns form data and validation.\n(()=>{\n if(!HTMLElement.prototype.showPopover||window.layoutSelect)return;\n const instances=new Map();let serial=0;\n const enhance=select=>{\n  if(instances.has(select)||select.multiple||select.hasAttribute('size')||select.closest('[data-select-native]'))return;\n  const group=select.parentElement;if(!group?.classList.contains('select-control'))return;\n  const controller=new AbortController(),on=(node,event,fn)=>node?.addEventListener(event,fn,{signal:controller.signal});\n  const id='layout-select-'+(++serial),trigger=document.createElement('button'),menu=document.createElement('div'),list=document.createElement('div'),search=document.createElement('input'),status=document.createElement('div');\n  const original={tabindex:select.getAttribute('tabindex'),hidden:select.getAttribute('aria-hidden')};\n  trigger.type='button';trigger.className='select-trigger';trigger.id=id;trigger.setAttribute('role','combobox');trigger.setAttribute('aria-haspopup','listbox');trigger.setAttribute('aria-controls',id+'-list');trigger.setAttribute('aria-expanded','false');\n  menu.className='select-menu';menu.setAttribute('popover','manual');list.className='select-options';list.id=id+'-list';list.setAttribute('role','listbox');\n  search.type='search';search.className='select-search';search.placeholder=group.dataset.searchPlaceholder||'Search options';search.setAttribute('aria-label',search.placeholder);search.setAttribute('role','combobox');search.setAttribute('aria-autocomplete','list');search.setAttribute('aria-controls',list.id);search.setAttribute('aria-expanded','false');search.autocomplete='off';\n  status.className='select-empty';status.setAttribute('role','status');menu.append(search,list,status);group.append(trigger,menu);select.setAttribute('tabindex','-1');select.setAttribute('aria-hidden','true');group.dataset.selectReady='';\n  const properties=[];for(const key of ['value','selectedIndex'])if(!Object.hasOwn(select,key)){const descriptor=Object.getOwnPropertyDescriptor(HTMLSelectElement.prototype,key);Object.defineProperty(select,key,{configurable:true,get(){return descriptor.get.call(this);},set(value){descriptor.set.call(this,value);sync();}});properties.push(key);}\n  let opened=false,active=-1,rows=[],query='',typed='',typedAt=0;\n  const disabled=option=>option.disabled||option.parentElement?.disabled;\n  const searchable=()=>{const setting=group.dataset.selectSearch??select.dataset.selectSearch;return setting==='false'?false:setting!==undefined||[...select.options].filter(o=>!o.hidden&&!o.parentElement.hidden).length>=10;};\n  const owner=()=>searchable()?search:trigger;\n  function sync(){\n   trigger.textContent=select.options[select.selectedIndex]?.label||group.dataset.placeholder||'Choose an option';trigger.disabled=select.matches(':disabled');\n   for(const attr of ['aria-describedby','aria-invalid','aria-required']){const value=attr==='aria-required'?String(select.required):select.getAttribute(attr);value===null?trigger.removeAttribute(attr):trigger.setAttribute(attr,value);}\n   const labels=[...select.labels||[]];labels.forEach((label,i)=>{if(!label.id)label.id=id+'-label-'+i;});\n   const implicit=labels.some(l=>l.contains(group)),labelled=select.getAttribute('aria-labelledby')||(!implicit?labels.map(l=>l.id).join(' '):'');\n   if(labelled){trigger.setAttribute('aria-labelledby',labelled+' '+id);list.setAttribute('aria-labelledby',labelled);}else{const name=select.getAttribute('aria-label')||(implicit?labels.map(l=>{const copy=l.cloneNode(true);copy.querySelectorAll('select,button,.select-menu').forEach(n=>n.remove());return copy.textContent.trim();}).join(' '):'')||select.name||'Choose an option';trigger.setAttribute('aria-label',name+': '+trigger.textContent);list.setAttribute('aria-label',name);}\n   if(trigger.disabled&&opened)close(false);\n  }\n  function highlight(index,scroll=true){\n   active=index;rows.forEach((row,i)=>row.node.toggleAttribute('data-active',i===active));\n   for(const node of [trigger,search])node.removeAttribute('aria-activedescendant');\n   const row=rows[active];if(row){owner().setAttribute('aria-activedescendant',row.node.id);if(scroll)row.node.scrollIntoView({block:'nearest'});}\n  }\n  function render(){\n   list.replaceChildren();rows=[];let lastGroup=null;\n   [...select.options].forEach((option,index)=>{\n    const parent=option.parentElement,heading=parent.tagName==='OPTGROUP'?parent.label:'';\n    if(option.hidden||parent.hidden||!(heading+' '+option.label).toLocaleLowerCase().includes(query.toLocaleLowerCase()))return;\n    if(heading&&parent!==lastGroup){const title=document.createElement('div');title.className='select-group';title.textContent=heading;title.setAttribute('role','presentation');list.append(title);}lastGroup=parent;\n    const node=document.createElement('div');node.className='select-option';node.id=id+'-option-'+index;node.setAttribute('role','option');node.setAttribute('aria-selected',String(option.selected));node.setAttribute('aria-disabled',String(!!disabled(option)));node.textContent=option.label;if(heading)node.setAttribute('aria-label',heading+': '+option.label);\n    node.addEventListener('pointerdown',e=>e.preventDefault());node.addEventListener('click',()=>choose(index));list.append(node);rows.push({node,option,index});\n   });\n   status.textContent=rows.length?'':group.dataset.emptyText||'No options found';status.hidden=!!rows.length;\n   const chosen=rows.findIndex(r=>r.option.selected&&!disabled(r.option));highlight(chosen<0?rows.findIndex(r=>!disabled(r.option)):chosen,false);\n  }\n  function place(){\n   if(!opened)return;const r=trigger.getBoundingClientRect(),v=window.visualViewport,left=v?.offsetLeft||0,top=v?.offsetTop||0,w=v?.width||innerWidth,h=v?.height||innerHeight;\n   const below=top+h-r.bottom-12,above=r.top-top-12,up=below<240&&above>below,room=Math.max(80,Math.min(360,up?above:below));\n   menu.style.width=Math.min(Math.max(r.width,220),w-16)+'px';menu.style.maxHeight=room+'px';menu.style.left=Math.max(left+8,Math.min(r.left,left+w-menu.offsetWidth-8))+'px';menu.style.top=(up?Math.max(top+8,r.top-menu.offsetHeight-6):r.bottom+6)+'px';\n  }\n  function open(){\n   sync();if(trigger.disabled||opened)return;opened=true;query='';search.value='';search.hidden=!searchable();render();menu.removeAttribute('aria-hidden');menu.showPopover();trigger.setAttribute('aria-expanded','true');search.setAttribute('aria-expanded','true');place();owner().focus({preventScroll:true});highlight(active);\n  }\n  function close(restore=true){if(!opened)return;opened=false;menu.setAttribute('aria-hidden','true');menu.hidePopover();for(const node of [trigger,search]){node.setAttribute('aria-expanded','false');node.removeAttribute('aria-activedescendant');}if(restore&&!trigger.disabled)trigger.focus({preventScroll:true});}\n  function choose(index){const option=select.options[index];if(!option||disabled(option)||select.matches(':disabled'))return;const changed=select.selectedIndex!==index;select.selectedIndex=index;sync();close();if(changed){select.dispatchEvent(new Event('input',{bubbles:true}));select.dispatchEvent(new Event('change',{bubbles:true}));}}\n  function key(e){\n   if(e.isComposing)return;\n   if(e.key==='Escape'&&opened){e.preventDefault();e.stopPropagation();close();return;}\n   if(e.key==='Tab'&&opened){close(false);if(e.target===search)trigger.focus({preventScroll:true});return;}\n   if(['ArrowDown','ArrowUp'].includes(e.key)){e.preventDefault();if(!opened){open();return;}const step=e.key==='ArrowDown'?1:-1;let i=active+step;while(i>=0&&i<rows.length&&disabled(rows[i].option))i+=step;if(i>=0&&i<rows.length)highlight(i);return;}\n   if(['Home','End'].includes(e.key)&&e.target!==search){e.preventDefault();if(!opened)open();const enabled=rows.map((r,i)=>disabled(r.option)?-1:i).filter(i=>i>=0);highlight(e.key==='Home'?enabled[0]:enabled.at(-1));return;}\n   if(e.key==='Enter'||(e.key===' '&&e.target!==search)){e.preventDefault();if(!opened)open();else if(active>=0)choose(rows[active].index);return;}\n   if(e.target===trigger&&e.key.length===1&&!e.ctrlKey&&!e.metaKey&&!e.altKey){e.preventDefault();if(!opened)open();if(searchable()){search.value+=e.key;query=search.value;render();place();return;}const now=Date.now();typed=now-typedAt>600?e.key:typed+e.key;typedAt=now;const match=rows.findIndex(r=>!disabled(r.option)&&r.option.label.toLocaleLowerCase().startsWith(typed.toLocaleLowerCase()));if(match>=0)highlight(match);}\n  }\n  on(trigger,'click',()=>opened?close():open());on(trigger,'keydown',key);on(search,'keydown',key);on(search,'input',()=>{query=search.value;render();place();});\n  on(select,'change',()=>{sync();if(opened)render();});on(select,'input',sync);on(select,'focus',()=>trigger.focus());on(select,'invalid',e=>{e.preventDefault();trigger.setAttribute('aria-invalid','true');trigger.focus();});\n  on(select.form,'reset',()=>{close(false);setTimeout(sync,0);});on(document,'pointerdown',e=>{if(opened&&!group.contains(e.target))close(false);});on(document,'focusin',e=>{if(opened&&!group.contains(e.target))close(false);});\n  on(window,'resize',place);on(window.visualViewport,'resize',place);on(window.visualViewport,'scroll',place);document.addEventListener('scroll',place,{capture:true,signal:controller.signal});\n  const observer=new MutationObserver(()=>{sync();if(opened){render();place();}});observer.observe(select,{subtree:true,childList:true,characterData:true,attributes:true});\n  for(let p=group.parentElement;p;p=p.parentElement)if(p.tagName==='FIELDSET')observer.observe(p,{attributes:true,attributeFilter:['disabled']});\n  instances.set(select,()=>{close(false);controller.abort();observer.disconnect();for(const key of properties)delete select[key];trigger.remove();menu.remove();delete group.dataset.selectReady;for(const [key,value] of [['tabindex',original.tabindex],['aria-hidden',original.hidden]])value===null?select.removeAttribute(key):select.setAttribute(key,value);});sync();\n }\n function refresh(){for(const [select,dispose] of instances)if(!select.isConnected||!select.parentElement?.classList.contains('select-control')){dispose();instances.delete(select);}document.querySelectorAll('.select-control>select').forEach(enhance);}\n const watcher=new MutationObserver(refresh);window.layoutSelect={refresh,destroy(){watcher.disconnect();for(const dispose of instances.values())dispose();instances.clear();delete window.layoutSelect;}};refresh();watcher.observe(document.documentElement,{childList:true,subtree:true});\n})();\n",
      "javascriptPath": "/library/assets/select.js",
      "javascriptBytes": 10532
    },
    "section-nav": {
      "path": "/library/assets/section-nav.css",
      "css": ".section-nav{position:sticky;top:var(--section-nav-offset,0px);z-index:5;display:flex;flex-wrap:nowrap;gap:24px;overflow-x:auto;min-width:0;background:var(--bg);box-shadow:inset 0 -1px var(--line);scrollbar-width:thin}.section-nav>a{flex:none;display:flex;align-items:center;min-height:48px;padding:8px 0;color:var(--muted);text-decoration:none;white-space:nowrap}.section-nav>a:hover,.section-nav>a[aria-current]{color:var(--accent)}.section-nav>a:focus-visible{outline-offset:-2px}.section-nav>a[aria-current]{box-shadow:inset 0 -2px var(--accent)}.section-nav[data-ready]>a[aria-current]{box-shadow:none}.section-nav::after{content:\"\";position:absolute;left:0;bottom:0;width:var(--section-width,0px);height:2px;background:var(--accent);transform:translateX(var(--section-x,0px));pointer-events:none}.section-target{scroll-margin-top:calc(var(--section-nav-offset,0px) + 80px)}@media(prefers-reduced-motion:no-preference){.section-nav[data-ready]::after{transition:transform var(--motion-normal) var(--motion-ease),width var(--motion-normal) var(--motion-ease)}}.section-nav .section-nav-mobile{display:none}@media(max-width:760px){.section-nav[data-mobile=select][data-ready]{padding:8px 0;overflow:visible}.section-nav[data-mobile=select][data-ready]>a,.section-nav[data-mobile=select][data-ready]::after{display:none}.section-nav .section-nav-mobile{display:flex;align-items:center;gap:12px;width:100%;font-size:.85rem}.section-nav-mobile>span{flex:none}.section-nav-mobile select{flex:1}}",
      "bytes": 1494,
      "hash": "79391cbb641d744f9caac8f4d421880d4fdd29ff0536d1504f6f4b9f6a1a8b77",
      "javascript": "// Native section links: visible content, URL fragments, and browser history.\n(()=>document.querySelectorAll('[data-section-nav]').forEach(nav=>{\n  if(nav.hasAttribute('data-ready'))return;\n  const links=[...nav.querySelectorAll('a[href^=\"#\"]')];\n  const targets=links.map(a=>{try{return document.getElementById(decodeURIComponent(a.hash.slice(1)))}catch{return null}});\n  if(!links.length||targets.some(t=>!t))return;\n  let select;\n  if(nav.dataset.mobile==='select'){\n    const label=document.createElement('label');\n    label.className='section-nav-mobile select-control';\n    const caption=document.createElement('span');\n    caption.className='sr-only';\n    caption.textContent=nav.getAttribute('aria-label')||'On this page';\n    select=document.createElement('select');\n    links.forEach(link=>{\n      const option=document.createElement('option');\n      option.value=link.hash;option.textContent=link.textContent.trim();select.append(option);\n    });\n    select.addEventListener('change',()=>{location.hash=select.value});\n    label.append(caption,select);nav.append(label);\n  }\n  let pending=false,active;\n  function update(){\n    pending=false;\n    const offset=parseFloat(getComputedStyle(nav).top)||0;\n    const threshold=offset+nav.offsetHeight+32;\n    let index=0;\n    targets.forEach((target,i)=>{if(target.getBoundingClientRect().top<=threshold)index=i});\n    if(window.scrollY>0&&window.scrollY+innerHeight>=document.documentElement.scrollHeight-2)index=links.length-1;\n    const link=links[index];\n    if(select)select.value=link.hash;\n    links.forEach(a=>{if(a===link)a.setAttribute('aria-current','location');else a.removeAttribute('aria-current')});\n    nav.style.setProperty('--section-x',link.offsetLeft+'px');\n    nav.style.setProperty('--section-width',link.offsetWidth+'px');\n    if(active!==link){\n      const box=link.getBoundingClientRect(),frame=nav.getBoundingClientRect();\n      if(box.left<frame.left||box.right>frame.right)nav.scrollBy({left:box.left-frame.left-(frame.width-box.width)/2,behavior:'instant'});\n      active=link;\n    }\n  }\n  const schedule=()=>{if(!pending){pending=true;requestAnimationFrame(update)}};\n  addEventListener('scroll',schedule,{passive:true});addEventListener('resize',schedule);addEventListener('hashchange',schedule);\n  const observer=new ResizeObserver(schedule);observer.observe(nav);targets.forEach(t=>observer.observe(t));\n  update();requestAnimationFrame(()=>nav.dataset.ready='');\n}))();\n",
      "javascriptPath": "/library/assets/section-nav.js",
      "javascriptBytes": 2460
    },
    "tabs": {
      "path": "/library/assets/tabs.css",
      "css": ".tabs{min-width:0;display:grid;gap:24px}.tab-list{position:relative;isolation:isolate;display:flex;gap:4px;overflow-x:auto;padding:4px;background:color-mix(in srgb,var(--ink) 6%,transparent);border-radius:var(--radius);min-width:0}.tab-list>button{flex:none;min-height:44px;padding:8px 16px;border:0;border-radius:max(0px,calc(var(--radius) - 4px));background:transparent;color:color-mix(in srgb,var(--muted) 80%,var(--ink));font:inherit;font-weight:500;white-space:nowrap}.tab-list>button[aria-selected=true]{background:color-mix(in srgb,var(--surface) 82%,transparent);color:var(--ink)}.tab-list>button:hover:not(:disabled){color:var(--accent)}.tab-list>button:focus-visible{outline-offset:-2px}.tabs-line>.tab-list{background:transparent;border-radius:0;position:relative;padding:0;box-shadow:inset 0 -1px var(--line)}.tabs-line>.tab-list>button{border-radius:0;border-bottom:2px solid transparent}.tabs-line>.tab-list>button[aria-selected=true]{background:transparent;color:var(--accent)}.tabs>section{min-width:0;overflow-wrap:anywhere}.tabs[data-orientation=vertical]{grid-template-columns:minmax(0,min(12rem,46%)) minmax(0,1fr)}.tabs[data-orientation=vertical]>.tab-list{flex-direction:column;align-self:start}.tabs[data-orientation=vertical]>.tab-list>button{white-space:normal;overflow-wrap:anywhere;text-align:start}@media(prefers-reduced-motion:no-preference){.tab-list>button{transition:color var(--motion-normal) var(--motion-ease),background-color var(--motion-normal) var(--motion-ease),border-color var(--motion-normal) var(--motion-ease)}}.tabs[data-tabs-ready]>.tab-list::after{content:\"\";position:absolute;left:0;top:0;width:var(--tab-width);height:var(--tab-height);border-radius:max(0px,calc(var(--radius) - 4px));background:color-mix(in srgb,var(--surface) 82%,transparent);z-index:-1;transform:translate(var(--tab-x),var(--tab-y));pointer-events:none}@media(prefers-reduced-motion:no-preference){.tab-list[data-motion-ready]::after{transition:transform var(--motion-normal) var(--motion-ease),width var(--motion-normal) var(--motion-ease),height var(--motion-normal) var(--motion-ease)}}.tabs[data-tabs-ready]>.tab-list>button[aria-selected=true]{background:transparent}.tabs-line[data-tabs-ready]>.tab-list::after{height:2px;border-radius:0;background:var(--accent)}",
      "bytes": 2290,
      "hash": "2348faeb58a739865bf2fc3b9116e1a3e949d331d8d2fa0d2826cb12336f3faa",
      "javascript": "// Enhance source-visible panels; no packages or network requests.\n(()=>{\n  document.querySelectorAll('[data-tabs]').forEach(root=>{\n    if(root.hasAttribute('data-tabs-ready'))return;\n    const list=root.querySelector(':scope > .tab-list');\n    if(!list)return;\n    const tabs=[...list.querySelectorAll(':scope > button[aria-controls]')];\n    const panels=tabs.map(tab=>document.getElementById(tab.getAttribute('aria-controls')));\n    if(!tabs.length||panels.some(panel=>!panel||panel.parentElement!==root)||new Set(panels).size!==panels.length||tabs.some(tab=>!tab.id))return;\n    const enabled=tabs.filter(tab=>!tab.hasAttribute('data-tab-disabled'));\n    if(!enabled.length)return;\n    const vertical=root.dataset.orientation==='vertical';\n    list.setAttribute('role','tablist');\n    list.setAttribute('aria-orientation',vertical?'vertical':'horizontal');\n    let selected;\n    const line=root.classList.contains('tabs-line');\n    function position(){\n      if(!selected)return;\n      list.style.setProperty('--tab-x',selected.offsetLeft+'px');\n      list.style.setProperty('--tab-width',selected.offsetWidth+'px');\n      list.style.setProperty('--tab-height',selected.offsetHeight+'px');\n      list.style.setProperty('--tab-y',(selected.offsetTop+(line?selected.offsetHeight-2:0))+'px');\n    }\n    function select(tab,focus=false){\n      selected=tab;\n      tabs.forEach((t,i)=>{\n        const active=t===tab;\n        t.setAttribute('aria-selected',String(active));t.tabIndex=active?0:-1;\n        panels[i].hidden=!active;\n      });\n      position();\n      if(focus)tab.focus();\n    }\n    tabs.forEach((tab,i)=>{\n      tab.disabled=tab.hasAttribute('data-tab-disabled');\n      tab.setAttribute('role','tab');\n      panels[i].setAttribute('role','tabpanel');\n      panels[i].setAttribute('aria-labelledby',tab.id);panels[i].tabIndex=0;\n      tab.addEventListener('click',()=>{if(!tab.disabled)select(tab);});\n      tab.addEventListener('keydown',event=>{\n        if(event.altKey||event.ctrlKey||event.metaKey)return;\n        const rtl=getComputedStyle(list).direction==='rtl';\n        const next=vertical?'ArrowDown':rtl?'ArrowLeft':'ArrowRight';\n        const prev=vertical?'ArrowUp':rtl?'ArrowRight':'ArrowLeft';\n        let index=enabled.indexOf(tab);\n        if(event.key===next)index=(index+1)%enabled.length;\n        else if(event.key===prev)index=(index-1+enabled.length)%enabled.length;\n        else if(event.key==='Home')index=0;\n        else if(event.key==='End')index=enabled.length-1;\n        else return;\n        event.preventDefault();\n        const target=enabled[index];\n        if(root.dataset.activation==='manual'){\n          enabled.forEach(t=>t.tabIndex=t===target?0:-1);target.focus();\n        }else select(target,true);\n      });\n    });\n    const fromHash=()=>tabs.find((tab,i)=>!tab.disabled&&(panels[i].id===location.hash.slice(1)||[...panels[i].querySelectorAll('[id]')].some(el=>el.id===location.hash.slice(1))));\n    select(fromHash()||enabled.find(tab=>tab.hasAttribute('data-tab-active'))||enabled[0]);\n    window.addEventListener('hashchange',()=>{const tab=fromHash();if(tab)select(tab);});\n    root.dataset.tabsReady='';\n    {\n      const observer=new ResizeObserver(position);\n      observer.observe(list);tabs.forEach(tab=>observer.observe(tab));\n      requestAnimationFrame(()=>list.dataset.motionReady='');\n    }\n  });\n})();\n",
      "javascriptPath": "/library/assets/tabs.js",
      "javascriptBytes": 3368
    },
    "accordion": {
      "path": "/library/assets/accordion.css",
      "css": ".accordion{min-width:0;border-top:1px solid var(--line)}.accordion>details>summary{min-height:44px}.accordion-bordered{border:1px solid var(--line);border-radius:var(--radius);background:color-mix(in srgb,var(--surface) 82%,transparent);padding-inline:24px}.accordion-bordered>details:last-child{border-bottom:0}.accordion>details>summary:hover{color:var(--accent)}@media(prefers-reduced-motion:no-preference){.accordion>details>summary{transition:color var(--motion-normal) var(--motion-ease)}}.accordion>details:not([open])>:not(summary){visibility:hidden}.accordion-plus>details>summary::after{width:12px;height:12px;border:0;background:linear-gradient(var(--muted),var(--muted)) center/100% 1.5px no-repeat,linear-gradient(var(--muted),var(--muted)) center/1.5px 100% no-repeat;transform:rotate(0deg)}.accordion-plus>details[open]>summary::after{transform:rotate(45deg)}",
      "bytes": 874,
      "hash": "7e557ca694e22cbc4e958d06e77731f7f8889b9cf118f992e2443e9cf3c4fd7e"
    },
    "tooltip": {
      "path": "/library/assets/tooltip.css",
      "css": ".tooltip{pointer-events:none;position:fixed;inset:auto;margin:0;z-index:1000;width:max-content;max-width:min(32ch,calc(100vw - 24px));padding:8px 12px;border:0;border-radius:var(--control-radius,8px);background:color-mix(in srgb,var(--surface) 92%,var(--ink));color:var(--ink);font:inherit;font-size:.8125rem;line-height:1.5;font-weight:400;filter:drop-shadow(0 2px 3px #00000012);overflow:visible;overflow-wrap:anywhere}.tooltip:not([data-open]){display:none}.tooltip[data-open]{display:block}@media(prefers-reduced-motion:no-preference){.tooltip[data-open]{animation:tooltip-slide var(--motion-normal) var(--motion-ease)}}.tooltip[data-side=top]{--tip-y:4px}.tooltip[data-side=bottom]{--tip-y:-4px}.tooltip[data-side=left]{--tip-x:4px}.tooltip[data-side=right]{--tip-x:-4px}@keyframes tooltip-slide{from{opacity:0;transform:translate(var(--tip-x,0px),var(--tip-y,0px))}}@media(forced-colors:active){.tooltip{outline:1px solid CanvasText}}.tooltip::after{content:\"\";position:absolute;width:8px;height:8px;background:inherit;left:var(--tip-left);top:var(--tip-top);transform:translate(-50%,-50%) rotate(45deg)}",
      "bytes": 1110,
      "hash": "a47721d5a2b2a9d45d133659fc5c3ae870c9a555571a80a91b82d3d315f1bdca",
      "javascript": "// Optional, delegated tooltip behavior. Works with dynamically added markup.\n(()=>{\n  if(document.documentElement.hasAttribute('data-tooltips-ready'))return;\n  document.documentElement.setAttribute('data-tooltips-ready','');\n  let trigger,tip,timer,dismissed,keyboard=false;\n  const find=node=>node instanceof Element?node.closest('[data-tooltip]'):null;\n  function close(){clearTimeout(timer);if(tip){if(tip.hidePopover&&tip.matches(':popover-open'))tip.hidePopover();delete tip.dataset.open;}trigger=tip=null;}\n  function open(target){\n    clearTimeout(timer);\n    if(target===dismissed||target.matches(':disabled'))return;\n    if(trigger===target)return;\n    close();\n    const id=target.getAttribute('aria-describedby')?.split(/\\s+/).find(id=>document.getElementById(id)?.classList.contains('tooltip'));\n    const next=id&&document.getElementById(id);\n    if(!next)return;\n    trigger=target;tip=next;tip.dataset.open='';\n    if(tip.showPopover)tip.showPopover();\n    const r=target.getBoundingClientRect(),b=tip.getBoundingClientRect(),w=document.documentElement.clientWidth,h=window.innerHeight,gap=12,pad=12;\n    let side=target.dataset.tooltip||'top';\n    if(!['top','bottom','left','right'].includes(side))side='top';\n    const room={top:r.top,bottom:h-r.bottom,left:r.left,right:w-r.right};\n    const opposite={top:'bottom',bottom:'top',left:'right',right:'left'};\n    if(room[side]<(side==='top'||side==='bottom'?b.height:b.width)+gap && room[opposite[side]]>room[side])side=opposite[side];\n    let x=r.left+(r.width-b.width)/2,y=r.top+(r.height-b.height)/2;\n    if(side==='top')y=r.top-b.height-gap;\n    if(side==='bottom')y=r.bottom+gap;\n    if(side==='left')x=r.left-b.width-gap;\n    if(side==='right')x=r.right+gap;\n    x=Math.max(pad,Math.min(x,w-b.width-pad));y=Math.max(pad,Math.min(y,h-b.height-pad));\n    tip.style.left=x+'px';tip.style.top=y+'px';tip.dataset.side=side;\n    const vertical=side==='top'||side==='bottom';\n    const length=vertical?b.width:b.height,radius=Math.min(parseFloat(getComputedStyle(tip).borderTopLeftRadius)||0,b.width/2,b.height/2),edge=Math.min(length/2,Math.max(12,radius+6));\n    const arrow=Math.max(edge,Math.min(vertical?r.left+r.width/2-x:r.top+r.height/2-y,length-edge))+'px';\n    tip.style.setProperty('--tip-left',vertical?arrow:side==='left'?'calc(100% - 2px)':'2px');\n    tip.style.setProperty('--tip-top',vertical?(side==='top'?'calc(100% - 2px)':'2px'):arrow);\n  }\n  function leave(event){if(dismissed&&dismissed.contains(event.target)&&!dismissed.contains(event.relatedTarget))dismissed=null;if(trigger&&trigger.contains(event.relatedTarget))return;clearTimeout(timer);timer=setTimeout(()=>{if(trigger&&!keyboard&&!trigger.matches(':hover'))close();},120);}\n  document.addEventListener('pointerover',event=>{if(event.pointerType==='touch')return;const target=find(event.target);if(target){keyboard=false;open(target);}});\n  document.addEventListener('pointerout',leave);\n  document.addEventListener('pointermove',event=>{if(!trigger||keyboard||event.pointerType==='touch')return;const r=trigger.getBoundingClientRect();if(event.clientX<r.left||event.clientX>r.right||event.clientY<r.top||event.clientY>r.bottom)close();});\n  document.addEventListener('focusin',event=>{const target=find(event.target);if(target){keyboard=target.matches(':focus-visible');open(target);}else close();});\n  document.addEventListener('focusout',leave);\n  document.addEventListener('keydown',event=>{if(event.key==='Escape'){dismissed=trigger;close();}});\n  document.addEventListener('pointerdown',event=>{const target=find(event.target);if(event.pointerType==='touch'&&target){if(trigger===target){dismissed=target;close();}else{dismissed=null;open(target);}}else if(!target&&!tip?.contains(event.target))close();});\n  window.addEventListener('resize',close);\n  document.addEventListener('scroll',close,true);\n})();\n",
      "javascriptPath": "/library/assets/tooltip.js",
      "javascriptBytes": 3858
    },
    "dialog": {
      "path": "/library/assets/dialog.css",
      "css": "layout-dialog{display:contents}.dialog{width:min(var(--dialog-width,480px),calc(100% - 32px));max-height:calc(100dvh - 32px);margin:auto;padding:0;border:1px solid var(--finish-edge,var(--line));border-radius:var(--radius);background:var(--finish-image,none) var(--finish-surface,var(--surface));backdrop-filter:var(--finish-blur,none);color:var(--ink);box-shadow:0 24px 80px #0003;overflow:auto;overflow-wrap:anywhere}.dialog[open]{display:flex;flex-direction:column}.dialog::backdrop{background:#0b151780}.dialog-header,.dialog-body,.dialog-footer{padding:clamp(16px,4vw,24px);min-height:0}.dialog-header,.dialog-footer{flex-shrink:0}.dialog-header{border-bottom:1px solid var(--line)}.dialog-header h2{font-size:1.25rem}.dialog-body{overflow:auto;overscroll-behavior:contain}.dialog-footer{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:12px;border-top:1px solid var(--line)}.dialog form{display:contents}.dialog-wide{--dialog-width:720px}html:has(.dialog:modal){overflow:auto;overflow-wrap:anywhere}@keyframes dialog-enter{from{opacity:0;transform:scale(.98)}}@keyframes dialog-backdrop{from{opacity:0}}@media(prefers-reduced-motion:no-preference){.dialog[open]{animation:dialog-enter var(--motion-normal) var(--motion-ease)}.dialog[open]::backdrop{animation:dialog-backdrop var(--motion-normal) var(--motion-ease)}}",
      "bytes": 1330,
      "hash": "a19b90322eeea79db35b4600cc449ac07b2906f42088ccac0890e046c870a972",
      "javascript": "// Native dialog owns focus containment, Escape, inertness and focus return.\n(()=>{\n  if(document.documentElement.hasAttribute('data-dialogs-ready'))return;\n  document.documentElement.setAttribute('data-dialogs-ready','');\n  let backdrop;\n  document.addEventListener('click',event=>{\n    const opener=event.target.closest('[data-dialog-open]');\n    if(opener&&!opener.matches(':disabled,[aria-disabled=true]')){\n      const dialog=document.getElementById(opener.dataset.dialogOpen);\n      if(dialog instanceof HTMLDialogElement&&!dialog.open){dialog.returnValue='cancel';dialog.showModal();dialog.querySelector('.dialog-body')?.scrollTo(0,0);}\n    }\n    const closer=event.target.closest('[data-dialog-close]');\n    if(closer)closer.closest('dialog')?.close(closer.dataset.dialogClose||'cancel');\n  });\n  // Keep Tab cycling inside embedded previews as well as standalone pages.\n  document.addEventListener('keydown',event=>{\n    const dialog=event.target.closest('dialog:modal');\n    if(event.key!=='Tab'||!dialog)return;\n    const stops=[...dialog.querySelectorAll('button,input,select,textarea,a[href],[tabindex]')].filter(el=>!el.matches(':disabled')&&el.tabIndex>=0&&el.getClientRects().length&&getComputedStyle(el).visibility==='visible'&&!el.closest('[inert]'));\n    const first=stops[0],last=stops.at(-1);\n    if(!first)return;\n    if(event.shiftKey&&(event.target===first||!stops.includes(event.target))){event.preventDefault();last.focus();}\n    else if(!event.shiftKey&&event.target===last){event.preventDefault();first.focus();}\n  });\n  const outside=event=>{\n    const dialog=event.target;\n    if(!(dialog instanceof HTMLDialogElement)||!dialog.hasAttribute('data-dialog-dismiss'))return null;\n    const r=dialog.getBoundingClientRect();\n    return event.clientX<r.left||event.clientX>r.right||event.clientY<r.top||event.clientY>r.bottom?dialog:null;\n  };\n  document.addEventListener('pointerdown',event=>{backdrop=event.button===0?outside(event):null;});\n  document.addEventListener('pointerup',event=>{if(backdrop&&outside(event)===backdrop)backdrop.close('cancel');backdrop=null;});\n  document.addEventListener('pointercancel',()=>{backdrop=null;});\n})();\n",
      "javascriptPath": "/library/assets/dialog.js",
      "javascriptBytes": 2172
    }
  },
  "items": [
    {
      "id": "empty-state",
      "name": "Empty",
      "kind": "components",
      "dependencies": [
        "empty-state",
        "button"
      ],
      "html": "<section class=\"empty-state\" aria-labelledby=\"empty-title\">\n  <h2 id=\"empty-title\">\n    Nothing here yet\n  </h2>\n  <p>\n    Add your first item to get started. It will appear here when you do.\n  </p>\n  <div class=\"cluster\">\n    <button class=\"btn\" type=\"button\">\n      Add Item\n    </button>\n  </div>\n</section>",
      "description": "A clear starting point when there is no content to show.",
      "contract": "Use a contextual heading, brief guidance, and optional native actions. Match the heading level to the page. Empty states are static, not alerts or loading indicators. Wire actions to your application; no runtime is included. Keep important guidance visible and reuse shared buttons and theme tokens.",
      "usage": [
        {
          "title": "Add an optional icon",
          "text": "Place an SVG with class icon before the heading. Use aria-hidden=true and focusable=false when the heading already communicates its meaning. Omit it for a simpler presentation."
        },
        {
          "title": "Give a clear next step",
          "text": "Use a short heading, a helpful explanation, and one or two relevant actions. Keep actions functional in your application."
        },
        {
          "title": "Keep it accessible",
          "text": "Choose the appropriate heading level and label the section with aria-labelledby. Avoid live announcements on initial load; announce updates separately when needed."
        }
      ],
      "javascriptBytes": 0,
      "url": "/components/empty-state"
    },
    {
      "id": "button",
      "name": "Buttons",
      "kind": "components",
      "dependencies": [
        "button"
      ],
      "html": "<div class=\"cluster\">\n  <a class=\"btn\" href=\"#content\">\n    Get Started\n  </a>\n  <a class=\"btn secondary\" href=\"#content\">\n    Learn More\n  </a>\n</div>\n<p id=\"content\">\n  Use real links for navigation and buttons for actions.\n</p>",
      "description": "Primary and secondary actions.",
      "contract": "Use a native button with type=button for actions or type=submit for form submission; use an anchor with a real href for navigation. btn is primary; secondary and ghost are quieter alternatives. btn-large, btn-icon, and btn-pill adjust size or shape. btn-gradient opts primary buttons into the theme\u2019s multicolor fill and hover paint; secondary and ghost remain quiet. btn-choice stacks a sample and label with balanced padding and an inset keyboard focus outline. Keep 44px minimum targets. Label icon-only buttons with aria-label and hide decorative SVGs. Use native disabled on buttons, never on anchors. aria-disabled alone does not prevent activation. Busy examples use disabled plus aria-busy and visible text; implement your own request, error handling, duplicate-submission guard, and completion announcement outside the busy control. All demo actions are illustrative. Shared 300ms color and hover-highlight transitions respect reduced motion and data-motion=none; focus remains immediate. No lift, scale, automatic loading animation, or runtime JavaScript is required.",
      "usage": [
        {
          "title": "Choose the right element",
          "text": "Use `button` for actions and an anchor with a real `href` for navigation. Set the button\u2019s `type` explicitly."
        },
        {
          "title": "Choose an emphasis",
          "text": "Start with `.btn`. Add `.secondary` or `.ghost` for quieter actions; use `.btn-large`, `.btn-icon`, or `.btn-pill` when needed."
        },
        {
          "title": "Handle states clearly",
          "text": "Label icon-only buttons. Use `disabled` for unavailable buttons, and visible progress text with `aria-busy` while working. Your app handles requests and errors."
        }
      ],
      "javascriptBytes": 0,
      "url": "/components/button"
    },
    {
      "id": "link",
      "name": "Links",
      "kind": "components",
      "dependencies": [
        "link"
      ],
      "html": "<a class=\"link-underline\" href=\"/docs\">\n  Explore the Docs\n</a>",
      "description": "Recognizable navigation with native browser behavior.",
      "contract": "Use a native anchor with a meaningful href and descriptive visible text. link-underline keeps a persistent background underline for standalone links, sharing position and thickness with link-animated; hover and press use shared color transitions, with immediate keyboard focus. Base CSS handles reduced motion and data-motion=none. Add link-animated to opt into a 300ms underline reveal on hover with immediate keyboard focus for short standalone links. Native underlines remain the default for prose and long labels; omit link-underline for those. RTL reverses the reveal direction; motion opt-outs make it immediate. Keep prose links underlined and sentence-cased in context; standalone navigation uses title case. Use a button for actions, never href=# or javascript: as a substitute. Do not add role=button, redundant tabindex, or disabled to an anchor. For unavailable destinations render plain explanatory text instead of a fake disabled link. Prefer the same tab; if a new tab is needed, disclose it in visible text and use target=_blank with rel=noopener. Set aria-current=page only for the current navigation destination. Keep real hrefs for crawling, browser menus, and modifier-click; no click interception or runtime is required. Use spacious cluster or navigation layouts for standalone targets; inline prose links follow text flow. Do not truncate meaningful link labels. Downloads must identify their content and use a real file URL; download is a browser hint, not a guarantee. Do not claim ranking improvements from this component.",
      "usage": [
        {
          "title": "Make destinations clear",
          "text": "Use a real `href` and descriptive text. Keep regular text links underlined. Actions belong on buttons."
        },
        {
          "title": "Choose an underline",
          "text": "Use `.link-underline` for short standalone links. Add `.link-animated` for the optional reveal. Leave these classes off long links in prose."
        },
        {
          "title": "Preserve browser behavior",
          "text": "Keep native navigation, keyboard focus, and modifier-click. If a link opens a new tab, say so and add `rel=\"noopener\"`."
        }
      ],
      "javascriptBytes": 0,
      "url": "/components/link"
    },
    {
      "id": "badge",
      "name": "Badges",
      "kind": "components",
      "dependencies": [
        "badge"
      ],
      "html": "<div class=\"cluster\">\n  <span class=\"badge\">\n    Published\n  </span>\n  <span class=\"badge badge-neutral\">\n    Draft\n  </span>\n</div>",
      "description": "Readable status labels.",
      "contract": "Use badge on a span for a short, noninteractive label. Add one emphasis class: badge-neutral, badge-outline, or badge-solid. Pills are the default; add badge-rounded for rounded rectangles or badge-count for numbers. badge-pill remains a compatibility alias. Icons inherit color and size; hide decorative SVGs from assistive technology. Keep visible text and context, never color alone. Do not add tabindex or button roles to static badges. Use a real link or button for actions, with a suitable hit area. For meaningful dynamic updates, put role=status on a containing message and update its text; avoid announcing every counter change. Static badges never animate or enter the tab order. Do not use a badge for an action or a dismissible chip. Customize the shared palette tokens for both themes and verify text contrast of at least 4.5:1 on the actual surface. Avoid truncating essential status text. Dismissible chips, notification overlays, and automatic count formatting are not implemented.",
      "usage": [
        {
          "title": "Label a status",
          "text": "Use `.badge` on a `span` for short text such as Published or Draft. Include words or context so color is never the only clue."
        },
        {
          "title": "Adjust the appearance",
          "text": "Choose `.badge-neutral`, `.badge-outline`, or `.badge-solid`. Pills are the default; use `.badge-rounded` for a rectangular shape."
        },
        {
          "title": "Keep it informational",
          "text": "Badges are not buttons or dismissible chips. Use `.badge-count` for numbers and announce only meaningful status changes."
        }
      ],
      "javascriptBytes": 0,
      "url": "/components/badge"
    },
    {
      "id": "card",
      "name": "Cards",
      "kind": "components",
      "dependencies": [
        "panel"
      ],
      "html": "<article class=\"panel stack\">\n  <p class=\"eyebrow\">\n    Your workspace\n  </p>\n  <h2>\n    A place for your next idea.\n  </h2>\n  <p class=\"muted\">\n    A flexible surface for content, actions, or a project summary.\n  </p>\n</article>",
      "description": "A themed surface with consistent spacing.",
      "contract": "Compose panel and stack with native header, content, and footer. --space controls padding and stack gaps (24px default, 16px compact). Use article only for standalone content; otherwise use a div or an appropriately named section. Choose heading levels from page context. Cards are static containers, not buttons: no tabindex, click handler, or hover lift. Use real anchors for navigation and native buttons for actions; never nest interactive controls in a wrapping link. Do not clip keyboard focus or impose fixed heights. Supply image width and height to reserve space, meaningful alt text for informative images, and empty alt for decoration; lazy-load only below-the-fold media. Use responsive local images; card CSS does not fetch media. Keep descriptions readable and verify contrast after customizing tokens. No runtime, entrance motion, data fetching, or persistence is included.",
      "usage": [
        {
          "title": "Compose the content",
          "text": "Combine `.panel` and `.stack`. Add a heading, body, and optional footer. Use `article` only when the content stands on its own."
        },
        {
          "title": "Keep spacing consistent",
          "text": "`--space` controls padding and gaps: 24px by default, or 16px for a compact card. Let content determine the height."
        },
        {
          "title": "Add actions and images",
          "text": "Use real links or buttons inside the card. Give images dimensions and appropriate alt text. Static cards do not need hover motion or keyboard focus."
        }
      ],
      "javascriptBytes": 0,
      "url": "/components/card"
    },
    {
      "id": "field",
      "name": "Input",
      "kind": "components",
      "dependencies": [
        "field"
      ],
      "html": "<div class=\"field\">\n  <label for=\"project-name\">\n    Project name\n  </label>\n  <input\n    class=\"input\"\n    id=\"project-name\"\n    name=\"project\"\n    type=\"text\"\n    placeholder=\"Website refresh\"\n    aria-describedby=\"project-help\"\n  >\n  <p id=\"project-help\" class=\"muted\">\n    Choose a name your team will recognize.\n  </p>\n</div>",
      "description": "Native text entry with clear labels and useful states.",
      "contract": "Use a native input with class=input and a visible associated label. Compose label, control, and help in field. Use unique ids, name for submitted values, aria-describedby for help/errors, and appropriate type, autocomplete and inputmode. Placeholder is an example, never a label. Mark required visibly and with required. Set aria-invalid=true only after validation, describe how to fix the error, and clear the state when corrected. aria-invalid does not enforce validity; validate on the server too. Disabled controls cannot be edited, focused, or submitted; readonly values remain focusable and submitted. Do not block paste or password managers. Default targets are at least 44px with 16px input text; input-large is 52px. Shared color transitions respect reduced motion and data-motion=none; focus is immediate. No automatic animation, masking, floating labels, requests, storage, or runtime JavaScript is included. Keep instructions in semantic HTML; input styling does not improve search ranking. The field kit id and field.css filename remain stable for existing kits.",
      "usage": [
        {
          "title": "Start with a label",
          "text": "Group a visible label, `input.input`, and help text in `.field`. Connect the label with `for` and a unique input `id`."
        },
        {
          "title": "Help people enter data",
          "text": "Choose the right `type`, `autocomplete`, and `inputmode`. Keep placeholders as examples and allow paste and password managers."
        },
        {
          "title": "Explain errors",
          "text": "Link help and errors with `aria-describedby`. Set `aria-invalid=\"true\"` after validation and explain the fix. Your server still needs to validate submissions."
        }
      ],
      "javascriptBytes": 0,
      "url": "/components/input"
    },
    {
      "id": "accordion",
      "name": "Accordion",
      "kind": "components",
      "dependencies": [
        "disclosure",
        "accordion"
      ],
      "html": "<div class=\"accordion\">\n  <details name=\"getting-started\" open>\n    <summary>\n      What do I need to get started?\n    </summary>\n    <p>\n      Plain HTML and CSS. No packages or build step required.\n    </p>\n  </details>\n  <details name=\"getting-started\">\n    <summary>\n      Can I customize the design?\n    </summary>\n    <p>\n      Use shared tokens for colors, spacing, and motion.\n    </p>\n  </details>\n  <details name=\"getting-started\">\n    <summary>\n      Does it work without JavaScript?\n    </summary>\n    <p>\n      Yes. The browser manages expansion and keyboard interaction.\n    </p>\n  </details>\n</div>",
      "description": "Related sections with native expansion and consistent spacing.",
      "contract": "Compose an accordion wrapper with native details and summary. Include base, disclosure, and accordion CSS once. Use a unique shared name per single-open group; omit name for multiple-open groups. Use open for initial state, with at most one initially open item per named group. Older browsers without name support allow multiple open items. Tab and Shift+Tab navigate; Enter and Space toggle. Arrow, Home, and End navigation are not implemented. Native semantics expose expansion; do not add redundant roles, tabindex, or aria-expanded. Keep summary first, descriptive, and free of nested controls. Use disclosure-content stack for rich bodies. Use an external context-appropriate heading to introduce the group. accordion-bordered adds a themed outline and inset spacing. accordion-plus replaces the chevron with a decorative plus that rotates 45 degrees into a cross. Native details has no disabled state: show unavailable information as visible text instead of faking disabled controls. Keep essential instructions visible, content in source HTML, and links inside bodies. No runtime, requests, persistence, or SEO ranking guarantee. Shared 300ms chevron and progressive height motion honor reduced motion and data-motion=none; unsupported browsers expand immediately. Check focus, collapsed links, rapid toggles, long text, narrow screens, RTL, and all palettes. Use the native open property and toggle event for application-owned behavior.",
      "usage": [
        {
          "title": "Start with a group",
          "text": "Wrap `details` in `.accordion`. Put each question in `summary` and its answer below."
        },
        {
          "title": "Choose what stays open",
          "text": "Share a unique `name` for one open item at a time. Omit it to allow several. Add `open` to start expanded."
        },
        {
          "title": "Make it yours",
          "text": "Use `.accordion-bordered` for an outline or `.accordion-plus` for a rotating plus."
        },
        {
          "title": "Keyboard and motion",
          "text": "Tab moves focus; Enter or Space toggles. Motion respects reduced motion. `data-motion=\"none\"` disables it locally."
        },
        {
          "title": "Keep answers usable",
          "text": "Put links in answers. Keep essential instructions outside the accordion."
        },
        {
          "title": "Know the limits",
          "text": "No JavaScript needed. Older browsers may allow multiple open answers. No arrow-key navigation or disabled items."
        }
      ],
      "javascriptBytes": 0,
      "url": "/components/accordion"
    },
    {
      "id": "disclosure",
      "name": "Disclosure",
      "kind": "components",
      "dependencies": [
        "disclosure"
      ],
      "html": "<details>\n  <summary>\n    Can I use only this component?\n  </summary>\n  <p>\n    Yes. Include the shared base and this component\u2019s CSS. No JavaScript is needed.\n  </p>\n</details>",
      "description": "Expandable content using native details.",
      "contract": "Put a descriptive summary first in details. Use Enter or Space to toggle and Tab to move between controls. Keep links and buttons out of the summary. Native semantics expose state; do not add redundant role, tabindex, or aria-expanded. Use open for initial state, the open property and toggle event for optional application logic, and a unique shared name for an exclusive group. Omit name for independent items. This is not an arrow-key accordion. Use disclosure-content stack for rich bodies. Keep essential instructions visible and content in the HTML; collapsed content is not a security boundary or an SEO guarantee. The label prevents accidental selection; body text stays selectable. Motion uses shared tokens, respects reduced motion and data-motion=none, and animates height where supported. Other browsers retain instant native expansion.",
      "usage": [
        {
          "title": "Reveal extra detail",
          "text": "Use native `details` with a descriptive `summary` first. Add `open` when the content should start visible."
        },
        {
          "title": "Add richer content",
          "text": "Use `.disclosure-content.stack` for paragraphs and links. Keep interactive controls out of the summary."
        },
        {
          "title": "Use native behavior",
          "text": "Tab moves focus; Enter or Space toggles. No JavaScript is needed. Motion respects user preferences, with immediate expansion in unsupported browsers."
        }
      ],
      "javascriptBytes": 0,
      "url": "/components/disclosure"
    },
    {
      "id": "table",
      "name": "Tables",
      "kind": "components",
      "dependencies": [
        "table"
      ],
      "html": "<div class=\"table-scroll\" role=\"region\" aria-labelledby=\"projects-caption\" tabindex=\"0\">\n  <table>\n    <caption id=\"projects-caption\">\n      Example projects\n    </caption>\n    <thead>\n      <tr>\n        <th scope=\"col\">\n          Project\n        </th>\n        <th scope=\"col\">\n          Status\n        </th>\n        <th class=\"table-number\" scope=\"col\">\n          Members\n        </th>\n      </tr>\n    </thead>\n    <tbody>\n      <tr>\n        <th scope=\"row\">\n          Website\n        </th>\n        <td>\n          Published\n        </td>\n        <td class=\"table-number\">\n          8\n        </td>\n      </tr>\n      <tr>\n        <th scope=\"row\">\n          Customer portal\n        </th>\n        <td>\n          In progress\n        </td>\n        <td class=\"table-number\">\n          4\n        </td>\n      </tr>\n      <tr>\n        <th scope=\"row\">\n          Documentation\n        </th>\n        <td>\n          Draft\n        </td>\n        <td class=\"table-number\">\n          2\n        </td>\n      </tr>\n    </tbody>\n  </table>\n</div>",
      "description": "A responsive semantic data table.",
      "contract": "Use native table, caption, thead, tbody, and scoped column/row headers. Keep row labels readable and numbers aligned with table-number; format currency and dates in the application. Wrap wide tables in table-scroll with tabindex=0, role=region, and an accessible name tied to the caption; provide a visible scrolling hint when needed. Inside table-scroll, --table-min sets a readable minimum width (28rem default); use 100% for short tables that should fit. Text wraps while table-number preserves numeric values. Preserve column relationships on small screens instead of hiding important columns or converting cells to cards. Table-striped and table-compact are optional; retain usable targets when cells contain controls. Use descriptive links or buttons inside cells, never clickable or focusable rows. Use colspan matching the column count for empty-state messages. Keep source data in HTML for no-JavaScript access and crawlers; escape untrusted text or use textContent. This static table has no sorting, selection, filtering, pagination, virtualization, or fetching. Implement those separately only when needed; sorting requires native buttons, accurate aria-sort, and announcements of changes. Do not use role=grid unless implementing its complete keyboard model. Static rows have no entrance motion or misleading hover affordance; interactive children use shared motion and focus standards. Validate keyboard scrolling, zoom/reflow, RTL, theme contrast, and screen-reader header associations.",
      "usage": [
        {
          "title": "Describe the data",
          "text": "Use a `caption` and scoped row and column headers. Apply `.table-number` to numbers that should align for comparison."
        },
        {
          "title": "Make room on small screens",
          "text": "Wrap wide tables in `.table-scroll` with a name and `tabindex=\"0\"`. Keep important columns visible and let the region scroll."
        },
        {
          "title": "Add only what you need",
          "text": "Use `.table-striped` or `.table-compact` for presentation. Sorting, filtering, pagination, and fetching are application features, not built into this table."
        }
      ],
      "javascriptBytes": 0,
      "url": "/components/table"
    },
    {
      "id": "bar-chart",
      "name": "Progress",
      "kind": "components",
      "dependencies": [
        "chart"
      ],
      "html": "<figure class=\"stack\">\n  <figcaption>\n    Example weekly signups\n  </figcaption>\n  <dl class=\"bar-chart\">\n    <div>\n      <dt>\n        Mon\n      </dt>\n      <dd>\n        <meter min=\"0\" max=\"100\" value=\"42\" aria-label=\"Mon: 42 signups\">\n          42 of 100\n        </meter>\n      </dd>\n      <dd>\n        42\n      </dd>\n    </div>\n    <div>\n      <dt>\n        Tue\n      </dt>\n      <dd>\n        <meter min=\"0\" max=\"100\" value=\"68\" aria-label=\"Tue: 68 signups\">\n          68 of 100\n        </meter>\n      </dd>\n      <dd>\n        68\n      </dd>\n    </div>\n    <div>\n      <dt>\n        Wed\n      </dt>\n      <dd>\n        <meter min=\"0\" max=\"100\" value=\"55\" aria-label=\"Wed: 55 signups\">\n          55 of 100\n        </meter>\n      </dd>\n      <dd>\n        55\n      </dd>\n    </div>\n    <div>\n      <dt>\n        Thu\n      </dt>\n      <dd>\n        <meter min=\"0\" max=\"100\" value=\"91\" aria-label=\"Thu: 91 signups\">\n          91 of 100\n        </meter>\n      </dd>\n      <dd>\n        91\n      </dd>\n    </div>\n  </dl>\n</figure>",
      "description": "Compact, labeled bars with native meters.",
      "contract": "For small nonnegative datasets. Give all meters the same maximum, visible labels and values, and accessible names. Not a general-purpose plotting engine.",
      "usage": [
        {
          "title": "Choose a fill",
          "text": "Bars use the theme accent by default. Set --bar-color on the chart or a row for another color. Keep visible labels and values; color must not carry meaning alone."
        },
        {
          "title": "Show a small comparison",
          "text": "Use these bars for a small set of nonnegative values. Give every meter the same maximum."
        },
        {
          "title": "Keep values readable",
          "text": "Show a label and number for each bar, with an accessible name. This is a compact summary, not a full charting engine."
        }
      ],
      "javascriptBytes": 0,
      "url": "/components/bar-chart"
    },
    {
      "id": "hero",
      "name": "Hero",
      "kind": "blocks",
      "dependencies": [
        "button",
        "panel"
      ],
      "html": "<section class=\"stack\">\n  <p class=\"eyebrow\">\n    Design with AI. Build like a pro.\n  </p>\n  <h2>\n    Your next idea.\n    <br>\n    Ready for the web.\n  </h2>\n  <p class=\"muted\">\n    Thoughtful components, balanced spacing, and a shared design language for you and your AI.\n  </p>\n  <div class=\"cluster\">\n    <a class=\"btn\" href=\"#details\">\n      Explore the Details\n    </a>\n  </div>\n</section>\n<section id=\"details\" class=\"panel stack\">\n  <h2>\n    Make it yours.\n  </h2>\n  <p>\n    Replace this example with your product\u2019s story.\n  </p>\n</section>",
      "description": "An introduction with a clear next step.",
      "contract": "Use a single page h1 when this is the main hero. Replace the sample destination with a real section or page.",
      "usage": [
        {
          "title": "Introduce the page",
          "text": "Replace the example copy with a clear promise and next step. Use the page\u2019s `h1` when this is its main introduction."
        },
        {
          "title": "Connect the action",
          "text": "Give the call to action a real destination. Keep the heading and supporting copy brief enough to scan."
        }
      ],
      "javascriptBytes": 0,
      "url": "/blocks/hero"
    },
    {
      "id": "features",
      "name": "Feature grid",
      "kind": "blocks",
      "dependencies": [
        "panel"
      ],
      "html": "<section class=\"stack\">\n  <h2>\n    Built around your work.\n  </h2>\n  <div class=\"grid\">\n    <article class=\"panel stack\">\n      <h3>\n        Familiar code\n      </h3>\n      <p class=\"muted\">\n        Semantic HTML and ordinary CSS you can edit.\n      </p>\n    </article>\n    <article class=\"panel stack\">\n      <h3>\n        Only what you need\n      </h3>\n      <p class=\"muted\">\n        Select the pieces that belong in your project.\n      </p>\n    </article>\n    <article class=\"panel stack\">\n      <h3>\n        Your design\n      </h3>\n      <p class=\"muted\">\n        Shared tokens keep details consistent.\n      </p>\n    </article>\n  </div>\n</section>",
      "description": "Three benefits in a responsive grid.",
      "contract": "Use descriptive headings. Grid collapses without JavaScript; cards share the foundation tokens.",
      "usage": [
        {
          "title": "Lead with benefits",
          "text": "Replace each sample with a useful benefit and a descriptive heading. Keep related descriptions similar in length."
        },
        {
          "title": "Let the grid adapt",
          "text": "The grid rearranges without JavaScript. Use shared spacing and color tokens to keep the cards consistent."
        }
      ],
      "javascriptBytes": 0,
      "url": "/blocks/features"
    },
    {
      "id": "stats",
      "name": "Stat cards",
      "kind": "blocks",
      "dependencies": [
        "panel"
      ],
      "html": "<section class=\"stack\">\n  <h2>\n    Workspace overview\n  </h2>\n  <p class=\"muted\">\n    Illustrative data, not connected to a service.\n  </p>\n  <div class=\"grid\">\n    <article class=\"panel stack\">\n      <h3>\n        Projects\n      </h3>\n      <p>\n        14\n      </p>\n    </article>\n    <article class=\"panel stack\">\n      <h3>\n        Members\n      </h3>\n      <p>\n        28\n      </p>\n    </article>\n    <article class=\"panel stack\">\n      <h3>\n        Published\n      </h3>\n      <p>\n        8\n      </p>\n    </article>\n  </div>\n</section>",
      "description": "A row of clearly labeled metrics.",
      "contract": "Label units and time ranges for real data. Do not imply demo values are live. No LayoutDB API exists yet.",
      "usage": [
        {
          "title": "Give numbers context",
          "text": "Label each metric and include its units and time range. Make clear whether a value is live or illustrative."
        },
        {
          "title": "Connect your own data",
          "text": "The examples use static values. Your application supplies updates; no database connection is included."
        }
      ],
      "javascriptBytes": 0,
      "url": "/blocks/stats"
    },
    {
      "id": "landing",
      "name": "Marketing starter",
      "kind": "templates",
      "dependencies": [
        "hero",
        "features",
        "disclosure"
      ],
      "html": "<section class=\"stack\">\n  <p class=\"eyebrow\">\n    Design with AI. Build like a pro.\n  </p>\n  <h2>\n    Your next idea.\n    <br>\n    Ready for the web.\n  </h2>\n  <p class=\"muted\">\n    Thoughtful components, balanced spacing, and a shared design language for you and your AI.\n  </p>\n  <div class=\"cluster\">\n    <a class=\"btn\" href=\"#details\">\n      Explore the Details\n    </a>\n  </div>\n</section>\n<section id=\"details\" class=\"panel stack\">\n  <h2>\n    Make it yours.\n  </h2>\n  <p>\n    Replace this example with your product\u2019s story.\n  </p>\n</section>\n<section class=\"stack\">\n  <h2>\n    Built around your work.\n  </h2>\n  <div class=\"grid\">\n    <article class=\"panel stack\">\n      <h3>\n        Familiar code\n      </h3>\n      <p class=\"muted\">\n        Semantic HTML and ordinary CSS you can edit.\n      </p>\n    </article>\n    <article class=\"panel stack\">\n      <h3>\n        Only what you need\n      </h3>\n      <p class=\"muted\">\n        Select the pieces that belong in your project.\n      </p>\n    </article>\n    <article class=\"panel stack\">\n      <h3>\n        Your design\n      </h3>\n      <p class=\"muted\">\n        Shared tokens keep details consistent.\n      </p>\n    </article>\n  </div>\n</section>\n<section class=\"stack\">\n  <h2>\n    Frequently asked questions\n  </h2>\n  <details>\n    <summary>\n      Can I use only this component?\n    </summary>\n    <p>\n      Yes. Include the shared base and this component\u2019s CSS. No JavaScript is needed.\n    </p>\n  </details>\n</section>",
      "description": "A composed hero, benefits, and FAQ.",
      "contract": "Replace sample content and destinations. Includes only its declared component styles. No form processing, analytics, or backend.",
      "usage": [
        {
          "title": "Tell your story",
          "text": "Replace the sample hero, benefits, and questions with your product\u2019s content. Check heading order and action destinations."
        },
        {
          "title": "Build on the foundation",
          "text": "The starter includes only its declared component styles. Add your own forms, analytics, or backend when needed."
        }
      ],
      "javascriptBytes": 0,
      "url": "/templates/landing",
      "sections": [
        "<section class=\"stack\"><p class=\"eyebrow\">Design with AI. Build like a pro.</p><h2>Your next idea.<br>Ready for the web.</h2><p class=\"muted\">Thoughtful components, balanced spacing, and a shared design language for you and your AI.</p><div class=\"cluster\"><a class=\"btn\" href=\"#details\">Explore the Details</a></div></section>",
        "<section id=\"details\" class=\"panel stack\"><h2>Make it yours.</h2><p>Replace this example with your product\u2019s story.</p></section>",
        "<section class=\"stack\"><h2>Built around your work.</h2><div class=\"grid\"><article class=\"panel stack\"><h3>Familiar code</h3><p class=\"muted\">Semantic HTML and ordinary CSS you can edit.</p></article><article class=\"panel stack\"><h3>Only what you need</h3><p class=\"muted\">Select the pieces that belong in your project.</p></article><article class=\"panel stack\"><h3>Your design</h3><p class=\"muted\">Shared tokens keep details consistent.</p></article></div></section>",
        "<section class=\"stack\"><h2>Frequently asked questions</h2><details><summary>Can I use only this component?</summary><p>Yes. Include the shared base and this component\u2019s CSS. No JavaScript is needed.</p></details></section>"
      ]
    },
    {
      "id": "dashboard",
      "name": "Dashboard starter",
      "kind": "templates",
      "dependencies": [
        "stats",
        "table",
        "bar-chart"
      ],
      "html": "<section class=\"stack\">\n  <h2>\n    Workspace overview\n  </h2>\n  <p class=\"muted\">\n    Illustrative data, not connected to a service.\n  </p>\n  <div class=\"grid\">\n    <article class=\"panel stack\">\n      <h3>\n        Projects\n      </h3>\n      <p>\n        14\n      </p>\n    </article>\n    <article class=\"panel stack\">\n      <h3>\n        Members\n      </h3>\n      <p>\n        28\n      </p>\n    </article>\n    <article class=\"panel stack\">\n      <h3>\n        Published\n      </h3>\n      <p>\n        8\n      </p>\n    </article>\n  </div>\n</section>\n<section class=\"panel stack\">\n  <h2>\n    Recent projects\n  </h2>\n  <div class=\"table-scroll\" role=\"region\" aria-labelledby=\"projects-caption\" tabindex=\"0\">\n    <table>\n      <caption id=\"projects-caption\">\n        Example projects\n      </caption>\n      <thead>\n        <tr>\n          <th scope=\"col\">\n            Project\n          </th>\n          <th scope=\"col\">\n            Status\n          </th>\n          <th class=\"table-number\" scope=\"col\">\n            Members\n          </th>\n        </tr>\n      </thead>\n      <tbody>\n        <tr>\n          <th scope=\"row\">\n            Website\n          </th>\n          <td>\n            Published\n          </td>\n          <td class=\"table-number\">\n            8\n          </td>\n        </tr>\n        <tr>\n          <th scope=\"row\">\n            Customer portal\n          </th>\n          <td>\n            In progress\n          </td>\n          <td class=\"table-number\">\n            4\n          </td>\n        </tr>\n        <tr>\n          <th scope=\"row\">\n            Documentation\n          </th>\n          <td>\n            Draft\n          </td>\n          <td class=\"table-number\">\n            2\n          </td>\n        </tr>\n      </tbody>\n    </table>\n  </div>\n</section>\n<section class=\"panel stack\">\n  <h2>\n    Audience activity\n  </h2>\n  <figure class=\"stack\">\n    <figcaption>\n      Example weekly signups\n    </figcaption>\n    <dl class=\"bar-chart\">\n      <div>\n        <dt>\n          Mon\n        </dt>\n        <dd>\n          <meter min=\"0\" max=\"100\" value=\"42\" aria-label=\"Mon: 42 signups\">\n            42 of 100\n          </meter>\n        </dd>\n        <dd>\n          42\n        </dd>\n      </div>\n      <div>\n        <dt>\n          Tue\n        </dt>\n        <dd>\n          <meter min=\"0\" max=\"100\" value=\"68\" aria-label=\"Tue: 68 signups\">\n            68 of 100\n          </meter>\n        </dd>\n        <dd>\n          68\n        </dd>\n      </div>\n      <div>\n        <dt>\n          Wed\n        </dt>\n        <dd>\n          <meter min=\"0\" max=\"100\" value=\"55\" aria-label=\"Wed: 55 signups\">\n            55 of 100\n          </meter>\n        </dd>\n        <dd>\n          55\n        </dd>\n      </div>\n      <div>\n        <dt>\n          Thu\n        </dt>\n        <dd>\n          <meter min=\"0\" max=\"100\" value=\"91\" aria-label=\"Thu: 91 signups\">\n            91 of 100\n          </meter>\n        </dd>\n        <dd>\n          91\n        </dd>\n      </div>\n    </dl>\n  </figure>\n</section>",
      "description": "A data overview assembled from shared pieces.",
      "contract": "Static example data. Integrate your own data source with safe text rendering; authentication, APIs and LayoutDB integration are not implemented.",
      "usage": [
        {
          "title": "Start with the overview",
          "text": "Replace the sample metrics, projects, and chart values. Give real data clear labels, units, and time ranges."
        },
        {
          "title": "Connect responsibly",
          "text": "Your application owns authentication and data fetching. Render untrusted values safely as text; the starter has no backend connection."
        }
      ],
      "javascriptBytes": 0,
      "url": "/templates/dashboard",
      "sections": [
        "<section class=\"stack\"><h2>Workspace overview</h2><p class=\"muted\">Illustrative data, not connected to a service.</p><div class=\"grid\"><article class=\"panel stack\"><h3>Projects</h3><p>14</p></article><article class=\"panel stack\"><h3>Members</h3><p>28</p></article><article class=\"panel stack\"><h3>Published</h3><p>8</p></article></div></section>",
        "<section class=\"panel stack\"><h2>Recent projects</h2><div class=\"table-scroll\" role=\"region\" aria-labelledby=\"projects-caption\" tabindex=\"0\"><table><caption id=\"projects-caption\">Example projects</caption><thead><tr><th scope=\"col\">Project</th><th scope=\"col\">Status</th><th class=\"table-number\" scope=\"col\">Members</th></tr></thead><tbody><tr><th scope=\"row\">Website</th><td>Published</td><td class=\"table-number\">8</td></tr><tr><th scope=\"row\">Customer portal</th><td>In progress</td><td class=\"table-number\">4</td></tr><tr><th scope=\"row\">Documentation</th><td>Draft</td><td class=\"table-number\">2</td></tr></tbody></table></div></section>",
        "<section class=\"panel stack\"><h2>Audience activity</h2><figure class=\"stack\"><figcaption>Example weekly signups</figcaption><dl class=\"bar-chart\"><div><dt>Mon</dt><dd><meter min=\"0\" max=\"100\" value=\"42\" aria-label=\"Mon: 42 signups\">42 of 100</meter></dd><dd>42</dd></div><div><dt>Tue</dt><dd><meter min=\"0\" max=\"100\" value=\"68\" aria-label=\"Tue: 68 signups\">68 of 100</meter></dd><dd>68</dd></div><div><dt>Wed</dt><dd><meter min=\"0\" max=\"100\" value=\"55\" aria-label=\"Wed: 55 signups\">55 of 100</meter></dd><dd>55</dd></div><div><dt>Thu</dt><dd><meter min=\"0\" max=\"100\" value=\"91\" aria-label=\"Thu: 91 signups\">91 of 100</meter></dd><dd>91</dd></div></dl></figure></section>"
      ]
    },
    {
      "id": "tooltip",
      "name": "Tooltips",
      "kind": "components",
      "dependencies": [
        "button",
        "tooltip"
      ],
      "html": "<div class=\"cluster\" style=\"padding:48px 0\">\n  <button\n    class=\"btn secondary\"\n    type=\"button\"\n    data-tooltip=\"top\"\n    aria-describedby=\"tip-top\"\n  >\n    Top\n  </button>\n  <span class=\"tooltip\" id=\"tip-top\" role=\"tooltip\" popover=\"manual\">\n    A little context, right where you need it.\n  </span>\n  <button\n    class=\"btn secondary\"\n    type=\"button\"\n    data-tooltip=\"right\"\n    aria-describedby=\"tip-right\"\n  >\n    Right\n  </button>\n  <span class=\"tooltip\" id=\"tip-right\" role=\"tooltip\" popover=\"manual\">\n    A little context, right where you need it.\n  </span>\n  <button\n    class=\"btn secondary\"\n    type=\"button\"\n    data-tooltip=\"bottom\"\n    aria-describedby=\"tip-bottom\"\n  >\n    Bottom\n  </button>\n  <span class=\"tooltip\" id=\"tip-bottom\" role=\"tooltip\" popover=\"manual\">\n    A little context, right where you need it.\n  </span>\n  <button\n    class=\"btn secondary\"\n    type=\"button\"\n    data-tooltip=\"left\"\n    aria-describedby=\"tip-left\"\n  >\n    Left\n  </button>\n  <span class=\"tooltip\" id=\"tip-left\" role=\"tooltip\" popover=\"manual\">\n    A little context, right where you need it.\n  </span>\n</div>",
      "description": "Small, contextual hints on hover, focus, or touch.",
      "contract": "Include tooltip.css and tooltip.js once. Link a focusable trigger to a unique role=tooltip ID with aria-describedby; add data-tooltip=top, right, bottom, or left. Position flips and clamps at viewport edges. Escape dismisses; focus stays on the trigger. Keep essential help visible and tooltip content short, plain text, and noninteractive. Do not use on disabled controls or as a replacement for an accessible label. Without JavaScript the supplemental hint stays hidden. Use textContent for dynamic text. Preserve the trigger\u2019s accessible name; aria-describedby may include other help IDs. Avoid title attributes, live regions, and focus transfer. Hints remain hoverable; Escape dismissal persists until leaving the trigger or a new touch. Touch preserves the trigger action. Placement directions are physical, including in RTL. Tooltips fade and slide 4px from the resolved side by default, using shared 300ms timing. The 120ms pointer-leave grace period allows travel into the hint. Reduced motion disables animation; data-motion=none on a common ancestor opts out locally. No packages required.",
      "usage": [
        {
          "title": "Add a little context",
          "text": "Connect the trigger to a unique tooltip ID with `aria-describedby`. Put `data-tooltip=\"top\"` on the trigger to choose a preferred side."
        },
        {
          "title": "Keep help accessible",
          "text": "Tooltips open on hover, focus, or touch; Escape dismisses them. Keep essential instructions visible and avoid links or buttons inside the hint."
        },
        {
          "title": "Choose the motion",
          "text": "Tooltips keep a simple theme-aware surface and arrow across styles, and fade and slide 4px. Both effects respect reduced motion. Include the tooltip script once."
        }
      ],
      "javascriptBytes": 3858,
      "url": "/components/tooltip"
    },
    {
      "id": "dialog",
      "name": "Dialog",
      "kind": "components",
      "dependencies": [
        "button",
        "field",
        "dialog"
      ],
      "html": "<div class=\"cluster\">\n  <button\n    class=\"btn secondary\"\n    type=\"button\"\n    data-dialog-open=\"welcome\"\n    aria-haspopup=\"dialog\"\n    aria-controls=\"welcome\"\n  >\n    Simple Dialog\n  </button>\n  <button\n    class=\"btn secondary\"\n    type=\"button\"\n    data-dialog-open=\"confirm\"\n    aria-haspopup=\"dialog\"\n    aria-controls=\"confirm\"\n  >\n    Confirmation\n  </button>\n  <button\n    class=\"btn secondary\"\n    type=\"button\"\n    data-dialog-open=\"profile\"\n    aria-haspopup=\"dialog\"\n    aria-controls=\"profile\"\n  >\n    Form Dialog\n  </button>\n  <button\n    class=\"btn secondary\"\n    type=\"button\"\n    data-dialog-open=\"guide\"\n    aria-haspopup=\"dialog\"\n    aria-controls=\"guide\"\n  >\n    Scrolling Content\n  </button>\n</div>\n<p class=\"muted\">\n  Try Escape, Tab, and Shift+Tab. Closing returns focus to the button. These examples do not save or delete data. JavaScript is required to open them.\n</p>\n<dialog\n  class=\"dialog\"\n  id=\"welcome\"\n  aria-labelledby=\"welcome-title\"\n  aria-describedby=\"welcome-description\"\n  data-dialog-dismiss\n>\n  <header class=\"dialog-header\">\n    <h2 id=\"welcome-title\">\n      A little room to focus.\n    </h2>\n  </header>\n  <div class=\"dialog-body stack\">\n    <p id=\"welcome-description\">\n      Keep a focused task in view while the rest of the page takes a step back.\n    </p>\n    <p class=\"muted\">\n      This example also closes when you click the backdrop.\n    </p>\n  </div>\n  <footer class=\"dialog-footer\">\n    <button class=\"btn\" type=\"button\" data-dialog-close autofocus>\n      Got It\n    </button>\n  </footer>\n</dialog>\n<dialog\n  class=\"dialog\"\n  id=\"confirm\"\n  role=\"alertdialog\"\n  aria-labelledby=\"confirm-title\"\n  aria-describedby=\"confirm-description\"\n>\n  <header class=\"dialog-header\">\n    <h2 id=\"confirm-title\">\n      Discard this draft?\n    </h2>\n  </header>\n  <div class=\"dialog-body\">\n    <p id=\"confirm-description\">\n      This is a confirmation preview. No draft will be discarded. In your app, explain the consequence before asking someone to continue.\n    </p>\n  </div>\n  <form method=\"dialog\">\n    <footer class=\"dialog-footer\">\n      <button class=\"btn secondary\" value=\"cancel\" autofocus>\n        Keep Draft\n      </button>\n      <button class=\"btn\" value=\"discard\">\n        Discard Draft\n      </button>\n    </footer>\n  </form>\n</dialog>\n<dialog\n  class=\"dialog\"\n  id=\"profile\"\n  aria-labelledby=\"profile-title\"\n  aria-describedby=\"profile-description\"\n>\n  <header class=\"dialog-header\">\n    <h2 id=\"profile-title\">\n      Make yourself at home.\n    </h2>\n  </header>\n  <form method=\"dialog\">\n    <div class=\"dialog-body stack\">\n      <p id=\"profile-description\" class=\"muted\">\n        Try native validation. This demo closes without saving your changes.\n      </p>\n      <label class=\"stack\">\n        Display name\n        <input\n          class=\"input\"\n          name=\"display-name\"\n          autocomplete=\"nickname\"\n          required\n          placeholder=\"Your name\"\n          autofocus\n        >\n      </label>\n      <label class=\"stack\">\n        Email address\n        <input\n          class=\"input\"\n          type=\"email\"\n          name=\"email\"\n          autocomplete=\"email\"\n          required\n          placeholder=\"you@example.com\"\n        >\n      </label>\n    </div>\n    <footer class=\"dialog-footer\">\n      <button class=\"btn secondary\" type=\"button\" data-dialog-close>\n        Cancel\n      </button>\n      <button class=\"btn\" value=\"done\">\n        Done\n      </button>\n    </footer>\n  </form>\n</dialog>\n<dialog\n  class=\"dialog dialog-wide\"\n  id=\"guide\"\n  aria-labelledby=\"guide-title\"\n  data-dialog-dismiss\n>\n  <header class=\"dialog-header\">\n    <h2 id=\"guide-title\" tabindex=\"-1\" autofocus>\n      Space for the whole story.\n    </h2>\n  </header>\n  <div class=\"dialog-body stack\" tabindex=\"0\" aria-label=\"Example reading content\">\n    <p>\n      The body scrolls while the heading and actions stay visible.\n    </p>\n    <h3>\n      One task at a time\n    </h3>\n    <p>\n      Keep the purpose clear. Use a page for a separate destination.\n    </p>\n    <h3>\n      A clear next step\n    </h3>\n    <p>\n      Choose specific labels. Give people a way to cancel.\n    </p>\n    <h3>\n      Every screen\n    </h3>\n    <p>\n      Try a narrow window or larger text. Actions stay within reach.\n    </p>\n    <h3>\n      Keyboard access\n    </h3>\n    <p>\n      Use Tab to move, Escape to close, and return to your starting point.\n    </p>\n  </div>\n  <footer class=\"dialog-footer\">\n    <button class=\"btn\" type=\"button\" data-dialog-close>\n      Close\n    </button>\n  </footer>\n</dialog>",
      "description": "Focused overlays for information, decisions, and forms.",
      "contract": "Use native dialog with class=dialog and aria-labelledby pointing to its heading. Open with a button carrying data-dialog-open=ID and aria-controls=ID; close with data-dialog-close or a form method=dialog. Include dialog.css and dialog.js once. Use autofocus deliberately; confirmations should focus the safe action. Escape cancels and native modal focus returns to the opener. Add data-dialog-dismiss only for optional backdrop dismissal. dialog-wide changes width; --dialog-width customizes it. Listen to the native close event and returnValue for choices. Real submissions need your own validation, persistence and error handling; do not treat closing as a successful save. Keep a visible cancel or close control. JavaScript is required to open these examples. Keep essential information available on the page without opening a modal. Escape, backdrop dismissal, and close controls produce cancel; method=dialog buttons return their explicit value. Opening resets returnValue and body scroll. Form and confirmation examples do not dismiss on backdrop clicks. Native disabled and aria-disabled openers are ignored. Verify Tab and Shift+Tab containment, focus return, validation, short viewports, RTL, reduced motion, and repeated opening. Initial focus should favor the safe action or a reading heading; do not autofocus a destructive action. Dialogs are for focused tasks, not indexable standalone destinations.",
      "usage": [
        {
          "title": "Open a focused task",
          "text": "Use `dialog.dialog` with a visible title linked by `aria-labelledby`. Connect an opener with `data-dialog-open` and the dialog\u2019s ID."
        },
        {
          "title": "Offer a clear way out",
          "text": "Include a close or cancel button. Escape closes the dialog and focus returns to the opener. Use `autofocus` deliberately, favoring the safe action."
        },
        {
          "title": "Own the result",
          "text": "Include the dialog script once. Your app handles validation, saving, and errors. Closing a dialog does not mean data was saved."
        }
      ],
      "javascriptBytes": 2172,
      "url": "/components/dialog"
    },
    {
      "id": "tabs",
      "name": "Tabs",
      "kind": "components",
      "dependencies": [
        "tabs",
        "select",
        "section-nav"
      ],
      "html": "<div class=\"tabs\" data-tabs>\n  <div class=\"tab-list\" aria-label=\"Project information\">\n    <button\n      type=\"button\"\n      id=\"project-overview-tab\"\n      aria-controls=\"project-overview\"\n      disabled\n    >\n      Overview\n    </button>\n    <button\n      type=\"button\"\n      id=\"project-activity-tab\"\n      aria-controls=\"project-activity\"\n      disabled\n    >\n      Activity\n    </button>\n    <button\n      type=\"button\"\n      id=\"project-settings-tab\"\n      aria-controls=\"project-settings\"\n      disabled\n    >\n      Settings\n    </button>\n  </div>\n  <section class=\"stack\" id=\"project-overview\">\n    <h3>\n      Project overview\n    </h3>\n    <p>\n      Keep the work, people, and next steps in one place.\n    </p>\n  </section>\n  <section class=\"stack\" id=\"project-activity\">\n    <h3>\n      Recent activity\n    </h3>\n    <p>\n      Your team has published the first design review.\n    </p>\n  </section>\n  <section class=\"stack\" id=\"project-settings\">\n    <h3>\n      Project settings\n    </h3>\n    <p>\n      Choose the preferences that fit your workflow.\n    </p>\n  </section>\n</div>",
      "description": "Switch between related views without leaving the page.",
      "contract": "Use .tabs[data-tabs] with a direct .tab-list and direct section panels. Label the list with aria-label. Each native button needs a unique id and aria-controls matching one panel id. Keep buttons disabled and all panels visible in source; tabs.js enables controls and adds tab semantics only after validating references. Include tabs.css and tabs.js once. Arrow keys follow orientation and RTL; Home/End select first/last enabled tab. Automatic activation is default for local content; data-activation=manual moves focus only until Enter/Space. data-orientation=vertical uses Up/Down. data-tab-disabled keeps a tab unavailable; explain why visibly. data-tab-active chooses the initial enabled tab; a matching URL fragment takes precedence. Hash changes reveal a matching panel; selecting a tab does not rewrite browser history. Inactive panels are hidden immediately; panel form state is retained. Never nest controls in tabs. Shared 300ms color and hover-highlight transitions respect reduced motion and data-motion=none; focus and panel selection are immediate. No fetching, routing, persistence, or dynamic tab insertion is provided. Re-running the script does not duplicate listeners. Use ordinary links for page/section navigation, not tab roles. Keep essential content in HTML; no SEO score is guaranteed. For page sections use nav.section-nav[data-section-nav] with native fragment links and section-target targets. Add data-mobile=select for the native mobile dropdown. Keep all sections visible; aria-current=location follows scroll. Set --section-nav-offset on a common ancestor for fixed headers. Never use panel-tab roles for navigation.",
      "usage": [
        {
          "title": "Navigate page sections",
          "text": "For sections that remain visible, use section-nav with native links and section-target IDs. Add data-mobile=select for the mobile dropdown. Try the full-page example below the panel examples."
        },
        {
          "title": "Group related views",
          "text": "Use tabs for alternate panels. Keep ordinary links for page navigation or jumping between visible sections."
        },
        {
          "title": "Connect each panel",
          "text": "Match each button\u2019s `aria-controls` to a unique panel ID. Load the tabs script once; without it, all panels remain readable."
        },
        {
          "title": "Choose the behavior",
          "text": "Arrow keys move between tabs. Use `data-activation=\"manual\"` to require Enter or Space before switching panels. Home and End move to the first and last tab."
        },
        {
          "title": "Make it yours",
          "text": "Use `.tabs-line` for an underline or `data-orientation=\"vertical\"` for a vertical list. Shared motion respects reduced-motion preferences."
        }
      ],
      "javascriptBytes": 3368,
      "url": "/components/tabs"
    },
    {
      "id": "datepicker",
      "name": "Date Picker",
      "kind": "components",
      "dependencies": [
        "field",
        "datepicker"
      ],
      "html": "<div class=\"field\">\n  <label for=\"start-date\">\n    Start date\n  </label>\n  <div class=\"datepicker\" data-datepicker>\n    <input\n      class=\"input\"\n      type=\"date\"\n      id=\"start-date\"\n      name=\"start\"\n      aria-describedby=\"date-help\"\n    >\n    <button type=\"button\" data-datepicker-open aria-label=\"Choose a date\" hidden>\n      <svg\n        class=\"icon\"\n        aria-hidden=\"true\"\n        focusable=\"false\"\n        xmlns=\"http://www.w3.org/2000/svg\"\n        viewbox=\"0 0 24 24\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        stroke-width=\"1.75\"\n        stroke-linecap=\"round\"\n        stroke-linejoin=\"round\"\n      >\n        <rect x=\"3\" y=\"5\" width=\"18\" height=\"16\" rx=\"2\">\n        </rect>\n        <path d=\"M7 3v4m10-4v4M3 10h18M7 14h2m4 0h2m-8 3h2\">\n        </path>\n      </svg>\n    </button>\n  </div>\n  <p class=\"muted\" id=\"date-help\">\n    Choose a date or enter it directly. This example does not save it.\n  </p>\n</div>",
      "description": "Choose a date with a clear calendar or familiar native input.",
      "contract": "Compose a labeled native input.input type=date inside datepicker[data-datepicker], with a hidden type=button data-datepicker-open. Include base, field and datepicker CSS plus deferred datepicker.js. Clicking the input or calendar button opens the picker; plain keyboard focus does not. Desktop input clicks preserve editing focus; mobile modal and button activation focus the calendar. Dismissal returns to the opener unless focus moved elsewhere. Keyboard instructions live in the documentation, not inside the calendar. Native entry remains available without JavaScript; hide the native calendar indicator only after enhancement; the optional calendar uses anchored nonmodal desktop dialog and a mobile modal below 480px, with immediate Escape and appropriate focus return. One date only, proleptic Gregorian years 0001\u20139999, ISO YYYY-MM-DD values with no time zone. Never parse localized text or convert date-only values through local Date constructors. Use min, max and step for date constraints; the calendar honors these and marks unavailable dates aria-disabled, keeping them navigable for context. Set consistent min/max limits; app/server validation remains required. Arrows move a day/week, Home/End a week edge, Page Up/Down a month and Shift+Page Up/Down a year. The six-row calendar includes muted, selectable adjacent-month dates, subject to the same limits. Month/year changes clamp the day for leap years and shorter months. Enter/Space selects and emits bubbling input/change; Backdrop clicks and Escape discard navigation. Native form reset restores the initial value. Disabled and readonly inputs cannot open the custom picker. Only one day is in the tab order. Dates, weekday names and heading use Intl with nearest lang and Gregorian calendar; Localize data-title, data-month, data-year and data-previous-month/data-next-month, data-previous-year/data-next-year, data-previous-years/data-next-years. Separate month/year buttons open 12-item selection grids with arrow/Home/End navigation; Page Up/Down changes year or 12-year page. Selecting either a year or month returns directly to the day calendar, preserving the other part where date limits allow. Weekday labels use Intl narrow forms with full names in column headers. Sunday starts the week; data-week-start=1 uses Monday. RTL reverses horizontal arrows. Date cells have 44px height; narrow calendars may have smaller widths but preserve spacing. Dialog is bounded by viewport and scrollable. Shared 300ms fade/4px slide honors reduced motion and data-motion=none; focus and selection are immediate. No package dependencies, requests, persistence, natural-language parsing, non-Gregorian calendar or arbitrary disabled-date callback. Custom time popup: use .datepicker[data-timepicker] with a native time input and hidden labeled button; datepicker.js enhances it. data-hour-format=12 enables AM/PM; default 24-hour. Native number inputs and chevron buttons apply valid changes immediately with input/change; invalid edits retain the last valid value. Escape/outside close. Localize data-title/hour/minute/second/increase/decrease/period/invalid. Pair with a labeled native input type=time for date-and-time, or use time alone with base and field CSS and no runtime. Native time values use HH:mm or HH:mm:ss; the enhanced text field follows the configured picker hour format, with validated text entry. step=60 selects minute precision and step=1 seconds. Preserve min/max/required, disabled/readonly, submission and reset. Application code owns time zones and daylight-saving ambiguity. For paired ranges, use data-date-range with data-range-start and data-range-end inputs and optional data-range-error message linked by aria-describedby. Enhancement combines the fields into one visible range button (data-range-label/data-range-placeholder localize its name/empty text); separate native fields remain form values and no-JavaScript fallback. One shared calendar: first select a draft start, then an end on or after it. Commit both inputs together only after the second selection; Escape/outside discard incomplete drafts. Same-day ranges are allowed. Localize data-start-prompt and data-end-prompt on the first datepicker wrapper. Typed reversed ranges set custom validity on the end and announce the error; values are never silently changed. data-range-message localizes the ordering error. Clearing and form reset update validation. Preserve native min/max/step and validate ordering on the server, including without JavaScript. Keep instructions in HTML; no SEO ranking guarantee. Test keyboard, touch, screen readers, zoom/reflow, forced colors, limits, step, reset and all palettes before release.",
      "usage": [
        {
          "title": "Start with a real date field",
          "text": "Use a visible label and `input type=\"date\"`. Clicking the field or calendar button opens the picker; Tab focus keeps direct keyboard entry available. The small calendar button is enabled by `datepicker.js`; native entry still works without it."
        },
        {
          "title": "Make choosing easy",
          "text": "Click the month or year separately to open a selection grid. Selecting a month or year returns directly to the calendar so you can choose a day. Arrows page through years in month view and groups of 12 years in year view. Muted dates belong to neighboring months and can also be selected. The calendar opens beside its field on desktop and as a compact modal on mobile. Arrows move by day or week; Home/End move to week edges. Page Up/Down changes month; hold Shift to change year. Enter selects. Escape or clicking outside closes without changing the value."
        },
        {
          "title": "Choose a date range",
          "text": "Wrap two date pickers in `data-date-range` and mark their inputs `data-range-start` and `data-range-end`. Use unique labels, IDs and names. JavaScript combines the fields into one visible range control and one shared calendar. The two native fields remain the submitted values and no-JavaScript fallback. The first selection starts a draft; the second commits both dates together. Escape or outside dismissal discards an unfinished draft. Same-day ranges are allowed. Typed reversed ranges receive native validation and an optional `[data-range-error]` live message; localize it with `data-range-message`. Clearing a date removes its ordering constraint. Native min/max/step stay intact. Without JavaScript, both fields still work; validate ordering on the server."
        },
        {
          "title": "Explain the limits",
          "text": "Set `min`, `max`, and `step` only when needed, and describe the allowed dates before entry. Mark required fields visibly and connect errors with `aria-describedby`."
        },
        {
          "title": "Add time when needed",
          "text": "Pair the calendar with a labeled `input type=\"time\"`, or use it alone with base and field styles. For a styled popup, wrap it in `.datepicker[data-timepicker]` with a hidden labeled button and include the Date picker styles and script. Use `data-hour-format=\"12\"` for AM/PM or omit it for 24 hours. Valid changes apply immediately; Escape and outside clicks close the picker. Invalid edits do not replace the last valid time. AM/PM and chevrons are disabled when their next change violates min/max/step; invalid typed entries show an error. Native entry works without JavaScript. Use `step=\"60\"` for minutes or `step=\"1\"` for seconds, with native `min`, `max`, and `required` constraints. The enhanced text field follows the picker\u2019s 12/24-hour format; submitted values use `HH:mm` or `HH:mm:ss`. Explain the scheduling time zone and resolve daylight-saving ambiguity in application code; a local date and time are not a UTC timestamp."
        },
        {
          "title": "Keep dates as dates",
          "text": "Read and submit `YYYY-MM-DD` values. A birthday or due date is not a timestamp; converting it through a time zone can shift the day."
        },
        {
          "title": "Localize the whole experience",
          "text": "Set `lang` and `dir`, translate the calendar action labels, and use `data-week-start=\"1\"` for Monday. The calendar uses Gregorian dates."
        },
        {
          "title": "Know what is included",
          "text": "This picker selects one date. Time-zone conversion, natural-language parsing, saving, and server validation belong to the application. Check assistive technology and mobile browsers for your audience."
        }
      ],
      "javascriptBytes": 0,
      "url": "/components/datepicker"
    },
    {
      "id": "slider",
      "name": "Slider",
      "kind": "components",
      "dependencies": [
        "slider"
      ],
      "html": "<div class=\"slider-field\">\n  <div class=\"slider-heading\">\n    <label for=\"volume\">\n      Volume\n    </label>\n    <output for=\"volume\" data-unit=\"%\">\n      50%\n    </output>\n  </div>\n  <input id=\"volume\" name=\"volume\" type=\"range\" min=\"0\" max=\"100\" step=\"1\" value=\"50\">\n</div>",
      "description": "Adjust a value within a defined range.",
      "contract": "Set --slider-track to a color or gradient for a custom track, with a visible label describing its scale. Use .slider-field with a labeled native input type=range and explicit min, max, step and value. Optional output with matching for and data-unit displays the value when deferred slider.js is included. Native arrows, Home and End, disabled, form submission and reset remain available. Dispatch input after programmatic changes. No network or persistence. Keep help linked with aria-describedby; use a number input when precise typed entry is essential.",
      "usage": [
        {
          "title": "Define a range",
          "text": "Set min, max and step to meaningful values. Include a visible label and optional output with units."
        },
        {
          "title": "Preserve native controls",
          "text": "Arrow keys adjust the value; Home and End reach the limits. Native disabled, form submission and reset remain intact. Use a number input when exact typing is essential."
        },
        {
          "title": "Show the value",
          "text": "Load slider.js to synchronize output on input and form reset. Dispatch input after programmatic changes. The range works without JavaScript."
        }
      ],
      "javascriptBytes": 0,
      "url": "/components/slider"
    },
    {
      "id": "spinner",
      "name": "Spinner",
      "kind": "components",
      "dependencies": [
        "button",
        "spinner"
      ],
      "html": "<div class=\"cluster\" role=\"status\">\n  <span class=\"spinner\" aria-hidden=\"true\">\n  </span>\n  <span>\n    Loading\u2026\n  </span>\n</div>",
      "description": "A quiet indicator for work in progress.",
      "contract": "Include base and spinner CSS. Use .spinner, optional .spinner-small or .spinner-large, or --spinner-size for sizing. It inherits currentColor. Pair the decorative aria-hidden spinner with visible loading text in a role=status container. Do not add a second live region inside an existing status or toast. Application code inserts and removes the indicator as real work starts and finishes; initial static status content is not guaranteed to be announced. Use aria-busy on the region being updated and clear it on completion. Keep known progress in a native progress element instead. Continuous linear rotation takes three shared motion durations (900ms by default), a purposeful ongoing-work exception; reduced motion and data-motion=none show a static clock icon with unchanged status text. Never imply percentage progress or completion. No JavaScript, dependencies beyond base CSS, focusable elements or network requests.",
      "usage": [
        {
          "title": "Explain the wait",
          "text": "Keep short, visible status text beside the spinner. Hide the ring itself from assistive technology when the text describes it. Use one polite status region, not nested announcements."
        },
        {
          "title": "Connect it to real work",
          "text": "Show the spinner only while an operation is pending. Remove it on success or failure and provide the result. Dismissing a loading toast does not cancel its operation."
        },
        {
          "title": "Keep progress honest",
          "text": "A spinner indicates an unknown duration. Use a progress element when the percentage is known. Mark the updating content region aria-busy and clear that state when it is ready."
        },
        {
          "title": "Respect motion preferences",
          "text": "Reduced motion and data-motion=none replace the rotating ring with a static clock icon and preserve readable status. The spinner needs no JavaScript."
        }
      ],
      "javascriptBytes": 0,
      "url": "/components/spinner"
    },
    {
      "id": "toast",
      "name": "Toast",
      "kind": "components",
      "dependencies": [
        "button",
        "spinner",
        "toast"
      ],
      "html": "<button\n  class=\"btn secondary\"\n  type=\"button\"\n  data-toast-target=\"notifications\"\n  data-toast-title=\"Changes saved\"\n  data-toast-description=\"Your preferences are up to date.\"\n  hidden\n>\n  Show Toast\n</button>\n<div\n  class=\"toasts\"\n  id=\"notifications\"\n  data-toasts\n  role=\"region\"\n  aria-label=\"Notifications\"\n>\n</div>\n<noscript>\n  <p>\n    Notifications need JavaScript. Keep important results visible in the page as well.\n  </p>\n</noscript>",
      "description": "Brief feedback that keeps people in their flow.",
      "contract": "Include base, button, spinner and toast styles plus deferred toast.js. Use one .toasts[data-toasts] region with a unique id and an accessible Notifications label. Declarative buttons start hidden and use data-toast-target, data-toast-title, optional data-toast-description, data-toast-tone and data-toast-duration. JavaScript reveals matching triggers. Place the region directly in body outside transformed or clipping ancestors. root.toast.show({title,description,tone,duration,urgent,action:{label,onClick}}) returns an id or null for invalid input or a full queue. update(id, options), dismiss(id), clear() manage messages. Action receives {id,dismiss}; application code owns real results and persistence. Titles/descriptions are rendered as text, never HTML. Default duration is zero (persistent); positive durations have a six-second minimum. Errors, loading messages and messages with actions remain until dismissed. Use tone:loading for the shared spinner; update the same id to success or error when work ends. Dismissal does not cancel the operation. Timers pause on pointer hover, focus within the toast and hidden documents, resuming remaining time. Maximum three visible and twenty total; overflow returns null so applications can keep a durable result elsewhere. Queued messages start their timer only when displayed. Updates reuse their place and restart the timer. Polite announcements are default; urgent:true explicitly opts into assertive announcements. Do not place live roles on the visual cards or their controls. Toasts never steal focus; Escape dismisses only when focus is inside a toast, returning to its connected origin. Dismiss buttons remain at least 44px. Default placement is bottom-center, set data-position to top-start, top-center, top-end, bottom-start, bottom-center or bottom-end. Legacy top means top-end. RTL mirrors start/end placement. data-dismiss localizes the close label. Shared 300ms opacity/transform entry respects reduced motion and data-motion=none; removal and focus are immediate. No packages, network, storage, promise tracking or background tasks. No-JavaScript triggers stay hidden; essential confirmation and errors belong in persistent page content. SEO belongs to documentation, not transient messages. Verify keyboard, focus return, queue pressure, timers, long text, RTL, narrow screens and all themes.",
      "usage": [
        {
          "title": "Choose a placement",
          "text": "Set `data-position` on the region: `top-start`, `top-center`, `top-end`, `bottom-start`, `bottom-center`, or `bottom-end`. The default is bottom-center. Start and end mirror in RTL. Choose one consistent position for each notification stream."
        },
        {
          "title": "Use meaningful status icons",
          "text": "Success uses a check circle, information an info circle, warning a triangle, and error an x circle. Shared SVG artwork comes from the icon catalog. Keep visible text alongside every icon; color alone must not convey the result."
        },
        {
          "title": "Confirm a result",
          "text": "Show a toast after an operation finishes. Keep important errors, receipts, and recovery steps visible in the page too. Toasts do not save or send anything."
        },
        {
          "title": "Give people enough time",
          "text": "Messages stay until dismissed by default. Timed messages last at least six seconds and pause on hover, keyboard focus, or a background tab. Errors and messages with actions stay visible."
        },
        {
          "title": "Keep announcements calm",
          "text": "Routine feedback is announced politely. Use `urgent: true` only for time-sensitive information that warrants interrupting a screen reader. A toast never moves focus on arrival."
        },
        {
          "title": "Handle several messages",
          "text": "Up to three messages are visible at once; additional messages wait their turn, up to twenty total. `show()` returns `null` when full. Update an existing message for progress instead of generating repeated notifications."
        },
        {
          "title": "Use the keyboard",
          "text": "Tab reaches actions and Dismiss. Escape closes the focused toast and returns focus to its origin when available. Reduced motion removes entry animation. Translate the region name, content, action labels, and `data-dismiss`."
        }
      ],
      "javascriptBytes": 0,
      "url": "/components/toast"
    },
    {
      "id": "color-picker",
      "name": "Color Picker",
      "kind": "components",
      "dependencies": [
        "button",
        "field",
        "select",
        "color-picker"
      ],
      "html": "<div class=\"field\">\n  <label for=\"brand-color\">\n    Brand color\n  </label>\n  <div class=\"color-picker\" data-color-picker>\n    <button type=\"button\" data-color-swatch aria-label=\"Choose Brand Color\" hidden>\n    </button>\n    <input\n      class=\"input\"\n      dir=\"ltr\"\n      id=\"brand-color\"\n      name=\"brand\"\n      value=\"#294cda\"\n      pattern=\"#[0-9a-fA-F]{6}\"\n      maxlength=\"7\"\n      spellcheck=\"false\"\n      autocomplete=\"off\"\n      required\n      aria-describedby=\"brand-color-help\"\n    >\n  </div>\n  <p class=\"muted\" id=\"brand-color-help\">\n    Choose a color or enter a six-digit hex value, such as #294cda.\n  </p>\n</div>",
      "description": "A color swatch and hex value in one clear field.",
      "contract": "Add data-swatch-only to hide the hex field after enhancement while retaining its full-picker swatch. Add data-hue-slider for an inline hue rail that preserves saturation and brightness, with data-hue for its accessible name. Add data-slider-swatch to show the rail beside its full-picker swatch without the hex field. Add data-slider-only to show only that rail after enhancement; the named hex input remains the form value and no-JavaScript fallback. Invalid hidden fields focus the rail; required slider-only inputs should start with a valid value. Dispatch input or color-picker-sync after programmatic changes. Use a labeled hex input inside .color-picker[data-color-picker] with a hidden type=button trigger marked data-color-swatch. Hex text is the named form value; use pattern=#[0-9a-fA-F]{6}, maxlength=7, and required when needed. Load base, field, select, and color-picker styles plus optional color-picker.js. JavaScript reveals and synchronizes the swatch; no-JavaScript text entry remains usable. A custom nonmodal dialog offers a pointer color area and equivalent native Hue, Saturation and Brightness sliders, and editable Hex, RGB, CSS, HSL, or HSB values. Switching formats preserves the color; the named form field always submits six-digit hex. Palette switches to all 209 source palette colors; selecting a shade returns to controls. Arrow keys navigate swatches, Home/End navigate a family, Ctrl+Home/End navigate all families. Color returns without changing the value. Escape returns focus; outside interaction dismisses. Values commit immediately. Opaque by default. Add data-alpha with maxlength=9 and a six-or-eight-digit hex pattern to allow opacity; submitted alpha uses #RRGGBBAA below full opacity and six-digit hex at 100%. RGB, HSL and HSB accept a fourth opacity value (0\u20131 or a percentage); CSS accepts hex, rgba() and hsla(). Palette and eyedropper preserve opacity. Add data-basic for only a hue slider, keyboard-focusable color area (no header, value editor or eyedropper); Escape or outside interaction closes it; left/right adjust saturation and up/down adjust brightness, with Shift for larger steps. Gradients and named colors are unsupported. Preserve disabled, readonly, reset, and native form validation. Optional empty values show a slashed swatch; typing invalid text does not change it. Give the swatch an independent accessible action name and associate visible format help with the text field. Programmatic value changes must dispatch input or color-picker-sync on the wrapper. Clicking the hex field opens the picker while preserving text focus; Tab focus alone does not open it. Keyboard Tab reaches the swatch and text separately. Localize data-title, data-close, data-hue, data-saturation, data-brightness, data-hex, data-palette and data-color. The custom dialog honors reduced motion and data-motion=none. Eyedropper appears only when the secure-context EyeDropper API exists. Explicit activation samples a screen color; cancellation preserves the value. Disabled state or closing aborts pending sampling. Localize data-eyedropper and data-sample-error. Formats are Hex, RGB, HSL, OKLCH and CSS token in that order. Tokens resolve against the bundled palette; unmatched custom colors display hex. OKLCH converts to the sRGB hex form value. No persistence or network requests. Verify contrast in the consuming design; selecting a color does not guarantee accessibility.",
      "usage": [
        {
          "title": "Offer direct hue adjustment",
          "text": "Add data-swatch-only for a swatch that opens the full picker without displaying the hex field after enhancement. Add data-hue-slider for a compact inline hue rail alongside the swatch and hex field. Add data-slider-swatch for a single row with a hue rail and a swatch that opens the full picker. Add data-slider-only to display just the rail after enhancement. data-hue supplies its accessible name. Hue preserves saturation and brightness; start with a chromatic color for visible changes. The original hex field remains the named form value and no-JavaScript fallback. Native arrows, Home and End adjust hue."
        },
        {
          "title": "Keep the value together",
          "text": "Use one labeled `color-picker` wrapper for the swatch button and editable hex value. Only the text field needs a form name."
        },
        {
          "title": "Choose or type",
          "text": "Click the swatch or hex field to open the picker. Clicking the field keeps text editing focused; Tab focus alone does not open it. Use the color area with a pointer or the labeled sliders with arrow keys. Escape closes the picker and returns focus. Choose Hex, RGB, CSS, HSL, or HSB to edit in that format; the form value remains hex. Changes apply immediately. Eyedropper samples a screen color in supporting secure browsers; Escape cancels without changing the value. Palette shows the full color scales; choose a shade to return, or choose Color without changing it. Arrow keys move between shades and families."
        },
        {
          "title": "Handle optional colors",
          "text": "Omit `required` to allow an empty value. Clear the hex field to remove the color. A slash marks an empty swatch; invalid text never silently replaces the last valid color."
        },
        {
          "title": "Preserve native behavior",
          "text": "Use disabled, readonly, form reset, and linked help. Update values through input events or `color-picker-sync` after programmatic changes. Validate submitted colors on the server."
        }
      ],
      "javascriptBytes": 0,
      "url": "/components/color-picker"
    },
    {
      "id": "password",
      "name": "Password",
      "kind": "components",
      "dependencies": [
        "field",
        "password"
      ],
      "html": "<div class=\"field\">\n  <label for=\"account-password\">\n    Password\n  </label>\n  <div class=\"password\" data-password>\n    <input\n      class=\"input\"\n      id=\"account-password\"\n      type=\"password\"\n      name=\"password\"\n      autocomplete=\"current-password\"\n      autocapitalize=\"none\"\n      spellcheck=\"false\"\n      aria-describedby=\"password-help\"\n    >\n    <button\n      type=\"button\"\n      aria-controls=\"account-password\"\n      aria-label=\"Show Password\"\n      data-password-toggle\n      data-show=\"Show Password\"\n      data-hide=\"Hide Password\"\n      hidden\n    >\n      <svg\n        class=\"icon password-eye\"\n        aria-hidden=\"true\"\n        focusable=\"false\"\n        xmlns=\"http://www.w3.org/2000/svg\"\n        viewbox=\"0 0 24 24\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        stroke-width=\"1.75\"\n        stroke-linecap=\"round\"\n        stroke-linejoin=\"round\"\n      >\n        <path d=\"M2.5 12s3.5-6 9.5-6 9.5 6 9.5 6-3.5 6-9.5 6-9.5-6-9.5-6Z\">\n        </path>\n        <circle cx=\"12\" cy=\"12\" r=\"2.5\">\n        </circle>\n      </svg>\n      <svg\n        class=\"icon password-eye-off\"\n        aria-hidden=\"true\"\n        focusable=\"false\"\n        xmlns=\"http://www.w3.org/2000/svg\"\n        viewbox=\"0 0 24 24\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        stroke-width=\"1.75\"\n        stroke-linecap=\"round\"\n        stroke-linejoin=\"round\"\n      >\n        <path d=\"m3 3 18 18M9 6.5A10 10 0 0 1 12 6c6 0 9.5 6 9.5 6a17 17 0 0 1-3 3.5M6 7.5A18 18 0 0 0 2.5 12s3.5 6 9.5 6a11 11 0 0 0 4-1M10.2 10.2a2.5 2.5 0 0 0 3.6 3.6\">\n        </path>\n      </svg>\n    </button>\n  </div>\n  <p id=\"password-help\" class=\"muted\">\n    Use a sample password here. This example does not send or save it.\n  </p>\n</div>",
      "description": "Private text entry with an accessible visibility control.",
      "contract": "Use a labeled native input type=password inside password[data-password], with class=input, unique id, name, and autocomplete=current-password for sign-in or new-password for creation. Keep paste, autofill, password managers, selection and native validation available; do not trim or transform values. Include a hidden type=button with data-password-toggle, matching aria-controls, and localized data-show/data-hide labels. The deferred password script reveals the button and toggles the existing input without copying its value, preserving selection and button focus. The decorative eye/eye-off icon and accessible action label change between Show Password and Hide Password; no aria-pressed is needed for this changing-label action. Without JavaScript the field remains masked and editable, and the toggle remains hidden. Disabled inputs and disabled fieldsets disable the toggle; readonly values remain focusable and revealable. Reset and pagehide remask the input. Do not serialize real passwords into HTML, URLs, logs, analytics, or browser storage. No strength scoring, authentication, persistence, requests, or security guarantee is provided. Application code owns password policy, server validation and errors; use visible requirements and aria-describedby, mark required visibly and natively, and clear aria-invalid with errors. Never use the reveal control as a submit button. Support RTL, 16px text, 44px targets, responsive wrapping, native forced colors, immediate focus and visibility changes, and shared reduced-motion safeguards. Password masking is visual privacy, not encryption; use secure application transport and credential handling.",
      "usage": [
        {
          "title": "Use the right autocomplete",
          "text": "Choose `current-password` for sign-in and `new-password` for creation or changes. Keep paste and password managers available."
        },
        {
          "title": "Make visibility explicit",
          "text": "The button changes between Show Password and Hide Password. It changes the existing input and never submits the form."
        },
        {
          "title": "Keep the fallback useful",
          "text": "Start with `type=\"password\"` and a hidden toggle. Without the component script, people can still enter a masked password."
        },
        {
          "title": "Explain requirements",
          "text": "Your application owns password rules and validation. Connect help and errors with `aria-describedby`; clear `aria-invalid` when corrected."
        },
        {
          "title": "Handle credentials responsibly",
          "text": "Do not put real passwords in page markup, URLs, logs, or browser storage. This component neither saves nor sends the value."
        },
        {
          "title": "Know what is included",
          "text": "Reset and leaving the page remask the field. Strength scoring and authentication are separate application responsibilities."
        }
      ],
      "javascriptBytes": 0,
      "url": "/components/password"
    },
    {
      "id": "switch",
      "name": "Switch",
      "kind": "components",
      "dependencies": [
        "switch"
      ],
      "html": "<label class=\"switch\">\n  <input type=\"checkbox\" role=\"switch\" name=\"notifications\" value=\"enabled\">\n  <span>\n    Project notifications\n  </span>\n</label>",
      "description": "Clear on/off settings with native keyboard behavior.",
      "contract": "Use label.switch containing input type=checkbox role=switch and a stable span label naming the setting. Native checked exposes the on/off state; do not add aria-checked, custom key handlers or tabindex. Use switches for binary settings, checkboxes for consent or selections, and radio buttons for mutually exclusive choices. Keep the label unchanged when toggled. No mixed or readonly state. Supply name and value; only checked enabled switches submit. Native checked sets initial state and form reset restores it. Use disabled or disabled fieldset with a visible explanation. Tab focuses and Space toggles; the full label is at least 44px tall around a 44 by 24px track. Position and color both indicate state. Link separate help via aria-describedby and keep links/buttons outside labels. The 16px thumb moves 20px over the shared 300ms duration; native state changes immediately. RTL reverses the travel. Reduced motion and data-motion=none remove animation; forced colors restores the native control. No runtime JavaScript, persistence, requests or automatic setting application is provided; application change handlers own saving, failure rollback and status messages. Demo changes are temporary. Do not claim success until saving completes. Keep text in source HTML, allow wrapping and verify focus, narrow layouts and all palettes. No SEO ranking guarantee.",
      "usage": [
        {
          "title": "Use it for a setting",
          "text": "Switches express on or off. Use a checkbox for consent or selections, and radio buttons to choose one of several options."
        },
        {
          "title": "Keep the label stable",
          "text": "Name the setting, such as Project notifications. Do not change the name to match its state; the native control announces on or off."
        },
        {
          "title": "Keep behavior native",
          "text": "Use `input type=\"checkbox\" role=\"switch\"` inside `label.switch`. Tab focuses it and Space toggles it. Do not duplicate checked state with ARIA."
        },
        {
          "title": "Explain unavailable settings",
          "text": "Use native `disabled` and visible help text. Link descriptions with `aria-describedby` and keep other controls outside the label."
        },
        {
          "title": "Connect saving deliberately",
          "text": "The component changes local state only. Your application owns saving, failure handling, and confirmation messages. Checked, enabled switches submit their name and value."
        },
        {
          "title": "Respect motion preferences",
          "text": "The thumb uses shared 300ms motion and reverses in RTL. Reduced motion and `data-motion=\"none\"` make it immediate."
        }
      ],
      "javascriptBytes": 0,
      "url": "/components/switch"
    },
    {
      "id": "textarea",
      "name": "Textarea",
      "kind": "components",
      "dependencies": [
        "field",
        "textarea"
      ],
      "html": "<div class=\"field\">\n  <label for=\"project-notes\">\n    Project notes\n  </label>\n  <textarea\n    class=\"input textarea\"\n    id=\"project-notes\"\n    name=\"notes\"\n    rows=\"4\"\n    aria-describedby=\"notes-help\"\n    placeholder=\"What should the team know?\"\n  >\n  </textarea>\n  <p id=\"notes-help\" class=\"muted\">\n    Include goals, context, and any important details.\n  </p>\n</div>",
      "description": "Room for longer thoughts, with native editing and clear guidance.",
      "contract": "Use a native textarea with class=\"input textarea\" inside field. Associate a visible label via unique for/id and help or errors via aria-describedby. Use name for submission and rows for initial height; default width is fluid, minimum height 96px, font size 16px, and vertical resizing remains available where the browser supports it. Content scrolls when it exceeds the resized area; do not clip overflow or animate height. Initial values go between textarea tags, never in a value attribute; escape untrusted content and use the value property for application updates. Preserve newlines, paste, selection, undo, IME composition and native Tab behavior. Enter inserts a newline, never auto-submits. Disabled controls are unfocusable and omitted from submission; readonly controls remain focusable, copyable and submitted. Mark required visibly and natively; use maxlength only for a real constraint and explain it before typing. Native length constraints count UTF-16 code units, not user-perceived characters. aria-invalid communicates an application-owned error and does not enforce validity; clear errors after correction and validate on the server. Form reset restores the initial text. Shared 300ms color feedback honors reduced motion and data-motion=none; focus is immediate. No runtime JavaScript, automatic growth, character counter, rich-text editor, persistence or requests are included. Keep labels and instructions in source HTML; there is no SEO ranking guarantee. Test long text, RTL, keyboard editing, zoom, narrow screens and all theme palettes.",
      "usage": [
        {
          "title": "Give it a clear purpose",
          "text": "Use a visible label and connect help text with `aria-describedby`. A placeholder is an example, never a replacement for the label."
        },
        {
          "title": "Leave room to write",
          "text": "Set `rows` for the starting height. Textarea fills its container and allows vertical resizing where supported. Automatic growth is not included."
        },
        {
          "title": "Preserve normal editing",
          "text": "Enter adds a new line. Keep paste, selection, undo, and keyboard navigation native. Put initial text between the tags, not in a value attribute."
        },
        {
          "title": "Explain constraints and errors",
          "text": "Show required status and length limits before typing. Associate useful errors and clear `aria-invalid` when corrected. Validate on the server too."
        },
        {
          "title": "Choose the right state",
          "text": "Disabled fields cannot be edited, focused, or submitted. Read-only fields remain focusable, copyable, and submitted."
        },
        {
          "title": "Keep responsibilities clear",
          "text": "Your application owns saving, validation messages, and any character counter. Styling does not store or send the text."
        }
      ],
      "javascriptBytes": 0,
      "url": "/components/textarea"
    },
    {
      "id": "radio",
      "name": "Radio",
      "kind": "components",
      "dependencies": [
        "radio"
      ],
      "html": "<fieldset class=\"radio-group\">\n  <legend>\n    Delivery frequency\n  </legend>\n  <label class=\"radio\">\n    <input type=\"radio\" name=\"frequency\" value=\"daily\" checked>\n    <span>\n      Daily\n    </span>\n  </label>\n  <label class=\"radio\">\n    <input type=\"radio\" name=\"frequency\" value=\"weekly\">\n    <span>\n      Weekly\n    </span>\n  </label>\n  <label class=\"radio\">\n    <input type=\"radio\" name=\"frequency\" value=\"monthly\">\n    <span>\n      Monthly\n    </span>\n  </label>\n</fieldset>",
      "description": "One choice from a clear, related group.",
      "contract": "Use fieldset.radio-group with a visible legend and label.radio wrapping each native input type=radio and span. Use the same nonempty name and distinct values within a group; unrelated groups need distinct names in the same form owner. Only one checked initial option per group; omit checked when a deliberate choice is required. Labels provide 44px targets around 20px controls. Tab enters/leaves the native group, arrow keys move and select enabled options, and Space selects; do not override native keyboard behavior or add roles, aria-checked or tabindex. No mixed state or readonly state exists. Use disabled on individual inputs or the fieldset, with a visible explanation. Required on a group member requires a selection; repeat it consistently on the group inputs and mark the legend required. Associate visible help/errors using aria-describedby on inputs; aria-invalid is application-owned and must clear with corrected errors. Only the checked enabled value submits; form reset restores initial checked state. Never auto-submit or navigate on selection. Keep links and other controls outside labels. Text wraps and logical spacing supports RTL. Shared 300ms color and dot opacity transitions honor reduced motion and data-motion=none; forced colors restores native appearance. No runtime JavaScript, requests, persistence, validation controller or SEO ranking guarantee. Keep labels and content in HTML; validate submitted values on the server.",
      "usage": [
        {
          "title": "Name the group",
          "text": "Use `fieldset.radio-group` and a visible legend. Wrap each radio and its text in `label.radio` so the whole label is clickable."
        },
        {
          "title": "Keep choices together",
          "text": "Give related inputs the same `name` and different `value` attributes. Use a different name for each independent group."
        },
        {
          "title": "Choose an initial state",
          "text": "Add `checked` to one sensible default, or leave the group unselected when the user must decide. Radio buttons have no mixed state."
        },
        {
          "title": "Explain validation",
          "text": "Mark required groups in the legend and add `required` to their inputs. Connect visible errors with `aria-describedby`; clear `aria-invalid` when corrected."
        },
        {
          "title": "Preserve native behavior",
          "text": "Arrow keys select within the group, Space selects, and Tab leaves it. Form reset restores the initial choice. Only the selected enabled value submits."
        },
        {
          "title": "Keep choices predictable",
          "text": "Do not navigate or submit on selection. Keep links outside labels, explain disabled options, and use checkboxes when several choices are allowed."
        }
      ],
      "javascriptBytes": 0,
      "url": "/components/radio"
    },
    {
      "id": "checkbox",
      "name": "Checkbox",
      "kind": "components",
      "dependencies": [
        "checkbox"
      ],
      "html": "<label class=\"checkbox\">\n  <input type=\"checkbox\" name=\"updates\" value=\"email\">\n  <span>\n    Email me project updates\n  </span>\n</label>",
      "description": "Independent choices with clear labels and native behavior.",
      "contract": "Use label.checkbox containing one native input type=checkbox and a span label. The whole label is a 44px-minimum target; the visual control is 20px. Give inputs a name and explicit value for form submission. Only checked enabled boxes submit; unchecked and disabled boxes are omitted. Use checked for initial state and native disabled or fieldset disabled, never aria-disabled alone. Use fieldset.checkbox-group with legend for related choices. Required applies to an individual checkbox, not at-least-one selection in a group. Associate help and visible errors via aria-describedby; set aria-invalid=true only after validation and clear it when corrected. Do not nest links or buttons in the label. Keyboard Tab and Space, form reset, focus and change events remain native; no redundant role or aria-checked. Set the JavaScript indeterminate property for a mixed state; it is not an HTML attribute and does not change checked/submission. Application code must derive mixed state from children and restore it after reset if needed. Use shared 300ms color and mark-opacity transitions; no movement or delayed state changes. Reduced-motion and data-motion=none are respected; forced colors restores native appearance. No runtime, requests, persistence, group controller, or SEO guarantee is provided.",
      "usage": [
        {
          "title": "Make the label clickable",
          "text": "Wrap one native checkbox and its text in `label.checkbox`. Keep links and other controls outside the label."
        },
        {
          "title": "Group related choices",
          "text": "Use `fieldset.checkbox-group` and a descriptive legend. Each choice stays independent; use radio buttons for exactly one choice."
        },
        {
          "title": "Explain validation",
          "text": "Connect help or errors with `aria-describedby`. Native `required` means this particular box must be checked, not any one box in a group."
        },
        {
          "title": "Understand form values",
          "text": "Only checked, enabled boxes submit their name and value. A native form reset restores the initial checked state."
        },
        {
          "title": "Use mixed state carefully",
          "text": "Set `input.indeterminate = true` in JavaScript. It describes a partial group selection; it is not a third submitted value or an HTML attribute."
        },
        {
          "title": "Keep motion consistent",
          "text": "Color and checkmark opacity use the shared 300ms timing. Reduced-motion preferences and `data-motion=\"none\"` disable transitions."
        }
      ],
      "javascriptBytes": 0,
      "url": "/components/checkbox"
    },
    {
      "id": "example",
      "name": "Example",
      "kind": "components",
      "dependencies": [
        "example",
        "button",
        "disclosure"
      ],
      "html": "<section class=\"example\" aria-label=\"Button example\">\n  <div class=\"example-frame\">\n    <div class=\"example-preview\" data-width=\"compact\">\n      <div class=\"example-content stack\">\n        <button class=\"btn secondary\" type=\"button\">\n          Try this button\n        </button>\n      </div>\n    </div>\n    <details>\n      <summary>\n        View code\n      </summary>\n      <div class=\"example-code\">\n        <pre tabindex=\"0\" aria-label=\"Button HTML\">\n          <code>\n            &lt;\n            button class=\"btn secondary\" type=\"button\"\n            &gt;\n            Try this button\n            &lt;\n            /button\n            &gt;\n          </code>\n        </pre>\n      </div>\n    </details>\n  </div>\n</section>",
      "description": "A reusable live preview with collapsible source code.",
      "contract": "Use section.example with an accessible name, .example-frame, .example-preview, and .example-content. Place actual component markup in the preview; menus and popovers use the page viewport and may extend beyond its border. Do not add overflow clipping or transforms to the frame. Set data-width=narrow for form controls, compact for small controls, or omit it for full width. Add a native details/summary and .example-code containing escaped code in pre/code; make long code scrollable and label its focusable pre. .example-toolbar arranges optional actions. This component needs no JavaScript. Include the demonstrated component CSS/JS separately, once per page. Give every example unique IDs, label/control references and radio names. It is for trusted component markup, not arbitrary user code. Keep full-page layouts and viewport-boundary demonstrations in sandboxed iframes.",
      "usage": [
        {
          "title": "Show working components",
          "text": "Render trusted component markup in the live preview. Load its shared styles and runtime once on the page."
        },
        {
          "title": "Keep overlays free",
          "text": "Menus, tooltips and dialogs should extend beyond the example border. Do not clip the preview; reserve iframes for page-layout and viewport-boundary demos."
        },
        {
          "title": "Keep examples independent",
          "text": "Use unique IDs and matching labels, ARIA references and radio names. Escape the source code inside pre/code."
        }
      ],
      "javascriptBytes": 0,
      "url": "/components/example"
    },
    {
      "id": "select",
      "name": "Select",
      "kind": "components",
      "dependencies": [
        "select"
      ],
      "html": "<div class=\"stack\" style=\"gap:8px;max-width:360px;width:100%\">\n  <label for=\"status\">\n    Project status\n  </label>\n  <div class=\"select-control\">\n    <select id=\"status\" name=\"status\" aria-describedby=\"status-help\">\n      <option value=\"planning\">\n        Planning\n      </option>\n      <option value=\"progress\">\n        In progress\n      </option>\n      <option value=\"review\">\n        In review\n      </option>\n      <option value=\"complete\">\n        Complete\n      </option>\n    </select>\n  </div>\n  <small class=\"muted\" id=\"status-help\">\n    Choose your current project stage.\n  </small>\n</div>",
      "description": "Animated choices with optional search and native form support.",
      "contract": "Wrap a labeled single native select in .select-control and load select.css plus deferred select.js. The runtime adds a themed combobox and animated top-layer listbox while the select retains name, value, required validation, disabled state, option groups, reset and form submission. Search appears automatically for ten or more visible options. Add data-select-search to enable it for shorter lists, or data-select-search=false to disable it; customize data-search-placeholder and data-empty-text. Arrow keys navigate enabled options, Enter/Space commits, Escape cancels, Tab dismisses, and select-only controls support type-ahead. Search accepts text and Arrow keys/Enter choose a result. Outside pointer/focus dismisses without changing the value. Options have 44px touch targets and scroll within viewport bounds. Opening and closing use shared motion tokens; reduced motion and data-motion=none disable animation. Native select remains the no-JavaScript/unsupported-Popover fallback; add data-select-native to opt out. Multiple and size listboxes stay native. Use visible labels and aria-describedby for help/errors. Set aria-invalid only with a presented error. Programmatic changes should emit input/change; layoutSelect.refresh() initializes newly added controls, also discovered automatically. Removed controls release listeners. No remote requests, rich HTML options, or virtual scrolling.",
      "usage": [
        {
          "title": "Offer a clear choice",
          "text": "Use Select for one value from a known list. Keep option names short and distinguishable. Search appears automatically for ten or more options. Add `data-select-search` to enable it for shorter lists, or `data-select-search=\"false\"` to disable it."
        },
        {
          "title": "Label the field",
          "text": "Associate a visible label with the select ID. Put help text outside `select-control` and connect it with `aria-describedby`."
        },
        {
          "title": "Explain errors",
          "text": "Use `required` for native validation. When presenting an error, add `aria-invalid=\"true\"` and reference the visible error message. Do not rely on color alone."
        },
        {
          "title": "Let the browser help",
          "text": "Load `select.js` for themed animated menus, keyboard navigation and optional search. Native form submission and reset remain intact. Without JavaScript or Popover support the native picker remains available."
        }
      ],
      "javascriptBytes": 0,
      "url": "/components/select"
    }
  ]
}
