product-planning(同步 26 改 65 新)
- 目录约定:docs/pm/<项目>/ ⇒ <工作区>/项目/<产品slug>/ 为产品根,下分 产品规划/ 与 产品原型/{designs,pages}
- ④段骨架六节 ⇒ 七节(新增「原型地址」;后两节改「页面导航」「原型状态取值」)
- ③段新增真机组件库;结案「源站只有暗色主题」,tokens.css(亮色)标为不可用于生产
- ③段改版规则反转为「一个产品只用一套原型、只留当前态」;②段新增 产品规划模板.md(表头唯一定义处)
- scripts:产品根定位重写(含 .registry/current-project 指针),新增 check_terms.py 与 selftest_product_root.py
session-mechanism(同步 3 改 2 新)
- references/06-技能优化规程.md 新增 §5「要求落在生成时看得见的面上」,总表 16 ⇒ 17 条;SKILL.md 同步
- scripts/collabctl.py 取回 _find_board() 候选定位(旧版写死 scripts/board.py,而看板已搬进 boards/会话看板/ ⇒ 旧版本就是坏的)
element-picker(新装)
- 按用户口径作「独立功能加入 product-planning」,非独立全局技能
- 修掉它自身三处路径漂移(技能根写法 ⇒ 同目录;README 两处旧落点;剪贴板示例文案与代码对齐)
工具归拢到技能级 assets/
- diagram-design / design-capture / element-picker / video-capture 从各段 assets/ 移到技能包 assets/
- 新增红线:本包有两处 assets/(技能级工具箱 vs 段内资产),引工具一律写「技能级 assets/<工具名>/」
- 路径口径由两类改三类(技能级工具箱 / 本段自带资产 / 同根技能)
不入库(磁盘保留)
- design-system-tiaoyue 整个目录(用户 2026-10-16 令):git rm -r --cached + .gitignore,理由=14 MB 且可 CDP 重采;
已在 ③段 SKILL.md 注明「取仓后这一层是空的属正常,用自带套需另取或重采」
- element-picker 的运行期存档 marks/(服务自己会建目录)
130 lines
9.5 KiB
HTML
130 lines
9.5 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Cloud budget bridge · Waterfall chart</title>
|
||
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||
<style>
|
||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||
:root {
|
||
--color-paper: #f5f5f5;
|
||
--color-ink: #2d3142;
|
||
--color-muted: #4f5d75;
|
||
--color-accent: #eb6c36;
|
||
--font-sans: 'Geist', system-ui, sans-serif;
|
||
--font-serif: 'Instrument Serif', serif;
|
||
--font-mono: 'Geist Mono', ui-monospace, monospace;
|
||
}
|
||
body { font-family: var(--font-sans); background: var(--color-paper); color: var(--color-ink); min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 3rem 2rem; }
|
||
.frame { max-width: 1200px; width: 100%; }
|
||
.eyebrow { font-family: var(--font-mono); font-size: 0.66rem; font-weight: 500; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.5rem; }
|
||
h1 { font-family: var(--font-serif); font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem); font-weight: 400; letter-spacing: -0.02em; line-height: 1.15; margin-bottom: 1.5rem; }
|
||
.diagram-container { width: 100%; overflow-x: auto; }
|
||
svg { width: 100%; min-width: 760px; display: block; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="frame">
|
||
<p class="eyebrow">Waterfall · Diagram Design</p>
|
||
<h1>Cloud budget bridge · FY25 → FY26</h1>
|
||
|
||
<div class="diagram-container">
|
||
<svg viewBox="0 0 1000 500" xmlns="http://www.w3.org/2000/svg" role="img" aria-labelledby="waterfall-title waterfall-desc">
|
||
<title id="waterfall-title">Cloud budget bridge · FY25 → FY26</title>
|
||
<desc id="waterfall-desc">Waterfall chart bridging the FY25 cloud budget of 240 thousand dollars a month to the FY26 plan of 236, through headcount growth, reserved-instance savings, new ML workloads, and storage tiering — the largest saving.</desc>
|
||
<defs>
|
||
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
|
||
<circle cx="1" cy="1" r="0.9" fill="rgba(45,49,66,0.10)"/>
|
||
</pattern>
|
||
</defs>
|
||
|
||
<rect width="100%" height="100%" fill="#f5f5f5"/>
|
||
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
|
||
|
||
<!-- Y-axis rotated label -->
|
||
<text transform="rotate(-90 24 230)" x="24" y="230" fill="#4f5d75" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">CLOUD SPEND · $K / MO</text>
|
||
|
||
<!-- Horizontal gridlines (drawn before bars). Domain 0–400, y = 420 − 0.95·v -->
|
||
<line x1="80" y1="325" x2="960" y2="325" stroke="rgba(45,49,66,0.08)" stroke-width="0.8"/>
|
||
<line x1="80" y1="230" x2="960" y2="230" stroke="rgba(45,49,66,0.08)" stroke-width="0.8"/>
|
||
<line x1="80" y1="135" x2="960" y2="135" stroke="rgba(45,49,66,0.08)" stroke-width="0.8"/>
|
||
<line x1="80" y1="40" x2="960" y2="40" stroke="rgba(45,49,66,0.06)" stroke-width="0.8"/>
|
||
|
||
<!-- Y-axis line and X-axis baseline (the domain floor: totals anchor here) -->
|
||
<line x1="80" y1="40" x2="80" y2="420" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
|
||
<line x1="80" y1="420" x2="960" y2="420" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
|
||
|
||
<!-- Y-axis tick labels -->
|
||
<text x="72" y="329" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">100</text>
|
||
<text x="72" y="234" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">200</text>
|
||
<text x="72" y="139" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">300</text>
|
||
<text x="72" y="44" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">400</text>
|
||
|
||
<!-- ─── CARRIES (drawn before bars; each transports the running total across a gap at its own level) ─── -->
|
||
<line x1="208" y1="192" x2="256" y2="192" stroke="rgba(45,49,66,0.55)" stroke-width="1" data-carry="240"/>
|
||
<line x1="352" y1="131" x2="400" y2="131" stroke="rgba(45,49,66,0.55)" stroke-width="1" data-carry="304"/>
|
||
<line x1="496" y1="167" x2="544" y2="167" stroke="rgba(45,49,66,0.55)" stroke-width="1" data-carry="266"/>
|
||
<line x1="640" y1="146" x2="688" y2="146" stroke="rgba(45,49,66,0.55)" stroke-width="1" data-carry="288"/>
|
||
<line x1="784" y1="196" x2="832" y2="196" stroke="rgba(45,49,66,0.55)" stroke-width="1" data-carry="236"/>
|
||
|
||
<!-- ─── BARS (width=96, pitch=144). Running total: 240 → 304 → 266 → 288 → 236 → 236 ─── -->
|
||
<!-- FY25 base: total 240, anchored at the floor. y(240)=192, h=228 -->
|
||
<rect x="112" y="192" width="96" height="228" fill="#f5f5f5"/>
|
||
<rect x="112" y="192" width="96" height="228" fill="rgba(45,49,66,0.08)" stroke="#2d3142" stroke-width="1" data-role="total" data-value="240" data-name="FY25 base"/>
|
||
<text x="160" y="184" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">240</text>
|
||
|
||
<!-- Headcount: +64 rises 240 → 304. y(304)=131, h=61 -->
|
||
<rect x="256" y="131" width="96" height="61" fill="#f5f5f5"/>
|
||
<rect x="256" y="131" width="96" height="61" fill="rgba(79,93,117,0.15)" stroke="#4f5d75" stroke-width="1" data-role="delta" data-value="+64" data-name="Headcount"/>
|
||
<text x="304" y="123" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">+64</text>
|
||
|
||
<!-- Reserved instances: −38 falls 304 → 266. Hollow = decrease. y=131, h=36 -->
|
||
<rect x="400" y="131" width="96" height="36" fill="#f5f5f5"/>
|
||
<rect x="400" y="131" width="96" height="36" fill="#f5f5f5" stroke="#4f5d75" stroke-width="1" data-role="delta" data-value="-38" data-name="Reserved inst."/>
|
||
<text x="448" y="179" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">−38</text>
|
||
|
||
<!-- ML workloads: +22 rises 266 → 288. y(288)=146, h=21 -->
|
||
<rect x="544" y="146" width="96" height="21" fill="#f5f5f5"/>
|
||
<rect x="544" y="146" width="96" height="21" fill="rgba(79,93,117,0.15)" stroke="#4f5d75" stroke-width="1" data-role="delta" data-value="+22" data-name="ML workloads"/>
|
||
<text x="592" y="138" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">+22</text>
|
||
|
||
<!-- Storage tiering FOCAL: −52 falls 288 → 236 — the largest saving, the story. y=146, h=50 -->
|
||
<rect x="688" y="146" width="96" height="50" fill="#f5f5f5"/>
|
||
<rect x="688" y="146" width="96" height="50" fill="rgba(235,108,54,0.12)" stroke="#eb6c36" stroke-width="1" data-role="delta" data-value="-52" data-name="Storage tiering"/>
|
||
<text x="736" y="208" fill="#2d3142" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" font-weight="600">−52</text>
|
||
|
||
<!-- FY26 plan: total 236 = 240 + 64 − 38 + 22 − 52, anchored at the floor. y(236)=196, h=224 -->
|
||
<rect x="832" y="196" width="96" height="224" fill="#f5f5f5"/>
|
||
<rect x="832" y="196" width="96" height="224" fill="rgba(45,49,66,0.08)" stroke="#2d3142" stroke-width="1" data-role="total" data-value="236" data-name="FY26 plan"/>
|
||
<text x="880" y="188" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">236</text>
|
||
|
||
<!-- X-axis category labels -->
|
||
<text x="160" y="440" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">FY25 base</text>
|
||
<text x="304" y="440" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Headcount</text>
|
||
<text x="448" y="440" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Reserved inst.</text>
|
||
<text x="592" y="440" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">ML workloads</text>
|
||
<text x="736" y="440" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Storage tiering</text>
|
||
<text x="880" y="440" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">FY26 plan</text>
|
||
|
||
<!-- Legend -->
|
||
<line x1="40" y1="462" x2="960" y2="462" stroke="rgba(45,49,66,0.10)" stroke-width="0.8"/>
|
||
<text x="40" y="478" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
|
||
|
||
<rect x="40" y="488" width="16" height="10" rx="2" fill="rgba(45,49,66,0.08)" stroke="#2d3142" stroke-width="1"/>
|
||
<text x="64" y="497" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Start / end total</text>
|
||
|
||
<rect x="200" y="488" width="16" height="10" rx="2" fill="rgba(79,93,117,0.15)" stroke="#4f5d75" stroke-width="1"/>
|
||
<text x="224" y="497" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Cost added</text>
|
||
|
||
<rect x="340" y="488" width="16" height="10" rx="2" fill="#f5f5f5" stroke="#4f5d75" stroke-width="1"/>
|
||
<text x="364" y="497" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Cost removed</text>
|
||
|
||
<rect x="480" y="488" width="16" height="10" rx="2" fill="rgba(235,108,54,0.12)" stroke="#eb6c36" stroke-width="1"/>
|
||
<text x="504" y="497" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Storage tiering · largest saving</text>
|
||
</svg>
|
||
</div>
|
||
</div>
|
||
</body>
|
||
</html>
|