④段整套同步(六节 + P/S/F/J + 模板)+ open-design 移到技能根
一、④段整套同步(用户选候选 A)
- 新增 `references/stage-proto-doc/assets/产品文档模板.html`(16 KB/247 行,从备份拷、转 LF)。
- ④段 SKILL.md 换成备份 15:28 那版,并按模板口径改齐:
· 「产品文档框架」从「四块+一附录」改成**六节** —— 页面结构/功能清单/使用场景/操作流程/
跨页与界面状态/待确认与偏离;与模板同构,⛔ 不从空白页重写结构。
· 编号从 R/S/F 改成 **P 页面/S 场景/F 功能/J 二级功能**,各自唯一、按展示顺序通排。
· 保留旧版有价值的要求:每项功能补齐 做什么/入口在哪/输入边界/异常与恢复;重点功能三处标红;
横向流程图每步写清页面/功能/操作/完成判据。
· 完成标准「七节」→「六节」,自查清单同步换掉「需求」那批条目,补模板与配图两条。
· 路径 `designs/<项目>/` → `docs/pm/<项目>/ui/`(2 处)。
· 补回本机那条「每份产出文档开头都要有一张图」的引用(指向主 skill,段内不复述)。
- 主 skill 同步:四段结构表④行、④段沿革、画图小节(④段画页面结构图 + 用户路径)。
二、open-design 移到技能根(用户选候选 A)
- `git mv product-planning/references/stage-delivery/assets/open-design → open-design/`(4178 文件)。
- `.gitignore` 白名单加 `!/open-design/` —— 否则移出去就不再纳入版本控制。
- 引用改 11 处:路径形态 `assets/open-design/...` → `<技能根>/open-design/...`;
「随段携带的自带件」改称「同根技能」;「路径怎么找」那条改成自带件(段内相对路径)与
同根技能(`<技能根>/<技能名>/`)两类分开写。
- open-design 自己的 ATTRIBUTION.md 两处同步(它现在与 oil-ui-pro 同层)。
- 副作用(有益):命名闸门读数由 2063 降到 225 —— 此前它把 open-design 里 919 个第三方 md
当成规则一起扫了;现在只扫本包 23 个 md。已确认④段的 H1 与 4a/4b/4c 子步仍在检查范围内。
三、验收
- check_naming.py --ws E:/ProgramData/AIProject/contentm_agent = 通过 225 / 失败 0 / 提示 27。
- 活文件里 `assets/open-design` 残留 0;工作区 CRLF 文件 0。
This commit is contained in:
1 parent
fa0a9026c8
commit
5afeb6b78a
4184 files changed
+326
-70
No files matched your search
@@ -0,0 +1,761 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Mira's First Month · A quiet panel</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Inter:wght@400;500;600&family=Caveat:wght@500;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
/* Soft, warm, parental */
|
||||
--bg: #fdf9f5;
|
||||
--paper: #ffffff;
|
||||
--paper-tint: #faf5ef;
|
||||
--paper-deep: #f4ece2;
|
||||
--line: #ece2d5;
|
||||
--line-strong: #d8c8b3;
|
||||
--hairline: rgba(60,40,20,0.06);
|
||||
--ink: #2a2520;
|
||||
--ink-soft: #4d433b;
|
||||
--muted: #8a7e72;
|
||||
--muted-2: #b3a89a;
|
||||
|
||||
/* Soft pastel accents */
|
||||
--peach: #f5b885;
|
||||
--peach-soft: #fce4cf;
|
||||
--sage: #9ec79e;
|
||||
--sage-soft: #e0eddc;
|
||||
--lavender: #c5b8e0;
|
||||
--lavender-soft: #e8e1f5;
|
||||
--sky: #b3d4e8;
|
||||
--sky-soft: #def0fa;
|
||||
--rose: #e8b3c2;
|
||||
--rose-soft: #fadfe6;
|
||||
--gold: #e8d094;
|
||||
--gold-soft: #faf1d6;
|
||||
|
||||
/* Type */
|
||||
--serif: 'Instrument Serif', Iowan, Charter, Georgia, serif;
|
||||
--display: 'Instrument Serif', Iowan, Charter, Georgia, serif;
|
||||
--body: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
--hand: 'Caveat', 'Instrument Serif', cursive;
|
||||
|
||||
--r: 16px; /* softer corners */
|
||||
--r-pill: 999px;
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; }
|
||||
html, body { margin: 0; padding: 0; }
|
||||
body {
|
||||
background:
|
||||
radial-gradient(800px 400px at 100% -10%, rgba(245,184,133,0.15), transparent 60%),
|
||||
radial-gradient(700px 400px at 0% 60%, rgba(197,184,224,0.12), transparent 60%),
|
||||
radial-gradient(900px 500px at 50% 110%, rgba(158,199,158,0.10), transparent 60%),
|
||||
var(--bg);
|
||||
color: var(--ink);
|
||||
font-family: var(--body);
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
text-rendering: optimizeLegibility;
|
||||
min-height: 100vh;
|
||||
}
|
||||
a { color: inherit; text-decoration: none; }
|
||||
button { font-family: inherit; cursor: pointer; }
|
||||
|
||||
/* ─────── Layout ─────── */
|
||||
main {
|
||||
max-width: 880px; margin: 0 auto;
|
||||
padding: 60px 32px 80px;
|
||||
}
|
||||
|
||||
header.app {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
padding: 22px 32px;
|
||||
border-bottom: 1px solid var(--hairline);
|
||||
}
|
||||
.word-mark {
|
||||
font-family: var(--serif); font-style: italic;
|
||||
font-size: 19px; letter-spacing: -0.005em;
|
||||
color: var(--ink);
|
||||
}
|
||||
.word-mark .small {
|
||||
font-family: var(--body); font-style: normal;
|
||||
font-size: 11px; color: var(--muted);
|
||||
text-transform: uppercase; letter-spacing: 0.16em;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.icon-btn {
|
||||
padding: 7px 14px; font-size: 12.5px;
|
||||
border-radius: var(--r-pill);
|
||||
background: var(--paper); border: 1px solid var(--line);
|
||||
color: var(--ink-soft); transition: all 0.15s;
|
||||
display: inline-flex; gap: 8px; align-items: center;
|
||||
}
|
||||
.icon-btn:hover { border-color: var(--line-strong); color: var(--ink); }
|
||||
.icon-btn.warm {
|
||||
background: linear-gradient(180deg, #fef4e6, var(--paper));
|
||||
border-color: rgba(245,184,133,0.45);
|
||||
color: #8a5a2a;
|
||||
}
|
||||
.icon-btn .ic { width: 13px; height: 13px; }
|
||||
.icon-btn.spin .ic { animation: spin 0.8s linear; }
|
||||
@keyframes spin { to { transform: rotate(360deg); } }
|
||||
|
||||
/* ─────── Hero ─────── */
|
||||
.hero {
|
||||
text-align: center;
|
||||
margin-bottom: 56px;
|
||||
}
|
||||
.hero .pre {
|
||||
font-size: 11px; color: var(--muted); text-transform: uppercase;
|
||||
letter-spacing: 0.2em; margin-bottom: 18px;
|
||||
}
|
||||
.hero .name {
|
||||
font-family: var(--serif); font-weight: 400;
|
||||
font-size: 80px; line-height: 1.05;
|
||||
margin: 0; letter-spacing: -0.02em;
|
||||
color: var(--ink);
|
||||
}
|
||||
.hero .name em {
|
||||
font-style: italic;
|
||||
background: linear-gradient(180deg, #f5b885 0%, #c5b8e0 100%);
|
||||
-webkit-background-clip: text; background-clip: text; color: transparent;
|
||||
}
|
||||
.hero .age {
|
||||
font-family: var(--hand);
|
||||
font-size: 32px; line-height: 1;
|
||||
color: var(--peach);
|
||||
margin-top: 14px;
|
||||
transform: rotate(-2deg);
|
||||
display: inline-block;
|
||||
}
|
||||
.hero .meta {
|
||||
margin-top: 22px;
|
||||
display: inline-flex; gap: 18px;
|
||||
font-size: 13px; color: var(--muted);
|
||||
}
|
||||
.hero .meta .v { color: var(--ink); }
|
||||
.hero .meta .sep { color: var(--muted-2); }
|
||||
|
||||
/* ─────── Section ─────── */
|
||||
section.block {
|
||||
margin-bottom: 44px;
|
||||
}
|
||||
.block-head {
|
||||
display: flex; align-items: baseline; justify-content: space-between;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
.block-head h2 {
|
||||
font-family: var(--serif); font-weight: 400; font-size: 30px;
|
||||
margin: 0; line-height: 1; letter-spacing: -0.005em;
|
||||
color: var(--ink);
|
||||
}
|
||||
.block-head h2 em {
|
||||
font-style: italic; color: var(--peach);
|
||||
margin-right: 8px; font-size: 21px; vertical-align: 4px;
|
||||
}
|
||||
.block-head .meta {
|
||||
font-size: 11.5px; color: var(--muted);
|
||||
text-transform: uppercase; letter-spacing: 0.14em;
|
||||
}
|
||||
.block-head .meta .src { color: var(--ink-soft); margin-right: 4px; }
|
||||
|
||||
.card {
|
||||
background: var(--paper);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--r);
|
||||
padding: 28px;
|
||||
box-shadow: 0 2px 0 rgba(60,40,20,0.02);
|
||||
}
|
||||
|
||||
/* ─────── Sleep circle ─────── */
|
||||
.sleep-card {
|
||||
display: grid; grid-template-columns: 280px 1fr; gap: 28px; align-items: center;
|
||||
}
|
||||
.sleep-circle {
|
||||
width: 280px; height: 280px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.sleep-circle svg { width: 100%; height: 100%; display: block; }
|
||||
.sleep-stat .lbl {
|
||||
font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.14em;
|
||||
}
|
||||
.sleep-stat .big {
|
||||
font-family: var(--serif); font-weight: 400; font-size: 56px;
|
||||
line-height: 1; margin-top: 8px; letter-spacing: -0.02em;
|
||||
}
|
||||
.sleep-stat .delta {
|
||||
margin-top: 10px; font-size: 13px; color: var(--sage);
|
||||
}
|
||||
.sleep-stat .delta strong { color: var(--ink); font-weight: 600; }
|
||||
.sleep-stat .quote {
|
||||
font-family: var(--serif); font-style: italic;
|
||||
font-size: 17px; color: var(--ink-soft);
|
||||
margin-top: 22px; padding-left: 14px;
|
||||
border-left: 2px solid var(--lavender);
|
||||
line-height: 1.5;
|
||||
max-width: 36ch;
|
||||
}
|
||||
|
||||
/* ─────── Feeding timeline ─────── */
|
||||
.feed-timeline {
|
||||
position: relative; padding: 28px 0 32px;
|
||||
}
|
||||
.feed-line {
|
||||
height: 2px;
|
||||
background: linear-gradient(90deg, var(--lavender), var(--peach), var(--sage));
|
||||
border-radius: 2px;
|
||||
position: relative;
|
||||
margin: 0 14px;
|
||||
}
|
||||
.feed-marker {
|
||||
position: absolute; top: 50%; transform: translate(-50%, -50%);
|
||||
width: 14px; height: 14px; border-radius: 50%;
|
||||
cursor: pointer;
|
||||
transition: transform 0.15s;
|
||||
}
|
||||
.feed-marker:hover { transform: translate(-50%, -50%) scale(1.4); }
|
||||
.feed-marker.bottle { background: var(--peach); box-shadow: 0 0 0 4px var(--peach-soft); }
|
||||
.feed-marker.breast { background: var(--rose); box-shadow: 0 0 0 4px var(--rose-soft); }
|
||||
.feed-marker.now {
|
||||
background: var(--ink); box-shadow: 0 0 0 4px var(--paper), 0 0 0 6px var(--ink);
|
||||
animation: nowBreath 2.4s ease-in-out infinite;
|
||||
}
|
||||
@keyframes nowBreath {
|
||||
0%, 100% { transform: translate(-50%, -50%) scale(1); }
|
||||
50% { transform: translate(-50%, -50%) scale(1.15); }
|
||||
}
|
||||
.feed-tip {
|
||||
position: absolute; top: -36px; transform: translate(-50%, 0);
|
||||
background: var(--ink); color: var(--paper);
|
||||
font-size: 11px; padding: 4px 8px; border-radius: 5px;
|
||||
white-space: nowrap;
|
||||
opacity: 0; pointer-events: none; transition: opacity 0.15s;
|
||||
font-family: var(--body);
|
||||
}
|
||||
.feed-tip::after {
|
||||
content: ""; position: absolute; left: 50%; top: 100%; transform: translateX(-50%);
|
||||
border: 4px solid transparent; border-top-color: var(--ink);
|
||||
}
|
||||
.feed-marker:hover .feed-tip { opacity: 1; }
|
||||
.feed-axis {
|
||||
margin-top: 36px; padding: 0 14px;
|
||||
display: grid; grid-template-columns: repeat(7, 1fr);
|
||||
font-family: var(--body); font-size: 11px; color: var(--muted);
|
||||
text-align: center;
|
||||
}
|
||||
.feed-legend {
|
||||
margin-top: 24px;
|
||||
display: flex; gap: 22px; flex-wrap: wrap;
|
||||
font-size: 12px; color: var(--ink-soft);
|
||||
}
|
||||
.feed-legend .swatch { display: inline-flex; gap: 8px; align-items: center; }
|
||||
.feed-legend .swatch::before { content: ""; width: 10px; height: 10px; border-radius: 50%; }
|
||||
.feed-legend .swatch.bottle::before { background: var(--peach); box-shadow: 0 0 0 3px var(--peach-soft); }
|
||||
.feed-legend .swatch.breast::before { background: var(--rose); box-shadow: 0 0 0 3px var(--rose-soft); }
|
||||
.feed-legend .swatch.now::before { background: var(--ink); }
|
||||
.feed-legend .v { font-family: var(--serif); font-style: italic; color: var(--ink); }
|
||||
|
||||
/* ─────── Weight curve ─────── */
|
||||
.growth-card { padding: 28px 32px; }
|
||||
.growth-card svg { width: 100%; height: 220px; display: block; margin-top: 8px; }
|
||||
.growth-stats {
|
||||
display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px;
|
||||
margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--hairline);
|
||||
}
|
||||
.growth-stats .stat .lbl { font-size: 10.5px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.14em; }
|
||||
.growth-stats .stat .v { font-family: var(--serif); font-size: 24px; line-height: 1; margin-top: 6px; letter-spacing: -0.01em; }
|
||||
.growth-stats .stat .pctile { color: var(--sage); font-size: 11.5px; margin-top: 4px; }
|
||||
|
||||
/* ─────── Today reminders ─────── */
|
||||
.reminders {
|
||||
display: grid; grid-template-columns: 1fr 1fr; gap: 14px;
|
||||
}
|
||||
.rem-card {
|
||||
padding: 18px 20px; border-radius: 12px;
|
||||
border: 1px solid var(--line);
|
||||
background: var(--paper);
|
||||
display: flex; flex-direction: column; gap: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
.rem-card:hover { transform: translateY(-2px); box-shadow: 0 6px 18px rgba(60,40,20,0.06); }
|
||||
.rem-card .l-row { display: flex; gap: 10px; align-items: center; color: var(--muted); font-size: 11.5px; text-transform: uppercase; letter-spacing: 0.14em; }
|
||||
.rem-card .glyph {
|
||||
width: 28px; height: 28px; border-radius: 50%;
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
font-size: 14px;
|
||||
}
|
||||
.rem-card.feed .glyph { background: var(--peach-soft); color: #8a5a2a; }
|
||||
.rem-card.nap .glyph { background: var(--lavender-soft); color: #5e4d8e; }
|
||||
.rem-card.appt .glyph { background: var(--sky-soft); color: #2a5e7a; }
|
||||
.rem-card.bath .glyph { background: var(--sage-soft); color: #4a6e4a; }
|
||||
.rem-card .when {
|
||||
font-family: var(--serif); font-size: 28px; line-height: 1; letter-spacing: -0.01em;
|
||||
}
|
||||
.rem-card .desc { font-size: 12.5px; color: var(--ink-soft); line-height: 1.45; }
|
||||
|
||||
/* ─────── AI insights ─────── */
|
||||
.insight-card {
|
||||
background: linear-gradient(180deg, var(--lavender-soft) 0%, var(--paper) 100%);
|
||||
border-color: rgba(197,184,224,0.4);
|
||||
padding: 28px 32px;
|
||||
}
|
||||
.insight-card .badge {
|
||||
display: inline-flex; gap: 8px; align-items: center;
|
||||
font-family: var(--body); font-size: 10.5px;
|
||||
text-transform: uppercase; letter-spacing: 0.16em;
|
||||
color: #6e5e9e;
|
||||
}
|
||||
.insight-card .badge .glyph {
|
||||
width: 22px; height: 22px; border-radius: 50%;
|
||||
background: linear-gradient(135deg, var(--lavender), var(--peach));
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
color: #fff; font-size: 12px; font-weight: 600;
|
||||
box-shadow: 0 0 12px rgba(197,184,224,0.4);
|
||||
}
|
||||
.insight-card h3 {
|
||||
font-family: var(--serif); font-weight: 400; font-size: 28px;
|
||||
line-height: 1.2; margin: 12px 0 6px; letter-spacing: -0.01em;
|
||||
}
|
||||
.insight-card h3 em { font-style: italic; color: #6e5e9e; }
|
||||
.insight-card p {
|
||||
color: var(--ink-soft); font-size: 14px; margin: 0;
|
||||
line-height: 1.55; max-width: 56ch;
|
||||
}
|
||||
.insight-list {
|
||||
margin-top: 16px; display: flex; flex-direction: column; gap: 10px;
|
||||
}
|
||||
.insight-list .row {
|
||||
display: grid; grid-template-columns: 22px 1fr; gap: 10px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
.insight-list .check {
|
||||
color: var(--sage); font-family: var(--serif); font-size: 18px; line-height: 1;
|
||||
}
|
||||
.insight-list .text { font-size: 13px; color: var(--ink-soft); line-height: 1.5; }
|
||||
.insight-list .text strong { color: var(--ink); }
|
||||
.insight-list .text .hand {
|
||||
font-family: var(--hand); font-size: 16px; color: var(--peach);
|
||||
transform: rotate(-1deg); display: inline-block; padding: 0 4px;
|
||||
}
|
||||
|
||||
/* ─────── Photo strip (placeholder) ─────── */
|
||||
.photo-strip {
|
||||
display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px;
|
||||
}
|
||||
.photo {
|
||||
aspect-ratio: 4 / 5; border-radius: 10px;
|
||||
background: linear-gradient(135deg, var(--peach-soft), var(--lavender-soft));
|
||||
border: 1px solid var(--line);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
transition: transform 0.15s;
|
||||
}
|
||||
.photo:hover { transform: translateY(-2px) rotate(-1deg); }
|
||||
.photo:nth-child(2) { background: linear-gradient(135deg, var(--rose-soft), var(--sky-soft)); transform: rotate(0.5deg); }
|
||||
.photo:nth-child(3) { background: linear-gradient(135deg, var(--sage-soft), var(--gold-soft)); transform: rotate(-0.5deg); }
|
||||
.photo:nth-child(4) { background: linear-gradient(135deg, var(--gold-soft), var(--peach-soft)); transform: rotate(0.8deg); }
|
||||
.photo .cap {
|
||||
position: absolute; bottom: 8px; left: 10px; right: 10px;
|
||||
font-family: var(--hand); font-size: 16px;
|
||||
color: var(--ink-soft); transform: rotate(-1deg);
|
||||
}
|
||||
.photo .day {
|
||||
position: absolute; top: 8px; right: 10px;
|
||||
font-family: var(--body); font-size: 10.5px; color: var(--muted);
|
||||
background: rgba(255,255,255,0.7); padding: 2px 6px; border-radius: 3px;
|
||||
backdrop-filter: blur(2px);
|
||||
}
|
||||
|
||||
/* Closing line */
|
||||
.closing {
|
||||
text-align: center; margin-top: 40px;
|
||||
font-family: var(--serif); font-style: italic;
|
||||
font-size: 18px; color: var(--muted);
|
||||
line-height: 1.5;
|
||||
}
|
||||
.closing .author {
|
||||
font-family: var(--body); font-style: normal;
|
||||
font-size: 11px; color: var(--muted-2);
|
||||
text-transform: uppercase; letter-spacing: 0.16em;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
/* Toast */
|
||||
.toast-box { position: fixed; bottom: 24px; right: 24px; z-index: 200; display: flex; flex-direction: column; gap: 10px; pointer-events: none; }
|
||||
.toast {
|
||||
pointer-events: auto;
|
||||
min-width: 280px; max-width: 380px;
|
||||
padding: 14px 16px;
|
||||
background: var(--paper);
|
||||
border: 1px solid var(--line);
|
||||
border-left: 3px solid var(--peach);
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 16px 40px rgba(60,40,20,0.10);
|
||||
font-size: 13px;
|
||||
color: var(--ink);
|
||||
animation: toastIn 0.32s cubic-bezier(0.2, 0.8, 0.2, 1);
|
||||
}
|
||||
.toast.lavender { border-left-color: var(--lavender); }
|
||||
.toast.sage { border-left-color: var(--sage); }
|
||||
.toast .t-title { font-family: var(--serif); font-size: 16px; line-height: 1.2; margin-bottom: 4px; }
|
||||
.toast .t-body { color: var(--ink-soft); line-height: 1.45; }
|
||||
.toast .t-body strong { color: var(--ink); }
|
||||
.toast.fade { opacity: 0; transform: translateX(20px); transition: all 0.25s; }
|
||||
@keyframes toastIn { from { opacity: 0; transform: translateX(20px); } to { opacity: 1; transform: translateX(0); } }
|
||||
|
||||
@media (max-width: 720px) {
|
||||
main { padding: 40px 18px 60px; }
|
||||
.hero .name { font-size: 56px; }
|
||||
.sleep-card { grid-template-columns: 1fr; }
|
||||
.sleep-circle { width: 220px; height: 220px; }
|
||||
.reminders { grid-template-columns: 1fr; }
|
||||
.photo-strip { grid-template-columns: repeat(2, 1fr); }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<header class="app">
|
||||
<div class="word-mark">
|
||||
<span class="small">a quiet panel from</span>
|
||||
<em>quiver</em>
|
||||
</div>
|
||||
<button class="icon-btn warm" id="logBtn">
|
||||
<svg class="ic" viewBox="0 0 16 16" fill="none"><path d="M8 3v10M3 8h10" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
|
||||
Log a feed
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
|
||||
<!-- ─────── Hero ─────── -->
|
||||
<div class="hero">
|
||||
<div class="pre">Mira's first month · 2026</div>
|
||||
<h1 class="name">Mira <em>Avery</em></h1>
|
||||
<div class="age">28 days & counting</div>
|
||||
<div class="meta">
|
||||
<span><span class="v">Born</span> April 9 · 2026</span>
|
||||
<span class="sep">·</span>
|
||||
<span><span class="v">3.42 kg</span> · 50.8 cm</span>
|
||||
<span class="sep">·</span>
|
||||
<span><span class="v">Mom & dad</span> have her back</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ─────── 1. Sleep ─────── -->
|
||||
<section class="block">
|
||||
<div class="block-head">
|
||||
<h2><em>i.</em>How she slept last night</h2>
|
||||
<span class="meta"><span class="src">Huckleberry</span>· synced 6:42 AM</span>
|
||||
</div>
|
||||
<div class="card sleep-card">
|
||||
<div class="sleep-circle">
|
||||
<svg viewBox="0 0 200 200">
|
||||
<defs>
|
||||
<radialGradient id="bgG" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#fff" stop-opacity="0.6"/>
|
||||
<stop offset="100%" stop-color="#c5b8e0" stop-opacity="0.06"/>
|
||||
</radialGradient>
|
||||
</defs>
|
||||
<circle cx="100" cy="100" r="92" fill="url(#bgG)" stroke="#ece2d5" stroke-width="0.8"/>
|
||||
<!-- 24h tick marks -->
|
||||
<g stroke="#d8c8b3" stroke-width="0.6">
|
||||
<line x1="100" y1="14" x2="100" y2="20"/>
|
||||
<line x1="186" y1="100" x2="180" y2="100"/>
|
||||
<line x1="100" y1="186" x2="100" y2="180"/>
|
||||
<line x1="14" y1="100" x2="20" y2="100"/>
|
||||
</g>
|
||||
<!-- Hour labels -->
|
||||
<g font-family="Inter,sans-serif" font-size="9" fill="#8a7e72">
|
||||
<text x="100" y="11" text-anchor="middle">12 am</text>
|
||||
<text x="194" y="103" text-anchor="end">6</text>
|
||||
<text x="100" y="197" text-anchor="middle">12 pm</text>
|
||||
<text x="6" y="103">6</text>
|
||||
</g>
|
||||
<!-- Sleep arcs (purple = night, peach = naps) -->
|
||||
<!-- Night sleep 7:30 PM → 6:30 AM (with 4 brief wakes) -->
|
||||
<!-- Stroke arcs at radius 76 -->
|
||||
<path d="M 154.5 145.5 A 76 76 0 0 1 100 24" fill="none" stroke="#c5b8e0" stroke-width="14" stroke-linecap="round" opacity="0.85"/>
|
||||
<path d="M 100 24 A 76 76 0 0 1 134.7 32.5" fill="none" stroke="#c5b8e0" stroke-width="14" stroke-linecap="round" opacity="0.5"/>
|
||||
<!-- 1st morning nap 9:00–10:30 -->
|
||||
<path d="M 162.4 76.6 A 76 76 0 0 1 173.1 105.4" fill="none" stroke="#f5b885" stroke-width="14" stroke-linecap="round" opacity="0.85"/>
|
||||
<!-- afternoon nap 1:30–3:00 -->
|
||||
<path d="M 142.1 168.6 A 76 76 0 0 1 100 176" fill="none" stroke="#f5b885" stroke-width="14" stroke-linecap="round" opacity="0.85"/>
|
||||
<!-- evening nap 6:00–6:45 -->
|
||||
<path d="M 24 100 A 76 76 0 0 1 32.5 65.3" fill="none" stroke="#f5b885" stroke-width="14" stroke-linecap="round" opacity="0.65"/>
|
||||
<!-- Center text -->
|
||||
<text x="100" y="92" text-anchor="middle" font-family="Instrument Serif,serif" font-size="34" fill="#2a2520" letter-spacing="-0.5">14h 22m</text>
|
||||
<text x="100" y="115" text-anchor="middle" font-family="Inter,sans-serif" font-size="10" fill="#8a7e72" letter-spacing="0.6">total · 24h window</text>
|
||||
<!-- now hand -->
|
||||
<line x1="100" y1="100" x2="100" y2="62" stroke="#2a2520" stroke-width="1.5" stroke-linecap="round" transform="rotate(105 100 100)"/>
|
||||
<circle cx="100" cy="100" r="3" fill="#2a2520"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="sleep-stat">
|
||||
<div class="lbl">Last night · 7:30 PM → 6:30 AM</div>
|
||||
<div class="big">9h 14m</div>
|
||||
<div class="delta">3 wakes (down from 4) · longest stretch <strong>3h 12m</strong></div>
|
||||
<div class="quote">"She slept 22 minutes longer per night this week. Not a streak — a rhythm."</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ─────── 2. Feeding ─────── -->
|
||||
<section class="block">
|
||||
<div class="block-head">
|
||||
<h2><em>ii.</em>How she ate today</h2>
|
||||
<span class="meta"><span class="src">Baby Tracker + scale</span>· last 24h</span>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="feed-timeline">
|
||||
<div class="feed-line">
|
||||
<!-- timeline runs midnight → midnight → markers as % -->
|
||||
<div class="feed-marker bottle" style="left:8.3%"><span class="feed-tip">2:00 AM · 90ml bottle</span></div>
|
||||
<div class="feed-marker breast" style="left:21%"><span class="feed-tip">5:00 AM · breast 18 min</span></div>
|
||||
<div class="feed-marker bottle" style="left:33%"><span class="feed-tip">8:00 AM · 110ml bottle</span></div>
|
||||
<div class="feed-marker breast" style="left:46%"><span class="feed-tip">11:00 AM · breast 22 min</span></div>
|
||||
<div class="feed-marker bottle" style="left:58%"><span class="feed-tip">2:00 PM · 100ml bottle</span></div>
|
||||
<div class="feed-marker now" style="left:60.5%"><span class="feed-tip">Now · 14:32</span></div>
|
||||
<div class="feed-marker breast" style="left:71%"><span class="feed-tip">5:00 PM · breast 16 min (est.)</span></div>
|
||||
<div class="feed-marker bottle" style="left:83%"><span class="feed-tip">8:00 PM · 100ml bottle (est.)</span></div>
|
||||
<div class="feed-marker breast" style="left:96%"><span class="feed-tip">11:30 PM · breast (est.)</span></div>
|
||||
</div>
|
||||
<div class="feed-axis">
|
||||
<span>12am</span><span>4am</span><span>8am</span><span>noon</span><span>4pm</span><span>8pm</span><span>12am</span>
|
||||
</div>
|
||||
<div class="feed-legend">
|
||||
<span class="swatch bottle">Bottle <span class="v" style="margin-left:4px;">4 today · 400 ml</span></span>
|
||||
<span class="swatch breast">Breast <span class="v" style="margin-left:4px;">5 today · ~110 min</span></span>
|
||||
<span class="swatch now">Now</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ─────── 3. Weight curve ─────── -->
|
||||
<section class="block">
|
||||
<div class="block-head">
|
||||
<h2><em>iii.</em>How she's growing</h2>
|
||||
<span class="meta"><span class="src">Withings smart scale</span>· weekly</span>
|
||||
</div>
|
||||
<div class="card growth-card">
|
||||
<svg viewBox="0 0 600 220" preserveAspectRatio="none">
|
||||
<defs>
|
||||
<linearGradient id="growthArea" x1="0" x2="0" y1="0" y2="1">
|
||||
<stop offset="0%" stop-color="#9ec79e" stop-opacity="0.28"/>
|
||||
<stop offset="100%" stop-color="#9ec79e" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="bandG" x1="0" x2="0" y1="0" y2="1">
|
||||
<stop offset="0%" stop-color="#c5b8e0" stop-opacity="0.18"/>
|
||||
<stop offset="100%" stop-color="#c5b8e0" stop-opacity="0.04"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<!-- WHO percentile band (10–90) -->
|
||||
<polygon fill="url(#bandG)" points="40,170 120,154 200,138 280,122 360,108 440,96 520,84 580,76 580,140 520,150 440,160 360,168 280,176 200,182 120,188 40,194"/>
|
||||
<!-- 50th percentile dashed line -->
|
||||
<polyline fill="none" stroke="#c5b8e0" stroke-width="1" stroke-dasharray="3 4"
|
||||
points="40,182 120,170 200,158 280,148 360,138 440,128 520,118 580,108"/>
|
||||
<!-- Mira's actual weight -->
|
||||
<polygon fill="url(#growthArea)" points="40,200 40,178 130,170 220,158 310,142 400,124 490,108 580,90 580,200"/>
|
||||
<polyline fill="none" stroke="#9ec79e" stroke-width="2.4" stroke-linejoin="round" stroke-linecap="round"
|
||||
points="40,178 130,170 220,158 310,142 400,124 490,108 580,90"/>
|
||||
<!-- Markers -->
|
||||
<g>
|
||||
<circle cx="40" cy="178" r="3.5" fill="#9ec79e"/>
|
||||
<circle cx="130" cy="170" r="3.5" fill="#9ec79e"/>
|
||||
<circle cx="220" cy="158" r="3.5" fill="#9ec79e"/>
|
||||
<circle cx="310" cy="142" r="3.5" fill="#9ec79e"/>
|
||||
<circle cx="400" cy="124" r="3.5" fill="#9ec79e"/>
|
||||
<circle cx="490" cy="108" r="3.5" fill="#9ec79e"/>
|
||||
<circle cx="580" cy="90" r="5" fill="#9ec79e" stroke="#fff" stroke-width="2"/>
|
||||
</g>
|
||||
<!-- X labels -->
|
||||
<g font-family="Inter,sans-serif" font-size="10" fill="#8a7e72" text-anchor="middle">
|
||||
<text x="40" y="215">birth</text>
|
||||
<text x="130" y="215">wk 1</text>
|
||||
<text x="220" y="215">wk 2</text>
|
||||
<text x="310" y="215">wk 3</text>
|
||||
<text x="400" y="215">wk 4</text>
|
||||
<text x="490" y="215">wk 5</text>
|
||||
<text x="580" y="215" fill="#2a2520" font-weight="600">today</text>
|
||||
</g>
|
||||
<!-- "now" callout -->
|
||||
<text x="572" y="80" text-anchor="end" font-family="Caveat,cursive" font-size="20" fill="#9ec79e" transform="rotate(-3 572 80)">on track ✓</text>
|
||||
<!-- Y axis labels -->
|
||||
<g font-family="Geist Mono,monospace" font-size="9.5" fill="#8a7e72">
|
||||
<text x="6" y="80">5.0</text>
|
||||
<text x="6" y="120">4.5</text>
|
||||
<text x="6" y="160">4.0</text>
|
||||
<text x="6" y="200">3.5</text>
|
||||
</g>
|
||||
</svg>
|
||||
<div class="growth-stats">
|
||||
<div class="stat">
|
||||
<div class="lbl">Weight today</div>
|
||||
<div class="v">4.84 kg</div>
|
||||
<div class="pctile">62nd percentile · WHO</div>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<div class="lbl">Length</div>
|
||||
<div class="v">53.4 cm</div>
|
||||
<div class="pctile">58th percentile · WHO</div>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<div class="lbl">Gain · 7 days</div>
|
||||
<div class="v">+218 g</div>
|
||||
<div class="pctile">healthy range (150–280g)</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ─────── 4. Today's reminders ─────── -->
|
||||
<section class="block">
|
||||
<div class="block-head">
|
||||
<h2><em>iv.</em>Today's gentle reminders</h2>
|
||||
<span class="meta">4 things, no alarms</span>
|
||||
</div>
|
||||
<div class="reminders">
|
||||
<div class="rem-card feed" data-rem="feed">
|
||||
<div class="l-row"><span class="glyph">◍</span> Next feed</div>
|
||||
<div class="when">in ~38 min</div>
|
||||
<div class="desc">Last feed at 2:00 PM (100 ml bottle). Suggested 100–110 ml.</div>
|
||||
</div>
|
||||
<div class="rem-card nap" data-rem="nap">
|
||||
<div class="l-row"><span class="glyph">☾</span> Next likely nap</div>
|
||||
<div class="when">3:30 — 5:00 PM</div>
|
||||
<div class="desc">She tends to fade ~90 min after morning rouse. Recent pattern.</div>
|
||||
</div>
|
||||
<div class="rem-card appt" data-rem="appt">
|
||||
<div class="l-row"><span class="glyph">✓</span> Doctor visit</div>
|
||||
<div class="when">Friday 10:30 AM</div>
|
||||
<div class="desc">1-month checkup with Dr. Wei. We'll bring the weight log.</div>
|
||||
</div>
|
||||
<div class="rem-card bath" data-rem="bath">
|
||||
<div class="l-row"><span class="glyph">~</span> Bath night</div>
|
||||
<div class="when">tonight, 6:30 PM</div>
|
||||
<div class="desc">Every other day · grandma's lullaby works.</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ─────── 5. AI Insight ─────── -->
|
||||
<section class="block">
|
||||
<div class="block-head">
|
||||
<h2><em>v.</em>What we noticed this week</h2>
|
||||
<span class="meta"><span class="src">Quiver</span>· soft patterns</span>
|
||||
</div>
|
||||
<div class="card insight-card">
|
||||
<div class="badge">
|
||||
<span class="glyph">✦</span>
|
||||
Quiver insights · weekly
|
||||
</div>
|
||||
<h3>She's settling into a <em>longer-stretch</em> sleep at night.</h3>
|
||||
<p>Five small patterns matched up this week. None demand action — they just paint a picture.</p>
|
||||
<div class="insight-list">
|
||||
<div class="row"><span class="check">✓</span><span class="text"><strong>Longer first stretch:</strong> first sleep block grew from 2h 14m → 3h 12m on average. <span class="hand">good for you both</span></span></div>
|
||||
<div class="row"><span class="check">✓</span><span class="text"><strong>Feed-then-sleep latency dropped to 14 min</strong> (was 28 min in week 2). She's calmer post-feed.</span></div>
|
||||
<div class="row"><span class="check">✓</span><span class="text"><strong>Bottle preference stabilized</strong> on the slow-flow Dr. Brown's after the size-2 trial. No spit-up since Monday.</span></div>
|
||||
<div class="row"><span class="check">✓</span><span class="text"><strong>62nd percentile</strong> on weight, up from 58th — within healthy range. Length is steady at 58th.</span></div>
|
||||
<div class="row"><span class="check">✓</span><span class="text"><strong>Diaper count averaged 8 per day</strong> (target ≥6). Hydration is comfortable.</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ─────── 6. Photo memory ─────── -->
|
||||
<section class="block">
|
||||
<div class="block-head">
|
||||
<h2><em>vi.</em>This week, in pictures</h2>
|
||||
<span class="meta"><span class="src">iCloud Photos</span>· auto-curated</span>
|
||||
</div>
|
||||
<div class="photo-strip">
|
||||
<div class="photo">
|
||||
<div class="day">Mon</div>
|
||||
<div class="cap">first finger-grip</div>
|
||||
</div>
|
||||
<div class="photo">
|
||||
<div class="day">Wed</div>
|
||||
<div class="cap">sun nap on the porch</div>
|
||||
</div>
|
||||
<div class="photo">
|
||||
<div class="day">Thu</div>
|
||||
<div class="cap">grandma met her</div>
|
||||
</div>
|
||||
<div class="photo">
|
||||
<div class="day">Sat</div>
|
||||
<div class="cap">first real smile (we think)</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="closing">
|
||||
"What you measure, you remember."<br/>
|
||||
<span class="author">— quiver, a quiet panel · auto-refreshes daily at 6:30 AM</span>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
<div class="toast-box" id="toastBox"></div>
|
||||
|
||||
<script>
|
||||
(() => {
|
||||
// Toast
|
||||
const toastBox = document.getElementById('toastBox');
|
||||
function toast(opts) {
|
||||
const { kind = '', title = '', body = '' } = (typeof opts === 'string') ? { body: opts } : opts;
|
||||
const node = document.createElement('div');
|
||||
node.className = `toast ${kind}`;
|
||||
node.innerHTML = `<div class="t-title">${title}</div><div class="t-body">${body}</div>`;
|
||||
toastBox.appendChild(node);
|
||||
setTimeout(() => {
|
||||
node.classList.add('fade');
|
||||
setTimeout(() => node.remove(), 280);
|
||||
}, 4200);
|
||||
}
|
||||
|
||||
// Reminders
|
||||
const remCopy = {
|
||||
feed: { kind: '', title: 'Logged a feed', body: 'Recorded · <strong>110 ml at 3:10 PM</strong> bottle. Updated next-feed estimate to 6:15 PM.' },
|
||||
nap: { kind: 'lavender', title: 'Nap window noted', body: 'We\'ll send a quiet ping at <strong>3:25 PM</strong> if she\'s still awake.' },
|
||||
appt: { kind: 'sage', title: 'Friday checkup', body: 'Calendar event opened · 1-month checkup with Dr. Wei · 10:30 AM.<br/>We\'ll auto-prepare the weight log.' },
|
||||
bath: { kind: '', title: 'Bath time saved', body: 'Tonight at 6:30 PM. We\'ll dim the smart lights at 6:25.' }
|
||||
};
|
||||
document.querySelectorAll('[data-rem]').forEach(r => {
|
||||
r.addEventListener('click', () => {
|
||||
const k = r.dataset.rem;
|
||||
if (remCopy[k]) toast(remCopy[k]);
|
||||
});
|
||||
});
|
||||
|
||||
// Log a feed (header button)
|
||||
document.getElementById('logBtn').addEventListener('click', () => {
|
||||
toast({ kind: 'sage', title: 'New feed logged', body: '<strong>110 ml</strong> bottle · 3:10 PM<br/>Avg gain trend: +218 g this week, on track.' });
|
||||
});
|
||||
|
||||
// Photos
|
||||
document.querySelectorAll('.photo').forEach(p => {
|
||||
p.addEventListener('click', () => {
|
||||
const cap = p.querySelector('.cap').textContent;
|
||||
toast({ kind: 'lavender', title: 'Photo memory', body: `"${cap}" · saved to your Mira album.` });
|
||||
});
|
||||
});
|
||||
|
||||
// Feed marker click (open detail)
|
||||
document.querySelectorAll('.feed-marker').forEach(m => {
|
||||
m.addEventListener('click', e => {
|
||||
const tip = m.querySelector('.feed-tip');
|
||||
if (tip) toast({ kind: '', title: 'Feed entry', body: tip.textContent });
|
||||
});
|
||||
});
|
||||
|
||||
})();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,919 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Monday morning briefing · Quiver</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Inter:wght@400;500;600;700&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
/* Editorial · calm Monday morning */
|
||||
--paper: #f5f1ea; /* warm cream background */
|
||||
--paper-elev: #fdfcf9; /* card surface */
|
||||
--paper-deep: #efe9dd; /* elevation contrast */
|
||||
--ink: #1c1c1f; /* deep near-black */
|
||||
--ink-soft: #3b3b3f;
|
||||
--muted: #6b6b66;
|
||||
--muted-2: #8e8e88;
|
||||
--line: #e3ddd0;
|
||||
--line-strong: #cdc4b1;
|
||||
--hairline: rgba(28,28,31,0.06);
|
||||
|
||||
/* Editorial accent palette — earthy, calm */
|
||||
--espresso: #6f4e37; /* coffee */
|
||||
--sage: #6e8865;
|
||||
--sage-soft: #e7eee2;
|
||||
--slate-blue: #4a6e8a;
|
||||
--slate-soft: #e3ebf2;
|
||||
--terracotta: #c8775c;
|
||||
--tc-soft: #f6e4dd;
|
||||
--gold: #c9a45a;
|
||||
--gold-soft: #f1e7ce;
|
||||
--plum: #6e4e6e;
|
||||
--plum-soft: #ece2ec;
|
||||
|
||||
/* Type */
|
||||
--serif: 'Instrument Serif', Iowan, Charter, Georgia, serif;
|
||||
--display: 'Instrument Serif', Iowan, Charter, Georgia, serif;
|
||||
--body: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
--mono: 'Geist Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
|
||||
--r: 12px;
|
||||
--r-pill: 999px;
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; }
|
||||
html, body { margin: 0; padding: 0; }
|
||||
body {
|
||||
background:
|
||||
/* paper grain — barely-there noise via SVG */
|
||||
url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160' viewBox='0 0 160 160'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0.07 0 0 0 0 0.06 0 0 0 0 0.04 0 0 0 0.06 0'/></filter><rect width='160' height='160' filter='url(%23n)'/></svg>"),
|
||||
var(--paper);
|
||||
color: var(--ink);
|
||||
font-family: var(--body);
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
text-rendering: optimizeLegibility;
|
||||
min-height: 100vh;
|
||||
}
|
||||
a { color: inherit; text-decoration: none; }
|
||||
button { font-family: inherit; cursor: pointer; }
|
||||
|
||||
/* ─────── Header ─────── */
|
||||
header.app {
|
||||
border-bottom: 1px solid var(--line);
|
||||
padding: 22px 32px 18px;
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
background: var(--paper);
|
||||
}
|
||||
.masthead {
|
||||
font-family: var(--serif);
|
||||
font-size: 22px;
|
||||
letter-spacing: -0.01em;
|
||||
line-height: 1;
|
||||
}
|
||||
.masthead .small {
|
||||
font-family: var(--mono); font-size: 10.5px;
|
||||
color: var(--muted); text-transform: uppercase; letter-spacing: 0.16em;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.masthead .word {
|
||||
display: inline-flex; gap: 8px; align-items: baseline;
|
||||
}
|
||||
.masthead .quiver {
|
||||
font-family: var(--serif); font-style: italic;
|
||||
color: var(--espresso);
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.head-right { display: flex; align-items: center; gap: 14px; }
|
||||
.pill-soft {
|
||||
padding: 6px 12px; font-size: 12px;
|
||||
border: 1px solid var(--line); border-radius: var(--r-pill);
|
||||
background: var(--paper-elev); color: var(--ink-soft);
|
||||
font-family: var(--mono); letter-spacing: 0.04em;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
.pill-soft:hover { border-color: var(--line-strong); color: var(--ink); }
|
||||
.pill-soft .dot {
|
||||
display: inline-block; width: 6px; height: 6px; border-radius: 50%;
|
||||
background: var(--sage); margin-right: 8px; vertical-align: 1px;
|
||||
box-shadow: 0 0 6px var(--sage);
|
||||
}
|
||||
.icon-btn {
|
||||
padding: 7px 14px; border-radius: var(--r-pill);
|
||||
background: var(--ink); color: var(--paper);
|
||||
font-size: 12px; font-weight: 500; letter-spacing: 0.01em;
|
||||
border: 1px solid var(--ink);
|
||||
display: inline-flex; gap: 8px; align-items: center;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
.icon-btn:hover { transform: translateY(-1px); }
|
||||
.icon-btn.ghost { background: transparent; color: var(--ink); }
|
||||
.icon-btn .ic { width: 13px; height: 13px; }
|
||||
.icon-btn.spin .ic { animation: spin 0.8s linear; }
|
||||
@keyframes spin { to { transform: rotate(360deg); } }
|
||||
|
||||
/* ─────── Layout ─────── */
|
||||
main {
|
||||
max-width: 1220px; margin: 0 auto;
|
||||
padding: 40px 32px 80px;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 280px;
|
||||
gap: 48px;
|
||||
}
|
||||
|
||||
/* ─────── Hero greeting ─────── */
|
||||
.greeting {
|
||||
margin-bottom: 36px;
|
||||
border-bottom: 1px solid var(--hairline);
|
||||
padding-bottom: 28px;
|
||||
}
|
||||
.greeting .pretitle {
|
||||
font-family: var(--mono);
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.18em;
|
||||
color: var(--muted);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.greeting h1 {
|
||||
font-family: var(--serif);
|
||||
font-weight: 400;
|
||||
font-size: 56px;
|
||||
line-height: 1.05;
|
||||
letter-spacing: -0.015em;
|
||||
margin: 0;
|
||||
color: var(--ink);
|
||||
}
|
||||
.greeting h1 em {
|
||||
font-style: italic; color: var(--espresso);
|
||||
}
|
||||
.greeting .lede {
|
||||
font-family: var(--serif);
|
||||
font-style: italic;
|
||||
font-size: 19px;
|
||||
color: var(--ink-soft);
|
||||
margin-top: 10px;
|
||||
max-width: 64ch;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.greeting .meta-row {
|
||||
margin-top: 18px;
|
||||
display: flex; gap: 24px; flex-wrap: wrap;
|
||||
font-family: var(--mono); font-size: 12px;
|
||||
color: var(--muted);
|
||||
}
|
||||
.greeting .meta-row .v { color: var(--ink); margin-right: 4px; }
|
||||
.greeting .meta-row .sep { color: var(--line-strong); }
|
||||
|
||||
/* ─────── Section style ─────── */
|
||||
section.block {
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
.block > .head {
|
||||
display: flex; align-items: baseline; justify-content: space-between;
|
||||
margin-bottom: 14px;
|
||||
padding-bottom: 6px;
|
||||
border-bottom: 1px solid var(--hairline);
|
||||
}
|
||||
.block > .head h2 {
|
||||
font-family: var(--serif);
|
||||
font-weight: 400;
|
||||
font-size: 26px;
|
||||
letter-spacing: -0.005em;
|
||||
line-height: 1;
|
||||
margin: 0;
|
||||
}
|
||||
.block > .head h2 .ord {
|
||||
font-family: var(--serif); font-style: italic; color: var(--espresso);
|
||||
margin-right: 12px; font-size: 18px; vertical-align: 4px;
|
||||
}
|
||||
.block > .head .meta {
|
||||
font-family: var(--mono); font-size: 11px;
|
||||
text-transform: uppercase; letter-spacing: 0.12em;
|
||||
color: var(--muted);
|
||||
}
|
||||
.block > .head .pill-soft { padding: 4px 10px; font-size: 11px; }
|
||||
|
||||
.card {
|
||||
background: var(--paper-elev);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--r);
|
||||
padding: 20px 22px;
|
||||
box-shadow: 0 1px 0 rgba(28,28,31,0.02);
|
||||
}
|
||||
|
||||
/* ─────── Revenue card ─────── */
|
||||
.rev-card {
|
||||
display: grid;
|
||||
grid-template-columns: 220px 1fr;
|
||||
gap: 28px;
|
||||
align-items: center;
|
||||
padding: 24px 28px;
|
||||
}
|
||||
.rev-stat .label {
|
||||
font-family: var(--mono); font-size: 10.5px;
|
||||
color: var(--muted); text-transform: uppercase; letter-spacing: 0.14em;
|
||||
}
|
||||
.rev-stat .big {
|
||||
font-family: var(--serif);
|
||||
font-weight: 400;
|
||||
font-size: 60px;
|
||||
line-height: 1;
|
||||
letter-spacing: -0.02em;
|
||||
margin-top: 8px;
|
||||
color: var(--ink);
|
||||
}
|
||||
.rev-stat .delta {
|
||||
margin-top: 10px; font-size: 13px;
|
||||
color: var(--sage); font-weight: 500;
|
||||
}
|
||||
.rev-stat .delta .arr { font-family: var(--mono); }
|
||||
.rev-stat .sub { color: var(--muted); font-size: 12px; margin-top: 12px; max-width: 30ch; }
|
||||
|
||||
.rev-chart svg { width: 100%; height: 140px; display: block; }
|
||||
|
||||
/* ─────── Email digest ─────── */
|
||||
.digest-list { display: flex; flex-direction: column; gap: 12px; }
|
||||
.digest-item {
|
||||
background: var(--paper-elev);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--r);
|
||||
padding: 16px 20px;
|
||||
display: grid;
|
||||
grid-template-columns: 38px 1fr auto;
|
||||
gap: 14px;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
.digest-item:hover {
|
||||
border-color: var(--line-strong);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 14px rgba(28,28,31,0.05);
|
||||
}
|
||||
.digest-av {
|
||||
width: 38px; height: 38px; border-radius: 50%;
|
||||
color: var(--paper); font-family: var(--serif); font-size: 16px;
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.digest-meta { font-family: var(--mono); font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.08em; }
|
||||
.digest-from { color: var(--ink); font-weight: 500; }
|
||||
.digest-subject {
|
||||
margin-top: 4px; font-family: var(--serif); font-size: 17px; line-height: 1.35;
|
||||
color: var(--ink);
|
||||
}
|
||||
.digest-snippet {
|
||||
margin-top: 4px; color: var(--muted); font-size: 12.5px;
|
||||
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
||||
max-width: 64ch;
|
||||
}
|
||||
.digest-tag {
|
||||
align-self: center;
|
||||
font-family: var(--mono); font-size: 10px;
|
||||
padding: 3px 9px; border-radius: 4px;
|
||||
text-transform: uppercase; letter-spacing: 0.1em;
|
||||
}
|
||||
.digest-tag.urgent { background: var(--tc-soft); color: var(--terracotta); }
|
||||
.digest-tag.warm { background: var(--gold-soft); color: #8e6f1f; }
|
||||
.digest-tag.calm { background: var(--sage-soft); color: #496938; }
|
||||
.digest-tag.fyi { background: var(--slate-soft); color: var(--slate-blue); }
|
||||
|
||||
/* ─────── Stuck issues ─────── */
|
||||
.stuck-list { display: flex; flex-direction: column; gap: 10px; }
|
||||
.stuck-row {
|
||||
background: var(--paper-elev);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--r);
|
||||
padding: 14px 18px;
|
||||
display: grid; grid-template-columns: 1fr auto auto auto; gap: 18px;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
.stuck-row:hover { border-color: var(--line-strong); }
|
||||
.stuck-row .id {
|
||||
font-family: var(--mono); font-size: 11px;
|
||||
color: var(--muted); margin-bottom: 4px;
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
.stuck-row .title { font-size: 14.5px; color: var(--ink); font-weight: 500; }
|
||||
.stuck-row .who {
|
||||
display: inline-flex; gap: 6px; align-items: center;
|
||||
font-size: 12px; color: var(--muted);
|
||||
}
|
||||
.stuck-row .who .av {
|
||||
width: 22px; height: 22px; border-radius: 50%;
|
||||
color: #fff; font-size: 10px; font-weight: 600;
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
}
|
||||
.stuck-row .age {
|
||||
font-family: var(--mono); font-size: 11.5px;
|
||||
padding: 3px 9px; border-radius: 5px;
|
||||
background: var(--tc-soft); color: var(--terracotta);
|
||||
font-weight: 600;
|
||||
}
|
||||
.stuck-row .age.warm { background: var(--gold-soft); color: #8e6f1f; }
|
||||
.stuck-row .nudge {
|
||||
padding: 5px 11px; font-family: var(--mono); font-size: 11px;
|
||||
border-radius: 6px; border: 1px solid var(--line); background: var(--paper);
|
||||
color: var(--ink-soft); font-weight: 500;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
.stuck-row .nudge:hover { background: var(--ink); color: var(--paper); border-color: var(--ink); }
|
||||
|
||||
/* ─────── Schedule timeline ─────── */
|
||||
.schedule {
|
||||
display: flex; flex-direction: column; gap: 0;
|
||||
padding: 0;
|
||||
background: var(--paper-elev);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--r);
|
||||
overflow: hidden;
|
||||
}
|
||||
.sched-row {
|
||||
display: grid; grid-template-columns: 80px 12px 1fr auto; gap: 16px;
|
||||
align-items: center;
|
||||
padding: 14px 22px;
|
||||
border-bottom: 1px solid var(--hairline);
|
||||
transition: background 0.12s;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
.sched-row:last-child { border-bottom: 0; }
|
||||
.sched-row:hover { background: var(--paper-deep); }
|
||||
.sched-row .time {
|
||||
font-family: var(--mono); font-size: 12px;
|
||||
color: var(--ink); font-weight: 500;
|
||||
}
|
||||
.sched-row .time .dur { color: var(--muted); font-size: 10.5px; display: block; margin-top: 2px; }
|
||||
.sched-row .dot {
|
||||
width: 10px; height: 10px; border-radius: 50%;
|
||||
background: var(--sage); justify-self: center;
|
||||
}
|
||||
.sched-row.now .dot {
|
||||
background: var(--terracotta);
|
||||
box-shadow: 0 0 0 4px var(--tc-soft);
|
||||
animation: nowPulse 2.4s ease-out infinite;
|
||||
}
|
||||
@keyframes nowPulse {
|
||||
0%, 100% { transform: scale(1); }
|
||||
50% { transform: scale(1.18); }
|
||||
}
|
||||
.sched-row.past .dot { background: var(--muted-2); opacity: 0.55; }
|
||||
.sched-row.past .title, .sched-row.past .desc { opacity: 0.55; }
|
||||
.sched-row.past .title { text-decoration: line-through; text-decoration-color: var(--muted-2); }
|
||||
.sched-row .title { font-size: 14.5px; color: var(--ink); font-weight: 500; }
|
||||
.sched-row .desc { font-size: 12px; color: var(--muted); margin-top: 2px; }
|
||||
.sched-row .source {
|
||||
font-family: var(--mono); font-size: 10.5px;
|
||||
padding: 2px 8px; border-radius: 4px;
|
||||
color: var(--muted); border: 1px solid var(--line);
|
||||
text-transform: uppercase; letter-spacing: 0.08em;
|
||||
}
|
||||
|
||||
/* ─────── PR list ─────── */
|
||||
.pr-list { display: flex; flex-direction: column; gap: 0;
|
||||
background: var(--paper-elev);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--r);
|
||||
overflow: hidden;
|
||||
}
|
||||
.pr-row {
|
||||
display: grid; grid-template-columns: 1fr auto auto auto; gap: 14px;
|
||||
padding: 14px 22px;
|
||||
border-bottom: 1px solid var(--hairline);
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
transition: background 0.12s;
|
||||
}
|
||||
.pr-row:last-child { border-bottom: 0; }
|
||||
.pr-row:hover { background: var(--paper-deep); }
|
||||
.pr-row .num { font-family: var(--mono); font-size: 11px; color: var(--muted); }
|
||||
.pr-row .title { font-size: 14px; color: var(--ink); font-weight: 500; margin-top: 2px; }
|
||||
.pr-row .files {
|
||||
font-family: var(--mono); font-size: 11px; color: var(--muted);
|
||||
background: var(--paper-deep);
|
||||
padding: 3px 8px; border-radius: 4px;
|
||||
}
|
||||
.pr-row .age {
|
||||
font-family: var(--mono); font-size: 11.5px;
|
||||
padding: 3px 8px; border-radius: 5px;
|
||||
color: var(--ink-soft);
|
||||
}
|
||||
.pr-row .age.urgent { background: var(--tc-soft); color: var(--terracotta); }
|
||||
.pr-row .age.warm { background: var(--gold-soft); color: #8e6f1f; }
|
||||
.pr-row .age.calm { background: var(--sage-soft); color: #496938; }
|
||||
|
||||
/* ─────── Wins (subtle bottom) ─────── */
|
||||
.wins {
|
||||
margin-top: 36px;
|
||||
padding: 32px 36px;
|
||||
background: linear-gradient(180deg, transparent, var(--paper-deep));
|
||||
border-radius: var(--r);
|
||||
border: 1px dashed var(--line-strong);
|
||||
}
|
||||
.wins-title {
|
||||
font-family: var(--serif); font-style: italic; font-size: 28px;
|
||||
color: var(--espresso); margin: 0 0 14px;
|
||||
line-height: 1; letter-spacing: -0.01em;
|
||||
}
|
||||
.wins-list { columns: 2; column-gap: 32px; }
|
||||
.wins-list .win {
|
||||
break-inside: avoid;
|
||||
margin-bottom: 14px;
|
||||
display: grid; grid-template-columns: 18px 1fr; gap: 8px; align-items: flex-start;
|
||||
}
|
||||
.wins-list .checkmark {
|
||||
color: var(--sage); font-family: var(--serif); font-size: 18px; line-height: 1;
|
||||
}
|
||||
.wins-list .text { font-size: 13.5px; color: var(--ink-soft); line-height: 1.5; }
|
||||
|
||||
/* ─────── Sidebar ─────── */
|
||||
aside.briefing-side {
|
||||
align-self: start;
|
||||
position: sticky; top: 32px;
|
||||
}
|
||||
.first-thing {
|
||||
background: var(--ink);
|
||||
color: var(--paper);
|
||||
border-radius: var(--r);
|
||||
padding: 20px 22px;
|
||||
margin-bottom: 14px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.first-thing::before {
|
||||
content: ""; position: absolute; inset: 0;
|
||||
background: radial-gradient(220px 120px at 110% -20%, rgba(255,180,120,0.12), transparent 70%);
|
||||
pointer-events: none;
|
||||
}
|
||||
.first-thing .label {
|
||||
font-family: var(--mono); font-size: 10.5px;
|
||||
text-transform: uppercase; letter-spacing: 0.14em;
|
||||
color: rgba(255,255,255,0.55);
|
||||
}
|
||||
.first-thing h3 {
|
||||
font-family: var(--serif); font-size: 22px; font-weight: 400;
|
||||
margin: 8px 0 8px; line-height: 1.15; color: var(--paper);
|
||||
letter-spacing: -0.005em;
|
||||
}
|
||||
.first-thing p {
|
||||
color: rgba(255,255,255,0.78); font-size: 13px; margin: 0;
|
||||
line-height: 1.55;
|
||||
}
|
||||
.first-thing .actions {
|
||||
margin-top: 16px; display: flex; gap: 8px;
|
||||
}
|
||||
.first-thing .a-btn {
|
||||
padding: 6px 12px; font-size: 12px; border-radius: 6px;
|
||||
border: 1px solid rgba(255,255,255,0.2);
|
||||
background: rgba(255,255,255,0.06); color: var(--paper);
|
||||
font-family: var(--mono); letter-spacing: 0.04em;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
.first-thing .a-btn:hover { background: rgba(255,255,255,0.14); }
|
||||
.first-thing .a-btn.primary {
|
||||
background: var(--gold); color: var(--ink); border-color: var(--gold);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.side-card {
|
||||
background: var(--paper-elev);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--r);
|
||||
padding: 16px 18px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.side-card h4 {
|
||||
font-family: var(--mono); font-size: 10.5px;
|
||||
text-transform: uppercase; letter-spacing: 0.14em;
|
||||
color: var(--muted); margin: 0 0 10px; font-weight: 500;
|
||||
}
|
||||
.quick-action {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
padding: 8px 0; font-size: 13px; color: var(--ink-soft);
|
||||
border-bottom: 1px dashed var(--hairline);
|
||||
cursor: pointer;
|
||||
transition: color 0.12s;
|
||||
}
|
||||
.quick-action:last-child { border-bottom: 0; }
|
||||
.quick-action:hover { color: var(--ink); }
|
||||
.quick-action .arr { font-family: var(--mono); color: var(--muted); }
|
||||
|
||||
.weather-card {
|
||||
display: flex; align-items: center; gap: 14px;
|
||||
padding: 14px 16px;
|
||||
}
|
||||
.weather-glyph {
|
||||
width: 44px; height: 44px;
|
||||
background: linear-gradient(180deg, #ffce72, #f0a23a);
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 0 22px rgba(255,180,80,0.45);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.weather-card .temp {
|
||||
font-family: var(--serif); font-size: 28px; line-height: 1; letter-spacing: -0.01em;
|
||||
}
|
||||
.weather-card .desc { color: var(--muted); font-size: 12px; margin-top: 4px; }
|
||||
|
||||
/* Toast */
|
||||
.toast-box {
|
||||
position: fixed; bottom: 24px; right: 24px; z-index: 200;
|
||||
display: flex; flex-direction: column; gap: 10px;
|
||||
pointer-events: none;
|
||||
}
|
||||
.toast {
|
||||
pointer-events: auto;
|
||||
min-width: 280px; max-width: 380px;
|
||||
padding: 14px 16px;
|
||||
background: var(--paper-elev);
|
||||
border: 1px solid var(--line);
|
||||
border-left: 3px solid var(--espresso);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 16px 48px rgba(28,28,31,0.10);
|
||||
font-size: 13px;
|
||||
color: var(--ink);
|
||||
animation: toastIn 0.32s cubic-bezier(0.2, 0.8, 0.2, 1);
|
||||
}
|
||||
.toast.success { border-left-color: var(--sage); }
|
||||
.toast.warn { border-left-color: var(--gold); }
|
||||
.toast.urgent { border-left-color: var(--terracotta); }
|
||||
.toast .t-title { font-family: var(--serif); font-size: 16px; line-height: 1.2; margin-bottom: 4px; }
|
||||
.toast .t-body { color: var(--ink-soft); line-height: 1.45; }
|
||||
.toast .t-body strong { color: var(--ink); }
|
||||
.toast.fade { opacity: 0; transform: translateX(20px); transition: all 0.25s; }
|
||||
@keyframes toastIn {
|
||||
from { opacity: 0; transform: translateX(20px); }
|
||||
to { opacity: 1; transform: translateX(0); }
|
||||
}
|
||||
|
||||
@media (max-width: 1080px) {
|
||||
main { grid-template-columns: 1fr; }
|
||||
aside.briefing-side { position: static; }
|
||||
.greeting h1 { font-size: 40px; }
|
||||
.rev-card { grid-template-columns: 1fr; }
|
||||
.wins-list { columns: 1; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<header class="app">
|
||||
<div class="masthead">
|
||||
<div class="small">Daily Briefing · Volume IV · Issue 18</div>
|
||||
<div class="word">
|
||||
<span>The Monday Memo</span>
|
||||
<span class="quiver">— from quiver</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="head-right">
|
||||
<span class="pill-soft"><span class="dot"></span>5 sources connected · synced 6:42 AM</span>
|
||||
<button class="icon-btn ghost" id="snoozeBtn">Snooze 1h</button>
|
||||
<button class="icon-btn" id="doneBtn">
|
||||
<svg class="ic" viewBox="0 0 16 16" fill="none"><path d="M3 8.5l3 3 7-7" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
Mark briefing done
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
|
||||
<div>
|
||||
|
||||
<!-- ─────── Greeting ─────── -->
|
||||
<div class="greeting">
|
||||
<div class="pretitle">Monday · May 6, 2026 · 8:42 AM</div>
|
||||
<h1>Good morning, <em>PT.</em></h1>
|
||||
<div class="lede">
|
||||
Six things deserve your attention this morning. The rest can wait until coffee #2.
|
||||
</div>
|
||||
<div class="meta-row">
|
||||
<span><span class="v">San Francisco</span></span><span class="sep">·</span>
|
||||
<span><span class="v">62°</span> light fog, clearing 11 AM</span><span class="sep">·</span>
|
||||
<span><span class="v">Sunset</span> 8:09 PM</span><span class="sep">·</span>
|
||||
<span><span class="v" style="color:var(--sage)">7h 24m</span> sleep</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ─────── 1. Revenue ─────── -->
|
||||
<section class="block">
|
||||
<div class="head">
|
||||
<h2><span class="ord">i.</span>This week's revenue</h2>
|
||||
<span class="meta">Stripe · 7-day rolling</span>
|
||||
</div>
|
||||
<div class="rev-card card">
|
||||
<div class="rev-stat">
|
||||
<div class="label">Past 7 days</div>
|
||||
<div class="big">$84,210</div>
|
||||
<div class="delta"><span class="arr">▲</span> +12.4% vs prior week · +$9,316</div>
|
||||
<div class="sub">Driven by 3 enterprise expansions on Wed–Thu. Two SMB churns absorbed in net new.</div>
|
||||
</div>
|
||||
<div class="rev-chart">
|
||||
<svg viewBox="0 0 600 140" preserveAspectRatio="none">
|
||||
<defs>
|
||||
<linearGradient id="revG" x1="0" x2="0" y1="0" y2="1">
|
||||
<stop offset="0%" stop-color="#6e8865" stop-opacity="0.32"/>
|
||||
<stop offset="100%" stop-color="#6e8865" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<!-- horizontal guides -->
|
||||
<line x1="0" y1="40" x2="600" y2="40" stroke="rgba(28,28,31,0.06)" stroke-dasharray="2 4"/>
|
||||
<line x1="0" y1="80" x2="600" y2="80" stroke="rgba(28,28,31,0.06)" stroke-dasharray="2 4"/>
|
||||
<line x1="0" y1="120" x2="600" y2="120" stroke="rgba(28,28,31,0.06)" stroke-dasharray="2 4"/>
|
||||
<!-- prior week ghost line -->
|
||||
<polyline fill="none" stroke="rgba(28,28,31,0.32)" stroke-width="1.2" stroke-dasharray="3 4"
|
||||
points="20,90 100,86 180,82 260,86 340,80 420,78 500,72 580,70"/>
|
||||
<!-- this week area + line -->
|
||||
<polygon fill="url(#revG)" points="20,140 20,82 100,76 180,68 260,70 340,58 420,52 500,40 580,30 580,140"/>
|
||||
<polyline fill="none" stroke="#6e8865" stroke-width="2.2" stroke-linejoin="round" stroke-linecap="round"
|
||||
points="20,82 100,76 180,68 260,70 340,58 420,52 500,40 580,30"/>
|
||||
<!-- weekday labels -->
|
||||
<g font-family="Geist Mono,monospace" font-size="9.5" fill="#6b6b66" text-anchor="middle">
|
||||
<text x="20" y="138">Mon</text>
|
||||
<text x="100" y="138">Tue</text>
|
||||
<text x="180" y="138">Wed</text>
|
||||
<text x="260" y="138">Thu</text>
|
||||
<text x="340" y="138">Fri</text>
|
||||
<text x="420" y="138">Sat</text>
|
||||
<text x="500" y="138">Sun</text>
|
||||
<text x="580" y="138" fill="#1c1c1f" font-weight="600">Mon</text>
|
||||
</g>
|
||||
<!-- "now" marker -->
|
||||
<circle cx="580" cy="30" r="3.5" fill="#1c1c1f"/>
|
||||
<text x="586" y="22" font-family="Instrument Serif,serif" font-style="italic" font-size="13" fill="#1c1c1f">today</text>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ─────── 2. Email digest ─────── -->
|
||||
<section class="block">
|
||||
<div class="head">
|
||||
<h2><span class="ord">ii.</span>Customers who deserve a reply</h2>
|
||||
<span class="meta">Gmail · curated by relevance</span>
|
||||
</div>
|
||||
<div class="digest-list" id="digest"></div>
|
||||
</section>
|
||||
|
||||
<!-- ─────── 3. Stuck Linear issues ─────── -->
|
||||
<section class="block">
|
||||
<div class="head">
|
||||
<h2><span class="ord">iii.</span>What's stuck on the team</h2>
|
||||
<span class="meta">Linear · idle > 48h</span>
|
||||
</div>
|
||||
<div class="stuck-list" id="stuck"></div>
|
||||
</section>
|
||||
|
||||
<!-- ─────── 4. Today's schedule ─────── -->
|
||||
<section class="block">
|
||||
<div class="head">
|
||||
<h2><span class="ord">iv.</span>Today, hour by hour</h2>
|
||||
<span class="meta">Google Calendar · 6 events</span>
|
||||
</div>
|
||||
<div class="schedule" id="schedule"></div>
|
||||
</section>
|
||||
|
||||
<!-- ─────── 5. PRs ─────── -->
|
||||
<section class="block">
|
||||
<div class="head">
|
||||
<h2><span class="ord">v.</span>Pull requests waiting on you</h2>
|
||||
<span class="meta">GitHub · 4 PRs</span>
|
||||
</div>
|
||||
<div class="pr-list" id="prList"></div>
|
||||
</section>
|
||||
|
||||
<!-- ─────── 6. Wins ─────── -->
|
||||
<div class="wins">
|
||||
<div class="wins-title">Yesterday, quietly:</div>
|
||||
<div class="wins-list">
|
||||
<div class="win"><span class="checkmark">✓</span><span class="text"><strong>Voltage Co.</strong> went live in production at 6:14 PM. Zero rollback. Onboarding kickoff tomorrow.</span></div>
|
||||
<div class="win"><span class="checkmark">✓</span><span class="text">Mira closed <strong>Atlas Cooperative</strong> ($240k) on a Sunday call. The handshake email landed at 11:47 PM.</span></div>
|
||||
<div class="win"><span class="checkmark">✓</span><span class="text">QA cleared the Q2 release — <strong>0 P0s, 2 P3s</strong>. Cleanest cut in 7 quarters.</span></div>
|
||||
<div class="win"><span class="checkmark">✓</span><span class="text"><strong>Nora's onboarding</strong> wrapped a week ahead. Already shipping in <code>src/agent/router</code>.</span></div>
|
||||
<div class="win"><span class="checkmark">✓</span><span class="text">Stripe weekly closed at <strong>$84.2k</strong> — fourth straight week above $80k. Comfortable Q2 trajectory.</span></div>
|
||||
<div class="win"><span class="checkmark">✓</span><span class="text">Sam shipped the auth migration with a <strong>54-line PR</strong>. The rollback plan was three commits.</span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- ─────── Sidebar ─────── -->
|
||||
<aside class="briefing-side">
|
||||
|
||||
<div class="first-thing">
|
||||
<div class="label">Your first thing today</div>
|
||||
<h3>Reply to Lattice Health by 10 AM.</h3>
|
||||
<p>Their legal review wraps tomorrow. A short "we're ready when you are" keeps the proposal on Friday's close list.</p>
|
||||
<div class="actions">
|
||||
<button class="a-btn primary" data-action="draft">Draft reply</button>
|
||||
<button class="a-btn" data-action="snooze1">Snooze 1h</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="weather-card side-card">
|
||||
<div class="weather-glyph"></div>
|
||||
<div>
|
||||
<div class="temp">62°</div>
|
||||
<div class="desc">Light fog · clearing by 11</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="side-card">
|
||||
<h4>Quick actions</h4>
|
||||
<div class="quick-action" data-action="generatePlan"><span>✦ Generate next week's plan</span><span class="arr">→</span></div>
|
||||
<div class="quick-action" data-action="standup"><span>Post standup digest</span><span class="arr">→</span></div>
|
||||
<div class="quick-action" data-action="snooze"><span>Snooze whole briefing 1h</span><span class="arr">→</span></div>
|
||||
<div class="quick-action" data-action="email"><span>Email me a copy at 7 PM</span><span class="arr">→</span></div>
|
||||
</div>
|
||||
|
||||
<div class="side-card">
|
||||
<h4>Connected sources</h4>
|
||||
<div style="display:flex;flex-direction:column;gap:8px;font-size:13px;">
|
||||
<div style="display:flex;justify-content:space-between;color:var(--ink-soft);">
|
||||
<span>Stripe</span><span style="font-family:var(--mono);font-size:11px;color:var(--sage);">live</span>
|
||||
</div>
|
||||
<div style="display:flex;justify-content:space-between;color:var(--ink-soft);">
|
||||
<span>Gmail</span><span style="font-family:var(--mono);font-size:11px;color:var(--sage);">live</span>
|
||||
</div>
|
||||
<div style="display:flex;justify-content:space-between;color:var(--ink-soft);">
|
||||
<span>Linear</span><span style="font-family:var(--mono);font-size:11px;color:var(--sage);">live</span>
|
||||
</div>
|
||||
<div style="display:flex;justify-content:space-between;color:var(--ink-soft);">
|
||||
<span>GitHub</span><span style="font-family:var(--mono);font-size:11px;color:var(--sage);">live</span>
|
||||
</div>
|
||||
<div style="display:flex;justify-content:space-between;color:var(--ink-soft);">
|
||||
<span>Calendar</span><span style="font-family:var(--mono);font-size:11px;color:var(--sage);">live</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="text-align:center;font-family:var(--serif);font-style:italic;color:var(--muted);font-size:14px;line-height:1.4;padding:14px 8px;">
|
||||
"What gets your attention<br/>shapes your week."
|
||||
<div style="font-family:var(--mono);font-style:normal;font-size:10.5px;color:var(--muted-2);margin-top:6px;letter-spacing:0.06em;">— quiver, vol. IV</div>
|
||||
</div>
|
||||
|
||||
</aside>
|
||||
|
||||
</main>
|
||||
|
||||
<div class="toast-box" id="toastBox"></div>
|
||||
|
||||
<script>
|
||||
(() => {
|
||||
// ─────── Email digest ───────
|
||||
const EMAILS = [
|
||||
{ from: 'Pioneer Robotics — Sarah Chen', subject: 'Re: Pricing 2-pager and onboarding timeline', snippet: 'Quick one — the team is ready to move forward, just need the term sheet by Wednesday so legal can…', tag: 'urgent', ago: '2h', av: 'S', avBg: '#1f2937' },
|
||||
{ from: 'Lattice Health — David Park', subject: 'Procurement asked for a SOC 2 letter', snippet: 'Hey, our procurement team had one last item on the list. Can you send the SOC 2 attestation letter to…', tag: 'warm', ago: '4h', av: 'D', avBg: '#0ea5e9' },
|
||||
{ from: 'Foundry Group — Thomas Brun', subject: 'Loved the demo recording — sharing internally', snippet: 'Just to close the loop: I forwarded the recording to two engineering directors. They\'re penciled in for…', tag: 'calm', ago: '14h', av: 'T', avBg: '#dc2626' },
|
||||
{ from: 'Ironclad Mfg — Priya Anand', subject: 'Term sheet v2 attached', snippet: 'Here\'s our redline. Two small changes on the indemnification and one substantial on the data residency…', tag: 'urgent', ago: '18h', av: 'P', avBg: '#475569' },
|
||||
{ from: 'Mosaic Health — Lin Chen', subject: 'Intro to procurement (cc\'d)', snippet: 'Looping in our head of procurement. They have time this Thursday or next Monday for a first call…', tag: 'fyi', ago: '1d', av: 'L', avBg: '#7c3aed' }
|
||||
];
|
||||
const dgEl = document.getElementById('digest');
|
||||
dgEl.innerHTML = EMAILS.map((e, i) => `
|
||||
<div class="digest-item" data-i="${i}">
|
||||
<span class="digest-av" style="background:${e.avBg}">${e.av}</span>
|
||||
<div>
|
||||
<div class="digest-meta"><span class="digest-from">${e.from}</span> · ${e.ago} ago</div>
|
||||
<div class="digest-subject">${e.subject}</div>
|
||||
<div class="digest-snippet">${e.snippet}</div>
|
||||
</div>
|
||||
<span class="digest-tag ${e.tag}">${e.tag === 'urgent' ? 'reply today' : e.tag === 'warm' ? 'this week' : e.tag === 'calm' ? 'reviewed' : 'fyi'}</span>
|
||||
</div>
|
||||
`).join('');
|
||||
dgEl.querySelectorAll('.digest-item').forEach(item => {
|
||||
const e = EMAILS[+item.dataset.i];
|
||||
item.addEventListener('click', () => {
|
||||
toast({ kind: e.tag === 'urgent' ? 'urgent' : 'warn', title: e.subject, body: `From <strong>${e.from}</strong> · Opening Gmail thread…` });
|
||||
});
|
||||
});
|
||||
|
||||
// ─────── Stuck issues ───────
|
||||
const STUCK = [
|
||||
{ id: 'ENG-1284', title: 'Fix flaky test in payment-rollback path', who: 'Mira O.', whoBg: '#f97316', age: '5d', ageClass: '' },
|
||||
{ id: 'ENG-1271', title: 'Refactor session token rotation (security review feedback)', who: 'Sam D.', whoBg: '#10b981', age: '3d', ageClass: 'warm' },
|
||||
{ id: 'GROW-462', title: 'Wire Mixpanel funnel to onboarding step 3', who: 'Jules K.', whoBg: '#a855f7', age: '4d', ageClass: '' },
|
||||
{ id: 'ENG-1268', title: 'Migrate cron worker to durable queue', who: 'Sam D.', whoBg: '#10b981', age: '6d', ageClass: '' },
|
||||
{ id: 'DESIGN-118', title: 'Settings page redesign — needs eng pairing', who: 'Nora L.', whoBg: '#0ea5e9', age: '2d', ageClass: 'warm' }
|
||||
];
|
||||
document.getElementById('stuck').innerHTML = STUCK.map((s, i) => `
|
||||
<div class="stuck-row" data-i="${i}">
|
||||
<div>
|
||||
<div class="id">${s.id}</div>
|
||||
<div class="title">${s.title}</div>
|
||||
</div>
|
||||
<span class="who"><span class="av" style="background:${s.whoBg}">${s.who[0]}</span>${s.who}</span>
|
||||
<span class="age ${s.ageClass}">stuck ${s.age}</span>
|
||||
<button class="nudge" data-i="${i}">Nudge</button>
|
||||
</div>
|
||||
`).join('');
|
||||
document.querySelectorAll('.stuck-row').forEach(row => {
|
||||
const s = STUCK[+row.dataset.i];
|
||||
row.querySelector('.nudge').addEventListener('click', e => {
|
||||
e.stopPropagation();
|
||||
toast({ kind: 'success', title: 'Nudge sent', body: `Sent ${s.who} a gentle Slack DM about <strong>${s.id}</strong>.` });
|
||||
});
|
||||
row.addEventListener('click', () => {
|
||||
toast({ title: s.id, body: `<strong>${s.title}</strong> · stuck ${s.age} · assigned ${s.who}` });
|
||||
});
|
||||
});
|
||||
|
||||
// ─────── Schedule ───────
|
||||
const NOW_HOUR = 8 * 60 + 42; // 8:42 AM
|
||||
const SCHED = [
|
||||
{ time: '7:30 AM', dur: '30m', title: 'Morning run', desc: 'Bay-side loop · Strava', source: 'CAL', t: 7*60 + 30 },
|
||||
{ time: '9:00 AM', dur: '15m', title: 'Sync with Mira', desc: 'Pipeline review · 1:1', source: 'GMEET', t: 9*60 },
|
||||
{ time: '10:00 AM', dur: '45m', title: 'Lattice Health follow-up', desc: 'Review SOC 2 + draft reply', source: 'FOCUS', t: 10*60 },
|
||||
{ time: '11:30 AM', dur: '30m', title: 'Eng standup', desc: 'Fri release + Q3 capacity', source: 'GMEET', t: 11*60 + 30 },
|
||||
{ time: '2:00 PM', dur: '60m', title: 'Pioneer Robotics — term sheet review', desc: 'Joint with Sam · legal walks through redlines', source: 'ZOOM', t: 14*60 },
|
||||
{ time: '4:30 PM', dur: '20m', title: 'PR review block', desc: 'Clear the 4 PRs in queue', source: 'FOCUS', t: 16*60 + 30 }
|
||||
];
|
||||
document.getElementById('schedule').innerHTML = SCHED.map((s, i) => {
|
||||
const past = s.t + parseInt(s.dur) < NOW_HOUR;
|
||||
const now = s.t <= NOW_HOUR && NOW_HOUR < s.t + parseInt(s.dur);
|
||||
const cls = past ? 'past' : (now ? 'now' : '');
|
||||
return `
|
||||
<div class="sched-row ${cls}" data-i="${i}">
|
||||
<div class="time">${s.time}<span class="dur">${s.dur}</span></div>
|
||||
<div class="dot"></div>
|
||||
<div>
|
||||
<div class="title">${s.title}</div>
|
||||
<div class="desc">${s.desc}</div>
|
||||
</div>
|
||||
<span class="source">${s.source}</span>
|
||||
</div>
|
||||
`;
|
||||
}).join('');
|
||||
document.querySelectorAll('.sched-row').forEach(r => {
|
||||
const s = SCHED[+r.dataset.i];
|
||||
r.addEventListener('click', () => toast({ title: s.title, body: `${s.time} · ${s.dur} · ${s.source.toLowerCase()}<br/>${s.desc}` }));
|
||||
});
|
||||
|
||||
// ─────── PRs ───────
|
||||
const PRS = [
|
||||
{ num: '#1284', title: 'Add session-token rotation primitive', files: '6 files', author: 'Sam', age: 'opened 18h ago', ageClass: 'urgent' },
|
||||
{ num: '#1271', title: 'Migrate cron worker to durable queue (RFC implementation)', files: '14 files', author: 'Sam', age: '2d ago', ageClass: 'urgent' },
|
||||
{ num: '#1268', title: 'Mixpanel funnel hooks for onboarding step 3', files: '4 files', author: 'Jules', age: '1d ago', ageClass: 'warm' },
|
||||
{ num: '#1262', title: 'Settings page redesign · scaffolding only', files: '22 files', author: 'Nora', age: '4h ago', ageClass: 'calm' }
|
||||
];
|
||||
document.getElementById('prList').innerHTML = PRS.map((p, i) => `
|
||||
<div class="pr-row" data-i="${i}">
|
||||
<div>
|
||||
<span class="num">${p.num} · @${p.author}</span>
|
||||
<div class="title">${p.title}</div>
|
||||
</div>
|
||||
<span class="files">${p.files}</span>
|
||||
<span class="age ${p.ageClass}">${p.age}</span>
|
||||
</div>
|
||||
`).join('');
|
||||
document.querySelectorAll('.pr-row').forEach(r => {
|
||||
const p = PRS[+r.dataset.i];
|
||||
r.addEventListener('click', () => toast({ title: p.num + ' · ' + p.title, body: `${p.files} · @${p.author} · ${p.age}<br/>Opening on GitHub…` }));
|
||||
});
|
||||
|
||||
// ─────── Toasts ───────
|
||||
const toastBox = document.getElementById('toastBox');
|
||||
function toast(opts) {
|
||||
const { kind = '', title = '', body = '' } = (typeof opts === 'string') ? { body: opts } : opts;
|
||||
const node = document.createElement('div');
|
||||
node.className = `toast ${kind}`;
|
||||
node.innerHTML = `<div class="t-title">${title}</div><div class="t-body">${body}</div>`;
|
||||
toastBox.appendChild(node);
|
||||
setTimeout(() => {
|
||||
node.classList.add('fade');
|
||||
setTimeout(() => node.remove(), 280);
|
||||
}, 4200);
|
||||
}
|
||||
|
||||
// ─────── Side actions + header ───────
|
||||
const map = {
|
||||
draft: { kind: 'success', title: 'Reply drafted', body: 'A short "we\'re ready when you are" is in your <strong>Lattice Health</strong> Gmail draft. Send when ready.' },
|
||||
snooze1: { kind: 'warn', title: 'Snoozed', body: 'Lattice Health bumped to 11 AM.' },
|
||||
generatePlan: { kind: '', title: 'Plan drafted', body: 'Next week\'s plan ready in Notion · 4 priorities · 14 commitments inferred from your week.' },
|
||||
standup: { kind: 'success', title: 'Standup posted', body: '#eng-standup got a 4-line digest of yesterday\'s wins + today\'s focus.' },
|
||||
snooze: { kind: 'warn', title: 'Briefing snoozed', body: 'See you in an hour. We\'ll re-pull fresh data.' },
|
||||
email: { kind: 'success', title: 'Email scheduled', body: 'You\'ll get a copy of this briefing in your inbox at 7 PM.' }
|
||||
};
|
||||
document.querySelectorAll('[data-action]').forEach(b => {
|
||||
b.addEventListener('click', () => {
|
||||
const k = b.dataset.action;
|
||||
if (map[k]) toast(map[k]);
|
||||
});
|
||||
});
|
||||
|
||||
document.getElementById('doneBtn').addEventListener('click', () => {
|
||||
toast({ kind: 'success', title: 'Briefing done', body: '6 of 6 sections reviewed. See you tomorrow at 6:42 AM.' });
|
||||
});
|
||||
document.getElementById('snoozeBtn').addEventListener('click', () => {
|
||||
toast({ kind: 'warn', title: 'Briefing snoozed', body: 'Re-pulling at 9:42 AM.' });
|
||||
});
|
||||
|
||||
})();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large.
Load diff
+16
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"title": "Quiver Live · Personal Portfolio",
|
||||
"slug": "quiver-live-portfolio",
|
||||
"pinned": true,
|
||||
"preview": {
|
||||
"type": "html",
|
||||
"entry": "index.html"
|
||||
},
|
||||
"document": {
|
||||
"format": "html_template_v1",
|
||||
"templatePath": "template.html",
|
||||
"generatedPreviewPath": "index.html",
|
||||
"dataPath": "data.json",
|
||||
"dataJson": {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,453 @@
|
||||
{
|
||||
"summary": {
|
||||
"brand": "quiver",
|
||||
"pro": "Live",
|
||||
"marketState": "NYSE Open · 14:32:18 EST",
|
||||
"lastSync": "14:32:18 EST",
|
||||
"totalLabel": "Total portfolio value · USD",
|
||||
"accountsLabel": "All accounts (Robinhood + IBKR)",
|
||||
"totalValue": "$284,521.64",
|
||||
"todayPnl": "+$3,182.40",
|
||||
"todayPct": "+1.13%",
|
||||
"todayDirClass": "up",
|
||||
"todayArrow": "▲",
|
||||
"allTimePnl": "+$84,521.64",
|
||||
"allTimePct": "+42.3%",
|
||||
"allTimeDirClass": "up",
|
||||
"allTimeArrow": "▲",
|
||||
"alphaLabel": "vs S&P · YTD",
|
||||
"alpha": "+18.7pp",
|
||||
"areaPoints": "10,150 10,128 60,124 110,116 160,118 210,108 260,114 310,98 360,90 410,96 460,80 510,72 560,76 610,60 660,54 710,46 760,40 810,32 860,28 870,28 870,150",
|
||||
"linePoints": "10,128 60,124 110,116 160,118 210,108 260,114 310,98 360,90 410,96 460,80 510,72 560,76 610,60 660,54 710,46 760,40 810,32 860,28",
|
||||
"markerX": "860",
|
||||
"markerY": "28",
|
||||
"axisTicks": [
|
||||
{ "label": "May '24" },
|
||||
{ "label": "Jul" },
|
||||
{ "label": "Sep" },
|
||||
{ "label": "Nov" },
|
||||
{ "label": "Jan '25" },
|
||||
{ "label": "Mar" },
|
||||
{ "label": "Today" }
|
||||
]
|
||||
},
|
||||
|
||||
"kpis": [
|
||||
{
|
||||
"label": "Today's P&L",
|
||||
"value": "+$3,182.40",
|
||||
"valueClass": "up",
|
||||
"sub": "+1.13% · 8 winners / 2 losers",
|
||||
"subClass": "up"
|
||||
},
|
||||
{
|
||||
"label": "Buying power",
|
||||
"value": "$12,438.21",
|
||||
"valueClass": "",
|
||||
"sub": "Cash + 2× margin available",
|
||||
"subClass": ""
|
||||
},
|
||||
{
|
||||
"label": "Best position · NVDA",
|
||||
"value": "+187.4%",
|
||||
"valueClass": "up",
|
||||
"sub": "Avg cost $321.55 · Now $924.31",
|
||||
"subClass": "up"
|
||||
},
|
||||
{
|
||||
"label": "Alpha vs S&P 500 · YTD",
|
||||
"value": "+18.7pp",
|
||||
"valueClass": "alpha",
|
||||
"sub": "Sharpe 1.42 · Beta 1.18 · Max DD −9.4%",
|
||||
"subClass": ""
|
||||
}
|
||||
],
|
||||
|
||||
"featured": {
|
||||
"iconText": "N",
|
||||
"iconBg": "linear-gradient(135deg,#76b900,#4d8400)",
|
||||
"iconFg": "#0a1502",
|
||||
"symbol": "NVDA",
|
||||
"name": "NVIDIA Corporation",
|
||||
"sector": "Semiconductors · Mkt cap $2.27T",
|
||||
"exchangeName": "NASDAQ",
|
||||
"exchangeUrl": "https://www.nasdaq.com/market-activity/stocks/nvda",
|
||||
"yahooUrl": "https://finance.yahoo.com/quote/NVDA",
|
||||
"tradingViewUrl": "https://www.tradingview.com/symbols/NASDAQ-NVDA/",
|
||||
"price": "$924.31",
|
||||
"delta": "▲ +$21.74 +2.41% today",
|
||||
"dirClass": "up",
|
||||
"stats": [
|
||||
{ "label": "Open", "value": "906.80" },
|
||||
{ "label": "High", "value": "929.18" },
|
||||
{ "label": "Low", "value": "902.44" },
|
||||
{ "label": "Volume", "value": "38.42M" },
|
||||
{ "label": "P/E", "value": "62.4" },
|
||||
{ "label": "52W range", "value": "412 — 974" }
|
||||
],
|
||||
"areaPoints": "40,260 40,220 80,210 120,200 160,194 200,184 240,180 280,170 320,160 360,150 400,138 440,128 480,120 520,108 560,100 600,86 640,76 680,64 720,56 760,48 800,40 840,30 850,30 850,260",
|
||||
"linePoints": "40,220 80,210 120,200 160,194 200,184 240,180 280,170 320,160 360,150 400,138 440,128 480,120 520,108 560,100 600,86 640,76 680,64 720,56 760,48 800,40 840,30",
|
||||
"priceY": "30",
|
||||
"priceLabelY": "21",
|
||||
"priceLabelTextY": "34",
|
||||
"priceLabelText": "924.31"
|
||||
},
|
||||
|
||||
"ai": {
|
||||
"tag": "Quiver AI · Signal #2417",
|
||||
"headlineAccent": "Add to NVDA",
|
||||
"headlineRest": "on Blackwell ramp + AI capex inflection.",
|
||||
"sub": "Three converging catalysts and resilient channel checks. Conviction has stepped up since last week's hyperscaler guidance.",
|
||||
"conviction": "87%",
|
||||
"convictionWidth": "87%",
|
||||
"targetLabel": "12-mo target",
|
||||
"target": "$1,050",
|
||||
"upside": "+13.6%",
|
||||
"size": "+12 sh",
|
||||
"sizeSub": "≈ $11,092 · 4.2% port",
|
||||
"catalysts": [
|
||||
{
|
||||
"n": "01",
|
||||
"title": "Blackwell shipments accelerating",
|
||||
"text": "Foxconn and Wiwynn guidance both implied an Aug volume step-up. Backlog visibility extends through Q1 '26."
|
||||
},
|
||||
{
|
||||
"n": "02",
|
||||
"title": "Hyperscaler capex revised up",
|
||||
"text": "MSFT, META, AMZN combined FY '25 capex now +34% YoY. AI infra is the binding allocation."
|
||||
},
|
||||
{
|
||||
"n": "03",
|
||||
"title": "Sentiment de-risked",
|
||||
"text": "Short interest at 6-month high but RSI cooling from overbought. Setup mirrors the May '24 base before the +47% leg."
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"holdingsPanel": {
|
||||
"title": "Holdings · 9 positions",
|
||||
"subtitle": "Live · last quote 14:32:18 EST"
|
||||
},
|
||||
"holdings": [
|
||||
{
|
||||
"symbol": "NVDA",
|
||||
"companyName": "NVIDIA Corporation",
|
||||
"icon": "N",
|
||||
"iconBg": "linear-gradient(135deg,#76b900,#3d6800)",
|
||||
"iconFg": "#0a1502",
|
||||
"exchName": "NASDAQ",
|
||||
"exchUrl": "https://www.nasdaq.com/market-activity/stocks/nvda",
|
||||
"shares": "100",
|
||||
"avgCost": "321.55",
|
||||
"last": "924.31",
|
||||
"marketValue": "$92,431",
|
||||
"pnl": "+$60,276",
|
||||
"pnlPct": "+187.4%",
|
||||
"dirClass": "up",
|
||||
"alloc": "32.5%",
|
||||
"allocWidth": "32%"
|
||||
},
|
||||
{
|
||||
"symbol": "AAPL",
|
||||
"companyName": "Apple Inc.",
|
||||
"icon": "A",
|
||||
"iconBg": "linear-gradient(135deg,#0c0c0c,#3a3a3a)",
|
||||
"iconFg": "#ffffff",
|
||||
"exchName": "NASDAQ",
|
||||
"exchUrl": "https://www.nasdaq.com/market-activity/stocks/aapl",
|
||||
"shares": "220",
|
||||
"avgCost": "152.10",
|
||||
"last": "228.74",
|
||||
"marketValue": "$50,323",
|
||||
"pnl": "+$16,861",
|
||||
"pnlPct": "+50.4%",
|
||||
"dirClass": "up",
|
||||
"alloc": "17.7%",
|
||||
"allocWidth": "18%"
|
||||
},
|
||||
{
|
||||
"symbol": "MSFT",
|
||||
"companyName": "Microsoft Corp.",
|
||||
"icon": "M",
|
||||
"iconBg": "linear-gradient(135deg,#0078d4,#005a9e)",
|
||||
"iconFg": "#ffffff",
|
||||
"exchName": "NASDAQ",
|
||||
"exchUrl": "https://www.nasdaq.com/market-activity/stocks/msft",
|
||||
"shares": "85",
|
||||
"avgCost": "328.40",
|
||||
"last": "438.12",
|
||||
"marketValue": "$37,240",
|
||||
"pnl": "+$9,326",
|
||||
"pnlPct": "+33.4%",
|
||||
"dirClass": "up",
|
||||
"alloc": "13.1%",
|
||||
"allocWidth": "13%"
|
||||
},
|
||||
{
|
||||
"symbol": "META",
|
||||
"companyName": "Meta Platforms",
|
||||
"icon": "M",
|
||||
"iconBg": "linear-gradient(135deg,#1877f2,#0c5fc3)",
|
||||
"iconFg": "#ffffff",
|
||||
"exchName": "NASDAQ",
|
||||
"exchUrl": "https://www.nasdaq.com/market-activity/stocks/meta",
|
||||
"shares": "55",
|
||||
"avgCost": "312.20",
|
||||
"last": "596.18",
|
||||
"marketValue": "$32,790",
|
||||
"pnl": "+$15,619",
|
||||
"pnlPct": "+91.0%",
|
||||
"dirClass": "up",
|
||||
"alloc": "11.5%",
|
||||
"allocWidth": "11%"
|
||||
},
|
||||
{
|
||||
"symbol": "GOOGL",
|
||||
"companyName": "Alphabet Class A",
|
||||
"icon": "G",
|
||||
"iconBg": "linear-gradient(135deg,#4285f4,#34a853)",
|
||||
"iconFg": "#ffffff",
|
||||
"exchName": "NASDAQ",
|
||||
"exchUrl": "https://www.nasdaq.com/market-activity/stocks/googl",
|
||||
"shares": "160",
|
||||
"avgCost": "128.90",
|
||||
"last": "186.40",
|
||||
"marketValue": "$29,824",
|
||||
"pnl": "+$9,200",
|
||||
"pnlPct": "+44.6%",
|
||||
"dirClass": "up",
|
||||
"alloc": "10.5%",
|
||||
"allocWidth": "10%"
|
||||
},
|
||||
{
|
||||
"symbol": "AMZN",
|
||||
"companyName": "Amazon.com Inc.",
|
||||
"icon": "A",
|
||||
"iconBg": "linear-gradient(135deg,#ff9900,#cc7a00)",
|
||||
"iconFg": "#1a0e00",
|
||||
"exchName": "NASDAQ",
|
||||
"exchUrl": "https://www.nasdaq.com/market-activity/stocks/amzn",
|
||||
"shares": "90",
|
||||
"avgCost": "142.50",
|
||||
"last": "218.94",
|
||||
"marketValue": "$19,705",
|
||||
"pnl": "+$6,880",
|
||||
"pnlPct": "+53.6%",
|
||||
"dirClass": "up",
|
||||
"alloc": "6.9%",
|
||||
"allocWidth": "7%"
|
||||
},
|
||||
{
|
||||
"symbol": "TSLA",
|
||||
"companyName": "Tesla, Inc.",
|
||||
"icon": "T",
|
||||
"iconBg": "linear-gradient(135deg,#cc0000,#660000)",
|
||||
"iconFg": "#ffffff",
|
||||
"exchName": "NASDAQ",
|
||||
"exchUrl": "https://www.nasdaq.com/market-activity/stocks/tsla",
|
||||
"shares": "42",
|
||||
"avgCost": "288.40",
|
||||
"last": "312.55",
|
||||
"marketValue": "$13,127",
|
||||
"pnl": "+$1,015",
|
||||
"pnlPct": "+8.4%",
|
||||
"dirClass": "up",
|
||||
"alloc": "4.6%",
|
||||
"allocWidth": "5%"
|
||||
},
|
||||
{
|
||||
"symbol": "AMD",
|
||||
"companyName": "Advanced Micro Devices",
|
||||
"icon": "A",
|
||||
"iconBg": "linear-gradient(135deg,#ed1c24,#7a0000)",
|
||||
"iconFg": "#ffffff",
|
||||
"exchName": "NASDAQ",
|
||||
"exchUrl": "https://www.nasdaq.com/market-activity/stocks/amd",
|
||||
"shares": "52",
|
||||
"avgCost": "158.90",
|
||||
"last": "142.06",
|
||||
"marketValue": "$7,387",
|
||||
"pnl": "−$876",
|
||||
"pnlPct": "−10.6%",
|
||||
"dirClass": "down",
|
||||
"alloc": "2.6%",
|
||||
"allocWidth": "3%"
|
||||
},
|
||||
{
|
||||
"symbol": "USD",
|
||||
"companyName": "Settled cash",
|
||||
"icon": "$",
|
||||
"iconBg": "linear-gradient(135deg,#facc15,#a17400)",
|
||||
"iconFg": "#1a1100",
|
||||
"exchName": "—",
|
||||
"exchUrl": "#",
|
||||
"shares": "—",
|
||||
"avgCost": "—",
|
||||
"last": "1.00",
|
||||
"marketValue": "$1,694",
|
||||
"pnl": "—",
|
||||
"pnlPct": "—",
|
||||
"dirClass": "",
|
||||
"alloc": "0.6%",
|
||||
"allocWidth": "1%"
|
||||
}
|
||||
],
|
||||
|
||||
"watchlistPanel": {
|
||||
"title": "Watchlist · curated",
|
||||
"subtitle": "12 tickers · 14:32 EST"
|
||||
},
|
||||
"watchlist": [
|
||||
{
|
||||
"symbol": "PLTR",
|
||||
"companyName": "Palantir",
|
||||
"price": "$58.42",
|
||||
"pct": "▲ 4.81%",
|
||||
"dirClass": "up",
|
||||
"sparkColor": "#22e58c",
|
||||
"sparkPoints": "0,28 20,30 40,26 60,24 80,28 100,22 120,18 140,20 160,14 180,10 200,6",
|
||||
"volume": "4.21M",
|
||||
"exchName": "NASDAQ",
|
||||
"exchUrl": "https://www.nasdaq.com/market-activity/stocks/pltr"
|
||||
},
|
||||
{
|
||||
"symbol": "SHOP",
|
||||
"companyName": "Shopify",
|
||||
"price": "$112.84",
|
||||
"pct": "▲ 2.13%",
|
||||
"dirClass": "up",
|
||||
"sparkColor": "#22e58c",
|
||||
"sparkPoints": "0,26 20,28 40,22 60,24 80,20 100,22 120,18 140,16 160,12 180,14 200,10",
|
||||
"volume": "6.84M",
|
||||
"exchName": "NYSE",
|
||||
"exchUrl": "https://www.nyse.com/quote/XNYS:SHOP"
|
||||
},
|
||||
{
|
||||
"symbol": "TSM",
|
||||
"companyName": "TSMC",
|
||||
"price": "$208.77",
|
||||
"pct": "▲ 1.62%",
|
||||
"dirClass": "up",
|
||||
"sparkColor": "#22e58c",
|
||||
"sparkPoints": "0,24 20,22 40,26 60,20 80,18 100,20 120,16 140,14 160,12 180,10 200,8",
|
||||
"volume": "12.4M",
|
||||
"exchName": "NYSE",
|
||||
"exchUrl": "https://www.nyse.com/quote/XNYS:TSM"
|
||||
},
|
||||
{
|
||||
"symbol": "COIN",
|
||||
"companyName": "Coinbase",
|
||||
"price": "$236.10",
|
||||
"pct": "▲ 3.48%",
|
||||
"dirClass": "up",
|
||||
"sparkColor": "#22e58c",
|
||||
"sparkPoints": "0,28 20,30 40,26 60,28 80,22 100,18 120,20 140,14 160,10 180,12 200,6",
|
||||
"volume": "8.92M",
|
||||
"exchName": "NASDAQ",
|
||||
"exchUrl": "https://www.nasdaq.com/market-activity/stocks/coin"
|
||||
},
|
||||
{
|
||||
"symbol": "CRWD",
|
||||
"companyName": "CrowdStrike",
|
||||
"price": "$352.09",
|
||||
"pct": "▼ 1.24%",
|
||||
"dirClass": "down",
|
||||
"sparkColor": "#ff4f6d",
|
||||
"sparkPoints": "0,14 20,12 40,16 60,14 80,18 100,20 120,18 140,22 160,24 180,28 200,30",
|
||||
"volume": "3.24M",
|
||||
"exchName": "NASDAQ",
|
||||
"exchUrl": "https://www.nasdaq.com/market-activity/stocks/crwd"
|
||||
},
|
||||
{
|
||||
"symbol": "SMCI",
|
||||
"companyName": "Super Micro",
|
||||
"price": "$48.93",
|
||||
"pct": "▲ 5.62%",
|
||||
"dirClass": "up",
|
||||
"sparkColor": "#22e58c",
|
||||
"sparkPoints": "0,30 20,32 40,28 60,30 80,26 100,20 120,16 140,18 160,12 180,8 200,4",
|
||||
"volume": "18.2M",
|
||||
"exchName": "NASDAQ",
|
||||
"exchUrl": "https://www.nasdaq.com/market-activity/stocks/smci"
|
||||
},
|
||||
{
|
||||
"symbol": "UBER",
|
||||
"companyName": "Uber Technologies",
|
||||
"price": "$72.41",
|
||||
"pct": "▲ 1.18%",
|
||||
"dirClass": "up",
|
||||
"sparkColor": "#22e58c",
|
||||
"sparkPoints": "0,24 20,26 40,22 60,20 80,22 100,18 120,16 140,18 160,14 180,12 200,12",
|
||||
"volume": "7.42M",
|
||||
"exchName": "NYSE",
|
||||
"exchUrl": "https://www.nyse.com/quote/XNYS:UBER"
|
||||
},
|
||||
{
|
||||
"symbol": "NFLX",
|
||||
"companyName": "Netflix",
|
||||
"price": "$728.50",
|
||||
"pct": "▼ 0.66%",
|
||||
"dirClass": "down",
|
||||
"sparkColor": "#ff4f6d",
|
||||
"sparkPoints": "0,16 20,14 40,18 60,16 80,18 100,16 120,20 140,18 160,22 180,20 200,24",
|
||||
"volume": "2.86M",
|
||||
"exchName": "NASDAQ",
|
||||
"exchUrl": "https://www.nasdaq.com/market-activity/stocks/nflx"
|
||||
}
|
||||
],
|
||||
|
||||
"newsPanel": {
|
||||
"title": "Live signals & news",
|
||||
"subtitle": "Filtered to your book"
|
||||
},
|
||||
"news": [
|
||||
{
|
||||
"time": "14:31",
|
||||
"source": "AI · Quiver Research",
|
||||
"headline": "▲ Hyperscaler capex tracker rolls forward — MSFT + META combined FY '25 guide now +34% YoY, reinforcing NVDA/SMCI thesis.",
|
||||
"impactLabel": "+ Bull",
|
||||
"impactClass": "pos"
|
||||
},
|
||||
{
|
||||
"time": "14:18",
|
||||
"source": "Bloomberg",
|
||||
"headline": "Tesla cuts Model Y prices in China by 6% as BYD pressure intensifies in Q2 sales window.",
|
||||
"impactLabel": "High risk",
|
||||
"impactClass": "high"
|
||||
},
|
||||
{
|
||||
"time": "13:54",
|
||||
"source": "Reuters",
|
||||
"headline": "FOMC minutes signal one more cut probable in Sept — yields fall 9 bps on the long end.",
|
||||
"impactLabel": "+ Bull",
|
||||
"impactClass": "pos"
|
||||
},
|
||||
{
|
||||
"time": "13:30",
|
||||
"source": "AI · Earnings whisper",
|
||||
"headline": "CRWD whisper revised −2.1% after channel checks show enterprise renewal slippage.",
|
||||
"impactLabel": "Watch",
|
||||
"impactClass": "med"
|
||||
},
|
||||
{
|
||||
"time": "12:48",
|
||||
"source": "CNBC",
|
||||
"headline": "Apple confirms Vision Pro 2 component orders for Q4 — 1.4M unit run rate implied.",
|
||||
"impactLabel": "+ Bull",
|
||||
"impactClass": "pos"
|
||||
},
|
||||
{
|
||||
"time": "11:22",
|
||||
"source": "AI · Anomaly detector",
|
||||
"headline": "▲ SMCI dark-pool prints up 220% vs 30-day baseline — likely block accumulation.",
|
||||
"impactLabel": "Watch",
|
||||
"impactClass": "med"
|
||||
}
|
||||
],
|
||||
|
||||
"footer": {
|
||||
"live": "Live data — refresh on demand",
|
||||
"source": "Quotes via IEX Cloud · Robinhood + IBKR linked",
|
||||
"version": "Quiver Live · v1.4.0"
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"generatedAt": "2026-05-06T19:32:18.000Z",
|
||||
"generatedBy": "agent",
|
||||
"notes": "Personal trading portfolio dashboard — total value, today's P&L, featured ticker chart, holdings table, watchlist, news/signals, and an AI-generated investment recommendation. Designed to be refreshable on demand: price/holdings/news fields can be rewritten in data.json without re-authoring the template. All numbers, percentages, and SVG polyline points are pre-formatted into display-ready strings so the template stays free of formatting/expression logic; direction is encoded via an explicit dirClass field. External links (exchange, Yahoo, TradingView, per-row exchange page) are bound as plain attribute interpolations — no inline scripting. Refresh runner should write a fresh data.json that matches the template's binding shape; daemon validates against bounded JSON limits and re-derives index.html from template.html + data.json. No credentials, OAuth tokens, cookies, raw provider responses, or HTTP envelopes are persisted; forbidden keys (raw, payload, body, headers, cookie, authorization, token, secret, credential, password) are not present in data.json.",
|
||||
"sources": [
|
||||
{
|
||||
"label": "Brokerage holdings (mocked)",
|
||||
"type": "connector"
|
||||
},
|
||||
{
|
||||
"label": "Quotes (mocked)",
|
||||
"type": "connector"
|
||||
},
|
||||
{
|
||||
"label": "AI recommendation",
|
||||
"type": "derived"
|
||||
}
|
||||
]
|
||||
}
|
||||
+1071
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user