chore: project scaffold, specs, and working record
Build and deploy / build-and-deploy (push) Failing after 5s
Build and deploy / build-and-deploy (push) Failing after 5s
This commit is contained in:
@@ -0,0 +1,344 @@
|
||||
# 01 — Information architecture
|
||||
|
||||
Authority: `AGENTS.md` §3 D5 (full multi-page), §6, §5 (audience model).
|
||||
Every claim in the copy outlines below must clear `AGENTS.md` §4.
|
||||
|
||||
---
|
||||
|
||||
## Why multi-page at all
|
||||
|
||||
The site being replaced is one scrolling page. One page can hold one title, one
|
||||
meta description, one canonical URL, and one primary topic. It therefore gets
|
||||
one shot at a search result.
|
||||
|
||||
The practice's target searches are not "Toronto mediator" — that term is owned by
|
||||
retired judges with twenty years of name recognition, and the strategy brief is
|
||||
explicit that competing there is the wrong game (§II). The winnable searches are
|
||||
specific: *construction lien mediation Ontario*, *SaaS contract arbitration
|
||||
Canada*, *SABS mediation Toronto*, *technology dispute neutral*, *Farsi-speaking
|
||||
mediator*. Each of those wants its own page, its own title, its own copy, and its
|
||||
own structured data.
|
||||
|
||||
That is the entire argument for the structure below. It is a discoverability
|
||||
decision, not an aesthetic one.
|
||||
|
||||
---
|
||||
|
||||
## Sitemap
|
||||
|
||||
```
|
||||
/ Home
|
||||
/about/ Biography, credentials, the professional record
|
||||
/mediation/ Mediation — the process, formats, rules
|
||||
/arbitration/ Arbitration — the process, tracks, rules
|
||||
/med-arb/ Med-Arb and hybrid processes
|
||||
/practice/ Practice areas index
|
||||
/practice/construction/ Construction and infrastructure disputes
|
||||
/practice/technology/ Technology, AI, and data disputes
|
||||
/practice/energy/ Energy, grid, and regulatory disputes
|
||||
/practice/insurance/ Insurance, SABS, and accident benefits
|
||||
/practice/shareholder/ Shareholder, partnership, and family business
|
||||
/practice/cross-cultural/ Cross-border and diaspora disputes
|
||||
/process/ What an engagement looks like, step by step
|
||||
/fees/ Fee schedule and engagement terms
|
||||
/for-parties/ Plain language: what mediation actually is
|
||||
/insights/ Article index
|
||||
/insights/[slug]/ Individual articles
|
||||
/contact/ Intake form and booking
|
||||
/legal/privacy/ Privacy policy — PIPEDA
|
||||
/legal/terms/ Terms of use
|
||||
```
|
||||
|
||||
Nineteen fixed URLs plus one per article.
|
||||
|
||||
### URL rules
|
||||
|
||||
- Lowercase, hyphenated, trailing slash, no file extensions.
|
||||
- `/practice/<area>/` is a stable namespace — new practice areas slot in without
|
||||
touching anything else.
|
||||
- `/insights/<slug>/` — no dates in the path. A dated URL makes a piece look
|
||||
stale at 18 months, and this content is mostly evergreen.
|
||||
- Never change a published URL. If one must move, ship a CloudFront Function
|
||||
301 and record it in the Change Log.
|
||||
|
||||
### Navigation
|
||||
|
||||
**Primary (header).** About · Mediation · Arbitration · Practice · Fees ·
|
||||
Insights · Contact
|
||||
|
||||
"Practice" is a dropdown to the six areas, with `/practice/` itself reachable.
|
||||
Build it as a `<details>` element or a CSS-only disclosure — no JavaScript.
|
||||
|
||||
**Footer.** Full sitemap in three columns, plus contact block, professional
|
||||
designations, LinkedIn, privacy, terms, and the SML Company Ltd. entity line.
|
||||
|
||||
**Deliberately not in primary nav:** `/process/`, `/for-parties/`, `/med-arb/`.
|
||||
These are linked contextually from the pages that lead to them. Seven items is
|
||||
the ceiling before a nav stops being scannable.
|
||||
|
||||
---
|
||||
|
||||
## Deliberate omission: Indigenous engagement
|
||||
|
||||
The strategy brief (§III.4) rates Indigenous engagement, IBA, and consultation-
|
||||
breakdown mediation as *"strategically the most valuable single niche"* for a
|
||||
Q.Med on the C.Med-Arb pathway.
|
||||
|
||||
There is no page for it at launch, on the following reasoning:
|
||||
|
||||
The brief itself says the niche *"requires deliberate relationship work with
|
||||
First Nations advisors, federal and provincial engagement staff, and corporate
|
||||
proponents over a multi-year horizon."* A practice page is a claim of present
|
||||
capability. Publishing one before that relationship work exists would be read as
|
||||
exactly what it is by the audience best positioned to notice — and that audience
|
||||
is small, well-connected, and unforgiving of practitioners who arrive claiming a
|
||||
seat. The cost of getting this wrong is much higher than the cost of waiting.
|
||||
|
||||
Revisit at month 12–18, once there is relationship history to point to.
|
||||
**This reasoning is Claude's, recorded for Pouya's decision — not yet his call.**
|
||||
|
||||
---
|
||||
|
||||
## Page specifications
|
||||
|
||||
Each page below gives its job, its primary audience, its target search intent,
|
||||
and its section outline. Copy itself is in `03-content-spec.md`.
|
||||
|
||||
### `/` — Home
|
||||
|
||||
**Job:** establish the unusual stack in under ten seconds, and route each of the
|
||||
four audiences to its surface.
|
||||
**Audience:** all four; leans in-house counsel.
|
||||
**Search intent:** brand and name searches; "Toronto ADR practice".
|
||||
|
||||
1. **Hero.** Eyebrow (`Mediation · Arbitration · Toronto`), display headline,
|
||||
two-sentence positioning paragraph, two CTAs (*Request a consultation* /
|
||||
*How I work*), portrait.
|
||||
2. **Credential row.** Three slots: `Q.Med` · `JD + ML` · `EN · FA`. Never
|
||||
matter counts — `AGENTS.md` §4.
|
||||
3. **The approach.** The "two directions at once" argument — law and engineering
|
||||
converging on the same dispute. Infinity mark as the visual anchor.
|
||||
4. **Two practices.** Mediation and Arbitration cards → `/mediation/`, `/arbitration/`.
|
||||
Med-Arb named here as the long-term arc, linking to `/med-arb/`.
|
||||
5. **Practice areas.** Six-card grid → `/practice/*`. This is the most important
|
||||
block on the page for search, because it distributes authority to the pages
|
||||
that can actually rank.
|
||||
6. **Process preview.** Compressed five-step strip → `/process/`.
|
||||
7. **Latest insights.** Three most recent → `/insights/`.
|
||||
8. **Contact band.** Intake CTA and booking link.
|
||||
|
||||
### `/about/` — Biography and credentials
|
||||
|
||||
**Job:** be the page an appointing body or opposing counsel reads before agreeing
|
||||
to an appointment. This page carries the verifiable record.
|
||||
**Audience:** appointing bodies, ADR institutions, opposing counsel.
|
||||
**Search intent:** `"Pouya Lajevardi"`, `Pouya Lajevardi mediator`.
|
||||
|
||||
1. Portrait, name, designation line.
|
||||
2. **Narrative biography**, 400–600 words. The three-track story — law,
|
||||
engineering, operating a company — told as one arc rather than three lists.
|
||||
3. **Credentials**, structured and scannable: designations, education,
|
||||
certifications, memberships. Every line from `AGENTS.md` §4 Verified.
|
||||
4. **The credentialing arc.** Q.Med held → Q.Arb in progress → C.Med-Arb as the
|
||||
endpoint. The brief (§V) treats the arc itself as part of the story; say so
|
||||
openly rather than implying a finished state.
|
||||
5. **Languages and cross-cultural practice.**
|
||||
6. **Speaking and publications.** Omit the section entirely until there is
|
||||
something in it. An empty "Speaking" heading is worse than no heading.
|
||||
7. `Person` JSON-LD. Downloadable one-page PDF bio — brief §VIII lists this as
|
||||
an asset for circulation with appointment proposals.
|
||||
|
||||
### `/mediation/`
|
||||
|
||||
**Job:** convert counsel who have already decided on mediation and are choosing a
|
||||
neutral.
|
||||
**Search intent:** `commercial mediator Toronto`, `ADRIC mediation rules`,
|
||||
`what happens at mediation Ontario`.
|
||||
|
||||
1. What the service is; the neutral's role stated plainly.
|
||||
2. **Formats:** full-day, half-day, shuttle, remote, hybrid.
|
||||
3. **Rules:** ADRIC Model Mediation Rules, or a bespoke protocol agreed by the
|
||||
parties.
|
||||
4. **What parties should bring** — briefs, documents, authority to settle.
|
||||
5. **Confidentiality and without-prejudice framing.**
|
||||
6. Practice areas → `/practice/*`.
|
||||
7. Fees → `/fees/`. Booking → `/contact/`.
|
||||
|
||||
### `/arbitration/`
|
||||
|
||||
**Job:** the same, for arbitration — and to state the Q.Arb position honestly.
|
||||
**Search intent:** `sole arbitrator Ontario`, `expedited arbitration Canada`,
|
||||
`documents-only arbitration`.
|
||||
|
||||
1. What the service is; sole-arbitrator, party-appointed, and tribunal-secretary
|
||||
appointments.
|
||||
2. **Tracks:** documents-only, expedited, full hearing.
|
||||
3. **Rules:** ADRIC, ADR Chambers, ad hoc.
|
||||
4. Awards — form, reasoning, timing.
|
||||
5. **Credentialing status, stated plainly.** The Q.Arb pathway is in progress;
|
||||
the page says so and describes what is available now (co-arbitration,
|
||||
tribunal secretary) versus what follows designation. Honesty here is a
|
||||
differentiator, not a weakness — and misstating it is a conduct problem.
|
||||
6. Fees, booking.
|
||||
|
||||
### `/med-arb/`
|
||||
|
||||
**Job:** own a term few Canadian neutrals explain well, and frame the C.Med-Arb
|
||||
endpoint.
|
||||
**Search intent:** `med-arb Canada`, `what is med-arb`, `arb-med`.
|
||||
|
||||
1. What Med-Arb is; how it differs from Arb-Med.
|
||||
2. The procedural fairness objection, addressed head-on rather than elided.
|
||||
3. When it fits and when it does not.
|
||||
4. The C.Med-Arb designation and why it is the practice's stated endpoint.
|
||||
|
||||
This page is a strong candidate for the best-performing page on the site.
|
||||
Search demand exists, competition is thin, and it maps exactly to the brand's
|
||||
long-term narrative.
|
||||
|
||||
### `/practice/` — index
|
||||
|
||||
Six cards, one paragraph each, linking onward. Also the natural home for the
|
||||
"also offered" strip: early neutral evaluation, settlement counsel, dispute-
|
||||
system design, and pre-dispute technical advisory.
|
||||
|
||||
### `/practice/construction/`
|
||||
|
||||
**Search intent:** `construction lien mediation Ontario`, `delay claim mediation`,
|
||||
`subcontract dispute arbitration Toronto`.
|
||||
|
||||
Dispute types (lien, delay, change orders, scheduling, subcontract, deficiency);
|
||||
what an active litigation practice in the same matters brings to the room; the
|
||||
Ontario megaproject pipeline as context — Darlington SMR, Bruce C, data centres,
|
||||
transit; typical process shape. Strongest immediate fit per brief §III.1.
|
||||
|
||||
### `/practice/technology/`
|
||||
|
||||
**Search intent:** `SaaS dispute arbitration Canada`, `AI vendor dispute`,
|
||||
`data residency dispute resolution`, `software contract mediator`.
|
||||
|
||||
The differentiator page. Dispute types: software contracts, SLA and MSA
|
||||
breakdowns, data residency and processing, AI vendor diligence, cloud
|
||||
sub-processor disputes, IP and licensing.
|
||||
|
||||
**Write this page in the register the brief demands:** a neutral who can read an
|
||||
API trace, a model card, or a System Impact Assessment on the same page as the
|
||||
contract. The brief warns explicitly against softening this to "technologically
|
||||
literate" — the claim is engineering practice, so the copy says engineering
|
||||
practice.
|
||||
|
||||
### `/practice/energy/`
|
||||
|
||||
**Search intent:** `Bill 40 dispute`, `IESO dispute resolution`,
|
||||
`OEB leave to construct dispute`, `grid connection dispute Ontario`.
|
||||
|
||||
Grid connection and allocation, leave-to-construct, proponent–municipality
|
||||
disputes, IESO market participation, data-centre connection allocation. Brief
|
||||
§III.2 frames this as a 24–36 month build. **Write it as a genuine position, not
|
||||
a claim of existing volume.**
|
||||
|
||||
### `/practice/insurance/`
|
||||
|
||||
**Search intent:** `SABS mediation`, `LAT pre-hearing mediation`,
|
||||
`accident benefits mediator Ontario`, `MIG dispute`.
|
||||
|
||||
Highest realistic near-term volume — it flows directly from the existing
|
||||
personal-injury and SABS practice, and brief §IV.7 notes the segment is
|
||||
underserved by senior mediators. Unglamorous and worth doing well.
|
||||
|
||||
### `/practice/shareholder/`
|
||||
|
||||
**Search intent:** `shareholder dispute mediation Ontario`,
|
||||
`partnership dissolution mediator`, `family business succession dispute`.
|
||||
|
||||
Shareholder and partnership disputes, co-founder breakdowns, family-business
|
||||
succession, SME exits. The operator angle — running SML Company Ltd. alongside
|
||||
the practice — is the differentiator here.
|
||||
|
||||
### `/practice/cross-cultural/`
|
||||
|
||||
**Search intent:** `Farsi speaking mediator Toronto`,
|
||||
`Iranian Canadian business dispute`, `diaspora shareholder dispute`.
|
||||
|
||||
Note that D4 makes the site English-only. This page describes Farsi-language
|
||||
capability in English; it is not a Farsi page. Diaspora family-business
|
||||
succession, dual-jurisdiction shareholder disputes, partnership disputes among
|
||||
diaspora entrepreneurs, cross-cultural commercial matters.
|
||||
|
||||
### `/process/`
|
||||
|
||||
Five steps, from intake to conclusion: confidential intake (day 0) · engagement
|
||||
and framing (1–7) · pre-session exchange (7–21) · the session (21–30) · binding
|
||||
conclusion (30+). Also: conflicts checking, confidentiality, and what happens if
|
||||
a matter does not settle.
|
||||
|
||||
### `/fees/`
|
||||
|
||||
**Blocked on `AGENTS.md` Q4 — do not invent numbers.**
|
||||
|
||||
Hourly rate; half-day and full-day mediation; preparation time policy;
|
||||
cancellation terms; administrative fee; HST treatment; who pays and how costs
|
||||
are shared between parties; payment terms. A real page with real numbers, or a
|
||||
`TODO(pouya)` — nothing in between.
|
||||
|
||||
### `/for-parties/`
|
||||
|
||||
**Job:** serve the self-represented tier without diluting the counsel-facing
|
||||
pages. Plain language, short sentences, no jargon.
|
||||
|
||||
What mediation is · what it is not · who the mediator is and is not (not your
|
||||
lawyer, not a judge) · what happens on the day · what it costs · what happens if
|
||||
you do not settle · how to prepare.
|
||||
|
||||
### `/insights/` and `/insights/[slug]/`
|
||||
|
||||
Astro content collection, MDX. Index reverse-chronological with topic filtering
|
||||
by practice area.
|
||||
|
||||
Article frontmatter: `title`, `description`, `publishDate`, `updatedDate`,
|
||||
`topics[]`, `practiceAreas[]`, `readingTime`, `draft`.
|
||||
|
||||
Content territories, from brief §VII: process explainers · regulatory commentary ·
|
||||
industry-specific dispute commentary · anonymised reflections · technical
|
||||
explainers for lawyers · credentialing and career-arc content.
|
||||
|
||||
`Article` JSON-LD with `author` pointing at the `Person` entity. Each article
|
||||
links to the relevant practice-area page — this is what turns the blog into
|
||||
ranking power for the pages that convert.
|
||||
|
||||
**The section stays out of primary navigation until at least two pieces are
|
||||
live.** An empty blog signals abandonment more loudly than no blog signals
|
||||
anything.
|
||||
|
||||
### `/contact/`
|
||||
|
||||
Intake form (`05-backend-spec.md`), booking embed, direct email and phone
|
||||
(Q3), Toronto by-appointment line, response-time expectation, and an explicit
|
||||
note that submitting the form does not create a retainer or a mediator–party
|
||||
relationship and does not itself create a conflict check.
|
||||
|
||||
### `/legal/privacy/` and `/legal/terms/`
|
||||
|
||||
Required, not optional — the intake form collects personal information about
|
||||
identifiable third parties in live legal disputes. What is collected, why, where
|
||||
it is stored (DynamoDB, region), retention period, who can access it, how to
|
||||
request deletion, and the contact for privacy inquiries. Must match what the
|
||||
backend actually does.
|
||||
|
||||
---
|
||||
|
||||
## Build order
|
||||
|
||||
Dependency-ordered, so nothing is blocked mid-stream:
|
||||
|
||||
1. Scaffold, tokens, base layout, header, footer, SEO component
|
||||
2. `/` — proves the design system end to end
|
||||
3. `/about/` — the credential spine everything else references
|
||||
4. `/mediation/`, `/arbitration/`, `/med-arb/`
|
||||
5. `/practice/` and the six area pages
|
||||
6. `/process/`, `/for-parties/`
|
||||
7. `/insights/` plumbing, then the drafted articles
|
||||
8. `/contact/` and the intake backend
|
||||
9. `/fees/` — last, since it is blocked on Q4
|
||||
10. `/legal/*` — written to match the backend as actually built
|
||||
11. Audit and cutover (`06-deployment.md`)
|
||||
@@ -0,0 +1,195 @@
|
||||
# 02 — Design system
|
||||
|
||||
Authority: `AGENTS.md` §3 D7 — *keep the palette and the infinity mark;
|
||||
modernize the execution.* The look is not up for redesign. What follows is the
|
||||
system that preserves it while fixing what the old build got wrong.
|
||||
|
||||
---
|
||||
|
||||
## What carries over unchanged
|
||||
|
||||
- The **palette**: cream, ink, maroon, gold.
|
||||
- The **infinity mark** — SML Company Ltd.'s actual logo, and the metaphor holds:
|
||||
a dispute is a loop, and the work is redrawing the loop into a line.
|
||||
- The **type pairing**: Instrument Serif for display, Geist for text, Geist Mono
|
||||
for eyebrows and labels.
|
||||
- The **editorial register** — generous whitespace, restrained colour, serif
|
||||
display type at large sizes.
|
||||
|
||||
## What changes
|
||||
|
||||
| Was | Is | Why |
|
||||
|---|---|---|
|
||||
| Google Fonts at runtime | Self-hosted, subset, preloaded | Removes a render-blocking third-party round trip from a page that collects legal inquiries |
|
||||
| Fixed px type sizes | Fluid `clamp()` scale | One scale from 360 px to 1600 px with no breakpoint jumps |
|
||||
| Ad hoc spacing values | 8 px base scale | Consistent vertical rhythm; no magic numbers |
|
||||
| Gold used as a text colour on cream | Gold restricted to decorative and on-dark | **It fails WCAG AA at 2.10:1.** Measured, not assumed |
|
||||
| Scroll-reveal on every element, always on | Reveal on major sections only, gated behind `prefers-reduced-motion` | Motion that reads as confident rather than decorative; accessible by default |
|
||||
| 2.2 MB single file, ~1 MB of base64 logos | Optimized SVG mark, AVIF/WebP photography | The mark is geometry; it should be vector, not a 470 KB PNG |
|
||||
| React 18 dev build + Babel Standalone in the browser | Static HTML, near-zero JS | The reason the site is invisible to crawlers |
|
||||
|
||||
---
|
||||
|
||||
## Colour
|
||||
|
||||
Tokens live in `src/styles/tokens.css`. Never write a raw hex value in a
|
||||
component.
|
||||
|
||||
| Token | Value | Use |
|
||||
|---|---|---|
|
||||
| `--cream` | `#faf7f2` | Page background |
|
||||
| `--cream-2` | `#f3ede0` | Alternating section background |
|
||||
| `--cream-3` | `#ebe3d1` | Cards and insets on cream |
|
||||
| `--ink` | `#1a1614` | Body text; dark section backgrounds |
|
||||
| `--ink-soft` | `#3a322c` | Secondary text |
|
||||
| `--muted` | `#6e6359` | Metadata, captions — **on cream only** |
|
||||
| `--maroon` | `#5a1a1c` | Primary action, accents, dark panels |
|
||||
| `--maroon-d` | `#3d1112` | Hover on maroon |
|
||||
| `--maroon-l` | `#7a2a2c` | Links on cream |
|
||||
| `--gold` | `#c9a876` | Rules, dividers, on-dark accent — **never text on cream** |
|
||||
| `--gold-d` | `#a88858` | Large decorative text on cream only |
|
||||
| `--gold-l` | `#e2c89a` | Text on ink or maroon |
|
||||
|
||||
### Contrast — measured, 2026-08-25
|
||||
|
||||
Against `--cream` `#faf7f2`:
|
||||
|
||||
| Colour | Ratio | AA body (4.5) | AA large (3.0) |
|
||||
|---|---|---|---|
|
||||
| `--ink` `#1a1614` | **16.81** | pass | pass |
|
||||
| `--ink-soft` `#3a322c` | **11.75** | pass | pass |
|
||||
| `--maroon` `#5a1a1c` | **12.29** | pass | pass |
|
||||
| `--maroon-l` `#7a2a2c` | **8.95** | pass | pass |
|
||||
| `--muted` `#6e6359` | **5.47** | pass | pass |
|
||||
| `--gold-d` `#a88858` | **3.11** | **FAIL** | pass |
|
||||
| `--gold` `#c9a876` | **2.10** | **FAIL** | **FAIL** |
|
||||
|
||||
Against `--maroon` `#5a1a1c`: cream 12.29 · gold-l 8.11 · gold 5.84 — all pass.
|
||||
Against `--ink` `#1a1614`: cream 16.81 · gold-l 11.09 · gold 8.00 — all pass.
|
||||
`--muted` on ink measures **3.07 and fails**; use `--gold-l` or cream at reduced
|
||||
opacity for secondary text on dark.
|
||||
|
||||
**Hard rules.**
|
||||
|
||||
1. `--gold` is never a text colour on cream. Rules, borders, dividers, icon
|
||||
strokes, and on-dark text only.
|
||||
2. `--gold-d` on cream only at 24 px+ / 19 px bold, and only for decorative
|
||||
display text — never for anything a reader must parse.
|
||||
3. Body text on cream is `--ink` or `--ink-soft`. Metadata may use `--muted`.
|
||||
4. Secondary text on dark is `--gold-l` or `--cream` at ≥ 70% opacity.
|
||||
5. **No dark mode.** A single committed light identity is the right call for a
|
||||
legal practice, and halving the surface area halves the ways contrast breaks.
|
||||
|
||||
---
|
||||
|
||||
## Typography
|
||||
|
||||
**Faces.** Instrument Serif (display) · Geist 300/400/500/600 (text) ·
|
||||
Geist Mono 400/500 (eyebrows, labels, data).
|
||||
|
||||
Self-host all three. Subset to Latin + Latin Extended-A. `font-display: swap`.
|
||||
Preload only the two faces used above the fold — Instrument Serif regular and
|
||||
Geist 400.
|
||||
|
||||
**Scale.** Fluid, `clamp()`, `1.25` ratio at the small end widening to `1.333` at
|
||||
the display end. Tokens `--text-xs` through `--text-6xl` in `tokens.css`.
|
||||
|
||||
**Rules.**
|
||||
|
||||
- Display type (`--font-serif`) at `--text-4xl` and above only. It has almost no
|
||||
hinting at small sizes and looks weak below 32 px.
|
||||
- Display line-height `0.95`–`1.05`; letter-spacing `-0.02em`.
|
||||
- Body line-height `1.6`. Measure capped at `68ch` — the old site ran full-bleed
|
||||
paragraphs at 1400 px, which is unreadable.
|
||||
- Eyebrows: mono, 11–12 px, `0.18em` tracking, uppercase, always paired with a
|
||||
real heading. An eyebrow is not a heading and never carries the `<h*>`.
|
||||
- Italic display (`.it`) is the one flourish the design allows. One italic phrase
|
||||
per headline, at most.
|
||||
- Never skip a heading level. `<h1>` once per page.
|
||||
|
||||
---
|
||||
|
||||
## Spacing and layout
|
||||
|
||||
8 px base: `--space-1` `4px` · `-2` `8` · `-3` `12` · `-4` `16` · `-5` `24` ·
|
||||
`-6` `32` · `-7` `48` · `-8` `64` · `-9` `96` · `-10` `128` · `-11` `160`.
|
||||
|
||||
Content width `1280px`; prose measure `68ch`; wide media `1440px`.
|
||||
Gutters: `24px` mobile, `48px` desktop.
|
||||
Section rhythm: `--space-9` (96px) mobile, `--space-11` (160px) desktop.
|
||||
|
||||
Grid: 12 columns desktop, 6 tablet, 4 mobile, `--space-5` gutter.
|
||||
|
||||
---
|
||||
|
||||
## Motion
|
||||
|
||||
The old build animated nearly everything on scroll. The replacement is
|
||||
deliberate and quiet.
|
||||
|
||||
- **Reveal on section entry only** — not on every child element. Sub-element
|
||||
stagger is limited to card grids, and capped at six children.
|
||||
- Duration `600ms`, easing `cubic-bezier(.2,.7,.2,1)`. Transform and opacity
|
||||
only — never layout properties.
|
||||
- Implement with `IntersectionObserver` in one tiny inline script, or
|
||||
`animation-timeline: view()` where supported. Not a framework, not a library.
|
||||
- **Content is visible without JavaScript.** The reveal is an enhancement layered
|
||||
on top of already-rendered HTML. If the observer never runs, the page reads
|
||||
normally. The old build had this exactly backwards.
|
||||
- Hover transitions `250ms`.
|
||||
|
||||
```css
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*, *::before, *::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
scroll-behavior: auto !important;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Nothing animates infinitely. Nothing autoplays. Nothing moves on page load
|
||||
except the reveal of the hero.
|
||||
|
||||
---
|
||||
|
||||
## Components
|
||||
|
||||
| Component | Notes |
|
||||
|---|---|
|
||||
| `InfinityMark` | Inline SVG, `currentColor`, `aria-hidden` when decorative. Never a PNG |
|
||||
| `SiteHeader` | Sticky, condenses on scroll. Practice dropdown as CSS-only `<details>` |
|
||||
| `SiteFooter` | Three-column sitemap, contact block, designations, entity line |
|
||||
| `Eyebrow` | Mono label with optional maroon dot |
|
||||
| `SectionHeading` | Eyebrow + display heading + optional lede, one measure |
|
||||
| `Button` | Variants `primary` (maroon) · `ghost` (outlined) · `gold` (ink bg, gold-l text). Renders `<a>` or `<button>` correctly |
|
||||
| `Pill` | Small bordered label for designations and sector chips |
|
||||
| `CredentialRow` | Three or four credential slots. **Never matter counts** — `AGENTS.md` §4 |
|
||||
| `PracticeCard` | Sector chip, heading, one paragraph, arrow link |
|
||||
| `ProcessStep` | Numbered step, timing, body |
|
||||
| `ArticleCard` | Title, description, date, topic pills, reading time |
|
||||
| `Prose` | Long-form wrapper. Owns all typographic defaults for MDX |
|
||||
| `SEO` | Title, description, canonical, OG, Twitter, JSON-LD — see `04-seo-spec.md` |
|
||||
|
||||
**Focus states.** Every interactive element gets a visible focus ring:
|
||||
`outline: 2px solid var(--maroon); outline-offset: 3px`. Use `:focus-visible`.
|
||||
Never `outline: none` without a replacement — the old build removed it globally.
|
||||
|
||||
---
|
||||
|
||||
## Accessibility floor
|
||||
|
||||
Not a polish pass. A build requirement.
|
||||
|
||||
- One `<h1>` per page; heading levels never skipped.
|
||||
- Landmarks: `<header>`, `<nav>`, `<main>`, `<footer>`. Skip-to-content link first
|
||||
in tab order.
|
||||
- Every image has `alt`. Decorative images get `alt=""`.
|
||||
- Colour never carries meaning alone.
|
||||
- All functionality reachable by keyboard; focus order matches visual order.
|
||||
- Forms: real `<label>` elements, `aria-describedby` for hints, errors announced
|
||||
with `role="alert"` and tied to their field.
|
||||
- Touch targets ≥ 44 × 44 px.
|
||||
- Test at 200% zoom and at 320 px width.
|
||||
- Every page must be readable and navigable with JavaScript disabled.
|
||||
@@ -0,0 +1,198 @@
|
||||
# 03 — Content and voice
|
||||
|
||||
Authority: `AGENTS.md` §4 (claim register) and §5 (audience model).
|
||||
Source material: `PL_ADR_Personal_Branding_Strategy_Brief.docx` (2026-05-26) and
|
||||
`ADR_Site_Content_Brief_for_Claude_Design.md` (2026-05-26).
|
||||
|
||||
---
|
||||
|
||||
## The one rule
|
||||
|
||||
**Every factual claim traces to `AGENTS.md` §4 Verified.** Read the Forbidden
|
||||
table before writing any number, statistic, or superlative. If you need a fact
|
||||
you do not have, write `TODO(pouya): <exact question>` and log it in §9. Do not
|
||||
infer, do not soften, do not carry anything over from the old site.
|
||||
|
||||
---
|
||||
|
||||
## Voice
|
||||
|
||||
**Restrained, precise, and unhedged.** The reader is usually a lawyer. They
|
||||
detect padding instantly and discount everything after it.
|
||||
|
||||
**Do:**
|
||||
|
||||
- Short declaratives. "I read the contract and the code." Not "clients benefit
|
||||
from a uniquely multidisciplinary perspective."
|
||||
- First person singular. This is a practitioner brand — "I", not "we", not "the
|
||||
practice". The old site's "we" implied a firm that does not exist.
|
||||
- Concrete nouns. *Lien claim. Change order. System Impact Assessment. Model
|
||||
card. Minutes of settlement.* Specificity is the credential.
|
||||
- Name the limits. "Sole-arbitrator appointments follow the Q.Arb designation;
|
||||
co-arbitration and tribunal-secretary work is available now." Precision about
|
||||
what you cannot yet do makes the rest believable.
|
||||
- Plain words over Latin. "Without prejudice" survives because it is a term of
|
||||
art; *inter alia* does not.
|
||||
|
||||
**Do not:**
|
||||
|
||||
- **Any claim or implication of legal licensure.** D13: the site asserts the JD
|
||||
and nothing more. Never "lawyer", "called to the bar", "licensed", "my law
|
||||
practice", "my litigation practice", "my clients", "acts for", "represents".
|
||||
Implication counts as much as assertion.
|
||||
|
||||
**The approved phrasing is "active litigation exposure" or "involvement in
|
||||
litigation and ADR matters" — never "practice" in that context.** Pouya's
|
||||
wording, 2026-08-26. So: *Director of Firm Operations at a Toronto litigation
|
||||
and ADR boutique, with active exposure to construction, personal injury, POA,
|
||||
and SABS matters.* Accurate, specific, and it claims nothing it should not.
|
||||
|
||||
This framing is **interim** — see `AGENTS.md` §12 R1. Raise it with Pouya
|
||||
rather than letting it settle in by default.
|
||||
- Superlatives. No "leading", "premier", "top-rated", "best". LSO marketing rules,
|
||||
and they read as insecure.
|
||||
- Outcome language that could be read as a guarantee.
|
||||
- "Passionate", "dedicated", "committed", "proven track record", "results-driven",
|
||||
"leverage", "synergy", "solutions".
|
||||
- Hedges that erase the claim. The strategy brief warns specifically against
|
||||
softening the technical claim to "technologically literate" — **the claim is
|
||||
engineering practice, so the copy says engineering practice.**
|
||||
- Em-dash-heavy rhythm and tricolon padding. One idea per sentence.
|
||||
- Second-person sales copy on counsel-facing pages. `/for-parties/` is the one
|
||||
page written to "you".
|
||||
|
||||
---
|
||||
|
||||
## The core positioning statement
|
||||
|
||||
Reused, adapted, across the hero, the About page, and the PDF bio:
|
||||
|
||||
> The dispute resolution practice of Pouya Lajevardi — a credentialed neutral
|
||||
> who is also a working litigator and a practising machine-learning and
|
||||
> infrastructure engineer. Built for commercial, construction, energy,
|
||||
> technology, and cross-cultural disputes that turn on facts most neutrals take
|
||||
> on faith: the contract, the code, the engineering documents, and the
|
||||
> regulatory overlay around them.
|
||||
|
||||
Every version of this must survive the §4 check. It does: each element is
|
||||
verified.
|
||||
|
||||
## Approved headline options
|
||||
|
||||
From the content brief; all three sit honestly with the practice.
|
||||
|
||||
1. *A mediator who reads the contract, the code, and the room.* — **recommended.**
|
||||
The cleanest one-sentence statement of the moat, and rare because it is rare.
|
||||
2. *Engineered for the cases that don't fit a courtroom.*
|
||||
3. *Disputes resolved by someone who has been on every side of one.*
|
||||
|
||||
## The credential row
|
||||
|
||||
Three slots, never counts:
|
||||
|
||||
| Slot | Value | Label |
|
||||
|---|---|---|
|
||||
| 1 | **Q.Med** | ADRIC / ADRIO designation |
|
||||
| 2 | **JD + ML** | Law and engineering |
|
||||
| 3 | **EN · FA** | Bilingual practice |
|
||||
|
||||
Fourth slot where the layout has one: **Q.Arb** — in progress.
|
||||
|
||||
The substitution principle (`AGENTS.md` §4): wherever the design wants a "how
|
||||
many", substitute a longer-arc credential. These are all true at launch and stay
|
||||
true; none grows by closing files.
|
||||
|
||||
---
|
||||
|
||||
## Per-page copy notes
|
||||
|
||||
### Home
|
||||
Hero headline from the approved list. Positioning paragraph above. CTAs:
|
||||
*Request a consultation →* and *How I work*. The approach section makes the
|
||||
"two directions at once" argument — law and engineering converging on the same
|
||||
dispute — and keeps the infinity metaphor: *disputes are loops; the work is
|
||||
redrawing the loop into a line.* First person: "my mark", not "our mark".
|
||||
|
||||
### About
|
||||
400–600 words of narrative, then structured credentials. Tell the three tracks
|
||||
as one arc, not three lists: a JD and an active litigation practice; a parallel
|
||||
career in machine learning and infrastructure engineering; a company run
|
||||
alongside both. The arc is the point — the credentialing pathway from Q.Med
|
||||
through Q.Arb to C.Med-Arb is stated openly as in progress. The brief treats
|
||||
that arc as part of the story rather than something to obscure.
|
||||
|
||||
Omit any section that would be empty. No "Speaking" heading until there is a
|
||||
talk to list.
|
||||
|
||||
### Mediation / Arbitration / Med-Arb
|
||||
Procedural, specific, unembellished. Name the rules. Describe the formats. State
|
||||
what a party should expect to do and when. On `/arbitration/`, state the Q.Arb
|
||||
position in plain terms — what is available now versus what follows designation.
|
||||
|
||||
`/med-arb/` addresses the procedural-fairness objection directly: the same
|
||||
neutral who heard a party's confidential caucus later decides the matter. Do not
|
||||
elide it. Explain the consent mechanics and when the process is inappropriate.
|
||||
Meeting the strongest objection is what makes the page worth reading.
|
||||
|
||||
### Practice areas
|
||||
Each page: dispute types, why this practice fits, what the process looks like,
|
||||
and the market context that makes the area live. Context comes from the strategy
|
||||
brief §III–IV — Ontario's megaproject pipeline, Bill 40 and grid connection, the
|
||||
2026 privacy statute, LAT volumes.
|
||||
|
||||
**Frame as positioning, not as history.** "Built to facilitate procurement and
|
||||
subcontract disputes on Ontario's megaproject pipeline" — not "extensive
|
||||
experience resolving". The first is true and forward-looking. The second is
|
||||
neither.
|
||||
|
||||
### Process
|
||||
Five steps with real timing. Say what happens if the matter does not settle —
|
||||
counsel want to know the downside shape before they commit a client's day.
|
||||
|
||||
### Fees
|
||||
**Blocked on Q4.** Real numbers or `TODO(pouya)`. Plain table, no "starting from"
|
||||
evasions, no "contact for pricing" after promising a rate card.
|
||||
|
||||
### For parties
|
||||
The one page in second person. Grade-9 reading level. Short sentences. Says
|
||||
explicitly: the mediator is not your lawyer and cannot give you legal advice; the
|
||||
mediator does not decide who is right. Answers what it costs and what happens if
|
||||
you do not settle.
|
||||
|
||||
### Insights
|
||||
1,200–1,800 words, monthly cadence (brief §VIII). Territories from §VII:
|
||||
process explainers · regulatory commentary · industry dispute commentary ·
|
||||
anonymised reflections · technical explainers for lawyers · credentialing content.
|
||||
|
||||
Every piece links to at least one practice-area page. Anonymised reflections must
|
||||
be genuinely unidentifiable — not merely name-stripped. If a matter could be
|
||||
recognised by the parties to it, it does not run.
|
||||
|
||||
### Launch article slate (D9)
|
||||
Drafted by Claude, **every word reviewed by Pouya before publication**:
|
||||
|
||||
1. *What the Ontario data-centre build-out means for dispute resolution* —
|
||||
technology + construction; the strongest single differentiator piece.
|
||||
2. *When Med-Arb is the right answer, and when it is not* — process explainer;
|
||||
feeds `/med-arb/`; high search intent, thin competition.
|
||||
3. *Bill 40 and grid connection: a dispute-resolution read* — regulatory
|
||||
commentary; establishes the energy niche.
|
||||
4. *What a System Impact Assessment actually evaluates* — technical explainer for
|
||||
lawyers; the clearest demonstration of the claim the whole brand rests on.
|
||||
5. *Choosing a neutral: what counsel should actually ask* — evergreen, useful,
|
||||
and it makes the case for this practice without arguing for it.
|
||||
|
||||
---
|
||||
|
||||
## Compliance checklist — before any page ships
|
||||
|
||||
- [ ] Every factual claim appears in `AGENTS.md` §4 Verified
|
||||
- [ ] No matter counts, settlement rates, dollar figures, or time-to-award stats
|
||||
- [ ] No testimonials, endorsements, or third-party quotes
|
||||
- [ ] No superlatives and no guarantee language
|
||||
- [ ] No claim or implication of legal licensure anywhere (D13)
|
||||
- [ ] Q.Arb described as commenced August 2026, never as held or nearly complete
|
||||
- [ ] Nothing implies a firm, a team, or offices that do not exist
|
||||
- [ ] Contact page states that an inquiry creates no retainer and no
|
||||
mediator–party relationship
|
||||
- [ ] Any comparative claim is factual and verifiable
|
||||
@@ -0,0 +1,132 @@
|
||||
# 04 — Discoverability
|
||||
|
||||
The problem this project exists to fix. `AGENTS.md` §2 has the measurements: a
|
||||
server-side fetch of the live site returns three words.
|
||||
|
||||
---
|
||||
|
||||
## The baseline being replaced
|
||||
|
||||
| | Now `[verified 2026-08-25]` | Target |
|
||||
|---|---|---|
|
||||
| Content in server HTML | `SML Company · DISPUTE RESOLUTION · Unpacking...` | Every word |
|
||||
| Indexable pages | 1 | 19 + articles |
|
||||
| `<title>` | `SML Company · Dispute Resolution` — pre-rebrand placeholder | Unique per page |
|
||||
| Meta description | none | Unique per page |
|
||||
| `<meta viewport>` | **absent** | Present |
|
||||
| Canonical URL | none | Every page |
|
||||
| OG / Twitter tags | none | Every page |
|
||||
| Structured data | none | Person, LegalService, Article, FAQ, Breadcrumb |
|
||||
| `robots.txt` | 403 | Served |
|
||||
| Sitemap | none | Generated at build |
|
||||
| Favicon | none | Full set |
|
||||
|
||||
Astro's static output solves most of this by existing. The rest is the spec below.
|
||||
|
||||
## Why static output matters more than usual here
|
||||
|
||||
Google can sometimes render client-side JavaScript. Bing largely does not.
|
||||
LinkedIn's preview crawler does not. Slack's unfurler does not. **And the
|
||||
crawlers behind AI assistants — increasingly how counsel and in-house teams
|
||||
find a neutral — generally do not.**
|
||||
|
||||
A site that requires three CDN round trips and an in-browser Babel compile before
|
||||
producing a sentence is invisible to all of them. That is the whole argument for
|
||||
D1.
|
||||
|
||||
---
|
||||
|
||||
## Metadata
|
||||
|
||||
Every page passes through one `SEO` component. A page without it is not finished.
|
||||
|
||||
```
|
||||
title 50–60 chars, unique. Pattern: "<Page> · Pouya Lajevardi"
|
||||
Home: "Pouya Lajevardi · Mediation & Arbitration · Toronto"
|
||||
description 140–160 chars, unique, written for a human, not stuffed
|
||||
canonical absolute, https, trailing slash
|
||||
og:title/description/image/url/type/site_name/locale (en_CA)
|
||||
twitter:card summary_large_image
|
||||
robots index,follow — except /legal/* which is noindex,follow
|
||||
```
|
||||
|
||||
**OG images:** 1200 × 630. Generate at build with `satori` or `astro-og-canvas`
|
||||
using the site's own type and palette. One template: display headline on cream,
|
||||
infinity mark, designation line. Never a screenshot.
|
||||
|
||||
## Structured data
|
||||
|
||||
JSON-LD only. Validate against Google's Rich Results Test before cutover.
|
||||
|
||||
| Type | Where | Notes |
|
||||
|---|---|---|
|
||||
| `Person` | `/about/`, referenced site-wide | `name`, `jobTitle`, `description`, `alumniOf` (Bond University), `knowsLanguage` (en, fa), `hasCredential` (Q.Med), `sameAs` (LinkedIn — **Q12**), `image`, `worksFor` |
|
||||
| `LegalService` | Home | `areaServed` Toronto/Ontario, `serviceType` Mediation/Arbitration, `provider` → Person, `priceRange` once `/fees/` is real |
|
||||
| `Service` | Each practice page | `serviceType`, `provider` → Person, `areaServed` |
|
||||
| `Article` | Each article | `headline`, `description`, `datePublished`, `dateModified`, `author` → Person, `image` |
|
||||
| `BreadcrumbList` | All nested pages | Matches visible breadcrumbs |
|
||||
| `FAQPage` | `/for-parties/`, `/med-arb/` | Only where the visible page genuinely is Q&A. Never fabricate questions to farm a rich result |
|
||||
|
||||
**`hasCredential` must reflect reality.** Q.Med is held. Q.Arb is not. Marking an
|
||||
unheld credential as held in structured data is a misrepresentation that happens
|
||||
to be machine-readable.
|
||||
|
||||
## Crawlability
|
||||
|
||||
**`public/robots.txt`:**
|
||||
|
||||
```
|
||||
User-agent: *
|
||||
Allow: /
|
||||
Disallow: /legal/
|
||||
|
||||
Sitemap: https://adr.smlcompany.ca/sitemap-index.xml
|
||||
```
|
||||
|
||||
Do not block AI crawlers. Being read by an assistant that a general counsel is
|
||||
using to shortlist neutrals is the point.
|
||||
|
||||
**Sitemap:** `@astrojs/sitemap`, excluding `/legal/*` and any `draft: true`
|
||||
article. Submit to Google Search Console and Bing Webmaster Tools at cutover.
|
||||
|
||||
**Internal linking.** Every practice page links to `/mediation/` and
|
||||
`/arbitration/`; those link back to the practice areas; every article links to
|
||||
at least one practice page. This is what turns Insights into ranking power for
|
||||
the pages that convert. Breadcrumbs on every nested page.
|
||||
|
||||
**404 page.** Real, styled, with search-intent links out. CloudFront must return
|
||||
it with a genuine 404 status — not a 200, which the S3 website-endpoint pattern
|
||||
gets wrong by default.
|
||||
|
||||
## Performance
|
||||
|
||||
Core Web Vitals are a ranking input, and the current build fails all of them.
|
||||
|
||||
| Metric | Budget |
|
||||
|---|---|
|
||||
| LCP | < 2.0 s, Slow 4G |
|
||||
| CLS | < 0.05 |
|
||||
| INP | < 150 ms |
|
||||
| JS per route | < 100 KB |
|
||||
| Lighthouse (mobile) | ≥ 95 all four categories |
|
||||
|
||||
How: static HTML, self-hosted preloaded subset fonts, AVIF/WebP with explicit
|
||||
dimensions, critical CSS inlined, no third-party scripts on any page except the
|
||||
booking embed on `/contact/` — and that one is lazy-loaded behind a click.
|
||||
|
||||
## Local and professional presence
|
||||
|
||||
Not code, but it belongs in the launch checklist: Google Business Profile for the
|
||||
practice; ADRIC and ADRIO directory listings pointing at the site; a LinkedIn
|
||||
profile whose headline and Featured section match the brand (brief §VIII);
|
||||
consistent name, address, and phone across all of them.
|
||||
|
||||
## Post-launch verification
|
||||
|
||||
- [ ] `curl -s https://adr.smlcompany.ca/ | grep -c "<h1"` returns ≥ 1
|
||||
- [ ] Every page renders its full text with JavaScript disabled
|
||||
- [ ] Rich Results Test passes on Person, LegalService, Article
|
||||
- [ ] OG preview renders correctly in LinkedIn Post Inspector and Slack
|
||||
- [ ] Sitemap submitted to Google Search Console and Bing
|
||||
- [ ] No page returns 200 for a URL that should 404
|
||||
- [ ] Lighthouse ≥ 95 mobile on `/`, `/about/`, one practice page, one article
|
||||
@@ -0,0 +1,175 @@
|
||||
# 05 — Intake, booking, and data handling
|
||||
|
||||
Authority: `AGENTS.md` §3 D10 — rebuilt intake form plus calendar booking.
|
||||
Existing infrastructure is documented in `AWS-Hosting-Guide.md` Parts 8–10.
|
||||
**Read that guide before changing anything**; the resources already exist and
|
||||
were built by hand in the console.
|
||||
|
||||
---
|
||||
|
||||
## What exists today
|
||||
|
||||
API Gateway (HTTP API) → Lambda → DynamoDB, with SES for notification email and
|
||||
a verified sender on `smlcompany.ca`. `[verified 2026-08-25 — AWS-Hosting-Guide.md]`
|
||||
|
||||
The shape is right. This is a hardening and rework pass, not a replacement.
|
||||
|
||||
## What this data actually is
|
||||
|
||||
The form collects, in a live legal dispute: the inquirer's identity and contact
|
||||
details, the names of opposing parties and their counsel, the nature of the
|
||||
dispute, and often the amounts at issue.
|
||||
|
||||
That is **personal information about identifiable third parties who have not
|
||||
consented and do not know the submission happened.** It is more sensitive than a
|
||||
typical contact form by a wide margin, and it is potentially conflict-relevant.
|
||||
Design accordingly. Nothing in this section is optional.
|
||||
|
||||
---
|
||||
|
||||
## Form fields
|
||||
|
||||
| Field | Type | Required | Notes |
|
||||
|---|---|---|---|
|
||||
| Name | text | yes | |
|
||||
| Email | email | yes | Validated server-side, not only in the browser |
|
||||
| Phone | tel | no | |
|
||||
| Role | select | yes | Counsel · In-house · Party · Institution · Other |
|
||||
| Firm / organisation | text | no | |
|
||||
| Process sought | select | yes | Mediation · Arbitration · Med-Arb · ENE · Not sure |
|
||||
| Practice area | select | yes | The six areas plus Other |
|
||||
| Other parties | text | no | Surfaced for conflicts screening |
|
||||
| Opposing counsel | text | no | Same |
|
||||
| Matter summary | textarea | yes | 2000 char cap. Hint: *no privileged or confidential detail* |
|
||||
| Timing | select | no | Urgent · 30 days · 90 days · Exploring |
|
||||
| Preferred contact | radio | no | Email · Phone |
|
||||
| Consent | checkbox | **yes** | Explicit, unchecked by default, links to `/legal/privacy/` |
|
||||
|
||||
**Do not collect** dollar amounts, document uploads, or anything the inquirer
|
||||
might reasonably treat as privileged. The intake call is for that.
|
||||
|
||||
### Consent text
|
||||
|
||||
> I consent to Pouya Lajevardi storing and using the information in this form to
|
||||
> respond to my inquiry and to run a conflicts check. I understand that
|
||||
> submitting this form does not create a retainer, does not appoint a neutral,
|
||||
> and does not itself establish a mediator–party relationship.
|
||||
|
||||
## Validation and abuse control
|
||||
|
||||
Client-side validation is a convenience. **The Lambda re-validates everything.**
|
||||
|
||||
- Required fields present; email well-formed; lengths within bounds
|
||||
- Reject any field over its cap rather than truncating silently
|
||||
- **Honeypot** field, hidden from sighted and screen-reader users, must be empty
|
||||
- **Timestamp check** — reject submissions completed in under 3 seconds
|
||||
- **Rate limit** by source IP at API Gateway: 5 requests / 5 minutes
|
||||
- No CAPTCHA. It is a third-party script on a page collecting legal information,
|
||||
and the two controls above stop the traffic that matters
|
||||
- CORS restricted to `https://adr.smlcompany.ca` — no wildcard
|
||||
- Strip HTML from every field before storage and before it enters an email body
|
||||
|
||||
## Storage
|
||||
|
||||
DynamoDB, `ca-central-1` — **Canadian data residency is a real selling point for
|
||||
a Canadian legal practice, and the privacy policy will say so.** Confirm the
|
||||
existing table's region and migrate if it is elsewhere (**Q10**).
|
||||
|
||||
| Attribute | |
|
||||
|---|---|
|
||||
| `pk` | `INTAKE#<uuid>` |
|
||||
| `sk` | `<ISO-8601 timestamp>` |
|
||||
| fields | as above |
|
||||
| `sourceIp`, `userAgent` | abuse investigation only |
|
||||
| `ttl` | epoch seconds — **automatic deletion** |
|
||||
|
||||
**Encryption at rest** with a customer-managed KMS key. **Point-in-time recovery
|
||||
on.** Table access limited to the Lambda role and one named administrative
|
||||
principal.
|
||||
|
||||
### Retention
|
||||
|
||||
**24 months, enforced by DynamoDB TTL.** Not a policy someone remembers — a
|
||||
mechanism that runs whether anyone remembers or not.
|
||||
|
||||
Rationale: long enough to serve conflicts screening across a normal matter
|
||||
lifecycle; short enough to be defensible under PIPEDA's requirement to retain
|
||||
personal information only as long as necessary. Whatever number ships must match
|
||||
`/legal/privacy/` exactly.
|
||||
|
||||
## Notification
|
||||
|
||||
SES on submission:
|
||||
|
||||
- **To Pouya:** the full submission, plainly formatted, replyable to the inquirer.
|
||||
- **To the inquirer:** confirmation of receipt, expected response time, a repeat
|
||||
of the no-retainer language, and a link to the privacy policy. This email is
|
||||
the reason the form beats a `mailto:` link.
|
||||
|
||||
SES must have SPF, DKIM, and DMARC aligned on `smlcompany.ca` or these land in
|
||||
spam. The guide covers domain verification; **DMARC needs confirming (Q3)**.
|
||||
|
||||
Failure handling: SES failure must never lose the submission. Write to DynamoDB
|
||||
first, then send. A dead-letter queue on the Lambda, and a CloudWatch alarm on
|
||||
DLQ depth ≥ 1.
|
||||
|
||||
## Booking
|
||||
|
||||
An embedded scheduler for the 30–45 minute confidential intake call
|
||||
(**Q5** — tool not yet chosen).
|
||||
|
||||
- Prefer a provider with Canadian or EU data residency and no advertising
|
||||
business. Cal.com self-hosted is the strongest privacy posture; Cal.com cloud
|
||||
or Calendly are acceptable.
|
||||
- **Lazy-load behind a click.** No third-party iframe on first paint, and no
|
||||
third-party script on any other page.
|
||||
- Provide a plain link fallback that works with JavaScript disabled.
|
||||
- The booking page must carry the same no-retainer language.
|
||||
- Disclose the provider by name in `/legal/privacy/`.
|
||||
|
||||
## Security headers
|
||||
|
||||
Set at CloudFront via a response-headers policy:
|
||||
|
||||
```
|
||||
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
|
||||
X-Content-Type-Options: nosniff
|
||||
Referrer-Policy: strict-origin-when-cross-origin
|
||||
Permissions-Policy: geolocation=(), microphone=(), camera=(), interest-cohort=()
|
||||
Content-Security-Policy: default-src 'self'; img-src 'self' data:;
|
||||
style-src 'self' 'unsafe-inline'; script-src 'self';
|
||||
frame-src <booking-provider>; form-action 'self' <api-endpoint>;
|
||||
base-uri 'self'; frame-ancestors 'none'
|
||||
```
|
||||
|
||||
Tighten CSP once the booking provider is chosen. `unsafe-inline` on styles is
|
||||
tolerable for critical CSS; `unsafe-inline` on scripts is not — use a hash or
|
||||
nonce for the reveal script.
|
||||
|
||||
## Privacy policy must state
|
||||
|
||||
Written to match what is actually built, not what is typical:
|
||||
|
||||
What is collected · why · lawful basis (consent) · where it is stored (DynamoDB,
|
||||
region, encrypted at rest) · **the retention period and that deletion is
|
||||
automatic** · who can access it · third parties involved (AWS, SES, the booking
|
||||
provider, analytics if any) · how to request access or deletion and the address
|
||||
to use · that submitting the form creates no retainer and no mediator–party
|
||||
relationship · cookie and analytics disclosure · last-updated date.
|
||||
|
||||
If analytics ship, prefer a cookieless privacy-preserving tool (Plausible,
|
||||
Fathom). GA4 on a page collecting legal-dispute information is a poor fit for a
|
||||
practice whose privacy posture is part of its offer (**Q11**).
|
||||
|
||||
## Definition of done
|
||||
|
||||
- [ ] Server-side validation independent of the client
|
||||
- [ ] Honeypot and timing checks live; rate limit configured
|
||||
- [ ] CORS restricted to the production origin
|
||||
- [ ] TTL set and verified by test record
|
||||
- [ ] KMS encryption and PITR enabled
|
||||
- [ ] Both emails send; SPF/DKIM/DMARC aligned; inbox-tested, not spam-tested
|
||||
- [ ] DLQ and CloudWatch alarm configured
|
||||
- [ ] Form usable by keyboard only; errors announced with `role="alert"`
|
||||
- [ ] Form degrades to a `mailto:` fallback with JavaScript disabled
|
||||
- [ ] Privacy policy matches the implementation line for line
|
||||
@@ -0,0 +1,274 @@
|
||||
# 06 — Deployment and cutover
|
||||
|
||||
Authority: `AGENTS.md` §3 D3 (git + GitHub Actions → existing S3/CloudFront) and
|
||||
D11 (build everything, one clean cutover).
|
||||
Existing infrastructure: `AWS-Hosting-Guide.md`.
|
||||
|
||||
---
|
||||
|
||||
## Topology
|
||||
|
||||
```
|
||||
GitHub push to main
|
||||
└─ GitHub Actions
|
||||
├─ npm ci && npm run build → ./dist
|
||||
├─ assume AWS role via OIDC (no stored keys)
|
||||
├─ aws s3 sync ./dist s3://<bucket>
|
||||
└─ cloudfront create-invalidation
|
||||
Namecheap DNS → CloudFront → S3 (OAC)
|
||||
API Gateway → Lambda → DynamoDB / SES (intake, unchanged path)
|
||||
```
|
||||
|
||||
DNS is at **Namecheap, not Route 53** `[verified 2026-08-25]`. Nothing in the
|
||||
pipeline touches DNS. Certificate renewal is ACM-automatic as long as the
|
||||
validation CNAME stays in place at Namecheap — **do not delete it.**
|
||||
|
||||
## CI runs on Gitea, not GitHub
|
||||
|
||||
`AGENTS.md` D3 as amended, 2026-08-26: self-hosted **Gitea**, repo `adr-sml`,
|
||||
local clone at `/Users/pouya/Dev/Websites/adr-sml`.
|
||||
|
||||
**The live pipeline is `.gitea/workflows/deploy.yml`.** Gitea Actions speaks
|
||||
GitHub Actions syntax, so it is a near-direct port — the build steps, the
|
||||
two-pass sync, and the cache headers are unchanged. `.github/workflows/deploy.yml`
|
||||
stays in the repo as the OIDC reference in case the project ever moves.
|
||||
|
||||
### The one real difference: no OIDC
|
||||
|
||||
Gitea is not an AWS OIDC provider. There is no role to assume, so deploys
|
||||
authenticate with a **scoped IAM user** whose access key lives only in the
|
||||
repository's Gitea secrets.
|
||||
|
||||
This is a genuine step down in security from the GitHub setup, and it should be
|
||||
treated as one. The mitigations are the policy scope and the rotation schedule.
|
||||
|
||||
**Create the user:**
|
||||
|
||||
1. IAM → Users → `adr-sml-deploy`. **Programmatic access only** — no console
|
||||
password, no MFA device, no group membership.
|
||||
2. Attach this inline policy and nothing else. Substitute the real bucket name,
|
||||
account ID, and distribution ID from `scripts/aws-discover.sh`:
|
||||
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "ListSiteBucket",
|
||||
"Effect": "Allow",
|
||||
"Action": "s3:ListBucket",
|
||||
"Resource": "arn:aws:s3:::BUCKET_NAME"
|
||||
},
|
||||
{
|
||||
"Sid": "WriteSiteObjects",
|
||||
"Effect": "Allow",
|
||||
"Action": ["s3:PutObject", "s3:PutObjectAcl", "s3:DeleteObject"],
|
||||
"Resource": "arn:aws:s3:::BUCKET_NAME/*"
|
||||
},
|
||||
{
|
||||
"Sid": "InvalidateOneDistribution",
|
||||
"Effect": "Allow",
|
||||
"Action": "cloudfront:CreateInvalidation",
|
||||
"Resource": "arn:aws:cloudfront::ACCOUNT_ID:distribution/DISTRIBUTION_ID"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
No `s3:*`. No `cloudfront:*`. No wildcard resources. If a deploy step needs a
|
||||
permission this policy lacks, the correct response is to question the step, not
|
||||
to widen the policy.
|
||||
|
||||
3. Create an access key. **Copy it once** — AWS will not show the secret again.
|
||||
|
||||
### Gitea configuration
|
||||
|
||||
**Repository → Settings → Actions → Secrets:**
|
||||
|
||||
| Name | Value |
|
||||
|---|---|
|
||||
| `AWS_ACCESS_KEY_ID` | from the IAM user |
|
||||
| `AWS_SECRET_ACCESS_KEY` | from the IAM user |
|
||||
|
||||
**The real values** (captured 2026-08-26, `aws-inventory.txt`):
|
||||
|
||||
| Variable | Value |
|
||||
|---|---|
|
||||
| `AWS_REGION` | `ca-central-1` |
|
||||
| `S3_BUCKET` | `adr-smlcompany-site` |
|
||||
| `CLOUDFRONT_DISTRIBUTION_ID` | `E1OK7G98KNKUTA` |
|
||||
| `INTAKE_ENDPOINT` | `https://4tl0m5igkj.execute-api.ca-central-1.amazonaws.com` |
|
||||
| `BOOKING_URL` | *(empty — parked, R6)* |
|
||||
|
||||
IAM policy substitutions: `BUCKET_NAME` = `adr-smlcompany-site`,
|
||||
`ACCOUNT_ID` = `327082975128`, `DISTRIBUTION_ID` = `E1OK7G98KNKUTA`.
|
||||
|
||||
> **Read this before creating the key.** Account `327082975128` is shared across
|
||||
> `meshkinilaw.ca`, `demesne.media`, `orynenergy.ca`, `lajirugs.ca`, and
|
||||
> `mlp-clientdb-prod-backups` — a law firm's client-database backups. A static
|
||||
> deploy key for a marketing site lives in the same account. The scoped policy is
|
||||
> what keeps a compromised Gitea runner from reaching any of that. Do not widen
|
||||
> it, and never put the `user/pouya` credentials in CI.
|
||||
|
||||
**Repository → Settings → Actions → Variables** (not secrets — these are not
|
||||
sensitive, and keeping them as variables means they appear in run logs where they
|
||||
are useful for debugging):
|
||||
|
||||
| Name | Value |
|
||||
|---|---|
|
||||
| `AWS_REGION` | e.g. `ca-central-1` |
|
||||
| `S3_BUCKET` | the site bucket |
|
||||
| `CLOUDFRONT_DISTRIBUTION_ID` | the `E...` ID |
|
||||
| `INTAKE_ENDPOINT` | API Gateway invoke URL |
|
||||
| `BOOKING_URL` | once chosen (Q5) |
|
||||
|
||||
### A runner must exist
|
||||
|
||||
Gitea Actions needs `act_runner` registered to this repository or its
|
||||
organisation, and Actions enabled both site-wide in `app.ini`
|
||||
(`[actions] ENABLED = true`) and per-repository. Without a runner the workflow
|
||||
queues silently and never runs — which looks exactly like a broken pipeline.
|
||||
|
||||
The workflow installs the AWS CLI if the runner image lacks it, and runs
|
||||
`aws sts get-caller-identity` before touching anything, so a credential problem
|
||||
fails loudly and early rather than halfway through a sync.
|
||||
|
||||
### Key rotation — an operational obligation
|
||||
|
||||
**Rotate `adr-sml-deploy` quarterly.** OIDC would have made this unnecessary;
|
||||
with a static key it is a standing task:
|
||||
|
||||
1. Create a second access key on the same user.
|
||||
2. Update the Gitea secrets.
|
||||
3. Run the workflow and confirm it succeeds.
|
||||
4. **Delete the old key.** Rotation that leaves the old key active is not
|
||||
rotation.
|
||||
|
||||
Set a calendar reminder. A key that is never rotated is the failure mode this
|
||||
whole section exists to bound.
|
||||
|
||||
## Finding the AWS identifiers
|
||||
|
||||
`scripts/aws-discover.sh` collects everything Q10 needs — bucket, distribution
|
||||
ID, regions, API endpoint, certificate, SES identities, and whether S3 versioning
|
||||
is on. Read-only; every call is a list or describe.
|
||||
|
||||
```bash
|
||||
chmod +x scripts/aws-discover.sh
|
||||
./scripts/aws-discover.sh > aws-inventory.txt
|
||||
```
|
||||
|
||||
The output contains resource names and IDs but no secrets.
|
||||
|
||||
## Why OIDC and not access keys
|
||||
|
||||
The alternative is a long-lived `AWS_ACCESS_KEY_ID` in GitHub secrets: a
|
||||
credential that never expires, is invisible once set, and grants its permissions
|
||||
to anyone who can reach the repository. OIDC issues a short-lived token per run,
|
||||
scoped to this repository and this branch.
|
||||
|
||||
One-time setup:
|
||||
|
||||
1. IAM → Identity providers → add OIDC provider `token.actions.githubusercontent.com`,
|
||||
audience `sts.amazonaws.com`.
|
||||
2. Create role `adr-site-deploy` trusting that provider, with a condition on
|
||||
`token.actions.githubusercontent.com:sub` equal to
|
||||
`repo:<org>/<repo>:ref:refs/heads/main` (**Q9**).
|
||||
3. Attach a policy granting **only**: `s3:PutObject`, `s3:DeleteObject`,
|
||||
`s3:ListBucket` on the site bucket, and `cloudfront:CreateInvalidation` on the
|
||||
one distribution. Nothing else. No `s3:*`, no `cloudfront:*`.
|
||||
4. Store the role ARN, bucket name, and distribution ID as repository
|
||||
**variables** (they are not secrets), and reference them in the workflow.
|
||||
|
||||
## Cache policy
|
||||
|
||||
The mistake to avoid is caching HTML aggressively — a stale index page is a site
|
||||
that does not update.
|
||||
|
||||
| Pattern | `Cache-Control` |
|
||||
|---|---|
|
||||
| `*.html` | `public, max-age=0, must-revalidate` |
|
||||
| `/_astro/*` (hashed) | `public, max-age=31536000, immutable` |
|
||||
| Fonts | `public, max-age=31536000, immutable` |
|
||||
| Images | `public, max-age=604800` |
|
||||
| `robots.txt`, `sitemap*.xml` | `public, max-age=3600` |
|
||||
|
||||
Sync in two passes: hashed assets first with the long TTL, then HTML with the
|
||||
short one. Uploading HTML last means a user never fetches a new page whose assets
|
||||
have not landed yet.
|
||||
|
||||
Invalidate `/*` on deploy. At this traffic volume the cost is nil, and partial
|
||||
invalidation paths are a reliable source of confusing bugs.
|
||||
|
||||
## CloudFront configuration
|
||||
|
||||
- Origin: S3 with **Origin Access Control**, bucket not public. The guide's
|
||||
Part 2.2 bucket policy already does this — verify it was not loosened.
|
||||
- Redirect HTTP → HTTPS. TLS 1.2 minimum.
|
||||
- Default root object `index.html`.
|
||||
- **Custom error response:** 404 → `/404.html` with **response code 404**, not
|
||||
200. Returning 200 for a missing page tells crawlers every bad URL is real
|
||||
content, and it is the single most common misconfiguration in this stack.
|
||||
- Compression on. Response-headers policy from `05-backend-spec.md`.
|
||||
- A CloudFront Function for trailing-slash normalisation, so `/about` and
|
||||
`/about/` do not both resolve as separate indexable URLs.
|
||||
|
||||
## Branch model
|
||||
|
||||
`main` is production; every push deploys. Work on short-lived branches, open a
|
||||
PR, let CI build and run Lighthouse, merge.
|
||||
|
||||
**Pull request checks (blocking):** `npm run build` · `astro check` · lint ·
|
||||
Lighthouse CI against the budgets in `04-seo-spec.md` · link check.
|
||||
|
||||
Tag every production deploy `v<year>.<n>` so a rollback has something to name.
|
||||
|
||||
## Rollback
|
||||
|
||||
1. Re-run the workflow at the last good tag, or
|
||||
2. `git revert` and push, or
|
||||
3. Restore from S3 object versioning — **enable versioning on the bucket if it is
|
||||
off**; it is the difference between a rollback and a rebuild.
|
||||
|
||||
Then invalidate `/*`.
|
||||
|
||||
## Cutover checklist — D11 is a single shot, so run all of it
|
||||
|
||||
**Content and compliance**
|
||||
- [ ] Every claim traced to `AGENTS.md` §4 Verified
|
||||
- [ ] No `TODO(pouya)` remains in any shipped page
|
||||
- [ ] No matter counts, rates, dollar figures, or testimonials anywhere
|
||||
- [ ] Q.Arb described as in progress everywhere it appears
|
||||
- [ ] `/fees/` carries real numbers (Q4) or the page does not ship
|
||||
- [ ] Privacy policy matches the backend as actually built
|
||||
|
||||
**Technical**
|
||||
- [ ] Lighthouse ≥ 95 mobile on `/`, `/about/`, a practice page, an article
|
||||
- [ ] Every page renders fully with JavaScript disabled
|
||||
- [ ] `curl` of each URL returns real content, not a shell
|
||||
- [ ] All internal links resolve; no orphan pages
|
||||
- [ ] Sitemap generated and correct; `robots.txt` served, not 403
|
||||
- [ ] Rich Results Test passes; OG previews render in LinkedIn and Slack
|
||||
- [ ] 404 returns a 404 status
|
||||
- [ ] Security headers present (`securityheaders.com` A or better)
|
||||
- [ ] **SES identities verified for sending** (Q18) — `aws sesv2 get-email-identity --email-identity smlcompany.ca` and confirm `VerifiedForSendingStatus: true`
|
||||
- [ ] **SES out of the sandbox** (Q19) — `aws sesv2 get-account --query 'ProductionAccessEnabled'`. In sandbox, mail reaches only pre-verified addresses and the inquirer's confirmation silently fails
|
||||
- [ ] Intake form tested end to end: DynamoDB record written to `adr-intake-submissions`, both emails delivered to a real inbox, TTL set
|
||||
- [ ] Booking link works, including the no-JavaScript fallback
|
||||
- [ ] Favicon set complete
|
||||
- [ ] Tested on iOS Safari, Android Chrome, desktop Safari/Chrome/Firefox
|
||||
- [ ] Tested at 320 px and at 200% zoom
|
||||
|
||||
**Infrastructure**
|
||||
- [ ] S3 versioning enabled
|
||||
- [ ] Bucket not publicly readable; OAC in force
|
||||
- [ ] ACM certificate valid; Namecheap validation CNAME still present
|
||||
- [ ] CloudWatch alarms: Lambda errors, DLQ depth, 5xx rate
|
||||
- [ ] Billing alarm still active (guide Part 0.3)
|
||||
|
||||
**Post-cutover, same day**
|
||||
- [ ] Sitemap submitted to Google Search Console and Bing Webmaster Tools
|
||||
- [ ] Live site fetched as an anonymous crawler to confirm indexable content
|
||||
- [ ] LinkedIn profile and ADRIC/ADRIO listings updated to point here
|
||||
- [ ] Archive the old single-file build to `_archive/` — do not delete it
|
||||
- [ ] `AGENTS.md` Change Log entry recording the cutover
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
# 07 — Fee research and recommended rate card
|
||||
|
||||
Authority: `AGENTS.md` §3 D8 (publish a full rate card) and D14 (two-tier
|
||||
structure, **pending Pouya's sign-off — Q14**).
|
||||
|
||||
**Nothing in this document publishes until Pouya confirms the figures.** These
|
||||
are researched recommendations, not decisions. This is business pricing
|
||||
information, not legal or financial advice.
|
||||
|
||||
Research date: 2026-08-26. All figures below are **plus HST** unless stated.
|
||||
|
||||
---
|
||||
|
||||
## The Ontario market, measured
|
||||
|
||||
### The regulated floor
|
||||
|
||||
Ontario's mandatory mediation tariff (Rule 24.1) sets the roster rate. ADR
|
||||
Chambers publishes it as **$600 to $825 depending on the number of parties**,
|
||||
covering *"one half hour of preparation time per party and up to three hours of
|
||||
mediation."*
|
||||
|
||||
This is the floor of the market, and it is a floor with a signal attached:
|
||||
pricing at or near it reads as roster-filler work.
|
||||
|
||||
### Published hourly bands
|
||||
|
||||
ADR Chambers, across its full roster:
|
||||
|
||||
| | Range |
|
||||
|---|---|
|
||||
| Mediators | **$150 – $750 / hour** *"depending on the experience of the mediator"* |
|
||||
| Arbitrators | **$250 – $800 / hour** |
|
||||
|
||||
Plus, on the arbitration side: an **$800 filing fee**, a **$800 venue fee** for a
|
||||
hearing room and one breakout room, and **$400** per additional room.
|
||||
|
||||
### Published practitioner rate cards
|
||||
|
||||
Four Ontario practitioners publishing real numbers:
|
||||
|
||||
| Practice | Half-day | Full day | Overtime | Notes |
|
||||
|---|---|---|---|---|
|
||||
| **Patey** — Tier 1, PI / insurance | $800 (3 h) | $1,200 (6 h) | $250 / h | Multi-party 3 h $1,200; multi-party full day $2,400; pro forma to 1.5 h $500 |
|
||||
| **Patey** — Tier 2, estate / employment / civil | $1,200 (3 h) | $2,400 (6 h) | $375 / h | Pre-mediation caucus $175 flat |
|
||||
| **Zuber** — video | $1,800 (3 h) | $2,800 (6 h) | $500 / h | +$500 per additional party |
|
||||
| **Zuber** — in person, GTA | — | $4,000 (6 h) | $500 / h | Eastern Ontario $3,500. Prep and travel included |
|
||||
| **Carroll** — Ottawa | $1,750 (incl. 1.5 h prep) | $3,000 (incl. 2 h prep) | $400 / h | Arbitration day rate $3,000 |
|
||||
|
||||
### What the shape of that data says
|
||||
|
||||
Three observations that drive the recommendation.
|
||||
|
||||
1. **The market is already segmented by matter type, not only by seniority.**
|
||||
Patey runs two published tiers off the same neutral. Insurance and PI work
|
||||
clears around $800–$1,200 a day; estate, employment, and civil work clears
|
||||
$2,400 for the same hours. This is the single most useful structural fact in
|
||||
the research.
|
||||
2. **Prep time is a pricing lever, disclosed differently by everyone.** Carroll
|
||||
bundles named hours (1.5 h and 2 h). Zuber bundles prep *and* travel. Patey
|
||||
bundles neither and sells a caucus separately. Bundling explicitly reads as
|
||||
more confident and removes an argument later.
|
||||
3. **Additional parties are always priced, never absorbed.** $300–$500 per party
|
||||
beyond two is the norm, and a four-party construction mediation is materially
|
||||
more work than a two-party one.
|
||||
|
||||
---
|
||||
|
||||
## Where this practice should sit
|
||||
|
||||
**Not at the floor.** Pouya's stack — JD, an operating role inside a litigation
|
||||
and ADR boutique, Q.Med held, Q.Arb commenced, and a working engineering career —
|
||||
is not a junior generalist profile. Entering at roster rates would anchor him
|
||||
into SABS volume work and make the commercial rate very hard to raise later.
|
||||
Published rates are close to unrecoverable once set: raising them looks
|
||||
opportunistic, discounting privately never becomes public knowledge.
|
||||
|
||||
**Not at the top either.** $4,000-a-day in-person GTA rates belong to neutrals
|
||||
with twenty years of name recognition. Asking that without an independent track
|
||||
record invites a comparison he loses.
|
||||
|
||||
**The position is the upper-middle: at or just above Patey Tier 2, just below
|
||||
Zuber and Carroll.** That reads as *credentialed and serious, priced to be taken
|
||||
seriously, not yet a marquee name* — which is exactly true.
|
||||
|
||||
---
|
||||
|
||||
## The confirmed rate card
|
||||
|
||||
**Set by Pouya on 2026-08-26 (D14). This is the card. Build `/fees/` from it.**
|
||||
|
||||
He declined the two-tier structure and set one rate for all mediation matters.
|
||||
All figures **plus HST**.
|
||||
|
||||
### Mediation — all matters, one rate
|
||||
|
||||
| Item | Fee |
|
||||
|---|---|
|
||||
| Half day — up to 3.5 h, including 2 h preparation | **$2,000** |
|
||||
| Full day — up to 7 h, including 3 h preparation | **$4,000** |
|
||||
| Each party beyond two | **$500** |
|
||||
| Overtime, per hour | **$500** |
|
||||
|
||||
### Arbitration
|
||||
|
||||
Available now as co-arbitrator; sole appointments follow the Q.Arb designation,
|
||||
commenced August 2026. The page must say so — see `03-content-spec.md`.
|
||||
|
||||
| Item | Fee |
|
||||
|---|---|
|
||||
| Hourly | **$500** |
|
||||
| Hearing day | **$4,000** |
|
||||
| Documents-only / expedited, flat — simple | **$6,500** |
|
||||
| Documents-only / expedited, flat — complex | **$9,500** |
|
||||
|
||||
**No tribunal-secretary rate.** Removed by Pouya. Do not reinstate it, and do not
|
||||
offer tribunal-secretary work on the site.
|
||||
|
||||
### Other services — hourly
|
||||
|
||||
Early neutral evaluation, settlement counsel, dispute-system design, and
|
||||
pre-dispute technical advisory: **$500 / hour**.
|
||||
|
||||
### Cancellation — adopted as recommended
|
||||
|
||||
| When | Fee |
|
||||
|---|---|
|
||||
| More than 30 days before | No fee. Disbursements only |
|
||||
| 15 – 30 days before | 50% of the booked fee |
|
||||
| Fewer than 15 days before | 100% of the booked fee |
|
||||
| Rescheduled with a new date fixed at the same time | No charge |
|
||||
| Reserved time filled by another matter of equal or greater value | Waived |
|
||||
|
||||
### Terms to state on the page
|
||||
|
||||
- All fees plus HST.
|
||||
- Shared equally between the parties unless they agree otherwise in writing.
|
||||
- Payable on rendering; interest on overdue accounts at 5% per annum.
|
||||
- **Video and in-person at the same rate.** Do not discount remote sessions —
|
||||
the preparation is identical, and discounting teaches the market that the
|
||||
session is the product.
|
||||
- Travel outside the GTA billed separately or bundled at a stated day rate.
|
||||
|
||||
### All parameters confirmed
|
||||
|
||||
Q15, Q16, and Q17 were closed on 2026-08-26. **Preparation time is bundled and
|
||||
must be stated on the page** — "including 2 hours of preparation", "including
|
||||
3 hours of preparation". Do not quietly fold it into the hours figure. At these
|
||||
rates, saying preparation is included is the selling point, not a footnote.
|
||||
|
||||
---
|
||||
|
||||
## Recorded dissent — for the 12-month review (R5)
|
||||
|
||||
Claude recommended a two-tier card; Pouya set a single rate. The reasoning is
|
||||
recorded here so the 12-month review has something to test against, not to
|
||||
re-open a settled decision.
|
||||
|
||||
**Where the single rate lands relative to the measured market:**
|
||||
|
||||
| Segment | Published market, full day | This card |
|
||||
|---|---|---|
|
||||
| Insurance / SABS / LAT | ~$1,200 – $2,400 | **$4,000** |
|
||||
| Commercial / civil / estate | ~$2,400 – $3,000 | **$4,000** |
|
||||
| Established GTA in person | ~$3,500 – $4,000 | **$4,000** |
|
||||
|
||||
$4,000 is at the ceiling of the published Ontario market — level with Zuber's
|
||||
in-person GTA rate, and roughly **three times** the going rate for the insurance
|
||||
and SABS segment.
|
||||
|
||||
**The consequence worth watching.** The strategy brief (§IV.7) identifies
|
||||
accident-benefits and LAT mediation as the highest realistic near-term volume,
|
||||
flowing directly from the firm's existing practice. At $4,000 a day that segment
|
||||
is priced out. This is a coherent choice — a premium specialist position that
|
||||
forgoes volume — **provided the volume was not being counted on.** If early
|
||||
appointment flow is slower than expected, the SABS tier is the first place to
|
||||
look, and reintroducing a second tier is a cleaner fix than cutting the headline
|
||||
rate.
|
||||
|
||||
**What makes the rate defensible.** $4,000 for a neutral who reads the contract,
|
||||
the code, and the System Impact Assessment is a fair price. $4,000 for a
|
||||
generalist is not. The rate and `/practice/technology/` are load-bearing for each
|
||||
other, which is an argument for shipping them in the same release — and for the
|
||||
Insights section carrying real technical depth rather than process explainers
|
||||
alone.
|
||||
|
||||
**One thing the single rate gets right.** Published rates are close to
|
||||
unrecoverable, and it is far easier to add a lower tier later than to raise a
|
||||
headline rate. Setting the ceiling first and discounting privately preserves
|
||||
more optionality than the reverse.
|
||||
|
||||
---
|
||||
|
||||
## Sources
|
||||
|
||||
- [ADR Chambers — Mediation Fees](https://adrchambers.com/mediation/fees/)
|
||||
- [ADR Chambers — Roster Rate / Mandatory Mediations](https://adrchambers.com/roster-rate-mediation/)
|
||||
- [ADR Chambers — Arbitration Fees](https://adrchambers.com/arbitration/fees/)
|
||||
- [Patey Mediations — Rates & Cancellation](https://pateymediations.com/rates/)
|
||||
- [Zuber Mediation — Fees](https://www.zubermediation.com/fees.html)
|
||||
- [Carroll Mediation — Rates & Cancellation](https://www.carrollmediation.ca/?page_id=16)
|
||||
- [O. Reg. 451/98 — Mediators' Fees (Rule 24.1)](https://www.canlii.org/en/on/laws/regu/o-reg-451-98/latest/o-reg-451-98.html)
|
||||
Reference in New Issue
Block a user