anentrypoint-design 0.0.438 → 0.0.440

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 (60) hide show
  1. package/dist/247420.css +78 -23
  2. package/dist/247420.js +23 -23
  3. package/dist/colors_and_type.css +86 -42
  4. package/dist/preview/buttons.html +5 -2
  5. package/dist/preview/colors-core.html +4 -1
  6. package/dist/preview/colors-lore.html +4 -1
  7. package/dist/preview/colors-semantic.html +4 -1
  8. package/dist/preview/dateline.html +4 -1
  9. package/dist/preview/dropzone.html +4 -1
  10. package/dist/preview/file-grid.html +4 -1
  11. package/dist/preview/file-row.html +4 -1
  12. package/dist/preview/file-toolbar.html +4 -1
  13. package/dist/preview/file-viewer.html +7 -1
  14. package/dist/preview/header.html +5 -2
  15. package/dist/preview/icons-unicode.html +26 -20
  16. package/dist/preview/index-row.html +4 -1
  17. package/dist/preview/inputs.html +4 -1
  18. package/dist/preview/manifesto.html +4 -1
  19. package/dist/preview/rules.html +4 -1
  20. package/dist/preview/spacing.html +17 -12
  21. package/dist/preview/stamps-lore.html +4 -1
  22. package/dist/preview/stamps.html +4 -1
  23. package/dist/preview/theme-ink.html +4 -1
  24. package/dist/preview/type-display.html +4 -1
  25. package/dist/preview/type-mono.html +4 -1
  26. package/dist/preview/type-prose.html +5 -2
  27. package/dist/preview/type-scale.html +40 -15
  28. package/dist/preview/wordmarks.html +18 -22
  29. package/dist/src/components/content.js +2 -1
  30. package/dist/src/components.js +21 -5
  31. package/dist/src/kits/os/theme.css +38 -5
  32. package/dist/ui_kits/aicat/app.js +1 -1
  33. package/dist/ui_kits/gallery/app.js +27 -7
  34. package/dist/ui_kits/signin/app.js +51 -10
  35. package/dist/ui_kits/system_primer/app.js +189 -3
  36. package/dist/ui_kits/terminal/app.js +56 -10
  37. package/package.json +1 -1
  38. package/src/components/content/cli.js +7 -1
  39. package/src/components/content/panel.js +7 -2
  40. package/src/components/shell/app-shell.js +7 -2
  41. package/src/components/shell/icons.js +4 -1
  42. package/src/css/app-shell/kits-appended.css +58 -21
  43. package/src/css/app-shell/panel-row.css +14 -0
  44. package/src/css/app-shell/responsive2-workspace.css +5 -1
  45. package/src/css/app-shell/topbar.css +1 -1
  46. package/src/kits/os/files-app.js +9 -2
  47. package/src/kits/os/icons.js +5 -0
  48. package/src/kits/os/launcher.css +7 -2
  49. package/src/kits/os/launcher.js +27 -3
  50. package/src/kits/os/monitor-app.js +8 -2
  51. package/src/kits/os/shell.js +55 -12
  52. package/src/kits/os/theme.css +20 -2
  53. package/src/kits/os/wm.js +41 -11
  54. package/types/components.d.ts +2 -0
  55. package/dist/slides/deck-stage-overlay.js +0 -63
  56. package/dist/slides/deck-stage-state.js +0 -81
  57. package/dist/slides/deck-stage-style.js +0 -117
  58. package/dist/slides/deck-stage.js +0 -159
  59. package/dist/src/kits/os/freddie/helpers.js +0 -59
  60. package/dist/src/kits/os/validator-app.js +0 -55
@@ -5,7 +5,9 @@
5
5
  <style>body{padding:var(--space-4);background:var(--panel-0);color:var(--panel-text);}</style><script defer src="./motion-default.js"></script>
6
6
  </head><body>
7
7
  <div class="dateline"><span>247420 · STAMPS · LORE</span><span class="spread"></span><span>FULL SET</span></div>
8
- <div style="margin-top:20px;display:flex;gap:20px;align-items:center;flex-wrap:wrap">
8
+
9
+ <h1 class="sr-only">Stamps lore</h1>
10
+ <main aria-label="Stamps lore specimens"><div style="margin-top:20px;display:flex;gap:20px;align-items:center;flex-wrap:wrap">
9
11
  <span class="stamp">247420</span>
10
12
  <span class="stamp ink">approved</span>
11
13
  <span class="stamp acid">live · v0.4.1</span>
@@ -18,4 +20,5 @@
18
20
  <button class="btn-stamp purple">420 mode</button>
19
21
  <button class="btn-stamp mascot">wild card</button>
20
22
  </div>
23
+ </main>
21
24
  </body></html>
@@ -5,11 +5,14 @@
5
5
  <style>body{padding:var(--space-4);background:var(--panel-0);color:var(--panel-text);}</style><script defer src="./motion-default.js"></script>
6
6
  </head><body>
7
7
  <div class="dateline"><span>247420 · COMPONENTS · STAMPS</span><span class="spread"></span><span>3 VARIANTS</span></div>
