heyiam 0.2.28 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. package/README.md +45 -0
  2. package/dist/config.js +10 -1
  3. package/dist/db.js +1 -2
  4. package/dist/export.js +40 -25
  5. package/dist/format-utils.js +5 -0
  6. package/dist/index.js +168 -0
  7. package/dist/mount.js +300 -102
  8. package/dist/parsers/claude.js +2 -28
  9. package/dist/parsers/codex.js +2 -26
  10. package/dist/parsers/cursor.js +2 -26
  11. package/dist/parsers/duration.js +35 -0
  12. package/dist/parsers/gemini.js +2 -20
  13. package/dist/parsers/types.js +0 -1
  14. package/dist/public/assets/index-BZ65TU_Y.js +40 -0
  15. package/dist/public/assets/index-CqCaW2cb.css +1 -0
  16. package/dist/public/index.html +2 -2
  17. package/dist/redact.js +4 -104
  18. package/dist/render/build-render-data.js +9 -2
  19. package/dist/render/index.js +32 -5
  20. package/dist/render/liquid.js +147 -7
  21. package/dist/render/mock-data.js +303 -0
  22. package/dist/render/templates/aurora/portfolio.liquid +204 -0
  23. package/dist/render/templates/aurora/project.liquid +260 -0
  24. package/dist/render/templates/aurora/session.liquid +223 -0
  25. package/dist/render/templates/aurora/styles.css +1178 -0
  26. package/dist/render/templates/bauhaus/portfolio.liquid +179 -0
  27. package/dist/render/templates/bauhaus/project.liquid +300 -0
  28. package/dist/render/templates/bauhaus/session.liquid +333 -0
  29. package/dist/render/templates/bauhaus/styles.css +1641 -0
  30. package/dist/render/templates/blueprint/portfolio.liquid +167 -0
  31. package/dist/render/templates/blueprint/project.liquid +286 -0
  32. package/dist/render/templates/blueprint/session.liquid +248 -0
  33. package/dist/render/templates/blueprint/styles.css +1285 -0
  34. package/dist/render/templates/canvas/portfolio.liquid +215 -0
  35. package/dist/render/templates/canvas/project.liquid +235 -0
  36. package/dist/render/templates/canvas/session.liquid +223 -0
  37. package/dist/render/templates/canvas/styles.css +1436 -0
  38. package/dist/render/templates/carbon/portfolio.liquid +170 -0
  39. package/dist/render/templates/carbon/project.liquid +249 -0
  40. package/dist/render/templates/carbon/session.liquid +190 -0
  41. package/dist/render/templates/carbon/styles.css +1091 -0
  42. package/dist/render/templates/chalk/portfolio.liquid +199 -0
  43. package/dist/render/templates/chalk/project.liquid +245 -0
  44. package/dist/render/templates/chalk/session.liquid +215 -0
  45. package/dist/render/templates/chalk/styles.css +1157 -0
  46. package/dist/render/templates/circuit/portfolio.liquid +162 -0
  47. package/dist/render/templates/circuit/project.liquid +247 -0
  48. package/dist/render/templates/circuit/session.liquid +205 -0
  49. package/dist/render/templates/circuit/styles.css +1403 -0
  50. package/dist/render/templates/cosmos/portfolio.liquid +232 -0
  51. package/dist/render/templates/cosmos/project.liquid +327 -0
  52. package/dist/render/templates/cosmos/session.liquid +239 -0
  53. package/dist/render/templates/cosmos/styles.css +1151 -0
  54. package/dist/render/templates/daylight/portfolio.liquid +217 -0
  55. package/dist/render/templates/daylight/project.liquid +229 -0
  56. package/dist/render/templates/daylight/session.liquid +219 -0
  57. package/dist/render/templates/daylight/styles.css +1311 -0
  58. package/dist/render/templates/editorial/portfolio.liquid +126 -0
  59. package/dist/render/templates/editorial/project.liquid +202 -0
  60. package/dist/render/templates/editorial/session.liquid +171 -0
  61. package/dist/render/templates/editorial/styles.css +822 -0
  62. package/dist/render/templates/ember/portfolio.liquid +318 -0
  63. package/dist/render/templates/ember/project.liquid +232 -0
  64. package/dist/render/templates/ember/session.liquid +202 -0
  65. package/dist/render/templates/ember/styles.css +1283 -0
  66. package/dist/render/templates/glacier/portfolio.liquid +271 -0
  67. package/dist/render/templates/glacier/project.liquid +288 -0
  68. package/dist/render/templates/glacier/session.liquid +217 -0
  69. package/dist/render/templates/glacier/styles.css +1200 -0
  70. package/dist/render/templates/grid/portfolio.liquid +265 -0
  71. package/dist/render/templates/grid/project.liquid +306 -0
  72. package/dist/render/templates/grid/session.liquid +260 -0
  73. package/dist/render/templates/grid/styles.css +1441 -0
  74. package/dist/render/templates/kinetic/portfolio.liquid +170 -0
  75. package/dist/render/templates/kinetic/project.liquid +242 -0
  76. package/dist/render/templates/kinetic/session.liquid +228 -0
  77. package/dist/render/templates/kinetic/styles.css +944 -0
  78. package/dist/render/templates/meridian/portfolio.liquid +255 -0
  79. package/dist/render/templates/meridian/project.liquid +376 -0
  80. package/dist/render/templates/meridian/session.liquid +298 -0
  81. package/dist/render/templates/meridian/styles.css +1369 -0
  82. package/dist/render/templates/minimal/portfolio.liquid +71 -0
  83. package/dist/render/templates/minimal/project.liquid +154 -0
  84. package/dist/render/templates/minimal/session.liquid +140 -0
  85. package/dist/render/templates/minimal/styles.css +525 -0
  86. package/dist/render/templates/mono/portfolio.liquid +291 -0
  87. package/dist/render/templates/mono/project.liquid +275 -0
  88. package/dist/render/templates/mono/session.liquid +276 -0
  89. package/dist/render/templates/mono/styles.css +1016 -0
  90. package/dist/render/templates/neon/portfolio.liquid +217 -0
  91. package/dist/render/templates/neon/project.liquid +225 -0
  92. package/dist/render/templates/neon/session.liquid +195 -0
  93. package/dist/render/templates/neon/styles.css +1265 -0
  94. package/dist/render/templates/noir/portfolio.liquid +137 -0
  95. package/dist/render/templates/noir/project.liquid +220 -0
  96. package/dist/render/templates/noir/session.liquid +241 -0
  97. package/dist/render/templates/noir/styles.css +1223 -0
  98. package/dist/render/templates/obsidian/portfolio.liquid +257 -0
  99. package/dist/render/templates/obsidian/project.liquid +280 -0
  100. package/dist/render/templates/obsidian/session.liquid +241 -0
  101. package/dist/render/templates/obsidian/styles.css +1401 -0
  102. package/dist/render/templates/paper/portfolio.liquid +267 -0
  103. package/dist/render/templates/paper/project.liquid +235 -0
  104. package/dist/render/templates/paper/session.liquid +271 -0
  105. package/dist/render/templates/paper/styles.css +1509 -0
  106. package/dist/render/templates/parallax/portfolio.liquid +305 -0
  107. package/dist/render/templates/parallax/project.liquid +275 -0
  108. package/dist/render/templates/parallax/session.liquid +295 -0
  109. package/dist/render/templates/parallax/styles.css +1874 -0
  110. package/dist/render/templates/parchment/portfolio.liquid +290 -0
  111. package/dist/render/templates/parchment/project.liquid +289 -0
  112. package/dist/render/templates/parchment/session.liquid +346 -0
  113. package/dist/render/templates/parchment/styles.css +1397 -0
  114. package/dist/render/templates/partials/_beats.liquid +16 -0
  115. package/dist/render/templates/partials/_breadcrumb.liquid +9 -0
  116. package/dist/render/templates/partials/_footer.liquid +7 -0
  117. package/dist/render/templates/partials/_growth-chart.liquid +7 -0
  118. package/dist/render/templates/partials/_key-decisions.liquid +20 -0
  119. package/dist/render/templates/partials/_links.liquid +16 -0
  120. package/dist/render/templates/partials/_narrative.liquid +8 -0
  121. package/dist/render/templates/partials/_phases.liquid +20 -0
  122. package/dist/render/templates/partials/_portfolio-header.liquid +20 -0
  123. package/dist/render/templates/partials/_portfolio-projects.liquid +16 -0
  124. package/dist/render/templates/partials/_portfolio-stats.liquid +19 -0
  125. package/dist/render/templates/partials/_qa.liquid +13 -0
  126. package/dist/render/templates/partials/_screenshot.liquid +15 -0
  127. package/dist/render/templates/partials/_session-cards.liquid +30 -0
  128. package/dist/render/templates/partials/_session-header.liquid +39 -0
  129. package/dist/render/templates/partials/_session-sidebar.liquid +30 -0
  130. package/dist/render/templates/partials/_skills.liquid +12 -0
  131. package/dist/render/templates/partials/_source-breakdown.liquid +22 -0
  132. package/dist/render/templates/partials/_stats.liquid +38 -0
  133. package/dist/render/templates/partials/_work-timeline.liquid +7 -0
  134. package/dist/render/templates/project.liquid +7 -4
  135. package/dist/render/templates/radar/portfolio.liquid +233 -0
  136. package/dist/render/templates/radar/project.liquid +278 -0
  137. package/dist/render/templates/radar/session.liquid +300 -0
  138. package/dist/render/templates/radar/styles.css +1049 -0
  139. package/dist/render/templates/showcase/portfolio.liquid +231 -0
  140. package/dist/render/templates/showcase/project.liquid +237 -0
  141. package/dist/render/templates/showcase/session.liquid +210 -0
  142. package/dist/render/templates/showcase/styles.css +1279 -0
  143. package/dist/render/templates/signal/portfolio.liquid +227 -0
  144. package/dist/render/templates/signal/project.liquid +278 -0
  145. package/dist/render/templates/signal/session.liquid +282 -0
  146. package/dist/render/templates/signal/styles.css +1395 -0
  147. package/dist/render/templates/strata/portfolio.liquid +192 -0
  148. package/dist/render/templates/strata/project.liquid +282 -0
  149. package/dist/render/templates/strata/session.liquid +261 -0
  150. package/dist/render/templates/strata/styles.css +1350 -0
  151. package/dist/render/templates/styles.css +1190 -0
  152. package/dist/render/templates/terminal/portfolio.liquid +118 -0
  153. package/dist/render/templates/terminal/project.liquid +161 -0
  154. package/dist/render/templates/terminal/session.liquid +145 -0
  155. package/dist/render/templates/terminal/styles.css +492 -0
  156. package/dist/render/templates/verdant/portfolio.liquid +333 -0
  157. package/dist/render/templates/verdant/project.liquid +309 -0
  158. package/dist/render/templates/verdant/session.liquid +237 -0
  159. package/dist/render/templates/verdant/styles.css +1257 -0
  160. package/dist/render/templates/zen/portfolio.liquid +136 -0
  161. package/dist/render/templates/zen/project.liquid +187 -0
  162. package/dist/render/templates/zen/session.liquid +203 -0
  163. package/dist/render/templates/zen/styles.css +1207 -0
  164. package/dist/render/templates.js +90 -0
  165. package/dist/routes/context.js +15 -10
  166. package/dist/routes/enhance.js +17 -40
  167. package/dist/routes/export.js +14 -4
  168. package/dist/routes/preview.js +480 -108
  169. package/dist/routes/projects.js +11 -19
  170. package/dist/routes/publish.js +15 -17
  171. package/dist/routes/settings.js +94 -1
  172. package/dist/routes/sse.js +9 -0
  173. package/dist/server.js +8 -2
  174. package/dist/settings.js +17 -9
  175. package/package.json +2 -4
  176. package/dist/public/assets/index-B_d6DlEI.js +0 -21
  177. package/dist/public/assets/index-Dalqz2mC.css +0 -1
