feat: HLS streaming, transcripts, agent course generation & landing-page course previews (#663)
New features: - HLS video encoding/streaming (hls routes, hls-encoder) and a media transcripts pipeline (transcripts route + media-transcript queries) - AI assistant chat vs. run model: agent-course-generation worker/queue, run-monitor pane, chat-changes card, content ask-ai bar - Per-theme course-page previews across org landing-page themes (bold, classic, corporate, editorial, etc.) with a shared course token/component set - View-as-student mode and login-link / link-invite invite flows Refactor: - Extract packages/core consolidating config (env, storage, dashboard-url), services and utils previously duplicated under apps/api/src/config - Remove legacy agent service modules (chat-tools, chat-context, context-window, document, fetch-url, lesson-content) - Relax Cross-Origin-Resource-Policy to allow cross-origin media embedding - Add format script to tenant-router so turbo run format covers it
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
import { tokenize, type CourseLandingTokens } from '../course-tokens-base';
|
||||
|
||||
export const courseTokens: CourseLandingTokens = tokenize({
|
||||
sectionShell: 'ui:py-12 ui:md:py-14 ui:px-4 ui:bg-[var(--landing-bg)]',
|
||||
sectionNavShell: 'ui:border-b ui:border-[var(--landing-border)] ui:bg-[var(--landing-bg)]',
|
||||
sectionNavItem: 'ui:font-mono',
|
||||
eyebrow:
|
||||
'ui:text-xs ui:font-mono ui:text-[var(--landing-accent)]' +
|
||||
' ui:[letter-spacing:var(--landing-eyebrow-tracking)]' +
|
||||
' ui:[text-transform:var(--landing-eyebrow-case)]',
|
||||
heading:
|
||||
'ui:text-3xl ui:md:text-4xl ui:text-[var(--landing-fg)] ui:font-mono' +
|
||||
' ui:[font-weight:var(--landing-heading-weight)]',
|
||||
body: 'ui:text-sm ui:text-[var(--landing-fg-muted)] ui:font-mono ui:leading-relaxed',
|
||||
socialProofShell: 'ui:py-8 ui:px-4 ui:border-y ui:border-[var(--landing-border)]',
|
||||
socialProofValue:
|
||||
'ui:text-2xl ui:font-mono ui:text-[var(--landing-fg)]' + ' ui:[font-weight:var(--landing-heading-weight)]',
|
||||
socialProofLabel:
|
||||
'ui:text-xs ui:font-mono ui:text-[var(--landing-fg-muted)]' +
|
||||
' ui:[letter-spacing:var(--landing-eyebrow-tracking)]' +
|
||||
' ui:[text-transform:var(--landing-eyebrow-case)]',
|
||||
socialProofDivider:
|
||||
'ui:size-2 ui:rounded-full ui:bg-[var(--landing-accent)]' + ' ui:[box-shadow:var(--landing-glow)]',
|
||||
infoBlock:
|
||||
'ui:flex ui:flex-col ui:gap-3 ui:p-6' +
|
||||
' ui:[background:linear-gradient(180deg,var(--landing-card)_0%,var(--landing-bg-section)_100%)]' +
|
||||
' ui:border ui:border-[var(--landing-border-soft)]' +
|
||||
' ui:[border-radius:var(--landing-radius-card)]',
|
||||
infoBlockHeading:
|
||||
'ui:text-xl ui:font-mono ui:text-[var(--landing-fg)]' + ' ui:[font-weight:var(--landing-heading-weight)]',
|
||||
infoBlockBody: 'ui:text-sm ui:font-mono ui:text-[var(--landing-fg-muted)] ui:leading-relaxed',
|
||||
curriculumContainer:
|
||||
'ui:p-2' +
|
||||
' ui:[background:linear-gradient(180deg,var(--landing-card)_0%,var(--landing-bg-section)_100%)]' +
|
||||
' ui:border ui:border-[var(--landing-border-soft)]' +
|
||||
' ui:[border-radius:var(--landing-radius-card)]',
|
||||
curriculumModule: 'ui:py-2',
|
||||
curriculumModuleHeader:
|
||||
'ui:flex ui:items-center ui:justify-between ui:gap-4 ui:px-4 ui:py-3 ui:cursor-pointer' +
|
||||
' ui:font-mono ui:text-[var(--landing-fg)]',
|
||||
curriculumModuleTitle:
|
||||
'ui:text-base ui:font-mono ui:text-[var(--landing-fg)]' + ' ui:[font-weight:var(--landing-heading-weight)]',
|
||||
curriculumModuleMeta: 'ui:text-xs ui:font-mono ui:text-[var(--landing-accent)]',
|
||||
curriculumLessonRow:
|
||||
'ui:flex ui:items-center ui:gap-3 ui:px-4 ui:py-2 ui:font-mono ui:text-sm' +
|
||||
' ui:[border-top:1px_dashed_var(--landing-border-soft)]',
|
||||
curriculumLessonPrefix: 'ui:text-xs ui:text-[var(--landing-accent)] ui:font-mono',
|
||||
curriculumLessonTitle: 'ui:text-sm ui:text-[var(--landing-fg-muted)] ui:font-mono',
|
||||
reviewCard:
|
||||
'ui:p-6' +
|
||||
' ui:[background:linear-gradient(180deg,var(--landing-card)_0%,var(--landing-bg-section)_100%)]' +
|
||||
' ui:border ui:border-[var(--landing-border-soft)]' +
|
||||
' ui:[border-radius:var(--landing-radius-card)]',
|
||||
reviewName: 'ui:text-sm ui:font-mono ui:text-[var(--landing-fg)]',
|
||||
reviewDate: 'ui:text-xs ui:font-mono ui:text-[var(--landing-fg-muted)]',
|
||||
reviewQuote: 'ui:text-sm ui:font-mono ui:text-[var(--landing-fg)] ui:leading-relaxed ui:mb-4',
|
||||
chip:
|
||||
'ui:inline-flex ui:items-center ui:px-3 ui:py-1.5 ui:text-xs ui:font-mono' +
|
||||
' ui:bg-transparent ui:text-[var(--landing-fg)]' +
|
||||
' ui:border ui:border-[var(--landing-border-soft)]' +
|
||||
' ui:[border-radius:var(--landing-radius-pill)]' +
|
||||
' ui:hover:border-[var(--landing-border)] ui:transition-colors',
|
||||
chipsShowAll:
|
||||
'ui:text-xs ui:font-mono ui:text-[var(--landing-accent)]' +
|
||||
' ui:[letter-spacing:var(--landing-eyebrow-tracking)]' +
|
||||
' ui:[text-transform:var(--landing-eyebrow-case)]' +
|
||||
' ui:cursor-pointer ui:ml-2 ui:bg-transparent ui:border-0',
|
||||
instructorShell:
|
||||
'ui:flex ui:flex-col ui:md:flex-row ui:gap-6 ui:items-start ui:p-6' +
|
||||
' ui:[background:linear-gradient(180deg,var(--landing-card)_0%,var(--landing-bg-section)_100%)]' +
|
||||
' ui:border ui:border-[var(--landing-border-soft)]' +
|
||||
' ui:[border-radius:var(--landing-radius-card)]',
|
||||
instructorAvatar: 'ui:size-20 ui:rounded-full ui:object-cover ui:border ui:border-[var(--landing-border-soft)]',
|
||||
instructorName:
|
||||
'ui:text-xl ui:font-mono ui:text-[var(--landing-fg)]' + ' ui:[font-weight:var(--landing-heading-weight)]',
|
||||
instructorRole: 'ui:text-sm ui:font-mono ui:text-[var(--landing-fg-muted)]',
|
||||
pricingShell:
|
||||
'ui:p-6' +
|
||||
' ui:[background:linear-gradient(180deg,var(--landing-card)_0%,var(--landing-bg-section)_100%)]' +
|
||||
' ui:border ui:border-[var(--landing-border-soft)]' +
|
||||
' ui:[border-radius:var(--landing-radius-card)]',
|
||||
pricingAmount:
|
||||
'ui:text-4xl ui:md:text-5xl ui:font-mono ui:text-[var(--landing-fg)]' +
|
||||
' ui:[font-weight:var(--landing-heading-weight)]',
|
||||
pricingFreeBadge:
|
||||
'ui:inline-block ui:px-3 ui:py-1 ui:text-xs ui:font-mono ui:text-[var(--landing-accent-fg)]' +
|
||||
' ui:bg-[var(--landing-accent)] ui:[border-radius:var(--landing-radius-pill)]' +
|
||||
' ui:[box-shadow:var(--landing-glow)]'
|
||||
});
|
||||
@@ -0,0 +1,64 @@
|
||||
<script lang="ts">
|
||||
import type { CourseLandingPageProps } from '../types';
|
||||
import TerminalNav from './nav.svelte';
|
||||
import TerminalHero from './hero.svelte';
|
||||
import OrgLandingPageFooter from '../landing-page-footer.svelte';
|
||||
import CourseSocialProof from '../course-social-proof.svelte';
|
||||
import CourseInfoBlocks from '../course-info-blocks.svelte';
|
||||
import CourseCurriculum from '../course-curriculum.svelte';
|
||||
import CourseChips from '../course-chips.svelte';
|
||||
import CourseSectionNav from '../course-section-nav.svelte';
|
||||
import CourseInstructor from '../course-instructor.svelte';
|
||||
import CourseReviews from '../course-reviews.svelte';
|
||||
import CoursePricing from '../course-pricing.svelte';
|
||||
import { themeStyle } from '../theme-style';
|
||||
import { alignHeroCtaWithPricing, buildCourseSectionNavItems } from '../course-landing-page.helpers';
|
||||
|
||||
let {
|
||||
orgName,
|
||||
logoUrl,
|
||||
navItems,
|
||||
authAction,
|
||||
hero,
|
||||
socialProof,
|
||||
info,
|
||||
curriculum,
|
||||
chips,
|
||||
instructor,
|
||||
reviews,
|
||||
pricing,
|
||||
footer,
|
||||
labels
|
||||
}: CourseLandingPageProps = $props();
|
||||
|
||||
const heroProps = $derived(alignHeroCtaWithPricing(hero, pricing));
|
||||
const sectionNavItems = $derived(
|
||||
buildCourseSectionNavItems({ info, curriculum, chips, instructor, reviews }, labels)
|
||||
);
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="ui:min-h-screen ui:bg-[var(--landing-bg)] ui:text-[var(--landing-fg)] ui:font-sans"
|
||||
style={themeStyle('terminal')}
|
||||
>
|
||||
<main>
|
||||
<TerminalHero {orgName} hero={heroProps} courses={[]}>
|
||||
{#snippet navigation()}
|
||||
<TerminalNav {orgName} {logoUrl} {navItems} {authAction} />
|
||||
{/snippet}
|
||||
</TerminalHero>
|
||||
|
||||
<CourseSectionNav variant="terminal" items={sectionNavItems} {labels} />
|
||||
<CourseSocialProof variant="terminal" {socialProof} {labels} />
|
||||
<CourseInfoBlocks variant="terminal" {info} {labels} />
|
||||
<CourseCurriculum variant="terminal" {curriculum} {labels} />
|
||||
{#if chips}
|
||||
<CourseChips variant="terminal" {chips} {labels} />
|
||||
{/if}
|
||||
<CourseInstructor variant="terminal" {instructor} {labels} />
|
||||
<CourseReviews variant="terminal" {reviews} {labels} />
|
||||
<CoursePricing variant="terminal" {pricing} {labels} />
|
||||
</main>
|
||||
|
||||
<OrgLandingPageFooter {orgName} {logoUrl} {footer} variant="terminal" />
|
||||
</div>
|
||||
@@ -9,9 +9,10 @@
|
||||
hero: OrgLandingPageProps['hero'];
|
||||
courses: OrgLandingPageProps['courses'];
|
||||
navigation: Snippet;
|
||||
showActions?: boolean;
|
||||
}
|
||||
|
||||
let { orgName = '', hero, courses = [], navigation }: Props = $props();
|
||||
let { orgName = '', hero, courses = [], navigation, showActions = true }: Props = $props();
|
||||
|
||||
const programsCount = $derived(courses.length);
|
||||
const learnersCount = $derived(courses.reduce((sum, course) => sum + (course.totalStudents ?? 0), 0));
|
||||
@@ -90,26 +91,28 @@
|
||||
{hero.heading}
|
||||
</h1>
|
||||
|
||||
<div class="ui:inline-flex ui:items-center ui:gap-2 ui:mb-8">
|
||||
{#if hero.secondaryAction}
|
||||
{#if showActions}
|
||||
<div class="ui:inline-flex ui:items-center ui:gap-2 ui:mb-8">
|
||||
{#if hero.secondaryAction}
|
||||
<Button
|
||||
href={hero.secondaryAction.href}
|
||||
size="sm"
|
||||
variant="outline"
|
||||
class="ui:rounded-full ui:px-4 ui:font-medium ui:bg-white/5 ui:border-[var(--landing-border)] ui:text-[var(--landing-fg)] ui:hover:bg-white/10"
|
||||
>
|
||||
{hero.secondaryAction.label}
|
||||
</Button>
|
||||
{/if}
|
||||
<Button
|
||||
href={hero.secondaryAction.href}
|
||||
href={hero.primaryAction.href}
|
||||
disabled={hero.primaryAction.disabled ?? false}
|
||||
size="sm"
|
||||
variant="outline"
|
||||
class="ui:rounded-full ui:px-4 ui:font-medium ui:bg-white/5 ui:border-[var(--landing-border)] ui:text-[var(--landing-fg)] ui:hover:bg-white/10"
|
||||
class="ui:rounded-full ui:px-4 ui:font-medium ui:bg-[#f4f5f7] ui:text-[#0a0b0e] ui:hover:bg-white"
|
||||
>
|
||||
{hero.secondaryAction.label}
|
||||
{hero.primaryAction.label}
|
||||
</Button>
|
||||
{/if}
|
||||
<Button
|
||||
href={hero.primaryAction.href}
|
||||
disabled={hero.primaryAction.disabled ?? false}
|
||||
size="sm"
|
||||
class="ui:rounded-full ui:px-4 ui:font-medium ui:bg-[#f4f5f7] ui:text-[#0a0b0e] ui:hover:bg-white"
|
||||
>
|
||||
{hero.primaryAction.label}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<p class="ui:text-[15px] ui:mb-7 ui:max-w-xl ui:mx-auto ui:text-[var(--landing-fg-muted)]">
|
||||
{hero.subheading}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
export { default as nav } from './nav.svelte';
|
||||
export { default as hero } from './hero.svelte';
|
||||
export { default as courseCard } from './course-card.svelte';
|
||||
export { default as org } from './org.svelte';
|
||||
export { default as course } from './course.svelte';
|
||||
|
||||
export const coursesGridClass = 'ui:grid ui:grid-cols-1 ui:@xl:grid-cols-2 ui:@4xl:grid-cols-3 ui:gap-[14px]';
|
||||
@@ -0,0 +1,166 @@
|
||||
<script lang="ts">
|
||||
import type { OrgLandingPageProps, CourseItem } from '../types';
|
||||
import OrgLandingPageEmbed from '../embed.svelte';
|
||||
import OrgLandingPageLinks from '../links.svelte';
|
||||
import OrgLandingPageCallout from '../callout.svelte';
|
||||
import OrgLandingPageFooter from '../landing-page-footer.svelte';
|
||||
import TerminalNav from './nav.svelte';
|
||||
import TerminalHero from './hero.svelte';
|
||||
import TerminalCourseCard from './course-card.svelte';
|
||||
import { Button } from '../../../base/button';
|
||||
import { themeStyle } from '../theme-style';
|
||||
import { getCourseTypeLandingMeta } from '../landing-page-utils';
|
||||
|
||||
let {
|
||||
orgName,
|
||||
logoUrl,
|
||||
navItems,
|
||||
authAction,
|
||||
hero,
|
||||
courses,
|
||||
hasMoreCourses = false,
|
||||
disableCourseLinks = false,
|
||||
embed,
|
||||
callout,
|
||||
links,
|
||||
footer,
|
||||
labels
|
||||
}: OrgLandingPageProps = $props();
|
||||
|
||||
function priceLabel(course: CourseItem): string {
|
||||
if (course.price) return course.price;
|
||||
if (!course.cost) return labels?.freeLabel ?? 'Free';
|
||||
|
||||
return new Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
currency: course.currency ?? 'USD',
|
||||
maximumFractionDigits: 0
|
||||
}).format(course.cost);
|
||||
}
|
||||
|
||||
type TabKey = 'all' | string;
|
||||
|
||||
const courseTypeKeys = $derived(
|
||||
Array.from(new Set(courses.map((course) => course.type).filter((t): t is string => Boolean(t))))
|
||||
);
|
||||
|
||||
const tabs = $derived<{ key: TabKey; label: string }[]>([
|
||||
{ key: 'all', label: labels?.filterAllLabel ?? 'All' },
|
||||
...courseTypeKeys.map((type) => {
|
||||
const meta = getCourseTypeLandingMeta({ id: '', title: '', description: '', type } as CourseItem);
|
||||
|
||||
return { key: type, label: meta?.label ?? type };
|
||||
})
|
||||
]);
|
||||
|
||||
let activeTab = $state<TabKey>('all');
|
||||
|
||||
const visibleCourses = $derived(
|
||||
activeTab === 'all' ? courses : courses.filter((course) => course.type === activeTab)
|
||||
);
|
||||
|
||||
function slugify(course: CourseItem): string {
|
||||
if (course.slug) return course.slug;
|
||||
|
||||
return course.title
|
||||
.toLowerCase()
|
||||
.trim()
|
||||
.replace(/[^a-z0-9]+/g, '-')
|
||||
.replace(/^-+|-+$/g, '');
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="ui:min-h-screen ui:font-sans ui:w-full ui:bg-[var(--landing-bg)] ui:text-[var(--landing-fg)]"
|
||||
style={themeStyle('terminal')}
|
||||
>
|
||||
<main>
|
||||
<TerminalHero {orgName} {hero} {courses}>
|
||||
{#snippet navigation()}
|
||||
<TerminalNav {orgName} {logoUrl} {navItems} {authAction} />
|
||||
{/snippet}
|
||||
</TerminalHero>
|
||||
|
||||
{#if courses.length > 0}
|
||||
<section
|
||||
class="ui:py-24 ui:px-6 ui:bg-[var(--landing-bg)]"
|
||||
style="border-top: 1px solid var(--landing-border-soft);"
|
||||
>
|
||||
<div class="ui:max-w-[1120px] ui:mx-auto">
|
||||
<p
|
||||
class="ui:font-mono ui:text-[11px] ui:tracking-[0.12em] ui:uppercase ui:mb-3 ui:inline-flex ui:items-center ui:gap-2"
|
||||
style="color: var(--landing-accent);"
|
||||
>
|
||||
<span
|
||||
class="ui:size-1.5 ui:rounded-full"
|
||||
style="background: var(--landing-accent); box-shadow: 0 0 12px var(--landing-accent);"
|
||||
></span>
|
||||
{labels?.catalogEyebrow ?? 'Catalog'}
|
||||
</p>
|
||||
|
||||
<h2
|
||||
class="ui:text-4xl ui:lg:text-[40px] ui:font-semibold ui:tracking-tight ui:leading-[1.08] ui:m-0 ui:mb-3 ui:max-w-[700px] ui:text-[var(--landing-fg)]"
|
||||
>
|
||||
{labels?.catalogHeading ?? 'Programs your team can actually finish.'}
|
||||
</h2>
|
||||
|
||||
<p class="ui:text-[15px] ui:max-w-xl ui:m-0 ui:mb-8 ui:text-[var(--landing-fg-muted)]">
|
||||
{labels?.catalogDescription ??
|
||||
'Tracks built around real-world artifacts — pull requests, postmortems, and runbooks. No filler.'}
|
||||
</p>
|
||||
|
||||
{#if tabs.length > 1}
|
||||
<div
|
||||
class="ui:inline-flex ui:items-center ui:gap-0.5 ui:p-1 ui:rounded-full ui:mb-8 ui:bg-[var(--landing-card)]"
|
||||
style="border: 1px solid var(--landing-border);"
|
||||
role="tablist"
|
||||
>
|
||||
{#each tabs as tab (tab.key)}
|
||||
{@const isActive = activeTab === tab.key}
|
||||
<button
|
||||
type="button"
|
||||
role="tab"
|
||||
aria-selected={isActive}
|
||||
onclick={() => (activeTab = tab.key)}
|
||||
class="ui:px-3.5 ui:py-1.5 ui:text-[13px] ui:font-medium ui:rounded-full ui:transition-colors ui:cursor-pointer"
|
||||
style={isActive
|
||||
? 'background: var(--landing-fg); color: var(--landing-bg);'
|
||||
: 'background: transparent; color: var(--landing-fg-muted);'}
|
||||
>
|
||||
{tab.label}
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="ui:grid ui:grid-cols-1 ui:md:grid-cols-2 ui:lg:grid-cols-3 ui:gap-[14px]">
|
||||
{#each visibleCourses as course, index (course.id)}
|
||||
<TerminalCourseCard {course} {disableCourseLinks} {labels} />
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
{#if hasMoreCourses}
|
||||
<div class="ui:mt-7 ui:flex ui:justify-center">
|
||||
<Button
|
||||
href={disableCourseLinks ? undefined : '/courses'}
|
||||
variant="outline"
|
||||
disabled={disableCourseLinks}
|
||||
class="ui:rounded-full ui:px-5 ui:bg-transparent ui:border-[var(--landing-border)] ui:text-[var(--landing-fg)] ui:hover:bg-white/5"
|
||||
>
|
||||
{labels?.browseCoursesLabel ?? 'View all programs →'}
|
||||
</Button>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</section>
|
||||
{/if}
|
||||
</main>
|
||||
|
||||
<OrgLandingPageLinks {links} {labels} variant="terminal" />
|
||||
|
||||
<OrgLandingPageEmbed {embed} {labels} variant="terminal" />
|
||||
|
||||
<OrgLandingPageCallout {callout} {labels} variant="terminal" />
|
||||
|
||||
<OrgLandingPageFooter {orgName} {logoUrl} {footer} variant="terminal" />
|
||||
</div>
|
||||
@@ -0,0 +1,36 @@
|
||||
import type { LandingThemeVars } from '../theme-vars-base';
|
||||
|
||||
export const vars: LandingThemeVars = {
|
||||
'--landing-bg': '#06070a',
|
||||
'--landing-bg-section': '#0c0e13',
|
||||
'--landing-card': '#0f1218',
|
||||
'--landing-card-soft': '#14171f',
|
||||
'--landing-fg': '#e9eaed',
|
||||
'--landing-fg-muted': '#9da1ab',
|
||||
'--landing-fg-faint': '#61656f',
|
||||
'--landing-border': '#1c1f28',
|
||||
'--landing-border-soft': '#161922',
|
||||
'--landing-accent': 'var(--primary)',
|
||||
'--landing-accent-fg': 'var(--primary-foreground)',
|
||||
'--landing-button-primary-bg': '#f4f5f7',
|
||||
'--landing-button-primary-fg': '#0a0b0e',
|
||||
'--landing-button-primary-bg-hover': '#ffffff',
|
||||
'--landing-button-secondary-bg': 'rgba(255,255,255,0.04)',
|
||||
'--landing-button-secondary-fg': '#e9eaed',
|
||||
'--landing-button-secondary-border': '#262a35',
|
||||
'--landing-button-secondary-bg-hover': 'rgba(255,255,255,0.08)',
|
||||
'--landing-button-tertiary-fg': 'var(--primary)',
|
||||
'--landing-button-tertiary-bg-hover': 'rgba(255,255,255,0.06)',
|
||||
'--landing-heading-weight': '600',
|
||||
'--landing-heading-tracking': 'normal',
|
||||
'--landing-heading-case': 'none',
|
||||
'--landing-eyebrow-tracking': '0.12em',
|
||||
'--landing-eyebrow-case': 'uppercase',
|
||||
'--landing-radius-card': '14px',
|
||||
'--landing-radius-pill': '9999px',
|
||||
'--landing-shadow-card': 'none',
|
||||
'--landing-divider': '1px solid #1a1c22',
|
||||
'--landing-mono-family': 'ui-monospace, "JetBrains Mono", "Fira Code", monospace',
|
||||
'--landing-glow': '0 0 12px color-mix(in oklab, var(--landing-accent), transparent 30%)',
|
||||
'--landing-panel-bg-accent': 'color-mix(in oklab, var(--landing-accent), #06070a 88%)'
|
||||
};
|
||||
Reference in New Issue
Block a user