8
- <div style="margin-top:20px;display:flex;gap:24px;align-items:center;flex-wrap:wrap">
8
+
9
+ <h1 class="sr-only">Stamps</h1>
10
+ <main aria-label="Stamps specimens"><div style="margin-top:20px;display:flex;gap:24px;align-items:center;flex-wrap:wrap">
9
11
  <span class="stamp">247420</span>
10
12
  <span class="stamp ink">approved</span>
11
13
  <span class="stamp acid">live · v0.4.1</span>
12
14
  <span class="stamp" style="transform:rotate(4deg)">do not ship</span>
13
15
  </div>
14
16
  <div class="t-meta" style="margin-top:18px">rubber-stamp motif · always rotated slightly · max 1 per page</div>
17
+ </main>
15
18
  </body></html>
@@ -5,7 +5,9 @@
5
5
  <style>body{padding:0;margin:0;background:var(--panel-0);color:var(--panel-text);min-height:200px;}</style><script defer src="./motion-default.js"></script>
6
6
  </head><body data-theme="dark">
7
7
  <div class="dateline" style="padding:20px"><span>247420 · THEME · INK MODE</span><span class="spread"></span><span>DEFAULT FOR DOCS · TOOLS</span></div>
8
- <div style="padding:0 20px 20px 20px">
8
+
9
+ <h1 class="sr-only">Theme ink</h1>
10
+ <main aria-label="Theme ink specimens"><div style="padding:0 20px 20px 20px">
9
11
  <h2 style="font-family:var(--ff-display);font-size:40px;margin:14px 0 6px 0;color:var(--panel-text)">gm runs in the dark</h2>
10
12
  <p class="t-meta" style="color:var(--panel-text-3)">ink mode inverts paper/ink; acid stays the same; border becomes paper.</p>
11
13
  <div style="margin-top:16px;display:flex;gap:12px;align-items:center">
@@ -13,4 +15,5 @@
13
15
  <button class="btn-stamp acid">ship it -></button>
14
16
  </div>
15
17
  </div>
18
+ </main>
16
19
  </body></html>
@@ -13,7 +13,9 @@
13
13
  @media (max-width:640px){.t-hero{font-size:48px !important;}.t-h1{font-size:32px !important;}}</style><script defer src="./motion-default.js"></script>
14
14
  </head><body>
15
15
  <div class="dateline"><span>247420 · TYPE · DISPLAY</span><span class="spread"></span><span>SYSTEM SANS</span></div>
16
- <div style="margin-top:20px">
16
+
17
+ <h1 class="sr-only">Type display</h1>
18
+ <main aria-label="Type display specimens"><div style="margin-top:20px">
17
19
  <div class="t-hero" style="font-size:96px">farts generally</div>
18
20
  <div class="t-micro" style="margin-top:8px">HERO / 96px · system-ui · letter-spacing -0.02em</div>
19
21
  </div>
@@ -22,4 +24,5 @@
22
24
  <div class="t-h1" style="font-size:56px">gm. it's 4:20 somewhere.</div>
23
25
  <div class="t-micro" style="margin-top:8px">H1 / 56px</div>
24
26
  </div>
27
+ </main>
25
28
  </body></html>
@@ -5,7 +5,9 @@
5
5
  <style>body{padding:var(--space-3-5);background:var(--panel-0);color:var(--panel-text);}</style><script defer src="./motion-default.js"></script>
6
6
  </head><body>
7
7
  <div class="dateline"><span>247420 · TYPE · MONO</span><span class="spread"></span><span>UI-MONOSPACE</span></div>
8
- <div style="margin-top:16px;display:grid;gap:10px">
8
+
9
+ <h1 class="sr-only">Type mono</h1>
10
+ <main aria-label="Type mono specimens"><div style="margin-top:16px;display:grid;gap:10px">
9
11
  <div><span class="t-label">LABEL / 12PX CAPS · TRACKED 0.14EM</span></div>
10
12
  <div class="t-body">body · 16px regular — the default UI voice. it should feel like a terminal with better table manners.</div>
11
13
  <div class="t-meta">meta · 12px · var(--panel-text-3) — timestamps, breadcrumbs, versions, commit hashes</div>
@@ -13,4 +15,5 @@
13
15
  <div><code>code · 14px · hairline box</code> — inline</div>
14
16
  <div class="t-h3">h3 · mono 28px / medium</div>
15
17
  </div>
18
+ </main>
16
19
  </body></html>
@@ -6,8 +6,11 @@
6
6
  </head><!-- lint-dead-controls:allow — specimen markup: this page exists to show what the component LOOKS like in isolation, so its anchors have nowhere real to point. Not a navigable surface. -->
7
7
  <body>
8
8
  <div class="dateline"><span>247420 · TYPE · PROSE</span><span class="spread"></span><span>SANS · LONG-FORM</span></div>
9
- <div class="ds-prose" style="margin-top:16px">
9
+
10
+ <h1 class="sr-only">Type prose</h1>
11
+ <main aria-label="Type prose specimens"><div class="ds-prose" style="margin-top:16px">
10
12
  <p>we make things. some of them work. a few of them become the future. we don't know which is which until later. the point is to ship — to put something into the world that didn't exist yesterday — and then pay attention to what happens next.</p>
