ghostwriter-muse-pages 13.0.0 → 14.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build_pages.py +2 -2
- package/kit.js +25 -19
- package/package.json +1 -1
package/build_pages.py
CHANGED
|
@@ -20,8 +20,8 @@ Every page it writes is a few lines that load the page code, at one exact versio
|
|
|
20
20
|
"""
|
|
21
21
|
import argparse, json, os, re, sys
|
|
22
22
|
|
|
23
|
-
BUILDER = "0.9.
|
|
24
|
-
KIT_VERSION = "
|
|
23
|
+
BUILDER = "0.9.2" # this builder's own version
|
|
24
|
+
KIT_VERSION = "14.0.0" # the exact version of the page code every page loads
|
|
25
25
|
KIT_URL = "https://cdn.jsdelivr.net/npm/ghostwriter-muse-pages@" + KIT_VERSION + "/kit.js"
|
|
26
26
|
FONTS_URL = "https://fonts.googleapis.com/css2?family=Caveat:wght@600;700&family=Inter:wght@400;500;600&family=Lora:ital,wght@0,400;0,500;0,600;1,400&display=swap"
|
|
27
27
|
GHOSTWRITERS = {"lawrence", "kenji", "saira", "octavio", "zora", "boo", "imani", "indigo", "idris", "ryu", "marcus-aurelius", "frederick-douglass", "jane-austen",
|
package/kit.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
/* GhostWriter & the Muse: the book's pages. Page kit
|
|
1
|
+
/* GhostWriter & the Muse: the book's pages. Page kit 14.0.0. */
|
|
2
2
|
(function(){
|
|
3
3
|
if (window.GW_KIT) return;
|
|
4
|
-
window.GW_KIT = "
|
|
4
|
+
window.GW_KIT = "14.0.0";
|
|
5
5
|
const CFG = window.GW_PAGE || {};
|
|
6
6
|
if (!document.querySelector('link[href*="fonts.googleapis.com"]')) { const l = document.createElement('link'); l.rel = 'stylesheet'; l.href = "https://fonts.googleapis.com/css2?family=Caveat:wght@600;700&family=Inter:wght@400;500;600&family=Lora:ital,wght@0,400;0,500;0,600;1,400&display=swap"; document.head.appendChild(l); }
|
|
7
|
-
const st = document.createElement('style'); st.textContent = "\n/* Layout: eight separate pages behind one top bar (Overview, Book Setup, Voice, Stories, Notes, Outline, Manuscript, Ghostwriter). Every item can be edited in place. The book's own prose appears as paper on the dark desk, and what the author has shared appears as sticky notes pinned to it. */\n:root{\n color-scheme:dark;\n --bg:hsl(0 0% 2%); --card:hsl(0 0% 4%); --raise:hsl(0 0% 8%); --line:hsl(0 0% 15%);\n --fg:hsl(0 0% 98%); --soft:hsl(0 0% 84%); --muted:hsl(0 0% 64%);\n --green:hsl(152 69% 50%); --green-ink:hsl(0 0% 5%); --green-wash:hsl(152 69% 50% / .10); --green-line:hsl(152 69% 50% / .38);\n --amber:hsl(38 92% 62%); --amber-wash:hsl(38 92% 62% / .10); --amber-line:hsl(38 92% 62% / .40);\n --paper:hsl(40 33% 94%); --paper-ink:hsl(30 10% 12%); --paper-muted:hsl(30 8% 38%); --paper-note:hsl(38 80% 86%); --paper-line:hsl(30 12% 72%);\n --n-story:hsl(50 96% 72%); --n-person:hsl(338 88% 83%); --n-place:hsl(200 86% 79%); --n-idea:hsl(96 60% 74%); --n-fact:hsl(26 95% 75%); --n-line:hsl(268 78% 85%); --n-wish:hsl(40 33% 94%);\n --hand:'Caveat','Segoe Print','Bradley Hand',cursive;\n --serif:'Lora',Georgia,'Times New Roman',serif;\n --sans:'Inter',system-ui,-apple-system,'Segoe UI',sans-serif;\n --r:8px;\n}\n*{box-sizing:border-box}\nbody{margin:0;background:var(--bg);color:var(--fg);font-family:var(--sans);font-size:15px;line-height:1.55;-webkit-font-smoothing:antialiased}\nbutton{font:inherit;color:inherit;cursor:pointer}\na{color:var(--green)}\n:focus-visible{outline:2px solid var(--green);outline-offset:2px;border-radius:4px}\n.wrap{max-width:1120px;margin-inline:auto;padding-inline:clamp(16px,4vw,32px)}\nh1,h2,h3,h4{font-family:var(--serif);font-weight:600;letter-spacing:-.01em;text-wrap:balance;margin:0}\nh2{font-size:22px} h3{font-size:18px;font-weight:500}\np{margin:0}\n.muted{color:var(--muted)} .fine{font-size:12px;color:var(--muted)} .num{font-variant-numeric:tabular-nums}\n.label{font-size:11px;font-weight:600;letter-spacing:.09em;text-transform:uppercase;color:var(--muted)}\n.label.green{color:var(--green)} .label.amber{color:var(--amber)}\n\n.bar{position:sticky;top:env(safe-area-inset-top,0px);z-index:20;background:var(--bg);border-bottom:1px solid var(--line)}\n.bar .wrap{display:flex;align-items:center;gap:8px 24px;flex-wrap:wrap;padding-block:10px}\n.brand{display:flex;align-items:center;gap:10px;font-family:var(--serif);font-weight:600;font-size:17px;white-space:nowrap;margin-right:auto}\n.bcol{display:grid;justify-items:start;line-height:1.25}\n.verchip{display:inline-flex;align-items:center;gap:6px;font-family:var(--sans);font-size:11px;font-weight:500;line-height:1.3;letter-spacing:.01em;padding:1px 0;border:0;background:transparent;color:var(--muted);white-space:nowrap}\n.verchip:hover{color:var(--fg)}\n.verchip:hover .num{text-decoration:underline;text-underline-offset:2px}\n.verchip i{width:6px;height:6px;border-radius:50%;background:var(--green);flex:none}\n.verchip .vl{color:var(--green)}\n.verchip .vl::before{content:\"·\";color:var(--muted);margin-right:6px}\n.verlist{margin:0;display:grid;grid-template-columns:minmax(0,1fr) auto;gap:8px 18px;border-top:1px solid var(--line);padding-top:14px;font-size:14px}\n.verlist dt{color:var(--muted)}\n.verlist dd{margin:0;text-align:right;font-variant-numeric:tabular-nums}\n.brand svg{width:18px;height:18px;color:var(--green);flex:none}\n.brand .amp{color:var(--green);opacity:.75}\n.tabs{display:flex;gap:2px;overflow-x:auto;max-width:100%;min-width:0}\n.tabs a{color:var(--muted);text-decoration:none;font-size:14px;font-weight:500;padding:7px 11px;border-radius:999px;white-space:nowrap}\n.tabs a:hover{color:var(--fg);background:var(--raise)}\n.tabs a[aria-current=\"page\"]{color:var(--green-ink);background:var(--green);font-weight:600}\n\n.page{padding-block:36px 72px;display:grid;gap:36px}\n.pagehead{display:grid;gap:10px}\n.pagehead h1{font-size:clamp(1.9rem,4.6vw,3rem);line-height:1.08}\n.pagehead .sub{font-family:var(--serif);font-style:italic;color:var(--soft);font-size:clamp(1rem,2vw,1.15rem);max-width:52ch}\n.chips{display:flex;gap:8px;flex-wrap:wrap;align-items:center}\n.chip{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:500;padding:3px 10px;border-radius:999px;border:1px solid var(--line);color:var(--soft);white-space:nowrap}\n.chip.green{border-color:var(--green-line);background:var(--green-wash);color:var(--green)}\n.chip.amber{border-color:var(--amber-line);background:var(--amber-wash);color:var(--amber)}\n.btn{background:var(--green);color:var(--green-ink);border:1px solid var(--green);border-radius:6px;padding:9px 16px;font-weight:600;font-size:14px}\n.btn:hover{filter:brightness(1.08)}\n.btn.ghost{background:transparent;color:var(--soft);border-color:var(--line);font-weight:500}\n.row{display:flex;gap:10px;align-items:center;flex-wrap:wrap}\n\n/* Overview */\n.btn{display:inline-flex;align-items:center;gap:8px}\n.btn svg{width:16px;height:16px;flex:none}\n.btn.soft{background:var(--green-wash);color:var(--green);border-color:var(--green-line)}\n.btn.soft:hover{background:var(--green);color:var(--green-ink);filter:none}\n.journey{display:flex;flex-wrap:wrap;gap:18px 28px}\n.jphase{flex:1 1 0;min-width:190px;display:grid;gap:10px;align-content:start}\n.jhead{display:flex;justify-content:space-between;align-items:baseline;gap:10px}\n.jgrid{display:grid;grid-template-columns:repeat(var(--n),minmax(0,1fr));gap:8px 4px;align-items:start}\n.jgrid i{height:6px;border-radius:999px;background:var(--line)}\n.jgrid i.done{background:var(--green);opacity:.55}\n.jgrid i.current{background:var(--green)}\n.jgrid i.waiting{background:var(--amber)}\n.jgrid span{font-size:12px;line-height:1.3;color:var(--muted);text-align:center;padding-inline:3px;min-width:0;overflow-wrap:break-word}\n.jgrid span.done{color:var(--soft)}\n.jgrid span.current{color:var(--green);font-weight:600}\n.jgrid span.waiting{color:var(--amber)}\n.jgrid small{display:block;font-size:11px;opacity:.85}\n.nextcard{display:grid;gap:14px;border:1px solid var(--green-line);background:var(--green-wash);border-radius:12px;padding:clamp(18px,3vw,26px)}\n.nexttext{font-family:var(--serif);font-size:clamp(1.15rem,2.2vw,1.4rem);line-height:1.35;max-width:36em}\n.sayrow{display:flex;gap:12px 20px;align-items:center;justify-content:space-between;flex-wrap:wrap;border-top:1px solid var(--green-line);padding-top:14px}\n.sayrow > div{min-width:0;display:grid;gap:2px}\n.say{font-family:var(--serif);font-size:17px;color:var(--fg)}\n.waithelp{max-width:62ch;font-size:14px}\n.pmsg{border-left:2px solid var(--green-line);padding-left:12px;color:var(--soft);white-space:pre-wrap;overflow-wrap:anywhere;font-size:14px;line-height:1.55}\n.waithelp b{color:var(--soft);font-weight:600}\n.wlist{list-style:none;margin:0;padding:0;display:grid;gap:12px}\n.witem{display:grid;gap:10px;border:1px solid var(--line);background:var(--card);border-radius:var(--r);padding:16px 18px;min-width:0}\n.witem.need{border-color:var(--amber-line)}\n.wmeta{display:flex;gap:6px 10px;align-items:center;flex-wrap:wrap}\n.witem .ask{font-family:var(--serif);font-size:17px;line-height:1.4}\n.sayings{display:flex;flex-wrap:wrap;gap:8px}\n.sayings .chip{white-space:normal;font-size:13px;padding:5px 12px;background:transparent;text-align:left}\n.sayings .chip:hover{border-color:var(--green-line);color:var(--green)}\n.stats{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:1px;background:var(--line);border:1px solid var(--line);border-radius:var(--r);overflow:hidden}\n.stat{background:var(--card);padding:14px;display:grid;gap:6px;align-content:start;min-width:0}\n.stat .v{font-family:var(--serif);font-size:24px;font-weight:600;line-height:1}\n.stat .v small{font-family:var(--sans);font-size:12px;font-weight:400;color:var(--muted);margin-left:6px}\n.meter{height:6px;border-radius:999px;background:var(--line);overflow:hidden}\n.meter i{display:block;height:100%;border-radius:999px;background:var(--green);min-width:2px}\n.cols{display:grid;grid-template-columns:minmax(0,1.6fr) minmax(0,1fr);gap:40px}\n.stack{display:grid;gap:32px;align-content:start;min-width:0}\n.block{display:grid;gap:14px;min-width:0}\n.notyet{display:grid;grid-template-columns:minmax(0,1.1fr) minmax(0,1fr);gap:28px 44px;align-items:start;border-top:1px solid var(--line);padding-top:30px}\n.nytext{display:grid;gap:12px;min-width:0}\n.nywhere{font-family:var(--serif);font-size:19px;line-height:1.45;max-width:40ch}\n.nytext .muted{max-width:54ch}\n.nysteps{display:grid;grid-template-columns:repeat(10,minmax(0,1fr));gap:5px;max-width:400px;margin-top:10px;padding:3px}\n.nysteps i,.nykey i{height:6px;border-radius:999px;background:var(--line)}\n.nysteps i.done{background:var(--green);opacity:.5}\n.nysteps i.now,.nykey i.now{background:var(--green)}\n.nysteps i.here,.nykey i.here{box-shadow:0 0 0 2px var(--bg),0 0 0 3.5px var(--n-place)}\n.nykey{display:flex;gap:8px 22px;flex-wrap:wrap;padding-left:3px}\n.nykey span{display:inline-flex;align-items:center;gap:9px}\n.nykey i{display:inline-block;width:16px;flex:none}\n.head{display:flex;align-items:baseline;justify-content:space-between;gap:12px;flex-wrap:wrap}\n.wait{display:grid;gap:0;border-top:1px solid var(--line)}\n.wait > div{padding:12px 0;border-bottom:1px solid var(--line);display:grid;gap:2px}\n.wait .ask{font-family:var(--serif);font-size:16px}\ndetails summary{cursor:pointer;font-size:13px;color:var(--muted);list-style:none;padding:10px 0}\ndetails summary::-webkit-details-marker{display:none}\ndetails summary::before{content:\"+ \";color:var(--green)}\ndetails[open] summary::before{content:\"− \"}\n.inchat{border:1px solid var(--green-line);background:var(--green-wash);border-radius:var(--r);padding:12px 14px;font-size:14px;color:var(--soft)}\n.mini{display:grid;grid-template-columns:84px minmax(0,1fr);gap:14px;align-items:center;border:1px solid var(--line);border-radius:var(--r);background:var(--card);padding:12px}\n.mini img{width:84px;border-radius:6px;display:block}\n.mini .name{font-family:var(--serif);font-size:19px;font-weight:600}\n.chrow{display:grid;gap:6px;padding:12px 0;border-bottom:1px solid var(--line)}\n.chrow:first-child{border-top:1px solid var(--line)}\n.chrow .t{font-family:var(--serif);font-size:16px}\n.chrow .t span{color:var(--green);opacity:.7;margin-right:8px}\n.log{display:grid;grid-template-columns:auto minmax(0,1fr);gap:6px 14px;font-size:13px}\n.log .d{color:var(--muted);white-space:nowrap}\n\n/* Outline */\n.thesis{font-family:var(--serif);font-size:clamp(1.15rem,2.4vw,1.5rem);line-height:1.4;max-width:34em}\n.facts{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:18px 40px}\n.facts div{display:grid;gap:4px;align-content:start}\n.facts p{color:var(--soft)}\n.och{border-top:1px solid var(--line);padding-top:24px;display:grid;gap:14px}\n.och .top{display:flex;gap:12px;align-items:baseline;justify-content:space-between;flex-wrap:wrap}\n.och h2 span{color:var(--green);opacity:.7;margin-right:10px}\n.och .brief{color:var(--soft);max-width:68ch}\n.osec{display:grid;grid-template-columns:44px minmax(0,1fr);gap:4px 12px;padding:14px 0;border-top:1px solid var(--line)}\n.osec .n{font-family:var(--serif);color:var(--green);opacity:.75}\n.osec .body{display:grid;gap:6px;min-width:0}\n.osec ul{margin:0;padding-left:18px;color:var(--soft);display:grid;gap:4px;font-size:14px}\n.osec details summary{padding:2px 0}\n\n/* Manuscript */\n.ms{display:grid;grid-template-columns:260px minmax(0,1fr);gap:40px;align-items:start}\n.toc{position:sticky;top:calc(env(safe-area-inset-top,0px) + 76px);display:grid;gap:18px}\n.tocch{display:grid;gap:8px}\n.tocch button{background:none;border:0;padding:0;text-align:left;font-family:var(--serif);font-size:16px;color:var(--soft)}\n.tocch button[aria-pressed=\"true\"]{color:var(--fg);font-weight:600}\n.tocch button span{color:var(--green);opacity:.7;margin-right:8px}\n.tocsec{display:grid;grid-template-columns:10px minmax(0,1fr);gap:8px;align-items:baseline;font-size:13px;color:var(--muted);padding-left:2px}\n.dot{width:8px;height:8px;border-radius:50%;border:1.5px solid var(--muted);display:inline-block}\n.dot.written{background:var(--green);border-color:var(--green)}\n.dot.gaps{background:var(--amber);border-color:var(--amber)}\n.legend{display:flex;gap:16px;flex-wrap:wrap;font-size:12px;color:var(--muted)}\n.legend span{display:inline-flex;gap:6px;align-items:center}\n.sheet{background:var(--paper);color:var(--paper-ink);border-radius:3px;max-width:680px;padding:clamp(28px,7vw,64px) clamp(22px,7vw,64px);font-family:var(--serif);font-size:17px;line-height:1.72;box-shadow:0 30px 80px hsl(0 0% 0% / .6)}\n.sheet .ch{font-family:var(--sans);font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--paper-muted);text-align:center}\n.sheet h3{font-size:28px;font-weight:600;text-align:center;margin:10px 0 6px;color:var(--paper-ink)}\n.sheet h4{font-size:18px;margin:34px 0 10px}\n.sheet p{text-indent:1.4em}\n.sheet h3 + p,.sheet h4 + p,.sheet .note + p,.sheet .device p{text-indent:0}\n.sheet .note{font-family:var(--sans);font-size:13px;line-height:1.5;background:var(--paper-note);border-radius:4px;padding:10px 12px;margin:14px 0}\n.sheet .note b{display:block;font-size:10px;letter-spacing:.1em;text-transform:uppercase;margin-bottom:2px}\n.sheet .unwritten{font-family:var(--sans);font-size:14px;line-height:1.5;border:1.5px dashed var(--paper-line);border-radius:4px;padding:14px 16px;color:var(--paper-muted)}\n.sheet .unwritten b{display:block;font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:var(--paper-ink);margin-bottom:4px}\n.sheet .device{border-top:1px solid var(--paper-line);margin-top:34px;padding-top:6px}\n.sheet .device p{margin-top:8px}\n.sheet blockquote{margin:14px 1.4em;font-style:italic}\n.sheet .li{text-indent:0;padding-left:1.4em}\n.sheet h5{font-family:var(--serif);font-size:17px;font-weight:600;margin:22px 0 6px}\n.sheet h5 + p,.sheet blockquote + p{text-indent:0}\n\n/* Notes */\n.kinds{display:flex;gap:8px;flex-wrap:wrap}\n.kind{display:inline-flex;align-items:center;gap:8px;font-size:13px;font-weight:500;padding:6px 12px;border-radius:999px;border:1px solid var(--line);background:transparent;color:var(--soft)}\n.kind i{width:11px;height:11px;border-radius:2px;background:var(--k);display:block;flex:none}\n.kind:hover{border-color:var(--muted)}\n.kind[aria-pressed=\"true\"]{border-color:var(--fg);color:var(--fg);background:var(--raise)}\n.kind .num{color:var(--muted)}\n.tools{display:flex;gap:10px 16px;flex-wrap:wrap;align-items:center}\n.tools label{display:inline-flex;gap:8px;align-items:center;font-size:13px;color:var(--muted);min-width:0}\n.tools select,.tools input{font:inherit;font-size:14px;color:var(--fg);background:var(--card);border:1px solid var(--line);border-radius:6px;padding:7px 10px;min-width:0;max-width:100%}\n.tools input{width:min(240px,100%)}\n.tools input::placeholder{color:var(--muted)}\n.group{display:grid;gap:16px}\n.group + .group{margin-top:20px}\n.board{display:grid;grid-template-columns:repeat(auto-fill,minmax(204px,1fr));gap:24px 20px;align-items:start}\n.sticky{--k:var(--n-story);--tilt:0deg;background:var(--k) linear-gradient(hsl(0 0% 0% / .07),hsl(0 0% 0% / 0) 22px);color:var(--paper-ink);padding:16px 16px 12px;min-height:196px;display:grid;grid-template-rows:auto auto 1fr auto;gap:7px;border-radius:2px;box-shadow:0 1px 0 hsl(0 0% 100% / .25) inset,0 16px 22px -10px hsl(0 0% 0% / .8);transform:rotate(var(--tilt));transition:transform .18s ease;min-width:0}\n.sticky:hover{transform:rotate(0deg) translateY(-3px)}\n.sticky .k{font-size:10px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;opacity:.62}\n.sticky .t{font-family:var(--hand);font-size:25px;font-weight:700;line-height:1.02;text-wrap:balance;overflow-wrap:anywhere}\n.sticky .x{font-size:13.5px;line-height:1.45;overflow-wrap:anywhere}\n.sticky .f{display:flex;justify-content:space-between;align-items:center;gap:8px;flex-wrap:wrap;font-size:11px;padding-top:6px;opacity:.78}\n.sticky .loose{border:1px dashed currentColor;border-radius:999px;padding:1px 8px}\n@keyframes land{from{opacity:0;transform:translateY(-18px) rotate(var(--tilt)) scale(1.05)}to{opacity:1;transform:rotate(var(--tilt))}}\n.sticky.fresh{animation:land .5s ease-out}\n.sticky .ed{font-size:11px;font-weight:600;font-style:italic;line-height:1.35}\n.sticky .acts{display:flex;gap:5px;flex-wrap:wrap;padding-top:8px;border-top:1px solid hsl(0 0% 0% / .14)}\n.nact{display:inline-flex;align-items:center;gap:4px;font-size:11px;font-weight:600;line-height:1.2;white-space:nowrap;color:var(--paper-ink);background:hsl(0 0% 100% / .38);border:1px solid hsl(0 0% 0% / .2);border-radius:999px;padding:5px 10px;flex:1 1 auto;justify-content:center}\n.nact:hover{background:hsl(0 0% 100% / .78)}\n.nact:focus-visible{outline-color:var(--paper-ink);border-radius:999px}\n@media (prefers-reduced-motion:reduce){.sticky{transition:none}.sticky.fresh{animation:none}}\n.pins{display:grid;gap:8px}\n.pin{display:grid;grid-template-columns:11px minmax(0,1fr);gap:10px;align-items:baseline;font-family:var(--serif);font-size:15px;color:var(--soft)}\n.pin i{width:11px;height:11px;border-radius:2px;background:var(--k);display:block}\n\n/* Notes: ways to look */\n.views{display:inline-flex;gap:2px;padding:3px;border:1px solid var(--line);border-radius:999px;background:var(--card);justify-self:start;max-width:100%}\n.view{border:0;background:transparent;color:var(--muted);font-size:14px;font-weight:500;padding:7px 16px;border-radius:999px;white-space:nowrap}\n.view:hover{color:var(--fg)}\n.view[aria-pressed=\"true\"]{background:var(--fg);color:var(--bg);font-weight:600}\n.gwrap{display:grid;grid-template-columns:minmax(0,1fr) 300px;gap:24px;align-items:start}\n.graph{position:relative;border:1px solid var(--line);border-radius:var(--r);background:radial-gradient(120% 90% at 50% 40%,var(--raise),var(--card) 70%);overflow:hidden;min-width:0}\n.graph canvas{display:block;width:100%;height:min(68vh,600px);min-height:380px;touch-action:pan-y;cursor:grab}\n.graph canvas:active{cursor:grabbing}\n.ghint{position:absolute;left:14px;right:14px;bottom:10px;pointer-events:none}\n.gside{display:grid;gap:14px;align-content:start;min-width:0}\n.gside .sticky{min-height:0}\n.glist{display:grid;gap:6px}\n.glink{display:grid;grid-template-columns:11px minmax(0,1fr);gap:10px;align-items:baseline;text-align:left;background:transparent;border:1px solid var(--line);border-radius:6px;padding:8px 10px;font-size:14px;color:var(--soft)}\n.glink:hover{border-color:var(--muted);color:var(--fg)}\n.glink i{width:11px;height:11px;border-radius:2px;background:var(--k);display:block}\n.tl{border-left:2px solid var(--line);margin-left:6px;padding-left:26px;display:grid;gap:30px}\n.tly{display:grid;gap:16px;position:relative}\n.tly::before{content:\"\";position:absolute;left:-34px;top:9px;width:14px;height:14px;border-radius:50%;background:var(--green);box-shadow:0 0 0 4px var(--bg)}\n.tlyear{font-size:28px;line-height:1}\n.tli{display:grid;gap:8px;align-content:start;min-width:0}\n.tlwhen{font-size:12px;color:var(--muted);min-height:18px}\n.tlwrap{display:grid;gap:28px}\n.tlwrap .group + .group,.tlwrap .group{margin-top:0}\n.tlgap{font-size:12px;color:var(--muted);font-style:italic}\n\n/* Ghostwriter */\n.spot{display:grid;grid-template-columns:220px minmax(0,1fr);gap:28px;align-items:center;border:1px solid var(--green-line);background:var(--green-wash);border-radius:var(--r);padding:22px}\n.spot img{width:100%;border-radius:6px;display:block}\n.spot .info{display:grid;gap:10px;min-width:0}\n.spot .name{font-family:var(--serif);font-size:30px;font-weight:600;line-height:1.1}\n.quote{font-family:var(--serif);font-style:italic;color:var(--soft);font-size:17px}\n.grid{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:12px}\n.card{background:var(--card);border:1px solid var(--line);border-radius:var(--r);padding:0;text-align:left;overflow:hidden;display:grid;align-content:start}\n.card:hover{border-color:var(--green-line)}\n.card[aria-pressed=\"true\"]{border-color:var(--green)}\n.card img{width:100%;aspect-ratio:4/5;object-fit:cover;display:block}\n.card span{display:block;padding:10px 12px 0;font-family:var(--serif);font-weight:600;font-size:15px}\n.card small{display:block;padding:2px 12px 12px;color:var(--muted);font-size:12px;line-height:1.4}\n@keyframes drift{0%{transform:perspective(1200px) translate3d(0,0,0) rotate(0)}18%{transform:perspective(1200px) translate3d(-4px,-5px,3px) rotate(.6deg)}35%{transform:perspective(1200px) translate3d(3px,-8px,-4px) rotate(-.4deg)}52%{transform:perspective(1200px) translate3d(-6px,-3px,5px) rotate(.8deg)}68%{transform:perspective(1200px) translate3d(5px,-6px,-3px) rotate(-.7deg)}84%{transform:perspective(1200px) translate3d(-3px,-4px,4px) rotate(.3deg)}100%{transform:perspective(1200px) translate3d(0,0,0) rotate(0)}}\n.drift{animation:drift 14s ease-in-out infinite;will-change:transform}\n@media (prefers-reduced-motion:reduce){.drift{animation:none}}\n\n.modal{position:fixed;inset:0;z-index:40;background:hsl(0 0% 0% / .68);display:grid;place-items:center;padding:16px}\n.modal[hidden]{display:none}\n.popup{width:100%;max-width:560px;max-height:100%;overflow:auto;background:var(--raise);border:1px solid var(--green-line);border-radius:12px;padding:clamp(18px,4vw,26px);display:grid;gap:12px;box-shadow:0 30px 80px hsl(0 0% 0% / .7)}\n.popup h2{font-size:21px;line-height:1.25}\n.popup.wide{max-width:820px}\n.modal:not(.manual) #copytext,.modal:not(.manual) #copybtn{display:none}\n.popup pre{margin:0;white-space:pre-wrap;overflow-wrap:anywhere;font-family:var(--sans);font-size:14px;line-height:1.5;background:var(--bg);border:1px solid var(--line);border-radius:6px;padding:12px 14px;max-height:40vh;overflow:auto}\n.modal:not(.manual) #copystate::before{content:\"\";display:inline-block;width:10px;height:6px;border-left:2px solid var(--green);border-bottom:2px solid var(--green);transform:rotate(-45deg) translateY(-3px);margin-right:8px}\n.modal.manual #copystate{color:var(--amber)}\n.fld{display:grid;gap:6px;font-size:12px;font-weight:600;letter-spacing:.04em;color:var(--muted)}\n.fld input,.fld textarea{font:inherit;font-size:15px;font-weight:400;letter-spacing:0;line-height:1.5;color:var(--fg);background:var(--bg);border:1px solid var(--line);border-radius:6px;padding:10px 12px;width:100%;resize:vertical}\n#noteerr{color:var(--amber)}\n\n/* Items: anything on a page the author can edit */\n.item{display:grid;gap:8px;min-width:0;align-content:start}\n.item .v{color:var(--soft);white-space:pre-wrap;overflow-wrap:anywhere}\n.item .v.big{font-family:var(--serif);font-size:clamp(1.1rem,2.2vw,1.4rem);line-height:1.45;color:var(--fg);max-width:36em}\n.item ul{margin:0;padding-left:18px;color:var(--soft);display:grid;gap:4px}\n.item blockquote{margin:0;padding-left:14px;border-left:2px solid var(--green-line);font-family:var(--serif);font-style:italic;color:var(--soft)}\n.acts2{display:flex;gap:6px;flex-wrap:wrap;align-items:center}\n.ibtn{font-size:12px;font-weight:600;line-height:1.2;white-space:nowrap;color:var(--soft);background:transparent;border:1px solid var(--line);border-radius:999px;padding:6px 12px}\n.ibtn:hover{border-color:var(--green-line);color:var(--green)}\n.ibtn.go{border-color:var(--green-line);color:var(--green);background:var(--green-wash)}\n.edtag{font-size:11px;font-weight:600;color:var(--green);white-space:nowrap}\n.edtag.local{color:var(--amber)}\n.hero2{display:grid;gap:22px;border:1px solid var(--line);background:var(--card);border-radius:12px;padding:clamp(18px,3vw,28px)}\n.hero2 .ttl{font-family:var(--serif);font-weight:600;font-size:clamp(1.7rem,4vw,2.6rem);line-height:1.1;overflow-wrap:anywhere}\n.hero2 .sub2{font-family:var(--serif);font-style:italic;color:var(--soft);font-size:clamp(1.05rem,2vw,1.25rem)}\n.hero2 .by{font-size:16px;color:var(--soft)}\n.facts2{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:30px 40px}\n.sugs{display:grid;gap:8px;border:1px dashed var(--green-line);border-radius:var(--r);padding:12px 14px}\n.sug{display:flex;gap:10px;align-items:baseline;justify-content:space-between;flex-wrap:wrap}\n.sug span{font-family:var(--serif);font-size:16px;min-width:0;overflow-wrap:anywhere}\n.storygrid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:18px}\n.story{display:grid;gap:10px;align-content:start;border:1px solid var(--line);border-top:3px solid var(--n-story);background:var(--card);border-radius:12px;padding:18px 20px;min-width:0}\n.story h3{font-size:20px;font-weight:600}\n.story .nar{color:var(--soft);white-space:pre-wrap;overflow-wrap:anywhere}\n.story .end{border-left:2px solid var(--green-line);padding-left:12px;color:var(--soft);display:grid;gap:2px}\n.story .end.need{border-left-color:var(--amber-line)}\n.story details p{font-size:13px;color:var(--soft);margin-bottom:8px}\n.witem.first{border-color:var(--green-line);background:var(--green-wash)}\n.witem .ans{border-left:2px solid var(--green-line);padding-left:12px;color:var(--soft);white-space:pre-wrap;overflow-wrap:anywhere;font-size:14px}\n.tip{display:none;font-size:13px;color:var(--fg);background:hsl(200 86% 79% / .10);border:1px solid hsl(200 86% 79% / .45);border-radius:var(--r);padding:9px 12px;max-width:64ch}\n.tip b{color:var(--n-place);font-weight:600}\nbody.helpon .tip{display:block}\n.helpbtn{flex:none;width:34px;height:34px;border-radius:50%;border:1px solid var(--line);background:transparent;color:var(--soft);font-weight:600;font-size:15px;display:inline-grid;place-items:center}\n.helpbtn:hover{border-color:var(--green-line);color:var(--green)}\n.helpbtn[aria-pressed=\"true\"]{background:var(--n-place);border-color:var(--n-place);color:var(--green-ink)}\n.toast{position:fixed;left:50%;bottom:calc(env(safe-area-inset-bottom,0px) + 20px);transform:translateX(-50%);z-index:50;background:var(--green);color:var(--green-ink);font-weight:600;font-size:14px;border-radius:999px;padding:9px 18px;box-shadow:0 10px 30px hsl(0 0% 0% / .6);max-width:calc(100% - 32px)}\n.talk{display:grid;gap:8px;max-height:36vh;overflow:auto}\n.talk:empty{display:none}\n.talk div{border-radius:10px;padding:9px 12px;max-width:90%;overflow-wrap:anywhere;white-space:pre-wrap;font-size:14px}\n.talk .me{justify-self:end;background:var(--bg);border:1px solid var(--line)}\n.talk .gw{justify-self:start;background:var(--green-wash);border:1px solid var(--green-line)}\n.talk .gw b{display:block;font-size:11px;letter-spacing:.06em;text-transform:uppercase;color:var(--green);margin-bottom:3px}\n.about{border-left:3px solid var(--green);background:var(--bg);border-radius:0 6px 6px 0;padding:8px 12px;font-size:14px;color:var(--soft);max-height:20vh;overflow:auto;white-space:pre-wrap;overflow-wrap:anywhere}\n#editfields{display:grid;gap:12px}\n#editerr,#talkstate.warn{color:var(--amber)}\n.och .acts2,.osec .acts2{margin-top:2px}\n.sheet .secdisc{margin:2px 0 14px;display:flex;gap:8px;align-items:center;flex-wrap:wrap}\n.sheet .secdisc .edtag{color:var(--paper-ink);border-color:var(--paper-line)}\n.sheet h4 .snum{font-family:var(--sans);font-size:12px;font-weight:600;color:var(--paper-muted);margin-right:10px;letter-spacing:.04em}\n.sheet .secdisc .ibtn{color:var(--paper-muted);border-color:var(--paper-line)}\n.sheet .secdisc .ibtn:hover{color:var(--paper-ink);border-color:var(--paper-ink)}\n.doclink{display:flex;gap:12px 18px;align-items:center;justify-content:space-between;flex-wrap:wrap;border:1px solid var(--green-line);background:var(--green-wash);border-radius:var(--r);padding:14px 16px}\n\n@media (max-width:860px){\n .facts2,.storygrid{grid-template-columns:minmax(0,1fr)}\n .gwrap{grid-template-columns:minmax(0,1fr)}\n .graph canvas{height:420px}\n .cols,.ms{grid-template-columns:minmax(0,1fr)}\n .jphase{min-width:100%}\n .facts{grid-template-columns:minmax(0,1fr)}\n .toc{position:static}\n .spot{grid-template-columns:minmax(0,1fr)}\n .spot img{max-width:180px}\n .grid{grid-template-columns:repeat(2,minmax(0,1fr))}\n .tabs a{padding:7px 10px;font-size:13px}\n .helpbtn{order:2}\n .tabs{order:3;flex:1 1 100%}\n .notyet{grid-template-columns:minmax(0,1fr)}\n}\n@media (max-width:520px){\n .jgrid span{font-size:11px;padding-inline:1px}\n .sayrow .btn,.witem .btn{width:100%;justify-content:center}\n .board{grid-template-columns:repeat(2,minmax(0,1fr));gap:18px 12px}\n .sticky{min-height:156px;padding:12px 12px 10px}\n .nact{padding:8px 10px}\n .sticky .t{font-size:22px}\n .sticky .x{font-size:13px}\n .tools label{flex:1 1 100%}\n .tools select,.tools input{flex:1;width:auto}\n .view{padding:7px 12px}\n .tl{padding-left:20px}\n .tly::before{left:-28px}\n}\n/* ---- The cover ---- */\n.cv{position:relative;aspect-ratio:2/3;width:100%;container-type:inline-size;overflow:hidden;border-radius:3px;background:var(--paper,#F3EBDD);color:var(--ink,#1F3B2D);text-align:left;line-height:1.2;box-shadow:0 30px 70px hsl(0 0% 0% / .75),inset 0 0 0 1px hsl(0 0% 100% / .04)}\n.cv::after{content:\"\";position:absolute;inset:0 auto 0 0;width:2.2cqw;background:linear-gradient(90deg,hsl(0 0% 0% / .22),transparent);pointer-events:none}\n.cv .cvl{display:block;width:max-content;white-space:nowrap}\n.cvtitle{font-family:var(--tf);font-weight:var(--tw,600);line-height:var(--tlh,1);letter-spacing:var(--tls,0);font-size:12cqw}\n.cv.caps .cvtitle{text-transform:uppercase}\n.cvsub{text-wrap:balance;overflow-wrap:anywhere}\n.cvauthor{font-size:3.5cqw;white-space:nowrap}\n.cvmotif svg{display:block;width:100%;height:100%;overflow:visible}\n.cv-paper .cvtop{position:absolute;left:9cqw;right:9cqw;top:10cqw}\n.cv-paper .cvsub{font-family:var(--xf);font-weight:500;line-height:1.3;margin-top:5cqw;max-width:78cqw;color:color-mix(in srgb,var(--ink) 80%,var(--paper))}\n.cv-paper .cvmotif{position:absolute;left:9cqw;width:82cqw;bottom:24cqw;height:24cqw}\n.cv-paper .cvauthor{position:absolute;left:9cqw;right:9cqw;bottom:9cqw;font-family:var(--xf);font-weight:600;letter-spacing:.22em;text-transform:uppercase}\n.cv-bold .cvmotif{position:absolute;left:0;top:0;width:100cqw;height:66cqw}\n.cv-bold .cvbase{position:absolute;left:8cqw;right:8cqw;bottom:8cqw;display:flex;flex-direction:column}\n.cv-bold .cvrule{width:18cqw;height:1cqw;background:var(--accent);margin:5cqw 0 6cqw;flex:none}\n.cv-bold .cvsub{font-family:var(--xf);font-weight:500;line-height:1.32;max-width:78cqw}\n.cv-bold .cvauthor{margin-top:7cqw;font-family:var(--xf);font-weight:600;letter-spacing:.2em;text-transform:uppercase;color:var(--accent)}\n.cv-quiet{text-align:center;background:radial-gradient(120% 80% at 60% 62%,color-mix(in srgb,var(--paper) 90%,var(--ink)) 0,var(--paper) 70%)}\n.cv-quiet .cvl,.cv-over .cvl{margin-inline:auto}\n.cv-quiet .cvtop{position:absolute;left:7cqw;right:7cqw;top:11cqw}\n.cv-quiet .cvsub{font-family:var(--xf);font-weight:500;letter-spacing:.2em;text-transform:uppercase;line-height:1.8;margin-top:4cqw;color:color-mix(in srgb,var(--ink) 72%,var(--paper))}\n.cv-quiet .cvmotif{position:absolute;left:0;top:50cqw;width:100cqw;height:86cqw}\n.cv-quiet .cvauthor{position:absolute;left:7cqw;right:7cqw;bottom:8cqw;font-family:var(--tf);font-weight:600;letter-spacing:.14em;text-transform:uppercase;font-size:5cqw}\n.cv-own{aspect-ratio:auto;background:hsl(0 0% 7%)}\n.cv-own img{display:block;width:100%;height:auto}\n.cv-over{aspect-ratio:2/3;text-align:center}\n.cv-over img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}\n.cv-over .cvscrim{position:absolute;inset:0;background:linear-gradient(180deg,var(--shade) 0,transparent 46%,transparent 74%,var(--shade) 100%)}\n.cv-over .cvtop{position:absolute;left:8cqw;right:8cqw;top:10cqw}\n.cv-over .cvsub{font-family:var(--xf);font-weight:500;line-height:1.4;margin-top:4cqw}\n.cv-over .cvauthor{position:absolute;left:8cqw;right:8cqw;bottom:8cqw;font-family:var(--xf);font-weight:600;letter-spacing:.2em;text-transform:uppercase}\n.cv-wait{display:grid;place-items:center;background:var(--card);color:var(--muted);border:1px dashed var(--line);box-shadow:none;text-align:center;font-size:13px;padding:12px}\n.cv-wait::after{display:none}\n.concepts{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:clamp(20px,3vw,36px);align-items:start}\n.cc{display:grid;gap:18px;min-width:0}\n.cwrap{padding:26px 30px;border:1px solid var(--line);border-radius:var(--r);background:radial-gradient(90% 70% at 50% 40%,hsl(0 0% 9%),var(--card))}\n.cc.picked .cwrap,.cwrap.picked{border-color:var(--green);box-shadow:0 0 0 1px var(--green)}\n.cwrap.own{max-width:300px}\n.cmeta{display:grid;gap:10px}\n.cmeta h3,.cown h3{font-size:21px;font-weight:600}\n.clook{display:flex;gap:12px;align-items:center;flex-wrap:wrap}\n.csws{display:inline-flex;gap:5px}\n.csws i{width:18px;height:18px;border-radius:50%;box-shadow:inset 0 0 0 1px hsl(0 0% 100% / .14)}\n.csect{display:grid;gap:22px;border-top:1px solid var(--line);padding-top:32px}\n.cintro{display:grid;gap:8px;max-width:68ch}\n.cintro p{color:var(--soft)}\n.cother{display:grid;grid-template-columns:minmax(0,1.5fr) minmax(0,1fr);gap:clamp(24px,4vw,48px);align-items:start}\n.cown{display:grid;gap:16px;align-content:start;min-width:0}\n.cslot{aspect-ratio:2/3;width:min(100%,250px);border:1.5px dashed var(--green-line);border-radius:6px;background:var(--green-wash);display:grid;place-content:center;justify-items:center;gap:10px;text-align:center;padding:18px;color:var(--soft)}\n.cslot svg{width:30px;height:30px;color:var(--green)}\n.cslot b{font-family:var(--serif);font-size:17px;font-weight:600;color:var(--fg)}\n.csteps{list-style:none;margin:0;padding:0;display:grid;gap:10px;counter-reset:cs}\n.csteps li{counter-increment:cs;display:grid;grid-template-columns:26px minmax(0,1fr);gap:10px;color:var(--soft)}\n.csteps li::before{content:counter(cs);width:22px;height:22px;border-radius:50%;border:1px solid var(--green-line);color:var(--green);font-size:12px;font-weight:600;display:grid;place-items:center;margin-top:1px}\n.cback{gap:14px}\n.cback .chl{font-family:var(--serif);font-size:26px;line-height:1.25;font-weight:600;color:var(--fg);max-width:30ch}\n.cback p{color:var(--soft);max-width:62ch}\n.cback .cbio{border-left:2px solid var(--green-line);padding-left:14px;color:var(--muted);font-size:14px}\n.ovhero{display:grid;grid-template-columns:auto minmax(0,1fr);gap:clamp(22px,4vw,44px);align-items:center}\n.ovcover{display:grid;gap:10px;justify-items:center;width:clamp(150px,17vw,208px);text-decoration:none;color:inherit}\n.ovcover .cv{box-shadow:0 22px 50px hsl(0 0% 0% / .7),0 0 0 1px hsl(0 0% 100% / .06);transform:rotate(-1.2deg)}\n.ovcover u{color:var(--green);text-underline-offset:3px}\n.notyet>.cown{border-left:1px solid var(--line);padding-left:clamp(20px,3vw,44px)}\n@media (max-width:900px){.concepts,.cother{grid-template-columns:minmax(0,1fr)}.cwrap{padding:22px clamp(30px,14vw,90px)}.cwrap.own{padding:22px 30px}.notyet>.cown{border-left:0;padding-left:0}}\n@media (max-width:640px){.ovhero{grid-template-columns:minmax(0,1fr);justify-items:start}.ovcover{width:150px}}\n"; document.head.appendChild(st);
|
|
7
|
+
const st = document.createElement('style'); st.textContent = "\n/* Layout: eight separate pages behind one top bar (Overview, Book Setup, Voice, Stories, Notes, Outline, Manuscript, Ghostwriter). Every item can be edited in place. The book's own prose appears as paper on the dark desk, and what the author has shared appears as sticky notes pinned to it. */\n:root{\n color-scheme:dark;\n --bg:hsl(0 0% 2%); --card:hsl(0 0% 4%); --raise:hsl(0 0% 8%); --line:hsl(0 0% 15%);\n --fg:hsl(0 0% 98%); --soft:hsl(0 0% 84%); --muted:hsl(0 0% 64%);\n --green:hsl(152 69% 50%); --green-ink:hsl(0 0% 5%); --green-wash:hsl(152 69% 50% / .10); --green-line:hsl(152 69% 50% / .38);\n --amber:hsl(38 92% 62%); --amber-wash:hsl(38 92% 62% / .10); --amber-line:hsl(38 92% 62% / .40);\n --paper:hsl(40 33% 94%); --paper-ink:hsl(30 10% 12%); --paper-muted:hsl(30 8% 38%); --paper-note:hsl(38 80% 86%); --paper-line:hsl(30 12% 72%);\n --n-story:hsl(50 96% 72%); --n-person:hsl(338 88% 83%); --n-place:hsl(200 86% 79%); --n-idea:hsl(96 60% 74%); --n-fact:hsl(26 95% 75%); --n-line:hsl(268 78% 85%); --n-wish:hsl(40 33% 94%);\n --hand:'Caveat','Segoe Print','Bradley Hand',cursive;\n --serif:'Lora',Georgia,'Times New Roman',serif;\n --sans:'Inter',system-ui,-apple-system,'Segoe UI',sans-serif;\n --r:8px;\n}\n*{box-sizing:border-box}\nbody{margin:0;background:var(--bg);color:var(--fg);font-family:var(--sans);font-size:15px;line-height:1.55;-webkit-font-smoothing:antialiased}\nbutton{font:inherit;color:inherit;cursor:pointer}\na{color:var(--green)}\n:focus-visible{outline:2px solid var(--green);outline-offset:2px;border-radius:4px}\n.wrap{max-width:1120px;margin-inline:auto;padding-inline:clamp(16px,4vw,32px)}\nh1,h2,h3,h4{font-family:var(--serif);font-weight:600;letter-spacing:-.01em;text-wrap:balance;margin:0}\nh2{font-size:22px} h3{font-size:18px;font-weight:500}\np{margin:0}\n.muted{color:var(--muted)} .fine{font-size:12px;color:var(--muted)} .num{font-variant-numeric:tabular-nums}\n.label{font-size:11px;font-weight:600;letter-spacing:.09em;text-transform:uppercase;color:var(--muted)}\n.label.green{color:var(--green)} .label.amber{color:var(--amber)}\n\n.bar{position:sticky;top:env(safe-area-inset-top,0px);z-index:20;background:var(--bg);border-bottom:1px solid var(--line)}\n.bar .wrap{display:flex;align-items:center;gap:8px 24px;flex-wrap:wrap;padding-block:10px}\n.brand{display:flex;align-items:center;gap:10px;font-family:var(--serif);font-weight:600;font-size:17px;white-space:nowrap;margin-right:auto}\n.bcol{display:grid;justify-items:start;line-height:1.25}\n.verchip{display:inline-flex;align-items:center;gap:6px;font-family:var(--sans);font-size:11px;font-weight:500;line-height:1.3;letter-spacing:.01em;padding:1px 0;border:0;background:transparent;color:var(--muted);white-space:nowrap}\n.verchip:hover{color:var(--fg)}\n.verchip:hover .num{text-decoration:underline;text-underline-offset:2px}\n.verchip i{width:6px;height:6px;border-radius:50%;background:var(--green);flex:none}\n.verchip .vl{color:var(--green)}\n.verchip .vl::before{content:\"·\";color:var(--muted);margin-right:6px}\n.verlist{margin:0;display:grid;grid-template-columns:minmax(0,1fr) auto;gap:8px 18px;border-top:1px solid var(--line);padding-top:14px;font-size:14px}\n.verlist dt{color:var(--muted)}\n.verlist dd{margin:0;text-align:right;font-variant-numeric:tabular-nums}\n.brand svg{width:18px;height:18px;color:var(--green);flex:none}\n.brand .amp{color:var(--green);opacity:.75}\n.tabs{display:flex;gap:2px;overflow-x:auto;max-width:100%;min-width:0}\n.tabs a{color:var(--muted);text-decoration:none;font-size:14px;font-weight:500;padding:7px 11px;border-radius:999px;white-space:nowrap}\n.tabs a:hover{color:var(--fg);background:var(--raise)}\n.tabs a[aria-current=\"page\"]{color:var(--green-ink);background:var(--green);font-weight:600}\n\n.page{padding-block:36px 72px;display:grid;gap:36px}\n.pagehead{display:grid;gap:10px}\n.pagehead h1{font-size:clamp(1.9rem,4.6vw,3rem);line-height:1.08}\n.pagehead .sub{font-family:var(--serif);font-style:italic;color:var(--soft);font-size:clamp(1rem,2vw,1.15rem);max-width:52ch}\n.chips{display:flex;gap:8px;flex-wrap:wrap;align-items:center}\n.chip{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:500;padding:3px 10px;border-radius:999px;border:1px solid var(--line);color:var(--soft);white-space:nowrap}\n.chip.green{border-color:var(--green-line);background:var(--green-wash);color:var(--green)}\n.chip.amber{border-color:var(--amber-line);background:var(--amber-wash);color:var(--amber)}\n.btn{background:var(--green);color:var(--green-ink);border:1px solid var(--green);border-radius:6px;padding:9px 16px;font-weight:600;font-size:14px}\n.btn:hover{filter:brightness(1.08)}\n.btn.ghost{background:transparent;color:var(--soft);border-color:var(--line);font-weight:500}\n.row{display:flex;gap:10px;align-items:center;flex-wrap:wrap}\n\n/* Overview */\n.btn{display:inline-flex;align-items:center;gap:8px}\n.btn svg{width:16px;height:16px;flex:none}\n.btn.soft{background:var(--green-wash);color:var(--green);border-color:var(--green-line)}\n.btn.soft:hover{background:var(--green);color:var(--green-ink);filter:none}\n.journey{display:flex;flex-wrap:wrap;gap:18px 28px}\n.jphase{flex:1 1 0;min-width:190px;display:grid;gap:10px;align-content:start}\n.jhead{display:flex;justify-content:space-between;align-items:baseline;gap:10px}\n.jgrid{display:grid;grid-template-columns:repeat(var(--n),minmax(0,1fr));gap:8px 4px;align-items:start}\n.jgrid i{height:6px;border-radius:999px;background:var(--line)}\n.jgrid i.done{background:var(--green);opacity:.55}\n.jgrid i.current{background:var(--green)}\n.jgrid i.waiting{background:var(--amber)}\n.jgrid span{font-size:12px;line-height:1.3;color:var(--muted);text-align:center;padding-inline:3px;min-width:0;overflow-wrap:break-word}\n.jgrid span.done{color:var(--soft)}\n.jgrid span.current{color:var(--green);font-weight:600}\n.jgrid span.waiting{color:var(--amber)}\n.jgrid small{display:block;font-size:11px;opacity:.85}\n.nextcard{display:grid;gap:14px;border:1px solid var(--green-line);background:var(--green-wash);border-radius:12px;padding:clamp(18px,3vw,26px)}\n.nexttext{font-family:var(--serif);font-size:clamp(1.15rem,2.2vw,1.4rem);line-height:1.35;max-width:36em}\n.sayrow{display:flex;gap:12px 20px;align-items:center;justify-content:space-between;flex-wrap:wrap;border-top:1px solid var(--green-line);padding-top:14px}\n.sayrow > div{min-width:0;display:grid;gap:2px}\n.say{font-family:var(--serif);font-size:17px;color:var(--fg)}\n.waithelp{max-width:62ch;font-size:14px}\n.pmsg{border-left:2px solid var(--green-line);padding-left:12px;color:var(--soft);white-space:pre-wrap;overflow-wrap:anywhere;font-size:14px;line-height:1.55}\n.waithelp b{color:var(--soft);font-weight:600}\n.wlist{list-style:none;margin:0;padding:0;display:grid;gap:12px}\n.witem{display:grid;gap:10px;border:1px solid var(--line);background:var(--card);border-radius:var(--r);padding:16px 18px;min-width:0}\n.witem.need{border-color:var(--amber-line)}\n.wmeta{display:flex;gap:6px 10px;align-items:center;flex-wrap:wrap}\n.witem .ask{font-family:var(--serif);font-size:17px;line-height:1.4}\n.sayings{display:flex;flex-wrap:wrap;gap:8px}\n.sayings .chip{white-space:normal;font-size:13px;padding:5px 12px;background:transparent;text-align:left}\n.sayings .chip:hover{border-color:var(--green-line);color:var(--green)}\n.stats{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:1px;background:var(--line);border:1px solid var(--line);border-radius:var(--r);overflow:hidden}\n.stat{background:var(--card);padding:14px;display:grid;gap:6px;align-content:start;min-width:0}\n.stat .v{font-family:var(--serif);font-size:24px;font-weight:600;line-height:1}\n.stat .v small{font-family:var(--sans);font-size:12px;font-weight:400;color:var(--muted);margin-left:6px}\n.meter{height:6px;border-radius:999px;background:var(--line);overflow:hidden}\n.meter i{display:block;height:100%;border-radius:999px;background:var(--green);min-width:2px}\n.cols{display:grid;grid-template-columns:minmax(0,1.6fr) minmax(0,1fr);gap:40px}\n.stack{display:grid;gap:32px;align-content:start;min-width:0}\n.block{display:grid;gap:14px;min-width:0}\n.notyet{display:grid;grid-template-columns:minmax(0,1.1fr) minmax(0,1fr);gap:28px 44px;align-items:start;border-top:1px solid var(--line);padding-top:30px}\n.nytext{display:grid;gap:12px;min-width:0}\n.nywhere{font-family:var(--serif);font-size:19px;line-height:1.45;max-width:40ch}\n.nytext .muted{max-width:54ch}\n.nysteps{display:grid;grid-template-columns:repeat(10,minmax(0,1fr));gap:5px;max-width:400px;margin-top:10px;padding:3px}\n.nysteps i,.nykey i{height:6px;border-radius:999px;background:var(--line)}\n.nysteps i.done{background:var(--green);opacity:.5}\n.nysteps i.now,.nykey i.now{background:var(--green)}\n.nysteps i.here,.nykey i.here{box-shadow:0 0 0 2px var(--bg),0 0 0 3.5px var(--n-place)}\n.nykey{display:flex;gap:8px 22px;flex-wrap:wrap;padding-left:3px}\n.nykey span{display:inline-flex;align-items:center;gap:9px}\n.nykey i{display:inline-block;width:16px;flex:none}\n.head{display:flex;align-items:baseline;justify-content:space-between;gap:12px;flex-wrap:wrap}\n.wait{display:grid;gap:0;border-top:1px solid var(--line)}\n.wait > div{padding:12px 0;border-bottom:1px solid var(--line);display:grid;gap:2px}\n.wait .ask{font-family:var(--serif);font-size:16px}\ndetails summary{cursor:pointer;font-size:13px;color:var(--muted);list-style:none;padding:10px 0}\ndetails summary::-webkit-details-marker{display:none}\ndetails summary::before{content:\"+ \";color:var(--green)}\ndetails[open] summary::before{content:\"− \"}\n.inchat{border:1px solid var(--green-line);background:var(--green-wash);border-radius:var(--r);padding:12px 14px;font-size:14px;color:var(--soft)}\n.mini{display:grid;grid-template-columns:84px minmax(0,1fr);gap:14px;align-items:center;border:1px solid var(--line);border-radius:var(--r);background:var(--card);padding:12px}\n.mini img{width:84px;border-radius:6px;display:block}\n.mini .name{font-family:var(--serif);font-size:19px;font-weight:600}\n.chrow{display:grid;gap:6px;padding:12px 0;border-bottom:1px solid var(--line)}\n.chrow:first-child{border-top:1px solid var(--line)}\n.chrow .t{font-family:var(--serif);font-size:16px}\n.chrow .t span{color:var(--green);opacity:.7;margin-right:8px}\n.log{display:grid;grid-template-columns:auto minmax(0,1fr);gap:6px 14px;font-size:13px}\n.log .d{color:var(--muted);white-space:nowrap}\n\n/* Outline */\n.thesis{font-family:var(--serif);font-size:clamp(1.15rem,2.4vw,1.5rem);line-height:1.4;max-width:34em}\n.facts{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:18px 40px}\n.facts div{display:grid;gap:4px;align-content:start}\n.facts p{color:var(--soft)}\n.och{border-top:1px solid var(--line);padding-top:24px;display:grid;gap:14px}\n.och .top{display:flex;gap:12px;align-items:baseline;justify-content:space-between;flex-wrap:wrap}\n.och h2 span{color:var(--green);opacity:.7;margin-right:10px}\n.och .brief{color:var(--soft);max-width:68ch}\n.osec{display:grid;grid-template-columns:44px minmax(0,1fr);gap:4px 12px;padding:14px 0;border-top:1px solid var(--line)}\n.osec .n{font-family:var(--serif);color:var(--green);opacity:.75}\n.osec .body{display:grid;gap:6px;min-width:0}\n.osec ul{margin:0;padding-left:18px;color:var(--soft);display:grid;gap:4px;font-size:14px}\n.osec details summary{padding:2px 0}\n\n/* Manuscript */\n.ms{display:grid;grid-template-columns:260px minmax(0,1fr);gap:40px;align-items:start}\n.toc{position:sticky;top:calc(env(safe-area-inset-top,0px) + 76px);display:grid;gap:18px}\n.tocch{display:grid;gap:8px}\n.tocch button{background:none;border:0;padding:0;text-align:left;font-family:var(--serif);font-size:16px;color:var(--soft)}\n.tocch button[aria-pressed=\"true\"]{color:var(--fg);font-weight:600}\n.tocch button span{color:var(--green);opacity:.7;margin-right:8px}\n.tocsec{display:grid;grid-template-columns:10px minmax(0,1fr);gap:8px;align-items:baseline;font-size:13px;color:var(--muted);padding-left:2px}\n.dot{width:8px;height:8px;border-radius:50%;border:1.5px solid var(--muted);display:inline-block}\n.dot.written{background:var(--green);border-color:var(--green)}\n.dot.gaps{background:var(--amber);border-color:var(--amber)}\n.legend{display:flex;gap:16px;flex-wrap:wrap;font-size:12px;color:var(--muted)}\n.legend span{display:inline-flex;gap:6px;align-items:center}\n.sheet{background:var(--paper);color:var(--paper-ink);border-radius:3px;max-width:680px;padding:clamp(28px,7vw,64px) clamp(22px,7vw,64px);font-family:var(--serif);font-size:17px;line-height:1.72;box-shadow:0 30px 80px hsl(0 0% 0% / .6)}\n.sheet .ch{font-family:var(--sans);font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--paper-muted);text-align:center}\n.sheet h3{font-size:28px;font-weight:600;text-align:center;margin:10px 0 6px;color:var(--paper-ink)}\n.sheet h4{font-size:18px;margin:34px 0 10px}\n.sheet p{text-indent:1.4em}\n.sheet h3 + p,.sheet h4 + p,.sheet .note + p,.sheet .device p{text-indent:0}\n.sheet .note{font-family:var(--sans);font-size:13px;line-height:1.5;background:var(--paper-note);border-radius:4px;padding:10px 12px;margin:14px 0}\n.sheet .note b{display:block;font-size:10px;letter-spacing:.1em;text-transform:uppercase;margin-bottom:2px}\n.sheet .unwritten{font-family:var(--sans);font-size:14px;line-height:1.5;border:1.5px dashed var(--paper-line);border-radius:4px;padding:14px 16px;color:var(--paper-muted)}\n.sheet .unwritten b{display:block;font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:var(--paper-ink);margin-bottom:4px}\n.sheet .device{border-top:1px solid var(--paper-line);margin-top:34px;padding-top:6px}\n.sheet .device p{margin-top:8px}\n.sheet blockquote{margin:14px 1.4em;font-style:italic}\n.sheet .li{text-indent:0;padding-left:1.4em}\n.sheet h5{font-family:var(--serif);font-size:17px;font-weight:600;margin:22px 0 6px}\n.sheet h5 + p,.sheet blockquote + p{text-indent:0}\n\n/* Notes */\n.kinds{display:flex;gap:8px;flex-wrap:wrap}\n.kind{display:inline-flex;align-items:center;gap:8px;font-size:13px;font-weight:500;padding:6px 12px;border-radius:999px;border:1px solid var(--line);background:transparent;color:var(--soft)}\n.kind i{width:11px;height:11px;border-radius:2px;background:var(--k);display:block;flex:none}\n.kind:hover{border-color:var(--muted)}\n.kind[aria-pressed=\"true\"]{border-color:var(--fg);color:var(--fg);background:var(--raise)}\n.kind .num{color:var(--muted)}\n.tools{display:flex;gap:10px 16px;flex-wrap:wrap;align-items:center}\n.tools label{display:inline-flex;gap:8px;align-items:center;font-size:13px;color:var(--muted);min-width:0}\n.tools select,.tools input{font:inherit;font-size:14px;color:var(--fg);background:var(--card);border:1px solid var(--line);border-radius:6px;padding:7px 10px;min-width:0;max-width:100%}\n.tools input{width:min(240px,100%)}\n.tools input::placeholder{color:var(--muted)}\n.group{display:grid;gap:16px}\n.group + .group{margin-top:20px}\n.board{display:grid;grid-template-columns:repeat(auto-fill,minmax(204px,1fr));gap:24px 20px;align-items:start}\n.sticky{--k:var(--n-story);--tilt:0deg;background:var(--k) linear-gradient(hsl(0 0% 0% / .07),hsl(0 0% 0% / 0) 22px);color:var(--paper-ink);padding:16px 16px 12px;min-height:196px;display:grid;grid-template-rows:auto auto 1fr auto;gap:7px;border-radius:2px;box-shadow:0 1px 0 hsl(0 0% 100% / .25) inset,0 16px 22px -10px hsl(0 0% 0% / .8);transform:rotate(var(--tilt));transition:transform .18s ease;min-width:0}\n.sticky:hover{transform:rotate(0deg) translateY(-3px)}\n.sticky .k{font-size:10px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;opacity:.62}\n.sticky .t{font-family:var(--hand);font-size:25px;font-weight:700;line-height:1.02;text-wrap:balance;overflow-wrap:anywhere}\n.sticky .x{font-size:13.5px;line-height:1.45;overflow-wrap:anywhere}\n.sticky .f{display:flex;justify-content:space-between;align-items:center;gap:8px;flex-wrap:wrap;font-size:11px;padding-top:6px;opacity:.78}\n.sticky .loose{border:1px dashed currentColor;border-radius:999px;padding:1px 8px}\n@keyframes land{from{opacity:0;transform:translateY(-18px) rotate(var(--tilt)) scale(1.05)}to{opacity:1;transform:rotate(var(--tilt))}}\n.sticky.fresh{animation:land .5s ease-out}\n.sticky .ed{font-size:11px;font-weight:600;font-style:italic;line-height:1.35}\n.sticky .acts{display:flex;gap:5px;flex-wrap:wrap;padding-top:8px;border-top:1px solid hsl(0 0% 0% / .14)}\n.nact{display:inline-flex;align-items:center;gap:4px;font-size:11px;font-weight:600;line-height:1.2;white-space:nowrap;color:var(--paper-ink);background:hsl(0 0% 100% / .38);border:1px solid hsl(0 0% 0% / .2);border-radius:999px;padding:5px 10px;flex:1 1 auto;justify-content:center}\n.nact:hover{background:hsl(0 0% 100% / .78)}\n.nact:focus-visible{outline-color:var(--paper-ink);border-radius:999px}\n@media (prefers-reduced-motion:reduce){.sticky{transition:none}.sticky.fresh{animation:none}}\n.pins{display:grid;gap:8px}\n.pin{display:grid;grid-template-columns:11px minmax(0,1fr);gap:10px;align-items:baseline;font-family:var(--serif);font-size:15px;color:var(--soft)}\n.pin i{width:11px;height:11px;border-radius:2px;background:var(--k);display:block}\n\n/* Notes: ways to look */\n.views{display:inline-flex;gap:2px;padding:3px;border:1px solid var(--line);border-radius:999px;background:var(--card);justify-self:start;max-width:100%}\n.view{border:0;background:transparent;color:var(--muted);font-size:14px;font-weight:500;padding:7px 16px;border-radius:999px;white-space:nowrap}\n.view:hover{color:var(--fg)}\n.view[aria-pressed=\"true\"]{background:var(--fg);color:var(--bg);font-weight:600}\n.gwrap{display:grid;grid-template-columns:minmax(0,1fr) 300px;gap:24px;align-items:start}\n.graph{position:relative;border:1px solid var(--line);border-radius:var(--r);background:radial-gradient(120% 90% at 50% 40%,var(--raise),var(--card) 70%);overflow:hidden;min-width:0}\n.graph canvas{display:block;width:100%;height:min(68vh,600px);min-height:380px;touch-action:pan-y;cursor:grab}\n.graph canvas:active{cursor:grabbing}\n.ghint{position:absolute;left:14px;right:14px;bottom:10px;pointer-events:none}\n.gside{display:grid;gap:14px;align-content:start;min-width:0}\n.gside .sticky{min-height:0}\n.glist{display:grid;gap:6px}\n.glink{display:grid;grid-template-columns:11px minmax(0,1fr);gap:10px;align-items:baseline;text-align:left;background:transparent;border:1px solid var(--line);border-radius:6px;padding:8px 10px;font-size:14px;color:var(--soft)}\n.glink:hover{border-color:var(--muted);color:var(--fg)}\n.glink i{width:11px;height:11px;border-radius:2px;background:var(--k);display:block}\n.tl{border-left:2px solid var(--line);margin-left:6px;padding-left:26px;display:grid;gap:30px}\n.tly{display:grid;gap:16px;position:relative}\n.tly::before{content:\"\";position:absolute;left:-34px;top:9px;width:14px;height:14px;border-radius:50%;background:var(--green);box-shadow:0 0 0 4px var(--bg)}\n.tlyear{font-size:28px;line-height:1}\n.tli{display:grid;gap:8px;align-content:start;min-width:0}\n.tlwhen{font-size:12px;color:var(--muted);min-height:18px}\n.tlwrap{display:grid;gap:28px}\n.tlwrap .group + .group,.tlwrap .group{margin-top:0}\n.tlgap{font-size:12px;color:var(--muted);font-style:italic}\n\n/* Ghostwriter */\n.spot{display:grid;grid-template-columns:220px minmax(0,1fr);gap:28px;align-items:center;border:1px solid var(--green-line);background:var(--green-wash);border-radius:var(--r);padding:22px}\n.spot img{width:100%;border-radius:6px;display:block}\n.spot .info{display:grid;gap:10px;min-width:0}\n.spot .name{font-family:var(--serif);font-size:30px;font-weight:600;line-height:1.1}\n.quote{font-family:var(--serif);font-style:italic;color:var(--soft);font-size:17px}\n.grid{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:12px}\n.card{background:var(--card);border:1px solid var(--line);border-radius:var(--r);padding:0;text-align:left;overflow:hidden;display:grid;align-content:start}\n.card:hover{border-color:var(--green-line)}\n.card[aria-pressed=\"true\"]{border-color:var(--green)}\n.card img{width:100%;aspect-ratio:4/5;object-fit:cover;display:block}\n.card span{display:block;padding:10px 12px 0;font-family:var(--serif);font-weight:600;font-size:15px}\n.card small{display:block;padding:2px 12px 12px;color:var(--muted);font-size:12px;line-height:1.4}\n@keyframes drift{0%{transform:perspective(1200px) translate3d(0,0,0) rotate(0)}18%{transform:perspective(1200px) translate3d(-4px,-5px,3px) rotate(.6deg)}35%{transform:perspective(1200px) translate3d(3px,-8px,-4px) rotate(-.4deg)}52%{transform:perspective(1200px) translate3d(-6px,-3px,5px) rotate(.8deg)}68%{transform:perspective(1200px) translate3d(5px,-6px,-3px) rotate(-.7deg)}84%{transform:perspective(1200px) translate3d(-3px,-4px,4px) rotate(.3deg)}100%{transform:perspective(1200px) translate3d(0,0,0) rotate(0)}}\n.drift{animation:drift 14s ease-in-out infinite;will-change:transform}\n@media (prefers-reduced-motion:reduce){.drift{animation:none}}\n\n.modal{position:fixed;inset:0;z-index:40;background:hsl(0 0% 0% / .68);display:grid;place-items:center;padding:16px}\n.modal[hidden]{display:none}\n.popup{width:100%;max-width:560px;max-height:100%;overflow:auto;background:var(--raise);border:1px solid var(--green-line);border-radius:12px;padding:clamp(18px,4vw,26px);display:grid;gap:12px;box-shadow:0 30px 80px hsl(0 0% 0% / .7)}\n.popup h2{font-size:21px;line-height:1.25}\n.popup.wide{max-width:820px}\n.modal:not(.manual) #copytext,.modal:not(.manual) #copybtn{display:none}\n.popup pre{margin:0;white-space:pre-wrap;overflow-wrap:anywhere;font-family:var(--sans);font-size:14px;line-height:1.5;background:var(--bg);border:1px solid var(--line);border-radius:6px;padding:12px 14px;max-height:40vh;overflow:auto}\n.modal:not(.manual) #copystate::before{content:\"\";display:inline-block;width:10px;height:6px;border-left:2px solid var(--green);border-bottom:2px solid var(--green);transform:rotate(-45deg) translateY(-3px);margin-right:8px}\n.modal.manual #copystate{color:var(--amber)}\n.fld{display:grid;gap:6px;font-size:12px;font-weight:600;letter-spacing:.04em;color:var(--muted)}\n.fld input,.fld textarea{font:inherit;font-size:15px;font-weight:400;letter-spacing:0;line-height:1.5;color:var(--fg);background:var(--bg);border:1px solid var(--line);border-radius:6px;padding:10px 12px;width:100%;resize:vertical}\n#noteerr{color:var(--amber)}\n\n/* Items: anything on a page the author can edit */\n.item{display:grid;gap:8px;min-width:0;align-content:start}\n.item .v{color:var(--soft);white-space:pre-wrap;overflow-wrap:anywhere}\n.item .v.big{font-family:var(--serif);font-size:clamp(1.1rem,2.2vw,1.4rem);line-height:1.45;color:var(--fg);max-width:36em}\n.item ul{margin:0;padding-left:18px;color:var(--soft);display:grid;gap:4px}\n.item blockquote{margin:0;padding-left:14px;border-left:2px solid var(--green-line);font-family:var(--serif);font-style:italic;color:var(--soft)}\n.acts2{display:flex;gap:6px;flex-wrap:wrap;align-items:center}\n.ibtn{font-size:12px;font-weight:600;line-height:1.2;white-space:nowrap;color:var(--soft);background:transparent;border:1px solid var(--line);border-radius:999px;padding:6px 12px}\n.ibtn:hover{border-color:var(--green-line);color:var(--green)}\n.ibtn.go{border-color:var(--green-line);color:var(--green);background:var(--green-wash)}\n.ibtn svg{width:13px;height:13px;vertical-align:-2px;margin-right:6px}\n.story .end .ibtn{margin-top:8px}\n.edtag{font-size:11px;font-weight:600;color:var(--green);white-space:nowrap}\n.edtag.local{color:var(--amber)}\n.hero2{display:grid;gap:22px;border:1px solid var(--line);background:var(--card);border-radius:12px;padding:clamp(18px,3vw,28px)}\n.hero2 .ttl{font-family:var(--serif);font-weight:600;font-size:clamp(1.7rem,4vw,2.6rem);line-height:1.1;overflow-wrap:anywhere}\n.hero2 .sub2{font-family:var(--serif);font-style:italic;color:var(--soft);font-size:clamp(1.05rem,2vw,1.25rem)}\n.hero2 .by{font-size:16px;color:var(--soft)}\n.facts2{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:30px 40px}\n.sugs{display:grid;gap:8px;border:1px dashed var(--green-line);border-radius:var(--r);padding:12px 14px}\n.sug{display:flex;gap:10px;align-items:baseline;justify-content:space-between;flex-wrap:wrap}\n.sug span{font-family:var(--serif);font-size:16px;min-width:0;overflow-wrap:anywhere}\n.storygrid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:18px}\n.story{display:grid;gap:10px;align-content:start;border:1px solid var(--line);border-top:3px solid var(--n-story);background:var(--card);border-radius:12px;padding:18px 20px;min-width:0}\n.story h3{font-size:20px;font-weight:600}\n.story .nar{color:var(--soft);white-space:pre-wrap;overflow-wrap:anywhere}\n.story .end{border-left:2px solid var(--green-line);padding-left:12px;color:var(--soft);display:grid;gap:2px}\n.story .end.need{border-left-color:var(--amber-line)}\n.story details p{font-size:13px;color:var(--soft);margin-bottom:8px}\n.witem.first{border-color:var(--green-line);background:var(--green-wash)}\n.witem .ans{border-left:2px solid var(--green-line);padding-left:12px;color:var(--soft);white-space:pre-wrap;overflow-wrap:anywhere;font-size:14px}\n.tip{display:none;font-size:13px;color:var(--fg);background:hsl(200 86% 79% / .10);border:1px solid hsl(200 86% 79% / .45);border-radius:var(--r);padding:9px 12px;max-width:64ch}\n.tip b{color:var(--n-place);font-weight:600}\nbody.helpon .tip{display:block}\n.helpbtn{flex:none;width:34px;height:34px;border-radius:50%;border:1px solid var(--line);background:transparent;color:var(--soft);font-weight:600;font-size:15px;display:inline-grid;place-items:center}\n.helpbtn:hover{border-color:var(--green-line);color:var(--green)}\n.helpbtn[aria-pressed=\"true\"]{background:var(--n-place);border-color:var(--n-place);color:var(--green-ink)}\n.toast{position:fixed;left:50%;bottom:calc(env(safe-area-inset-bottom,0px) + 20px);transform:translateX(-50%);z-index:50;background:var(--green);color:var(--green-ink);font-weight:600;font-size:14px;border-radius:999px;padding:9px 18px;box-shadow:0 10px 30px hsl(0 0% 0% / .6);max-width:calc(100% - 32px)}\n.talk{display:grid;gap:8px;max-height:36vh;overflow:auto}\n.talk:empty{display:none}\n.talk div{border-radius:10px;padding:9px 12px;max-width:90%;overflow-wrap:anywhere;white-space:pre-wrap;font-size:14px}\n.talk .me{justify-self:end;background:var(--bg);border:1px solid var(--line)}\n.talk .gw{justify-self:start;background:var(--green-wash);border:1px solid var(--green-line)}\n.talk .gw b{display:block;font-size:11px;letter-spacing:.06em;text-transform:uppercase;color:var(--green);margin-bottom:3px}\n.about{border-left:3px solid var(--green);background:var(--bg);border-radius:0 6px 6px 0;padding:8px 12px;font-size:14px;color:var(--soft);max-height:20vh;overflow:auto;white-space:pre-wrap;overflow-wrap:anywhere}\n#editfields{display:grid;gap:12px}\n#editerr,#talkstate.warn{color:var(--amber)}\n.och .acts2,.osec .acts2{margin-top:2px}\n.sheet .secdisc{margin:2px 0 14px;display:flex;gap:8px;align-items:center;flex-wrap:wrap}\n.sheet .secdisc .edtag{color:var(--paper-ink);border-color:var(--paper-line)}\n.sheet h4 .snum{font-family:var(--sans);font-size:12px;font-weight:600;color:var(--paper-muted);margin-right:10px;letter-spacing:.04em}\n.sheet .secdisc .ibtn{color:var(--paper-muted);border-color:var(--paper-line)}\n.sheet .secdisc .ibtn:hover{color:var(--paper-ink);border-color:var(--paper-ink)}\n.sheet .note .ibtn,.sheet .unwritten .ibtn{color:var(--paper-ink);border-color:var(--paper-line);background:transparent;margin-top:10px;font-family:var(--sans)}\n.sheet .note .ibtn:hover,.sheet .unwritten .ibtn:hover{border-color:var(--paper-ink)}\n.doclink{display:flex;gap:12px 18px;align-items:center;justify-content:space-between;flex-wrap:wrap;border:1px solid var(--green-line);background:var(--green-wash);border-radius:var(--r);padding:14px 16px}\n\n@media (max-width:860px){\n .facts2,.storygrid{grid-template-columns:minmax(0,1fr)}\n .gwrap{grid-template-columns:minmax(0,1fr)}\n .graph canvas{height:420px}\n .cols,.ms{grid-template-columns:minmax(0,1fr)}\n .jphase{min-width:100%}\n .facts{grid-template-columns:minmax(0,1fr)}\n .toc{position:static}\n .spot{grid-template-columns:minmax(0,1fr)}\n .spot img{max-width:180px}\n .grid{grid-template-columns:repeat(2,minmax(0,1fr))}\n .tabs a{padding:7px 10px;font-size:13px}\n .helpbtn{order:2}\n .tabs{order:3;flex:1 1 100%}\n .notyet{grid-template-columns:minmax(0,1fr)}\n}\n@media (max-width:520px){\n .jgrid span{font-size:11px;padding-inline:1px}\n .sayrow .btn,.witem .btn{width:100%;justify-content:center}\n .board{grid-template-columns:repeat(2,minmax(0,1fr));gap:18px 12px}\n .sticky{min-height:156px;padding:12px 12px 10px}\n .nact{padding:8px 10px}\n .sticky .t{font-size:22px}\n .sticky .x{font-size:13px}\n .tools label{flex:1 1 100%}\n .tools select,.tools input{flex:1;width:auto}\n .view{padding:7px 12px}\n .tl{padding-left:20px}\n .tly::before{left:-28px}\n}\n/* ---- The cover ---- */\n.cv{position:relative;aspect-ratio:2/3;width:100%;container-type:inline-size;overflow:hidden;border-radius:3px;background:var(--paper,#F3EBDD);color:var(--ink,#1F3B2D);text-align:left;line-height:1.2;box-shadow:0 30px 70px hsl(0 0% 0% / .75),inset 0 0 0 1px hsl(0 0% 100% / .04)}\n.cv::after{content:\"\";position:absolute;inset:0 auto 0 0;width:2.2cqw;background:linear-gradient(90deg,hsl(0 0% 0% / .22),transparent);pointer-events:none}\n.cv .cvl{display:block;width:max-content;white-space:nowrap}\n.cvtitle{font-family:var(--tf);font-weight:var(--tw,600);line-height:var(--tlh,1);letter-spacing:var(--tls,0);font-size:12cqw}\n.cv.caps .cvtitle{text-transform:uppercase}\n.cvsub{text-wrap:balance;overflow-wrap:anywhere}\n.cvauthor{font-size:3.5cqw;white-space:nowrap}\n.cvmotif svg{display:block;width:100%;height:100%;overflow:visible}\n.cv-paper .cvtop{position:absolute;left:9cqw;right:9cqw;top:10cqw}\n.cv-paper .cvsub{font-family:var(--xf);font-weight:500;line-height:1.3;margin-top:5cqw;max-width:78cqw;color:color-mix(in srgb,var(--ink) 80%,var(--paper))}\n.cv-paper .cvmotif{position:absolute;left:9cqw;width:82cqw;bottom:24cqw;height:24cqw}\n.cv-paper .cvauthor{position:absolute;left:9cqw;right:9cqw;bottom:9cqw;font-family:var(--xf);font-weight:600;letter-spacing:.22em;text-transform:uppercase}\n.cv-bold .cvmotif{position:absolute;left:0;top:0;width:100cqw;height:66cqw}\n.cv-bold .cvbase{position:absolute;left:8cqw;right:8cqw;bottom:8cqw;display:flex;flex-direction:column}\n.cv-bold .cvrule{width:18cqw;height:1cqw;background:var(--accent);margin:5cqw 0 6cqw;flex:none}\n.cv-bold .cvsub{font-family:var(--xf);font-weight:500;line-height:1.32;max-width:78cqw}\n.cv-bold .cvauthor{margin-top:7cqw;font-family:var(--xf);font-weight:600;letter-spacing:.2em;text-transform:uppercase;color:var(--accent)}\n.cv-quiet{text-align:center;background:radial-gradient(120% 80% at 60% 62%,color-mix(in srgb,var(--paper) 90%,var(--ink)) 0,var(--paper) 70%)}\n.cv-quiet .cvl,.cv-over .cvl{margin-inline:auto}\n.cv-quiet .cvtop{position:absolute;left:7cqw;right:7cqw;top:11cqw}\n.cv-quiet .cvsub{font-family:var(--xf);font-weight:500;letter-spacing:.2em;text-transform:uppercase;line-height:1.8;margin-top:4cqw;color:color-mix(in srgb,var(--ink) 72%,var(--paper))}\n.cv-quiet .cvmotif{position:absolute;left:0;top:50cqw;width:100cqw;height:86cqw}\n.cv-quiet .cvauthor{position:absolute;left:7cqw;right:7cqw;bottom:8cqw;font-family:var(--tf);font-weight:600;letter-spacing:.14em;text-transform:uppercase;font-size:5cqw}\n.cv-own{aspect-ratio:auto;background:hsl(0 0% 7%)}\n.cv-own img{display:block;width:100%;height:auto}\n.cv-over{aspect-ratio:2/3;text-align:center}\n.cv-over img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}\n.cv-over .cvscrim{position:absolute;inset:0;background:linear-gradient(180deg,var(--shade) 0,transparent 46%,transparent 74%,var(--shade) 100%)}\n.cv-over .cvtop{position:absolute;left:8cqw;right:8cqw;top:10cqw}\n.cv-over .cvsub{font-family:var(--xf);font-weight:500;line-height:1.4;margin-top:4cqw}\n.cv-over .cvauthor{position:absolute;left:8cqw;right:8cqw;bottom:8cqw;font-family:var(--xf);font-weight:600;letter-spacing:.2em;text-transform:uppercase}\n.cv-wait{display:grid;place-items:center;background:var(--card);color:var(--muted);border:1px dashed var(--line);box-shadow:none;text-align:center;font-size:13px;padding:12px}\n.cv-wait::after{display:none}\n.concepts{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:clamp(20px,3vw,36px);align-items:start}\n.cc{display:grid;gap:18px;min-width:0}\n.cwrap{padding:26px 30px;border:1px solid var(--line);border-radius:var(--r);background:radial-gradient(90% 70% at 50% 40%,hsl(0 0% 9%),var(--card))}\n.cc.picked .cwrap,.cwrap.picked{border-color:var(--green);box-shadow:0 0 0 1px var(--green)}\n.cwrap.own{max-width:300px}\n.cmeta{display:grid;gap:10px}\n.cmeta h3,.cown h3{font-size:21px;font-weight:600}\n.clook{display:flex;gap:12px;align-items:center;flex-wrap:wrap}\n.csws{display:inline-flex;gap:5px}\n.csws i{width:18px;height:18px;border-radius:50%;box-shadow:inset 0 0 0 1px hsl(0 0% 100% / .14)}\n.csect{display:grid;gap:22px;border-top:1px solid var(--line);padding-top:32px}\n.cintro{display:grid;gap:8px;max-width:68ch}\n.cintro p{color:var(--soft)}\n.cother{display:grid;grid-template-columns:minmax(0,1.5fr) minmax(0,1fr);gap:clamp(24px,4vw,48px);align-items:start}\n.cown{display:grid;gap:16px;align-content:start;min-width:0}\n.cslot{aspect-ratio:2/3;width:min(100%,250px);border:1.5px dashed var(--green-line);border-radius:6px;background:var(--green-wash);display:grid;place-content:center;justify-items:center;gap:10px;text-align:center;padding:18px;color:var(--soft)}\n.cslot svg{width:30px;height:30px;color:var(--green)}\n.cslot b{font-family:var(--serif);font-size:17px;font-weight:600;color:var(--fg)}\n.csteps{list-style:none;margin:0;padding:0;display:grid;gap:10px;counter-reset:cs}\n.csteps li{counter-increment:cs;display:grid;grid-template-columns:26px minmax(0,1fr);gap:10px;color:var(--soft)}\n.csteps li::before{content:counter(cs);width:22px;height:22px;border-radius:50%;border:1px solid var(--green-line);color:var(--green);font-size:12px;font-weight:600;display:grid;place-items:center;margin-top:1px}\n.cback{gap:14px}\n.cback .chl{font-family:var(--serif);font-size:26px;line-height:1.25;font-weight:600;color:var(--fg);max-width:30ch}\n.cback p{color:var(--soft);max-width:62ch}\n.cback .cbio{border-left:2px solid var(--green-line);padding-left:14px;color:var(--muted);font-size:14px}\n.ovhero{display:grid;grid-template-columns:auto minmax(0,1fr);gap:clamp(22px,4vw,44px);align-items:center}\n.ovcover{display:grid;gap:10px;justify-items:center;width:clamp(150px,17vw,208px);text-decoration:none;color:inherit}\n.ovcover .cv{box-shadow:0 22px 50px hsl(0 0% 0% / .7),0 0 0 1px hsl(0 0% 100% / .06);transform:rotate(-1.2deg)}\n.ovcover u{color:var(--green);text-underline-offset:3px}\n.notyet>.cown{border-left:1px solid var(--line);padding-left:clamp(20px,3vw,44px)}\n@media (max-width:900px){.concepts,.cother{grid-template-columns:minmax(0,1fr)}.cwrap{padding:22px clamp(30px,14vw,90px)}.cwrap.own{padding:22px 30px}.notyet>.cown{border-left:0;padding-left:0}}\n@media (max-width:640px){.ovhero{grid-template-columns:minmax(0,1fr);justify-items:start}.ovcover{width:150px}}\n"; document.head.appendChild(st);
|
|
8
8
|
const boot = document.getElementById('gw-boot'); if (boot) boot.remove();
|
|
9
9
|
document.body.insertAdjacentHTML('beforeend', "<header class=\"bar\"><div class=\"wrap\">\n <div class=\"brand\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M10 3l1.9 5.1L17 10l-5.1 1.9L10 17l-1.9-5.1L3 10l5.1-1.9z\"/><path d=\"M19 14l.8 2.2L22 17l-2.2.8L19 20l-.8-2.2L16 17l2.2-.8z\"/></svg><div class=\"bcol\"><span>GhostWriter <span class=\"amp\">&</span> the Muse</span><button class=\"verchip\" id=\"verbtn\" data-act=\"version\" aria-haspopup=\"dialog\" hidden></button></div></div>\n <nav class=\"tabs\" id=\"tabs\" aria-label=\"Pages\"></nav>\n <button class=\"helpbtn\" id=\"helpbtn\" data-act=\"help\" aria-pressed=\"false\" aria-label=\"Show or hide help on this page\" title=\"Help\">?</button>\n</div></header>\n<main id=\"app\" class=\"wrap\"></main>\n<div id=\"copybox\" class=\"modal\" hidden><div class=\"popup\" role=\"dialog\" aria-modal=\"true\" aria-labelledby=\"copytitle\" aria-describedby=\"copyhint\">\n <div class=\"label green\" id=\"copystate\">Copied to your clipboard</div>\n <h2 id=\"copytitle\">Now paste it into your conversation with your ghostwriter</h2>\n <p class=\"muted\" id=\"copyhint\"></p>\n <pre id=\"copytext\" tabindex=\"0\"></pre>\n <div class=\"row\"><button class=\"btn\" data-act=\"closecopy\" id=\"closebtn\">Done</button><button class=\"btn ghost\" data-act=\"copy\" id=\"copybtn\">Copy again</button></div>\n</div></div>\n\n<div id=\"verbox\" class=\"modal\" hidden><div class=\"popup\" role=\"dialog\" aria-modal=\"true\" aria-labelledby=\"vertitle\" aria-describedby=\"verhint\">\n <div class=\"label green\" id=\"verstate\"></div>\n <h2 id=\"vertitle\"></h2>\n <p class=\"muted\" id=\"verhint\"></p>\n <dl class=\"verlist\" id=\"verlist\"></dl>\n <div class=\"row\"><button class=\"btn\" data-act=\"verclose\" id=\"verclose\">Done</button></div>\n</div></div>\n\n<div id=\"notebox\" class=\"modal\" hidden><div class=\"popup\" role=\"dialog\" aria-modal=\"true\" aria-labelledby=\"notehead\">\n <div class=\"label green\" id=\"notekind\"></div>\n <h2 id=\"notehead\">Edit this note</h2>\n <label class=\"fld\" for=\"notetitle\">Title<input id=\"notetitle\" type=\"text\" maxlength=\"120\" autocomplete=\"off\"></label>\n <label class=\"fld\" for=\"notetext\">In your words<textarea id=\"notetext\" rows=\"7\" maxlength=\"4000\"></textarea></label>\n <p class=\"fine\" id=\"notehint\"></p>\n <p class=\"fine\" id=\"noteerr\" hidden></p>\n <div class=\"row\"><button class=\"btn\" data-act=\"nsave\" id=\"notesave\">Save</button><button class=\"btn ghost\" data-act=\"nclose\" id=\"notecancel\">Cancel</button></div>\n</div></div>\n\n<div id=\"editbox\" class=\"modal\" hidden><div class=\"popup\" role=\"dialog\" aria-modal=\"true\" aria-labelledby=\"edithead\">\n <div class=\"label green\" id=\"editpage\"></div>\n <h2 id=\"edithead\"></h2>\n <div id=\"editfields\"></div>\n <p class=\"fine\" id=\"edithint\"></p>\n <p class=\"fine\" id=\"editerr\" hidden></p>\n <div class=\"row\"><button class=\"btn\" data-act=\"esave\" id=\"editsave\">Save</button><button class=\"btn ghost\" data-act=\"eclose\" id=\"editcancel\">Cancel</button></div>\n</div></div>\n\n<div id=\"toast\" class=\"toast\" role=\"status\" hidden></div>\n");
|
|
10
10
|
|
|
@@ -99,6 +99,10 @@ const SAYINGS = ["What's next?", "Where are we?", "Show me the draft.", "Move on
|
|
|
99
99
|
const COPYICON = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15V6a2 2 0 0 1 2-2h9"/></svg>';
|
|
100
100
|
let PROMPTS = [], waitAll = false, downOn = null, copyFrom = null, copyKind = "prompt", edits = {}, editing = null;
|
|
101
101
|
const regP = text => PROMPTS.push(String(text)) - 1;
|
|
102
|
+
/* Wherever the book is waiting on the author, a button copies a ready message for the conversation, so they never have to work out what to say. */
|
|
103
|
+
const copyBtn = (say, id, label, cls) => '<button class="' + (cls || "ibtn go") + '" data-act="prompt" data-i="' + regP(say) + '" id="' + id + '">' + COPYICON + label + '</button>';
|
|
104
|
+
const storySay = s => "For my book: section " + s.num + ', "' + s.title + '", still needs a real story from me.' + (s.purpose ? "\n\nWhat the section is for: " + s.purpose : "") + "\n\nAsk me one question at a time to draw the story out, then put it in my story bank.";
|
|
105
|
+
const yesBtn = (waiting, what, id) => waiting ? copyBtn("Yes to my " + what + " as it reads on my pages now.", "yes-" + id, "Copy a yes") : "";
|
|
102
106
|
function waitPrompt(x){
|
|
103
107
|
if (x.prompt) return String(x.prompt);
|
|
104
108
|
return "Let's work on something you are waiting on from me for my book.\n\n" + (x.where ? x.where + ": " : "") + x.ask + "\n\nAsk me what you need, one question at a time. When you have it, put it where it belongs in the book and take it off my list.";
|
|
@@ -183,7 +187,7 @@ const docId = (kind, ref) => (kind + "-" + String(ref).replace(/[^A-Za-z0-9_.~:@
|
|
|
183
187
|
const hash = t => { let h = 5381; for (let i = 0; i < t.length; i++) h = ((h << 5) + h + t.charCodeAt(i)) >>> 0; return h.toString(36); };
|
|
184
188
|
const clip = (t, max) => { t = String(t == null ? "" : t); return t.length > max ? t.slice(0, max - 1).trimEnd() + "…" : t; };
|
|
185
189
|
const nowVal = (ref, base) => over[ref] ? over[ref].value : (base == null ? "" : String(base));
|
|
186
|
-
const edTag = ref => over[ref] ? '<span class="edtag' + (over[ref].local ? ' local' : '') + '">' + (over[ref].local ? 'Changed here, not saved yet' : 'Changed by you') + '</span>' : '';
|
|
190
|
+
const edTag = ref => over[ref] ? '<span class="edtag' + (over[ref].local ? ' local' : '') + '">' + (over[ref].local ? 'Changed here, not saved yet' : over[ref].by === "suggestion" ? 'A suggestion you chose' : 'Changed by you') + '</span>' : '';
|
|
187
191
|
const domId = ref => String(ref).replace(/[^A-Za-z0-9]/g, "-");
|
|
188
192
|
function reg(item){ ITEMS[item.ref] = item; return item.ref; }
|
|
189
193
|
function acts(ref, extra){
|
|
@@ -219,7 +223,7 @@ function setupPage(){
|
|
|
219
223
|
if (!setup || !(setup.items || []).length) return notYet("setup", "Book Setup", "What the book is, who it is for, and what it promises.", "Your ghostwriter drafts it from your first answers, and every line stays yours to change.");
|
|
220
224
|
const xs = setup.items.map(it => lineItem("Book Setup", "setup", it)), by = k => xs.find(x => x.it.key === k), t = by("title"), s = by("subtitle"), a = by("author"), rest = xs.filter(x => !["title","subtitle","author"].includes(x.it.key));
|
|
221
225
|
const head = (x, label) => '<div class="head"><div class="label">' + label + '</div><div class="chips">' + edTag(x.ref) + '</div></div>';
|
|
222
|
-
return '<div class="pagehead"><div class="chips">' + statusChip(setup) + (mode === "snapshot" ? '<span class="chip">Saved copy</span>' : '') + '</div><h1>Book Setup</h1><p class="sub">What the book is, who it is for, and what it promises.</p></div>' +
|
|
226
|
+
return '<div class="pagehead"><div class="chips">' + statusChip(setup) + yesBtn(setup.status !== "confirmed", "Book Setup", "setup") + (mode === "snapshot" ? '<span class="chip">Saved copy</span>' : '') + '</div><h1>Book Setup</h1><p class="sub">What the book is, who it is for, and what it promises.</p></div>' +
|
|
223
227
|
tip('Every line here came from what you told your ghostwriter, or is your ghostwriter\'s first guess. Press <b>Edit</b> to change any line yourself. It is saved straight away, and your ghostwriter carries it through the book the next time you write in your conversation. ' + HELPLINE) +
|
|
224
228
|
'<div class="hero2">' +
|
|
225
229
|
(t ? '<div class="item">' + head(t, "Working title") + '<div class="ttl">' + esc(t.value) + '</div>' + acts(t.ref) + '</div>' : '') +
|
|
@@ -234,7 +238,7 @@ function voicePage(){
|
|
|
234
238
|
if (!has) return notYet("voice", "Voice", "How you sound on the page, drawn from your own words.", "Your ghostwriter writes this page after hearing you. A story told out loud, or a few things you have written, is plenty to start.");
|
|
235
239
|
const xs = (voice.items || []).map(it => lineItem("Voice", "voice", it)), lead = xs.find(x => x.it.key === "voice-in-one-paragraph"), rest = xs.filter(x => x !== lead);
|
|
236
240
|
const st = voice.style, sx = st ? (st.items || []).map(it => lineItem("Voice", "style", it)) : [];
|
|
237
|
-
return '<div class="pagehead"><div class="chips">' + statusChip(voice) + (mode === "snapshot" ? '<span class="chip">Saved copy</span>' : '') + '</div><h1>Voice</h1><p class="sub">How you sound on the page. Your ghostwriter rereads this before writing anything.</p></div>' +
|
|
241
|
+
return '<div class="pagehead"><div class="chips">' + statusChip(voice) + yesBtn(voice.status !== "confirmed", "Voice page", "voice") + (mode === "snapshot" ? '<span class="chip">Saved copy</span>' : '') + '</div><h1>Voice</h1><p class="sub">How you sound on the page. Your ghostwriter rereads this before writing anything.</p></div>' +
|
|
238
242
|
tip('This is your ghostwriter\'s description of how you write, taken from your own words. If a line does not sound like you, press <b>Edit</b> and fix it. More of your own writing or talking makes this sharper. ' + HELPLINE) +
|
|
239
243
|
(lead ? '<div class="hero2">' + itemBlock(lead, "big", sampler ? '<button class="ibtn go" data-act="suggest" data-k="tighten" id="sug-tighten">Tighten this</button>' : '') + '</div>' : '') +
|
|
240
244
|
(rest.length ? '<div class="facts2">' + rest.map(x => itemBlock(x)).join("") + '</div>' : '') +
|
|
@@ -256,13 +260,13 @@ function storiesPage(){
|
|
|
256
260
|
const card = st => { const x = storyItem(st), v = x.v;
|
|
257
261
|
return '<article class="story"><div class="head"><div class="chips">' + (STRENGTH[String(st.strength || "").toLowerCase()] ? '<span class="chip">' + STRENGTH[String(st.strength).toLowerCase()] + '</span>' : '') + '<span class="chip">' + (v.when ? esc(v.when) : 'No date yet') + '</span>' + (st.told ? '<span class="chip green">Told in the book</span>' : '<span class="chip">Not told yet</span>') + '</div>' + (x.changed ? '<span class="edtag' + (x.local ? ' local' : '') + '">' + (x.local ? 'Changed here, not saved yet' : 'Changed by you') + '</span>' : '') + '</div>' +
|
|
258
262
|
'<h3>' + esc(v.title) + '</h3><p class="nar">' + esc(v.narrative) + '</p>' +
|
|
259
|
-
(v.ended ? '<div class="end"><div class="label">How it ended</div><div>' + esc(v.ended) + '</div></div>' : '<div class="end need"><div class="label amber">How it ended</div><div>Your ghostwriter still needs this from you. Press Edit to add it, or tell it in your conversation.</div></div>') +
|
|
263
|
+
(v.ended ? '<div class="end"><div class="label">How it ended</div><div>' + esc(v.ended) + '</div></div>' : '<div class="end need"><div class="label amber">How it ended</div><div>Your ghostwriter still needs this from you. Press Edit to add it, or tell it in your conversation.</div><div>' + copyBtn('For my book: here is how the story "' + v.title + '" ended:', "endcopy-" + domId(st.ref), "Copy a prompt to tell the ending") + '</div></div>') +
|
|
260
264
|
(st.home ? '<p class="fine">Lives in ' + esc(st.home) + (st.role ? ' · ' + esc(st.role) : '') + '</p>' : '') +
|
|
261
265
|
(st.point || st.people || st.facts ? '<details><summary>What it shows, who is in it, and the facts that must not change</summary>' + (st.point ? '<p><b>What it shows.</b> ' + esc(st.point) + '</p>' : '') + (st.people ? '<p><b>Who is in it.</b> ' + esc(st.people) + '</p>' : '') + (st.facts ? '<p><b>Facts that must not change.</b> ' + esc(st.facts) + '</p>' : '') + '</details>' : '') +
|
|
262
266
|
acts(st.ref) + '</article>'; };
|
|
263
|
-
const need = ({c, s}) => { const say =
|
|
267
|
+
const need = ({c, s}) => { const say = storySay(s);
|
|
264
268
|
return '<li class="witem need"><div class="wmeta"><span class="chip amber">Needs a real story</span><span class="fine">' + esc(c.head) + ', section ' + esc(s.num) + '</span></div><p class="ask">' + esc(s.title) + '</p><p class="muted">' + esc(s.purpose || "") + '</p><div><button class="btn ghost" data-act="prompt" data-i="' + regP(say) + '" id="needcopy-' + domId(s.num) + '">' + COPYICON + 'Copy a prompt for this story</button></div></li>'; };
|
|
265
|
-
return '<div class="pagehead"><div class="chips">' + (list.length ? '<span class="chip green num">' + n(list.length) + (list.length === 1 ? ' story' : ' stories') + '</span><span class="chip num">' + n(told) + ' told in the book</span>' : '') + (needs.length ? '<span class="chip amber num">' + n(needs.length) + ' still needed</span>' : '') + (stories ? statusChip(stories) : '') + (mode === "snapshot" ? '<span class="chip">Saved copy</span>' : '') + '</div><h1>Stories</h1><p class="sub">The real stories your book is built on, in your words.</p></div>' +
|
|
269
|
+
return '<div class="pagehead"><div class="chips">' + (list.length ? '<span class="chip green num">' + n(list.length) + (list.length === 1 ? ' story' : ' stories') + '</span><span class="chip num">' + n(told) + ' told in the book</span>' : '') + (needs.length ? '<span class="chip amber num">' + n(needs.length) + ' still needed</span>' : '') + (stories ? statusChip(stories) + yesBtn(list.length && stories.status !== "confirmed", "story bank", "stories") : '') + (mode === "snapshot" ? '<span class="chip">Saved copy</span>' : '') + '</div><h1>Stories</h1><p class="sub">The real stories your book is built on, in your words.</p></div>' +
|
|
266
270
|
tip('Each card is a story you told. Press <b>Edit</b> to fix a title, a date, or what happened. To add to a story, tell your ghostwriter in your conversation. Your ghostwriter never invents a detail, so a gap here is a question for you. ' + HELPLINE) +
|
|
267
271
|
(list.length ? '<div class="storygrid">' + list.map(card).join("") + '</div>' : '') +
|
|
268
272
|
(needs.length ? '<section class="block"><div class="head"><h2>Sections still waiting for a story</h2></div><p class="muted waithelp">These parts of the outline need something that really happened to you. Gaps are fine; you do not have to fill them all today.</p><ul class="wlist">' + needs.map(need).join("") + '</ul></section>' : '');
|
|
@@ -292,24 +296,24 @@ function saveEdit(){
|
|
|
292
296
|
const from = e.from; closeEdit(); applyEdit(it, changed, from);
|
|
293
297
|
}
|
|
294
298
|
function editMessage(it, changed){
|
|
295
|
-
return "I edited something on my " + it.page + " page.\n\n" + (it.fields.length > 1 || it.label !== changed[0].f.label ? it.label + "\n" : "") + changed.map(x => x.f.label + " should now read: " + x.v).join("\n\n") + "\n\nPlease update my book to match, then tell me what you changed.";
|
|
299
|
+
return (changed.every(x => x.by === "suggestion") ? "I chose a suggestion on my " : "I edited something on my ") + it.page + " page.\n\n" + (it.fields.length > 1 || it.label !== changed[0].f.label ? it.label + "\n" : "") + changed.map(x => x.f.label + " should now read: " + x.v).join("\n\n") + "\n\nPlease update my book to match, then tell me what you changed.";
|
|
296
300
|
}
|
|
297
301
|
async function applyEdit(it, changed, from){
|
|
298
302
|
const at = new Date().toISOString(), msg = editMessage(it, changed), copied = db ? null : copyText(msg);
|
|
299
303
|
let saved = !!db;
|
|
300
304
|
if (db) { try { for (const x of changed) {
|
|
301
305
|
if (x.v === x.f.base) await db.doc("inbox/" + docId("edit", x.f.ref)).delete();
|
|
302
|
-
else await db.doc("inbox/" + docId("edit", x.f.ref)).set({type: "edit", ref: x.f.ref, page: it.page, item: it.label, label: x.f.label, value: x.v, was: x.f.base, at});
|
|
306
|
+
else await db.doc("inbox/" + docId("edit", x.f.ref)).set(Object.assign({type: "edit", ref: x.f.ref, page: it.page, item: it.label, label: x.f.label, value: x.v, was: x.f.base, at}, x.by ? {by: x.by} : {}));
|
|
303
307
|
} } catch (ex) { saved = false; } }
|
|
304
308
|
const next = Object.assign({}, over);
|
|
305
309
|
for (const x of changed) {
|
|
306
310
|
if (x.v === x.f.base) delete next[x.f.ref];
|
|
307
|
-
else next[x.f.ref] = {ref: x.f.ref, value: x.v, was: x.f.base, label: x.f.label, page: it.page, at, local: !saved};
|
|
311
|
+
else next[x.f.ref] = Object.assign({ref: x.f.ref, value: x.v, was: x.f.base, label: x.f.label, page: it.page, at, local: !saved}, x.by ? {by: x.by} : {});
|
|
308
312
|
}
|
|
309
313
|
over = next; render();
|
|
310
314
|
if (saved) {
|
|
311
315
|
toast("Saved.");
|
|
312
|
-
addLog((it.shown || it.label) + ": " + changed.map(x => (it.fields.length > 1 ? x.f.label + " " : "") + 'changed to "' + clip(x.v, 90) + '"').join("; "), it.page);
|
|
316
|
+
addLog((it.shown || it.label) + ": " + changed.map(x => (it.fields.length > 1 ? x.f.label + " " : "") + (x.by === "suggestion" ? 'a suggestion chosen: "' : 'changed to "') + clip(x.v, 90) + '"').join("; "), it.page);
|
|
313
317
|
} else { const ok = copied ? await copied : await copyText(msg); openCopy(msg, ok, from, "unsaved"); }
|
|
314
318
|
}
|
|
315
319
|
|
|
@@ -329,7 +333,7 @@ async function suggest(kind){
|
|
|
329
333
|
} catch (e) { sug = {ref, kind, busy: false, lines: [], err: e && e.code === "not_granted" ? "That needs your permission to use Claude from this page." : "No suggestions just now. Try again, or ask your ghostwriter in your conversation."}; }
|
|
330
334
|
render();
|
|
331
335
|
}
|
|
332
|
-
function useSug(i){ const s = sug; if (!s) return; const it = ITEMS[s.ref], f = it && it.fields && it.fields[0], v = s.lines[i]; sug = null; if (f && v && v !== f.value) applyEdit(it, [{f, v}], "ie-" + domId(s.ref)); else render(); }
|
|
336
|
+
function useSug(i){ const s = sug; if (!s) return; const it = ITEMS[s.ref], f = it && it.fields && it.fields[0], v = s.lines[i]; sug = null; if (f && v && v !== f.value) applyEdit(it, [{f, v, by: "suggestion"}], "ie-" + domId(s.ref)); else render(); }
|
|
333
337
|
function setHelp(on){ helpOn = !!on; document.body.classList.toggle("helpon", helpOn); const b = document.getElementById("helpbtn"); if (b) b.setAttribute("aria-pressed", String(helpOn)); try { localStorage.setItem("gw-help", helpOn ? "1" : ""); } catch (e) {} }
|
|
334
338
|
|
|
335
339
|
function kindOf(x){ return KINDS.some(k => k[0] === x.kind) ? x.kind : "idea"; }
|
|
@@ -496,12 +500,12 @@ function outlinePage(){
|
|
|
496
500
|
reg({ref, page: "Outline", label: "Section " + s.num + ": " + s.title, shown: f[0].value, what: 'section ' + s.num + ', "' + f[0].value + '"', text: f[1].value + ((s.beats || []).length ? "\n\nIt has to cover:\n- " + s.beats.join("\n- ") : ""), fields: f});
|
|
497
501
|
return {ref, title: f[0].value, purpose: f[1].value, changed: f.some(x => over[x.ref])}; };
|
|
498
502
|
const changedTag = '<span class="edtag">Changed by you</span>';
|
|
499
|
-
return '<div class="pagehead"><div class="chips">' + (o.confirmed ? '<span class="chip green">Confirmed ' + esc(o.confirmed) + '</span>' : '<span class="chip amber">Draft, waiting for your yes</span>') + (mode === "snapshot" ? '<span class="chip">Saved copy</span>' : '') + '</div><h1>Outline</h1><p class="fine">The plan for the book, chapter by chapter. Sections are numbered so you can point to one in your conversation.</p></div>' +
|
|
503
|
+
return '<div class="pagehead"><div class="chips">' + (o.confirmed ? '<span class="chip green">Confirmed ' + esc(o.confirmed) + '</span>' : '<span class="chip amber">Draft, waiting for your yes</span>' + yesBtn(true, "outline", "outline")) + (mode === "snapshot" ? '<span class="chip">Saved copy</span>' : '') + '</div><h1>Outline</h1><p class="fine">The plan for the book, chapter by chapter. Sections are numbered so you can point to one in your conversation.</p></div>' +
|
|
500
504
|
tip('This is the order your book argues in. Press <b>Edit</b> on a chapter or a section to change its title or what it is for. Each section has a number, like 1.2, that you can use with your ghostwriter: "look at 1.2". To add, drop, or move a chapter, tell your ghostwriter in your conversation. ' + HELPLINE) +
|
|
501
505
|
'<div class="block"><div class="label green">The claim this book makes</div><p class="thesis">' + esc(o.thesis) + '</p><p class="fine">To change this, go to <a href="' + href("setup") + '">Book Setup</a>.</p></div>' +
|
|
502
506
|
'<div class="facts">' + fact("Written for", o.audience) + fact("The promise", o.promise) + fact("Every chapter carries", o.device) + fact("Length", o.length) + fact("Point of view", o.pointOfView) + '</div>' +
|
|
503
507
|
m.chs.map(c => { const x = chapter(c); return '<div class="och"><div class="top"><h2>' + tagSpan(c) + esc(x.title) + '</h2><div class="chips">' + (x.changed ? changedTag : '') + '<span class="chip' + (c.words ? ' green' : '') + '">' + (c.words ? esc(c.status || "Drafting") : 'Not started') + '</span></div></div><p class="brief">' + esc(x.brief) + '</p><p class="fine">' + (c.leaves ? esc(c.leaves) + ' · ' : '') + 'About ' + n(c.target) + ' words</p>' + acts(x.ref) + '<div>' +
|
|
504
|
-
c.secs.filter(s => !s.implicit).map(s => { const y = section(s); return '<div class="osec"><div class="n num">' + esc(s.num) + '</div><div class="body"><h3>' + esc(y.title) + '</h3><p class="muted">' + esc(y.purpose) + '</p><details><summary>What it has to cover</summary><ul>' + (s.beats || []).map(b => '<li>' + esc(b) + '</li>').join("") + '</ul></details><div class="row">' + (s.story ? '<span class="chip">Story: ' + esc(s.story) + '</span>' : s.needsStory ? '<span class="chip amber">Needs a real story</span>' : '') + '<span class="fine">' + STATUS[s.state] + '</span>' + (y.changed ? changedTag : '') + '</div>' + acts(y.ref) + '</div></div>'; }).join("") + '</div></div>'; }).join("");
|
|
508
|
+
c.secs.filter(s => !s.implicit).map(s => { const y = section(s); return '<div class="osec"><div class="n num">' + esc(s.num) + '</div><div class="body"><h3>' + esc(y.title) + '</h3><p class="muted">' + esc(y.purpose) + '</p><details><summary>What it has to cover</summary><ul>' + (s.beats || []).map(b => '<li>' + esc(b) + '</li>').join("") + '</ul></details><div class="row">' + (s.story ? '<span class="chip">Story: ' + esc(s.story) + '</span>' : s.needsStory ? '<span class="chip amber">Needs a real story</span>' : '') + '<span class="fine">' + STATUS[s.state] + '</span>' + (y.changed ? changedTag : '') + '</div>' + acts(y.ref, !s.story && s.needsStory ? copyBtn(storySay({num: s.num, title: y.title, purpose: y.purpose}), "ostory-" + domId(s.num), "Copy a prompt for this story") : '') + '</div></div>'; }).join("") + '</div></div>'; }).join("");
|
|
505
509
|
}
|
|
506
510
|
|
|
507
511
|
/* Prose travels as plain lines: a paragraph, "> " a quoted line, "- " a list line, "### " a small heading, "[STORY NEEDED: ...]" a gap. */
|
|
@@ -527,15 +531,17 @@ function manuscriptPage(){
|
|
|
527
531
|
reg({ref, page: "Manuscript", label: cur.head + (implicit ? "" : ", section " + (/^extra-/.test(num) ? "" : num + " ") + '"' + title + '"'), shown: implicit ? cur.title : (/^extra-/.test(num) ? "" : num + " ") + title, wide: true,
|
|
528
532
|
hint: "Saved here straight away. Your ghostwriter keeps your words exactly as you leave them, the next time you write in your conversation. A line in [SQUARE BRACKETS] is a gap waiting for you: replace it with what really happened, or leave it.",
|
|
529
533
|
fields: [{ref, label: "Text", shown: "The words of this section. Leave a blank line between paragraphs.", value, base, long: true, rows: 18, max: 60000}]});
|
|
530
|
-
|
|
534
|
+
const where = cur.head + (implicit || /^extra-/.test(num) ? (implicit ? "" : ', "' + title + '"') : ", section " + num + ', "' + title + '"');
|
|
535
|
+
return {ref, html: blocks(over[ref] ? {blocks: proseBlocks(value)} : t, where, ref) + '<div class="secdisc">' + edTag(ref) + acts(ref) + '</div>'}; };
|
|
531
536
|
const dot = k => '<span class="dot ' + k + '" aria-hidden="true"></span>';
|
|
532
|
-
const
|
|
537
|
+
const gapSay = (b, where) => { const fact = /fact/.test(b.label || ""); return "For my book: " + where + " has a gap waiting on " + (fact ? "a fact" : "a real story") + " from me.\n\nThe gap: " + b.text + "\n\n" + (fact ? "Here is the fact:" : "Here is what really happened:"); };
|
|
538
|
+
const blocks = (t, where, ref) => t.blocks.map((b, i) => b.t === "note" ? '<div class="note"><b>' + esc(b.label) + '</b>' + esc(b.text) + (where ? '<div>' + copyBtn(gapSay(b, where), "gap-" + domId(ref) + "-" + i, "Copy a prompt to fill this", "ibtn") + '</div>' : '') + '</div>' : b.t === "quote" ? '<blockquote>' + rich(b.text) + '</blockquote>' : b.t === "li" ? '<p class="li">' + rich(b.text) + '</p>' : b.t === "sub" ? '<h5>' + rich(b.text) + '</h5>' : '<p>' + rich(b.text) + '</p>').join("");
|
|
533
539
|
return '<div class="pagehead"><h1>Manuscript</h1><p class="fine num">' + n(m.words) + ' of ' + n(m.target) + ' words · ' + n(m.started) + ' of ' + n(m.sections) + ' sections started · ' + n(m.gaps) + ' gaps left for you</p><div class="legend"><span>' + dot("written") + 'Written</span><span>' + dot("gaps") + 'Written, with gaps for you</span><span>' + dot("missing") + 'Not written yet</span></div></div>' +
|
|
534
540
|
tip('This is your book as it reads today. Amber boxes are gaps only you can fill. ' + 'Press <b>Edit</b> under a section to change its words yourself; your ghostwriter keeps your changes exactly. Each section has a number, like 1.2, that you can use in your conversation: "tighten 1.2". ' + HELPLINE) +
|
|
535
541
|
(doc ? '<div class="doclink"><div><div class="label green">Your manuscript document</div><div class="muted">Edit the chapters like any document. It also saves as Word or PDF.</div></div><a class="btn" href="' + esc(doc) + '" target="_blank" rel="noopener">Open your manuscript</a></div>' : '') +
|
|
536
542
|
'<div class="ms"><aside class="toc" aria-label="Chapters">' + m.chs.map(c => '<div class="tocch"><button data-act="chapter" data-k="' + esc(c.key) + '" id="toc-' + esc(c.key) + '" aria-pressed="' + (c.key === cur.key) + '">' + tagSpan(c) + esc(c.title) + '</button>' + c.secs.map(s => '<div class="tocsec">' + dot(s.state) + '<span>' + (s.implicit ? '' : '<span class="num">' + esc(s.num) + '</span> ' + esc(s.title) + ' · ') + '<span class="num">' + (s.words ? n(s.words) + ' words' : 'not yet') + '</span></span></div>').join("") + '</div>').join("") + '</aside>' +
|
|
537
543
|
'<article class="sheet"><div class="ch">' + esc(cur.head) + '</div><h3>' + esc(cur.title) + '</h3>' +
|
|
538
|
-
cur.secs.map(s => (s.implicit ? '' : '<h4><span class="snum num">' + esc(s.num) + '</span>' + esc(s.title) + '</h4>') + (s.text && s.words ? sec(s.text, s.num, s.title, s.implicit).html : '<div class="unwritten"><b>Not written yet</b>' + esc(s.purpose) + (s.needsStory ? ' Waiting on a real story from you.' : s.story ? ' Its story is ready: ' + esc(s.story) + '.' : '') + '</div>')).join("") +
|
|
544
|
+
cur.secs.map(s => (s.implicit ? '' : '<h4><span class="snum num">' + esc(s.num) + '</span>' + esc(s.title) + '</h4>') + (s.text && s.words ? sec(s.text, s.num, s.title, s.implicit).html : '<div class="unwritten"><b>Not written yet</b>' + esc(s.purpose) + (s.needsStory && !s.story ? ' Waiting on a real story from you.<div>' + copyBtn(storySay(s), "mstory-" + domId(s.num), "Copy a prompt for this story", "ibtn") + '</div>' : s.story ? ' Its story is ready: ' + esc(s.story) + '.' : '') + '</div>')).join("") +
|
|
539
545
|
cur.extra.map(t => '<div class="device"><h4>' + esc(t.title) + '</h4>' + sec(t, t.num, t.title, false).html + '</div>').join("") +
|
|
540
546
|
'</article></div>';
|
|
541
547
|
}
|
package/package.json
CHANGED