--- version: alpha name: harborview-design description: "A deep teal-green canvas (#16211f) with warm cream ink (#f3ece1), terracotta (#c97b4a) standing for sun and warmth, and a cooler teal (#3f9aa3) standing for water and night. Cormorant Garamond carries every headline at weight 600; Karla carries the surrounding prose. The system reads as an actual small inn's site, unhurried, a little sun-warmed, never a stock-photo chain-hotel booking engine." --- ## Brand Positioning Harborview is for travelers who want four rooms and a real tide table, not four hundred rooms and a loyalty program. It exists because most boutique-hotel sites borrow big-chain booking-engine chrome (carousel heroes, urgency banners, "3 people are looking at this room") that has nothing to do with what a small inn actually offers: a handful of rooms, an honest amenity list, and the water outside. Harborview's bet: the site's one piece of drama should be the sky itself, changing overhead exactly the way it does from a room's real window. Every visual decision below serves that one bet. If a component doesn't help someone pick a room and ask a real question, or doesn't come from a real hospitality interaction (booking calendar, room card, table, form, status), it doesn't belong in this system. colors: accent: "#c97b4a" accent-alt: "#3f9aa3" ink: "#f3ece1" ink-muted: "#a3b0aa" canvas: "#16211f" surface: "#1c2926" surface-raised: "#233330" border: "#2c3d39" focus-ring: "#c97b4a" ## Logo / Wordmark System The mark is a rounded-square horizon card: a horizontal line (the water) with a rising terracotta semicircle above it (the sun clearing the horizon), literally the sun-and-horizon the hero's scroll mechanic animates. It is not a generic icon, it's a 1:1 miniature of the `.horizon` component's opening state. ```svg ``` - **Lockup**: mark, then a `0.5rem` gap, then the wordmark "Harborview" in Cormorant Garamond/600, sentence case, mark always left of the wordmark, never stacked or reversed. - **Minimum size**: mark not smaller than `16px` square, below that the sun arc and horizon line merge. Wordmark not smaller than `0.9rem`. - **Clearspace**: empty margin equal to the mark's own height (22px at default size) on all four sides of the lockup. - **Don't**: recolor the mark for a light background, it only ever renders on `--panel`/`--bg`. Don't swap the sun to the teal or the water line to terracotta, the mark's whole logic is warm-above/cool-below. Don't animate the mark itself, the sun-and-sky motion belongs to the hero, not the nav. Don't rotate or stretch it. ## Color System ### Roles | Role | Token | Value | Used for | |---|---|---|---| | Canvas | `--bg` | `#16211f` | Page background, night end of the sky gradient | | Surface | `--panel` | `#1c2926` | Panels, nav, room cards, table header | | Surface raised | `--surface-raised` | `#233330` | Hover state on a surface (input focus bg, row hover, open stay-card hover) | | Border | `--border` | `#2c3d39` | Panel edges, dividers, input outlines, table rules | | Ink | `--ink` | `#f3ece1` | Primary text, moon disc | | Ink muted | `--dim` | `#a3b0aa` | Secondary text, helper text, placeholders, booked stay-dates | | Sun / warmth | `--accent` | `#c97b4a` | Sun disc, primary button fill, open stay-date status, focus ring | | Water / night | `--accent-alt` | `#3f9aa3` | Horizon line, moon glow tint, confirmed-booking badge, included-amenity status | There is deliberately no red or amber "urgency" color. A four-room inn doesn't create scarcity theater, an open night just reads in terracotta and a booked one just dims, no countdown, no "only 1 left." ### Contrast (computed from the hex values above via WCAG relative luminance) | Pairing | Ratio | Passes | |---|---|---| | `--ink` text on `--bg` | ~14.1:1 | AAA | | `--dim` text on `--bg` | ~7.4:1 | AAA | | `--accent` text on `--bg` (open status, links) | ~5.1:1 | AA (body text) | | `--accent-alt` text on `--bg` (included status, badge) | ~5.0:1 | AA (body text) | | `--bg` label on `--accent` fill (primary button) | ~5.1:1 | AA | | `--ink` on `--panel` | ~12.8:1 | AAA | | `--ink` on `--surface-raised` | ~11.3:1 | AAA | The rule this table exists to enforce: both accents sit right at AA for body text, so neither is ever used for text smaller than `--text-small` (0.85rem) without also pairing it with the `--ink`-on-dark weight the rest of the page carries, and a button filled with `--accent` always labels itself in `--bg`, never `--ink`, which would undershoot AA at that weight. typography: display: fontFamily: Cormorant Garamond fontWeight: 600 body: fontFamily: Karla fontWeight: 400 ## Type Scale | Token | Size | Weight | Family | Line-height | Letter-spacing | Used for | |---|---|---|---|---|---|---| | `--text-display` | `clamp(2.75rem, 7vw, 5rem)` | 600 | Cormorant Garamond | 1.05 | 0 | Hero H1 only | | `--text-h2` | `2rem` (homepage) / `1.75rem` (second page) | 600 | Cormorant Garamond | 1.15 | 0 | Section headings | | `--text-h3` | `1.35rem` | 600 | Cormorant Garamond | 1.2 | 0 | Room card names | | `--text-body` | `1rem` | 400 | Karla | 1.6 | 0 | Paragraph copy | | `--text-small` | `0.85rem` | 400 | Karla | 1.5 | 0.01em | Footer, nav utility links, helper text, table cells | | `--text-date` | `1.75rem` | 600 | Cormorant Garamond | 1 | 0 | The date number inside a stay-card | Rule for serif vs. sans: Cormorant Garamond is reserved for the hero headline, section headings, room names, and the stay-card date number, the moments meant to feel like a printed inn brochure. Everything read quickly at small size (nav, buttons, form labels, table cells, amenity rows) is Karla. If a component's whole job is legibility at a glance, it's Karla, if it's a moment meant to feel considered, it's the serif. ## 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: 10px` for panels, cards, inputs, and table containers. `--radius-pill: 999px` for buttons and the confirmed-booking badge. The sun and moon discs are the one intentional exception, full circles, everything else on the page uses the `--radius`/`--radius-pill` pair. ## Iconography No icon library. The only non-text glyphs are the logo mark, the sun/moon discs, and one Unicode glyph, `✓` (included/confirmed), set at the same size as the text beside it. Room and amenity status is communicated with a plain-language word ("Open", "Booked", "Included") paired with color, never an icon standing in for the word. If a future page needs more iconography, the fallback is Phosphor (line style, `1.5` stroke), that decision just hasn't been needed yet. ## Motion Principles Scrolling the hero moves a sun disc along a fixed horizon arc while the sky gradient behind it shifts continuously from dawn through midday and dusk into night, the sun fading into a moon partway through, all driven by scroll position (`scrub`, not a fixed-duration animation), so the sky is always exactly where the scrollbar is. Below the hero, the week's stay-dates fade and lift into place once, the first time that section enters view. These are the only scripted animations on either page, everything else (buttons, inputs, room-card hovers, table rows) uses only `:hover`/`:active`/`:focus-visible` CSS transitions. The stay-date reveal never replays on repeated scroll passes, and the whole system honors `prefers-reduced-motion` by freezing the sky at a fixed midday frame and skipping the stagger reveal entirely. ## Voice & Tone warm, unhurried, sensory, minimal exclamation. In practice: - **Button label**: "Check availability", not "Book Now and Save!" - **Error message**: "Hold expired. Your 15-minute room hold ran out.", not "Oops! Time's up!" - **Empty state**: "No rooms open for those dates.", not "Sorry, we're all booked! Try different dates?" - **Success status**: "Reserved, The Ketch", not "Woohoo! You're all set! 🎉" ## Accessibility Baseline - Focus ring: `2px solid var(--accent)`, `2px` offset, applied via `:focus-visible` on every interactive element (links, buttons, inputs, the scrollable stay-strip), never removed without a visible replacement. - Reduced motion: the sky scrub, sun/moon crossfade, and stay-card stagger reveal all check `prefers-reduced-motion: reduce`, freezing to a fixed midday sky with the sun visible and all cards shown at full opacity. - Minimum tap target: `44px` in the smallest dimension for every button, input, and stay-card on touch viewports. - Color is never the only signal: open vs. booked stay-dates pair color with the words "Open"/"Booked", amenity status pairs color with "Included"/a specific day, not a bare dot. ## Components ### Navigation Single-line bar, `64px` tall, `--panel` background with a `--border` bottom hairline, sticky to the top. Left: a small "back to gallery" utility link. Center-left: the mark + wordmark lockup. Right: a Rooms link plus one primary button. Collapses to just the lockup and primary button under `480px`, no hamburger, there are only two destinations. ### Buttons Three variants, one radius (`--radius-pill`), two accents used with intent: - **Primary**: `--accent` fill, `--bg` label, used once per view for the single real action ("Check availability"). - **Secondary**: transparent fill, `1px solid --border`, `--ink` label. Per-room "Hold" actions, retries, form submits that aren't the primary conversion. - **Ghost**: text-only, `--dim` label, `--ink` on hover. Tertiary actions like "see open nights." All variants define `:hover` (brightness/border shift), `:active` (`transform: scale(0.98)`), `:disabled` (`opacity: .4`, `pointer-events: none`), and `:focus-visible` (accent outline). ### Stay-date strip A horizontally scrollable row of date cards (`.stay-card`), each showing weekday, date, and a status word. Open cards are interactive (hover lifts the border to `--accent`), booked cards sit at reduced opacity and aren't clickable. `scroll-snap-type: x proximity` keeps the strip from landing mid-card. This is the booking-calendar analogue for a four-room inn, a full month grid would be overkill. ### Room cards A flat `.panel` per room: name in the serif, one line of detail in `--dim`, and a price/action row. No photo, the same reason the whole system avoids stock imagery, the room's own copy has to do the work. ### Table Header row in `--panel`/`--dim` Karla labels, body rows in `--bg`, one `--border` rule between rows, row hover to `--surface-raised`. Used for the amenity list, status column reads "Included" in `--accent-alt` or a specific day in `--dim`, never a checkbox glyph alone. ### Forms Label above input, `--text-small`/`--dim`. Input: `--panel` background, `1px solid --border`, `--radius`, `--ink` text, `--dim` placeholder. On `:focus`, border becomes `--accent` and the focus ring applies. On error, the border becomes `--accent-alt` and the helper text explains what to fix in plain language. ### Status / feedback states - **Loading**: a skeleton shaped like the real content (bars matching text line-heights), not a spinner. - **Empty**: a plain sentence in `--dim`, no illustration. - **Error**: an inline message using `--accent` (not `--accent-alt`, which is reserved for positive/water-adjacent meaning here) with a retry action, never a silent failure. - **Success / status badge**: `--radius-pill`, `1px solid --accent-alt`, `--accent-alt` text on transparent fill, not filled, filled would compete with the primary button's exclusive use of a solid `--accent` fill. ### Footer One row: property name/tagline on the left, real page links on the right (Rooms, back to gallery). No invented legal text, no links to pages that don't exist. ## Do's and Don'ts ### Do - Let the sky gradient and sun/moon arc be the only continuously-scroll-driven element on the page, everything else reveals once. - Reserve `--accent` for warmth/sun/primary-action meaning and `--accent-alt` for water/night/confirmation meaning, keep that split consistent everywhere, not interchangeable. - Pair every status word with color, never color alone. - Use `--bg` as the label color on any `--accent`-filled surface. - Keep room and amenity copy specific (square footage, bed count, which two days), never marketing filler like "cozy" or "charming." ### Don't - Don't add urgency banners, countdown timers, or "X people viewing this room" social-proof chrome, this is a four-room inn, not a chain booking engine. - Don't treat the sun/moon disc's glow as a generic UI accent to prune, it's diegetic light from an actual sun and moon, a mechanical slop-detector will flag it as a "glowing shadow" tell, that's a false positive here, not a fix to make. - Don't use a stock photo anywhere, the room cards and hero stay illustrative/typographic on purpose. - Don't replay the stay-date stagger reveal on every scroll pass, reveal once per view. - Don't stack more than one primary button in the same view. - Don't fill a status badge solid, badges stay outlined so the primary button remains the only solid-fill element on screen. ## Known Constraints - Two routes now exist (home, rooms), still no real checkout or payment flow, no responsive breakpoint table beyond a single-column mobile collapse, no authenticated guest account (the "Check availability" and "Hold" actions are not wired to anything real, this is a concept demo).