summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--packages/console/app/src/asset/lander/brand-assets-dark.svg10
-rw-r--r--packages/console/app/src/asset/lander/brand-assets-light.svg10
-rw-r--r--packages/console/app/src/asset/lander/brand.pngbin0 -> 72109 bytes
-rw-r--r--packages/console/app/src/asset/lander/logo-dark.svg11
-rw-r--r--packages/console/app/src/asset/lander/logo-light.svg11
-rw-r--r--packages/console/app/src/asset/lander/wordmark-dark.svg3
-rw-r--r--packages/console/app/src/asset/lander/wordmark-light.svg3
-rw-r--r--packages/console/app/src/component/header-context-menu.css55
-rw-r--r--packages/console/app/src/component/header.tsx125
-rw-r--r--packages/console/app/src/routes/brand/index.css443
-rw-r--r--packages/console/app/src/routes/brand/index.tsx100
11 files changed, 766 insertions, 5 deletions
diff --git a/packages/console/app/src/asset/lander/brand-assets-dark.svg b/packages/console/app/src/asset/lander/brand-assets-dark.svg
new file mode 100644
index 000000000..196ed8de2
--- /dev/null
+++ b/packages/console/app/src/asset/lander/brand-assets-dark.svg
@@ -0,0 +1,10 @@
+<svg width="22" height="26" viewBox="0 0 22 26" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M15 7H7V19H15V7ZM19 23H3V3H19V23Z" fill="url(#paint0_linear_1311_86681)" stroke="#F1ECEC"/>
+<path d="M3 0V26M19 0V26M15 0V26M7 0V26M0 3H22M0 7H22M0 19H22M0 23H22" stroke="#4B4646" stroke-opacity="0.4"/>
+<defs>
+<linearGradient id="paint0_linear_1311_86681" x1="11" y1="3" x2="11" y2="23" gradientUnits="userSpaceOnUse">
+<stop stop-color="#1B1818"/>
+<stop offset="1" stop-color="#2D2828"/>
+</linearGradient>
+</defs>
+</svg>
diff --git a/packages/console/app/src/asset/lander/brand-assets-light.svg b/packages/console/app/src/asset/lander/brand-assets-light.svg
new file mode 100644
index 000000000..3f898f2a0
--- /dev/null
+++ b/packages/console/app/src/asset/lander/brand-assets-light.svg
@@ -0,0 +1,10 @@
+<svg width="22" height="26" viewBox="0 0 22 26" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M15 7H7V19H15V7ZM19 23H3V3H19V23Z" fill="url(#paint0_linear_1311_86671)" stroke="#8E8B8B"/>
+<path d="M3 0V26M19 0V26M15 0V26M7 0V26M0 3H22M0 7H22M0 19H22M0 23H22" stroke="#110000" stroke-opacity="0.121569"/>
+<defs>
+<linearGradient id="paint0_linear_1311_86671" x1="11" y1="3" x2="11" y2="23" gradientUnits="userSpaceOnUse">
+<stop stop-color="#F9F8F8"/>
+<stop offset="1" stop-color="#E9E8E8"/>
+</linearGradient>
+</defs>
+</svg>
diff --git a/packages/console/app/src/asset/lander/brand.png b/packages/console/app/src/asset/lander/brand.png
new file mode 100644
index 000000000..6172775e5
--- /dev/null
+++ b/packages/console/app/src/asset/lander/brand.png
Binary files differ
diff --git a/packages/console/app/src/asset/lander/logo-dark.svg b/packages/console/app/src/asset/lander/logo-dark.svg
new file mode 100644
index 000000000..8a2f7ebbb
--- /dev/null
+++ b/packages/console/app/src/asset/lander/logo-dark.svg
@@ -0,0 +1,11 @@
+<svg width="22" height="26" viewBox="0 0 22 26" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_1311_86675)">
+<path d="M15 19H7V11H15V19Z" fill="#4B4646"/>
+<path d="M15 7H7V19H15V7ZM19 23H3V3H19V23Z" fill="#F1ECEC"/>
+</g>
+<defs>
+<clipPath id="clip0_1311_86675">
+<rect width="16" height="20" fill="white" transform="translate(3 3)"/>
+</clipPath>
+</defs>
+</svg>
diff --git a/packages/console/app/src/asset/lander/logo-light.svg b/packages/console/app/src/asset/lander/logo-light.svg
new file mode 100644
index 000000000..54f90aeda
--- /dev/null
+++ b/packages/console/app/src/asset/lander/logo-light.svg
@@ -0,0 +1,11 @@
+<svg width="22" height="26" viewBox="0 0 22 26" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_1311_86665)">
+<path d="M15 19H7V11H15V19Z" fill="#BCBBBB"/>
+<path d="M15 7H7V19H15V7ZM19 23H3V3H19V23Z" fill="#211E1E"/>
+</g>
+<defs>
+<clipPath id="clip0_1311_86665">
+<rect width="16" height="20" fill="white" transform="translate(3 3)"/>
+</clipPath>
+</defs>
+</svg>
diff --git a/packages/console/app/src/asset/lander/wordmark-dark.svg b/packages/console/app/src/asset/lander/wordmark-dark.svg
new file mode 100644
index 000000000..42f8e22a6
--- /dev/null
+++ b/packages/console/app/src/asset/lander/wordmark-dark.svg
@@ -0,0 +1,3 @@
+<svg width="22" height="26" viewBox="0 0 22 26" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M4.33203 7.99967V6.33301H10.9987M17.6654 7.99967V6.33301H10.9987M10.9987 6.33301V19.6663M10.9987 19.6663H9.33203M10.9987 19.6663H12.6654" stroke="#F1ECEC" stroke-width="2" stroke-linecap="square"/>
+</svg>
diff --git a/packages/console/app/src/asset/lander/wordmark-light.svg b/packages/console/app/src/asset/lander/wordmark-light.svg
new file mode 100644
index 000000000..398278da6
--- /dev/null
+++ b/packages/console/app/src/asset/lander/wordmark-light.svg
@@ -0,0 +1,3 @@
+<svg width="22" height="26" viewBox="0 0 22 26" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M4.33203 7.99967V6.33301H10.9987M17.6654 7.99967V6.33301H10.9987M10.9987 6.33301V19.6663M10.9987 19.6663H9.33203M10.9987 19.6663H12.6654" stroke="black" stroke-width="2" stroke-linecap="square"/>
+</svg>
diff --git a/packages/console/app/src/component/header-context-menu.css b/packages/console/app/src/component/header-context-menu.css
new file mode 100644
index 000000000..e88af0c58
--- /dev/null
+++ b/packages/console/app/src/component/header-context-menu.css
@@ -0,0 +1,55 @@
+.context-menu {
+ position: fixed;
+ z-index: 1000;
+ min-width: 160px;
+ border-radius: 8px;
+ background-color: var(--color-background);
+ box-shadow: 0 0 0 1px rgba(19, 16, 16, 0.08), 0 6px 8px -4px rgba(19, 16, 16, 0.12), 0 4px 3px -2px rgba(19, 16, 16, 0.12), 0 1px 2px -1px rgba(19, 16, 16, 0.12);
+ padding: 6px;
+
+ @media (prefers-color-scheme: dark) {
+ box-shadow: 0 0 0 1px rgba(247, 237, 237, 0.1);
+ }
+}
+
+.context-menu-item {
+ display: flex;
+ gap: 12px;
+ width: 100%;
+ padding: 8px 16px 8px 8px;
+ font-weight: 500;
+ cursor: pointer;
+ background: none;
+ border: none;
+ align-items: center;
+ color: var(--color-text);
+ font-size: var(--font-size-sm);
+ text-align: left;
+ cursor: pointer;
+ border-radius: 2px;
+ transition: background-color 0.2s ease;
+
+ [data-slot="copy dark"] {
+ display: none;
+ }
+
+ @media (prefers-color-scheme: dark) {
+ [data-slot="copy light"] {
+ display: none;
+ }
+ [data-slot="copy dark"] {
+ display: block;
+ }
+ }
+
+ &:hover {
+ background-color: var(--color-background-weak-hover);
+ color: var(--color-text-strong);
+ }
+}
+
+.context-menu-divider {
+ border: none;
+ border-top: 1px solid var(--color-border);
+ margin: var(--space-1) 0;
+}
diff --git a/packages/console/app/src/component/header.tsx b/packages/console/app/src/component/header.tsx
index ea8921f30..8f686e389 100644
--- a/packages/console/app/src/component/header.tsx
+++ b/packages/console/app/src/component/header.tsx
@@ -1,9 +1,17 @@
import logoLight from "../asset/logo-ornate-light.svg"
import logoDark from "../asset/logo-ornate-dark.svg"
+import copyLogoLight from "../asset/lander/logo-light.svg"
+import copyLogoDark from "../asset/lander/logo-dark.svg"
+import copyWordmarkLight from "../asset/lander/wordmark-light.svg"
+import copyWordmarkDark from "../asset/lander/wordmark-dark.svg"
+import copyBrandAssetsLight from "../asset/lander/brand-assets-light.svg"
+import copyBrandAssetsDark from "../asset/lander/brand-assets-dark.svg"
import { A, createAsync } from "@solidjs/router"
import { createMemo, Match, Show, Switch } from "solid-js"
import { createStore } from "solid-js/store"
import { github } from "~/lib/github"
+import { createEffect, onCleanup } from "solid-js"
+import "./header-context-menu.css"
export function Header(props: { zen?: boolean }) {
const githubData = createAsync(() => github())
@@ -18,14 +26,121 @@ export function Header(props: { zen?: boolean }) {
const [store, setStore] = createStore({
mobileMenuOpen: false,
+ contextMenuOpen: false,
+ contextMenuPosition: { x: 0, y: 0 },
})
+ createEffect(() => {
+ const handleClickOutside = () => {
+ setStore("contextMenuOpen", false)
+ }
+
+ const handleContextMenu = (event: MouseEvent) => {
+ event.preventDefault()
+ setStore("contextMenuOpen", false)
+ }
+
+ if (store.contextMenuOpen) {
+ document.addEventListener("click", handleClickOutside)
+ document.addEventListener("contextmenu", handleContextMenu)
+ onCleanup(() => {
+ document.removeEventListener("click", handleClickOutside)
+ document.removeEventListener("contextmenu", handleContextMenu)
+ })
+ }
+ })
+
+ const handleLogoContextMenu = (event: MouseEvent) => {
+ event.preventDefault()
+ setStore("contextMenuPosition", { x: event.clientX, y: event.clientY })
+ setStore("contextMenuOpen", true)
+ }
+
+ const copyWordmarkToClipboard = async () => {
+ try {
+ const wordmarkSvg = `<svg width="234" height="42" viewBox="0 0 234 42" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_1306_86249)">
+<path d="M18 30H6V18H18V30Z" fill="#CFCECD"/>
+<path d="M18 12H6V30H18V12ZM24 36H0V6H24V36Z" fill="#656363"/>
+<path d="M48 30H36V18H48V30Z" fill="#CFCECD"/>
+<path d="M36 30H48V12H36V30ZM54 36H36V42H30V6H54V36Z" fill="#656363"/>
+<path d="M84 24V30H66V24H84Z" fill="#CFCECD"/>
+<path d="M84 24H66V30H84V36H60V6H84V24ZM66 18H78V12H66V18Z" fill="#656363"/>
+<path d="M108 36H96V18H108V36Z" fill="#CFCECD"/>
+<path d="M108 12H96V36H90V6H108V12ZM114 36H108V12H114V36Z" fill="#656363"/>
+<path d="M144 30H126V18H144V30Z" fill="#CFCECD"/>
+<path d="M144 12H126V30H144V36H120V6H144V12Z" fill="#211E1E"/>
+<path d="M168 30H156V18H168V30Z" fill="#CFCECD"/>
+<path d="M168 12H156V30H168V12ZM174 36H150V6H174V36Z" fill="#211E1E"/>
+<path d="M198 30H186V18H198V30Z" fill="#CFCECD"/>
+<path d="M198 12H186V30H198V12ZM204 36H180V6H198V0H204V36Z" fill="#211E1E"/>
+<path d="M234 24V30H216V24H234Z" fill="#CFCECD"/>
+<path d="M216 12V18H228V12H216ZM234 24H216V30H234V36H210V6H234V24Z" fill="#211E1E"/>
+</g>
+<defs>
+<clipPath id="clip0_1306_86249">
+<rect width="234" height="42" fill="white"/>
+</clipPath>
+</defs>
+</svg>
+`
+ await navigator.clipboard.writeText(wordmarkSvg)
+ } catch (err) {
+ console.error("Failed to copy wordmark to clipboard:", err)
+ }
+ }
+
+ const copyLogoToClipboard = async () => {
+ try {
+ const logoSvg = `<svg width="80" height="100" viewBox="0 0 80 100" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M60 80H20V40H60V80Z" fill="#BCBBBB"/>
+<path d="M60 20H20V80H60V20ZM80 100H0V0H80V100Z" fill="#211E1E"/>
+</svg>
+`
+ await navigator.clipboard.writeText(logoSvg)
+ } catch (err) {
+ console.error("Failed to copy logo to clipboard:", err)
+ }
+ }
+
return (
<section data-component="top">
- <A href="/">
- <img data-slot="logo light" src={logoLight} alt="opencode logo light" />
- <img data-slot="logo dark" src={logoDark} alt="opencode logo dark" />
- </A>
+ <div onContextMenu={handleLogoContextMenu}>
+ <A href="/">
+ <img data-slot="logo light" src={logoLight} alt="opencode logo light" />
+ <img data-slot="logo dark" src={logoDark} alt="opencode logo dark" />
+ </A>
+ </div>
+
+ <Show when={store.contextMenuOpen}>
+ <div
+ class="context-menu"
+ style={`left: ${store.contextMenuPosition.x}px; top: ${store.contextMenuPosition.y}px;`}
+ >
+ <button class="context-menu-item" onClick={copyLogoToClipboard}>
+ <img data-slot="copy light" src={copyLogoLight}
+ alt="Logo"/>
+ <img data-slot="copy dark" src={copyLogoDark}
+ alt="Logo"/>
+ Copy logo as SVG
+ </button>
+ <button class="context-menu-item" onClick={copyWordmarkToClipboard}>
+ <img data-slot="copy light" src={copyWordmarkLight}
+ alt="Wordmark"/>
+ <img data-slot="copy dark" src={copyWordmarkDark}
+ alt="Wordmark"/>
+ Copy wordmark as SVG
+ </button>
+ <button class="context-menu-item"
+ onClick={() => (window.location.href = "/brand")}>
+ <img data-slot="copy light" src={copyBrandAssetsLight}
+ alt="Brand Assets"/>
+ <img data-slot="copy dark" src={copyBrandAssetsDark}
+ alt="Brand Assets"/>
+ Brand assets
+ </button>
+ </div>
+ </Show>
<nav data-component="nav-desktop">
<ul>
<li>
@@ -42,7 +157,7 @@ export function Header(props: { zen?: boolean }) {
<li>
<Switch>
<Match when={props.zen}>
- <a href="/auth">Login</a>
+ <a href="/auth">Login</a>
</Match>
<Match when={!props.zen}>
<A href="/zen">Zen</A>
diff --git a/packages/console/app/src/routes/brand/index.css b/packages/console/app/src/routes/brand/index.css
new file mode 100644
index 000000000..8cb0cc032
--- /dev/null
+++ b/packages/console/app/src/routes/brand/index.css
@@ -0,0 +1,443 @@
+::selection {
+ background: var(--color-background-interactive);
+ color: var(--color-text-strong);
+
+ @media (prefers-color-scheme: dark) {
+ background: var(--color-background-interactive);
+ color: var(--color-text-inverted);
+ }
+}
+
+
+[data-page="enterprise"] {
+ --color-background: hsl(0, 20%, 99%);
+ --color-background-weak: hsl(0, 8%, 97%);
+ --color-background-weak-hover: hsl(0, 8%, 94%);
+ --color-background-strong: hsl(0, 5%, 12%);
+ --color-background-strong-hover: hsl(0, 5%, 18%);
+ --color-background-interactive: hsl(62, 84%, 88%);
+ --color-background-interactive-weaker: hsl(64, 74%, 95%);
+
+ --color-text: hsl(0, 1%, 39%);
+ --color-text-weak: hsl(0, 1%, 60%);
+ --color-text-weaker: hsl(30, 2%, 81%);
+ --color-text-strong: hsl(0, 5%, 12%);
+ --color-text-inverted: hsl(0, 20%, 99%);
+ --color-text-success: hsl(119, 100%, 35%);
+
+ --color-border: hsl(30, 2%, 81%);
+ --color-border-weak: hsl(0, 1%, 85%);
+
+ --color-icon: hsl(0, 1%, 55%);
+ --color-success: hsl(142, 76%, 36%);
+
+ background: var(--color-background);
+ font-family: var(--font-mono);
+ color: var(--color-text);
+ padding-bottom: 5rem;
+
+ @media (prefers-color-scheme: dark) {
+ --color-background: hsl(0, 9%, 7%);
+ --color-background-weak: hsl(0, 6%, 10%);
+ --color-background-weak-hover: hsl(0, 6%, 15%);
+ --color-background-strong: hsl(0, 15%, 94%);
+ --color-background-strong-hover: hsl(0, 15%, 97%);
+ --color-background-interactive: hsl(62, 100%, 90%);
+ --color-background-interactive-weaker: hsl(60, 20%, 8%);
+
+ --color-text: hsl(0, 4%, 71%);
+ --color-text-weak: hsl(0, 2%, 49%);
+ --color-text-weaker: hsl(0, 3%, 28%);
+ --color-text-strong: hsl(0, 15%, 94%);
+ --color-text-inverted: hsl(0, 9%, 7%);
+ --color-text-success: hsl(119, 60%, 72%);
+
+
+ --color-border: hsl(0, 3%, 28%);
+ --color-border-weak: hsl(0, 4%, 23%);
+
+ --color-icon: hsl(10, 3%, 43%);
+ --color-success: hsl(142, 76%, 46%);
+ }
+
+ /* Header and Footer styles - copied from index.css */
+ [data-component="top"] {
+ padding: 24px 5rem;
+ height: 80px;
+ position: sticky;
+ top: 0;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ background: var(--color-background);
+ border-bottom: 1px solid var(--color-border-weak);
+ z-index: 10;
+
+ @media (max-width: 60rem) {
+ padding: 24px 1.5rem;
+ }
+
+ img {
+ height: 34px;
+ width: auto;
+ }
+
+ [data-component="nav-desktop"] {
+ ul {
+ display: flex;
+ justify-content: space-between;
+ gap: 48px;
+ li {
+ display: inline-block;
+ a {
+ text-decoration: none;
+ span {
+ color: var(--color-text-weak);
+ }
+ }
+ a:hover {
+ text-decoration: underline;
+ text-underline-offset: 2px;
+ text-decoration-thickness: 1px;
+ }
+ }
+ }
+
+ @media (max-width: 40rem) {
+ display: none;
+ }
+ }
+
+ [data-component="nav-mobile"] {
+ button > svg {
+ color: var(--color-icon);
+ }
+ }
+
+ [data-component="nav-mobile-toggle"] {
+ border: none;
+ background: none;
+ outline: none;
+ height: 40px;
+ width: 40px;
+ cursor: pointer;
+ margin-right: -8px;
+ }
+
+ [data-component="nav-mobile-toggle"]:hover {
+ background: var(--color-background-weak);
+ }
+
+ [data-component="nav-mobile"] {
+ display: none;
+
+ @media (max-width: 40rem) {
+ display: block;
+
+ [data-component="nav-mobile-icon"] {
+ cursor: pointer;
+ height: 40px;
+ width: 40px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+
+ [data-component="nav-mobile-menu-list"] {
+ position: fixed;
+ background: var(--color-background);
+ top: 80px;
+ left: 0;
+ right: 0;
+ height: 100vh;
+
+ ul {
+ list-style: none;
+ padding: 20px 0;
+
+ li {
+ a {
+ text-decoration: none;
+ padding: 20px;
+ display: block;
+
+ span {
+ color: var(--color-text-weak);
+ }
+ }
+
+ a:hover {
+ background: var(--color-background-weak);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ [data-slot="logo dark"] {
+ display: none;
+ }
+
+ @media (prefers-color-scheme: dark) {
+ [data-slot="logo light"] {
+ display: none;
+ }
+ [data-slot="logo dark"] {
+ display: block;
+ }
+ }
+ }
+
+ [data-component="footer"] {
+ border-top: 1px solid var(--color-border-weak);
+ display: flex;
+ flex-direction: row;
+
+ @media (max-width: 65rem) {
+ border-bottom: 1px solid var(--color-border-weak);
+ }
+
+ [data-slot="cell"] {
+ flex: 1;
+ text-align: center;
+
+ a {
+ text-decoration: none;
+ padding: 2rem 0;
+ width: 100%;
+ display: block;
+
+ span {
+ color: var(--color-text-weak);
+
+ @media (max-width: 40rem) {
+ display: none;
+ }
+ }
+ }
+
+ a:hover {
+ background: var(--color-background-weak);
+ text-decoration: underline;
+ text-underline-offset: 2px;
+ text-decoration-thickness: 1px;
+ }
+ }
+
+ [data-slot="cell"] + [data-slot="cell"] {
+ border-left: 1px solid var(--color-border-weak);
+
+ @media (max-width: 40rem) {
+ border-left: none;
+ }
+ }
+
+ /* Mobile: third column on its own row */
+ @media (max-width: 25rem) {
+ flex-wrap: wrap;
+
+ [data-slot="cell"] {
+ flex: 1 0 100%;
+ border-left: none;
+ border-top: 1px solid var(--color-border-weak);
+ }
+
+ [data-slot="cell"]:nth-child(1) {
+ border-top: none;
+ }
+ }
+ }
+
+ [data-component="container"] {
+ max-width: 67.5rem;
+ margin: 0 auto;
+ border: 1px solid var(--color-border-weak);
+ border-top: none;
+
+ @media (max-width: 65rem) {
+ border: none;
+ }
+ }
+
+ [data-component="content"] {
+ }
+
+
+ [data-component="brand-content"] {
+ padding: 4rem 5rem;
+
+ h2 {
+ font-size: 1.5rem;
+ font-weight: 500;
+ color: var(--color-text-strong);
+ margin-bottom: 1rem;
+ }
+
+ h3 {
+ font-size: 1.25rem;
+ font-weight: 500;
+ color: var(--color-text-strong);
+ margin: 2rem 0 1rem 0;
+ }
+
+ p {
+ line-height: 1.6;
+ margin-bottom: 2.5rem;
+ color: var(--color-text);
+ }
+
+ [data-component="download-button"] {
+ padding: 8px 12px 8px 20px;
+ background: var(--color-background-strong);
+ color: var(--color-text-inverted);
+ border: none;
+ border-radius: 4px;
+ font-weight: 500;
+ cursor: pointer;
+ display: flex;
+ width: fit-content;
+ gap: 12px;
+ transition: background-color 0.2s ease;
+ text-decoration: none;
+
+
+ &:hover:not(:disabled) {
+ background: var(--color-background-strong-hover);
+ }
+
+ &:disabled {
+ opacity: 0.6;
+ cursor: not-allowed;
+ }
+ }
+
+ img {
+ width: 100%;
+ height: auto;
+ display: block;
+ margin-top: 4rem;
+ margin-bottom: 2rem;
+ }
+
+
+ @media (max-width: 60rem) {
+ padding: 2rem 1.5rem;
+ }
+ }
+
+
+
+
+ [data-component="faq"] {
+ border-top: 1px solid var(--color-border-weak);
+ padding: 4rem 5rem;
+
+ @media (max-width: 60rem) {
+ padding: 2rem 1.5rem;
+ }
+
+ [data-slot="section-title"] {
+ margin-bottom: 24px;
+
+ h3 {
+ font-size: 16px;
+ font-weight: 500;
+ color: var(--color-text-strong);
+ margin-bottom: 12px;
+ }
+
+ p {
+ margin-bottom: 12px;
+ color: var(--color-text);
+ }
+ }
+
+ ul {
+ padding: 0;
+
+ li {
+ list-style: none;
+ margin-bottom: 24px;
+ line-height: 200%;
+
+ @media (max-width: 60rem) {
+ line-height: 180%;
+ }
+ }
+ }
+
+ [data-slot="faq-question"] {
+ display: flex;
+ gap: 16px;
+ margin-bottom: 8px;
+ color: var(--color-text-strong);
+ font-weight: 500;
+ cursor: pointer;
+ background: none;
+ border: none;
+ padding: 0;
+
+ [data-slot="faq-icon-plus"] {
+ flex-shrink: 0;
+ color: var(--color-text-weak);
+ margin-top: 2px;
+
+ [data-closed] & {
+ display: block;
+ }
+ [data-expanded] & {
+ display: none;
+ }
+ }
+ [data-slot="faq-icon-minus"] {
+ flex-shrink: 0;
+ color: var(--color-text-weak);
+ margin-top: 2px;
+
+ [data-closed] & {
+ display: none;
+ }
+ [data-expanded] & {
+ display: block;
+ }
+ }
+ [data-slot="faq-question-text"] {
+ flex-grow: 1;
+ text-align: left;
+ }
+ }
+
+ [data-slot="faq-answer"] {
+ margin-left: 40px;
+ margin-bottom: 32px;
+ color: var(--color-text);
+ }
+ }
+
+ [data-component="legal"] {
+ color: var(--color-text-weak);
+ text-align: center;
+ padding: 2rem 5rem;
+
+ @media (max-width: 60rem) {
+ padding: 2rem 1.5rem;
+ }
+
+ a {
+ color: var(--color-text-weak);
+ text-decoration: none;
+ }
+ }
+
+ a {
+ color: var(--color-text-strong);
+ text-decoration: underline;
+ text-underline-offset: 2px;
+ text-decoration-thickness: 1px;
+
+ &:hover {
+ text-decoration-thickness: 2px;
+ }
+ }
+}
diff --git a/packages/console/app/src/routes/brand/index.tsx b/packages/console/app/src/routes/brand/index.tsx
new file mode 100644
index 000000000..53ce22bb9
--- /dev/null
+++ b/packages/console/app/src/routes/brand/index.tsx
@@ -0,0 +1,100 @@
+import "./index.css"
+import { Title, Meta } from "@solidjs/meta"
+import { Header } from "~/component/header"
+import { Footer } from "~/component/footer"
+import { Legal } from "~/component/legal"
+import { Faq } from "~/component/faq"
+import brand from "../../asset/lander/brand.png"
+
+
+export default function Brand() {
+
+ return (
+ <main data-page="enterprise">
+ <Title>OpenCode | Brand</Title>
+ <Meta name="description" content="OpenCode brand guidelines" />
+ <div data-component="container">
+ <Header />
+
+ <div data-component="content">
+ <section data-component="brand-content">
+ <h2>Brand guidelines</h2>
+ <p>
+ Resources and assets to help you work with the OpenCode brand.
+ </p>
+ <a data-component="download-button" href="#">Download brand assets<svg
+ width="24" height="24" viewBox="0 0 24 24" fill="none"
+ xmlns="http://www.w3.org/2000/svg">
+ <path d="M12 6.5L12 17M7.5 13L12 17.5L16.5 13" stroke="currentColor"
+ stroke-width="1.5" stroke-linecap="square"/>
+ </svg>
+ </a>
+ <img src={brand} alt=""/>
+
+ <p>If you need any help with anything brand related <a href="mailto:[email protected]">contact us</a>.</p>
+ </section>
+
+ <section data-component="faq">
+ <div data-slot="section-title">
+ <h3>FAQ</h3>
+ </div>
+ <ul>
+ <li>
+ <Faq question="Does Opencode store our code or context data?">
+ No. OpenCode never stores your code or context data. All
+ processing happens locally or directly with your AI provider.
+ </Faq>
+ </li>
+ <li>
+ <Faq question="Who owns the code generated with OpenCode?">
+ You do. All code produced is yours, with no licensing
+ restrictions or ownership claims.
+ </Faq>
+ </li>
+ <li>
+ <Faq
+ question="How can we trial OpenCode inside our organization?">
+ Simply install and run an internal trial with your team. Since
+ OpenCode doesn’t store any data, your developers can get
+ started right away.
+ </Faq>
+ </li>
+ <li>
+ <Faq
+ question="What happens if someone uses the `/share` feature?">
+ By default, sharing is disabled. If enabled, conversations are
+ sent to our share service and cached through our CDN. For
+ enterprise use, we recommend disabling or self-hosting this
+ feature.
+ </Faq>
+ </li>
+ <li>
+ <Faq question="Can OpenCode integrate with our company’s SSO?">
+ Yes. Enterprise deployments can include SSO integration so all
+ sessions and shared conversations are protected by your
+ authentication system.
+ </Faq>
+ </li>
+ <li>
+ <Faq question="Can OpenCode be self-hosted?">
+ Absolutely. You can fully self-host OpenCode, including the
+ share feature, ensuring that data and pages are accessible
+ only after authentication.
+ </Faq>
+ </li>
+ <li>
+ <Faq
+ question="How do we get started with enterprise deployment?">
+ Contact us to discuss pricing, implementation, and enterprise
+ options like SSO, private registries, and self-hosting.
+ </Faq>
+ </li>
+ </ul>
+ </section>
+ </div>
+ <Footer />
+ </div>
+ <Legal />
+ </main>
+ )
+}