outcrop 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -42,7 +42,7 @@ function writeSettings(file, s) {
42
42
  }
43
43
 
44
44
  // src/styles.gen.css
45
- var styles_gen_default = "*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:\"\"}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:var(--font-ui);font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.detail-body h1,.detail-body h2,.detail-body h3,.detail-body h4{margin-top:1.25rem;margin-bottom:.375rem;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.detail-body h1:first-child,.detail-body h2:first-child,.detail-body h3:first-child,.detail-body h4:first-child{margin-top:0}.detail-body h1:is(.dark *),.detail-body h2:is(.dark *),.detail-body h3:is(.dark *),.detail-body h4:is(.dark *){--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.detail-body p{margin-top:.375rem;margin-bottom:.375rem;font-size:13px;line-height:1.625;--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.detail-body p:is(.dark *){--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.detail-body ul{margin-top:.375rem;margin-bottom:.375rem}.detail-body ul>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem*var(--tw-space-y-reverse))}.detail-body ul{padding-left:1rem;font-size:13px;line-height:1.625;--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.detail-body ul:is(.dark *){--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.detail-body ul:not(.ordered)>li{list-style-type:disc}.detail-body li.check{margin-left:-1rem;display:flex;list-style-type:none;align-items:flex-start;gap:.5rem}.detail-body li.check .box{margin-top:.125rem;display:flex;height:15px;width:15px;flex-shrink:0;cursor:pointer;align-items:center;justify-content:center;border-radius:.25rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(212 212 216/var(--tw-border-opacity,1));font-size:10px;line-height:1;--tw-text-opacity:1;color:rgb(5 150 105/var(--tw-text-opacity,1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.detail-body li.check .box:hover{--tw-border-opacity:1;border-color:rgb(16 185 129/var(--tw-border-opacity,1))}.detail-body li.check .box:is(.dark *){--tw-border-opacity:1;border-color:rgb(82 82 91/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(52 211 153/var(--tw-text-opacity,1))}.detail-body li.check .box:hover:is(.dark *){--tw-border-opacity:1;border-color:rgb(16 185 129/var(--tw-border-opacity,1))}.detail-body li.check.done{--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1));text-decoration-line:line-through}.detail-body li.check.done:is(.dark *){--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.detail-body li.check.done .box{border-color:rgba(16,185,129,.5);--tw-bg-opacity:1;background-color:rgb(236 253 245/var(--tw-bg-opacity,1))}.detail-body li.check.done .box:is(.dark *){background-color:rgba(16,185,129,.1)}.detail-body ul.ordered>li{list-style-type:none}.detail-body code{border-radius:.25rem;--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1));padding:1px .25rem;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:12px;--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.detail-body code:is(.dark *){--tw-bg-opacity:1;background-color:rgb(39 39 42/var(--tw-bg-opacity,1));--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.detail-body pre{margin-top:.5rem;margin-bottom:.5rem;overflow-x:auto;border-radius:.375rem;--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1));padding:.75rem;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:12px;line-height:1.625;--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.detail-body pre:is(.dark *){background-color:rgba(39,39,42,.8);--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.detail-body .ref{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:12px;--tw-text-opacity:1;color:rgb(var(--accent-600)/var(--tw-text-opacity,1))}.detail-body .ref:hover{text-decoration-line:underline}.detail-body .ref:is(.dark *){--tw-text-opacity:1;color:rgb(var(--accent-400)/var(--tw-text-opacity,1))}.detail-body table{margin-top:.5rem;margin-bottom:.5rem;width:100%;border-collapse:collapse;font-size:12.5px}.detail-body th{border-bottom-width:1px;--tw-border-opacity:1;border-color:rgb(228 228 231/var(--tw-border-opacity,1));padding-top:.25rem;padding-bottom:.25rem;padding-right:.75rem;text-align:left;font-size:10.5px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.detail-body th:is(.dark *){--tw-border-opacity:1;border-color:rgb(63 63 70/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.detail-body td{border-bottom-width:1px;--tw-border-opacity:1;border-color:rgb(244 244 245/var(--tw-border-opacity,1));padding-top:.375rem;padding-bottom:.375rem;padding-right:.75rem;vertical-align:top;--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.detail-body td:is(.dark *){--tw-border-opacity:1;border-color:rgb(39 39 42/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.detail-body a.mdlink{--tw-text-opacity:1;color:rgb(var(--accent-600)/var(--tw-text-opacity,1))}.detail-body a.mdlink:hover{text-decoration-line:underline}.detail-body a.mdlink:is(.dark *){--tw-text-opacity:1;color:rgb(var(--accent-400)/var(--tw-text-opacity,1))}.doc-body h1{margin-bottom:.75rem;margin-top:.5rem;font-size:21px;font-weight:600;letter-spacing:-.025em;--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.doc-body h1:is(.dark *){--tw-text-opacity:1;color:rgb(250 250 250/var(--tw-text-opacity,1))}.doc-body h2{margin-bottom:.5rem;margin-top:1.75rem;border-bottom-width:1px;--tw-border-opacity:1;border-color:rgb(228 228 231/var(--tw-border-opacity,1));padding-bottom:.375rem;font-size:16px;font-weight:600;--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.doc-body h2:is(.dark *){--tw-border-opacity:1;border-color:rgb(39 39 42/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(244 244 245/var(--tw-text-opacity,1))}.doc-body h3{margin-bottom:.375rem;margin-top:1.25rem;font-size:13.5px;font-weight:600;--tw-text-opacity:1;color:rgb(39 39 42/var(--tw-text-opacity,1))}.doc-body h3:is(.dark *){--tw-text-opacity:1;color:rgb(228 228 231/var(--tw-text-opacity,1))}.doc-body h4{margin-bottom:.25rem;margin-top:1rem;font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.doc-body h4:is(.dark *){--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.doc-body p{margin-top:.5rem;margin-bottom:.5rem;font-size:13.5px;line-height:1.625;--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.doc-body p:is(.dark *){--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.doc-body ul{margin-top:.5rem;margin-bottom:.5rem}.doc-body ul>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem*var(--tw-space-y-reverse))}.doc-body ul{padding-left:1.25rem;font-size:13.5px;line-height:1.625;--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.doc-body ul:is(.dark *){--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.doc-body ul:not(.ordered)>li{list-style-type:disc}.doc-body ul.ordered>li{list-style-type:none}.doc-body li.check{margin-left:-1.25rem;display:flex;list-style-type:none;align-items:flex-start;gap:.5rem}.doc-body li.check .box{margin-top:.25rem;display:flex;height:14px;width:14px;flex-shrink:0;align-items:center;justify-content:center;border-radius:.25rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(212 212 216/var(--tw-border-opacity,1));font-size:9px;line-height:1;--tw-text-opacity:1;color:rgb(5 150 105/var(--tw-text-opacity,1))}.doc-body li.check .box:is(.dark *){--tw-border-opacity:1;border-color:rgb(82 82 91/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(52 211 153/var(--tw-text-opacity,1))}.doc-body li.check.done{--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1));text-decoration-line:line-through}.doc-body li.check.done:is(.dark *){--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.doc-body code{border-radius:.25rem;--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1));padding:1px .25rem;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:12px;--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.doc-body code:is(.dark *){--tw-bg-opacity:1;background-color:rgb(39 39 42/var(--tw-bg-opacity,1));--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.doc-body pre{margin-top:.75rem;margin-bottom:.75rem;overflow-x:auto;border-radius:.375rem;--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1));padding:.75rem;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:12px;line-height:1.625;--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.doc-body pre:is(.dark *){background-color:rgba(39,39,42,.8);--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.doc-body table{margin-top:.75rem;margin-bottom:.75rem;width:100%;border-collapse:collapse;font-size:13px}.doc-body th{border-bottom-width:1px;--tw-border-opacity:1;border-color:rgb(212 212 216/var(--tw-border-opacity,1));padding-top:.375rem;padding-bottom:.375rem;padding-right:1rem;text-align:left;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.doc-body th:is(.dark *){--tw-border-opacity:1;border-color:rgb(63 63 70/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.doc-body td{border-bottom-width:1px;--tw-border-opacity:1;border-color:rgb(244 244 245/var(--tw-border-opacity,1));padding-top:.5rem;padding-bottom:.5rem;padding-right:1rem;vertical-align:top;--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.doc-body td:is(.dark *){border-color:rgba(39,39,42,.6);--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.doc-body img{margin-left:auto;margin-right:auto;display:inline-block;height:auto;max-width:100%}.doc-body [align=center]{text-align:center}.doc-body [align=right]{text-align:right}.doc-body .ref,.doc-body a.mdlink{--tw-text-opacity:1;color:rgb(var(--accent-600)/var(--tw-text-opacity,1))}.doc-body .ref:hover,.doc-body a.mdlink:hover{text-decoration-line:underline}.doc-body .ref:is(.dark *),.doc-body a.mdlink:is(.dark *){--tw-text-opacity:1;color:rgb(var(--accent-400)/var(--tw-text-opacity,1))}.doc-body .ref{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:12.5px}.doc-body .mermaid{margin-top:1rem;margin-bottom:1rem;display:flex;justify-content:center;overflow-x:auto}.doc-body strong{font-weight:600;--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.doc-body strong:is(.dark *){--tw-text-opacity:1;color:rgb(244 244 245/var(--tw-text-opacity,1))}.detail-body hr,.doc-body hr{margin-top:1.5rem;margin-bottom:1.5rem;--tw-border-opacity:1;border-color:rgb(228 228 231/var(--tw-border-opacity,1))}.detail-body hr:is(.dark *),.doc-body hr:is(.dark *){--tw-border-opacity:1;border-color:rgb(39 39 42/var(--tw-border-opacity,1))}.doc-folder>summary{display:flex;cursor:pointer;list-style-type:none;align-items:center;gap:.375rem;border-radius:.375rem;padding:.25rem .5rem;font-size:13.5px;font-weight:600;--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.doc-folder>summary:hover{--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1))}.doc-folder>summary:is(.dark *){--tw-text-opacity:1;color:rgb(228 228 231/var(--tw-text-opacity,1))}.doc-folder>summary:hover:is(.dark *){background-color:rgba(39,39,42,.7)}.doc-folder>summary::-webkit-details-marker{display:none}.doc-folder>summary:before{content:\"›\";display:inline-block;width:.5rem;flex-shrink:0;font-size:13px;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1));transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.doc-folder>summary:is(.dark *):before{--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.doc-folder[open]>summary:before{transform:rotate(90deg)}.doc-folder-count{margin-left:auto;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:10px;font-weight:400;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.doc-folder-count:is(.dark *){--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.doc-folder+.doc-folder{margin-top:.375rem}.doc-link{margin-left:9px;display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;border-radius:.375rem;border-left-width:1px;--tw-border-opacity:1;border-color:rgb(244 244 245/var(--tw-border-opacity,1));padding:.25rem .5rem .25rem .625rem;font-size:12px;--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.doc-link:hover{--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1))}.doc-link:is(.dark *){--tw-border-opacity:1;border-color:rgb(39 39 42/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.doc-link:hover:is(.dark *){background-color:rgba(39,39,42,.7)}.doc-link.active{--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1));font-weight:500;--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.doc-link.active:is(.dark *){--tw-bg-opacity:1;background-color:rgb(39 39 42/var(--tw-bg-opacity,1));--tw-text-opacity:1;color:rgb(244 244 245/var(--tw-text-opacity,1))}#doc-outline.empty{display:none}.outline-link{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;border-left-width:2px;border-color:transparent;padding:.25rem .25rem .25rem .625rem;font-size:12px;line-height:1.375;--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.outline-link:hover{--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.outline-link:is(.dark *){--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.outline-link:hover:is(.dark *){--tw-text-opacity:1;color:rgb(244 244 245/var(--tw-text-opacity,1))}.outline-link.lvl-2{padding-left:1.25rem}.outline-link.lvl-3{padding-left:2rem;font-size:11.5px}.outline-link.active{--tw-border-opacity:1;border-color:rgb(var(--accent-500)/var(--tw-border-opacity,1));font-weight:500;--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.outline-link.active:is(.dark *){--tw-text-opacity:1;color:rgb(244 244 245/var(--tw-text-opacity,1))}#doc-sidebar.collapsed,#doc-sidebar.collapsed+.sidebar-resizer{display:none!important}#search-results mark{border-radius:.125rem;--tw-bg-opacity:1;background-color:rgb(var(--accent-200)/var(--tw-bg-opacity,1));padding-left:.125rem;padding-right:.125rem;font-weight:500;--tw-text-opacity:1;color:rgb(var(--accent-950)/var(--tw-text-opacity,1))}#search-results mark:is(.dark *){background-color:rgb(var(--accent-600)/.5);--tw-text-opacity:1;color:rgb(var(--accent-100)/var(--tw-text-opacity,1))}::highlight(doc-search){background-color:rgb(var(--accent-300));color:rgb(var(--accent-950))}.dark ::highlight(doc-search){background-color:rgb(var(--accent-600));color:#fff}::highlight(doc-match){background-color:rgb(var(--accent-500));color:#fff}.dark ::highlight(doc-match){background-color:rgb(var(--accent-300));color:rgb(var(--accent-950))}.swatch{height:1.5rem;width:1.5rem;border-radius:9999px;border-width:2px;border-color:transparent;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-opacity:1;--tw-ring-color:rgb(228 228 231/var(--tw-ring-opacity,1));transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.swatch:hover{--tw-scale-x:1.1;--tw-scale-y:1.1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.swatch:is(.dark *){--tw-ring-opacity:1;--tw-ring-color:rgb(63 63 70/var(--tw-ring-opacity,1))}.swatch.active{--tw-border-opacity:1;border-color:rgb(24 24 27/var(--tw-border-opacity,1))}.swatch.active:is(.dark *){--tw-border-opacity:1;border-color:rgb(244 244 245/var(--tw-border-opacity,1))}.swatch-violet{background-color:#8b5cf6}.swatch-blue{background-color:#3b82f6}.swatch-emerald{background-color:#10b981}.swatch-amber{background-color:#f59e0b}.swatch-rose{background-color:#f43f5e}.pick{flex:1 1 0%;border-radius:.375rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(228 228 231/var(--tw-border-opacity,1));padding:.25rem .5rem;font-size:12px;--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.pick:hover{--tw-border-opacity:1;border-color:rgb(212 212 216/var(--tw-border-opacity,1))}.pick:is(.dark *){--tw-border-opacity:1;border-color:rgb(63 63 70/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.pick:hover:is(.dark *){--tw-border-opacity:1;border-color:rgb(82 82 91/var(--tw-border-opacity,1))}.pick.active{--tw-border-opacity:1;border-color:rgb(var(--accent-400)/var(--tw-border-opacity,1));--tw-bg-opacity:1;background-color:rgb(var(--accent-50)/var(--tw-bg-opacity,1));--tw-text-opacity:1;color:rgb(var(--accent-700)/var(--tw-text-opacity,1))}.pick.active:is(.dark *){--tw-border-opacity:1;border-color:rgb(var(--accent-500)/var(--tw-border-opacity,1));background-color:rgb(var(--accent-500)/.1);--tw-text-opacity:1;color:rgb(var(--accent-300)/var(--tw-text-opacity,1))}.theme-opt.theme-active{--tw-border-opacity:1;border-color:rgb(var(--accent-400)/var(--tw-border-opacity,1));--tw-bg-opacity:1;background-color:rgb(var(--accent-50)/var(--tw-bg-opacity,1));--tw-text-opacity:1;color:rgb(var(--accent-700)/var(--tw-text-opacity,1))}.theme-opt.theme-active:is(.dark *){--tw-border-opacity:1;border-color:rgb(var(--accent-500)/var(--tw-border-opacity,1));background-color:rgb(var(--accent-500)/.1);--tw-text-opacity:1;color:rgb(var(--accent-300)/var(--tw-text-opacity,1))}.set-group{border-bottom-width:1px;--tw-border-opacity:1;border-color:rgb(244 244 245/var(--tw-border-opacity,1))}.set-group:is(.dark *){--tw-border-opacity:1;border-color:rgb(39 39 42/var(--tw-border-opacity,1))}.set-group>summary{display:flex;cursor:pointer;list-style-type:none;align-items:center;gap:.5rem;padding-top:.625rem;padding-bottom:.625rem;font-size:12.5px;font-weight:500;--tw-text-opacity:1;color:rgb(39 39 42/var(--tw-text-opacity,1))}.set-group>summary:is(.dark *){--tw-text-opacity:1;color:rgb(228 228 231/var(--tw-text-opacity,1))}.set-group>summary::-webkit-details-marker{display:none}.set-group>summary:after{content:\"›\";margin-left:auto;font-size:15px;line-height:1;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1));transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.set-group>summary:is(.dark *):after{--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.set-group[open]>summary:after{transform:rotate(90deg)}.set-sub{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:11px;font-weight:400;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.set-sub:is(.dark *){--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.set-row{display:flex;align-items:flex-start;gap:.75rem;padding-top:.375rem;padding-bottom:.375rem}.set-label{width:72px;flex-shrink:0;padding-top:.375rem;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.set-label:is(.dark *){--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}#sheet.fullscreen{max-width:none}#sheet.fullscreen #sheet-content{margin-left:auto;margin-right:auto;max-width:42rem}.task-card{width:100%;cursor:pointer;border-radius:.375rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(228 228 231/var(--tw-border-opacity,1));--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1));padding:.625rem .75rem;text-align:left;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.task-card:hover{--tw-border-opacity:1;border-color:rgb(212 212 216/var(--tw-border-opacity,1))}.task-card:focus-visible{outline-style:solid;outline-width:2px;outline-offset:1px;outline-color:rgb(var(--accent-500)/1)}.task-card:is(.dark *){--tw-border-opacity:1;border-color:rgb(39 39 42/var(--tw-border-opacity,1));--tw-bg-opacity:1;background-color:rgb(24 24 27/var(--tw-bg-opacity,1))}.task-card:hover:is(.dark *){--tw-border-opacity:1;border-color:rgb(63 63 70/var(--tw-border-opacity,1))}.card-id{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:11px;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.card-id:is(.dark *){--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.card-title{font-size:13px;font-weight:500;line-height:1.375;--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.card-title:is(.dark *){--tw-text-opacity:1;color:rgb(244 244 245/var(--tw-text-opacity,1))}.card-progress{margin-left:auto;display:inline-flex;align-items:center;gap:.375rem;font-size:10px;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.card-progress:is(.dark *){--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.epic-chip{border-radius:.25rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(228 228 231/var(--tw-border-opacity,1));padding:1px .375rem;font-size:10px;font-weight:500;--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.epic-chip:hover{--tw-border-opacity:1;border-color:rgb(212 212 216/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.epic-chip:is(.dark *){--tw-border-opacity:1;border-color:rgb(63 63 70/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.epic-chip:hover:is(.dark *){--tw-border-opacity:1;border-color:rgb(82 82 91/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(228 228 231/var(--tw-text-opacity,1))}.prio{display:inline-flex;align-items:center;gap:.25rem;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.prio:is(.dark *){--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.prio-label{font-size:10px;font-weight:500;letter-spacing:.025em}.dep-chip{display:inline-flex;align-items:center;gap:.25rem;border-radius:.25rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(228 228 231/var(--tw-border-opacity,1));padding:1px .375rem;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:10px;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.dep-chip:hover{--tw-border-opacity:1;border-color:rgb(212 212 216/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.dep-chip:is(.dark *){--tw-border-opacity:1;border-color:rgb(63 63 70/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.dep-chip:hover:is(.dark *){--tw-border-opacity:1;border-color:rgb(82 82 91/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.dep-chip.done{border-color:transparent;--tw-bg-opacity:1;background-color:rgb(236 253 245/var(--tw-bg-opacity,1));--tw-text-opacity:1;color:rgb(5 150 105/var(--tw-text-opacity,1))}.dep-chip.done:hover{--tw-bg-opacity:1;background-color:rgb(209 250 229/var(--tw-bg-opacity,1))}.dep-chip.done:is(.dark *){background-color:rgba(16,185,129,.1);--tw-text-opacity:1;color:rgb(52 211 153/var(--tw-text-opacity,1))}.dep-chip.done:hover:is(.dark *){background-color:rgba(16,185,129,.2)}.backlink-row{display:flex;width:100%;align-items:center;gap:.5rem;border-radius:.375rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(244 244 245/var(--tw-border-opacity,1));padding:.375rem .625rem;text-align:left;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.backlink-row:hover{--tw-border-opacity:1;border-color:rgb(212 212 216/var(--tw-border-opacity,1))}.backlink-row:is(.dark *){--tw-border-opacity:1;border-color:rgb(39 39 42/var(--tw-border-opacity,1))}.backlink-row:hover:is(.dark *){--tw-border-opacity:1;border-color:rgb(82 82 91/var(--tw-border-opacity,1))}.epic-row{display:flex;width:100%;cursor:pointer;align-items:center;gap:.5rem;border-radius:.375rem;padding:.375rem .5rem;text-align:left;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.epic-row:hover{--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1))}.epic-row:hover:is(.dark *){background-color:rgba(39,39,42,.7)}.epic-row.epic-active{--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1))}.epic-row.epic-active:is(.dark *){--tw-bg-opacity:1;background-color:rgb(39 39 42/var(--tw-bg-opacity,1))}#sidebar>*{flex-shrink:0}#sidebar.collapsed,#sidebar.collapsed+.sidebar-resizer{display:none!important}section.drop-target{background-color:rgb(var(--accent-50)/.6);--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-inset:inset;--tw-ring-opacity:1;--tw-ring-color:rgb(var(--accent-300)/var(--tw-ring-opacity,1))}section.drop-target:is(.dark *){background-color:rgb(var(--accent-500)/.05);--tw-ring-color:rgb(var(--accent-500)/0.4)}.task-card.\\!dragging,.task-card.dragging{opacity:.4}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.invisible{visibility:hidden}.collapse{visibility:collapse}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{inset:0}.inset-y-0{top:0;bottom:0}.-top-0\\.5{top:-.125rem}.bottom-3{bottom:.75rem}.bottom-5{bottom:1.25rem}.left-1{left:.25rem}.left-1\\/2{left:50%}.left-3{left:.75rem}.left-44{left:11rem}.right-0{right:0}.right-3{right:.75rem}.right-4{right:1rem}.right-5{right:1.25rem}.top-0{top:0}.top-4{top:1rem}.z-10{z-index:10}.z-40{z-index:40}.z-50{z-index:50}.z-\\[60\\]{z-index:60}.mx-auto{margin-left:auto;margin-right:auto}.-ml-0\\.5{margin-left:-.125rem}.mb-1{margin-bottom:.25rem}.mb-1\\.5{margin-bottom:.375rem}.mb-2{margin-bottom:.5rem}.mb-2\\.5{margin-bottom:.625rem}.mb-3{margin-bottom:.75rem}.mb-8{margin-bottom:2rem}.ml-3{margin-left:.75rem}.ml-auto{margin-left:auto}.mr-1\\.5{margin-right:.375rem}.mr-16{margin-right:4rem}.mt-1{margin-top:.25rem}.mt-1\\.5{margin-top:.375rem}.mt-10{margin-top:2.5rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-5{margin-top:1.25rem}.mt-6{margin-top:1.5rem}.block{display:block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-1\\.5{height:.375rem}.h-12{height:3rem}.h-2{height:.5rem}.h-3\\.5{height:.875rem}.h-5{height:1.25rem}.h-8{height:2rem}.h-\\[calc\\(\\(100vh\\/var\\(--zoom\\)\\)-3rem\\)\\]{height:calc(100vh/var(--zoom) - 3rem)}.h-\\[calc\\(100vh\\/var\\(--zoom\\)\\)\\]{height:calc(100vh/var(--zoom))}.h-full{height:100%}.min-h-\\[60px\\]{min-height:60px}.w-1{width:.25rem}.w-1\\.5{width:.375rem}.w-2{width:.5rem}.w-3\\.5{width:.875rem}.w-44{width:11rem}.w-56{width:14rem}.w-60{width:15rem}.w-\\[290px\\]{width:290px}.w-full{width:100%}.min-w-0{min-width:0}.max-w-3xl{max-width:48rem}.max-w-\\[460px\\]{max-width:460px}.max-w-full{max-width:100%}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.grow{flex-grow:1}.-translate-x-1\\/2{--tw-translate-x:-50%}.-translate-x-1\\/2,.translate-x-full{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-full{--tw-translate-x:100%}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-col-resize{cursor:col-resize}.cursor-grab{cursor:grab}.cursor-pointer{cursor:pointer}.resize-y{resize:vertical}.resize{resize:both}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-\\[90px_1fr\\]{grid-template-columns:90px 1fr}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-baseline{align-items:baseline}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0\\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-1\\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-2\\.5{gap:.625rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-y-1\\.5{row-gap:.375rem}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem*var(--tw-space-y-reverse))}.space-y-1\\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.375rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.375rem*var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px*var(--tw-divide-y-reverse))}.divide-zinc-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(228 228 231/var(--tw-divide-opacity,1))}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis}.truncate,.whitespace-nowrap{white-space:nowrap}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-sm{border-radius:.125rem}.border{border-width:1px}.border-y{border-top-width:1px}.border-b,.border-y{border-bottom-width:1px}.border-l{border-left-width:1px}.border-l-2{border-left-width:2px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-dashed{border-style:dashed}.border-rose-300{--tw-border-opacity:1;border-color:rgb(253 164 175/var(--tw-border-opacity,1))}.border-rose-500\\/70{border-color:rgba(244,63,94,.7)}.border-zinc-100{--tw-border-opacity:1;border-color:rgb(244 244 245/var(--tw-border-opacity,1))}.border-zinc-200{--tw-border-opacity:1;border-color:rgb(228 228 231/var(--tw-border-opacity,1))}.bg-accent-50{--tw-bg-opacity:1;background-color:rgb(var(--accent-50)/var(--tw-bg-opacity,1))}.bg-accent-600{--tw-bg-opacity:1;background-color:rgb(var(--accent-600)/var(--tw-bg-opacity,1))}.bg-amber-50{--tw-bg-opacity:1;background-color:rgb(255 251 235/var(--tw-bg-opacity,1))}.bg-black\\/20{background-color:rgba(0,0,0,.2)}.bg-emerald-500{--tw-bg-opacity:1;background-color:rgb(16 185 129/var(--tw-bg-opacity,1))}.bg-rose-50{--tw-bg-opacity:1;background-color:rgb(255 241 242/var(--tw-bg-opacity,1))}.bg-transparent{background-color:transparent}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.bg-zinc-100{--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1))}.bg-zinc-300{--tw-bg-opacity:1;background-color:rgb(212 212 216/var(--tw-bg-opacity,1))}.bg-zinc-50{--tw-bg-opacity:1;background-color:rgb(250 250 250/var(--tw-bg-opacity,1))}.bg-zinc-900{--tw-bg-opacity:1;background-color:rgb(24 24 27/var(--tw-bg-opacity,1))}.p-0\\.5{padding:.125rem}.p-1{padding:.25rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.p-96{padding:24rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-1\\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-8{padding-left:2rem;padding-right:2rem}.py-0\\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-3\\.5{padding-top:.875rem;padding-bottom:.875rem}.py-5{padding-top:1.25rem;padding-bottom:1.25rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.py-px{padding-top:1px;padding-bottom:1px}.pb-1{padding-bottom:.25rem}.pb-3{padding-bottom:.75rem}.pl-2\\.5{padding-left:.625rem}.pr-1{padding-right:.25rem}.pr-3{padding-right:.75rem}.pt-1{padding-top:.25rem}.pt-3{padding-top:.75rem}.pt-4{padding-top:1rem}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.font-sans{font-family:var(--font-ui)}.font-serif{font-family:ui-serif,Georgia,Cambria,Times New Roman,Times,serif}.text-\\[10\\.5px\\]{font-size:10.5px}.text-\\[10px\\]{font-size:10px}.text-\\[11px\\]{font-size:11px}.text-\\[12\\.5px\\]{font-size:12.5px}.text-\\[12px\\]{font-size:12px}.text-\\[13px\\]{font-size:13px}.text-\\[16px\\]{font-size:16px}.text-\\[9\\.5px\\]{font-size:9.5px}.text-\\[9px\\]{font-size:9px}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.capitalize{text-transform:capitalize}.normal-case{text-transform:none}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.leading-5{line-height:1.25rem}.leading-relaxed{line-height:1.625}.leading-snug{line-height:1.375}.tracking-tight{letter-spacing:-.025em}.tracking-wide{letter-spacing:.025em}.tracking-wider{letter-spacing:.05em}.text-accent-600{--tw-text-opacity:1;color:rgb(var(--accent-600)/var(--tw-text-opacity,1))}.text-amber-600{--tw-text-opacity:1;color:rgb(217 119 6/var(--tw-text-opacity,1))}.text-rose-500{--tw-text-opacity:1;color:rgb(244 63 94/var(--tw-text-opacity,1))}.text-rose-700{--tw-text-opacity:1;color:rgb(190 18 60/var(--tw-text-opacity,1))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.text-zinc-100{--tw-text-opacity:1;color:rgb(244 244 245/var(--tw-text-opacity,1))}.text-zinc-300{--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.text-zinc-400{--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.text-zinc-500{--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.text-zinc-600{--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.text-zinc-700{--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.text-zinc-800{--tw-text-opacity:1;color:rgb(39 39 42/var(--tw-text-opacity,1))}.text-zinc-900{--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.accent-accent-600{accent-color:rgb(var(--accent-600)/1)}.opacity-0{opacity:0}.opacity-100{opacity:1}.shadow{--tw-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px -1px rgba(0,0,0,.1);--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color)}.shadow,.shadow-lg{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow-sm{--tw-shadow:0 1px 2px 0 rgba(0,0,0,.05);--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.shadow-sm,.shadow-xl{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px rgba(0,0,0,.1),0 8px 10px -6px rgba(0,0,0,.1);--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color),0 8px 10px -6px var(--tw-shadow-color)}.outline-none{outline:2px solid transparent;outline-offset:2px}.outline{outline-style:solid}.ring{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.blur{--tw-blur:blur(8px)}.blur,.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}:root,html[data-accent=violet]{--accent-50:245 243 255;--accent-100:237 233 254;--accent-200:221 214 254;--accent-300:196 181 253;--accent-400:167 139 250;--accent-500:139 92 246;--accent-600:124 58 237;--accent-700:109 40 217;--accent-950:46 16 101}html[data-accent=blue]{--accent-50:239 246 255;--accent-100:219 234 254;--accent-200:191 219 254;--accent-300:147 197 253;--accent-400:96 165 250;--accent-500:59 130 246;--accent-600:37 99 235;--accent-700:29 78 216;--accent-950:23 37 84}html[data-accent=emerald]{--accent-50:236 253 245;--accent-100:209 250 229;--accent-200:167 243 208;--accent-300:110 231 183;--accent-400:52 211 153;--accent-500:16 185 129;--accent-600:5 150 105;--accent-700:4 120 87;--accent-950:2 44 34}html[data-accent=amber]{--accent-50:255 251 235;--accent-100:254 243 199;--accent-200:253 230 138;--accent-300:252 211 77;--accent-400:251 191 36;--accent-500:245 158 11;--accent-600:217 119 6;--accent-700:180 83 9;--accent-950:69 26 3}html[data-accent=rose]{--accent-50:255 241 242;--accent-100:255 228 230;--accent-200:254 205 211;--accent-300:253 164 175;--accent-400:251 113 133;--accent-500:244 63 94;--accent-600:225 29 72;--accent-700:190 18 60;--accent-950:76 5 25}:root,html[data-font=sans]{--font-ui:-apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,\"Helvetica Neue\",Arial,sans-serif}html[data-font=serif]{--font-ui:ui-serif,Georgia,Cambria,\"Times New Roman\",Times,serif}html[data-font=mono]{--font-ui:ui-monospace,SFMono-Regular,\"SF Mono\",Menlo,Consolas,\"Liberation Mono\",monospace}:root{--zoom:1}html[data-size=compact]{--zoom:0.9}html[data-size=large]{--zoom:1.15}html[data-size=compact] body,html[data-size=large] body{zoom:var(--zoom)}@media (prefers-reduced-motion:no-preference){html.theme-anim,html.theme-anim *,html.theme-anim :after,html.theme-anim :before{transition:background-color .26s cubic-bezier(.4,0,.2,1),border-color .26s cubic-bezier(.4,0,.2,1),color .26s cubic-bezier(.4,0,.2,1),fill .26s cubic-bezier(.4,0,.2,1),stroke .26s cubic-bezier(.4,0,.2,1),box-shadow .26s cubic-bezier(.4,0,.2,1)!important;transition-delay:0s!important}}.placeholder\\:text-zinc-400::-moz-placeholder{--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.placeholder\\:text-zinc-400::placeholder{--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.hover\\:border-zinc-300:hover{--tw-border-opacity:1;border-color:rgb(212 212 216/var(--tw-border-opacity,1))}.hover\\:bg-accent-400\\/70:hover{background-color:rgb(var(--accent-400)/.7)}.hover\\:bg-accent-500:hover{--tw-bg-opacity:1;background-color:rgb(var(--accent-500)/var(--tw-bg-opacity,1))}.hover\\:bg-zinc-100:hover{--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1))}.hover\\:bg-zinc-200:hover{--tw-bg-opacity:1;background-color:rgb(228 228 231/var(--tw-bg-opacity,1))}.hover\\:bg-zinc-50:hover{--tw-bg-opacity:1;background-color:rgb(250 250 250/var(--tw-bg-opacity,1))}.hover\\:text-zinc-600:hover{--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.hover\\:text-zinc-900:hover{--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.hover\\:underline:hover{text-decoration-line:underline}.focus\\:border-accent-400:focus{--tw-border-opacity:1;border-color:rgb(var(--accent-400)/var(--tw-border-opacity,1))}.focus\\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus-visible\\:opacity-100:focus-visible{opacity:1}.active\\:bg-accent-400\\/70:active{background-color:rgb(var(--accent-400)/.7)}.group:hover .group-hover\\:opacity-100{opacity:1}@media (prefers-reduced-motion:no-preference){.motion-safe\\:transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.motion-safe\\:duration-200{transition-duration:.2s}}.dark\\:divide-zinc-800:is(.dark *)>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(39 39 42/var(--tw-divide-opacity,1))}.dark\\:border-rose-500\\/40:is(.dark *){border-color:rgba(244,63,94,.4)}.dark\\:border-zinc-700:is(.dark *){--tw-border-opacity:1;border-color:rgb(63 63 70/var(--tw-border-opacity,1))}.dark\\:border-zinc-800:is(.dark *){--tw-border-opacity:1;border-color:rgb(39 39 42/var(--tw-border-opacity,1))}.dark\\:bg-accent-500\\/10:is(.dark *){background-color:rgb(var(--accent-500)/.1)}.dark\\:bg-amber-500\\/10:is(.dark *){background-color:rgba(245,158,11,.1)}.dark\\:bg-black\\/50:is(.dark *){background-color:rgba(0,0,0,.5)}.dark\\:bg-rose-500\\/10:is(.dark *){background-color:rgba(244,63,94,.1)}.dark\\:bg-zinc-100:is(.dark *){--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1))}.dark\\:bg-zinc-800:is(.dark *){--tw-bg-opacity:1;background-color:rgb(39 39 42/var(--tw-bg-opacity,1))}.dark\\:bg-zinc-900:is(.dark *){--tw-bg-opacity:1;background-color:rgb(24 24 27/var(--tw-bg-opacity,1))}.dark\\:bg-zinc-950:is(.dark *){--tw-bg-opacity:1;background-color:rgb(9 9 11/var(--tw-bg-opacity,1))}.dark\\:text-accent-400:is(.dark *){--tw-text-opacity:1;color:rgb(var(--accent-400)/var(--tw-text-opacity,1))}.dark\\:text-amber-400:is(.dark *){--tw-text-opacity:1;color:rgb(251 191 36/var(--tw-text-opacity,1))}.dark\\:text-rose-300:is(.dark *){--tw-text-opacity:1;color:rgb(253 164 175/var(--tw-text-opacity,1))}.dark\\:text-zinc-100:is(.dark *){--tw-text-opacity:1;color:rgb(244 244 245/var(--tw-text-opacity,1))}.dark\\:text-zinc-200:is(.dark *){--tw-text-opacity:1;color:rgb(228 228 231/var(--tw-text-opacity,1))}.dark\\:text-zinc-300:is(.dark *){--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.dark\\:text-zinc-400:is(.dark *){--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.dark\\:text-zinc-50:is(.dark *){--tw-text-opacity:1;color:rgb(250 250 250/var(--tw-text-opacity,1))}.dark\\:text-zinc-500:is(.dark *){--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.dark\\:text-zinc-600:is(.dark *){--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.dark\\:text-zinc-700:is(.dark *){--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.dark\\:text-zinc-900:is(.dark *){--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.dark\\:brightness-125:is(.dark *){--tw-brightness:brightness(1.25);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.dark\\:placeholder\\:text-zinc-600:is(.dark *)::-moz-placeholder{--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.dark\\:placeholder\\:text-zinc-600:is(.dark *)::placeholder{--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.dark\\:hover\\:border-zinc-600:hover:is(.dark *){--tw-border-opacity:1;border-color:rgb(82 82 91/var(--tw-border-opacity,1))}.dark\\:hover\\:bg-accent-500\\/60:hover:is(.dark *){background-color:rgb(var(--accent-500)/.6)}.dark\\:hover\\:bg-zinc-700:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(63 63 70/var(--tw-bg-opacity,1))}.dark\\:hover\\:bg-zinc-800:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(39 39 42/var(--tw-bg-opacity,1))}.dark\\:hover\\:bg-zinc-900:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(24 24 27/var(--tw-bg-opacity,1))}.dark\\:hover\\:text-zinc-100:hover:is(.dark *){--tw-text-opacity:1;color:rgb(244 244 245/var(--tw-text-opacity,1))}.dark\\:hover\\:text-zinc-300:hover:is(.dark *){--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.dark\\:focus\\:border-accent-500:focus:is(.dark *){--tw-border-opacity:1;border-color:rgb(var(--accent-500)/var(--tw-border-opacity,1))}.dark\\:active\\:bg-accent-500\\/60:active:is(.dark *){background-color:rgb(var(--accent-500)/.6)}@media (min-width:640px){.sm\\:block{display:block}}@media (min-width:768px){.md\\:block{display:block}.md\\:flex{display:flex}}@media (min-width:1024px){.lg\\:block{display:block}}.dark\\:\\[\\&\\>option\\]\\:bg-zinc-900>option:is(.dark *){--tw-bg-opacity:1;background-color:rgb(24 24 27/var(--tw-bg-opacity,1))}";
45
+ var styles_gen_default = "*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:\"\"}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:var(--font-ui);font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.detail-body h1,.detail-body h2,.detail-body h3,.detail-body h4{margin-top:1.25rem;margin-bottom:.375rem;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.detail-body h1:first-child,.detail-body h2:first-child,.detail-body h3:first-child,.detail-body h4:first-child{margin-top:0}.detail-body h1:is(.dark *),.detail-body h2:is(.dark *),.detail-body h3:is(.dark *),.detail-body h4:is(.dark *){--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.detail-body p{margin-top:.375rem;margin-bottom:.375rem;font-size:13px;line-height:1.625;--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.detail-body p:is(.dark *){--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.detail-body ul{margin-top:.375rem;margin-bottom:.375rem}.detail-body ul>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem*var(--tw-space-y-reverse))}.detail-body ul{padding-left:1rem;font-size:13px;line-height:1.625;--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.detail-body ul:is(.dark *){--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.detail-body ul:not(.ordered)>li{list-style-type:disc}.detail-body li.check{margin-left:-1rem;display:flex;list-style-type:none;align-items:flex-start;gap:.5rem}.detail-body li.check .box{margin-top:.125rem;display:flex;height:15px;width:15px;flex-shrink:0;cursor:pointer;align-items:center;justify-content:center;border-radius:.25rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(212 212 216/var(--tw-border-opacity,1));font-size:10px;line-height:1;--tw-text-opacity:1;color:rgb(5 150 105/var(--tw-text-opacity,1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.detail-body li.check .box:hover{--tw-border-opacity:1;border-color:rgb(16 185 129/var(--tw-border-opacity,1))}.detail-body li.check .box:is(.dark *){--tw-border-opacity:1;border-color:rgb(82 82 91/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(52 211 153/var(--tw-text-opacity,1))}.detail-body li.check .box:hover:is(.dark *){--tw-border-opacity:1;border-color:rgb(16 185 129/var(--tw-border-opacity,1))}.detail-body li.check.done{--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1));text-decoration-line:line-through}.detail-body li.check.done:is(.dark *){--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.detail-body li.check.done .box{border-color:rgba(16,185,129,.5);--tw-bg-opacity:1;background-color:rgb(236 253 245/var(--tw-bg-opacity,1))}.detail-body li.check.done .box:is(.dark *){background-color:rgba(16,185,129,.1)}.detail-body ul.ordered>li{list-style-type:none}.detail-body code{border-radius:.25rem;--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1));padding:1px .25rem;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:12px;--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.detail-body code:is(.dark *){--tw-bg-opacity:1;background-color:rgb(39 39 42/var(--tw-bg-opacity,1));--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.detail-body pre{margin-top:.5rem;margin-bottom:.5rem;overflow-x:auto;border-radius:.375rem;--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1));padding:.75rem;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:12px;line-height:1.625;--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.detail-body pre:is(.dark *){background-color:rgba(39,39,42,.8);--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.detail-body .ref{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:12px;--tw-text-opacity:1;color:rgb(var(--accent-600)/var(--tw-text-opacity,1))}.detail-body .ref:hover{text-decoration-line:underline}.detail-body .ref:is(.dark *){--tw-text-opacity:1;color:rgb(var(--accent-400)/var(--tw-text-opacity,1))}.detail-body table{margin-top:.5rem;margin-bottom:.5rem;width:100%;border-collapse:collapse;font-size:12.5px}.detail-body th{border-bottom-width:1px;--tw-border-opacity:1;border-color:rgb(228 228 231/var(--tw-border-opacity,1));padding-top:.25rem;padding-bottom:.25rem;padding-right:.75rem;text-align:left;font-size:10.5px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.detail-body th:is(.dark *){--tw-border-opacity:1;border-color:rgb(63 63 70/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.detail-body td{border-bottom-width:1px;--tw-border-opacity:1;border-color:rgb(244 244 245/var(--tw-border-opacity,1));padding-top:.375rem;padding-bottom:.375rem;padding-right:.75rem;vertical-align:top;--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.detail-body td:is(.dark *){--tw-border-opacity:1;border-color:rgb(39 39 42/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.detail-body a.mdlink{--tw-text-opacity:1;color:rgb(var(--accent-600)/var(--tw-text-opacity,1))}.detail-body a.mdlink:hover{text-decoration-line:underline}.detail-body a.mdlink:is(.dark *){--tw-text-opacity:1;color:rgb(var(--accent-400)/var(--tw-text-opacity,1))}.doc-body h1{margin-bottom:.75rem;margin-top:.5rem;font-size:21px;font-weight:600;letter-spacing:-.025em;--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.doc-body h1:is(.dark *){--tw-text-opacity:1;color:rgb(250 250 250/var(--tw-text-opacity,1))}.doc-body h2{margin-bottom:.5rem;margin-top:1.75rem;border-bottom-width:1px;--tw-border-opacity:1;border-color:rgb(228 228 231/var(--tw-border-opacity,1));padding-bottom:.375rem;font-size:16px;font-weight:600;--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.doc-body h2:is(.dark *){--tw-border-opacity:1;border-color:rgb(39 39 42/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(244 244 245/var(--tw-text-opacity,1))}.doc-body h3{margin-bottom:.375rem;margin-top:1.25rem;font-size:13.5px;font-weight:600;--tw-text-opacity:1;color:rgb(39 39 42/var(--tw-text-opacity,1))}.doc-body h3:is(.dark *){--tw-text-opacity:1;color:rgb(228 228 231/var(--tw-text-opacity,1))}.doc-body h4{margin-bottom:.25rem;margin-top:1rem;font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.doc-body h4:is(.dark *){--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.doc-body p{margin-top:.5rem;margin-bottom:.5rem;font-size:13.5px;line-height:1.625;--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.doc-body p:is(.dark *){--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.doc-body ul{margin-top:.5rem;margin-bottom:.5rem}.doc-body ul>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem*var(--tw-space-y-reverse))}.doc-body ul{padding-left:1.25rem;font-size:13.5px;line-height:1.625;--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.doc-body ul:is(.dark *){--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.doc-body ul:not(.ordered)>li{list-style-type:disc}.doc-body ul.ordered>li{list-style-type:none}.doc-body li.check{margin-left:-1.25rem;display:flex;list-style-type:none;align-items:flex-start;gap:.5rem}.doc-body li.check .box{margin-top:.25rem;display:flex;height:14px;width:14px;flex-shrink:0;align-items:center;justify-content:center;border-radius:.25rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(212 212 216/var(--tw-border-opacity,1));font-size:9px;line-height:1;--tw-text-opacity:1;color:rgb(5 150 105/var(--tw-text-opacity,1))}.doc-body li.check .box:is(.dark *){--tw-border-opacity:1;border-color:rgb(82 82 91/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(52 211 153/var(--tw-text-opacity,1))}.doc-body li.check.done{--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1));text-decoration-line:line-through}.doc-body li.check.done:is(.dark *){--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.doc-body code{border-radius:.25rem;--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1));padding:1px .25rem;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:12px;--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.doc-body code:is(.dark *){--tw-bg-opacity:1;background-color:rgb(39 39 42/var(--tw-bg-opacity,1));--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.doc-body pre{margin-top:.75rem;margin-bottom:.75rem;overflow-x:auto;border-radius:.375rem;--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1));padding:.75rem;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:12px;line-height:1.625;--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.doc-body pre:is(.dark *){background-color:rgba(39,39,42,.8);--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.codeblock{position:relative}.copy-code{position:absolute;right:.5rem;top:.5rem;border-radius:.25rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(228 228 231/var(--tw-border-opacity,1));--tw-bg-opacity:1;background-color:rgb(250 250 250/var(--tw-bg-opacity,1));padding:.25rem;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1));opacity:0;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.copy-code:hover{--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.copy-code:focus-visible{opacity:1}.copy-code:is(.dark *){--tw-border-opacity:1;border-color:rgb(63 63 70/var(--tw-border-opacity,1));--tw-bg-opacity:1;background-color:rgb(24 24 27/var(--tw-bg-opacity,1))}.copy-code:hover:is(.dark *){--tw-text-opacity:1;color:rgb(228 228 231/var(--tw-text-opacity,1))}.codeblock:hover .copy-code,.copy-code:focus-visible{opacity:1}@media (hover:none){.copy-code{opacity:1}}.copy-code.copied{border-color:rgba(16,185,129,.4);--tw-text-opacity:1;color:rgb(5 150 105/var(--tw-text-opacity,1));opacity:1}.copy-code.copied:is(.dark *){--tw-text-opacity:1;color:rgb(52 211 153/var(--tw-text-opacity,1))}.codeblock>pre{margin-top:0;margin-bottom:0}.detail-body .codeblock,.doc-body .codeblock,.doc-body table{margin-top:.75rem;margin-bottom:.75rem}.doc-body table{width:100%;border-collapse:collapse;font-size:13px}.doc-body th{border-bottom-width:1px;--tw-border-opacity:1;border-color:rgb(212 212 216/var(--tw-border-opacity,1));padding-top:.375rem;padding-bottom:.375rem;padding-right:1rem;text-align:left;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.doc-body th:is(.dark *){--tw-border-opacity:1;border-color:rgb(63 63 70/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.doc-body td{border-bottom-width:1px;--tw-border-opacity:1;border-color:rgb(244 244 245/var(--tw-border-opacity,1));padding-top:.5rem;padding-bottom:.5rem;padding-right:1rem;vertical-align:top;--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.doc-body td:is(.dark *){border-color:rgba(39,39,42,.6);--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.doc-body img{margin-left:auto;margin-right:auto;display:inline-block;height:auto;max-width:100%}.doc-body [align=center]{text-align:center}.doc-body [align=right]{text-align:right}.doc-body .ref,.doc-body a.mdlink{--tw-text-opacity:1;color:rgb(var(--accent-600)/var(--tw-text-opacity,1))}.doc-body .ref:hover,.doc-body a.mdlink:hover{text-decoration-line:underline}.doc-body .ref:is(.dark *),.doc-body a.mdlink:is(.dark *){--tw-text-opacity:1;color:rgb(var(--accent-400)/var(--tw-text-opacity,1))}.doc-body .ref{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:12.5px}.doc-body .mermaid{margin-top:1rem;margin-bottom:1rem;display:flex;justify-content:center;overflow-x:auto}.doc-body strong{font-weight:600;--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.doc-body strong:is(.dark *){--tw-text-opacity:1;color:rgb(244 244 245/var(--tw-text-opacity,1))}.detail-body hr,.doc-body hr{margin-top:1.5rem;margin-bottom:1.5rem;--tw-border-opacity:1;border-color:rgb(228 228 231/var(--tw-border-opacity,1))}.detail-body hr:is(.dark *),.doc-body hr:is(.dark *){--tw-border-opacity:1;border-color:rgb(39 39 42/var(--tw-border-opacity,1))}.doc-folder>summary{display:flex;cursor:pointer;list-style-type:none;align-items:center;gap:.375rem;border-radius:.375rem;padding:.25rem .5rem;font-size:13.5px;font-weight:600;--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.doc-folder>summary:hover{--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1))}.doc-folder>summary:is(.dark *){--tw-text-opacity:1;color:rgb(228 228 231/var(--tw-text-opacity,1))}.doc-folder>summary:hover:is(.dark *){background-color:rgba(39,39,42,.7)}.doc-folder>summary::-webkit-details-marker{display:none}.doc-folder>summary:before{content:\"›\";display:inline-block;width:.5rem;flex-shrink:0;font-size:13px;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1));transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.doc-folder>summary:is(.dark *):before{--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.doc-folder[open]>summary:before{transform:rotate(90deg)}.doc-folder-count{margin-left:auto;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:10px;font-weight:400;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.doc-folder-count:is(.dark *){--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.doc-folder+.doc-folder{margin-top:.375rem}.doc-folder>summary{padding-left:calc(.5rem + var(--doc-depth, 0)*.75rem)}.doc-link{margin-left:9px;display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;border-radius:.375rem;border-left-width:1px;--tw-border-opacity:1;border-color:rgb(244 244 245/var(--tw-border-opacity,1));padding:.25rem .5rem .25rem .625rem;font-size:12px;--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.doc-link:hover{--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1))}.doc-link:is(.dark *){--tw-border-opacity:1;border-color:rgb(39 39 42/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.doc-link:hover:is(.dark *){background-color:rgba(39,39,42,.7)}.doc-link{padding-left:calc(.625rem + (var(--doc-depth, 1) - 1)*.75rem)}.doc-link.active{--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1));font-weight:500;--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.doc-link.active:is(.dark *){--tw-bg-opacity:1;background-color:rgb(39 39 42/var(--tw-bg-opacity,1));--tw-text-opacity:1;color:rgb(244 244 245/var(--tw-text-opacity,1))}#doc-outline.empty{display:none}.outline-link{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;border-left-width:2px;border-color:transparent;padding:.25rem .25rem .25rem .625rem;font-size:12px;line-height:1.375;--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.outline-link:hover{--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.outline-link:is(.dark *){--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.outline-link:hover:is(.dark *){--tw-text-opacity:1;color:rgb(244 244 245/var(--tw-text-opacity,1))}.outline-link.lvl-2{padding-left:1.25rem}.outline-link.lvl-3{padding-left:2rem;font-size:11.5px}.outline-link.active{--tw-border-opacity:1;border-color:rgb(var(--accent-500)/var(--tw-border-opacity,1));font-weight:500;--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.outline-link.active:is(.dark *){--tw-text-opacity:1;color:rgb(244 244 245/var(--tw-text-opacity,1))}#doc-sidebar.collapsed,#doc-sidebar.collapsed+.sidebar-resizer{display:none!important}#search-results mark{border-radius:.125rem;--tw-bg-opacity:1;background-color:rgb(var(--accent-200)/var(--tw-bg-opacity,1));padding-left:.125rem;padding-right:.125rem;font-weight:500;--tw-text-opacity:1;color:rgb(var(--accent-950)/var(--tw-text-opacity,1))}#search-results mark:is(.dark *){background-color:rgb(var(--accent-600)/.5);--tw-text-opacity:1;color:rgb(var(--accent-100)/var(--tw-text-opacity,1))}::highlight(doc-search){background-color:rgb(var(--accent-300));color:rgb(var(--accent-950))}.dark ::highlight(doc-search){background-color:rgb(var(--accent-600));color:#fff}::highlight(doc-match){background-color:rgb(var(--accent-500));color:#fff}.dark ::highlight(doc-match){background-color:rgb(var(--accent-300));color:rgb(var(--accent-950))}.swatch{height:1.5rem;width:1.5rem;border-radius:9999px;border-width:2px;border-color:transparent;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-opacity:1;--tw-ring-color:rgb(228 228 231/var(--tw-ring-opacity,1));transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.swatch:hover{--tw-scale-x:1.1;--tw-scale-y:1.1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.swatch:is(.dark *){--tw-ring-opacity:1;--tw-ring-color:rgb(63 63 70/var(--tw-ring-opacity,1))}.swatch.active{--tw-border-opacity:1;border-color:rgb(24 24 27/var(--tw-border-opacity,1))}.swatch.active:is(.dark *){--tw-border-opacity:1;border-color:rgb(244 244 245/var(--tw-border-opacity,1))}.swatch-violet{background-color:#8b5cf6}.swatch-blue{background-color:#3b82f6}.swatch-emerald{background-color:#10b981}.swatch-amber{background-color:#f59e0b}.swatch-rose{background-color:#f43f5e}.pick{flex:1 1 0%;border-radius:.375rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(228 228 231/var(--tw-border-opacity,1));padding:.25rem .5rem;font-size:12px;--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.pick:hover{--tw-border-opacity:1;border-color:rgb(212 212 216/var(--tw-border-opacity,1))}.pick:is(.dark *){--tw-border-opacity:1;border-color:rgb(63 63 70/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.pick:hover:is(.dark *){--tw-border-opacity:1;border-color:rgb(82 82 91/var(--tw-border-opacity,1))}.pick.active{--tw-border-opacity:1;border-color:rgb(var(--accent-400)/var(--tw-border-opacity,1));--tw-bg-opacity:1;background-color:rgb(var(--accent-50)/var(--tw-bg-opacity,1));--tw-text-opacity:1;color:rgb(var(--accent-700)/var(--tw-text-opacity,1))}.pick.active:is(.dark *){--tw-border-opacity:1;border-color:rgb(var(--accent-500)/var(--tw-border-opacity,1));background-color:rgb(var(--accent-500)/.1);--tw-text-opacity:1;color:rgb(var(--accent-300)/var(--tw-text-opacity,1))}.theme-opt.theme-active{--tw-border-opacity:1;border-color:rgb(var(--accent-400)/var(--tw-border-opacity,1));--tw-bg-opacity:1;background-color:rgb(var(--accent-50)/var(--tw-bg-opacity,1));--tw-text-opacity:1;color:rgb(var(--accent-700)/var(--tw-text-opacity,1))}.theme-opt.theme-active:is(.dark *){--tw-border-opacity:1;border-color:rgb(var(--accent-500)/var(--tw-border-opacity,1));background-color:rgb(var(--accent-500)/.1);--tw-text-opacity:1;color:rgb(var(--accent-300)/var(--tw-text-opacity,1))}.set-group{border-bottom-width:1px;--tw-border-opacity:1;border-color:rgb(244 244 245/var(--tw-border-opacity,1))}.set-group:is(.dark *){--tw-border-opacity:1;border-color:rgb(39 39 42/var(--tw-border-opacity,1))}.set-group>summary{display:flex;cursor:pointer;list-style-type:none;align-items:center;gap:.5rem;padding-top:.625rem;padding-bottom:.625rem;font-size:12.5px;font-weight:500;--tw-text-opacity:1;color:rgb(39 39 42/var(--tw-text-opacity,1))}.set-group>summary:is(.dark *){--tw-text-opacity:1;color:rgb(228 228 231/var(--tw-text-opacity,1))}.set-group>summary::-webkit-details-marker{display:none}.set-group>summary:after{content:\"›\";margin-left:auto;font-size:15px;line-height:1;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1));transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.set-group>summary:is(.dark *):after{--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.set-group[open]>summary:after{transform:rotate(90deg)}.set-sub{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:11px;font-weight:400;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.set-sub:is(.dark *){--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.set-row{display:flex;align-items:flex-start;gap:.75rem;padding-top:.375rem;padding-bottom:.375rem}.set-label{width:72px;flex-shrink:0;padding-top:.375rem;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.set-label:is(.dark *){--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}#sheet.fullscreen{max-width:none}#sheet.fullscreen #sheet-content{margin-left:auto;margin-right:auto;max-width:42rem}.task-card{position:relative;width:100%;cursor:pointer;border-radius:.375rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(228 228 231/var(--tw-border-opacity,1));--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1));padding:.625rem .75rem;text-align:left;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.task-card:hover{--tw-border-opacity:1;border-color:rgb(212 212 216/var(--tw-border-opacity,1))}.task-card:focus-visible{outline-style:solid;outline-width:2px;outline-offset:1px;outline-color:rgb(var(--accent-500)/1)}.task-card:is(.dark *){--tw-border-opacity:1;border-color:rgb(39 39 42/var(--tw-border-opacity,1));--tw-bg-opacity:1;background-color:rgb(24 24 27/var(--tw-bg-opacity,1))}.task-card:hover:is(.dark *){--tw-border-opacity:1;border-color:rgb(63 63 70/var(--tw-border-opacity,1))}.card-group>summary{display:flex;cursor:pointer;list-style-type:none;align-items:center;gap:.375rem;border-radius:.25rem;padding:.25rem .375rem;font-size:11px;font-weight:500;--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.card-group>summary:hover{--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1))}.card-group>summary:is(.dark *){--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.card-group>summary:hover:is(.dark *){background-color:rgba(39,39,42,.6)}.card-group>summary::-webkit-details-marker{display:none}.card-group>summary:before{content:\"›\";display:inline-block;width:.5rem;flex-shrink:0;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1));transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.card-group>summary:is(.dark *):before{--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.card-group[open]>summary:before{transform:rotate(90deg)}.card-group-count{margin-left:auto;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:10px;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.card-group-count:is(.dark *){--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.card-group-body{display:flex;flex-direction:column;gap:.5rem;padding-bottom:.25rem;padding-top:.25rem}.card-group-body:empty{min-height:28px}.card-move{position:absolute;right:.25rem;top:.25rem;z-index:10;display:flex;flex-direction:column;opacity:0;transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.task-card:focus-within .card-move,.task-card:hover .card-move{opacity:1}.card-move>button{border-radius:.25rem;padding:.125rem;--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.card-move>button:hover{--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1));--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.card-move>button:is(.dark *){--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.card-move>button:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(39 39 42/var(--tw-bg-opacity,1));--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}@media (hover:none){.card-move{opacity:1}}.drop-marker{margin-top:-.125rem;margin-bottom:-.125rem;height:.125rem;border-radius:9999px;--tw-bg-opacity:1;background-color:rgb(var(--accent-500)/var(--tw-bg-opacity,1))}.drop-marker:is(.dark *){--tw-bg-opacity:1;background-color:rgb(var(--accent-400)/var(--tw-bg-opacity,1))}.doc-edit{position:absolute;right:2rem;top:1.5rem;display:inline-flex;align-items:center;gap:.375rem;border-radius:.375rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(228 228 231/var(--tw-border-opacity,1));--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1));padding:.25rem .5rem;font-size:12px;--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1));opacity:0;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.doc-edit:hover{--tw-text-opacity:1;color:rgb(39 39 42/var(--tw-text-opacity,1))}.doc-edit:focus-visible{opacity:1}.doc-edit:is(.dark *){--tw-border-opacity:1;border-color:rgb(39 39 42/var(--tw-border-opacity,1));--tw-bg-opacity:1;background-color:rgb(24 24 27/var(--tw-bg-opacity,1));--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.doc-edit:hover:is(.dark *){--tw-text-opacity:1;color:rgb(244 244 245/var(--tw-text-opacity,1))}#doc-content{position:relative}#doc-content:hover .doc-edit{opacity:1}@media (hover:none){.doc-edit{opacity:1}}.detail-edit{display:inline-flex;align-items:center;gap:.375rem;border-radius:.375rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(228 228 231/var(--tw-border-opacity,1));padding:.25rem .5rem;font-size:11.5px;--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.detail-edit:hover{--tw-border-opacity:1;border-color:rgb(212 212 216/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(39 39 42/var(--tw-text-opacity,1))}.detail-edit:is(.dark *){--tw-border-opacity:1;border-color:rgb(39 39 42/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.detail-edit:hover:is(.dark *){--tw-border-opacity:1;border-color:rgb(82 82 91/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(244 244 245/var(--tw-text-opacity,1))}.md-editor{margin-top:.5rem}.md-editor-area{display:block;width:100%;resize:none;border-radius:.375rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(var(--accent-400)/var(--tw-border-opacity,1));--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1));padding:.75rem;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:12.5px;line-height:1.625;--tw-text-opacity:1;color:rgb(39 39 42/var(--tw-text-opacity,1));outline:2px solid transparent;outline-offset:2px}.md-editor-area:is(.dark *){border-color:rgb(var(--accent-500)/.6);--tw-bg-opacity:1;background-color:rgb(24 24 27/var(--tw-bg-opacity,1));--tw-text-opacity:1;color:rgb(228 228 231/var(--tw-text-opacity,1))}.md-editor-area{min-height:12rem}.md-editor-status{margin-top:.375rem;font-size:11px;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.md-editor-status:is(.dark *){--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.md-editor-status.is-stale{font-weight:500;--tw-text-opacity:1;color:rgb(217 119 6/var(--tw-text-opacity,1))}.md-editor-status.is-stale:is(.dark *){--tw-text-opacity:1;color:rgb(251 191 36/var(--tw-text-opacity,1))}.card-id{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:11px;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.card-id:is(.dark *){--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.card-title{font-size:13px;font-weight:500;line-height:1.375;--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.card-title:is(.dark *){--tw-text-opacity:1;color:rgb(244 244 245/var(--tw-text-opacity,1))}.card-progress{margin-left:auto;display:inline-flex;align-items:center;gap:.375rem;font-size:10px;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.card-progress:is(.dark *){--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.epic-chip{border-radius:.25rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(228 228 231/var(--tw-border-opacity,1));padding:1px .375rem;font-size:10px;font-weight:500;--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.epic-chip:hover{--tw-border-opacity:1;border-color:rgb(212 212 216/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.epic-chip:is(.dark *){--tw-border-opacity:1;border-color:rgb(63 63 70/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.epic-chip:hover:is(.dark *){--tw-border-opacity:1;border-color:rgb(82 82 91/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(228 228 231/var(--tw-text-opacity,1))}.prio{display:inline-flex;align-items:center;gap:.25rem;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.prio:is(.dark *){--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.prio-label{font-size:10px;font-weight:500;letter-spacing:.025em}.dep-chip{display:inline-flex;align-items:center;gap:.25rem;border-radius:.25rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(228 228 231/var(--tw-border-opacity,1));padding:1px .375rem;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:10px;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.dep-chip:hover{--tw-border-opacity:1;border-color:rgb(212 212 216/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.dep-chip:is(.dark *){--tw-border-opacity:1;border-color:rgb(63 63 70/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.dep-chip:hover:is(.dark *){--tw-border-opacity:1;border-color:rgb(82 82 91/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.dep-chip.done{border-color:transparent;--tw-bg-opacity:1;background-color:rgb(236 253 245/var(--tw-bg-opacity,1));--tw-text-opacity:1;color:rgb(5 150 105/var(--tw-text-opacity,1))}.dep-chip.done:hover{--tw-bg-opacity:1;background-color:rgb(209 250 229/var(--tw-bg-opacity,1))}.dep-chip.done:is(.dark *){background-color:rgba(16,185,129,.1);--tw-text-opacity:1;color:rgb(52 211 153/var(--tw-text-opacity,1))}.dep-chip.done:hover:is(.dark *){background-color:rgba(16,185,129,.2)}.backlink-row{display:flex;width:100%;align-items:center;gap:.5rem;border-radius:.375rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(244 244 245/var(--tw-border-opacity,1));padding:.375rem .625rem;text-align:left;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.backlink-row:hover{--tw-border-opacity:1;border-color:rgb(212 212 216/var(--tw-border-opacity,1))}.backlink-row:is(.dark *){--tw-border-opacity:1;border-color:rgb(39 39 42/var(--tw-border-opacity,1))}.backlink-row:hover:is(.dark *){--tw-border-opacity:1;border-color:rgb(82 82 91/var(--tw-border-opacity,1))}.epic-row{display:flex;width:100%;cursor:pointer;align-items:center;gap:.5rem;border-radius:.375rem;padding:.375rem .5rem;text-align:left;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.epic-row:hover{--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1))}.epic-row:hover:is(.dark *){background-color:rgba(39,39,42,.7)}.epic-row.epic-active{--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1))}.epic-row.epic-active:is(.dark *){--tw-bg-opacity:1;background-color:rgb(39 39 42/var(--tw-bg-opacity,1))}#sidebar>*{flex-shrink:0}#sidebar.collapsed,#sidebar.collapsed+.sidebar-resizer{display:none!important}section.drop-target{background-color:rgb(var(--accent-50)/.6);--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-inset:inset;--tw-ring-opacity:1;--tw-ring-color:rgb(var(--accent-300)/var(--tw-ring-opacity,1))}section.drop-target:is(.dark *){background-color:rgb(var(--accent-500)/.05);--tw-ring-color:rgb(var(--accent-500)/0.4)}.task-card.\\!dragging,.task-card.dragging{opacity:.4}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.invisible{visibility:hidden}.collapse{visibility:collapse}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{inset:0}.inset-y-0{top:0;bottom:0}.-top-0\\.5{top:-.125rem}.bottom-3{bottom:.75rem}.bottom-5{bottom:1.25rem}.left-1{left:.25rem}.left-1\\/2{left:50%}.left-3{left:.75rem}.left-44{left:11rem}.right-0{right:0}.right-3{right:.75rem}.right-4{right:1rem}.right-5{right:1.25rem}.top-0{top:0}.top-4{top:1rem}.z-10{z-index:10}.z-40{z-index:40}.z-50{z-index:50}.z-\\[60\\]{z-index:60}.mx-auto{margin-left:auto;margin-right:auto}.-ml-0\\.5{margin-left:-.125rem}.mb-1{margin-bottom:.25rem}.mb-1\\.5{margin-bottom:.375rem}.mb-2{margin-bottom:.5rem}.mb-2\\.5{margin-bottom:.625rem}.mb-3{margin-bottom:.75rem}.mb-8{margin-bottom:2rem}.ml-3{margin-left:.75rem}.ml-auto{margin-left:auto}.mr-1\\.5{margin-right:.375rem}.mr-16{margin-right:4rem}.mt-1{margin-top:.25rem}.mt-1\\.5{margin-top:.375rem}.mt-10{margin-top:2.5rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-5{margin-top:1.25rem}.mt-6{margin-top:1.5rem}.block{display:block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-1\\.5{height:.375rem}.h-12{height:3rem}.h-2{height:.5rem}.h-3\\.5{height:.875rem}.h-5{height:1.25rem}.h-8{height:2rem}.h-\\[calc\\(\\(100vh\\/var\\(--zoom\\)\\)-3rem\\)\\]{height:calc(100vh/var(--zoom) - 3rem)}.h-\\[calc\\(100vh\\/var\\(--zoom\\)\\)\\]{height:calc(100vh/var(--zoom))}.h-full{height:100%}.min-h-\\[60px\\]{min-height:60px}.w-1{width:.25rem}.w-1\\.5{width:.375rem}.w-2{width:.5rem}.w-3\\.5{width:.875rem}.w-44{width:11rem}.w-56{width:14rem}.w-60{width:15rem}.w-\\[290px\\]{width:290px}.w-full{width:100%}.min-w-0{min-width:0}.max-w-3xl{max-width:48rem}.max-w-\\[460px\\]{max-width:460px}.max-w-full{max-width:100%}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.grow{flex-grow:1}.-translate-x-1\\/2{--tw-translate-x:-50%}.-translate-x-1\\/2,.translate-x-full{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-full{--tw-translate-x:100%}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-col-resize{cursor:col-resize}.cursor-grab{cursor:grab}.cursor-pointer{cursor:pointer}.resize-y{resize:vertical}.resize{resize:both}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-\\[90px_1fr\\]{grid-template-columns:90px 1fr}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-baseline{align-items:baseline}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0\\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-1\\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-2\\.5{gap:.625rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-y-1\\.5{row-gap:.375rem}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem*var(--tw-space-y-reverse))}.space-y-1\\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.375rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.375rem*var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px*var(--tw-divide-y-reverse))}.divide-zinc-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(228 228 231/var(--tw-divide-opacity,1))}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis}.truncate,.whitespace-nowrap{white-space:nowrap}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-sm{border-radius:.125rem}.border{border-width:1px}.border-y{border-top-width:1px}.border-b,.border-y{border-bottom-width:1px}.border-l{border-left-width:1px}.border-l-2{border-left-width:2px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-dashed{border-style:dashed}.border-rose-300{--tw-border-opacity:1;border-color:rgb(253 164 175/var(--tw-border-opacity,1))}.border-rose-500\\/70{border-color:rgba(244,63,94,.7)}.border-zinc-100{--tw-border-opacity:1;border-color:rgb(244 244 245/var(--tw-border-opacity,1))}.border-zinc-200{--tw-border-opacity:1;border-color:rgb(228 228 231/var(--tw-border-opacity,1))}.bg-accent-50{--tw-bg-opacity:1;background-color:rgb(var(--accent-50)/var(--tw-bg-opacity,1))}.bg-accent-600{--tw-bg-opacity:1;background-color:rgb(var(--accent-600)/var(--tw-bg-opacity,1))}.bg-amber-50{--tw-bg-opacity:1;background-color:rgb(255 251 235/var(--tw-bg-opacity,1))}.bg-black\\/20{background-color:rgba(0,0,0,.2)}.bg-emerald-500{--tw-bg-opacity:1;background-color:rgb(16 185 129/var(--tw-bg-opacity,1))}.bg-rose-50{--tw-bg-opacity:1;background-color:rgb(255 241 242/var(--tw-bg-opacity,1))}.bg-transparent{background-color:transparent}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.bg-zinc-100{--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1))}.bg-zinc-300{--tw-bg-opacity:1;background-color:rgb(212 212 216/var(--tw-bg-opacity,1))}.bg-zinc-50{--tw-bg-opacity:1;background-color:rgb(250 250 250/var(--tw-bg-opacity,1))}.bg-zinc-900{--tw-bg-opacity:1;background-color:rgb(24 24 27/var(--tw-bg-opacity,1))}.p-0\\.5{padding:.125rem}.p-1{padding:.25rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.p-96{padding:24rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-1\\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-8{padding-left:2rem;padding-right:2rem}.py-0\\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-3\\.5{padding-top:.875rem;padding-bottom:.875rem}.py-5{padding-top:1.25rem;padding-bottom:1.25rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.py-px{padding-top:1px;padding-bottom:1px}.pb-1{padding-bottom:.25rem}.pb-3{padding-bottom:.75rem}.pl-2\\.5{padding-left:.625rem}.pr-1{padding-right:.25rem}.pr-3{padding-right:.75rem}.pt-1{padding-top:.25rem}.pt-3{padding-top:.75rem}.pt-4{padding-top:1rem}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.font-sans{font-family:var(--font-ui)}.font-serif{font-family:ui-serif,Georgia,Cambria,Times New Roman,Times,serif}.text-\\[10\\.5px\\]{font-size:10.5px}.text-\\[10px\\]{font-size:10px}.text-\\[11px\\]{font-size:11px}.text-\\[12\\.5px\\]{font-size:12.5px}.text-\\[12px\\]{font-size:12px}.text-\\[13px\\]{font-size:13px}.text-\\[16px\\]{font-size:16px}.text-\\[9\\.5px\\]{font-size:9.5px}.text-\\[9px\\]{font-size:9px}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.capitalize{text-transform:capitalize}.normal-case{text-transform:none}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.leading-5{line-height:1.25rem}.leading-relaxed{line-height:1.625}.leading-snug{line-height:1.375}.tracking-tight{letter-spacing:-.025em}.tracking-wide{letter-spacing:.025em}.tracking-wider{letter-spacing:.05em}.text-accent-600{--tw-text-opacity:1;color:rgb(var(--accent-600)/var(--tw-text-opacity,1))}.text-amber-600{--tw-text-opacity:1;color:rgb(217 119 6/var(--tw-text-opacity,1))}.text-rose-500{--tw-text-opacity:1;color:rgb(244 63 94/var(--tw-text-opacity,1))}.text-rose-700{--tw-text-opacity:1;color:rgb(190 18 60/var(--tw-text-opacity,1))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.text-zinc-100{--tw-text-opacity:1;color:rgb(244 244 245/var(--tw-text-opacity,1))}.text-zinc-300{--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.text-zinc-400{--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.text-zinc-500{--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.text-zinc-600{--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.text-zinc-700{--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.text-zinc-800{--tw-text-opacity:1;color:rgb(39 39 42/var(--tw-text-opacity,1))}.text-zinc-900{--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.accent-accent-600{accent-color:rgb(var(--accent-600)/1)}.opacity-0{opacity:0}.opacity-100{opacity:1}.shadow{--tw-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px -1px rgba(0,0,0,.1);--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color)}.shadow,.shadow-lg{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow-sm{--tw-shadow:0 1px 2px 0 rgba(0,0,0,.05);--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.shadow-sm,.shadow-xl{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px rgba(0,0,0,.1),0 8px 10px -6px rgba(0,0,0,.1);--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color),0 8px 10px -6px var(--tw-shadow-color)}.outline-none{outline:2px solid transparent;outline-offset:2px}.outline{outline-style:solid}.ring{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.blur{--tw-blur:blur(8px)}.blur,.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}:root,html[data-accent=violet]{--accent-50:245 243 255;--accent-100:237 233 254;--accent-200:221 214 254;--accent-300:196 181 253;--accent-400:167 139 250;--accent-500:139 92 246;--accent-600:124 58 237;--accent-700:109 40 217;--accent-950:46 16 101}html[data-accent=blue]{--accent-50:239 246 255;--accent-100:219 234 254;--accent-200:191 219 254;--accent-300:147 197 253;--accent-400:96 165 250;--accent-500:59 130 246;--accent-600:37 99 235;--accent-700:29 78 216;--accent-950:23 37 84}html[data-accent=emerald]{--accent-50:236 253 245;--accent-100:209 250 229;--accent-200:167 243 208;--accent-300:110 231 183;--accent-400:52 211 153;--accent-500:16 185 129;--accent-600:5 150 105;--accent-700:4 120 87;--accent-950:2 44 34}html[data-accent=amber]{--accent-50:255 251 235;--accent-100:254 243 199;--accent-200:253 230 138;--accent-300:252 211 77;--accent-400:251 191 36;--accent-500:245 158 11;--accent-600:217 119 6;--accent-700:180 83 9;--accent-950:69 26 3}html[data-accent=rose]{--accent-50:255 241 242;--accent-100:255 228 230;--accent-200:254 205 211;--accent-300:253 164 175;--accent-400:251 113 133;--accent-500:244 63 94;--accent-600:225 29 72;--accent-700:190 18 60;--accent-950:76 5 25}:root,html[data-font=sans]{--font-ui:-apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,\"Helvetica Neue\",Arial,sans-serif}html[data-font=serif]{--font-ui:ui-serif,Georgia,Cambria,\"Times New Roman\",Times,serif}html[data-font=mono]{--font-ui:ui-monospace,SFMono-Regular,\"SF Mono\",Menlo,Consolas,\"Liberation Mono\",monospace}:root{--zoom:1}html[data-size=compact]{--zoom:0.9}html[data-size=large]{--zoom:1.15}html[data-size=compact] body,html[data-size=large] body{zoom:var(--zoom)}@media (prefers-reduced-motion:no-preference){html.theme-anim,html.theme-anim *,html.theme-anim :after,html.theme-anim :before{transition:background-color .26s cubic-bezier(.4,0,.2,1),border-color .26s cubic-bezier(.4,0,.2,1),color .26s cubic-bezier(.4,0,.2,1),fill .26s cubic-bezier(.4,0,.2,1),stroke .26s cubic-bezier(.4,0,.2,1),box-shadow .26s cubic-bezier(.4,0,.2,1)!important;transition-delay:0s!important}}.placeholder\\:text-zinc-400::-moz-placeholder{--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.placeholder\\:text-zinc-400::placeholder{--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.hover\\:border-zinc-300:hover{--tw-border-opacity:1;border-color:rgb(212 212 216/var(--tw-border-opacity,1))}.hover\\:bg-accent-400\\/70:hover{background-color:rgb(var(--accent-400)/.7)}.hover\\:bg-accent-500:hover{--tw-bg-opacity:1;background-color:rgb(var(--accent-500)/var(--tw-bg-opacity,1))}.hover\\:bg-zinc-100:hover{--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1))}.hover\\:bg-zinc-200:hover{--tw-bg-opacity:1;background-color:rgb(228 228 231/var(--tw-bg-opacity,1))}.hover\\:bg-zinc-50:hover{--tw-bg-opacity:1;background-color:rgb(250 250 250/var(--tw-bg-opacity,1))}.hover\\:text-zinc-600:hover{--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.hover\\:text-zinc-900:hover{--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.hover\\:underline:hover{text-decoration-line:underline}.focus\\:border-accent-400:focus{--tw-border-opacity:1;border-color:rgb(var(--accent-400)/var(--tw-border-opacity,1))}.focus\\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus-visible\\:opacity-100:focus-visible{opacity:1}.active\\:bg-accent-400\\/70:active{background-color:rgb(var(--accent-400)/.7)}.group:hover .group-hover\\:opacity-100{opacity:1}@media (prefers-reduced-motion:no-preference){.motion-safe\\:transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.motion-safe\\:duration-200{transition-duration:.2s}}.dark\\:divide-zinc-800:is(.dark *)>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(39 39 42/var(--tw-divide-opacity,1))}.dark\\:border-rose-500\\/40:is(.dark *){border-color:rgba(244,63,94,.4)}.dark\\:border-zinc-700:is(.dark *){--tw-border-opacity:1;border-color:rgb(63 63 70/var(--tw-border-opacity,1))}.dark\\:border-zinc-800:is(.dark *){--tw-border-opacity:1;border-color:rgb(39 39 42/var(--tw-border-opacity,1))}.dark\\:bg-accent-500\\/10:is(.dark *){background-color:rgb(var(--accent-500)/.1)}.dark\\:bg-amber-500\\/10:is(.dark *){background-color:rgba(245,158,11,.1)}.dark\\:bg-black\\/50:is(.dark *){background-color:rgba(0,0,0,.5)}.dark\\:bg-rose-500\\/10:is(.dark *){background-color:rgba(244,63,94,.1)}.dark\\:bg-zinc-100:is(.dark *){--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1))}.dark\\:bg-zinc-800:is(.dark *){--tw-bg-opacity:1;background-color:rgb(39 39 42/var(--tw-bg-opacity,1))}.dark\\:bg-zinc-900:is(.dark *){--tw-bg-opacity:1;background-color:rgb(24 24 27/var(--tw-bg-opacity,1))}.dark\\:bg-zinc-950:is(.dark *){--tw-bg-opacity:1;background-color:rgb(9 9 11/var(--tw-bg-opacity,1))}.dark\\:text-accent-400:is(.dark *){--tw-text-opacity:1;color:rgb(var(--accent-400)/var(--tw-text-opacity,1))}.dark\\:text-amber-400:is(.dark *){--tw-text-opacity:1;color:rgb(251 191 36/var(--tw-text-opacity,1))}.dark\\:text-rose-300:is(.dark *){--tw-text-opacity:1;color:rgb(253 164 175/var(--tw-text-opacity,1))}.dark\\:text-zinc-100:is(.dark *){--tw-text-opacity:1;color:rgb(244 244 245/var(--tw-text-opacity,1))}.dark\\:text-zinc-200:is(.dark *){--tw-text-opacity:1;color:rgb(228 228 231/var(--tw-text-opacity,1))}.dark\\:text-zinc-300:is(.dark *){--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.dark\\:text-zinc-400:is(.dark *){--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.dark\\:text-zinc-50:is(.dark *){--tw-text-opacity:1;color:rgb(250 250 250/var(--tw-text-opacity,1))}.dark\\:text-zinc-500:is(.dark *){--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.dark\\:text-zinc-600:is(.dark *){--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.dark\\:text-zinc-700:is(.dark *){--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.dark\\:text-zinc-900:is(.dark *){--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.dark\\:brightness-125:is(.dark *){--tw-brightness:brightness(1.25);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.dark\\:placeholder\\:text-zinc-600:is(.dark *)::-moz-placeholder{--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.dark\\:placeholder\\:text-zinc-600:is(.dark *)::placeholder{--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.dark\\:hover\\:border-zinc-600:hover:is(.dark *){--tw-border-opacity:1;border-color:rgb(82 82 91/var(--tw-border-opacity,1))}.dark\\:hover\\:bg-accent-500\\/60:hover:is(.dark *){background-color:rgb(var(--accent-500)/.6)}.dark\\:hover\\:bg-zinc-700:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(63 63 70/var(--tw-bg-opacity,1))}.dark\\:hover\\:bg-zinc-800:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(39 39 42/var(--tw-bg-opacity,1))}.dark\\:hover\\:bg-zinc-900:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(24 24 27/var(--tw-bg-opacity,1))}.dark\\:hover\\:text-zinc-100:hover:is(.dark *){--tw-text-opacity:1;color:rgb(244 244 245/var(--tw-text-opacity,1))}.dark\\:hover\\:text-zinc-300:hover:is(.dark *){--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.dark\\:focus\\:border-accent-500:focus:is(.dark *){--tw-border-opacity:1;border-color:rgb(var(--accent-500)/var(--tw-border-opacity,1))}.dark\\:active\\:bg-accent-500\\/60:active:is(.dark *){background-color:rgb(var(--accent-500)/.6)}@media (min-width:640px){.sm\\:block{display:block}}@media (min-width:768px){.md\\:block{display:block}.md\\:flex{display:flex}}@media (min-width:1024px){.lg\\:block{display:block}}.dark\\:\\[\\&\\>option\\]\\:bg-zinc-900>option:is(.dark *){--tw-bg-opacity:1;background-color:rgb(24 24 27/var(--tw-bg-opacity,1))}";
46
46
 