11
13
  <p>this is the <a href="#">long-form voice</a>. quiet sans, generous line-height, designed for reading without flourish.</p>
12
14
  <!-- lint-dead-controls:allow specimen -->
13
- </div>
15
+ </div>
16
+ </main>
14
17
  </body></html>
@@ -2,20 +2,45 @@
2
2
  <html lang="en" data-theme="auto" class="ds-247420"><head><meta charset="utf-8">
3
3
  <title>type scale / 247420 preview</title><link rel="stylesheet" href="../colors_and_type.css">
4
4
  <link rel="stylesheet" href="../app-shell.css">
5
- <style>body{padding:var(--space-3-5);background:var(--panel-0);color:var(--panel-text);}</style><script defer src="./motion-default.js"></script>
5
+ <style>body{padding:var(--space-3-5);background:var(--panel-0);color:var(--panel-text);}
6
+ .ts-grid{margin-top:var(--space-3);display:grid;grid-template-columns:150px 1fr;gap:var(--space-2) var(--space-3);align-items:baseline;font-family:var(--ff-mono)}
7
+ .ts-spec{line-height:1}
8
+ .ts-display{font-family:var(--ff-display)}
9
+ .ts-mega{font-size:var(--fs-mega)}
10
+ .ts-hero{font-size:var(--fs-hero)}
11
+ .ts-h1{font-size:var(--fs-h1)}
12
+ .ts-h2{font-size:var(--fs-h2)}
13
+ .ts-h3{font-size:var(--fs-h3)}
14
+ .ts-h4{font-size:var(--fs-h4)}
15
+ .ts-xl{font-size:var(--fs-xl)}
16
+ .ts-lg{font-size:var(--fs-lg)}
17
+ .ts-body{font-size:var(--fs-body)}
18
+ .ts-sm{font-size:var(--fs-sm)}
19
+ .ts-xs{font-size:var(--fs-xs)}
20
+ .ts-tiny{font-size:var(--fs-tiny)}
21
+ .ts-micro{font-size:var(--fs-micro)}
22
+ .ts-nano{font-size:var(--fs-nano)}
23
+ .ts-pico{font-size:var(--fs-pico)}
24
+ .ts-note{margin-top:var(--space-4)}</style><script defer src="./motion-default.js"></script>
6
25
  </head><body>
7
- <div class="dateline"><span>247420 · TYPE · SCALE</span><span class="spread"></span><span>11 STEPS</span></div>
8
- <div style="margin-top:14px;display:grid;grid-template-columns:120px 1fr;gap:8px 16px;align-items:baseline;font-family:var(--ff-mono)">
9
- <div class="t-micro">HERO · 128</div><div style="font-family:var(--ff-display);font-size:48px;line-height:1">Aa</div>
10
- <div class="t-micro">H1 · 72</div><div style="font-family:var(--ff-display);font-size:36px;line-height:1">Aa</div>
11
- <div class="t-micro">H2 · 44</div><div style="font-family:var(--ff-display);font-size:28px;line-height:1">Aa</div>
12
- <div class="t-micro">H3 · 28</div><div style="font-size:22px;line-height:1">Aa</div>
13
- <div class="t-micro">H4 · 22</div><div style="font-size:18px;line-height:1">Aa</div>
14
- <div class="t-micro">LG · 18</div><div style="font-size:16px;line-height:1">Aa</div>
15
- <div class="t-micro">BODY · 16</div><div style="font-size:14px;line-height:1">Aa</div>
16
- <div class="t-micro">SM · 14</div><div style="font-size:14px;line-height:1">Aa</div>
17
- <div class="t-micro">XS · 13</div><div style="font-size:13px;line-height:1">Aa</div>
18
- <div class="t-micro">TINY · 13</div><div style="font-size:13px;line-height:1">Aa</div>
19
- <div class="t-micro">MICRO · 12</div><div style="font-size:12px;line-height:1">Aa</div>
20
- </div>
26
+ <div class="dateline"><span>247420 · TYPE · SCALE</span><span class="spread"></span><span>15 STEPS</span></div>
27
+ <h1 class="sr-only">Type scale</h1>
28
+ <main class="ts-grid" aria-label="Type scale specimens">
29
+ <div class="t-micro">MEGA</div><div class="ts-spec ts-display ts-mega">Aa</div>
30
+ <div class="t-micro">HERO</div><div class="ts-spec ts-display ts-hero">Aa</div>
31
+ <div class="t-micro">H1</div><div class="ts-spec ts-display ts-h1">Aa</div>
32
+ <div class="t-micro">H2</div><div class="ts-spec ts-display ts-h2">Aa</div>
33
+ <div class="t-micro">H3</div><div class="ts-spec ts-h3">Aa</div>
34
+ <div class="t-micro">H4</div><div class="ts-spec ts-h4">Aa</div>
35
+ <div class="t-micro">XL · 21</div><div class="ts-spec ts-xl">Aa</div>
36
+ <div class="t-micro">LG · 18</div><div class="ts-spec ts-lg">Aa</div>
37
+ <div class="t-micro">BODY · 16</div><div class="ts-spec ts-body">Aa</div>
38
+ <div class="t-micro">SM · 15</div><div class="ts-spec ts-sm">Aa</div>
39
+ <div class="t-micro">XS · 14</div><div class="ts-spec ts-xs">Aa</div>
40
+ <div class="t-micro">TINY · 13</div><div class="ts-spec ts-tiny">Aa</div>
41
+ <div class="t-micro">MICRO · 12</div><div class="ts-spec ts-micro">Aa</div>
42
+ <div class="t-micro">NANO · 11</div><div class="ts-spec ts-nano">Aa</div>
43
+ <div class="t-micro">PICO · 10</div><div class="ts-spec ts-pico">Aa</div>
44
+ </main>
45
+ <div class="t-meta ts-note">the five display rungs are fluid clamps and resize with the container; the ten text rungs are fixed px. pico and nano are the glanceable floor, secondary non-prose only.</div>
21
46
  </body></html>
