# ShiftOps Design System — llms-full.txt (v1.0) The entire design system in one plain-text file. Source pages: https://design.shiftops.ai/ (foundations, components, brand, voice, getting-started). Tokens ship as https://design.shiftops.ai/tokens.css (CSS custom properties, both themes, keyframes, utilities) and https://design.shiftops.ai/tokens.json (same values as JSON). Index: https://design.shiftops.ai/llms.txt ## Thesis ShiftOps is a command center for event labor — schedules, gates, zones, payroll. The design language keeps the chrome monochrome so the operation can speak: photographs carry the atmosphere, editorial gradients carry the stories, violet appears only when an agent is working, and green means someone actually showed up. The interface is ink; the world brings the color. ## The four rules (no exceptions) 1. Ink is the accent. Primary actions are near-black (--accent: #0a0a0a light / #f5f5f5 dark), never a hue. When the chrome never shouts, a single filled button is loud enough. Every use of real color must be earned. 2. Violet means AI. The --ai token (#6b4fe0 light / #b3a4ff dark) appears in exactly one situation: an agent is perceiving, deciding, or acting. Nothing else may borrow it. If a surface is violet, software did something. 3. Color comes from the world. Palette lives in photography of real labor and grainy editorial gradients — never in the UI chrome itself. The interface frames the event; it doesn't compete with it. 4. Status is sacred. --ok / --warn / --danger report real operational states (filled zone, silent worker, short gate). Never decoration, so always believed. ## Color tokens — 23 roles, every one ships a light and a dark value Dark theme is [data-theme="dark"] on (or on any nested wrapper to force one region). Format: token · light · dark · role. Surfaces & lines - --bg · #ffffff · #08090a · page background - --bg-elev · #ffffff · #101113 · cards, panels, anything raised - --bg-sunken · #fafafa · #0c0d0e · wells, code blocks, stat cards - --line · #e9e9e9 · #1c1d20 · default hairline border - --line-strong · #d8d8d8 · #2a2b2f · hover borders, emphasized rules Ink - --fg · #0a0a0a · #f5f5f5 · headlines, primary text - --fg-secondary · #3a3a3a · #b8b8b8 · body copy in dense UI - --fg-muted · #6b6b6b · #8a8a8a · supporting copy, ledes, metadata - --fg-subtle · #9a9a9a · #5a5a5a · index numbers, faint labels Accent (the accent is ink) - --accent · #0a0a0a · #f5f5f5 · primary actions — near-black, never a hue - --accent-hover · #2a2a2a · #e4e4e4 · primary action hover - --accent-soft · #f4f4f5 · rgba(255,255,255,0.1) · selected states, quiet emphasis fills - --accent-line · #e4e4e7 · rgba(255,255,255,0.18) · borders on accent-soft surfaces - --accent-fg · #ffffff · #0a0a0a · text on accent fills AI (the only hue in the chrome) - --ai · #6b4fe0 · #b3a4ff · only where an agent perceives, decides, or acts - --ai-soft · #f0ecfb · rgba(179,164,255,0.14) · agent surface fills - --ai-line · #ddd3f7 · rgba(179,164,255,0.3) · agent surface borders Status (real states only) - --ok · #067647 · #4ade80 · filled zone, verified check-in, live - --ok-soft · #ecfdf3 · rgba(74,222,128,0.12) · ok pill fill - --warn · #b45309 · #f59e0b · zone trending short, silent worker - --warn-soft · #fef3c7 · rgba(245,158,11,0.12) · warn pill fill - --danger · #b42318 · #f87171 · short gate, failed credential - --danger-soft · #fee4e2 · rgba(248,113,113,0.12) · danger pill fill ## Typography - --font-sans: "Geist", "Geist Variable", ui-sans-serif, system-ui, sans-serif - --font-mono: "Geist Mono", "Geist Mono Variable", ui-monospace, monospace - Fonts are NOT embedded in tokens.css — load Geist/Geist Mono yourself or accept fallbacks. - One family, two voices: sans carries the argument; mono carries the data (timestamps, zone names, fill percentages). Headings run tight and medium-to-semibold, never bold. Scale - display: clamp(2.75rem–5rem) · 600 · tracking −0.035em · line-height 0.98 - h2: clamp(1.875rem–2.25rem) · 500 · −0.025em - h3 / card title: 19px · 500 · −0.015em - lede: 18px · 400 · line-height 1.6 · --fg-muted - body: 16px · 400 · line-height 1.6 - small / meta: 13px · 400 · --fg-muted - eyebrow: mono · 11px · 500 · uppercase · letter-spacing 0.14em · --fg-muted - mono data: 11–12px · 400/500 · tabular-nums ## Radius --radius-xs 4px · sm 6px · md 8px · lg 12px · xl 16px · 2xl 20px · 3xl 28px · full 9999px. Assignments: inline code 4px · swatches 6px · inputs and nav links 8px · mock rows 12px · stat cards 16px · cards and specimens 20px · photo cutouts and hero media 28px · buttons, pills, chips always full. ## Layout & spacing --container: 1280px; gutters 20/24/32px as the viewport grows; everything on a 4px grid. Sections at 96px vertical rhythm (56px on docs pages); cards pad 20–24px. ## Elevation - --shadow-pop: 0 1px 2px rgba(0,0,0,0.04), 0 8px 24px -8px rgba(0,0,0,0.4) — floating chrome (toasts, mega panels). - --shadow-cmd: 0 1px 2px rgba(0,0,0,0.4), 0 16px 48px -16px rgba(0,0,0,0.8) — product mockups, so they read as windows. Everything else is a 1px border (--line, --line-strong on hover). Both shadows are black-based and shared across themes; in dark, the slightly stronger hairlines carry edges. ## Motion --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1) --dur-fast 150ms (hovers, toggles) · --dur-med 300ms (accordions, panels) · --dur-reveal 600ms (fade-up on scroll) · --dur-pulse 2s (live dots) · --dur-marquee 40s (ticker loops). Keyframes shipped in tokens.css: fade-up, pulse-dot, marquee, drift-a/b/c (aurora orbs). Each move has one meaning: fade-up reveals content, pulse-dot means live (not cached) data, marquee carries long lists, aurora drift warms empty hero backgrounds. Respect prefers-reduced-motion (tokens.css disables animate-* classes under it). ## Grain .grain overlays SVG fractal noise (feTurbulence baseFrequency 0.85) at 3.5% opacity — enough to kill banding on gradients and give flat color a printed feel, never enough to read as texture. Every editorial gradient wears it. ## Component inventory (all render from tokens.css variables) Buttons — one filled (ink) button per view. btn-primary: background --accent, text --accent-fg, hover --accent-hover. btn-secondary: --bg-elev with 1px --line-strong border. btn-ghost: transparent, --fg-secondary. All radius-full, 14px/500, padding 10px 20px; optional trailing arrow translates 2px on hover — that's the whole animation. Pills & capability chips — .pill: sans metadata, 12px, bordered, rounded-full. .chip: mono 11px capability with a 6px status dot. .chip-ai flips border/fill to --ai-line/--ai-soft with a violet dot — only for capabilities an agent performs. Status pills — .status-pill status-ok/warn/danger: 10px/500 text on the matching -soft fill with a 6px dot. Add .animate-pulse-dot to mean "this number is live" (2s opacity pulse). Reserved for real states: filled zone, trending short, short gate. Cards — .card: 1px --line border, 20px radius, --bg-elev, 24px padding, border darkens on hover. .stat-card: sunken, 16px radius, 34px/600 number over a 12px label. .update-card: bordered card whose only color is an 8px editorial-gradient top bar; body holds title + category/date meta row. MockFrame — window chrome for every product mockup: three 10px neutral dots, mono lowercase label in "section · view" form (e.g. "deploy · live gap analysis"), optional LIVE ok-pill with pulsing dot, --shadow-cmd elevation, 12px radius. Body rows are bordered 12px-radius strips pairing a zone name with a status pill. Lifecycle scrubber — row of mono time-pills (T-30 days … Day of) switching a caption (time / title / description). Selected pill fills with --accent. Content: the event lifecycle from build-the-event through staff-internal-first, cascade, confirmation sweep, gates open, reconcile. FAQ accordion — 1px dividers, one item open at a time, plus icon rotates 45 degrees. Reveal is a grid-template-rows 0fr→1fr transition over --dur-med with --ease-out-expo — no measured heights, no JS animation. Photo cutout — photos never run full-bleed: 28px-radius bordered frame, bottom-up rgba(0,0,0,0.72) scrim, white 14px/500 caption with a 12px 70%-white subline. Captions name the place and hour ("Gate A, 5:40 a.m."). Mega panel — floating nav panel on --shadow-pop, 16px radius, two columns of eyebrow-labeled groups. Items: 32px mono-glyph tile + title + one-line description. .mi-ai tile goes violet only where an agent does the work. Dark theme — dark is the same tokens flipped by [data-theme="dark"]; nothing is redesigned and no component has a dark variant. Set the attribute on for the whole page, or nest it on a wrapper (painted with background: var(--bg)) to force one region. Photography and gradients are shared across themes; hairlines get slightly stronger in dark (--line #1c1d20 on #08090a) so edges keep reading. ## Brand The wordmark is the sole identity. ShiftOps has no logo symbol: the identity is the text wordmark — "Shift" weight 600 in --fg + "Ops" weight 400 in --fg-muted, set in Geist with letter-spacing −0.02em and no space between. The weight step and the muted "Ops" are the entire logo; it inverts with the theme through the ink tokens. Wordmark spec - type: Geist (or the tokens.css sans fallback stack) - weights: Shift 600 · Ops 400 - color: Shift var(--fg) · Ops var(--fg-muted) - tracking: −0.02em, no space between the words - nav size 20px · minimum 14px (below that the weight contrast stops reading) - clearspace: the height of the "S" on all sides - on photos/gradients: over a scrim only — Shift #fff, Ops rgba(255,255,255,0.7) HTML: ShiftOps Do: Geist, 600+400, −0.02em, no space; "Ops" in --fg-muted; clearspace = height of the "S"; invert via tokens; white-on-scrim over imagery. Don't: add a symbol, icon, or container; recolor "Ops" to a hue (no violet, no green, no gradient); set it in another typeface, one weight, all-caps, or spaced ("Shift Ops"); track out, stretch, outline, or shadow it; render below 14px. Favicon & app icon (not a logo). The three-bar rounded-square tile (24×24 viewBox, rx 6, #0a0a0a square, #fff bars at opacity 1/0.7/0.45, third bar shorter) was never approved as a logo. It survives only as the browser favicon and app icon (16/32px slots) and never appears in product or marketing chrome, decks, documents, or print. Where a layout wants a mark, use the wordmark. Editorial gradients — where the color actually lives. Always worn with .grain. Recipe: three stops, 130–170° angle, mid-saturation start, near-black or dusty finish — event light (dusk over a course, sodium lamps at load-in), never candy. Captions on gradients are white mono at 85% opacity. 1. Agentic operations: linear-gradient(140deg, #8a7bd8 0%, #c77f9e 45%, #e8944a 100%) 2. Vertical physics: linear-gradient(160deg, #7d95a8 0%, #9db4c8 40%, #3d4a55 100%) 3. Trust & fill rate: linear-gradient(150deg, #8a9b6e 0%, #5c7355 50%, #243421 100%) 4. The hidden tax: linear-gradient(160deg, #e8c48a 0%, #c08f4a 50%, #6b4a20 100%) 5. The fragmented stack: linear-gradient(160deg, #c77f9e 0%, #a86a7f 50%, #552a3a 100%) 6. Labor elasticity: linear-gradient(135deg, #8a7bd8 0%, #9d8fc7 45%, #c8a0b8 100%) Photography — real labor, shot dark. Real crews, real venues, working light: floodlit gates, dawn load-ins, concourse sodium glow. Low ambient light, one strong practical source, deep shadow detail. Never full-bleed — always inside a 28px-radius bordered cutout with a bottom-up black scrim and a white caption naming place and hour. Never bright stock imagery, posed team shots, unscrimmed text on photos, or color grades that fight the token palette. Photography and gradients are shared across light and dark themes. ## Voice Write like the ops trailer: certain about what it knows, specific about what happened, done talking before the radio crackles. Principles 1. Confident — say the thing flatly ("Orchestrate the entire workforce"). No hedges, no "helps you," no "designed to." 2. Concrete — name the zone, the hour, the dollar ("Club Level, 18 of 20, T-1 day"). Abstract benefit language means the writer doesn't know what the product did. 3. Short — sentences that fit on a radio; headlines under eight words. Numbers over adjectives: never "high fill rates" — "96% fill rate." Never "significant savings" — "12% less labor burn." Never "fully auditable" — "every clock-in location-verified." An adjective is a claim; a number is a receipt. No real number? Describe the mechanism instead. Banned words: seamless · leverage · empower · unlock · simply · just · easy. Each is a placeholder for a fact the writer didn't have — replace the word with the fact. Never tell an operator at hour fourteen of load-in that their job is easy. Example rewrites (before → after) - "Our platform seamlessly empowers you to unlock workforce efficiency." → "One demand plan. Five labor sources fill it. You watch coverage climb from a golf cart." - "We leverage cutting-edge AI to simply optimize your labor operations." → "At T-1 day the agent calls, texts, and re-offers every silent shift. A human signs off before any zone is cut." - "Trusted by leading venues for reliable, end-to-end staffing solutions." → "96% fill rate. 12% less labor burn. Every clock-in location-verified." Glossary (use exactly; never invent synonyms) - internal-first fill: ranked shift offers go to your own people before anyone else — by certification, prior performance at the venue, and cost against budget. Every internal acceptance is a shift with no markup. - true gaps: the open shifts that survive internal fill; the only demand that goes external. - the cascade: true gaps fan out as structured orders to agencies and marketplaces at once — one order to three, four, or more partners, each commitment tracked from placed to filled. - verified hours: timestamped, location-verified clock-ins and clock-outs; the unit every invoice and payroll run is built from. - one rate rail: every hour carries its true cost (wage or bill rate, markup, burden, premiums) through one pipeline from schedule to invoice to payroll. - zones & service locations: the physical units of the plan (Gate A, Club Level, Concourse, Suites, Paddock); coverage, cost, and status always report by zone. - confirmation sweep: the T-1 day pass where app, text, and AI-agent calls confirm every scheduled worker; silent shifts are re-offered. Agent-performed — so it wears violet in the UI. ## Getting started One stylesheet, no build step: The variable contract — write variables, never hex: - --bg / --bg-elev / --bg-sunken: page, raised panels, recessed wells. Depth = these three + a border, not shadows. - --line / --line-strong: every border is 1px in one of these; -strong is hover/emphasis. - --fg / --fg-secondary / --fg-muted / --fg-subtle: four ink tiers; hierarchy by tier, never opacity hacks. - --accent family: primary actions and selected states. The accent is ink; there is no brand hue to substitute. - --ai family: violet, only where an agent perceives, decides, or acts. Never links, focus rings, or "premium" styling. - --ok / --warn / --danger (+ -soft): real operational states only, never decoration. - --radius-xs…3xl + full; --container 1280px. Buttons and pills always radius-full. - --shadow-pop and --shadow-cmd are the only shadows. - --ease-out-expo; --dur-fast/med/reveal/pulse/marquee. Respect prefers-reduced-motion. Dark theme wiring — set data-theme="dark" on and every token flips. Persist to localStorage ("shiftops-theme") and apply in before the stylesheets so the page never flashes: (function () { var t = localStorage.getItem("shiftops-theme") || (matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"); document.documentElement.setAttribute("data-theme", t); })(); — End of llms-full.txt. Every page on the site also has a "Copy for LLM" cluster with a per-page Markdown digest, tokens.css, tokens.json, this file, and llms.txt.