47
47
  // assets/logo.svg
48
48
  var logo_default = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="120 12 800 800">
@@ -515,11 +515,177 @@ var board_default = `(() => {
515
515
  const el = document.activeElement;
516
516
  return e.key === "/" && !e.metaKey && !e.ctrlKey && !e.altKey && !/INPUT|TEXTAREA|SELECT/.test(el?.tagName ?? "") && !el?.isContentEditable;
517
517
  }
518
+ function initCopyCode() {
519
+ document.addEventListener("click", (e) => {
520
+ const btn = e.target.closest(".copy-code");
521
+ const pre = btn?.previousElementSibling;
522
+ if (!btn || !(pre instanceof HTMLElement))
523
+ return;
524
+ copyText(pre.innerText).then((ok) => {
525
+ showToast(ok ? "Copied" : "Copy failed");
526
+ if (!ok)
527
+ return;
528
+ btn.classList.add("copied");
529
+ setTimeout(() => btn.classList.remove("copied"), 1200);
530
+ });
531
+ });
532
+ }
533
+ async function copyText(text) {
534
+ try {
535
+ if (navigator.clipboard) {
536
+ await navigator.clipboard.writeText(text);
537
+ return true;
538
+ }
539
+ } catch {}
540
+ const ta = document.createElement("textarea");
541
+ ta.value = text;
542
+ ta.setAttribute("readonly", "");
543
+ ta.className = "fixed opacity-0";
544
+ document.body.append(ta);
545
+ ta.select();
546
+ const ok = document.execCommand("copy");
547
+ ta.remove();
548
+ return ok;
549
+ }
550
+ var editing = null;
551
+ var isEditing = () => editing !== null;
552
+ var SAVE_DEBOUNCE_MS = 700;
553
+ async function startEditing(target) {
554
+ if (editing)
555
+ return;
556
+ const loaded = await target.load().catch(() => null);
557
+ if (!loaded)
558
+ return showToast("Could not read the source");
559
+ editing = target;
560
+ let rev = loaded.rev;
561
+ let saved = loaded.body;
562
+ let timer;
563
+ const box = document.createElement("div");
564
+ box.className = "md-editor";
565
+ const area = document.createElement("textarea");
566
+ area.className = "md-editor-area";
567
+ area.value = loaded.body;
568
+ area.spellcheck = false;
569
+ const status = document.createElement("div");
570
+ status.className = "md-editor-status";
571
+ status.textContent = "Editing — ⌘S to save, Esc to finish";
572
+ box.append(area, status);
573
+ target.view.hidden = true;
574
+ target.view.insertAdjacentElement("afterend", box);
575
+ const fit = () => {
576
+ area.style.height = "auto";
577
+ area.style.height = area.scrollHeight + "px";
578
+ };
579
+ fit();
580
+ area.focus();
581
+ let inflight = null;
582
+ async function flush() {
583
+ clearTimeout(timer);
584
+ while (inflight)
585
+ await inflight.catch(() => false);
586
+ if (area.value === saved)
587
+ return true;
588
+ const run = save();
589
+ inflight = run;
590
+ try {
591
+ return await run;
592
+ } finally {
593
+ if (inflight === run)
594
+ inflight = null;
595
+ }
596
+ }
597
+ async function save() {
598
+ const sending = area.value;
599
+ status.textContent = "Saving…";
600
+ const res = await target.save(sending, rev);
601
+ if (res.stale) {
602
+ status.textContent = "Changed on disk elsewhere — copy your text, then reopen";
603
+ status.classList.add("is-stale");
604
+ return false;
605
+ }
606
+ if (!res.ok) {
607
+ status.textContent = "Save failed";
608
+ status.classList.add("is-stale");
609
+ return false;
610
+ }
611
+ saved = sending;
612
+ rev = res.rev ?? rev;
613
+ status.classList.remove("is-stale");
614
+ status.textContent = area.value === saved ? "Saved" : "Editing…";
615
+ return true;
616
+ }
617
+ area.addEventListener("input", () => {
618
+ fit();
619
+ status.textContent = "Editing…";
620
+ clearTimeout(timer);
621
+ timer = setTimeout(() => void flush(), SAVE_DEBOUNCE_MS);
622
+ });
623
+ async function stop() {
624
+ if (!editing)
625
+ return;
626
+ if (!await flush())
627
+ return;
628
+ editing = null;
629
+ pendingStop = null;
630
+ box.remove();
631
+ target.view.hidden = false;
632
+ target.done();
633
+ }
634
+ area.addEventListener("keydown", (e) => {
635
+ if ((e.metaKey || e.ctrlKey) && e.key === "s") {
636
+ e.preventDefault();
637
+ flush();
638
+ }
639
+ if (e.key === "Escape") {
640
+ e.preventDefault();
641
+ stop();
642
+ }
643
+ });
644
+ area.addEventListener("blur", () => void flush());
645
+ pendingStop = stop;
646
+ }
647
+ var pendingStop = null;
648
+ function stopEditing() {
649
+ pendingStop?.();
650
+ }
651
+ function initBodyEditor() {
652
+ document.addEventListener("click", (e) => {
653
+ if (!e.target.closest?.("[data-edit-body]"))
654
+ return;
655
+ const root = sheetContent.querySelector(".detail-root");
656
+ const view = sheetContent.querySelector(".detail-body");
657
+ if (!root || !view || isEditing())
658
+ return;
659
+ const id = root.dataset.id;
660
+ startEditing({
661
+ view,
662
+ load: () => fetch("/api/items/" + encodeURIComponent(id)).then((r) => r.ok ? r.json() : Promise.reject()).then((t) => ({ body: t.body, rev: t.rev })),
663
+ save: async (body, rev) => {
664
+ const res = await fetch("/api/items/" + encodeURIComponent(id), {
665
+ method: "PATCH",
666
+ headers: { "content-type": "application/json" },
667
+ body: JSON.stringify({ body, rev })
668
+ });
669
+ if (res.status === 409)
670
+ return { ok: false, stale: true };
671
+ if (!res.ok)
672
+ return { ok: false };
673
+ return { ok: true, rev: (await res.json()).rev };
674
+ },
675
+ done: () => {
676
+ clearPaneCache();
677
+ openSheet(id);
678
+ }
679
+ });
680
+ });
681
+ }
518
682
  function initChrome() {
519
683
  initResizer();
520
684
  initTheme();
521
685
  initSheet();
522
686
  initCheckboxes();
687
+ initCopyCode();
688
+ initBodyEditor();
523
689
  }
524
690
 
525
691
  // src/client/board.ts
@@ -540,6 +706,11 @@ var board_default = `(() => {
540
706
  for (const r of document.querySelectorAll("[data-filter-epic]")) {
541
707
  r.classList.toggle("epic-active", (r.dataset.filterEpic || null) === epic);
542
708
  }
709
+ for (const g of document.querySelectorAll(".card-group")) {
710
+ const shown = g.querySelectorAll(".task-card:not(.hidden)").length;
711
+ g.classList.toggle("hidden", shown === 0);
712
+ g.querySelector(".card-group-count").textContent = String(shown);
713
+ }
543
714
  for (const s of document.querySelectorAll("section[data-col]")) {
544
715
  s.querySelector(".col-count").textContent = String(s.querySelectorAll(".task-card:not(.hidden)").length);
545
716
  }
@@ -584,6 +755,7 @@ var board_default = `(() => {
584
755
  document.getElementById("app").innerHTML = doc.getElementById("app").innerHTML;
585
756
  document.getElementById("header-meta").innerHTML = doc.getElementById("header-meta").innerHTML;
586
757
  applySidebarState();
758
+ applyGroupFolds();
587
759
  applyFilter(activeEpic);
588
760
  clearPaneCache();
589
761
  if (sheetIsOpen() && location.hash.length > 1) {
@@ -604,6 +776,8 @@ var board_default = `(() => {
604
776
  return;
605
777
  if (target.closest("[data-open]"))
606
778
  return;
779
+ if (target.closest("[data-move]"))
780
+ return;
607
781
  const row = target.closest("[data-filter-epic]");
608
782
  if (row) {
609
783
  applyFilter(row.dataset.filterEpic === activeEpic ? null : row.dataset.filterEpic || null);
@@ -633,17 +807,73 @@ var board_default = `(() => {
633
807
  return;
634
808
  }
635
809
  const target = e.target;
810
+ if (e.altKey && (e.key === "ArrowUp" || e.key === "ArrowDown")) {
811
+ const card = target.closest(".task-card");
812
+ if (card) {
813
+ e.preventDefault();
814
+ moveCard(card, e.key === "ArrowUp" ? "up" : "down");
815
+ return;
816
+ }
817
+ }
636
818
  if ((e.key === "Enter" || e.key === " ") && target instanceof HTMLElement && target.getAttribute("role") === "button") {
637
819
  e.preventDefault();
638
820
  target.click();
639
821
  }
640
822
  });
823
+ var groupsCollapsedKey = "boardCollapsedGroups";
824
+ function collapsedGroups() {
825
+ try {
826
+ return new Set(JSON.parse(localStorage.getItem(groupsCollapsedKey) || "[]"));
827
+ } catch {
828
+ return new Set;
829
+ }
830
+ }
831
+ function applyGroupFolds() {
832
+ const shut = collapsedGroups();
833
+ for (const d of document.querySelectorAll(".card-group"))
834
+ d.open = !shut.has(d.dataset.group);
835
+ }
836
+ document.addEventListener("toggle", (e) => {
837
+ const d = e.target.closest(".card-group");
838
+ if (!d)
839
+ return;
840
+ const shut = collapsedGroups();
841
+ if (d.open)
842
+ shut.delete(d.dataset.group);
843
+ else
844
+ shut.add(d.dataset.group);
845
+ localStorage.setItem(groupsCollapsedKey, JSON.stringify([...shut]));
846
+ }, true);
847
+ function visibleSiblings(card) {
848
+ const body = card.closest(".card-group-body");
849
+ return body ? [...body.querySelectorAll(".task-card:not(.hidden)")] : [card];
850
+ }
851
+ function moveCard(card, dir) {
852
+ const sibs = visibleSiblings(card);
853
+ const at = sibs.indexOf(card);
854
+ if (at === -1 || dir === "up" && at === 0 || dir === "down" && at === sibs.length - 1)
855
+ return;
856
+ const target = sibs[dir === "up" ? at - 1 : at + 1];
857
+ target.insertAdjacentElement(dir === "up" ? "beforebegin" : "afterend", card);
858
+ card.focus();
859
+ patchItem(card.dataset.taskId, { move: dir }, card.dataset.rev);
860
+ }
861
+ document.addEventListener("click", (e) => {
862
+ const btn = e.target.closest?.("[data-move]");
863
+ if (!btn)
864
+ return;
865
+ moveCard(btn.closest(".task-card"), btn.dataset.move);
866
+ });
641
867
  var dragId = null;
642
868
  var dragRev = null;
643
869
  var dragStatus = null;
870
+ var dragEpic = null;
871
+ var marker = document.createElement("div");
872
+ marker.className = "drop-marker";
644
873
  function clearDropTargets() {
645
874
  for (const s of document.querySelectorAll("section.drop-target"))
646
875
  s.classList.remove("drop-target");
876
+ marker.remove();
647
877
  }
648
878
  document.addEventListener("dragstart", (e) => {
649
879
  const card = e.target.closest?.("[data-task-id]");
@@ -652,6 +882,7 @@ var board_default = `(() => {
652
882
  dragId = card.dataset.taskId;
653
883
  dragRev = card.dataset.rev;
654
884
  dragStatus = card.dataset.status;
885
+ dragEpic = card.dataset.epic;
655
886
  e.dataTransfer.effectAllowed = "move";
656
887
  card.classList.add("dragging");
657
888
  });
@@ -660,23 +891,54 @@ var board_default = `(() => {
660
891
  clearDropTargets();
661
892
  dragId = null;
662
893
  });
894
+ function dropAnchor(body, y) {
895
+ const cards = [...body.querySelectorAll(".task-card:not(.dragging):not(.hidden)")];
896
+ for (const c of cards) {
897
+ const box = c.getBoundingClientRect();
898
+ if (y < box.top + box.height / 2)
899
+ return { id: c.dataset.taskId, where: "before" };
900
+ }
901
+ const last = cards[cards.length - 1];
902
+ return last ? { id: last.dataset.taskId, where: "after" } : null;
903
+ }
663
904
  document.addEventListener("dragover", (e) => {
905
+ if (!dragId)
906
+ return;
664
907
  const col = e.target.closest?.("section[data-col]");
665
- if (col && dragId) {
666
- e.preventDefault();
667
- e.dataTransfer.dropEffect = "move";
668
- if (!col.classList.contains("drop-target")) {
669
- clearDropTargets();
670
- col.classList.add("drop-target");
671
- }
908
+ if (!col)
909
+ return;
910
+ e.preventDefault();
911
+ e.dataTransfer.dropEffect = "move";
912
+ if (!col.classList.contains("drop-target")) {
913
+ clearDropTargets();
914
+ col.classList.add("drop-target");
672
915
  }
916
+ const body = e.target.closest?.(".card-group-body");
917
+ if (!body)
918
+ return marker.remove();
919
+ const anchor = dropAnchor(body, e.clientY);
920
+ if (!anchor)
921
+ return body.append(marker);
922
+ const el = body.querySelector(\`[data-task-id="\${anchor.id}"]\`);
923
+ el.insertAdjacentElement(anchor.where === "before" ? "beforebegin" : "afterend", marker);
673
924
  });
674
925
  document.addEventListener("drop", (e) => {
675
926
  const col = e.target.closest?.("section[data-col]");
676
927
  if (col && dragId) {
677
928
  e.preventDefault();
929
+ const body = e.target.closest?.(".card-group-body");
930
+ const patch = {};
678
931
  if (col.dataset.col !== dragStatus)
679
- patchItem(dragId, { status: col.dataset.col }, dragRev);
932
+ patch.status = col.dataset.col;
933
+ if (body && body.dataset.dropEpic !== dragEpic)
934
+ patch.epic = body.dataset.dropEpic;
935
+ if (body) {
936
+ const anchor = dropAnchor(body, e.clientY);
937
+ if (anchor && anchor.id !== dragId)
938
+ patch[anchor.where] = anchor.id;
939
+ }
940
+ if (Object.keys(patch).length)
941
+ patchItem(dragId, patch, dragRev);
680
942
  }
681
943
  clearDropTargets();
682
944
  dragId = null;
@@ -687,6 +949,7 @@ var board_default = `(() => {
687
949
  });
688
950
  connectLive(() => void refreshApp(), 120);
689
951
  applySidebarState();
952
+ applyGroupFolds();
690
953
  if (location.hash.length > 1)
691
954
  openSheet(location.hash.slice(1));
692
955
  })();
@@ -1100,11 +1363,177 @@ var docs_default = `(() => {
1100
1363
  const el = document.activeElement;
1101
1364
  return e.key === "/" && !e.metaKey && !e.ctrlKey && !e.altKey && !/INPUT|TEXTAREA|SELECT/.test(el?.tagName ?? "") && !el?.isContentEditable;
1102
1365
  }
1366
+ function initCopyCode() {
1367
+ document.addEventListener("click", (e) => {
1368
+ const btn = e.target.closest(".copy-code");
1369
+ const pre = btn?.previousElementSibling;
1370
+ if (!btn || !(pre instanceof HTMLElement))
1371
+ return;
1372
+ copyText(pre.innerText).then((ok) => {
1373
+ showToast(ok ? "Copied" : "Copy failed");
1374
+ if (!ok)
1375
+ return;
1376
+ btn.classList.add("copied");
1377
+ setTimeout(() => btn.classList.remove("copied"), 1200);
1378
+ });
1379
+ });
1380
+ }
1381
+ async function copyText(text) {
1382
+ try {
1383
+ if (navigator.clipboard) {
1384
+ await navigator.clipboard.writeText(text);
1385
+ return true;
1386
+ }
1387
+ } catch {}
1388
+ const ta = document.createElement("textarea");
1389
+ ta.value = text;
1390
+ ta.setAttribute("readonly", "");
1391
+ ta.className = "fixed opacity-0";
1392
+ document.body.append(ta);
1393
+ ta.select();
1394
+ const ok = document.execCommand("copy");
1395
+ ta.remove();
1396
+ return ok;
1397
+ }
1398
+ var editing = null;
1399
+ var isEditing = () => editing !== null;
1400
+ var SAVE_DEBOUNCE_MS = 700;
1401
+ async function startEditing(target) {
1402
+ if (editing)
1403
+ return;
1404
+ const loaded = await target.load().catch(() => null);
1405
+ if (!loaded)
1406
+ return showToast("Could not read the source");
1407
+ editing = target;
1408
+ let rev = loaded.rev;
1409
+ let saved = loaded.body;
1410
+ let timer;
1411
+ const box = document.createElement("div");
1412
+ box.className = "md-editor";
1413
+ const area = document.createElement("textarea");
1414
+ area.className = "md-editor-area";
1415
+ area.value = loaded.body;
1416
+ area.spellcheck = false;
1417
+ const status = document.createElement("div");
1418
+ status.className = "md-editor-status";
1419
+ status.textContent = "Editing — ⌘S to save, Esc to finish";
1420
+ box.append(area, status);
1421
+ target.view.hidden = true;
1422
+ target.view.insertAdjacentElement("afterend", box);
1423
+ const fit = () => {
1424
+ area.style.height = "auto";
1425
+ area.style.height = area.scrollHeight + "px";
1426
+ };
1427
+ fit();
1428
+ area.focus();
1429
+ let inflight = null;
1430
+ async function flush() {
1431
+ clearTimeout(timer);
1432
+ while (inflight)
1433
+ await inflight.catch(() => false);
1434
+ if (area.value === saved)
1435
+ return true;
1436
+ const run = save();
1437
+ inflight = run;
1438
+ try {
1439
+ return await run;
1440
+ } finally {
1441
+ if (inflight === run)
1442
+ inflight = null;
1443
+ }
1444
+ }
1445
+ async function save() {
1446
+ const sending = area.value;
1447
+ status.textContent = "Saving…";
1448
+ const res = await target.save(sending, rev);
1449
+ if (res.stale) {
1450
+ status.textContent = "Changed on disk elsewhere — copy your text, then reopen";
1451
+ status.classList.add("is-stale");
1452
+ return false;
1453
+ }
1454
+ if (!res.ok) {
1455
+ status.textContent = "Save failed";
1456
+ status.classList.add("is-stale");
1457
+ return false;
1458
+ }
1459
+ saved = sending;
1460
+ rev = res.rev ?? rev;
1461
+ status.classList.remove("is-stale");
1462
+ status.textContent = area.value === saved ? "Saved" : "Editing…";
1463
+ return true;
1464
+ }
1465
+ area.addEventListener("input", () => {
1466
+ fit();
1467
+ status.textContent = "Editing…";
1468
+ clearTimeout(timer);
1469
+ timer = setTimeout(() => void flush(), SAVE_DEBOUNCE_MS);
1470
+ });
1471
+ async function stop() {
1472
+ if (!editing)
1473
+ return;
1474
+ if (!await flush())
1475
+ return;
1476
+ editing = null;
1477
+ pendingStop = null;
1478
+ box.remove();
1479
+ target.view.hidden = false;
1480
+ target.done();
1481
+ }
1482
+ area.addEventListener("keydown", (e) => {
1483
+ if ((e.metaKey || e.ctrlKey) && e.key === "s") {
1484
+ e.preventDefault();
1485
+ flush();
1486
+ }
1487
+ if (e.key === "Escape") {
1488
+ e.preventDefault();
1489
+ stop();
1490
+ }
1491
+ });
1492
+ area.addEventListener("blur", () => void flush());
1493
+ pendingStop = stop;
1494
+ }
1495
+ var pendingStop = null;
1496
+ function stopEditing() {
1497
+ pendingStop?.();
1498
+ }
1499
+ function initBodyEditor() {
1500
+ document.addEventListener("click", (e) => {
1501
+ if (!e.target.closest?.("[data-edit-body]"))
1502
+ return;
1503
+ const root = sheetContent.querySelector(".detail-root");
1504
+ const view = sheetContent.querySelector(".detail-body");
1505
+ if (!root || !view || isEditing())
1506
+ return;
1507
+ const id = root.dataset.id;
1508
+ startEditing({
1509
+ view,
1510
+ load: () => fetch("/api/items/" + encodeURIComponent(id)).then((r) => r.ok ? r.json() : Promise.reject()).then((t) => ({ body: t.body, rev: t.rev })),
1511
+ save: async (body, rev) => {
1512
+ const res = await fetch("/api/items/" + encodeURIComponent(id), {
1513
+ method: "PATCH",
1514
+ headers: { "content-type": "application/json" },
1515
+ body: JSON.stringify({ body, rev })
1516
+ });
1517
+ if (res.status === 409)
1518
+ return { ok: false, stale: true };
1519
+ if (!res.ok)
1520
+ return { ok: false };
1521
+ return { ok: true, rev: (await res.json()).rev };
1522
+ },
1523
+ done: () => {
1524
+ clearPaneCache();
1525
+ openSheet(id);
1526
+ }
1527
+ });
1528
+ });
1529
+ }
1103
1530
  function initChrome() {
1104
1531
  initResizer();
1105
1532
  initTheme();
1106
1533
  initSheet();
1107
1534
  initCheckboxes();
1535
+ initCopyCode();
1536
+ initBodyEditor();
1108
1537
  }
1109
1538
 
1110
1539
  // src/client/docs.ts
@@ -1311,8 +1740,42 @@ var docs_default = `(() => {
1311
1740
  refreshing = false;
1312
1741
  }
1313
1742
  }
1314
- var refreshDoc = () => loadDoc(location.pathname + location.search, { conditional: true });
1743
+ var refreshDoc = () => isEditing() ? Promise.resolve() : loadDoc(location.pathname + location.search, { conditional: true });
1315
1744
  hooks.refreshView = refreshDoc;
1745
+ function currentDocFile() {
1746
+ return new URLSearchParams(location.search).get("file") || "README.md";
1747
+ }
1748
+ function editDoc() {
1749
+ const view = document.querySelector("#doc-content .doc-body");
1750
+ if (!view)
1751
+ return;
1752
+ const file = currentDocFile();
1753
+ const url = "/api/docs?file=" + encodeURIComponent(file);
1754
+ startEditing({
1755
+ view,
1756
+ load: () => fetch(url).then((r) => r.ok ? r.json() : Promise.reject()),
1757
+ save: async (body, rev) => {
1758
+ const res = await fetch(url, {
1759
+ method: "PATCH",
1760
+ headers: { "content-type": "application/json" },
1761
+ body: JSON.stringify({ body, rev })
1762
+ });
1763
+ if (res.status === 409)
1764
+ return { ok: false, stale: true };
1765
+ if (!res.ok)
1766
+ return { ok: false };
1767
+ return { ok: true, rev: (await res.json()).rev };
1768
+ },
1769
+ done: () => {
1770
+ etag = "";
1771
+ loadDoc(location.pathname + location.search);
1772
+ }
1773
+ });
1774
+ }
1775
+ document.addEventListener("click", (e) => {
1776
+ if (e.target.closest("#doc-edit"))
1777
+ editDoc();
1778
+ });
1316
1779
  function markActiveDoc() {
1317
1780
  const current = new URLSearchParams(location.search).get("file") || "README.md";
1318
1781
  for (const a of document.querySelectorAll("#doc-sidebar .doc-link")) {
@@ -1333,6 +1796,7 @@ var docs_default = `(() => {
1333
1796
  if (here === location.pathname + location.search)
1334
1797
  return;
1335
1798
  e.preventDefault();
1799
+ stopEditing();
1336
1800
  loadDoc(here, { push: true });
1337
1801
  });
1338
1802
  window.addEventListener("popstate", () => void loadDoc(location.pathname + location.search));
@@ -1516,6 +1980,13 @@ var docs_default = `(() => {
1516
1980
  if (slashShouldFocus(e)) {
1517
1981
  e.preventDefault();
1518
1982
  searchEl.focus();
1983
+ return;
1984
+ }
1985
+ const el = e.target;
1986
+ const typing = el.matches("input, textarea, select") || el.isContentEditable;
1987
+ if (e.key === "e" && !typing && !e.metaKey && !e.ctrlKey && !e.altKey && !isEditing()) {
1988
+ e.preventDefault();
1989
+ editDoc();
1519
1990
  }
1520
1991
  });
1521
1992
  if (localStorage.getItem("docsSidebarCollapsed") === "1")
@@ -1935,11 +2406,177 @@ var graph_default = `(() => {
1935
2406
  const el = document.activeElement;
1936
2407
  return e.key === "/" && !e.metaKey && !e.ctrlKey && !e.altKey && !/INPUT|TEXTAREA|SELECT/.test(el?.tagName ?? "") && !el?.isContentEditable;
1937
2408
  }
2409
+ function initCopyCode() {
2410
+ document.addEventListener("click", (e) => {
2411
+ const btn = e.target.closest(".copy-code");
2412
+ const pre = btn?.previousElementSibling;
2413
+ if (!btn || !(pre instanceof HTMLElement))
2414
+ return;
2415
+ copyText(pre.innerText).then((ok) => {
2416
+ showToast(ok ? "Copied" : "Copy failed");
2417
+ if (!ok)
2418
+ return;
2419
+ btn.classList.add("copied");
2420
+ setTimeout(() => btn.classList.remove("copied"), 1200);
2421
+ });
2422
+ });
2423
+ }
2424
+ async function copyText(text) {
2425
+ try {
2426
+ if (navigator.clipboard) {
2427
+ await navigator.clipboard.writeText(text);
2428
+ return true;
2429
+ }
2430
+ } catch {}
2431
+ const ta = document.createElement("textarea");
2432
+ ta.value = text;
2433
+ ta.setAttribute("readonly", "");
2434
+ ta.className = "fixed opacity-0";
2435
+ document.body.append(ta);
2436
+ ta.select();
2437
+ const ok = document.execCommand("copy");
2438
+ ta.remove();
2439
+ return ok;
2440
+ }
2441
+ var editing = null;
2442
+ var isEditing = () => editing !== null;
2443
+ var SAVE_DEBOUNCE_MS = 700;
2444
+ async function startEditing(target) {
2445
+ if (editing)
2446
+ return;
2447
+ const loaded = await target.load().catch(() => null);
2448
+ if (!loaded)
2449
+ return showToast("Could not read the source");
2450
+ editing = target;
2451
+ let rev = loaded.rev;
2452
+ let saved = loaded.body;
2453
+ let timer;
2454
+ const box = document.createElement("div");
2455
+ box.className = "md-editor";
2456
+ const area = document.createElement("textarea");
2457
+ area.className = "md-editor-area";
2458
+ area.value = loaded.body;
2459
+ area.spellcheck = false;
2460
+ const status = document.createElement("div");
2461
+ status.className = "md-editor-status";
2462
+ status.textContent = "Editing — ⌘S to save, Esc to finish";
2463
+ box.append(area, status);
2464
+ target.view.hidden = true;
2465
+ target.view.insertAdjacentElement("afterend", box);
2466
+ const fit = () => {
2467
+ area.style.height = "auto";
2468
+ area.style.height = area.scrollHeight + "px";
2469
+ };
2470
+ fit();
2471
+ area.focus();
2472
+ let inflight = null;
2473
+ async function flush() {
2474
+ clearTimeout(timer);
2475
+ while (inflight)
2476
+ await inflight.catch(() => false);
2477
+ if (area.value === saved)
2478
+ return true;
2479
+ const run = save();
2480
+ inflight = run;
2481
+ try {
2482
+ return await run;
2483
+ } finally {
2484
+ if (inflight === run)
2485
+ inflight = null;
2486
+ }
2487
+ }
2488
+ async function save() {
2489
+ const sending = area.value;
2490
+ status.textContent = "Saving…";
2491
+ const res = await target.save(sending, rev);
2492
+ if (res.stale) {
2493
+ status.textContent = "Changed on disk elsewhere — copy your text, then reopen";
2494
+ status.classList.add("is-stale");
2495
+ return false;
2496
+ }
2497
+ if (!res.ok) {
2498
+ status.textContent = "Save failed";
2499
+ status.classList.add("is-stale");
2500
+ return false;
2501
+ }
2502
+ saved = sending;
2503
+ rev = res.rev ?? rev;
2504
+ status.classList.remove("is-stale");
2505
+ status.textContent = area.value === saved ? "Saved" : "Editing…";
2506
+ return true;
2507
+ }
2508
+ area.addEventListener("input", () => {
2509
+ fit();
2510
+ status.textContent = "Editing…";
2511
+ clearTimeout(timer);
2512
+ timer = setTimeout(() => void flush(), SAVE_DEBOUNCE_MS);
2513
+ });
2514
+ async function stop() {
2515
+ if (!editing)
2516
+ return;
2517
+ if (!await flush())
2518
+ return;
2519
+ editing = null;
2520
+ pendingStop = null;
2521
+ box.remove();
2522
+ target.view.hidden = false;
2523
+ target.done();
2524
+ }
2525
+ area.addEventListener("keydown", (e) => {
2526
+ if ((e.metaKey || e.ctrlKey) && e.key === "s") {
2527
+ e.preventDefault();
2528
+ flush();
2529
+ }
2530
+ if (e.key === "Escape") {
2531
+ e.preventDefault();
2532
+ stop();
2533
+ }
2534
+ });
2535
+ area.addEventListener("blur", () => void flush());
2536
+ pendingStop = stop;
2537
+ }
2538
+ var pendingStop = null;
2539
+ function stopEditing() {
2540
+ pendingStop?.();
2541
+ }
2542
+ function initBodyEditor() {
2543
+ document.addEventListener("click", (e) => {
2544
+ if (!e.target.closest?.("[data-edit-body]"))
2545
+ return;
2546
+ const root = sheetContent.querySelector(".detail-root");
2547
+ const view = sheetContent.querySelector(".detail-body");
2548
+ if (!root || !view || isEditing())
2549
+ return;
2550
+ const id = root.dataset.id;
2551
+ startEditing({
2552
+ view,
2553
+ load: () => fetch("/api/items/" + encodeURIComponent(id)).then((r) => r.ok ? r.json() : Promise.reject()).then((t) => ({ body: t.body, rev: t.rev })),
2554
+ save: async (body, rev) => {
2555
+ const res = await fetch("/api/items/" + encodeURIComponent(id), {
2556
+ method: "PATCH",
2557
+ headers: { "content-type": "application/json" },
2558
+ body: JSON.stringify({ body, rev })
2559
+ });
2560
+ if (res.status === 409)
2561
+ return { ok: false, stale: true };
2562
+ if (!res.ok)
2563
+ return { ok: false };
2564
+ return { ok: true, rev: (await res.json()).rev };
2565
+ },
2566
+ done: () => {
2567
+ clearPaneCache();
2568
+ openSheet(id);
2569
+ }
2570
+ });
2571
+ });
2572
+ }
1938
2573
  function initChrome() {
1939
2574
  initResizer();
1940
2575
  initTheme();
1941
2576
  initSheet();
1942
2577
  initCheckboxes();
2578
+ initCopyCode();
2579
+ initBodyEditor();
1943
2580
  }
1944
2581
 
1945
2582
  // src/client/graph.ts
@@ -2582,6 +3219,7 @@ function parseTask(file) {
2582
3219
  epic: fm.epic ?? "",
2583
3220
  status: fm.status ?? "backlog",
2584
3221
  priority: fm.priority ?? "P3",
3222
+ order: fm.order !== undefined && fm.order.trim() !== "" && Number.isFinite(Number(fm.order)) ? Number(fm.order) : null,
2585
3223
  depends_on: parseInlineList(fm.depends_on ?? ""),
2586
3224
  created: fm.created ?? "",
2587
3225
  body,
@@ -2657,6 +3295,8 @@ function setFrontmatterValue(raw, key, value) {
2657
3295
  function getFrontmatterValue(raw, key) {
2658
3296
  return parseFrontmatter(raw)?.fm[key] ?? "";
2659
3297
  }
3298
+ var FENCE = /^([ \t]*)```/;
3299
+ var COPY_BUTTON = `<button type="button" class="copy-code" aria-label="Copy code" title="Copy">` + `<svg width="13" height="13" viewBox="0 0 16 16" fill="none" aria-hidden="true">` + `<rect x="5.5" y="5.5" width="8" height="8" rx="1.5" stroke="currentColor" stroke-width="1.3"/>` + `<path d="M10.5 3.5 A1.5 1.5 0 0 0 9 2.5 H4 A1.5 1.5 0 0 0 2.5 4 v5 a1.5 1.5 0 0 0 1 1.4" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/>` + `</svg></button>`;
2660
3300
  function toggleCheckboxLine(raw, index, checked) {
2661
3301
  const m = raw.match(/^(---\n[\s\S]*?\n---\n?)([\s\S]*)$/);
2662
3302
  const head = m ? m[1] : "";
@@ -2665,7 +3305,7 @@ function toggleCheckboxLine(raw, index, checked) {
2665
3305
  let inCode = false;
2666
3306
  let count = 0;
2667
3307
  for (let i = 0;i < lines.length; i++) {
2668
- if (lines[i].startsWith("```")) {
3308
+ if (FENCE.test(lines[i])) {
2669
3309
  inCode = !inCode;
2670
3310
  continue;
2671
3311
  }
@@ -2760,6 +3400,7 @@ function mdToHtml(md, opts = {}) {
2760
3400
  const out = [];
2761
3401
  let inCode = false;
2762
3402
  let codeLang = "";
3403
+ let codeIndent = 0;
2763
3404
  let inList = false;
2764
3405
  let cbIndex = 0;
2765
3406
  let tableBuf = [];
@@ -2785,21 +3426,23 @@ function mdToHtml(md, opts = {}) {
2785
3426
  }
2786
3427
  };
2787
3428
  for (const line of lines) {
2788
- if (line.startsWith("```")) {
3429
+ const fence = line.match(FENCE);
3430
+ if (fence) {
2789
3431
  flushTable();
2790
3432
  closeList();
2791
3433
  if (!inCode) {
2792
- codeLang = line.slice(3).trim();
3434
+ codeIndent = fence[1].length;
3435
+ codeLang = line.slice(fence[0].length).trim();
2793
3436
  inCode = true;
2794
- out.push(opts.docMode && codeLang === "mermaid" ? '<div class="mermaid">' : "<pre>");
3437
+ out.push(opts.docMode && codeLang === "mermaid" ? '<div class="mermaid">' : `<div class="codeblock"><pre>`);
2795
3438
  } else {
2796
- out.push(opts.docMode && codeLang === "mermaid" ? "</div>" : "</pre>");
3439
+ out.push(opts.docMode && codeLang === "mermaid" ? "</div>" : `</pre>${COPY_BUTTON}</div>`);
2797
3440
  inCode = false;
2798
3441
  }
2799
3442
  continue;
2800
3443
  }
2801
3444
  if (inCode) {
2802
- out.push(line);
3445
+ out.push(line.slice(0, codeIndent).trim() ? line : line.slice(codeIndent));
2803
3446
  continue;
2804
3447
  }
2805
3448
  if (/^\s*\|/.test(line)) {
@@ -2853,7 +3496,7 @@ function mdToHtml(md, opts = {}) {
2853
3496
  flushTable();
2854
3497
  closeList();
2855
3498
  if (inCode)
2856
- out.push("</pre>");
3499
+ out.push(`</pre>${COPY_BUTTON}</div>`);
2857
3500
  return out.join(`
2858
3501
  `);
2859
3502
  }
@@ -2915,32 +3558,30 @@ function normalizeRel(baseDir, href) {
2915
3558
  }
2916
3559
  var buildBacklinks = memoByVersion(() => {
2917
3560
  const map = new Map;
2918
- for (const g of listDocs()) {
2919
- for (const rel of g.files) {
2920
- const parsed = parseCached("fm", join(PLANNER_DIR, ...rel.split("/")), (raw) => parseFrontmatter(raw) ?? { fm: {}, body: raw });
2921
- if (!parsed)
2922
- continue;
2923
- const { fm, body } = parsed;
2924
- const deps = new Set(parseInlineList(fm.depends_on ?? ""));
2925
- const targets = new Set;
2926
- for (const m of body.matchAll(/\b(ST-\d{3}|E\d{2})\b/g))
2927
- targets.add(m[1]);
2928
- if (fm.epic)
2929
- targets.add(fm.epic);
2930
- for (const d of deps)
2931
- targets.add(d);
2932
- const baseDir = dirname(rel) === "." ? "" : dirname(rel);
2933
- for (const m of body.matchAll(/\]\(([^)\s]+\.md)\)/g)) {
2934
- if (!/^https?:/.test(m[1]))
2935
- targets.add(normalizeRel(baseDir, m[1]));
2936
- }
2937
- targets.delete(fm.id ?? "");
2938
- targets.delete(rel);
2939
- for (const t of targets) {
2940
- const arr = map.get(t) ?? [];
2941
- arr.push({ file: rel, id: fm.id, title: fm.title ?? basename(rel), isDep: deps.has(t) });
2942
- map.set(t, arr);
2943
- }
3561
+ for (const rel of allDocFiles()) {
3562
+ const parsed = parseCached("fm", join(PLANNER_DIR, ...rel.split("/")), (raw) => parseFrontmatter(raw) ?? { fm: {}, body: raw });
3563
+ if (!parsed)
3564
+ continue;
3565
+ const { fm, body } = parsed;
3566
+ const deps = new Set(parseInlineList(fm.depends_on ?? ""));
3567
+ const targets = new Set;
3568
+ for (const m of body.matchAll(/\b(ST-\d{3}|E\d{2})\b/g))
3569
+ targets.add(m[1]);
3570
+ if (fm.epic)
3571
+ targets.add(fm.epic);
3572
+ for (const d of deps)
3573
+ targets.add(d);
3574
+ const baseDir = dirname(rel) === "." ? "" : dirname(rel);
3575
+ for (const m of body.matchAll(/\]\(([^)\s]+\.md)\)/g)) {
3576
+ if (!/^https?:/.test(m[1]))
3577
+ targets.add(normalizeRel(baseDir, m[1]));
3578
+ }
3579
+ targets.delete(fm.id ?? "");
3580
+ targets.delete(rel);
3581
+ for (const t of targets) {
3582
+ const arr = map.get(t) ?? [];
3583
+ arr.push({ file: rel, id: fm.id, title: fm.title ?? basename(rel), isDep: deps.has(t) });
3584
+ map.set(t, arr);
2944
3585
  }
2945
3586
  }
2946
3587
  return map;
@@ -2948,14 +3589,12 @@ var buildBacklinks = memoByVersion(() => {
2948
3589
  var buildGraph = memoByVersion(() => {
2949
3590
  const nodes = new Map;
2950
3591
  const byItemId = new Map;
2951
- for (const g of listDocs()) {
2952
- for (const rel of g.files) {
2953
- const parsed = parseCached("fm", join(PLANNER_DIR, ...rel.split("/")), (raw) => parseFrontmatter(raw) ?? { fm: {}, body: raw });
2954
- const fm = parsed?.fm ?? {};
2955
- nodes.set(rel, { id: rel, label: fm.title || basename(rel).replace(/\.md$/i, ""), group: g.group, deg: 0 });
2956
- if (fm.id)
2957
- byItemId.set(fm.id, rel);
2958
- }
3592
+ for (const rel of allDocFiles()) {
3593
+ const parsed = parseCached("fm", join(PLANNER_DIR, ...rel.split("/")), (raw) => parseFrontmatter(raw) ?? { fm: {}, body: raw });
3594
+ const fm = parsed?.fm ?? {};
3595
+ nodes.set(rel, { id: rel, label: fm.title || basename(rel).replace(/\.md$/i, ""), group: docGroup(rel), deg: 0 });
3596
+ if (fm.id)
3597
+ byItemId.set(fm.id, rel);
2959
3598
  }
2960
3599
  const links = [];
2961
3600
  const seen = new Set;
@@ -2991,73 +3630,93 @@ function backlinksHtml(sources, heading, docMode) {
2991
3630
  return `<a href="${href}" class="${rowClass}">${inner}</a>`;
2992
3631
  }).join("");
2993
3632
  return `<div class="mt-6 border-t border-zinc-100 pt-4 dark:border-zinc-800">
2994
- <div class="mb-2 text-[11px] font-semibold uppercase tracking-wider text-zinc-400 dark:text-zinc-500">${heading} <span class="font-mono text-[10px] normal-case text-zinc-300 dark:text-zinc-600">${sources.length}</span></div>
2995
- <div class="space-y-1.5">${rows}</div>
3633
+ <div class="mb-2 text-[11px] font-semibold uppercase tracking-wider text-zinc-400 dark:text-zinc-500">${heading} <span class="font-mono text-[10px] normal-case text-zinc-300 dark:text-zinc-600">${sources.length}</span></div>
3634
+ <div class="space-y-1.5">${rows}</div>
2996
3635
  </div>`;
2997
3636
  }
2998
3637
  function cardHtml(t, byId) {
2999
3638
  const deps = t.depends_on.map((d) => depChip(d, byId.get(d)?.status === "done")).join("");
3000
3639
  const progress = t.subtasksTotal > 0 ? `<span class="card-progress" title="${t.subtasksDone} of ${t.subtasksTotal} subtasks done">
3001
- ${progressRing(t.subtasksDone, t.subtasksTotal)}
3002
- ${t.subtasksDone}/${t.subtasksTotal}</span>` : "";
3640
+ ${progressRing(t.subtasksDone, t.subtasksTotal)}
3641
+ ${t.subtasksDone}/${t.subtasksTotal}</span>` : "";
3003
3642
  const search = esc(`${t.id} ${t.title} ${t.epic} ${t.body}`.toLowerCase().replace(/\s+/g, " ").slice(0, 2000));
3004
3643
  return `<div role="button" tabindex="0" draggable="true" data-task-id="${esc(t.id)}" data-epic="${esc(t.epic)}" data-status="${esc(t.status)}" data-rev="${t.rev}" data-search="${search}"
3005
- class="task-card">
3006
- <div class="mb-1 flex items-center justify-between gap-2">
3007
- <span class="card-id">${esc(t.id)}</span>
3008
- ${priorityIcon(t.priority)}
3009
- </div>
3010
- <div class="card-title">${esc(t.title)}</div>
3011
- <div class="mt-2 flex flex-wrap items-center gap-1.5">
3012
- <button type="button" data-open="${esc(t.epic)}" class="epic-chip" title="open epic ${esc(t.epic)}">${esc(t.epic)}</button>
3013
- ${deps}${progress}
3014
- </div>
3644
+ class="task-card">
3645
+ <div class="card-move">
3646
+ <button type="button" data-move="up" tabindex="-1" aria-label="Move ${esc(t.id)} up" title="Move up (Alt+↑)">
3647
+ <svg width="11" height="11" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M4 10 8 6 12 10" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/></svg>
3648
+ </button>
3649
+ <button type="button" data-move="down" tabindex="-1" aria-label="Move ${esc(t.id)} down" title="Move down (Alt+↓)">
3650
+ <svg width="11" height="11" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M4 6 8 10 12 6" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/></svg>
3651
+ </button>
3652
+ </div>
3653
+ <div class="mb-1 flex items-center justify-between gap-2">
3654
+ <span class="card-id">${esc(t.id)}</span>
3655
+ ${priorityIcon(t.priority)}
3656
+ </div>
3657
+ <div class="card-title">${esc(t.title)}</div>
3658
+ <div class="mt-2 flex flex-wrap items-center gap-1.5">
3659
+ <button type="button" data-open="${esc(t.epic)}" class="epic-chip" title="open epic ${esc(t.epic)}">${esc(t.epic)}</button>
3660
+ ${deps}${progress}
3661
+ </div>
3015
3662
  </div>`;
3016
3663
  }
3017
3664
  function statusSelect(current) {
3018
3665
  const options = [...new Set([...settings.columns, current])].map((s) => `<option value="${esc(s)}" ${s === current ? "selected" : ""}>${esc(s.replace("-", " "))}</option>`).join("");
3019
3666
  return `<select data-status-select aria-label="Status"
3020
- class="cursor-pointer rounded border border-zinc-200 bg-transparent px-1 py-0.5 text-[12px] capitalize text-zinc-600 hover:border-zinc-300 focus:outline-none dark:border-zinc-700 dark:text-zinc-300 dark:hover:border-zinc-600 dark:[&>option]:bg-zinc-900">${options}</select>`;
3667
+ class="cursor-pointer rounded border border-zinc-200 bg-transparent px-1 py-0.5 text-[12px] capitalize text-zinc-600 hover:border-zinc-300 focus:outline-none dark:border-zinc-700 dark:text-zinc-300 dark:hover:border-zinc-600 dark:[&>option]:bg-zinc-900">${options}</select>`;
3021
3668
  }
3022
3669
  function taskDetailHtml(t, byId, backlinks) {
3023
3670
  const deps = t.depends_on.length ? t.depends_on.map((d) => depChip(d, byId.get(d)?.status === "done")).join(" ") : '<span class="text-zinc-400 dark:text-zinc-500">none</span>';
3024
3671
  return `<div class="detail-root" data-id="${esc(t.id)}" data-rev="${t.rev}">
3025
- <div class="flex items-center gap-2 text-[12px] text-zinc-500 dark:text-zinc-400">
3026
- ${statusIcon(t.status)}${statusSelect(t.status)}
3027
- <span class="font-mono text-[11px]">${esc(t.id)}</span>
3028
- <span class="ml-auto mr-16">${priorityIcon(t.priority)}</span>
3029
- </div>
3030
- <h2 class="mt-2 text-[16px] font-semibold leading-snug text-zinc-900 dark:text-zinc-50">${esc(t.title)}</h2>
3031
- <dl class="mt-4 grid grid-cols-[90px_1fr] items-baseline gap-y-1.5 border-y border-zinc-100 py-3 text-[12px] dark:border-zinc-800">
3032
- <dt class="text-zinc-400 dark:text-zinc-500">Epic</dt><dd><button type="button" data-open="${esc(t.epic)}" class="font-mono text-accent-600 hover:underline dark:text-accent-400">${esc(t.epic)}</button></dd>
3033
- <dt class="text-zinc-400 dark:text-zinc-500">Depends on</dt><dd class="flex flex-wrap gap-1">${deps}</dd>
3034
- <dt class="text-zinc-400 dark:text-zinc-500">Created</dt><dd class="text-zinc-700 dark:text-zinc-300">${esc(t.created)}</dd>
3035
- <dt class="text-zinc-400 dark:text-zinc-500">File</dt><dd class="font-mono text-[11px] text-zinc-500 dark:text-zinc-400"><a class="hover:underline" href="/docs?file=tasks/${esc(t.file)}">tasks/${esc(t.file)}</a></dd>
3036
- </dl>
3037
- <div class="detail-body mt-4">${mdToHtml(t.body)}</div>
3038
- ${backlinksHtml(backlinks.get(t.id) ?? [], "Referenced by", false)}
3672
+ <div class="flex items-center gap-2 text-[12px] text-zinc-500 dark:text-zinc-400">
3673
+ ${statusIcon(t.status)}${statusSelect(t.status)}
3674
+ <span class="font-mono text-[11px]">${esc(t.id)}</span>
3675
+ <span class="ml-auto mr-16">${priorityIcon(t.priority)}</span>
3676
+ </div>
3677
+ <h2 class="mt-2 text-[16px] font-semibold leading-snug text-zinc-900 dark:text-zinc-50">${esc(t.title)}</h2>
3678
+ <dl class="mt-4 grid grid-cols-[90px_1fr] items-baseline gap-y-1.5 border-y border-zinc-100 py-3 text-[12px] dark:border-zinc-800">
3679
+ <dt class="text-zinc-400 dark:text-zinc-500">Epic</dt><dd><button type="button" data-open="${esc(t.epic)}" class="font-mono text-accent-600 hover:underline dark:text-accent-400">${esc(t.epic)}</button></dd>
3680
+ <dt class="text-zinc-400 dark:text-zinc-500">Depends on</dt><dd class="flex flex-wrap gap-1">${deps}</dd>
3681
+ <dt class="text-zinc-400 dark:text-zinc-500">Created</dt><dd class="text-zinc-700 dark:text-zinc-300">${esc(t.created)}</dd>
3682
+ <dt class="text-zinc-400 dark:text-zinc-500">File</dt><dd class="font-mono text-[11px] text-zinc-500 dark:text-zinc-400"><a class="hover:underline" href="/docs?file=tasks/${esc(t.file)}">tasks/${esc(t.file)}</a></dd>
3683
+ </dl>
3684
+ <div class="mt-4 flex items-center gap-2">
3685
+ <button type="button" class="detail-edit" data-edit-body title="Edit body (e)">
3686
+ <svg width="12" height="12" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M11.5 2.5 13.5 4.5 5.5 12.5 2.5 13.5 3.5 10.5 Z" stroke="currentColor" stroke-width="1.3" stroke-linejoin="round"/></svg>
3687
+ <span>Edit</span>
3688
+ </button>
3689
+ </div>
3690
+ <div class="detail-body mt-2">${mdToHtml(t.body)}</div>
3691
+ ${backlinksHtml(backlinks.get(t.id) ?? [], "Referenced by", false)}
3039
3692
  </div>`;
3040
3693
  }
3041
3694
  function epicDetailHtml(e, tasks, backlinks) {
3042
3695
  const stories = tasks.filter((t) => t.epic === e.id);
3043
3696
  const storyList = stories.length ? stories.map((t) => `<button type="button" data-open="${esc(t.id)}" class="backlink-row">
3044
- ${statusIcon(t.status, 12)}
3045
- <span class="font-mono text-[11px] text-zinc-400 dark:text-zinc-500">${esc(t.id)}</span>
3046
- <span class="truncate text-[12.5px] text-zinc-700 dark:text-zinc-300">${esc(t.title)}</span>
3047
- </button>`).join("") : '<p class="text-[12px] text-zinc-400">No stories yet.</p>';
3697
+ ${statusIcon(t.status, 12)}
3698
+ <span class="font-mono text-[11px] text-zinc-400 dark:text-zinc-500">${esc(t.id)}</span>
3699
+ <span class="truncate text-[12.5px] text-zinc-700 dark:text-zinc-300">${esc(t.title)}</span>
3700
+ </button>`).join("") : '<p class="text-[12px] text-zinc-400">No stories yet.</p>';
3048
3701
  const done = stories.filter((t) => t.status === "done").length;
3049
3702
  return `<div class="detail-root" data-id="${esc(e.id)}" data-rev="${e.rev}">
3050
- <div class="flex items-center gap-2 text-[12px] text-zinc-500 dark:text-zinc-400">
3051
- <span class="rounded bg-zinc-100 px-1.5 py-px text-[10px] font-medium uppercase tracking-wide text-zinc-500 dark:bg-zinc-800 dark:text-zinc-400">Epic</span>
3052
- <span class="font-mono text-[11px]">${esc(e.id)}</span>
3053
- <span class="text-zinc-300 dark:text-zinc-600">&middot;</span>
3054
- <span>${esc(e.milestone)}</span>
3055
- <span class="ml-auto mr-16 inline-flex items-center gap-1.5 text-zinc-400 dark:text-zinc-500">${progressRing(done, stories.length)}<span class="font-mono text-[11px]">${done}/${stories.length} done</span></span>
3056
- </div>
3057
- <h2 class="mt-2 text-[16px] font-semibold leading-snug text-zinc-900 dark:text-zinc-50">${esc(e.title)}</h2>
3058
- <div class="mt-4 space-y-1.5">${storyList}</div>
3059
- <div class="detail-body mt-5 border-t border-zinc-100 pt-4 dark:border-zinc-800">${mdToHtml(e.body)}</div>
3060
- ${backlinksHtml((backlinks.get(e.id) ?? []).filter((s) => !stories.some((t) => t.id === s.id)), "Referenced by", false)}
3703
+ <div class="flex items-center gap-2 text-[12px] text-zinc-500 dark:text-zinc-400">
3704
+ <span class="rounded bg-zinc-100 px-1.5 py-px text-[10px] font-medium uppercase tracking-wide text-zinc-500 dark:bg-zinc-800 dark:text-zinc-400">Epic</span>
3705
+ <span class="font-mono text-[11px]">${esc(e.id)}</span>
3706
+ <span class="text-zinc-300 dark:text-zinc-600">&middot;</span>
3707
+ <span>${esc(e.milestone)}</span>
3708
+ <span class="ml-auto mr-16 inline-flex items-center gap-1.5 text-zinc-400 dark:text-zinc-500">${progressRing(done, stories.length)}<span class="font-mono text-[11px]">${done}/${stories.length} done</span></span>
3709
+ </div>
3710
+ <h2 class="mt-2 text-[16px] font-semibold leading-snug text-zinc-900 dark:text-zinc-50">${esc(e.title)}</h2>
3711
+ <div class="mt-4 space-y-1.5">${storyList}</div>
3712
+ <div class="mt-5 flex items-center gap-2 border-t border-zinc-100 pt-4 dark:border-zinc-800">
3713
+ <button type="button" class="detail-edit" data-edit-body title="Edit body (e)">
3714
+ <svg width="12" height="12" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M11.5 2.5 13.5 4.5 5.5 12.5 2.5 13.5 3.5 10.5 Z" stroke="currentColor" stroke-width="1.3" stroke-linejoin="round"/></svg>
3715
+ <span>Edit</span>
3716
+ </button>
3717
+ </div>
3718
+ <div class="detail-body mt-2">${mdToHtml(e.body)}</div>
3719
+ ${backlinksHtml((backlinks.get(e.id) ?? []).filter((s) => !stories.some((t) => t.id === s.id)), "Referenced by", false)}
3061
3720
  </div>`;
3062
3721
  }
3063
3722
  function itemPaneHtml(id) {
@@ -3074,24 +3733,24 @@ function sidebarHtml(epics, tasks) {
3074
3733
  const stories = tasks.filter((t) => t.epic === e.id);
3075
3734
  const done = stories.filter((t) => t.status === "done").length;
3076
3735
  return `<div role="button" tabindex="0" data-filter-epic="${esc(e.id)}" class="epic-row group" title="${esc(e.title)}: click to filter the board">
3077
- <span class="text-zinc-400 dark:text-zinc-500">${progressRing(done, stories.length)}</span>
3078
- <span class="min-w-0 flex-1 truncate text-[12.5px] text-zinc-700 dark:text-zinc-300"><span class="font-mono text-[10.5px] text-zinc-400 dark:text-zinc-500">${esc(e.id)}</span> ${esc(e.title)}</span>
3079
- <span class="font-mono text-[10px] text-zinc-400 dark:text-zinc-600">${done}/${stories.length}</span>
3080
- <button type="button" data-open="${esc(e.id)}" aria-label="Open epic ${esc(e.id)}"
3081
- class="rounded p-0.5 text-zinc-300 opacity-0 transition-opacity hover:bg-zinc-200 hover:text-zinc-600 group-hover:opacity-100 focus-visible:opacity-100 dark:text-zinc-600 dark:hover:bg-zinc-700 dark:hover:text-zinc-300">
3082
- <svg width="11" height="11" viewBox="0 0 12 12" fill="none" aria-hidden="true"><path d="M4.5 2.5 H9.5 V7.5 M9.5 2.5 L2.5 9.5" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>
3083
- </button>
3084
- </div>`;
3736
+ <span class="text-zinc-400 dark:text-zinc-500">${progressRing(done, stories.length)}</span>
3737
+ <span class="min-w-0 flex-1 truncate text-[12.5px] text-zinc-700 dark:text-zinc-300"><span class="font-mono text-[10.5px] text-zinc-400 dark:text-zinc-500">${esc(e.id)}</span> ${esc(e.title)}</span>
3738
+ <span class="font-mono text-[10px] text-zinc-400 dark:text-zinc-600">${done}/${stories.length}</span>
3739
+ <button type="button" data-open="${esc(e.id)}" aria-label="Open epic ${esc(e.id)}"
3740
+ class="rounded p-0.5 text-zinc-300 opacity-0 transition-opacity hover:bg-zinc-200 hover:text-zinc-600 group-hover:opacity-100 focus-visible:opacity-100 dark:text-zinc-600 dark:hover:bg-zinc-700 dark:hover:text-zinc-300">
3741
+ <svg width="11" height="11" viewBox="0 0 12 12" fill="none" aria-hidden="true"><path d="M4.5 2.5 H9.5 V7.5 M9.5 2.5 L2.5 9.5" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>
3742
+ </button>
3743
+ </div>`;
3085
3744
  }).join("");
3086
3745
  return `<aside id="sidebar" class="hidden w-60 shrink-0 flex-col gap-0.5 overflow-y-auto border-r border-zinc-200 bg-white px-2.5 py-3 dark:border-zinc-800 dark:bg-zinc-950 md:flex">
3087
- <div class="mb-1 px-2 text-[10.5px] font-semibold uppercase tracking-wider text-zinc-400 dark:text-zinc-500">Epics</div>
3088
- <div role="button" tabindex="0" data-filter-epic=""
3089
- class="epic-row epic-active">
3090
- <svg width="12" height="12" viewBox="0 0 12 12" fill="none" aria-hidden="true" class="text-zinc-400"><rect x="1" y="1" width="4.4" height="4.4" rx="1" stroke="currentColor" stroke-width="1.2"/><rect x="6.6" y="1" width="4.4" height="4.4" rx="1" stroke="currentColor" stroke-width="1.2"/><rect x="1" y="6.6" width="4.4" height="4.4" rx="1" stroke="currentColor" stroke-width="1.2"/><rect x="6.6" y="6.6" width="4.4" height="4.4" rx="1" stroke="currentColor" stroke-width="1.2"/></svg>
3091
- <span class="flex-1 text-[12.5px] text-zinc-700 dark:text-zinc-300">All epics</span>
3092
- <span class="font-mono text-[10px] text-zinc-400 dark:text-zinc-600">${tasks.length}</span>
3093
- </div>
3094
- ${rows}
3746
+ <div class="mb-1 px-2 text-[10.5px] font-semibold uppercase tracking-wider text-zinc-400 dark:text-zinc-500">Epics</div>
3747
+ <div role="button" tabindex="0" data-filter-epic=""
3748
+ class="epic-row epic-active">
3749
+ <svg width="12" height="12" viewBox="0 0 12 12" fill="none" aria-hidden="true" class="text-zinc-400"><rect x="1" y="1" width="4.4" height="4.4" rx="1" stroke="currentColor" stroke-width="1.2"/><rect x="6.6" y="1" width="4.4" height="4.4" rx="1" stroke="currentColor" stroke-width="1.2"/><rect x="1" y="6.6" width="4.4" height="4.4" rx="1" stroke="currentColor" stroke-width="1.2"/><rect x="6.6" y="6.6" width="4.4" height="4.4" rx="1" stroke="currentColor" stroke-width="1.2"/></svg>
3750
+ <span class="flex-1 text-[12.5px] text-zinc-700 dark:text-zinc-300">All epics</span>
3751
+ <span class="font-mono text-[10px] text-zinc-400 dark:text-zinc-600">${tasks.length}</span>
3752
+ </div>
3753
+ ${rows}
3095
3754
  </aside>`;
3096
3755
  }
3097
3756
  function boardColumns(tasks) {
@@ -3103,11 +3762,11 @@ function settingsPanelHtml() {
3103
3762
  const hint = "mt-1 text-[11px] leading-relaxed text-zinc-400 dark:text-zinc-500";
3104
3763
  const row = (name, control, note = "") => `<div class="set-row"><span class="set-label">${name}</span><div class="min-w-0 flex-1">${control}${note ? `<p class="${hint}">${note}</p>` : ""}</div></div>`;
3105
3764
  const group = (title, sub, body, open = false) => `<details class="set-group" name="settings-group"${open ? " open" : ""}>
3106
- <summary>${title}<span class="set-sub">${sub}</span></summary>
3107
- <div class="pb-3">${body}</div>
3108
- </details>`;
3765
+ <summary>${title}<span class="set-sub">${sub}</span></summary>
3766
+ <div class="pb-3">${body}</div>
3767
+ </details>`;
3109
3768
  const appearance = row("Theme", `<div class="flex gap-1">${["light", "dark"].map((t) => `<button type="button" data-set-theme="${t}"
3110
- class="theme-opt pick capitalize">${t}</button>`).join("")}</div>`) + row("Accent", `<div class="flex flex-wrap gap-1.5">${[
3769
+ class="theme-opt pick capitalize">${t}</button>`).join("")}</div>`) + row("Accent", `<div class="flex flex-wrap gap-1.5">${[
3111
3770
  ["violet", "swatch swatch-violet"],
3112
3771
  ["blue", "swatch swatch-blue"],
3113
3772
  ["emerald", "swatch swatch-emerald"],
@@ -3127,48 +3786,80 @@ function settingsPanelHtml() {
3127
3786
  ["docs", "Docs", "Markdown viewer and search"],
3128
3787
  ["graph", "Graph", "Force-directed map of links"]
3129
3788
  ].map(([v, name, why]) => `
3130
- <label class="flex items-center gap-2 text-[12.5px] text-zinc-700 dark:text-zinc-300">
3131
- <input type="checkbox" name="view-${v}" class="h-3.5 w-3.5 accent-accent-600" ${settings.views.includes(v) ? "checked" : ""}>
3132
- ${name} <span class="text-[11px] text-zinc-400 dark:text-zinc-500">${why}</span>
3133
- </label>`).join("")}</div>`, "Turn off whatever this workspace does not need. At least one has to stay on.") + row("Columns", `<input id="set-columns" name="columns" class="${field}" value="${esc(settings.columns.join(", "))}">`, "Comma separated, in board order. A status used by a task always gets a column even if listed nowhere here, so nothing goes missing.");
3789
+ <label class="flex items-center gap-2 text-[12.5px] text-zinc-700 dark:text-zinc-300">
3790
+ <input type="checkbox" name="view-${v}" class="h-3.5 w-3.5 accent-accent-600" ${settings.views.includes(v) ? "checked" : ""}>
3791
+ ${name} <span class="text-[11px] text-zinc-400 dark:text-zinc-500">${why}</span>
3792
+ </label>`).join("")}</div>`, "Turn off whatever this workspace does not need. At least one has to stay on.") + row("Columns", `<input id="set-columns" name="columns" class="${field}" value="${esc(settings.columns.join(", "))}">`, "Comma separated, in board order. A status used by a task always gets a column even if listed nowhere here, so nothing goes missing.");
3134
3793
  const git = row("Commits", `<label class="flex items-center gap-2 text-[12.5px] text-zinc-700 dark:text-zinc-300">
3135
- <input type="checkbox" name="gitCommit" class="h-3.5 w-3.5 accent-accent-600" ${settings.gitCommit ? "checked" : ""} ${GIT_COMMIT_ENV ? "disabled" : ""}>
3136
- Commit every board mutation
3137
- </label>`, GIT_COMMIT_ENV ? 'Forced on by <code class="font-mono">BOARD_GIT_COMMIT=1</code>.' : "Each status change or subtask toggle becomes its own commit.");
3794
+ <input type="checkbox" name="gitCommit" class="h-3.5 w-3.5 accent-accent-600" ${settings.gitCommit ? "checked" : ""} ${GIT_COMMIT_ENV ? "disabled" : ""}>
3795
+ Commit every board mutation
3796
+ </label>`, GIT_COMMIT_ENV ? 'Forced on by <code class="font-mono">BOARD_GIT_COMMIT=1</code>.' : "Each status change or subtask toggle becomes its own commit.");
3138
3797
  return `<template id="detail-settings"><div class="detail-root" data-id="settings" data-views="${settings.views.join(",")}">
3139
- <h2 class="text-[16px] font-semibold leading-snug text-zinc-900 dark:text-zinc-50">Settings</h2>
3140
- <p class="${hint}">Workspace settings live in <code class="font-mono">${SETTINGS_FILENAME}</code> and are meant to be committed. Appearance is per-browser.</p>
3798
+ <h2 class="text-[16px] font-semibold leading-snug text-zinc-900 dark:text-zinc-50">Settings</h2>
3799
+ <p class="${hint}">Workspace settings live in <code class="font-mono">${SETTINGS_FILENAME}</code> and are meant to be committed. Appearance is per-browser.</p>
3141
3800
 
3142
- <div class="mt-4 border-t border-zinc-100 dark:border-zinc-800">
3143
- ${group("Appearance", "Theme, accent, typeface, size", appearance, true)}
3144
- ${group("Board", "Title, views, columns", board)}
3145
- ${group("Git", GIT_COMMIT_ENV || settings.gitCommit ? "Auto-commit on" : "Auto-commit off", git)}
3146
- </div>
3801
+ <div class="mt-4 border-t border-zinc-100 dark:border-zinc-800">
3802
+ ${group("Appearance", "Theme, accent, typeface, size", appearance, true)}
3803
+ ${group("Board", "Title, views, columns", board)}
3804
+ ${group("Git", GIT_COMMIT_ENV || settings.gitCommit ? "Auto-commit on" : "Auto-commit off", git)}
3805
+ </div>
3147
3806
 
3148
- <p id="settings-error" class="mt-4 hidden rounded-md border border-rose-300 bg-rose-50 px-2.5 py-1.5 text-[12px] text-rose-700 dark:border-rose-500/40 dark:bg-rose-500/10 dark:text-rose-300" role="alert"></p>
3807
+ <p id="settings-error" class="mt-4 hidden rounded-md border border-rose-300 bg-rose-50 px-2.5 py-1.5 text-[12px] text-rose-700 dark:border-rose-500/40 dark:bg-rose-500/10 dark:text-rose-300" role="alert"></p>
3149
3808
 
3150
- <div class="mt-4 flex items-center gap-2 border-t border-zinc-100 pt-4 dark:border-zinc-800">
3151
- <button id="settings-save" type="button"
3152
- class="rounded-md bg-accent-600 px-3 py-1.5 text-[12.5px] font-medium text-white transition-colors hover:bg-accent-500">Save</button>
3153
- <span class="font-mono text-[10.5px] text-zinc-400 dark:text-zinc-600">${esc(SETTINGS_FILENAME)}</span>
3154
- </div>
3809
+ <div class="mt-4 flex items-center gap-2 border-t border-zinc-100 pt-4 dark:border-zinc-800">
3810
+ <button id="settings-save" type="button"
3811
+ class="rounded-md bg-accent-600 px-3 py-1.5 text-[12.5px] font-medium text-white transition-colors hover:bg-accent-500">Save</button>
3812
+ <span class="font-mono text-[10.5px] text-zinc-400 dark:text-zinc-600">${esc(SETTINGS_FILENAME)}</span>
3813
+ </div>
3155
3814
  </div></template>`;
3156
3815
  }
3816
+ function compareTasks(a, b) {
3817
+ if (a.order !== b.order) {
3818
+ if (a.order === null)
3819
+ return 1;
3820
+ if (b.order === null)
3821
+ return -1;
3822
+ return a.order - b.order;
3823
+ }
3824
+ return (PRIORITY_ORDER[a.priority] ?? 9) - (PRIORITY_ORDER[b.priority] ?? 9) || a.id.localeCompare(b.id);
3825
+ }
3826
+ function siblings(tasks, status, epic) {
3827
+ return tasks.filter((t) => t.status === status && t.epic === epic).sort(compareTasks);
3828
+ }
3829
+ function groupByEpic(cards, epics) {
3830
+ const titles = new Map(epics.map((e) => [e.id, e.title]));
3831
+ const byEpic = new Map;
3832
+ for (const t of cards)
3833
+ byEpic.set(t.epic, [...byEpic.get(t.epic) ?? [], t]);
3834
+ return [...byEpic.entries()].sort(([a], [b]) => a === "" ? 1 : b === "" ? -1 : a.localeCompare(b)).map(([epic, group]) => ({
3835
+ epic,
3836
+ label: epic ? `${epic} ${titles.get(epic) ?? ""}`.trim() : "No epic",
3837
+ tasks: group.sort(compareTasks)
3838
+ }));
3839
+ }
3157
3840
  function appHtml(tasks, epics) {
3158
3841
  const byId = new Map(tasks.map((t) => [t.id, t]));
3159
3842
  const columns = boardColumns(tasks).map((col) => {
3160
- const cards = tasks.filter((t) => t.status === col).sort((a, b) => (PRIORITY_ORDER[a.priority] ?? 9) - (PRIORITY_ORDER[b.priority] ?? 9) || a.id.localeCompare(b.id));
3843
+ const cards = tasks.filter((t) => t.status === col);
3844
+ const groups = groupByEpic(cards, epics);
3845
+ const body = groups.length ? groups.map((g) => `<details class="card-group" open data-group="${esc(col)}/${esc(g.epic)}" data-epic="${esc(g.epic)}">
3846
+ <summary><span class="truncate">${esc(g.label)}</span><span class="card-group-count">${g.tasks.length}</span></summary>
3847
+ <div class="card-group-body" data-drop-epic="${esc(g.epic)}">
3848
+ ${g.tasks.map((t) => cardHtml(t, byId)).join(`
3849
+ `)}
3850
+ </div>
3851
+ </details>`).join(`
3852
+ `) : '<div class="rounded-md border border-dashed border-zinc-200 py-6 text-center text-[12px] text-zinc-300 dark:border-zinc-800 dark:text-zinc-700">No tasks</div>';
3161
3853
  return `<section data-col="${esc(col)}" class="flex w-[290px] shrink-0 flex-col rounded-lg p-1 transition-colors">
3162
- <header class="mb-2.5 flex items-center gap-2 px-1 pt-1">
3163
- ${statusIcon(col)}
3164
- <h2 class="text-[13px] font-medium capitalize text-zinc-700 dark:text-zinc-300">${esc(col.replace("-", " "))}</h2>
3165
- <span class="col-count font-mono text-[11px] text-zinc-400 dark:text-zinc-600">${cards.length}</span>
3166
- </header>
3167
- <div class="flex min-h-[60px] flex-1 flex-col gap-2">
3168
- ${cards.map((t) => cardHtml(t, byId)).join(`
3169
- `) || '<div class="rounded-md border border-dashed border-zinc-200 py-6 text-center text-[12px] text-zinc-300 dark:border-zinc-800 dark:text-zinc-700">No tasks</div>'}
3170
- </div>
3171
- </section>`;
3854
+ <header class="mb-2.5 flex items-center gap-2 px-1 pt-1">
3855
+ ${statusIcon(col)}
3856
+ <h2 class="text-[13px] font-medium capitalize text-zinc-700 dark:text-zinc-300">${esc(col.replace("-", " "))}</h2>
3857
+ <span class="col-count font-mono text-[11px] text-zinc-400 dark:text-zinc-600">${cards.length}</span>
3858
+ </header>
3859
+ <div class="flex min-h-[60px] flex-1 flex-col gap-1.5">
3860
+ ${body}
3861
+ </div>
3862
+ </section>`;
3172
3863
  }).join(`
3173
3864
  `);
3174
3865
  return `${sidebarHtml(epics, tasks)}
@@ -3199,25 +3890,25 @@ var ICON_BUTTON = "rounded p-1 text-zinc-400 hover:bg-zinc-100 hover:text-zinc-6
3199
3890
  function headerHtml(active, meta, showSidebarToggle) {
3200
3891
  return `<header class="flex h-12 items-center gap-3 border-b border-zinc-200 bg-white px-4 dark:border-zinc-800 dark:bg-zinc-950">
3201
3892
  ${showSidebarToggle ? `<button id="sidebar-toggle" type="button" aria-label="Toggle sidebar" title="Toggle sidebar"
3202
- class="hidden ${ICON_BUTTON} md:block">
3203
- <svg width="15" height="15" viewBox="0 0 16 16" fill="none" aria-hidden="true"><rect x="1.5" y="2.5" width="13" height="11" rx="2" stroke="currentColor" stroke-width="1.4"/><path d="M6 2.5 V13.5" stroke="currentColor" stroke-width="1.4"/></svg>
3893
+ class="hidden ${ICON_BUTTON} md:block">
3894
+ <svg width="15" height="15" viewBox="0 0 16 16" fill="none" aria-hidden="true"><rect x="1.5" y="2.5" width="13" height="11" rx="2" stroke="currentColor" stroke-width="1.4"/><path d="M6 2.5 V13.5" stroke="currentColor" stroke-width="1.4"/></svg>
3204
3895
  </button>` : ""}
3205
3896
  <span class="flex items-center gap-2 text-[13px] font-semibold tracking-tight">
3206
- <img src="/logo.svg" width="17" height="17" alt="" class="shrink-0 dark:brightness-125">${esc(settings.title)}
3897
+ <img src="/logo.svg" width="17" height="17" alt="" class="shrink-0 dark:brightness-125">${esc(settings.title)}
3207
3898
  </span>
3208
3899
  ${navTabs(active)}
3209
3900
  <span id="live-dot" class="h-1.5 w-1.5 rounded-full bg-emerald-500" title="Live: file changes appear automatically"></span>
3210
3901
  <div class="ml-auto flex items-center gap-2.5">
3211
- <input id="search" type="search" placeholder="Search" autocomplete="off" spellcheck="false"
3212
- class="hidden w-44 rounded-md border border-zinc-200 bg-zinc-50 px-2.5 py-1 text-[12px] outline-none transition-colors placeholder:text-zinc-400 focus:border-accent-400 dark:border-zinc-800 dark:bg-zinc-900 dark:placeholder:text-zinc-600 dark:focus:border-accent-500 sm:block">
3213
- <div id="header-meta" class="flex items-center gap-2.5">${meta}</div>
3214
- <button type="button" data-open="new" aria-label="New item" title="New epic, story or document" class="${ICON_BUTTON}">
3215
- <svg width="15" height="15" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M8 3.5 V12.5 M3.5 8 H12.5" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg>
3216
- </button>
3217
- <button id="theme-toggle" type="button" aria-label="Change theme" class="${ICON_BUTTON}"></button>
3218
- <button type="button" data-open="settings" aria-label="Settings" title="Settings" class="${ICON_BUTTON}">
3219
- <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"/><circle cx="12" cy="12" r="3"/></svg>
3220
- </button>
3902
+ <input id="search" type="search" placeholder="Search" autocomplete="off" spellcheck="false"
3903
+ class="hidden w-44 rounded-md border border-zinc-200 bg-zinc-50 px-2.5 py-1 text-[12px] outline-none transition-colors placeholder:text-zinc-400 focus:border-accent-400 dark:border-zinc-800 dark:bg-zinc-900 dark:placeholder:text-zinc-600 dark:focus:border-accent-500 sm:block">
3904
+ <div id="header-meta" class="flex items-center gap-2.5">${meta}</div>
3905
+ <button type="button" data-open="new" aria-label="New item" title="New epic, story or document" class="${ICON_BUTTON}">
3906
+ <svg width="15" height="15" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M8 3.5 V12.5 M3.5 8 H12.5" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg>
3907
+ </button>
3908
+ <button id="theme-toggle" type="button" aria-label="Change theme" class="${ICON_BUTTON}"></button>
3909
+ <button type="button" data-open="settings" aria-label="Settings" title="Settings" class="${ICON_BUTTON}">
3910
+ <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"/><circle cx="12" cy="12" r="3"/></svg>
3911
+ </button>
3221
3912
  </div>
3222
3913
  </header>`;
3223
3914
  }
@@ -3227,12 +3918,12 @@ var SHEET_HTML = `<div id="backdrop" class="fixed inset-0 z-40 hidden bg-black/2
3227
3918
  <aside id="sheet" tabindex="-1" role="dialog" aria-modal="true" aria-label="Details"
3228
3919
  class="fixed inset-y-0 right-0 z-50 w-full max-w-[460px] translate-x-full overflow-y-auto border-l border-zinc-200 bg-white p-6 shadow-xl outline-none motion-safe:transition-transform motion-safe:duration-200 dark:border-zinc-800 dark:bg-zinc-900">
3229
3920
  <div class="absolute right-4 top-4 flex items-center gap-0.5">
3230
- <button id="sheet-expand" type="button" aria-label="Toggle fullscreen" title="Toggle fullscreen" class="${ICON_BUTTON}">
3231
- <svg width="14" height="14" viewBox="0 0 14 14" fill="none" aria-hidden="true"><path d="M8.5 1.5 H12.5 V5.5 M12.5 1.5 L8 6 M5.5 12.5 H1.5 V8.5 M1.5 12.5 L6 8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
3232
- </button>
3233
- <button id="sheet-close" type="button" aria-label="Close" title="Close (Esc)" class="${ICON_BUTTON}">
3234
- <svg width="14" height="14" viewBox="0 0 14 14" fill="none" aria-hidden="true"><path d="M3 3 L11 11 M11 3 L3 11" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg>
3235
- </button>
3921
+ <button id="sheet-expand" type="button" aria-label="Toggle fullscreen" title="Toggle fullscreen" class="${ICON_BUTTON}">
3922
+ <svg width="14" height="14" viewBox="0 0 14 14" fill="none" aria-hidden="true"><path d="M8.5 1.5 H12.5 V5.5 M12.5 1.5 L8 6 M5.5 12.5 H1.5 V8.5 M1.5 12.5 L6 8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
3923
+ </button>
3924
+ <button id="sheet-close" type="button" aria-label="Close" title="Close (Esc)" class="${ICON_BUTTON}">
3925
+ <svg width="14" height="14" viewBox="0 0 14 14" fill="none" aria-hidden="true"><path d="M3 3 L11 11 M11 3 L3 11" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg>
3926
+ </button>
3236
3927
  </div>
3237
3928
  <div id="sheet-content"></div>
3238
3929
  </aside>
@@ -3260,22 +3951,63 @@ ${SHEET_HTML}
3260
3951
  <script>${board_default}</script>
3261
3952
  </body></html>`;
3262
3953
  }
3263
- function listDocs() {
3264
- const groups = [];
3265
- const rootFiles = readdirSync(PLANNER_DIR).filter((f) => f.endsWith(".md")).sort((a, b) => {
3266
- const ia = ROOT_DOC_ORDER.indexOf(a);
3267
- const ib = ROOT_DOC_ORDER.indexOf(b);
3268
- return (ia === -1 ? 99 : ia) - (ib === -1 ? 99 : ib) || a.localeCompare(b);
3954
+ var MAX_DOC_DEPTH = 8;
3955
+ function readDocDir(rel, depth) {
3956
+ const node = {
3957
+ name: rel === "" ? "planner" : basename(rel),
3958
+ path: rel === "" ? "planner" : rel,
3959
+ files: [],
3960
+ dirs: []
3961
+ };
3962
+ let entries;
3963
+ try {
3964
+ entries = readdirSync(join(PLANNER_DIR, rel), { withFileTypes: true });
3965
+ } catch {
3966
+ return node;
3967
+ }
3968
+ for (const e of entries) {
3969
+ if (e.name.startsWith(".") || DOC_EXCLUDE.has(e.name))
3970
+ continue;
3971
+ const child = rel ? `${rel}/${e.name}` : e.name;
3972
+ if (e.isDirectory()) {
3973
+ if (depth >= MAX_DOC_DEPTH)
3974
+ continue;
3975
+ const sub = readDocDir(child, depth + 1);
3976
+ if (sub.files.length || sub.dirs.length)
3977
+ node.dirs.push(sub);
3978
+ } else if (e.name.endsWith(".md")) {
3979
+ node.files.push(child);
3980
+ }
3981
+ }
3982
+ node.files.sort((a, b) => {
3983
+ if (rel === "") {
3984
+ const ia = ROOT_DOC_ORDER.indexOf(a);
3985
+ const ib = ROOT_DOC_ORDER.indexOf(b);
3986
+ if (ia !== ib)
3987
+ return (ia === -1 ? 99 : ia) - (ib === -1 ? 99 : ib);
3988
+ }
3989
+ return a.localeCompare(b);
3269
3990
  });
3270
- if (rootFiles.length)
3271
- groups.push({ group: "planner", files: rootFiles });
3272
- const dirs = readdirSync(PLANNER_DIR, { withFileTypes: true }).filter((d) => d.isDirectory() && !DOC_EXCLUDE.has(d.name)).map((d) => d.name).sort();
3273
- for (const dir of dirs) {
3274
- const files = readdirSync(join(PLANNER_DIR, dir)).filter((f) => f.endsWith(".md")).sort();
3275
- if (files.length)
3276
- groups.push({ group: dir, files: files.map((f) => `${dir}/${f}`) });
3277
- }
3278
- return groups;
3991
+ node.dirs.sort((a, b) => a.name.localeCompare(b.name));
3992
+ return node;
3993
+ }
3994
+ function listDocs() {
3995
+ const root = readDocDir("", 0);
3996
+ const groups = root.files.length ? [{ ...root, dirs: [] }] : [];
3997
+ return [...groups, ...root.dirs];
3998
+ }
3999
+ function docGroup(rel) {
4000
+ const i = rel.indexOf("/");
4001
+ return i === -1 ? "planner" : rel.slice(0, i);
4002
+ }
4003
+ function allDocFiles() {
4004
+ const out = [];
4005
+ const walk = (d) => {
4006
+ out.push(...d.files);
4007
+ d.dirs.forEach(walk);
4008
+ };
4009
+ listDocs().forEach(walk);
4010
+ return out;
3279
4011
  }
3280
4012
  var RAW_TYPES = {
3281
4013
  svg: "image/svg+xml",
@@ -3356,7 +4088,7 @@ function timelineHtml(rows) {
3356
4088
  while (+cursor < max) {
3357
4089
  const p = pct(+cursor);
3358
4090
  months.push(`<div class="absolute inset-y-0 border-l border-zinc-200 dark:border-zinc-800" style="left:${p.toFixed(2)}%"></div>
3359
- <div class="absolute top-0 -translate-x-1/2 text-[9.5px] font-medium uppercase tracking-wide text-zinc-400 dark:text-zinc-600" style="left:${p.toFixed(2)}%">${cursor.toLocaleDateString("en", { month: "short" })}</div>`);
4091
+ <div class="absolute top-0 -translate-x-1/2 text-[9.5px] font-medium uppercase tracking-wide text-zinc-400 dark:text-zinc-600" style="left:${p.toFixed(2)}%">${cursor.toLocaleDateString("en", { month: "short" })}</div>`);
3360
4092
  cursor.setMonth(cursor.getMonth() + 1);
3361
4093
  }
3362
4094
  const bars = rows.map((r) => {
@@ -3367,43 +4099,47 @@ function timelineHtml(rows) {
3367
4099
  const label = `${r.id} ${r.title}`.trim();
3368
4100
  const dates = `${r.start.toISOString().slice(0, 10)} to ${r.end.toISOString().slice(0, 10)}`;
3369
4101
  return `<div class="flex h-8 items-center">
3370
- <div class="w-44 shrink-0 truncate pr-3 text-[12px] text-zinc-600 dark:text-zinc-400" title="${esc(label)}">
3371
- ${r.epic ? `<a href="/#${esc(r.epic)}" class="font-mono text-[10.5px] text-accent-600 hover:underline dark:text-accent-400">${esc(r.epic)}</a> ` : ""}<span class="font-medium text-zinc-800 dark:text-zinc-200">${esc(r.id)}</span> ${esc(r.title)}
3372
- </div>
3373
- <div class="relative h-5 flex-1">
3374
- <div class="track absolute inset-y-0 hidden"></div>
3375
- <div class="absolute inset-y-0 overflow-hidden whitespace-nowrap rounded-md px-2 text-[10px] font-medium leading-5 text-white shadow-sm" style="left:${left.toFixed(2)}%;width:${width.toFixed(2)}%;background:${color}" title="${esc(label)}: ${dates} (${status})">${esc(r.title)}</div>
3376
- </div>
3377
- </div>`;
4102
+ <div class="w-44 shrink-0 truncate pr-3 text-[12px] text-zinc-600 dark:text-zinc-400" title="${esc(label)}">
4103
+ ${r.epic ? `<a href="/#${esc(r.epic)}" class="font-mono text-[10.5px] text-accent-600 hover:underline dark:text-accent-400">${esc(r.epic)}</a> ` : ""}<span class="font-medium text-zinc-800 dark:text-zinc-200">${esc(r.id)}</span> ${esc(r.title)}
4104
+ </div>
4105
+ <div class="relative h-5 flex-1">
4106
+ <div class="track absolute inset-y-0 hidden"></div>
4107
+ <div class="absolute inset-y-0 overflow-hidden whitespace-nowrap rounded-md px-2 text-[10px] font-medium leading-5 text-white shadow-sm" style="left:${left.toFixed(2)}%;width:${width.toFixed(2)}%;background:${color}" title="${esc(label)}: ${dates} (${status})">${esc(r.title)}</div>
4108
+ </div>
4109
+ </div>`;
3378
4110
  }).join("");
3379
4111
  const todayMark = today >= min && today <= max ? `<div class="absolute inset-y-0 z-10 border-l-2 border-rose-500/70" style="left:calc(11rem + (100% - 11rem) * ${(pct(today) / 100).toFixed(4)})">
3380
- <span class="absolute -top-0.5 left-1 text-[9px] font-semibold uppercase tracking-wide text-rose-500">today</span>
3381
- </div>` : "";
4112
+ <span class="absolute -top-0.5 left-1 text-[9px] font-semibold uppercase tracking-wide text-rose-500">today</span>
4113
+ </div>` : "";
3382
4114
  return `<div class="mb-8 rounded-lg border border-zinc-200 bg-white p-4 dark:border-zinc-800 dark:bg-zinc-900">
3383
- <div class="mb-3 flex items-baseline justify-between">
3384
- <span class="text-[11px] font-semibold uppercase tracking-wider text-zinc-400 dark:text-zinc-500">Timeline</span>
3385
- <span class="text-[10.5px] text-zinc-400 dark:text-zinc-600">${new Date(min + 2 * DAY).toISOString().slice(0, 10)} to ${new Date(max - 2 * DAY).toISOString().slice(0, 10)}</span>
3386
- </div>
3387
- <div class="relative pt-4">
3388
- <div class="absolute inset-y-0 left-44 right-0">${months.join("")}</div>
3389
- ${todayMark}
3390
- <div class="relative flex flex-col gap-1">${bars}</div>
3391
- </div>
3392
- <div class="mt-3 flex gap-4 text-[10px] text-zinc-400 dark:text-zinc-500">
3393
- <span class="inline-flex items-center gap-1.5"><span class="h-2 w-2 rounded-sm" style="background:${TIMELINE_COLOR.done}"></span>done</span>
3394
- <span class="inline-flex items-center gap-1.5"><span class="h-2 w-2 rounded-sm" style="background:${TIMELINE_COLOR.active}"></span>active</span>
3395
- <span class="inline-flex items-center gap-1.5"><span class="h-2 w-2 rounded-sm" style="background:${TIMELINE_COLOR.pending}"></span>pending</span>
3396
- </div>
4115
+ <div class="mb-3 flex items-baseline justify-between">
4116
+ <span class="text-[11px] font-semibold uppercase tracking-wider text-zinc-400 dark:text-zinc-500">Timeline</span>
4117
+ <span class="text-[10.5px] text-zinc-400 dark:text-zinc-600">${new Date(min + 2 * DAY).toISOString().slice(0, 10)} to ${new Date(max - 2 * DAY).toISOString().slice(0, 10)}</span>
4118
+ </div>
4119
+ <div class="relative pt-4">
4120
+ <div class="absolute inset-y-0 left-44 right-0">${months.join("")}</div>
4121
+ ${todayMark}
4122
+ <div class="relative flex flex-col gap-1">${bars}</div>
4123
+ </div>
4124
+ <div class="mt-3 flex gap-4 text-[10px] text-zinc-400 dark:text-zinc-500">
4125
+ <span class="inline-flex items-center gap-1.5"><span class="h-2 w-2 rounded-sm" style="background:${TIMELINE_COLOR.done}"></span>done</span>
4126
+ <span class="inline-flex items-center gap-1.5"><span class="h-2 w-2 rounded-sm" style="background:${TIMELINE_COLOR.active}"></span>active</span>
4127
+ <span class="inline-flex items-center gap-1.5"><span class="h-2 w-2 rounded-sm" style="background:${TIMELINE_COLOR.pending}"></span>pending</span>
4128
+ </div>
3397
4129
  </div>`;
3398
4130
  }
3399
- function docsNavHtml(groups) {
3400
- return groups.map((g) => `<details class="doc-folder" open data-folder="${esc(g.group)}">
3401
- <summary>${esc(g.group)}<span class="doc-folder-count">${g.files.length}</span></summary>
3402
- ${g.files.map((f) => {
4131
+ function countDocs(d) {
4132
+ return d.files.length + d.dirs.reduce((n, sub) => n + countDocs(sub), 0);
4133
+ }
4134
+ function docsNavHtml(dirs, depth = 0) {
4135
+ return dirs.map((d) => `<details class="doc-folder" open data-folder="${esc(d.path)}" style="--doc-depth:${depth}">
4136
+ <summary>${esc(d.name)}<span class="doc-folder-count">${countDocs(d)}</span></summary>
4137
+ ${docsNavHtml(d.dirs, depth + 1)}
4138
+ ${d.files.map((f) => {
3403
4139
  const name = basename(f);
3404
- return `<a href="/docs?file=${encodeURIComponent(f)}" class="doc-link" data-file="${esc(f)}" title="${esc(f)}">${esc(name)}</a>`;
4140
+ return `<a href="/docs?file=${encodeURIComponent(f)}" class="doc-link" data-file="${esc(f)}" title="${esc(f)}" style="--doc-depth:${depth + 1}">${esc(name)}</a>`;
3405
4141
  }).join("")}
3406
- </details>`).join("");
4142
+ </details>`).join("");
3407
4143
  }
3408
4144
  function docsPageHtml(relFile) {
3409
4145
  const groups = listDocs();
@@ -3418,7 +4154,7 @@ function docsPageHtml(relFile) {
3418
4154
  const parsed = parseFrontmatter(raw);
3419
4155
  const body = parsed ? parsed.body : raw;
3420
4156
  const fm = parsed?.fm ?? {};
3421
- hasMermaid = /^```mermaid/m.test(body);
4157
+ hasMermaid = /^[ \t]*```mermaid/m.test(body);
3422
4158
  const isTimeline = fm.render === "timeline" || basename(relFile).toUpperCase() === "TIMELINE.MD";
3423
4159
  const timeline = isTimeline ? parseTimeline(body) : null;
3424
4160
  const baseDir = dirname(relFile) === "." ? "" : dirname(relFile);
@@ -3426,9 +4162,14 @@ function docsPageHtml(relFile) {
3426
4162
  const sources = [...backlinks.get(relFile) ?? [], ...fm.id ? backlinks.get(fm.id) ?? [] : []];
3427
4163
  const seen = new Set;
3428
4164
  const unique = sources.filter((s) => seen.has(s.file) ? false : (seen.add(s.file), true));
3429
- main = `${timeline ? timelineHtml(timeline) : ""}<article class="doc-body">${mdToHtml(body, { docMode: true, baseDir })}</article>
3430
- ${backlinksHtml(unique, "Linked mentions", true)}
3431
- <div class="mt-10 border-t border-zinc-100 pt-3 font-mono text-[10.5px] text-zinc-400 dark:border-zinc-800 dark:text-zinc-600">${esc(relFile)}</div>`;
4165
+ main = `${timeline ? timelineHtml(timeline) : ""}
4166
+ <button type="button" id="doc-edit" class="doc-edit" title="Edit this document (e)" aria-label="Edit this document">
4167
+ <svg width="13" height="13" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M11.5 2.5 13.5 4.5 5.5 12.5 2.5 13.5 3.5 10.5 Z" stroke="currentColor" stroke-width="1.3" stroke-linejoin="round"/></svg>
4168
+ <span>Edit</span>
4169
+ </button>
4170
+ <article class="doc-body">${mdToHtml(body, { docMode: true, baseDir })}</article>
4171
+ ${backlinksHtml(unique, "Linked mentions", true)}
4172
+ <div class="mt-10 border-t border-zinc-100 pt-3 font-mono text-[10.5px] text-zinc-400 dark:border-zinc-800 dark:text-zinc-600">${esc(relFile)}</div>`;
3432
4173
  }
3433
4174
  return `<!doctype html>
3434
4175
  <html lang="en"><head><meta charset="utf-8">
@@ -3444,22 +4185,22 @@ ${headerHtml("docs", "", true)}
3444
4185
 
3445
4186
  <div class="flex h-[calc((100vh/var(--zoom))-3rem)]">
3446
4187
  <aside id="doc-sidebar" class="hidden w-60 shrink-0 flex-col overflow-y-auto border-r border-zinc-200 bg-white px-2.5 py-2 dark:border-zinc-800 dark:bg-zinc-950 md:flex">
3447
- <div class="mb-1 flex items-center gap-1 px-2 pb-1">
3448
- <span class="text-[10.5px] font-semibold uppercase tracking-wider text-zinc-400 dark:text-zinc-500">Files</span>
3449
- <button id="fold-all" type="button" class="${ICON_BUTTON} ml-auto" aria-label="Collapse all folders" title="Collapse all folders">
3450
- <svg width="14" height="14" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M4 6.5 8 2.5 12 6.5 M4 9.5 8 13.5 12 9.5" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>
3451
- </button>
3452
- </div>
3453
- <div id="doc-tree">${nav}</div>
4188
+ <div class="mb-1 flex items-center gap-1 px-2 pb-1">
4189
+ <span class="text-[10.5px] font-semibold uppercase tracking-wider text-zinc-400 dark:text-zinc-500">Files</span>
4190
+ <button id="fold-all" type="button" class="${ICON_BUTTON} ml-auto" aria-label="Collapse all folders" title="Collapse all folders">
4191
+ <svg width="14" height="14" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M4 6.5 8 2.5 12 6.5 M4 9.5 8 13.5 12 9.5" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>
4192
+ </button>
4193
+ </div>
4194
+ <div id="doc-tree">${nav}</div>
3454
4195
  </aside>
3455
4196
  ${RESIZER_HTML}
3456
4197
  <main id="doc-main" class="flex-1 overflow-y-auto">
3457
- <div id="search-results" class="mx-auto hidden max-w-3xl divide-y divide-zinc-200 px-8 py-5 dark:divide-zinc-800"></div>
3458
- <div id="doc-content" class="mx-auto max-w-3xl px-8 py-8"${abs ? ` data-file="${esc(relFile)}" data-rev="${esc(fileRev(abs))}"` : ""}>${main}</div>
4198
+ <div id="search-results" class="mx-auto hidden max-w-3xl divide-y divide-zinc-200 px-8 py-5 dark:divide-zinc-800"></div>
4199
+ <div id="doc-content" class="mx-auto max-w-3xl px-8 py-8"${abs ? ` data-file="${esc(relFile)}" data-rev="${esc(fileRev(abs))}"` : ""}>${main}</div>
3459
4200
  </main>
3460
4201
  <aside id="doc-outline" class="hidden w-56 shrink-0 overflow-y-auto border-l border-zinc-200 bg-white px-3 py-3 dark:border-zinc-800 dark:bg-zinc-950 lg:block">
3461
- <div class="mb-1.5 px-2 text-[10.5px] font-semibold uppercase tracking-wider text-zinc-400 dark:text-zinc-500">On this page</div>
3462
- <nav id="outline-list"></nav>
4202
+ <div class="mb-1.5 px-2 text-[10.5px] font-semibold uppercase tracking-wider text-zinc-400 dark:text-zinc-500">On this page</div>
4203
+ <nav id="outline-list"></nav>
3463
4204
  </aside>
3464
4205
  </div>
3465
4206
 
@@ -3478,57 +4219,57 @@ function newItemPanelHtml() {
3478
4219
  const columns = boardColumns(allTasks());
3479
4220
  const opt = (v, text) => `<option value="${esc(v)}">${esc(text)}</option>`;
3480
4221
  return `<template id="detail-new"><div class="detail-root" data-id="new">
3481
- <h2 class="text-[16px] font-semibold leading-snug text-zinc-900 dark:text-zinc-50">New</h2>
3482
- <p class="${hint}">Writes a markdown file into the workspace. Ids and filenames are assigned for you.</p>
4222
+ <h2 class="text-[16px] font-semibold leading-snug text-zinc-900 dark:text-zinc-50">New</h2>
4223
+ <p class="${hint}">Writes a markdown file into the workspace. Ids and filenames are assigned for you.</p>
3483
4224
 
3484
- <div class="mt-4 flex gap-1" data-kind-picker>
3485
- ${[
4225
+ <div class="mt-4 flex gap-1" data-kind-picker>
4226
+ ${[
3486
4227
  ["task", "User story"],
3487
4228
  ["epic", "Epic"],
3488
4229
  ["doc", "Document"]
3489
4230
  ].map(([k, name]) => `<button type="button" class="pick" data-kind="${k}">${name}</button>`).join("")}
3490
- </div>
4231
+ </div>
3491
4232
 
3492
- <div class="mt-4"><label class="${label}" for="new-title">Title</label>
3493
- <input id="new-title" name="title" class="${field}" maxlength="200" placeholder="Short imperative title">
3494
- </div>
4233
+ <div class="mt-4"><label class="${label}" for="new-title">Title</label>
4234
+ <input id="new-title" name="title" class="${field}" maxlength="200" placeholder="Short imperative title">
4235
+ </div>
3495
4236
 
3496
- <div class="mt-3" data-for="doc"><label class="${label}" for="new-path">File path</label>
3497
- <input id="new-path" name="path" class="${field}" placeholder="notes/my-document.md">
3498
- <p class="${hint}">Relative to the workspace root. Folders are created as needed.</p>
3499
- </div>
4237
+ <div class="mt-3" data-for="doc"><label class="${label}" for="new-path">File path</label>
4238
+ <input id="new-path" name="path" class="${field}" placeholder="notes/my-document.md">
4239
+ <p class="${hint}">Relative to the workspace root. Folders are created as needed.</p>
4240
+ </div>
3500
4241
 
3501
- <div class="mt-3 grid grid-cols-2 gap-3" data-for="task">
3502
- <div><label class="${label}" for="new-epic">Epic</label>
3503
- <select id="new-epic" name="epic" class="${field}">
3504
- ${opt("", "(none)")}${epics.map((e) => opt(e.id, `${e.id} ${e.title}`)).join("")}
3505
- </select>
3506
- </div>
3507
- <div><label class="${label}" for="new-priority">Priority</label>
3508
- <select id="new-priority" name="priority" class="${field}">
3509
- ${PRIORITIES.map((p) => opt(p, p)).join("")}
3510
- </select>
3511
- </div>
3512
- <div><label class="${label}" for="new-status">Status</label>
3513
- <select id="new-status" name="status" class="${field}">${columns.map((c) => opt(c, c.replace(/-/g, " "))).join("")}</select>
3514
- </div>
4242
+ <div class="mt-3 grid grid-cols-2 gap-3" data-for="task">
4243
+ <div><label class="${label}" for="new-epic">Epic</label>
4244
+ <select id="new-epic" name="epic" class="${field}">
4245
+ ${opt("", "(none)")}${epics.map((e) => opt(e.id, `${e.id} ${e.title}`)).join("")}
4246
+ </select>
3515
4247
  </div>
3516
-
3517
- <div class="mt-3"><label class="${label}" for="new-body">Description</label>
3518
- <textarea id="new-body" name="body" rows="5" class="${field} resize-y" placeholder="Markdown. Optional."></textarea>
4248
+ <div><label class="${label}" for="new-priority">Priority</label>
4249
+ <select id="new-priority" name="priority" class="${field}">
4250
+ ${PRIORITIES.map((p) => opt(p, p)).join("")}
4251
+ </select>
3519
4252
  </div>
3520
-
3521
- <div class="mt-3" data-for="task"><label class="${label}" for="new-subtasks">Subtasks</label>
3522
- <textarea id="new-subtasks" name="subtasks" rows="3" class="${field} resize-y" placeholder="One per line"></textarea>
4253
+ <div><label class="${label}" for="new-status">Status</label>
4254
+ <select id="new-status" name="status" class="${field}">${columns.map((c) => opt(c, c.replace(/-/g, " "))).join("")}</select>
3523
4255
  </div>
4256
+ </div>
3524
4257
 
3525
- <p id="new-error" class="mt-4 hidden rounded-md border border-rose-300 bg-rose-50 px-2.5 py-1.5 text-[12px] text-rose-700 dark:border-rose-500/40 dark:bg-rose-500/10 dark:text-rose-300" role="alert"></p>
4258
+ <div class="mt-3"><label class="${label}" for="new-body">Description</label>
4259
+ <textarea id="new-body" name="body" rows="5" class="${field} resize-y" placeholder="Markdown. Optional."></textarea>
4260
+ </div>
3526
4261
 
3527
- <div class="mt-4 flex items-center gap-2 border-t border-zinc-100 pt-4 dark:border-zinc-800">
3528
- <button id="new-save" type="button"
3529
- class="rounded-md bg-accent-600 px-3 py-1.5 text-[12.5px] font-medium text-white transition-colors hover:bg-accent-500">Create</button>
3530
- <span id="new-target" class="font-mono text-[10.5px] text-zinc-400 dark:text-zinc-600"></span>
3531
- </div>
4262
+ <div class="mt-3" data-for="task"><label class="${label}" for="new-subtasks">Subtasks</label>
4263
+ <textarea id="new-subtasks" name="subtasks" rows="3" class="${field} resize-y" placeholder="One per line"></textarea>
4264
+ </div>
4265
+
4266
+ <p id="new-error" class="mt-4 hidden rounded-md border border-rose-300 bg-rose-50 px-2.5 py-1.5 text-[12px] text-rose-700 dark:border-rose-500/40 dark:bg-rose-500/10 dark:text-rose-300" role="alert"></p>
4267
+
4268
+ <div class="mt-4 flex items-center gap-2 border-t border-zinc-100 pt-4 dark:border-zinc-800">
4269
+ <button id="new-save" type="button"
4270
+ class="rounded-md bg-accent-600 px-3 py-1.5 text-[12.5px] font-medium text-white transition-colors hover:bg-accent-500">Create</button>
4271
+ <span id="new-target" class="font-mono text-[10.5px] text-zinc-400 dark:text-zinc-600"></span>
4272
+ </div>
3532
4273
  </div></template>`;
3533
4274
  }
3534
4275
  var GRAPH_BUTTON = "rounded-md border border-zinc-200 bg-white px-2 py-1 text-[11px] text-zinc-500 transition-colors hover:text-zinc-900 dark:border-zinc-800 dark:bg-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-100";
@@ -3550,8 +4291,8 @@ ${headerHtml("graph", `<span class="text-[11px] text-zinc-400 dark:text-zinc-600
3550
4291
  <div id="graph-empty" class="pointer-events-none absolute inset-0 hidden items-center justify-center text-[13px] text-zinc-400 dark:text-zinc-600">No documents to graph yet.</div>
3551
4292
  <div id="graph-hint" class="pointer-events-none absolute bottom-3 left-3 text-[10.5px] text-zinc-400 dark:text-zinc-600">Scroll to zoom · drag to pan · click a note to open it</div>
3552
4293
  <div class="absolute bottom-3 right-3 flex items-center gap-1.5">
3553
- <button id="graph-shape" type="button" class="${GRAPH_BUTTON}">Globe</button>
3554
- <button id="graph-reset" type="button" title="Reset view" class="${GRAPH_BUTTON}">Reset</button>
4294
+ <button id="graph-shape" type="button" class="${GRAPH_BUTTON}">Globe</button>
4295
+ <button id="graph-reset" type="button" title="Reset view" class="${GRAPH_BUTTON}">Reset</button>
3555
4296
  </div>
3556
4297
  </div>
3557
4298
 
@@ -3560,7 +4301,7 @@ ${newItemPanelHtml()}
3560
4301
  ${SHEET_HTML}
3561
4302
 
3562
4303
  <!-- The graph ships with the page instead of as a second request. It is already built here for the
3563
- header count, and one round trip on a cold connection is the slowest part of this view. -->
4304
+ header count, and one round trip on a cold connection is the slowest part of this view. -->
3564
4305
  <script type="application/json" id="graph-data">${JSON.stringify({ nodes, links }).replace(/</g, "\\u003c")}</script>
3565
4306
  <script>${graph_default}</script>
3566
4307
  </body></html>`;
@@ -3634,6 +4375,51 @@ function htmlPage(key, req, render) {
3634
4375
  headers: { "content-type": "text/html; charset=utf-8", etag: hit.etag, "cache-control": "no-cache" }
3635
4376
  });
3636
4377
  }
4378
+ var ORDER_STEP = 100;
4379
+ var ORDER_MIN_GAP = 0.001;
4380
+ function placeInOrder(rest, patch, self) {
4381
+ let index;
4382
+ if (patch.move === "up" || patch.move === "down") {
4383
+ const here = rest.filter((t) => compareTasks(t, self) < 0).length;
4384
+ index = patch.move === "up" ? here - 1 : here + 1;
4385
+ if (index < 0 || index > rest.length)
4386
+ return { own: null, writes: new Map };
4387
+ } else {
4388
+ const anchor = patch.before ?? patch.after;
4389
+ const at = rest.findIndex((t) => t.id === anchor);
4390
+ if (at === -1)
4391
+ return null;
4392
+ index = patch.before !== undefined ? at : at + 1;
4393
+ }
4394
+ const writes = new Map;
4395
+ if (rest.some((t) => t.order === null)) {
4396
+ rest.forEach((t, i) => {
4397
+ const value = (i + 1) * ORDER_STEP;
4398
+ if (t.order !== value)
4399
+ writes.set(t.id, value);
4400
+ t.order = value;
4401
+ });
4402
+ }
4403
+ const ranks = rest.map((t) => t.order);
4404
+ const before = index > 0 ? ranks[index - 1] : null;
4405
+ const after = index < ranks.length ? ranks[index] : null;
4406
+ if (before !== null && after !== null) {
4407
+ if (after - before > ORDER_MIN_GAP)
4408
+ return { own: (before + after) / 2, writes };
4409
+ const respaced = [...rest];
4410
+ respaced.splice(index, 0, self);
4411
+ respaced.forEach((t, i) => {
4412
+ if (t.id !== self.id)
4413
+ writes.set(t.id, (i + 1) * ORDER_STEP);
4414
+ });
4415
+ return { own: (index + 1) * ORDER_STEP, writes };
4416
+ }
4417
+ if (after !== null)
4418
+ return { own: after - ORDER_STEP, writes };
4419
+ if (before !== null)
4420
+ return { own: before + ORDER_STEP, writes };
4421
+ return { own: ORDER_STEP, writes };
4422
+ }
3637
4423
  async function handlePatch(id, req, ctx) {
3638
4424
  const path = findItemPath(id);
3639
4425
  if (!path)
@@ -3675,6 +4461,49 @@ async function handlePatch(id, req, ctx) {
3675
4461
  raw = updated;
3676
4462
  messages.push(`${id}: toggle subtask via board`);
3677
4463
  }
4464
+ if (patch.body !== undefined) {
4465
+ const body = cleanDocument(patch.body);
4466
+ if (body === null)
4467
+ return json({ error: `body must be text under ${MAX_DOC_BYTES} characters` }, 413);
4468
+ const updated = replaceBody(raw, body);
4469
+ if (updated !== raw) {
4470
+ raw = updated;
4471
+ messages.push(`${id}: edit body via board`);
4472
+ }
4473
+ }
4474
+ if (patch.epic !== undefined) {
4475
+ if (patch.epic !== "" && !allEpics().some((e) => e.id === patch.epic)) {
4476
+ return json({ error: `unknown epic ${patch.epic}` }, 400);
4477
+ }
4478
+ const old = getFrontmatterValue(raw, "epic");
4479
+ if (old !== patch.epic) {
4480
+ raw = setFrontmatterValue(raw, "epic", patch.epic);
4481
+ messages.push(`${id}: epic ${old || "none"} -> ${patch.epic || "none"} via board`);
4482
+ }
4483
+ }
4484
+ if (patch.move !== undefined || patch.before !== undefined || patch.after !== undefined) {
4485
+ const self = allTasks().find((t) => t.id === id);
4486
+ if (!self)
4487
+ return json({ error: `unknown task ${id}` }, 404);
4488
+ const status = patch.status ?? self.status;
4489
+ const epic = patch.epic ?? self.epic;
4490
+ const placed = placeInOrder(siblings(allTasks(), status, epic).filter((t) => t.id !== id), patch, self);
4491
+ if (placed === null)
4492
+ return json({ error: "move target not found in this group" }, 400);
4493
+ if (placed.own !== null) {
4494
+ for (const [target, value] of placed.writes) {
4495
+ if (target === id)
4496
+ continue;
4497
+ const p = findItemPath(target);
4498
+ if (p)
4499
+ writeFileSync2(p, setFrontmatterValue(readFileSync2(p, "utf8"), "order", String(value)));
4500
+ }
4501
+ if (getFrontmatterValue(raw, "order") !== String(placed.own)) {
4502
+ raw = setFrontmatterValue(raw, "order", String(placed.own));
4503
+ messages.push(`${id}: order ${placed.own} via board`);
4504
+ }
4505
+ }
4506
+ }
3678
4507
  const rel = relative(PLANNER_DIR, path);
3679
4508
  if (messages.length === 0) {
3680
4509
  ctx.detail = `${rel} unchanged`;
@@ -3709,6 +4538,20 @@ function nextId(prefix, width, ids) {
3709
4538
  }
3710
4539
  return prefix + String(max + 1).padStart(width, "0");
3711
4540
  }
4541
+ var MAX_DOC_BYTES = 1e6;
4542
+ function cleanDocument(v) {
4543
+ if (typeof v !== "string" || v.length > MAX_DOC_BYTES)
4544
+ return null;
4545
+ return [...v.replace(/\r\n?/g, `
4546
+ `)].filter((c) => c >= " " || c === `
4547
+ ` || c === "\t").join("");
4548
+ }
4549
+ function replaceBody(raw, body) {
4550
+ const m = raw.match(/^(---\n[\s\S]*?\n---\n?)([\s\S]*)$/);
4551
+ return (m ? m[1] : "") + (body.endsWith(`
4552
+ `) || body === "" ? body : body + `
4553
+ `);
4554
+ }
3712
4555
  function cleanBody(v) {
3713
4556
  if (typeof v !== "string")
3714
4557
  return "";
@@ -3830,9 +4673,24 @@ async function handleDocPatch(rel, req, ctx) {
3830
4673
  if (patch.rev && fileRev(path) !== String(patch.rev)) {
3831
4674
  return json({ error: "stale: file changed since it was rendered" }, 409);
3832
4675
  }
4676
+ const raw = readFileSync2(path, "utf8");
4677
+ if (patch.body !== undefined) {
4678
+ const body = cleanDocument(patch.body);
4679
+ if (body === null)
4680
+ return json({ error: `body must be text under ${MAX_DOC_BYTES} characters` }, 413);
4681
+ const updated2 = replaceBody(raw, body);
4682
+ if (updated2 === raw) {
4683
+ ctx.detail = `${rel} unchanged`;
4684
+ return json({ ok: true, rev: fileRev(path), unchanged: true });
4685
+ }
4686
+ writeFileSync2(path, updated2);
4687
+ gitCommitFile(path, `${rel}: edit via board`);
4688
+ scheduleBroadcast();
4689
+ ctx.detail = `${rel}: edit${gitEnabled() ? " [committed]" : ""}`;
4690
+ return json({ ok: true, rev: fileRev(path) });
4691
+ }
3833
4692
  if (!patch.checkbox)
3834
4693
  return json({ error: "nothing to change" }, 400);
3835
- const raw = readFileSync2(path, "utf8");
3836
4694
  const updated = toggleCheckboxLine(raw, Number(patch.checkbox.index), Boolean(patch.checkbox.checked));
3837
4695
  if (updated === null)
3838
4696
  return json({ error: `checkbox ${patch.checkbox.index} not found` }, 400);
@@ -3963,8 +4821,8 @@ async function handle(req, ctx) {
3963
4821
  const q = (url.searchParams.get("q") ?? "").trim().toLowerCase();
3964
4822
  const hits = [];
3965
4823
  if (q) {
3966
- for (const g of listDocs()) {
3967
- for (const f of g.files) {
4824
+ for (const f of allDocFiles()) {
4825
+ {
3968
4826
  const abs = safeDocPath(f);
3969
4827
  if (!abs)
3970
4828
  continue;
@@ -3992,6 +4850,15 @@ async function handle(req, ctx) {
3992
4850
  if (url.pathname === "/api/docs" && req.method === "PATCH") {
3993
4851
  return handleDocPatch(url.searchParams.get("file") ?? "", req, ctx);
3994
4852
  }
4853
+ if (url.pathname === "/api/docs" && req.method === "GET") {
4854
+ const rel = url.searchParams.get("file") ?? "";
4855
+ const abs = safeDocPath(rel);
4856
+ if (!abs)
4857
+ return json({ error: `unknown document ${rel}` }, 404);
4858
+ const raw = readFileSync2(abs, "utf8");
4859
+ ctx.detail = rel;
4860
+ return json({ file: rel, body: parseFrontmatter(raw)?.body ?? raw, rev: fileRev(abs) });
4861
+ }
3995
4862
  const createMatch = url.pathname.match(/^\/api\/(tasks|epics|docs)$/);
3996
4863
  if (createMatch && req.method === "POST") {
3997
4864
  let body;