diff --git a/app/assets/scss/_layout.scss b/app/assets/scss/_layout.scss index b5c2978..ef7ec36 100644 --- a/app/assets/scss/_layout.scss +++ b/app/assets/scss/_layout.scss @@ -59,6 +59,7 @@ .nav-links a { text-decoration: none; color: var(--color-text-muted); + font-family: var(--font-mono); font-size: 0.95rem; font-weight: 500; transition: color 0.3s ease; diff --git a/app/assets/scss/base.scss b/app/assets/scss/base.scss index 7829bd5..8fe7d99 100644 --- a/app/assets/scss/base.scss +++ b/app/assets/scss/base.scss @@ -4,15 +4,33 @@ @use 'utilities'; :root { - --color-bg: #050816; - --color-bg-secondary: #0d111d; - --color-terminal: rgba(13, 17, 23, 0.85); - --color-text: #fff; - --color-text-muted: #9ca3af; - --color-accent: #00f5ff; - --color-error: #f85149; - --color-success: #3fb950; - --color-glass-border: rgba(0, 245, 255, 0.2); + // Canonical MCTL design tokens, vendored from @mctlhq/tokens (the same + // dependency-free pattern mctl-telegram and mctl-instruments use). Keep the + // existing --color-* variable names so consumers don't all need rewiring; + // only the values change — from the old #050816/#00f5ff fork to the + // canonical ink/cyan palette and the hairline (not glow) aesthetic. + --color-bg: #0a0b0d; // ink + --color-bg-secondary: #0f1114; // ink2 / elevated + --color-card: #15181d; // ink3 / card + --color-terminal: rgba(21, 24, 29, 0.85); + --color-text: #e6e7e9; // fg + --color-text-muted: #a4a8ae; // fg2 + --color-text-subtle: #6b7079; // fg3 + --color-accent: #00e5ff; // cyan + --color-accent-highlight: #7df2ff; + --color-error: #ff6b6b; // status bad + --color-success: #7cf2a4; // status ok + --color-warn: #f5a524; // status warn + --color-line: #1f242b; // hairline + --color-line-strong: #2a313a; + // Borders are neutral hairlines, not cyan, in the canonical language. + --color-glass-border: var(--color-line); + --color-accent-soft: rgba(0, 229, 255, 0.1); + // Elevation is shadow-light; hover lifts on a hairline + a quiet drop, no glow. + --shadow-card-hover: 0 16px 48px rgba(0, 0, 0, 0.48); + + --font-display: 'Geist', system-ui, -apple-system, sans-serif; + --font-editorial: 'Instrument Serif', Georgia, serif; --font-mono: 'JetBrains Mono', 'Fira Code', 'Courier New', monospace; --container-width: 1400px; --container-width-md: 1200px; @@ -32,8 +50,9 @@ html { } body { + position: relative; font-size: 14px; - font-family: var(--font-mono); + font-family: var(--font-display); background-color: var(--color-bg); color: var(--color-text); line-height: 1.6; @@ -46,6 +65,24 @@ body { } } +// Ambient background: a faint neutral blueprint grid — engineering texture, +// not a glow. Recoloured from cyan to a hairline tone to match the design +// system's restraint; fixed so it stays put while content scrolls. +body::before { + content: ''; + position: fixed; + inset: 0; + z-index: -1; + pointer-events: none; + background-image: + linear-gradient(rgba(255, 255, 255, 0.018) 1px, transparent 1px), + linear-gradient(90deg, rgba(255, 255, 255, 0.018) 1px, transparent 1px); + background-size: 56px 56px, 56px 56px; + // Fade the grid out toward the bottom so it reads as atmosphere, not a table. + mask-image: linear-gradient(180deg, #000 0%, #000 55%, transparent 100%); + -webkit-mask-image: linear-gradient(180deg, #000 0%, #000 55%, transparent 100%); +} + a { color: inherit; text-decoration: none; @@ -65,7 +102,7 @@ h1, h2, h3, h4, h5, h6 { h1 { font-size: 32px; - font-weight: 800; + font-weight: 700; text-align: center; color: #fff; margin-bottom: 1.5rem; @@ -83,7 +120,7 @@ h1 { h2 { font-size: 28px; - font-weight: 800; + font-weight: 700; @media (min-width: 768px) { font-size: 36px; diff --git a/app/components/main/HeroBlock.vue b/app/components/main/HeroBlock.vue index 735b75c..f79cd6e 100644 --- a/app/components/main/HeroBlock.vue +++ b/app/components/main/HeroBlock.vue @@ -6,16 +6,54 @@ const { t } = useI18n()
-

- {{ t('hero.title') }} -

-

- - {{ t('hero.cta') }} - +

+ + + {{ t('hero.badge') }} + +

+ {{ t('hero.title') }} +

+

+

+ + {{ t('hero.cta') }} + + + {{ t('hero.cta_secondary') }} + + +
+
+ +
@@ -24,7 +62,7 @@ const { t } = useI18n() .hero-block { position: relative; padding: 60px 0; - height: calc(100vh - var(--header-height)); + min-height: calc(100vh - var(--header-height)); display: flex; align-items: center; justify-content: center; @@ -36,9 +74,40 @@ const { t } = useI18n() font-size: clamp(2.5rem, 4.5vw, 4.25rem); } + &__intro { + position: relative; + z-index: 1; + } + + &__badge { + display: inline-flex; + align-items: center; + gap: 0.5rem; + margin-bottom: 1.75rem; + padding: 0.4rem 0.9rem; + + font-size: 0.75rem; + letter-spacing: 1px; + text-transform: uppercase; + color: var(--color-accent); + + background: var(--color-accent-soft); + border: 1px solid var(--color-glass-border); + border-radius: 999px; + } + + &__badge-dot { + width: 7px; + height: 7px; + border-radius: 50%; + background: var(--color-accent); + box-shadow: 0 0 8px var(--color-accent); + animation: heroPulse 2s ease-in-out infinite; + } + &__description { max-width: 900px; - margin: 0 auto 48px; + margin: 0 auto 40px; text-align: center; font-size: 18px; color: var(--color-text-muted); @@ -49,16 +118,141 @@ const { t } = useI18n() } } + &__actions { + display: flex; + align-items: center; + justify-content: center; + flex-wrap: wrap; + gap: 1.25rem; + } + + &__secondary { + display: inline-flex; + align-items: center; + gap: 0.5rem; + + color: var(--color-text-muted); + font-weight: 500; + transition: color 0.25s ease, gap 0.25s ease; + + span { + transition: transform 0.25s ease; + } + + &:hover { + color: var(--color-accent); + + span { + transform: translateX(4px); + } + } + } + &__glow { position: absolute; - top: 20%; + top: 18%; left: 50%; transform: translate(-50%, -50%); - width: 60vw; - height: 60vw; - background: radial-gradient(circle, rgba(0, 245, 255, 0.1) 0%, transparent 70%); + width: 70vw; + height: 70vw; + max-width: 900px; + max-height: 900px; + // Quiet ambient wash — restrained per the design system's hairline language. + background: radial-gradient(circle, rgba(0, 229, 255, 0.05) 0%, transparent 65%); z-index: 0; pointer-events: none; } } + +// Terminal mockup — gives the hero a concrete focal point and shows +// the core "push / ask AI / live" loop in the product's own voice. +.hero-terminal { + position: relative; + z-index: 1; + max-width: 640px; + margin: 4rem auto 0; + + text-align: left; + background: var(--color-terminal); + border: 1px solid var(--color-glass-border); + border-radius: 12px; + overflow: hidden; + backdrop-filter: blur(8px); + box-shadow: var(--shadow-card-hover); + + &__bar { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.75rem 1rem; + background: rgba(255, 255, 255, 0.03); + border-bottom: 1px solid rgba(255, 255, 255, 0.06); + } + + &__dot { + width: 11px; + height: 11px; + border-radius: 50%; + + &--red { background: #ff5f56; } + &--amber { background: #ffbd2e; } + &--green { background: #27c93f; } + } + + &__title { + margin-left: auto; + font-size: 0.75rem; + color: var(--color-text-muted); + letter-spacing: 0.5px; + } + + &__body { + padding: 1.5rem 1.5rem 1.75rem; + // Body inherits the page's Geist now; force mono so the terminal reads + // as a terminal. + font-family: var(--font-mono); + font-size: 0.85rem; + line-height: 1.9; + + @media (max-width: 480px) { + font-size: 0.72rem; + } + } + + &__line { + margin: 0; + + &--muted { color: var(--color-text-muted); } + &--ok { color: var(--color-success); } + } + + &__prompt { color: var(--color-accent); } + &__accent { color: var(--color-accent); } + &__str { color: #ffbd2e; } + + &__cursor { + display: inline-block; + width: 8px; + height: 1em; + margin-left: 4px; + vertical-align: text-bottom; + background: var(--color-accent); + animation: heroBlink 1.1s step-end infinite; + } +} + +@keyframes heroPulse { + 0%, 100% { opacity: 1; transform: scale(1); } + 50% { opacity: 0.4; transform: scale(0.85); } +} + +@keyframes heroBlink { + 0%, 100% { opacity: 1; } + 50% { opacity: 0; } +} + +@media (prefers-reduced-motion: reduce) { + .hero-block__badge-dot, + .hero-terminal__cursor { animation: none; } +} diff --git a/app/components/main/PricingSection.vue b/app/components/main/PricingSection.vue index 8818534..a345720 100644 --- a/app/components/main/PricingSection.vue +++ b/app/components/main/PricingSection.vue @@ -44,7 +44,10 @@ const plans = [ :title="t('pricing.title')" container-size="md" > -