@@ -4,28 +4,24 @@
4
4
  <link rel="stylesheet" href="../app-shell.css">
5
5
  <style>body{padding:var(--space-3-5);background:var(--panel-0);color:var(--panel-text);}
6
6
  .preview-grid > *{min-width:0;overflow:hidden;}
7
- @media (max-width:640px){.preview-grid{grid-template-columns:repeat(2,minmax(0,1fr)) !important;}}</style><script defer src="./motion-default.js"></script>
7
+ @media (max-width:640px){.preview-grid{grid-template-columns:repeat(2,minmax(0,1fr)) !important;}}
8
+ .wm-grid{margin-top:var(--space-3);display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--space-3) var(--space-4)}
9
+ .wm-cell{padding:var(--space-3);display:flex;align-items:center;justify-content:center;min-height:80px}
10
+ .wm-cell span{font-family:var(--ff-mono);font-size:var(--fs-h4);font-weight:500}
11
+ .wm-lead span{font-size:var(--fs-h3)}
12
+ .wm-invert{background:var(--panel-text);color:var(--panel-0)}
13
+ .wm-invert span{font-family:var(--ff-display);font-size:var(--fs-h3)}
14
+ .wm-note{margin-top:var(--space-3)}</style><script defer src="./motion-default.js"></script>
8
15
  </head><body>
9
16
  <div class="dateline"><span>247420 · BRAND · WORDMARKS</span><span class="spread"></span><span>TYPOGRAPHIC ONLY</span></div>
10
- <div style="margin-top:16px;display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:16px 24px" class="preview-grid">
11
- <div style="padding:16px;display:flex;align-items:center;justify-content:center;min-height:80px">
12
- <span style="font-family:var(--ff-mono);font-size:28px;font-weight:500">gm</span>
13
- </div>
14
- <div style="padding:16px;display:flex;align-items:center;justify-content:center;min-height:80px">
15
- <span style="font-family:var(--ff-mono);font-size:24px;font-weight:500">flatspace</span>
16
- </div>
17
- <div style="padding:16px;display:flex;align-items:center;justify-content:center;min-height:80px">
18
- <span style="font-family:var(--ff-mono);font-size:24px;font-weight:500">-> thebird</span>
19
- </div>
20
- <div style="padding:16px;display:flex;align-items:center;justify-content:center;min-height:80px">
21
- <span style="font-family:var(--ff-mono);font-size:24px;font-weight:500">spoint</span>
22
- </div>
23
- <div style="padding:16px;display:flex;align-items:center;justify-content:center;min-height:80px">
24
- <span style="font-family:var(--ff-mono);font-size:24px;font-weight:500">zellous</span>
25
- </div>
26
- <div style="padding:16px;display:flex;align-items:center;justify-content:center;min-height:80px;background:var(--panel-text);color:var(--panel-0)">
27
- <span style="font-family:var(--ff-display);font-size:28px">247420</span>
28
- </div>
29
- </div>
30
- <div class="t-meta" style="margin-top:16px">no pictorial marks. one mono glyph allowed as prefix.</div>
17
+ <h1 class="sr-only">Wordmarks</h1>
18
+ <main class="wm-grid preview-grid" aria-label="Wordmark specimens">
19
+ <div class="wm-cell wm-lead"><span>gm</span></div>
20
+ <div class="wm-cell"><span>flatspace</span></div>
21
+ <div class="wm-cell"><span>-> thebird</span></div>
22
+ <div class="wm-cell"><span>spoint</span></div>
23
+ <div class="wm-cell"><span>zellous</span></div>
24
+ <div class="wm-cell wm-invert"><span>247420</span></div>
25
+ </main>
26
+ <div class="t-meta wm-note">no pictorial marks. one mono glyph allowed as prefix.</div>
31
27
  </body></html>
@@ -15,6 +15,7 @@ import { WorksList, WritingList, EventList } from './content/lists.js';
15
15
  import { Kpi, Sparkline, BarChart } from './content/charts.js';
16
16
  import { Table, HealthTable, ProcessRegistryTable } from './content/table.js';
17
17
  import { SearchInput, TextField, Select, Form } from './content/fields.js';
18
+ import { InputOTP } from './content/otp-input.js';
18
19
  import { Spinner, Skeleton, Alert, FilterPills } from './content/feedback.js';
19
20
  import { HomeView, ProjectView } from './content/views.js';
20
21
 
