# ConnectIQ phase 1 — the CRM

Workspace knowledge holds the binding rules. This file adds phase-1 specifics and the reference
values those rules apply to; it overrides nothing there.

**ConnectIQ** is an internal operational platform for Giant Pumpkin, a business that sells hardware,
software subscriptions and services into multi-site customers — restaurant and retail chains — and
then physically deploys equipment to each site.

**Phase 1 replaces HubSpot and builds the CRM.** Airtable keeps running operations untouched, and
phase 1 **does not connect to it at all**: no read sync, no `int_*_mirror` table, no Airtable
credential in the application. Locations are ConnectIQ's own `cl_` master data (DR-01) and the sync
that would have carried them was cut with it (DR-18). If a feature seems to need live Airtable data,
**stop and say so** — it is a scope change, not a task.

## Stack

Postgres (Supabase, Pro plan) with row-level security on every table · Supabase Auth, email+password
and Google OAuth, no anonymous sign-in · server functions inside the app, one deployable · React +
TypeScript strict, file-based routing, loader-fetched routes with a query cache · forms on a schema
validator shared with the server · background work as queue tables in Postgres drained by a scheduled
worker · **Vercel** hosting at `connectiq.giant-pumpkin.com`, connected to the production branch only
· **Resend** for email, from `notifications@connectiq.giant-pumpkin.com`.

**Email has two paths.** Supabase Auth mail — invitations, resets — goes over Resend *SMTP*,
configured in Supabase; the built-in sender is capped at two an hour and is not for production.
Application mail — approval requests — goes through `plat_email_queue` and the worker, via the Resend
*HTTP API*. The API key lives in Supabase secrets, never in Lovable and never in the repo.

## Environments and how we work

Three tiers. **Development** — a Supabase *preview* branch per feature, starting empty and seeded from
`seed.sql` (synthetic, committed, minimal). **Staging** — a persistent branch holding untested code
against *a copy of production, unmodified*, set once and allowed to drift. **Production** — `main`,
deployed by Vercel. There is no fourth environment: the M8 migration rehearses **in production**,
wiping and retrying while it is still empty and unused.

**Two data artifacts, never conflated.** `seed.sql` is synthetic and lives in the repo. Staging's
dataset is a restore from production and does not.

**One repository, one Lovable project**, a branch per feature — branches, never forks. Cut every
branch from `staging`, never from `main`, and merge it back into `staging`. Nothing reaches `main`
except `staging`, and the gate between them is a person. Lovable's **active branch belongs to the
project, not the person** — two people cannot drive it on two branches at once. Delete preview
branches when the work merges; they are billed by the hour, and switch back to `main` first.

Work only inside your domain's folder, `src/domains/<domain>/`.

## The prototype

A Lovable MVP exists and is the most detailed description of intended behaviour available. **It is the
specification. It is not the codebase.** It was built by an assistant against a moving brief, so it
also contains decisions nobody made deliberately — a legacy `deals` table alongside the opportunity
model, front-end files acting as sources of truth for data that belongs in the database, permission
bypasses, hardcoded approval checks that always pass. Treat it as *a requirements document with the
answers already sketched in*, not as a design to reproduce.

## Quality bar — every milestone

TypeScript strict with no escape hatches left in · every new table's RLS policies in the same
migration · permissions verified **from the database side**, not by hiding buttons · semantic tokens
only, lint passing · keyboard reachable, visible focus, status never by colour alone · empty, loading
and error states designed rather than defaulted · demoed to a real user of that surface before it is
called done.

**Tested specifically** — money maths (totals, discounts, VAT inclusive and exclusive, term
multiplication, currency conversion) as a standalone module; permission resolution as a matrix per
role; import mapping against fixtures including the malformed rows found in profiling; sync
idempotency, meaning the same payload applied twice changes nothing; quote approval history staying
attached to what was approved. Generated tests — browser
per epic, plus frontend and backend — run alongside. **Generated tests prove the application still
works; hand-written tests prove the numbers are right, and only the second kind blocks a merge on its
own.**

## Visual reference values

The rules are in workspace knowledge. `style-kit/tokens.json` and `style-kit/DESIGN.md` are the
source of truth — read both before a UI change; these are the values at a glance.

**Themes.** Light is the default and every operational screen — `semantic.color`. Dark is the same
application on dark surfaces — `semantic.dark.color`, identical density, grid, radius and hues.
Presentation is decks, reports and customer-facing dashboards only — dark surfaces plus
`presentation.*`.

