        /* ==========================================================================
           #R  Settings — the form at the back of the book
           --------------------------------------------------------------------------
           A room off the sidebar, drawn by public/29c-settings.js. Rebuilt into the deck
           log 2026-09-08 — it was the last whole page of the outgoing world, and the one
           a traveller reaches for when something is wrong.

           No icons and no switches. The page it replaced had a glyph at the left of every
           row and a chevron at the right, and toggles for its states; this world writes
           what a thing is set to, in words, and lets the words be the control. A word that
           is in force is underlined, which is the same grammar as the state words on Saved
           and the measures in the Tool kit.

           Magenta: Delete, and Save on the password form. Both change the record.
           ========================================================================== */
        #settingsPage {
            background: var(--log-stock);
        }

        #settingsPage.active {
            display: flex;
            flex-direction: column;
            height: 100vh;
            height: 100dvh;
            min-height: 0;
            overflow: clip;
        }

        /* ---- the head ------------------------------------------------------------ */

        /* The head, its title and Back are the room head (.room-bar, css/11-home.css, since
           2026-09-16). What is this room's own: the page is a column with its own scroller,
           so the head must not shrink into it. */
        #settingsPage .st-bar,
        #passwordPage .st-bar {
            flex: none;
        }

        /* Password's Save — the one action a head in these two rooms carries. It wears
           Back's word, so it keeps the declarations Back now gets from the room head. */
        #settingsPage .st-act,
        #passwordPage .st-act {
            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;
            background: none;
            border: none;
            padding: 0;
            cursor: pointer;
            color: var(--log-ink);
            display: inline-flex;
            align-items: center;
            min-height: 44px;
        }

        /* Deleting everything is the gravest act in the app and takes the ink that means
           an act changes the record. It asks for the word DELETE before it does anything;
           the colour is not the guard, the question is. */
        #settingsPage .st-act.is-mark,
        #passwordPage .st-act.is-mark { color: var(--log-magenta); }

        /* ⚠️ PASSWORD'S SAVE AT THE RIGHT, AS THE RULE SAYS, WITHOUT LOSING ITS TARGET
           (owner, 2026-09-16: save要去最右边). Every other room's head action carries
           justify-self: end; this one did not, so as a grid item it stretched across the whole
           right-hand 1fr column and the word sat at x 263, right of centre — a departure from
           "at most one action at the right" that had gone unseen because nothing looked broken.

           The first way of fixing it would have cost the thumb: at the right edge the box
           shrinks to the word, 30.8 wide, from an accidental 142.8. So it takes the same answer
           as Back, mirrored — a 44 minimum, with its content laid to the END, so the box grows
           LEFTWARD into the empty cell and the word sits flush against the head's right margin.
           Measured: the word's right edge at 406, which is the bar's content edge; target
           44 × 44. Scoped to the head (.st-bar), because Settings uses .st-act in its body too,
           for Delete everything, and that one is not the head's to move. */
        #passwordPage .st-bar .st-act {
            justify-self: end;
            min-width: 44px;
            justify-content: flex-end;
        }

        /* ---- the sheet ----------------------------------------------------------- */

        #settingsPage .st-body,
        #passwordPage .st-body {
            flex: 1;
            min-height: 0;
            display: flex;
            flex-direction: column;
            overflow-y: auto;
            -webkit-overflow-scrolling: touch;
            padding-bottom: calc(var(--sp-2xl) + env(safe-area-inset-bottom, 0px));
        }

        #settingsPage .st-band,
        #passwordPage .st-band {
            margin: 0;
            padding: var(--sp-xs) var(--sp-lg);
            background: var(--log-stock-band);
            border-bottom: 1px solid var(--log-rule-firm);
            font-family: var(--log-print);
            font-size: var(--fs-micro);
            font-weight: 600;
            letter-spacing: 0.18em;
            text-transform: uppercase;
            color: var(--log-ink);
        }
        #settingsPage .st-band + .st-band,
        #passwordPage .st-band + .st-band,
        #settingsPage .st-note + .st-band,
        #passwordPage .st-note + .st-band,
        #settingsPage .st-line + .st-band,
        #passwordPage .st-line + .st-band { border-top: 1px solid var(--log-rule-firm); }

        /* ---- a row --------------------------------------------------------------- */

        /* The Register's entry row once more: what it is at the left, where it stands at
           the right. Every list in this app is ruled at one pitch and this is no different
           for being a form. */
        #settingsPage .st-line,
        #passwordPage .st-line {
            display: grid;
            grid-template-columns: 1fr auto;
            align-items: center;
            gap: var(--sp-sm);
            width: 100%;
            min-height: var(--log-row);
            padding: var(--sp-2xs) var(--sp-lg);
            border-bottom: 1px solid var(--log-rule);
            background: none;
            text-align: left;
        }

        /* A whole row that is a door. No chevron: the row's own name is the word, and this
           world has no glyphs for actions. */
        #settingsPage .st-line.is-door {
            border-left: none;
            border-right: none;
            border-top: none;
            cursor: pointer;
            font: inherit;
            color: inherit;
        }
        #settingsPage .st-line.is-door:hover { background: var(--log-stock-band); }
        #settingsPage .st-line.is-door .st-what {
            text-decoration: underline;
            text-underline-offset: 0.25em;
            text-decoration-thickness: 1px;
        }
        #settingsPage .st-line.is-door:focus-visible {
            outline: 2px solid var(--log-ink);
            outline-offset: -2px;
        }

        #settingsPage .st-what,
        #passwordPage .st-what {
            font-family: var(--log-write);
            font-size: var(--fs-body);
            color: var(--log-ink);
            min-width: 0;
        }

        /* What it holds now, in the softer pressure — the hierarchy of this whole world. */
        /* Wraps rather than clips. At 430px this cut Import's line to "back into your
           acc…" and worse at 375 — an ellipsis in the middle of the only sentence that says
           what a door does. A value long enough to need two lines gets two lines; the rows
           are ruled at their own pitch and one of them being taller is what a ruled sheet
           looks like. */
        #settingsPage .st-now {
            font-family: var(--log-write);
            font-size: var(--fs-small);
            line-height: 1.4;
            color: var(--log-ink-soft);
            text-align: right;
            max-width: 52vw;
        }

        /* ---- the words that were the control ------------------------------------- */

        /* .st-words and .st-word went on 2026-09-17 (owner: 设置里的几个内容改成下拉菜单). Every option was
           printed and the one in force underlined, so here an underline meant "chosen" while everywhere else
           it means "press" (critique P1). Each row is the currency row's picker now (stChoice, 29c-settings.js),
           dressed below with the currency list. */

        /* ---- a row for something that is not here yet ---------------------------- */

        /* The sidebar's device, brought inside: it is not a control and it says so. A
           switch that answers nothing is what this page was rebuilt to stop having. */
        /* ⚠️ NOTHING ON THIS SHEET MAY BE SQUASHED.

           .st-body is a column flex container that scrolls, and a flex item's default
           `flex-shrink: 1` applies BEFORE the overflow is handed to the scrollbar — so
           every row taller than its neighbours was being compressed to fit. The two
           stamped rows needed fifty-eight pixels and were given fifty-three, which is why
           the descenders of "Signing in with a face or a fingerprint" and "in English
           only, for now" sat on the rule below them. `min-height` cannot stop it; only
           refusing to shrink can.

           The finish review saw it on those two rows and read it as a baseline problem.
           It was not: it is every row on the page, and only the ones that wrap show it.
           Measured 2026-09-08 — the row wanted 58.5px and the box was 53px.

           .st-rule-out keeps its flex:1, because filling what is left is its whole job. */
        #settingsPage .st-body > * { flex: none; }
        #settingsPage .st-rule-out { flex: 1; }

        #settingsPage .st-line.is-soon {
            grid-template-columns: 1fr auto;
            grid-template-rows: auto auto;
            align-items: start;
        }
        #settingsPage .st-line.is-soon .st-what { color: var(--log-ink-soft); }
        #settingsPage .st-stamp {
            font-family: var(--log-print);
            font-size: var(--fs-micro);
            font-weight: 600;
            letter-spacing: 0.16em;
            text-transform: uppercase;
            color: var(--log-ink-soft);
            border: 1px solid var(--log-rule-firm);
            padding: 1px 6px;
            white-space: nowrap;
        }
        #settingsPage .st-why {
            grid-column: 1 / -1;
            margin-bottom: var(--sp-2xs);
            font-family: var(--log-write);
            font-size: var(--fs-small);
            line-height: 1.4;
            color: var(--log-ink-soft);
        }

        /* Logout: set apart above the grave row, its own act at the right. Ink, not
           magenta — signing out changes nothing that is written down. */
        /* Two rows: the label and the act on the first, who you are on the second.
           The row keeps .st-line's own centring — `align-items: start` put the label at the
           top of its track while the forty-four-pixel act stayed centred in the same one,
           so "Signed in" and "Log out" sat on two different optical lines. */
        #settingsPage .st-line.is-apart {
            border-top: 1px solid var(--log-rule-firm);
            grid-template-rows: auto auto;
        }
        #settingsPage .st-line.is-apart .st-why { margin-top: calc(var(--sp-2xs) * -1); }

        /* ---- the gravest row ----------------------------------------------------- */

        /* Set apart by a firm rule above it rather than by a red panel: this world marks
           what matters with its ruling, and the act carries the magenta on its own. */
        #settingsPage .st-line.is-grave { border-top: 1px solid var(--log-rule-firm); }

        /* ---- what a section means ------------------------------------------------ */

        #settingsPage .st-note,
        #passwordPage .st-note {
            margin: 0;
            padding: var(--sp-xs) var(--sp-lg) var(--sp-sm);
            font-family: var(--log-write);
            font-size: var(--fs-small);
            line-height: 1.45;
            color: var(--log-ink-soft);
        }
        #settingsPage .st-note.is-warn { color: var(--log-port); }

        /* The Ruled-To-The-Foot Rule: what is left of the sheet is ruled stock. */
        #settingsPage .st-rule-out,
        #passwordPage .st-rule-out {
            flex: 1;
            min-height: calc(var(--log-row) * 2);
            background-image: repeating-linear-gradient(to bottom,
                transparent 0,
                transparent calc(var(--log-row) - 1px),
                var(--log-rule) calc(var(--log-row) - 1px),
                var(--log-rule) var(--log-row));
        }

        /* ==========================================================================
           The password form, and the currency picker
           --------------------------------------------------------------------------
           Both live on this stylesheet because they are the same form in the same hand:
           #passwordPage is drawn by 29d-password.js with these very classes, so the two
           pages cannot drift apart by being dressed in two places.

           ⚠️ THAT SENTENCE WAS TRUE AND BESIDE THE POINT. They were dressed in one place
           and drifted anyway, because a selector names a page: eleven classes were drawn on
           #passwordPage and only three had a rule that reached it, so the whole form
           rendered in raw browser chrome — and because `.st-in` was one of the three, and
           sets `background: transparent; border: none`, its three fields were invisible. A
           reader could not see where to type, on the one form in the app that has to work.
           Found by the finish review, 2026-09-08.

           A comment cannot check itself, so dev/shared-dress.test.js does: it reads the
           classes each borrowing page draws and fails if one has no rule under that page's
           own selector. It names all nine of the missing ones against the old file. Add a
           class to a shared sheet and it will tell you which page you forgot. */
        #passwordPage {
            background: var(--log-stock);
        }
        #passwordPage.active {
            display: flex;
            flex-direction: column;
            height: 100vh;
            height: 100dvh;
            min-height: 0;
            /* Three fields on a page a keyboard will cover: a hidden box scrolled by a
               focused field pushes the head off the screen. */
            overflow: clip;
        }

        /* A row written in rather than read: the label at the left, the field filling the
           rest. The writing line's grammar, the same as Saved's search and the Tool kit's
           amount — transparent on the stock, the write face, the ink caret; focused, the
           band ground and a two-pixel ink baseline. */
        #settingsPage .st-line.is-field,
        #passwordPage .st-line.is-field {
            grid-template-columns: minmax(0, 10rem) 1fr;
        }
        #settingsPage .st-line.is-field:focus-within,
        #passwordPage .st-line.is-field:focus-within {
            background: var(--log-stock-lit, #DEE4E2);
            box-shadow: inset 0 -2px 0 0 var(--log-ink);
        }

        #settingsPage .st-in,
        #passwordPage .st-in {
            min-width: 0;
            min-height: 44px;
            padding: 0;
            border: none;
            background: transparent;
            font-family: var(--log-write);
            font-size: var(--fs-body);
            color: var(--log-ink);
            caret-color: var(--log-ink);
            text-align: right;
            -webkit-appearance: none;
            appearance: none;
        }
        #settingsPage .st-in:focus-visible,
        #passwordPage .st-in:focus-visible { outline: none; }

        /* Save waits until there is something to save. Disabled takes the softer pressure
           of the one ink rather than a grey of its own. */
        #passwordPage .st-act:disabled {
            color: var(--log-ink-soft);
            cursor: default;
            text-decoration-color: var(--log-rule);
        }

        /* ---- the currency picker ------------------------------------------------- */

        /* The component the expense form, the edit form, the activity form and the Tool kit
           all use, dressed for this room. The row used to open a modal in the outgoing
           world's colours; this is the control the rest of the app has, in this world ink. */
        #settingsPage .st-pick {
            position: relative;
            display: inline-flex;
            justify-content: flex-end;
        }

        #settingsPage .category-selector.st-code {
            display: inline-flex;
            align-items: center;
            justify-content: flex-end;
            min-height: 44px;
            padding: 0 0 0 var(--sp-xs);
            border: none;
            border-radius: 0;
            background: none;
            box-shadow: none;
            font-family: var(--log-print);
            font-size: var(--fs-micro);
            font-weight: 600;
            letter-spacing: 0.16em;
            text-transform: uppercase;
            color: var(--log-ink);
            /* Underlined: a code that can be changed is an action word, and this world has
               no glyph to say so with. */
            text-decoration: underline;
            text-underline-offset: 0.25em;
            text-decoration-thickness: 1px;
            cursor: pointer;
        }
        #settingsPage .category-selector.st-code:focus-visible {
            outline: 2px solid var(--log-ink);
            outline-offset: 2px;
        }

        /* Hung from the right edge of the code, not stretched between the cell sides — the
           positioned ancestor is the width of AUD, which is what made the Tool kit first
           attempt a column two letters wide. */
        #settingsPage .currency-dropdown.st-menu {
            position: absolute;
            top: calc(100% - 6px);
            right: 0;
            left: auto;
            box-sizing: border-box;
            min-width: 13rem;
            max-width: 78vw;
            background: var(--log-stock);
            border: 1px solid var(--log-rule-firm);
            border-radius: 0;
            margin: 0;
            box-shadow: none;
            text-align: left;
            /* A fixed height with a fade and a short fall, not an animated height:
               animating one relays out the hundred and fifty-five rows inside it on every
               frame, which on a phone is the difference between opening and stuttering. */
            max-height: 19rem;
            overflow: hidden;
            opacity: 0;
            visibility: hidden;
            transform: translateY(-6px);
            transition: opacity 0.18s ease, transform 0.18s ease, visibility 0s linear 0.18s;
            z-index: 2000;
        }
        #settingsPage .currency-dropdown.st-menu.active {
            opacity: 1;
            visibility: visible;
            transform: none;
            transition: opacity 0.18s ease, transform 0.18s ease, visibility 0s;
            overflow-y: auto;
            -webkit-overflow-scrolling: touch;
        }
        @media (prefers-reduced-motion: reduce) {
            #settingsPage .currency-dropdown.st-menu,
            #settingsPage .currency-dropdown.st-menu.active {
                transform: none;
                transition: opacity 0.01s linear, visibility 0s;
            }
        }

        #settingsPage .st-menu .cur-search {
            position: sticky;
            top: 0;
            z-index: 1;
            padding: 0 var(--sp-sm);
            background: var(--log-stock-band);
            border-bottom: 1px solid var(--log-rule-firm);
        }
        #settingsPage .st-menu .cur-search-input {
            width: 100%;
            box-sizing: border-box;
            min-height: 44px;
            padding: 0;
            border: none;
            border-radius: 0;
            background: transparent;
            font-family: var(--log-write);
            font-size: var(--fs-body);
            letter-spacing: normal;
            text-transform: none;
            color: var(--log-ink);
            caret-color: var(--log-ink);
        }
        #settingsPage .st-menu .cur-search-input::placeholder { color: var(--log-ink-soft); }
        #settingsPage .st-menu .cur-search:focus-within { box-shadow: inset 0 -2px 0 0 var(--log-ink); }
        #settingsPage .st-menu .cur-search-input:focus-visible { outline: none; border: none; }

        #settingsPage .st-menu .currency-option {
            display: flex;
            align-items: center;
            min-height: 44px;
            padding: 0 var(--sp-sm);
            border-bottom: 1px solid var(--log-rule);
            background: none;
            font-family: var(--log-print);
            font-size: var(--fs-micro);
            font-weight: 600;
            letter-spacing: 0.16em;
            text-transform: uppercase;
            color: var(--log-ink);
            cursor: pointer;
        }
        #settingsPage .st-menu .currency-option:last-child { border-bottom: none; }
        /* The value in force, in a list of a few (stChoice): the band's ground, as a row being walked to has,
           and not an underline — in this world that is the mark of something to press. */
        #settingsPage .st-menu .currency-option.is-on { background: var(--log-stock-band); }
        #settingsPage .st-menu .currency-option:hover { background: var(--log-stock-band); }
        /* The rows can be walked with the arrow keys now, so they have to show where the
           walk has got to. Focus takes the band ground and the world's own outline. */
        #settingsPage .st-menu .currency-option:focus-visible {
            background: var(--log-stock-band);
            outline: 2px solid var(--log-ink);
            outline-offset: -2px;
        }

        #settingsPage .st-menu .cur-empty {
            padding: var(--sp-sm);
            font-family: var(--log-write);
            font-size: var(--fs-small);
            text-transform: none;
            letter-spacing: normal;
            color: var(--log-ink-soft);
        }
        #settingsPage .st-menu ::selection { background: var(--log-stock-band); color: var(--log-ink); }
