/* ===================================================================
   Folio — the document

   This is the CV itself, not the interface around it. It is laid out at
   true A4 metrics: 210mm wide, 16mm/18mm margins, 10pt body. The
   preview scales it down visually with a transform, which changes how
   big it looks and nothing about where the lines break. That is the
   whole point. A preview laid out at 368px and a PDF laid out at 210mm
   are two different documents wearing the same content.

   Pagination is done in JS, in js/docview.js, and the result is a list
   of .doc-page elements. The fragmentation properties below are kept
   anyway as a second line of defence: if a measurement is off by a
   hair in the print renderer, the browser still refuses to split a job
   entry across two pages.

   Rules the layout is not allowed to break, from the ATS research:
   one column, no tables, no page headers or footers, contact details
   inside the first lines of the body, conventional section headings.
   =================================================================== */

/* ─── page geometry ──────────────────────────────────────────────── */

@page {
    size: A4;
    /* The only margins the document has. No running header, no footer:
       most parsers never read either, so a name up there is a name the
       employer's system does not have. */
    margin: 16mm 18mm;
}

.doc {
    /* Paper is paper in either theme. The interface inverts in dark
       mode; a printed page does not, so the palette is re-scoped here
       rather than inherited. Still tokens, never raw hex outside
       variables.css and this one deliberate block. */
    --doc-paper:  #FFFFFF;
    --doc-ink:    #1B1A17;
    --doc-ink-2:  #33302A;
    --doc-soft:   #5E594F;
    --doc-faint:  #8C867A;
    --doc-rule:   #D6D1C7;
    --doc-pen:    #2B4BD8;

    --doc-page-width:   210mm;
    --doc-page-height:  297mm;
    --doc-margin-y:     16mm;
    --doc-margin-x:     18mm;

    /* The template. These are the "Rule" values from js/doctemplates.js,
       which is where all six live; the blocks at the foot of this file
       override them. Body size stays inside the 10-12pt band parsers
       expect, in every one of the six. */
    --t-name:            21pt;
    --t-headline:        11pt;
    --t-contact:         9pt;
    --t-body:            10pt;
    --t-title:           11pt;
    --t-date:            9.5pt;
    --t-note:            9.5pt;
    --t-heading:         14.5pt;
    --t-heading-track:   0.01em;
    --t-heading-case:    none;
    --t-heading-weight:  600;
    --t-heading-rule:    0.25mm;
    --t-heading-rule-w:  13mm;
    --t-heading-rule-gap: 1.2mm;
    --t-head-rule:       0.35mm;
    --t-section-gap:     5.5mm;
    --t-heading-below:   2mm;
    --t-minor-gap:       0.6;
    --t-prose-measure:   36em;
    --t-entry-gap:       3.8mm;
    --t-lead:            1.4;
    --t-accent:          var(--doc-pen);
    --t-date-font:       var(--font-mono);

    /* --t-rail is deliberately not declared here. js/doctemplates.js sets it
       on <html> so that one number defines the Ledger rail for the page and
       for the PDF; undeclared, the fallback below inherits it. A page that
       never runs that code, like the landing hero, gets 0. */

    font-family: var(--font-body);
    font-size: var(--t-body);
    line-height: calc(var(--t-lead) * var(--t-compress-lead, 1));
    color: var(--doc-ink-2);
    text-rendering: optimizeLegibility;

    /* Never leave one or two lines of a paragraph stranded alone at a page
       boundary.

       These two used to be decoration. The browser applies them where it
       fragments the flow itself, and this document is fragmented in JS, so
       phase 1 recorded that they did nothing at all. They do now:
       DocView.splitProse reads them off the paragraph with getComputedStyle
       and divides a page-long summary by them, so the declaration here is
       what decides rather than a second copy of the number living in the
       paginator. Firefox implements neither and returns nothing, and the
       paginator falls back to two, which is the classical minimum and the
       CSS initial value. */
    orphans: 3;
    widows: 3;

    /* The app draws its interface with subpixel antialiasing off. The
       sheet is drawn on that same page out of the same elements, so it
       inherited the setting on build.html and not in the harness, and
       the two surfaces were rendering the same glyphs differently. It
       is declared here so the document carries it wherever it is
       drawn. */
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

/* ─── the document declares itself ───────────────────────────────────
   Everything the document depends on is declared in this file, and
   nothing it depends on is left to whatever page it happens to be
   drawn on.

   That is not a style preference. The sheet is built out of ordinary
   h1, h2, p, ul and li on the same page as the interface, so every
   global rule in css/style.css lands on it too unless this file has an
   opinion. Phase 5 found two properties where it had none — the reset
   put line-height 1.15 on every heading and text-wrap pretty on every
   paragraph — and since tests/ats/harness.html loads only variables.css
   and this file, every number measured since phase 1 described a
   document the app was not drawing. Five pixels a heading, forty nine
   over a CV with ten sections, and paragraphs wrapping where pdfmake
   does not.

   That was closed with a guard in style.css, which fixes two
   properties and not the family: a new global rule there, silent here
   and absent from the guard, splits the two surfaces again. So the
   guard lives here now instead. A rule in style.css can only ever
   patch the app into agreeing with the harness, and only for the
   properties somebody remembered to list. A rule here defines the
   document on every surface at once, because every surface that draws
   it loads this file.

   :where() keeps the whole block at the specificity of .doc alone, so
   anything below with an opinion of its own still wins — .doc-name
   keeps its 1.05 leading, .doc-heading keeps its tracking, .doc-page
   keeps its own box model.

   tests/css-parity.test.mjs is the half that does not rely on anyone
   remembering: it reads every computed property of every element of
   all sixty documents on both surfaces and fails on a single
   difference. box-sizing and the transition below came out of its
   first run, after the guard. */
.doc :where(h1, h2, h3, h4, h5, h6) {
    line-height: inherit;
    letter-spacing: inherit;
}

.doc :where(*) {
    /* The reset in style.css sets this on everything; nothing set it
       here. It was inert on today's document, which is exactly why it
       was worth closing: the day a rule below gives a padded element a
       width, the app and the instrument start paginating differently
       and neither says so. */
    box-sizing: border-box;

    /* pretty and balance both move where a line breaks, and pdfmake
       and Word do neither, so a preview with them on wraps where the
       file will not. */
    text-wrap: wrap;

    /* Paper does not animate. The interface transitions link colour,
       which reached the sheet and cost nothing; a transition on a
       property that moves a box would be read mid-flight by the
       paginator, which measures the instant after it writes the
       markup. */
    transition: none;
}

.doc-page {
    width: var(--doc-page-width);
    min-height: var(--doc-page-height);

    /* And never taller. A unit that could not fit used to make the sheet
       grow instead: the preview drew 525mm of paper and called it two pages,
       while the file it came from was dropping the contents in silence. A
       sheet that cannot be A4 is the first place that should have shown.

       Not clipped, deliberately. overflow:hidden would take the page numbers
       with it, since they sit outside the sheet, and hiding what does not fit
       is the same fault as the one this phase exists to remove. The ceiling
       keeps the sheet honest about its size; .doc-page-over says the rest is
       past the bottom of it. */
    max-height: var(--doc-page-height);

    padding: var(--doc-margin-y) var(--doc-margin-x);
    background: var(--doc-paper);
    box-sizing: border-box;
    position: relative;

    /* The sheet is its own stacking context. The head sits at z-index -1
       so that Chrome's print path, which writes the PDF stream in paint
       order, opens each page with the name instead of closing with it -
       but a negative phase belongs to the nearest stacking context, and
       without this line that was the page root, which paints before this
       element's own background. The sheet covered its own header on
       screen, on every document, and every instrument passed: they all
       read objects and computed style, and covering happens in
       composition. Phase 10's eye check caught it; the hit test in
       tests/css-parity.test.mjs keeps it caught. Isolation scopes the
       negative phase to the sheet, so the head paints after the paper
       and still ahead of the flow. */
    isolation: isolate;
}

/* Not in the measuring host. That one lays the whole document out as a
   single continuous flow on purpose, and reads every unit's offset from it,
   so a sheet-high ceiling in there would be a ceiling on the document. */
#docMeasure .doc-page {
    max-height: none;
}

