heyiam 0.2.29 → 0.3.1

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 (186) hide show
  1. package/README.md +45 -0
  2. package/dist/auth.js +29 -3
  3. package/dist/config.js +10 -1
  4. package/dist/db.js +0 -1
  5. package/dist/export.js +124 -27
  6. package/dist/format-utils.js +5 -0
  7. package/dist/github.js +381 -0
  8. package/dist/index.js +168 -0
  9. package/dist/mount.js +300 -102
  10. package/dist/parsers/claude.js +2 -28
  11. package/dist/parsers/codex.js +2 -26
  12. package/dist/parsers/cursor.js +2 -26
  13. package/dist/parsers/duration.js +35 -0
  14. package/dist/parsers/gemini.js +2 -20
  15. package/dist/parsers/index.js +22 -3
  16. package/dist/parsers/types.js +0 -1
  17. package/dist/public/assets/index-Coilyhtr.css +1 -0
  18. package/dist/public/assets/index-D0noVMFu.js +44 -0
  19. package/dist/public/index.html +2 -2
  20. package/dist/redact.js +4 -104
  21. package/dist/render/build-render-data.js +9 -2
  22. package/dist/render/index.js +32 -5
  23. package/dist/render/liquid.js +147 -7
  24. package/dist/render/mock-data.js +303 -0
  25. package/dist/render/templates/aurora/portfolio.liquid +192 -0
  26. package/dist/render/templates/aurora/project.liquid +260 -0
  27. package/dist/render/templates/aurora/session.liquid +223 -0
  28. package/dist/render/templates/aurora/styles.css +1184 -0
  29. package/dist/render/templates/bauhaus/portfolio.liquid +169 -0
  30. package/dist/render/templates/bauhaus/project.liquid +300 -0
  31. package/dist/render/templates/bauhaus/session.liquid +333 -0
  32. package/dist/render/templates/bauhaus/styles.css +1645 -0
  33. package/dist/render/templates/blueprint/portfolio.liquid +153 -0
  34. package/dist/render/templates/blueprint/project.liquid +286 -0
  35. package/dist/render/templates/blueprint/session.liquid +248 -0
  36. package/dist/render/templates/blueprint/styles.css +1289 -0
  37. package/dist/render/templates/canvas/portfolio.liquid +203 -0
  38. package/dist/render/templates/canvas/project.liquid +235 -0
  39. package/dist/render/templates/canvas/session.liquid +223 -0
  40. package/dist/render/templates/canvas/styles.css +1440 -0
  41. package/dist/render/templates/carbon/portfolio.liquid +160 -0
  42. package/dist/render/templates/carbon/project.liquid +249 -0
  43. package/dist/render/templates/carbon/session.liquid +190 -0
  44. package/dist/render/templates/carbon/styles.css +1097 -0
  45. package/dist/render/templates/chalk/portfolio.liquid +189 -0
  46. package/dist/render/templates/chalk/project.liquid +245 -0
  47. package/dist/render/templates/chalk/session.liquid +215 -0
  48. package/dist/render/templates/chalk/styles.css +1161 -0
  49. package/dist/render/templates/circuit/portfolio.liquid +152 -0
  50. package/dist/render/templates/circuit/project.liquid +247 -0
  51. package/dist/render/templates/circuit/session.liquid +205 -0
  52. package/dist/render/templates/circuit/styles.css +1409 -0
  53. package/dist/render/templates/cosmos/portfolio.liquid +222 -0
  54. package/dist/render/templates/cosmos/project.liquid +327 -0
  55. package/dist/render/templates/cosmos/session.liquid +239 -0
  56. package/dist/render/templates/cosmos/styles.css +1157 -0
  57. package/dist/render/templates/daylight/portfolio.liquid +207 -0
  58. package/dist/render/templates/daylight/project.liquid +229 -0
  59. package/dist/render/templates/daylight/session.liquid +219 -0
  60. package/dist/render/templates/daylight/styles.css +1315 -0
  61. package/dist/render/templates/editorial/portfolio.liquid +110 -0
  62. package/dist/render/templates/editorial/project.liquid +202 -0
  63. package/dist/render/templates/editorial/session.liquid +171 -0
  64. package/dist/render/templates/editorial/styles.css +826 -0
  65. package/dist/render/templates/ember/portfolio.liquid +306 -0
  66. package/dist/render/templates/ember/project.liquid +232 -0
  67. package/dist/render/templates/ember/session.liquid +202 -0
  68. package/dist/render/templates/ember/styles.css +1289 -0
  69. package/dist/render/templates/glacier/portfolio.liquid +261 -0
  70. package/dist/render/templates/glacier/project.liquid +288 -0
  71. package/dist/render/templates/glacier/session.liquid +217 -0
  72. package/dist/render/templates/glacier/styles.css +1204 -0
  73. package/dist/render/templates/grid/portfolio.liquid +255 -0
  74. package/dist/render/templates/grid/project.liquid +306 -0
  75. package/dist/render/templates/grid/session.liquid +260 -0
  76. package/dist/render/templates/grid/styles.css +1445 -0
  77. package/dist/render/templates/kinetic/portfolio.liquid +158 -0
  78. package/dist/render/templates/kinetic/project.liquid +242 -0
  79. package/dist/render/templates/kinetic/session.liquid +228 -0
  80. package/dist/render/templates/kinetic/styles.css +948 -0
  81. package/dist/render/templates/meridian/portfolio.liquid +243 -0
  82. package/dist/render/templates/meridian/project.liquid +376 -0
  83. package/dist/render/templates/meridian/session.liquid +298 -0
  84. package/dist/render/templates/meridian/styles.css +1375 -0
  85. package/dist/render/templates/minimal/portfolio.liquid +71 -0
  86. package/dist/render/templates/minimal/project.liquid +154 -0
  87. package/dist/render/templates/minimal/session.liquid +140 -0
  88. package/dist/render/templates/minimal/styles.css +529 -0
  89. package/dist/render/templates/mono/portfolio.liquid +281 -0
  90. package/dist/render/templates/mono/project.liquid +275 -0
  91. package/dist/render/templates/mono/session.liquid +276 -0
  92. package/dist/render/templates/mono/styles.css +1022 -0
  93. package/dist/render/templates/neon/portfolio.liquid +207 -0
  94. package/dist/render/templates/neon/project.liquid +225 -0
  95. package/dist/render/templates/neon/session.liquid +195 -0
  96. package/dist/render/templates/neon/styles.css +1271 -0
  97. package/dist/render/templates/noir/portfolio.liquid +137 -0
  98. package/dist/render/templates/noir/project.liquid +220 -0
  99. package/dist/render/templates/noir/session.liquid +241 -0
  100. package/dist/render/templates/noir/styles.css +1229 -0
  101. package/dist/render/templates/obsidian/portfolio.liquid +247 -0
  102. package/dist/render/templates/obsidian/project.liquid +280 -0
  103. package/dist/render/templates/obsidian/session.liquid +241 -0
  104. package/dist/render/templates/obsidian/styles.css +1407 -0
  105. package/dist/render/templates/paper/portfolio.liquid +257 -0
  106. package/dist/render/templates/paper/project.liquid +235 -0
  107. package/dist/render/templates/paper/session.liquid +271 -0
  108. package/dist/render/templates/paper/styles.css +1513 -0
  109. package/dist/render/templates/parallax/portfolio.liquid +295 -0
  110. package/dist/render/templates/parallax/project.liquid +275 -0
  111. package/dist/render/templates/parallax/session.liquid +295 -0
  112. package/dist/render/templates/parallax/styles.css +1880 -0
  113. package/dist/render/templates/parchment/portfolio.liquid +280 -0
  114. package/dist/render/templates/parchment/project.liquid +289 -0
  115. package/dist/render/templates/parchment/session.liquid +346 -0
  116. package/dist/render/templates/parchment/styles.css +1401 -0
  117. package/dist/render/templates/partials/_beats.liquid +16 -0
  118. package/dist/render/templates/partials/_breadcrumb.liquid +9 -0
  119. package/dist/render/templates/partials/_footer.liquid +7 -0
  120. package/dist/render/templates/partials/_growth-chart.liquid +7 -0
  121. package/dist/render/templates/partials/_key-decisions.liquid +20 -0
  122. package/dist/render/templates/partials/_links.liquid +16 -0
  123. package/dist/render/templates/partials/_narrative.liquid +8 -0
  124. package/dist/render/templates/partials/_phases.liquid +20 -0
  125. package/dist/render/templates/partials/_portfolio-header.liquid +20 -0
  126. package/dist/render/templates/partials/_portfolio-projects.liquid +16 -0
  127. package/dist/render/templates/partials/_portfolio-stats.liquid +19 -0
  128. package/dist/render/templates/partials/_qa.liquid +13 -0
  129. package/dist/render/templates/partials/_screenshot.liquid +15 -0
  130. package/dist/render/templates/partials/_session-cards.liquid +30 -0
  131. package/dist/render/templates/partials/_session-header.liquid +39 -0
  132. package/dist/render/templates/partials/_session-sidebar.liquid +30 -0
  133. package/dist/render/templates/partials/_skills.liquid +12 -0
  134. package/dist/render/templates/partials/_source-breakdown.liquid +22 -0
  135. package/dist/render/templates/partials/_stats.liquid +38 -0
  136. package/dist/render/templates/partials/_work-timeline.liquid +7 -0
  137. package/dist/render/templates/project.liquid +7 -4
  138. package/dist/render/templates/radar/portfolio.liquid +223 -0
  139. package/dist/render/templates/radar/project.liquid +278 -0
  140. package/dist/render/templates/radar/session.liquid +300 -0
  141. package/dist/render/templates/radar/styles.css +1055 -0
  142. package/dist/render/templates/showcase/portfolio.liquid +221 -0
  143. package/dist/render/templates/showcase/project.liquid +237 -0
  144. package/dist/render/templates/showcase/session.liquid +210 -0
  145. package/dist/render/templates/showcase/styles.css +1284 -0
  146. package/dist/render/templates/signal/portfolio.liquid +217 -0
  147. package/dist/render/templates/signal/project.liquid +278 -0
  148. package/dist/render/templates/signal/session.liquid +282 -0
  149. package/dist/render/templates/signal/styles.css +1401 -0
  150. package/dist/render/templates/strata/portfolio.liquid +180 -0
  151. package/dist/render/templates/strata/project.liquid +282 -0
  152. package/dist/render/templates/strata/session.liquid +261 -0
  153. package/dist/render/templates/strata/styles.css +1354 -0
  154. package/dist/render/templates/styles.css +1190 -0
  155. package/dist/render/templates/terminal/portfolio.liquid +102 -0
  156. package/dist/render/templates/terminal/project.liquid +161 -0
  157. package/dist/render/templates/terminal/session.liquid +145 -0
  158. package/dist/render/templates/terminal/styles.css +497 -0
  159. package/dist/render/templates/verdant/portfolio.liquid +321 -0
  160. package/dist/render/templates/verdant/project.liquid +309 -0
  161. package/dist/render/templates/verdant/session.liquid +237 -0
  162. package/dist/render/templates/verdant/styles.css +1261 -0
  163. package/dist/render/templates/zen/portfolio.liquid +124 -0
  164. package/dist/render/templates/zen/project.liquid +187 -0
  165. package/dist/render/templates/zen/session.liquid +203 -0
  166. package/dist/render/templates/zen/styles.css +1211 -0
  167. package/dist/render/templates.js +90 -0
  168. package/dist/routes/auth.js +7 -3
  169. package/dist/routes/context.js +17 -10
  170. package/dist/routes/delete.js +195 -0
  171. package/dist/routes/enhance.js +57 -40
  172. package/dist/routes/export.js +14 -4
  173. package/dist/routes/github.js +254 -0
  174. package/dist/routes/index.js +2 -0
  175. package/dist/routes/portfolio-render-data.js +160 -0
  176. package/dist/routes/preview.js +555 -108
  177. package/dist/routes/projects.js +61 -24
  178. package/dist/routes/publish.js +320 -31
  179. package/dist/routes/settings.js +194 -1
  180. package/dist/routes/sse.js +9 -0
  181. package/dist/search.js +6 -0
  182. package/dist/server.js +11 -3
  183. package/dist/settings.js +112 -9
  184. package/package.json +3 -4
  185. package/dist/public/assets/index-CC9G8EF1.js +0 -21
  186. package/dist/public/assets/index-Dalqz2mC.css +0 -1