@@ -27,7 +28,7 @@ export {
27
28
  WorksList, WritingList, EventList,
28
29
  Kpi, Sparkline, BarChart,
29
30
  Table, HealthTable, ProcessRegistryTable,
30
- SearchInput, TextField, Select, Form,
31
+ SearchInput, TextField, Select, Form, InputOTP,
31
32
  Spinner, Skeleton, Alert, FilterPills,
32
33
  HomeView, ProjectView,
33
34
  };
@@ -15,7 +15,7 @@ export {
15
15
  Hero, HeroFromPageData, Marquee, Install, CliBlock, Receipt, Changelog,
16
16
  WorksList, WritingList, Manifesto, Section, PageHeader,
17
17
  Kpi, Sparkline, BarChart, Table, HealthTable, ProcessRegistryTable, SearchInput, TextField, Select, EventList,
18
- HomeView, ProjectView, Form,
18
+ HomeView, ProjectView, Form, InputOTP,
19
19
  Spinner, Skeleton, Alert, FilterPills, Avatar, avatarInitial
20
20
  } from './components/content.js';
21
21
 
@@ -33,7 +33,7 @@ export {
33
33
  ConversationList, SessionCard, SessionDashboard, SessionMeta, fmtDuration, fmtTime, fmtAgo, AgentListSkeleton
34
34
  } from './components/sessions.js';
35
35
 
36
- export { ContextPane } from './components/context-pane.js';
36
+ export { ContextPane, ContextMeter, ContextTreemap, ContextXRayPanel } from './components/context-pane.js';
37
37
 
38
38
  export { SpreadsheetPreview } from './components/spreadsheet-preview.js';
39
39
 
@@ -49,7 +49,8 @@ export { ModelsConfig } from './components/models-config.js';
49
49
 
50
50
  export {
51
51
  DEFAULT_PHASES, PhaseWalk, TreeNode, BarRow, RateCell,
52
- StatTile, StatsGrid, SubGrid, SessionRow, DevRow, LiveLogEntry, LiveLog
52
+ StatTile, StatsGrid, SubGrid, SessionRow, DevRow, LiveLogEntry, LiveLog,
53
+ Progress
53
54
  } from './components/data-density.js';
54
55
 