/* The tripwire, and it should never be seen. DocView marks a page carrying a
   unit it could not place. The words still reach the file; they just do not
   reach the bottom of this page.

   A summary running over a whole sheet used to be the way to see it, and
   seeing it was the point: the preview stopped at the ceiling and the file
   carried the rest, so the download showed more than the preview and this
   product's one promise was upside down. D-16 sent that case to
   DocView.splitProse, which divides a paragraph at a line boundary the way
   any typesetter would. What is left for the tripwire is a single bullet
   longer than a sheet of paper, and it stays as the guard against the first
   case coming back. */
.doc-page-over::after {
    content: "More text here than one page holds. Shorten it to see the rest.";
    position: absolute;
    inset-inline: 0;
    bottom: 0;
    padding: 2.5mm var(--doc-margin-x);
    font-family: var(--font-body);
    font-size: 9pt;
    line-height: 1.4;
    color: #FFFFFF;
    background: #B3261E;
}

/* ─── the head: name, headline, contact ──────────────────────────── */

/* The head carries the three things a reader looks for first: who, what,
   and how to reach them. It used to be four evenly spaced lines with no
   grouping, so the name did not lead and the contact details read as more
   body text. Now the name and the headline sit together, and the contact
   lines are set as one tighter block below them. */
/* The hairline is drawn as a box rather than as a border, and it is the
   second time this file has had to learn that. A border-width is rounded to
   whole pixels by the browser before anything can read it back: 0.35mm is
   1.3228px and getComputedStyle answers 1px, so the number the design states
   is a number the page never draws and the file has no way of matching. The
   mark under a heading has always been a box for a different reason - it has
   to be shorter than the heading - and it renders 0.25mm as 0.25mm.

   Absolutely positioned, so it is out of flow and the padding below carries
   the whole gap. In flow it would collapse its own margins on the three
   templates that set --t-head-rule to 0, and those three would lose 3.2mm
   under the contact block with nothing to say why.

   tests/visual-parity.py is what found this. */