**Light.** Warm off-white canvas, white surfaces, black primary text, neutral grey secondary and
muted. Primary action is a black background with white text. Orange is brand and active accent; mint green is
selected, success, complete; yellow is warning, pending, scheduled, expiring; red is error,
destructive, severe incident; blue is informational and in-progress.

**Dark.** True black `#000000` canvas bleeding edge to edge. Surfaces `#13151A` cards and panels,
`#1E1E1E` emphasised rows and inline chips, `#0A0B0D` recessed or disabled — keep the blue cast in
`#13151A`, a neutral grey card reads flat. Text white for headings and figures,
`#D2D2D2` body, `#94959D` muted, `#858585` units and footnotes. Primary action inverts to white
background, black text. Elevation is the surface step plus a hairline border, never shadow. Tinted
state backgrounds are ~12% alpha washes of the semantic hue, chart area fills ~32% — never a solid
hex. `warning` is the one hue that shifts by theme: `#E0A83A` dark, `#F1BF00` light. Paused or
de-prioritised is grey at 50% opacity, never red.

**Type.** Inter with the `tokens.json` fallback stack. `font.family.display` — Neue Montreal falling
back to Inter — for display headings, page titles and presentation headings only, never body,
controls or table text. Poppins and IBM Plex Mono are not ConnectIQ. Application text is
**14px** (`font.size.base`) at ~1.4 line height; `font.size.md` no longer exists, use `base` for 14
and `lg` for 16. Dashboard heading 28–32/800 · page title 24/700 · section title 16–18/600–700 · body
14/400 · emphasised label 14/500–600 · table and meta 12 · uppercase data label 11/500 at 0.06em ·
KPI value 32–36/500–600. The mono family is for compact table headers, identifiers, serials and codes.

**Spacing.** 4px base grid, 8px primary rhythm. Use 4, 8, 12, 16, 20, 24, 32, 40, 48, 64.

**Radius, identical in every theme — 4/6/8/12/16/20/pill.** 6px compact tags and small controls · 8px fields and standard
controls · 12px cards, tables, panels · 16–20px dialogs and large floating surfaces · pill for status
badges, compact tags and switches. **Never 10px or 14px** — both were explicitly rejected as
off-scale.

**Elevation and borders.** Cards take a thin neutral border and no shadow; shadows are for menus,
dropdowns, popovers and dialogs. Default border 1px. Focus is a visible 2px orange ring with a small
offset. Selected controls take a pale green background and green border.

**Layout.** Sidebar ~224px, collapsed ~64px. Maximum page width 1440px. Page padding 24px desktop,
16px compact and tablet, 12px mobile. At narrow widths turn the sidebar into a drawer or rail, stack
columns, make tabs horizontally scrollable, and keep tables readable through column priority or
horizontal scroll.

**Controls.** Heights 32px compact, 40px standard, 48px large or customer-facing. Labels above fields,
helper and validation text directly below the field, searchable selects visually matching text
inputs, disabled controls still readable, destructive actions red and explicitly worded.

**Navigation.** Active sidebar items take a quiet neutral or pale green surface with a narrow orange
indicator; active tabs take orange text and a thin orange underline. Inactive is muted or secondary
text, and hover stays subtler than active.

**Feedback.** Inline feedback on pale semantic backgrounds; toasts on white with semantic borders and
icons — mint success, yellow warning, red error, blue information, orange only for workflow guidance
tied to navigation.

**Tables and charts.** Tables compact, bordered, white, 12px body text with 11px uppercase
micro-labels and a very pale neutral background on expanded rows. Chart series follow the semantic
hues, with `modelled` for projected values always carrying a second cue such as a dashed or hatched
fill. Dark neutral tooltips with white text, subtle grid lines, never more than five simultaneous
categorical colours, and always a label or legend so colour is not the only signal.

**Icons and motion.** One outline family, Lucide preferred, at 16px standard, 20px prominent, 24px
major status. Motion is restrained — 120ms hover and press, 180ms standard, 240ms dialogs and drawers
— respects reduced-motion, and never bounces or decorates.

## Where the full detail lives

**Architecture** is binding on anything structural. **Delivery plan** owns scope, milestones, the
data model and the `DR-nn` register. **Development strategy** owns method. A rule changes in the
architecture first; these two knowledge files are regenerated from it.