@@ -0,0 +1,255 @@
1
+ <div class="heyiam-portfolio grid" data-render-version="2" data-template="grid" data-username="{{ user.username }}">
2
+
3
+ <div class="container">
4
+ <div class="bento" aria-label="Portfolio dashboard">
5
+
6
+ {%- comment -%} Cell 1: Identity (2-wide) {%- endcomment -%}
7
+ {% if hasProfile %}
8
+ <div class="bento-cell cell-identity">
9
+ <div class="identity-top">
10
+ {% if user.photoUrl %}
11
+ <img src="{{ user.photoUrl }}" alt="{{ user.displayName }}" class="profile-photo portfolio-photo" data-portfolio-field="photoBase64"{% unless user.photoUrl %} data-portfolio-empty="true"{% endunless %}>
12
+ {% endif %}
13
+ <div class="identity-text">
14
+ {% if user.displayName != blank %}
15
+ <h1 data-portfolio-field="displayName">{{ user.displayName }}</h1>
16
+ {% endif %}
17
+ {% if user.location != blank %}
18
+ <p class="location portfolio-location">
19
+ <svg 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 0 1 18 0z"/><circle cx="12" cy="10" r="3"/></svg>
20
+ <span data-portfolio-field="location">{{ user.location }}</span>
21
+ </p>
22
+ {% endif %}
23
+ {% if user.bio != blank %}
24
+ <p class="bio portfolio-bio" data-portfolio-field="bio">{{ user.bio }}</p>
25
+ {% endif %}
26
+ </div>
27
+ </div>
28
+ <div class="contact-row">
29
+ <a href="{% if user.email %}mailto:{{ user.email }}{% endif %}" data-portfolio-field="email"{% unless user.email %} data-portfolio-empty="true"{% endunless %}><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="m2 4 10 8 10-8"/></svg> {{ user.email }}</a>
30
+ <a href="{% if user.linkedinUrl %}{{ user.linkedinUrl }}{% endif %}" target="_blank" rel="noopener" data-portfolio-field="linkedinUrl"{% unless user.linkedinUrl %} data-portfolio-empty="true"{% endunless %}><svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><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> LinkedIn</a>
31
+ <a href="{% if user.githubUrl %}{{ user.githubUrl }}{% endif %}" target="_blank" rel="noopener" data-portfolio-field="githubUrl"{% unless user.githubUrl %} data-portfolio-empty="true"{% endunless %}><svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><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> GitHub</a>
32
+ <a href="{% if user.twitterHandle %}https://x.com/{{ user.twitterHandle }}{% endif %}" target="_blank" rel="noopener" data-portfolio-field="twitterHandle"{% unless user.twitterHandle %} data-portfolio-empty="true"{% endunless %}><svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><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> @{{ user.twitterHandle }}</a>
33
+ <a href="{% if user.websiteUrl %}{{ user.websiteUrl }}{% endif %}" target="_blank" rel="noopener" data-portfolio-field="websiteUrl"{% unless user.websiteUrl %} data-portfolio-empty="true"{% endunless %}>{{ user.websiteUrl | stripProtocol }}</a>
34
+ {% if user.resumeUrl %}
35
+ <a href="{{ user.resumeUrl }}" class="resume-btn" download><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><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> Resume (PDF)</a>
36
+ {% endif %}
37
+ </div>
38
+ </div>
39
+ {% endif %}
40
+
41
+ {%- comment -%} Cell 2: Stat -- Projects {%- endcomment -%}
42
+ <section class="bento-cell cell-stat" aria-label="Projects count">
43
+ <div class="stat-icon" aria-hidden="true">
44
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></svg>
45
+ </div>
46
+ <div>
47
+ <div class="stat-number" data-target="{{ projects.size }}">0</div>
48
+ <div class="stat-label">Projects</div>
49
+ </div>
50
+ </section>
51
+
52
+ {%- comment -%} Cell 3: Stat -- Sessions {%- endcomment -%}
53
+ <section class="bento-cell cell-stat" aria-label="Sessions count">
54
+ <div class="stat-icon" aria-hidden="true">
55
+ <svg 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>
56
+ </div>
57
+ <div>
58
+ <div class="stat-number" data-target="{{ totalSessions }}">0</div>
59
+ <div class="stat-label">Sessions</div>
60
+ </div>
61
+ </section>
62
+
63
+ {%- comment -%} Cell 4: Stat -- Human Time {%- endcomment -%}
64
+ {% if efficiencyMultiplier %}
65
+ <section class="bento-cell cell-stat" aria-label="Human time">
66
+ <div class="stat-icon" aria-hidden="true">
67
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
68
+ </div>
69
+ <div>
70
+ <div class="stat-number">{{ totalDurationMinutes | formatDuration }}</div>
71
+ <div class="stat-label">You</div>
72
+ </div>
73
+ </section>
74
+
75
+ {%- comment -%} Cell 4b: Stat -- Agent Time {%- endcomment -%}
76
+ <section class="bento-cell cell-stat" aria-label="Agent time">
77
+ <div class="stat-icon" aria-hidden="true">
78
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="4" width="16" height="16" rx="2"/><line x1="9" y1="9" x2="9" y2="9.01"/><line x1="15" y1="9" x2="15" y2="9.01"/><path d="M8 14s1.5 2 4 2 4-2 4-2"/></svg>
79
+ </div>
80
+ <div>
81
+ <div class="stat-number" style="color: var(--accent);">{{ totalAgentDurationMinutes | formatDuration }}</div>
82
+ <div class="stat-label">Agents</div>
83
+ </div>
84
+ </section>
85
+
86
+ {%- comment -%} Cell 4c: Stat -- Multiplier {%- endcomment -%}
87
+ <section class="bento-cell cell-stat" aria-label="Multiplier">
88
+ <div class="stat-icon" aria-hidden="true">
89
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="23 6 13.5 15.5 8.5 10.5 1 18"/><polyline points="17 6 23 6 23 12"/></svg>
90
+ </div>
91
+ <div>
92
+ <div class="stat-number">{{ efficiencyMultiplier }}</div>
93
+ <div class="stat-label">Multiplier</div>
94
+ </div>
95
+ </section>
96
+ {% else %}
97
+ <section class="bento-cell cell-stat" aria-label="Total time">
98
+ <div class="stat-icon" aria-hidden="true">
99
+ <svg 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>
100
+ </div>
101
+ <div>
102
+ <div class="stat-number">{{ totalDurationMinutes | formatDuration }}</div>
103
+ <div class="stat-label">Total Time</div>
104
+ </div>
105
+ </section>
106
+ {% endif %}
107
+
108
+ {%- comment -%} Cell 5: Stat -- LOC {%- endcomment -%}
109
+ <section class="bento-cell cell-stat" aria-label="Lines changed">
110
+ <div class="stat-icon" aria-hidden="true">
111
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>
112
+ </div>
113
+ <div>
114
+ <div class="stat-number" data-target="{{ totalLoc }}" data-format="comma">0</div>
115
+ <div class="stat-label">Lines Changed</div>
116
+ </div>
117
+ </section>
118
+
119
+ {%- comment -%} Cell 6: Source Mix (2-wide) {%- endcomment -%}
120
+ {% if sourceCounts.size > 0 %}
121
+ <section class="bento-cell cell-source" aria-label="Source breakdown">
122
+ <h2>Source Mix</h2>
123
+ <div class="source-bars">
124
+ {% assign totalSourceSessions = 0 %}
125
+ {% for src in sourceCounts %}
126
+ {% assign totalSourceSessions = totalSourceSessions | plus: src.count %}
127
+ {% endfor %}
128
+ {% for src in sourceCounts %}
129
+ <div class="source-item">
130
+ <span class="source-name">{{ src.tool }}</span>
131
+ <div class="source-bar-track">
132
+ {% if totalSourceSessions > 0 %}
133
+ {% assign srcPct = src.count | times: 100.0 | divided_by: totalSourceSessions | round %}
134
+ <div class="source-bar-fill{% unless forloop.first %} source-bar-fill--secondary{% endunless %}" style="width: {{ srcPct }}%" role="progressbar" aria-valuenow="{{ srcPct }}" aria-valuemin="0" aria-valuemax="100" aria-label="{{ src.tool }} usage"></div>
135
+ {% endif %}
136
+ </div>
137
+ {% if totalSourceSessions > 0 %}
138
+ {% assign srcPctDisplay = src.count | times: 100.0 | divided_by: totalSourceSessions | round %}
139
+ <span class="source-pct">{{ srcPctDisplay }}%</span>
140
+ {% endif %}
141
+ </div>
142
+ {% endfor %}
143
+ </div>
144
+ </section>
145
+ {% endif %}
146
+
147
+ {%- comment -%} Cell 7: Project Cards (2-wide each) {%- endcomment -%}
148
+ {% for p in projects %}
149
+ <a href="/{{ user.username }}/{{ p.slug }}" class="bento-cell cell-project project-card" aria-label="View {{ p.title }} project">
150
+ <div class="project-header">
151
+ <h2 class="project-title">{{ p.title }}</h2>
152
+ <svg class="project-arrow" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="7" y1="17" x2="17" y2="7"/><polyline points="7 7 17 7 17 17"/></svg>
153
+ </div>
154
+ {% if p.narrative %}
155
+ <p class="project-narrative">{{ p.narrative | truncate: 120 }}</p>
156
+ {% endif %}
157
+ <div class="project-meta">
158
+ <span class="project-meta-item"><strong>{{ p.totalSessions }}</strong> sessions</span>
159
+ <span class="project-meta-item"><strong>{{ p.totalDurationMinutes | formatDuration }}</strong></span>
160
+ <span class="project-meta-item"><strong>{{ p.totalLoc | formatLoc }}</strong> LOC</span>
161
+ </div>
162
+ {% if p.skills.size > 0 %}
163
+ <div class="project-skills">
164
+ {% for skill in p.skills %}
165
+ <span class="skill-chip chip">{{ skill }}</span>
166
+ {% endfor %}
167
+ </div>
168
+ {% endif %}
169
+ {% if p.sourceCounts.size > 0 %}
170
+ {% assign pTotalSrc = 0 %}
171
+ {% for src in p.sourceCounts %}
172
+ {% assign pTotalSrc = pTotalSrc | plus: src.count %}
173
+ {% endfor %}
174
+ {% if pTotalSrc > 0 %}
175
+ <div class="project-source-bar" aria-label="Source breakdown">
176
+ {% for src in p.sourceCounts %}
177
+ {% assign pSrcPct = src.count | times: 100.0 | divided_by: pTotalSrc | round: 1 %}
178
+ <div class="project-source-segment" style="width: {{ pSrcPct }}%; background: var(--accent{% unless forloop.first %}-light{% endunless %});"></div>
179
+ {% endfor %}
180
+ </div>
181
+ {% endif %}
182
+ {% endif %}
183
+ </a>
184
+ {% endfor %}
185
+
186
+ {%- comment -%} Cell: Activity Heatmap (2-wide) {%- endcomment -%}
187
+ {% if activityByDay.size > 0 %}
188
+ <section class="bento-cell cell-activity" aria-label="Activity over time">
189
+ <h2>
190
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><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>
191
+ Activity
192
+ </h2>
193
+ <div class="heatmap" role="img" aria-label="Activity heatmap showing session density over time">
194
+ {% for day in activityByDay %}
195
+ {% if day.count >= 4 %}{% assign level = 4 %}
196
+ {% elsif day.count >= 3 %}{% assign level = 3 %}
197
+ {% elsif day.count >= 2 %}{% assign level = 2 %}
198
+ {% elsif day.count >= 1 %}{% assign level = 1 %}
199
+ {% else %}{% assign level = 0 %}{% endif %}
200
+ <div class="heatmap-cell{% if level > 0 %} heatmap-cell--l{{ level }}{% endif %}" title="{{ day.date }}: {{ day.count }} sessions" aria-hidden="true"></div>
201
+ {% endfor %}
202
+ </div>
203
+ <div class="heatmap-legend">
204
+ <span class="heatmap-legend-label">Less</span>
205
+ <span class="heatmap-legend-cell" aria-hidden="true"></span>
206
+ <span class="heatmap-legend-cell heatmap-cell--l1" aria-hidden="true"></span>
207
+ <span class="heatmap-legend-cell heatmap-cell--l2" aria-hidden="true"></span>
208
+ <span class="heatmap-legend-cell heatmap-cell--l3" aria-hidden="true"></span>
209
+ <span class="heatmap-legend-cell heatmap-cell--l4" aria-hidden="true"></span>
210
+ <span class="heatmap-legend-label">More</span>
211
+ </div>
212
+ </section>
213
+ {% endif %}
214
+
215
+ {%- comment -%} Cell: All Skills (2-wide) {%- endcomment -%}
216
+ {% if allSkills.size > 0 %}
217
+ <section class="bento-cell cell-all-skills" aria-label="All skills across projects">
218
+ <h2>
219
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><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>
220
+ Skills
221
+ </h2>
222
+ <div class="all-skills-grid">
223
+ {% for skill in allSkills %}
224
+ <span class="skill-chip">{{ skill }}</span>
225
+ {% endfor %}
226
+ </div>
227
+ </section>
228
+ {% endif %}
229
+
230
+ {%- comment -%} Cell: Recent Sessions (4-wide) {%- endcomment -%}
231
+ {% if recentSessions.size > 0 %}
232
+ <section class="bento-cell cell-recent" aria-label="Recent sessions">
233
+ <h2>
234
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg>
235
+ Recent Sessions
236
+ </h2>
237
+ <div class="recent-list" role="table" aria-label="Recent sessions list">
238
+ {% for rs in recentSessions %}
239
+ <div class="recent-row" role="row">
240
+ <span class="recent-title" role="cell">{{ rs.title }}</span>
241
+ <span class="recent-project" role="cell">{{ rs.projectTitle }}</span>
242
+ <span class="recent-date" role="cell">{% if rs.recordedAt %}{{ rs.recordedAt | formatDate: "short" }}{% endif %}</span>
243
+ <span class="recent-duration" role="cell">{{ rs.durationMinutes }}m</span>
244
+ </div>
245
+ {% endfor %}
246
+ </div>
247
+ </section>
248
+ {% endif %}
249
+
250
+ </div>
251
+ </div>
252
+
253
+ {%- comment -%} Stat counter animation {%- endcomment -%}
254
+
255
+ </div>
@@ -0,0 +1,306 @@
1
+ <div class="heyiam-project grid" data-render-version="2" data-template="grid"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
2
+
3
+ <div class="container">
4
+ <div class="breadcrumb" aria-label="Breadcrumb">
5
+ <a href="/{{ user.username }}">{{ user.username }}</a>
6
+ <span class="breadcrumb-sep" aria-hidden="true">/</span>
7
+ <span aria-current="page">{{ project.slug }}</span>
8
+ </div>
9
+
10
+ <div class="bento" aria-label="Project dashboard">
11
+
12
+ {%- comment -%} Cell 1: Title (3-wide) {%- endcomment -%}
13
+ <div class="bento-cell cell-title">
14
+ <h1 data-editable="title">{{ project.title }}</h1>
15
+ {% if project.repoUrl or project.projectUrl %}
16
+ <div class="project-links">
17
+ {% if project.repoUrl %}
18
+ <a href="{{ project.repoUrl }}" target="_blank" rel="noopener">
19
+ <svg 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 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"/></svg>
20
+ {{ project.repoUrl | stripProtocol }}
21
+ </a>
22
+ {% endif %}
23
+ {% if project.projectUrl %}
24
+ <a href="{{ project.projectUrl }}" target="_blank" rel="noopener">
25
+ <svg 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 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>
26
+ {{ project.projectUrl | stripProtocol }}
27
+ </a>
28
+ {% endif %}
29
+ </div>
30
+ {% endif %}
31
+ </div>
32
+
33
+ {%- comment -%} Cell 2: Screenshot (spans 2 rows) {%- endcomment -%}
34
+ {% if project.screenshotUrl %}
35
+ <div class="bento-cell cell-screenshot">
36
+ <div class="browser-chrome">
37
+ <span class="browser-dot" aria-hidden="true"></span>
38
+ <span class="browser-dot" aria-hidden="true"></span>
39
+ <span class="browser-dot" aria-hidden="true"></span>
40
+ {% if project.projectUrl %}
41
+ <span class="browser-bar">{{ project.projectUrl | stripProtocol }}</span>
42
+ {% else %}
43
+ <span class="browser-bar">{{ project.slug }}</span>
44
+ {% endif %}
45
+ </div>
46
+ <div class="browser-viewport">
47
+ <img src="{{ project.screenshotUrl }}" alt="{{ project.title }} screenshot" style="width: 100%; height: 100%; object-fit: cover; border-radius: 0 0 var(--radius-sm) var(--radius-sm);">
48
+ </div>
49
+ </div>
50
+ {% endif %}
51
+
52
+ {%- comment -%} Cell 3: Stat -- Sessions {%- endcomment -%}
53
+ <section class="bento-cell cell-stat" aria-label="Sessions count">
54
+ <div class="stat-icon" aria-hidden="true">
55
+ <svg 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>
56
+ </div>
57
+ <div>
58
+ <div class="stat-number" data-target="{{ project.totalSessions }}">0</div>
59
+ <div class="stat-label">Sessions</div>
60
+ </div>
61
+ </section>
62
+
63
+ {%- comment -%} Cell 4: Stat -- Human Time {%- endcomment -%}
64
+ {% if efficiencyMultiplier %}
65
+ <section class="bento-cell cell-stat" aria-label="Human time">
66
+ <div class="stat-icon" aria-hidden="true">
67
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
68
+ </div>
69
+ <div>
70
+ <div class="stat-number">{{ project.totalDurationMinutes | formatDuration }}</div>
71
+ <div class="stat-label">You</div>
72
+ </div>
73
+ </section>
74
+
75
+ {%- comment -%} Cell 4b: Stat -- Agent Time {%- endcomment -%}
76
+ <section class="bento-cell cell-stat" aria-label="Agent time">
77
+ <div class="stat-icon" aria-hidden="true">
78
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="4" width="16" height="16" rx="2"/><line x1="9" y1="9" x2="9" y2="9.01"/><line x1="15" y1="9" x2="15" y2="9.01"/><path d="M8 14s1.5 2 4 2 4-2 4-2"/></svg>
79
+ </div>
80
+ <div>
81
+ <div class="stat-number" style="color: var(--accent);">{{ project.totalAgentDurationMinutes | formatDuration }}</div>
82
+ <div class="stat-label">Agents</div>
83
+ </div>
84
+ </section>
85
+
86
+ {%- comment -%} Cell 4c: Stat -- Multiplier {%- endcomment -%}
87
+ <section class="bento-cell cell-stat" aria-label="Multiplier">
88
+ <div class="stat-icon" aria-hidden="true">
89
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="23 6 13.5 15.5 8.5 10.5 1 18"/><polyline points="17 6 23 6 23 12"/></svg>
90
+ </div>
91
+ <div>
92
+ <div class="stat-number">{{ efficiencyMultiplier }}</div>
93
+ <div class="stat-label">Multiplier</div>
94
+ </div>
95
+ </section>
96
+ {% else %}
97
+ <section class="bento-cell cell-stat" aria-label="Total time">
98
+ <div class="stat-icon" aria-hidden="true">
99
+ <svg 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>
100
+ </div>
101
+ <div>
102
+ <div class="stat-number">{{ project.totalDurationMinutes | formatDuration }}</div>
103
+ <div class="stat-label">Time</div>
104
+ </div>
105
+ </section>
106
+ {% endif %}
107
+
108
+ {%- comment -%} Cell 5: Stat -- LOC {%- endcomment -%}
109
+ <section class="bento-cell cell-stat" aria-label="Lines changed">
110
+ <div class="stat-icon" aria-hidden="true">
111
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>
112
+ </div>
113
+ <div>
114
+ <div class="stat-number" data-target="{{ project.totalLoc }}" data-format="comma">0</div>
115
+ <div class="stat-label">Lines Changed</div>
116
+ </div>
117
+ </section>
118
+
119
+ {%- comment -%} Cell 6: Stat -- Files {%- endcomment -%}
120
+ <section class="bento-cell cell-stat" aria-label="Files changed">
121
+ <div class="stat-icon" aria-hidden="true">
122
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><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"/></svg>
123
+ </div>
124
+ <div>
125
+ <div class="stat-number" data-target="{{ project.totalFilesChanged }}">0</div>
126
+ <div class="stat-label">Files</div>
127
+ </div>
128
+ </section>
129
+
130
+ {%- comment -%} Cell 7: Stat -- Tokens {%- endcomment -%}
131
+ {% if project.totalTokens %}
132
+ <section class="bento-cell cell-stat" aria-label="Tokens used">
133
+ <div class="stat-icon" aria-hidden="true">
134
+ <svg 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"/><path d="M12 6v6l4 2"/></svg>
135
+ </div>
136
+ <div>
137
+ <div class="stat-number">{{ project.totalTokens | formatTokens }}</div>
138
+ <div class="stat-label">Tokens</div>
139
+ </div>
140
+ </section>
141
+ {% endif %}
142
+
143
+ {%- comment -%} Narrative (3-wide) {%- endcomment -%}
144
+ {% if project.narrative %}
145
+ <section class="bento-cell cell-narrative" aria-label="Project narrative">
146
+ <h2 class="cell-heading">
147
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 20h9"/><path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"/></svg>
148
+ Narrative
149
+ </h2>
150
+ <p>{{ project.narrative }}</p>
151
+ </section>
152
+ {% endif %}
153
+
154
+ {%- comment -%} Phases (2-wide) {%- endcomment -%}
155
+ {% if arc.size > 0 %}
156
+ <section class="bento-cell cell-phases" aria-label="Project phases">
157
+ <h2 class="cell-heading">
158
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg>
159
+ Phases
160
+ </h2>
161
+ <ol class="phase-list" role="list">
162
+ {% for item in arc %}
163
+ <li class="phase-item">
164
+ <span class="phase-number" aria-hidden="true">{{ item.phase }}</span>
165
+ <div class="phase-content">
166
+ <div class="phase-name">{{ item.title }}</div>
167
+ {% if item.dates %}
168
+ <div class="phase-dates">{{ item.dates }}</div>
169
+ {% endif %}
170
+ <div class="phase-desc">{{ item.description }}</div>
171
+ </div>
172
+ </li>
173
+ {% endfor %}
174
+ </ol>
175
+ </section>
176
+ {% endif %}
177
+
178
+ {%- comment -%} Skills (2-wide) {%- endcomment -%}
179
+ {% if project.skills.size > 0 %}
180
+ <section class="bento-cell cell-skills" aria-label="Skills used">
181
+ <h2 class="cell-heading">
182
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><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>
183
+ Skills
184
+ </h2>
185
+ <div class="skills-grid">
186
+ {% for skill in project.skills %}
187
+ <span class="skill-chip chip">{{ skill }}</span>
188
+ {% endfor %}
189
+ </div>
190
+ </section>
191
+ {% endif %}
192
+
193
+ {%- comment -%} Key Decisions (2-wide) {%- endcomment -%}
194
+ {% if arc.size > 0 %}
195
+ <section class="bento-cell cell-decisions" aria-label="Key decisions">
196
+ <h2 class="cell-heading">
197
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>
198
+ Key Decisions
199
+ </h2>
200
+ <ol class="decision-list" role="list">
201
+ {% for item in arc %}
202
+ <li class="decision-item">
203
+ <span class="decision-text">{{ item.description }}</span>
204
+ </li>
205
+ {% endfor %}
206
+ </ol>
207
+ </section>
208
+ {% endif %}
209
+
210
+ {%- comment -%} Source Breakdown (2-wide) {%- endcomment -%}
211
+ {% if sourceCounts.size > 0 %}
212
+ <section class="bento-cell cell-source" aria-label="Source breakdown">
213
+ <h2 class="cell-heading">
214
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"/><line x1="3" y1="9" x2="21" y2="9"/><line x1="9" y1="21" x2="9" y2="9"/></svg>
215
+ Source Breakdown
216
+ </h2>
217
+ <div class="source-bars">
218
+ {% assign totalSourceSessions = 0 %}
219
+ {% for src in sourceCounts %}
220
+ {% assign totalSourceSessions = totalSourceSessions | plus: src.count %}
221
+ {% endfor %}
222
+ {% for src in sourceCounts %}
223
+ <div class="source-item">
224
+ <span class="source-name">{{ src.tool }}</span>
225
+ <div class="source-bar-track">
226
+ {% if totalSourceSessions > 0 %}
227
+ {% assign srcPct = src.count | times: 100.0 | divided_by: totalSourceSessions | round: 1 %}
228
+ <div class="source-bar-fill{% unless forloop.first %} source-bar-fill--secondary{% endunless %}" style="width: {{ srcPct }}%"></div>
229
+ {% endif %}
230
+ </div>
231
+ {% if totalSourceSessions > 0 %}
232
+ {% assign srcPctDisplay = src.count | times: 100.0 | divided_by: totalSourceSessions | round: 1 %}
233
+ <span class="source-pct">{{ srcPctDisplay }}%</span>
234
+ {% endif %}
235
+ </div>
236
+ {% endfor %}
237
+ </div>
238
+ </section>
239
+ {% endif %}
240
+
241
+ {%- comment -%} Work Timeline table (4-wide) -- CSS-only, no JS charts {%- endcomment -%}
242
+ {% if featuredSessions.size > 0 %}
243
+ <section class="bento-cell cell-timeline" aria-label="Work timeline">
244
+ <h2 class="cell-heading">
245
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="12" y1="20" x2="12" y2="10"/><line x1="18" y1="20" x2="18" y2="4"/><line x1="6" y1="20" x2="6" y2="16"/></svg>
246
+ Work Timeline
247
+ </h2>
248
+ <div class="timeline-chart" role="table" aria-label="Session timeline">
249
+ <div class="timeline-row timeline-header" role="row">
250
+ <span role="columnheader">#</span>
251
+ <span role="columnheader">Session</span>
252
+ <span role="columnheader" style="text-align:end">Date</span>
253
+ <span role="columnheader" style="text-align:end">Time</span>
254
+ <span role="columnheader" style="text-align:end">LOC</span>
255
+ </div>
256
+ {% for s in featuredSessions %}
257
+ <div class="timeline-row" role="row">
258
+ <span class="timeline-num" role="cell">{{ forloop.index }}</span>
259
+ <span class="timeline-title" role="cell">{{ s.title }}</span>
260
+ <span class="timeline-date" role="cell">{% if s.recordedAt %}{{ s.recordedAt | formatDate: "short" }}{% endif %}</span>
261
+ <span class="timeline-duration" role="cell">{{ s.durationMinutes }}m</span>
262
+ <span class="timeline-loc" role="cell">{{ s.locChanged | localeNumber }}</span>
263
+ </div>
264
+ {% endfor %}
265
+ </div>
266
+ </section>
267
+ {% endif %}
268
+
269
+ {%- comment -%} Featured Session Cards (2-wide each) {%- endcomment -%}
270
+ {% if featuredSessions.size > 0 %}
271
+ {% for s in featuredSessions %}
272
+ <a href="{{ sessionBaseUrl }}/{{ s.slug }}{{ sessionSuffix }}" class="bento-cell cell-session session-card" aria-label="View session: {{ s.title }}">
273
+ <div class="session-header">
274
+ <span class="session-title">{{ s.title }}</span>
275
+ {% if s.skills.size > 0 %}
276
+ <span class="session-tag">{{ s.skills | first }}</span>
277
+ {% endif %}
278
+ </div>
279
+ <div class="session-meta">
280
+ <span>{{ s.durationMinutes | formatDuration }}</span>
281
+ <span>{{ s.locChanged | localeNumber }} LOC</span>
282
+ {% if s.agentSummary and s.agentSummary.agents.size > 0 %}
283
+ <span>{{ s.agentSummary.agents.size }} agents</span>
284
+ {% elsif s.filesChanged %}
285
+ <span>{{ s.filesChanged }} files</span>
286
+ {% endif %}
287
+ </div>
288
+ {% if s.agentSummary and s.agentSummary.agents.size > 0 %}
289
+ <div class="session-agents" aria-label="Agent roles used">
290
+ {% assign agent_colors = '#7c3aed,#0891b2,#059669,#475569,#e11d48,#d97706' | split: ',' %}
291
+ {% for agent in s.agentSummary.agents %}
292
+ {% assign colorIdx = forloop.index0 | modulo: 6 %}
293
+ <span class="agent-dot" style="background: {{ agent_colors[colorIdx] }};" title="{{ agent.role }}"></span>
294
+ {% endfor %}
295
+ </div>
296
+ {% endif %}
297
+ </a>
298
+ {% endfor %}
299
+ {% endif %}
300
+
301
+ </div>
302
+ </div>
303
+
304
+ {%- comment -%} Stat counter animation {%- endcomment -%}
305
+
306
+ </div>