.doc-head {
    position: relative;
    /* Behind its in-flow siblings in paint order, which nothing overlaps so
       nothing changes on screen - but paint order is the order Chrome's
       print path writes the PDF content stream in, and a positioned element
       paints after every in-flow block. Until phase 9 measured the print
       output, the name and the contact details of every printed CV sat at
       the END of the file's stream: pdf.js-family parsers read the whole
       body first and the candidate's name last, and the contacts fell out
       of the first-lines window entirely. A negative z-index moves the
       block to the phase before in-flow content, so the stream begins with
       the name again. */
    z-index: -1;
    padding-bottom: calc(3.2mm + var(--t-head-rule));
    margin-bottom: 3.5mm;
}

.doc-head::after {
    content: "";
    position: absolute;
    inset-inline: 0;
    bottom: 0;
    height: var(--t-head-rule);
    background: var(--doc-rule);
}

.doc-name {
    font-size: var(--t-name);
    font-weight: 600;
    line-height: 1.05;
    letter-spacing: -0.02em;
    color: var(--doc-ink);
    margin: 0;
}

.doc-headline {
    font-size: var(--t-headline);
    line-height: 1.3;
    color: var(--t-accent);
    margin: 0.8mm 0 0;
}

/* Contact details are body text, deliberately. See the note on @page. */
.doc-contact {
    font-family: var(--t-date-font);
    font-size: var(--t-contact);
    line-height: 1.45;
    color: var(--doc-soft);
    margin: 0;
    word-break: break-word;
}

/* The gap belongs above the block, not between its lines. */
.doc-contact:first-of-type { margin-top: 2.2mm; }

.doc-contact a { color: inherit; text-decoration: none; }

/* The separator between two contact details, and it used to be two things
   the file could not copy. It was --doc-soft at 40%, which is a colour that
   exists on the page and in no stylesheet - a PDF names one colour per glyph
   and has no opacity to apply, so pdfmake drew --doc-faint and the LaTeX
   export drew --doc-rule and the sheet drew neither. And its air was CSS
   padding, which the file spelled as two spaces on each side, so the same
   line of contact details came out 26pt longer in the file than on the page.

   One token, and the spaces are real characters in the same face on all
   three surfaces. tests/visual-parity.py found both. */
