d25d482dc2
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
120 lines
5.4 KiB
TypeScript
120 lines
5.4 KiB
TypeScript
import {
|
||
LANDING_GUTTER,
|
||
LANDING_HERO_TOP_PADDING,
|
||
LANDING_SECTION_RHYTHM,
|
||
} from '@/app/(landing)/components/landing-layout'
|
||
|
||
/**
|
||
* Solutions-layout spacing - the single source of truth for every gutter, gap,
|
||
* inset, and reserved dimension used across the solutions-page components.
|
||
*
|
||
* The padding fortress lives here. No solutions component accepts a `className`
|
||
* or `style`, and none hard-codes a spacing value inline. Every measurement a
|
||
* reviewer might want to audit - the horizontal gutter, the inter-section rhythm,
|
||
* the card-grid gaps, the card stack, and the fixed visual-slot dimensions - is
|
||
* named in this one file. To change spacing you edit a constant here; consumer
|
||
* pages only choose from controlled component variants.
|
||
*
|
||
* All values are Tailwind class fragments (not raw numbers) so they compose
|
||
* directly into `className` strings inside the components and stay legible.
|
||
*/
|
||
export const SOLUTIONS_SPACING = {
|
||
/**
|
||
* The one horizontal gutter, owned solely by `SolutionsPage`. Matches the
|
||
* navbar and landing hero exactly (`px-20 max-lg:px-8 max-sm:px-5`) so
|
||
* solutions content starts on the wordmark's vertical line at every
|
||
* breakpoint. Sections and cards never set their own gutter.
|
||
*/
|
||
gutter: LANDING_GUTTER,
|
||
/**
|
||
* Inter-section vertical rhythm - the gap of the `<main>` flex column that
|
||
* `SolutionsPage` owns. Sections carry no vertical margin/padding of their own,
|
||
* so this is the only knob for the space between the hero, logos, and every
|
||
* card row. Tightens on smaller screens in lockstep with the landing `<main>`
|
||
* (`gap-[120px] max-lg:gap-[88px] max-sm:gap-16`).
|
||
*/
|
||
sectionRhythm: LANDING_SECTION_RHYTHM,
|
||
/** Hero text top padding, matching the landing hero at every breakpoint. */
|
||
heroTopPadding: LANDING_HERO_TOP_PADDING,
|
||
/** Vertical stack gap inside the hero header column (headline → description → CTA). */
|
||
heroStack: 'gap-[22px]',
|
||
/** Gap between the hero header column and the full-width hero visual beneath it. */
|
||
heroToVisual: 'gap-12',
|
||
/** Gap between a card row's header block and the card grid beneath it. */
|
||
cardRowHeaderToGrid: 'gap-12',
|
||
/** Vertical stack gap inside a card row's header (title → subtitle → CTA). */
|
||
cardRowHeaderStack: 'gap-5',
|
||
/**
|
||
* Extra top separation for the header CTA over the stack gap. Title and
|
||
* subtitle are one copy group and stay tight; the CTA is a separate action
|
||
* group, so its subtitle→CTA gap lands at 2× the title→subtitle gap
|
||
* (standard: 20px + 20px = 40px; feature: 12px + 12px = 24px).
|
||
*/
|
||
cardRowHeaderCtaGap: 'mt-5',
|
||
cardRowHeaderCtaGapFeature: 'mt-3',
|
||
/** Gap between cards within a card-row grid (both axes). */
|
||
cardGridGap: 'gap-8',
|
||
/** Minimum gap between a card's text block and its visual panel. */
|
||
cardTextToVisual: 'gap-5',
|
||
/** Vertical stack gap inside a card's text block (title → description). */
|
||
cardTextStack: 'gap-2',
|
||
/** Inner padding for feature tiles where copy and the visual slot share one frame. */
|
||
cardFeatureTilePadding: 'p-8 max-lg:p-6',
|
||
} as const
|
||
|
||
/**
|
||
* Readable text measures for the recurring solutions-page copy surfaces.
|
||
* Paragraph width is expressed in characters so line length tracks type size
|
||
* instead of the viewport. `min-w-0` keeps flex items wrapping cleanly on narrow
|
||
* screens, and `w-full` gives centered copy a stable measure.
|
||
*/
|
||
export const SOLUTIONS_TEXT_MEASURE = {
|
||
/** Hero support copy: broad enough for the primary value prop, still under long-form prose width. */
|
||
heroDescription: 'w-full min-w-0 max-w-[58ch]',
|
||
/** Section subtitles: slightly tighter than hero copy so centered headers feel intentional. */
|
||
rowSubtitle: 'w-full min-w-0 max-w-[52ch]',
|
||
/** Card descriptions: short scan lines inside three-up card grids. */
|
||
cardDescription: 'min-w-0 max-w-[38ch]',
|
||
} as const
|
||
|
||
/**
|
||
* Reserved fixed dimensions for the component-owned visual frames. A dropped-in
|
||
* `ReactNode` renders into a frame of exactly these dimensions, so it can never
|
||
* shift surrounding layout (CLS = 0) nor change its own frame padding. The node
|
||
* fills `h-full w-full` inside; it owns nothing about the frame.
|
||
*/
|
||
export const SOLUTIONS_VISUAL = {
|
||
/** Full-width hero visual aspect ratio - reserves height before paint. */
|
||
heroAspect: 'aspect-[16/9]',
|
||
/** Fixed height of a card's visual panel - uniform across every card. */
|
||
cardHeight: 'h-[240px]',
|
||
/**
|
||
* Minimum height for framed feature tiles with copy and future UI in one
|
||
* surface. One value at every breakpoint: the tallest tile vignettes (audit
|
||
* ledger, staging panel) need ~300px of visual slot below the copy block, so
|
||
* shrinking the tile on small screens crops their tops against the slot's
|
||
* `overflow-hidden`.
|
||
*/
|
||
featureTileMinHeight: 'min-h-[440px]',
|
||
} as const
|
||
|
||
/**
|
||
* Feature-tile surface + copy colors. Pages pick a tone per card via
|
||
* {@link SolutionsCardConfig.featureTileTone}; the component maps it here so
|
||
* each tile can diverge without shared hard-coded classes.
|
||
*/
|
||
export const SOLUTIONS_FEATURE_TILE_TONE = {
|
||
light: {
|
||
surface: 'bg-[var(--surface-3)]',
|
||
title: 'text-[var(--text-primary)]',
|
||
description: 'text-[var(--text-muted)]',
|
||
},
|
||
dark: {
|
||
surface: 'bg-[var(--text-secondary)]',
|
||
title: 'text-[var(--text-inverse)]',
|
||
description: 'text-[var(--surface-3)]',
|
||
/** Softer body copy on dark tiles — `#E6E6E6` via `--surface-6` (`#E5E5E5`). */
|
||
descriptionSoft: 'text-[var(--surface-6)]',
|
||
},
|
||
} as const
|