/home page Step 2 told users to mkdir ~/Desktop/<workspace_dir>, but the pasted install script's Step 2 pwd-check expected $HOME/<workspace_dir> and warned 'normally installed in ~/FoundryAI' — sending users on the Desktop path through an unnecessary 'install here' confirmation. Align Step 2 (pwd check + warning copy + manual mkdir hint), Step 9 restart-claude cue, post-install /home hero, and the 'don't create Projects/' callout to ~/Desktop/<workspace_dir>. Update tests.
3242 lines
129 KiB
HTML
3242 lines
129 KiB
HTML
{% extends "base.html" %}
|
||
|
||
{% block title %}Setup — {{ instance_name or "AI Data Analyst" }}{% endblock %}
|
||
|
||
{% block content %}
|
||
{% include "_page_chrome.html" %}
|
||
<style>
|
||
.home-mock {
|
||
/* Design-system scope — tokens live globally in
|
||
app/web/static/css/design-tokens.css (loaded by base.html).
|
||
This rule only applies the typography + base ink that the
|
||
redesign opts into. */
|
||
font-family: var(--ds-font);
|
||
color: var(--ds-text-primary);
|
||
font-size: 15px;
|
||
line-height: 1.55;
|
||
}
|
||
.home-mock,
|
||
.home-mock * { font-family: var(--ds-font); }
|
||
.home-mock code, .home-mock kbd, .home-mock pre,
|
||
.home-mock .install-cmd, .home-mock .install-cmd *,
|
||
.home-mock .terminal-body, .home-mock .terminal-body * {
|
||
font-family: var(--ds-font-mono);
|
||
}
|
||
.home-mock * { box-sizing: border-box; }
|
||
|
||
.home-mock .install-hero {
|
||
position: relative;
|
||
background: linear-gradient(135deg, var(--ds-hero-bg) 0%, var(--ds-hero-bg-deep) 100%);
|
||
color: var(--ds-hero-ink);
|
||
border-radius: 16px;
|
||
padding: 38px 40px;
|
||
margin-bottom: 22px;
|
||
box-shadow: var(--ds-shadow-lg);
|
||
}
|
||
/* Subtle green accent strip across the top of the install-hero so the
|
||
wizard reads as the brand's primary CTA card. */
|
||
.home-mock .install-hero::before {
|
||
content: "";
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
height: 3px;
|
||
background: linear-gradient(90deg, var(--ds-brand-accent) 0%, var(--ds-primary) 60%, transparent 100%);
|
||
border-top-left-radius: 16px;
|
||
border-top-right-radius: 16px;
|
||
}
|
||
.home-mock .install-hero-close {
|
||
position: absolute;
|
||
top: 14px;
|
||
right: 14px;
|
||
width: 32px;
|
||
height: 32px;
|
||
border: none;
|
||
border-radius: 50%;
|
||
background: rgba(255, 255, 255, 0.14);
|
||
color: white;
|
||
font-size: 18px;
|
||
line-height: 1;
|
||
cursor: pointer;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
transition: background 0.15s;
|
||
}
|
||
.home-mock .install-hero-close:hover,
|
||
.home-mock .install-hero-close:focus-visible {
|
||
background: rgba(255, 255, 255, 0.28);
|
||
outline: none;
|
||
}
|
||
.home-mock .offboard-strip {
|
||
margin: 0 0 22px;
|
||
padding: 10px 14px;
|
||
border: 1px solid var(--ds-border);
|
||
border-radius: 10px;
|
||
background: var(--ds-border-light);
|
||
color: var(--ds-text-secondary);
|
||
font-size: 13px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 12px;
|
||
}
|
||
.home-mock .offboard-strip button {
|
||
border: 1px solid var(--ds-border);
|
||
background: white;
|
||
border-radius: 6px;
|
||
padding: 4px 10px;
|
||
font-size: 13px;
|
||
cursor: pointer;
|
||
}
|
||
.home-mock .offboard-strip button:hover { background: var(--ds-border-light); }
|
||
.home-mock .install-hero .eyebrow {
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.8px;
|
||
opacity: 0.85;
|
||
margin-bottom: 10px;
|
||
}
|
||
.home-mock .install-hero h1 {
|
||
font-size: 30px;
|
||
font-weight: 600;
|
||
letter-spacing: -0.4px;
|
||
margin-bottom: 12px;
|
||
line-height: 1.2;
|
||
color: white;
|
||
}
|
||
.home-mock .install-hero .lead {
|
||
font-size: 15px;
|
||
opacity: 0.94;
|
||
line-height: 1.6;
|
||
margin-bottom: 22px;
|
||
}
|
||
.home-mock .install-hero .lead strong { font-weight: 600; }
|
||
|
||
/* ── Getting Started + Overview + Usage modes (added in PR #289) ───
|
||
Three new content cards rendered between the install-hero and the
|
||
connector tiles. Getting Started + Usage modes ship in OSS;
|
||
Overview body comes from instance.overview yaml (operator-owned),
|
||
hidden when unset. Generic dismiss pattern: any element with
|
||
`<button class="home-card-close" data-dismiss-key="...">` gets
|
||
per-device localStorage dismissibility for free. */
|
||
|
||
.home-mock .home-overview {
|
||
position: relative;
|
||
background: white;
|
||
border: 1px solid var(--ds-border);
|
||
border-radius: 12px;
|
||
padding: 22px 24px;
|
||
margin-bottom: 18px;
|
||
box-shadow: 0 1px 3px rgba(15, 23, 42, 0.04);
|
||
}
|
||
/* `.home-usage` (the Surfaces section) used to inherit the white-card
|
||
chrome above; in the design spec the section is bare (just header +
|
||
grid on the page bg), so we leave it un-styled here and only set
|
||
bottom-margin. */
|
||
.home-mock .home-usage {
|
||
position: relative;
|
||
background: transparent;
|
||
border: none;
|
||
border-radius: 0;
|
||
padding: 0;
|
||
margin-bottom: 36px;
|
||
box-shadow: none;
|
||
}
|
||
/* Install-hero is the scroll target for Getting Started's first row.
|
||
Offset the anchor jump by the 72px sticky .app-header height + a
|
||
bit of breathing room so the hero's eyebrow lands cleanly under
|
||
the header bar. */
|
||
.home-mock .install-hero { scroll-margin-top: 88px; }
|
||
|
||
.home-mock .home-overview > h2,
|
||
.home-mock .home-usage > header h2 {
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
margin: 0 0 6px;
|
||
color: var(--ds-text-primary);
|
||
}
|
||
.home-mock .home-usage > header p {
|
||
font-size: 13px;
|
||
color: var(--ds-text-secondary);
|
||
margin: 0 0 14px;
|
||
}
|
||
|
||
.home-mock .home-card-close {
|
||
position: absolute;
|
||
top: 12px;
|
||
right: 12px;
|
||
width: 28px;
|
||
height: 28px;
|
||
border: none;
|
||
border-radius: 50%;
|
||
background: transparent;
|
||
color: var(--ds-text-muted);
|
||
font-size: 18px;
|
||
line-height: 1;
|
||
cursor: pointer;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
.home-mock .home-card-close:hover,
|
||
.home-mock .home-card-close:focus-visible {
|
||
background: var(--ds-border-light);
|
||
color: var(--ds-text-primary);
|
||
outline: none;
|
||
}
|
||
|
||
|
||
.home-mock .home-overview-body {
|
||
font-size: 14px;
|
||
line-height: 1.6;
|
||
color: var(--ds-text-primary);
|
||
}
|
||
.home-mock .home-overview-body p { margin: 0 0 10px; }
|
||
.home-mock .home-overview-body p:last-child { margin-bottom: 0; }
|
||
.home-mock .home-overview-body a { color: var(--ds-primary); }
|
||
|
||
.home-mock .home-usage-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 1fr);
|
||
gap: 12px;
|
||
margin-bottom: 12px;
|
||
}
|
||
.home-mock .home-usage-item {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 4px;
|
||
padding: 14px;
|
||
border: 1px solid var(--ds-border);
|
||
border-radius: 10px;
|
||
text-decoration: none;
|
||
color: inherit;
|
||
transition: border-color 0.15s, background 0.15s;
|
||
}
|
||
/* Hover affordance only on the anchor variants (VS Code, Claude
|
||
Desktop). The Terminal tile renders as a plain <div> — the setup
|
||
hero above already covers the terminal install path, so a
|
||
click-through from here would round-trip to content the user just
|
||
scrolled past. */
|
||
.home-mock a.home-usage-item:hover {
|
||
border-color: var(--ds-primary);
|
||
background: var(--ds-primary-light);
|
||
}
|
||
.home-mock .home-usage-item .ico { font-size: 20px; }
|
||
.home-mock .home-usage-item strong { font-weight: 600; }
|
||
.home-mock .home-usage-item span {
|
||
font-size: 13px;
|
||
color: var(--ds-text-secondary);
|
||
line-height: 1.5;
|
||
}
|
||
.home-mock .home-usage-foot {
|
||
font-size: 13px;
|
||
color: var(--ds-text-secondary);
|
||
margin: 0;
|
||
}
|
||
@media (max-width: 720px) {
|
||
.home-mock .home-usage-grid { grid-template-columns: 1fr; }
|
||
}
|
||
|
||
.home-mock .what-is {
|
||
display: grid;
|
||
grid-template-columns: repeat(2, 1fr);
|
||
gap: 14px;
|
||
margin-bottom: 18px;
|
||
}
|
||
.home-mock .what-is-item {
|
||
background: white;
|
||
border: 1px solid var(--ds-border);
|
||
border-radius: 12px;
|
||
padding: 18px 22px;
|
||
color: inherit;
|
||
text-decoration: none;
|
||
display: block;
|
||
transition: border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
|
||
}
|
||
.home-mock a.what-is-item:hover {
|
||
border-color: var(--ds-primary);
|
||
box-shadow: 0 4px 12px rgba(46, 168, 119, 0.14);
|
||
transform: translateY(-1px);
|
||
}
|
||
.home-mock .what-is-item .ico { font-size: 22px; margin-bottom: 8px; display: block; }
|
||
.home-mock .what-is-item .ttl {
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
color: var(--ds-text-primary);
|
||
margin-bottom: 4px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 8px;
|
||
}
|
||
.home-mock .what-is-item .ttl .arrow {
|
||
color: var(--ds-primary);
|
||
font-weight: 600;
|
||
transition: transform 0.15s ease;
|
||
}
|
||
.home-mock a.what-is-item:hover .ttl .arrow {
|
||
transform: translateX(2px);
|
||
}
|
||
.home-mock .what-is-item .desc {
|
||
font-size: 13px;
|
||
color: var(--ds-text-secondary);
|
||
line-height: 1.55;
|
||
}
|
||
|
||
.home-mock .look-around-lead {
|
||
font-size: 13px;
|
||
color: var(--ds-text-secondary);
|
||
line-height: 1.55;
|
||
margin: -4px 0 12px;
|
||
}
|
||
.home-mock .look-around-grid {
|
||
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
|
||
margin-bottom: 22px;
|
||
}
|
||
|
||
.home-mock .install-block {
|
||
background: rgba(15, 23, 42, 0.55);
|
||
border: 1px solid rgba(255, 255, 255, 0.10);
|
||
border-radius: 12px;
|
||
padding: 18px 20px;
|
||
}
|
||
.home-mock .install-block + .install-block { margin-top: 14px; }
|
||
.home-mock .os-tabs {
|
||
display: flex;
|
||
gap: 4px;
|
||
margin-bottom: 8px;
|
||
border-bottom: 1px solid rgba(255, 255, 255, 0.10);
|
||
}
|
||
.home-mock .os-tab {
|
||
background: transparent;
|
||
border: none;
|
||
color: rgba(255, 255, 255, 0.60);
|
||
font-size: 12px;
|
||
font-weight: 500;
|
||
padding: 8px 14px;
|
||
cursor: pointer;
|
||
border-bottom: 2px solid transparent;
|
||
margin-bottom: -1px;
|
||
font-family: inherit;
|
||
}
|
||
.home-mock .os-tab:hover { color: rgba(255, 255, 255, 0.85); }
|
||
.home-mock .os-tab.is-active {
|
||
color: white;
|
||
border-bottom-color: #FBBF24;
|
||
}
|
||
/* `[hidden]` UA stylesheet has lower specificity than `.install-cmd { display: flex }`,
|
||
so we need an explicit override to actually hide the inactive tab panel. */
|
||
.home-mock .install-cmd[hidden] { display: none; }
|
||
.home-mock .install-block .label {
|
||
font-size: 11px;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.6px;
|
||
opacity: 0.78;
|
||
margin-bottom: 10px;
|
||
font-weight: 600;
|
||
}
|
||
.home-mock .install-cmd {
|
||
background: #0F172A;
|
||
border: 1px solid rgba(255, 255, 255, 0.10);
|
||
border-radius: 8px;
|
||
padding: 14px 18px;
|
||
font-family: var(--ds-font-mono);
|
||
font-size: 13.5px;
|
||
color: #FBBF24;
|
||
display: flex;
|
||
align-items: flex-start;
|
||
justify-content: space-between;
|
||
gap: 16px;
|
||
}
|
||
.home-mock .install-cmd .multiline { display: block; line-height: 1.7; white-space: pre-wrap; flex: 1; }
|
||
.home-mock .install-cmd .copy-btn {
|
||
background: rgba(255, 255, 255, 0.10);
|
||
color: white;
|
||
border: 1px solid rgba(255, 255, 255, 0.18);
|
||
padding: 7px 14px;
|
||
border-radius: 6px;
|
||
font-size: 12px;
|
||
cursor: pointer;
|
||
font-weight: 500;
|
||
flex-shrink: 0;
|
||
}
|
||
.home-mock .install-cmd .copy-btn:hover { background: rgba(255, 255, 255, 0.18); }
|
||
|
||
.home-mock .install-note {
|
||
font-size: 12.5px;
|
||
color: rgba(255, 255, 255, 0.85);
|
||
margin-top: 12px;
|
||
line-height: 1.55;
|
||
}
|
||
/* Inline `<code>` chips inside the blue install-hero (lead paragraphs +
|
||
install-note prose). Previous rule was `rgba(255,255,255,0.12)` bg +
|
||
inherited near-white text — barely-visible faint-white film on the
|
||
blue gradient with low-contrast white text on top (≈2:1, fails WCAG
|
||
AA). The visual effect read as a muddy bluish blob — the user
|
||
reported the text as "dark blue/black font" because the chip blended
|
||
into the hero and the white text lost its edges.
|
||
Switched to the same amber-on-dark-navy palette as the .install-cmd
|
||
copy-button boxes below (≈9:1 contrast). Border tint matches the
|
||
amber accent so chips read as clickable affordances even with the
|
||
transparent navy bg. */
|
||
.home-mock .install-hero code,
|
||
.home-mock .install-note code,
|
||
.home-mock .install-note > code {
|
||
background: rgba(15, 23, 42, 0.75);
|
||
color: #FBBF24;
|
||
border: 1px solid rgba(251, 191, 36, 0.30);
|
||
padding: 1px 6px;
|
||
border-radius: 4px;
|
||
font-family: var(--ds-font-mono);
|
||
font-size: 11.5px;
|
||
}
|
||
/* Links inside the blue install-hero need a non-blue color — the default
|
||
`<a>` style elsewhere on /home renders as `var(--ds-primary)` blue,
|
||
which is invisible against the hero's blue background. Use the same
|
||
high-contrast white-with-underline pattern the lead paragraph uses,
|
||
and bump hover to full white so the affordance stays obvious. */
|
||
.home-mock .install-hero a {
|
||
color: #ffffff;
|
||
text-decoration: underline;
|
||
text-decoration-color: rgba(255, 255, 255, 0.6);
|
||
text-underline-offset: 2px;
|
||
}
|
||
.home-mock .install-hero a:hover,
|
||
.home-mock .install-hero a:focus {
|
||
color: #ffffff;
|
||
text-decoration-color: #ffffff;
|
||
}
|
||
|
||
.home-mock .section-label {
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.6px;
|
||
color: var(--ds-text-secondary);
|
||
margin: 30px 0 12px;
|
||
}
|
||
|
||
.home-mock .grid-2 {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
gap: 18px;
|
||
margin-bottom: 18px;
|
||
}
|
||
.home-mock .card {
|
||
background: white;
|
||
border: 1px solid var(--ds-border);
|
||
border-radius: 12px;
|
||
padding: 22px;
|
||
}
|
||
.home-mock .card h3 { font-size: 14px; font-weight: 600; margin-bottom: 6px; }
|
||
.home-mock .card p { font-size: 13px; color: var(--ds-text-secondary); line-height: 1.55; margin-bottom: 12px; }
|
||
.home-mock .card-list { list-style: none; padding: 0; margin: 0; }
|
||
.home-mock .card-list li {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: 10px;
|
||
padding: 8px 0;
|
||
font-size: 13px;
|
||
border-bottom: 1px solid var(--ds-border-light);
|
||
}
|
||
.home-mock .card-list li:last-child { border-bottom: none; }
|
||
.home-mock .card-list .num {
|
||
width: 22px; height: 22px;
|
||
border-radius: 50%;
|
||
background: var(--ds-primary-light);
|
||
color: var(--ds-primary);
|
||
flex-shrink: 0;
|
||
display: flex; align-items: center; justify-content: center;
|
||
font-weight: 700; font-size: 11px;
|
||
}
|
||
.home-mock .card-list .step-text { flex: 1; line-height: 1.55; }
|
||
.home-mock .card-list .step-text strong { display: block; margin-bottom: 2px; }
|
||
.home-mock .card-list .step-text code {
|
||
font-family: var(--ds-font-mono);
|
||
font-size: 11.5px;
|
||
background: var(--ds-border-light);
|
||
padding: 1px 5px;
|
||
border-radius: 3px;
|
||
color: var(--ds-text-primary);
|
||
}
|
||
|
||
.home-mock .explore-list { list-style: none; padding: 0; margin: 0; }
|
||
.home-mock .explore-list li + li { margin-top: 8px; }
|
||
.home-mock .explore-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
padding: 12px 14px;
|
||
background: var(--ds-border-light);
|
||
border: 1px solid transparent;
|
||
border-radius: 8px;
|
||
text-decoration: none;
|
||
color: var(--ds-text-primary);
|
||
transition: border-color 0.15s ease, background 0.15s ease;
|
||
}
|
||
.home-mock .explore-item:hover {
|
||
border-color: var(--ds-primary);
|
||
background: var(--ds-primary-light);
|
||
}
|
||
.home-mock .explore-item .ico {
|
||
font-size: 22px;
|
||
flex-shrink: 0;
|
||
width: 28px;
|
||
text-align: center;
|
||
}
|
||
.home-mock .explore-item .explore-text {
|
||
flex: 1;
|
||
font-size: 13px;
|
||
color: var(--ds-text-secondary);
|
||
line-height: 1.45;
|
||
}
|
||
.home-mock .explore-item .explore-text strong {
|
||
display: block;
|
||
color: var(--ds-text-primary);
|
||
font-size: 13.5px;
|
||
font-weight: 600;
|
||
margin-bottom: 2px;
|
||
}
|
||
.home-mock .explore-item .arrow {
|
||
color: var(--ds-primary);
|
||
font-size: 16px;
|
||
flex-shrink: 0;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.home-mock .advanced-pointer {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 14px;
|
||
margin-top: 10px;
|
||
padding: 16px 22px;
|
||
background: white;
|
||
border: 1px solid var(--ds-border);
|
||
border-left: 4px solid var(--ds-primary);
|
||
border-radius: 12px;
|
||
text-decoration: none;
|
||
color: inherit;
|
||
transition: background 0.15s ease, border-color 0.15s ease;
|
||
}
|
||
.home-mock .advanced-pointer:hover {
|
||
background: var(--ds-primary-light);
|
||
border-color: var(--ds-primary);
|
||
}
|
||
.home-mock .advanced-pointer .ico {
|
||
font-size: 22px;
|
||
flex-shrink: 0;
|
||
line-height: 1;
|
||
}
|
||
.home-mock .advanced-pointer-text {
|
||
flex: 1;
|
||
font-size: 13px;
|
||
color: var(--ds-text-secondary);
|
||
line-height: 1.55;
|
||
}
|
||
.home-mock .advanced-pointer-text strong {
|
||
display: block;
|
||
color: var(--ds-text-primary);
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
margin-bottom: 2px;
|
||
}
|
||
.home-mock .advanced-pointer .arrow {
|
||
color: var(--ds-primary);
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.home-mock .setup-cta-lead {
|
||
font-size: 13px;
|
||
color: rgba(255, 255, 255, 0.82);
|
||
margin-bottom: 14px;
|
||
line-height: 1.55;
|
||
}
|
||
.home-mock .setup-cta-lead code {
|
||
background: rgba(255, 255, 255, 0.12);
|
||
padding: 1px 6px;
|
||
border-radius: 4px;
|
||
font-family: var(--ds-font-mono);
|
||
font-size: 11.5px;
|
||
}
|
||
.home-mock .setup-cta-row {
|
||
display: flex;
|
||
align-items: center;
|
||
flex-wrap: wrap;
|
||
gap: 14px;
|
||
}
|
||
.home-mock .btn-setup-primary {
|
||
font-family: inherit;
|
||
font-size: 13px;
|
||
font-weight: 600;
|
||
color: var(--ds-primary);
|
||
background: #FFFFFF;
|
||
border: none;
|
||
border-radius: 6px;
|
||
padding: 10px 22px;
|
||
cursor: pointer;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
white-space: nowrap;
|
||
transition: background 0.15s ease;
|
||
}
|
||
.home-mock .btn-setup-primary:hover { background: var(--primary-light); }
|
||
.home-mock .btn-setup-primary.copied { background: #047857; color: #fff; }
|
||
.home-mock .btn-setup-primary[disabled] { opacity: 0.7; cursor: wait; }
|
||
.home-mock .setup-cta-meta {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 14px;
|
||
flex-wrap: wrap;
|
||
}
|
||
.home-mock .setup-cta-hint {
|
||
font-size: 12px;
|
||
color: rgba(255, 255, 255, 0.65);
|
||
}
|
||
.home-mock details.manual-fallback {
|
||
margin-top: 14px;
|
||
border-top: 1px solid rgba(255, 255, 255, 0.10);
|
||
padding-top: 12px;
|
||
}
|
||
.home-mock details.manual-fallback > summary {
|
||
cursor: pointer;
|
||
list-style: none;
|
||
font-size: 12.5px;
|
||
color: rgba(255, 255, 255, 0.78);
|
||
padding: 4px 0;
|
||
user-select: none;
|
||
}
|
||
.home-mock details.manual-fallback > summary::-webkit-details-marker { display: none; }
|
||
.home-mock details.manual-fallback > summary::before {
|
||
content: "▸ ";
|
||
display: inline-block;
|
||
transition: transform 0.15s ease;
|
||
margin-right: 4px;
|
||
}
|
||
.home-mock details.manual-fallback[open] > summary::before { content: "▾ "; }
|
||
.home-mock .manual-preview-wrap { margin-top: 10px; }
|
||
.home-mock .manual-preview-wrap .setup-preview-pre {
|
||
background: #0F172A;
|
||
color: #E2E8F0;
|
||
border: 1px solid rgba(255, 255, 255, 0.10);
|
||
border-radius: 8px;
|
||
padding: 14px 16px;
|
||
font-family: var(--ds-font-mono);
|
||
font-size: 12.5px;
|
||
line-height: 1.55;
|
||
overflow-x: auto;
|
||
white-space: pre-wrap;
|
||
word-break: break-word;
|
||
margin: 0;
|
||
}
|
||
/* Override the global `code { background: var(--bg); }` rule so the inner
|
||
<code> renders white-on-navy instead of dark-on-pale (unreadable). */
|
||
.home-mock .manual-preview-wrap .setup-preview-pre code,
|
||
.home-mock .manual-preview-wrap .setup-preview-code {
|
||
background: transparent;
|
||
color: #E2E8F0;
|
||
padding: 0;
|
||
border-radius: 0;
|
||
font-family: inherit;
|
||
font-size: inherit;
|
||
}
|
||
.home-mock .manual-preview-wrap .placeholder-token {
|
||
background: rgba(251, 191, 36, 0.20);
|
||
color: #FBBF24;
|
||
padding: 0 4px;
|
||
border-radius: 3px;
|
||
font-style: italic;
|
||
}
|
||
.home-mock .manual-preview-wrap .token-revealed {
|
||
background: rgba(16, 185, 129, 0.18);
|
||
color: #6EE7B7;
|
||
padding: 0 4px;
|
||
border-radius: 3px;
|
||
font-style: normal;
|
||
word-break: break-all;
|
||
}
|
||
|
||
/* In-hero (dark) variant: shown for not-onboarded users underneath the
|
||
inline install steps inside the blue hero. */
|
||
.home-mock .self-mark {
|
||
margin-top: 16px;
|
||
font-size: 13px;
|
||
color: rgba(255, 255, 255, 0.85);
|
||
}
|
||
.home-mock .self-mark button {
|
||
background: rgba(255, 255, 255, 0.15);
|
||
color: white;
|
||
border: 1px solid rgba(255, 255, 255, 0.30);
|
||
padding: 6px 12px;
|
||
border-radius: 6px;
|
||
font-size: 12px;
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
margin-left: 6px;
|
||
}
|
||
.home-mock .self-mark button:hover { background: rgba(255, 255, 255, 0.25); }
|
||
.home-mock .self-mark button:disabled { opacity: 0.5; cursor: default; }
|
||
.home-mock .self-mark .status { margin-left: 10px; font-style: italic; }
|
||
|
||
/* Onboarded "steps complete" badge — shown inside the blue hero in
|
||
place of the hidden Step 1 + Step 2 install-blocks. */
|
||
.home-mock .install-done {
|
||
margin-top: 4px;
|
||
margin-bottom: 4px;
|
||
padding: 12px 16px;
|
||
background: rgba(255, 255, 255, 0.10);
|
||
border: 1px solid rgba(255, 255, 255, 0.18);
|
||
border-radius: 10px;
|
||
color: rgba(255, 255, 255, 0.92);
|
||
font-size: 13px;
|
||
line-height: 1.55;
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: 10px;
|
||
}
|
||
.home-mock .install-done .check {
|
||
font-size: 16px;
|
||
line-height: 1.3;
|
||
flex-shrink: 0;
|
||
}
|
||
.home-mock .install-done strong { font-weight: 600; color: white; }
|
||
|
||
/* News perex (admin-edited) — bottom of /home.
|
||
Keeps the styling within the admin-defined class vocabulary
|
||
(callout, video-embed) so authors get a predictable surface.
|
||
Full-width grid classes are intentionally NOT included here —
|
||
they live on /news where the layout has more room. */
|
||
.home-mock .home-news {
|
||
margin-top: 28px;
|
||
background: white;
|
||
border: 1px solid var(--ds-border);
|
||
border-radius: 12px;
|
||
overflow: hidden; /* keep the green strip's corners rounded */
|
||
}
|
||
.home-mock .home-news-head {
|
||
display: flex;
|
||
align-items: baseline;
|
||
justify-content: space-between;
|
||
gap: 16px;
|
||
padding: 12px 22px;
|
||
background: #D1FAE5; /* same green as .callout-success */
|
||
border-bottom: 1px solid #A7F3D0;
|
||
}
|
||
.home-mock .home-news-head h2 {
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
color: #047857; /* darker green for contrast on the band */
|
||
margin: 0;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.6px;
|
||
}
|
||
.home-mock .home-news-more {
|
||
font-size: 13px;
|
||
font-weight: 500;
|
||
color: #047857;
|
||
text-decoration: none;
|
||
}
|
||
.home-mock .home-news-more:hover { text-decoration: underline; }
|
||
.home-mock .home-news-body {
|
||
font-size: 14px;
|
||
line-height: 1.55;
|
||
color: var(--ds-text-primary);
|
||
padding: 18px 22px;
|
||
}
|
||
.home-mock .home-news-body p { margin: 0 0 8px; }
|
||
.home-mock .home-news-body p:last-child { margin-bottom: 0; }
|
||
|
||
/* Callout, video-embed, news-hero, news-grid-*, news-cta — shared news
|
||
content vocabulary now lives in app/web/static/style-custom.css under
|
||
"News content vocabulary (shared)". Editing those rules in one place
|
||
updates /home perex, /news, and /admin/news preview together. */
|
||
|
||
.home-mock .install-done code {
|
||
background: rgba(15, 23, 42, 0.55);
|
||
border: 1px solid rgba(255, 255, 255, 0.18);
|
||
padding: 1px 6px;
|
||
border-radius: 4px;
|
||
font-family: var(--ds-font-mono);
|
||
font-size: 12px;
|
||
color: #FBBF24;
|
||
}
|
||
|
||
/* ─────────────────────────────────────────────────────────────────
|
||
Onboarding polish — friction fixes from internal usability testing.
|
||
Each block is tagged with its priority (P0/P1/P2) — P0 resolves the
|
||
highest-frequency confusion, P2 is nice-to-have polish.
|
||
───────────────────────────────────────────────────────────────── */
|
||
|
||
/* P0-1 — "Don't have a terminal open?" disclosure inside Step 1's
|
||
blue install-block. Yellow accent matches Claude footer mode dots
|
||
so it reads as a hint, not an error. */
|
||
.home-mock details.terminal-howto {
|
||
margin-top: 10px;
|
||
padding: 8px 12px;
|
||
background: rgba(251, 191, 36, 0.10);
|
||
border: 1px solid rgba(251, 191, 36, 0.30);
|
||
border-radius: 8px;
|
||
}
|
||
.home-mock details.terminal-howto > summary {
|
||
cursor: pointer;
|
||
list-style: none;
|
||
font-size: 12.5px;
|
||
font-weight: 600;
|
||
color: #FBBF24;
|
||
user-select: none;
|
||
}
|
||
.home-mock details.terminal-howto > summary::-webkit-details-marker { display: none; }
|
||
.home-mock details.terminal-howto > summary::before { content: "▸ "; margin-right: 4px; }
|
||
.home-mock details.terminal-howto[open] > summary::before { content: "▾ "; }
|
||
.home-mock .terminal-howto-body {
|
||
margin-top: 10px;
|
||
font-size: 12.5px;
|
||
color: rgba(255, 255, 255, 0.92);
|
||
line-height: 1.6;
|
||
}
|
||
.home-mock .terminal-howto-body p { margin: 0 0 6px; }
|
||
.home-mock .terminal-howto-body kbd {
|
||
background: rgba(255, 255, 255, 0.15);
|
||
border: 1px solid rgba(255, 255, 255, 0.30);
|
||
border-bottom-width: 2px;
|
||
border-radius: 4px;
|
||
padding: 1px 6px;
|
||
font-size: 11px;
|
||
font-family: var(--ds-font-mono);
|
||
color: white;
|
||
}
|
||
|
||
/* P1-6 — Auto-detect badge replaces the "Mark me as onboarded"
|
||
button as the primary affordance. Pulse dot signals it's actively
|
||
listening; the manual self-mark button stays as a fallback. */
|
||
.home-mock .auto-detect-badge {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
margin-top: 16px;
|
||
background: rgba(255, 255, 255, 0.10);
|
||
border: 1px solid rgba(255, 255, 255, 0.20);
|
||
color: rgba(255, 255, 255, 0.92);
|
||
padding: 8px 14px;
|
||
border-radius: 8px;
|
||
font-size: 12.5px;
|
||
}
|
||
.home-mock .auto-detect-badge .pulse {
|
||
width: 8px; height: 8px;
|
||
border-radius: 50%;
|
||
background: #FBBF24;
|
||
box-shadow: 0 0 0 0 rgba(251, 191, 36, 0.7);
|
||
animation: agnes-pulse 1.6s infinite;
|
||
}
|
||
.home-mock .auto-detect-badge code {
|
||
background: rgba(0, 0, 0, 0.18);
|
||
padding: 1px 5px;
|
||
border-radius: 3px;
|
||
font-family: var(--ds-font-mono);
|
||
font-size: 11.5px;
|
||
}
|
||
@keyframes agnes-pulse {
|
||
0% { box-shadow: 0 0 0 0 rgba(251, 191, 36, 0.65); }
|
||
70% { box-shadow: 0 0 0 8px rgba(251, 191, 36, 0); }
|
||
100% { box-shadow: 0 0 0 0 rgba(251, 191, 36, 0); }
|
||
}
|
||
|
||
/* P0-2 — Post-CTA modal with "where to paste" 3-step guide. Modal
|
||
markup lives at body level; this styles the backdrop + card. The
|
||
.home-mock prefix is intentionally NOT used so the modal is
|
||
render-isolated from the home page's CSS variables. */
|
||
.cta-modal-backdrop {
|
||
position: fixed;
|
||
inset: 0;
|
||
background: rgba(15, 23, 42, 0.55);
|
||
-webkit-backdrop-filter: blur(2px);
|
||
backdrop-filter: blur(2px);
|
||
display: none;
|
||
align-items: center;
|
||
justify-content: center;
|
||
z-index: 1000;
|
||
padding: 20px;
|
||
}
|
||
.cta-modal-backdrop.is-open { display: flex; }
|
||
.cta-modal {
|
||
background: white;
|
||
border-radius: 14px;
|
||
padding: 28px 32px;
|
||
max-width: 520px;
|
||
width: 100%;
|
||
box-shadow: 0 24px 80px rgba(0, 0, 0, 0.35);
|
||
color: #111827;
|
||
font-family: inherit;
|
||
}
|
||
.cta-modal h2 {
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
margin: 0 0 6px;
|
||
color: #047857;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
.cta-modal .lead {
|
||
font-size: 13px;
|
||
color: #6B7280;
|
||
margin: 0 0 18px;
|
||
line-height: 1.55;
|
||
}
|
||
.cta-modal ol {
|
||
list-style: none;
|
||
padding: 0;
|
||
margin: 0 0 18px;
|
||
counter-reset: step;
|
||
}
|
||
.cta-modal ol li {
|
||
counter-increment: step;
|
||
display: flex;
|
||
gap: 14px;
|
||
align-items: flex-start;
|
||
padding: 12px;
|
||
background: #F9FAFB;
|
||
border-radius: 8px;
|
||
margin-bottom: 10px;
|
||
font-size: 13.5px;
|
||
line-height: 1.5;
|
||
}
|
||
.cta-modal ol li::before {
|
||
content: counter(step);
|
||
width: 26px; height: 26px;
|
||
border-radius: 50%;
|
||
background: var(--primary);
|
||
color: white;
|
||
font-weight: 700;
|
||
font-size: 12px;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
flex-shrink: 0;
|
||
}
|
||
.cta-modal ol li code,
|
||
.cta-modal ol li kbd {
|
||
background: #fff;
|
||
border: 1px solid #E5E7EB;
|
||
padding: 1px 6px;
|
||
border-radius: 4px;
|
||
font-family: var(--ds-font-mono, ui-monospace, monospace);
|
||
font-size: 12px;
|
||
}
|
||
.cta-modal ol li kbd { border-bottom-width: 2px; }
|
||
.cta-modal ol li strong { display: block; margin-bottom: 4px; color: #111827; }
|
||
.cta-modal-foot {
|
||
display: flex; justify-content: space-between; align-items: center;
|
||
gap: 12px;
|
||
padding-top: 12px;
|
||
border-top: 1px solid #F3F4F6;
|
||
}
|
||
.cta-modal-foot .meta { font-size: 12px; color: #6B7280; }
|
||
.cta-modal-foot button {
|
||
background: var(--primary);
|
||
color: white;
|
||
border: none;
|
||
border-radius: 6px;
|
||
padding: 8px 18px;
|
||
font-size: 13px;
|
||
font-weight: 600;
|
||
cursor: pointer;
|
||
font-family: inherit;
|
||
}
|
||
.cta-modal-foot button:hover { background: var(--ds-primary-dark); }
|
||
|
||
/* ─────────────────────────────────────────────────────────────────
|
||
Reskin overlay — value-first intro hero, pillars, progress chip,
|
||
first-session story, surfaces chrome, and browse-card upgrades.
|
||
Layered ON TOP of the existing wizard markup so the install-hero,
|
||
install-block, .home-usage, and .what-is structural classes (which
|
||
the test suite pins) keep their identities; this section adds the
|
||
new visual language around them.
|
||
───────────────────────────────────────────────────────────────── */
|
||
|
||
/* Top intro hero — dark navy card matching the design spec. Green
|
||
radial glow in the corner, white H1, rgba-white lede, green
|
||
eyebrow, green pill primary CTA on navy. Pillars row sits inside
|
||
the same card separated by a hairline border. */
|
||
.home-mock .home-hero-intro {
|
||
/* Hero gradient follows the active theme via `--ds-hero-*`. */
|
||
background: linear-gradient(135deg, var(--ds-hero-bg) 0%, var(--ds-hero-bg-deep) 100%);
|
||
color: var(--ds-hero-ink);
|
||
border-radius: 20px;
|
||
padding: 44px 44px 36px;
|
||
margin-bottom: 22px;
|
||
box-shadow: var(--ds-shadow-md);
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
.home-mock .home-hero-intro::after {
|
||
content: "";
|
||
position: absolute;
|
||
top: -80px;
|
||
right: -80px;
|
||
width: 280px;
|
||
height: 280px;
|
||
background: radial-gradient(circle, rgba(84,211,160,0.25) 0%, transparent 70%);
|
||
pointer-events: none;
|
||
}
|
||
.home-mock .home-hero-intro .eyebrow {
|
||
font-size: 12px;
|
||
font-weight: 700;
|
||
text-transform: uppercase;
|
||
letter-spacing: 1.5px;
|
||
/* Eyebrow accent follows the theme: mint-green on navy
|
||
(default) or translucent white on blue. `--ds-hero-eyebrow`
|
||
is set per-theme in design-tokens.css. */
|
||
color: var(--ds-hero-eyebrow);
|
||
margin-bottom: 14px;
|
||
position: relative;
|
||
}
|
||
.home-mock .home-hero-intro h1 {
|
||
font-size: 44px;
|
||
font-weight: 800;
|
||
letter-spacing: -1px;
|
||
margin: 0 0 16px;
|
||
color: #ffffff;
|
||
line-height: 1.1;
|
||
position: relative;
|
||
}
|
||
.home-mock .home-hero-intro h1 .accent { color: var(--ds-brand-accent); }
|
||
.home-mock .home-hero-intro .lede {
|
||
font-size: 18px;
|
||
line-height: 1.6;
|
||
color: rgba(255, 255, 255, 0.8);
|
||
margin: 0 0 28px;
|
||
max-width: 720px;
|
||
position: relative;
|
||
}
|
||
.home-mock .home-hero-intro .lede strong { color: #ffffff; font-weight: 600; }
|
||
.home-mock .home-hero-cta {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 12px;
|
||
margin-bottom: 28px;
|
||
position: relative;
|
||
}
|
||
.home-mock .btn-intro {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
padding: 12px 22px;
|
||
border-radius: 10px;
|
||
font-size: 14.5px;
|
||
font-weight: 600;
|
||
text-decoration: none;
|
||
transition: transform 0.12s ease, background 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
|
||
border: 1px solid transparent;
|
||
}
|
||
.home-mock .btn-intro-primary {
|
||
/* Primary CTA pill on the intro hero. Mint-green on navy by
|
||
default; white on blue under the "blue" theme.
|
||
`--ds-hero-cta-*` flips per theme in design-tokens.css. */
|
||
background: var(--ds-hero-cta-bg);
|
||
color: var(--ds-hero-cta-fg);
|
||
border-color: var(--ds-hero-cta-bg);
|
||
}
|
||
.home-mock .btn-intro-primary:hover {
|
||
background: var(--ds-hero-cta-bg-hover);
|
||
border-color: var(--ds-hero-cta-bg-hover);
|
||
transform: translateY(-1px);
|
||
box-shadow: 0 4px 12px var(--ds-hero-shadow);
|
||
color: var(--ds-hero-cta-fg);
|
||
}
|
||
.home-mock .btn-intro-secondary {
|
||
background: rgba(255, 255, 255, 0.10);
|
||
color: #ffffff;
|
||
border-color: rgba(255, 255, 255, 0.20);
|
||
}
|
||
.home-mock .btn-intro-secondary:hover {
|
||
background: rgba(255, 255, 255, 0.18);
|
||
border-color: rgba(255, 255, 255, 0.32);
|
||
transform: translateY(-1px);
|
||
}
|
||
|
||
/* Pillars row — 4 columns inside the dark hero, separated from the
|
||
CTAs by a hairline top border. Each pillar header carries a green
|
||
square dot. */
|
||
.home-mock .home-hero-pillars {
|
||
display: grid;
|
||
grid-template-columns: repeat(4, 1fr);
|
||
gap: 18px;
|
||
padding-top: 26px;
|
||
border-top: 1px solid rgba(255, 255, 255, 0.12);
|
||
position: relative;
|
||
}
|
||
.home-mock .pillar {
|
||
color: rgba(255, 255, 255, 0.82);
|
||
font-size: 13.5px;
|
||
background: transparent;
|
||
border: none;
|
||
padding: 0;
|
||
border-radius: 0;
|
||
}
|
||
.home-mock .pillar-h {
|
||
font-size: 14.5px;
|
||
font-weight: 600;
|
||
color: #ffffff;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
margin-bottom: 6px;
|
||
}
|
||
.home-mock .pillar-h::before {
|
||
content: "";
|
||
display: inline-block;
|
||
width: 8px;
|
||
height: 8px;
|
||
border-radius: 2px;
|
||
background: var(--ds-brand-accent);
|
||
flex-shrink: 0;
|
||
}
|
||
.home-mock .pillar p {
|
||
font-size: 13px;
|
||
color: rgba(255, 255, 255, 0.78);
|
||
line-height: 1.55;
|
||
margin: 0;
|
||
}
|
||
@media (max-width: 880px) {
|
||
.home-mock .home-hero-intro { padding: 32px 24px 28px; }
|
||
.home-mock .home-hero-intro h1 { font-size: 26px; }
|
||
.home-mock .home-hero-pillars { grid-template-columns: repeat(2, 1fr); }
|
||
}
|
||
@media (max-width: 480px) {
|
||
.home-mock .home-hero-pillars { grid-template-columns: 1fr; }
|
||
}
|
||
|
||
/* Setup-wizard chrome — progress chip + step number badges, layered
|
||
ON the existing .install-hero / .install-block markup. */
|
||
.home-mock .setup-meta {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 8px;
|
||
margin: 10px 0 14px;
|
||
}
|
||
.home-mock .setup-chip {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
padding: 4px 10px;
|
||
border-radius: 999px;
|
||
background: var(--ds-brand-accent);
|
||
color: var(--ds-hero-bg);
|
||
font-size: 11.5px;
|
||
font-weight: 600;
|
||
letter-spacing: 0.2px;
|
||
}
|
||
.home-mock .setup-chip-quiet {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
padding: 4px 10px;
|
||
border-radius: 999px;
|
||
background: rgba(255, 255, 255, 0.10);
|
||
color: rgba(255, 255, 255, 0.82);
|
||
font-size: 11.5px;
|
||
font-weight: 500;
|
||
border: 1px solid rgba(255, 255, 255, 0.14);
|
||
}
|
||
.home-mock .progress-bar {
|
||
height: 4px;
|
||
background: rgba(255, 255, 255, 0.10);
|
||
border-radius: 999px;
|
||
overflow: hidden;
|
||
margin-bottom: 22px;
|
||
}
|
||
.home-mock .progress-fill {
|
||
height: 100%;
|
||
background: linear-gradient(90deg, var(--ds-brand-accent), var(--ds-primary));
|
||
border-radius: 999px;
|
||
transition: width 0.3s ease;
|
||
}
|
||
|
||
/* Step number badges inside install-block. The existing .install-block
|
||
stays as the container (test-pinned); .step-num is positioned as a
|
||
left-side circle so the existing .label/.os-tabs/.install-cmd flow
|
||
continues to the right. */
|
||
.home-mock .install-block {
|
||
position: relative;
|
||
padding-left: 56px; /* room for step badge */
|
||
}
|
||
.home-mock .install-block .step-num {
|
||
position: absolute;
|
||
top: 18px;
|
||
left: 18px;
|
||
width: 28px;
|
||
height: 28px;
|
||
border-radius: 50%;
|
||
background: var(--ds-brand-accent);
|
||
color: var(--ds-hero-bg);
|
||
font-weight: 700;
|
||
font-size: 13px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
/* First-session story — bare section on the page bg (no wrapping
|
||
card). Matches the design spec where the eyebrow + h2 + lede sit
|
||
inline on the page and individual session-step rows have their
|
||
own visual rhythm via the 48px green circle on the left. */
|
||
.home-mock .first-session {
|
||
background: transparent;
|
||
border: none;
|
||
border-radius: 0;
|
||
padding: 0;
|
||
margin: 36px 0;
|
||
}
|
||
.home-mock .first-session > .eyebrow {
|
||
font-size: 12px;
|
||
font-weight: 700;
|
||
text-transform: uppercase;
|
||
letter-spacing: 1.5px;
|
||
color: var(--ds-primary-dark);
|
||
margin-bottom: 14px;
|
||
}
|
||
/* First-session beat sizing — verbatim from the design spec. h2 28px,
|
||
lede 18px, session-step a 48px / 1fr grid, session-num a 40px
|
||
GREEN circle with WHITE text + soft green shadow, content h3 18px
|
||
+ p 15px. */
|
||
.home-mock .first-session > h2 {
|
||
font-size: 28px;
|
||
font-weight: 700;
|
||
letter-spacing: -0.5px;
|
||
line-height: 1.2;
|
||
margin: 0 0 8px;
|
||
color: var(--ds-text-primary);
|
||
}
|
||
.home-mock .first-session > .lede {
|
||
font-size: 18px;
|
||
color: var(--ds-text-secondary);
|
||
line-height: 1.55;
|
||
margin: 0 0 24px;
|
||
max-width: 720px;
|
||
}
|
||
/* Session-intro card — white surface w/ mint icon block, frames the
|
||
"five beats" tagline above the walkthrough. Per design spec. */
|
||
.home-mock .session-intro {
|
||
background: var(--ds-surface);
|
||
border: 1px solid var(--ds-border);
|
||
border-radius: 14px;
|
||
padding: 20px 24px;
|
||
margin: 4px 0 28px;
|
||
display: flex;
|
||
gap: 16px;
|
||
align-items: center;
|
||
font-size: 14.5px;
|
||
color: var(--ds-text-secondary);
|
||
line-height: 1.55;
|
||
box-shadow: var(--ds-shadow-sm);
|
||
}
|
||
.home-mock .session-intro-icon {
|
||
width: 44px;
|
||
height: 44px;
|
||
border-radius: 12px;
|
||
background: var(--ds-primary-light);
|
||
color: var(--ds-primary-dark);
|
||
display: grid;
|
||
place-items: center;
|
||
font-size: 22px;
|
||
flex-shrink: 0;
|
||
}
|
||
.home-mock .session-intro strong { color: var(--ds-text-primary); }
|
||
|
||
.home-mock .session-walk {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 36px;
|
||
margin-top: 8px;
|
||
}
|
||
.home-mock .session-step {
|
||
display: grid;
|
||
grid-template-columns: 48px 1fr;
|
||
gap: 22px;
|
||
padding: 0;
|
||
border-top: none;
|
||
}
|
||
.home-mock .session-step:first-of-type { padding-top: 0; }
|
||
.home-mock .session-num {
|
||
width: 40px;
|
||
height: 40px;
|
||
border-radius: 50%;
|
||
background: var(--ds-primary);
|
||
color: #ffffff;
|
||
display: grid;
|
||
place-items: center;
|
||
font-weight: 700;
|
||
font-size: 16px;
|
||
box-shadow: 0 2px 6px rgba(46, 168, 119, 0.25);
|
||
flex-shrink: 0;
|
||
}
|
||
.home-mock .session-content { min-width: 0; }
|
||
.home-mock .session-content h3 {
|
||
margin: 0 0 6px;
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
color: var(--ds-text-primary);
|
||
}
|
||
.home-mock .session-content > p {
|
||
font-size: 15px;
|
||
color: var(--ds-text-secondary);
|
||
line-height: 1.55;
|
||
margin: 0 0 10px;
|
||
}
|
||
.home-mock .session-content > p:last-child { margin-bottom: 0; }
|
||
.home-mock .session-content .annotation {
|
||
font-size: 13.5px;
|
||
color: var(--ds-text-muted);
|
||
margin: 10px 0 0;
|
||
line-height: 1.55;
|
||
font-weight: 400;
|
||
text-transform: none;
|
||
letter-spacing: 0;
|
||
}
|
||
.home-mock .session-content .annotation strong { color: var(--ds-text-primary); }
|
||
.home-mock .session-content code {
|
||
background: var(--ds-bg);
|
||
border: 1px solid var(--ds-border);
|
||
padding: 1px 6px;
|
||
border-radius: 4px;
|
||
font-family: var(--ds-font-mono);
|
||
font-size: 12.5px;
|
||
}
|
||
.home-mock .session-tldr {
|
||
background: var(--ds-primary-light);
|
||
border-left: 4px solid var(--ds-primary-dark);
|
||
padding: 20px 24px;
|
||
border-radius: 0 8px 8px 0;
|
||
margin-top: 28px;
|
||
font-size: 15px;
|
||
color: var(--ds-text-primary);
|
||
line-height: 1.6;
|
||
}
|
||
.home-mock .session-tldr strong { color: var(--ds-primary-dark); }
|
||
|
||
/* Terminal frame — exact design colors. Box-shadow uses the global
|
||
--ds-shadow-md so the frame lifts off the white first-session
|
||
surface; macOS traffic-light reds/yellows/greens match the real
|
||
colors (#ff5f57 / #febc2e / #28c840) so the framing reads as a
|
||
terminal, not a generic dark card. */
|
||
.home-mock .terminal-frame {
|
||
background: var(--ds-code-bg);
|
||
border: 1px solid #1f2a4a;
|
||
border-radius: 10px;
|
||
overflow: hidden;
|
||
box-shadow: var(--ds-shadow-md);
|
||
margin: 10px 0 4px;
|
||
}
|
||
.home-mock .terminal-bar {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
background: #182241;
|
||
padding: 8px 14px;
|
||
border-bottom: 1px solid #1f2a4a;
|
||
}
|
||
.home-mock .terminal-bar .traffic {
|
||
width: 11px;
|
||
height: 11px;
|
||
border-radius: 50%;
|
||
background: #4a5168;
|
||
display: inline-block;
|
||
}
|
||
.home-mock .terminal-bar .traffic.r { background: #ff5f57; }
|
||
.home-mock .terminal-bar .traffic.y { background: #febc2e; }
|
||
.home-mock .terminal-bar .traffic.g { background: #28c840; }
|
||
.home-mock .terminal-bar .terminal-title {
|
||
margin-left: 8px;
|
||
font-family: var(--ds-font-mono);
|
||
font-size: 12px;
|
||
color: rgba(255, 255, 255, 0.55);
|
||
}
|
||
.home-mock .terminal-body {
|
||
font-family: var(--ds-font-mono);
|
||
font-size: 13px;
|
||
line-height: 1.65;
|
||
color: var(--ds-code-ink);
|
||
padding: 16px 20px;
|
||
white-space: pre-wrap;
|
||
word-break: normal;
|
||
}
|
||
.home-mock .terminal-body .prompt {
|
||
color: var(--ds-primary);
|
||
margin-right: 8px;
|
||
user-select: none;
|
||
}
|
||
.home-mock .terminal-body .you { color: #ffd866; }
|
||
.home-mock .terminal-body .cmd { color: #ffd866; }
|
||
.home-mock .terminal-body .ai-name { color: var(--ds-primary); font-weight: 600; }
|
||
.home-mock .terminal-body .path { color: #8b9fe0; }
|
||
.home-mock .terminal-body .dim { color: rgba(215, 228, 255, 0.55); }
|
||
.home-mock .terminal-body strong { color: #ffffff; font-weight: 600; }
|
||
.home-mock .terminal-body .caret {
|
||
display: inline-block;
|
||
width: 8px;
|
||
height: 14px;
|
||
background: var(--ds-primary);
|
||
vertical-align: text-bottom;
|
||
animation: agnes-blink 1s steps(2, end) infinite;
|
||
}
|
||
@keyframes agnes-blink { 50% { opacity: 0; } }
|
||
|
||
/* ── Surfaces — 4-card layout matching the design spec verbatim. h2
|
||
28px / 700, surface-card padding 28px, .feature variant uses a
|
||
brand-light gradient + 2px brand border, .incomplete variant uses
|
||
a red-tinted gradient + 2px red border. Steps render in a tinted
|
||
panel with surface-dim bg. */
|
||
.home-mock .surfaces { padding: 24px 28px; }
|
||
.home-mock .surfaces > header { margin-bottom: 20px; }
|
||
.home-mock .surfaces > header .eyebrow {
|
||
font-size: 12px;
|
||
font-weight: 700;
|
||
text-transform: uppercase;
|
||
letter-spacing: 1.5px;
|
||
color: var(--ds-primary-dark);
|
||
margin-bottom: 14px;
|
||
}
|
||
.home-mock .surfaces > header h2 {
|
||
font-size: 28px;
|
||
font-weight: 700;
|
||
letter-spacing: -0.5px;
|
||
line-height: 1.2;
|
||
margin: 0 0 8px;
|
||
color: var(--ds-text-primary);
|
||
}
|
||
.home-mock .surfaces > header p {
|
||
font-size: 18px;
|
||
color: var(--ds-text-secondary);
|
||
line-height: 1.55;
|
||
margin: 0 0 12px;
|
||
max-width: 760px;
|
||
}
|
||
.home-mock .surfaces-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(2, 1fr);
|
||
gap: 16px;
|
||
margin-bottom: 14px;
|
||
}
|
||
.home-mock .surface-card {
|
||
background: var(--ds-surface);
|
||
border: 1px solid var(--ds-border);
|
||
border-radius: 14px;
|
||
padding: 28px;
|
||
box-shadow: var(--ds-shadow-sm);
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 10px;
|
||
}
|
||
.home-mock .surface-card.feature {
|
||
border: 2px solid var(--ds-primary);
|
||
background: linear-gradient(180deg, var(--ds-primary-light) 0%, var(--ds-surface) 40%);
|
||
}
|
||
.home-mock .surface-card.incomplete {
|
||
border: 2px solid #e35e5e;
|
||
background: linear-gradient(180deg, #fdecec 0%, var(--ds-surface) 60%);
|
||
}
|
||
.home-mock .surface-icon {
|
||
font-size: 26px;
|
||
line-height: 1;
|
||
margin-bottom: 6px;
|
||
}
|
||
.home-mock .surface-card h3 {
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
margin: 0;
|
||
color: var(--ds-text-primary);
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
flex-wrap: wrap;
|
||
}
|
||
.home-mock .surface-card .badge {
|
||
background: var(--ds-primary);
|
||
color: #ffffff;
|
||
font-size: 10px;
|
||
font-weight: 700;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.5px;
|
||
padding: 3px 8px;
|
||
border-radius: 4px;
|
||
}
|
||
.home-mock .surface-card .badge-warn {
|
||
background: #e35e5e;
|
||
color: #ffffff;
|
||
font-size: 10px;
|
||
font-weight: 700;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.5px;
|
||
padding: 3px 8px;
|
||
border-radius: 4px;
|
||
}
|
||
.home-mock .surface-card .what {
|
||
font-size: 14px;
|
||
color: var(--ds-text-secondary);
|
||
line-height: 1.55;
|
||
margin: 0;
|
||
}
|
||
.home-mock .surface-card .what code {
|
||
background: var(--ds-primary-light);
|
||
color: var(--ds-primary-dark);
|
||
border: 1px solid rgba(46, 168, 119, 0.30);
|
||
padding: 1px 6px;
|
||
border-radius: 4px;
|
||
font-family: var(--ds-font-mono);
|
||
font-size: 12px;
|
||
}
|
||
/* VS Code thumb — anchor wrapping the real screenshot. Clicking opens
|
||
the lightbox. `.thumb-fallback` is only visible when the `<img>` is
|
||
missing/errors (handled by inline onerror → `.thumb-empty`). */
|
||
.home-mock .vscode-thumb {
|
||
display: block;
|
||
position: relative;
|
||
border-radius: 8px;
|
||
overflow: hidden;
|
||
border: 1px solid var(--ds-border);
|
||
background: #0c1224;
|
||
text-decoration: none;
|
||
color: inherit;
|
||
margin: 2px 0 4px;
|
||
cursor: zoom-in;
|
||
transition: transform 0.12s, box-shadow 0.12s, border-color 0.12s;
|
||
}
|
||
.home-mock .vscode-thumb:hover {
|
||
transform: translateY(-1px);
|
||
box-shadow: 0 4px 16px rgba(15, 27, 58, 0.25);
|
||
border-color: var(--ds-brand-accent);
|
||
}
|
||
.home-mock .vscode-thumb img {
|
||
display: block;
|
||
width: 100%;
|
||
height: auto;
|
||
}
|
||
.home-mock .vscode-thumb.thumb-empty {
|
||
aspect-ratio: 16 / 9;
|
||
background: var(--ds-surface-dim);
|
||
cursor: default;
|
||
display: grid;
|
||
place-items: center;
|
||
}
|
||
.home-mock .vscode-thumb.thumb-empty:hover {
|
||
transform: none;
|
||
box-shadow: none;
|
||
border-color: var(--ds-border);
|
||
}
|
||
.home-mock .vscode-thumb .thumb-fallback {
|
||
display: none;
|
||
padding: 18px;
|
||
font-size: 12px;
|
||
color: var(--ds-text-muted);
|
||
text-align: center;
|
||
}
|
||
.home-mock .vscode-thumb.thumb-empty .thumb-fallback { display: block; }
|
||
.home-mock .vscode-thumb .thumb-fallback-row {
|
||
display: flex;
|
||
justify-content: center;
|
||
gap: 14px;
|
||
flex-wrap: wrap;
|
||
font-weight: 600;
|
||
color: #d7e4ff;
|
||
}
|
||
.home-mock .vscode-thumb .dot {
|
||
width: 10px;
|
||
height: 10px;
|
||
border-radius: 2px;
|
||
display: inline-block;
|
||
margin-right: 4px;
|
||
vertical-align: middle;
|
||
}
|
||
.home-mock .vscode-thumb .dot.d-explorer { background: #ffd866; }
|
||
.home-mock .vscode-thumb .dot.d-t1 { background: var(--ds-brand-accent); }
|
||
.home-mock .vscode-thumb .dot.d-t2 { background: #e07b3f; }
|
||
.home-mock .vscode-thumb .dot.d-t3 { background: #8b6ec7; }
|
||
.home-mock .vscode-thumb .thumb-fallback-hint {
|
||
margin-top: 10px;
|
||
font-size: 11.5px;
|
||
color: rgba(215, 228, 255, 0.6);
|
||
}
|
||
.home-mock .vscode-thumb .thumb-fallback-hint code {
|
||
font-size: 11.5px;
|
||
}
|
||
.home-mock .vscode-thumb .thumb-caption {
|
||
position: absolute;
|
||
left: 12px;
|
||
bottom: 12px;
|
||
background: rgba(15, 27, 58, 0.78);
|
||
color: white;
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
letter-spacing: 0.3px;
|
||
padding: 4px 10px;
|
||
border-radius: 999px;
|
||
-webkit-backdrop-filter: blur(4px);
|
||
backdrop-filter: blur(4px);
|
||
}
|
||
|
||
/* Steps inner panel — surface-dim bg matching design + small caps
|
||
eyebrow on the first <strong>. */
|
||
.home-mock .surface-card .steps {
|
||
background: var(--ds-surface-dim);
|
||
border-radius: 8px;
|
||
padding: 12px 16px;
|
||
margin: 4px 0;
|
||
font-size: 13px;
|
||
color: var(--ds-text-primary);
|
||
}
|
||
.home-mock .surface-card .steps .steps-eyebrow {
|
||
font-size: 12px;
|
||
letter-spacing: 1px;
|
||
color: var(--ds-text-muted);
|
||
display: block;
|
||
margin-bottom: 8px;
|
||
font-weight: 700;
|
||
text-transform: uppercase;
|
||
}
|
||
.home-mock .surface-card .steps ol {
|
||
margin: 0;
|
||
padding-left: 18px;
|
||
list-style: decimal;
|
||
}
|
||
.home-mock .surface-card .steps li {
|
||
margin-bottom: 4px;
|
||
line-height: 1.55;
|
||
}
|
||
.home-mock .surface-card .steps li:last-child { margin-bottom: 0; }
|
||
.home-mock .surface-card .steps code {
|
||
background: var(--ds-primary-light);
|
||
color: var(--ds-primary-dark);
|
||
border: 1px solid rgba(46, 168, 119, 0.30);
|
||
padding: 1px 5px;
|
||
border-radius: 4px;
|
||
font-family: var(--ds-font-mono);
|
||
font-size: 12px;
|
||
}
|
||
.home-mock .surface-card .steps kbd {
|
||
background: var(--ds-surface);
|
||
border: 1px solid var(--ds-border);
|
||
border-bottom-width: 2px;
|
||
padding: 1px 6px;
|
||
border-radius: 4px;
|
||
font-size: 12px;
|
||
font-family: var(--ds-font-mono);
|
||
}
|
||
.home-mock .surface-card .steps a {
|
||
color: var(--ds-primary-dark);
|
||
text-decoration: underline;
|
||
}
|
||
|
||
/* `.code-output` extracted to shared
|
||
app/web/static/css/components.css — markup renamed accordingly
|
||
(`<div class="expected-output">` → `<div class="code-output">`). */
|
||
|
||
/* When-to-use footer — top-border + ink-muted body, brand-dark for
|
||
links. Per design spec. */
|
||
.home-mock .surface-card .when-to-use {
|
||
font-size: 13px;
|
||
color: var(--ds-text-muted);
|
||
border-top: 1px solid var(--ds-border);
|
||
padding-top: 12px;
|
||
margin-top: 4px;
|
||
line-height: 1.55;
|
||
}
|
||
.home-mock .surface-card .when-to-use strong { color: var(--ds-text-primary); }
|
||
.home-mock .surface-card .when-to-use a { color: var(--ds-primary-dark); text-decoration: underline; }
|
||
.home-mock .surface-card .when-to-use a:hover { color: var(--ds-primary); }
|
||
|
||
.home-mock .incomplete-callout {
|
||
background: #ffffff;
|
||
border: 1px dashed #e35e5e;
|
||
border-radius: 8px;
|
||
padding: 12px 16px;
|
||
font-size: 13px;
|
||
color: #7a1f1f;
|
||
line-height: 1.55;
|
||
}
|
||
.home-mock .incomplete-callout strong {
|
||
color: #5a0e0e;
|
||
}
|
||
.home-mock .incomplete-callout ul {
|
||
margin: 8px 0 4px 18px;
|
||
padding: 0;
|
||
color: #7a1f1f;
|
||
}
|
||
.home-mock .incomplete-callout li { margin-bottom: 4px; }
|
||
@media (max-width: 880px) {
|
||
.home-mock .surfaces-grid { grid-template-columns: 1fr; }
|
||
}
|
||
|
||
/* RECOMMENDED chip on the VS Code surface tile. Doesn't change the
|
||
.home-usage-item structure — just adds the badge. */
|
||
.home-mock .recommend-pill {
|
||
display: inline-block;
|
||
margin-left: 6px;
|
||
font-size: 10.5px;
|
||
font-weight: 700;
|
||
letter-spacing: 0.5px;
|
||
text-transform: uppercase;
|
||
padding: 2px 8px;
|
||
background: var(--ds-brand-accent);
|
||
color: var(--ds-hero-bg);
|
||
border-radius: 999px;
|
||
vertical-align: middle;
|
||
}
|
||
|
||
/* Surfaces wrapper is now bare (no white card). Padding lives on the
|
||
inner header + grid, not on .home-usage itself. The global
|
||
`header { display: flex; justify-content: space-between; ... }`
|
||
rule absorbed from style.css makes our section header lay out
|
||
the eyebrow/h2/lede side-by-side; force block + no padding/border
|
||
so the title sits on the LEFT under the eyebrow as the design has. */
|
||
.home-mock .home-usage { padding: 0; text-align: left; }
|
||
.home-mock .home-usage > header,
|
||
.home-mock .first-session > header,
|
||
.home-mock .browse-section > header,
|
||
.home-mock section > header {
|
||
display: block;
|
||
padding: 0;
|
||
margin-bottom: 20px;
|
||
border-bottom: none;
|
||
flex-wrap: initial;
|
||
gap: 0;
|
||
justify-content: initial;
|
||
align-items: initial;
|
||
}
|
||
.home-mock .home-usage > header h2,
|
||
.home-mock .home-usage > header p {
|
||
text-align: left;
|
||
width: 100%;
|
||
}
|
||
|
||
/* Browse-section — bare section on the page bg, 5-col grid, cards
|
||
with green-hover lift. Per design spec. */
|
||
.home-mock .browse-section {
|
||
margin: 36px 0;
|
||
}
|
||
.home-mock .browse-section .eyebrow {
|
||
font-size: 12px;
|
||
font-weight: 700;
|
||
text-transform: uppercase;
|
||
letter-spacing: 1.5px;
|
||
color: var(--ds-primary-dark);
|
||
margin-bottom: 14px;
|
||
}
|
||
.home-mock .browse-section h2 {
|
||
font-size: 28px;
|
||
font-weight: 700;
|
||
letter-spacing: -0.5px;
|
||
line-height: 1.2;
|
||
margin: 0 0 8px;
|
||
color: var(--ds-text-primary);
|
||
}
|
||
.home-mock .browse-section .lede {
|
||
font-size: 18px;
|
||
color: var(--ds-text-secondary);
|
||
line-height: 1.55;
|
||
margin: 0 0 20px;
|
||
max-width: 720px;
|
||
}
|
||
.home-mock .browse-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(5, 1fr);
|
||
gap: 14px;
|
||
}
|
||
.home-mock .browse-card {
|
||
background: var(--ds-surface);
|
||
border: 1px solid var(--ds-border);
|
||
border-radius: 12px;
|
||
padding: 22px 20px;
|
||
text-decoration: none;
|
||
color: var(--ds-text-primary);
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 6px;
|
||
transition: transform 0.1s, box-shadow 0.1s, border-color 0.1s;
|
||
box-shadow: none;
|
||
transform: none;
|
||
position: relative;
|
||
}
|
||
.home-mock .browse-card:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: var(--ds-shadow-md);
|
||
border-color: var(--ds-primary);
|
||
}
|
||
.home-mock .browse-card .browse-icon {
|
||
font-size: 28px;
|
||
margin-bottom: 4px;
|
||
line-height: 1;
|
||
}
|
||
.home-mock .browse-card .browse-title {
|
||
font-weight: 600;
|
||
font-size: 15px;
|
||
color: var(--ds-text-primary);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 8px;
|
||
}
|
||
.home-mock .browse-card .browse-title .arrow {
|
||
color: var(--ds-primary-dark);
|
||
font-weight: 600;
|
||
transition: transform 0.15s ease;
|
||
}
|
||
.home-mock .browse-card:hover .browse-title .arrow {
|
||
transform: translateX(2px);
|
||
}
|
||
.home-mock .browse-card .browse-desc {
|
||
font-size: 12.5px;
|
||
color: var(--ds-text-muted);
|
||
line-height: 1.45;
|
||
flex-grow: 1;
|
||
}
|
||
/* "NEW" corner badge — design spec verbatim. */
|
||
.home-mock .browse-card.new::after {
|
||
content: "NEW";
|
||
position: absolute;
|
||
top: 12px;
|
||
right: 12px;
|
||
background: var(--ds-primary);
|
||
color: #ffffff;
|
||
font-size: 10px;
|
||
font-weight: 700;
|
||
padding: 2px 6px;
|
||
border-radius: 3px;
|
||
letter-spacing: 0.5px;
|
||
}
|
||
/* Equal heights — grid stretches items by default; this just keeps
|
||
inner content laid out so titles + descs line up across cards. */
|
||
.home-mock .browse-card { align-self: stretch; }
|
||
@media (max-width: 880px) {
|
||
.home-mock .browse-grid { grid-template-columns: repeat(2, 1fr); }
|
||
}
|
||
@media (max-width: 480px) {
|
||
.home-mock .browse-grid { grid-template-columns: 1fr; }
|
||
}
|
||
.home-mock .home-usage-item {
|
||
padding: 16px;
|
||
transition: border-color 0.15s ease, background 0.15s ease, transform 0.15s ease;
|
||
}
|
||
.home-mock a.home-usage-item:hover { transform: translateY(-1px); }
|
||
|
||
/* Browse-cards (the existing .what-is look-around-grid) — give the
|
||
tiles a bit more breathing room and a green-on-hover highlight that
|
||
matches the new palette. */
|
||
.home-mock .what-is.look-around-grid {
|
||
margin-top: 6px;
|
||
}
|
||
.home-mock .what-is-item {
|
||
padding: 20px;
|
||
}
|
||
|
||
/* Mobile tweaks for the install-hero now that the .install-block has
|
||
a 56px left-padding step badge. */
|
||
@media (max-width: 560px) {
|
||
.home-mock .install-hero { padding: 24px 18px; }
|
||
.home-mock .install-block { padding-left: 18px; }
|
||
.home-mock .install-block .step-num { position: static; margin-bottom: 8px; }
|
||
}
|
||
|
||
/* ─────────────────────────────────────────────────────────────────
|
||
Setup-wizard LIGHT theme — design spec has the install-hero as a
|
||
white surface card with light step rows, NOT a dark navy card.
|
||
This override block flips all the in-hero rules from the original
|
||
dark-navy palette to a light surface that matches the design. The
|
||
code-panel (.install-cmd) and terminal-frame mockups STAY dark
|
||
so they stand out as "what the user will type".
|
||
───────────────────────────────────────────────────────────────── */
|
||
.home-mock .install-hero {
|
||
background: var(--ds-surface);
|
||
color: var(--ds-text-primary);
|
||
border: 1px solid var(--ds-border);
|
||
border-radius: 16px;
|
||
padding: 36px;
|
||
box-shadow: var(--ds-shadow-sm);
|
||
}
|
||
/* Drop the navy gradient + sweep-shadow remnant; keep the slim green
|
||
accent strip across the top so the wizard reads as the brand CTA. */
|
||
.home-mock .install-hero::before { content: none; }
|
||
.home-mock .install-hero-close {
|
||
background: var(--ds-surface);
|
||
border: 1px solid var(--ds-border);
|
||
color: var(--ds-text-muted);
|
||
}
|
||
.home-mock .install-hero-close:hover,
|
||
.home-mock .install-hero-close:focus-visible {
|
||
background: var(--ds-bg);
|
||
color: var(--ds-text-primary);
|
||
}
|
||
.home-mock .install-hero .eyebrow {
|
||
color: var(--ds-primary);
|
||
opacity: 1;
|
||
}
|
||
.home-mock .install-hero h1 {
|
||
color: var(--ds-text-primary);
|
||
font-size: 24px;
|
||
}
|
||
.home-mock .install-hero .lead,
|
||
.home-mock .install-hero .lead-privacy {
|
||
color: var(--ds-text-secondary);
|
||
opacity: 1;
|
||
}
|
||
.home-mock .install-hero .lead strong { color: var(--ds-text-primary); }
|
||
.home-mock .install-hero a {
|
||
color: var(--ds-primary);
|
||
text-decoration: underline;
|
||
text-decoration-color: rgba(46, 168, 119, 0.4);
|
||
}
|
||
.home-mock .install-hero a:hover,
|
||
.home-mock .install-hero a:focus {
|
||
color: var(--ds-primary-dark);
|
||
text-decoration-color: var(--ds-primary-dark);
|
||
}
|
||
/* In-lead `<code>` chips — light variant (design uses brand-light bg). */
|
||
.home-mock .install-hero code,
|
||
.home-mock .install-note code,
|
||
.home-mock .install-note > code {
|
||
background: var(--ds-primary-light);
|
||
color: var(--ds-primary-dark);
|
||
border-color: rgba(46, 168, 119, 0.30);
|
||
}
|
||
/* Setup-meta chips on light bg — primary chip uses brand-light pill,
|
||
secondary chips lose the white-on-dark treatment for ink-muted. */
|
||
.home-mock .setup-chip {
|
||
background: var(--ds-primary-light);
|
||
color: var(--ds-primary-dark);
|
||
}
|
||
.home-mock .setup-chip-quiet {
|
||
background: var(--ds-bg);
|
||
color: var(--ds-text-secondary);
|
||
border-color: var(--ds-border);
|
||
}
|
||
.home-mock .progress-bar {
|
||
height: 6px;
|
||
background: var(--ds-bg);
|
||
}
|
||
.home-mock .progress-fill {
|
||
background: var(--ds-primary);
|
||
}
|
||
|
||
/* Step rows — grid layout with a light circle for the step number,
|
||
borders between rows, no dark card. */
|
||
.home-mock .install-block {
|
||
background: transparent;
|
||
border: none;
|
||
border-top: 1px solid var(--ds-border);
|
||
border-radius: 0;
|
||
padding: 22px 0 22px 64px;
|
||
margin-top: 0 !important;
|
||
}
|
||
.home-mock .install-block:first-of-type {
|
||
border-top: none;
|
||
padding-top: 6px;
|
||
}
|
||
.home-mock .install-block + .install-block { margin-top: 0; }
|
||
.home-mock .install-block .step-num {
|
||
top: 22px;
|
||
left: 8px;
|
||
width: 40px;
|
||
height: 40px;
|
||
background: var(--ds-surface-dim);
|
||
color: var(--ds-text-secondary);
|
||
font-size: 16px;
|
||
font-weight: 700;
|
||
}
|
||
.home-mock .install-block .label {
|
||
color: var(--ds-text-primary);
|
||
font-size: 17px;
|
||
font-weight: 700;
|
||
text-transform: none;
|
||
letter-spacing: 0;
|
||
opacity: 1;
|
||
margin-bottom: 6px;
|
||
line-height: 1.35;
|
||
}
|
||
/* Subhead under each step label — short body type explaining what
|
||
the step accomplishes before the OS tabs / code panel appear. */
|
||
.home-mock .install-block .step-lede {
|
||
color: var(--ds-text-secondary);
|
||
font-size: 14.5px;
|
||
line-height: 1.55;
|
||
margin: 0 0 12px;
|
||
max-width: 720px;
|
||
}
|
||
.home-mock .install-block .step-lede strong { color: var(--ds-text-primary); font-weight: 600; }
|
||
.home-mock .install-block .step-lede code {
|
||
background: var(--ds-primary-light);
|
||
color: var(--ds-primary-dark);
|
||
border: 1px solid rgba(46, 168, 119, 0.30);
|
||
padding: 1px 6px;
|
||
border-radius: 4px;
|
||
font-family: var(--ds-font-mono);
|
||
font-size: 12px;
|
||
}
|
||
/* OS tabs sit on white now — borders + colors flip to ink. */
|
||
.home-mock .os-tabs {
|
||
border-bottom-color: var(--ds-border);
|
||
}
|
||
.home-mock .os-tab {
|
||
color: var(--ds-text-muted);
|
||
}
|
||
.home-mock .os-tab:hover { color: var(--ds-text-primary); }
|
||
.home-mock .os-tab.is-active {
|
||
color: var(--ds-primary-dark);
|
||
border-bottom-color: var(--ds-primary-dark);
|
||
}
|
||
/* Code panel (.install-cmd) STAYS dark — it's the "type this" surface.
|
||
Just adjust spacing + ensure it pops on the white step row. */
|
||
.home-mock .install-cmd {
|
||
background: var(--ds-code-bg);
|
||
border-color: rgba(0, 0, 0, 0.08);
|
||
color: var(--ds-code-yellow);
|
||
}
|
||
/* Install-note prose — ink-soft secondary on white, 14px to match
|
||
design's body type. */
|
||
.home-mock .install-note {
|
||
color: var(--ds-text-secondary);
|
||
font-size: 14px;
|
||
line-height: 1.55;
|
||
margin-top: 10px;
|
||
}
|
||
/* `.callout-rec` and `.callout-hint` extracted to shared
|
||
app/web/static/css/components.css — markup renamed accordingly
|
||
(`<div class="rec">` → `<div class="callout-rec">`). */
|
||
/* Mode-tabs — Auto / YOLO toggle in Step 6 selecting which shell
|
||
function gets installed. Visually mirrors `.os-tabs` but with a
|
||
labeled heading above. */
|
||
.home-mock .install-block .step-mode-heading {
|
||
font-weight: 600;
|
||
margin: 14px 0 6px;
|
||
color: var(--ds-text-primary);
|
||
}
|
||
.home-mock .install-block .mode-tabs {
|
||
display: flex;
|
||
gap: 4px;
|
||
margin-bottom: 8px;
|
||
border-bottom: 1px solid var(--ds-border);
|
||
}
|
||
.home-mock .install-block .mode-tab {
|
||
background: transparent;
|
||
border: none;
|
||
color: var(--ds-text-muted);
|
||
font-size: 13px;
|
||
font-weight: 600;
|
||
padding: 8px 14px;
|
||
cursor: pointer;
|
||
border-bottom: 2px solid transparent;
|
||
margin-bottom: -1px;
|
||
font-family: inherit;
|
||
}
|
||
.home-mock .install-block .mode-tab.is-active {
|
||
color: var(--ds-primary-dark);
|
||
border-bottom-color: var(--ds-primary-dark);
|
||
}
|
||
/* Single-line description below the mode-tab — kept on one row.
|
||
The text is tight enough to fit at typical widths; on narrow
|
||
viewports it wraps gracefully. */
|
||
.home-mock .install-block .mode-panel-lede {
|
||
font-size: 13.5px;
|
||
line-height: 1.5;
|
||
color: var(--ds-text-secondary);
|
||
margin: 10px 0 12px;
|
||
}
|
||
.home-mock .install-block .mode-panel-lede code {
|
||
background: var(--ds-primary-light);
|
||
color: var(--ds-primary-dark);
|
||
padding: 1px 6px;
|
||
border-radius: 4px;
|
||
font-family: var(--ds-font-mono);
|
||
font-size: 12px;
|
||
}
|
||
/* `.setup-section-header` (eyebrow + heading + lede above a wizard
|
||
card) extracted to shared app/web/static/css/components.css. */
|
||
.setup-section-header .lead.lead-privacy {
|
||
font-size: 13.5px;
|
||
color: var(--ds-text-muted);
|
||
margin-bottom: 0;
|
||
}
|
||
.setup-section-header .lead.lead-privacy strong { color: var(--ds-text-secondary); }
|
||
/* Step 5 manual-fallback details — inline with the copy button on the
|
||
right of the cta row. When closed, just the summary is visible;
|
||
when opened, the expanded preview spans the full row width. */
|
||
.home-mock .install-block .setup-cta-row {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
align-items: center;
|
||
gap: 12px 16px;
|
||
margin-top: 4px;
|
||
}
|
||
.home-mock .install-block .setup-cta-row .manual-fallback {
|
||
margin-left: auto;
|
||
font-size: 13px;
|
||
}
|
||
.home-mock .install-block .setup-cta-row .manual-fallback[open] {
|
||
flex-basis: 100%;
|
||
margin-left: 0;
|
||
}
|
||
.home-mock .install-block .setup-cta-row .manual-fallback > summary {
|
||
color: var(--ds-text-muted);
|
||
cursor: pointer;
|
||
list-style: none;
|
||
}
|
||
.home-mock .install-block .setup-cta-row .manual-fallback > summary::-webkit-details-marker { display: none; }
|
||
.home-mock .install-block .setup-cta-row .manual-fallback > summary::before {
|
||
content: "\25B8 ";
|
||
color: var(--ds-text-muted);
|
||
}
|
||
.home-mock .install-block .setup-cta-row .manual-fallback[open] > summary::before { content: "\25BE "; }
|
||
.home-mock .install-block .setup-cta-row .manual-fallback > summary:hover { color: var(--ds-text-primary); }
|
||
/* Setup-finish strip — flex row separating "waiting for agnes pull"
|
||
status from the right-aligned "Mark me as onboarded" fallback link.
|
||
Matches design spec's footer row on the setup card. */
|
||
.home-mock .install-hero .setup-finish-strip {
|
||
border-top: 1px solid var(--ds-border);
|
||
padding-top: 16px;
|
||
margin-top: 20px;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
flex-wrap: wrap;
|
||
gap: 12px;
|
||
font-size: 13px;
|
||
color: var(--ds-text-muted);
|
||
}
|
||
.home-mock .install-hero .setup-finish-text code {
|
||
background: var(--ds-primary-light);
|
||
color: var(--ds-primary-dark);
|
||
padding: 1px 6px;
|
||
border-radius: 4px;
|
||
font-family: var(--ds-font-mono);
|
||
font-size: 11.5px;
|
||
}
|
||
.home-mock .install-hero .setup-finish-link {
|
||
color: var(--ds-text-muted);
|
||
text-decoration: underline;
|
||
cursor: pointer;
|
||
}
|
||
.home-mock .install-hero .setup-finish-link:hover { color: var(--ds-text-primary); }
|
||
.home-mock .install-hero .setup-finish-strip .status {
|
||
width: 100%;
|
||
font-style: italic;
|
||
margin-top: 4px;
|
||
}
|
||
/* Setup-CTA inside Step 4 — green primary button on light. */
|
||
.home-mock .setup-cta-lead {
|
||
color: var(--ds-text-secondary);
|
||
}
|
||
.home-mock .setup-cta-lead code {
|
||
background: var(--ds-primary-light);
|
||
color: var(--ds-primary-dark);
|
||
}
|
||
.home-mock .btn-setup-primary {
|
||
background: var(--ds-primary);
|
||
color: #ffffff;
|
||
}
|
||
.home-mock .btn-setup-primary:hover { background: var(--ds-primary-dark); }
|
||
.home-mock .setup-cta-hint { color: var(--ds-text-muted); }
|
||
.home-mock details.manual-fallback {
|
||
border-top-color: var(--ds-border);
|
||
}
|
||
.home-mock details.manual-fallback > summary {
|
||
color: var(--ds-text-secondary);
|
||
}
|
||
/* Self-mark + auto-detect badge — light variants. */
|
||
.home-mock .self-mark { color: var(--ds-text-secondary); }
|
||
.home-mock .self-mark button {
|
||
background: var(--ds-surface);
|
||
color: var(--ds-text-primary);
|
||
border: 1px solid var(--ds-border);
|
||
}
|
||
.home-mock .self-mark button:hover { background: var(--ds-bg); }
|
||
.home-mock .auto-detect-badge {
|
||
background: var(--ds-primary-light);
|
||
color: var(--ds-primary-dark);
|
||
border: 1px solid rgba(46, 168, 119, 0.30);
|
||
}
|
||
.home-mock .auto-detect-badge .pulse { background: var(--ds-primary); }
|
||
.home-mock .auto-detect-badge code {
|
||
background: rgba(46, 168, 119, 0.18);
|
||
color: var(--ds-primary-dark);
|
||
}
|
||
/* Terminal-howto disclosure — blue info-hint box matching the design's
|
||
`.step-body .hint` vocabulary (light blue bg, blue left-border,
|
||
blue ink, white kbd chips with thin border). */
|
||
.home-mock details.terminal-howto {
|
||
background: var(--ds-info-bg);
|
||
border: none;
|
||
border-left: 3px solid var(--ds-info-line);
|
||
border-radius: 8px;
|
||
padding: 12px 16px;
|
||
margin-top: 10px;
|
||
}
|
||
.home-mock details.terminal-howto > summary {
|
||
color: var(--ds-info-ink);
|
||
font-weight: 700;
|
||
font-size: 14px;
|
||
}
|
||
.home-mock details.terminal-howto > summary::before { color: var(--ds-info-ink); }
|
||
.home-mock .terminal-howto-body {
|
||
color: var(--ds-info-ink);
|
||
margin-top: 8px;
|
||
font-size: 14px;
|
||
}
|
||
.home-mock .terminal-howto-body p { margin: 0 0 6px; }
|
||
.home-mock .terminal-howto-body p:last-child { margin: 0; }
|
||
.home-mock .terminal-howto-body strong { color: var(--ds-info-ink); }
|
||
.home-mock .terminal-howto-body kbd {
|
||
background: var(--ds-surface);
|
||
color: var(--ds-text-primary);
|
||
border: 1px solid var(--ds-border);
|
||
border-bottom-width: 2px;
|
||
padding: 1px 6px;
|
||
border-radius: 4px;
|
||
font-size: 12px;
|
||
font-family: var(--ds-font-mono);
|
||
}
|
||
/* Step 3 (home_automode) extra .install-cmd nested inside — already
|
||
dark via .install-cmd rule above. Notes around it use ink-soft. */
|
||
.home-mock .install-cmd .copy-btn {
|
||
background: rgba(255, 255, 255, 0.08);
|
||
color: #ffffff;
|
||
border: 1px solid rgba(255, 255, 255, 0.15);
|
||
}
|
||
.home-mock .install-cmd .copy-btn:hover { background: rgba(255, 255, 255, 0.15); }
|
||
/* Manual-preview-pre stays dark (rendered already) — no change needed. */
|
||
|
||
@media (max-width: 560px) {
|
||
.home-mock .install-block { padding-left: 0; }
|
||
.home-mock .install-block .step-num { position: static; margin-bottom: 8px; }
|
||
}
|
||
</style>
|
||
|
||
<div class="home-mock">
|
||
|
||
{# Value-first intro hero — explains *what* {{ instance_brand }} is
|
||
before any install step. Renders for both onboarded and
|
||
not-onboarded users: onboarded analysts get the same product
|
||
narrative on every visit, just without the "Set up in ~15 min"
|
||
CTA. Pillars row mirrors the four primary nav destinations
|
||
(catalog / marketplace / corporate-memory / marketplace?skills)
|
||
so the value proposition aligns with where the user can act on
|
||
it. #}
|
||
{% set display_name = (user.name or (user.email or "").split("@")[0] or "there") %}
|
||
<section class="home-hero-intro">
|
||
<div class="eyebrow">Welcome, {{ display_name }}</div>
|
||
<h1>{{ instance_brand }} is your team's <span class="accent">AI workspace.</span></h1>
|
||
<p class="lede">
|
||
One place to reach {{ instance_brand }}'s curated data, plugins, skills, and shared memory —
|
||
so your AI agent stops guessing and starts answering. Curated, versioned, kept current.
|
||
The system learns from every session and extends coverage so the next question doesn't
|
||
trip on the same gap.
|
||
</p>
|
||
<p class="lede">
|
||
Think of it as your <em>AI Chief of Staff</em> — it lives in a folder on your computer,
|
||
gives you access to company data and curated skills, and lets you share your own skills
|
||
and plugins back to the team.
|
||
</p>
|
||
<div class="home-hero-cta">
|
||
{% if not onboarded %}
|
||
<a class="btn-intro btn-intro-primary" href="#install-hero">Set up in ~15 min →</a>
|
||
{% endif %}
|
||
<a class="btn-intro btn-intro-secondary" href="#look-around">Just browse — no install needed</a>
|
||
</div>
|
||
<div class="home-hero-pillars">
|
||
<div class="pillar">
|
||
<div class="pillar-h">Data packages</div>
|
||
<p>Tables, schema, metric definitions your team has registered — documented business rules.</p>
|
||
</div>
|
||
<div class="pillar">
|
||
<div class="pillar-h">Plugins</div>
|
||
<p>Asana, Jira, Google Workspace, Atlassian, GitHub. Curated & community-built.</p>
|
||
</div>
|
||
<div class="pillar">
|
||
<div class="pillar-h">Skills</div>
|
||
<p>Reusable workflow templates Claude follows — brainstorming, planning, review, debugging. Curated company-wide plus a flea market to share yours.</p>
|
||
</div>
|
||
<div class="pillar">
|
||
<div class="pillar-h">Memory</div>
|
||
<p>Shared analyst knowledge and prior solutions, fed back into Claude's context on demand.</p>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
{# Homepage status frame — five counters with 24h/7d toggle.
|
||
Two gates: (a) operator flag instance.home.show_status_frame /
|
||
AGNES_HOME_SHOW_STATUS_FRAME (default on, evaluated in router and
|
||
passed as `status_frame_enabled`); (b) the user being onboarded —
|
||
fresh users see a clean install-hero before zero-value stats.
|
||
Router skips `compute_home_stats` (saves the DB hit) when either
|
||
gate is closed, so `home_stats` is None in that branch. #}
|
||
{% if status_frame_enabled and onboarded and home_stats %}
|
||
{% include "_home_stats.html" %}
|
||
{% endif %}
|
||
|
||
{% set display_name = (user.name or (user.email or "").split("@")[0] or "there") %}
|
||
|
||
{# Install-hero renders only for not-onboarded users. Once `agnes init`
|
||
POSTs /api/me/onboarded (or the user clicks the in-hero X) the hero
|
||
disappears entirely — the rest of /home (connector tiles, news,
|
||
etc.) stays. Offboarding escape hatch moved to a discrete strip
|
||
below; see `.offboard-strip`. #}
|
||
{% if not onboarded %}
|
||
<div class="setup-section-header" id="install-hero">
|
||
<div class="eyebrow">First time here</div>
|
||
<h2 class="setup-heading">Set up {{ instance_brand }} on your machine</h2>
|
||
<p class="lead">
|
||
~15 minutes, one time. You'll install one CLI tool, pick a folder, and paste a script we generate for you. After that you have a working AI workspace that knows your team's data. Everything lives in <code>~/Desktop/{{ workspace_dir }}</code> and can be removed in one command.
|
||
</p>
|
||
</div>
|
||
<div class="install-hero">
|
||
<button type="button" class="install-hero-close" id="installHeroClose"
|
||
data-target-source="self_acknowledged"
|
||
aria-label="I'm already set up — close this setup hero">×</button>
|
||
|
||
{# Progress meta — visual hint that the wizard is bounded ("~15 min,
|
||
one-time, reversible"). Six steps total when home_automode is
|
||
enabled, five when it's disabled (the "Launch Claude with
|
||
auto-approve" step hides via {% if home_automode.show %}).
|
||
Pre-empt: when more steps land in future iterations, just
|
||
bump the total_steps default; per-step numbering inside
|
||
.install-block carries its own count already. #}
|
||
{% set total_steps = 6 if home_automode.show else 5 %}
|
||
<div class="setup-meta" aria-label="Setup overview">
|
||
<span class="setup-chip">Step 1 of {{ total_steps }}</span>
|
||
<span class="setup-chip-quiet">~15 min total</span>
|
||
<span class="setup-chip-quiet">One-time setup</span>
|
||
<span class="setup-chip-quiet">Reversible — remove with one command</span>
|
||
</div>
|
||
<div class="progress-bar" aria-hidden="true">
|
||
<div class="progress-fill" style="width: {{ (100 // total_steps) }}%"></div>
|
||
</div>
|
||
|
||
<div class="install-block">
|
||
<div class="step-num" aria-hidden="true"><span>1</span></div>
|
||
<div class="label">Install Claude Code on your computer</div>
|
||
<p class="step-lede">Claude Code is the CLI tool that runs Claude on your machine. {{ instance_brand }} plugs into it. <strong>~3 min.</strong></p>
|
||
<div class="os-tabs" role="tablist" aria-label="Operating system">
|
||
<button type="button" role="tab" class="os-tab is-active"
|
||
data-os-tab="unix" aria-selected="true">macOS / Linux</button>
|
||
<button type="button" role="tab" class="os-tab"
|
||
data-os-tab="windows" aria-selected="false">Windows (PowerShell)</button>
|
||
</div>
|
||
<div class="install-cmd" role="tabpanel" data-os-panel="unix">
|
||
<span class="multiline" id="install-cmd-claude-unix">curl -fsSL https://claude.ai/install.sh | bash</span>
|
||
<button class="copy-btn" data-copy-target="install-cmd-claude-unix">Copy</button>
|
||
</div>
|
||
<div class="install-cmd" role="tabpanel" data-os-panel="windows" hidden>
|
||
<span class="multiline" id="install-cmd-claude-windows">irm https://claude.ai/install.ps1 | iex</span>
|
||
<button class="copy-btn" data-copy-target="install-cmd-claude-windows">Copy</button>
|
||
</div>
|
||
|
||
<div class="callout-hint">
|
||
<strong>Don't have a terminal open yet?</strong>
|
||
On macOS, press <kbd>⌘</kbd> + <kbd>Space</kbd>, type <em>Terminal</em>, hit Enter.
|
||
On Windows, press the <kbd>⊞</kbd> key, type <em>PowerShell</em>, hit Enter.
|
||
</div>
|
||
|
||
<p class="step-lede" style="margin-top: 14px;">
|
||
Once it finishes, verify with <code>claude --version</code>, then sign in with <code>claude</code>
|
||
(one-time OAuth in your browser). Don't have a Claude license yet?
|
||
<a href="/setup-advanced#claude-plan">Here's how to get one</a>
|
||
(Pro / Max 5× / Max 20× / Enterprise — Enterprise is the option for Finance and Legal).
|
||
</p>
|
||
</div>
|
||
|
||
<div class="install-block">
|
||
<div class="step-num" aria-hidden="true"><span>2</span></div>
|
||
<div class="label">Pick a folder for {{ instance_brand }}</div>
|
||
<p class="step-lede">This is where {{ instance_brand }} — and all your AI projects — will live. <strong>~1 min.</strong></p>
|
||
|
||
<div class="callout-rec">
|
||
<strong>My recommendation: put it on your Desktop.</strong>
|
||
Desktop is easy to see in Finder/Explorer and easy to find from anywhere
|
||
(<code>~/Desktop/{{ workspace_dir }}</code> on macOS, <code>%USERPROFILE%\Desktop\{{ workspace_dir }}</code> on Windows).
|
||
I keep mine there because I open it 10× a day. Your home folder is fine too —
|
||
the install will work anywhere.
|
||
</div>
|
||
|
||
<div class="os-tabs" role="tablist" aria-label="Operating system">
|
||
<button type="button" role="tab" class="os-tab is-active"
|
||
data-os-tab="unix" aria-selected="true">macOS / Linux</button>
|
||
<button type="button" role="tab" class="os-tab"
|
||
data-os-tab="windows" aria-selected="false">Windows (PowerShell)</button>
|
||
</div>
|
||
<div class="install-cmd" role="tabpanel" data-os-panel="unix">
|
||
<span class="multiline" id="install-cmd-mkdir-unix">mkdir -p ~/Desktop/{{ workspace_dir }} && cd ~/Desktop/{{ workspace_dir }}</span>
|
||
<button class="copy-btn" data-copy-target="install-cmd-mkdir-unix">Copy</button>
|
||
</div>
|
||
<div class="install-cmd" role="tabpanel" data-os-panel="windows" hidden>
|
||
<span class="multiline" id="install-cmd-mkdir-windows">New-Item -ItemType Directory -Force -Path "$HOME\Desktop\{{ workspace_dir }}" | Out-Null
|
||
Set-Location "$HOME\Desktop\{{ workspace_dir }}"</span>
|
||
<button class="copy-btn" data-copy-target="install-cmd-mkdir-windows">Copy</button>
|
||
</div>
|
||
<div class="callout-hint">
|
||
<strong>What does <code>mkdir -p</code> do?</strong>
|
||
It creates a folder. The <code>-p</code> just means "don't complain if it already exists."
|
||
After this command, you have an empty folder called <code>{{ workspace_dir }}</code> on your Desktop.
|
||
You can verify by opening Finder/Explorer and looking on your Desktop — it'll be there.
|
||
</div>
|
||
</div>
|
||
|
||
{# Step 3 — open a terminal inside the workspace folder. No
|
||
shell command here: this step is just the OS-level "you are
|
||
now standing in the right directory" reassurance, mirroring
|
||
the design spec's Step 3. Step 2's `cd` already lands the
|
||
current shell here; this step covers the case where the
|
||
analyst closes that terminal between steps. #}
|
||
<div class="install-block">
|
||
<div class="step-num" aria-hidden="true"><span>3</span></div>
|
||
<div class="label">Open a terminal inside that folder</div>
|
||
<p class="step-lede">The terminal is how you tell Claude what to do. You need to "be in" the <code>{{ workspace_dir }}</code> folder before you launch Claude. <strong>~1 min.</strong></p>
|
||
|
||
<div class="callout-hint">
|
||
<strong>Two ways to get there.</strong> Pick whichever feels easier.
|
||
<ol>
|
||
<li><strong>Drag-and-drop (easy).</strong> Open Terminal. Type <code>cd</code> followed by a space. Then drag the <code>{{ workspace_dir }}</code> folder from your file browser right onto the Terminal window — it pastes the path for you. Hit Enter.</li>
|
||
<li><strong>Type the path (faster).</strong> See the command below.</li>
|
||
</ol>
|
||
</div>
|
||
|
||
<div class="os-tabs" role="tablist" aria-label="Operating system">
|
||
<button type="button" role="tab" class="os-tab is-active"
|
||
data-os-tab="unix" aria-selected="true">macOS / Linux</button>
|
||
<button type="button" role="tab" class="os-tab"
|
||
data-os-tab="windows" aria-selected="false">Windows (PowerShell)</button>
|
||
</div>
|
||
<div class="install-cmd" role="tabpanel" data-os-panel="unix">
|
||
<span class="multiline" id="install-cmd-cd-unix">cd ~/Desktop/{{ workspace_dir }}</span>
|
||
<button class="copy-btn" data-copy-target="install-cmd-cd-unix">Copy</button>
|
||
</div>
|
||
<div class="install-cmd" role="tabpanel" data-os-panel="windows" hidden>
|
||
<span class="multiline" id="install-cmd-cd-windows">Set-Location "$HOME\Desktop\{{ workspace_dir }}"</span>
|
||
<button class="copy-btn" data-copy-target="install-cmd-cd-windows">Copy</button>
|
||
</div>
|
||
|
||
<div class="callout-hint">
|
||
<strong>What does <code>cd</code> mean?</strong>
|
||
"Change directory" — like double-clicking into a folder, but in the terminal.
|
||
After this command, every command you type happens <em>inside</em> the <code>{{ workspace_dir }}</code> folder.
|
||
</div>
|
||
|
||
<div class="code-output">$ pwd
|
||
/Users/yourname/Desktop/{{ workspace_dir }}</div>
|
||
|
||
<p class="step-lede" style="font-size: 13px; color: var(--ds-text-muted); margin-top: 6px;">
|
||
(Optional: run <code>pwd</code> to print the path you're currently in. If it ends in <code>/Desktop/{{ workspace_dir }}</code>, you're in the right place.)
|
||
</p>
|
||
</div>
|
||
|
||
{% if home_automode.show %}
|
||
<div class="install-block">
|
||
<div class="step-num" aria-hidden="true"><span>4</span></div>
|
||
<div class="label">Launch Claude with auto-approve on</div>
|
||
<p class="step-lede">The next step pastes a setup script that runs ~20 shell commands. Launching Claude with the permission-skip flag lets it complete without per-command Yes/No prompts.</p>
|
||
<div class="install-cmd">
|
||
<span class="multiline" id="install-cmd-claude-yolo">claude --dangerously-skip-permissions</span>
|
||
<button class="copy-btn" data-copy-target="install-cmd-claude-yolo">Copy</button>
|
||
</div>
|
||
<div class="install-note">
|
||
Session-scoped — drops on next plain <code>claude</code>. Safe here because the script you paste in the next step is generated by this server and ends after a fixed sequence; the flag does not weaken future Claude sessions. Prefer reviewing each command? Run plain <code>claude</code> and press <strong>Shift + Tab</strong> to cycle on <strong>auto-accept edits</strong> (default → auto-accept edits → plan mode; footer shows <code>⏵⏵</code>). Covers file edits, not Bash — expect ~20 prompt clicks during the next step.
|
||
<br><br>
|
||
Persistent YOLO (allowlisted): see <a href="/setup-advanced#yolo" target="_blank" rel="noopener">YOLO mode</a> on /setup-advanced — pairs <code>--dangerously-skip-permissions</code> with a reviewed <code>~/.claude/settings.local.json</code> allowlist.
|
||
</div>
|
||
|
||
<div class="code-output">Welcome to Claude Code!
|
||
|
||
What can I help you with?
|
||
></div>
|
||
<p class="step-lede" style="margin-top: 12px;">
|
||
When you see the <code>></code> prompt, you're ready for the next step.
|
||
</p>
|
||
</div>
|
||
{% endif %}
|
||
|
||
<div class="install-block">
|
||
<div class="step-num" aria-hidden="true"><span>{% if home_automode.show %}5{% else %}4{% endif %}</span></div>
|
||
<div class="label">Get the install script and paste it into Claude</div>
|
||
<p class="step-lede">
|
||
Click the button below. {{ instance_brand }} generates a one-time install script
|
||
(with a 90-day login token) and copies it to your clipboard. Then paste it into the
|
||
Claude prompt from Step 4 (<kbd>⌘</kbd>+<kbd>V</kbd> on macOS,
|
||
<kbd>Ctrl</kbd>+<kbd>V</kbd> on Windows) and hit Enter. The script installs ~20 things
|
||
(the marketplace, your team's plugins, data package definitions, hooks that keep
|
||
everything in sync) into <code>~/Desktop/{{ workspace_dir }}</code>.
|
||
<strong>~30 sec to copy; ~5 min to run.</strong>
|
||
</p>
|
||
<div class="setup-cta-row">
|
||
<button type="button" id="setupClaudeBtn" class="btn-setup-primary" onclick="setupNewClaude(this)">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
|
||
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
|
||
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
|
||
</svg>
|
||
Copy install script to clipboard
|
||
</button>
|
||
<span class="setup-cta-meta">
|
||
<span class="setup-cta-hint">Token valid 90 days · stays in clipboard only</span>
|
||
</span>
|
||
<details class="manual-fallback">
|
||
<summary>Or paste manually (preview the script)</summary>
|
||
<div class="manual-preview-wrap">
|
||
{% with preview_mode=True %}
|
||
{% include "_claude_setup_instructions.jinja" %}
|
||
{% endwith %}
|
||
</div>
|
||
<div class="install-note">
|
||
The preview above is the exact text the button copies; the placeholder is replaced with a real token at click time. Don't create <code>~/Desktop/{{ workspace_dir }}/Projects/</code> manually — the bundled plugin offers to set it up after install.
|
||
</div>
|
||
</details>
|
||
</div>
|
||
<div id="setupClaudeError" class="setup-error" role="alert" style="display:none;"></div>
|
||
|
||
<div class="callout-hint">
|
||
<strong>You're done when</strong> the script finishes and {{ instance_brand }} auto-detects
|
||
your first <code>agnes pull</code> (~30 seconds after the script ends). This page will
|
||
update on its own.
|
||
</div>
|
||
</div>
|
||
|
||
{# Step 6 — Optional one-word shortcut. Mirrors the design spec's
|
||
final step: a shell alias that drops the analyst into the
|
||
workspace folder and launches Claude with a single keyword.
|
||
Pure quality-of-life polish, gated by an inline <details>
|
||
so the wizard stays compact for analysts who just want the
|
||
default `claude` invocation. #}
|
||
<div class="install-block">
|
||
<div class="step-num" aria-hidden="true"><span>{% if home_automode.show %}6{% else %}5{% endif %}</span></div>
|
||
<div class="label">Optional: create a one-word shortcut for next time</div>
|
||
<p class="step-lede">
|
||
So you can stop retyping the long flag and stop typing <code>cd</code>. Adds a shell
|
||
function that <em>both</em> changes into your <code>{{ workspace_dir }}</code> folder
|
||
<em>and</em> launches Claude — one word, anywhere on your machine. <strong>~30 seconds.</strong>
|
||
</p>
|
||
|
||
<p class="step-mode-heading">Pick a permission level:</p>
|
||
|
||
<div class="mode-tabs" role="tablist" aria-label="Permission level">
|
||
<button type="button" role="tab" class="mode-tab is-active"
|
||
data-mode-tab="auto" aria-selected="true">Auto (safer — recommended)</button>
|
||
<button type="button" role="tab" class="mode-tab"
|
||
data-mode-tab="yolo" aria-selected="false">YOLO (faster, broader)</button>
|
||
</div>
|
||
|
||
{# AUTO mode panel — `--permission-mode auto` is the right default. #}
|
||
<div data-mode-panel="auto">
|
||
<p class="mode-panel-lede"><code>--permission-mode auto</code> — auto-approves edits and safe read-only commands; still asks before anything destructive. Right default for most people.</p>
|
||
<div class="os-tabs" role="tablist" aria-label="Operating system">
|
||
<button type="button" role="tab" class="os-tab is-active"
|
||
data-os-tab="unix" aria-selected="true">macOS / Linux (zsh)</button>
|
||
<button type="button" role="tab" class="os-tab"
|
||
data-os-tab="windows" aria-selected="false">Windows (PowerShell)</button>
|
||
</div>
|
||
<div class="install-cmd" role="tabpanel" data-os-panel="unix">
|
||
<span class="multiline" id="install-cmd-shortcut-auto-unix">echo '{{ workspace_dir | lower }}() { cd ~/Desktop/{{ workspace_dir }} && claude --permission-mode auto "$@"; }' >> ~/.zshrc && source ~/.zshrc</span>
|
||
<button class="copy-btn" data-copy-target="install-cmd-shortcut-auto-unix">Copy</button>
|
||
</div>
|
||
<div class="install-cmd" role="tabpanel" data-os-panel="windows" hidden>
|
||
<span class="multiline" id="install-cmd-shortcut-auto-windows">if (!(Test-Path $PROFILE)) { New-Item $PROFILE -Force }; Add-Content $PROFILE 'function {{ workspace_dir | lower }} { Push-Location "$env:USERPROFILE\Desktop\{{ workspace_dir }}"; claude --permission-mode auto @args }'</span>
|
||
<button class="copy-btn" data-copy-target="install-cmd-shortcut-auto-windows">Copy</button>
|
||
</div>
|
||
<p class="step-lede" style="margin-top: 8px;">
|
||
Now typing <code>{{ workspace_dir | lower }}</code> from any terminal hops into the
|
||
<code>{{ workspace_dir }}</code> folder and launches Claude in auto mode. Same on
|
||
day 1 as day 100.
|
||
</p>
|
||
</div>
|
||
|
||
{# YOLO mode panel — `--dangerously-skip-permissions` skips every prompt. #}
|
||
<div data-mode-panel="yolo" hidden>
|
||
<p class="mode-panel-lede"><code>--dangerously-skip-permissions</code> — auto-approves <em>everything</em>, no prompts. Faster, broader blast radius. Read the security callout below first.</p>
|
||
<div class="os-tabs" role="tablist" aria-label="Operating system">
|
||
<button type="button" role="tab" class="os-tab is-active"
|
||
data-os-tab="unix" aria-selected="true">macOS / Linux (zsh)</button>
|
||
<button type="button" role="tab" class="os-tab"
|
||
data-os-tab="windows" aria-selected="false">Windows (PowerShell)</button>
|
||
</div>
|
||
<div class="install-cmd" role="tabpanel" data-os-panel="unix">
|
||
<span class="multiline" id="install-cmd-shortcut-yolo-unix">echo 'yolo() { cd ~/Desktop/{{ workspace_dir }} && claude --dangerously-skip-permissions "$@"; }' >> ~/.zshrc && source ~/.zshrc</span>
|
||
<button class="copy-btn" data-copy-target="install-cmd-shortcut-yolo-unix">Copy</button>
|
||
</div>
|
||
<div class="install-cmd" role="tabpanel" data-os-panel="windows" hidden>
|
||
<span class="multiline" id="install-cmd-shortcut-yolo-windows">if (!(Test-Path $PROFILE)) { New-Item $PROFILE -Force }; Add-Content $PROFILE 'function yolo { Push-Location "$env:USERPROFILE\Desktop\{{ workspace_dir }}"; claude --dangerously-skip-permissions @args }'</span>
|
||
<button class="copy-btn" data-copy-target="install-cmd-shortcut-yolo-windows">Copy</button>
|
||
</div>
|
||
<p class="step-lede" style="margin-top: 8px;">
|
||
Now typing <code>yolo</code> from any terminal hops into the
|
||
<code>{{ workspace_dir }}</code> folder and launches Claude in full auto-approve.
|
||
Use it when you trust the work; switch back to <code>{{ workspace_dir | lower }}</code>
|
||
(auto mode) when you don't.
|
||
</p>
|
||
</div>
|
||
|
||
<div class="install-note">
|
||
Bash users: swap <code>~/.zshrc</code> for <code>~/.bashrc</code>. The shortcut is reversible — open the rc file and delete the function line to remove it.
|
||
</div>
|
||
|
||
<div class="callout-rec">
|
||
<strong>The install bundled a curated security allowlist — review it before going further.</strong>
|
||
{{ instance_brand }}'s setup script installed a reviewed <code>~/.claude/settings.local.json</code> for you,
|
||
with sensible defaults: read freely, run common dev tools, gate destructive ops. This is your
|
||
actual safety net — the auto / YOLO mode is only safe because of it.
|
||
<br><br>
|
||
Open the file (<code>open ~/.claude/settings.local.json</code> on macOS, or in VS Code) and
|
||
skim what's allowed and what's gated. Add or remove rules as you learn what you trust.
|
||
<a href="/setup-advanced#yolo">More on the allowlist →</a>
|
||
</div>
|
||
</div>
|
||
|
||
{# Setup-finish strip — flex row matching design spec: hourglass +
|
||
"Waiting for first agnes pull" on the left, "Already set up?
|
||
Mark me as onboarded →" link on the right. Auto-detect lands
|
||
via agnes-init's POST to /api/me/onboarded; the right-aligned
|
||
link is the manual fallback. JS hooks `self-mark-btn`. #}
|
||
<div class="setup-finish-strip" role="status" aria-live="polite">
|
||
<span class="setup-finish-text">
|
||
<span aria-hidden="true">⏳</span>
|
||
Waiting for your first <code>agnes pull</code> — auto-detects within ~30 sec of the install script finishing.
|
||
</span>
|
||
<a href="#" id="self-mark-btn" class="setup-finish-link"
|
||
data-target-onboarded="true"
|
||
data-target-source="self_acknowledged">Already set up? Mark me as onboarded →</a>
|
||
<span id="self-mark-status" class="status" role="status" aria-live="polite"></span>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
{% if onboarded %}
|
||
{# Offboarding escape hatch shown only after the hero has disappeared.
|
||
Lets the analyst (e.g. after wiping ~/Desktop/{{ workspace_dir }}) flip the
|
||
users.onboarded boolean back to false so the full install hero
|
||
renders again on next reload. Discrete by design — onboarded
|
||
users land on /home expecting the nav hub, not a setup screen. #}
|
||
<div class="offboard-strip">
|
||
<span>Workspace ready — wiped it and need the full setup view back?</span>
|
||
<button id="offboard-btn" type="button"
|
||
data-target-source="self_unmark">Mark me as offboarded</button>
|
||
<span id="offboard-status" class="status" role="status" aria-live="polite"></span>
|
||
</div>
|
||
{% endif %}
|
||
|
||
{# Auto-mode card used to live here as a `<details>` reference block;
|
||
moved into the install-hero as the new Step 2 so users enable it
|
||
BEFORE Step 3's install runs ~20 commands. Gated by the same
|
||
`home_automode.show` flag at the call site. #}
|
||
|
||
{# Getting Started was previously rendered HERE (between the offboard
|
||
strip and Overview) as a full-card <section>. Moved up to render
|
||
BEFORE the install-hero as a collapsed-by-default <details> — see
|
||
the block right after `{% if not onboarded %}` near line ~1357. #}
|
||
|
||
{# First-session story — five-beat narrative showing what the analyst
|
||
will actually see on their first run. Terminal frames design the
|
||
real output. Renders for both onboarded (reference / refresher)
|
||
and not-onboarded (preview of what's coming) so the visual
|
||
language stays consistent across visits. #}
|
||
<section class="first-session" id="first-session">
|
||
<div class="eyebrow">Your first session</div>
|
||
<h2>What happens when you launch {{ instance_brand }}</h2>
|
||
<p class="lede">A real session, end-to-end. You'll see exactly what appears on screen and what to type at each step. No prior terminal experience needed.</p>
|
||
|
||
<div class="session-intro">
|
||
<span class="session-intro-icon" aria-hidden="true">🧭</span>
|
||
<span>The whole loop is <strong>five beats</strong>: launch → pick a project → Claude loads its memory → ask anything → close when done. Every part of {{ instance_brand }} — plugins, data packages, skills, shared memory — quietly does its job inside this loop. You don't have to manage them.</span>
|
||
</div>
|
||
|
||
<div class="session-walk">
|
||
<div class="session-step">
|
||
<div class="session-num">1</div>
|
||
<div class="session-content">
|
||
<h3>Launch your workspace</h3>
|
||
<p><strong>Open a terminal</strong> — in VS Code, press <kbd>⌘</kbd>+<kbd>`</kbd> (Mac) or <kbd>Ctrl</kbd>+<kbd>`</kbd> (Windows). On macOS you can also open the standalone Terminal app from Spotlight.</p>
|
||
<p><strong>Type one word</strong> — <code>{{ workspace_dir | lower }}</code> (or <code>claude</code> if you skipped Step 6) — and hit Enter.</p>
|
||
<div class="terminal-frame">
|
||
<div class="terminal-bar"><span class="traffic r"></span><span class="traffic y"></span><span class="traffic g"></span><span class="terminal-title">~/Desktop/{{ workspace_dir }} — zsh</span></div>
|
||
<div class="terminal-body"><span class="prompt">$</span><span class="you">{{ workspace_dir | lower }}</span></div>
|
||
</div>
|
||
<p class="annotation">That single word jumps into your <code>~/Desktop/{{ workspace_dir }}</code> folder <em>and</em> launches Claude in one go — same on day 1 as day 100. No <code>cd</code>, no flags, no setup to remember. (The shortcut was installed in Step 6 of the setup above.)</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="session-step">
|
||
<div class="session-num">2</div>
|
||
<div class="session-content">
|
||
<h3>{{ instance_brand }} greets you with today's menu</h3>
|
||
<p>Claude reads your recent activity and shows the five projects you've touched most recently. Your screen looks something like this:</p>
|
||
<div class="terminal-frame">
|
||
<div class="terminal-bar"><span class="traffic r"></span><span class="traffic y"></span><span class="traffic g"></span><span class="terminal-title">claude — {{ instance_brand }}</span></div>
|
||
<div class="terminal-body">2026-05-20
|
||
|
||
What would you like to do?
|
||
|
||
<strong>[1]</strong> Talk to {{ instance_brand }} — workspace questions, setup, admin
|
||
<strong>[2]</strong> New project — scaffold a new project
|
||
<strong>[3]</strong> Switch to recent project:
|
||
a. RevenueAnalysis <span class="dim">(2 hours ago)</span>
|
||
b. WeeklyReview <span class="dim">(today)</span>
|
||
c. Onboarding <span class="dim">(1 day ago)</span>
|
||
d. OpsDb <span class="dim">(4 days ago)</span>
|
||
e. HRHandShake <span class="dim">(5 days ago)</span>
|
||
<strong>[4]</strong> Switch to another project — browse the full registry
|
||
<span class="prompt">></span><span class="caret"></span></div>
|
||
</div>
|
||
<p class="annotation">The recent-projects list updates every session based on what you actually worked on. Pick a code (like <code>3a</code>) or just type free text — <em>"open the news project"</em> or <em>"keep working on the marketing plan"</em> work the same way. Claude figures out the intent.</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="session-step">
|
||
<div class="session-num">3</div>
|
||
<div class="session-content">
|
||
<h3>Pick a project — Claude loads its memory</h3>
|
||
<p>Type your choice and hit Enter. Claude switches into that project's folder, reads its <code>CLAUDE.md</code> (the project's living briefing doc), checks for new inputs you've dropped in, and tells you the state of play.</p>
|
||
<div class="terminal-frame">
|
||
<div class="terminal-bar"><span class="traffic r"></span><span class="traffic y"></span><span class="traffic g"></span><span class="terminal-title">claude — {{ instance_brand }}</span></div>
|
||
<div class="terminal-body"><span class="prompt">></span><span class="you">3a</span>
|
||
|
||
Switched to <strong>RevenueAnalysis</strong> — quarterly revenue
|
||
review program.
|
||
|
||
<span class="dim">Loaded: CLAUDE.md, 8 knowledge files, 4 active decisions</span>
|
||
<span class="dim">Last session: yesterday, 4:12 PM — "Q1 close review"</span>
|
||
|
||
One unprocessed input detected:
|
||
<span class="path">inputs/Q1_close_DRAFT.pdf</span> (3.4 MB)
|
||
|
||
Want me to process it into <span class="path">knowledge/</span> now?
|
||
<span class="prompt">></span><span class="caret"></span></div>
|
||
</div>
|
||
<p class="annotation">Claude now has full project context loaded — past decisions, key numbers, ongoing work, who's involved. You don't have to re-explain anything every session. New files in <code>inputs/</code> are auto-detected so nothing gets forgotten on your hard drive.</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="session-step">
|
||
<div class="session-num">4</div>
|
||
<div class="session-content">
|
||
<h3>Ask anything — Claude is now scoped to that project</h3>
|
||
<p>Plain English. No commands to memorise. Claude pulls from the project's files, your registered data packages, installed plugins, and the team's shared memory — quoting exact sources as it goes.</p>
|
||
<div class="terminal-frame">
|
||
<div class="terminal-bar"><span class="traffic r"></span><span class="traffic y"></span><span class="traffic g"></span><span class="terminal-title">claude — {{ instance_brand }}</span></div>
|
||
<div class="terminal-body"><span class="prompt">></span><span class="you">what's the latest on the Q1 close? Give me the
|
||
headline numbers and the open risks.</span>
|
||
|
||
<span class="dim">Reading <span class="path">knowledge/q1-close-input.md</span>…</span>
|
||
<span class="dim">Reading <span class="path">temp/v4/close.md</span>…</span>
|
||
|
||
<strong>Q1 close summary — v4, Apr 2026</strong>
|
||
|
||
• Revenue: $124.3M → $131.0M (+5.4% QoQ)
|
||
• Gross margin: 62.1% → 63.4%
|
||
• 4 markets above plan, 2 below
|
||
• Customer churn flat at 6.1%
|
||
|
||
<strong>Open risks</strong>
|
||
|
||
• FX hedging assumption is 6 weeks old — needs refresh
|
||
• No DRI yet on the EMEA segmentation prerequisite
|
||
• Q2 forecast model still in review
|
||
|
||
Want me to draft a 1-page brief, or dig into one of these?
|
||
<span class="prompt">></span><span class="caret"></span></div>
|
||
</div>
|
||
<p class="annotation"><strong>Cited sources are clickable paths</strong> — tap to open the actual file in your editor. If Claude needs data from a remote system, your registered plugins kick in silently. No copy-pasting between tabs.</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="session-step">
|
||
<div class="session-num">5</div>
|
||
<div class="session-content">
|
||
<h3>When you're done, just close — everything's saved</h3>
|
||
<p>Type <code>/exit</code> or close the terminal window. {{ instance_brand }} saves the session, captures any new knowledge into the project's memory, and puts today's project at the top of tomorrow's menu.</p>
|
||
<div class="terminal-frame">
|
||
<div class="terminal-bar"><span class="traffic r"></span><span class="traffic y"></span><span class="traffic g"></span><span class="terminal-title">claude — {{ instance_brand }}</span></div>
|
||
<div class="terminal-body"><span class="prompt">></span><span class="you">/exit</span>
|
||
|
||
<span class="dim">Session saved — 12 prompts, 4 files referenced.</span>
|
||
<span class="dim">RevenueAnalysis will appear as "today" in your menu tomorrow.</span>
|
||
|
||
<span class="ai-name">See you next time.</span>
|
||
<span class="prompt">$</span><span class="caret"></span></div>
|
||
</div>
|
||
<p class="annotation">Nothing to clean up, no "save your work" prompt, no version to remember. Next time you type <code>{{ workspace_dir | lower }}</code>, this project is right at the top of the recent list — and Claude remembers every decision, file, and follow-up from today.</p>
|
||
<p class="annotation session-privacy">
|
||
<strong>What leaves your machine.</strong> Session telemetry — prompts, tool calls, and tool responses — flows back to the central catalog so the team can analyse failure patterns. Raw data rows you query locally stay on your machine; only the prompt/response transcript travels. Need a session off the record? Toggle Private in Claude Code (type <code>/agnes-private</code>) to disable telemetry for that chat — nothing from that session reaches the catalog.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="session-tldr">
|
||
<strong>That's the whole loop.</strong> One word to launch — pick a project — ask — close. Everything else (plugins, data packages, skills, shared memory) is Claude doing more for you inside the same five-beat rhythm. You learn it once on day 1 and never re-learn it.
|
||
</div>
|
||
</section>
|
||
|
||
{# Overview section — operator-owned, opt-in. Body comes from the
|
||
`instance.overview` yaml field via get_instance_overview()
|
||
(`AGNES_INSTANCE_OVERVIEW` env override). Empty value hides the
|
||
whole section, keeping the OSS vendor-neutral. #}
|
||
{# Overview is operator-owned reference content, not per-user
|
||
chrome — no dismiss button on purpose. A one-time per-device
|
||
hide would mean returning users who wanted to re-read the
|
||
privacy posture / telemetry policy can't get back to it
|
||
without clearing localStorage. The whole section is opt-in at
|
||
the operator level (empty yaml → section absent), which is
|
||
the right axis of control. #}
|
||
{% if config.INSTANCE_OVERVIEW %}
|
||
<section class="home-overview">
|
||
<h2>Overview</h2>
|
||
<div class="home-overview-body">{{ config.INSTANCE_OVERVIEW | safe }}</div>
|
||
</section>
|
||
{% endif %}
|
||
|
||
{# Surfaces — four places to use {{ instance_brand }}. Mirrors the
|
||
design spec's "Where you can use it" section: VS Code (RECOMMENDED),
|
||
Terminal, Claude Desktop, Cowork (claude.ai). Each card carries
|
||
a short pitch + an uppercase eyebrow (DAILY USE / QUICK ACCESS /
|
||
CONNECT IT) + a numbered step list + a one-liner "Best for".
|
||
The .home-usage class is kept on the wrapper so existing test
|
||
assertions and the legacy `.home-usage-grid` mobile media query
|
||
still apply. #}
|
||
<section class="home-usage surfaces" id="where-you-can-use-it">
|
||
<header>
|
||
<div class="eyebrow">Where you can use it</div>
|
||
<h2>Four places, one workspace</h2>
|
||
<p class="lede">Same plugins, same data access, same credentials — pick whichever fits the moment. VS Code is the most comfortable for most people; the others are useful when the situation calls for it.</p>
|
||
</header>
|
||
<div class="surfaces-grid">
|
||
<article class="surface-card feature">
|
||
<div class="surface-icon" aria-hidden="true">📝</div>
|
||
<h3>VS Code <span class="badge">RECOMMENDED</span></h3>
|
||
<p class="what">The best place to live in {{ instance_brand }}. Multi-panel layout, built-in file editor, diff viewer, and a terminal Claude runs in — all in one window. Easier than a bare terminal for most people.</p>
|
||
<a href="#" id="vscode-screenshot-trigger" class="vscode-thumb">
|
||
<img src="/static/img/vscode-layout.png" alt="VS Code with terminal-centric layout"
|
||
onerror="this.parentElement.classList.add('thumb-empty');this.remove();">
|
||
<div class="thumb-fallback">
|
||
<div class="thumb-fallback-inner">
|
||
<div class="thumb-fallback-row">
|
||
<span class="dot d-explorer"></span>EXPLORER
|
||
<span class="dot d-t1"></span>TERMINAL 1
|
||
<span class="dot d-t2"></span>TERMINAL 2
|
||
<span class="dot d-t3"></span>TERMINAL 3
|
||
</div>
|
||
<div class="thumb-fallback-hint">Screenshot pending — drop at <code>app/web/static/img/vscode-layout.png</code></div>
|
||
</div>
|
||
</div>
|
||
<span class="thumb-caption">Click to enlarge — recommended layout</span>
|
||
</a>
|
||
<div class="steps">
|
||
<strong class="steps-eyebrow">DAILY USE</strong>
|
||
<ol>
|
||
<li>Open VS Code → <strong>File → Open Folder</strong> → select your <code>~/Desktop/{{ workspace_dir }}</code> folder.</li>
|
||
<li>Open terminal: <kbd>⌘</kbd>+<kbd>`</kbd> (macOS) or <kbd>Ctrl</kbd>+<kbd>`</kbd> (Windows).</li>
|
||
<li>Right-click the terminal tab → <em>"Move Terminal into Editor Area"</em>. Split with <kbd>⌘</kbd>+<kbd>\</kbd> — one panel for the conversation, one for diffs.</li>
|
||
<li>Type <code>claude</code> (or <code>{{ workspace_dir | lower }}</code> if you set the Step 6 shortcut). The welcome menu fires — pick a project or describe what you want.</li>
|
||
</ol>
|
||
</div>
|
||
<div class="when-to-use">
|
||
<strong>Full step-by-step guide:</strong> <a href="/setup-advanced#vscode">Open VS Code setup guide →</a>
|
||
</div>
|
||
</article>
|
||
|
||
<article class="surface-card">
|
||
<div class="surface-icon" aria-hidden="true">⌨️</div>
|
||
<h3>Terminal</h3>
|
||
<p class="what">Quick access — when you don't want a whole IDE open. Same Claude, same plugins, just bare. Best for short questions and one-off scripts.</p>
|
||
<div class="steps">
|
||
<strong class="steps-eyebrow">QUICK ACCESS</strong>
|
||
<ol>
|
||
<li>Open <strong>Terminal</strong> (macOS: <kbd>⌘</kbd>+<kbd>Space</kbd>, type <em>Terminal</em>) or <strong>PowerShell</strong> (Windows: <kbd>⊞</kbd>, type <em>PowerShell</em>).</li>
|
||
<li>Go to your folder: <code>cd ~/Desktop/{{ workspace_dir }}</code></li>
|
||
<li>Type <code>claude</code> (or <code>{{ workspace_dir | lower }}</code>) and hit Enter.</li>
|
||
<li>Welcome menu fires — same as in VS Code.</li>
|
||
</ol>
|
||
</div>
|
||
<div class="code-output">2026-05-20
|
||
|
||
What would you like to do?
|
||
|
||
[1] Talk to {{ instance_brand }} — workspace questions
|
||
[2] New project — scaffold a new project
|
||
[3] Switch to recent project:
|
||
a. RevenueAnalysis (2 hours ago)
|
||
b. WeeklyReview (1 day ago)
|
||
c. Onboarding (3 days ago)
|
||
[4] Browse full registry</div>
|
||
<div class="when-to-use"><strong>Best for:</strong> quick questions, scripts, when you already have a terminal open from something else.</div>
|
||
</article>
|
||
|
||
<article class="surface-card">
|
||
<div class="surface-icon" aria-hidden="true">🖥️</div>
|
||
<h3>Claude Code (Desktop app)</h3>
|
||
<p class="what">The Claude Code agent running inside the Claude Desktop app instead of in a terminal. You point it at your <code>{{ workspace_dir }}</code> folder and work from there with a native UI for diffs, file tree, and chat.</p>
|
||
<div class="steps">
|
||
<strong class="steps-eyebrow">CONNECT IT</strong>
|
||
<ol>
|
||
<li>Install Claude Desktop from <a href="https://claude.ai/download" target="_blank" rel="noopener">claude.ai/download</a>.</li>
|
||
<li>Sign in with the same account you use for Claude Code.</li>
|
||
<li>Open the <code>{{ workspace_dir }}</code> folder on your Desktop in Claude Code (<strong>File → Open Folder</strong>).</li>
|
||
<li>Everything in the workspace — plugins, skills, data packages, memory — is available there automatically.</li>
|
||
</ol>
|
||
</div>
|
||
<div class="when-to-use"><strong>Best for:</strong> people who prefer a native UI to a terminal. Same Claude Code engine, same workspace.</div>
|
||
</article>
|
||
|
||
<article class="surface-card incomplete">
|
||
<div class="surface-icon" aria-hidden="true">🤝</div>
|
||
<h3>Cowork (claude.ai) <span class="badge-warn">INSTRUCTIONS NEEDED</span></h3>
|
||
<p class="what">Claude Cowork — the collaborative workspace on <a href="https://claude.ai/" target="_blank" rel="noopener">claude.ai</a>. Currently {{ instance_brand }} supports <strong>skills only</strong> here (no data packages or plugins yet).</p>
|
||
<div class="incomplete-callout">
|
||
⚠️ <strong>This section is missing detailed instructions.</strong> Add the step-by-step here:
|
||
<ul>
|
||
<li>How to enable {{ instance_brand }}'s skills in Cowork (URL, sign-in, where to paste config)</li>
|
||
<li>What does and doesn't sync (skills ✅, plugins ❌, data packages ❌, memory ❌)</li>
|
||
<li>When to use Cowork vs the other surfaces</li>
|
||
<li>Known limits / roadmap for plugin + data package support</li>
|
||
</ul>
|
||
</div>
|
||
<div class="when-to-use"><strong>Status:</strong> partial support — skills only. Full instructions to be written.</div>
|
||
</article>
|
||
</div>
|
||
<p class="home-usage-foot">For the deepest integration, create every project under <code>~/Desktop/{{ workspace_dir }}/Projects/</code> — existing or new. The bundled plugin keeps each project in sync with the central catalog automatically, and the session-analysis loop is scoped to that root. Anything outside <code>{{ workspace_dir }}/</code> is invisible to the platform.</p>
|
||
</section>
|
||
|
||
{# Connectors `<details data-section="connectors">` block removed —
|
||
the install-hero's Step 4 clipboard payload (rendered via
|
||
`_claude_setup_instructions.jinja` inside the "Or paste manually"
|
||
fallback) already inlines the same Asana / GWS / Atlassian
|
||
prompts from app/web/connector_prompts.py via
|
||
app/web/setup_instructions.py::_connectors_block. Showing them
|
||
a second time as standalone cards duplicated UX without adding
|
||
reach — the install script visits them all in sequence. Brief
|
||
mention in the install-hero lead paragraph above covers the
|
||
benefits ("third-party tools (Asana, Google Workspace,
|
||
Atlassian)"); deep ops live in /setup-advanced. #}
|
||
|
||
<section class="browse-section" id="look-around">
|
||
<div class="eyebrow">{{ instance_brand }} workspace</div>
|
||
<h2>Explore your workspace</h2>
|
||
<p class="lede">Jump into any part of {{ instance_brand }} — what your team has registered, what's curated company-wide, and what's shared by your colleagues.</p>
|
||
|
||
<div class="browse-grid what-is look-around-grid">
|
||
<a class="browse-card what-is-item" href="/marketplace">
|
||
<span class="browse-icon ico" aria-hidden="true">🧩</span>
|
||
<span class="browse-title ttl">Plugin marketplace <span class="arrow">→</span></span>
|
||
<span class="browse-desc desc">Two tiers: <strong>Curated</strong> (official, vetted — Asana, Jira, GWS, GitHub) and <strong>Flea Market</strong> (team-shared, lower bar — anyone can publish for the team).</span>
|
||
</a>
|
||
<a class="browse-card what-is-item" href="/catalog">
|
||
<span class="browse-icon ico" aria-hidden="true">📦</span>
|
||
<span class="browse-title ttl">Data packages <span class="arrow">→</span></span>
|
||
<span class="browse-desc desc">Tables, schema, metric definitions registered by your team. Subscribe + query.</span>
|
||
</a>
|
||
<a class="browse-card new what-is-item" href="/marketplace?type=skills">
|
||
<span class="browse-icon ico" aria-hidden="true">⚡</span>
|
||
<span class="browse-title ttl">Skills <span class="arrow">→</span></span>
|
||
<span class="browse-desc desc">Two tiers: <strong>Curated</strong> (official workflow templates — brainstorming, planning, review) and <strong>Flea Market</strong> (team-shared skills you and your colleagues publish).</span>
|
||
</a>
|
||
{# Curated Memory is user-facing — the /corporate-memory route runs
|
||
on get_current_user, so the tile shows for everyone, matching
|
||
its primary-nav placement. #}
|
||
<a class="browse-card what-is-item" href="/corporate-memory">
|
||
<span class="browse-icon ico" aria-hidden="true">🧠</span>
|
||
<span class="browse-title ttl">Memory <span class="arrow">→</span></span>
|
||
<span class="browse-desc desc">Shared analyst knowledge and prior solutions. Searchable, versioned.</span>
|
||
</a>
|
||
{# Activity center admin-only — matches the top-nav gating logic. #}
|
||
{% if is_admin %}
|
||
<a class="browse-card what-is-item" href="/activity-center">
|
||
<span class="browse-icon ico" aria-hidden="true">📈</span>
|
||
<span class="browse-title ttl">Activity center <span class="arrow">→</span></span>
|
||
<span class="browse-desc desc">Adoption analytics across your team. Sessions, plugins, prompt patterns.</span>
|
||
</a>
|
||
{% endif %}
|
||
</div>
|
||
</section>
|
||
|
||
{# Legacy `.advanced-pointer` row removed — same link now lives in
|
||
the Getting Started card at the top of /home. `.advanced-pointer`
|
||
CSS (~line 721) is harmless dead style; left in place to keep
|
||
this diff focused. #}
|
||
|
||
{% if news_intro %}
|
||
<section class="home-news">
|
||
<header class="home-news-head">
|
||
<h2>What's new</h2>
|
||
<a class="home-news-more" href="/news">Read more →</a>
|
||
</header>
|
||
<div class="home-news-body">{{ news_intro | safe }}</div>
|
||
</section>
|
||
{% endif %}
|
||
|
||
</div>
|
||
|
||
{# P0-2 — Post-CTA modal. Opens after the shared CTA include below has
|
||
created the token + copied the script. _claude_setup_cta.jinja still
|
||
owns the token request + clipboard write; this modal layers on top
|
||
with a 3-step "where to paste" guide. Lives at body level so the
|
||
home-mock styles don't bleed into it. #}
|
||
{% if not onboarded %}
|
||
<div class="cta-modal-backdrop" id="cta-modal-backdrop" role="dialog" aria-modal="true" aria-labelledby="cta-modal-title" hidden>
|
||
<div class="cta-modal">
|
||
<h2 id="cta-modal-title"><span aria-hidden="true">✅</span> Setup script copied to clipboard</h2>
|
||
<p class="lead">Now paste it into Claude Code on your machine — three steps:</p>
|
||
<ol>
|
||
<li>
|
||
<div>
|
||
<strong>Open a terminal</strong>
|
||
macOS: <kbd>⌘</kbd>+<kbd>Space</kbd>, type "Terminal". Windows: <kbd>Win</kbd>+<kbd>R</kbd>, type <code>powershell</code>. Linux: <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>T</kbd>.
|
||
</div>
|
||
</li>
|
||
<li>
|
||
<div>
|
||
<strong>Start Claude Code</strong>
|
||
Type <code>claude</code> and press <kbd>Enter</kbd>. You'll see a prompt waiting for input.
|
||
</div>
|
||
</li>
|
||
<li>
|
||
<div>
|
||
<strong>Paste & press Enter</strong>
|
||
macOS: <kbd>⌘</kbd>+<kbd>V</kbd>. Windows/Linux: <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>V</kbd>. Claude runs the setup and tells you when it's done.
|
||
</div>
|
||
</li>
|
||
</ol>
|
||
<div class="cta-modal-foot">
|
||
<span class="meta">Token is in clipboard only — never displayed here.</span>
|
||
<button type="button" id="cta-modal-close">Got it</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
{# Shared "Setup a new Claude Code" CTA behaviour — provides the JS that
|
||
POSTs /auth/tokens, copies the rendered instructions to the clipboard,
|
||
and falls back to a modal when the clipboard API is blocked. The button
|
||
above (id="setupClaudeBtn") is the primary trigger. #}
|
||
{% include "_claude_setup_cta.jinja" %}
|
||
|
||
{# VS Code screenshot lightbox — full-bleed overlay; click to close. #}
|
||
<div class="lightbox" id="vscode-lightbox" aria-hidden="true">
|
||
<img src="/static/img/vscode-layout.png" alt="VS Code with terminal-centric layout">
|
||
</div>
|
||
|
||
<script>
|
||
(function () {
|
||
function wireCopy(btn) {
|
||
btn.addEventListener('click', function () {
|
||
var src = document.getElementById(btn.getAttribute('data-copy-target'));
|
||
if (!src) return;
|
||
// textContent (not innerText) so collapsed <details> content still
|
||
// copies — innerText returns "" for nodes whose ancestor has
|
||
// display:none (which is what closed <details> applies).
|
||
var raw = src.textContent || '';
|
||
var text = raw.replace(/ /g, ' ');
|
||
navigator.clipboard.writeText(text).then(function () {
|
||
var orig = btn.textContent;
|
||
btn.textContent = 'Copied';
|
||
setTimeout(function () { btn.textContent = orig; }, 1500);
|
||
}).catch(function () { btn.textContent = 'Copy failed'; });
|
||
});
|
||
}
|
||
document.querySelectorAll('.copy-btn[data-copy-target]').forEach(wireCopy);
|
||
|
||
// OS tab switching for Step 1. Flips both the command panel AND the
|
||
// P0-1 terminal-howto body so the howto matches the active OS.
|
||
document.querySelectorAll('.os-tab[data-os-tab]').forEach(function (tab) {
|
||
tab.addEventListener('click', function () {
|
||
var target = tab.getAttribute('data-os-tab');
|
||
// Scope OS-tab switching to the nearest mode-panel when nested
|
||
// inside one (Step 6's Auto/YOLO panels each have their own
|
||
// OS toggle); otherwise scope to the install-block.
|
||
var scope = tab.closest('[data-mode-panel]') || tab.closest('.install-block') || document;
|
||
scope.querySelectorAll('.os-tab[data-os-tab]').forEach(function (t) {
|
||
var on = t.getAttribute('data-os-tab') === target;
|
||
t.classList.toggle('is-active', on);
|
||
t.setAttribute('aria-selected', on ? 'true' : 'false');
|
||
});
|
||
scope.querySelectorAll('[data-os-panel]').forEach(function (p) {
|
||
if (p.getAttribute('data-os-panel') === target) {
|
||
p.removeAttribute('hidden');
|
||
} else {
|
||
p.setAttribute('hidden', '');
|
||
}
|
||
});
|
||
// P0-1 — flip the howto bodies in lockstep.
|
||
scope.querySelectorAll('[data-howto-panel]').forEach(function (p) {
|
||
if (p.getAttribute('data-howto-panel') === target) {
|
||
p.removeAttribute('hidden');
|
||
} else {
|
||
p.setAttribute('hidden', '');
|
||
}
|
||
});
|
||
});
|
||
});
|
||
|
||
// Mode tabs — Step 6's Auto / YOLO toggle. Mirrors os-tab handler
|
||
// but switches `[data-mode-panel]` siblings.
|
||
document.querySelectorAll('.mode-tab[data-mode-tab]').forEach(function (tab) {
|
||
tab.addEventListener('click', function () {
|
||
var target = tab.getAttribute('data-mode-tab');
|
||
var scope = tab.closest('.install-block') || document;
|
||
scope.querySelectorAll('.mode-tab[data-mode-tab]').forEach(function (t) {
|
||
var on = t.getAttribute('data-mode-tab') === target;
|
||
t.classList.toggle('is-active', on);
|
||
t.setAttribute('aria-selected', on ? 'true' : 'false');
|
||
});
|
||
scope.querySelectorAll('[data-mode-panel]').forEach(function (p) {
|
||
if (p.getAttribute('data-mode-panel') === target) {
|
||
p.removeAttribute('hidden');
|
||
} else {
|
||
p.setAttribute('hidden', '');
|
||
}
|
||
});
|
||
});
|
||
});
|
||
|
||
// P0-2 — Post-CTA modal. _claude_setup_cta.jinja owns the click
|
||
// handler that POSTs /auth/tokens + copies the script; we wait for
|
||
// its success signal (agnes:setup-script-copied custom event) and
|
||
// then open the "where to paste" guide. Without the event the modal
|
||
// simply never opens — the include's own fallback paths handle
|
||
// older browsers / blocked clipboard.
|
||
var ctaModal = document.getElementById('cta-modal-backdrop');
|
||
var ctaModalClose = document.getElementById('cta-modal-close');
|
||
function openCtaModal() {
|
||
if (!ctaModal) return;
|
||
ctaModal.removeAttribute('hidden');
|
||
ctaModal.classList.add('is-open');
|
||
if (ctaModalClose) ctaModalClose.focus();
|
||
}
|
||
function closeCtaModal() {
|
||
if (!ctaModal) return;
|
||
ctaModal.classList.remove('is-open');
|
||
ctaModal.setAttribute('hidden', '');
|
||
var setupBtn = document.getElementById('setupClaudeBtn');
|
||
if (setupBtn) setupBtn.focus();
|
||
}
|
||
if (ctaModal) {
|
||
document.addEventListener('agnes:setup-script-copied', openCtaModal);
|
||
if (ctaModalClose) ctaModalClose.addEventListener('click', closeCtaModal);
|
||
ctaModal.addEventListener('click', function (ev) {
|
||
if (ev.target === ctaModal) closeCtaModal();
|
||
});
|
||
document.addEventListener('keydown', function (ev) {
|
||
if (ev.key === 'Escape' && ctaModal.classList.contains('is-open')) closeCtaModal();
|
||
});
|
||
}
|
||
|
||
// Shared poster for /api/me/onboarded — reused by every UI surface
|
||
// that flips users.onboarded (in-hero X close, "Mark me as onboarded"
|
||
// fallback button, the offboard strip). Reloads on success so the
|
||
// template re-renders with the new state.
|
||
function postOnboarded(triggerBtn, statusEl, targetOnboarded, targetSource) {
|
||
if (triggerBtn) triggerBtn.disabled = true;
|
||
if (statusEl) statusEl.textContent = targetOnboarded ? 'Marking…' : 'Resetting…';
|
||
fetch('/api/me/onboarded', {
|
||
method: 'POST',
|
||
credentials: 'same-origin',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ source: targetSource, onboarded: targetOnboarded }),
|
||
}).then(function (resp) {
|
||
if (resp.ok) {
|
||
if (statusEl) statusEl.textContent = 'Done. Reloading…';
|
||
window.location.reload();
|
||
} else {
|
||
if (statusEl) statusEl.textContent = 'Failed (' + resp.status + '). Try again.';
|
||
if (triggerBtn) triggerBtn.disabled = false;
|
||
}
|
||
}).catch(function () {
|
||
if (statusEl) statusEl.textContent = 'Network error. Try again.';
|
||
if (triggerBtn) triggerBtn.disabled = false;
|
||
});
|
||
}
|
||
|
||
// "Mark me as onboarded" fallback button inside the hero (rendered
|
||
// only when not-onboarded — the X close button is the primary path).
|
||
var btn = document.getElementById('self-mark-btn');
|
||
var status = document.getElementById('self-mark-status');
|
||
if (btn) {
|
||
btn.addEventListener('click', function (e) {
|
||
if (btn.tagName === 'A') e.preventDefault();
|
||
postOnboarded(
|
||
btn, status, true,
|
||
btn.getAttribute('data-target-source') || 'self_acknowledged'
|
||
);
|
||
});
|
||
}
|
||
|
||
// Hero X close — confirm first so a stray click doesn't flip state.
|
||
var heroClose = document.getElementById('installHeroClose');
|
||
if (heroClose) {
|
||
heroClose.addEventListener('click', function () {
|
||
var ok = window.confirm(
|
||
"Are you already onboarded? Closing this will mark your account as onboarded " +
|
||
"and hide the setup steps. You can revert later from the strip below the hero."
|
||
);
|
||
if (!ok) return;
|
||
postOnboarded(
|
||
heroClose, status, true,
|
||
heroClose.getAttribute('data-target-source') || 'self_acknowledged'
|
||
);
|
||
});
|
||
}
|
||
|
||
// Offboarding strip — only rendered when onboarded. Flips back to
|
||
// the install hero on next reload.
|
||
var offBtn = document.getElementById('offboard-btn');
|
||
var offStatus = document.getElementById('offboard-status');
|
||
if (offBtn) {
|
||
offBtn.addEventListener('click', function () {
|
||
postOnboarded(
|
||
offBtn, offStatus, false,
|
||
offBtn.getAttribute('data-target-source') || 'self_unmark'
|
||
);
|
||
});
|
||
}
|
||
|
||
// ── Generic dismiss-card handler ─────────────────────────────────
|
||
// Any `<button class="home-card-close" data-dismiss-key="...">`
|
||
// inside a `<section>` becomes dismissible: clicking sets the
|
||
// localStorage key to '1' and hides the section; on next page
|
||
// load the section starts hidden if the key is set. Used by the
|
||
// Getting Started card; future dismissible cards drop in with
|
||
// zero per-card JS.
|
||
document.querySelectorAll('.home-card-close[data-dismiss-key]').forEach(function (btn) {
|
||
var key = btn.getAttribute('data-dismiss-key');
|
||
var section = btn.closest('section, details');
|
||
if (!section || !key) return;
|
||
try {
|
||
if (localStorage.getItem(key) === '1') {
|
||
section.hidden = true;
|
||
return;
|
||
}
|
||
} catch (e) { /* private-mode: render visible, no-op */ }
|
||
btn.addEventListener('click', function () {
|
||
try { localStorage.setItem(key, '1'); } catch (e) { /* ignore */ }
|
||
section.hidden = true;
|
||
});
|
||
});
|
||
|
||
// ── VS Code screenshot lightbox ──────────────────────────────────
|
||
// Anchor `.vscode-thumb` opens `#vscode-lightbox`. Skips wiring when
|
||
// the screenshot is missing (`.thumb-empty` set by the <img> onerror).
|
||
var vsLightbox = document.getElementById('vscode-lightbox');
|
||
var vsThumb = document.getElementById('vscode-screenshot-trigger');
|
||
if (vsThumb && vsLightbox && !vsThumb.classList.contains('thumb-empty')) {
|
||
vsThumb.addEventListener('click', function (e) {
|
||
e.preventDefault();
|
||
vsLightbox.classList.add('open');
|
||
document.body.style.overflow = 'hidden';
|
||
});
|
||
vsLightbox.addEventListener('click', function () {
|
||
vsLightbox.classList.remove('open');
|
||
document.body.style.overflow = '';
|
||
});
|
||
document.addEventListener('keydown', function (e) {
|
||
if (e.key === 'Escape' && vsLightbox.classList.contains('open')) {
|
||
vsLightbox.classList.remove('open');
|
||
document.body.style.overflow = '';
|
||
}
|
||
});
|
||
}
|
||
})();
|
||
</script>
|
||
{% endblock %}
|