--- version: alpha name: foothold-design description: "A deep green canvas (#0d1210) with soft mint ink (#eef5f1), teal (#2bd4a5) as the only positive/progress accent and amber (#ffb454) reserved for 'needs attention' states, there is no red anywhere in this system, on purpose. Space Grotesk carries display moments (headings, milestone titles, the score readout) at weight 700; Inter carries anything meant to be read quickly. The system reads as a career tool that maps you forward, it never grades you down." --- ## Brand Positioning Foothold exists for job seekers who are tired of tools that treat a resume like a test to fail. Most ATS-optimizer products borrow the visual language of a spellchecker, red underlines, error counts, a score that feels like a report card. Foothold's bet: the same information (a match score, a list of gaps) lands better when the interface never implies the reader did something wrong. There is no red token in this palette. A weak keyword match is not an "error," it's a "tweak," rendered in amber, the same color the brand uses for anything worth a second look but nothing to be ashamed of. Every visual decision below serves that one bet: if a component would make someone feel graded instead of guided, it doesn't belong in this system. colors: accent: "#2bd4a5" accent-alt: "#ffb454" ink: "#eef5f1" ink-muted: "#8fa89d" canvas: "#0d1210" surface: "#141d19" surface-raised: "#1b2620" border: "#1f2c26" focus-ring: "#2bd4a5" ## Logo / Wordmark System The mark is a milestone marker on a winding path: a `--border`-stroked rounded square containing an S-curve in `--accent`, ascending bottom-left to top-right, with a filled `--accent` dot sitting at the top of the curve, the "foothold" the product is named for, the point on the path you've actually reached. ```svg ``` - **Lockup**: mark, then a `0.5rem` gap, then the wordmark "Foothold" in Space Grotesk/700, mark always left of the wordmark, never stacked or reversed. Title case, not lowercase, this is a warm consumer brand, not a terminal. - **Minimum size**: mark not smaller than `16px` square, below that the curve and the dot stop reading as separate shapes. Wordmark not smaller than `0.85rem`. - **Clearspace**: empty margin equal to the mark's own height (22px at default size) on all four sides of the lockup, nothing else docks closer, including the nav's edge padding. - **Don't**: recolor the mark for a light or busy background, it only ever renders on `--bg`/`--panel`. Don't stretch it non-uniformly. Don't rotate it or flip the curve's direction, it always ascends left to right, mirroring forward progress. Don't render the dot in `--accent-alt`, amber means "needs a tweak," the mark itself is never in a state of needing anything. Don't use the dot alone as a decorative list bullet, it means "you are here," not "here is an item." ## Color System ### Roles | Role | Token | Value | Used for | |---|---|---|---| | Canvas | `--bg` | `#0d1210` | Page background | | Surface | `--panel` | `#141d19` | Panels, nav, cards, milestone cards, table header | | Surface raised | `--surface-raised` | `#1b2620` | Hover state on a surface (input focus bg, row hover) | | Border | `--border` | `#1f2c26` | Panel edges, dividers, input outlines, table rules | | Ink | `--ink` | `#eef5f1` | Primary text | | Ink muted | `--dim` | `#8fa89d` | Secondary text, timestamps, helper text, placeholders | | Progress / positive | `--accent` | `#2bd4a5` | Path stroke, gauge fill, primary button fill, "looking good" status, focus ring | | Attention | `--accent-alt` | `#ffb454` | "Needs a tweak" state, secondary emphasis, invalid-input state | There is deliberately no red or error-red token anywhere in this system. Every state that a typical product would route to red (an invalid field, a low score, a failed upload) routes to `--accent-alt` amber instead, paired with encouraging copy, never a scolding one. If a future component feels like it wants red, that's a signal the copy needs to change, not the palette. ### Contrast (computed from the hex values above via WCAG relative luminance) | Pairing | Ratio | Passes | |---|---|---| | `--ink` text on `--bg` | ~17:1 | AAA | | `--dim` text on `--bg` | ~7.4:1 | AAA (body text) | | `--accent` text on `--bg` (gauge readout, links) | ~10:1 | AAA | | `--accent-alt` text on `--bg` ("needs a tweak" copy) | ~10.7:1 | AAA | | `--bg` label on `--accent` fill (primary button, gauge center) | ~10:1 | AAA | | `--bg` label on `--accent-alt` fill (amber badge, if ever filled) | ~10.7:1 | AAA | | `--ink` on `--panel` | ~15.5:1 | AAA | | `--dim` on `--panel` | ~6.8:1 | AA (comfortably above 4.5:1) | The one rule this table exists to enforce: a button or badge filled with `--accent` or `--accent-alt` always labels itself in `--bg`, never white or `--ink`. And the rule the whole palette exists to enforce: "needs attention" states use `--accent-alt` amber, never a red this system doesn't have. typography: display: fontFamily: Space Grotesk fontWeight: 700 body: fontFamily: Inter fontWeight: 400 ## Type Scale | Token | Size | Weight | Family | Line-height | Letter-spacing | Used for | |---|---|---|---|---|---|---| | `--text-display` | `clamp(2.5rem, 6vw, 4.5rem)` | 700 | Space Grotesk | 1.05 | -0.01em | Hero H1 only | | `--text-h2` | `1.5rem` | 700 | Space Grotesk | 1.2 | 0 | Section headings | | `--text-h3` | `1.125rem` | 700 | Space Grotesk | 1.3 | 0 | Milestone/card titles | | `--text-score` | `clamp(2.25rem, 5vw, 3.25rem)` | 700 | Space Grotesk | 1 | -0.01em | Gauge and score readouts, tabular numerals | | `--text-body` | `1rem` | 400 | Inter | 1.6 | 0 | Paragraph copy | | `--text-small` | `0.85rem` | 400 | Inter | 1.5 | 0.01em | Footer, nav utility links, timestamps, helper text | Rule for Space Grotesk vs. Inter: Space Grotesk is reserved for a moment worth pausing on, the hero headline, section headings, milestone titles, and the score number itself. Inter carries anything meant to be read quickly and functionally, body copy, nav labels, button labels, form labels and placeholders, table cells. If a piece of copy is the payoff (a heading, a score), it's Space Grotesk, if it's the path to the payoff (a paragraph, a label, an input), it's Inter. ## Spacing Scale Base unit `8px`. Tokens in use: `--space-2: 0.5rem`, `--space-3: 1rem`, `--space-4: 1.5rem`, `--space-6: 2rem`, `--space-8: 3rem`, `--space-12: 4rem`. ## Shape One radius scale: `--radius: 16px` for panels, cards, milestone cards, inputs, and table containers, a little softer than a typical dev-tool radius, this is a warmer brand. `--radius-pill: 999px` for buttons and status badges. Nothing else rounds. ## Iconography No icon library. The only non-text glyphs on the page are the logo mark and two Unicode status glyphs, `✓` (looking good) and `✎` (needs a tweak, a pencil, not a warning triangle, because the next step is a small edit, not an alarm). Both set in Space Grotesk at the same size as the text they sit next to, and always paired with a plain-language word, color is never the only signal. If a future page needs more iconography, the fallback is Phosphor (line style, `1.5` stroke), not hand-drawn SVG paths, that decision just hasn't been needed yet. ## Signature Interaction A winding path draws itself as you scroll, each milestone card flipping open like a resume section; an ATS-match gauge fills live from 0 to a target score. The path is scroll-scrubbed (`ScrollTrigger` with `scrub: true`), it must never auto-play ahead of the reader, at any scroll position the drawn length matches how far down the section the reader actually is. The gauge fills once, on entering view (`once: true`), a single clean count-up with no confetti, no sound, no badge, one confident number is the entire payoff. `prefers-reduced-motion` renders both fully resolved with no animation, the path fully drawn, the gauge already at its target value. ## Voice & Tone Encouraging, direct, no jargon. In practice: - **Button label**: "See your score", not "Unlock your career potential now" - **Needs-a-tweak message** (amber, never red): "Let's tighten this up. Two more measurable outcomes would push this past 90%.", not "Error: insufficient keyword density." - **Empty state**: "Let's get started. Drop in a resume and a job link.", not "No data available." - **Looking-good status**: "Looking good. This one's ready to send.", not "Validation passed." ## Accessibility Baseline - Focus ring: `2px solid var(--accent)`, `2px` offset, applied via `:focus-visible` on every interactive element (links, buttons, inputs), never removed without a visible replacement. - Reduced motion: the path draw, milestone reveal, and gauge count-up all check `prefers-reduced-motion: reduce` and render fully resolved with no animation when it's set. - Minimum tap target: `44px` in the smallest dimension for every button and input on touch viewports, including icon-only or ghost buttons. - Color is never the only signal: the invalid-input state pairs amber with an inline encouraging message, not just an amber border, status pairs a glyph (`✓`/`✎`) with a word, not just a color. ## Components ### Navigation Single-line bar, `68px` tall, `--panel` background with a `--border` bottom hairline, sticky to the top. Left: the mark + wordmark lockup. Center-right: two same-page links (Path, Results sample). Right: one primary button. No dropdown, no hamburger, a one-product site has nothing to collapse into a menu. ### Buttons Three variants, one radius (`--radius-pill`), one positive accent: - **Primary**: `--accent` fill, `--bg` label, used once per view for the single real action ("See your score"). - **Secondary**: transparent fill, `1px solid --border`, `--ink` label. Same-page anchor jumps and lower-emphasis actions, never the primary conversion action. - **Ghost**: text-only, `--dim` label, `--ink` on hover. Tertiary actions like "back." All variants define `:hover` (brightness/border shift), `:active` (`transform: scale(0.98)`), `:disabled` (`opacity: .45`, `pointer-events: none`, no color change, disabled never reads as an error state), and `:focus-visible` (accent outline). Disabled state never relies on color alone, opacity carries it too, and it is never amber or red-adjacent, a disabled button just isn't ready yet, it isn't wrong. ### Hero Left-aligned, not centered: headline, one line of subtext, primary + secondary CTA, sticky in a left column so it stays in view. The winding path begins in the same viewport, in the right column, not below a separate empty hero, so the hero reads as the top of the map rather than a sealed-off banner. On narrow viewports (`860px` and under) the columns stack and the sticky positioning drops. ### Never-list (written before the impeccable-pass fix) 1. No empty two-thirds-of-the-viewport hero, the winding path starts drawing in the same first viewport as the headline, not after a full screen of black space beneath it. 2. No second, redundant "Where you've been" text section separating the hero from the path it introduces, that copy now frames the path directly. ### Milestone cards A flat `.milestone` card: `--panel` background, `1px solid --border`, `--radius`, no shadow, no gradient. Sits along the path with specific, human copy (a real-sounding job title and outcome), never generic corporate-speak ("led high-impact initiatives"). Elevation is communicated by the border, never a drop shadow. ### Score-capture form Label above input, `--text-small`/`--dim`. Input: `--panel` background, `1px solid --border`, `--radius`, `--ink` text, `--dim` placeholder (never placeholder-as-label). On `:focus`, border becomes `--accent` and the focus ring applies. Helper text sits below in `--text-small`/`--dim`. On an invalid entry, the border becomes `--accent-alt` amber (never red) and the helper text is replaced with an encouraging, specific correction ("That doesn't look like an email yet, mind rechecking it?"), never the word "invalid," "error," or "failed." ### Status / feedback states - **Loading**: a skeleton shaped like the real content (bars matching text line-heights), not a spinner. - **Empty**: a plain encouraging sentence in `--dim` ("Let's get started."), no illustration. - **Needs a tweak**: an inline `--accent-alt` message paired with the `✎` glyph and a specific, actionable next step, never framed as a failure. - **Looking good / status badge**: `--radius-pill`, `1px solid` the semantic color, semantic-color text on transparent fill, paired with the `✓` glyph (not a filled badge, filled would compete with the primary button's use of `--accent` as a fill). ### Table Header row in `--panel`/`--dim` Inter labels, body rows in `--bg`, one `--border` rule between rows (not both top and bottom), row hover to `--surface-raised`. Used on the results page for the keyword match breakdown, matched vs. worth-adding, status cells pair the `✓`/`✎` glyph with a plain-language word, never a red "missing" flag. ### Footer One row: product name/tagline on the left, real page links on the right (Results sample, back to gallery). No invented legal text, no links to pages that don't exist. ## Do's and Don'ts ### Do - Route every state a typical product would color red to `--accent-alt` amber instead, paired with encouraging, specific copy. - Let the path feel literally walked, scroll-scrubbed, never auto-playing ahead of the reader. - Make the ATS gauge feel earned, not gamified, one clean fill, no confetti, no sound, no badge. - Keep milestone copy specific and human (real-sounding job titles and outcomes), not generic corporate-speak. - Use `--bg` as the label color on any `--accent`- or `--accent-alt`-filled surface. - Pair every color-coded state with text or a glyph, not color alone. ### Don't - Don't use red or error tones anywhere, on a button, a border, a badge, or a status message, this product is about encouragement, not deficiency. - Don't let the path animation outrun the user's scroll position, it must stay scrub-linked throughout, never auto-play. - Don't gamify the gauge with sound effects, badges, or confetti, one confident number is the payoff. - Don't write invalid-input or low-score copy as a failure ("Error," "Invalid," "Failed"), reframe it as a next step ("let's tighten this up"). - Don't add gradients, glassmorphism, or glossy rounded panels, elevation comes from the border, not a shine. - Don't stack more than one primary button in the same view, one conversion action per screen. ## Known Constraints - Two routes now exist (marketing home, a sample results/report page), still no docs/pricing pages, no responsive breakpoint table beyond a single-column mobile collapse, no authenticated states. The resume-upload and score-capture flows are not wired to a real ATS parser or backend, this is a concept demo, submitting always resolves to the same sample score.