.doc-contact .doc-sep { color: var(--doc-faint); white-space: pre; }

/* ─── sections ───────────────────────────────────────────────────── */

/* Headings and entries are siblings rather than a heading nested with its
   entries inside a section wrapper. A section has to be able to carry on
   over a page boundary, and a wrapper cannot be in two places. */

.doc-heading {
    font-size: var(--t-heading);
    font-weight: var(--t-heading-weight);
    letter-spacing: var(--t-heading-track);
    text-transform: var(--t-heading-case);
    color: var(--doc-ink);

    /* Two gaps, not one. The space above says a new section begins; the space
       below binds the heading to the section it opens, and it used to be
       whatever margin collapse handed over from the entry underneath. The
       thirteen references keep the first at about two and a half times the
       second, and ours had it at one and a half, which is a heading floating
       between two sections rather than belonging to one. See
       js/doctemplates.js. */
    margin-top: calc(var(--t-section-gap) * var(--t-compress, 1));
    margin-bottom: calc(var(--t-heading-below) * var(--t-compress, 1));

    /* A heading alone at the foot of a page, with its content overleaf,
       is the defect this whole phase exists to remove. */
    break-after: avoid;
    page-break-after: avoid;
}

/* A section holding one thing is not a chapter. It keeps its heading and its
   rule and takes less room to arrive in, which is what tells a page of seven
   short sections apart from a page with two long ones. DocView.unitList marks
   them; the fraction is DocTemplates.MINOR_GAP. */
.doc-heading-minor {
    margin-top: calc(var(--t-section-gap) * var(--t-minor-gap) * var(--t-compress, 1));
}

/* The mark under the heading, and it is a mark rather than a divider. A
   hairline the width of the column reads as a line across the page, and the
   second page of a long CV had seven of them stacked down it with a line or
   two of content between each pair. Drawn as a box rather than a border so it
   can be shorter than the heading's own width; pdfmake draws the same line on
   a canvas at the same length. */
.doc-heading::after {
    content: "";
    display: block;
    width: var(--t-heading-rule-w);
    height: var(--t-heading-rule);
    margin-top: var(--t-heading-rule-gap);
    background: var(--doc-rule);
}

.doc-heading + * {
    break-before: avoid;
    page-break-before: avoid;
}

/* Whatever lands at the top of a page starts at the top of it. */
.doc-page > :first-child { margin-top: 0; }

/* ─── entries ────────────────────────────────────────────────────── */

/* One job, one degree, one project. Indivisible.

   --t-compress is set by js/doctemplates.js and is 1 unless squeezing the
   gaps would save a whole page. See DocView.plan. */
.doc-entry {
    margin-top: calc(var(--t-entry-gap) * var(--t-compress, 1));
    break-inside: avoid;
    page-break-inside: avoid;
}

/* The first entry of a section is not the next entry in a list, so it takes
   the heading's own gap rather than the one between two jobs. Adjacent
   margins collapse to the larger, and the entry gap is the larger on every
   template, so without this the heading's carefully set space below is a
   number nothing reads. It has to come after .doc-entry to win, and it is
   spelled out rather than written as .doc-heading + * so that .doc-prose and
   .doc-inline, which carry no margin at all, keep taking the gap from the
   heading above them. */
.doc-heading + .doc-entry {
    margin-top: calc(var(--t-heading-below) * var(--t-compress, 1));
}

.doc-entry-head {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 6mm;
}

.doc-entry-title {
    font-size: var(--t-title);
    font-weight: 600;
    color: var(--doc-ink);
    line-height: 1.25;
}

.doc-date {
    font-family: var(--t-date-font);
    font-size: var(--t-date);
    color: var(--doc-soft);
    white-space: nowrap;
    flex-shrink: 0;
}

/* Ledger only: the date leads the line from a fixed rail, and everything
   under it ranges off the same edge. --t-rail is 0 everywhere else, so
   these rules cost nothing in the other five.

   .doc-entry-body was missing from this list, so a project or an award
   described in a single sentence began at the left margin while the company
   line above it and the link below it both began 40mm in. The PDF has always
   indented it. Found in the phase 4 screenshots, not by any check: the page
   count was right, the recall was a hundred percent, and the page was wrong. */
