<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="theme-color" content="#131416">
<title>Phone app: every desktop place on the phone</title>
<style>
:root {
    --side: #0d0e10;
    --bg: #131416;
    --raised: #18191c;
    --hover: #1d1e22;
    --sel: #212329;
    --border: #25272b;
    --border-2: #2f3136;
    --line: #22252b;
    --text: #e6e7ea;
    --text-2: #a9acb3;
    --text-3: #83868e;
    --accent: #5e64c9;
    --accent-text: #a3a8f0;
    --accent-dim: #2a2d5a;
    --progress: #5b8def;
    --blocking: #d9a441;
    --good: #63b37c;
    --open: #8b8e96;
    --danger: #df8b7e;
    --badge: #d70015;
    --font: -apple-system, "SF Pro Text", system-ui, sans-serif;
    --mono: ui-monospace, "SF Mono", Menlo, monospace;
    --push: cubic-bezier(0.32, 0.72, 0, 1);
}
* {
    box-sizing: border-box;
}
html,
body {
    margin: 0;
    height: 100%;
    background: var(--side);
    color: var(--text);
    font-family: var(--font);
    font-size: 17px;
    line-height: 1.35;
    -webkit-text-size-adjust: 100%;
    -webkit-tap-highlight-color: transparent;
}
a {
    color: var(--accent-text);
}
a:hover {
    color: var(--text);
}
button {
    font: inherit;
    color: inherit;
    cursor: pointer;
}
/* ---------- page around the phone (desktop) ---------- */
.page {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 390px;
    gap: 48px;
    max-width: 1280px;
    min-height: 100%;
    margin: 0 auto;
    padding: 40px 40px 60px;
}
.notes {
    min-width: 0;
    font-size: 14px;
    line-height: 1.5;
    color: var(--text-2);
}
.notes h1 {
    margin: 0 0 6px;
    font-size: 26px;
    line-height: 1.2;
    color: var(--text);
    text-wrap: pretty;
}
.notes h2 {
    margin: 32px 0 10px;
    font-size: 15px;
    color: var(--text);
}
.notes p {
    margin: 0 0 10px;
    max-width: 68ch;
    text-wrap: pretty;
}
.opts {
    display: grid;
    gap: 10px;
}
.opt {
    display: grid;
    grid-template-columns: 34px minmax(0, 1fr);
    gap: 12px;
    padding: 12px 14px;
    border: 1px solid var(--border);
    border-radius: 12px;
    background: var(--raised);
    text-align: left;
}
.opt.on {
    border-color: var(--accent);
    background: color-mix(in oklab, var(--accent) 12%, var(--raised));
}
.opt-id {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    border-radius: 9px;
    background: var(--sel);
    color: var(--text);
    font-weight: 700;
}
.opt.on .opt-id {
    background: var(--accent);
}
.opt b {
    display: block;
    color: var(--text);
    font-size: 14px;
}
.opt span {
    font-size: 13px;
}
.pick {
    display: inline-block;
    margin-left: 6px;
    padding: 1px 7px;
    border-radius: 6px;
    background: var(--accent-dim);
    color: var(--accent-text);
    font-size: 11px;
    font-weight: 600;
    vertical-align: 1px;
}
.walk {
    display: grid;
    gap: 6px;
    margin: 0;
    padding: 0;
    list-style: none;
    counter-reset: walk;
}
.walk li {
    display: flex;
    gap: 10px;
    align-items: baseline;
    counter-increment: walk;
}
.walk li::before {
    content: counter(walk);
    flex: none;
    width: 18px;
    color: var(--text-3);
    font-variant-numeric: tabular-nums;
}
.go {
    flex: none;
    padding: 2px 8px;
    border: 1px solid var(--border-2);
    border-radius: 6px;
    background: none;
    color: var(--accent-text);
    font-size: 12px;
}
.go:hover {
    background: var(--hover);
}
.inv {
    width: 100%;
    border-collapse: collapse;
    font-size: 12.5px;
}
.inv th {
    padding: 14px 8px 6px 0;
    color: var(--text);
    font-size: 12.5px;
    text-align: left;
}
.inv td {
    padding: 5px 8px 5px 0;
    border-top: 1px solid var(--line);
    vertical-align: top;
}
.inv td:first-child {
    color: var(--text);
}
.tag {
    display: inline-block;
    padding: 0 6px;
    border-radius: 5px;
    font-size: 11px;
    font-weight: 600;
    white-space: nowrap;
}
.tag.today {
    background: var(--sel);
    color: var(--text-2);
}
.tag.new {
    background: color-mix(in oklab, var(--good) 18%, transparent);
    color: var(--good);
}
.tag.out {
    background: color-mix(in oklab, var(--blocking) 18%, transparent);
    color: var(--blocking);
}
.stage {
    position: sticky;
    top: 40px;
    align-self: start;
}
/* ---------- the phone ---------- */
.phone {
    --safe-top: 47px;
    --safe-bottom: 34px;
    position: relative;
    width: 390px;
    height: 844px;
    overflow: hidden;
    border-radius: 48px;
    background: var(--bg);
    box-shadow: 0 0 0 10px #1f2024, 0 0 0 11px #2c2d31, 0 30px 80px rgb(0 0 0 / 55%);
    isolation: isolate;
}
.status {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    z-index: 30;
    display: flex;
    align-items: center;
    justify-content: space-between;
    height: 47px;
    padding: 0 34px 0 44px;
    font-size: 15px;
    font-weight: 600;
    pointer-events: none;
}
.status i {
    width: 120px;
    height: 34px;
    border-radius: 20px;
    background: #000;
}
.status s {
    width: 26px;
    height: 12px;
    border: 1.5px solid var(--text);
    border-radius: 4px;
    opacity: 0.8;
}
.proto-pill {
    position: absolute;
    top: max(4px, env(safe-area-inset-top));
    left: 50%;
    z-index: 40;
    display: none;
    transform: translateX(-50%);
    padding: 3px 10px;
    border: 1px solid var(--border-2);
    border-radius: 12px;
    background: rgb(19 20 22 / 85%);
    color: var(--text-3);
    font-size: 11px;
}
@media (max-width: 560px) {
    html,
    body {
        background: var(--bg);
    }
    .page {
        display: block;
        padding: 0;
    }
    .notes {
        display: none;
    }
    .stage {
        position: static;
    }
    .phone {
        --safe-top: calc(env(safe-area-inset-top) + 32px);
        --safe-bottom: env(safe-area-inset-bottom);
        width: 100vw;
        height: 100dvh;
        border-radius: 0;
        box-shadow: none;
    }
    .status {
        display: none;
    }
    .proto-pill {
        display: block;
    }
}
.screen {
    position: absolute;
    inset: 0 0 var(--tabs-h, 0) 0;
    display: flex;
    flex-direction: column;
    background: var(--bg);
    padding-top: var(--safe-top);
}
.phone.has-tabs .screen {
    --tabs-h: calc(50px + var(--safe-bottom));
}
.screen.push {
    animation: push 380ms var(--push);
}
.screen.pop {
    animation: pop 300ms var(--push);
}
@keyframes push {
    from {
        transform: translateX(100%);
        box-shadow: -10px 0 30px rgb(0 0 0 / 40%);
    }
}
@keyframes pop {
    from {
        transform: translateX(-30%);
        opacity: 0.6;
    }
}
.navbar {
    position: relative;
    display: flex;
    flex: none;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    min-height: 44px;
    padding: 0 8px;
    border-bottom: 1px solid transparent;
}
.navbar.under {
    border-bottom-color: var(--line);
}
.nav-back {
    display: flex;
    align-items: center;
    gap: 2px;
    min-width: 44px;
    min-height: 44px;
    max-width: 45%;
    padding: 0 8px 0 2px;
    border: 0;
    background: none;
    color: var(--accent-text);
    font-size: 17px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.nav-mid {
    position: absolute;
    left: 50%;
    max-width: 50%;
    transform: translateX(-50%);
    overflow: hidden;
    font-size: 17px;
    font-weight: 600;
    white-space: nowrap;
    text-overflow: ellipsis;
    opacity: 0;
    transition: opacity 160ms linear;
}
.navbar.under .nav-mid {
    opacity: 1;
}
.nav-end {
    display: flex;
    gap: 2px;
    margin-left: auto;
}
.icon-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    min-width: 44px;
    min-height: 44px;
    padding: 0 6px;
    border: 0;
    border-radius: 10px;
    background: none;
    color: var(--accent-text);
}
.icon-btn svg,
.ic,
.chev {
    width: 22px;
    height: 22px;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.8;
    stroke-linecap: round;
    stroke-linejoin: round;
}
.scroll {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
    padding: 0 16px 24px;
}
.big {
    margin: 2px 0 2px;
    font-size: 34px;
    font-weight: 700;
    line-height: 1.15;
    letter-spacing: 0.01em;
}
.big-sub {
    margin: 0 0 14px;
    color: var(--text-3);
    font-size: 15px;
}
.intro {
    margin: -6px 0 14px;
    color: var(--text-3);
    font-size: 15px;
    text-wrap: pretty;
}
/* grouped lists */
.group-head {
    margin: 22px 4px 7px;
    color: var(--text-3);
    font-size: 13px;
    font-weight: 600;
    text-transform: none;
}
.group-head:first-child {
    margin-top: 6px;
}
.group {
    overflow: hidden;
    border-radius: 12px;
    background: var(--raised);
}
.group-foot {
    margin: 7px 4px 0;
    color: var(--text-3);
    font-size: 13px;
}
.cell {
    position: relative;
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%;
    min-height: 48px;
    padding: 10px 14px;
    border: 0;
    background: none;
    text-align: left;
}
.cell + .cell::before,
.sw + .sw::before,
.cell + .sw::before,
.sw + .cell::before {
    content: "";
    position: absolute;
    top: 0;
    left: 14px;
    right: 0;
    z-index: 2;
    border-top: 1px solid var(--line);
}
.cell:active {
    background: var(--hover);
}
.cell-ic {
    display: flex;
    flex: none;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    border-radius: 8px;
    background: var(--sel);
    color: var(--text-2);
}
.cell-ic .ic {
    width: 18px;
    height: 18px;
}
.cell-main {
    flex: 1;
    min-width: 0;
}
.cell-title {
    display: block;
    font-size: 17px;
    line-height: 1.3;
}
.cell-sub {
    display: block;
    margin-top: 2px;
    color: var(--text-3);
    font-size: 14px;
    line-height: 1.3;
}
.cell-count {
    color: var(--text-3);
    font-size: 16px;
    font-variant-numeric: tabular-nums;
}
.cell-count.hot {
    min-width: 22px;
    padding: 1px 7px;
    border-radius: 11px;
    background: var(--accent);
    color: #fff;
    font-size: 14px;
    font-weight: 600;
    text-align: center;
}
.chev {
    flex: none;
    width: 16px;
    height: 16px;
    color: var(--text-3);
    stroke-width: 2.4;
}
.cell.danger .cell-title {
    color: var(--danger);
}
.cell.accent .cell-title {
    color: var(--accent-text);
}
/* state dots */
.dot {
    flex: none;
    width: 10px;
    height: 10px;
    border-radius: 50%;
    border: 2px solid var(--open);
}
.dot.doing {
    border-color: var(--progress);
    background: conic-gradient(var(--progress) 0 50%, transparent 0);
}
.dot.blocked {
    border-color: var(--blocking);
    background: var(--blocking);
}
.dot.review {
    border-color: var(--accent-text);
    background: var(--accent-text);
}
.dot.done {
    border-color: var(--good);
    background: var(--good);
}
.dot.waiting {
    border-color: var(--accent-text);
    background: var(--accent-text);
}
/* swipeable rows */
.sw {
    position: relative;
    overflow: hidden;
}
.sw-lead,
.sw-trail {
    position: absolute;
    top: 0;
    bottom: 0;
    display: flex;
}
.sw-lead {
    left: 0;
    right: 0;
    align-items: center;
    padding-left: 22px;
    background: var(--good);
    color: #0b1a10;
    font-weight: 600;
}
.sw-trail {
    right: 0;
}
.sw-act {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 84px;
    border: 0;
    color: #fff;
    font-size: 15px;
    font-weight: 600;
}
.sw-act.block {
    background: #a8792a;
}
.sw-act.start {
    background: #3f63ad;
}
.sw-act.more {
    background: #45474e;
}
.sw-front {
    position: relative;
    z-index: 1;
    background: var(--raised);
    touch-action: pan-y;
    transition: transform 260ms var(--push);
    user-select: none;
    -webkit-user-select: none;
    -webkit-touch-callout: none;
}
.sw-front.dragging {
    transition: none;
}
.sw-front .cell:active {
    background: none;
}
.meta {
    display: flex;
    flex-wrap: wrap;
    gap: 2px 8px;
    margin-top: 3px;
    color: var(--text-3);
    font-size: 14px;
}
.meta .hi {
    color: var(--danger);
}
.meta .why {
    color: var(--blocking);
}
.hint {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    margin: 0 0 12px;
    padding: 10px 12px;
    border-radius: 12px;
    background: var(--accent-dim);
    color: var(--text);
    font-size: 14px;
}
.hint button {
    flex: none;
    margin-left: auto;
    padding: 0 4px;
    border: 0;
    background: none;
    color: var(--accent-text);
    font-size: 14px;
}
/* segmented and chips */
.seg {
    display: flex;
    margin: 0 0 14px;
    padding: 2px;
    border-radius: 9px;
    background: var(--sel);
}
.seg button {
    flex: 1;
    min-height: 32px;
    border: 0;
    border-radius: 7px;
    background: none;
    color: var(--text-2);
    font-size: 14px;
    font-weight: 600;
}
.seg button.on {
    background: #3a3c43;
    color: var(--text);
}
.chips {
    display: flex;
    gap: 8px;
    margin: 0 -16px 14px;
    padding: 0 16px;
    overflow-x: auto;
    scrollbar-width: none;
}
.chip {
    display: flex;
    flex: none;
    align-items: center;
    gap: 6px;
    min-height: 34px;
    padding: 0 13px;
    border: 1px solid var(--border-2);
    border-radius: 17px;
    background: none;
    color: var(--text-2);
    font-size: 14px;
    font-weight: 600;
}
.chip.on {
    border-color: var(--accent);
    background: var(--accent-dim);
    color: var(--text);
}
.chip em {
    color: var(--text-3);
    font-style: normal;
    font-weight: 400;
}
.search {
    display: flex;
    align-items: center;
    gap: 8px;
    min-height: 38px;
    margin: 0 0 14px;
    padding: 0 10px;
    border-radius: 10px;
    background: var(--sel);
    color: var(--text-3);
}
.search input {
    flex: 1;
    min-width: 0;
    border: 0;
    background: none;
    color: var(--text);
    font: inherit;
    font-size: 17px;
    outline: none;
}
.search .ic {
    width: 17px;
    height: 17px;
}
.toolrow {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin: 0 0 10px;
}
.linkbtn {
    min-height: 36px;
    padding: 0 2px;
    border: 0;
    background: none;
    color: var(--accent-text);
    font-size: 15px;
}
.empty {
    padding: 28px 12px;
    color: var(--text-3);
    font-size: 15px;
    text-align: center;
}
/* bottom bars: new button and item actions */
.foot {
    flex: none;
    display: grid;
    gap: 8px;
    padding: 10px 16px 10px;
    border-top: 1px solid var(--line);
    background: var(--bg);
}
.foot-row {
    display: grid;
    grid-auto-columns: minmax(0, 1fr);
    grid-auto-flow: column;
    gap: 8px;
}
.btn {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    min-height: 50px;
    padding: 0 14px;
    border: 0;
    border-radius: 14px;
    background: var(--sel);
    color: var(--text);
    font-size: 17px;
    font-weight: 600;
}
.btn.primary {
    background: var(--accent);
    color: #fff;
}
.btn.good {
    background: #2f7a49;
    color: #fff;
}
.btn.danger {
    background: color-mix(in oklab, var(--danger) 22%, var(--sel));
    color: var(--danger);
}
.btn.small {
    min-height: 44px;
    padding: 0 8px;
    font-size: 15px;
}
.cell-hit {
    display: flex;
    flex: 1;
    align-items: center;
    gap: 12px;
    min-width: 0;
    min-height: 44px;
    padding: 0;
    border: 0;
    background: none;
    text-align: left;
}
.btn.plain {
    background: none;
    color: var(--accent-text);
}
.btn:disabled {
    opacity: 0.45;
}
.newbtn {
    display: flex;
    align-items: center;
    gap: 8px;
    min-height: 48px;
    padding: 0 4px;
    border: 0;
    background: none;
    color: var(--accent-text);
    font-size: 17px;
    font-weight: 600;
}
.newbtn .plus {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    border-radius: 50%;
    background: var(--accent);
    color: #fff;
}
.newbtn .plus .ic {
    width: 16px;
    height: 16px;
    stroke-width: 2.4;
}
.foot.new {
    display: flex;
    justify-content: space-between;
    padding: 4px 12px;
}
/* reader */
.kind {
    color: var(--text-3);
    font-size: 14px;
}
.title {
    margin: 4px 0 12px;
    font-size: 26px;
    font-weight: 700;
    line-height: 1.2;
    text-wrap: pretty;
}
.facts {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: 6px 16px;
    margin: 0 0 16px;
    padding: 12px 14px;
    border-radius: 12px;
    background: var(--raised);
    font-size: 15px;
}
.facts dt {
    color: var(--text-3);
}
.facts dd {
    margin: 0;
}
.facts dd.why {
    color: var(--blocking);
}
.body p {
    margin: 0 0 12px;
    text-wrap: pretty;
}
.body h2 {
    margin: 20px 0 8px;
    font-size: 19px;
}
.body ul {
    margin: 0 0 12px;
    padding-left: 20px;
}
.section-title {
    margin: 22px 0 8px;
    color: var(--text-3);
    font-size: 13px;
    font-weight: 600;
}
.comment {
    padding: 10px 0;
    border-top: 1px solid var(--line);
    font-size: 15px;
}
.comment b {
    font-size: 14px;
}
.comment span {
    color: var(--text-3);
    font-size: 13px;
}
.comment p {
    margin: 4px 0 0;
}
.options {
    display: grid;
    gap: 8px;
    margin: 10px 0 6px;
}
.option {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    min-height: 50px;
    padding: 12px 14px;
    border: 1px solid var(--border-2);
    border-radius: 14px;
    background: var(--raised);
    text-align: left;
}
.option.chosen {
    border-color: var(--good);
}
.option small {
    display: block;
    color: var(--text-3);
    font-size: 14px;
}
.rec {
    margin-left: auto;
    color: var(--accent-text);
    font-size: 12px;
    font-weight: 600;
    white-space: nowrap;
}
.phase {
    display: flex;
    gap: 12px;
    padding: 10px 0;
    border-top: 1px solid var(--line);
}
.phase .dot {
    margin-top: 6px;
}
.mono {
    font-family: var(--mono);
    font-size: 13px;
}
.term {
    padding: 12px;
    border-radius: 12px;
    background: #0b0c0e;
    color: #c9ccd2;
    font-family: var(--mono);
    font-size: 12.5px;
    line-height: 1.55;
    white-space: pre-wrap;
}
.diff {
    overflow: hidden;
    border-radius: 12px;
    background: #0f1012;
    font-family: var(--mono);
    font-size: 12px;
    line-height: 1.6;
}
.diff div {
    padding: 0 10px;
    white-space: pre-wrap;
}
.diff .add {
    background: oklch(0.62 0.1 150 / 0.12);
    color: oklch(0.8 0.08 150);
}
.diff .del {
    background: oklch(0.62 0.12 25 / 0.12);
    color: oklch(0.8 0.09 25);
}
.diff .hd {
    color: var(--text-3);
    background: var(--raised);
}
/* switches */
.switch {
    position: relative;
    flex: none;
    width: 51px;
    height: 31px;
    border: 0;
    border-radius: 16px;
    background: #3a3c43;
    transition: background 200ms;
}
.switch::after {
    content: "";
    position: absolute;
    top: 2px;
    left: 2px;
    width: 27px;
    height: 27px;
    border-radius: 50%;
    background: #fff;
    transition: transform 220ms var(--push);
}
.switch.on {
    background: #34a058;
}
.switch.on::after {
    transform: translateX(20px);
}
.bar {
    height: 8px;
    margin: 8px 0 4px;
    overflow: hidden;
    border-radius: 4px;
    background: var(--sel);
}
.bar i {
    display: block;
    height: 100%;
    border-radius: 4px;
    background: var(--progress);
}
/* home cards */
.card {
    margin: 0 0 18px;
}
.card-head {
    display: flex;
    align-items: baseline;
    gap: 8px;
    margin: 0 4px 7px;
}
.card-head h2 {
    margin: 0;
    font-size: 20px;
    font-weight: 700;
}
.card-head span {
    color: var(--text-3);
    font-size: 15px;
}
.card-head .linkbtn {
    margin-left: auto;
}
.card-none {
    margin: 0 4px 14px;
    color: var(--text-3);
    font-size: 15px;
}
.new-mark {
    padding: 1px 6px;
    border-radius: 9px;
    background: var(--accent);
    color: #fff;
    font-size: 12px;
    font-weight: 600;
}
/* tabs */
.tabs {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 10;
    display: flex;
    padding: 0 8px var(--safe-bottom);
    border-top: 1px solid var(--line);
    background: rgb(19 20 22 / 94%);
    backdrop-filter: blur(20px);
}
.tab {
    display: flex;
    flex: 1;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    min-height: 50px;
    padding: 5px 0 3px;
    border: 0;
    background: none;
    color: var(--text-3);
}
.tab.on {
    color: var(--accent-text);
}
.tab .ic {
    width: 25px;
    height: 25px;
}
.tab-label {
    font-size: 10.5px;
    font-weight: 600;
}
.tab-icon {
    position: relative;
    display: flex;
}
.badge {
    position: absolute;
    top: -5px;
    left: calc(100% - 9px);
    min-width: 18px;
    height: 18px;
    padding: 0 5px;
    border-radius: 9px;
    background: var(--badge);
    color: #fff;
    font-size: 12px;
    font-weight: 600;
    line-height: 18px;
    text-align: center;
}
/* chat */
.chat-top {
    display: flex;
    flex: none;
    align-items: center;
    gap: 8px;
    min-height: 52px;
    padding: 0 12px 0 16px;
}
.place {
    display: flex;
    flex: 1;
    flex-direction: column;
    min-width: 0;
    min-height: 44px;
    justify-content: center;
    padding: 0;
    border: 0;
    background: none;
    text-align: left;
}
.place-name {
    display: flex;
    min-width: 0;
    white-space: nowrap;
    align-items: center;
    gap: 7px;
    font-size: 17px;
    font-weight: 600;
}
.place-dot {
    flex: none;
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: #c98a5e;
}
.place-env {
    color: var(--text-3);
    font-size: 13px;
}
.pill {
    display: flex;
    align-items: center;
    gap: 6px;
    min-height: 32px;
    padding: 0 11px;
    border: 0;
    border-radius: 16px;
    background: var(--sel);
    color: var(--text-2);
    font-size: 13px;
    font-weight: 600;
    white-space: nowrap;
}
.pill .live {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--good);
    box-shadow: 0 0 0 3px color-mix(in oklab, var(--good) 25%, transparent);
}
.pill .live.paused {
    background: var(--blocking);
    box-shadow: none;
}
.needs {
    display: flex;
    flex: none;
    align-items: center;
    gap: 10px;
    margin: 0 12px 6px;
    padding: 9px 12px;
    border: 0;
    border-radius: 12px;
    background: var(--accent-dim);
    text-align: left;
}
.needs b {
    font-size: 15px;
}
.needs span {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    color: var(--text-2);
    font-size: 14px;
    white-space: nowrap;
    text-overflow: ellipsis;
}
.feed {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    padding: 8px 16px 12px;
}
.day {
    margin: 10px 0;
    color: var(--text-3);
    font-size: 12px;
    font-weight: 600;
    text-align: center;
}
.msg {
    margin: 0 0 14px;
    user-select: none;
    -webkit-user-select: none;
    -webkit-touch-callout: none;
}
.msg.me {
    display: flex;
    justify-content: flex-end;
}
.msg.me .bubble {
    max-width: 82%;
    padding: 9px 13px;
    border-radius: 18px 18px 6px 18px;
    background: var(--accent);
    color: #fff;
}
.msg.agent .bubble {
    max-width: 100%;
}
.msg .who {
    display: block;
    margin-bottom: 2px;
    color: var(--text-3);
    font-size: 12px;
    font-weight: 600;
}
.msg p {
    margin: 0 0 6px;
}
.msg p:last-child {
    margin: 0;
}
.ref {
    display: inline-flex;
    text-align: left;
    align-items: center;
    gap: 5px;
    margin: 2px 2px;
    padding: 2px 9px;
    border: 1px solid var(--border-2);
    border-radius: 8px;
    background: var(--raised);
    color: var(--text);
    font-size: 14px;
}
.ref .dot {
    width: 8px;
    height: 8px;
}
.pinned {
    display: flex;
    flex: none;
    align-items: center;
    gap: 8px;
    margin: 0 12px 6px;
    padding: 8px 12px;
    border: 1px solid var(--border-2);
    border-radius: 12px;
    font-size: 14px;
}
.pinned span {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}
.dock {
    flex: none;
    padding: 6px 10px 8px;
}
.working {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 0 6px 6px;
    color: var(--text-3);
    font-size: 13px;
}
.compose {
    display: flex;
    align-items: flex-end;
    gap: 6px;
}
.compose textarea {
    flex: 1;
    min-height: 40px;
    max-height: 120px;
    padding: 9px 14px;
    border: 1px solid var(--border-2);
    border-radius: 20px;
    background: var(--raised);
    color: var(--text);
    font: inherit;
    font-size: 17px;
    resize: none;
    outline: none;
}
.round {
    display: flex;
    flex: none;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border: 0;
    border-radius: 50%;
    background: var(--sel);
    color: var(--text-2);
}
.round.send {
    background: var(--accent);
    color: #fff;
}
/* sheets */
.scrim {
    position: absolute;
    inset: 0;
    z-index: 50;
    background: rgb(0 0 0 / 50%);
    animation: fade 200ms linear;
}
@keyframes fade {
    from {
        opacity: 0;
    }
}
.sheet {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 51;
    display: flex;
    flex-direction: column;
    max-height: 88%;
    padding: 6px 16px calc(12px + var(--safe-bottom));
    border-radius: 16px 16px 0 0;
    background: #1c1d21;
    animation: rise 380ms var(--push);
}
@keyframes rise {
    from {
        transform: translateY(100%);
    }
}
.grab {
    flex: none;
    width: 38px;
    height: 5px;
    margin: 2px auto 10px;
    border-radius: 3px;
    background: #44464d;
}
.sheet-head {
    display: flex;
    flex: none;
    align-items: center;
    gap: 8px;
    margin: 0 0 4px;
}
.sheet-head h2 {
    flex: 1;
    margin: 0;
    font-size: 20px;
    font-weight: 700;
    text-wrap: pretty;
}
.sheet-sub {
    flex: none;
    margin: 0 0 12px;
    color: var(--text-3);
    font-size: 15px;
    text-wrap: pretty;
}
.sheet-body {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    margin: 0 -16px;
    padding: 0 16px 4px;
}
.sheet .group {
    background: #26272c;
}
.sheet .search,
.sheet .seg {
    background: #2b2c32;
}
.sheet-foot {
    display: grid;
    flex: none;
    gap: 8px;
    padding-top: 12px;
}
.field {
    display: grid;
    gap: 6px;
    margin: 0 0 12px;
}
.field label {
    color: var(--text-2);
    font-size: 14px;
    font-weight: 600;
}
.field input,
.field textarea {
    width: 100%;
    min-height: 46px;
    padding: 11px 13px;
    border: 1px solid var(--border-2);
    border-radius: 12px;
    background: #26272c;
    color: var(--text);
    font: inherit;
    font-size: 17px;
    outline: none;
}
.field textarea {
    min-height: 96px;
    resize: none;
}
.field input:focus,
.field textarea:focus {
    border-color: var(--accent);
}
.check {
    margin-left: auto;
    color: var(--accent-text);
}
.faces {
    display: flex;
    justify-content: space-between;
    margin: 0 0 12px;
    padding: 8px 10px;
    border-radius: 14px;
    background: #26272c;
}
.faces button {
    flex: 1;
    min-width: 44px;
    height: 44px;
    border: 0;
    border-radius: 50%;
    background: none;
    font-size: 15px;
    font-weight: 700;
    color: var(--text-2);
}
.share-link {
    padding: 12px;
    border-radius: 12px;
    background: #26272c;
    font-family: var(--mono);
    font-size: 13px;
    word-break: break-all;
}
/* toast */
.toast {
    position: absolute;
    left: 12px;
    right: 12px;
    bottom: calc(62px + var(--safe-bottom));
    z-index: 60;
    display: flex;
    align-items: center;
    gap: 10px;
    min-height: 48px;
    padding: 6px 8px 6px 16px;
    border-radius: 14px;
    background: #2c2e34;
    box-shadow: 0 8px 24px rgb(0 0 0 / 45%);
    font-size: 15px;
    animation: rise 300ms var(--push);
}
.phone:not(.has-tabs) .toast {
    bottom: calc(84px + var(--safe-bottom));
}
.toast span {
    flex: 1;
}
.toast button {
    min-height: 36px;
    padding: 0 10px;
    border: 0;
    background: none;
    color: var(--accent-text);
    font-weight: 600;
}
.ms-hide {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
}
</style>
</head>
<body>
<div class="page">
    <aside class="notes" id="notes"></aside>
    <div class="stage">
        <div class="phone has-tabs" id="phone" data-screen-label="Phone prototype"></div>
    </div>
