/* print.css — the scroll on paper (2026-09-13).

   Every other medium on this site is a simulation of paper. This one is not,
   and until now it was the only surface where the metaphor was available and
   unclaimed: printing `/` gave you the fixed full-viewport ink canvas, the
   floating chrome, the vinyl player pinned over the copy, the theme switch,
   the toc rail, and a `/gallery/` rendered as a black rectangle.

   WHY A SEPARATE FILE, LINKED `media="print"`:
   1. The CSP is `style-src 'self'` with no `unsafe-inline`. A print block MUST
      be a real file; an inline `<style media="print">` is dropped.
   2. A `media="print"` sheet cannot reach the screen, so this file is free to
      be blunt in ways no screen rule is allowed to be.
   3. One new file adds one new `?v=` pin instead of bumping the four existing
      pins that `@media print` blocks appended to styles / plate / museum /
      business-chrome would have moved.

   Everything below is ALSO wrapped in `@media print`, belt and braces: a page
   that ever links this sheet without the media attribute still cannot be
   touched by it.

   ON `!important`. This sheet is a whole-medium override, the same shape as
   the `prefers-reduced-motion` block at the foot of styles.css, and it fights
   selectors it does not own: `.flow-item[data-offset="0"]`,
   `.flow:not(.is-live) .flow-track`, `.museum-page .gallery-shell[…] #hall-2`.
   Out-specifying each of those from here would mean copying their internals
   into this file, where they would rot the first time either side moved.
   `!important` is used ONLY where the screen sheets pin layout the print
   medium cannot keep — the drops, the unpins, the unstacking — and never for
   colour, which flows through tokens and needs no force.

   NO `url()` ANYWHERE IN THIS FILE. Print rules are the least-looked-at rules
   on a site, so an asset reference here would rot silently; the sheet borrows
   the paper's material through the existing `--grain-layers` token instead of
   naming any file of its own. `test/print.test.js` pins that.

   BACKGROUNDS ARE NOT FORCED. Chrome prints background colours and images only
   when the reader asks for them, and that is the reader's decision, not this
   sheet's — a full-page 宣纸 grain pushed onto someone's toner is rude. So
   every page here reads as ink on white with backgrounds OFF, and gains the
   paper's 帘纹 grain when they are ON. Two elements are exempt, and both are
   exempt because the colour IS the content: the palette strip in the colophon
   (纸 / 墨 / 朱 / 青 with the pigment removed is not a quieter strip, it is a
   blank pill) and the three chops on /seal/ (a seal bench without its seals is
   not a seal bench). Those two opt in with `print-color-adjust: exact`; nothing
   else does. */