.doc-org,
.doc-note,
.doc-entry-body,
.doc-entry ul { margin-inline-start: var(--t-rail, 0mm); }

.doc-org {
    color: var(--doc-soft);
    margin-block: 0.4mm 0;
}

/* A GPA printed in the faint grey reads at about 3:1 against white, which
   is fine on a backlit screen and washes out on paper. Anything that is
   part of the document gets a colour that survives a printer. */
.doc-note {
    font-size: var(--t-note);
    color: var(--doc-soft);
    margin-block: 0.4mm 0;
}

.doc-link {
    font-family: var(--t-date-font);
    font-size: var(--t-contact);
    color: var(--t-accent);
    text-decoration: none;
    word-break: break-word;
}

.doc-entry ul {
    margin-block: 1.2mm 0;
    padding-inline-start: 4.2mm;
    list-style: disc;      /* a real bullet. An icon reads to a parser as junk */
}

.doc-entry li {
    margin-top: 0.7mm;
    padding-inline-start: 0.5mm;
    break-inside: avoid;
    page-break-inside: avoid;
}

/* A description of one sentence. It gets no bullet, because a list of one
   reads as a mistake rather than as emphasis. See CVDocument.entry. */
.doc-entry-body {
    margin-block: 1.2mm 0;
}

/* What is left of an entry that was taller than a whole page, carried on
   below the break. It has no head of its own: a repeated job title is a job
   the parser files twice. The list loses its own top margin because the
   entry above it already supplies the gap, which is what PdfDocument.more
   does by passing zero. See DocView.splitUnit and D-01. */
.doc-entry-more > ul {
    margin-block-start: 0;
}

.doc-entry li::marker { color: var(--doc-faint); }

/* The summary, and the only place in the document the classic measure
   applies. Track C measured nineteen templates, ours and thirteen respected
   ones, and not one of them comes near the 60 to 75 characters the guides
   name: the range is 83 to 132, and narrowing the whole column to 75 would
   put us outside every one of them and lengthen the document by a quarter.
   That work was cancelled and stays cancelled.

   What the guide is actually about is the return sweep, the eye's journey
   back to the start of the next line, and it only costs anything when there
   are lines in a row. Every other block here is a point or two long, so it
   makes that journey once. The summary is the one run of continuous prose in
   a CV, four or five lines deep, and it is the one place the rule bites.

   36em is about 73 characters at the body size, inside the guidance and
   inside the reference distribution for a paragraph. pdfmake sets the same
   width from the same number; see PdfDocument.proseWidthPt. */
.doc-prose {
    margin: 0;
    max-width: var(--t-prose-measure);
}

/* Skills and languages: one run of comma-separated text, not a table.
   Workday cannot read a two-column skills table, and a two-column skills
   table is the most common way people build one. */
.doc-inline { margin: 0; }

/* ─── the six templates ──────────────────────────────────────────── */

/* The values come from js/doctemplates.js, which the PDF builder reads
   too. The attribute goes on <html> so that the hidden element the
   paginator measures in is under it as well: measuring one template and
   drawing another is how a preview starts lying about its page count.

   All six are one column with the same sections in the same order. What
   changes is size, rhythm, weight and where the dates sit. Two columns is
   the formatting mistake that breaks parsers outright, so it is not on
   offer in any of them.

   --t-heading-track never exceeds 0.08em, in any of the six. Past that the
   gap between two letters reaches the width of a space and the heading
   stops being a word to anything reading the file. The reasoning and the
   measurement are in js/doctemplates.js; tests/doctemplates.test.mjs reads
   both files and fails if they stop agreeing. */