</div>
<script>
"use strict";
/* ---------------- icons (original, simple strokes) ---------------- */
const PATHS = {
    chat: '<path d="M4 5.5h16v10.5H10l-6 4.5z"/>',
    home: '<path d="M4 11 12 4l8 7v9h-5.5v-5.5h-5V20H4z"/>',
    todo: '<circle cx="12" cy="12" r="8.5"/><path d="m8.4 12.3 2.6 2.6 4.8-5.2"/>',
    journal: '<rect x="5" y="3.5" width="14" height="17" rx="2.5"/><path d="M9 8.5h6M9 12h6M9 15.5h3.5"/>',
    right: '<path d="m9.5 6 6 6-6 6"/>',
    back: '<path d="m15 5.5-6.5 6.5 6.5 6.5"/>',
    plus: '<path d="M12 5v14M5 12h14"/>',
    search: '<circle cx="10.5" cy="10.5" r="6"/><path d="m15 15 5 5"/>',
    share: '<path d="M12 3.5v11M8 7.5l4-4 4 4M6 11v8.5h12V11"/>',
    more: '<circle cx="6" cy="12" r="1.2"/><circle cx="12" cy="12" r="1.2"/><circle cx="18" cy="12" r="1.2"/>',
    send: '<path d="M12 19V6M6.5 11.5 12 6l5.5 5.5"/>',
    down: '<path d="m6 9.5 6 6 6-6"/>',
    close: '<path d="m6.5 6.5 11 11M17.5 6.5l-11 11"/>',
    flag: '<path d="M6 20.5V4M6 4.5h11l-2.5 4 2.5 4H6"/>',
    bulb: '<path d="M9 17.5h6M10 20.5h4M8 13.5a5.5 5.5 0 1 1 8 0c-1 1-1.4 2-1.5 3.5h-5c-.1-1.5-.5-2.5-1.5-3.5z"/>',
    folder: '<path d="M3.5 7V18.5h17V8.5H12L10 6H3.5z"/>',
    board: '<rect x="4" y="4.5" width="4.5" height="15" rx="1.2"/><rect x="10" y="4.5" width="4.5" height="10" rx="1.2"/><rect x="16" y="4.5" width="4" height="12.5" rx="1.2"/>',
    file: '<path d="M6.5 3.5h7.5l4 4v13h-11.5z"/><path d="M13.5 3.5V8h4.5"/>',
    report: '<rect x="5" y="4" width="14" height="16.5" rx="2"/><path d="M9 15.5v-3M12 15.5V9M15 15.5v-5"/>',
    bolt: '<path d="M13 3.5 5.5 13.5H11L10 20.5l8-10.5h-5.5z"/>',
    steps: '<path d="M9 6.5h11M9 12h11M9 17.5h11"/><circle cx="5" cy="6.5" r="1.2"/><circle cx="5" cy="12" r="1.2"/><circle cx="5" cy="17.5" r="1.2"/>',
    checkbox: '<rect x="4.5" y="4.5" width="15" height="15" rx="3"/><path d="m8.5 12.2 2.4 2.4 4.6-4.9"/>',
    plug: '<path d="M9 3.5v5M15 3.5v5M6.5 8.5h11V12a5.5 5.5 0 0 1-11 0zM12 17.5v3"/>',
    gear: '<circle cx="12" cy="12" r="3"/><path d="M12 3.5v2.5M12 18v2.5M3.5 12H6M18 12h2.5M6 6l1.8 1.8M16.2 16.2 18 18M6 18l1.8-1.8M16.2 7.8 18 6"/>',
    mail: '<rect x="3.5" y="5.5" width="17" height="13" rx="2"/><path d="m4 7 8 6 8-6"/>',
    pin: '<path d="M12 21v-6M7.5 15h9l-1.5-4V5h-6v6z"/>',
    clock: '<circle cx="12" cy="12" r="8.5"/><path d="M12 7.5V12l3 2"/>',
    rules: '<path d="M8.5 6.5h11M8.5 12h11M8.5 17.5h11M4.5 6.5h.5M4.5 12h.5M4.5 17.5h.5"/>',
    wrench: '<path d="M14.5 4a4.5 4.5 0 0 0 4.9 6.1L11 18.5a2 2 0 0 1-2.9-2.9l8.4-8.4"/>',
    ticket: '<path d="M4 7.5h16v3a1.8 1.8 0 0 0 0 3.5v3H4v-3a1.8 1.8 0 0 0 0-3.5z"/>',
    book: '<path d="M5 4.5h10.5a3 3 0 0 1 3 3v12H8a3 3 0 0 1-3-3z"/><path d="M5 16.5a3 3 0 0 1 3-3h10.5"/>',
    copy: '<rect x="8" y="8" width="11.5" height="11.5" rx="2"/><path d="M5 15.5V5.5a1 1 0 0 1 1-1h9.5"/>',
    face: '<circle cx="12" cy="12" r="8.5"/><path d="M8.5 14.5c1.8 2 5.2 2 7 0M9.5 10h.01M14.5 10h.01"/>',
    reply: '<path d="M10 7 4.5 12 10 17M5 12h9a5.5 5.5 0 0 1 5.5 5.5"/>',
    people: '<circle cx="9" cy="9" r="3.2"/><path d="M3.5 19a5.5 5.5 0 0 1 11 0"/><circle cx="16.5" cy="8.5" r="2.6"/><path d="M15.5 13.6A4.6 4.6 0 0 1 21 18"/>',
    branch: '<circle cx="7" cy="5.5" r="2"/><circle cx="7" cy="18.5" r="2"/><circle cx="17" cy="8.5" r="2"/><path d="M7 7.5v9M17 10.5c0 4-6 3-9.5 6.5"/>',
    play: '<path d="M8 5.5v13l10-6.5z"/>',
    bot: '<rect x="5" y="8" width="14" height="11" rx="3"/><path d="M12 4.5V8M9.5 13h.01M14.5 13h.01"/>',
    terminal: '<rect x="3.5" y="5" width="17" height="14" rx="2"/><path d="m7.5 10 2.5 2-2.5 2M12 15h4.5"/>',
    eye: '<path d="M2.5 12S6 5.5 12 5.5 21.5 12 21.5 12 18 18.5 12 18.5 2.5 12 2.5 12z"/><circle cx="12" cy="12" r="2.8"/>',
    info: '<circle cx="12" cy="12" r="8.5"/><path d="M12 11v5.5M12 7.8h.01"/>',
    shield: '<path d="M12 3.5 19 6v6c0 4.5-3 7.5-7 8.5-4-1-7-4-7-8.5V6z"/>',
    bell: '<path d="M6.5 16.5V11a5.5 5.5 0 0 1 11 0v5.5l1.5 1.5H5zM10 20.5h4"/>',
    phone: '<rect x="7" y="3" width="10" height="18" rx="2.5"/><path d="M11 17.5h2"/>',
    help: '<circle cx="12" cy="12" r="8.5"/><path d="M9.6 9.5a2.5 2.5 0 1 1 3.4 2.3c-.7.3-1 .8-1 1.5v.5M12 16.8h.01"/>',
    dots: '<circle cx="12" cy="12" r="1.5"/>',
    inbox: '<path d="M4 13.5 6.5 5.5h11l2.5 8v5H4z"/><path d="M4 13.5h4.5l1 2h5l1-2H20"/>',
    template: '<rect x="4.5" y="4.5" width="15" height="15" rx="2"/><path d="M4.5 9.5h15M9.5 9.5v10"/>',
    voice: '<path d="M5 18.5V7.5a3 3 0 0 1 3-3h8a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H9z"/><path d="M9 9.5h6M9 12.5h4"/>',
    link: '<path d="M10 14a4 4 0 0 0 5.7 0l3-3a4 4 0 0 0-5.7-5.7l-1 1M14 10a4 4 0 0 0-5.7 0l-3 3a4 4 0 0 0 5.7 5.7l1-1"/>',
    work: '<circle cx="12" cy="12" r="8.5"/><path d="M10 8.5v7l5.5-3.5z"/>',
    star: '<path d="m12 4 2.4 5 5.4.6-4 3.7 1.1 5.4L12 16l-4.9 2.7 1.1-5.4-4-3.7 5.4-.6z"/>',
};
const icon = (name, cls = "ic") => `<svg class="${cls}" viewBox="0 0 24 24" aria-hidden="true">${PATHS[name] || PATHS.dots}</svg>`;
const esc = (t) => String(t ?? "").replace(/[&<>"]/g, (c) => ({"&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;"})[c]);
const enc = (o) => esc(JSON.stringify(o));
/* ---------------- the kinds of things the journal keeps ---------------- */
const KINDS = {
    todo: {one: "To-do", many: "To-dos", icon: "todo", intro: "What the agent is asked to do in this environment.", make: "New to-do", lead: "done", trail: ["block", "more"]},
    plan: {one: "Plan", many: "Plans", icon: "flag", intro: "Plans the agent wrote. Approve one and it starts.", make: "New plan", trail: ["more"]},
    suggestion: {one: "Suggestion", many: "Suggestions", icon: "bulb", intro: "Things the agent proposes. Pick an answer or write your own.", trail: ["more"]},
    question: {one: "Question", many: "Questions", icon: "help", intro: "Questions the agent asked you.", trail: ["more"]},
    collection: {one: "Collection", many: "Collections", icon: "folder", intro: "Items and files you grouped together.", make: "New collection", trail: ["more"]},
    doc: {one: "Document", many: "Documents", icon: "file", intro: "Documents of the whole project: designs, briefs, notes.", make: "New document", trail: ["more"]},
    report: {one: "Report", many: "Reports", icon: "report", intro: "What the agent reports when a job ends.", lead: "close", trail: ["more"]},
    message: {one: "Message", many: "Messages", icon: "mail", intro: "Messages between agents and from helpers.", lead: "close", trail: ["more"]},
    fact: {one: "Fact", many: "Facts", icon: "pin", intro: "Things the agent learned about this project and keeps in mind.", make: "New fact", trail: ["more"]},
    reminder: {one: "Reminder", many: "Reminders", icon: "clock", intro: "Things the agent is reminded of at set moments.", make: "New reminder", trail: ["more"]},
    rule: {one: "Rule", many: "Rules", icon: "rules", intro: "Rules the agent follows in every session.", make: "New rule", trail: ["more"]},
    tool: {one: "Tool", many: "Tools", icon: "wrench", intro: "Commands the agent may run for this project.", make: "New tool", trail: ["more"]},
    ticket: {one: "Ticket", many: "Tickets", icon: "ticket", intro: "Work for the board, each done by its own agent.", make: "New ticket", trail: ["more"]},
    trigger: {one: "Trigger", many: "Triggers", icon: "bolt", intro: "Words or moments that start something by themselves.", make: "New trigger", trail: ["more"]},
    sequence: {one: "Sequence", many: "Sequences", icon: "steps", intro: "Steps the agent goes through in order.", make: "New sequence", trail: ["more"]},
    check: {one: "Check", many: "Checks", icon: "checkbox", intro: "Checks that run over the code and report what they find.", trail: ["more"]},
    skill: {one: "Skill", many: "Skills", icon: "book", intro: "The instructions the agent loads for each kind of work.", trail: ["more"]},
    template: {one: "Template", many: "Templates", icon: "template", intro: "Starting points for new documents and plans.", make: "New template", trail: ["more"]},
    profile: {one: "Way of talking", many: "Ways of talking", icon: "voice", intro: "How the agent addresses you and writes to you.", make: "New way of talking", trail: ["more"]},
    share: {one: "Share link", many: "Share links", icon: "link", intro: "Links that let someone else read one item.", trail: ["more"]},
    plugin: {one: "Plugin", many: "Plugins", icon: "plug", intro: "Installed plugins, with their pages and settings.", trail: ["more"]},
    work: {one: "Work", many: "Work", icon: "work", intro: "Work the agents are doing right now.", trail: ["more"]},
    agent: {one: "Agent", many: "Agents", icon: "bot", intro: "The main agent and its helpers.", trail: ["more"]},
};
const ago = (m) => (m < 1 ? "just now" : m < 60 ? `${m} min ago` : m < 1440 ? `${Math.round(m / 60)} h ago` : `${Math.round(m / 1440)} d ago`);
const D = {
    todo: [
        {n: 140, title: "Show to-dos on the phone", state: "doing", priority: "High", assigned: "Frontend", made: 180, body: ["The phone shows to-dos the way the desktop does: by state, with the same actions.", "Open questions go to the plan, not here."], comments: [{who: "You", at: 50, text: "Keep the swipe to mark done."}], after: []},
        {n: 144, title: "Pick which tab opens first", state: "review", priority: "Normal", assigned: "Frontend", made: 90, body: ["The agent needs your answer before it goes on."], comments: [], after: []},
        {n: 141, title: "Swipe a to-do to finish or block it", state: "todo", priority: "Normal", assigned: "Frontend", made: 170, body: ["Swipe right to mark it done. Swipe left to block it or see every action."], comments: [], after: [140]},
        {n: 142, title: "Fit the board's lanes on a narrow screen", state: "todo", priority: "Normal", assigned: "Frontend", made: 160, body: ["One lane at a time, with the lane names in a row of buttons above."], comments: [], after: []},
        {n: 143, title: "Open sheets from the bottom of the screen", state: "todo", priority: "Low", assigned: "", made: 150, body: ["Every choice opens in a sheet that slides up, so the thumb reaches it."], comments: [], after: []},
        {n: 138, title: "Send a phone alert when a to-do is blocked", state: "blocked", why: "Waits for the tunler account to allow a second phone.", priority: "Normal", assigned: "Backend", made: 2900, body: ["When the agent blocks a to-do, the phone gets an alert."], comments: [{who: "Agent", at: 2800, text: "Blocked until the second phone is allowed."}], after: []},
        {n: 133, title: "Search finds files attached to documents", state: "done", priority: "Normal", assigned: "Backend", made: 4300, outcome: "Files attached to documents show under their document.", body: ["Done."], comments: [], after: []},
        {n: 131, title: "Remember the text size in the reader", state: "done", priority: "Low", assigned: "Frontend", made: 5800, outcome: "Stored per phone.", body: ["Done."], comments: [], after: []},
    ],
    plan: [
        {n: 21, title: "The phone reaches every desktop place", state: "waiting", made: 40, goal: "Everything the desktop offers can be found and done on the phone.", phases: [{t: "Inventory of desktop places and actions", s: "done"}, {t: "Four tabs: Chat, Home, To-dos, Journal", s: "todo"}, {t: "Lists with swipe actions and bottom sheets", s: "todo"}, {t: "Settings, plugins and services on the phone", s: "todo"}], oneTree: false, comments: []},
        {n: 20, title: "Board lanes on narrow screens", state: "doing", made: 1500, goal: "The board works at phone width.", phases: [{t: "Lane buttons", s: "done"}, {t: "One lane at a time", s: "doing"}, {t: "Move a card from its sheet", s: "todo"}], oneTree: true, comments: []},
        {n: 18, title: "Plain words in every viewer heading", state: "done", made: 9000, goal: "Every heading says what it is about.", phases: [{t: "Headings", s: "done"}], oneTree: false, comments: []},
    ],
    suggestion: [
        {n: 33, title: "Group the Journal tab by who uses it", state: "waiting", made: 30, body: ["The Journal tab could list places in the order people use them most."], choices: [{l: "Yes, group it that way", note: "To-dos and plans first, setup last", rec: true}, {l: "Keep the desktop order", note: "The same order as the sidebar"}], chosen: "", comments: []},
        {n: 32, title: "Show a count beside each tab", state: "done", made: 3000, body: ["Answered."], choices: [{l: "Only for Home", rec: true}, {l: "On every tab"}], chosen: "Only for Home", comments: []},
    ],
    question: [
        {n: 51, title: "Which tab should the phone open on?", state: "waiting", made: 12, body: ["When you open the app, it starts on one tab."], choices: [{l: "Chat", note: "Where you talk to the agent", rec: true}, {l: "Home", note: "What needs you and your lists"}], chosen: "", comments: []},
    ],
    collection: [
        {n: 4, title: "Phone screenshots", state: "open", made: 400, body: ["Screens to compare with the desktop."], items: ["doc:88", "todo:140", "report:61"], comments: []},
        {n: 3, title: "Release notes drafts", state: "open", made: 6000, body: ["Drafts for the next releases."], items: ["doc:87"], comments: []},
    ],
    doc: [
        {n: 88, title: "Phone navigation", state: "final", made: 200, summary: "Four tabs and where every desktop place lives.", sections: [{t: "Must have", b: "Every desktop place has a place on the phone. Every action is one or two taps from its item."}, {t: "Nice to have", b: "A search that reaches every kind."}], files: ["tabs.png"], comments: [{who: "You", at: 100, text: "Looks right."}]},
        {n: 87, title: "Swipe actions brief", state: "draft", made: 600, summary: "Which swipe does what, per kind.", sections: [{t: "Draft", b: "Right swipe marks done. Left swipe shows block and more."}], files: [], comments: []},
    ],
    report: [
        {n: 61, title: "Coverage reached 97 percent", state: "new", made: 25, body: ["The test suite now covers 97 percent of the code.", "Three files still fall short; they are listed in the check."], comments: []},
        {n: 60, title: "The dashboard formats its lines a third faster", state: "read", made: 1300, body: ["Actions are looked up once per kind."], comments: []},
    ],
    message: [{n: 12, title: "From helper Ada: the lanes are done", state: "new", made: 35, body: ["The lane buttons work. Ready for review."], comments: []}],
    fact: [{n: 9, title: "The phone app reads the same journal as the viewer", state: "open", made: 4000, body: ["Nothing is copied; the phone asks your computer."], comments: []}],
    reminder: [{n: 5, title: "Release after every significant change", state: "open", made: 9000, body: ["At the end of each change: bump, changelog, tag and push."], when: "After each commit", comments: []}],
    rule: [{n: 7, title: "Every viewer heading says plainly what it is about", state: "open", made: 12000, body: ["A heading names what the user is choosing or reading."], pinned: true, comments: []}],
    tool: [{n: 3, title: "Screenshot a page", state: "open", made: 7000, body: ["Takes a picture of a viewer page at phone width."], comments: []}],
    ticket: [{n: 14, title: "Lanes: narrow layout", state: "doing", stage: "Doing", made: 300, body: ["One lane at a time on a phone."], comments: []}],
    trigger: [{n: 4, title: "When a plan is approved", state: "open", made: 8000, words: ["approve", "approved"], does: "Start the sequence Ship it", on: true, comments: []}],
    sequence: [{n: 2, title: "Ship it", state: "open", made: 8000, steps: ["Run the checks", "Bump the version", "Write the changelog", "Tag and push"], running: "Step 2 of 4, started 3 min ago", comments: []}],
    check: [{n: 6, title: "Code commandments", state: "open", made: 9000, last: "2 findings, 14 min ago", findings: ["phone/PhoneBoard.vue:88 · a guard written twice", "api/phone.js:45 · a string key where a type fits"], comments: []}],
    skill: [
        {n: 1, title: "Reports", state: "open", made: 0, body: ["How to write a report the user reads on a phone."], loads: true, comments: []},
        {n: 2, title: "Memory", state: "open", made: 0, body: ["What to keep as a fact and what to forget."], loads: false, comments: []},
        {n: 3, title: "Python: flow", state: "open", made: 0, body: ["Check preconditions at the top and leave early."], loads: false, comments: []},
    ],
    template: [{n: 2, title: "Bug report", state: "open", made: 9000, body: ["What happened, what you expected, how to repeat it."], comments: []}],
    profile: [{n: 1, title: "Plain and short", state: "open", made: 9000, body: ["Short sentences. Says you, never the user."], comments: []}],
    share: [{n: 3, title: "Phone navigation", state: "open", made: 150, body: ["Anyone with the link can read document 88."], url: "https://jesse.tunler.dev/s/7fq2-phone-navigation", opens: 4, comments: []}],
    plugin: [
        {n: 1, title: "tunler", state: "open", made: 9000, body: ["Reaches this journal from your phone and from share links."], version: "1.8.2", pages: [], services: ["Phone tunnel"], settingsList: ["Domain: jesse.tunler.dev"], comments: []},
        {n: 2, title: "worldwatch-pages", state: "open", made: 9000, body: ["Builds the worldwatch pages and shows them in the viewer."], version: "0.4.0", pages: ["Pages preview"], services: ["Pages web server"], settingsList: ["Build on save: on"], comments: []},
    ],
    work: [{n: 9, title: "Phone: swipe rows", state: "doing", made: 12, body: ["Helper Ada is working in its own copy of the code."], comments: []}],
    agent: [
        {n: 1, title: "Main agent", state: "doing", made: 3, body: ["Working on to-do 140."], comments: []},
        {n: 2, title: "Helper Ada", state: "doing", made: 12, body: ["Short errand: the lane buttons."], comments: []},
        {n: 3, title: "Helper Grace", state: "review", made: 20, body: ["Asks for permission to run the test suite."], comments: []},
    ],
};
const ENVS = [
    {name: "main", running: true, note: "Agent working"},
    {name: "phone-tabs", running: true, note: "Agent working"},
    {name: "billing-fix", running: false, note: "No agent running"},
];
const JOURNALS = [
    {name: "agent-journal", color: "#c98a5e", note: "3 environments", needs: 3},
    {name: "worldwatch", color: "#5fa8a0", note: "1 environment", needs: 0},
    {name: "tunler", color: "#8a7fd6", note: "Stopped", needs: 0, stopped: true},
];
const ROLES = ["Frontend", "Backend", "Reviewer", "Nobody"];
const LANES = [
    {key: "todo", title: "To do"},
    {key: "blocked", title: "Held"},
    {key: "doing", title: "Doing"},
    {key: "review", title: "Needs you"},
    {key: "done", title: "Done"},
];
const STATE_WORD = {todo: "To do", doing: "Doing", blocked: "Blocked", review: "Needs you", done: "Done", waiting: "Waiting for you", open: "Open", final: "Final", draft: "Draft", new: "New", read: "Read", closed: "Closed"};
const DOMAINS = [
    {name: "Phone app", roles: [{t: "Frontend", who: "Main agent", now: true}, {t: "Reviewer", who: "Helper Grace", now: false}]},
    {name: "Journal server", roles: [{t: "Backend", who: "Helper Ada", now: true}]},
];
const SERVICES = [
    {n: 1, title: "Journal server", state: "running", by: "The journal", log: "12:04:11 listening on 127.0.0.1:8640\n12:04:12 phone paired: iPhone\n12:31:40 row update todo:140"},
    {n: 2, title: "Phone tunnel", state: "running", by: "tunler plugin", log: "12:04:15 tunnel open: jesse.tunler.dev\n12:20:02 phone connected"},
    {n: 3, title: "Pages web server", state: "stopped", by: "worldwatch-pages plugin", log: "11:58:00 stopped by you"},
];
const FEATURES = [
    {k: "plans", t: "Plans", line: "The agent writes a plan and waits for your approval.", on: true},
    {k: "board", t: "Board", line: "To-dos in lanes, from To do to Done.", on: true},
    {k: "helpers", t: "Helpers", line: "The agent sends short errands to separate agents.", on: true},
    {k: "triggers", t: "Triggers", line: "Words or moments start sequences by themselves.", on: true},
    {k: "checks", t: "Checks", line: "Checks run over the code after each change.", on: true},
    {k: "reminders", t: "Reminders", line: "The agent is reminded of things at set moments.", on: true},
    {k: "autoarchive", t: "Close finished items by themselves", line: "Done items close after a day.", on: false},
    {k: "autoupdate", t: "Update the journal by itself", line: "New versions install when the agent is idle.", on: true},
];
const CHAT_VIEWS = [
    {k: "thoughts", t: "What the agent is thinking", on: false},
    {k: "skills", t: "Skills the agent loads", on: true},
    {k: "subagents", t: "Subagents", on: true},
    {k: "made", t: "Documents the agent made", on: true},
    {k: "commands", t: "Long commands and what they printed", on: false},
    {k: "commits", t: "Commits", on: true},
    {k: "rules", t: "Rules, facts and reminders", on: false},
    {k: "triggers", t: "Triggers and sequences", on: false},
];
const FILES = [
    {path: "src/web/src/phone", dir: true},
    {path: "src/web/src/phone/PhoneTabs.vue", size: "2 KB", attached: ""},
    {path: "src/web/src/phone/PhoneBoard.vue", size: "9 KB", attached: ""},
    {path: "docs/phone-navigation.md", size: "4 KB", attached: "Document 88"},
    {path: "tabs.png", size: "180 KB", attached: "Document 88"},
];
const CHAT = [
    {id: 1, who: "me", at: "11:58", text: "I would like to see the to-dos on the phone too."},
    {id: 2, who: "agent", at: "11:59", text: "Started on it. The plan is ready for you to read:", refs: ["plan:21"]},
    {id: 3, who: "agent", at: "12:21", text: "Helper Ada finished the lane buttons and committed them.", refs: ["commit:9f2d76a", "message:12"]},
    {id: 4, who: "agent", at: "12:30", text: "One question before I go on:", refs: ["question:51"]},
    {id: 5, who: "agent", at: "12:34", text: "Working on the to-do list now.", refs: ["todo:140"]},
];
D.plan.splice(2, 0, {n: 19, title: "Phone alerts for blocked to-dos", state: "approved", made: 2000, goal: "The phone tells you when a to-do is blocked.", phases: [{t: "Alert", s: "todo"}], oneTree: false, comments: []});
/* ---------------- state ---------------- */
const S = {
    opt: new URLSearchParams(location.search).get("option") || "A",
    tab: "chat",
    stacks: {chat: [], home: [], todos: [], journal: []},
    sheet: null,
    sheets: [],
    toast: null,
    anim: "",
    todoView: "list",
    lane: "doing",
    showDone: false,
    shown: {},
    sort: {},
    hint: true,
    editCards: false,
    cards: ["todo", "question", "plan", "report", "doc", "work", "agent"],
    agent: {state: "working", auto: true, mode: "builder", context: 62},
    env: "main",
    journal: "agent-journal",
    pinned: null,
    doneLane: true,
    size: 0,
    chat: CHAT.slice(),
    scroll: {},
};
let FNS = [];
const on = (fn) => (FNS.push(fn), `data-fn="${FNS.length - 1}"`);
const parse = (ref) => {
    const [type, n] = ref.split(":");
    return {type, n: Number(n)};
};
const find = (ref) => {
    const {type, n} = parse(ref);
    return (D[type] || []).find((i) => i.n === n);
};
const refOf = (type, i) => `${type}:${i.n}`;
const name = (type, i) => `${KINDS[type].one.toLowerCase()} ${i.n}`;
const closedStates = ["done", "closed", "read"];
const isClosed = (i) => closedStates.includes(i.state);
const waitingRefs = () => [
    "question:51",
    "plan:21",
    "suggestion:33",
    "todo:144",
    "report:61",
    "agent:3",
].filter((ref) => {
    const i = find(ref);
    return i && ["waiting", "review", "new"].includes(i.state) && !(i.chosen);
});
const WAIT_WORD = {question: "Question", plan: "Plan to approve", suggestion: "Suggestion", todo: "To-do waits for you", report: "New report", agent: "Asks for permission"};
function render() {
    const keep = document.querySelector("#phone .scroll, #phone .feed");
    if (keep && S.lastKey) S.scroll[S.lastKey] = keep.scrollTop;
    FNS = [];
    const tabs = TABS[S.opt];
    if (!tabs.includes(S.tab)) S.tab = "chat";
    const stack = S.stacks[S.tab];
    const screen = stack[stack.length - 1] || {s: S.tab};
    const key = `${S.tab}:${stack.length}:${JSON.stringify(screen)}`;
    const phone = document.getElementById("phone");
    const showTabs = true;
    phone.classList.toggle("has-tabs", showTabs);
    phone.innerHTML = `
        <div class="status"><span>9:41</span><i></i><s></s></div>
        <button type="button" class="proto-pill" ${on(() => sheet(notesSheet()))}>Prototype · option ${S.opt}</button>
        <section class="screen ${S.anim}" data-screen-label="${esc(titleOf(screen))}">${SCREENS[screen.s](screen)}</section>
        ${tabBar(tabs)}
        ${S.sheet ? sheetHtml(S.sheet) : ""}
        ${S.toast ? `<div class="toast" role="status"><span>${esc(S.toast.text)}</span>${S.toast.undo ? `<button type="button" ${on(() => (S.toast.undo(), (S.toast = null), render()))}>Undo</button>` : ""}</div>` : ""}
    `;
    S.anim = "";
    const sc = phone.querySelector(".scroll, .feed");
    if (sc) {
        if (S.scroll[key] != null) sc.scrollTop = S.scroll[key];
        else if (sc.classList.contains("feed")) sc.scrollTop = sc.scrollHeight;
        sc.addEventListener("scroll", () => phone.querySelector(".navbar")?.classList.toggle("under", sc.scrollTop > 44), {passive: true});
        phone.querySelector(".navbar")?.classList.toggle("under", sc.scrollTop > 44);
    }
    S.lastKey = key;
    renderNotes();
}
const TABS = {A: ["chat", "home", "todos", "journal"], B: ["chat", "home", "journal"], C: ["chat", "home"]};
const TAB_INFO = {chat: {label: "Chat", icon: "chat"}, home: {label: "Home", icon: "home"}, todos: {label: "To-dos", icon: "todo"}, journal: {label: "Journal", icon: "journal"}};
function tabBar(tabs) {
    const count = waitingRefs().length;
    return `<nav class="tabs" role="tablist" aria-label="Screens">${tabs
        .map((t) => {
            const badge = t === "home" && count ? `<span class="badge">${count}</span>` : "";
            const label = t === "home" && count ? `Home, ${count} need you` : TAB_INFO[t].label;
            return `<button type="button" role="tab" aria-selected="${S.tab === t}" aria-label="${label}" class="tab ${S.tab === t ? "on" : ""}" ${on(() => goTab(t))}><span class="tab-icon">${icon(TAB_INFO[t].icon)}${badge}</span><span class="tab-label" aria-hidden="true">${TAB_INFO[t].label}</span></button>`;
        })
        .join("")}</nav>`;
}
function goTab(t) {
    if (S.tab === t && S.stacks[t].length) {
        S.stacks[t] = [];
        S.anim = "pop";
    }
    S.tab = t;
    S.sheet = null;
    render();
}
function push(screen) {
    S.stacks[S.tab].push(screen);
    S.anim = "push";
    S.sheet = null;
    render();
}
function back() {
    S.stacks[S.tab].pop();
    S.anim = "pop";
    render();
}
function open(ref) {
    if (ref.startsWith("commit:")) return push({s: "commit", sha: ref.split(":")[1]});
    push({s: "item", ref});
}
function go(target) {
    const tabs = TABS[S.opt];
    let {tab, stack = []} = target;
    if (!tabs.includes(tab)) {
        if (tab === "todos") {
            stack = [{s: "todos"}, ...stack];
            tab = S.opt === "B" ? "journal" : "home";
        } else if (tab === "journal") tab = "home";
    }
    if (target.view) S.todoView = target.view;
    S.tab = tab;
    S.stacks[tab] = stack.slice();
    S.sheet = null;
    S.anim = "push";
    render();
    if (target.sheet) sheet(target.sheet());
}
function toast(text, undo) {
    S.toast = {text, undo};
    clearTimeout(toast.t);
    toast.t = setTimeout(() => ((S.toast = null), render()), 4500);
}
function sheet(sh) {
    S.toast = null;
    if (S.sheet && sh) S.sheets.push(S.sheet);
    S.sheet = sh;
    render();
}
function closeSheet() {
    S.sheet = null;
    S.sheets = [];
    render();
}
function sheetBack() {
    S.sheet = S.sheets.pop() || null;
    render();
}
const titleOf = (sc) =>
    ({
        chat: () => "Chat",
        home: () => "Home",
        todos: () => "To-dos",
        journal: () => "Journal",
        list: () => KINDS[sc.type].many,
        item: () => {
            const {type, n} = parse(sc.ref);
            return `${KINDS[type].one} ${n}`;
        },
        settings: () => "Settings",
        region: () => (REGIONS[sc.key] || HIDDEN_REGIONS[sc.key]).t,
        service: () => SERVICES.find((x) => x.n === sc.n).title,
        org: () => "Agents and roles",
        search: () => "Search",
        files: () => "Project files",
        file: () => sc.path.split("/").pop(),
        commit: () => `Commit ${sc.sha}`,
        steps: () => "Edit the steps",
        terminal: () => "Terminal",
    })[sc.s]();
const backLabel = () => {
    const st = S.stacks[S.tab];
    const prev = st[st.length - 2] || {s: S.tab};
    return titleOf(prev);
};
/* ---------------- actions on an item ---------------- */
function setState(type, i, change, words) {
    const before = {...i};
    Object.assign(i, change);
    toast(words, () => Object.keys(i).forEach((k) => (i[k] = before[k])));
    render();
}
const EDITABLE = ["todo", "plan", "collection", "doc", "fact", "reminder", "rule", "tool", "ticket", "template", "profile", "trigger", "sequence"];
const COLLECTABLE = ["todo", "plan", "suggestion", "doc", "report", "message", "fact", "rule", "ticket"];
const SHAREABLE = ["doc", "collection", "report"];
const MOVABLE = ["todo", "plan", "suggestion", "collection", "report", "fact", "reminder"];
const CLOSABLE = ["report", "message", "fact", "reminder", "rule", "tool", "template", "profile", "collection", "ticket", "doc", "suggestion", "question"];
const COMMENTABLE = ["todo", "plan", "suggestion", "question", "collection", "doc", "report", "message", "fact", "reminder", "rule", "tool", "ticket", "trigger", "sequence", "check", "template", "profile"];
const DELETABLE = [...EDITABLE, "report", "message", "suggestion", "share"];
const A = {
    start: {
        label: "Start it",
        when: (t, i) => t === "todo" && i.state === "todo",
        run: (t, i) => setState(t, i, {state: "doing"}, `Started ${name(t, i)}`),
    },
    done: {
        label: "Mark done",
        when: (t, i) => t === "todo" && i.state !== "done",
        run: (t, i) =>
            sheet({kind: "text", title: `Mark ${name(t, i)} done`, sub: i.title, label: "How did it end? (you can leave this empty)", placeholder: "Say how it ended", button: "Mark done", good: true, run: (text) => (closeSheet(), setState(t, i, {state: "done", outcome: text}, `Marked ${name(t, i)} done`))}),
        quick: (t, i) => setState(t, i, {state: "done"}, `Marked ${name(t, i)} done`),
    },
    block: {
        label: "Block it",
        when: (t, i) => t === "todo" && !["blocked", "done"].includes(i.state),
        run: (t, i) =>
            sheet({kind: "text", title: `Why is ${name(t, i)} blocked?`, sub: i.title, label: "What it waits for", placeholder: "For example: waits for the second phone to be allowed", button: "Block it", required: true, run: (text) => (closeSheet(), setState(t, i, {state: "blocked", why: text}, `Blocked ${name(t, i)}`))}),
    },
    unblock: {
        label: "Unblock it",
        when: (t, i) => t === "todo" && i.state === "blocked",
        run: (t, i) => setState(t, i, {state: "todo", why: ""}, `Unblocked ${name(t, i)}`),
    },
    reopen: {
        label: "Reopen",
        when: (t, i) => isClosed(i) && !["skill", "plugin", "agent", "work", "share"].includes(t),
        run: (t, i) =>
            sheet({kind: "text", title: `Reopen ${name(t, i)}`, sub: i.title, label: "Why reopen it?", placeholder: "Say what is still missing", button: "Reopen", required: true, run: () => (closeSheet(), setState(t, i, {state: t === "todo" ? "todo" : "open"}, `Reopened ${name(t, i)}`))}),
    },
    approve: {
        label: "Approve the plan",
        when: (t, i) => t === "plan" && i.state === "waiting",
        run: (t, i) => sheet({kind: "confirm", title: "Approve the plan?", sub: `${i.title}. The agent starts on it right away.`, button: "Yes, approve the plan", keep: "Not yet", run: () => (closeSheet(), setState(t, i, {state: "doing"}, `Approved plan ${i.n}`))}),
    },
    changes: {
        label: "Ask for changes",
        when: (t, i) => t === "plan" && i.state === "waiting",
        run: (t, i) => sheet({kind: "text", title: "What should change?", sub: `Plan ${i.n} · ${i.title}`, label: "Your words go to the agent in the chat", placeholder: "For example: put Settings in its own tab", button: "Send to the agent", required: true, run: (text) => (closeSheet(), say(text, `plan:${i.n}`), toast("Sent to the agent"), render())}),
    },
    review: {
        label: "Ask for a review",
        when: (t, i) => t === "plan" && !isClosed(i),
        run: (t, i) => sheet(reviewSheet(i)),
    },
    planstart: {
        label: "Start the plan",
        when: (t, i) => t === "plan" && i.state === "approved",
        run: (t, i) => setState(t, i, {state: "doing"}, `Started plan ${i.n}`),
    },
    onetree: {
        label: "Do every ticket in one copy of the code",
        when: (t, i) => t === "plan" && !isClosed(i),
        run: (t, i) =>
            sheet({kind: "list", title: "Where the plan's tickets are done", sub: "One copy of the code for all tickets, one after another, or a copy per ticket side by side.", cells: [
                {label: "One copy for every ticket", sub: "Done one after another by the plan's own agent", check: i.oneTree, run: () => (closeSheet(), setState(t, i, {oneTree: true}, "Every ticket now uses one copy"))},
                {label: "A copy for each ticket", sub: "Tickets can run side by side", check: !i.oneTree, run: () => (closeSheet(), setState(t, i, {oneTree: false}, "Each ticket now gets its own copy"))},
            ]}),
    },
    closeplan: {
        label: "Close without finishing",
        when: (t, i) => t === "plan" && !isClosed(i),
        run: (t, i) => sheet({kind: "text", title: `Close plan ${i.n} without finishing`, sub: i.title, label: "Why stop it here?", placeholder: "Say why", button: "Close the plan", danger: true, required: true, run: () => (closeSheet(), setState(t, i, {state: "closed"}, `Closed plan ${i.n}`))}),
    },
    makeplan: {
        label: "Make a plan from it",
        when: (t, i) => t === "doc" && i.state === "final" && !i.planned,
        run: (t, i) => sheet({kind: "confirm", title: "Make a plan from this document?", sub: "The plan covers every point under Must have. You approve it before anything starts.", button: "Make the plan", keep: "Cancel", run: () => (closeSheet(), (i.planned = true), toast("The agent is writing the plan"), render())}),
    },
    answer: {
        label: "Answer in your own words",
        when: (t, i) => ["suggestion", "question"].includes(t) && !i.chosen,
        run: (t, i) => sheet({kind: "text", title: "Your answer", sub: i.title, label: "Write your answer", placeholder: "Write your answer", button: "Send", required: true, run: (text) => (closeSheet(), setState(t, i, {chosen: text, state: "done"}, "Answer sent"))}),
    },
    unanswer: {
        label: "Change the answer",
        when: (t, i) => ["suggestion", "question"].includes(t) && i.chosen,
        run: (t, i) => setState(t, i, {chosen: "", state: "waiting"}, "Answer taken back"),
    },
    priority: {
        label: "Change priority",
        when: (t, i) => t === "todo" && !isClosed(i),
        run: (t, i) => sheet({kind: "list", title: `Priority of ${name(t, i)}`, sub: i.title, cells: ["High", "Normal", "Low"].map((p) => ({label: p, check: i.priority === p, run: () => (closeSheet(), setState(t, i, {priority: p}, `Priority set to ${p.toLowerCase()}`))}))}),
    },
    assign: {
        label: "Assign to a role",
        when: (t, i) => ["todo", "ticket"].includes(t) && !isClosed(i),
        run: (t, i) => sheet({kind: "list", title: `Who works on ${name(t, i)}`, sub: "Roles come from Agents and roles.", cells: ROLES.map((r) => ({label: r, check: (i.assigned || "Nobody") === r, run: () => (closeSheet(), setState(t, i, {assigned: r === "Nobody" ? "" : r}, r === "Nobody" ? "No one assigned" : `Assigned to ${r}`))}))}),
    },
    lane: {
        label: "Move to another lane",
        when: (t, i) => t === "todo" && S.board !== false,
        run: (t, i) => sheet({kind: "list", title: `Move ${name(t, i)} to`, sub: "Lanes of the board", cells: LANES.map((l) => ({label: l.title, check: i.state === l.key, run: () => (closeSheet(), setState(t, i, {state: l.key}, `Moved ${name(t, i)} to ${l.title}`))}))}),
    },
    stage: {
        label: "Move to another stage",
        when: (t) => t === "ticket",
        run: (t, i) => sheet({kind: "list", title: `Move ticket ${i.n} to`, cells: LANES.map((l) => ({label: l.title, check: i.stage === l.title, run: () => (closeSheet(), setState(t, i, {stage: l.title}, `Moved ticket ${i.n} to ${l.title}`))}))}),
    },
    moveenv: {
        label: "Move to another environment",
        when: (t, i) => MOVABLE.includes(t) && !isClosed(i),
        run: (t, i) => sheet({kind: "list", title: `Move ${name(t, i)} to`, sub: "It leaves main and shows up in the environment you pick.", cells: ENVS.filter((e) => e.name !== S.env).map((e) => ({label: e.name, sub: e.note, run: () => (closeSheet(), toast(`Moved ${name(t, i)} to ${e.name}`), render())}))}),
    },
    collect: {
        label: "Add to a collection",
        when: (t) => COLLECTABLE.includes(t),
        run: (t, i) => sheet({kind: "list", title: "Add to which collection?", sub: i.title, cells: [...D.collection.map((c) => ({label: c.title, sub: `${c.items.length} items`, check: c.items.includes(refOf(t, i)), run: () => (closeSheet(), c.items.includes(refOf(t, i)) || c.items.push(refOf(t, i)), toast(`Added to ${c.title}`), render())})), {label: "New collection", accent: true, icon: "plus", run: () => sheet(newSheet("collection"))}]}),
    },
    attach: {
        label: "Attach files",
        when: (t) => ["todo", "doc", "collection"].includes(t),
        run: () => sheet(attachSheet("Attach files to it")),
    },
    edit: {
        label: "Edit title and details",
        when: (t) => EDITABLE.includes(t),
        run: (t, i) => sheet({kind: "form", title: `Edit ${name(t, i)}`, fields: [{k: "title", label: "Title", value: i.title}, {k: "summary", label: "Summary, one short line", value: i.summary || ""}, {k: "body", label: "Details", value: (i.body || []).join("\n\n"), area: true}], button: "Save", run: (v) => (closeSheet(), setState(t, i, {title: v.title || i.title, summary: v.summary, body: v.body ? v.body.split(/\n\n+/) : i.body}, "Saved"))}),
    },
    share: {
        label: "Share",
        when: (t) => SHAREABLE.includes(t),
        run: (t, i) => sheet(shareSheet(t, i)),
    },
    hide: {
        label: "Hide from the chat",
        when: (t, i) => t === "report" && !isClosed(i),
        run: (t, i) => (toast("Hidden from the chat; it stays in Reports"), render()),
    },
    pin: {
        label: "Pin to the chat",
        when: (t, i) => t === "rule",
        labelFor: (t, i) => (i.pinned ? "Unpin from the chat" : "Pin to the chat"),
        run: (t, i) => setState(t, i, {pinned: !i.pinned}, i.pinned ? "Unpinned from the chat" : "Pinned to the chat"),
    },
    trigwords: {
        label: "Change what starts it",
        when: (t) => t === "trigger",
        run: (t, i) => sheet({kind: "text", title: "Watch for these words in your messages", sub: "Separate the words with commas.", label: "Words", value: i.words.join(", "), button: "Save", required: true, run: (text) => (closeSheet(), setState(t, i, {words: text.split(",").map((w) => w.trim()).filter(Boolean)}, "Saved"))}),
    },
    trigwhat: {
        label: "Change what it does",
        when: (t) => t === "trigger",
        run: (t, i) => sheet({kind: "list", title: "When it starts, the journal will", cells: ["Start the sequence Ship it", "Send the agent a message", "Make a to-do"].map((x) => ({label: x, check: i.does === x, run: () => (closeSheet(), setState(t, i, {does: x}, "Saved"))}))}),
    },
    trigonoff: {
        label: "Turn it off",
        when: (t) => t === "trigger",
        labelFor: (t, i) => (i.on ? "Turn it off" : "Turn it on"),
        run: (t, i) => setState(t, i, {on: !i.on}, i.on ? "Trigger turned off" : "Trigger turned on"),
    },
    steps: {
        label: "Edit the steps",
        when: (t) => t === "sequence",
        run: (t, i) => push({s: "steps", n: i.n}),
    },
    load: {
        label: "Load it at session start",
        when: (t) => t === "skill",
        labelFor: (t, i) => (i.loads ? "Stop loading it at session start" : "Load it at session start"),
        run: (t, i) => setState(t, i, {loads: !i.loads}, i.loads ? "Not loaded at session start" : "Loaded at every session start"),
    },
    update: {
        label: "Check for updates",
        when: (t) => t === "plugin",
        run: (t, i) => (toast(`${i.title} is up to date (${i.version})`), render()),
    },
    setup: {
        label: "Run setup again",
        when: (t) => t === "plugin",
        run: (t, i) => sheet({kind: "confirm", title: `Run the setup of ${i.title} again?`, sub: "Its settings stay. Its services restart.", button: "Run setup again", keep: "Cancel", run: () => (closeSheet(), toast(`Setup of ${i.title} started`), render())}),
    },
    stophelper: {
        label: "Stop the helper",
        when: (t, i) => t === "agent" && i.n > 1,
        run: (t, i) => sheet({kind: "confirm", title: `Stop ${i.title}?`, sub: "What it did so far stays in its copy of the code.", button: "Stop", danger: true, keep: "Keep working", run: () => (closeSheet(), setState(t, i, {state: "done"}, `${i.title} stopped`))}),
    },
    comment: {
        label: "Comment",
        when: (t) => COMMENTABLE.includes(t),
        run: (t, i) => sheet({kind: "text", title: `Comment on ${name(t, i)}`, sub: i.title, label: "Your comment", placeholder: "Write a comment", button: "Comment", required: true, run: (text) => (closeSheet(), (i.comments = [...(i.comments || []), {who: "You", at: 0, text}]), toast("Comment added"), render())}),
    },
    close: {
        label: "Close it",
        when: (t, i) => CLOSABLE.includes(t) && !isClosed(i),
        labelFor: (t) => (t === "report" || t === "message" ? "Mark as read" : t === "doc" ? "Mark it final" : "Close it"),
        run: (t, i) => {
            if (t === "report" || t === "message") return setState(t, i, {state: "read"}, `Marked ${name(t, i)} as read`);
            if (t === "doc") return setState(t, i, {state: "final"}, `Document ${i.n} is final`);
            sheet({kind: "text", title: `Close ${name(t, i)}`, sub: i.title, label: "How did it end? (you can leave this empty)", placeholder: "Say how it ended", button: "Close it", run: () => (closeSheet(), setState(t, i, {state: "closed"}, `Closed ${name(t, i)}`))});
        },
        quick: (t, i) => setState(t, i, {state: "read"}, `Marked ${name(t, i)} as read`),
    },
    remove: {
        label: "Remove",
        when: (t) => t === "plugin",
        danger: true,
        run: (t, i) => sheet({kind: "confirm", title: `Remove ${i.title}?`, sub: "Its pages, settings and services go. Items it made are deleted too.", button: "Remove it", danger: true, keep: "Keep it", run: () => (closeSheet(), (D.plugin = D.plugin.filter((x) => x !== i)), back(), toast(`Removed ${i.title}`))}),
    },
    delete: {
        label: "Delete",
        when: (t) => DELETABLE.includes(t),
        danger: true,
        labelFor: (t) => (t === "share" ? "Stop the link" : "Delete"),
        run: (t, i) =>
            sheet({kind: "confirm", title: t === "share" ? "Stop this share link?" : `Delete ${name(t, i)}?`, sub: t === "share" ? "The link stops working for everyone who has it." : "It is removed for good.", button: t === "share" ? "Stop the link" : "Delete it", danger: true, keep: "Keep it", run: () => {
                closeSheet();
                D[t] = D[t].filter((x) => x !== i);
                const st = S.stacks[S.tab];
                if (st.length && st[st.length - 1].ref === refOf(t, i)) st.pop();
                S.anim = "pop";
                toast(t === "share" ? "Link stopped" : `Deleted ${name(t, i)}`);
                render();
            }}),
    },
};
const ORDER = ["start", "done", "unblock", "block", "approve", "changes", "planstart", "review", "onetree", "makeplan", "answer", "unanswer", "trigonoff", "trigwords", "trigwhat", "steps", "load", "update", "setup", "share", "hide", "pin", "priority", "assign", "lane", "stage", "moveenv", "collect", "attach", "edit", "close", "reopen", "closeplan", "stophelper", "remove", "delete"];
const actionsFor = (t, i) => ORDER.filter((k) => A[k].when(t, i));
const labelOf = (k, t, i) => (A[k].labelFor ? A[k].labelFor(t, i) : A[k].label);
function primaryOf(t, i) {
    if (t === "todo") return {todo: "start", doing: "done", review: "done", blocked: "unblock", done: "reopen"}[i.state];
    if (t === "plan") return i.state === "waiting" ? "approve" : i.state === "approved" ? "planstart" : null;
    if (t === "doc") return A.makeplan.when(t, i) ? "makeplan" : "share";
    if (["report", "collection"].includes(t)) return "share";
    if (t === "plugin") return "update";
    if (t === "sequence") return "steps";
    return null;
}
const moreSheet = (t, i) => ({
    kind: "list",
    title: i.title,
    sub: `${KINDS[t].one} ${i.n} · everything you can do with it`,
    cells: actionsFor(t, i).map((k) => ({label: labelOf(k, t, i), danger: A[k].danger, run: () => ((S.sheet = null), A[k].run(t, i))})),
});
function say(text, about) {
    S.chat.push({id: Date.now(), who: "me", at: "now", text, refs: about ? [about] : []});
    S.agent.state = "working";
    setTimeout(() => {
        S.chat.push({id: Date.now() + 1, who: "agent", at: "now", text: "Got it. I am on it."});
        if (S.tab === "chat") render();
    }, 1400);
}
/* ---------------- building blocks ---------------- */
const chev = () => icon("right", "chev");
function navbar(title, end = "") {
    const st = S.stacks[S.tab];
    const backBtn = st.length ? `<button type="button" class="nav-back" aria-label="Back to ${esc(backLabel())}" ${on(back)}>${icon("back")}<span>${esc(backLabel())}</span></button>` : "<span></span>";
    return `<header class="navbar">${backBtn}<span class="nav-mid" aria-hidden="true">${esc(title)}</span><span class="nav-end">${end}</span></header>`;
}
function cell(o) {
    const ic = o.icon ? `<span class="cell-ic" ${o.tint ? `style="background:${o.tint};color:#fff"` : ""}>${icon(o.icon)}</span>` : o.dot ? `<span class="dot ${o.dot}"></span>` : "";
    const count = o.count != null && o.count !== "" ? `<span class="cell-count ${o.hot ? "hot" : ""}">${o.count}</span>` : "";
    const end = o.end != null ? o.end : o.check !== undefined ? (o.check ? `<span class="check">${icon("todo")}</span>` : "") : o.run && !o.noChev ? chev() : "";
    const tag = o.run ? "button" : "div";
    return `<${tag} ${o.run ? `type="button" ${on(o.run)}` : ""} class="cell ${o.danger ? "danger" : ""} ${o.accent ? "accent" : ""}" data-find="${esc((o.label + " " + (o.sub || "")).toLowerCase())}">${ic}<span class="cell-main"><span class="cell-title">${esc(o.label)}</span>${o.sub ? `<span class="cell-sub">${esc(o.sub)}</span>` : ""}${o.meta || ""}</span>${count}${end}</${tag}>`;
}
const group = (cells, head, foot) => `${head ? `<h2 class="group-head">${esc(head)}</h2>` : ""}<div class="group">${cells.join("")}</div>${foot ? `<p class="group-foot">${esc(foot)}</p>` : ""}`;
function metaOf(t, i) {
    const bits = [];
    if (t === "todo") {
        if (i.state === "blocked" && i.why) bits.push(`<span class="why">Blocked: ${esc(i.why)}</span>`);
        if (i.priority === "High") bits.push(`<span class="hi">High priority</span>`);
        bits.push(`<span>#${i.n}</span>`);
        if (i.assigned) bits.push(`<span>${esc(i.assigned)}</span>`);
    } else {
        bits.push(`<span>${KINDS[t].one} ${i.n}</span>`);
        if (STATE_WORD[i.state] && !["open"].includes(i.state)) bits.push(`<span>${STATE_WORD[i.state]}</span>`);
        if (t === "ticket") bits.push(`<span>${esc(i.stage)}</span>`);
        if (t === "collection") bits.push(`<span>${i.items.length} items</span>`);
    }
    if (i.made) bits.push(`<span>${ago(i.made)}</span>`);
    return `<span class="meta">${bits.join("")}</span>`;
}
const dotOf = (t, i) => (t === "todo" || t === "agent" || t === "work" ? i.state : ["waiting", "new"].includes(i.state) ? "waiting" : isClosed(i) ? "done" : t === "plan" && i.state === "doing" ? "doing" : "");
function row(t, i) {
    const k = KINDS[t];
    const lead = k.lead && !isClosed(i) ? k.lead : null;
    const trail = [];
    if (t === "todo") {
        if (i.state === "todo") trail.push(["start", "Start", "start"]);
        if (i.state === "blocked") trail.push(["unblock", "Unblock", "start"]);
        else if (!isClosed(i)) trail.push(["block", "Block", "block"]);
    }
    trail.push(["more", "More", "more"]);
    const leadWord = lead === "done" ? "Done" : "Read";
    const trailHtml = trail
        .map(([key, word, cls]) => `<button type="button" class="sw-act ${cls}" ${on(() => (key === "more" ? sheet(moreSheet(t, i)) : A[key].run(t, i)))}>${word}</button>`)
        .join("");
    const tap = FNS.push(() => open(refOf(t, i))) - 1;
    const hold = FNS.push(() => sheet(moreSheet(t, i))) - 1;
    const leadFn = lead ? FNS.push(() => A[lead].quick(t, i)) - 1 : "";
    const title = esc(i.title);
    return `<div class="sw" data-find="${esc(i.title.toLowerCase())} ${i.n}">
        ${lead ? `<div class="sw-lead" aria-hidden="true">${icon("todo")}&nbsp;${leadWord}</div>` : ""}
        <div class="sw-trail" style="width:${trail.length * 84}px">${trailHtml}</div>
        <div class="sw-front" data-tap="${tap}" data-hold="${hold}" data-lead="${leadFn}" data-trail="${trail.length * 84}" role="button" tabindex="0" aria-label="${title}">
            <div class="cell"><span class="dot ${dotOf(t, i)}"></span><span class="cell-main"><span class="cell-title">${title}</span>${metaOf(t, i)}</span>${chev()}</div>
        </div>
    </div>`;
}
const rowsGroup = (t, list) => `<div class="group">${list.map((i) => row(t, i)).join("")}</div>`;
const searchField = (placeholder) => `<label class="search">${icon("search")}<input type="search" placeholder="${esc(placeholder)}" data-filter aria-label="${esc(placeholder)}"></label>`;
const big = (title, sub) => `<h1 class="big">${esc(title)}</h1>${sub ? `<p class="big-sub">${esc(sub)}</p>` : ""}`;
/* ---------------- screens ---------------- */
const SCREENS = {};
SCREENS.chat = () => {
    const waiting = waitingRefs();
    const first = waiting[0] && find(waiting[0]);
    const atWork = D.agent.filter((a) => a.state === "doing").length;
    const paused = S.agent.state === "paused";
    const feed = S.chat
        .map((m) => {
            const refs = (m.refs || [])
                .map((r) => {
                    if (r.startsWith("commit:")) return `<button type="button" class="ref" ${on(() => open(r))}>${icon("branch", "ic")}Commit ${esc(r.split(":")[1])}</button>`;
                    const i = find(r);
                    if (!i) return "";
                    const {type} = parse(r);
                    return `<button type="button" class="ref" ${on(() => open(r))}><span class="dot ${dotOf(type, i)}"></span>${KINDS[type].one} ${i.n} · ${esc(i.title)}</button>`;
                })
                .join("");
            const hold = FNS.push(() => sheet(messageSheet(m))) - 1;
            return `<div class="msg ${m.who === "me" ? "me" : "agent"}" data-hold-msg="${hold}"><div class="bubble">${m.who === "agent" ? `<span class="who">Agent · ${esc(m.at)}</span>` : ""}<p>${esc(m.text)}</p>${refs ? `<p>${refs}</p>` : ""}</div></div>`;
        })
        .join("");
    return `
        <div class="chat-top">
            <button type="button" class="place" aria-label="Switch journal or environment" ${on(() => sheet(placesSheet()))}>
                <span class="place-name"><span class="place-dot"></span>${esc(S.journal)}${icon("down", "chev")}</span>
                <span class="place-env">${esc(S.env)}</span>
            </button>
            <button type="button" class="pill" ${on(() => sheet(atWorkSheet()))}>${atWork} at work</button>
            <button type="button" class="pill" aria-haspopup="dialog" ${on(() => sheet(agentSheet()))}><span class="live ${paused ? "paused" : ""}"></span>${paused ? "Paused" : S.agent.auto ? "Working · auto" : "Working"}</button>
        </div>
        ${waiting.length ? `<button type="button" class="needs" ${on(() => sheet(needsSheet()))}><b>${waiting.length} need you</b><span>${esc(first.title)}</span>${chev()}</button>` : ""}
        ${S.pinned ? `<div class="pinned">${icon("pin")}<span>${esc(S.pinned.text)}</span><button type="button" class="icon-btn" aria-label="Unpin" ${on(() => ((S.pinned = null), render()))}>${icon("close")}</button></div>` : ""}
        <div class="feed" data-scroller><p class="day">Today</p>${feed}</div>
        <div class="dock">
            <div class="working"><span class="pill" style="min-height:22px;padding:0 8px;font-size:12px"><span class="live ${paused ? "paused" : ""}"></span>${paused ? "Paused" : "Working · 34 min"}</span>${paused ? "" : "Doing to-do 140"}</div>
            <div class="compose">
                <button type="button" class="round" aria-label="Attach files or photos" ${on(() => sheet(attachSheet("Add to your message")))}>${icon("plus")}</button>
                <textarea rows="1" placeholder="Message the agent" aria-label="Message the agent" id="draft"></textarea>
                <button type="button" class="round send" aria-label="Send to the agent" ${on(() => {
                    const el = document.getElementById("draft");
                    if (!el.value.trim()) return;
                    say(el.value.trim());
                    render();
                })}>${icon("send")}</button>
            </div>
        </div>`;
};
function homeCards() {
    const waiting = waitingRefs();
    const needs = waiting.length
        ? `<section class="card"><div class="card-head"><h2>Needs you</h2><span>${waiting.length}</span></div><div class="group">${waiting
              .map((r) => {
                  const {type} = parse(r);
                  const i = find(r);
                  return cell({dot: "waiting", label: i.title, sub: `${WAIT_WORD[type]} · ${ago(i.made)}`, run: () => open(r)});
              })
              .join("")}</div></section>`
        : `<p class="card-none">Nothing needs you right now.</p>`;
    const cards = S.cards
        .map((t, idx) => {
            const list = (D[t] || []).filter((i) => !isClosed(i));
            const tools = S.editCards
                ? `<span style="margin-left:auto;display:flex">${[
                      ["Move up", "↑", () => (idx > 0 && S.cards.splice(idx - 1, 0, S.cards.splice(idx, 1)[0]), render())],
                      ["Move down", "↓", () => (S.cards.splice(idx + 1, 0, S.cards.splice(idx, 1)[0]), render())],
                      ["Remove", "×", () => (S.cards.splice(idx, 1), render())],
                  ]
                      .map(([l, g, f]) => `<button type="button" class="icon-btn" aria-label="${l} ${KINDS[t].many}" ${on(f)}>${g}</button>`)
                      .join("")}</span>`
                : `<button type="button" class="linkbtn" ${on(() => (t === "todo" && TABS[S.opt].includes("todos") ? goTab("todos") : push(t === "todo" ? {s: "todos"} : {s: "list", type: t})))}>See all</button>`;
            if (!list.length && !S.editCards) return `<p class="card-none">${KINDS[t].many} · none yet</p>`;
            return `<section class="card"><div class="card-head"><h2>${KINDS[t].many}</h2><span>${list.length}</span>${tools}</div>${S.editCards ? "" : rowsGroup(t, list.slice(0, 4))}</section>`;
        })
        .join("");
    const missing = ["todo", "question", "suggestion", "plan", "report", "doc", "work", "agent", "collection"].filter((k) => !S.cards.includes(k));
    const add = S.editCards && missing.length ? `<section class="card"><div class="card-head"><h2>Add a card</h2></div><div class="chips" style="flex-wrap:wrap;margin:0">${missing.map((k) => `<button type="button" class="chip" ${on(() => (S.cards.push(k), render()))}>+ ${KINDS[k].many}</button>`).join("")}</div></section>` : "";
    return `${needs}${cards}${add}<button type="button" class="btn plain" style="width:100%" ${on(() => ((S.editCards = !S.editCards), render()))}>${S.editCards ? "Done" : "Edit cards"}</button>`;
}
SCREENS.home = () => `${navbar("Home")}<div class="scroll">${big("Home", `${S.journal} · ${S.env}`)}${homeCards()}${S.opt === "C" ? `<h2 class="group-head" style="font-size:20px;color:var(--text);margin-top:28px">Everything else</h2>${journalGroups()}` : ""}</div>`;
const todoGroups = [
    ["doing", "Doing now"],
    ["review", "Waiting for you"],
    ["todo", "To do"],
    ["blocked", "Blocked"],
];
function sorted(t, list) {
    const how = S.sort[t] || "new";
    const copy = list.slice();
    if (how === "az") copy.sort((a, b) => a.title.localeCompare(b.title));
    else if (how === "pri") copy.sort((a, b) => ["High", "Normal", "Low"].indexOf(a.priority) - ["High", "Normal", "Low"].indexOf(b.priority));
    else copy.sort((a, b) => a.made - b.made);
    return copy;
}
const SORTS = {new: "Newest first", az: "A to Z", pri: "Priority"};
const sortSheet = (t) => ({kind: "list", title: `Order the ${KINDS[t].many.toLowerCase()}`, cells: Object.entries(SORTS).filter(([k]) => k !== "pri" || t === "todo").map(([k, l]) => ({label: l, check: (S.sort[t] || "new") === k, run: () => ((S.sort[t] = k), closeSheet())}))});
SCREENS.todos = () => {
    const all = D.todo;
    const open = all.filter((i) => i.state !== "done");
    const seg = `<div class="seg" role="tablist" aria-label="Show the to-dos as">${[["list", "List"], ["board", "Board"]].map(([k, l]) => `<button type="button" role="tab" aria-selected="${S.todoView === k}" class="${S.todoView === k ? "on" : ""}" ${on(() => ((S.todoView = k), render()))}>${l}</button>`).join("")}</div>`;
    const hint = S.hint && S.todoView === "list" ? `<div class="hint"><span>Swipe a to-do right to mark it done, or left to block it. Hold it to see everything you can do.</span><button type="button" ${on(() => ((S.hint = false), render()))}>Got it</button></div>` : "";
    let body;
    if (S.todoView === "list") {
        body =
            todoGroups
                .map(([state, head]) => {
                    const list = sorted("todo", all.filter((i) => i.state === state));
                    return list.length ? `<h2 class="group-head">${head} · ${list.length}</h2>${rowsGroup("todo", list)}` : "";
                })
                .join("") +
            (() => {
                const done = all.filter((i) => i.state === "done");
                return `<h2 class="group-head">Done · ${done.length}</h2>${S.showDone ? rowsGroup("todo", done) : ""}<button type="button" class="linkbtn" ${on(() => ((S.showDone = !S.showDone), render()))}>${S.showDone ? "Hide the done to-dos" : `Show ${done.length} done`}</button>`;
            })();
    } else {
        const lanes = LANES.filter((l) => S.doneLane || l.key !== "done");
        const chips = `<div class="chips" role="tablist" aria-label="Lanes">${lanes.map((l) => `<button type="button" role="tab" aria-selected="${S.lane === l.key}" class="chip ${S.lane === l.key ? "on" : ""}" ${on(() => ((S.lane = l.key), render()))}>${l.title} <em>${all.filter((i) => i.state === l.key).length}</em></button>`).join("")}</div>`;
        const list = all.filter((i) => i.state === S.lane);
        body = `${chips}${list.length ? rowsGroup("todo", list) : `<p class="empty">No to-dos in this lane.</p>`}<p class="group-foot">Hold a to-do to move it to another lane.</p>`;
    }
    const foot =
        S.todoView === "list"
            ? `<footer class="foot new"><button type="button" class="newbtn" ${on(() => sheet(newSheet("todo")))}><span class="plus">${icon("plus")}</span>New to-do</button><button type="button" class="linkbtn" ${on(() => sheet(sortSheet("todo")))}>${SORTS[S.sort.todo || "new"]}</button></footer>`
            : `<footer class="foot new"><button type="button" class="newbtn" ${on(() => sheet(newWorkSheet()))}><span class="plus">${icon("plus")}</span>New work</button><button type="button" class="linkbtn" ${on(() => sheet(boardSheet()))}>Board settings</button></footer>`;
    return `${navbar("To-dos")}<div class="scroll">${big("To-dos", `${S.env} · ${open.length} open`)}${seg}${searchField(S.todoView === "list" ? "Search to-dos" : "Find a card on the board")}${hint}${body}</div>${foot}`;
};
function journalGroups() {
    const n = (t) => (D[t] || []).filter((i) => !isClosed(i)).length;
    const hot = (t) => (D[t] || []).some((i) => ["waiting", "new", "review"].includes(i.state));
    const kindCell = (t) => cell({icon: KINDS[t].icon, label: KINDS[t].many, count: n(t), hot: hot(t), run: () => push({s: "list", type: t})});
    const envCells = [
        ...(S.opt !== "A" ? [cell({icon: "todo", label: "To-dos", count: n("todo"), run: () => push({s: "todos"})})] : []),
        ...["plan", "suggestion", "question", "collection", "work", "agent"].map(kindCell),
    ];
    const projectCells = [
        cell({icon: "board", label: "Board", sub: "To-dos in lanes", run: () => go({tab: "todos", view: "board"})}),
        ...["doc", "ticket", "trigger", "sequence", "check"].map(kindCell),
    ];
    return [
        group(envCells, `In this environment (${S.env})`),
        group(projectCells, "In the whole project"),
        group(["message", "report", "fact", "reminder", "rule"].map(kindCell), "What the agent wrote down"),
        group([...["skill", "tool", "template", "profile"].map(kindCell), cell({icon: "people", label: "Agents and roles", sub: "Domains and who works in them", run: () => push({s: "org"})})], "How the agent works"),
        group(
            [
                cell({icon: "branch", label: "Journals and environments", sub: `${JOURNALS.length} journals · ${ENVS.length} environments`, run: () => sheet(placesSheet())}),
                kindCell("plugin"),
                kindCell("share"),
                cell({icon: "folder", label: "Project files", run: () => push({s: "files"})}),
                cell({icon: "gear", label: "Settings", run: () => push({s: "settings"})}),
            ],
            "Setting up this journal"
        ),
    ].join("");
}
SCREENS.journal = () => `${navbar("Journal")}<div class="scroll">${big("Journal", `Everything ${S.journal} keeps`)}<button type="button" class="search" style="width:100%;border:0" ${on(() => push({s: "search"}))}>${icon("search")}<span>Search everything in ${esc(S.env)}</span></button>${journalGroups()}</div>`;
SCREENS.list = (sc) => {
    const t = sc.type;
    const k = KINDS[t];
    const all = sorted(t, D[t] || []);
    const openList = all.filter((i) => !isClosed(i));
    const closed = all.filter(isClosed);
    const shown = S.shown[t];
    const body = `${openList.length ? rowsGroup(t, openList) : `<p class="empty">No open ${k.many.toLowerCase()}.</p>`}${closed.length ? `<button type="button" class="linkbtn" ${on(() => ((S.shown[t] = !shown), render()))}>${shown ? `Hide the closed ${k.many.toLowerCase()}` : `Show ${closed.length} closed`}</button>${shown ? rowsGroup(t, closed) : ""}` : ""}`;
    const foot = `<footer class="foot new">${k.make ? `<button type="button" class="newbtn" ${on(() => sheet(newSheet(t)))}><span class="plus">${icon("plus")}</span>${k.make}</button>` : t === "plugin" ? `<button type="button" class="newbtn" ${on(() => sheet(pluginAddSheet()))}><span class="plus">${icon("plus")}</span>Add a plugin</button>` : "<span></span>"}<button type="button" class="linkbtn" ${on(() => sheet(sortSheet(t)))}>${SORTS[S.sort[t] || "new"]}</button></footer>`;
    return `${navbar(k.many)}<div class="scroll">${big(k.many)}<p class="intro">${esc(k.intro)}</p>${searchField(`Search ${k.many.toLowerCase()}`)}${body}</div>${foot}`;
};
const SIZES = [17, 19, 22];
SCREENS.item = (sc) => {
    const {type: t} = parse(sc.ref);
    const i = find(sc.ref);
    if (!i) return `${navbar("Gone")}<div class="scroll"><p class="empty">This was deleted.</p></div>`;
    const k = KINDS[t];
    const end = `${SHAREABLE.includes(t) ? `<button type="button" class="icon-btn" aria-label="Share" ${on(() => sheet(shareSheet(t, i)))}>${icon("share")}</button>` : ""}<button type="button" class="icon-btn" aria-label="Text size" style="font-weight:600;font-size:16px" ${on(() => ((S.size = (S.size + 1) % SIZES.length), render()))}>Aa</button>`;
    const parts = [];
    if (t === "todo") {
        const facts = [["State", STATE_WORD[i.state]]];
        if (i.state === "blocked" && i.why) facts.push(["Why", i.why, "why"]);
        facts.push(["Priority", i.priority], ["Assigned to", i.assigned || "Nobody"]);
        if (i.after.length) facts.push(["Waits for", i.after.map((a) => `<button type="button" class="ref" ${on(() => open(`todo:${a}`))}>To-do ${a}</button>`).join(""), "", true]);
        facts.push(["Made", ago(i.made)]);
        if (i.outcome) facts.push(["How it ended", i.outcome]);
        parts.push(`<dl class="facts">${facts.map(([a, b, c, raw]) => `<dt>${a}</dt><dd class="${c || ""}">${raw ? b : esc(b)}</dd>`).join("")}</dl>`);
    }
    if (t === "plan") {
        parts.push(`<p><b>Goal:</b> ${esc(i.goal)}</p><h2 class="section-title">Phases</h2>${i.phases.map((p, x) => `<div class="phase"><span class="dot ${p.s}"></span><span>${x + 1}. ${esc(p.t)}</span></div>`).join("")}<dl class="facts" style="margin-top:14px"><dt>State</dt><dd>${STATE_WORD[i.state] || i.state}</dd><dt>Tickets run in</dt><dd>${i.oneTree ? "One copy of the code, one after another" : "A copy of the code each"}</dd></dl>`);
    }
    if (t === "suggestion" || t === "question") {
        parts.push(`<div class="options">${i.choices
            .map((c) => `<button type="button" class="option ${i.chosen === c.l ? "chosen" : ""}" ${i.chosen ? "disabled" : on(() => setState(t, i, {chosen: c.l, state: "done"}, `You chose: ${c.l}`))}><span><b>${esc(c.l)}</b>${c.note ? `<small>${esc(c.note)}</small>` : ""}</span>${c.rec ? `<span class="rec">Recommended</span>` : ""}</button>`)
            .join("")}</div>${i.chosen ? `<p class="group-foot">Your answer: ${esc(i.chosen)}</p>` : `<button type="button" class="linkbtn" ${on(() => A.answer.run(t, i))}>Answer in your own words</button>`}`);
    }
    if (t === "doc") {
        parts.push(`<p class="intro" style="margin:0 0 12px">${esc(i.summary)}</p><dl class="facts"><dt>State</dt><dd>${STATE_WORD[i.state]}</dd><dt>Files</dt><dd>${i.files.length ? esc(i.files.join(", ")) : "None"}</dd>${i.planned ? "<dt>Plan</dt><dd>Being written</dd>" : ""}</dl><div class="body">${i.sections.map((s) => `<h2>${esc(s.t)}</h2><p>${esc(s.b)}</p>`).join("")}</div>`);
    }
    if (t === "collection") {
        parts.push(`<h2 class="section-title">In this collection · ${i.items.length}</h2>${group(i.items.map((r) => {
            const x = find(r);
            const {type} = parse(r);
            return x ? cell({dot: dotOf(type, x), label: x.title, sub: `${KINDS[type].one} ${x.n}`, run: () => open(r)}) : "";
        }))}`);
    }
    if (t === "trigger") {
        parts.push(`<dl class="facts"><dt>Is it on?</dt><dd>${i.on ? "On" : "Off"}</dd><dt>Watches for</dt><dd>${i.words.map((w) => `“${esc(w)}”`).join(", ")} in your messages</dd><dt>Then it will</dt><dd>${esc(i.does)}</dd></dl>`);
    }
    if (t === "sequence") {
        parts.push(`<h2 class="section-title">Steps</h2>${i.steps.map((s, x) => `<div class="phase"><span class="dot ${x === 0 ? "done" : x === 1 ? "doing" : ""}"></span><span>${x + 1}. ${esc(s)}</span></div>`).join("")}<h2 class="section-title">Running now</h2><p>${esc(i.running)}</p>`);
    }
    if (t === "check") {
        parts.push(`<dl class="facts"><dt>Last run</dt><dd>${esc(i.last)}</dd></dl><h2 class="section-title">What it found</h2>${group(i.findings.map((f) => cell({label: f.split(" · ")[1], sub: f.split(" · ")[0]})))}`);
    }
    if (t === "skill") {
        parts.push(group([cell({label: "Load at session start", sub: "The agent reads it when a session begins", end: `<button type="button" class="switch ${i.loads ? "on" : ""}" role="switch" aria-checked="${i.loads}" aria-label="Load at session start" ${on(() => A.load.run(t, i))}></button>`})]) + `<h2 class="section-title">Instructions</h2>`);
    }
    if (t === "plugin") {
        parts.push(`<dl class="facts"><dt>Version</dt><dd>${esc(i.version)}</dd></dl>${group(i.pages.map((p) => cell({icon: "file", label: p, run: () => (toast(`${p} opens here`), render())})), "Its pages", i.pages.length ? "" : "It has no pages.")}${group(i.settingsList.map((s) => cell({label: s.split(":")[0], sub: s.split(":")[1], run: () => sheet({kind: "text", title: s.split(":")[0], label: "Value", value: s.split(":")[1].trim(), button: "Save", run: () => (closeSheet(), toast("Saved"), render())})})), "Its settings")}${group(i.services.map((s) => {
            const sv = SERVICES.find((x) => x.title === s);
            return cell({dot: sv.state === "running" ? "done" : "", label: s, sub: sv.state === "running" ? "Running" : "Stopped", run: () => push({s: "service", n: sv.n})});
        }), "Its services")}`);
    }
    if (t === "share") {
        parts.push(`<div class="share-link">${esc(i.url)}</div><dl class="facts" style="margin-top:12px"><dt>Opened</dt><dd>${i.opens} times</dd></dl><button type="button" class="btn small" style="width:100%" ${on(() => (toast("Link copied"), render()))}>${icon("copy")}Copy the link</button>`);
    }
    if (t === "agent") {
        if (i.n === 3 && i.state === "review")
            parts.push(`<div class="hint" style="display:block"><b>Asks for permission</b><p style="margin:4px 0 10px">Run the whole test suite (about 4 minutes).</p><div class="foot-row"><button type="button" class="btn small" ${on(() => setState(t, i, {state: "done"}, "Denied"))}>Deny</button><button type="button" class="btn small primary" ${on(() => setState(t, i, {state: "doing"}, "Allowed"))}>Allow</button></div></div>`);
        parts.push(`<dl class="facts"><dt>State</dt><dd>${STATE_WORD[i.state] || i.state}</dd><dt>Working for</dt><dd>${ago(i.made).replace(" ago", "")}</dd></dl>`);
    }
    if (t === "ticket") parts.push(`<dl class="facts"><dt>Stage</dt><dd>${esc(i.stage)}</dd></dl>`);
    if (t === "reminder") parts.push(`<dl class="facts"><dt>When</dt><dd>${esc(i.when)}</dd></dl>`);
    if (t === "rule") parts.push(`<dl class="facts"><dt>Pinned to the chat</dt><dd>${i.pinned ? "Yes" : "No"}</dd></dl>`);
    const body = `<div class="body">${(i.body || []).map((p) => `<p>${esc(p)}</p>`).join("")}</div>`;
    if (t !== "todo") parts.unshift(body);
    else parts.push(body);
    const comments = COMMENTABLE.includes(t) ? `<h2 class="section-title">Comments · ${(i.comments || []).length}</h2>${(i.comments || []).map((c) => `<div class="comment"><b>${esc(c.who)}</b> <span>${ago(c.at)}</span><p>${esc(c.text)}</p></div>`).join("") || `<p class="group-foot" style="margin:0">No comments yet.</p>`}` : "";
    const primary = primaryOf(t, i);
    const btns = [];
    if (primary) btns.push(`<button type="button" class="btn ${primary === "done" ? "good" : "primary"}" ${on(() => A[primary].run(t, i))}>${labelOf(primary, t, i)}</button>`);
    const second = [];
    if (t === "plan" && i.state === "waiting") second.push(`<button type="button" class="btn small" ${on(() => A.changes.run(t, i))}>Ask for changes</button>`);
    if (t === "todo" && A.block.when(t, i)) second.push(`<button type="button" class="btn small" ${on(() => A.block.run(t, i))}>Block</button>`);
    if (COMMENTABLE.includes(t)) second.push(`<button type="button" class="btn small" ${on(() => A.comment.run(t, i))}>Comment</button>`);
    second.push(`<button type="button" class="btn small" aria-label="Everything you can do with it" ${on(() => sheet(moreSheet(t, i)))}>More</button>`);
    return `${navbar(`${k.one} ${i.n}`, end)}<div class="scroll" style="font-size:${SIZES[S.size]}px"><span class="kind">${k.one} ${i.n}${i.made ? ` · ${ago(i.made)}` : ""}</span><h1 class="title">${esc(i.title)}</h1>${parts.join("")}${comments}</div><footer class="foot">${btns.join("")}<div class="foot-row">${second.join("")}</div></footer>`;
};
const REGIONS = {
    features: {t: "Features", icon: "star", line: "What the agent and the journal do. Each can be switched off on its own."},
    chatviews: {t: "What the chat shows", icon: "eye", line: "Pick what appears between the messages."},
    notify: {t: "Alerts on this phone", icon: "bell", line: "When this phone gets an alert."},
    services: {t: "Services", icon: "play", line: "What the journal and its plugins keep running."},
    phone: {t: "Phone and share links", icon: "phone", line: "Your phone and share links reach this journal through tunler."},
    pluginsettings: {t: "Plugin settings", icon: "plug", line: "The settings of each installed plugin."},
    environments: {t: "Environments", icon: "branch", line: "Each has its own to-dos, agent and history."},
    system: {t: "System", icon: "gear", line: "Updates, the project folder and shutting down."},
    developer: {t: "Developer", icon: "terminal", line: "Only needed when you work on the journal itself."},
    about: {t: "About this journal", icon: "info", line: "Version and what changed."},
};
const HIDDEN_REGIONS = {changed: {t: "Changed from the default", icon: "dots", line: "Settings you changed. Switch one back to return to the default."}};
SCREENS.settings = () => {
    const changed = FEATURES.filter((f) => !f.on).length + CHAT_VIEWS.filter((c) => c.on && ["thoughts"].includes(c.k)).length;
    return `${navbar("Settings")}<div class="scroll">${big("Settings")}${searchField("Search settings")}${group([cell({icon: "dots", label: "Changed from the default", count: changed, run: () => push({s: "region", key: "changed"})})])}${group(
        Object.entries(REGIONS).filter(([key]) => key !== "changed").map(([key, r]) => cell({icon: r.icon, label: r.t, sub: r.line, run: () => push({s: "region", key})})),
        "Everything you can set"
    )}</div>`;
};
const sw = (on_, label, fn) => `<button type="button" class="switch ${on_ ? "on" : ""}" role="switch" aria-checked="${on_}" aria-label="${esc(label)}" ${on(fn)}></button>`;
SCREENS.region = (sc) => {
    const r = REGIONS[sc.key] || HIDDEN_REGIONS[sc.key];
    if (sc.key === "changed") {
        const list = [...FEATURES.filter((f) => !f.on), ...CHAT_VIEWS.filter((c) => c.on && c.k === "thoughts")];
        return `${navbar(r.t)}<div class="scroll">${big(r.t)}<p class="intro">${esc(r.line)}</p>${list.length ? group(list.map((f) => cell({label: f.t, sub: f.line || "What the chat shows", end: sw(f.on, f.t, () => ((f.on = !f.on), render()))}))) : `<p class="empty">Every setting is at its default.</p>`}</div>`;
    }
    let body = "";
    if (sc.key === "features") body = group(FEATURES.map((f) => cell({label: f.t, sub: f.line, end: sw(f.on, f.t, () => ((f.on = !f.on), toast(`${f.t} ${f.on ? "on" : "off"}`), render()))})));
    if (sc.key === "chatviews") body = group(CHAT_VIEWS.map((f) => cell({label: f.t, end: sw(f.on, f.t, () => ((f.on = !f.on), render()))})));
    if (sc.key === "notify") body = group([cell({label: "Alerts on this phone", sub: "Turned on for iPhone", end: sw(true, "Alerts", () => (toast("Alerts are managed by the phone's own settings"), render()))}), ...["When something needs you", "When the agent finishes", "When a to-do is blocked"].map((l, x) => cell({label: l, end: sw(x !== 2, l, () => (toast("Saved"), render()))}))]);
    if (sc.key === "services") body = group(SERVICES.map((s) => cell({dot: s.state === "running" ? "done" : "", label: s.title, sub: `${s.state === "running" ? "Running" : "Stopped"} · ${s.by}`, run: () => push({s: "service", n: s.n})})));
    if (sc.key === "phone")
        body =
            group([cell({icon: "phone", label: "iPhone", sub: "This phone · paired 3 days ago"}), cell({label: "Unpair this phone", danger: true, run: () => sheet({kind: "confirm", title: "Unpair this phone?", sub: "It stops reaching the journal until you pair it again.", button: "Unpair", danger: true, keep: "Keep it paired", run: () => (closeSheet(), toast("Unpaired (in this prototype nothing changes)"), render())})})], "Phones") +
            group([cell({label: "Tunler account", sub: "jesse.tunler.dev · signed in"}), cell({icon: "link", label: "Share links", count: D.share.length, run: () => push({s: "list", type: "share"})})], "Reaching the journal from outside");
    if (sc.key === "pluginsettings") body = group(D.plugin.map((p) => cell({icon: "plug", label: p.title, sub: p.settingsList.join(" · "), run: () => push({s: "item", ref: `plugin:${p.n}`})})));
    if (sc.key === "environments") body = group([...ENVS.map((e) => cell({icon: "branch", label: e.name, sub: e.note, run: () => sheet(envSheet(e))})), cell({label: "New environment", accent: true, icon: "plus", run: () => sheet(newEnvSheet())})]);
    if (sc.key === "system")
        body = group([cell({label: "Project folder", sub: "~/projects/agent-journal"}), cell({label: "Version", sub: "2.253.5 · up to date"}), cell({label: "Update the journal now", accent: true, run: () => (toast("The journal is up to date"), render())}), cell({label: "Restart the journal", run: () => sheet({kind: "confirm", title: "Restart the journal?", sub: "The agent keeps its place. The phone reconnects by itself.", button: "Restart", keep: "Cancel", run: () => (closeSheet(), toast("Restarting"), render())})}), cell({label: "Shut down the journal", danger: true, run: () => sheet({kind: "confirm", title: "Shut down the journal?", sub: "The phone cannot reach it until you start it on your computer.", button: "Shut down", danger: true, keep: "Keep it running", run: () => closeSheet()})})]);
    if (sc.key === "developer") body = group([cell({label: "Record this session", sub: "Saves every step for replay", end: sw(false, "Record this session", () => (toast("Recording started"), render()))}), cell({icon: "terminal", label: "Diagnostics log", run: () => push({s: "terminal", what: "log"})})]);
    if (sc.key === "about") body = `<dl class="facts"><dt>Version</dt><dd>2.253.5</dd><dt>Released</dt><dd>4 days ago</dd></dl><h2 class="section-title">What changed</h2>${group(["2.253.5 · The chat's git chips show branches and worktrees", "2.253.4 · An upgrade fills in every folder the release added", "2.253.3 · A cold dashboard formats its lines a third faster"].map((l) => cell({label: l.split(" · ")[1], sub: l.split(" · ")[0]})))}`;
    return `${navbar(r.t)}<div class="scroll">${big(r.t)}<p class="intro">${esc(r.line)}</p>${body}</div>`;
};
SCREENS.service = (sc) => {
    const s = SERVICES.find((x) => x.n === sc.n);
    const run = s.state === "running";
    const act = (word, state) => () => ((s.state = state), toast(`${s.title}: ${word.toLowerCase()}`), render());
    return `${navbar(s.title)}<div class="scroll">${big(s.title, `${run ? "Running" : "Stopped"} · kept running by ${s.by}`)}<div class="foot-row" style="margin-bottom:16px">${run ? `<button type="button" class="btn small" ${on(act("Stopped", "stopped"))}>Stop</button><button type="button" class="btn small" ${on(act("Restarted", "running"))}>Restart</button>` : `<button type="button" class="btn small primary" ${on(act("Started", "running"))}>Start</button>`}</div><h2 class="section-title">Its log</h2><div class="term">${esc(s.log)}</div></div>`;
};
SCREENS.org = () =>
    `${navbar("Agents and roles")}<div class="scroll">${big("Agents and roles")}<p class="intro">The project's domains, the roles under them, and who fills each role.</p>${DOMAINS.map((d) => group(d.roles.map((r) => cell({icon: "people", label: r.t, sub: `${r.who}${r.now ? " · working now" : ""}`, run: () => open(`agent:${r.who === "Main agent" ? 1 : r.who === "Helper Ada" ? 2 : 3}`)})), d.name)).join("")}<div style="height:16px"></div>${group([cell({label: "Ask the agent to draft a domain", accent: true, icon: "plus", run: () => sheet({kind: "text", title: "Ask the agent to draft a domain", label: "What part of the project is it about?", placeholder: "For example: billing", button: "Send to the agent", required: true, run: (x) => (closeSheet(), say(`Draft a domain: ${x}`), toast("Sent to the agent"), render())})})])}</div>`;
SCREENS.search = () => {
    const cells = Object.keys(D)
        .flatMap((t) => D[t].map((i) => cell({dot: dotOf(t, i), label: i.title, sub: `${KINDS[t].one} ${i.n}`, run: () => open(refOf(t, i))})))
        .join("");
    return `${navbar("Search")}<div class="scroll">${big("Search")}${searchField(`Search everything in ${S.env}`)}<div class="group" data-results>${cells}</div><p class="group-foot">Titles, text and attached files.</p></div>`;
};
SCREENS.files = () =>
    `${navbar("Project files")}<div class="scroll">${big("Project files")}${searchField("Find a file in the project")}${group(FILES.map((f) => cell({icon: f.dir ? "folder" : "file", label: f.path.split("/").pop(), sub: `${f.path}${f.attached ? ` · attached to ${f.attached}` : ""}`, run: f.dir ? () => (toast("Opens the folder"), render()) : () => push({s: "file", path: f.path})})))}</div>`;
SCREENS.file = (sc) => {
    const f = FILES.find((x) => x.path === sc.path);
    return `${navbar(sc.path.split("/").pop(), `<button type="button" class="icon-btn" aria-label="Share" ${on(() => (toast("Opens the phone's share sheet"), render()))}>${icon("share")}</button>`)}<div class="scroll">${big(sc.path.split("/").pop(), `${f.size}${f.attached ? ` · attached to ${f.attached}` : ""}`)}<div class="term">&lt;script setup&gt;
import Icon from "../kit/Icon.vue";
const TABS = [
    {key: "chat", label: "Chat", icon: "chat"},
    {key: "home", label: "Home", icon: "home"},
];
&lt;/script&gt;</div></div>`;
};
SCREENS.commit = (sc) =>
    `${navbar(`Commit ${sc.sha}`)}<div class="scroll"><span class="kind">Commit ${esc(sc.sha)} · 12 min ago · Helper Ada</span><h1 class="title">The board's lanes fit a narrow screen</h1><p class="intro" style="margin:0 0 12px">2 files changed · 18 added · 6 removed</p><div class="diff"><div class="hd">src/web/src/board/BoardLanes.vue</div><div class="del">-    &lt;div class="lanes"&gt;</div><div class="add">+    &lt;div class="lanes" :class="{narrow}"&gt;</div><div class="add">+        &lt;LanePicks :lanes="lanePicks" /&gt;</div><div class="hd">src/web/src/board/lanes.js</div><div class="add">+export const narrow = matchMedia("(max-width: 600px)");</div></div></div>`;
SCREENS.steps = (sc) => {
    const i = D.sequence.find((x) => x.n === sc.n);
    S.draftSteps = S.draftSteps || i.steps.slice();
    const st = S.draftSteps;
    const moveBtn = (x, d, g, l) => `<button type="button" class="icon-btn" aria-label="${l}" ${on(() => {
        const y = x + d;
        if (y < 0 || y >= st.length) return;
        [st[x], st[y]] = [st[y], st[x]];
        render();
    })}>${g}</button>`;
    return `${navbar("Edit the steps")}<div class="scroll">${big("Edit the steps", i.title)}${group(st.map((s, x) => cell({label: `${x + 1}. ${s}`, end: `<span style="display:flex">${moveBtn(x, -1, "↑", "Move up")}${moveBtn(x, 1, "↓", "Move down")}<button type="button" class="icon-btn" aria-label="Remove this step" ${on(() => (st.splice(x, 1), render()))}>${icon("close")}</button></span>`})))}<div style="height:12px"></div>${group([cell({label: "Add a step", accent: true, icon: "plus", run: () => sheet({kind: "text", title: "Add a step", label: "What the agent does in this step", placeholder: "For example: run the checks", button: "Add", required: true, run: (x) => (closeSheet(), st.push(x), render())})})])}</div><footer class="foot"><div class="foot-row"><button type="button" class="btn small" ${on(() => ((S.draftSteps = null), back()))}>Cancel</button><button type="button" class="btn small primary" ${on(() => ((i.steps = st.slice()), (S.draftSteps = null), back(), toast("Steps saved")))}>Save steps</button></div></footer>`;
};
SCREENS.terminal = (sc) =>
    `${navbar(sc.what === "log" ? "Diagnostics log" : "Terminal")}<div class="scroll">${big(sc.what === "log" ? "Diagnostics log" : "Terminal", sc.what === "log" ? "Read only" : "What the agent's terminal shows · read only")}<div class="term">${sc.what === "log" ? "12:04:11 server started\n12:04:12 phone paired\n12:31:40 todo 140 changed by the agent" : "$ npm run test -- phone\n\n ✓ PhoneTabs (4)\n ✓ PhoneBoard (7)\n ✓ PhoneReader (9)\n\nTests  20 passed (20)\nTime   3.41s"}</div></div>`;
/* ---------------- sheets ---------------- */
function sheetHtml(sh) {
    const head = `<div class="grab"></div><div class="sheet-head">${S.sheets.length ? `<button type="button" class="nav-back" style="max-width:none" ${on(sheetBack)}>${icon("back")}Back</button>` : ""}<h2>${esc(sh.title)}</h2><button type="button" class="icon-btn" aria-label="Close" ${on(closeSheet)}>${icon("close")}</button></div>${sh.sub ? `<p class="sheet-sub">${esc(sh.sub)}</p>` : ""}`;
    let body = "";
    let foot = "";
    if (sh.kind === "list") {
        body = group(sh.cells.map((c) => cell({...c, noChev: true})));
        foot = `<button type="button" class="btn" ${on(closeSheet)}>Cancel</button>`;
    }
    if (sh.kind === "text") {
        body = `<div class="field"><label for="sheet-text">${esc(sh.label)}</label><textarea id="sheet-text" placeholder="${esc(sh.placeholder || "")}">${esc(sh.value || "")}</textarea></div>`;
        foot = `<button type="button" class="btn ${sh.danger ? "danger" : sh.good ? "good" : "primary"}" ${on(() => {
            const v = document.getElementById("sheet-text").value.trim();
            if (sh.required && !v) return document.getElementById("sheet-text").focus();
            sh.run(v);
        })}>${esc(sh.button)}</button><button type="button" class="btn plain" ${on(closeSheet)}>Cancel</button>`;
    }
    if (sh.kind === "confirm") {
        foot = `<button type="button" class="btn ${sh.danger ? "danger" : "primary"}" ${on(sh.run)}>${esc(sh.button)}</button><button type="button" class="btn" ${on(closeSheet)}>${esc(sh.keep || "Cancel")}</button>`;
    }
    if (sh.kind === "form") {
        body = sh.fields.map((f) => `<div class="field"><label for="f-${f.k}">${esc(f.label)}</label>${f.area ? `<textarea id="f-${f.k}" placeholder="${esc(f.placeholder || "")}">${esc(f.value || "")}</textarea>` : `<input id="f-${f.k}" value="${esc(f.value || "")}" placeholder="${esc(f.placeholder || "")}">`}</div>`).join("") + (sh.extra || "");
        foot = `<button type="button" class="btn primary" ${on(() => {
            const v = Object.fromEntries(sh.fields.map((f) => [f.k, document.getElementById(`f-${f.k}`).value.trim()]));
            if (sh.fields[0].required && !v[sh.fields[0].k]) return document.getElementById(`f-${sh.fields[0].k}`).focus();
            sh.run(v);
        })}>${esc(sh.button)}</button><button type="button" class="btn plain" ${on(closeSheet)}>Cancel</button>`;
    }
    if (sh.kind === "custom") {
        body = sh.html();
        foot = sh.foot ? sh.foot() : "";
    }
    return `<div class="scrim" ${on(closeSheet)}></div><div class="sheet" role="dialog" aria-modal="true" aria-label="${esc(sh.title)}">${head}<div class="sheet-body">${body}</div>${foot ? `<div class="sheet-foot">${foot}</div>` : ""}</div>`;
}
function placesSheet() {
    return {
        kind: "custom",
        title: "Switch journal or environment",
        html: () =>
            group(
                JOURNALS.map((j) =>
                    cell({
                        icon: "journal",
                        tint: j.color,
                        label: j.name,
                        sub: j.note + (j.needs ? ` · ${j.needs} need you` : ""),
                        check: j.name === S.journal,
                        end: j.stopped ? `<button type="button" class="linkbtn" ${on(() => ((JOURNALS.splice(JOURNALS.indexOf(j), 1)), toast(`${j.name} removed from the list until it runs again`), render()))}>Remove</button>` : undefined,
                        run: j.stopped ? null : () => ((S.journal = j.name), closeSheet(), toast(`Switched to ${j.name}`), render()),
                    })
                ),
                "Journals on this computer"
            ) +
            group(
                [
                    ...ENVS.map(
                        (e) =>
                            `<div class="cell"><button type="button" class="cell-hit" aria-label="Switch to ${esc(e.name)}" ${on(() => ((S.env = e.name), closeSheet(), toast(`Switched to ${e.name}`), render()))}><span class="dot ${e.running ? "done" : ""}"></span><span class="cell-main"><span class="cell-title">${esc(e.name)}</span><span class="cell-sub">${esc(e.note)}</span></span>${e.name === S.env ? `<span class="check">${icon("todo")}</span>` : ""}</button><button type="button" class="linkbtn" ${on(() => ((e.running = !e.running), (e.note = e.running ? "Agent working" : "No agent running"), toast(e.running ? `Agent started in ${e.name}` : `Agent stopped in ${e.name}`), render()))}>${e.running ? "Stop agent" : "Start agent"}</button></div>`
                    ),
                    cell({label: "New environment", accent: true, icon: "plus", run: () => sheet(newEnvSheet())}),
                ],
                `Environments of ${S.journal}`
            ),
    };
}
const newEnvSheet = () => ({
    kind: "form",
    title: "New environment",
    sub: "Its own copy of the code, with its own to-dos, agent and history.",
    fields: [{k: "name", label: "Name", placeholder: "for example billing-fix", required: true}],
    extra: group([cell({label: "Start an agent in it", end: `<button type="button" class="switch on" role="switch" aria-checked="true" aria-label="Start an agent in it" onclick="this.classList.toggle('on')"></button>`})]),
    button: "Create",
    run: (v) => (ENVS.push({name: v.name, running: true, note: "Agent starting"}), closeSheet(), toast(`Made ${v.name}`), render()),
});
const envSheet = (e) => ({
    kind: "list",
    title: e.name,
    sub: e.note,
    cells: [
        {label: "Switch to it", run: () => ((S.env = e.name), closeSheet(), toast(`Switched to ${e.name}`), render())},
        {label: e.running ? "Stop its agent" : "Start an agent in it", run: () => ((e.running = !e.running), (e.note = e.running ? "Agent working" : "No agent running"), closeSheet(), render())},
        {label: "Close the environment", danger: true, run: () => sheet({kind: "confirm", title: `Close ${e.name}?`, sub: "Its agent stops. Its to-dos and history stay readable.", button: "Close it", danger: true, keep: "Keep it", run: () => (ENVS.splice(ENVS.indexOf(e), 1), closeSheet(), toast(`Closed ${e.name}`), render())})},
    ],
});
function agentSheet() {
    const a = S.agent;
    const MODES = [
        ["builder", "Builder", "Builds it itself, and sends helpers when a job is better done beside it."],
        ["orchestrator", "Orchestrator", "Plans, hands the work to helpers, then reviews and merges it."],
        ["solo", "Solo", "Does everything itself, with no helpers."],
    ];
    return {
        kind: "custom",
        title: "Main agent",
        sub: `In ${S.env} · ${a.state === "paused" ? "paused" : "working for 34 min"}`,
        html: () =>
            group([cell({icon: "todo", label: "Doing now", sub: "To-do 140 · Show to-dos on the phone", run: () => (closeSheet(), open("todo:140"))}), cell({label: "Memory used", meta: `<div class="bar"><i style="width:${a.context}%"></i></div><span class="cell-sub">${a.context}% of what it can hold in mind; it summarises the talk when it is full</span>`})], "State") +
            group([cell({label: "Auto", sub: "Goes on to the next to-do without asking", end: sw(a.auto, "Auto", () => ((a.auto = !a.auto), render()))})]) +
            group(MODES.map(([k, l, n]) => cell({label: l, sub: n, check: a.mode === k, run: () => ((a.mode = k), render())})), "Work mode") +
            group([cell({icon: "bot", label: "Helpers", count: 2, run: () => (closeSheet(), push({s: "list", type: "agent"}))}), cell({icon: "terminal", label: "Its terminal", run: () => (closeSheet(), push({s: "terminal"}))}), cell({icon: "eye", label: "What the chat shows", run: () => (closeSheet(), push({s: "region", key: "chatviews"}))})], "More about it"),
        foot: () =>
            `<div class="foot-row"><button type="button" class="btn" ${on(() => ((a.state = a.state === "paused" ? "working" : "paused"), toast(a.state === "paused" ? "Paused the agent" : "The agent works again"), render()))}>${a.state === "paused" ? "Keep working" : "Pause"}</button><button type="button" class="btn danger" ${on(() => sheet({kind: "confirm", title: "Stop the agent?", sub: "It stops what it is doing now. You can start it again from Journals and environments.", button: "Stop", danger: true, keep: "Cancel", run: () => ((a.state = "paused"), closeSheet(), toast("The agent stopped"), render())}))}>Stop</button></div>`,
    };
}
const atWorkSheet = () => ({
    kind: "list",
    title: "At work now",
    sub: "Separate agents, one job each.",
    cells: D.agent.map((a) => ({dot: a.state, label: a.title, sub: a.body[0], run: () => (closeSheet(), open(`agent:${a.n}`))})),
});
const needsSheet = () => ({
    kind: "list",
    title: "Needs you",
    cells: waitingRefs().map((r) => {
        const i = find(r);
        return {dot: "waiting", label: i.title, sub: `${WAIT_WORD[parse(r).type]} · ${ago(i.made)}`, run: () => (closeSheet(), open(r))};
    }),
});
const attachSheet = (title) => ({
    kind: "list",
    title,
    cells: [
        {icon: "eye", label: "Take a photo", run: () => (closeSheet(), toast("Opens the camera"), render())},
        {icon: "template", label: "Choose from your photos", run: () => (closeSheet(), toast("Opens your photos"), render())},
        {icon: "file", label: "Choose a file", run: () => (closeSheet(), toast("Opens your files"), render())},
        {icon: "inbox", label: "Hand over a pile of files", sub: "The agent reads them and sorts them into a collection", run: () => (closeSheet(), toast("Pick the files; the agent sorts them"), render())},
    ],
});
function shareSheet(t, i) {
    return {
        kind: "custom",
        title: `Share ${name(t, i)}`,
        sub: "Anyone with the link can read this one item, and nothing else.",
        html: () => (S.madeLink ? `<div class="share-link">https://jesse.tunler.dev/s/${i.n}x7q-${t}</div>` : ""),
        foot: () =>
            S.madeLink
                ? `<button type="button" class="btn primary" ${on(() => (toast("Link copied"), (S.madeLink = false), closeSheet()))}>Copy the link</button><button type="button" class="btn" ${on(() => ((S.madeLink = false), closeSheet()))}>Done</button>`
                : `<button type="button" class="btn primary" ${on(() => ((S.madeLink = true), render()))}>Make a share link</button><button type="button" class="btn plain" ${on(closeSheet)}>Cancel</button>`,
    };
}
function reviewSheet(i) {
    S.rv = S.rv || {agents: 2, depth: "Normal"};
    return {
        kind: "custom",
        title: "Ask for a review",
        sub: `Other agents read plan ${i.n} and say what they would change.`,
        html: () =>
            `<h2 class="group-head">How many agents</h2><div class="seg">${[1, 2, 3].map((n) => `<button type="button" class="${S.rv.agents === n ? "on" : ""}" ${on(() => ((S.rv.agents = n), render()))}>${n}</button>`).join("")}</div><h2 class="group-head">How thorough</h2><div class="seg">${["Quick", "Normal", "Thorough"].map((n) => `<button type="button" class="${S.rv.depth === n ? "on" : ""}" ${on(() => ((S.rv.depth = n), render()))}>${n}</button>`).join("")}</div>`,
        foot: () => `<button type="button" class="btn primary" ${on(() => (closeSheet(), toast(`Review asked from ${S.rv.agents} agents`), render()))}>Ask for the review</button>`,
    };
}
function messageSheet(m) {
    return {
        kind: "custom",
        title: m.who === "me" ? "Your message" : "The agent's message",
        sub: m.text,
        html: () =>
            `<div class="faces" role="group" aria-label="React">${["+1", "Yes", "No", "?", "Thanks"].map((f) => `<button type="button" aria-label="React ${f}" ${on(() => (closeSheet(), toast(`You reacted ${f}`), render()))}>${f}</button>`).join("")}</div>` +
            group([
                cell({icon: "reply", noChev: true, label: "Reply to it", run: () => (closeSheet(), setTimeout(() => document.getElementById("draft")?.focus(), 50))}),
                cell({icon: "copy", noChev: true, label: "Copy the text", run: () => (closeSheet(), toast("Copied"), render())}),
                cell({icon: "pin", noChev: true, label: "Pin it over the chat", run: () => ((S.pinned = m), closeSheet())}),
                ...(m.who === "me" ? [cell({icon: "close", noChev: true, label: "Delete your message", danger: true, run: () => sheet({kind: "confirm", title: "Delete your message?", sub: "It goes from the chat. The agent has already read it.", button: "Delete it", danger: true, keep: "Keep it", run: () => ((S.chat = S.chat.filter((x) => x !== m)), closeSheet())})})] : []),
            ]),
    };
}
function newSheet(t) {
    const fields = [{k: "title", label: "Title", placeholder: `${KINDS[t].one} title, at most 80 characters`, required: true}];
    if (t !== "collection") fields.push({k: "summary", label: "One short line about it", placeholder: "Optional"}, {k: "body", label: "Details", placeholder: "Extra details, as long as they need to be", area: true});
    const extra =
        t === "plan"
            ? `<h2 class="group-head">How thorough</h2><div class="seg"><button type="button" class="on">Normal</button><button type="button">Thorough</button></div>`
            : t === "doc"
              ? group([cell({label: "Start from", sub: "No template", run: () => (toast("Pick a template: Bug report, Design brief"), render())})])
              : t === "todo"
                ? group([cell({label: "Priority", sub: "Normal"})])
                : "";
    return {
        kind: "form",
        title: KINDS[t].make || `New ${KINDS[t].one.toLowerCase()}`,
        fields,
        extra,
        button: "Add",
        run: (v) => {
            const n = Math.max(0, ...D[t].map((x) => x.n)) + 1;
            D[t].unshift({n, title: v.title, state: t === "todo" ? "todo" : "open", priority: "Normal", assigned: "", made: 0, body: v.body ? [v.body] : [], summary: v.summary, comments: [], after: [], items: [], sections: [], files: [], phases: [], steps: [], words: [], choices: []});
            closeSheet();
            toast(`Added ${KINDS[t].one.toLowerCase()} ${n}`);
            render();
        },
    };
}
const newWorkSheet = () => ({kind: "text", title: "New work for the board", sub: "Describe the work. The agent drafts the tickets and you approve them.", label: "What should be done?", placeholder: "For example: phone alerts for blocked to-dos", button: "Draft the tickets", required: true, run: (x) => (closeSheet(), say(`New work: ${x}`), toast("The agent drafts the tickets"), render())});
const boardSheet = () => ({
    kind: "custom",
    title: "Board settings",
    html: () => group([cell({label: "Show the Done lane", end: sw(S.doneLane, "Show the Done lane", () => ((S.doneLane = !S.doneLane), render()))}), cell({label: "Roles on the board", sub: ROLES.slice(0, 3).join(", "), run: () => (closeSheet(), push({s: "org"}))}), cell({label: "New board", accent: true, icon: "plus", run: () => sheet({kind: "text", title: "New board", label: "What is the board for?", placeholder: "For example: the phone release", button: "Make the board", required: true, run: () => (closeSheet(), toast("Board made"), render())})})]),
});
const pluginAddSheet = () => ({
    kind: "list",
    title: "Add a plugin",
    cells: [
        {icon: "plug", label: "Install a plugin", sub: "From owner/repo, a GitHub link or a folder", run: () => sheet({kind: "text", title: "Install a plugin", label: "Where it comes from", placeholder: "owner/repo, a GitHub link or a folder", button: "Install", required: true, run: (x) => (closeSheet(), toast(`Installing ${x}`), render())})},
        {icon: "bulb", label: "Make a plugin", sub: "Describe it and the agent builds it", run: () => sheet({kind: "text", title: "Make a plugin", label: "What should the plugin do?", placeholder: "Describe it in a few lines", button: "Send to the agent", required: true, run: (x) => (closeSheet(), say(`Make a plugin: ${x}`), toast("Sent to the agent"), render())})},
    ],
});
/* ---------------- the checklist: every desktop place and action ---------------- */
const L = (type) => ({s: "list", type});
const I = (ref) => ({s: "item", ref});
const J = (...stack) => ({tab: "journal", stack});
const R = (key) => J({s: "settings"}, {s: "region", key});
const INVENTORY = [
    ["Places on the desktop", [
        ["Chat with the agent", "today", "Chat tab", {tab: "chat"}],
        ["Home", "today", "Home tab", {tab: "home"}],
        ["To-dos", "new", "To-dos tab, List (Home card today)", {tab: "todos", view: "list"}],
        ["Board", "new", "To-dos tab, Board; also Journal › Board", {tab: "todos", view: "board"}],
        ["Suggestions", "part", "Journal › Suggestions (Home card today)", J(L("suggestion"))],
        ["Plans", "part", "Journal › Plans (Home card and reader today)", J(L("plan"))],
        ["Questions", "today", "Home › Needs you; Journal › Questions", J(L("question"))],
        ["Collections", "new", "Journal › Collections", J(L("collection"))],
        ["Documents", "part", "Journal › Documents (Home card today)", J(L("doc"))],
        ["Reports", "part", "Journal › Reports (Home card today)", J(L("report"))],
        ["Work in progress", "today", "Home card; Journal › Work", J(L("work"))],
        ["Agents, helpers, subagents", "today", "Chat › at work, agent button; Journal › Agents", J(L("agent"))],
        ["Triggers", "new", "Journal › Triggers", J(L("trigger"))],
        ["Sequences", "new", "Journal › Sequences", J(L("sequence"))],
        ["Checks", "new", "Journal › Checks", J(L("check"))],
        ["Tickets", "new", "Journal › Tickets", J(L("ticket"))],
        ["Messages between agents", "new", "Journal › Messages", J(L("message"))],
        ["Facts", "new", "Journal › Facts", J(L("fact"))],
        ["Reminders", "new", "Journal › Reminders", J(L("reminder"))],
        ["Rules", "new", "Journal › Rules", J(L("rule"))],
        ["Tools", "new", "Journal › Tools", J(L("tool"))],
        ["Skills", "new", "Journal › Skills", J(L("skill"))],
        ["Templates", "new", "Journal › Templates", J(L("template"))],
        ["Ways of talking (profiles)", "new", "Journal › Ways of talking", J(L("profile"))],
        ["Share links", "new", "Journal › Share links", J(L("share"))],
        ["Organization: domains and roles", "new", "Journal › Agents and roles", J({s: "org"})],
        ["Plugins and their pages", "new", "Journal › Plugins › a plugin", J(L("plugin"), I("plugin:2"))],
        ["Settings: features", "new", "Journal › Settings › Features", R("features")],
        ["Settings: system", "new", "Journal › Settings › System", R("system")],
        ["Settings: services and logs", "new", "Journal › Settings › Services", R("services")],
        ["Settings: phone and share links", "new", "Journal › Settings › Phone and share links", R("phone")],
        ["Settings: plugin settings", "new", "Journal › Settings › Plugin settings", R("pluginsettings")],
        ["Settings: environments", "new", "Journal › Settings › Environments", R("environments")],
        ["Settings: developer, diagnostics log", "new", "Journal › Settings › Developer", R("developer")],
        ["Settings: changed from the default", "new", "Journal › Settings, first line", J({s: "settings"})],
        ["What the chat shows (feed filters)", "new", "Settings › What the chat shows; agent sheet", R("chatviews")],
        ["About: version and changes", "new", "Journal › Settings › About this journal", R("about")],
        ["Search", "new", "Journal › Search", J({s: "search"})],
        ["Project files and one file", "new", "Journal › Project files", J({s: "files"})],
        ["A commit", "new", "Chat › tap a commit in a message", {tab: "chat", stack: [{s: "commit", sha: "9f2d76a"}]}],
        ["The agent's terminal", "new", "Agent sheet › Its terminal", {tab: "chat", stack: [{s: "terminal"}]}],
        ["Journals on this computer", "today", "Chat › journal name at the top", {tab: "chat", sheet: () => placesSheet()}],
        ["Environments list", "today", "Chat › journal name at the top", {tab: "chat", sheet: () => placesSheet()}],
        ["Notifications", "today", "Phone alerts; Settings › Alerts on this phone", R("notify")],
        ["What happened while you were away", "today", "Chat marks what is new since you last looked", {tab: "chat"}],
        ["Quick menu (command search)", "part", "Each command has its own place above; finding is Search", J({s: "search"})],
    ]],
    ["Actions on the desktop", [
        ["Send a message, attach files", "today", "Chat › message field and +", {tab: "chat"}],
        ["Hand the agent a pile of files", "new", "Chat › + › Hand over a pile of files", {tab: "chat", sheet: () => attachSheet("Add to your message")}],
        ["React, reply, copy", "today", "Chat › hold a message", {tab: "chat"}],
        ["Pin a message over the chat", "new", "Chat › hold a message › Pin it", {tab: "chat"}],
        ["Delete your message", "new", "Chat › hold your message", {tab: "chat"}],
        ["Answer a question or suggestion", "today", "The item, its answer buttons", J(L("question"), I("question:51"))],
        ["Answer in your own words, change the answer", "part", "The item; More", J(L("suggestion"), I("suggestion:33"))],
        ["Approve a plan, ask for changes", "today", "Plan, bottom buttons", J(L("plan"), I("plan:21"))],
        ["Ask for a review of a plan", "today", "Plan › More", J(L("plan"), I("plan:21"))],
        ["Start an approved plan", "new", "Plan, bottom button", J(L("plan"), I("plan:19"))],
        ["One copy of the code for every ticket", "new", "Plan › More", J(L("plan"), I("plan:20"))],
        ["Close a plan without finishing", "new", "Plan › More", J(L("plan"), I("plan:20"))],
        ["Start a to-do", "new", "Swipe left › Start; to-do, bottom button", {tab: "todos", view: "list"}],
        ["Mark a to-do done, with how it ended", "new", "Swipe right; to-do, bottom button", {tab: "todos", stack: [I("todo:140")]}],
        ["Block a to-do, with why; unblock it", "new", "Swipe left › Block; to-do, bottom buttons", {tab: "todos", stack: [I("todo:138")]}],
        ["Change a to-do's priority", "new", "To-do › More", {tab: "todos", stack: [I("todo:141")]}],
        ["Assign to a role", "new", "To-do or ticket › More", {tab: "todos", stack: [I("todo:142")]}],
        ["Move a card to another lane", "new", "Board › hold a card", {tab: "todos", view: "board"}],
        ["Move a ticket to another stage", "new", "Ticket › More", J(L("ticket"), I("ticket:14"))],
        ["Move to another environment", "new", "Item › More", {tab: "todos", stack: [I("todo:143")]}],
        ["Comment", "today", "Every item, bottom button", {tab: "todos", stack: [I("todo:140")]}],
        ["Share an item, make another link, stop a link", "part", "Document, report or collection, share button; Share links", J(L("doc"), I("doc:88"))],
        ["Make a plan from a final document", "new", "Document, bottom button", J(L("doc"), I("doc:88"))],
        ["Add to a collection", "new", "Item › More", J(L("report"), I("report:61"))],
        ["Attach files to an item", "new", "To-do, document or collection › More", J(L("doc"), I("doc:87"))],
        ["Edit title and details", "new", "Item › More", J(L("doc"), I("doc:87"))],
        ["Close, mark as read, mark final", "new", "Item › More; swipe right on a report", J(L("report"))],
        ["Reopen", "new", "A closed item › bottom button or More", {tab: "todos", stack: [I("todo:133")]}],
        ["Delete, with a confirm", "new", "Item › More › Delete", J(L("fact"), I("fact:9"))],
        ["Make a new item of any kind", "new", "The New button at the bottom of each list", J(L("fact"))],
        ["Order a list (newest, A to Z, priority)", "new", "The order button at the bottom of each list", {tab: "todos", view: "list"}],
        ["Show the closed items", "new", "Bottom of each list", J(L("plan"))],
        ["Filter a list", "new", "The search field at the top of each list", J(L("plan"))],
        ["New work for the board, board settings, new board", "new", "To-dos › Board, bottom bar", {tab: "todos", view: "board"}],
        ["Pin a rule to the chat", "new", "Rule › More", J(L("rule"), I("rule:7"))],
        ["Change a trigger's words and what it does; turn it off", "new", "Trigger › More", J(L("trigger"), I("trigger:4"))],
        ["Edit a sequence's steps", "new", "Sequence, bottom button", J(L("sequence"), I("sequence:2"))],
        ["Load a skill at session start", "new", "Skill, switch", J(L("skill"), I("skill:2"))],
        ["Install or make a plugin", "new", "Plugins, Add a plugin", J(L("plugin"))],
        ["Update, set up again, remove a plugin", "new", "Plugin, bottom buttons and More", J(L("plugin"), I("plugin:1"))],
        ["Start, stop, restart a service; read its log", "new", "Settings › Services › a service", J({s: "settings"}, {s: "region", key: "services"}, {s: "service", n: 3})],
        ["Switch features on and off", "new", "Settings › Features", R("features")],
        ["Ask the agent to draft a domain", "new", "Agents and roles, bottom", J({s: "org"})],
        ["Pause, keep working, stop the agent", "today", "Chat › agent button", {tab: "chat", sheet: () => agentSheet()}],
        ["Auto, work mode", "today", "Chat › agent button", {tab: "chat", sheet: () => agentSheet()}],
        ["How much the agent holds in mind", "new", "Chat › agent button › Memory used", {tab: "chat", sheet: () => agentSheet()}],
        ["Allow or deny a helper", "today", "Helper › Allow, Deny", J(L("agent"), I("agent:3"))],
        ["Stop a helper", "new", "Helper › More", J(L("agent"), I("agent:2"))],
        ["Switch journal or environment", "today", "Chat › journal name", {tab: "chat", sheet: () => placesSheet()}],
        ["Start or stop the agent of an environment", "today", "Journal name › Start agent, Stop agent", {tab: "chat", sheet: () => placesSheet()}],
        ["New environment", "new", "Journal name › New environment", {tab: "chat", sheet: () => newEnvSheet()}],
        ["Close an environment", "new", "Settings › Environments › an environment", R("environments")],
        ["Remove a stopped journal from the list", "new", "Journal name › Remove", {tab: "chat", sheet: () => placesSheet()}],
        ["Update, restart, shut down the journal", "new", "Settings › System", R("system")],
        ["Unpair this phone", "new", "Settings › Phone and share links", R("phone")],
        ["Text size while reading", "today", "Aa in every item", {tab: "todos", stack: [I("todo:140")]}],
    ]],
    ["Left out, with the reason", [
        ["Floating chat, full screen, layout presets, side-by-side panes, collapsing the sidebar", "out", "These arrange windows on a big screen. A phone shows one screen at a time, so there is nothing to arrange.", null],
        ["Keyboard shortcuts", "out", "A phone has no keyboard shortcuts; each command has a button.", null],
    ]],
];
const WALK = [
    ["Chat: hold a message, open the agent button, open the journal name", {tab: "chat"}],
    ["Home: what needs you, then Edit cards", {tab: "home"}],
    ["To-dos: swipe a to-do right, then left; hold one", {tab: "todos", view: "list"}],
    ["To-dos › Board: change lane, hold a card, Board settings", {tab: "todos", view: "board"}],
    ["A to-do: Start, Mark done, Block, More", {tab: "todos", stack: [I("todo:141")]}],
    ["Journal: every place, grouped", {tab: "journal"}],
    ["A plan waiting for approval", J(L("plan"), I("plan:21"))],
    ["Settings and its parts", J({s: "settings"})],
    ["A plugin with its pages, settings and services", J(L("plugin"), I("plugin:2"))],
];
const OPTIONS = {
    A: ["Four tabs: Chat, Home, To-dos, Journal", "To-dos get their own tab, with List and Board. Journal lists every other place, grouped like the desktop sidebar. The pick: to-dos are what you asked to see, and a tab is one thumb tap from anywhere."],
    B: ["Three tabs: Chat, Home, Journal", "To-dos sit first inside Journal. Fewer tabs, but to-dos are always two taps away."],
    C: ["Two tabs as today: Chat, Home", "Home grows an Everything else part under its cards. Nothing new to learn, but Home gets long and every place is a scroll away."],
};
function renderNotes() {
    const el = document.getElementById("notes");
    if (!el || getComputedStyle(el).display === "none") return;
    const mark = {today: '<span class="tag today">On the phone today</span>', part: '<span class="tag today">Partly today</span>', new: '<span class="tag new">New</span>', out: '<span class="tag out">Left out</span>'};
    el.innerHTML = `
        <h1>The phone app reaches every desktop place</h1>
        <p>A clickable prototype of the phone app. Chat stays the first tab and the heart of the app; everything the desktop viewer offers now has a place one or two taps away. Choices open in sheets from the bottom, so the thumb reaches them. The look follows the phone app as it is: dark, the system font, the viewer's colours.</p>
        <p>Open this file on your phone to try it full screen. On a phone, the small Prototype button at the top switches the option.</p>
        <h2>How you move around: three options</h2>
        <div class="opts">${Object.entries(OPTIONS).map(([k, [t, d]]) => `<button type="button" class="opt ${S.opt === k ? "on" : ""}" data-opt="${k}"><span class="opt-id">${k}</span><span><b>${t}${k === "A" ? '<span class="pick">The pick</span>' : ""}</b><span>${d}</span></span></button>`).join("")}</div>
        <h2>Screens to walk through</h2>
        <ol class="walk">${WALK.map(([t], x) => `<li><span style="flex:1">${t}</span><button type="button" class="go" data-walk="${x}">Open</button></li>`).join("")}</ol>
        <h2>Every desktop place and action, and where it lives on the phone</h2>
        <table class="inv">${INVENTORY.map(([head, rows], g) => `<tr><th colspan="3">${head}</th></tr>${rows.map(([what, st, where, target], x) => `<tr><td>${what}</td><td>${mark[st]}</td><td>${where}${target ? ` <button type="button" class="go" data-inv="${g}:${x}">Open</button>` : ""}</td></tr>`).join("")}`).join("")}</table>
    `;
}
const notesSheet = () => ({
    kind: "custom",
    title: "About this prototype",
    sub: "Pick how you move around. Option A is the pick.",
    html: () => group(Object.entries(OPTIONS).map(([k, [t, d]]) => cell({label: `${k}: ${t}`, sub: d, check: S.opt === k, run: () => ((S.opt = k), closeSheet())}))) + group(WALK.map(([t, target]) => cell({label: t, run: () => go(target)})), "Screens to walk through"),
});
/* ---------------- gestures and events ---------------- */
const phoneEl = document.getElementById("phone");
document.addEventListener("click", (e) => {
    const opt = e.target.closest("[data-opt]");
    if (opt) {
        S.opt = opt.dataset.opt;
        S.stacks = {chat: [], home: [], todos: [], journal: []};
        return render();
    }
    const walk = e.target.closest("[data-walk]");
    if (walk) return go(WALK[Number(walk.dataset.walk)][1]);
    const inv = e.target.closest("[data-inv]");
    if (inv) {
        const [g, x] = inv.dataset.inv.split(":").map(Number);
        return go(INVENTORY[g][1][x][3]);
    }
    const f = e.target.closest("[data-fn]");
    if (f && phoneEl.contains(f)) {
        e.preventDefault();
        FNS[Number(f.dataset.fn)](e, f);
    }
});
phoneEl.addEventListener("input", (e) => {
    if (!e.target.matches("[data-filter]")) return;
    const q = e.target.value.trim().toLowerCase();
    const scope = e.target.closest(".scroll");
    const results = scope.querySelector("[data-results]");
    scope.querySelectorAll(results ? "[data-results] [data-find]" : ".group [data-find]").forEach((el) => {
        el.style.display = !q || el.dataset.find.includes(q) ? "" : "none";
    });
    if (results) results.style.display = q ? "" : "none";
});
let drag = null;
phoneEl.addEventListener("pointerdown", (e) => {
    const front = e.target.closest(".sw-front");
    const msg = e.target.closest("[data-hold-msg]");
    const screen = e.target.closest(".screen");
    const box = phoneEl.getBoundingClientRect();
    if (screen && S.stacks[S.tab].length && e.clientX - box.left < 22 && !S.sheet) {
        drag = {edge: true, x: e.clientX, screen};
        return;
    }
    if (!front && !msg) return;
    drag = {front, msg, x: e.clientX, y: e.clientY, dx: 0, moving: false, held: false, open: front && front.dataset.open === "1"};
    drag.timer = setTimeout(() => {
        if (!drag || drag.moving) return;
        drag.held = true;
        if (navigator.vibrate) navigator.vibrate(8);
        FNS[Number((front || msg).dataset[front ? "hold" : "holdMsg"])]();
    }, 480);
});
phoneEl.addEventListener("pointermove", (e) => {
    if (!drag) return;
    const dx = e.clientX - drag.x;
    if (drag.edge) {
        if (dx > 0) drag.screen.style.transform = `translateX(${dx}px)`;
        drag.dx = dx;
        return;
    }
    const dy = e.clientY - drag.y;
    if (!drag.moving && (Math.abs(dx) > 8 || Math.abs(dy) > 8)) {
        clearTimeout(drag.timer);
        if (!drag.front || Math.abs(dy) > Math.abs(dx)) {
            drag = drag.held ? drag : null;
            return;
        }
        drag.moving = true;
        drag.front.classList.add("dragging");
        drag.front.setPointerCapture(e.pointerId);
    }
    if (!drag.moving) return;
    const hasLead = drag.front.dataset.lead !== "";
    const trailW = Number(drag.front.dataset.trail);
    let x = (drag.open ? -trailW : 0) + dx;
    x = Math.max(-trailW - 30, Math.min(hasLead ? 240 : 0, x));
    drag.dx = x;
    drag.front.style.transform = `translateX(${x}px)`;
});
function closeOpenRows(except) {
    phoneEl.querySelectorAll('.sw-front[data-open="1"]').forEach((f) => {
        if (f === except) return;
        f.dataset.open = "";
        f.style.transform = "";
    });
}
phoneEl.addEventListener("pointerup", (e) => {
    if (!drag) return;
    const d = drag;
    drag = null;
    clearTimeout(d.timer);
    if (d.edge) {
        d.screen.style.transform = "";
        if (d.dx > 90) back();
        return;
    }
    if (d.held) return;
    if (d.moving) {
        d.front.classList.remove("dragging");
        const trailW = Number(d.front.dataset.trail);
        if (d.dx > 110 && d.front.dataset.lead !== "") {
            d.front.style.transform = "translateX(100%)";
            setTimeout(() => FNS[Number(d.front.dataset.lead)](), 180);
        } else if (d.dx < -50) {
            closeOpenRows(d.front);
            d.front.style.transform = `translateX(${-trailW}px)`;
            d.front.dataset.open = "1";
        } else {
            d.front.style.transform = "";
            d.front.dataset.open = "";
        }
        return;
    }
    if (d.front) {
        if (d.open) {
            d.front.style.transform = "";
            d.front.dataset.open = "";
            return;
        }
        if (phoneEl.querySelector('.sw-front[data-open="1"]')) return closeOpenRows();
        FNS[Number(d.front.dataset.tap)]();
    }
});
phoneEl.addEventListener("pointercancel", () => {
    if (drag && drag.front) {
        drag.front.classList.remove("dragging");
        drag.front.style.transform = "";
    }
    if (drag && drag.screen) drag.screen.style.transform = "";
    if (drag) clearTimeout(drag.timer);
    drag = null;
});
phoneEl.addEventListener("contextmenu", (e) => {
    if (e.target.closest(".sw-front, [data-hold-msg]")) e.preventDefault();
});
phoneEl.addEventListener("keydown", (e) => {
    const front = e.target.closest(".sw-front");
    if (front && (e.key === "Enter" || e.key === " ")) {
        e.preventDefault();
        FNS[Number(front.dataset.tap)]();
    }
});
window.addEventListener("resize", () => renderNotes());
render();
</script>
</body>
</html>
