
        /* ══════════════════════════════════════════════════════════════
           MOVED OUT OF css/09-modal.css · 2026-09-21  (prepended)
           1 rules. DESIGN.md: a surface's rules go in that surface's part, and the
           legacy parts only shrink.
           ⚠️ 09-modal.css loaded BEFORE this file. Placing them
           at the head keeps their order against THIS PART’S OWN rules — and nothing else.
           Against the parts BETWEEN #28 and #8 the order is reversed by this move, and
           whether that matters was a separate question somebody had to answer. See DESIGN.md.
           ══════════════════════════════════════════════════════════════ */
        /* detail */
        .jrnl-dt-text { padding:16px 24px 8px; font-size:15px; color:var(--log-ink); line-height:1.62; }

        /* ==========================================================================
           #X  The journal — an entry, read
           --------------------------------------------------------------------------
           Drawn by public/07-diary.js (renderJournalPage, renderJournalDrawer,
           openJournalPhotosPage). Migrated into the deck log on 2026-09-10.

           WHAT IT REPLACES. The whole outgoing costume: a cream ground, Vollkorn set with
           oldstyle figures, chevron glyphs for PREVIOUS and NEXT, a map-pin before the
           place, a ⋯ menu carrying two disabled promises, and a details drawer that opened
           on a rotating chevron. Sixty-five rules in `06-pagestack.css`, using the
           deck log's tokens zero times.

           ⚠️ THE DATE BAR IS SHARED WITH THE DAY VIEW. `.jp-nav` and `.jp-date` are emitted
           by renderJournalPage AND by the day page (07-diary.js, both places) — the note in
           the old file put it exactly right: "same control, same meaning, so it should not
           be a second set of styles that can drift." Migrating it here migrates both, which
           is the point. Both markups lost their chevrons in the same commit.

           THE SERIF IS GONE, and it was the one thing worth arguing about. A diary is the
           place a serif has the best case in this whole app — it is the traveller's own
           writing, set as a book. But this world committed to two faces and no display
           face: printed is Archivo Narrow, written is Hanken Grotesk, and the authority is
           the ruling. An entry read in a face no other room uses would be a third voice.
           Recorded as a decision so it is not re-argued by accident; it is one token to
           reverse if the owner wants it back.
           ========================================================================== */

        .pgstack-root .pgstack-page.jp-page .pgstack-body {
            overflow: hidden;
            display: flex;
            flex-direction: column;
            padding-bottom: 0;
            background: var(--log-stock);
        }

        /* ---- Previous / Next -------------------------------------------------- */
        /* A band, like the day's own band on the trip page: a printed word at each end and
           what you are looking at between them. The chevrons went the way every glyph in
           this world goes — a way somewhere is a word. */
        .pgstack-root .jp-nav {
            flex: none;
            display: grid;
            grid-template-columns: 1fr auto 1fr;
            align-items: baseline;
            gap: var(--sp-sm);
            padding: var(--sp-xs) var(--sp-lg);
            background: var(--log-stock-band);
            border-bottom: 1px solid var(--log-rule-firm);
        }

        .pgstack-root .jp-nav-btn {
            display: inline-flex;
            align-items: center;
            min-height: 44px;
            padding: 0;
            border: none;
            background: none;
            cursor: pointer;
            font-family: var(--log-print);
            font-size: var(--fs-micro);
            font-weight: 600;
            letter-spacing: 0.16em;
            text-transform: uppercase;
            text-decoration: underline;
            text-underline-offset: 0.25em;
            text-decoration-thickness: 1px;
            color: var(--log-ink);
        }
        .pgstack-root .jp-nav-btn.right { justify-self: end; }

        /* Not every day has an entry, and the ends of the run have nowhere further to go.
           Disabled rather than hidden — a control that vanishes moves the date, and the
           quieted state is itself the information: you are at the last entry. It keeps its
           weight and gives up its ink and its underline, which is how this world says a
           word is present but not a door. */
        .pgstack-root .jp-nav-btn:disabled {
            color: var(--log-ink-soft);
            text-decoration: none;
            cursor: default;
        }

        /* Which entry this is. Printed, because the date is the instrument's stamp on the
           page rather than something the traveller wrote — what they wrote is below, and
           its own first line is its title. */
        .pgstack-root .jp-date {
            font-family: var(--log-print);
            font-size: var(--fs-small);
            font-weight: 600;
            letter-spacing: 0.14em;
            text-transform: uppercase;
            color: var(--log-ink);
            white-space: nowrap;
        }
        .pgstack-root .jp-date span {
            margin-left: var(--sp-xs);
            font-weight: 400;
            letter-spacing: 0.06em;
            color: var(--log-ink-soft);
        }

        /* ---- the entry --------------------------------------------------------- */
        .pgstack-root .jp-scroll {
            flex: 1;
            min-height: 0;
            overflow-y: auto;
            -webkit-overflow-scrolling: touch;
            background: var(--log-stock);
        }

        /* Tags are words on a rule, not pills. */
        .pgstack-root .jp-tags {
            display: flex;
            flex-wrap: wrap;
            gap: var(--sp-2xs) var(--sp-md);
            margin: 0;
            padding: var(--sp-sm) var(--sp-lg) 0;
        }
        /* ⚠️ THE TAG'S OWN DRESSING IS NOT HERE ANY MORE. It was — and it left the `#` in
           front of each tag to `.jrnl-tag::before` in 19-legacy-media.css, a file two rooms
           away, which is where it stayed until the journal's list was migrated and that
           file was emptied. For one day the tags on this page read as plain underlined
           words with nothing saying they were tags.

           One owner now, in css/17n-journal-list.css, deliberately unscoped: a tag is a tag
           wherever it appears. This page keeps only the LAYOUT of the row (`.jp-tags`
           above). */

        /* THE ENTRY ITSELF. The written face, at the one size on this app's scale meant for
           reading rather than for labelling, on the log's own stock. The measure is what the
           frame gives it; at 430 that is about 52 characters, which is short of the 65–75
           a page wants and is the phone's answer rather than this page's. */
        /* ⚠️ ONE STEP DOWN (owner, 2026-09-10: 字体有些大 … 我希望小些). It was --fs-base,
           16px, which is the scale's step for emphasised body — and it read large, for a
           reason this app has written down before: a CJK glyph fills the whole em box, so
           the same size that is comfortable in English is noticeably bigger in Chinese, and
           this is the one surface in the app that is mostly the traveller's own long-form
           writing. --fs-body is what every other room reads at.

           The line height stays generous. Coming down a step is not the same as tightening,
           and a page of somebody's own writing wants air between the lines more than it
           wants big letters. */
        .pgstack-root .jp-text {
            padding: var(--sp-md) var(--sp-lg) var(--sp-2xl);
            font-family: var(--log-write);
            font-size: var(--fs-body);
            line-height: 1.7;
            color: var(--log-ink);
        }

        /* THE OPENING LINE IS THE TITLE. The editor has had this rule since it was written;
           the reading side had no counterpart, so an entry written with a title was read back
           as flat body text. Same idea, one step up from the body, and no display face — the
           size IS the emphasis, which is the only kind a log has.

           ⚠️ `.jp-title`, NOT `:first-child`. The first child is not always the title: an
           entry that opens with a blank line is `<p><br></p>` first, so the size landed on
           nothing and the real title stayed at body size — which is why two entries had two
           title sizes with nothing on screen to explain it. normaliseRenderedFirstBlock marks
           the first block that actually says something. */
        .pgstack-root .jp-text > .jp-title {
            font-family: var(--log-write);
            font-size: var(--fs-base);
            font-weight: 500;
            line-height: 1.3;
            margin-bottom: var(--sp-md);
        }
        /* A block with nothing in it has nothing to say and should not take a line's worth
           of space saying it. `:empty` does not catch a paragraph holding a `<br>`, which is
           what a rich-text editor writes for a blank line, so the JS that marks the title
           is also where a genuinely empty opener stops mattering — it is simply skipped. */

        .pgstack-root .jp-text p { margin: 0 0 var(--sp-md); }
        .pgstack-root .jp-text p:last-child { margin-bottom: 0; }
        .pgstack-root .jp-text img {
            display: block;
            max-width: 100%;
            margin: var(--sp-md) 0;
            border: 1px solid var(--log-rule-firm);
        }

        /* ---- the details drawer ------------------------------------------------ */
        .pgstack-root .jp-drawer {
            flex: none;
            background: var(--log-stock-band);
            border-top: 1px solid var(--log-rule-firm);
            padding-bottom: env(safe-area-inset-bottom, 0px);
        }

        /* The fold, as a word. It was a chevron that rotated — a glyph, and a state you had
           to read off an angle. The same answer the Days view came to. */
        .pgstack-root .jp-drawer-toggle {
            display: grid;
            grid-template-columns: auto 1fr auto;
            align-items: baseline;
            gap: var(--sp-sm);
            width: 100%;
            box-sizing: border-box;
            min-height: 44px;
            padding: var(--sp-xs) var(--sp-lg);
            border: none;
            background: none;
            cursor: pointer;
            text-align: left;
            font-family: var(--log-print);
            font-size: var(--fs-micro);
            font-weight: 600;
            letter-spacing: 0.16em;
            text-transform: uppercase;
            color: var(--log-ink);
        }
        .pgstack-root .jp-drawer-toggle svg { display: none; }
        .pgstack-root .jp-drawer-toggle::after {
            content: 'Show';
            justify-self: end;
            text-decoration: underline;
            text-underline-offset: 0.25em;
            text-decoration-thickness: 1px;
        }
        .pgstack-root .jp-drawer.open .jp-drawer-toggle::after { content: 'Hide'; }

        .pgstack-root .jp-drawer-count {
            font-weight: 400;
            letter-spacing: 0.06em;
            color: var(--log-ink-soft);
        }

        .pgstack-root .jp-drawer-body {
            max-height: 0;
            overflow: hidden;
            transition: max-height 0.28s cubic-bezier(0.22, 1, 0.36, 1);
        }
        .pgstack-root .jp-drawer.open .jp-drawer-body { max-height: 60vh; overflow-y: auto; }

        @media (prefers-reduced-motion: reduce) {
            .jp-drawer-body { transition: none; }
        }

        /* Every row in the drawer is the same ruled row: what it is on the left, what it
           says on the right. */
        .jp-meta-row,
        .jp-day-row,
        .pgstack-root .jp-photos-row {
            display: flex;
            align-items: baseline;
            justify-content: space-between;
            gap: var(--sp-sm);
            padding: var(--sp-sm) var(--sp-lg);
            border-top: 1px solid var(--log-rule);
            font-family: var(--log-write);
            font-size: var(--fs-small);
            line-height: 1.4;
            color: var(--log-ink-soft);
        }
        .pgstack-root .jp-meta-row:first-child { border-top: none; }

        .pgstack-root .jp-seeall {
            padding: 0;
            border: none;
            background: none;
            cursor: pointer;
            font-family: var(--log-print);
            font-size: var(--fs-micro);
            font-weight: 600;
            letter-spacing: 0.14em;
            text-transform: uppercase;
            color: var(--log-ink);
            text-decoration: underline;
            text-underline-offset: 0.25em;
            text-decoration-thickness: 1px;
            min-height: 44px;
        }

        /* The linked activity, read in full. A row, not a card. */
        .pgstack-root .jp-linked-card {
            display: grid;
            grid-template-columns: auto 1fr;
            align-items: center;
            gap: var(--sp-sm);
            padding: var(--sp-sm) var(--sp-lg);
            border-top: 1px solid var(--log-rule);
            cursor: pointer;
        }
        .pgstack-root .jp-linked-thumb {
            width: 2px;
            align-self: stretch;
            min-height: 32px;
            background: var(--jp-cat, var(--log-rule-firm));
        }
        .pgstack-root .jp-linked-name {
            font-family: var(--log-write);
            font-size: var(--fs-body);
            line-height: 1.35;
            color: var(--log-ink);
        }
        .pgstack-root .jp-linked-addr {
            font-family: var(--log-write);
            font-size: var(--fs-small);
            line-height: 1.4;
            color: var(--log-ink-soft);
        }

        /* The strip of what is in this entry. Square, hairlined, unrounded: an index of the
           photographs, not a display of them — the display is the photographs page. */
        .pgstack-root .jp-strip {
            display: flex;
            gap: var(--sp-2xs);
            padding: 0 var(--sp-lg) var(--sp-sm);
        }
        .pgstack-root .jp-strip-tile {
            position: relative;
            width: 56px;
            height: 56px;
            flex: none;
            overflow: hidden;
            border: 1px solid var(--log-rule-firm);
            background: var(--log-stock);
        }
        .pgstack-root .jp-strip-tile img { width: 100%; height: 100%; object-fit: cover; display: block; }
        .pgstack-root .jp-strip-more {
            position: absolute;
            inset: 0;
            display: flex;
            align-items: center;
            justify-content: center;
            background: rgba(22, 25, 26, 0.62);
            font-family: var(--log-print);
            font-size: var(--fs-micro);
            font-weight: 600;
            letter-spacing: 0.08em;
            color: var(--log-on-oilskin);
        }

        /* ---- the photographs page ---------------------------------------------- */
        .pgstack-root .jp-ph-sec { padding: 0 0 var(--sp-lg); }

        .pgstack-root .jp-ph-head {
            display: flex;
            align-items: baseline;
            justify-content: space-between;
            gap: var(--sp-sm);
            padding: var(--sp-xs) var(--sp-lg);
            background: var(--log-stock-band);
            border-top: 1px solid var(--log-rule-firm);
            border-bottom: 1px solid var(--log-rule-firm);
        }
        .pgstack-root .jp-ph-title {
            font-family: var(--log-print);
            font-size: var(--fs-micro);
            font-weight: 600;
            letter-spacing: 0.16em;
            text-transform: uppercase;
            color: var(--log-ink);
        }
        /* `.jp-ph-act` is a SPAN carrying the activity these photographs came from — a
           label, not an act, and it was dressed as one here for a day. Soft ink, no
           underline: nothing about it goes anywhere. */
        .pgstack-root .jp-ph-act {
            font-family: var(--log-print);
            font-size: var(--fs-micro);
            font-weight: 400;
            letter-spacing: 0.06em;
            color: var(--log-ink-soft);
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
        }

        /* `.jp-ph-add` IS the act — it writes photographs into the entry — so it takes the
           magenta, and says so as a word. */
        .pgstack-root .jp-ph-add {
            padding: 0;
            border: none;
            background: none;
            cursor: pointer;
            min-height: 44px;
            white-space: nowrap;
            font-family: var(--log-print);
            font-size: var(--fs-micro);
            font-weight: 600;
            letter-spacing: 0.14em;
            text-transform: uppercase;
            color: var(--log-magenta);
            text-decoration: underline;
            text-underline-offset: 0.25em;
            text-decoration-thickness: 1px;
        }
        .pgstack-root .jp-ph-add:disabled {
            color: var(--log-ink-soft);
            text-decoration: none;
            cursor: default;
        }

        .pgstack-root .jp-ph-grid {
            display: grid;
            grid-template-columns: repeat(3, 1fr);
            gap: 1px;
            padding: 1px var(--sp-lg) 0;
        }
        .pgstack-root .jp-ph-tile {
            position: relative;
            aspect-ratio: 1;
            overflow: hidden;
            border: 1px solid var(--log-rule-firm);
            background: var(--log-stock);
            cursor: pointer;
        }
        .pgstack-root .jp-ph-tile img { width: 100%; height: 100%; object-fit: cover; display: block; }

        .pgstack-root .jp-ph-empty {
            margin: 0;
            padding: var(--sp-md) var(--sp-lg);
            font-family: var(--log-write);
            font-size: var(--fs-small);
            line-height: 1.5;
            color: var(--log-ink-soft);
        }

        /* ---- the ⋯ menu, removed ------------------------------------------------
           `.jp-menu-wrap`, `.jp-menu-btn` and `.jp-menu` were a three-dot glyph opening a
           four-item menu, two of whose items were permanently disabled: "Change visibility"
           and "Share / export". Both were promises with no backend at all — `diary_entries`
           has no visibility column and there is no export endpoint — and the owner retired
           the whole visibility idea on 2026-09-10.

           What was left is two acts, and two acts are two words. They are in the page's own
           actions slot now, which is where a room's acts live in this world; the rules below
           only strip what the browser gives a button, because the words are all there is. */
        .pgstack-root .jp-menu-wrap { display: flex; align-items: center; gap: var(--sp-md); }
        .pgstack-root .jp-act {
            padding: 0;
            border: none;
            background: none;
            cursor: pointer;
            min-height: 44px;
            font-family: var(--log-print);
            font-size: var(--fs-micro);
            font-weight: 600;
            letter-spacing: 0.16em;
            text-transform: uppercase;
            text-decoration: underline;
            text-underline-offset: 0.25em;
            text-decoration-thickness: 1px;
            color: var(--log-ink);
        }
        /* Deleting an entry takes a line out of the record, which is the one thing on this
           page the magenta is for. */
        .pgstack-root .jp-act.is-remove { color: var(--log-magenta); }

        /* Removal, at the foot of the details and nowhere else. Its own ruled row with room
           around it — a rare irreversible act should not sit shoulder to shoulder with the
           rows that merely say how many words there are. */
        .pgstack-root .jp-remove-row {
            padding: var(--sp-sm) var(--sp-lg) var(--sp-md);
            border-top: 1px solid var(--log-rule);
        }

        /* THE PAGE STACK'S CHROME, FOR EVERY ROOM THAT HAS MOVED.

           `.pgstack-header` is shared by all nine stack pages and is still
           `var(--bg-primary)` — the outgoing cream — so a migrated room sat under a cream bar
           with a firm rule between them, which reads as two pages stuck together. The BACK
           word came off the chevron for all nine on the owner's word; the GROUND could not,
           because a deck-log header over a room still in the outgoing world is the same seam
           the other way up.

           ⚠️ KEYED ON `.log-page`, WHICH THE ROOM ADDS TO ITSELF. It was `.jp-page` — scoped
           to the journal entry, with a note saying "until the next stack page is migrated".
           The day page is the next one (2026-09-11), and the answer to a second room wanting
           the same six declarations is not a second copy of them: it is a class that says
           "this room has moved". Exactly the `is-log` toggle the section sheet grew on the
           same day, for the same reason.

           Three pages carry it today — the journal entry, its photographs, and the day.
           Adding the fourth is one word where that page is pushed. */
        /* THE STACK'S HEADER, for every room that has moved. A pale band, not the oilskin.

           ⚠️ IT WAS THE OILSKIN FOR AN HOUR, on the argument that this world already has a
           material for the app's own chrome — the cover boards, which the sign-in screen's
           `.log-band` has used since the world was built — and that a deck log is black
           boards with a ruled sheet inside. It photographed well. The owner put it back
           (2026-09-11: 页眉还是不要深色的 单独看好看 放到app里不是很好看), and the reason is
           the one a single screenshot cannot show: **a stack page opens OVER the journey's
           own room, whose header is pale**, so the board arrived as a black bar dropping
           over a light one every time anybody pressed anything. A material that is right for
           a cover is wrong for a thing that slides in and out over another page.

           The complaint that started this — four bands of the same grey down the top of the
           day page — is answered where it actually was, in css/17p-day.css: the date bar and
           the day's line became ONE block, and the section heads stopped being bars. The
           header was the one of the four that was allowed to stay a band, because it is the
           only one of the four that is not part of the page. */
        .pgstack-page.log-page .pgstack-header {
            background: var(--log-stock-band);
            border-bottom: 1px solid var(--log-rule-firm);

            /* ⚠️ ONE RULING TALL, AND IN THE PAGE'S OWN MARGIN COLUMN (owner, 2026-09-11:
               open day 的页眉 是不是比 calendar 的页眉高). It was, by sixteen pixels —
               measured at 69 against the section sheet's 53 — and it was inset sixteen
               where everything under it is inset twenty-four. Two rooms one press apart,
               and the bar across the top of each was a different size and started in a
               different place.

               53 is not a number picked to match the sheet. It is `--log-row`, this world's
               ruling pitch, declared when the world was built so that a written row and an
               unwritten one below it are the same height and the sheet reads as continuously
               ruled. The sheet's header already sat on it; the stack's was carrying
               --sp-sm above and below a 44px control instead of --sp-2xs, for no reason
               anybody had written down.

               ⚠️ The notch keeps its own padding on top of that. The header's background
               fills the inset rather than the page being padded, so the bar reads as one
               surface running to the top edge of the screen — and on a desktop browser the
               inset resolves to 0 and this is identical to plain padding. */
            min-height: var(--log-row);
            padding: var(--sp-2xs) var(--sp-lg);
            padding-top: calc(var(--sp-2xs) + env(safe-area-inset-top, 0px));
        }
        /* The title is a label, so it is in the print face, as the section sheet's head is (17n-journal-list.css). It
           was the stack's shared --font-sans, Hanken Grotesk, thinner and wider than the CLOSE and BACK beside it, on
           every migrated stack page (finish review of the checklist and the weather, 2026-09-14). */
        .pgstack-page.log-page .pgstack-title {
            font-family: var(--log-print);
            font-size: var(--fs-micro);
            font-weight: 600;
            letter-spacing: 0.18em;
            text-transform: uppercase;
            color: var(--log-ink);
        }

        /* ---- the photographs page ----------------------------------------------- */
        /* It is the same room as the entry — one is the writing, one is what was pinned to
           it — so it carries `.jp-page` too and takes the ground, the header and the way out
           from the rules above. It had neither until 2026-09-10: it was pushed with no class
           of its own, so a migrated entry handed straight onto an outgoing-world page. Its
           header title was set in a display face as well, which this world does not have.

           ⚠️ AND THE PICKED STATE CAME BACK. The old block carried `.jp-ph-grid.selectable`
           and `.picked`, and cutting the block took them with it — so for one commit
           selecting a photograph left no mark at all. The file's own comment recorded that
           this exact fault had happened once before, for a different reason: "Selection was
           working the whole time — the button enabled, the count updated — it simply left no
           mark, which reads as 'these photos can't be selected'." Twice is a rule the code
           does not enforce, so the capture asserts it now.

           The box mirrors the unpicked selector, grid class included, because
           `.jp-ph-tile.picked::after` alone scores LOWER than
           `.jp-ph-grid.selectable .jp-ph-tile::after` — which is how the tick failed to fill
           the first time. */
        .pgstack-root .jp-ph-grid.selectable .jp-ph-tile::after {
            content: '';
            position: absolute;
            top: 6px;
            left: 6px;
            width: 16px;
            height: 16px;
            border: 1px solid var(--log-on-oilskin);
            background: rgba(22, 25, 26, 0.34);
        }
        .pgstack-root .jp-ph-grid.selectable .jp-ph-tile.picked::after {
            background: var(--log-magenta);
            border-color: var(--log-magenta);
        }

        /* A tick inside the box, so the picked state survives a bright screen rather than
           relying on a coloured square against a photograph. */
        .pgstack-root .jp-ph-grid.selectable .jp-ph-tile.picked::before {
            --tick-inset: 6px;
            --tick-dia: 16px;
            --tick-w: 5px;
            --tick-h: 9px;
            content: '';
            position: absolute;
            top: calc(var(--tick-inset) + var(--tick-dia) / 2 - var(--tick-h) / 2 - 1.74px);
            left: calc(var(--tick-inset) + var(--tick-dia) / 2 - var(--tick-w) / 2 + 0.15px);
            width: var(--tick-w);
            height: var(--tick-h);
            border: solid var(--log-on-oilskin);
            border-width: 0 1.5px 1.5px 0;
            transform: rotate(45deg);
            z-index: 1;
        }
        .pgstack-root .jp-ph-grid.selectable .jp-ph-tile.picked img { opacity: 0.55; }

        /* ══════════════════════════════════════════════════════════════
           MOVED OUT OF css/19-legacy-media.css · 2026-09-21  (appended)
           1 rules. DESIGN.md: a surface's rules go in that surface's part, and the
           legacy parts only shrink.
           ⚠️ 19-legacy-media.css loaded BEFORE this file. This placement keeps
           their order against THIS PART’S OWN rules and nothing else: against the 10 part(s)
           between #19 and #30 the move reverses it. The banner used to claim they "keep that
           place" full stop, which was never true (review, 2026-09-22). Checked separately.
           ══════════════════════════════════════════════════════════════ */
        /* Images inside a saved entry read as part of the writing, not as attachments. */
        .jrnl-dt-text img {
            display: block;
            max-width: 100%;
            height: auto;
            margin: var(--sp-md) 0;
        }
