feat: build step 2 — the home page; close Q35, Q37, Q39; Q39's answer corrected the register
Pouya's rulings and the page are one commit, not two, because they are not separable: Q37 changed the credential label the page renders, Q39 scoped the arbitration copy, and Q35 gave Med-Arb the row that lets the footer link stand. Splitting them would produce a commit where the register says one thing and the page says another. RULINGS Q39 — ANSWERED, and my assertion was FALSE as a universal. Pouya checked rather than defended. Family arbitration in Ontario is gated: prescribed training, 14h on screening for domestic violence and power imbalances, 30h of Ontario family law for arbitrators outside the bar, 10h ongoing per two years. claims-auditor produced that counter-example from inside the repo on 2026-08-26 and it was right. The source is now IN the repo per R14 — docs/reference/ontario-family-arbitration-training.md, retrieved with its provenance — and §4 states which half it establishes: the family gate directly, the commercial half only by absence. So "commercial arbitration in Ontario requires no licence and no designation" is recorded as Pouya's stated position, unstamped. What disposes of the question is the scope exclusion: he does not do family arbitration, so it is NOT OFFERED and the gate never bears on the practice. The false universal survived in three more live places, found by grep, not recall: §4's NOT-NEGOTIABLE boundary bullet (the boundary paragraph breaching its own boundary), §9's Q33 closure row, and a comment in SiteHeader.astro. Q35 — ANSWERED, all four items, and the answer supplied a gate that was missing. Med-Arb gets an Offerings row and stays in the footer sitemap. The six subject-matter labels pass test 1. §4 gains "Subject-matter areas — the publication gate": competence to accept an appointment, AND a page that frames it as positioning without claiming history. Nothing in the nav or footer is unrowed any more. Q37 — ANSWERED. "Law and engineering" becomes "Legal training and engineering practice". A degree is not a practice; the parallel was doing the implying. Q38/R13 — the committed SVG does not close it; the walk-back went too far. It renders faithfully BECAUSE it is the raster. R13 stays open. R1 — surfaced and acknowledged; the interim licensure framing is now carried by a shipped page rather than a spec, which raises the stakes. BUILD STEP 2 Seven of docs/01's eight home sections, six new components, zero JavaScript. Section 7 (Latest insights) ships at step 7 with the collection it lists — recorded in docs/01 and in the page, not just here. Four credential slots, not three: §4's paired-disclosure condition requires the Q.Arb stage on any page that offers arbitration. No booking link (R6). The masthead tagline is suppressed on / (it duplicated the hero eyebrow). The step-1 proof sheet is deleted, and five live references to it were found by grep. WHAT THE REVIEWS FOUND — 24 findings across the two passes claims-auditor FAILED it on ten, every one implication or scope rather than fabrication, which is where D13 says the risk lives. The four that mattered: "I mediate and arbitrate" asserted a track record §4 does not hold for arbitration; the JSON-LD asserted arbitration twice and stated the stage nowhere (a crawler-only claim is still a claim); "at one published rate with preparation time included" misdescribed money against docs/07's two day rates and capped prep allowance; and "Law and engineering are not blended here" was Q37's struck parallel relocated into prose one day after Pouya struck it. It also found the Canadian Tax Foundation missing from §9 Q8 — a stale second copy of a fact that would have bitten at step 3. adversarial-reviewer found fourteen, three blocking. The first was class="section-head" on <SectionHeading> never matching — the parent-cannot- style-a-child defect for the FOURTH time, written into a diff where I had just added fresh warnings about it to two other components. Measured: 0px margin, 0px gap, headings over the card edges, with astro check and eslint both clean. I had looked at a screenshot of that section and passed over it. Fixed with a page-owned wrapper (48px, measured) and the prop is deleted from six components so it is now a build error. Also: the credential row was never "two up on a phone" and its comment said it was; PROCESS was hardcoded in the page against the reason written in site.ts; 83px of residual overflow at a 200% default font size, now 3px. Seven more I found myself first, including <Picture widths> declaring the untouched 1600px master as the <img src> fallback (254,626 B for a 476px slot, and the build log said "before: 349kB" either way), and a prop named `as` silently turning off type-checking for a whole component. VERIFICATION — run, not asserted. Full figures in AGENTS.md entry (w). npx tsc 6.0.3 · check 0/0/0 · lint clean · build clean · audit 0 1 <script> and it is JSON-LD; non-JSON-LD scripts 0; no JS bundle; identical page with script execution disabled (444 nodes, 6,578 chars) Phase 5 minifier check: no `animation` shorthand beside animation-timeline overflow 0 at 14 widths, AND every mark measured at 1.5654-1.5657 vs 1.5657 one h1, no heading skips, focus order == DOM order across 44 focusables 31 painted contrast pairs at 3 widths, 0 failures 72/72 hit-test points across 8 cards resolve to the card's link print 0 hidden, reduced-motion 0 hidden Lighthouse NOT RUN — tool unavailable until step 7 (R11) HTML validator NOT RUN, and 4 of 12 srcsets carry a duplicate 1x descriptor Opened for Pouya: Q40 (one OG image for nineteen pages), Q41 (does Q37 reach prose; may the comparative be restored; what LAT pre-hearing mediation means), Q42 (the four "also offered" processes have no row), Q43 (the process timings are published commitments with no row). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0148NztQskLKKApP5SzAA78e
This commit is contained in:
co-authored by
Claude Opus 5
parent
8a2f513577
commit
165d259f5c
@@ -0,0 +1,141 @@
|
||||
/**
|
||||
* JSON-LD builders. Spec: docs/04-seo-spec.md §Structured data.
|
||||
*
|
||||
* WHY THIS FILE EXISTS. Structured data is a factual claim in machine-readable
|
||||
* form, and it is the one place a claim can be wrong without any human reading
|
||||
* it. AGENTS.md §4 governs it exactly as it governs visible copy — docs/04 says
|
||||
* so in as many words: "Marking an unheld credential as held in structured data
|
||||
* is a misrepresentation that happens to be machine-readable."
|
||||
*
|
||||
* So the Person node is built ONCE, here, from src/data/site.ts, and every page
|
||||
* that needs it references the same @id. `/` (step 2) and `/about/` (step 3)
|
||||
* would otherwise hand-type it twice.
|
||||
*
|
||||
* THREE THINGS ARE DELIBERATELY ABSENT. Each is a decision, not an omission:
|
||||
*
|
||||
* 1. `LegalService` — NEVER. docs/04: schema.org defines it as a business
|
||||
* providing legal advice and *representation*, which asserts in
|
||||
* machine-readable form exactly what D13 bars. `ProfessionalService`.
|
||||
* 2. `worksFor` — omitted. Populating it either names the boutique (D16) or
|
||||
* misstates the employer. `jobTitle` carries the role on its own.
|
||||
* 3. `priceRange` — omitted until `/fees/` exists (build step 9). docs/04
|
||||
* gates it on that page being real.
|
||||
*
|
||||
* AND Q.Arb IS NOT IN `hasCredential`. It commenced August 2026 and is not
|
||||
* held. Q.Med is. That asymmetry is the whole point of the property.
|
||||
*/
|
||||
import { CONTACT, CREDENTIALS, ROLE, SITE } from './site';
|
||||
|
||||
/** Stable node ids, so pages cross-reference rather than duplicate. */
|
||||
export const PERSON_ID = `${SITE.url}/about/#person`;
|
||||
export const SERVICE_ID = `${SITE.url}/#practice`;
|
||||
|
||||
/**
|
||||
* `Person`. docs/04 anchors this on /about/ and references it site-wide, which
|
||||
* is why PERSON_ID points at /about/ regardless of which page emits the node.
|
||||
*
|
||||
* `description` IS OFFERING-SHAPED, NOT ROLE-SHAPED, and that was a correction.
|
||||
* It read "Mediator and commercial arbitrator in Toronto", which `claims-auditor`
|
||||
* flagged 2026-08-27: §4 verifies that he **accepts** arbitral appointments and
|
||||
* separately verifies "multiple completed sole mediations" — there is **no
|
||||
* counterpart row for a completed arbitration**, so "arbitrator" as a practised
|
||||
* role asserted something the register does not hold.
|
||||
*
|
||||
* AND IT CARRIES THE Q.Arb STAGE. Same finding, and it is the sharper half:
|
||||
* §4 Offerings permits the arbitration offering only while the site states the
|
||||
* stage of the arc plainly — "neither half may be dropped". The VISIBLE page
|
||||
* satisfied that with the fourth credential slot; this graph asserted
|
||||
* arbitration twice (here and in `serviceType`) and stated the stage nowhere.
|
||||
* A machine-readable claim is still a claim.
|
||||
*
|
||||
* `hasCredential` stays Q.Med-only regardless — the stage belongs in prose, not
|
||||
* in a field that means "holds".
|
||||
*
|
||||
* Every clause traces: Q.Med [verified], the JD [verified], engineering
|
||||
* practice [verified], Toronto [verified], the Q.Arb pathway commenced August
|
||||
* 2026 [verified]. It claims no licensure and implies none — D13 bars
|
||||
* implication as hard as assertion, and a crawler summary is a place where an
|
||||
* implication travels unedited.
|
||||
*/
|
||||
export function personNode(imageUrl?: string) {
|
||||
return {
|
||||
'@type': 'Person',
|
||||
'@id': PERSON_ID,
|
||||
name: SITE.name,
|
||||
url: `${SITE.url}/about/`,
|
||||
jobTitle: ROLE.title,
|
||||
description:
|
||||
'Mediator in Toronto, accepting commercial arbitration appointments. ' +
|
||||
'Q.Med designation through ADRIC and ADRIO; the Q.Arb pathway commenced ' +
|
||||
'in August 2026. JD, Bond University; practising machine-learning and ' +
|
||||
'infrastructure engineer.',
|
||||
knowsLanguage: ['en', 'fa'],
|
||||
alumniOf: { '@type': 'CollegeOrUniversity', name: 'Bond University' },
|
||||
// Q.Med only. See the header comment.
|
||||
hasCredential: {
|
||||
'@type': 'EducationalOccupationalCredential',
|
||||
name: CREDENTIALS.designations[0],
|
||||
credentialCategory: 'Professional designation',
|
||||
recognizedBy: [
|
||||
{ '@type': 'Organization', name: 'ADR Institute of Canada' },
|
||||
{ '@type': 'Organization', name: 'ADR Institute of Ontario' },
|
||||
],
|
||||
},
|
||||
sameAs: [CONTACT.linkedin],
|
||||
email: `mailto:${CONTACT.email}`,
|
||||
...(imageUrl ? { image: imageUrl } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* `ProfessionalService` for the home page, with the Person as `provider`.
|
||||
*
|
||||
* `serviceType` lists what §4 Offerings actually records as offered now —
|
||||
* mediation, arbitration, med-arb. **Arbitration is scoped to commercial**
|
||||
* (Q39, 2026-08-27): family arbitration in Ontario carries prescribed training
|
||||
* and is separately not offered, so an unscoped "Arbitration" here would be the
|
||||
* struck universal reappearing in a machine-readable field where nobody reads
|
||||
* it. Do not widen these strings without a row to widen them from.
|
||||
*
|
||||
* No `priceRange`, no `aggregateRating`, no `review` — the last two have no
|
||||
* underlying data and §4 Forbidden bars the fabricated testimonial that the
|
||||
* previous site carried.
|
||||
*/
|
||||
export function professionalServiceNode(imageUrl?: string) {
|
||||
return {
|
||||
'@type': 'ProfessionalService',
|
||||
'@id': SERVICE_ID,
|
||||
name: `${SITE.name} — Mediation & Arbitration`,
|
||||
url: `${SITE.url}/`,
|
||||
description:
|
||||
'Commercial mediation and arbitration for construction, technology, ' +
|
||||
'energy, insurance, shareholder and cross-border disputes. Toronto, ' +
|
||||
'by appointment. Q.Med held; the Q.Arb pathway commenced August 2026.',
|
||||
provider: { '@id': PERSON_ID },
|
||||
areaServed: [
|
||||
{ '@type': 'City', name: 'Toronto' },
|
||||
{ '@type': 'AdministrativeArea', name: 'Ontario' },
|
||||
],
|
||||
serviceType: [
|
||||
'Mediation',
|
||||
'Commercial arbitration',
|
||||
'Mediation-arbitration (med-arb)',
|
||||
],
|
||||
availableLanguage: ['en', 'fa'],
|
||||
email: `mailto:${CONTACT.email}`,
|
||||
...(imageUrl ? { image: imageUrl } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* The home page's graph: the service and the person it resolves to.
|
||||
*
|
||||
* A `@graph` rather than two `<script>` blocks, so `provider: {'@id': …}`
|
||||
* resolves inside one document instead of relying on a crawler joining two.
|
||||
*/
|
||||
export function homeGraph(imageUrl?: string) {
|
||||
return {
|
||||
'@context': 'https://schema.org',
|
||||
'@graph': [professionalServiceNode(imageUrl), personNode(imageUrl)],
|
||||
};
|
||||
}
|
||||
+163
-13
@@ -118,22 +118,48 @@ export const ROLE = {
|
||||
export const CREDENTIAL_ROW = [
|
||||
{ value: 'Q.Med', label: 'ADRIC / ADRIO designation' },
|
||||
/**
|
||||
* TODO(pouya): AGENTS.md Q37 — is "Law and engineering" the right label?
|
||||
* Q37 CLOSED 2026-08-27. This label read 'Law and engineering' and it is now
|
||||
* 'Legal training and engineering practice'. Pouya's reasoning, kept because
|
||||
* it is the finding rather than the fix:
|
||||
*
|
||||
* §4's substitution principle supplies the three VALUES (`Q.Med / JD + ML /
|
||||
* EN · FA`); the labels come from docs/03 and are not themselves registered.
|
||||
* `claims-auditor` reads this one as a D13 implication risk: "engineering" is
|
||||
* shorthand for a verified *practising* claim, so the parallel invites "Law"
|
||||
* to be read the same way — and D13 bars implication as hard as assertion.
|
||||
* "The parallel was doing the implying — a degree and a practice under one
|
||||
* noun. The asymmetry is the honest part."
|
||||
*
|
||||
* Left as written, because it is docs/03's approved copy and changing
|
||||
* approved copy is not an implementer's call. Nothing renders it yet. Answer
|
||||
* before step 2 builds the credential row.
|
||||
* A JD is a degree. Engineering is a practice, and a verified one (§4).
|
||||
* Setting the two in parallel invited the reader to supply the symmetry, and
|
||||
* for 'Law' the missing half is a licence — which D13 bars by implication as
|
||||
* hard as by assertion.
|
||||
*
|
||||
* IT IS DELIBERATELY LOPSIDED AND LONGER. Do not tidy it back into a
|
||||
* parallel, and do not shorten it to fit a layout; change the layout.
|
||||
*/
|
||||
{ value: 'JD + ML', label: 'Law and engineering' },
|
||||
{ value: 'JD + ML', label: 'Legal training and engineering practice' },
|
||||
{ value: 'EN · FA', label: 'Bilingual practice' },
|
||||
] as const;
|
||||
|
||||
/**
|
||||
* The FOURTH credential slot — separate on purpose, so a three-slot layout
|
||||
* cannot be handed four by accident and a page has to opt in.
|
||||
*
|
||||
* docs/03: 'Fourth slot where the layout has one: Q.Arb — commenced August
|
||||
* 2026. Use that wording, not "in progress"' — the weaker form drifts toward
|
||||
* 'nearly complete', which §4 Forbidden bars outright.
|
||||
*
|
||||
* REQUIRED on any page that offers arbitration, not decorative. §4 Offerings
|
||||
* carries a paired-disclosure condition: the site may make the offering only
|
||||
* while 'stating the second plainly', and 'neither half may be dropped'. The
|
||||
* footer's designation strip satisfies it site-wide; a page whose opening
|
||||
* sentence says 'arbitrator' should not make the reader scroll to the footer
|
||||
* for the stage.
|
||||
*
|
||||
* The em-dash in docs/03's string is carried by the layout (value over label),
|
||||
* not by the text. Same wording, same pairing.
|
||||
*/
|
||||
export const CREDENTIAL_ROW_ARB = {
|
||||
value: 'Q.Arb',
|
||||
label: 'Commenced August 2026',
|
||||
} as const; // [verified 2026-08-26 — Pouya]
|
||||
|
||||
/** Analytics: privacy-first and cookieless (D15). No GA4, no consent banner. */
|
||||
export const ANALYTICS = {
|
||||
/**
|
||||
@@ -241,29 +267,98 @@ export const PRACTICE_SLUGS = [
|
||||
|
||||
export type PracticeSlug = (typeof PRACTICE_SLUGS)[number];
|
||||
|
||||
/**
|
||||
* The six areas, with the one-line blurb each card renders.
|
||||
*
|
||||
* THE BLURBS LIVE HERE, not in the pages, because `/` and `/practice/` both
|
||||
* render them and two copies of a claim-bearing sentence is one copy that will
|
||||
* eventually be wrong. Same reasoning as ROLE and CREDENTIAL_ROW above.
|
||||
*
|
||||
* EVERY BLURB IS DISPUTE TYPES, NOT HISTORY. docs/03: "Frame as positioning,
|
||||
* not as history" — 'Built to facilitate ... on Ontario's megaproject pipeline',
|
||||
* never 'extensive experience resolving'. AGENTS.md §4 (Q35, 2026-08-27) makes
|
||||
* that condition 2 of the publication gate for naming a practice area at all.
|
||||
* A blurb that claims volume fails the gate even though the label passes.
|
||||
*
|
||||
* Nothing here may carry a count, a value, a settlement rate, or a superlative
|
||||
* (§4 Forbidden). Dispute types are not claims of caseload.
|
||||
*/
|
||||
export const PRACTICE_AREAS = [
|
||||
{
|
||||
slug: 'construction',
|
||||
name: 'Construction & Infrastructure',
|
||||
chip: 'Construction',
|
||||
blurb:
|
||||
'Liens, delay and change-order claims, scheduling, subcontract and ' +
|
||||
"deficiency disputes. Built for Ontario's megaproject pipeline.",
|
||||
},
|
||||
{
|
||||
slug: 'technology',
|
||||
name: 'Technology, AI & Data',
|
||||
chip: 'Technology',
|
||||
blurb:
|
||||
'Software contracts, SLA and MSA breakdowns, data residency and ' +
|
||||
'processing, AI vendor diligence, IP and licensing.',
|
||||
},
|
||||
{
|
||||
slug: 'energy',
|
||||
name: 'Energy, Grid & Regulatory',
|
||||
chip: 'Energy',
|
||||
blurb:
|
||||
'Grid connection and allocation, leave-to-construct, ' +
|
||||
'proponent–municipality disputes, IESO market participation.',
|
||||
},
|
||||
{
|
||||
slug: 'insurance',
|
||||
name: 'Insurance, SABS & LAT',
|
||||
chip: 'Insurance',
|
||||
/**
|
||||
* "LAT pre-hearing mediation" was flagged by `claims-auditor` 2026-08-27:
|
||||
* `docs/01` §`/practice/insurance/` lists that phrase as a SEARCH INTENT,
|
||||
* not as an offering, and as a service blurb it reads as conducting
|
||||
* mediation inside the LAT's own statutory process — which is not what a
|
||||
* privately retained neutral is appointed to do.
|
||||
*
|
||||
* Reworded to the offering that is actually rowed in §4 (mediation) applied
|
||||
* to the subject matter that is actually verified (insurance / SABS).
|
||||
* Q41 asks Pouya to confirm the intent before the page ships at step 5.
|
||||
*/
|
||||
blurb:
|
||||
'Accident benefits and SABS entitlement, MIG disputes, and private ' +
|
||||
'mediation of matters before the LAT.',
|
||||
},
|
||||
{ slug: 'technology', name: 'Technology, AI & Data', chip: 'Technology' },
|
||||
{ slug: 'energy', name: 'Energy, Grid & Regulatory', chip: 'Energy' },
|
||||
{ slug: 'insurance', name: 'Insurance, SABS & LAT', chip: 'Insurance' },
|
||||
{
|
||||
slug: 'shareholder',
|
||||
name: 'Shareholder & Family Business',
|
||||
chip: 'Shareholder',
|
||||
/**
|
||||
* "Family Business" means COMMERCIAL disputes among family shareholders.
|
||||
* The blurb says "family-held companies" and names commercial dispute types
|
||||
* for that reason — AGENTS.md Q39, 2026-08-27.
|
||||
*
|
||||
* The explicit exclusion — family law matters are not accepted — belongs on
|
||||
* the PAGE, one sentence, at build step 5 (docs/01 §/practice/shareholder/).
|
||||
* Pouya scoped it there. Do not add it to this blurb: on a six-card grid it
|
||||
* unbalances the row and reads defensively, and the wording here already
|
||||
* makes the area unambiguously commercial.
|
||||
*/
|
||||
blurb:
|
||||
'Shareholder and partnership disputes, co-founder breakdowns, and ' +
|
||||
'business succession in family-held companies.',
|
||||
},
|
||||
{
|
||||
slug: 'cross-cultural',
|
||||
name: 'Cross-Border & Diaspora',
|
||||
chip: 'Cross-cultural',
|
||||
blurb:
|
||||
'Diaspora business succession, dual-jurisdiction shareholder disputes, ' +
|
||||
'and cross-cultural commercial matters. Conducted in English or Farsi.',
|
||||
},
|
||||
] as const satisfies ReadonlyArray<{
|
||||
slug: PracticeSlug;
|
||||
name: string;
|
||||
chip: string;
|
||||
blurb: string;
|
||||
}>;
|
||||
|
||||
/**
|
||||
@@ -285,6 +380,61 @@ type _SlugsWithoutAnArea = Exclude<
|
||||
>;
|
||||
export type _SlugCoverage = _AssertNever<_SlugsWithoutAnArea>;
|
||||
|
||||
/**
|
||||
* The five process steps. **HERE, NOT IN THE PAGE**, for the reason written
|
||||
* against PRACTICE_AREAS above and applied by `adversarial-reviewer` 2026-08-27:
|
||||
* `/` renders a compressed strip of these and `/process/` renders them in full
|
||||
* at build step 6, so a copy typed into one page is a copy that will eventually
|
||||
* disagree with the other. The proof is already in the history — the step-3 body
|
||||
* below carried a fee claim that the same session's claims audit found to be
|
||||
* wrong against `docs/07`, and it existed in exactly one place. After step 6 it
|
||||
* would have existed in two.
|
||||
*
|
||||
* TIMINGS ARE `docs/01` §`/process/`'s, verbatim: "confidential intake (day 0) ·
|
||||
* engagement and framing (1–7) · pre-session exchange (7–21) · the session
|
||||
* (21–30) · binding conclusion (30+)". `docs/03` §Process requires them REAL
|
||||
* rather than illustrative, so they are not softened to "typically" — and they
|
||||
* are not invented either. **They have no §4 row; that is Q43.**
|
||||
*
|
||||
* Step 5 is labelled from the spec but its body says what actually concludes —
|
||||
* minutes of settlement in a mediation, an award where the process is arbitral.
|
||||
* "Binding conclusion" alone would read as though a mediation binds, which it
|
||||
* does not until the parties sign.
|
||||
*
|
||||
* NO FEE CLAIM IN ANY BODY. `docs/07` bundles a CAPPED preparation allowance
|
||||
* (2 h in the half day, 3 h in the full day) and says in terms that it "must be
|
||||
* stated on the page... Do not quietly fold it into the hours figure." A
|
||||
* five-word strip cannot state it properly, and stating it improperly
|
||||
* misdescribes money. `/fees/` at step 9.
|
||||
*/
|
||||
export const PROCESS = [
|
||||
{
|
||||
title: 'Confidential intake',
|
||||
timing: 'Day 0',
|
||||
body: 'A scheduled call to scope the matter, identify the parties, and run conflicts.',
|
||||
},
|
||||
{
|
||||
title: 'Engagement and framing',
|
||||
timing: 'Days 1–7',
|
||||
body: 'Terms of appointment, the issues in dispute, and who attends.',
|
||||
},
|
||||
{
|
||||
title: 'Pre-session exchange',
|
||||
timing: 'Days 7–21',
|
||||
body: 'Briefs and documents, exchanged in advance so the session starts informed.',
|
||||
},
|
||||
{
|
||||
title: 'The session',
|
||||
timing: 'Days 21–30',
|
||||
body: 'Half day or full day, in person or by video.',
|
||||
},
|
||||
{
|
||||
title: 'Conclusion',
|
||||
timing: 'Day 30 onward',
|
||||
body: 'Minutes of settlement — or an award, where the process is arbitral.',
|
||||
},
|
||||
] as const;
|
||||
|
||||
/** Seven items is the ceiling before a nav stops being scannable. */
|
||||
export const PRIMARY_NAV = [
|
||||
{ href: '/about/', label: 'About' },
|
||||
|
||||
Reference in New Issue
Block a user