[data-doc-template="plain"] .doc {
    --t-name: 19pt;   --t-headline: 10.5pt; --t-contact: 9.5pt;
    --t-body: 10pt; --t-title: 11pt; --t-date: 9.5pt; --t-note: 9.5pt;
    --t-heading: 15pt; --t-heading-track: 0; --t-heading-case: none;
    --t-heading-weight: 400;
    --t-heading-rule: 0; --t-heading-rule-w: 0; --t-heading-rule-gap: 0;
    --t-head-rule: 0;
    --t-section-gap: 6mm; --t-heading-below: 2.2mm; --t-entry-gap: 3.8mm; --t-lead: 1.4;
    --t-accent: var(--doc-ink); --t-date-font: var(--font-body);
}

[data-doc-template="stack"] .doc {
    --t-name: 23pt;   --t-headline: 11.5pt; --t-contact: 9pt;
    --t-body: 10pt; --t-title: 11pt; --t-date: 9.5pt; --t-note: 9.5pt;
    --t-heading: 13.5pt; --t-heading-track: 0; --t-heading-case: none;
    --t-heading-rule: 0; --t-heading-rule-w: 0; --t-heading-rule-gap: 0;
    --t-head-rule: 0;
    --t-section-gap: 7.5mm; --t-heading-below: 2.6mm; --t-entry-gap: 5.2mm; --t-lead: 1.5;
}

[data-doc-template="dense"] .doc {
    --t-name: 18pt; --t-headline: 10pt; --t-contact: 8.5pt;
    --t-body: 10pt; --t-title: 10.5pt; --t-date: 9pt; --t-note: 9pt;
    --t-heading: 12pt; --t-heading-track: 0.01em;
    --t-heading-rule-w: 11mm;
    --t-section-gap: 4mm; --t-heading-below: 1.5mm; --t-entry-gap: 2.8mm; --t-lead: 1.28;
    --t-accent: var(--doc-ink);
}

[data-doc-template="caps"] .doc {
    --t-name: 20pt;   --t-headline: 10.5pt; --t-contact: 9pt;
    --t-body: 10pt; --t-title: 11pt; --t-date: 9.5pt; --t-note: 9.5pt;
    --t-heading: 13pt; --t-heading-track: 0.08em; --t-heading-case: uppercase;
    --t-heading-rule: 0; --t-heading-rule-w: 0; --t-heading-rule-gap: 0;
    --t-section-gap: 6.5mm; --t-heading-below: 2.4mm; --t-entry-gap: 4.2mm; --t-lead: 1.42;
    --t-accent: var(--doc-ink);
}

[data-doc-template="ledger"] .doc {
    /* The mark under a heading is the width of the date rail rather than a
       short one, so the column the dates stand in is declared on every
       section, including the ones with no dates in them. One number, and
       tests/doctemplates.test.mjs asserts it is the same number as the rail
       the entries below are ranged off. */
    --t-heading: 14pt; --t-heading-rule-w: 40mm;
    --t-entry-gap: 4.2mm;
}

/* Ledger puts the date first on the line rather than to the right of it.
   js/docview.js emits it in that order too, so the text comes out of the
   file in the order it is read on the page. */
[data-doc-template="ledger"] .doc-entry-head {
    justify-content: flex-start;
    gap: 0;
}

[data-doc-template="ledger"] .doc-date {
    width: var(--t-rail, 0mm);
    text-align: left;
}

/* ─── screen presentation ────────────────────────────────────────── */

/* Holds the unscaled document for printing. Off screen until the print
   stylesheet turns it on, so Ctrl+P and the download button are working
   from the same page list the preview drew. */
.doc-print-root { display: none; }

/* The landing page's hero sheet. A real page shrunk to the column rather
   than a drawing of one, so it cannot quietly stop matching what the app
   produces. The scale comes from js/landing.js, which knows the column
   width; the tilt makes it read as a thing on a desk. */
.doc-poster {
    transform-origin: top left;
    transform: scale(var(--doc-scale, 0.5)) rotate(-1.4deg);
    will-change: transform;
}

@media (prefers-reduced-motion: no-preference) {
    .doc-poster { transition: transform var(--transition-slow); }
    .hero-sheet-wrap:hover .doc-poster {
        transform: scale(var(--doc-scale, 0.5)) rotate(-0.6deg) translateY(-8px);
    }
}

/* The document is laid out at 210mm and shrunk to fit whatever space the
   preview has. .doc-fit gets its height from JS, because a transform
   leaves layout size untouched. */
