Build and deploy / build-and-deploy (push) Failing after 4s
Pouya's rulings of 2026-09-03 (the last two D20 findings) and 2026-09-04 (the spam observation and four mitigations), in one change set. D20 finding 10 — med-arb is billed BY PHASE, each phase at the rates already published, so /fees/'s "Every figure is on this page" is true as written rather than narrowed. FEES.medArb is the single source; docs/07 §Med-arb carries the rule INTERIM against R5, and R5 now carries it back, because a derived price moves silently when a rate moves. D20 finding 13 — conduct undertaking (g), attested 2026-09-03, published as his wording verbatim on /legal/privacy/ and /contact/. The clause that raised the finding promised to DISCLOSE a conflicts check's outcome, which the attestation does not cover; it is struck. D20 now partitions 17 fixed / 2 refuted / 1 owed. Spam, 2026-09-04 — recorded in docs/05 §Observed abuse with the date and signature. A second honeypot (a decoy checkbox, own class, `hidden`, a label that tells a human not to tick it) and scoring that LABELS and never rejects: nothing is dropped, nothing new is stored, and only the operator notification changes. Q65 opens the WAF cost call. The timing floor could not be built: there is no timing check and never has been. docs/05 carries it struck, and every mechanism that would give a real per-visitor clock breaks zero-JS, handler-and-form-only, or D1. Q66. configure.mjs gains section 5 — a custom origin request policy forwarding CloudFront-Viewer-Address on /api/*. Written, dry-run against the live distribution, NOT applied. It reads the handler's own header reads and refuses to run if the whitelist omits one. And reading the live account to do it found four AGENTS.md §7 rows saying the intake backend was undeployed, two days after it went live — corrected against get-function-configuration, get-routes, get-stage, get-policy and the deployed zip, which was downloaded and read. Review: adversarial-reviewer only (claims-auditor is D20's cutover pass and has run). Round 1 five lenses, 56 findings, 7 blocking, 4 refuted by an independent refuter; round 2 four lenses, 36 findings, 33 of them defects in round 1's own repairs. Stopped at two per D19. Gates, exit status read for each: check 0 · build 0 (23 pages) · check:claims 0 · check:intake 0 · og:proof 0 · lint 0 · spam-score.test 39/39 with 6/6 mutations killed · router.test 30/30 · minifier grep 1 (clean) · lighthouse 0, no category below 95 · configure.mjs dry run 0, nothing written. Nothing deployed and nothing applied. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Md3GndFqWPzK78xAoebsg5
460 lines
22 KiB
Plaintext
460 lines
22 KiB
Plaintext
---
|
||
/**
|
||
* `/legal/privacy/` — build step 10. Spec: docs/01 §`/legal/*`,
|
||
* docs/05-backend-spec.md §Privacy policy must state.
|
||
*
|
||
* ⚠️ **THE GOVERNING INSTRUCTION IS "WRITTEN TO MATCH WHAT IS ACTUALLY BUILT,
|
||
* NOT WHAT IS TYPICAL" — docs/05 — AND THAT IS WHY THIS PAGE IS BUILT LAST IN
|
||
* THE ORDER.** `docs/01`: *"/legal/* — written to match the backend as actually
|
||
* built."* On this page a sentence that describes an intended control rather than
|
||
* a real one is a false statement to the public in a legal document, and it is
|
||
* the kind that fails silently: nothing breaks, and the sentence reads correctly.
|
||
*
|
||
* So three things are DERIVED rather than written, and each closes a specific
|
||
* way this page could quietly become untrue:
|
||
*
|
||
* 1. **The list of what is collected is rendered from `INTAKE_FIELDS`** — the
|
||
* same array `/contact/` builds the form from. A field added to the form
|
||
* appears here on the same build. A hand-written list is the copy nobody
|
||
* re-reads, which is the SES-DKIM shape in a document with legal weight.
|
||
* 2. **The retention period is rendered from `RETENTION_MONTHS`**, which is the
|
||
* figure `backend/intake/handler.mjs` writes into the `ttl` attribute.
|
||
* docs/05: *"Whatever number ships must match `/legal/privacy/` exactly."*
|
||
* 3. **The analytics paragraph is rendered from `ANALYTICS.installed`.** D15
|
||
* decided Plausible; §7 records that no script is on any page. Deciding is
|
||
* not installing, and a policy naming a processor that processes nothing is
|
||
* a false disclosure. Today it says there are none.
|
||
*
|
||
* ⚠️ **WHAT THIS PAGE DELIBERATELY DOES NOT CLAIM, AND THE OMISSIONS ARE THE
|
||
* POINT.** docs/05 specifies a customer-managed KMS key, point-in-time recovery,
|
||
* and DynamoDB TTL. `AGENTS.md` §7 is the register for whether each of the three
|
||
* is enabled, and **this comment does not restate what it says** — it did once,
|
||
* went stale within the day, and had to be pulled back (§12 R19). So:
|
||
*
|
||
* - "Encrypted at rest" IS stated, because DynamoDB encrypts every table at
|
||
* rest unconditionally — it is true whether or not the customer-managed key
|
||
* in docs/05 has been configured.
|
||
* - The customer-managed key and point-in-time recovery are NOT mentioned.
|
||
* Neither is a fact a reader needs, and neither is verified.
|
||
* - **Automatic deletion IS stated, and it asserts a MECHANISM rather than only
|
||
* a period** — the one promise here whose truth lives entirely outside this
|
||
* repository. The handler writes the `ttl` attribute, and ⚠️ **writing the
|
||
* attribute is not the mechanism**: TTL must also be enabled on the table,
|
||
* which §7 records — **and the setting being on still does not prove a
|
||
* record is ever deleted.** Only a record written with a near-future `ttl`
|
||
* and watched to vanish proves that. docs/05's definition of done carries
|
||
* "TTL set and verified by test record" and `docs/06`'s cutover checklist
|
||
* names this page as what that item protects. ⚠️ **This read "both halves
|
||
* before this page is public" and the page went public first — Pouya's
|
||
* ruling of 2026-09-03: publish, then confirm the deletion, reading from
|
||
* 2026-09-04.** So the second half is now owed rather than pending, which is
|
||
* a weaker position and is recorded as one. See the comment on the retention
|
||
* section below, and §9 Q60.
|
||
*
|
||
* ⚠️ **NO LICENSURE CLAIM AND NO ANSWER TO THE CAPACITY QUESTION.** A privacy
|
||
* policy is where "legal advice" phrasing arrives by convention. §4 records
|
||
* licence status as `[unestablished]` and instructs this repository to answer
|
||
* neither way; `docs/03`'s ratified pattern is role, then consequence for the
|
||
* reader, and no verb of capacity. Applied throughout.
|
||
*/
|
||
import BaseLayout from '../../layouts/BaseLayout.astro';
|
||
import Eyebrow from '../../components/Eyebrow.astro';
|
||
import Undertaking from '../../components/Undertaking.astro';
|
||
import { getImage } from 'astro:assets';
|
||
import ogDefault from '../../assets/og-portrait.jpg';
|
||
import { pageGraph } from '../../data/schema';
|
||
import {
|
||
ANALYTICS,
|
||
CONDUCT_UNDERTAKINGS,
|
||
CONTACT,
|
||
SITE,
|
||
} from '../../data/site';
|
||
import { INTAKE_FIELDS } from '../../data/intake';
|
||
|
||
const ldImage = await getImage({
|
||
src: ogDefault,
|
||
format: 'jpeg',
|
||
width: 1200,
|
||
height: 630,
|
||
});
|
||
const graph = pageGraph(new URL(ldImage.src, Astro.site).href);
|
||
|
||
/**
|
||
* ⚠️ MUST MATCH `RETENTION_MONTHS` IN `backend/intake/handler.mjs`, which is
|
||
* the figure written into the record's `ttl`. docs/05: "Whatever number ships
|
||
* must match /legal/privacy/ exactly." The handler is a separately deployed
|
||
* artefact and cannot be imported here, so this is a second copy — and unlike
|
||
* the intake field tables there is no `check:` script over it. Treat a change to
|
||
* either as a change to both, and see docs/06's cutover checklist.
|
||
*/
|
||
const RETENTION_MONTHS = 24;
|
||
|
||
/** Bump this on ANY substantive edit. A privacy policy with a stale date is a
|
||
* policy a reader cannot tell they are reading an old version of. */
|
||
const LAST_UPDATED = '4 September 2026';
|
||
|
||
/* Rendered from the form's own field list, so the two cannot drift. `consent`
|
||
and the honeypot are absent from `INTAKE_FIELDS` deliberately and are
|
||
described in prose below instead — one is not information about the inquirer,
|
||
and the other is not information at all. */
|
||
const COLLECTED = INTAKE_FIELDS.map((field) => field.label);
|
||
---
|
||
|
||
<BaseLayout
|
||
title="Privacy Policy · Intake and Data Handling · Pouya Lajevardi"
|
||
description="What the intake form collects, why, where it is stored, how long it is kept, who can see it, and how to have it deleted. Written to match what is built."
|
||
jsonLd={graph}
|
||
noindex
|
||
>
|
||
<section class="section hero">
|
||
<div class="wrap">
|
||
<Eyebrow dot>Privacy</Eyebrow>
|
||
<h1 class="display hero-h">
|
||
What the intake form collects, and for how long.
|
||
</h1>
|
||
<p class="hero-lede">
|
||
This describes what actually happens to what you send me, not what is
|
||
typical. Last updated {LAST_UPDATED}.
|
||
</p>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="section legal-body">
|
||
<div class="wrap">
|
||
<div class="prose">
|
||
<h2>What is collected</h2>
|
||
<p>
|
||
One form on this site collects personal information: the intake form
|
||
on the <a href="/contact/">contact page</a>. It asks for the
|
||
following, and the fields marked required on the form are the only
|
||
ones that must be completed.
|
||
</p>
|
||
<ul>
|
||
{COLLECTED.map((label) => <li>{label}</li>)}
|
||
</ul>
|
||
{
|
||
/* ⚠️ DO NOT WRITE "your IP address" HERE, AND DO NOT CONCLUDE ANYTHING
|
||
ABOUT WHETHER THE ADDRESS IDENTIFIES ANYONE. The handler stores
|
||
`requestContext.http.sourceIp` — behind the `/api/*` behaviour that
|
||
is a CloudFront edge, so the expected value is Amazon's. **Expected,
|
||
not measured:** `docs/09` Part 7.2 measures it at cutover and
|
||
enumerates three outcomes, one of which is that the reader's own
|
||
address does land. This copy therefore states only why the field is
|
||
kept, which is true in all three. A previous form hedged "usually
|
||
not yours" and then asserted "not precise enough to identify you" —
|
||
false in exactly the branch the hedge admitted. `claims-auditor`,
|
||
then `adversarial-reviewer` round 2. */
|
||
}
|
||
<p>
|
||
Submitting the form also records the date and time and your browser's
|
||
user-agent string. Those two are kept for investigating abuse of the
|
||
form and are not used for anything else.
|
||
</p>
|
||
<p>
|
||
It also records the network address the request arrived from. This
|
||
site sits behind a content delivery network, so that address is
|
||
normally the network's own rather than your connection's — which is
|
||
why it is kept simply because it arrives with the request, and not as
|
||
a way of identifying anyone.
|
||
</p>
|
||
<p>
|
||
Nothing else on this site collects personal information. There is no
|
||
newsletter, no account, no comment form and no upload.
|
||
</p>
|
||
|
||
<h2>Information about other people</h2>
|
||
<p>
|
||
The form asks for the other parties to the dispute and their counsel.
|
||
That is information about people who have not filled in the form and
|
||
may not know it was sent. It is asked for one reason, and the reason
|
||
is a commitment rather than an observation.
|
||
</p>
|
||
{
|
||
/* `<Undertaking>` AND `CONDUCT_UNDERTAKINGS`, NEVER TYPED PROSE —
|
||
§4's third class, whose characteristic failure mode is that a
|
||
promise gets quietly smaller and nothing fails. Undertaking (g),
|
||
attested 2026-09-03.
|
||
|
||
⚠️ IT IS THE COMPONENT FOR THE REASON THE COMPONENT EXISTS: one
|
||
treatment on every page, so a reader can tell a promise from a
|
||
description. This shipped for one pass as an ordinary paragraph in
|
||
`“`/`”` — the only such entities in `src/`, and a
|
||
commitment set as body prose reads as another sentence about
|
||
process.
|
||
|
||
It REPLACED the hand-typed "I cannot accept an appointment before
|
||
conflicts are checked", which stated the same proposition as a
|
||
constraint; keeping both would have set the undertaking beside its
|
||
own paraphrase — the (e)/(f) treatment. */
|
||
}
|
||
<Undertaking>{CONDUCT_UNDERTAKINGS.conflictsCheck}</Undertaking>
|
||
<p>
|
||
The check needs names. Please give names and nothing more about them.
|
||
The form asks you not to include privileged or confidential detail
|
||
anywhere in it, and the summary field says so directly. There is
|
||
deliberately no field for amounts in dispute and no way to attach a
|
||
document.
|
||
</p>
|
||
|
||
<h2>Why it is collected, and on what basis</h2>
|
||
<p>
|
||
To reply to your inquiry and to run a conflicts check. The basis is
|
||
your consent, which the form asks for explicitly with an unchecked box
|
||
you have to tick. The wording you agree to is on the form itself, and
|
||
it names <strong>SML Company Ltd</strong>, the company that holds this
|
||
practice's systems.
|
||
</p>
|
||
<p>
|
||
It is not used for marketing. It is not sold, rented or shared with
|
||
anyone for their own purposes.
|
||
</p>
|
||
|
||
<h2>Where it is stored</h2>
|
||
<p>
|
||
In a DynamoDB table in Amazon Web Services' Canada Central region, in
|
||
Canada. It is encrypted at rest. Two emails are sent when you submit
|
||
the form — a notification to the practice and a confirmation to you —
|
||
using Amazon Simple Email Service, also in the same Canadian region.
|
||
</p>
|
||
<p>
|
||
The table sits in an Amazon Web Services account that also runs
|
||
systems unrelated to this practice.
|
||
</p>
|
||
{
|
||
/* ⚠️ TWO PROCESSORS, AND BOTH MUST BE NAMED. `AGENTS.md` §7 records
|
||
mail hosting as **Google Workspace** and D18 sends the notification to
|
||
`info@smlcompany.ca`, so Google receives and stores every submission —
|
||
including the opposing parties and their counsel, the most sensitive
|
||
thing this form collects. A reader making a PIPEDA access request
|
||
needs both names. This paragraph replaced one asserting *"No other
|
||
third party receives it"*; see entry (ao). §7 is cited, not restated —
|
||
no MX record here. */
|
||
}
|
||
<p>
|
||
Two companies therefore process it, and both are named because a
|
||
reader asking for a copy or a deletion needs to know where it went. <strong
|
||
>Amazon Web Services</strong
|
||
> stores the submission and sends the two emails, in Canada. <strong
|
||
>Google</strong
|
||
> receives the notification email, because the practice's mail is on Google
|
||
Workspace — so a copy of what you send, including any names you give me,
|
||
sits in that mailbox. If you reply to the confirmation, that reply goes
|
||
there too.
|
||
</p>
|
||
<p>
|
||
The confirmation sent to you is delivered to whoever runs your email.
|
||
That is your provider rather than mine, and I have no control over
|
||
what they keep.
|
||
</p>
|
||
<p>
|
||
No one else is sent it. There is no CRM, no mailing list and no
|
||
analytics on the submission. Who can read what is stored is a
|
||
different question from who it is sent to, and it is answered under
|
||
"Who can see it" below.
|
||
</p>
|
||
|
||
<h2>How long it is kept</h2>
|
||
{
|
||
/* The sentence below asserts a MECHANISM, not just a period, and the
|
||
mechanism is still unobserved — AGENTS.md §9 Q60, open. **Pouya
|
||
ruled 2026-09-03 that the page publishes now and the deletion is
|
||
confirmed after launch**; the observation window opened 2026-09-02
|
||
and the earliest useful reading is 2026-09-04 (`docs/09` Part 10).
|
||
That decision is why this is no longer a `TODO(pouya)`. The table
|
||
setting lives in §7 and is deliberately not restated here — it was
|
||
once, and went stale within the day (§12 R19). Do not answer Q60
|
||
from the handler code, which only writes the attribute. */
|
||
}
|
||
<p>
|
||
<strong>{RETENTION_MONTHS} months from the date you send it</strong>,
|
||
after which the record is deleted automatically by the database rather
|
||
than by someone remembering to do it. That period is long enough to
|
||
run a conflicts check across the normal life of a matter and no longer
|
||
than necessary for that purpose.
|
||
</p>
|
||
<p>
|
||
Emails are a separate matter. The notification sits in the practice's
|
||
mailbox and the confirmation sits in yours, and neither is deleted by
|
||
that mechanism.
|
||
</p>
|
||
|
||
<h2>Who can see it</h2>
|
||
{
|
||
/* ⚠️ THIS SECTION AND THE SENTENCES BELOW ANSWER THE SAME QUESTION
|
||
AND CHANGE TOGETHER — by OPENING PHRASE, never by count. **Here:**
|
||
"The record in the table", "The system that receives", "The
|
||
notification goes to", "The confirmation that went to you".
|
||
**§Where it is stored:** "In a DynamoDB table", "The table sits in an
|
||
Amazon Web Services account", "Two companies therefore process it",
|
||
"The confirmation sent to you", "No one else is sent it". **§How long
|
||
it is kept:** "Emails are a separate matter".
|
||
⚠️ **THIS SECTION STATES WHO, NOT HOW — Pouya's ruling, 2026-09-02.
|
||
NOT TO BE RESTORED HERE:** the measurement paragraph, the
|
||
root-credential sentence, the single-sign-on and federated-login
|
||
enumeration, the resource-policy clause, the "company that runs a
|
||
database" aside, the deploy-credential sentence and the three-copies
|
||
summary. All true, all still in `AGENTS.md` §7 and
|
||
`docs/reference/intake-table-access-verification.md`. **No human
|
||
headcount** — a simulation counts identities, not people.
|
||
⚠️ **SENTENCE 1 IS SCOPED TO THE STORED RECORD** (paragraph 3 names
|
||
administrative staff, who read the mailbox and **cannot** read the
|
||
table) **AND PREDICATED ON ADMINISTERING THE ACCOUNT** (what §7
|
||
measures). §4 carries the row and the bar: **never widen it to
|
||
running, founding, practising or acting.**
|
||
**TWO CLAIMS GO STALE ON THEIR OWN** — who administers the account,
|
||
and who reads `info@smlcompany.ca`. §7 holds both; §12 **R21** is the
|
||
trigger.
|
||
⚠️ **THERE ARE THREE COPIES AND THE THIRD IS THE READER'S OWN** — the
|
||
handler puts the whole submission into the confirmation it sends the
|
||
inquirer. Never write "the one other place a copy exists". */
|
||
}
|
||
<p>
|
||
The record in the table: me, and the small number of people who
|
||
administer the account it sits in with me.
|
||
</p>
|
||
<p>
|
||
The system that receives what you send
|
||
<strong
|
||
>can only add a record — it cannot read back what is stored.</strong
|
||
>
|
||
</p>
|
||
<p>
|
||
The notification goes to the practice's mailbox, which is read by me
|
||
and by administrative staff and is hosted on Google Workspace — so
|
||
Google holds a copy of whatever you send me.
|
||
</p>
|
||
<p>
|
||
The confirmation that went to you sits with whoever runs your email.
|
||
That copy is in your hands rather than mine.
|
||
</p>
|
||
|
||
<h2>Cookies and analytics</h2>
|
||
{
|
||
ANALYTICS.installed ? (
|
||
<p>
|
||
Visits are counted using{' '}
|
||
{ANALYTICS.provider === 'plausible' ? 'Plausible' : 'Fathom'},
|
||
which is cookieless and collects no personal information and no
|
||
cross-site identifiers. There is nothing to consent to and no
|
||
banner, because it sets no cookies and stores no identifier on
|
||
your device.
|
||
</p>
|
||
) : (
|
||
<p>
|
||
<strong>This site sets no cookies and runs no analytics.</strong>
|
||
There is no tracking script on any page, and there is therefore
|
||
nothing to consent to and no banner. If cookies or analytics are
|
||
ever introduced, this page changes on the same day and its last
|
||
updated date moves with it. Your browser does cache this site's
|
||
fonts, stylesheets and images for up to a year so a return visit
|
||
loads faster, and those are the same files for every visitor.
|
||
</p>
|
||
)
|
||
}
|
||
<p>
|
||
There are no third-party scripts of any kind on this site, no embedded
|
||
video, no web fonts fetched from another company's servers, and no
|
||
social media widgets. The pages you are reading make no request to
|
||
anyone but this site.
|
||
</p>
|
||
|
||
<h2>Asking for a copy, or asking me to delete it</h2>
|
||
<p>
|
||
Email <a href={`mailto:${CONTACT.email}`}>{CONTACT.email}</a> and ask. You
|
||
can ask for a copy of what you sent, ask me to correct it, or ask me to
|
||
delete it before the {RETENTION_MONTHS} months are up.
|
||
{' '}{CONTACT.responseTime}
|
||
</p>
|
||
{
|
||
/* ⚠️ THE CLAUSE THAT WAS HERE PROMISED TO DISCLOSE THE OUTCOME OF A
|
||
CONFLICTS CHECK — *"I will tell you what its outcome was rather than
|
||
pretending the inquiry did not happen"* — and that is an UNDERTAKING,
|
||
which §4 may publish only where Pouya has made it in terms. He had
|
||
not. D20 finding 13, and it is closed by his attestation of
|
||
2026-09-03, which covers RUNNING the check and says nothing about
|
||
reporting it. The sentence now states what deletion does not undo and
|
||
stops there. Do not restore the promise without a second attestation:
|
||
it is a different commitment from the one he made. */
|
||
}
|
||
<p>
|
||
Deletion removes the record. It does not retract the emails already
|
||
sent, and it does not undo a conflicts check that has already been
|
||
run.
|
||
</p>
|
||
|
||
<h2>What an inquiry is not</h2>
|
||
<p>
|
||
Sending the form does not create a retainer, does not appoint me as a
|
||
neutral in your matter, and does not itself establish a mediator–party
|
||
relationship. It also does not, by itself, complete a conflicts check
|
||
— it gives me what I need to run one.
|
||
</p>
|
||
|
||
<h2>Changes to this page</h2>
|
||
<p>
|
||
If what happens to your information changes, this page is edited on
|
||
the same day and the date at the top moves. There is no archive of
|
||
previous versions.
|
||
</p>
|
||
|
||
<h2>Contact</h2>
|
||
<p>
|
||
Questions about any of the above:
|
||
<a href={`mailto:${CONTACT.email}`}>{CONTACT.email}</a>. The site is
|
||
{' '}{SITE.url}, and correspondence is by email — {CONTACT.location}.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</BaseLayout>
|
||
|
||
<style>
|
||
.hero {
|
||
padding-block-start: var(--space-9);
|
||
}
|
||
.hero-h {
|
||
margin-block: var(--space-4) var(--space-5);
|
||
/* --text-4xl, not --text-6xl. A legal page's job is to be read rather than
|
||
to land; at 96px this headline takes four lines before the reader reaches
|
||
the date they came to check. */
|
||
font-size: var(--text-4xl);
|
||
max-inline-size: 30ch;
|
||
}
|
||
.hero-lede {
|
||
max-inline-size: 58ch;
|
||
font-size: var(--text-lg);
|
||
line-height: var(--leading-body);
|
||
color: var(--text-secondary);
|
||
}
|
||
|
||
/* NOT `.reveal`. A legal document is the one page class where content must be
|
||
at full opacity the moment it renders, whatever the reader's scroll position
|
||
or motion setting — and where a reader may well arrive via Cmd-F. */
|
||
.legal-body {
|
||
padding-block-start: var(--space-7);
|
||
}
|
||
|
||
/* `global.css`'s `.prose` supplies the measure and paragraph spacing. These
|
||
are the two element types this page introduces that no other page's prose
|
||
block uses: headings inside a document, and a plain list. */
|
||
.prose h2 {
|
||
margin-block-start: var(--space-8);
|
||
font-family: var(--font-serif);
|
||
font-size: var(--text-2xl);
|
||
line-height: var(--leading-tight);
|
||
}
|
||
.prose h2:first-child {
|
||
margin-block-start: 0;
|
||
}
|
||
.prose ul {
|
||
margin-block-start: var(--space-4);
|
||
padding-inline-start: var(--space-6);
|
||
max-inline-size: var(--width-prose);
|
||
line-height: var(--leading-body);
|
||
color: var(--text-secondary);
|
||
}
|
||
.prose li + li {
|
||
margin-block-start: var(--space-2);
|
||
}
|
||
</style>
|