@media print {

/* — 1. The page box —
   Wide enough for the 宣纸's measure to reflow into a column that still reads,
   with a foot deep enough for the 落款 when it lands last. */
@page {
  margin: 16mm 14mm 18mm;
}

/* — 2. The medium is light. Always. —
   Two rooms on this site are dark by construction and three more follow the
   system, so "print it" can mean white type on white paper. Every room is
   re-grounded through its own tokens rather than patched component by
   component: the tokens are already the single dial each room turns, so
   turning them is the whole fix. `color-scheme` goes with them, or the
   `light-dark()` calls in the shared chrome keep handing back night values. */
:root,
:root:has(.gallery-page),
:root:has(.games-page) {
  color-scheme: light;
  background: #fff;
}

/* `:root`, not `html`. styles.css declares the palette on `:root`, which
   scores (0,1,0); a bare `html` scores (0,0,1) and loses to it however late it
   is declared. The first capture of `/icons/` came back on the cream `#f3f2ea`
   ground because of exactly that, and the homepage only looked right because
   site.js had stamped `data-theme` on it. */
:root,
:root[data-theme] {
  /* The 纸墨 palette, restated so the 科技 profile — or a dark system under
     the `light dark` rooms — cannot print its night values. */
  --paper: #fff;
  --paper-raised: #fff;
  --paper-deep: #fff;
  --ground: #fff;
  --ink: #222a22;
  --ink-soft: #3c453c;
  --ink-mute: #525b50;
  --line: #d8d5c6;
  --line-dark: #b9b6a5;
  --cinnabar: #d2352c;
  --cinnabar-deep: #b02e26;
  --celadon: #a9bfad;
  --celadon-deep: #48604c;

  /* — 3. The 宣纸 on white, with the grain kept light —
     The sheet stays a sheet: same deckled edge, same 帘纹, same fibre. But the
     thing it is printed on is already paper, so the sheet's own colour goes to
     plain white and the material drops to about half strength. Above that the
     grain competes with the type instead of sitting under it. The lift shadow
     goes entirely: a drop shadow exists to raise the sheet off the living
     wash, and the wash is not here. */
  --rice: 255, 255, 255;
  --rice-o: 1;
  --rice-fiber: .26;
  --rice-lift: transparent;
  --sheet-shadow: none;

  /* 洒金 does NOT print, and this is measured rather than squeamish. The gold
     is a flat `--gold` fill that only becomes flecks because an external
     `sajin.svg` masks it. The first capture of `/` with background graphics on
     came back a solid beige page: #b0862c at .20 over white is #efe7d5, which
     is exactly what the whole sheet measured, because the mask had not
     resolved when `Page.printToPDF` took its snapshot. A decorative layer whose
     only failure mode is "tint the entire page" does not belong on paper.
     The 帘纹 and the fibre stay — those are gradients and data-URI filters
     inside `--grain-layers`, with no file to arrive late — so the sheet keeps
     the grain the idea asked for and loses the one layer that can smear. */
  --sajin-o: 0;
  --sajin-o-foot: 0;
}

html,
body {
  width: auto;
  min-height: 0;
  margin: 0;
  padding: 0;
  background: #fff;
}

/* Every child route reserves a band at the top and a column at the side for
   the floating chrome. The chrome is gone below, so the clearance it bought
   is dead space — on /ke/ it was 150px of nothing above the masthead. */
:root {
  --qb-chrome-gap: 0px;
  --qb-chrome-size: 0px;
  --qb-chrome-clear-block: 0px;
  --qb-chrome-clear-inline: 0px;
}

/* `.rice::before` is the blurred sheet-with-a-shadow layer. On the homepage
   `.scroll-sheet::before` already cancels it; cancel it everywhere else too,
   because a 26px blur across a multi-page document is a rasterisation cost
   paid for a shadow with nothing left to fall on. */
.rice::before { content: none; }

/* The 装裱 hardware — the 天杆 batten and its cords, the 地杆 roller and its
   bone caps — is how a scroll hangs on a wall. On paper the sheet IS the
   artefact, and painting the rod it used to hang from is a picture of
   furniture. */
.stave-mount,
.stave,
.roller-mount,
.roller { display: none !important; }

/* — 4. Furniture that does not print —
   Anything a reader cannot press, scrub, hover or switch. The ink stage is
   first because it is the loudest: a fixed full-viewport canvas prints as a
   grey field under page one and as nothing at all under the rest. */
.ink-stage,
.art-fallback,
.qg-backdrop,
.toc-rail,
.theme-col,
.theme-switch,
.theme-live,
.skip-link,
.salon-skip,
.qb-chrome,
.qb-player,
.contact-form,
.flow-browse,
.flow-controls,
.flow-overview,
.flow-reflection,
.museum-enter,
.museum-status,
.cut-recut,
.cut-status,
.bench-controls,
.scene-status,
.scene-controls,
.projector-reel,
.projector-status,
.qg-toasts,
.qg-curtain,
.qg-tower-overlay,
.nf-curtain,
.nf-actions--sticky,
.nf-dock-top,
.nf-hud-ability,
.nf-card-fly,
.nf-load-bar,
.nf-mapsel-acts,
.nf-bigsheet-foot,
.nf-welcome-go,
.nf-report-acts,
.qg-mj-fx,
.qg-confirm,
.qg-rules,
.qg-identity,
.qg-identity-status,
/* `button:not(.stone)`, because on /seal/ the three chops ARE the page and
   each one is a <button> — "press a chop to stamp it again" is the
   interaction, the impression is the content. The first capture of /seal/
   printed three readings with nothing above them. */
button:not(.stone),
input,
select,
textarea,
video,
canvas,
iframe { display: none !important; }

/* Hiding the inputs does not unset them: `:checked` still drives the seal
   bench's 朱文 / 白文 `:has()` switch, so the printed bench shows whichever
   cut was selected on screen rather than collapsing to neither. */

/* Each chop is a cinnabar FILL behind an SVG mask, so it is a background in
   the browser's sense and vanishes when the reader prints without background
   graphics. It opts into its own colour for the same reason the palette strip
   does: a seal bench printed without its seals is not a printed seal bench. */
.stone,
.stone * {
  print-color-adjust: exact;
  -webkit-print-color-adjust: exact;
}

/* Dropping the films and the live canvases is not a loss. On this site every
   one of them is layered OVER a still that was always there for no-JS and
   reduced motion, so removing the moving layer uncovers the printable one:
   the museum portal under `.museum-film`, the 花信 poster under
   `.rose-canvas`, the album plate under `.flow-canvas`. */
.rose-poster,
.museum-still,
.museum-still img { opacity: 1; }

/* — 5. Nothing stays pinned —
   CSS cannot select "whatever computes to fixed", so this is the enumerated
   list, taken from every `position: fixed | sticky` in the public stylesheets.
   `test/print.test.js` re-derives that list from source and fails when a new
   pinned box appears without a line here, because the failure mode is silent:
   a fixed box prints once, on page one, over whatever else is there. */
.qb-chrome__back,
.qb-chrome__top,
.qg-actions,
.qg-lr-plays:has(.qg-actions--liar),
.qg-mj-plays,
.qg-sj-plays,
.qg-xq-plays,
.counterline .counter-receipt,
.capture-plate .rose-plate {
  position: static !important;
  inset: auto !important;
}

/* — 6. Boxes that clip must stop clipping —
   A fragmentation container with `overflow` other than `visible` does not
   paginate: Chrome prints the first page of it and drops the rest. The
   homepage puts its whole document inside `.mount` and `.unroll-clip`, both
   clipped, so without this the site prints as a one-page excerpt. The unroll
   clip-path is the same trap by another mechanism — a page captured mid-roll
   is clipped to however far the roll had travelled. */
.mount,
.unroll-clip,
.unroll-paper,
.flow,
.museum,
.rose-plate,
.cut-sheet {
  overflow: visible !important;
  min-height: 0;
}

html[data-unroll] .unroll-clip { clip-path: none !important; }
html[data-unroll] .unroll-paper { opacity: 1; transform: none; }

/* — 7. Links carry their destination —
   A printed link is a dead end unless its address travels with it. Three
   suppressions follow, each for a link that would otherwise print its address
   twice or hang one on a picture. CSS cannot compare an element's text with
   its href, so every one of them is NAMED rather than detected; each is a real
   case found by looking at a capture, not a guess. */
a[href^="http"]::after {
  content: " (" attr(href) ")";
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: .82em;
  font-weight: 400;
  letter-spacing: .01em;
  word-break: break-all;
  color: #525b50;
}

/* `a.rose-source`, not `.rose-source`: a pseudo-element counts as a TYPE, so
   `a[href^="http"]::after` scores (0,1,2) and a bare `.rose-source::after`
   scores (0,1,1) and loses no matter how late it is declared. The first
   capture of /rose/ printed the address twice and said so. */
a[href^="http"]:has(> img)::after,
a.rose-source::after { content: none; }

/* The third case, and the one that showed up as broken layout rather than as
   redundancy: `.elsewhere` is a register that already prints each destination
   in its own right-hand column — @jackie-qin, venuslabs.net,
   jackie-qin/skills. Trailing the full URL after the link text repeated it and
   then `word-break: break-all` wrapped the repeat into the 50px between two
   flex columns, where it ran down over the two rows below. `[href]` rather
   than a bare descendant so this clears `a[href^="http"]::after` on
   specificity instead of on source order. */
.elsewhere a[href]::after { content: none; }

.rose-source {
  width: auto;
  height: auto;
  color: #3c453c;
}

.rose-source-note {
  position: static;
  max-width: none;
  padding: 0;
  border: 0;
  background: none;
  opacity: 1;
  visibility: visible;
  transform: none;
}

/* — 8. The 落款 prints as a real colophon —
   The 干支 date, the copyright and the 立轴 format line are the signature block
   of the work, and the four-band strip beside them is its pigment note. Keep
   them together on one page and give the strip its actual colours: the bands
   ARE 纸 / 墨 / 朱 / 青, which is why this is one of the two places the sheet
   overrides the reader's background-graphics setting. */
.site-footer {
  color: #3c453c;
  break-inside: avoid;
  page-break-inside: avoid;
}

.footer-inner { break-inside: avoid; page-break-inside: avoid; }

.design-strip,
.spec-band {
  break-inside: avoid;
  page-break-inside: avoid;
  print-color-adjust: exact;
  -webkit-print-color-adjust: exact;
}

/* — 9. Page breaks —
   A figure split across a fold is two half pictures, and a heading alone at
   the bottom of a page is a promise the page does not keep. */
figure,
.plate-artwork,
.cut,
.flow-item,
.project-card,
.leaf,
.seal-card { break-inside: avoid; page-break-inside: avoid; }

/* `.museum` is deliberately NOT in that list. It is a 460px architectural
   still plus its overlay type, and asking it to stay whole cost a page: the
   first capture of `/gallery/` opened on a header, a rule, and 900px of
   nothing, with the portal pushed to page two. A picture this large is allowed
   to begin where it begins. */

h1, h2, h3, h4, h5, h6 {
  break-inside: avoid;
  break-after: avoid;
  page-break-after: avoid;
}

p, li, dd, blockquote { orphans: 3; widows: 3; }

img { max-width: 100%; }

/* — 10. The dark rooms, printed light —
   `/gallery/` is a fixed dark museum and `/games/` is a night table; both
   declare their own palette, so re-grounding the palette re-grounds every
   surface, placard and caption inside them without touching a component rule.
   Everything that was light type on a dark wall becomes ink on paper, in
   place, with its layout intact. */
.gallery-page {
  --gallery-wall: #fff;
  --gallery-wall-raised: #fff;
  --gallery-floor: #fff;
  --gallery-light: #b9b6a5;
  --gallery-ink: #222a22;
  --gallery-ink-muted: #525b50;
  --gallery-rim: #b9b6a5;
  --gallery-cinnabar: #b02e26;
  --gallery-glass: #fff;
  --gallery-glass-solid: #fff;
  color-scheme: light;
  background: #fff;
}

/* flow.css reads `--gallery-*` when a page defines them and falls back to the
   same dark values when it does not, so the Nolan room hung outside the
   gallery needs the fallbacks turned as well. */
.flow,
.flow--room {
  --flow-room-wall: #fff;
  --flow-room-wall-raised: #fff;
  --flow-room-floor: #fff;
  --flow-room-light: #b9b6a5;
  --flow-room-ink: #222a22;
  --flow-room-muted: #525b50;
  --flow-room-cinnabar: #b02e26;
  --flow-paper: #222a22;
  --flow-mute: #525b50;
  color: #222a22;
  background: none;
}

/* On screen the museum is one room at a time: the building until you enter,
   then the hall you chose. On paper there is no entering, so the sheet prints
   the building AND both halls — the same move as unfolding the rose credit out
   of its tooltip. The rules being beaten are attribute- and id-based state
   selectors, which is why this is forced rather than out-specified. */
.museum-page .museum,
.museum-page .flow--room { display: block !important; }

/* The album is a Cover Flow: every plate absolutely positioned at the same
   left:50% and fanned apart with 3D transforms. On paper that is one pile. It
   becomes what a printed album has always been — a plate book: one work to a
   leaf, centred, with its title under it.

   PLAIN BLOCK FLOW, not a grid. A two-up grid was the first cut and it was
   wrong for the medium, not for the look: Chrome fragments a grid container
   badly, and `/gallery/` came back 18 pages with page three entirely blank.
   Block flow is the layout mode paginated media has always handled, and one
   plate to a leaf is also the better book. */
.flow-track {
  position: static !important;
  display: block !important;
  padding: 0;
  overflow: visible !important;
  transform: none !important;
  transform-style: flat;
  perspective: none;
}

.flow-item {
  position: static !important;
  z-index: auto;
  display: block;
  width: auto !important;
  max-width: 420px;
  min-width: 0;
  margin: 0 auto 26px !important;
  opacity: 1 !important;
  /* The room gives every plate `aspect-ratio: 1 / var(--flow-ratio)` so the
     works hang on one foot line. The picture inside carries its own ratio;
     forcing the figure to a second one only letterboxes it. */
  aspect-ratio: auto;
  transform: none !important;
  transform-style: flat;
}

.flow-item img {
  object-fit: contain;
  background: none;
}

.flow-item .flow-caption {
  position: static !important;
  width: auto !important;
  min-width: 0;
  min-height: 0;
  margin-top: 8px;
  opacity: 1 !important;
  overflow-wrap: anywhere;
  transform: none !important;
  color: #3c453c;
}

/* The room's own architecture — the lit wall, the brass moulding round each
   plate, the velvet rope on its posts, the floor line and the polished-stone
   sheen — is an installation. On paper there is no room to install anything
   in, and every one of those layers is a gradient that would either vanish
   (backgrounds off) or print as a grey smear (backgrounds on). */
.flow--room {
  min-height: 0 !important;
  padding: 0;
}

.flow--room::before,
.flow--room::after,
.flow--room .flow-track::before,
.flow--room .flow-track::after,
.flow--room .flow-item::before,
.flow--room .flow-item::after { content: none !important; }

/* The room dims every plate but the one you are facing, so the wall reads as
   half-light and the eye goes where the keys go. There is no facing plate in a
   book: the first capture printed nineteen works at brightness(.5), and a
   dimmed picture on paper is just an underexposed one. */
.flow--room.is-live .flow-item,
.flow--room.is-live .flow-item:not([data-offset="0"]) { filter: none; }

/* The label is a brass plaque with screw heads, brushed metal and engraved
   type. On paper it is a caption: the metal goes, the reserved 100px of wall
   under each frame goes with it, and the words are set in ink. */
.flow--room .flow-item .flow-caption {
  min-height: 0;
  width: auto;
  max-width: none;
  padding: 0;
  border-radius: 0;
  background: none;
  color: #3c453c;
}

.flow--room .flow-item .flow-caption b { color: #222a22; }

.flow--room .flow-item .flow-caption span,
.flow--room .flow-item .flow-caption i { color: #525b50; }

/* The museum portal prints as the still it already is. Its overlay type and
   the scrim that seats it come out of absolute position and stack under the
   picture, because a box whose only children are absolute collapses to nothing
   the moment its viewport height is taken away. */
.museum {
  height: auto !important;
  color: #222a22;
  background: none;
}

.museum-still,
.museum-foyer,
.museum-halls {
  position: static !important;
  inset: auto !important;
  background: none;
  color: #222a22;
}

.museum-door,
.museum-title,
.museum-gloss { color: #222a22; background: none; }

/* — the arcade —
   `--qg-cream` is the arcade's paper: a text colour everywhere this sheet
   keeps, and a FACE only on the chips, dice, avatars, buttons and toasts —
   every one of which is a control already dropped above. So the token flips to
   ink and nothing is left pointing the wrong way. */
:root:has(.games-page) {
  --qg-night: #fff;
  --qg-felt: #fff;
  --qg-felt-deep: #fff;
  --qg-panel: #fff;
  --qg-panel-deep: #fff;
  --qg-panel-edge: #b9b6a5;
  --qg-cream: #222a22;
  --qg-cream-deep: #525b50;
  --qg-cream-ink: #222a22;
  --qg-mute: #525b50;
  --qg-outline: #222a22;
  --qg-red-text: #b02e26;
}

.games-page {
  color: #222a22;
  background: #fff;
}

/* The lobby's two forms are the whole of what a visitor types here. The
   identity panel is dropped by class with the rest of the furniture; the join
   panel carries no class of its own, so it is named by what it holds. */
.qg-panel:has(> .qg-join) { display: none; }

/* The panels were a raised chassis over felt. On paper they are a hairline
   rule around a block of text, which is what a panel has always been in
   print. */
.qg-panel {
  border: 1px solid #b9b6a5;
  background: none;
}

/* — /plate/, /ke/ and /seal/ follow the system —
   Each declares `color-scheme: light dark` and carries a
   `@media (prefers-color-scheme: dark)` block that re-grounds its tokens, so a
   reader printing from a dark desktop would otherwise get white type on white
   paper. This sheet is linked last on every page, so at equal specificity it
   lands after those blocks and the light values stand. */
.plate-page,
.cuts-page,
.seal-page {
  color-scheme: light;
  --paper: #fff;
  --paper-raised: #fff;
  --paper-deep: #fff;
  --ink: #222a22;
  --ink-soft: #3c453c;
  --ink-mute: #525b50;
  --line: #d8d5c6;
  --line-dark: #b9b6a5;
  --cinnabar: #d2352c;
  --cinnabar-deep: #b02e26;
  --celadon: #a9bfad;
  --celadon-deep: #48604c;
  background: #fff;
  color: #222a22;
}

/* The gallery is a `.plate-page` too and wants the room's grounding, not the
   scroll's; it is restated after so the block above cannot undo it. */
.gallery-page {
  --paper: #fff;
  --paper-raised: #fff;
  --paper-deep: #fff;
  --ink: #222a22;
  --ink-soft: #3c453c;
  --ink-mute: #525b50;
}

.plate-page,
.cuts-page,
.seal-page,
.gallery-page { background-image: none; }

/* — The rooms outside the scroll —
   Three more routes ground themselves in their own tokens, and a reader who
   prints one of them gets a wall of toner. Each keeps its structure and its
   accent; only the ground and the ink change hands.
   `/business/salons/arc-form/` and `/business/creative/ice-cube/` are already
   light and need nothing. */

/* 烟雨江南 — a night canal. The mist was the type; the ink was the water. */
.yanyu-page {
  --yanyu-ink: #fff;
  --yanyu-ink-raised: #fff;
  --yanyu-mist: #222a22;
  --yanyu-paper: #3c453c;
  --yanyu-celadon: #48604c;
  color-scheme: light;
}

/* Counterline follows the system, and a reader on a dark desktop was printing
   #f3f0e8 type on a #141311 kitchen. Declared after that media block, at the
   same specificity, so the light values stand. */
.counterline {
  --counter-paper: #fff;
  --counter-ink: #141311;
  --counter-mute: #5d5c57;
  --counter-steel: #f3f2ea;
  --counter-steel-deep: #b9b6a5;
  --counter-black: #141311;
  --counter-black-soft: #3c453c;
  --counter-board-ink: #141311;
  --counter-board-mute: #5d5c57;
  --counter-choice: #fff;
  color-scheme: light;
}

/* Red Spur is a light playbill tacked over a dark room. On paper the room goes
   white and the playbill is simply the page; `--spur-paper` stays the sheet,
   so only the type that used to sit on the ROOM has to move to ink.
   `body[...]`, not a bare attribute selector: the study declares its palette on
   `body[data-study="red-spur"]` at (0,1,1), and (0,1,0) loses to it. */
body[data-study="red-spur"] {
  --spur-room: #fff;
  --spur-room-deep: #fff;
  --spur-paper: #fff;
  --spur-paper-deep: #f3f2ea;
  color-scheme: light;
  color: var(--spur-ink);
}

.spur-topline a,
.spur-topline p { color: var(--spur-ink); }

/* The 装裱 placeholder grounds: `.vignette` and `.approach-frame` carry a
   near-black fill so a lazily-arriving leaf has something to sit on. The leaf
   is exactly 5:3 and covers it completely on screen, but on paper that ground
   becomes a black band the moment the reader prints with backgrounds on. */
.vignette,
.approach-frame { background: none; }

/* — 11. No shadows, cast or typographic —
   A cast shadow is a lighting effect, and paper has no lights. It is also not
   a background, so it prints even when the reader has background graphics off:
   the first capture of `/` put a 38px grey haze around the Venus leaf on an
   otherwise clean white page. The inset hairlines go with them — every box
   that used one as its only edge is either a photograph that fills its frame
   or a control this sheet has already dropped. Text shadows are the arcade's,
   and a drop shadow behind 30px pixel type is a smudge at print resolution. */
*,
*::before,
*::after {
  box-shadow: none !important;
  text-shadow: none !important;
}

/* — 12. Motion never resolves on paper —
   A page captured mid-transition prints whatever half-state it was in: an
   album plate at opacity .4, a cut fading up, the 惊燕 plate mid-soak. Land
   every animation on its end state before the snapshot is taken. */
*,
*::before,
*::after {
  animation-duration: 0s !important;
  animation-delay: 0s !important;
  animation-iteration-count: 1 !important;
  transition-duration: 0s !important;
  transition-delay: 0s !important;
  scroll-behavior: auto !important;
}

}
