
        /* ══════════════════════════════════════════════════════════════
           MOVED OUT OF css/04-legacy-forms.css · 2026-09-21  (prepended)
           2 rules. DESIGN.md: a surface's rules go in that surface's part, and the
           legacy parts only shrink.
           ⚠️ 04-legacy-forms.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 #4 and #3 the order is reversed by this move, and
           whether that matters was a separate question somebody had to answer. See DESIGN.md.
           ══════════════════════════════════════════════════════════════ */
        /* Rounded-corner toggle matching the Plan/Timeline tabbar look (4px radius,
           solid fill on the active state) instead of a sliding pill — safer geometry
           too, no thumb-overflow risk. guards against the two older
           .toggle-switch/.toggle-slider definitions elsewhere in the app. */

        .modal-content .form-input {
            width: 100%;
            padding: var(--sp-sm) var(--sp-md);
            border: 1px solid var(--log-rule-firm);
            border-radius: 0;
            font-size: 15px;
            font-family: var(--log-write);
            color: var(--log-ink);
            transition: border-color 0.2s;
        }


        .modal-content .form-input:focus {
            outline: none;
            border-color: var(--log-ink, #43276B);
        }


        /* ══════════════════════════════════════════════════════════════
           MOVED OUT OF css/03-legacy-home-nav.css · 2026-09-21  (prepended)
           5 rules. DESIGN.md: a surface's rules go in that surface's part, and the
           legacy parts only shrink.
           ⚠️ 03-legacy-home-nav.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 #5 and #3 the order is reversed by this move, and
           whether that matters was a separate question somebody had to answer. See DESIGN.md.
           ══════════════════════════════════════════════════════════════ */
        /* Settings Page */

        input:checked+.toggle-slider {
            background-color: var(--log-ink);
        }


        input:checked+.toggle-slider:before {
            transform: translateX(22px);
        }


        input:disabled+.toggle-slider {
            opacity: 0.5;
            cursor: not-allowed;
        }

        /* Profile Page */

        .form-group {
            display: flex;
            flex-direction: column;
            gap: var(--sp-xs);
        }


        .form-label {
            /* 阶段一：字段标签降级 —— 规格判据「在给区块命名还是给字段贴标签？后者一律降级」。
               letter-spacing 一并删：字距是全大写的配套，常规大小写不需要。 */
            font-size: var(--fs-small);
            color: var(--log-ink-soft);
            font-weight: var(--fw-medium);
        }

        /* ══════════════════════════════════════════════════════════════
           阶段二 · 第一批 · 手段 2：透明命中区
           §2.4「视觉尺寸可以更小，但可点区域必须用内边距或伪元素撑到 44」
           选伪元素：可见尺寸、字号、间距一个都不动，只在中心叠一个 44×44
           的透明命中区。绝对定位 → 脱离文档流 → 不撑父容器、不改行高。
           验收靠版面指纹逐屏比对（scripts/compare-layout.mjs）——
           不是「应该不会变」，是「量过没变」。

           ⚠️ 2026-09-16 — THIS RULE HAS NEVER ENLARGED A HIT AREA. `pointer-events: none`,
           below, is what the whole mechanism turns on: a pseudo-element that does not take
           pointer events does not extend its parent's clickable region, so every selector
           here has always been clickable over its VISIBLE box and no more. Measured, not
           reasoned — `elementFromPoint` walked outward from each `.jw-tool`'s centre and
           returned the visible box to the pixel (4×44, 9×44, 17×44 against visible 4.3, 8.6,
           16). `.jw-action` reads 40×44 and 31×44 the same way. The claim above and the
           claim in §2.4 are both false as written.

           The two claims went unnoticed together because of the last line of the original
           comment: the fingerprint comparison measured that the LAYOUT had not moved, which
           is true and was never the question, and `scripts/compare-layout.mjs` is not in this
           repository — there is no `scripts/` directory. A check for the wrong property, and
           then the check itself gone.

           `pointer-events: none` is NOT simply removed, and that is a decision, not an
           oversight: these 44×44 boxes are centred and this world's rows are tighter than
           44, so switching them live would let one control's invisible target sit over its
           neighbour's — a tap landing on the WRONG action, which is worse than a small one.
           The working pattern in this repo is the one at css/17q-editor.css
           `.diary-tag-chip button::after`, which omits `pointer-events: none` and measures
           41×45 for a 5.8×12 mark, because nothing sits beside it. Each remaining selector
           has to be read for its neighbours before it can be switched or given real width.
           The thirteen are listed in DEBTS.md; do not assume any of them is covered. */
        .modal-close, .rich-text-btn,
        /* `.jw-tool` came off both lists on 2026-09-16, for the reason `.jw-status-toggle` and
           `.tcal-day-open` did before it: it carries its own target now (`min-width: 24px` in
           css/17q-editor.css, with the measurement written out there). It is the first one to
           come off because the pseudo-element was NOT holding it up — see the warning below. */
        .jw-close,
        .jw-tag-add, .jw-action, .image-tab, .home-btn-icon, .stats-back,
        .stop-detail-add-link, .__cf_email__, .toggle-switch,
        .trip-section-sheet-btn, .jw-drawer-close, .jw-link-btn, .jw-link-chip,
        /* `.map-control-btn` was in both of these lists until 2026-09-08. It belonged to
           the world map's two unlabelled icon buttons, which went when that page was
           migrated into the deck log — this world has no glyphs for actions. Everything
           else here still exists; taking the whole rule out with it, which is what a
           first pass at this did, would have dropped the tap-target floor for a dozen
           live controls. */
        #agreeTerms {
            position: relative;
        }
        .modal-close::after, .rich-text-btn::after,
                /* `.jw-status-toggle` came off both lists on 2026-09-11: it is a word with 44px
           of its own now, and an absolutely-positioned pretend target over a control that is
           already big enough took the word out of flow and left the button zero pixels wide. */
        .jw-close::after,
        .jw-tag-add::after, .jw-action::after, .image-tab::after, .home-btn-icon::after,
        /* `.tcal-day-open` came off both of these lists on 2026-09-11: it is a word with
           44px of its own now, and a pseudo-element hit area over a control that is already
           big enough is one more thing that can overlap its neighbour. */
        .stats-back::after, .stop-detail-add-link::after,
        .__cf_email__::after, .toggle-switch::after,
        .trip-section-sheet-btn::after, .jw-drawer-close::after, .jw-link-btn::after,
        .jw-link-chip::after, #agreeTerms::after {
            content: '';
            position: absolute;
            inset: 50% auto auto 50%;
            transform: translate(-50%, -50%);
            width: 44px;
            height: 44px;
            pointer-events: none;
        }

        /* ══════════════════════════════════════════════════════════════
           A 44 TARGET THAT WORKS · 2026-09-17 (owner: 之后把44px做完)

           The rule above never enlarged anything; this one does, and the difference is one
           declaration it leaves out — pointer-events. A pseudo-element that takes the pointer
           answers for its control, so a tap on it is a tap on the button. It is ::before
           because nothing in the app draws with these controls' ::before (checked for every
           class, in every state), and ::after is taken by the dead rule above on three of them.
           Centred on the control, 44 or the control's own size in each direction, whichever is
           more: nothing moves, nothing is drawn.

           What the old comment warned of — a pretend target lying over a neighbour — was
           measured before this was written, not assumed away: `node dev/capture.mjs --tap focus`
           prints, beside each short control, the free pixels on each side to the nearest other
           control and to the screen's edge. Every class here had room for its half of the
           shortfall on both sides, with two exceptions, which grow one way (below). The same
           run afterwards is the proof: a target that overlapped a neighbour would shrink the
           neighbour's reading.

           The journal's tool rack is here too. Its eight tools are 24 wide with 21 between them,
           so 10 a side fits with a pixel to spare: the target reaches 44 with no instrument taken
           away. The open question in DEBTS.md (fewer instruments, or a rack that scrolls) was
           about a VISIBLE 44, and stays open for that. */
        .jw-tool, .bo-word, .trip-section-sheet-btn, .sv-word, .fr-act, .tp-view, .tsv-edit,
        .jp-act, .tk-act, .stop-edit-toggle, .jw-action, .jp-nav-btn, .wm-paper,
        .category-selector, .log-back, .pgstack-back, .tp-back, .image-tab,
        .ptrip-close, .fs-close, .pgstack-close, .wm-act, .fr-filter, .uw-word, .tk-word,
        .mg2-filter, .wall-foot-word, .pf-word {
            position: relative;
        }
        .jw-tool::before, .bo-word::before, .trip-section-sheet-btn::before, .sv-word::before,
        .fr-act::before, .tp-view::before, .tsv-edit::before, .jp-act::before, .tk-act::before,
        .stop-edit-toggle::before, .jw-action::before, .jp-nav-btn::before, .wm-paper::before,
        .category-selector::before, .log-back::before, .pgstack-back::before, .tp-back::before,
        .image-tab::before, .ptrip-close::before, .fs-close::before,
        .pgstack-close::before, .wm-act::before, .fr-filter::before, .uw-word::before,
        .tk-word::before, .mg2-filter::before,
        /* "try again" in the Wall's and a profile's sentence. WCAG excepts a target in running
           text; this product's 44 does not, and both have 44 free on every side. The register
           sheet's Terms and Privacy links are not here: they are inside the label of the
           agreement's checkbox, and a bigger link would take its area from the box's target. */
        .wall-foot-word::before, .pf-word::before {
            content: '';
            position: absolute;
            top: 50%;
            left: 50%;
            width: max(100%, 44px);
            height: max(100%, 44px);
            transform: translate(-50%, -50%);
        }
        /* A ticket's and the tool kit's words touch the control on their left (0px free), so
           they grow rightward, where 24 is free. */
        .tk-act::before { left: 0; transform: translateY(-50%); }
        /* Friends' filters have 1px above them and 44 below, so they grow downward. */
        .fr-filter::before { top: 0; transform: translateX(-50%); }
        /* The chart's papers sit on the sheet's bottom edge, which is the plate's edge, and the
           plate clips (overflow: hidden) — centred, 2 of the 4 were cut off and it read 42. They
           grow up the sheet instead, over the drawing, which answers to nothing. In the sheet's
           own frame, so it holds turned or not. */
        .wm-paper::before { top: auto; bottom: 0; transform: translateX(-50%); }

        /* ══════════════════════════════════════════════════════════════
           日期选择器 —— 这里原本是 Flatpickr 的「纸色皮」，2026-09-11 整段删除。

           REMOVAL, not override. Flatpickr itself is gone (both CDN tags out of
           index.html), so eighty lines dressing `.flatpickr-*` would have been eighty
           lines of rules matching nothing — the kind of dead weight the next reader has
           to prove is dead before they dare touch it.

           这一节的替代品是 css/17r-datepicker.css，那是这个世界自己画的月份；
           理由写在 public/15b-datepicker-log.js 的开头。
           ══════════════════════════════════════════════════════════════ */
        /* ══════════════════════════════════════════════════════════════
           焦点态 · 全局一条
           两处旧注释（原 4671 / 4733）已经写明「阶段二收成一条全局
           :focus-visible，消掉这两个特例 —— 焦点态是全局行为」。这就是那一条。

           裁定：字段类用 2px 底边转墨色。

           ⚠️ 2026-09-14 起是墨色，不再是 --accent（#5D675B，灰绿，出去的那个世界的颜色；owner: 先做1吧）。
           The Ink-Not-Glow Rule: focus is ink. The fallback is the ink itself, because this rule reaches
           elements off the token list at the head of 02-auth.css, where var(--log-ink) resolves to nothing.
           On a dark ground the room draws the light ink instead (02-auth.css's band, 11-home.css's corner
           buttons, 17y-viewer.css's night plate, 17n's filled days); dev/capture.mjs's `focus` audit asks.
           但底边只对「有底边的东西」成立 —— 按钮、菜单项、勾选框没有底边，
           而 2px 底边在本系统里已经被 §22 占用（标签页的「当前」）。
           所以非字段类沿用代码里已有的 outline: 2px solid（颜色 2026-09-14 起是墨色，见下），
           不新造第三种表达。
           ══════════════════════════════════════════════════════════════ */
        input:focus-visible,
        textarea:focus-visible,
        select:focus-visible,
        .form-input:focus-visible,
        .amount-input:focus-visible,
        .pa-search input:focus-visible,
        .mg2-searchinput:focus-visible,
        .country-search input:focus-visible {
            outline: none;
            border-bottom: 2px solid var(--log-ink, #43276B);
        }
        a:focus-visible,
        button:focus-visible,
        [role="button"]:focus-visible,
        [tabindex]:focus-visible,
        .btn:focus-visible,
        .currency-option:focus-visible,
        .confirm-btn:focus-visible {
            outline: 2px solid var(--log-ink, #43276B);
            outline-offset: 2px;
        }
        /* ⚠️ A LAYER HOLDING FOCUS IS NOT A CONTROL, SO IT DRAWS NO RING (2026-09-16). holdFocus
           (01-core.js) puts focus on the page or the modal itself when it opens — the layer,
           not its first field, so opening a form does not raise a phone's keyboard — and gives
           it tabindex="-1" to make that possible. The `[tabindex]:focus-visible` line above
           would then draw an outline round an entire page whenever it was opened from the
           keyboard. The ring marks a thing that can be pressed; a whole page cannot. Its
           controls keep theirs. */
        .pgstack-page[tabindex="-1"]:focus-visible,
        .modal[tabindex="-1"]:focus-visible,
        .confirm-dialog[tabindex="-1"]:focus-visible {
            outline: none;
        }
        /* ⚠️ A CONTROL THAT MEETS ITS SCROLLER'S EDGE DRAWS ITS FOCUS INSIDE (focus audit, 2026-09-14). Outside, the
           outline's sides fall past the edge and are cut off: the ledger's bands showed only their top and bottom lines.
           The Ink-Not-Glow Rule's inset, as an outline pulled 2px in, so each room's own colour for it still applies.
           Each selector is written to outrank the room rule that would otherwise set the offset back to 2px: a room's
           `#page :focus-visible` carries an id, so a selector under it carries that id too. */
        .login-page .log-reveal:focus-visible,
        .register-page .log-reveal:focus-visible,
        #tripSectionSheet .ex-group-band:focus-visible,
        #tripSectionSheet .ex-row:focus-visible,
        #tripSectionSheet .mg2-footbtn:focus-visible,
        #tripSectionSheet .jrnl-write:focus-visible,
        .pgstack-root .jp-drawer-toggle:focus-visible,
        .pgstack-root .tday-add-tile:focus-visible,
        #planTripModal .image-upload-area:focus-visible,
        #tripMapPage .image-upload-area:focus-visible,
        #mainPage .uw-open:focus-visible,
        #worldMapPage .wm-paper:focus-visible,
        #mainPage .home-sheet-grip:focus-visible,
        /* and the top and bottom as well as the sides (review of the focus round, 2026-09-15) */
        #tripMapPage .tp-view:focus-visible,
        #tripSectionSheet .mg2-filter:focus-visible,
        #tripSectionSheet .mg2-baraction:focus-visible,
        .pgstack-page.media-page .fs-close:focus-visible,
        .pgstack-page.media-page .fs-word:focus-visible,
        #writeJournalModal .jw-tool:focus-visible,
        #writeJournalModal .jw-status-toggle:focus-visible,
        #stopDetailSheet .stop-detail-add-link:focus-visible {
            /* `#boswellPage .bo-ask:focus-visible` was a member of this list and is gone
               (2026-09-25). Nothing has ever emitted a bare `.bo-ask` — what the scripts write
               is `.bo-ask-line`, which is a different class and is alive. Found by
               dev/css-dead-members.mjs on its first run, hours after this same file had been
               swept by hand: css-dead could not see it, because the rule it sits in is alive. */
            outline-offset: -2px;
        }
        /* ══════════════════════════════════════════════════════════════
           阶段二 · 第一批 · 手段 1：元素自身长大
           块级、周围有空间的，长到 44 不影响版面。
           不动字号、不动 padding —— 撑高从 padding 手里拿走，交给 min-height。
           ══════════════════════════════════════════════════════════════ */
        /* .btn 是后补的：间距归并把 #expenseModal 那条 .btn 的垂直 padding 从 13 收到 12，
           按钮高度掉到 38 —— 它原来是靠 padding 凑到 44 的。这正是本阶段要拆掉的依赖：
           高度归 min-height 管，padding 只管内边距。补上之后归并再动 padding 也不会破点击区。 */
        .btn,
        .category-selector, .jw-input,
        .home-btn-primary, .home-btn-secondary,
        .amount-input, .form-input,
        .form-textarea, .rich-text-select, .mp-add,
        #checklistInput, #photoActivitySearch,
        /* 本轮补账：下面这些是逐行/逐个点选的目标，但当初盘点只按「按钮与输入」过，
           所以没进口径 —— 选项行 34/42px、确认按钮 33px、chip 25px、搜索框 30/29px。
           仍走手段 1（撑高交给 min-height，不动字号也不动 padding）。 */
        .currency-option, .confirm-btn,
        .pa-search input, .mg2-searchinput {
            min-height: 44px;
        }
        .category-selector,
        .home-btn-primary,
        .home-btn-secondary,
        .rich-text-select,
        .mp-add {
            display: flex;
            align-items: center;
            justify-content: center;
        }

        /* 阶段二 · 第一批 · 手段 3「整行可点」那条 `.tcal-row { min-height: 44px }`
           已随日历房间搬到 css/17o-calendar.css(2026-09-11)—— 判断没变(密集日历行
           也仍然是控件),变的是它现在和那一行别的声明在同一处,只有一个所有者。 */
        /* 登录/注册页正文里的行内链接（Forgot? / Terms / Privacy / Sign in）。
           高 15px，撑大会破坏行高 —— 同手段 2，只加命中区。
           限定在这两屏的表单内，避免波及导航与按钮里的 <a>。 */
        .login-page a, .register-page a { position: relative; }
        .login-page a::after, .register-page a::after {
            content: '';
            position: absolute;
            inset: 50% auto auto 50%;
            transform: translate(-50%, -50%);
            width: 100%;
            min-width: 44px;
            height: 44px;
            pointer-events: none;
        }

        /* HOTFIX（既存缺陷，不属于任何迁移阶段）
           openModal() 只做一件事：modal.classList.add('active')。
           .modal-overlay 家族三个成员的标记上都带内联 style="display:none"，
           而全库没有任何 .modal-overlay.active 规则 —— 加上的类无人接收，
           内联的 display:none 从未被覆盖。结果是设置页的「隐私政策」和
           「服务条款」两个入口点了完全没反应。
           同族的 addFriendModal 之所以能用，是因为它绕开 openModal() 直接
           改 style.display —— 野生弹窗是症状，缺这条规则才是病因。 */

        .modal-header {
            display: flex;
            align-items: center;
            justify-content: space-between;
            padding: var(--sp-lg);
            border-bottom: 1px solid var(--log-rule);
        }

        .modal-title {
            /* 阶段一：删全大写。planTripModal / expenseModal / writeJournalModal 三处早已 text-transform:none，这里只是把它变成默认。 */
            font-family: var(--log-write);
            font-size: 15px;
            font-weight: var(--fw-regular);
            /* 阶段一：删 letter-spacing —— 字距是 caps token 的配套，正常大小写不需要 */
            margin: 0;
        }

        .modal-close {
            background: none;
            border: none;
            cursor: pointer;
            padding: var(--sp-xs);
            display: flex;
            align-items: center;
            justify-content: center;
        }

        .modal-close svg {
            width: 20px;
            height: 20px;
            stroke: var(--log-ink-soft);
        }

        .modal-close:hover svg {
            stroke: var(--log-ink);
        }

        .modal-content {
            padding: var(--sp-lg);
        }

        /* Search Bar */
        /* Sidebar */
        /* The sidebar was dressed here until 2026-09-07: the drawer, its head, its rows,
           their icons and the scrim. It is `public/css/17b-sidebar.css` now, in the deck
           log; this part is the outgoing world and the drawer is no longer in it. */

        /* Tabs */
        .tabs {
            display: flex;
            justify-content: center;
            padding: 2rem 1.5rem 1rem;
            gap: 2rem;
            border-bottom: 1px solid var(--log-rule);
            background: var(--log-stock);
        }

        .tab {
            font-size: 15px;
            letter-spacing: 2px;
            text-transform: uppercase;
            color: var(--log-ink-soft);
            cursor: pointer;
            padding-bottom: 1rem;
            border-bottom: 2px solid transparent;
            transition: all 0.3s;
            flex: 1;
            text-align: center;
        }

        .tab.active {
            color: var(--log-ink);
            border-bottom-color: var(--log-ink);
        }

        /* Google Maps Container */
        /* World Map page: flex layout so map fills remaining space *//* Map Controls *//* Map Legend *//* SVG Map Container (fallback) *//* Trip Markers on SVG Map */

        /* ══════════════════════════════════════════════════════════════
           MOVED OUT OF css/19-legacy-media.css · 2026-09-21  (appended)
           4 rules. DESIGN.md: a surface's rules go in that surface's part, and the
           legacy parts only shrink.
           ⚠️ 19-legacy-media.css loaded AFTER this file. This placement keeps
           their order against THIS PART’S OWN rules and nothing else: against the 13 part(s)
           between #5 and #19 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.
           ══════════════════════════════════════════════════════════════ */
        /* Controls a travel mate can see but not use. Dimmed rather than hidden so the
           layout stays the same for everyone — a missing button reads as a bug, a greyed
           one reads as a permission. Still tappable: tapping explains why. */
        .is-locked {
            opacity: 0.38;
        }


        .is-locked:active {
            transform: none;
        }

        /* ⚠️ THE OUTGOING BASE — and the reason four rooms each wrote their own dressing
           for this list. It is unscoped and it loads after every one of those rooms' files,
           so each of them had to name itself to be heard, and the fifth room to ask would
           have done the same. Its INK went on 2026-09-11; the shared deck-log dressing is in
           css/17t-stop.css and each room now says only where its list sits.

           What stays is the fallback geometry: a list with no room-specific rule still has
           to appear somewhere sensible rather than in the document's flow at zero. */
        .place-suggestions {
            display: none;
            position: absolute;
            left: 0;
            right: 0;
            top: calc(100% + 4px);
            z-index: 50;
            max-height: 220px;
            overscroll-behavior: contain;
        }

        /* The lifted source stays visible but recedes, so it's clear which photo is in
           flight without losing the sense of where it came from. */
        .is-dragging { opacity: 0.3; }

        /* ⚠️ CARRIED FROM css/19-legacy-media.css ON ITS DELETION, 2026-09-21. It explains the
           .place-suggestions rule that moved into this part: why six rooms share one list.
           The prose did not travel with the rule, because the mover takes only the comment
           directly above one and this sat two comments away. */
/* ===== Place suggestions & diary tags ===== */
        /* 地点联想下拉 · 全库一套。
           日记的地点框、停靠点的 MAP LOCATION、出发城市、住宿地址、景点位置、
           通用卡片位置 —— 六处用的是同一个 Places 联想,以前却是两套写法:
           这一套(有类名),和另外五处**内联在 index.html 上的面板 + 内联在
           04-stops.js 里的行**(padding / 字号 / 边框 / 两个 onmouseover 改
           style.background,逐字复制了三份)。内联的那套在样式表里改多少次
           都不生效,而且改一处漏两处。现在六处都走这两个类。
           几何值与币种 / 国家 / 朋友三套对齐:纸底、发丝边、13px、约 30 高。 */

        /* ══════════════════════════════════════════════════════════════
           MOVED OUT OF css/10-legacy-home-sheet.css · 2026-09-21  (appended)
           12 rules. DESIGN.md: a surface's rules go in that surface's part, and the
           legacy parts only shrink.
           ⚠️ 10-legacy-home-sheet.css loaded AFTER this file. This placement keeps
           their order against THIS PART’S OWN rules and nothing else: against the 4 part(s)
           between #5 and #10 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.
           ══════════════════════════════════════════════════════════════ */
        /* ---- Destinations 的国家选择器 ----
           一套定义,不是「基础全屏 + 一层下拉覆盖」。原来它是 position:fixed
           整屏页面挂在 body 上,于是要跟设置页比 z-index(设置页在页栈之上还会
           被抬一次,比输了就是「点了没反应」),而且改一个字段变成一次换页。
           现在它挂在字段下面。全屏那套的规则全部删掉 —— 留着就是下一次
           「为什么改了没变化」的来源。 */
        .country-modal {
            display: none;
            flex-direction: column;
            position: absolute;
            top: calc(100% - 1px);
            left: 0;
            right: auto;
            bottom: auto;
            width: 220px;
            max-height: 300px;
            z-index: 20;
            /* The panel, the search row and the option rows all took the outgoing world’s field
               white (#FBFAF6) and its grey rule. This component’s own comment below says the
               option rows follow the currency dropdown — 「照币种下拉那一套」 — and that set was
               settled in the deck log in 17v-expense-form.css: the stock for a ground, the firm
               rule for an edge, the soft ink for a placeholder. (2026-09-24) */
            background: var(--log-stock);
            border: 1px solid var(--log-rule-firm);
            overflow: hidden;
        }


        .country-modal.active { display: flex; }

        /* 搜索行明显高过列表行(48 对 30),读得出这是两个东西而不是第一条结果。
           flex: none 不能省 —— 面板是纵向 flex 且有 max-height,列表 flex:1
           撑满之后,默认 flex-shrink:1 会把这一行从 48 压到 18(一行文字的高度)。
           读 getComputedStyle 时列表还是空的,量到的仍是 48,所以这个坑不看
           打开后的实际渲染是发现不了的。 */
        .country-search {
            flex: none;
            display: flex;
            align-items: center;
            gap: 9px;
            height: 48px;
            padding: 0 10px;
            box-sizing: border-box;
            border-bottom: 1px solid var(--log-rule-firm);
            background: var(--log-stock);
            color: var(--log-ink-soft);
        }


        .country-search svg { flex: none; }

        .country-search:focus-within { color: var(--log-ink, #43276B); border-bottom-color: var(--log-ink, #43276B); }


        .country-search input {
            width: 100%;
            height: 100%;
            box-sizing: border-box;
            padding: 0;
            border: none;
            background: none;
            border-radius: 0;
            font-family: var(--log-write);
            font-size: 13px;
            letter-spacing: normal;
            color: var(--log-ink);
            outline: none;
        }


        .country-search input::placeholder {
            color: var(--log-ink-soft);
            font-style: normal;
        }


        .country-list {
            flex: 1;
            min-height: 0;
            padding: 4px 0;
            overflow-y: auto;
            overscroll-behavior: contain;
        }

        /* 选项行照币种下拉那一套(§13 里唯一已收口的一套):13px、约 30 高。 */
        .country-list-item {
            display: flex;
            align-items: center;
            justify-content: space-between;
            padding: 7px 10px;
            min-height: 0;
            background: var(--log-stock);
            cursor: pointer;
        }


        .country-list-item:hover { background: var(--log-stock-band); }


        .country-name {
            font-family: var(--log-write);
            font-size: 13px;
            letter-spacing: normal;
        }

        /* 勾选框拿掉 —— 选中与否上面那行 chip 已经说了一遍,列表里再画一个方框
           是第二遍。已选的那几行认得出来就够了。 */

        .country-list-item.selected .country-name {
            /* Was --accent, the retired sage. A chosen row is marked the way Saved marks one
               (15-saved.css:469) — the band under it — and the name goes to full ink with its
               weight. Stamped-State: the mark is a change of pressure, not a second hue. */
            color: var(--log-ink);
            font-weight: 600;
        }

        /* ══════════════════════════════════════════════════════════════
           MOVED OUT OF css/10-legacy-home-sheet.css · 2026-09-21  (appended)
           18 rules. DESIGN.md: a surface's rules go in that surface's part, and the
           legacy parts only shrink.
           ⚠️ 10-legacy-home-sheet.css loaded AFTER this file. This placement keeps
           their order against THIS PART’S OWN rules and nothing else: against the 4 part(s)
           between #5 and #10 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.
           ══════════════════════════════════════════════════════════════ */
        /* Rich Text Editor */
        .rich-text-toolbar {
            display: flex;
            gap: var(--sp-2xs);
            padding: var(--sp-xs);
            border: 1px solid var(--log-rule-firm);
            border-bottom: none;
            border-radius: 0;
            background: var(--log-stock-band);
            flex-wrap: wrap;
            align-items: center;
        }


        .rich-text-sep {
            width: 1px;
            height: 20px;
            background: var(--log-rule);
            margin: 0 0.25rem;
        }


        .rich-text-btn {
            padding: 8px;
            border: none;
            background: transparent;
            cursor: pointer;
            border-radius: 0;
            color: var(--log-ink-soft);
            transition: all 0.2s;
            width: 30px;
            height: 30px;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 15px;
        }


        .rich-text-btn:hover {
            background: var(--log-stock-band);
            color: var(--log-ink);
        }


        .rich-text-btn.active {
            background: var(--log-stock-band);
            color: var(--log-ink);
        }


        .rich-text-select {
            /* 阶段二 · 第二批：垂直 padding 删除 —— min-height:44 已主导高度，
               这份 padding 不再影响尺寸，只剩水平方向起作用。删它零视觉变化。 */
            padding: 0 8px;
            border: 1px solid var(--log-rule-firm);
            border-radius: 0;
            background: var(--log-stock);
            font-size: var(--fs-small);
            color: var(--log-ink-soft);
            cursor: pointer;
            height: 30px;
            outline: none;
            font-family: var(--log-write);
        }


        .rich-text-select:focus {
            border-color: var(--log-ink, #43276B);
        }

        /* 收窄：这六个属性（有框、有底、无衬线）是「富文本编辑器在表单里」这一种用法，
           不是全局默认。日记编辑器要的是无框、透明、衬线正文（规格阶段三 05：
           正文 15px 衬线、单栏、不要浮动工具条），它本来就不该来压这条规则。
           排除 .jw-editor 之后，日记侧的一整组 全部不再需要。 */
        .rich-text-editor:not(.jw-editor) {
            min-height: 150px;
            padding: var(--sp-md);
            border: 1px solid var(--log-rule-firm);
            border-radius: 0;
            background: var(--log-stock);
            font-family: var(--log-write);
            font-size: 15px;
            line-height: 1.6;
            outline: none;
        }


        .rich-text-editor:focus {
            border-color: var(--log-ink, #43276B);
        }


        .tiptap-editor-container {
            min-height: 100px;
            padding: 8px 0;
            background: transparent;
            font-family: var(--log-write);
            font-size: var(--fs-base);
            line-height: 1.6;
        }


        .tiptap-content {
            outline: none;
            min-height: 100px;
        }


        .tiptap-content p { margin: 0 0 0.6em; }

        .tiptap-content p:last-child { margin-bottom: 0; }

        .tiptap-content ul, .tiptap-content ol { padding-left: 1.4em; margin: 0 0 0.6em; }

        .tiptap-content h2 { font-family: var(--log-write); font-size: 19px; margin: 0.6em 0 0.4em; }


        .rich-text-editor:empty:before {
            content: attr(data-placeholder);
            color: var(--log-ink-soft);
            font-style: normal; letter-spacing: 0.05em;
        }


        .rich-text-editor hr {
            border: none;
            border-top: 1px solid var(--log-rule);
            margin: 0.75rem 0;
        }


        .rich-text-editor a {
            color: var(--log-ink);
            text-decoration: underline;
        }

        /* ══════════════════════════════════════════════════════════════
           MOVED OUT OF css/10-legacy-home-sheet.css · 2026-09-21  (appended)
           63 rules (the banner said 64: the 64th was .modal-content, which belonged inside a
           @media and went to 09-modal.css instead — see that file). DESIGN.md: a surface's rules go in that surface's part, and the
           legacy parts only shrink.
           ⚠️ 10-legacy-home-sheet.css loaded AFTER this file. This placement keeps
           their order against THIS PART’S OWN rules and nothing else: against the 4 part(s)
           between #5 and #10 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.
           ══════════════════════════════════════════════════════════════ */
        /* Form Styles */
        .form-section {
            margin-bottom: 2rem;
        }


        .form-label {
            /* ── carried from css/03-legacy-home-nav.css, 2026-09-22: the one declaration of that
               file’s .form-label this one had not already re-declared. ── */
            font-weight: var(--fw-medium);
            /* 阶段一：字段标签降级 —— 规格判据「在给区块命名还是给字段贴标签？后者一律降级」。
               letter-spacing 一并删：字距是全大写的配套，常规大小写不需要。 */
            display: block;
            font-size: var(--fs-small);
            color: var(--log-ink-soft);
            margin-bottom: 12px;
        }


        .form-input,
        .form-textarea {
            width: 100%;
            padding: var(--sp-md);
            border: none;
            border-bottom: 1px solid var(--log-rule-firm);
            background: transparent;
            font-family: var(--log-write);
            font-size: var(--fs-base);
            font-style: normal; letter-spacing: 0.05em;
            color: var(--log-ink);
            outline: none;
            transition: border-color 0.3s;
        }


        .form-input:focus,
        .form-textarea:focus {
            border-bottom-color: var(--log-ink, #43276B);
        }


        .form-textarea {
            font-family: var(--log-write);
            font-style: normal;
            resize: vertical;
            min-height: 100px;
        }


        .upload-preview,
        .link-preview {
            width: 100%;
            height: 200px;
            background-size: cover;
            background-position: center;
            border-radius: 0;
            border: 1px solid var(--log-rule);
        }


        .link-preview {
            margin-top: 1rem;
            display: none;
        }


        .link-preview.active {
            display: block;
        }

        /* Modal Actions */
        .modal-actions {
            display: flex;
            gap: var(--sp-md);
            margin-top: 2rem;
            padding: 0 2rem;
        }


        .modal-body .modal-actions {
            padding: 0;
        }

        /* ⚠️ `flex: 1` 拿掉了（2026-09-21）。动作行现在是靠左排的印刷体词，靠
           `order` 把 SAVE 推到右边（css/24-tail.css）；等分三颗是实心方块那一版的
           几何，等分之后 margin-left: auto 就没有剩余空间可推了。 */
        .modal-actions .btn {
            flex: none;
        }


        .btn {
            padding: var(--sp-md);
            border: none;
            border-radius: 0;
            font-family: var(--log-write);
            font-size: 15px;
            letter-spacing: 2px;
            text-transform: uppercase;
            cursor: pointer;
            transition: all 0.3s;
        }

        /* ⚠️ 这三条是基底，2026-09-21 随动作行一起改写为甲板日志的取值。
           全库唯一还在用 .btn-* 的地方是 #addActivityModal 的 .modal-actions（实测
           DOM：btn-primary / -secondary / -danger 各 1 颗，都在那一行里），而那一行
           由 css/24-tail.css 以 (0,2,0) 改写。所以这三条实际上一条都赢不到 ——
           留着并跟着改值，是为了「基底和改写说同一句话」：将来若有第二处用到
           .btn-primary 而不在 .modal-actions 里，它拿到的不该还是上一个世界的棕色。
           取值与 24-tail / 17s-plan-trip 一致：写记录用品红，出口用墨色。 */
        .btn-primary {
            background: none;
            color: var(--log-magenta);
        }


        .btn-primary:hover {
            color: var(--log-magenta);
        }


        .btn-secondary {
            background: none;
            color: var(--log-ink);
            border: none;
        }


        .btn-secondary:hover {
            background: none;
        }

        /* 危险按钮 · 全库一条
           原来是实心 #4A3728 —— 那个棕是 --primary 那一族（住宿、木、室内），
           拿它当危险色是语义错位：同一个色在别处表示「安全的主色」。
           2026-09-11 改成安静式：纸面、1px brick 描边、brick 字。
           2026-09-21 再改：brick 不在这个世界的词汇表里（港红 / 右舷绿 / 品红 三支
           而已），而删除是品红 —— 主人 2026-09-17：打开表单算写记录，删除统一用品红。 */
        .btn-danger {
            background: transparent;
            border: none;
            color: var(--log-magenta);
        }

        /* Date Input Styling */
        input[type="date"],
        input[type="time"] {
            position: relative;
            color: var(--log-ink);
            /* Was #4A3728, and for one commit it was the ONLY thing left wearing it: the app’s
               default accent moved to body as --log-magenta on 2026-09-21 and this one did not
               follow, so checked boxes went magenta while the day picked in a date field stayed
               the outgoing brown. Two accents where there had always been one, which is worse
               than the brown was. (Found in review, 2026-09-22.) */
            accent-color: var(--log-magenta, #A81E6E);
            color-scheme: light;
        }


        input[type="date"]::-webkit-calendar-picker-indicator,
        input[type="time"]::-webkit-calendar-picker-indicator {
            position: absolute;
            right: 0;
            cursor: pointer;
            filter: invert(20%) sepia(15%) saturate(800%) hue-rotate(345deg) brightness(90%) contrast(90%);
        }

        /* Override browser default date picker colors */
        input[type="date"]::-webkit-datetime-edit-fields-wrapper {
            color: var(--log-ink);
        }


        /* The separators between day, month and year are the quieter voice. */
        input[type="date"]::-webkit-datetime-edit-text {
            color: var(--log-ink-soft);
        }


        input[type="date"]::-webkit-datetime-edit-month-field,
        input[type="date"]::-webkit-datetime-edit-day-field,
        input[type="date"]::-webkit-datetime-edit-year-field {
            color: var(--log-ink);
        }


        input[type="date"]:focus::-webkit-datetime-edit-month-field:focus,
        input[type="date"]:focus::-webkit-datetime-edit-day-field:focus,
        input[type="date"]:focus::-webkit-datetime-edit-year-field:focus {
            background-color: var(--log-stock-band);
            color: var(--log-ink, #43276B);
        }

        /* Currency Dropdown */
        .currency-dropdown {
            position: absolute;
            top: 100%;
            left: 0;
            right: 0;
            /* left:0 + right:0 已经让面板跟随定位祖先，但 box-sizing 默认 content-box 时
               那 2px 边框会加在外面，再有任何 min-width 就会撑出去。两条一起锁死。 */
            box-sizing: border-box;
            min-width: 0;
            background: var(--log-stock);
            border: 1px solid var(--log-rule-firm);
            border-radius: 0;
            margin-top: 0.5rem;
            max-height: 0;
            overflow: hidden;
            opacity: 0;
            visibility: hidden;
            transition: all 0.3s;
            z-index: 10;
            box-shadow: none;
        }

        /* ══════════════════════════════════════════════════════════════
           币种下拉 · 输入即筛
           一百五十多个币种按字母排，滚到 IDR 要划十几屏。搜索行替掉那段滚动 ——
           它不是新增装饰，是把「滚」换成「打」。
           样式沿用 §组件 的下划线式搜索框（无衬线、1px 底线、无框），不新造一套。
           ══════════════════════════════════════════════════════════════ */
        .cur-search {
            position: sticky;
            top: 0;
            z-index: 1;
            background: var(--log-stock);
            border-bottom: 1px solid var(--log-rule-firm);
            padding: 0 8px;
        }

        .cur-search-input {
            width: 100%;
            box-sizing: border-box;
            min-height: 44px;
            border: none;
            border-radius: 0;
            background: transparent;
            font-family: var(--log-write);
            font-size: 13px;
            letter-spacing: normal;
            text-transform: none;
            color: var(--log-ink);
        }

        .cur-search-input::placeholder {
            color: var(--log-ink-soft);
        }

        .cur-search-input:focus-visible {
            outline: none;
            border-bottom: 2px solid var(--log-ink, #43276B);
        }

        .cur-empty {
            padding: 12px 8px;
            font-family: var(--log-write);
            font-size: 13px;
            color: var(--log-ink-soft);
        }

        .currency-dropdown.active {
            max-height: 300px;
            opacity: 1;
            visibility: visible;
            overflow-y: auto;
            z-index: 2000;
        }


        .currency-option {
            padding: var(--sp-sm) var(--sp-md);
            cursor: pointer;
            transition: background 0.2s;
        }


        .currency-option:hover {
            background: var(--log-stock-band);
        }

        /* Location Input */
        .location-input {
            position: relative;
        }


        .location-icon {
            position: absolute;
            left: 1rem;
            top: 50%;
            transform: translateY(-50%);
            width: 20px;
            height: 20px;
            stroke: var(--log-ink-soft);
            stroke-width: 2;
            fill: none;
        }

        /* Toggle Switch */
        .toggle-switch {
            position: relative;
            display: inline-block;
            width: 60px;
            height: 32px;
        }


        .toggle-switch input {
            opacity: 0;
            width: 0;
            height: 0;
        }


        .toggle-slider {
            position: absolute;
            cursor: pointer;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background-color: var(--log-rule);
            transition: 0.4s;
            border-radius: 0;
        }


        .toggle-slider:before {
            position: absolute;
            content: "";
            height: 24px;
            width: 24px;
            left: 4px;
            bottom: 4px;
            background-color: var(--log-stock);
            transition: 0.4s;
            border-radius: 0;
        }


        .toggle-switch input:checked+.toggle-slider {
            background-color: var(--log-ink);
        }


        .toggle-switch input:checked+.toggle-slider:before {
            transform: translateX(28px);
        }

        /* Activity Detail */
        .activity-detail {
            padding: var(--sp-md) 0;
        }

        /* Register Entry Form Styles */
        .form-row-split {
            display: flex;
            gap: var(--sp-md);
            margin-bottom: 1.5rem;
        }


        .form-row-split .form-group {
            margin-bottom: 0;
        }


        .form-input {
            width: 100%;
            padding: var(--sp-md);
            border: 1px solid var(--log-rule-firm);
            border-radius: 0;
            font-size: var(--fs-base);
            font-family: var(--log-write);
            transition: border-color 0.3s;
            min-height: 56px;
            box-sizing: border-box;
        }


        .form-input:focus {
            outline: none;
            border-color: var(--log-ink, #43276B);
        }


        .form-textarea {
            width: 100%;
            padding: var(--sp-md);
            border: 1px solid var(--log-rule-firm);
            border-radius: 0;
            font-size: var(--fs-base);
            font-family: var(--log-write);
            resize: vertical;
            transition: border-color 0.3s;
        }


        .form-textarea:focus {
            outline: none;
            border-color: var(--log-ink, #43276B);
        }


        .amount-input-wrapper {
            position: relative;
            display: flex;
            align-items: center;
        }


        .currency-symbol {
            position: absolute;
            left: 1rem;
            font-size: var(--fs-xl);
            color: var(--log-ink);
            font-weight: var(--fw-strong);
            pointer-events: none;
            z-index: 1;
        }


        .amount-input {
            width: 100%;
            padding: 1rem 1rem 1rem 2.5rem;
            border: 1px solid var(--log-rule-firm);
            border-radius: 0;
            font-size: var(--fs-lg);
            font-weight: var(--fw-strong);
            font-family: var(--log-write);
            position: relative;
            z-index: 2;
            background: transparent;
        }


        .amount-input:focus {
            outline: none;
            border-color: var(--log-ink, #43276B);
        }


        .category-selector {
            position: relative;
            padding: var(--sp-md);
            border: 1px solid var(--log-rule-firm);
            border-radius: 0;
            cursor: pointer;
            display: flex;
            justify-content: space-between;
            align-items: center;
            background: var(--log-stock-band);
            min-height: 56px;
        }


        /* Ink-Not-Glow: a hover thickens toward the ink, it does not light a hue. */
        .category-selector:hover {
            border-color: var(--log-ink);
        }


        .selected-category {
            display: flex;
            align-items: center;
            gap: var(--sp-sm);
        }


        .category-icon-display {
            width: 24px;
            height: 24px;
            display: flex;
            align-items: center;
            justify-content: center;
            flex-shrink: 0;
        }


        .category-icon-display svg {
            width: 20px;
            height: 20px;
            fill: none;
            stroke: var(--log-ink);
            stroke-width: 1.5;
            color: var(--log-ink);
        }


        .dropdown-arrow {
            width: 20px;
            height: 20px;
            stroke: var(--log-ink-soft);
            fill: none;
            stroke-width: 2;
            flex-shrink: 0;
        }


        .upload-text {
            color: var(--log-ink-soft);
            font-size: 15px;
        }


        .upload-preview {
            position: relative;
        }

        /* Attachment Upload */
        .attachment-upload {
            border: 2px dashed var(--log-rule-firm);
            border-radius: 0;
            padding: var(--sp-2xl) var(--sp-xl);
            text-align: center;
            cursor: pointer;
            transition: all 0.3s;
            margin-top: 12px;
        }


        .attachment-upload:hover {
            border-color: var(--log-ink);
            background: var(--log-stock-band);
        }


        .attachment-upload .upload-text {
            font-size: 15px;
            color: var(--log-ink-soft);
            text-transform: uppercase;
            letter-spacing: 1px;
            margin-bottom: 0.5rem;
        }


        .attachment-upload .upload-subtext {
            font-size: var(--fs-small);
            color: var(--log-ink-soft);
            font-style: normal; letter-spacing: 0.05em;
        }


        /* The narrow-screen override of .modal-content that was appended here on 2026-09-21
           has gone to css/09-modal.css, beside the rule it overrides. It arrived here
           by accident: it lived inside @media (max-width: 768px) in 10-legacy-home-sheet.css and
           the mover read it as a top-level rule, which would have applied margin:0 and
           max-height:100vh to every modal at every width. The mover refuses at-rules now. */

        /* ══════════════════════════════════════════════════════════════
           MOVED OUT OF css/08-legacy-itinerary.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.
           ⚠️ 08-legacy-itinerary.css loaded AFTER this file. Placing them
           at the foot keeps their order against THIS PART’S OWN rules — and nothing else.
           Against the parts BETWEEN #5 and #7 the order is reversed by this move, and
           whether that matters was a separate question somebody had to answer. See DESIGN.md.
           ══════════════════════════════════════════════════════════════ */
        /* Journal Section */
        .journal-section {
            padding: var(--sp-md) 0;
        }

        /* ══════════════════════════════════════════════════════════════
           MOVED OUT OF css/23-legacy-plate.css · 2026-09-21  (appended)
           24 rules. DESIGN.md: a surface's rules go in that surface's part, and the
           legacy parts only shrink.
           ⚠️ 23-legacy-plate.css loaded AFTER this file. Placing them
           at the foot keeps their order against THIS PART’S OWN rules — and nothing else.
           Against the parts BETWEEN #5 and #50 the order is reversed by this move, and
           whether that matters was a separate question somebody had to answer. See DESIGN.md.
           ══════════════════════════════════════════════════════════════ */
/* ----- Add-a-country sheet ----- */
        .acs {
            position: fixed;
            inset: 0;
            z-index: 60;
            display: flex;
            align-items: flex-end;
            background: rgba(22, 25, 26, 0.4);   /* the oilskin, not the outgoing #242424 (2026-09-17) */
            opacity: 0;
            visibility: hidden;
            transition: opacity 0.25s ease, visibility 0s linear 0.25s;
        }


        .acs.is-open { opacity: 1; visibility: visible; transition: opacity 0.25s ease, visibility 0s; }

        /* Migrated into the deck log 2026-09-04 with the Register itself. It was still
           the outgoing world entire — warm parchment, Vollkorn, a brown stamp — and it
           slid up over a cool blue-grey sheet the moment anyone tapped "Add one by hand".
           Edited in place rather than overridden from the #C block: nothing else in the
           app opens this sheet, so there is one set of rules for it and no argument about
           which wins. */
        .acs-panel {
            width: 100%;
            max-height: 82dvh;
            overflow-y: auto;
            background: var(--log-stock);
            border-top: 1px solid var(--log-rule-firm);
            padding: var(--sp-lg) var(--sp-lg) calc(var(--sp-lg) + env(safe-area-inset-bottom, 0px));
            transform: translateY(12px);
            transition: transform 0.25s ease;
        }


        .acs.is-open .acs-panel { transform: translateY(0); }


        .acs-head { display: flex; align-items: baseline; margin-bottom: var(--sp-md); }


        .acs-title {
            flex: 1;
            font-family: var(--log-print);
            font-size: var(--fs-body);
            font-weight: 600;
            letter-spacing: 0.16em;
            text-transform: uppercase;
            color: var(--log-ink);
        }


        .acs-cancel {
            padding: 0;
            border: none;
            background: transparent;
            cursor: pointer;
            font-family: var(--log-print);
            font-size: var(--fs-micro);
            font-weight: 600;
            letter-spacing: 0.16em;
            text-transform: uppercase;
            color: var(--log-ink);
            min-height: 44px;
        }


        .acs-input {
            width: 100%;
            padding: var(--sp-sm) var(--sp-md);
            border: 1px solid var(--log-rule-firm);
            background: var(--log-stock-band);
            font-family: var(--log-write);
            /* --fs-base, not --fs-lg. At list scale these are entries to scan, not
               headings; at 430px the larger size pushed "Saint Vincent and the
               Grenadines" onto two lines. */
            font-size: var(--fs-base);
            color: var(--log-ink);
        }


        .acs-list { border: 1px solid var(--log-rule-firm); border-top: none; }

        /* A button, because tapping one picks a country. It was a div with an onclick:
           unreachable by keyboard, unnamed to a screen reader, and the whole picker with
           it. */
        .acs-row {
            display: flex;
            width: 100%;
            align-items: baseline;
            gap: var(--sp-sm);
            padding: var(--sp-xs) var(--sp-md);
            min-height: 44px;
            border: none;
            border-bottom: 1px solid var(--log-rule);
            background: none;
            text-align: left;
            cursor: pointer;
            font-family: var(--log-write);
            font-size: var(--fs-base);
            color: var(--log-ink);
        }


        .acs-row:last-child { border-bottom: none; }


        .acs-row-tag {
            margin-left: auto;
            font-family: var(--log-print);
            font-size: var(--fs-micro);
            font-weight: 600;
            letter-spacing: 0.14em;
            text-transform: uppercase;
            color: var(--log-ink-soft);
        }


        .acs-empty {
            padding: var(--sp-md);
            font-family: var(--log-write);
            font-size: var(--fs-small);
            color: var(--log-ink-soft);
        }


        .acs-year { margin-top: var(--sp-lg); }

        /* The list is empty until something is typed, so it must not draw a box around
           nothing. */
        .acs-list:empty { border: none; }


        .acs-year-label {
            display: block;
            font-family: var(--log-print);
            font-size: var(--fs-micro);
            font-weight: 600;
            letter-spacing: 0.16em;
            text-transform: uppercase;
            color: var(--log-ink);
            margin-bottom: var(--sp-sm);
        }

        /* The stamp. This is the one moment on the Register that is genuinely an action,
           so it is the one place oilskin belongs — the same black the sign-in sheet binds
           its foot with. */
        .acs-go {
            width: 100%;
            margin-top: var(--sp-lg);
            padding: var(--sp-md);
            min-height: 52px;
            border: none;
            background: var(--log-oilskin);
            color: var(--log-on-oilskin);
            cursor: pointer;
            font-family: var(--log-print);
            font-size: var(--fs-micro);
            font-weight: 600;
            letter-spacing: 0.16em;
            text-transform: uppercase;
        }


        .acs-go:disabled {
            background: var(--log-stock-band);
            color: var(--log-ink-soft);
            cursor: default;
        }


        .acs-note {
            margin: var(--sp-sm) 0 0;
            font-family: var(--log-write);
            font-size: var(--fs-small);
            line-height: 1.5;
            color: var(--log-ink-soft);
        }


        .acs-yr-input {
            width: 100%;
            margin-top: var(--sp-xs);
            padding: var(--sp-xs) var(--sp-md);
            min-height: 44px;
            border: 1px solid var(--log-rule-firm);
            background: transparent;
            font-family: var(--log-write);
            font-size: var(--fs-base);
            font-variant-numeric: lining-nums;
            color: var(--log-ink);
        }


        .acs-yr-input::placeholder { color: var(--log-ink-soft); }


        #addCountrySheet :focus-visible {
            outline: 2px solid var(--log-ink);
            outline-offset: 2px;
        }

        /* Strip the spinner: it offers to step through years one at a time, which is not
           how anyone reaches 2017. */
        .acs-yr-input::-webkit-outer-spin-button,
        .acs-yr-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

        .acs-yr-input { -moz-appearance: textfield; }

        /* ══════════════════════════════════════════════════════════════
           MOVED OUT OF css/22-diagnostics.css · 2026-09-21  (appended)
           3 rules. DESIGN.md: a surface's rules go in that surface's part, and the
           legacy parts only shrink.
           ⚠️ 22-diagnostics.css loaded AFTER this file. Placing them
           at the foot keeps their order against THIS PART’S OWN rules — and nothing else.
           Against the parts BETWEEN #5 and #49 the order is reversed by this move, and
           whether that matters was a separate question somebody had to answer. See DESIGN.md.
           ══════════════════════════════════════════════════════════════ */
        /* ===== Favourite toggle =====
           One rule pair for both hearts — the gallery tile and the fullscreen viewer.
           They were two separate implementations with two separate bugs: the tile's fill
           was written from a template variable (frozen until re-render) and the viewer's
           was hardcoded `fill="none"` in the markup with nothing overriding it, so it
           could never fill at all.

           Controlled with fill-opacity rather than fill, deliberately. A leftover
           `fill="none"` attribute anywhere would beat a `fill` declaration's intent by
           painting nothing; fill-opacity is a different property, so the shape stays
           `currentColor` and only its opacity moves. One less way for this to break
           quietly a third time. */
        .fav-toggle svg path {
            fill: currentColor;
            fill-opacity: 0;
            transition: fill-opacity 0.3s;
        }


        .fav-toggle.is-fav svg path { fill-opacity: 1; }

        /* Warm ochre rather than the usual accent: this is a personal mark, not a system
           state, and it has to read at thumbnail size against any photograph. */
        .fav-toggle.is-fav { color: #E0B45C; opacity: 1; }