@@ -0,0 +1,333 @@
1
+ <div class="heyiam-portfolio verdant" data-render-version="2" data-template="verdant" data-username="{{ user.username }}">
2
+
3
+ <div class="vd-page">
4
+
5
+ <main id="main-content">
6
+
7
+ {% if hasProfile %}
8
+ {%- comment -%} Hero / Profile {%- endcomment -%}
9
+ <section class="vd-section vd-hero" aria-label="Profile">
10
+ {% if user.photoUrl %}
11
+ <img src="{{ user.photoUrl }}" alt="{{ user.displayName }}" class="vd-hero__photo" width="120" height="120">
12
+ {% elsif user.displayName != blank %}
13
+ <div class="vd-hero__avatar" aria-hidden="true">{{ user.displayName | slice: 0 }}</div>
14
+ {% endif %}
15
+ {% if user.displayName != blank %}
16
+ <h1>{{ user.displayName }}</h1>
17
+ {% endif %}
18
+ {% if user.bio != blank %}
19
+ <p class="vd-hero__bio">{{ user.bio }}</p>
20
+ {% endif %}
21
+ {% if user.location != blank %}
22
+ <p class="vd-hero__location">
23
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0118 0z"/><circle cx="12" cy="10" r="3"/></svg>
24
+ {{ user.location }}
25
+ </p>
26
+ {% endif %}
27
+ {% if user.email or user.linkedinUrl or user.githubUrl or user.twitterHandle or user.websiteUrl or user.resumeUrl %}
28
+ <div class="vd-hero__links">
29
+ {% if user.email %}
30
+ <a href="mailto:{{ user.email }}">
31
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="m2 4 10 8 10-8"/></svg>
32
+ {{ user.email }}
33
+ </a>
34
+ {% endif %}
35
+ {% if user.phone %}
36
+ <a href="tel:{{ user.phone }}">
37
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M22 16.92v3a2 2 0 01-2.18 2 19.79 19.79 0 01-8.63-3.07 19.5 19.5 0 01-6-6 19.79 19.79 0 01-3.07-8.67A2 2 0 014.11 2h3a2 2 0 012 1.72c.127.96.361 1.903.7 2.81a2 2 0 01-.45 2.11L8.09 9.91a16 16 0 006 6l1.27-1.27a2 2 0 012.11-.45c.907.339 1.85.573 2.81.7A2 2 0 0122 16.92z"/></svg>
38
+ {{ user.phone }}
39
+ </a>
40
+ {% endif %}
41
+ {% if user.linkedinUrl %}
42
+ <a href="{{ user.linkedinUrl }}">
43
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M20.5 2h-17A1.5 1.5 0 002 3.5v17A1.5 1.5 0 003.5 22h17a1.5 1.5 0 001.5-1.5v-17A1.5 1.5 0 0020.5 2zM8 19H5v-9h3zM6.5 8.25A1.75 1.75 0 118.3 6.5a1.78 1.78 0 01-1.8 1.75zM19 19h-3v-4.74c0-1.42-.6-1.93-1.38-1.93A1.74 1.74 0 0013 14.19V19h-3v-9h2.9v1.3a3.11 3.11 0 012.7-1.4c1.55 0 3.36.86 3.36 3.66z"/></svg>
44
+ LinkedIn
45
+ </a>
46
+ {% endif %}
47
+ {% if user.githubUrl %}
48
+ <a href="{{ user.githubUrl }}">
49
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z"/></svg>
50
+ GitHub
51
+ </a>
52
+ {% endif %}
53
+ {% if user.twitterHandle %}
54
+ <a href="https://x.com/{{ user.twitterHandle }}">
55
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>
56
+ @{{ user.twitterHandle }}
57
+ </a>
58
+ {% endif %}
59
+ {% if user.websiteUrl %}
60
+ <a href="{{ user.websiteUrl }}">{{ user.websiteUrl | stripProtocol }}</a>
61
+ {% endif %}
62
+ {% if user.resumeUrl %}
63
+ <a href="{{ user.resumeUrl }}">
64
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="12" y1="18" x2="12" y2="12"/><polyline points="9 15 12 18 15 15"/></svg>
65
+ Resume (PDF)
66
+ </a>
67
+ {% endif %}
68
+ </div>
69
+ {% endif %}
70
+ </section>
71
+ {% endif %}
72
+
73
+ {%- comment -%} Leaf Divider {%- endcomment -%}
74
+ <div class="vd-leaf-divider" role="separator">
75
+ <div class="vd-leaf-divider__line"></div>
76
+ <svg class="vd-leaf-divider__icon" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
77
+ <path d="M17 8C8 10 5.9 16.17 3.82 21.34l1.89.66.95-2.3c.48.17.98.3 1.34.3C19 20 22 3 22 3c-1 2-8 2.25-13 3.25S2 11.5 2 13.5s1.75 3.75 1.75 3.75"/>
78
+ </svg>
79
+ <div class="vd-leaf-divider__line"></div>
80
+ </div>
81
+
82
+ {%- comment -%} Aggregate Stats {%- endcomment -%}
83
+ <section class="vd-section" aria-label="Statistics">
84
+ <div class="vd-stats">
85
+ <div class="vd-stat-card">
86
+ <div class="vd-stat-value">{{ projects.size }}</div>
87
+ <div class="vd-stat-label">Projects</div>
88
+ </div>
89
+ <div class="vd-stat-card">
90
+ <div class="vd-stat-value">{{ totalSessions }}</div>
91
+ <div class="vd-stat-label">Sessions</div>
92
+ </div>
93
+ {% if efficiencyMultiplier %}
94
+ <div class="vd-stat-card">
95
+ {% assign totalCombined = totalDurationMinutes | plus: totalAgentDurationMinutes %}
96
+ {% if totalCombined > 0 %}
97
+ {% assign humanPct = totalDurationMinutes | times: 100 | divided_by: totalCombined %}
98
+ {% assign agentPct = 100 | minus: humanPct %}
99
+ <div style="display: flex; align-items: center; gap: 4px; margin-bottom: 6px;">
100
+ <div style="flex: {{ humanPct }}; height: 6px; border-radius: 3px; background: rgba(0,0,0,0.12);"></div>
101
+ <div style="flex: {{ agentPct }}; height: 6px; border-radius: 3px; background: var(--vd-accent);"></div>
102
+ </div>
103
+ {% endif %}
104
+ <div style="display: flex; justify-content: space-between; align-items: baseline;">
105
+ <div class="vd-stat-value" style="font-size: 1.125rem;">{{ totalDurationMinutes | formatDuration }}</div>
106
+ <div class="vd-stat-value" style="font-size: 1.125rem; color: var(--vd-accent);">{{ totalAgentDurationMinutes | formatDuration }}</div>
107
+ </div>
108
+ <div style="display: flex; justify-content: space-between;">
109
+ <div class="vd-stat-label">You</div>
110
+ <div class="vd-stat-label">Agents</div>
111
+ </div>
112
+ </div>
113
+ <div class="vd-stat-card">
114
+ <div class="vd-stat-value" style="color: var(--vd-accent);">{{ efficiencyMultiplier }}</div>
115
+ <div class="vd-stat-label">Multiplier</div>
116
+ </div>
117
+ {% else %}
118
+ <div class="vd-stat-card">
119
+ <div class="vd-stat-value">{{ totalDurationMinutes | formatDuration }}</div>
120
+ <div class="vd-stat-label">Time</div>
121
+ </div>
122
+ {% endif %}
123
+ <div class="vd-stat-card">
124
+ <div class="vd-stat-value">{{ totalLoc | formatLoc }}</div>
125
+ <div class="vd-stat-label">Lines Changed</div>
126
+ </div>
127
+ </div>
128
+ </section>
129
+
130
+ {%- comment -%} Leaf Divider {%- endcomment -%}
131
+ <div class="vd-leaf-divider" role="separator">
132
+ <div class="vd-leaf-divider__line"></div>
133
+ <svg class="vd-leaf-divider__icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
134
+ <path d="M6.5 21.5s2-3 2-6c0-4-3-6-3-6s3 1 6 1 7-2 7-2-1 4-1 7-1.5 6-1.5 6"/>
135
+ <path d="M8 14s-1.5-2-1.5-4 2-4.5 2-4.5"/>
136
+ </svg>
137
+ <div class="vd-leaf-divider__line"></div>
138
+ </div>
139
+
140
+ {%- comment -%} Projects {%- endcomment -%}
141
+ {% if projects.size > 0 %}
142
+ <section class="vd-section" aria-label="Projects">
143
+ <div class="vd-section-header">
144
+ <div class="vd-section-header__icon" aria-hidden="true">
145
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 01-2 2H4a2 2 0 01-2-2V5a2 2 0 012-2h5l2 3h9a2 2 0 012 2z"/></svg>
146
+ </div>
147
+ <h2>Projects</h2>
148
+ </div>
149
+
150
+ <div class="vd-projects-grid">
151
+ {% for p in projects %}
152
+ <a href="/{{ user.username }}/{{ p.slug }}" class="vd-project-card">
153
+ <div class="vd-project-card__header">
154
+ <h3>{{ p.title }}</h3>
155
+ <span class="vd-project-card__arrow" aria-hidden="true">&rarr;</span>
156
+ </div>
157
+ {% if p.narrative != blank %}
158
+ <p class="vd-project-card__narrative">{{ p.narrative }}</p>
159
+ {% endif %}
160
+ <div class="vd-project-card__meta">
161
+ <span>{{ p.totalSessions }} sessions</span>
162
+ <span>&middot;</span>
163
+ <span>{{ p.totalDurationMinutes | formatDuration }}</span>
164
+ <span>&middot;</span>
165
+ <span>{{ p.totalLoc | formatLoc }} LOC</span>
166
+ </div>
167
+ {% if p.skills.size > 0 %}
168
+ <div class="vd-skills" aria-label="Skills used">
169
+ {% for skill in p.skills %}
170
+ <span class="vd-skill-chip">{{ skill }}</span>
171
+ {% endfor %}
172
+ </div>
173
+ {% endif %}
174
+ {% if p.sourceCounts.size > 0 %}
175
+ {% assign pFirstSrc = p.sourceCounts | first %}
176
+ {% assign pFirstPct = pFirstSrc.count | times: 100.0 | divided_by: p.totalSessions | round %}
177
+ <div class="vd-source-bar" aria-label="Source mix: {{ pFirstSrc.tool }} {{ pFirstPct }}%">
178
+ <div class="vd-source-bar-fill" data-width="{{ pFirstPct }}"></div>
179
+ </div>
180
+ <div class="vd-source-labels">
181
+ {% for src in p.sourceCounts %}
182
+ {% assign sPct = src.count | times: 100.0 | divided_by: p.totalSessions | round %}
183
+ <span>{{ src.tool }} {{ sPct }}%</span>
184
+ {% endfor %}
185
+ </div>
186
+ {% endif %}
187
+ </a>
188
+ {% endfor %}
189
+ </div>
190
+ </section>
191
+ {% endif %}
192
+
193
+ {%- comment -%} Leaf Divider {%- endcomment -%}
194
+ {% if allSkills.size > 0 %}
195
+ <div class="vd-leaf-divider" role="separator">
196
+ <div class="vd-leaf-divider__line"></div>
197
+ <svg class="vd-leaf-divider__icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
198
+ <path d="M6.5 21.5s2-3 2-6c0-4-3-6-3-6s3 1 6 1 7-2 7-2-1 4-1 7-1.5 6-1.5 6"/>
199
+ <path d="M8 14s-1.5-2-1.5-4 2-4.5 2-4.5"/>
200
+ </svg>
201
+ <div class="vd-leaf-divider__line"></div>
202
+ </div>
203
+
204
+ {%- comment -%} Skills Garden {%- endcomment -%}
205
+ <section class="vd-section" aria-label="Skills overview">
206
+ <div class="vd-section-header">
207
+ <div class="vd-section-header__icon" aria-hidden="true">
208
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>
209
+ </div>
210
+ <h2>Skills Garden</h2>
211
+ </div>
212
+ <div class="vd-skills-garden">
213
+ {% for skill in allSkills %}
214
+ <span class="vd-skill-garden-chip{% if skill.projectCount >= 4 %} vd-skill-garden-chip--primary{% elsif skill.projectCount >= 2 %} vd-skill-garden-chip--secondary{% endif %}">{{ skill.name }}</span>
215
+ {% endfor %}
216
+ </div>
217
+ </section>
218
+ {% endif %}
219
+
220
+ {%- comment -%} Leaf Divider {%- endcomment -%}
221
+ {% if sourceCounts.size > 0 %}
222
+ <div class="vd-leaf-divider" role="separator">
223
+ <div class="vd-leaf-divider__line"></div>
224
+ <svg class="vd-leaf-divider__icon" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
225
+ <path d="M17 8C8 10 5.9 16.17 3.82 21.34l1.89.66.95-2.3c.48.17.98.3 1.34.3C19 20 22 3 22 3c-1 2-8 2.25-13 3.25S2 11.5 2 13.5s1.75 3.75 1.75 3.75"/>
226
+ </svg>
227
+ <div class="vd-leaf-divider__line"></div>
228
+ </div>
229
+
230
+ {%- comment -%} Source Overview {%- endcomment -%}
231
+ <section class="vd-section" aria-label="Source breakdown">
232
+ <div class="vd-section-header">
233
+ <div class="vd-section-header__icon" aria-hidden="true">
234
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M16 18l6-6-6-6"/><path d="M8 6l-6 6 6 6"/></svg>
235
+ </div>
236
+ <h2>Source Breakdown</h2>
237
+ </div>
238
+ <div class="vd-source-overview">
239
+ {% for sc in sourceCounts %}
240
+ {% assign pct = sc.count | times: 100 | divided_by: totalSourceSessions %}
241
+ <div class="vd-source-card">
242
+ <div class="vd-source-card__pct">{{ pct }}%</div>
243
+ <div class="vd-source-card__label">{{ sc.tool }}</div>
244
+ <div class="vd-source-card__sessions">{{ sc.count }} sessions</div>
245
+ </div>
246
+ {% endfor %}
247
+ </div>
248
+ </section>
249
+ {% endif %}
250
+
251
+ {%- comment -%} Leaf Divider {%- endcomment -%}
252
+ <div class="vd-leaf-divider" role="separator">
253
+ <div class="vd-leaf-divider__line"></div>
254
+ <svg class="vd-leaf-divider__icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
255
+ <path d="M6.5 21.5s2-3 2-6c0-4-3-6-3-6s3 1 6 1 7-2 7-2-1 4-1 7-1.5 6-1.5 6"/>
256
+ <path d="M8 14s-1.5-2-1.5-4 2-4.5 2-4.5"/>
257
+ </svg>
258
+ <div class="vd-leaf-divider__line"></div>
259
+ </div>
260
+
261
+ {%- comment -%} Monthly Activity Timeline {%- endcomment -%}
262
+ {% if activityByMonth.size > 0 %}
263
+ <section class="vd-section" aria-label="Monthly activity">
264
+ <div class="vd-section-header">
265
+ <div class="vd-section-header__icon" aria-hidden="true">
266
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
267
+ </div>
268
+ <h2>Monthly Activity</h2>
269
+ </div>
270
+ {% assign maxMonthlySessions = 0 %}
271
+ {% for m in activityByMonth %}
272
+ {% if m.sessions > maxMonthlySessions %}{% assign maxMonthlySessions = m.sessions %}{% endif %}
273
+ {% endfor %}
274
+ <div class="vd-timeline-compact" id="monthly-bars">
275
+ {% for m in activityByMonth %}
276
+ <div class="vd-timeline-month">
277
+ <span class="vd-timeline-month__label">{{ m.month }}</span>
278
+ <div class="vd-timeline-month__bar">
279
+ {% if maxMonthlySessions > 0 %}
280
+ {% assign fillWidth = m.sessions | times: 100 | divided_by: maxMonthlySessions %}
281
+ {% else %}
282
+ {% assign fillWidth = 0 %}
283
+ {% endif %}
284
+ <div class="vd-timeline-month__fill" data-width="{{ fillWidth }}"></div>
285
+ </div>
286
+ <span class="vd-timeline-month__count">{{ m.sessions }} session{% if m.sessions != 1 %}s{% endif %}</span>
287
+ </div>
288
+ {% endfor %}
289
+ </div>
290
+ </section>
291
+ {% endif %}
292
+
293
+ {%- comment -%} Activity Heatmap Grid {%- endcomment -%}
294
+ {% if activityByDay.size > 0 %}
295
+ <section class="vd-section" aria-label="Activity heatmap" style="margin-top: 2rem;">
296
+ <div class="vd-section-header">
297
+ <div class="vd-section-header__icon" aria-hidden="true">
298
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg>
299
+ </div>
300
+ <h2>Activity Heatmap</h2>
301
+ </div>
302
+ <div class="vd-activity-grid" aria-label="Weekly activity heatmap, darker means more activity">
303
+ {% for day in activityByDay %}
304
+ {% if day.count >= 4 %}{% assign level = 4 %}
305
+ {% elsif day.count >= 3 %}{% assign level = 3 %}
306
+ {% elsif day.count >= 2 %}{% assign level = 2 %}
307
+ {% elsif day.count >= 1 %}{% assign level = 1 %}
308
+ {% else %}{% assign level = 0 %}{% endif %}
309
+ <div class="vd-activity-cell{% if level > 0 %} vd-activity-cell--l{{ level }}{% endif %}" title="{{ day.date }}: {{ day.count }} sessions"></div>
310
+ {% endfor %}
311
+ </div>
312
+ <div class="vd-activity-legend">
313
+ <span>Less</span>
314
+ <div class="vd-activity-legend__cell" data-level="0"></div>
315
+ <div class="vd-activity-legend__cell" data-level="1"></div>
316
+ <div class="vd-activity-legend__cell" data-level="2"></div>
317
+ <div class="vd-activity-legend__cell" data-level="3"></div>
318
+ <div class="vd-activity-legend__cell" data-level="4"></div>
319
+ <span>More</span>
320
+ </div>
321
+ </section>
322
+ {% endif %}
323
+
324
+ </main>
325
+
326
+ <footer class="vd-footer">
327
+ <p>Built with <a href="https://heyi.am">heyi.am</a> &mdash; the signal-first portfolio</p>
328
+ </footer>
329
+
330
+ </div>
331
+
332
+
333
+ </div>
@@ -0,0 +1,309 @@
1
+ <div class="heyiam-project verdant" data-render-version="2" data-template="verdant"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
2
+
3
+ <div class="vd-page">
4
+
5
+ <main id="main-content">
6
+
7
+ {%- comment -%} Breadcrumb {%- endcomment -%}
8
+ <nav class="vd-breadcrumb" aria-label="Breadcrumb">
9
+ <a href="/{{ user.username }}">{{ user.username }}</a>
10
+ <span class="vd-breadcrumb__sep" aria-hidden="true">/</span>
11
+ <span>{{ project.slug }}</span>
12
+ </nav>
13
+
14
+ {%- comment -%} Project Header {%- endcomment -%}
15
+ <section class="vd-section vd-project-header" aria-label="Project header">
16
+ <h1>{{ project.title }}</h1>
17
+ {% if project.repoUrl or project.projectUrl %}
18
+ <div class="vd-project-links">
19
+ {% if project.repoUrl %}
20
+ <a href="{{ project.repoUrl }}" class="vd-project-link" target="_blank" rel="noopener">
21
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 00-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0020 4.77 5.07 5.07 0 0019.91 1S18.73.65 16 2.48a13.38 13.38 0 00-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 005 4.77a5.44 5.44 0 00-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 009 18.13V22"/></svg>
22
+ {{ project.repoUrl | stripProtocol }}
23
+ </a>
24
+ {% endif %}
25
+ {% if project.projectUrl %}
26
+ <a href="{{ project.projectUrl }}" class="vd-project-link" target="_blank" rel="noopener">
27
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>
28
+ {{ project.projectUrl | stripProtocol }}
29
+ </a>
30
+ {% endif %}
31
+ </div>
32
+ {% endif %}
33
+ </section>
34
+
35
+ {%- comment -%} Screenshot {%- endcomment -%}
36
+ {% if project.screenshotUrl %}
37
+ <section class="vd-section" aria-label="Screenshot">
38
+ <div class="vd-browser-chrome">
39
+ <div class="vd-browser-chrome__bar">
40
+ <span class="vd-browser-chrome__dot vd-browser-chrome__dot--red" aria-hidden="true"></span>
41
+ <span class="vd-browser-chrome__dot vd-browser-chrome__dot--yellow" aria-hidden="true"></span>
42
+ <span class="vd-browser-chrome__dot vd-browser-chrome__dot--green" aria-hidden="true"></span>
43
+ {% if project.projectUrl %}
44
+ <span class="vd-browser-chrome__url">{{ project.projectUrl | stripProtocol }}</span>
45
+ {% endif %}
46
+ </div>
47
+ <div class="vd-browser-chrome__viewport">
48
+ <img src="{{ project.screenshotUrl }}" alt="{{ project.title }} screenshot" style="width: 100%; height: 100%; object-fit: cover;">
49
+ </div>
50
+ </div>
51
+ </section>
52
+ {% endif %}
53
+
54
+ {%- comment -%} Leaf Divider {%- endcomment -%}
55
+ <div class="vd-leaf-divider" role="separator">
56
+ <div class="vd-leaf-divider__line"></div>
57
+ <svg class="vd-leaf-divider__icon" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
58
+ <path d="M17 8C8 10 5.9 16.17 3.82 21.34l1.89.66.95-2.3c.48.17.98.3 1.34.3C19 20 22 3 22 3c-1 2-8 2.25-13 3.25S2 11.5 2 13.5s1.75 3.75 1.75 3.75"/>
59
+ </svg>
60
+ <div class="vd-leaf-divider__line"></div>
61
+ </div>
62
+
63
+ {%- comment -%} Narrative {%- endcomment -%}
64
+ {% if project.narrative != blank %}
65
+ <section class="vd-section" aria-label="Narrative">
66
+ <div class="vd-section-header">
67
+ <div class="vd-section-header__icon" aria-hidden="true">
68
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 20h9"/><path d="M16.5 3.5a2.121 2.121 0 013 3L7 19l-4 1 1-4L16.5 3.5z"/></svg>
69
+ </div>
70
+ <h2>Narrative</h2>
71
+ </div>
72
+ <div class="vd-narrative">
73
+ {{ project.narrative }}
74
+ </div>
75
+ </section>
76
+ {% endif %}
77
+
78
+ {%- comment -%} Leaf Divider {%- endcomment -%}
79
+ <div class="vd-leaf-divider" role="separator">
80
+ <div class="vd-leaf-divider__line"></div>
81
+ <svg class="vd-leaf-divider__icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
82
+ <path d="M6.5 21.5s2-3 2-6c0-4-3-6-3-6s3 1 6 1 7-2 7-2-1 4-1 7-1.5 6-1.5 6"/>
83
+ <path d="M8 14s-1.5-2-1.5-4 2-4.5 2-4.5"/>
84
+ </svg>
85
+ <div class="vd-leaf-divider__line"></div>
86
+ </div>
87
+
88
+ {%- comment -%} Stats {%- endcomment -%}
89
+ <section class="vd-section" aria-label="Project statistics">
90
+ <div class="vd-stat-grid">
91
+ <div class="vd-stat-card">
92
+ <div class="vd-stat-value">{{ project.totalSessions }}</div>
93
+ <div class="vd-stat-label">Sessions</div>
94
+ </div>
95
+ {% if efficiencyMultiplier %}
96
+ <div class="vd-stat-card">
97
+ {% assign totalCombined = project.totalDurationMinutes | plus: project.totalAgentDurationMinutes %}
98
+ {% if totalCombined > 0 %}
99
+ {% assign humanPct = project.totalDurationMinutes | times: 100 | divided_by: totalCombined %}
100
+ {% assign agentPct = 100 | minus: humanPct %}
101
+ <div style="display: flex; align-items: center; gap: 4px; margin-bottom: 6px;">
102
+ <div style="flex: {{ humanPct }}; height: 6px; border-radius: 3px; background: rgba(0,0,0,0.12);"></div>
103
+ <div style="flex: {{ agentPct }}; height: 6px; border-radius: 3px; background: var(--vd-accent);"></div>
104
+ </div>
105
+ {% endif %}
106
+ <div style="display: flex; justify-content: space-between; align-items: baseline;">
107
+ <div class="vd-stat-value" style="font-size: 1.125rem;">{{ project.totalDurationMinutes | formatDuration }}</div>
108
+ <div class="vd-stat-value" style="font-size: 1.125rem; color: var(--vd-accent);">{{ project.totalAgentDurationMinutes | formatDuration }}</div>
109
+ </div>
110
+ <div style="display: flex; justify-content: space-between;">
111
+ <div class="vd-stat-label">You</div>
112
+ <div class="vd-stat-label">Agents</div>
113
+ </div>
114
+ </div>
115
+ <div class="vd-stat-card">
116
+ <div class="vd-stat-value" style="color: var(--vd-accent);">{{ efficiencyMultiplier }}</div>
117
+ <div class="vd-stat-label">Multiplier</div>
118
+ </div>
119
+ {% else %}
120
+ <div class="vd-stat-card">
121
+ <div class="vd-stat-value">{{ project.totalDurationMinutes | formatDuration }}</div>
122
+ <div class="vd-stat-label">Time</div>
123
+ </div>
124
+ {% endif %}
125
+ <div class="vd-stat-card">
126
+ <div class="vd-stat-value">{{ project.totalLoc | formatLoc }}</div>
127
+ <div class="vd-stat-label">Lines Changed</div>
128
+ </div>
129
+ <div class="vd-stat-card">
130
+ <div class="vd-stat-value">{{ project.totalFilesChanged }}</div>
131
+ <div class="vd-stat-label">Files</div>
132
+ </div>
133
+ {% if project.totalTokens %}
134
+ <div class="vd-stat-card">
135
+ <div class="vd-stat-value">{{ project.totalTokens | formatTokens }}</div>
136
+ <div class="vd-stat-label">Tokens</div>
137
+ </div>
138
+ {% endif %}
139
+ </div>
140
+ </section>
141
+
142
+ {%- comment -%} Phase Vine Timeline {%- endcomment -%}
143
+ {% if arc.size > 0 %}
144
+ <section class="vd-section" aria-label="Project phases">
145
+ <div class="vd-section-header">
146
+ <div class="vd-section-header__icon" aria-hidden="true">
147
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 20V10"/><path d="M18 20V4"/><path d="M6 20v-4"/></svg>
148
+ </div>
149
+ <h2>Growth Phases</h2>
150
+ </div>
151
+ <div class="vd-vine-timeline" role="list" aria-label="Phase timeline">
152
+ {% for phase in arc %}
153
+ <div class="vd-vine-phase" role="listitem">
154
+ <div class="vd-vine-node" aria-hidden="true"><div class="vd-vine-node__inner"></div></div>
155
+ <div class="vd-vine-phase__title">{{ phase.title }}</div>
156
+ {% if phase.dates %}
157
+ <div class="vd-vine-phase__dates">{{ phase.dates }}</div>
158
+ {% endif %}
159
+ <div class="vd-vine-phase__desc">{{ phase.description }}</div>
160
+ </div>
161
+ {% endfor %}
162
+ </div>
163
+ </section>
164
+
165
+ {%- comment -%} Leaf Divider {%- endcomment -%}
166
+ <div class="vd-leaf-divider" role="separator">
167
+ <div class="vd-leaf-divider__line"></div>
168
+ <svg class="vd-leaf-divider__icon" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
169
+ <path d="M17 8C8 10 5.9 16.17 3.82 21.34l1.89.66.95-2.3c.48.17.98.3 1.34.3C19 20 22 3 22 3c-1 2-8 2.25-13 3.25S2 11.5 2 13.5s1.75 3.75 1.75 3.75"/>
170
+ </svg>
171
+ <div class="vd-leaf-divider__line"></div>
172
+ </div>
173
+ {% endif %}
174
+
175
+ {%- comment -%} Work Timeline (CSS-only bars) {%- endcomment -%}
176
+ {% if featuredSessions.size > 0 %}
177
+ {% assign maxDur = 1 %}
178
+ {% for s in featuredSessions %}
179
+ {% if s.durationMinutes > maxDur %}{% assign maxDur = s.durationMinutes %}{% endif %}
180
+ {% endfor %}
181
+ <section class="vd-section" aria-label="Work timeline">
182
+ <div class="vd-section-header">
183
+ <div class="vd-section-header__icon" aria-hidden="true">
184
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
185
+ </div>
186
+ <h2>Work Timeline</h2>
187
+ </div>
188
+ <div class="vd-work-timeline" aria-label="Session timeline chart">
189
+ {% for s in featuredSessions %}
190
+ {% assign barPct = s.durationMinutes | times: 100 | divided_by: maxDur %}
191
+ <div class="vd-timeline-row">
192
+ <span class="vd-timeline-row__num">{{ forloop.index }}</span>
193
+ <div class="vd-timeline-bar">
194
+ <div class="vd-timeline-bar-fill" data-width="{{ barPct }}" aria-hidden="true"></div>
195
+ <span class="vd-timeline-bar__label">{{ s.title }}</span>
196
+ </div>
197
+ <span class="vd-timeline-row__dur">{{ s.durationMinutes }}m</span>
198
+ </div>
199
+ {% endfor %}
200
+ </div>
201
+ </section>
202
+ {% endif %}
203
+
204
+ {%- comment -%} Skills {%- endcomment -%}
205
+ {% if project.skills.size > 0 %}
206
+ <section class="vd-section" aria-label="Skills">
207
+ <div class="vd-section-header">
208
+ <div class="vd-section-header__icon" aria-hidden="true">
209
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>
210
+ </div>
211
+ <h2>Skills</h2>
212
+ </div>
213
+ <div class="vd-skills">
214
+ {% for skill in project.skills %}
215
+ <span class="vd-skill-chip">{{ skill }}</span>
216
+ {% endfor %}
217
+ </div>
218
+ </section>
219
+ {% endif %}
220
+
221
+ {%- comment -%} Key Decisions {%- endcomment -%}
222
+ {% if arc.size > 0 %}
223
+ <section class="vd-section" aria-label="Key decisions">
224
+ <div class="vd-section-header">
225
+ <div class="vd-section-header__icon" aria-hidden="true">
226
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 3h6a4 4 0 014 4v14a3 3 0 00-3-3H2z"/><path d="M22 3h-6a4 4 0 00-4 4v14a3 3 0 013-3h7z"/></svg>
227
+ </div>
228
+ <h2>Key Decisions</h2>
229
+ </div>
230
+ <div class="vd-decisions">
231
+ {% for item in arc %}
232
+ <div class="vd-decision"><strong>{{ forloop.index }}.</strong> {{ item.description }}</div>
233
+ {% endfor %}
234
+ </div>
235
+ </section>
236
+ {% endif %}
237
+
238
+ {%- comment -%} Source Breakdown {%- endcomment -%}
239
+ {% if sourceCounts.size > 0 %}
240
+ <section class="vd-section" aria-label="Source breakdown">
241
+ <div class="vd-section-header">
242
+ <div class="vd-section-header__icon" aria-hidden="true">
243
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M16 18l6-6-6-6"/><path d="M8 6l-6 6 6 6"/></svg>
244
+ </div>
245
+ <h2>Source Breakdown</h2>
246
+ </div>
247
+ <div class="vd-source-breakdown">
248
+ {% assign firstSource = sourceCounts | first %}
249
+ {% assign firstPct = firstSource.count | times: 100.0 | divided_by: project.totalSessions | round %}
250
+ <div class="vd-source-bar-wide" aria-label="{{ firstSource.tool }} {{ firstPct }}%">
251
+ <div class="vd-source-bar-wide__fill" data-width="{{ firstPct }}"></div>
252
+ </div>
253
+ <div class="vd-source-legend">
254
+ {% for source in sourceCounts %}
255
+ {% assign srcPct = source.count | times: 100.0 | divided_by: project.totalSessions | round %}
256
+ <span><span class="vd-source-legend__dot {% if forloop.first %}vd-source-legend__dot--claude{% else %}vd-source-legend__dot--cursor{% endif %}" aria-hidden="true"></span>{{ source.tool }} {{ srcPct }}%</span>
257
+ {% endfor %}
258
+ </div>
259
+ </div>
260
+ </section>
261
+ {% endif %}
262
+
263
+ {%- comment -%} Leaf Divider {%- endcomment -%}
264
+ <div class="vd-leaf-divider" role="separator">
265
+ <div class="vd-leaf-divider__line"></div>
266
+ <svg class="vd-leaf-divider__icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
267
+ <path d="M6.5 21.5s2-3 2-6c0-4-3-6-3-6s3 1 6 1 7-2 7-2-1 4-1 7-1.5 6-1.5 6"/>
268
+ <path d="M8 14s-1.5-2-1.5-4 2-4.5 2-4.5"/>
269
+ </svg>
270
+ <div class="vd-leaf-divider__line"></div>
271
+ </div>
272
+
273
+ {%- comment -%} Featured Sessions {%- endcomment -%}
274
+ {% if featuredSessions.size > 0 %}
275
+ <section class="vd-section" aria-label="Featured sessions">
276
+ <div class="vd-section-header">
277
+ <div class="vd-section-header__icon" aria-hidden="true">
278
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>
279
+ </div>
280
+ <h2>Featured Sessions</h2>
281
+ </div>
282
+ <div class="vd-sessions-grid">
283
+ {% for s in featuredSessions %}
284
+ <a href="{{ sessionBaseUrl }}/{{ s.slug }}{{ sessionSuffix }}" class="vd-session-card">
285
+ {% if s.sourceTool %}
286
+ <span class="vd-session-card__tag">{{ s.sourceTool }}</span>
287
+ {% endif %}
288
+ <h3>{{ s.title }}</h3>
289
+ <div class="vd-session-card__meta">
290
+ <span>{{ s.durationMinutes | formatDuration }}</span>
291
+ <span>{{ s.locChanged | formatLoc }} LOC</span>
292
+ <span>{{ s.filesChanged }} files</span>
293
+ </div>
294
+ </a>
295
+ {% endfor %}
296
+ </div>
297
+ </section>
298
+ {% endif %}
299
+
300
+ </main>
301
+
302
+ <footer class="vd-footer">
303
+ <p>Built with <a href="https://heyi.am">heyi.am</a> &mdash; the signal-first portfolio</p>
304
+ </footer>
305
+
306
+ </div>
307
+
308
+
309
+ </div>