55
56
  export {
@@ -78,12 +79,21 @@ export {
78
79
  playCompletionCue
79
80
  } from './components/voice.js';
80
81
 
82
+ export {
83
+ LiveCursorOverlay, RemoteSelectionRings, RecentEditHighlightFlash,
84
+ AgentPresenceChip, PresenceBar
85
+ } from './components/collab.js';
86
+
81
87
  export { ThemeToggle } from './components/theme-toggle.js';
82
88
 
83
89
  export {
84
90
  Checkbox, Radio, RadioGroup, Toggle, Field, useFormValidation, focusFirstInvalidField
85
91
  } from './components/form-primitives.js';
86
92
 
93
+ export { Slider } from './components/slider.js';
94
+
95
+ export { Carousel } from './components/carousel.js';
96
+
87
97
  export {
88
98
  useDraggable, useDropTarget, useNumberScrub, usePointerDrag, Reorderable,
89
99
  useKeyboardShortcut, formatShortcut, ShortcutHint, ShortcutList,
@@ -103,7 +113,7 @@ export {
103
113
  Pager, JsonViewer,
104
114
  Grid, GridItem,
105
115
  Collapse, CollapseGroup,
106
- Divider,
116
+ Divider, AspectRatio,
107
117
  useMediaQuery,
108
118
  BP_SM, BP_MD, BP_LG, BP_XL,
109
119
  InfoRow, InfoSection, DiagnosticsPanel,
@@ -114,7 +124,8 @@ export {
114
124
  Tooltip, Popover, Dropdown, useLongPress, useFloating,
115
125
  CommandPalette, EmojiPicker, BootOverlay, SettingsPopover,
116
126
  AuthModal, VideoLightbox, PermissionMenu, ApprovalPrompt, withBusy,
117
- MenuButton
127
+ MenuButton,
128
+ HoverCard, Menubar
118
129
  } from './components/overlay-primitives.js';
119
130
 
120
131
  export {
@@ -129,3 +140,8 @@ export {
129
140
  } from './components/freddie/runtime.js';
130
141
 
131
142
  export { mountCommunityApp } from './community-app.js';
143
+
144
+ export {
145
+ Calendar, DatePicker, DateRangePicker,
146
+ WEEKDAY_LABELS, buildMonthGrid, formatDate, monthLabel
147
+ } from './components/calendar.js';
@@ -415,7 +415,12 @@ html, body {
415
415
  .app-text-cursor { animation: app-cursor-blink 0.5s step-end infinite; }
416
416
  @keyframes app-cursor-blink { 50% { opacity: 0; } }
417
417
  }
418
- @media (max-width: 1023px) and (min-width: 768px) {
418
+ /* (min-height: 501px): short-landscape windows (<501px tall) in this width
419
+ * band are on the mobile contract, not tablet — see the matching guard on
420
+ * the .wm-root tablet-inset rule below. Without the guard this block showed
421
+ * the side-rail and rail-offset the menubar/taskbar while the mobile band
422
+ * simultaneously tried to lay out rail-less full-screen windows. */
423
+ @media (max-width: 1023px) and (min-width: 768px) and (min-height: 501px) {
419
424
  .os-side-rail { display: flex; }
420
425
  .ds-247420 .wm-root { left: calc(var(--os-rail-w) + env(safe-area-inset-left, 0px)); }
421
426
  .os-menubar { padding-left: calc(var(--os-rail-w) + env(safe-area-inset-left, 0px) + var(--space-2, 8px)); }
@@ -563,8 +568,16 @@ html, body {
563
568
  /* With bars overlaying the wm-root, a maximized window's 34px titlebar would
564
569
  * sit exactly under the 34px menubar (untappable) and its body under the
565
570
  * taskbar; keep maximized windows between the bars on desktop/tablet. The
566
- * mobile band re-asserts its own contract in the responsive layer below. */
567
- @media (min-width: 768px) {
571
+ * mobile band re-asserts its own contract in the responsive layer below.
572
+ * The (min-height: 501px) guard keeps this rule out of the short-landscape
573
+ * mobile band (768-1023 wide AND <500 tall): the mobile .wm-root already
574
+ * carries the bar-inset there, so this between-the-bars geometry used to
575
+ * DOUBLE-inset maximized windows — a restored wm-max window measured
576
+ * y=104/h=240 instead of y=52/h=344 at 782x448 (witnessed live on thebird
577
+ * 2026-07-29). Guarding here (same idiom as the tablet-band rules below)
578
+ * instead of overriding inside the mobile band keeps the !important
579
+ * ratchet flat — no new declaration was needed. */
580
+ @media (min-width: 768px) and (min-height: 501px) {
568
581
  /* LOAD-BEARING !important: wm.css declares `.wm-win.wm-max{top:0!important;
569
582
  * height:100%!important}` (wm.css:137-143). Only another !important can beat
570
583
  * an !important, regardless of specificity. */
@@ -640,6 +653,12 @@ html.ds-247420 { touch-action: pan-x pan-y; overscroll-behavior: none; -webkit-t
640
653
  color: var(--os-fg);
641
654
  min-width: 280px;
642
655
  box-shadow: var(--shadow-overlay);
656
+ /* Uncapped, the switcher grew one row per open window with no bound —
657
+ * witnessed live at 27 windows on a 448px-tall viewport: the overlay
658
+ * measured y=-344 / h=1136, its top rows unreachable above the screen.
659
+ * Cap to the viewport between the bars and scroll the overflow. */
660
+ max-height: calc(100dvh - (2 * var(--os-bar-h, 34px)) - 24px);
661
+ overflow-y: auto;
643
662
  }
644
663
  .ds-247420 .wm-switcher-item {
645
664
  padding: var(--space-2) var(--space-2-75);
@@ -1488,6 +1507,11 @@ html.ds-247420 { touch-action: pan-x pan-y; overscroll-behavior: none; -webkit-t
1488
1507
  border-radius: 0;
1489
1508
  box-shadow: none;
1490
1509
  }
1510
+ /* A maximized window on the mobile band is just the full-screen window the
1511
+ * rule above already describes — the desktop/tablet `.wm-win.wm-max`
1512
+ * between-the-bars rule is kept out of this band by its own
1513
+ * (min-height: 501px) guard (see its comment above), so no override is
1514
+ * needed here and the !important ratchet stays flat. */
1491
1515
  /* titlebar must host a 44px close button -> 44px bar (beats the 34px brand
1492
1516
  * clamp by later source order at equal specificity) */
1493
1517
  .ds-247420 .wm-bar {
@@ -1529,8 +1553,17 @@ html.ds-247420 { touch-action: pan-x pan-y; overscroll-behavior: none; -webkit-t
1529
1553
  * the scoped inset:0; re-asserted here at equal (0,2,0) specificity but later
1530
1554
  * source order, which is enough now that the earlier rule is also scoped.
1531
1555
  * `inset` alone carries the left offset — the separate `left` declaration it
1532
- * used to be paired with was redundant with this shorthand. --- */
1533
- @media (min-width: 768px) and (max-width: 1023px) {
1556
+ * used to be paired with was redundant with this shorthand.
1557
+ * The (min-height: 501px) guard excludes the short-landscape overlap band
1558
+ * (768-1023 wide AND <500 tall): both this query and the mobile band's
1559
+ * (max-height: 500px)+(orientation: landscape) match there, and this rule —
1560
+ * later in source order — used to override the mobile bar-inset, dropping
1561
+ * .wm-root to top:0 so full-screen windows covered the menubar and their
1562
+ * close buttons landed under it occluded and untappable (witnessed live on
1563
+ * thebird at 782x448: elementFromPoint on the chat close button hit
1564
+ * .os-clock, menubar z 601 over window z 102). Short-landscape is the
1565
+ * phone/mobile contract, not tablet. --- */
1566
+ @media (min-width: 768px) and (max-width: 1023px) and (min-height: 501px) {
1534
1567
  .ds-247420 .wm-root {
1535
1568
  inset: 0 0 0 var(--os-rail-w);
1536
1569
  }
@@ -160,7 +160,7 @@ function App() {
160
160
  ]
161
161
  }),
162
162
  main: [
163
- h('div', { class: 'ds-section' },
163
+ h('div', { class: 'ds-app-surface ds-section-pad' },
164
164
  Heading({ level: 1, children: 'aicat' }),
165
165
  Lede({ children: 'an ai assistant with a cat persona. she replies in text, code (highlighted), markdown, images, pdfs, file attachments, or link cards — depending on what you ask.' }),
166
166
  AICatPortrait({
@@ -1,5 +1,5 @@
1
1
  import * as webjsx from 'webjsx';
2
- import { Topbar, Crumb, Status, Side, AppShell, Panel, Heading, Lede, Chip } from 'ds/components.js';
2
+ import { Topbar, Crumb, Status, Side, AppShell, Panel, Heading, Lede, Chip, Carousel } from 'ds/components.js';
3
3
  import { mountKit } from 'ds/bootstrap.js';
4
4
  const h = webjsx.createElement;
5
5
 
@@ -115,9 +115,21 @@ function Swatch(t) {
115
115
  );
116
116
  }
117
117
 
118
+ // Real Carousel adoption: browsing between tiles inside the lightbox without
119
+ // closing and reopening. All 12 items go into the track; the carousel scrolls
120
+ // the opened tile into view on open via a ref (Carousel exposes no imperative
121
+ // scroll-to-index API, so this reaches its own rendered track element the
122
+ // same way editor-primitives/split-panel.js reaches its own root).
123
+ function LightboxTile(it) {
124
+ return h('div', { class: 'ds-lightbox-preview', style: '--tile-tone:var(--' + it.tone + ')' },
125
+ h('div', {}, it.caption),
126
+ h('p', { class: 'ds-m0' }, h('strong', {}, it.label))
127
+ );
128
+ }
129
+
118
130
  function Lightbox() {
119
131
  if (!state.open) return null;
120
- const it = items.find((i) => i.id === state.open);
132
+ const openIndex = items.findIndex((i) => i.id === state.open);
121
133
  const close = () => { state.open = null; kit.render(); };
122
134
  return h('div', {
123
135
  onclick: close,
@@ -126,16 +138,24 @@ function Lightbox() {
126
138
  ref: (el) => { if (el && !el._dsLbFocused) { el._dsLbFocused = true; el.focus(); } },
127
139
  class: 'ds-lightbox'
128
140
  },
129
- h('div', { onclick: (e) => e.stopPropagation(), class: 'ds-lightbox-card' },
141
+ h('div', {
142
+ onclick: (e) => e.stopPropagation(), class: 'ds-lightbox-card ds-lightbox-card--carousel',
143
+ ref: (el) => {
144
+ if (!el || el._dsLbScrolled === state.open) return;
145
+ el._dsLbScrolled = state.open;
146
+ const track = el.querySelector('.ds-carousel-track');
147
+ const item = track && track.children[openIndex];
148
+ if (item) item.scrollIntoView({ inline: 'start', behavior: 'instant' });
149
+ }
150
+ },
130
151
  h('div', { class: 'ds-lightbox-head' },
131
- h('span', { class: 'ds-lightbox-tag' }, 'tile · ' + it.id),
152
+ h('span', { class: 'ds-lightbox-tag' }, 'tile · ' + (openIndex + 1) + ' of ' + items.length),
132
153
  h('button', { class: 'btn', onclick: close }, 'close')
133
154
  ),
134
- h('div', { class: 'ds-lightbox-preview', style: '--tile-tone:var(--' + it.tone + ')' }, it.caption),
135
- h('p', { class: 'ds-m0' }, h('strong', {}, it.label)),
155
+ Carousel({ items, renderItem: LightboxTile, label: 'gallery tiles' }),
136
156
  // Tells the reader how to leave, which is the one thing they need
137
157
  // from a lightbox. It previously described its own implementation.
138
- h('p', { class: 'ds-m0 ds-text-2' }, 'esc, or click anywhere outside, to close.')
158
+ h('p', { class: 'ds-m0 ds-text-2' }, 'use the carousel arrows to browse tiles, esc or click outside to close.')
139
159
  )
140
160
  );
141
161
  }
@@ -1,13 +1,32 @@
1
1
  import * as webjsx from 'webjsx';
2
- import { Topbar, Crumb, Status, AppShell, Panel, Heading, Lede, Chip, Icon, Divider } from 'ds/components.js';
2
+ import { Topbar, Crumb, Status, AppShell, Panel, Heading, Lede, Chip, Icon, Divider, InputOTP } from 'ds/components.js';
3
3
  import { mountKit } from 'ds/bootstrap.js';
4
4
  import { shortUid } from 'ds/uid.js';
5
5
  const h = webjsx.createElement;
6
6
 
7
7
  const root = document.getElementById('root');
8
- const state = { mode: 'signin', email: '', password: '', remember: true, sent: false, error: '', loading: null, demoUrl: '' };
8
+ const state = { mode: 'signin', email: '', password: '', remember: true, sent: false, error: '', loading: null, demoUrl: '', otp: '', otpVerified: false, otpError: '' };
9
9
 
10
- function setMode(m) { state.mode = m; state.sent = false; state.error = ''; state.loading = null; state.demoUrl = ''; kit.render(); }
10
+ function setMode(m) { state.mode = m; state.sent = false; state.error = ''; state.loading = null; state.demoUrl = ''; state.otp = ''; state.otpVerified = false; state.otpError = ''; kit.render(); }
11
+
12
+ // A specimen page cannot actually deliver an email, so the interactive
13
+ // stand-in for "click the link in your inbox" is a fixed demo code the
14
+ // sent-state copy tells the visitor to type back — exercises real InputOTP
15
+ // wiring (auto-advance, backspace-retreat, paste-split, onComplete) without
16
+ // pretending to be a real server round trip.
17
+ const DEMO_CODE = '247420';
18
+
19
+ function verifyOtp(code) {
20
+ state.otp = code;
21
+ if (code.length < 6) { state.otpError = ''; kit.render(); return; }
22
+ if (code === DEMO_CODE) {
23
+ state.otpVerified = true;
24
+ state.otpError = '';
25
+ } else {
26
+ state.otpError = 'that code doesn\'t match — demo code is ' + DEMO_CODE + '.';
27
+ }
28
+ kit.render();
29
+ }
11
30
 
12
31
  function submit(e) {
13
32
  e.preventDefault();
@@ -141,15 +160,30 @@ function Form() {
141
160
  // the only recovery was a page reload. Every sent state that waits on
142
161
  // an email now offers the correction path.
143
162
  const waiting = state.mode === 'magic' || state.mode === 'reset';
163
+ const magicVerify = state.mode === 'magic' && !state.otpVerified;
144
164
  return h('div', { class: 'ds-auth-form ds-auth-sent' },
145
- h('div', { class: 'ds-auth-sent-glyph' }, '[x]'),
146
- h('p', { class: 'ds-auth-sent-title' }, state.mode === 'magic' ? 'check your email' : (state.mode === 'reset' ? 'reset link sent' : 'welcome back')),
147
- h('p', { class: 'ds-auth-sent-sub' }, state.mode === 'magic'
148
- ? 'we sent a sign-in link to ' + state.email + '. it expires in 15 minutes.'
165
+ h('div', { class: 'ds-auth-sent-glyph' }, state.otpVerified ? '[ok]' : '[x]'),
166
+ h('p', { class: 'ds-auth-sent-title' },
167
+ state.otpVerified ? 'verified' :
168
+ state.mode === 'magic' ? 'check your email' : (state.mode === 'reset' ? 'reset link sent' : 'welcome back')),
169
+ h('p', { class: 'ds-auth-sent-sub' },
170
+ state.otpVerified ? 'signed in. taking you to the index.' :
171
+ state.mode === 'magic'
172
+ ? 'we sent a sign-in link to ' + state.email + '. it expires in 15 minutes — or enter the ' + DEMO_CODE.length + '-digit code from the email below (demo code: ' + DEMO_CODE + ').'
149
173
  : (state.mode === 'reset' ? 'we sent a reset link to ' + state.email + '. follow it to set a new password.' : 'signed in. taking you to the index.')),
150
- waiting ? h('button', {
174
+ magicVerify ? h('div', { class: 'ds-auth-otp-wrap' },
175
+ InputOTP({
176
+ length: DEMO_CODE.length, value: state.otp,
177
+ onChange: (code) => verifyOtp(code),
178
+ onComplete: (code) => verifyOtp(code),
179
+ error: Boolean(state.otpError),
180
+ label: 'sign-in code',
181
+ }),
182
+ state.otpError ? h('div', { class: 'ds-auth-error', role: 'alert' }, state.otpError) : null
183
+ ) : null,
184
+ waiting && !state.otpVerified ? h('button', {
151
185
  class: 'btn',
152
- onclick: (e) => { e.preventDefault(); state.sent = false; kit.render(); }
186
+ onclick: (e) => { e.preventDefault(); state.sent = false; state.otp = ''; state.otpVerified = false; state.otpError = ''; kit.render(); }
153
187
  }, 'use a different email') : null
154
188
  );
155
189
  }
@@ -206,7 +240,14 @@ function App() {
206
240
  topbar: Topbar({ brand: '247420', leaf: 'auth', items: [['index', '../../']] }),
207
241
  crumb: Crumb({ trail: ['247420', 'kits'], leaf: state.mode === 'signin' ? 'signin' : 'signin · ' + state.mode }),
208
242
  main: [
209
- h('div', { class: 'ds-section ds-auth-wrap' },
243
+ // .ds-app-surface, not .ds-section: an auth screen is an Operate
244
+ // surface, so its h1 belongs on the app typescale. Under .ds-section
245
+ // the title rendered at the 64px marketing display ceiling (77px
246
+ // measured) and the root carried a 96px editorial margin, which
247
+ // together pushed a 430px card to a 946px scroll height inside a
248
+ // ~514px pane and gave .app-main its own inner scrollbar at every
249
+ // real window height.
250
+ h('div', { class: 'ds-app-surface ds-auth-wrap' },
210
251
  h('div', { class: 'ds-auth-col' },
211
252
  Heading({ level: 1, children: headings[0] }),
212
253
  Lede({ children: headings[1] }),