.doc-fit {
    position: relative;
    overflow: hidden;
}

.doc-scale {
    transform-origin: top left;
    transform: scale(var(--doc-scale, 1));
}

@media screen {
    .doc-page {
        /* A ring rather than a border. A border is inside the box, and the
           box is 210mm with box-sizing: border-box, so one pixel of screen
           furniture was taking two pixels of column away from the document -
           on the sheet and in the measuring host both, while the file it is
           compared against had the full width. Two pixels never moved a line
           in sixty documents, and a line is exactly what it could move. A
           spread shadow draws the same hairline and costs no layout.

           tests/visual-parity.py found this: every line on the sheet started
           0.75pt further in than the same line in the file. */
        box-shadow: 0 0 0 1px var(--rule), var(--shadow-sheet);

        /* 3px, and not --radius-xs, which is 0.1875rem. rem is the
           interface's scale: css/style.css drops the root to 15px under
           768 and the sheet's corner came out at 2.8125px on a phone and
           3px everywhere else. A corner is nothing, but the document
           being sized by the interface's type scale is the same fault as
           D-24 wearing different clothes, and the next value someone
           writes in rem here might be a margin.

           tests/css-parity.test.mjs found this at 390px. */
        border-radius: 3px;
    }

    .doc-page + .doc-page { margin-top: 6mm; }
}

/* The page number sits beside the sheet in the preview and is not part of
   the document, so it is never printed and never exported. */
.doc-page-mark {
    position: absolute;
    top: 0;
    left: 100%;
    margin-inline-start: 3mm;
    font-family: var(--font-mono);
    font-size: 9pt;
    color: var(--ink-faint);
    white-space: nowrap;
}

/* ─── print ──────────────────────────────────────────────────────── */

/* Everything but the document leaves the page. The document itself is
   already paginated, so each .doc-page ends where the preview said it
   would; @page supplies the margins, so the on-screen padding is
   dropped to avoid doubling them. */
@media print {
    /* The interface paints warm paper, and printing warm paper means the
       document arrives tinted, or blank where the reader has background
       graphics switched off. On paper, paper is the paper. */
    html, body { background: none; }
    body::before { display: none; }

    /* The document's own ink is not the browser's to economise. Both
       hairlines are background boxes, and with the print dialog's
       Background-graphics checkbox off - the default - Chrome dropped them
       entirely: phase 9 read the printed file and both rules came back
       white. The same economy pass darkened the faint contact separator
       from its declared #8C867A to #383530. Exact means the marks and the
       greys leave the printer as the stylesheet states them; the page
       background above stays unprinted because it is set to none, not
       because the browser decided. */
    .doc {
        -webkit-print-color-adjust: exact;
        print-color-adjust: exact;
    }

    .app-container,
    .preview-toggle,
    .modal,
    /* Toasts hang off <body>, not off the app container, so the hide list
       above never covered them. Phase 9 printed the page while "Previous
       data loaded" was still up and the printed file carried the toast: two
       words in the margin and a box over the document. Anyone printing
       right after a download toast got the same. */
    .toast,
    .skip-link { display: none !important; }

    .doc-print-root { display: block !important; }

    .doc-fit { overflow: visible; }
    .doc-scale { transform: none; }

    .doc-page {
        width: auto;
        min-height: 0;
        /* @page owns the sheet when printing, and a ceiling here would cut
           the last lines off every page rather than guard one. */
        max-height: none;
        padding: 0;
        margin: 0;
        border: none;
        border-radius: 0;
        box-shadow: none;
        background: transparent;
    }

    .doc-page + .doc-page { margin-top: 0; }
    .doc-page:not(:last-child) { break-after: page; page-break-after: always; }

    .doc-page-mark,
    .doc-page-over::after { display: none; }

    /* The headline and the links used to be forced to --doc-ink-2 here,
       which made Ctrl+P a third colour for the same line: the sheet and
       both downloads say --t-accent. The rule predated the accent work and
       survived because no test emulated print media; phase 9's print
       measurement flagged it on every template and it is gone. */

    a { text-decoration: none; }
}
