The dedicated `<details data-section="connectors">` section on /home duplicated content that the install hero's Step 4 clipboard payload already inlines. Both surfaces sourced the same prompt strings from `app/web/connector_prompts.py` (home tiles via `<code id="*-prompt">`, setup script via `app/web/setup_instructions.py::_connectors_block`), so users walking the install script visited each connector inline and then had no reason to scroll back up. Removed the full block (3 tiles + summary + section-label). Lead paragraph in the install hero now mentions the connector families briefly so the benefit is visible before kick-off: "... your team's curated data, plugins, third-party tools (Asana, Google Workspace, Atlassian), and shared knowledge ... the install script also connects your tools for you, so there's no extra page to visit." The "Email admin" mailto CTA, previously gated inside the GWS tile when admin_email was set + GWS unconfigured, moves implicitly to the install script's GWS step (Claude prompts the user when the OAuth gating wall lands). Tests updated: - test_connectors_section_removed_from_home (renamed from test_connectors_render_flat_when_onboarded_by_default) — asserts `class="connector-tiles"` and `data-section="connectors"` are absent in BOTH onboarded states, and that the lead paragraph still mentions the three connector families so the benefit isn't lost. - test_home_renders_connector_prompts_from_shared_module — DROPPED. Was a parity check between the home tiles and the setup script's connector_prompts.py source. One surface now → no drift risk → test redundant. Replaced with an inline comment pointing future readers at where the strings flow (setup_instructions.py::_connectors_block). - test_home_no_longer_shows_email_admin_button (renamed from test_home_shows_email_admin_button_when_admin_email_set_and_gws_unconfigured) — asserts the mailto CTA is gone from /home regardless of admin_email / GWS-configured state; documents the path-move. CSS for `.connector-tile*` left in place as dead bytes — small footprint, no behavior, easy follow-up if/when someone audits.
2017 lines
73 KiB
HTML
2017 lines
73 KiB
HTML
{% extends "base.html" %}
|
||
|
||
{% block title %}Setup — {{ instance_name or "AI Data Analyst" }}{% endblock %}
|
||
|
||
{% block content %}
|
||
{% include "_page_chrome.html" %}
|
||
<style>
|
||
.home-mock {
|
||
--hp-primary: #0073D1;
|
||
--hp-primary-dark: #0056A3;
|
||
--hp-primary-light: #E6F3FC;
|
||
--hp-border: #E5E7EB;
|
||
--hp-border-light: #F3F4F6;
|
||
--hp-text-primary: #111827;
|
||
--hp-text-secondary: #6B7280;
|
||
--hp-text-muted: #9CA3AF;
|
||
--hp-orange: #C2410C;
|
||
--hp-orange-light: #FED7AA;
|
||
--hp-font-mono: ui-monospace, "SF Mono", Consolas, monospace;
|
||
color: var(--hp-text-primary);
|
||
font-size: 14px;
|
||
line-height: 1.5;
|
||
}
|
||
.home-mock * { box-sizing: border-box; }
|
||
|
||
.home-mock .install-hero {
|
||
position: relative;
|
||
background: linear-gradient(135deg, #0073D1 0%, #0056A3 100%);
|
||
color: white;
|
||
border-radius: 16px;
|
||
padding: 38px 40px;
|
||
margin-bottom: 22px;
|
||
box-shadow: 0 8px 24px rgba(0, 86, 163, 0.18);
|
||
}
|
||
.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(--hp-border);
|
||
border-radius: 10px;
|
||
background: var(--hp-border-light);
|
||
color: var(--hp-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(--hp-border);
|
||
background: white;
|
||
border-radius: 6px;
|
||
padding: 4px 10px;
|
||
font-size: 13px;
|
||
cursor: pointer;
|
||
}
|
||
.home-mock .offboard-strip button:hover { background: var(--hp-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-getting-started,
|
||
.home-mock .home-overview,
|
||
.home-mock .home-usage {
|
||
position: relative;
|
||
background: white;
|
||
border: 1px solid var(--hp-border);
|
||
border-radius: 12px;
|
||
padding: 22px 24px;
|
||
margin-bottom: 18px;
|
||
box-shadow: 0 1px 3px rgba(15, 23, 42, 0.04);
|
||
}
|
||
/* Getting Started uses <details> for native collapsed-by-default
|
||
behaviour. The summary owns the padding when collapsed; row layout
|
||
reveals on expand. Chevron rotates 90deg when open. */
|
||
.home-mock details.home-getting-started {
|
||
padding: 0;
|
||
}
|
||
.home-mock .home-gs-summary {
|
||
list-style: none;
|
||
cursor: pointer;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
padding: 16px 24px;
|
||
user-select: none;
|
||
}
|
||
.home-mock .home-gs-summary::-webkit-details-marker { display: none; }
|
||
.home-mock .home-gs-summary-title {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
color: var(--hp-text-primary);
|
||
}
|
||
.home-mock .home-gs-summary-hint {
|
||
flex: 1;
|
||
font-size: 13px;
|
||
color: var(--hp-text-secondary);
|
||
}
|
||
.home-mock .home-gs-summary-chev {
|
||
font-size: 18px;
|
||
color: var(--hp-text-muted);
|
||
transition: transform 0.15s;
|
||
}
|
||
.home-mock details.home-getting-started[open] .home-gs-summary-chev {
|
||
transform: rotate(90deg);
|
||
}
|
||
.home-mock details.home-getting-started[open] {
|
||
padding-bottom: 18px;
|
||
}
|
||
.home-mock details.home-getting-started[open] .home-gs-item {
|
||
margin-left: 24px;
|
||
margin-right: 24px;
|
||
}
|
||
/* 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(--hp-text-primary);
|
||
}
|
||
.home-mock .home-usage > header p {
|
||
font-size: 13px;
|
||
color: var(--hp-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(--hp-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(--hp-border-light);
|
||
color: var(--hp-text-primary);
|
||
outline: none;
|
||
}
|
||
|
||
.home-mock .home-gs-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
padding: 12px 14px;
|
||
border: 1px solid var(--hp-border);
|
||
border-radius: 10px;
|
||
text-decoration: none;
|
||
color: inherit;
|
||
margin-top: 8px;
|
||
transition: border-color 0.15s, background 0.15s;
|
||
}
|
||
.home-mock .home-gs-item:hover {
|
||
border-color: var(--hp-primary);
|
||
background: var(--hp-primary-light);
|
||
}
|
||
.home-mock .home-gs-item .ico {
|
||
font-size: 20px;
|
||
flex: 0 0 24px;
|
||
text-align: center;
|
||
}
|
||
.home-mock .home-gs-item .text {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 2px;
|
||
}
|
||
.home-mock .home-gs-item .text strong {
|
||
font-weight: 600;
|
||
color: var(--hp-text-primary);
|
||
}
|
||
.home-mock .home-gs-item .text .desc {
|
||
font-size: 13px;
|
||
color: var(--hp-text-secondary);
|
||
}
|
||
.home-mock .home-gs-item .arrow {
|
||
color: var(--hp-text-muted);
|
||
font-size: 16px;
|
||
}
|
||
|
||
.home-mock .home-overview-body {
|
||
font-size: 14px;
|
||
line-height: 1.6;
|
||
color: var(--hp-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(--hp-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(--hp-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(--hp-primary);
|
||
background: var(--hp-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(--hp-text-secondary);
|
||
line-height: 1.5;
|
||
}
|
||
.home-mock .home-usage-foot {
|
||
font-size: 13px;
|
||
color: var(--hp-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(--hp-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(--hp-primary);
|
||
box-shadow: 0 4px 12px rgba(0, 115, 209, 0.10);
|
||
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(--hp-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(--hp-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(--hp-text-secondary);
|
||
line-height: 1.55;
|
||
}
|
||
|
||
.home-mock .look-around-lead {
|
||
font-size: 13px;
|
||
color: var(--hp-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 .connector-tiles {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||
gap: 16px;
|
||
margin-bottom: 26px;
|
||
}
|
||
.home-mock .connector-tile {
|
||
background: white;
|
||
border: 1px solid var(--hp-border);
|
||
border-radius: 12px;
|
||
padding: 20px 22px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
.home-mock .connector-tile .ico {
|
||
font-size: 24px;
|
||
margin-bottom: 10px;
|
||
line-height: 1;
|
||
}
|
||
.home-mock .connector-tile .ttl {
|
||
font-size: 15px;
|
||
font-weight: 600;
|
||
color: var(--hp-text-primary);
|
||
margin-bottom: 6px;
|
||
}
|
||
.home-mock .connector-tile .desc {
|
||
font-size: 13px;
|
||
color: var(--hp-text-secondary);
|
||
line-height: 1.55;
|
||
margin-bottom: 14px;
|
||
flex: 1;
|
||
}
|
||
.home-mock .connector-tile .connector-actions {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
}
|
||
.home-mock .connector-tile .connector-copy {
|
||
align-self: flex-start;
|
||
}
|
||
|
||
.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(--hp-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;
|
||
}
|
||
.home-mock .install-note code,
|
||
.home-mock .install-note > code {
|
||
background: rgba(255, 255, 255, 0.12);
|
||
padding: 1px 6px;
|
||
border-radius: 4px;
|
||
font-family: var(--hp-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(--hp-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 .automode-card {
|
||
margin: 22px 0;
|
||
background: white;
|
||
border: 1px solid var(--hp-border);
|
||
border-radius: 12px;
|
||
padding: 18px 22px;
|
||
}
|
||
.home-mock .automode-head {
|
||
display: flex;
|
||
gap: 14px;
|
||
align-items: flex-start;
|
||
margin-bottom: 14px;
|
||
}
|
||
.home-mock .automode-head .ico {
|
||
font-size: 22px;
|
||
flex-shrink: 0;
|
||
line-height: 1;
|
||
}
|
||
.home-mock .automode-head h3 {
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
margin-bottom: 4px;
|
||
color: var(--hp-text-primary);
|
||
}
|
||
.home-mock .automode-head p {
|
||
font-size: 13px;
|
||
color: var(--hp-text-secondary);
|
||
line-height: 1.55;
|
||
margin: 0;
|
||
}
|
||
.home-mock .automode-grid {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
gap: 14px;
|
||
}
|
||
.home-mock .automode-step {
|
||
display: flex;
|
||
gap: 10px;
|
||
align-items: flex-start;
|
||
padding: 12px;
|
||
background: var(--hp-border-light);
|
||
border-radius: 8px;
|
||
}
|
||
.home-mock .automode-step .num {
|
||
width: 22px; height: 22px;
|
||
border-radius: 50%;
|
||
background: var(--hp-primary-light);
|
||
color: var(--hp-primary);
|
||
flex-shrink: 0;
|
||
display: flex; align-items: center; justify-content: center;
|
||
font-weight: 700; font-size: 11px;
|
||
}
|
||
.home-mock .automode-step .step-text {
|
||
flex: 1;
|
||
font-size: 12.5px;
|
||
line-height: 1.5;
|
||
color: var(--hp-text-secondary);
|
||
}
|
||
.home-mock .automode-step .step-text strong {
|
||
display: block;
|
||
color: var(--hp-text-primary);
|
||
margin-bottom: 4px;
|
||
font-size: 13px;
|
||
}
|
||
.home-mock .automode-step kbd {
|
||
background: white;
|
||
border: 1px solid var(--hp-border);
|
||
border-bottom-width: 2px;
|
||
border-radius: 4px;
|
||
padding: 1px 6px;
|
||
font-size: 11px;
|
||
font-family: var(--hp-font-mono);
|
||
color: var(--hp-text-primary);
|
||
}
|
||
.home-mock .automode-step code {
|
||
font-family: var(--hp-font-mono);
|
||
font-size: 11.5px;
|
||
background: white;
|
||
padding: 1px 5px;
|
||
border-radius: 3px;
|
||
color: var(--hp-text-primary);
|
||
border: 1px solid var(--hp-border);
|
||
}
|
||
.home-mock .automode-code {
|
||
background: #0F172A;
|
||
color: #FBBF24;
|
||
border-radius: 6px;
|
||
padding: 8px 10px;
|
||
margin-top: 6px;
|
||
font-family: var(--hp-font-mono);
|
||
font-size: 11.5px;
|
||
line-height: 1.5;
|
||
overflow-x: auto;
|
||
white-space: pre;
|
||
}
|
||
.home-mock .automode-foot {
|
||
margin-top: 12px;
|
||
padding-top: 10px;
|
||
border-top: 1px solid var(--hp-border-light);
|
||
font-size: 12px;
|
||
color: var(--hp-text-secondary);
|
||
line-height: 1.5;
|
||
}
|
||
.home-mock .automode-foot a { color: var(--hp-primary); text-decoration: underline; }
|
||
.home-mock .automode-foot code {
|
||
font-family: var(--hp-font-mono);
|
||
font-size: 11px;
|
||
background: var(--hp-border-light);
|
||
padding: 1px 4px;
|
||
border-radius: 3px;
|
||
}
|
||
|
||
.home-mock .section-label {
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.6px;
|
||
color: var(--hp-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(--hp-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(--hp-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(--hp-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(--hp-primary-light);
|
||
color: var(--hp-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,
|
||
.home-mock .card-mini-cmd code {
|
||
font-family: var(--hp-font-mono);
|
||
font-size: 11.5px;
|
||
background: var(--hp-border-light);
|
||
padding: 1px 5px;
|
||
border-radius: 3px;
|
||
color: var(--hp-text-primary);
|
||
}
|
||
|
||
.home-mock .card-mini-cmd {
|
||
margin-top: 6px;
|
||
padding: 8px 10px;
|
||
background: #0F172A;
|
||
color: #FBBF24;
|
||
border-radius: 6px;
|
||
font-family: var(--hp-font-mono);
|
||
font-size: 11.5px;
|
||
line-height: 1.5;
|
||
display: flex;
|
||
align-items: flex-start;
|
||
justify-content: space-between;
|
||
gap: 8px;
|
||
}
|
||
.home-mock .card-mini-cmd code {
|
||
background: transparent;
|
||
color: inherit;
|
||
padding: 0;
|
||
flex: 1;
|
||
white-space: pre-wrap;
|
||
word-break: break-word;
|
||
}
|
||
.home-mock .connector-head {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 12px;
|
||
}
|
||
.home-mock .connector-head strong {
|
||
display: inline;
|
||
margin-bottom: 0;
|
||
}
|
||
.home-mock .connector-copy {
|
||
background: var(--hp-primary);
|
||
color: white;
|
||
border: none;
|
||
border-radius: 6px;
|
||
padding: 5px 12px;
|
||
font-size: 12px;
|
||
font-weight: 600;
|
||
cursor: pointer;
|
||
font-family: inherit;
|
||
flex-shrink: 0;
|
||
}
|
||
.home-mock .connector-copy:hover { background: var(--hp-primary-dark); }
|
||
.home-mock .connector-copy.copied { background: #047857; }
|
||
.home-mock details.connector-preview {
|
||
margin-top: 6px;
|
||
}
|
||
.home-mock details.connector-preview > summary {
|
||
cursor: pointer;
|
||
list-style: none;
|
||
font-size: 11.5px;
|
||
color: var(--hp-text-secondary);
|
||
padding: 2px 0;
|
||
user-select: none;
|
||
}
|
||
.home-mock details.connector-preview > summary::-webkit-details-marker { display: none; }
|
||
.home-mock details.connector-preview > summary::before {
|
||
content: "▸ ";
|
||
margin-right: 2px;
|
||
}
|
||
.home-mock details.connector-preview[open] > summary::before { content: "▾ "; }
|
||
.home-mock details.connector-preview > summary:hover { color: var(--hp-text-primary); }
|
||
.home-mock details.connector-preview .card-mini-cmd { margin-top: 6px; }
|
||
|
||
.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(--hp-border-light);
|
||
border: 1px solid transparent;
|
||
border-radius: 8px;
|
||
text-decoration: none;
|
||
color: var(--hp-text-primary);
|
||
transition: border-color 0.15s ease, background 0.15s ease;
|
||
}
|
||
.home-mock .explore-item:hover {
|
||
border-color: var(--hp-primary);
|
||
background: var(--hp-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(--hp-text-secondary);
|
||
line-height: 1.45;
|
||
}
|
||
.home-mock .explore-item .explore-text strong {
|
||
display: block;
|
||
color: var(--hp-text-primary);
|
||
font-size: 13.5px;
|
||
font-weight: 600;
|
||
margin-bottom: 2px;
|
||
}
|
||
.home-mock .explore-item .arrow {
|
||
color: var(--hp-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(--hp-border);
|
||
border-left: 4px solid var(--hp-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(--hp-primary-light);
|
||
border-color: var(--hp-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(--hp-text-secondary);
|
||
line-height: 1.55;
|
||
}
|
||
.home-mock .advanced-pointer-text strong {
|
||
display: block;
|
||
color: var(--hp-text-primary);
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
margin-bottom: 2px;
|
||
}
|
||
.home-mock .advanced-pointer .arrow {
|
||
color: var(--hp-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(--hp-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(--hp-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: #F0F7FF; }
|
||
.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(--hp-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(--hp-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(--hp-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; }
|
||
|
||
/* Setup-collapsible: each post-install section (Step 3 auto-mode,
|
||
Connect-your-tools) is wrapped in a <details open> so it keyboard-
|
||
collapses without JS. Default state on /home: summary hidden, body
|
||
flat — visually identical to the pre-collapse layout.
|
||
|
||
When the user clicks "Minimize setup view" in the hero, JS sets
|
||
`data-setup-minimized="1"` on `.home-mock` and removes the `open`
|
||
attribute on each <details>. The summary then appears as a slim
|
||
gray bar; the body collapses; clicking the bar re-opens that one
|
||
section. localStorage persists the minimize state per device. */
|
||
.home-mock .setup-collapsible { margin-top: 22px; }
|
||
.home-mock .setup-collapsible > summary {
|
||
/* Default: summary is structural-only. The "flat" rendering is
|
||
handled by the inner blocks (.automode-card already provides its
|
||
own padding/border/background; the connect-your-tools section
|
||
starts with a .section-label-flat). */
|
||
display: none;
|
||
}
|
||
.home-mock .setup-collapsible > summary::-webkit-details-marker { display: none; }
|
||
|
||
/* Minimize ON: summary becomes a slim clickable bar; the inner body
|
||
inherits the existing block styling but tucks under the summary
|
||
when the <details> is closed. */
|
||
.home-mock[data-setup-minimized="1"] .setup-collapsible {
|
||
border: 1px solid var(--hp-border);
|
||
border-radius: 12px;
|
||
background: white;
|
||
overflow: hidden;
|
||
}
|
||
.home-mock[data-setup-minimized="1"] .setup-collapsible > summary {
|
||
list-style: none;
|
||
cursor: pointer;
|
||
padding: 14px 22px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
color: var(--hp-text-primary);
|
||
background: #F9FAFB;
|
||
border-bottom: 1px solid transparent;
|
||
transition: background 120ms ease;
|
||
user-select: none;
|
||
}
|
||
.home-mock[data-setup-minimized="1"] .setup-collapsible > summary:hover {
|
||
background: #F3F4F6;
|
||
}
|
||
.home-mock[data-setup-minimized="1"] .setup-collapsible > summary .ico {
|
||
font-size: 18px;
|
||
line-height: 1;
|
||
}
|
||
.home-mock[data-setup-minimized="1"] .setup-collapsible > summary .ttl { flex: 1; }
|
||
.home-mock[data-setup-minimized="1"] .setup-collapsible > summary .ttl small {
|
||
color: var(--hp-text-secondary);
|
||
font-weight: 400;
|
||
margin-left: 6px;
|
||
}
|
||
.home-mock[data-setup-minimized="1"] .setup-collapsible > summary .chev {
|
||
font-size: 18px;
|
||
color: var(--hp-text-secondary);
|
||
transition: transform 160ms ease;
|
||
}
|
||
.home-mock[data-setup-minimized="1"] .setup-collapsible[open] > summary {
|
||
border-bottom-color: var(--hp-border);
|
||
background: #F3F4F6;
|
||
}
|
||
.home-mock[data-setup-minimized="1"] .setup-collapsible[open] > summary .chev {
|
||
transform: rotate(90deg);
|
||
}
|
||
/* Tighten the body padding so collapsed sections don't gain margin
|
||
when inside the white wrapper card. */
|
||
.home-mock[data-setup-minimized="1"] .setup-collapsible[open] > .automode-card,
|
||
.home-mock[data-setup-minimized="1"] .setup-collapsible[open] > .connector-tiles {
|
||
margin: 18px 22px 22px;
|
||
}
|
||
.home-mock[data-setup-minimized="1"] .setup-collapsible[open] > .section-label-flat {
|
||
padding: 14px 22px 0;
|
||
}
|
||
/* When a section's <details> is closed in minimize mode, the body
|
||
already auto-hides via the browser's <details> behaviour — no extra
|
||
rule needed. */
|
||
|
||
/* Minimize toggle in the blue hero, only present when onboarded. */
|
||
.home-mock .setup-minimize {
|
||
margin-top: 14px;
|
||
font-size: 13px;
|
||
color: rgba(255, 255, 255, 0.85);
|
||
}
|
||
.home-mock .setup-minimize button {
|
||
background: rgba(255, 255, 255, 0.10);
|
||
color: white;
|
||
border: 1px solid rgba(255, 255, 255, 0.25);
|
||
padding: 6px 14px;
|
||
border-radius: 6px;
|
||
font-size: 12px;
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
}
|
||
.home-mock .setup-minimize button:hover { background: rgba(255, 255, 255, 0.18); }
|
||
|
||
/* 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(--hp-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(--hp-font-mono);
|
||
color: white;
|
||
}
|
||
|
||
/* P0-3 — connector card time badge ("~5 min · self-serve") and the
|
||
post-copy "now paste into Step 2" hint with arrow back. */
|
||
.home-mock .connector-tile .ttl-row {
|
||
display: flex; align-items: center; justify-content: space-between; gap: 8px;
|
||
margin-bottom: 6px;
|
||
}
|
||
.home-mock .time-badge {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
padding: 2px 8px;
|
||
border-radius: 999px;
|
||
background: var(--hp-primary-light);
|
||
color: var(--hp-primary-dark);
|
||
flex-shrink: 0;
|
||
}
|
||
.home-mock .time-badge.is-warn {
|
||
background: #FEF3C7;
|
||
color: #92400E;
|
||
}
|
||
.home-mock .copy-next-hint {
|
||
font-size: 12px;
|
||
color: var(--hp-primary-dark);
|
||
background: var(--hp-primary-light);
|
||
padding: 6px 10px;
|
||
border-radius: 6px;
|
||
margin-top: 6px;
|
||
display: none;
|
||
align-items: center;
|
||
gap: 6px;
|
||
line-height: 1.45;
|
||
}
|
||
.home-mock .copy-next-hint.is-visible { display: inline-flex; }
|
||
.home-mock .copy-next-hint code {
|
||
background: rgba(0, 0, 0, 0.06);
|
||
padding: 1px 5px;
|
||
border-radius: 3px;
|
||
font-family: var(--hp-font-mono);
|
||
font-size: 11px;
|
||
}
|
||
|
||
/* P1-8 — GWS gating note + Email-admin button. Yellow background
|
||
matches the same friction-level used by other "admin help likely"
|
||
surfaces (advanced setup warnings). */
|
||
.home-mock .gating-note {
|
||
font-size: 12px;
|
||
color: #92400E;
|
||
background: #FEF3C7;
|
||
border: 1px solid #FCD34D;
|
||
border-radius: 6px;
|
||
padding: 8px 10px;
|
||
margin-bottom: 10px;
|
||
line-height: 1.5;
|
||
}
|
||
.home-mock .email-admin {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
background: white;
|
||
border: 1px solid var(--hp-border);
|
||
color: var(--hp-text-primary);
|
||
padding: 5px 12px;
|
||
border-radius: 6px;
|
||
font-size: 12px;
|
||
font-weight: 500;
|
||
text-decoration: none;
|
||
cursor: pointer;
|
||
align-self: flex-start;
|
||
margin-top: 6px;
|
||
}
|
||
.home-mock .email-admin:hover {
|
||
text-decoration: none;
|
||
border-color: var(--hp-primary);
|
||
color: var(--hp-primary-dark);
|
||
}
|
||
|
||
/* 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(--hp-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: #0073D1;
|
||
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(--hp-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: #0073D1;
|
||
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: #0056A3; }
|
||
</style>
|
||
|
||
<div class="home-mock">
|
||
|
||
{# 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 %}
|
||
{# Getting Started renders FIRST in the not-onboarded flow as a
|
||
collapsed-by-default <details>. Click the summary to expand the
|
||
two-row map. First row anchors back to the install hero just
|
||
below (#install-hero); second row leaves the page for
|
||
/setup-advanced. Per-device dismiss X (data-dismiss-key) survives
|
||
on the generic .home-card-close handler — selector widened in JS
|
||
to accept <details> containers too. Disappears post-onboarding
|
||
alongside the hero so the in-page anchor never dangles. #}
|
||
<details class="home-getting-started" id="homeGettingStarted">
|
||
<summary class="home-gs-summary">
|
||
<span class="home-gs-summary-title">Getting Started</span>
|
||
<span class="home-gs-summary-hint">Two quick next steps — click to expand</span>
|
||
<span class="home-gs-summary-chev" aria-hidden="true">›</span>
|
||
</summary>
|
||
<button type="button" class="home-card-close"
|
||
data-dismiss-key="agnes_home_gs_dismissed"
|
||
aria-label="Dismiss Getting Started">×</button>
|
||
<a class="home-gs-item" href="#install-hero">
|
||
<span class="ico" aria-hidden="true">🚀</span>
|
||
<div class="text">
|
||
<strong>Setup {{ instance_brand }} in your Claude Code</strong>
|
||
<span class="desc">One-time install — walkthrough in the section below.</span>
|
||
</div>
|
||
<span class="arrow" aria-hidden="true">↓</span>
|
||
</a>
|
||
<a class="home-gs-item" href="/setup-advanced">
|
||
<span class="ico" aria-hidden="true">🛠️</span>
|
||
<div class="text">
|
||
<strong>Go deeper into your AI workspace</strong>
|
||
<span class="desc">VS Code layout, recommended plugins, multi-model second opinions, custom skills + rules + hooks, project workflows.</span>
|
||
</div>
|
||
<span class="arrow" aria-hidden="true">→</span>
|
||
</a>
|
||
</details>
|
||
|
||
<div class="install-hero" id="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>
|
||
<div class="eyebrow">Welcome, {{ display_name }} — let's get you set up</div>
|
||
<h1>Connect Claude Code on your machine to your team's data</h1>
|
||
<p class="lead">
|
||
{{ instance_brand }} gives <strong>Claude Code</strong> on your computer access to your team's <strong>curated data, plugins, third-party tools (Asana, Google Workspace, Atlassian), and shared knowledge</strong> — so you can ask questions and get answers in plain language, right from your terminal. This page walks you through the <strong>one-time setup (~10 minutes)</strong>; the install script also connects your tools for you, so there's no extra page to visit. Everything it installs lives in your home folder (<code style="background: rgba(255,255,255,0.12); padding: 1px 6px; border-radius: 4px; font-family: var(--hp-font-mono); font-size: 12.5px;">~/{{ workspace_dir }}</code>) and can be removed in one command.
|
||
</p>
|
||
|
||
<div class="install-block">
|
||
<div class="label">Step 1 — install Claude Code</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 / WSL</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>
|
||
|
||
{# P0-1 — terminal-howto disclosure. Two panels (unix / windows)
|
||
that flip in lockstep with the install-cmd OS tabs above. #}
|
||
<details class="terminal-howto">
|
||
<summary>Don't have a terminal open? — show how</summary>
|
||
<div class="terminal-howto-body" data-howto-panel="unix">
|
||
<p><strong>macOS:</strong> press <kbd>⌘</kbd> + <kbd>Space</kbd>, type <kbd>Terminal</kbd>, press <kbd>Enter</kbd>.</p>
|
||
<p><strong>Linux:</strong> press <kbd>Ctrl</kbd> + <kbd>Alt</kbd> + <kbd>T</kbd> (most distros), or open the apps menu and search "Terminal".</p>
|
||
<p><strong>WSL:</strong> open the Windows Start menu, search "Ubuntu" (or your installed distro), press <kbd>Enter</kbd>.</p>
|
||
<p>Paste the command above with <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>V</kbd> (Linux/WSL) or <kbd>⌘</kbd> + <kbd>V</kbd> (macOS), then press <kbd>Enter</kbd>.</p>
|
||
</div>
|
||
<div class="terminal-howto-body" data-howto-panel="windows" hidden>
|
||
<p><strong>Windows:</strong> press <kbd>Win</kbd> + <kbd>R</kbd>, type <kbd>powershell</kbd>, press <kbd>Enter</kbd>. (Or: open Start menu, type "PowerShell".)</p>
|
||
<p>Paste the command above with <kbd>Ctrl</kbd> + <kbd>V</kbd> and press <kbd>Enter</kbd>.</p>
|
||
</div>
|
||
</details>
|
||
|
||
<div class="install-note">
|
||
Verify with <code>claude --version</code>. Sign in once with <code>claude</code> and complete the OAuth flow.
|
||
Don't have a Claude license yet? See <a href="/setup-advanced#claude-plan">plan options on /setup-advanced</a> (Pro / Max 5× / Max 20×).
|
||
</div>
|
||
</div>
|
||
|
||
{% if home_automode.show %}
|
||
<div class="install-block">
|
||
<div class="label">Step 2 — turn on auto-mode (recommended before Step 4)</div>
|
||
<div class="install-note">
|
||
In the Claude Code session you just signed into, press <strong>Shift + Tab</strong>. Claude cycles modes: default → <strong>auto-accept edits</strong> → plan mode → default; the footer shows <code>⏵⏵</code> when auto-accept is on. On the first cycle to auto-accept, Claude asks whether to make it the default — say <strong>yes</strong>. Closed the session already? Run <code>claude</code> again, then press <strong>Shift + Tab</strong>.
|
||
<br><br>
|
||
Want full auto-approve including Bash? See <a href="/setup-advanced#yolo">YOLO mode</a> on /setup-advanced — pairs <code>--dangerously-skip-permissions</code> with a reviewed <code>~/.claude/settings.local.json</code> allowlist. Skip if you're not sure.
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
<div class="install-block">
|
||
<div class="label">Step 3 — create your workspace folder</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 / WSL</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 ~/{{ workspace_dir }} && cd ~/{{ 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\{{ workspace_dir }}" | Out-Null
|
||
Set-Location "$HOME\{{ workspace_dir }}"</span>
|
||
<button class="copy-btn" data-copy-target="install-cmd-mkdir-windows">Copy</button>
|
||
</div>
|
||
<div class="install-note">
|
||
This is where {{ instance_brand }} will live. Run the command in the terminal you opened for Step 1, then keep that terminal handy — the next step pastes the setup script into Claude Code from this same directory.
|
||
</div>
|
||
</div>
|
||
|
||
<div class="install-block">
|
||
<div class="label">Step 4 — install {{ instance_brand }} from inside Claude Code</div>
|
||
<p class="setup-cta-lead">
|
||
Click the button — {{ instance_brand }} <strong>creates a 90-day login token</strong>, copies a ready-to-paste setup script to your clipboard, and a follow-up popup tells you exactly where to paste it. The script bootstraps everything in <code>~/{{ workspace_dir }}</code> once Claude Code receives it.
|
||
</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>
|
||
Setup a new Claude Code
|
||
</button>
|
||
<span class="setup-cta-meta">
|
||
<span class="setup-cta-hint">Valid 90 days · token stays in clipboard only</span>
|
||
</span>
|
||
</div>
|
||
<div id="setupClaudeError" class="setup-error" role="alert" style="display:none;"></div>
|
||
|
||
<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>~/{{ workspace_dir }}/Projects/</code> manually — the bundled plugin offers to set it up after install.
|
||
</div>
|
||
</details>
|
||
|
||
</div>
|
||
|
||
{# P1-6 — auto-detect badge is the PRIMARY affordance after the
|
||
install-script copy: agnes-init's first POST to
|
||
/api/me/onboarded flips state automatically and the page
|
||
reloads. The manual "Mark me as onboarded" button below it
|
||
stays as a fallback when auto-flip never lands. #}
|
||
<div class="auto-detect-badge" role="status" aria-live="polite">
|
||
<span class="pulse" aria-hidden="true"></span>
|
||
<span>Waiting for your first <code>agnes pull</code> — auto-detects within ~30 s of the setup script finishing.</span>
|
||
</div>
|
||
|
||
{# Self-mark fallback for the auto-flip. The hero's X close button
|
||
does the same thing more visibly; both target the not-onboarded
|
||
→ onboarded direction. The onboarded → offboarded variant lives
|
||
below the hero (.offboard-strip) so it stays reachable once the
|
||
hero is gone. #}
|
||
<div class="self-mark">
|
||
Already set this up?
|
||
<button id="self-mark-btn" type="button"
|
||
data-target-onboarded="true"
|
||
data-target-source="self_acknowledged">Mark me as onboarded</button>
|
||
<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 ~/FoundryAI) 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. #}
|
||
|
||
{# 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 %}
|
||
|
||
{# Usage modes — Terminal / VS Code / Claude Desktop · claude.ai.
|
||
Generic OSS-shipped copy linking to /setup and to /setup-advanced
|
||
anchors. Helps onboarded users find the right surface once they
|
||
know which workflow fits them. #}
|
||
<section class="home-usage">
|
||
<header>
|
||
<h2>Where you can use {{ instance_brand }}</h2>
|
||
<p>Same workspace, three surfaces. Pick whichever fits your flow — all three share the same plugins, data access, and credentials.</p>
|
||
</header>
|
||
<div class="home-usage-grid">
|
||
{# Terminal tile is informational — the setup hero above already
|
||
walks through the Claude Code CLI install, so a click-through
|
||
here would round-trip back to content the user just scrolled
|
||
past. Rendered as a non-anchor div; hover/cursor styles only
|
||
apply to the anchor variants below. #}
|
||
<div class="home-usage-item">
|
||
<span class="ico" aria-hidden="true">⌨️</span>
|
||
<strong>Terminal</strong>
|
||
<span>Default. Run <code>claude</code> from any project under <code>~/{{ workspace_dir }}</code>. Lowest overhead, fastest feedback loop.</span>
|
||
</div>
|
||
<a class="home-usage-item" href="/setup-advanced#vscode">
|
||
<span class="ico" aria-hidden="true">📑</span>
|
||
<strong>VS Code</strong>
|
||
<span>Split-terminal layout — conversation on one side, diffs and tool output on the other. See <em>Go deeper</em> for the recommended layout.</span>
|
||
</a>
|
||
<a class="home-usage-item" href="/setup-advanced#claude-app">
|
||
<span class="ico" aria-hidden="true">💻</span>
|
||
<strong>Claude Desktop / claude.ai</strong>
|
||
<span>Connect this {{ instance_brand }} instance's plugin marketplace to your Claude Desktop or claude.ai account — same plugins, no terminal required.</span>
|
||
</a>
|
||
</div>
|
||
<p class="home-usage-foot">For the deepest integration, create every project under <code>~/{{ 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.</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. #}
|
||
|
||
<div class="section-label">Want to look around first?</div>
|
||
<p class="look-around-lead">You don't need {{ instance_brand }} installed locally to browse what's available. Anything you bookmark or subscribe to will be there waiting after you set {{ instance_brand }} up.</p>
|
||
|
||
<div class="what-is look-around-grid">
|
||
<a class="what-is-item" href="/marketplace">
|
||
<span class="ico">🧩</span>
|
||
<div class="ttl">Plugin marketplace <span class="arrow">→</span></div>
|
||
<div class="desc">Curated and community-built plugins for Claude Code — Asana, Jira, Google Workspace, and more. Browse, install, sync.</div>
|
||
</a>
|
||
<a class="what-is-item" href="/catalog">
|
||
<span class="ico">📦</span>
|
||
<div class="ttl">Curated data packages <span class="arrow">→</span></div>
|
||
<div class="desc">Tables, schema, and metric definitions your team has registered. Subscribe and Claude can query them with documented business rules.</div>
|
||
</a>
|
||
{# Corporate memory + Activity center are both admin-only — matches
|
||
the top-nav gating logic (the "Memory" link is hidden for
|
||
non-admin in _app_header.html, and the /corporate-memory route
|
||
is `require_admin`). Without this gate non-admin users would
|
||
see a Corporate memory tile that 403s when clicked. #}
|
||
{% if is_admin %}
|
||
<a class="what-is-item" href="/corporate-memory">
|
||
<span class="ico">🧠</span>
|
||
<div class="ttl">Corporate memory <span class="arrow">→</span></div>
|
||
<div class="desc">Shared analyst knowledge and prior solutions to draw from. Searchable, versioned, fed back into Claude's context on demand.</div>
|
||
</a>
|
||
<a class="what-is-item" href="/activity-center">
|
||
<span class="ico">📈</span>
|
||
<div class="ttl">Activity center <span class="arrow">→</span></div>
|
||
<div class="desc">Per-user analytics on {{ instance_brand }} adoption across your team. Sessions, plugin installs, prompt patterns.</div>
|
||
</a>
|
||
{% endif %}
|
||
</div>
|
||
|
||
{# 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" %}
|
||
|
||
<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);
|
||
|
||
// P0-3 + P2-12 — Connector copy buttons. Three things on click:
|
||
// 1) copy the prompt (same textContent trick so closed <details>
|
||
// still copies);
|
||
// 2) flip the button to "✓ Copied" + disabled for 8 s so the user
|
||
// stops mashing it while figuring out where to paste;
|
||
// 3) reveal a paste hint pointing the user at Claude Code.
|
||
document.querySelectorAll('.connector-copy[data-copy-target]').forEach(function (btn) {
|
||
btn.addEventListener('click', function () {
|
||
var src = document.getElementById(btn.getAttribute('data-copy-target'));
|
||
if (!src) return;
|
||
var raw = src.textContent || '';
|
||
var connector = btn.getAttribute('data-connector') || '';
|
||
var hint = connector
|
||
? document.querySelector('.copy-next-hint[data-hint-for="' + connector + '"]')
|
||
: null;
|
||
navigator.clipboard.writeText(raw).then(function () {
|
||
var orig = btn.textContent;
|
||
btn.classList.add('copied');
|
||
btn.textContent = '✓ Copied';
|
||
btn.disabled = true;
|
||
if (hint) hint.classList.add('is-visible');
|
||
setTimeout(function () {
|
||
btn.classList.remove('copied');
|
||
btn.textContent = orig;
|
||
btn.disabled = false;
|
||
if (hint) hint.classList.remove('is-visible');
|
||
}, 8000);
|
||
}).catch(function () { btn.textContent = 'Copy failed'; });
|
||
});
|
||
});
|
||
|
||
// 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');
|
||
var scope = 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', '');
|
||
}
|
||
});
|
||
});
|
||
});
|
||
|
||
// 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 () {
|
||
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'
|
||
);
|
||
});
|
||
}
|
||
|
||
// ── Minimize-setup toggle ────────────────────────────────────────
|
||
// Default OFF: sections render flat (no <summary> visible).
|
||
// ON: data-setup-minimized="1" on .home-mock activates the slim
|
||
// gray summary bars; <details> open/close handles per-section
|
||
// expansion. Per-device via localStorage; survives reloads but
|
||
// resets on a new machine — which is correct (the user might
|
||
// want the full reference there).
|
||
var KEY = 'agnes_home_setup_minimized';
|
||
var mockEl = document.querySelector('.home-mock');
|
||
var minToggle = document.getElementById('setupMinimizeToggle');
|
||
var collapsibles = document.querySelectorAll('.setup-collapsible');
|
||
|
||
function applyMinimize(on) {
|
||
if (!mockEl) return;
|
||
if (on) {
|
||
mockEl.setAttribute('data-setup-minimized', '1');
|
||
collapsibles.forEach(function (d) { d.removeAttribute('open'); });
|
||
if (minToggle) {
|
||
minToggle.textContent = 'Show full setup view';
|
||
minToggle.setAttribute('aria-pressed', 'true');
|
||
}
|
||
} else {
|
||
mockEl.removeAttribute('data-setup-minimized');
|
||
collapsibles.forEach(function (d) { d.setAttribute('open', ''); });
|
||
if (minToggle) {
|
||
minToggle.textContent = 'Minimize setup view';
|
||
minToggle.setAttribute('aria-pressed', 'false');
|
||
}
|
||
}
|
||
}
|
||
|
||
// Initial state — only when the toggle exists (= onboarded view).
|
||
if (minToggle) {
|
||
try { applyMinimize(localStorage.getItem(KEY) === '1'); }
|
||
catch (e) { /* ignore localStorage failures (private mode) */ }
|
||
minToggle.addEventListener('click', function () {
|
||
var nowOn = mockEl.getAttribute('data-setup-minimized') !== '1';
|
||
try { localStorage.setItem(KEY, nowOn ? '1' : '0'); } catch (e) {}
|
||
applyMinimize(nowOn);
|
||
});
|
||
}
|
||
|
||
// ── 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;
|
||
});
|
||
});
|
||
})();
|
||
</script>
|
||
{% endblock %}
|