/* ===========================================================================
   css/office-shell.css — the redesigned office shell (rail · page · margin)

   See documentation/UI_REDESIGN_HANDOFF.md for the full design and build plan.

   Phases 1-5 built this file behind a `body.shell-v2` dev flag, kept scoped so
   it could coexist with the old parchment pass without interleaving edits.
   Phase 6 (2026-09-24) made this shell the app's only office shell and
   dropped the flag itself -- `body` now carries `shell-v2` as a permanent
   literal class (js/shell-flag.js, the toggle mechanism, is gone).

   THIS FILE'S OWN SELECTORS ARE STILL SCOPED UNDER `.shell-v2`, ON PURPOSE,
   NOT AS A LEFTOVER. A full unscope was attempted the same day and reverted:
   several rules here (e.g. the #tradition-entry/#mode-selection recolor
   rules below) use `.shell-v2` as extra specificity weight to beat a real,
   still-existing competing rule of the same target in css/office.css --
   stripping it dropped those rules below the office.css rule's own
   specificity and caused a real, verified regression (screenshot diff, not
   guessed). office.css's own old parchment CSS for those two screens
   couldn't be deleted either, the same day, for the matching reason (Stage
   3's own deferral: office-shell.css only recolors those screens' cards, it
   never rebuilt the grid layout the base rules still provide). Unscoping
   this file safely needs that office.css cleanup done FIRST, not the other
   way around -- future work, not done here. See RESUME_PROJECT_NOTE.md and
   AUDIT_GOVERNANCE_LEDGER.md for the full account.

   TWO RULES THAT STILL GOVERN THIS FILE:

   1. NO EXISTING RULE IN css/office.css IS EDITED. This file loads AFTER
      office.css and wins on specificity where it needs to. It does not reach
      back and change office.css.

   2. TOKEN NAMES ARE PREFIXED `--uo-`. Verified 2026-09-12: no `--uo-` custom
      property existed anywhere in css/, js/ or index.html before this file, so
      these names cannot collide with the (now largely deleted) `--app-*`
      tokens of the parchment pass or with the original theme's `--gold` /
      `--ink` / `--rubric`.

   Book of Needs still runs the parchment pass and gets its own design pass
   separately, per UI_REDESIGN_HANDOFF.md section 9 -- Phase 6 only ever
   covered the office screen. The navigation-architecture governance conflict
   that used to sit in front of Phase 6 was resolved 2026-09-21 (Josh's
   ruling; see AUDIT_GOVERNANCE_LEDGER.md, gov:navigation-architecture-
   surface-superseded) before this phase started.
   =========================================================================== */


/* ---------------------------------------------------------------------------
   SELF-HOSTED FACES

   Vendored and subsetted 2026-09-12 from upstream, not pulled from a CDN:

     Cormorant Garamond — github.com/CatharsisFonts/Cormorant, SIL Open Font
       License. Subset from the upstream TTFs with pyftsubset to Latin,
       Latin Extended-A/B and Greek, keeping kern/liga/clig/calt/onum/dlig.
       452–453 glyphs each, ~45–50KB per face.
     IBM Plex Mono — @ibm/plex-mono 2.5.0, SIL Open Font License. Shipped as
       woff2 upstream; taken as published.

   Licences travel with the files, as the OFL requires: assets/fonts/OFL-CormorantGaramond.txt
   and assets/fonts/LICENSE-IBMPlexMono.txt. Do not delete them.

   @font-face cannot be scoped to a class — it is a global declaration by
   nature. That is safe here: declaring a face only makes the family AVAILABLE.
   Browsers do not fetch a font until something actually renders in it, and
   nothing does while the flag is off. No bytes are downloaded for a user who
   never sees shell-v2.

   Cormorant is a display face by its designer's own description, intended for
   large sizes and usable as text at high resolution. The 25px body floor in the
   handoff is inside that intended range, not a stretch of it.
   --------------------------------------------------------------------------- */

@font-face {
    font-family: 'Cormorant Garamond';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('../assets/fonts/CormorantGaramond-Regular.woff2') format('woff2');
}

@font-face {
    font-family: 'Cormorant Garamond';
    font-style: italic;
    font-weight: 400;
    font-display: swap;
    src: url('../assets/fonts/CormorantGaramond-Italic.woff2') format('woff2');
}

@font-face {
    font-family: 'Cormorant Garamond';
    font-style: normal;
    font-weight: 600;
    font-display: swap;
    src: url('../assets/fonts/CormorantGaramond-SemiBold.woff2') format('woff2');
}

@font-face {
    font-family: 'IBM Plex Mono';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('../assets/fonts/IBMPlexMono-Regular.woff2') format('woff2');
}


/* ---------------------------------------------------------------------------
   NIGHT — the default theme under the flag.
   Compline, Evening Prayer, Vespers, Ramsha.

   Theme is keyed to the OFFICE, not the clock: Evening Prayer said at 5pm in
   June still renders night. The Auto/Light/Dark control that selects between
   these two token sets is built in Phase 2; until then `body.shell-v2` simply
   carries the night values and `body.shell-v2.uo-day` the day ones.
   --------------------------------------------------------------------------- */

:root {
    --uo-ground:        #08070c;
    --uo-ground-raised: #0b0912;
    --uo-ink:           #e6dcc4;
    --uo-ink-quiet:     rgba(230, 220, 196, 0.62);
    --uo-accent:        #c9a84c;   /* gold */
    /* #c0392b as proposed measures 3.69:1 on this ground. At the 25px body
       floor that is WCAG "large text" and passes the 3:1 threshold — but the
       handoff's own acceptance criterion says 4.5:1 on EVERY text element, and
       rubrics also set at smaller sizes. Lightened 6.5% at identical hue and
       saturation to reach 4.55:1. Revert to #c0392b if the designer prefers the
       darker red and the criterion is relaxed for large text. */
    --uo-rubric:        #d34839;
    --uo-hairline:      rgba(201, 168, 76, 0.12);

    /* Seasonal dot. Rendered ONLY where the lane itself supplies a sourced
       colour. Anglican reads the existing `liturgicalColor` field in
       data/season/*.json (populated on 397 days). Eastern lanes emit their own,
       as sourced content with a named witness per tradition — and for East
       Syriac no dot is the likely correct result, which is honest silence, not
       a gap. An invented colour here would sit in the ordo line, the most
       authoritative-looking strip on the screen. */
    --uo-season-green:  #4a7c59;
    --uo-season-red:    #9b2335;
    --uo-season-purple: #6b3070;
    --uo-season-rose:   #a04060;
    --uo-season-white:  #c9a84c;

    /* Structural metrics. Consumed in Phase 2. */
    --uo-rail-width:    236px;
    --uo-margin-width:  268px;
    --uo-ordo-height:   64px;
    --uo-keeping-height: 46px;
    --uo-gutter-width:  70px;

    /* Type. The two new families are NOT loaded yet — Phase 2 self-hosts and
       subsets Cormorant Garamond and IBM Plex Mono rather than pulling full
       families from a CDN. The fallbacks below are deliberate and must stay
       usable on their own until then. */
    --uo-face-title:    'Cinzel', Georgia, serif;
    --uo-face-prayed:   'Cormorant Garamond', Georgia, serif;
    --uo-face-machine:  'IBM Plex Mono', ui-monospace, 'Courier New', monospace;
    --uo-size-body:     25px;      /* floor, not a starting point */

    /* MEASURE — the width of the prayed text, not of the column.
       Set in `ch` so it tracks the font rather than the viewport: if the body
       size or the face changes, the line length follows. 62ch lands around
       58-64 characters for Cormorant Garamond, inside the 45-75 that is legible
       for continuous reading, and well under the ~800px of unbroken line the
       page had after the parchment card's own 820px cap was removed with it.
       Prayed text is read aloud, line by line, and an over-long measure makes
       the eye lose its place on return — the specific failure this prevents. */
    --uo-measure:       62ch;
    --uo-leading-body:  1.72;
}


/* ---------------------------------------------------------------------------
   DAY — Morning Prayer, the Sixth Hour.
   --------------------------------------------------------------------------- */

body.uo-day {
    --uo-ground:        #f2ebdf;
    --uo-ground-raised: #f4ece0;
    --uo-ink:           #2a2118;
    --uo-ink-quiet:     rgba(42, 33, 24, 0.7);
    /* #8a6a24 as proposed measures 4.25:1 on the day ground, and this token
       sets 10-11px machine labels — small text, where 4.5:1 is required and
       there is no large-text exemption. Darkened 1.5% at identical hue and
       saturation to reach 4.59:1. The change is not perceptible side by side;
       the failure was. */
    --uo-accent:        #846522;   /* bronze */
    --uo-rubric:        #8b1a10;
    --uo-hairline:      rgba(103, 58, 31, 0.14);
}


/* ---------------------------------------------------------------------------
   DEMOLITION — the parchment pass does not reach the office screen

   Phase 6 brought forward, for the office screen only, because coexistence was
   costing more than it saved. The old skin dressed the prayed text in a rounded
   860px card with its own background, border, inset highlight and 62px drop
   shadow — inside the page column. That is why the running app still looked
   like the old page in a new frame, and it is why the theme could never settle:
   two systems were colouring the same pixels from different state.

   Under `shell-v2` the shell owns the ground, the card and the theme outright.
   Everything below neutralises a specific rule in css/office.css rather than
   blanketing, so what is being undone stays legible and Phase 6 can delete the
   originals by line.

   SPECIFICITY: the rules being undone sit at (0,2,0) to (0,3,1). Each override
   below is written at least as heavy and this file loads last. Anything new
   added here must be too, or it will silently lose — that failure has already
   happened once in this project and cost a full patch to diagnose.

   OFF the office screen nothing changes: the splash, the Book of Needs, the
   Bible browser and the admin dashboard keep the parchment skin and the old
   theme behaviour untouched.
   --------------------------------------------------------------------------- */

/* office.css 1632 + 1648 — the card itself. Nothing but layout survives.
   The dark-mode-qualified selector this rule used to also carry (needed
   while body.shell-v2 was the only anchor) is now a literal duplicate of
   the bare .office-container above -- dropped rather than kept as dead
   weight, 2026-09-25 unscoping Stage 4.

   KEPT .office-active here, not stripped fully bare: a fully-bare
   .office-container would be an EXACT specificity tie with office.css's
   own bare .office-container rule (line 365 -- background, max-width,
   width, margin, padding, border-radius, box-shadow, color, nearly every
   property this rule also sets), decided only by <link> load order --
   the same fragile pattern already caught and guarded for ::before/
   ::after below. Not flagged by the original three-agent audit (which
   only checked this pair's ::before/::after variant), found by directly
   re-verifying which office.css rule was actually winning after the
   naive strip, not assumed safe from the audit's table alone. */
.office-active .office-container {
    width: auto;
    max-width: none;
    margin: 0;
    padding: 0;
    border: none;
    border-radius: 0;
    background: none;
    box-shadow: none;
    color: var(--uo-ink);
}

/* office.css 1658 + 1665 — the inset rule and the corner ornament. */
.office-active .office-container::before,
.office-active .office-container::after {
    display: none;
    content: none;
}

/* office.css 1670 — the card's headings return to the shell's own faces. */
.office-container h1,
.office-container h2,
.office-container h3 {
    font-family: var(--uo-face-title);
    color: var(--uo-ink);
}

/* The prayed text is Cormorant at the 25px floor, not the skin's Georgia.
   First selector keeps .office-active for the same tie reason as the base
   rule above -- office.css's bare .office-container also sets `color`. */
.office-active .office-container,
.office-container p,
.office-container li {
    font-family: var(--uo-face-prayed);
    font-size: var(--uo-size-body);
    line-height: var(--uo-leading-body);
}

/* THE SAME FLOOR HAS TO REACH THE CONTENT CLASSES, NOT ONLY p/li.

   Bug found by Josh in the 2026-09-20 screenshot review ("body prayer text
   renders in inconsistent fonts/sizes across the page") and diagnosed from the
   cascade, not guessed: the rule immediately above matches `.office-container`,
   `p` and `li` and nothing else. Every unit of prayed text the Anglican lane
   actually emits is one of four classes -- `.component-text` (collects,
   canticles, antiphons, the Gloria, bare text), `.reading-text`,
   `.psalm-block`/`.psalm-stanza`/`.psalm-half-verse` -- see js/office-ui.js's
   bcpEmitBlock / bcpEmitReading / bcpEmitPsalmBlock / bcpEmitBare. css/office.css
   line 2378 sets font-family, font-size, line-height AND colour on exactly those
   classes at (0,3,0), and a SPECIFIED declaration beats an INHERITED one at any
   specificity, so the shell's face and size reached none of them. They kept the
   skin's Georgia at clamp(1.02rem, 1.12vw, 1.18rem) -- roughly 16-19px against
   the shell's 25px.

   What made it visible on the page rather than merely wrong everywhere: scripture
   readings are flowed into real <p> elements by formatScriptureAsFlow(), and
   those <p>s DO match the rule above. So a reading rendered in Cormorant at 25px
   sat directly above a collect in Georgia at ~17px, in the same gutter column, on
   the same screen. `.psalm-half-verse` additionally carries its own
   `color: var(--ink)` at (0,2,0) in office.css, the old skin's token rather than
   the shell's, so it is named here too.

   This is the demolition pass finishing its own job: office.css 1611/1632/1648/
   1658/1665/1670 were already undone by line further up this file; 2378 is the
   same kind of rule and was simply missed. Phase 6 deletes it outright.

   SCREEN ONLY, the same scoping decision and the same reason as the gutter grid
   below: css/office.css:819 sets its own print size (11pt) for .component-text,
   and at (0,4,0) this rule would beat that in print too and put 25px on paper.
   Print stays exactly as it was.

   CORRECTED 2026-09-25: this rule used to also list .office-container
   .psalm-verse, and the paragraph above used to cite office.css's print rule
   for .psalm-verse alongside .component-text. Both citations are gone now --
   confirmed via a repo-wide grep for the class, nothing anywhere renders
   .psalm-verse; the live psalm renderer emits .psalm-block/.psalm-stanza/
   .psalm-half-verse instead (already covered by the other selectors here). It
   was leftover from an earlier, superseded psalm-rendering approach. Dropped
   from this selector list (matched nothing) and from office.css's own print
   block (same reason), rather than left as dead weight pointing at itself. */
@media screen {
    .office-container .component-text,
    .office-container .reading-text,
    .office-container .psalm-block,
    .office-container .psalm-stanza,
    .office-container .psalm-stanza .psalm-half-verse {
        font-family: var(--uo-face-prayed);
        font-size: var(--uo-size-body);
        line-height: var(--uo-leading-body);
        color: var(--uo-ink);
    }
}

/* Rubrics keep their colour from the shell's tokens, which are contrast-checked
   against both grounds; the skin's values were not. */
.office-container .rubric,
.office-container .rubric-text {
    color: var(--uo-rubric);
}

/* The gutter grid, handoff doc §1: "a two-column grid — a 70px right-aligned
   mono gutter label and the text". Phase 3 follow-on to the renderBcpOffice()
   refactor (js/office-ui.js), which was what made per-block DOM grouping
   possible in the first place -- js/office-ui.js's block-emission helpers
   wrap each unit's own content in a .uo-block/.uo-gutter-label pair; this is
   purely the grid and typography, no content decision made here. Citation-
   bearing units (readings, psalms) show the actual reference; kind-labelled
   units (antiphon, canticle, collect, invitatory) show that word; everything
   else gets an empty gutter cell -- left-edge alignment stays one consistent
   column down the whole page regardless of which blocks carry a label and
   which don't, rather than the page text jumping left and right block to
   block.

   SCREEN ONLY, entirely: css/office.css:818 has its own @media print rule
   for .passage-reference (line number corrected 2026-09-25 -- the file has
   shifted since this comment was written; the rule itself is unchanged and
   still live), and the handoff doc's own hazards list (§10) warns
   print regresses silently if anything touching the grid forgets it. Rather
   than design print behaviour for the gutter now (a separate task, not what
   was asked for), this feature is scoped to the screen so print stays
   byte-identical to before it existed: the .uo-block wrapper collapses back
   to plain block flow (no grid columns) and .passage-reference prints
   exactly as its own existing print rule already handles it. */
@media screen {
    .office-container .uo-block {
        display: grid;
        grid-template-columns: var(--uo-gutter-width) 1fr;
        column-gap: 18px;
        align-items: start;
    }

    .office-container .uo-gutter-label {
        grid-column: 1;
        text-align: right;
        font-family: var(--uo-face-machine);
        font-size: 10px;
        letter-spacing: 0.16em;
        text-transform: uppercase;
        color: var(--uo-ink-quiet);
        padding-top: 0.3em;   /* nudges the label toward the body text's cap-height rather than its ascender */
    }

    .office-container .uo-block-body {
        grid-column: 2;
        min-width: 0;   /* lets long citations/reading text wrap inside the track instead of overflowing it */
    }

    /* The citation now lives in the gutter, not inline in the body -- the
       page is "the prayed text only, nothing else" (handoff doc §1). The
       element stays in the DOM; only the on-screen view hides it. */
    .office-container .uo-block-body .passage-reference {
        display: none;
    }
}

/* office.css 1611 — #main-content's own padding cleared the old 340px sidebar.
   The grid handles its own spacing; the sidebar clearance is restored in the
   sidebar rule below so the drawer still has room while it exists. */
/* Phase 4 (2026-09-24): the 340px legacy sidebar no longer sits beside the
   grid under the flag -- Office Settings is one drawer, opened from the
   keeping-place bar (js/office-drawer.js). No clearance to reserve. */
.office-active #main-content.app-primary-canvas,
.office-active #main-content.app-primary-canvas.sidebar-hidden {
    /* CORRECTED 2026-09-25: the "office.css:2270/2274" citation was already
       stale -- that region holds unrelated Roman Breviary content today.
       The real, current competitors are office.css's bare #main-content
       (padding:40px 40px 40px 340px, (1,0,0)) and .sidebar-hidden
       (padding-left:80px, (1,1,0)), neither !important -- already beaten
       on specificity alone at (1,2,0)/(1,3,0) once unscoped, and beaten
       twice over since every declaration here is !important regardless. */
    padding: 0 !important;
}

.office-active #main-content.app-primary-canvas.sidebar-hidden {
    padding-left: 0;
}


/* ---------------------------------------------------------------------------
   ONE THEME CONTROL, NOT TWO

   The legacy sidebar Dark Mode checkbox is hidden while the flag is on. The
   three-state Auto/Light/Dark control in the ordo line IS the dark toggle the
   governance requires on every screen, so removing the duplicate satisfies that
   rule rather than breaking it.

   Hidden HERE rather than from JS, after a JS hide failed twice: the navigator
   is rebuilt with innerHTML on every render, which wipes an inline style and
   leaves the box visible until an observer catches up — a race the user sees.
   The SECTION is targeted, not the label: office-ui.js writes
   style="display:flex" inline on that label, and an inline style beats any
   stylesheet rule. The section wrapper carries no inline style.

   CORRECTED 2026-09-25: the claim above ("Phase 4 deletes these sidebars
   and this rule goes with them") is stale. The four legacy sidebars ARE
   now deleted (Phase 6), but this rule was never about them -- it hides
   the shared office navigator's OWN built-in "Appearance" card
   (js/office-ui.js's renderSharedOfficeNavigation(), rendered whenever a
   lane config sets showAppearanceToggle: true -- still true for three
   lanes today, confirmed by grep, so this card still actively renders).
   That card carries its own legacy Dark Mode checkbox, superseded by the
   shell's own Auto/Light/Dark control same as the sidebars' copy was --
   a separate duplicate, not the same one. Still needed; not going away.
   Unscoped since office.css has no rule at all for this class (confirmed
   by grep) -- nothing to compete with regardless of specificity. */

.shared-office-nav-appearance-card {
    display: none;
}


/* ---------------------------------------------------------------------------
   PHASE 2 — THE THREE-COLUMN SHELL

   Ordo line across the top, rail / page / margin beneath, keeping-place bar at
   the foot. The rail carries a placeholder and the margin is empty until the
   envelope lands in Phase 3.
   --------------------------------------------------------------------------- */

/* SPECIFICITY — historical note, kept because it explains why the two rules
   below still carry more classes than a fresh file would use.
   Originally css/office.css carried a competing
   `body.office-active #main-content.app-primary-canvas { display: block; }`
   at (0,3,1), which beat this file's plainer first draft and silently
   defeated `display: grid`. Matched at equal specificity rather than reaching
   for `!important`, relying on this file loading after office.css to win on
   order. A `.dark-mode` sibling needed (0,4,1) to clear the same way.

   Phase 6 (2026-09-24) deleted both of those competing office.css rules
   outright, so THIS SPECIFIC fight is over -- confirmed by grep, not assumed.
   The extra classes below are no longer load-bearing for THIS pair and could
   be trimmed, but a blanket unscope of the whole file was tried the same day
   and reverted (see this file's own header comment) after it broke a
   DIFFERENT pair of rules (the entry-screen ones) whose office.css opponent
   is still very much alive. Left at full weight here too, for consistency,
   rather than trim rule-by-rule and invite the same class of mistake. */

#main-content.app-primary-canvas {
    display: grid;
    grid-template-columns: var(--uo-rail-width) minmax(0, 1fr) var(--uo-margin-width);
    /* Rows are sized against THIS element's own fixed height (css/office.css's
       `height: 100vh`, deliberately left untouched -- see the 2026-09-20
       comment on .uo-page below for why). This element itself does not
       scroll (overflow-y: hidden, just below): .uo-page is the one grid
       item that scrolls its own overflow, within whatever pixel height
       this row resolves to. */
    grid-template-rows: auto minmax(320px, auto) auto;
    grid-template-areas:
        "ordo ordo ordo"
        "rail page margin"
        "keep keep keep"
    ;
    column-gap: 0;
    color: var(--uo-ink);
    font-family: var(--uo-face-prayed);
    overflow-y: hidden;
}

/* Beats body.office-active.dark-mode #main-content (0,3,1) at (0,4,1). The
   parchment gradients are two stacked background layers, so this must set
   `background` wholesale rather than background-color. */
.office-active #main-content.app-primary-canvas {
    background: var(--uo-ground);
    position: relative;
}

/* ---------------------------------------------------------------------------
   PER-LANE GROUND IMAGERY -- 2026-09-24. HANDOFF.md §6 (Imagery): "Lane-
   appropriate... grounds", each "under a heavy veil at 0.4-0.55 opacity as
   texture, never as wallpaper" -- called for in the original design proposal,
   never built in any of Phases 1-5 (confirmed: #main-content's computed
   background was a flat solid colour, no image, checked directly against a
   live render before writing this). Found on Josh's third time raising it.

   A separate ::before layer, not filter:blur() on #main-content itself --
   that would blur the rail/page/margin text it contains, not just the
   background. ::before is position:absolute, which removes it from grid
   flow automatically (the CSS Grid spec excludes absolutely-positioned grid
   items from placement), so it cannot disturb the ordo/rail/page/margin/
   keeping grid areas above it; z-index keeps it under them.

   Keyed on data-uo-tradition (set by applyTraditionGround() in
   js/office-shell.js from the envelope's own env.tradition -- never guessed
   from the rendered page). A tradition with no rule below simply shows the
   plain --uo-ground colour underneath -- an honest, disclosed gap, not a
   placeholder or a borrowed image; see the ledger for which lanes still
   lack one and why. NEVER a Western Gothic image behind an Eastern lane --
   HANDOFF.md's own explicit rule.
   --------------------------------------------------------------------------- */

.office-active #main-content.app-primary-canvas::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    background-repeat: no-repeat;
    background-position: center center;
    background-size: cover;
    filter: blur(3px) saturate(0.7) brightness(0.42);
    opacity: 0.48;
}

#main-content.app-primary-canvas > .uo-ordo,
#main-content.app-primary-canvas > .uo-rail,
#main-content.app-primary-canvas > .uo-page,
#main-content.app-primary-canvas > .uo-margin,
#main-content.app-primary-canvas > .uo-keeping {
    position: relative;
    z-index: 1;
}

/* Anglican (ANG) -- images already in this repo, already vetted as
   Western/Gothic-appropriate (used elsewhere in this same app: rood-screen.png
   for #uo-threshold's own "night-prayer aesthetic", chartres-rose.png behind
   the entry screen). Night offices (the default palette) get the rood-screen
   archway; day offices (body.uo-day) get the Chartres rose window, matching
   HANDOFF.md 1b's own description of a stained-glass band under a heavy veil
   for Morning Prayer. */
.office-active #main-content.app-primary-canvas[data-uo-tradition="ANG"]::before {
    background-image: url('/images/rood-screen.png');
}
/* The rose window is far busier and more saturated than the rood-screen archway
   -- at the shared blur/opacity above it read as wallpaper, not texture, exactly
   what HANDOFF.md's own rule warns against. Heavier blur and a lower opacity
   specifically for this image, confirmed live before shipping (screenshot
   comparison, not guessed). */
body.uo-day.office-active #main-content.app-primary-canvas[data-uo-tradition="ANG"]::before {
    background-image: url('/images/chartres-rose.png');
    filter: blur(9px) saturate(0.35) brightness(0.7);
    /* ADJUSTED 2026-09-28, Josh's live report ("in light mode, the
       background has disappeared"): the rule was applying correctly
       (confirmed live, computed style unchanged) -- 0.22 opacity against
       this theme's near-white ground was simply too faint to register as
       present rather than absent. Raised to make it reliably perceptible;
       still well under the day-mode ceiling other lanes' own day rules use. */
    opacity: 0.38;
}

/* Coptic (OOR) -- Walters W.739, fol. 1r: a Coptic parchment fragment of the
   Book of Exodus, 8th century CE. CORRECTED 2026-09-24: first attributed in
   this file as "W.592, 1684 Arabic Gospels" and its license recorded as CC0
   -- both wrong, caught by reading the file's own embedded XMP/IPTC metadata
   rather than trusting the verbal description that came with it. The actual
   embedded rights field reads: "Licensed for use under Creative Commons
   Attribution-NonCommercial-ShareAlike 3.0 Unported... creativecommons.org/
   licenses/by-nc-sa/3.0/legalcode" -- CC BY-NC-SA, not CC0. Fine for this
   non-commercial app with attribution given (see images/CREDITS.md), but a
   real correctness catch, not a formality -- do not re-cite the old "CC0"
   claim anywhere. Supplied by Josh directly, cropped locally
   (images/coptic-walters-w739.jpg) to remove the black photography
   background and the pale binding-gutter strip down the original scan's
   left edge -- an uncropped scan would have shown a hard black border once
   veiled, exactly the failure Josh flagged.

   This image is busier and higher-contrast than either Anglican image --
   its own legible ink glyphs compete with live UI text far more readily
   than blurred architecture does, so it needed its own tuning pass, not a
   copy of the Anglican numbers. VERIFIED BY MEASUREMENT, not eyeballed, and
   iterated THREE times, the third time on Josh's own direct correction
   ("Agpeya just looks like a blur") after the first two passes had chased
   contrast numbers into invisibility -- a real lesson, not just a number
   to hit: pass 1 (blur 14px, opacity 0.24, brightness 0.32) measured 14:1,
   very safe, image essentially invisible; pass 2 (blur 16px, opacity 0.6,
   brightness 0.85) measured 10.1:1 median, more visible but still read as
   an undifferentiated wash, not recognizable manuscript texture -- both
   were measured correctly and both still failed the actual goal. Pass 3
   dropped blur from 16px to 6px (opacity/brightness otherwise similar) so
   the page's own text columns became genuinely perceptible, confirmed by
   looking at the live screenshot, not the contrast number alone; still
   safe at 12.4:1 median, 11.4:1 p95, 6.2:1 p99 (all clear WCAG AA's 4.5:1,
   AAA's 7:1 except the same single top-0.01% outlier pixel as before). */
.office-active #main-content.app-primary-canvas[data-uo-tradition="OOR"]::before {
    background-image: url('/images/coptic-walters-w739.jpg');
    filter: blur(6px) saturate(0.5) brightness(0.6);
    opacity: 0.5;
}
/* Day-mode override -- found 2026-09-24 while checking whether Byzantine's own
   day-mode "flat wash" bug (see that rule's own comment) also affected this
   lane. It did, and worse: Coptic's Morning Office (Prime), the real day
   office, measured only 4.87:1 median contrast (uncomfortably close to AA's
   4.5:1 floor, not the 12.4:1 the night-mode comment above cites) and the
   manuscript was essentially invisible on screen -- the exact "just a blur"
   failure, this time never caught because this lane was only ever verified
   against its own night offices. Same fix as Byzantine's day override:
   brightness raised toward the source image instead of darkened further,
   opacity lowered instead of relying on darkness to veil it; blur unchanged
   (6px already confirmed legible). Re-verified by screenshot against real
   Morning Office text. */
body.uo-day.office-active #main-content.app-primary-canvas[data-uo-tradition="OOR"]::before {
    filter: blur(6px) saturate(0.5) brightness(1.15);
    opacity: 0.44;
}

/* East Syriac (COE) -- Wikimedia Commons, "File:East Syriac Script Thaksa.jpg":
   an 18th-century Thaksa (service book) opening, Chaldean Syrian Church,
   Thrissur. Public domain, verified directly on the Commons file page (not
   assumed): tagged both CC-PD-Mark and PD-old-70-expired. Source:
   https://commons.wikimedia.org/wiki/File:East_Syriac_Script_Thaksa.jpg .
   Cropped locally (images/east-syriac-thaksa.jpg) to remove a thin black
   scan-edge vignette, not a wide photography backdrop like the Coptic scan
   needed. Low source resolution (624x386 before crop) -- acceptable here
   because the blur this treatment already applies erases fine pixelation
   regardless of source size; confirmed by looking at the actual live render,
   not assumed from the numbers alone. Tuned and VERIFIED BY MEASUREMENT the
   same way as Coptic (see that rule's own comment for the method) --
   numbers below are this image's own result, not copied from Coptic's. */
.office-active #main-content.app-primary-canvas[data-uo-tradition="COE"]::before {
    background-image: url('/images/east-syriac-thaksa.jpg');
    filter: blur(10px) saturate(0.5) brightness(0.6);
    opacity: 0.42;
}
/* Day-mode override -- found 2026-09-24 in the same sweep as the Coptic and
   Byzantine day-mode fixes (see those rules' own comments for the full
   account). Sapra (Morning Prayer), the real day office, measured a safer
   8.36:1 median than Coptic's day-mode failure, and the cross ornament stayed
   faintly perceptible rather than vanishing outright, but noticeably fainter
   than this same image's own night-mode rendering (already on record as
   "clearly recognizable"). Same fix: brightness raised toward the source
   image instead of darkened, opacity lowered; blur unchanged (10px already
   confirmed legible at night). Re-verified by screenshot against real Sapra
   text. */
body.uo-day.office-active #main-content.app-primary-canvas[data-uo-tradition="COE"]::before {
    filter: blur(10px) saturate(0.5) brightness(1.05);
    opacity: 0.3;
}

/* Byzantine/Eastern Orthodox (BYZC) -- Walters W.528, fol. 188r: the ornamented
   headpiece and zoomorphic initial opening the Gospel of John, Greek Gospel
   Book, early 13th century CE. DELIBERATELY an ornament-and-text page, not a
   figural image: UI_REDESIGN_HANDOFF.md's own correction to the original
   design proposal states plainly that an icon "is a venerated object, not a
   texture" and vetoes using one this way; W.528's own single surviving
   miniature (a different folio, an Evangelist portrait) was never a
   candidate for that same reason. Confirmed by looking at the actual page
   before cropping -- geometric interlace, floral ornament, two peacocks and
   the manuscript's own incipit text, nothing figural. License CC BY-NC-SA
   3.0 Unported, per that manuscript's own TEI description (same terms as
   the Coptic W.739 page -- both Walters manuscripts, not CC0). Cropped
   locally (images/byzantine-w528-headpiece.jpg) to remove the photography
   background.

   Corrected 2026-09-24: this selector originally read data-uo-tradition="EOR",
   copying the general ANG/LAT/EOR/OOR/COE sanctoral convention used elsewhere
   in the project. HorologionEngine's own authoritative constant (const
   TRADITION = 'BYZC' in js/horologion-engine.js) is 'BYZC', not 'EOR' -- fixed
   here, in images/CREDITS.md, and in the resume note/ledger, as part of
   building the Horologion envelope port. Blur dropped from an initial 14px to 6px, matching the same fix
   the Coptic rule needed after Josh flagged both as "just a blur" -- at
   14px the headpiece's own interlace and ornament weren't recognizable as
   anything, just a soft brown wash; at 6px the pattern and the two
   peacocks are genuinely visible. Contrast against the stand-in text: 11.8:1
   median, 9.5:1 p95 (both clear AAA), 4.0:1 p99 (essentially at AA's
   4.5:1 line). Re-verify by the same measurement method against REAL
   Horologion text once that lane ships a real envelope -- both the
   image's visibility and this p99 figure specifically. */
.office-active #main-content.app-primary-canvas[data-uo-tradition="BYZC"]::before {
    background-image: url('/images/byzantine-w528-headpiece.jpg');
    filter: blur(6px) saturate(0.55) brightness(0.55);
    opacity: 0.42;
}
/* Day-mode override, added once the Horologion lane shipped a real envelope
   and this rule could finally be checked against real Horologion text rather
   than a stand-in -- caught live 2026-09-24: the night-mode numbers above
   (darkened image, brightness 0.55) hold up well against the near-black night
   ground (#08070c), but the SAME darkened image composited at low opacity over
   the near-white day ground (#f2ebdf, body.shell-v2.uo-day) compresses toward
   a flat light wash -- contrast against day ink (#2a2118) still measured safe
   (6.8:1 median, 15:1 max, real screenshot of live Orthros text), but the
   image's own internal tonal range read as washed-out, not textured. Fixed the
   way ANG's own existing day override already solves it for the rose window:
   do not darken toward the night palette at all -- raise brightness back
   toward the source image's own tones and lower opacity instead, so veiling
   comes from transparency, not from crushing the image dark first. Re-verified
   by screenshot against real Orthros text (Great Lent, Tone 6, March 2026):
   the headpiece's interlace and the two peacocks are recognizable, not a wash. */
body.uo-day.office-active #main-content.app-primary-canvas[data-uo-tradition="BYZC"]::before {
    filter: blur(6px) saturate(0.5) brightness(1.05);
    opacity: 0.3;
}

#main-content > .uo-ordo    { grid-area: ordo; }
#main-content > .uo-rail    { grid-area: rail; }
#main-content > .uo-page    { grid-area: page; }
#main-content > .uo-margin  { grid-area: margin; }
#main-content > .uo-keeping { grid-area: keep; }

/* Hairlines and light do the separating — no borders around prayer. */
.uo-ordo {
    display: flex;
    align-items: center;
    gap: 18px;
    padding: 0 22px;
    min-height: var(--uo-ordo-height);
    border-bottom: 1px solid var(--uo-hairline);
    position: sticky;
    top: 0;
    z-index: 5;
    background: var(--uo-ground);
}

.uo-ordo-mark {
    font-family: var(--uo-face-title);
    font-size: 12px;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: var(--uo-accent);
    white-space: nowrap;
}

/* 2026-10-06: the wordmark is a button that returns to the main page; it keeps the plain-text look. */
button.uo-ordo-mark {
    background: none;
    border: 0;
    padding: 2px 0;
    margin: 0;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}

button.uo-ordo-mark:hover {
    text-decoration: underline;
    text-underline-offset: 4px;
}

button.uo-ordo-mark:focus-visible {
    outline: 2px solid var(--uo-accent);
    outline-offset: 3px;
}

.uo-ordo-day {
    flex: 1 1 auto;
    text-align: center;
    font-size: 17px;
    color: var(--uo-ink);
}

/* #office-mode-title and #office-context-actions are MOVED into the ordo line
   rather than hidden, so every existing affordance stays reachable in Phase 2.
   Phase 4 gives them their proper homes. */
.uo-ordo > #office-mode-title {
    flex-shrink: 0; /* 2026-10-06: the "My Parish" button joined the line; the title keeps its full name and the date line wraps instead */
    margin: 0;
    font-family: var(--uo-face-title);
    font-size: 15px;
    font-weight: 400;
    letter-spacing: 0.08em;
    color: var(--uo-ink-quiet);
    white-space: nowrap;
}

.uo-ordo > #office-context-actions {
    margin: 0;
    display: flex;
    gap: 8px;
}

/* The app's "Back to Modes" button is position:fixed at top:12px right:16px,
   written inline in index.html, so it floats over the ordo line and lands on
   top of the Auto/Light/Dark control. Under the flag it becomes part of the
   ordo row instead of floating above it: static, in flow, at the end of the
   line. Inline styles beat stylesheet rules, so each property set inline has to
   be named explicitly here — a blanket rule would not touch them.

   Phase 4 rehomes this button properly; this keeps it usable until then. */
#main-content.app-primary-canvas > .uo-ordo > button[onclick*="backToSplash"],
.uo-ordo button[onclick*="backToSplash"] {
    position: static;
    top: auto;
    right: auto;
    flex: 0 0 auto;
    margin: 0;
}

/* ── The rail ─────────────────────────────────────────────────────────────── */

/* FIXED 2026-09-25, Josh live: "The order....is longer than this....it needs
   to scroll with the content on the right." .uo-rail sits in the same grid
   row as .uo-page (grid-template-areas "rail page margin"), and that row's
   height resolves against #main-content's own fixed height, not the rail's
   content -- #main-content itself is overflow-y:hidden (see its comment
   above). .uo-page already solves this exact problem for itself with
   overflow-y:auto + min-height:0 (a grid item's default min-height:auto
   blocks it from shrinking to its track size, which is what overflow-y:auto
   needs something to scroll inside -- see that rule's own long comment for
   the full explanation, already worked out once this session). The rail
   never got the same treatment, so a long order list (East Syriac and
   Horologion both regularly exceed 30 items) simply overflowed past the
   row and was clipped by #main-content's overflow-y:hidden with no
   scrollbar anywhere -- confirmed live before fixing: 33 rail items on
   Church of the East Ramsha measured scrollHeight 1551 vs clientHeight 788,
   overflow-y computed as visible, scrollTop had no effect. Same fix, same
   reason, applied to the rail. The mobile media query below already gives
   .uo-rail its own overflow-y:auto (with an explicit max-height instead,
   since mobile collapses it to a capped top strip rather than a full-height
   column) -- this is the desktop-width equivalent of that. */
.uo-rail {
    padding: 26px 20px;
    border-right: 1px solid var(--uo-hairline);
    text-align: left;
    overflow-y: auto;
    min-height: 0;
}

/* Mobile-only collapse/expand tap target (see the @media (max-width:768px)
   block below for its real layout) -- the desktop column has no collapsed
   state, .uo-rail-head already tells a reader what this panel is. */
.uo-rail-summary-bar {
    display: none;
}

.uo-rail-head,
.uo-margin-head {
    font-family: var(--uo-face-machine);
    font-size: 10px;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: var(--uo-accent);
    margin-bottom: 18px;
}

.uo-rail-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.uo-rail-item {
    display: flex;
    align-items: baseline;
    gap: 10px;
    padding: 5px 0;
    font-size: 19px;
    line-height: 1.4;
    color: var(--uo-ink-quiet);
}

/* 2026-10-06 (Josh): rail entries are clickable and take the reader to that part of the office. */
.uo-rail-item[role="button"] {
    cursor: pointer;
}

.uo-rail-item[role="button"]:hover .uo-rail-label {
    color: var(--uo-ink);
    text-decoration: underline;
    text-underline-offset: 4px;
}

.uo-rail-item[role="button"]:focus-visible {
    outline: 2px solid var(--uo-accent);
    outline-offset: 2px;
}

.uo-rail-dot {
    flex: 0 0 auto;
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: var(--uo-ink-quiet);
    opacity: 0.55;
    transform: translateY(-3px);
}

/* Contract §6: the label is the tradition's own word. Nothing here
   capitalises, truncates or re-cases it. */
.uo-rail-label {
    min-width: 0;
}

.uo-rail-item.is-current {
    color: var(--uo-ink);
    font-family: var(--uo-face-title);
    font-size: 17px;
    letter-spacing: 0.04em;
}

.uo-rail-item.is-current .uo-rail-dot {
    width: 9px;
    height: 9px;
    opacity: 1;
    background: var(--uo-accent);
    box-shadow: 0 0 8px 1px rgba(201, 168, 76, 0.45);
}

.uo-rail-foot {
    margin-top: 22px;
    padding-top: 14px;
    border-top: 1px solid var(--uo-hairline);
    font-family: var(--uo-face-machine);
    font-size: 10px;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--uo-ink-quiet);
}

.uo-rail-placeholder {
    font-size: 15px;
    line-height: 1.6;
    font-style: italic;
    color: var(--uo-ink-quiet);
}

/* ── The page ─────────────────────────────────────────────────────────────── */

/* THE PAGE IS THE SCROLL CONTAINER, 2026-09-20. Superseded twice in one
   session before landing here -- read this before touching it again.

   ATTEMPT 1 (reverted): let #main-content itself grow past the viewport and
   scroll. Broke every long office completely -- #main-content is not
   actually the outermost box on this screen. #daily-office-section
   (css/office.css line ~181) wraps it as `position:fixed; inset:0;
   overflow:hidden`, a hard-clipped viewport-sized box by original design
   (it's what lets the sidebar clip cleanly on its slide-out animation).
   Content taller than that was simply clipped, no scrollbar anywhere.

   ATTEMPT 2 (this one, tested live end-to-end by Josh across a full Morning
   Prayer, ordo/keeping bars confirmed fixed top and bottom throughout, no
   text cut off): put the scroll on THIS element instead, and stop
   #main-content from scrolling at all (`overflow-y: hidden` added to
   `#main-content.app-primary-canvas` below). #main-content
   keeps its original fixed height (css/office.css's `height: 100vh`,
   untouched), so the grid's rows resolve to real pixel heights against it
   as normal, and THIS row's box -- whatever height that resolves to --
   becomes .uo-page's own internal viewport.

   AN EARLIER COMMENT HERE (now removed) recorded that an overflow context
   on this element had been tried once before and failed worse ("Confession
   of Sin was cut off mid-word rather than scrolling"). The missing piece,
   as far as can be told without knowing exactly what that earlier attempt
   set: `min-height: 0`. A CSS grid item's default `min-height` is `auto`,
   which means "at least as tall as my content" -- that default actively
   BLOCKS the item from shrinking down to its track's assigned size, which
   is what `overflow-y: auto` needs in order to have anything to scroll
   inside. Without `min-height: 0`, the item just keeps growing past its
   row instead, and depending on what the ANCESTOR box does with that
   overflow, the result is either the clipping the old comment described or
   the total-scroll-failure of attempt 1 above. With both `overflow-y: auto`
   and `min-height: 0` set together, AND #main-content no longer competing
   for the same scroll, this is the standard, well-established pattern for
   a scrollable middle panel between a fixed header and footer. */
.uo-page {
    padding: 34px 40px 48px;
    font-size: var(--uo-size-body);
    line-height: var(--uo-leading-body);
    overflow-y: auto;
    min-height: 0;
}

/* The measure is applied to the CONTENT, not the column: the page column keeps
   its grid width so the ordo line, the rule under the title and the fade at the
   foot still span it, while the text itself is held to a readable line and
   centred within. Capping the column instead would drag every one of those to
   the left edge. */
.uo-page > #office-display,
.uo-page > .saint-section,
.uo-page > .uo-ornament {
    max-width: var(--uo-measure);
    margin-inline: auto;
}

.uo-ornament {
    display: block;
    width: 84px;
    height: 12px;
    margin: 0 auto 26px;
    color: var(--uo-accent);
    opacity: 0.85;
}

/* ── The margin ───────────────────────────────────────────────────────────── */

.uo-margin {
    padding: 26px 20px;
    border-left: 1px solid var(--uo-hairline);
    font-size: 15px;
    line-height: 1.62;
    color: var(--uo-ink-quiet);
}

/* Contract §9/§11, handoff §2: overlay cards carry a red left-bar, diagnostic
   cards a gold one — the same left-bar-as-category convention, never a
   rounded card or a drop shadow (handoff §7's "no rounded cards" rule
   applies here same as on the page). */
.uo-margin-card {
    padding: 4px 0 4px 12px;
    margin-bottom: 16px;
    border-left: 3px solid var(--uo-hairline);
}

.uo-margin-card.is-overlay {
    border-left-color: var(--uo-rubric);
}

.uo-margin-card.is-diagnostic {
    border-left-color: var(--uo-accent);
}

.uo-margin-card-head {
    font-family: var(--uo-face-machine);
    font-size: 10px;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--uo-ink-quiet);
    margin-bottom: 4px;
}

.uo-margin-card-body {
    font-size: 15px;
    line-height: 1.5;
    color: var(--uo-ink);
}

/* Handoff §1: at most two cards visible; the rest collapse to this quiet
   line rather than a caption explaining the margin. */
.uo-margin-more {
    background: none;
    border: none;
    padding: 0;
    font-family: var(--uo-face-machine);
    font-size: 11px;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--uo-accent);
    cursor: pointer;
}

/* Collapsed cards stay in the DOM (never omitted) so print — which cannot
   run the click handler that reveals them on screen — still carries full
   overlay/diagnostic attribution. See the @media print override below. */
.uo-margin-card-collapsed {
    display: none;
}

/* ── The keeping-place bar ────────────────────────────────────────────────── */

.uo-keeping {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 18px;
    padding: 0 22px;
    min-height: var(--uo-keeping-height);
    border-top: 1px solid var(--uo-hairline);
    position: sticky;
    bottom: 0;
    z-index: 5;
    background: var(--uo-ground);
}

.uo-keeping-actions {
    display: flex;
    align-items: center;
    gap: 10px;
}

.uo-keeping-hint {
    font-family: var(--uo-face-machine);
    font-size: 10px;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--uo-ink-quiet);
}

/* ── Auto / Light / Dark ──────────────────────────────────────────────────── */

.uo-theme-control {
    display: flex;
    gap: 2px;
    flex: 0 0 auto;
}

.uo-theme-button {
    font-family: var(--uo-face-machine);
    font-size: 10px;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    padding: 4px 9px;
    background: transparent;
    color: var(--uo-ink-quiet);
    border: 1px solid transparent;
    cursor: pointer;
}

.uo-theme-button:hover {
    color: var(--uo-ink);
}

.uo-theme-button.is-on {
    color: var(--uo-accent);
    border-color: var(--uo-hairline);
}

.uo-theme-button:focus-visible {
    outline: 2px solid var(--uo-accent);
    outline-offset: 2px;
}

/* ── Mobile: rail becomes a top strip, margin moves inline ────────────────── */

@media (max-width: 768px) {
    #main-content.app-primary-canvas {
        grid-template-columns: minmax(0, 1fr);
        grid-template-rows: auto auto minmax(0, 1fr) auto auto;
        grid-template-areas:
            "ordo"
            "rail"
            "page"
            "margin"
            "keep";
    }

    /* MOBILE ORDO HEADER FIX, 2026-09-28: this row was built for one wide
       desktop line -- wordmark, office title, Book of Needs/Defaults, the
       liturgical day, Auto/Light/Dark, Back to Modes, roughly 1200px of
       content -- and never got a mobile counterpart. flex-wrap stayed
       'nowrap' at every width, so at 390px everything past the office
       title silently ran off the right edge of the screen: not clipped,
       not scrollable, just gone. Confirmed live (getBoundingClientRect()
       on every child): the theme control sat at x:838-998 and the ONLY
       way back to the mode picker (backToSplash) sat at x:1016-1194 --
       both fully off a 390px viewport, both unreachable on a phone.
       Fixed by wrapping the row and dropping the two purely decorative
       items -- the constant "The Universal Office" wordmark, which
       duplicates the office title sitting right next to it, and the
       liturgical day line, which the in-page dateline already repeats --
       so what's left is the title and the three real controls, wrapping
       onto short rows instead of running off the screen. */
    .uo-ordo-mark,
    .uo-ordo-day {
        display: none;
    }

    .uo-ordo {
        flex-wrap: wrap;
        gap: 8px;
        padding: 10px 18px;
        min-height: 0;
    }

    .uo-ordo > #office-mode-title {
        flex: 1 1 100%;
        font-size: 13px;
        white-space: normal;
    }

    /* "Top strip" only means anything if it's actually short: an office with
       a long step list (East Syriac, Horologion) has 20-30+ .uo-rail-item
       entries, and this media query was dead code until the specificity fix
       just above it, so nothing ever caught that an uncapped rail crowds the
       actual prayer text (.uo-page) down to a sliver on a long office.
       Capped, matching how .uo-page already scrolls its own overflow rather
       than growing past its row.

       FIXED 2026-09-28 (Josh, live, screenshot: "The site is completely
       unusable on mobile" -- the rail showing "Kyrie...Antiphon", near the
       end of Compline, while the content below sat at "Opening Blessing",
       the very start): this strip used to be independently touch-scrollable
       (overflow-y:auto + -webkit-overflow-scrolling:touch). On desktop the
       rail sits beside .uo-page as its own column, so a reader can freely
       scroll it by hand without losing their place -- the whole office text
       stays visible the entire time (the reasoning the 2026-09-25 fix that
       introduced this recorded, in .uo-rail's own base-rule comment above).
       On mobile the rail is stacked ABOVE the content instead, filling the
       top third of a small screen exactly where a thumb naturally lands to
       swipe up and read on -- an ordinary reading swipe that starts on the
       strip scrolled the RAIL, not the page, and nothing ever scrolled it
       back: the strip was left showing wherever the reader's thumb had
       carried it, silently disconnected from the actual content underneath,
       with no way back except manually swiping the strip itself back up.
       overflow-y:hidden still fully supports updateRailCurrent()'s own
       programmatic scrollIntoView() call (confirmed live: an element does
       not need a visible scrollbar or user-input-driven scroll for
       Element.scrollTop / scrollIntoView() to work -- only overflow:visible
       blocks it) -- so the current item still always scrolls into view
       within the capped strip exactly as before, driven only by the real
       content position. What's removed is solely the reader's ability to
       drag the strip somewhere else by hand on a touch device, which is
       the accidental trap, not a feature worth keeping at this width.

       COLLAPSED BY DEFAULT, 2026-09-28 (Josh: "the area where the user
       could read the prayer... was so tiny, no one would use it... We
       need to maximize the prayer window, with options and navigational
       layers... taking up very little room unless necessary for a task").
       Measured before touching anything, on a 390x844 viewport: the rail
       alone was consuming 316px (37% of the screen) permanently, on
       every single office, whether or not the reader ever looked at it --
       the header (106px) and this strip together left only 322px (38%)
       for the actual prayer text, and that 38% didn't even start until
       the SECOND screen's worth of scrolling (the first screen was pure
       chrome, no prayer text at all). Collapsed to a single-line summary
       bar (below) showing the current position -- the same tracking this
       rail already computes for the desktop column and the old capped
       strip, just displayed compactly -- and expands to the full list,
       exactly like the old strip, only when the reader actively taps it.
       Nothing about the tracking logic changes; only when the list is
       visible does. */
    .uo-rail {
        border-right: none;
        border-bottom: 1px solid var(--uo-hairline);
        padding: 0;
        max-height: 52px;
        overflow-y: hidden;
        transition: max-height 0.22s ease;
    }

    .uo-rail.is-expanded {
        max-height: 65vh;
        overflow-y: auto;
        -webkit-overflow-scrolling: touch;
    }

    /* The tap target: always visible, in both states -- doubles as the
       "what am I currently reading" glance and the expand/collapse
       control, so there's exactly one thing to learn, not two. Hidden
       entirely above this width -- the desktop column has no collapsed
       state at all, .uo-rail-head plays this role there already. */
    .uo-rail-summary-bar {
        display: flex;
        align-items: center;
        gap: 10px;
        padding: 14px 18px;
        min-height: 24px;
        cursor: pointer;
        -webkit-tap-highlight-color: transparent;
        user-select: none;
    }

    .uo-rail-summary-label {
        flex: 1 1 auto;
        min-width: 0;
        /* Long titles wrap (to two lines) instead of being cut off with an ellipsis. */
        white-space: normal;
        line-height: 1.25;
        font-family: var(--uo-face-title);
        font-size: 15px;
        color: var(--uo-ink);
    }

    .uo-rail-summary-count {
        flex: 0 0 auto;
        font-family: var(--uo-face-machine);
        font-size: 10px;
        letter-spacing: 0.14em;
        text-transform: uppercase;
        color: var(--uo-ink-quiet);
    }

    .uo-rail-summary-chevron {
        flex: 0 0 auto;
        width: 9px;
        height: 9px;
        border-right: 1.5px solid var(--uo-accent);
        border-bottom: 1.5px solid var(--uo-accent);
        transform: rotate(45deg);
        transition: transform 0.22s ease;
        margin-top: -4px;
    }

    .uo-rail.is-expanded .uo-rail-summary-chevron {
        transform: rotate(-135deg);
        margin-top: 4px;
    }

    /* The full list -- identical markup and tracking to the desktop
       column and the old always-open strip, just hidden until expanded.
       .uo-rail-placeholder is excluded: it's already a short one-line
       message for the rare no-blocks-emitted case, nothing to collapse. */
    .uo-rail-head,
    .uo-rail-list,
    .uo-rail-foot {
        display: none;
    }

    .uo-rail.is-expanded .uo-rail-head,
    .uo-rail.is-expanded .uo-rail-list,
    .uo-rail.is-expanded .uo-rail-foot {
        display: block;
    }

    .uo-rail-placeholder {
        padding: 0 18px 14px;
    }

    .uo-rail.is-expanded .uo-rail-head,
    .uo-rail.is-expanded .uo-rail-list,
    .uo-rail.is-expanded .uo-rail-foot {
        padding-left: 18px;
        padding-right: 18px;
    }

    .uo-rail.is-expanded .uo-rail-foot {
        padding-bottom: 14px;
    }

    .uo-margin {
        border-left: none;
        border-top: 1px solid var(--uo-hairline);
    }

    /* Most offices emit no overlay/diagnostic cards, and
       renderMarginFromEnvelope() leaves the element genuinely empty
       (margin.textContent = '') in that case -- so the reserved
       border/padding is pure wasted vertical space on a phone screen
       when there is nothing in it to separate from the page above. */
    .uo-margin:empty {
        border-top: none;
        padding: 0;
        margin: 0;
    }

    .uo-page { padding: 24px 20px 36px; }

    /* On a phone the viewport is already narrower than the measure; letting the
       cap apply there would leave the text floating in a column narrower than
       the screen. */
    .uo-page > #office-display,
    .uo-page > .saint-section,
    .uo-page > .uo-ornament {
        max-width: none;
    }

    /* The gutter grid deliberately does NOT port to mobile as-is (handoff doc
       §10: mobile is a named hazard, "rewrite deliberately; do not port the
       old rules" -- the same principle that already collapsed the rail to a
       top strip applies here). A 70px reserved column plus its gap eats a
       disproportionate share of an already-narrow phone viewport, so the
       gutter label stacks ABOVE its block's text instead of beside it,
       matching how the rail already collapses rather than force-fitting a
       sidebar onto a phone screen. */
    .office-container .uo-block {
        display: block;
    }

    .office-container .uo-gutter-label {
        display: block;
        text-align: left;
        margin-bottom: 2px;
    }
}

/* ── Print: the page prints, the chrome does not ──────────────────────────── */

@media print {
    #main-content.app-primary-canvas {
        display: block;
        min-height: 0;
        background: #fff;
        color: #000;
    }

    .uo-ordo,
    .uo-rail,
    .uo-keeping {
        display: none;
    }

    /* The margin is NOT blanket-hidden: overlay attribution and diagnostics
       must print as footnotes, because a printed office carrying a borrowed
       prayer with its attribution stripped breaks the governance rule on paper,
       where it cannot be recovered. Populated for the Anglican lane as of
       2026-09-16 (js/anglican-envelope.js); the other three lanes still emit
       no envelope, so this rule still has nothing to act on for them. */
    .uo-margin {
        border: none;
        padding: 0;
        margin-top: 18px;
        font-size: 11px;
        color: #000;
    }

    /* On screen, a card past the first two is collapsed behind a click. Print
       has no click, so every card prints regardless of on-screen collapse —
       the expand control itself is the one thing that must not print, since
       it is a UI affordance with nothing left for it to do on paper. */
    .uo-margin-card-collapsed {
        display: block;
    }

    .uo-margin-more {
        display: none;
    }
}


/* ---------------------------------------------------------------------------
   PHASE 2 BEGINS BELOW THIS LINE.

   The three-column grid, the two bars, the type scale and the Auto/Light/Dark
   control. Nothing here yet — Phase 1's acceptance criterion is that the app is
   byte-identical in behaviour with the flag off and does not crash with it on.
   --------------------------------------------------------------------------- */


/* ---------------------------------------------------------------------------
   PHASE 4 — OFFICE SETTINGS, ONE DRAWER
   Target: documentation/design/screens/1c-threshold-ordo-drawer.png.
   Governance: UI_REDESIGN_HANDOFF.md §3.1 (heading "Office Settings",
   section I "The Ordo"), §5, §3.7 (BCP Only at the foot).

   CLOSED 2026-09-25, Phase 6 sidebar-deletion refactor: the four legacy
   sidebars this comment used to describe (kept permanently in the DOM,
   hidden by two rules that lived here) are gone. Getting there took three
   real prerequisites, each confirmed by reading the actual code before
   touching it, not assumed: (1) mode detection was rebuilt on
   js/office-ui.js's own selectedMode instead of reading a mode-hidden
   class off these exact divs (js/office-shell.js's currentLane(),
   js/office-drawer.js's currentModeKey()); (2) renderSharedOfficeNavigation()
   was repointed at one neutral host instead of injecting into whichever
   sidebar matched the active mode; (3) every remaining real control
   (Rite, Gospel placement, Creed, Marian element/position, East Syriac's
   Cathedral/Monastic use, Horologion's calendar/display-depth selects,
   the Diagnostics toggle, the BCP-Only-Mode gate on the Marian rows, and
   more) was relocated into that same neutral host as static HTML, so
   every id/name lookup that used to find them inside a legacy sidebar
   keeps finding them, just relocated. Only then were the sidebars and
   #sidebar-toggle deleted from index.html, along with the two hiding
   rules that used to sit here. See RESUME_PROJECT_NOTE.md and the
   ui:phase6-retire-old-skin row in audit-ledger.html for the full staged
   account.
   --------------------------------------------------------------------------- */

.uo-drawer-open {
    background: transparent;
    border: 0;
    padding: 4px 0;
    color: var(--uo-accent);
    font-family: var(--uo-face-title);
    font-size: 12px;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    cursor: pointer;
}
.uo-drawer-open:hover,
.uo-drawer-open:focus-visible { color: var(--uo-ink); }

dialog.uo-drawer {
    position: fixed;
    inset: 0 0 0 auto;
    margin: 0;
    width: 430px;
    max-width: 100vw;
    height: 100%;
    max-height: 100%;
    padding: 0;
    box-sizing: border-box;
    display: none;
    flex-direction: column;
    background: var(--uo-ground-raised);
    color: var(--uo-ink);
    border: 0;
    border-left: 1px solid rgba(201, 168, 76, 0.28);
    font-family: var(--uo-face-prayed);
}
dialog.uo-drawer[open] { display: flex; }
dialog.uo-drawer::backdrop { background: rgba(4, 3, 8, 0.45); }
body.uo-day dialog.uo-drawer::backdrop { background: rgba(42, 33, 24, 0.18); }

.uo-drawer-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    padding: 40px 36px 8px;
}
.uo-drawer-title {
    margin: 0;
    font-family: var(--uo-face-machine);
    font-weight: 400;
    font-size: 11px;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: var(--uo-accent);
}
.uo-drawer-close {
    background: transparent;
    border: 0;
    color: var(--uo-ink-quiet);
    font-size: 26px;
    line-height: 1;
    cursor: pointer;
}

.uo-drawer-body {
    flex: 1 1 auto;
    overflow-y: auto;
    padding: 8px 36px 24px;
}

.uo-drawer-section-head {
    margin: 26px 0 12px;
    font-family: var(--uo-face-machine);
    font-size: 10px;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: var(--uo-ink-quiet);
}

/* I · The Ordo */
.uo-drawer-stepper {
    display: flex;
    align-items: center;
    justify-content: space-between;
    border: 1px solid rgba(201, 168, 76, 0.28);
    height: 52px;
    padding: 0 6px;
}
.uo-drawer-step {
    background: transparent;
    border: 0;
    width: 40px;
    height: 40px;
    color: var(--uo-ink-quiet);
    font-size: 14px;
    cursor: pointer;
}
.uo-drawer-step:hover { color: var(--uo-ink); }
.uo-drawer-date {
    font-family: var(--uo-face-title);
    font-variant: small-caps;
    font-size: 18px;
    letter-spacing: 0.06em;
    color: var(--uo-ink);
}
.uo-drawer-dayline {
    margin-top: 12px;
    font-size: 17px;
    color: var(--uo-ink-quiet);
}
.uo-drawer-dot {
    display: inline-block;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    margin-right: 8px;
    vertical-align: 2px;
}
.uo-drawer-fact {
    display: flex;
    justify-content: space-between;
    gap: 16px;
    margin-top: 8px;
    font-size: 16px;
}
.uo-drawer-fact-value { color: var(--uo-ink-quiet); text-align: right; }
.uo-drawer-datetools {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-top: 10px;
}
.uo-drawer-link {
    background: transparent;
    border: 0;
    padding: 4px 0;
    color: var(--uo-accent);
    font-family: var(--uo-face-machine);
    font-size: 10px;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    cursor: pointer;
}
.uo-drawer-datepick {
    background: transparent;
    border: 0;
    border-bottom: 1px solid var(--uo-hairline);
    color: var(--uo-ink-quiet);
    font-family: var(--uo-face-machine);
    font-size: 11px;
    color-scheme: dark;
}
body.uo-day .uo-drawer-datepick { color-scheme: light; }

/* II · Which office */
.uo-drawer-offices {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
}
.uo-drawer-office {
    min-height: 46px;
    padding: 8px 14px;
    text-align: left;
    background: transparent;
    border: 1px solid rgba(201, 168, 76, 0.2);
    color: var(--uo-ink-quiet);
    font-family: var(--uo-face-prayed);
    font-size: 17px;
    cursor: pointer;
}
.uo-drawer-office:hover { color: var(--uo-ink); }
.uo-drawer-office[aria-checked="true"] {
    border-color: rgba(201, 168, 76, 0.7);
    background: rgba(201, 168, 76, 0.1);
    color: var(--uo-ink);
}
.uo-drawer-office:disabled { opacity: 0.35; cursor: default; }

/* III · How you keep it — label left, value right, no boxes */
.uo-drawer-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    min-height: 34px;
    font-size: 18px;
    color: var(--uo-ink);
    cursor: pointer;
}
.uo-drawer-row-value {
    appearance: none;
    -webkit-appearance: none;
    background: transparent;
    border: 0;
    padding: 4px 0;
    text-align: right;
    text-align-last: right;
    color: var(--uo-ink);
    font-family: var(--uo-face-title);
    font-size: 13px;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    cursor: pointer;
    /* FIXED 2026-09-28, Josh's report ("menu options should not be cut off"):
       a long selected option (e.g. Horologion's Calendar row, "New Calendar
       (Revised Julian Fixed Feasts)") simply hard-clipped at the old 60%
       cap with no overflow indicator at all. Widened the cap and added an
       ellipsis so long values degrade visibly instead of vanishing
       mid-word; short values (most rows) are unaffected either way. */
    max-width: 72%;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.uo-drawer-row-value option {
    background: var(--uo-ground-raised);
    color: var(--uo-ink);
    text-transform: none;
}
.uo-drawer-row-value:focus-visible,
.uo-drawer-office:focus-visible,
.uo-drawer-step:focus-visible,
.uo-drawer-link:focus-visible {
    outline: 1px solid var(--uo-accent);
    outline-offset: 2px;
}
.uo-drawer-rows > .uo-drawer-link { margin-top: 6px; }

.uo-drawer-borrowed {
    margin-top: 14px;
    padding-top: 12px;
    border-top: 1px solid var(--uo-hairline);
}
.uo-drawer-borrowed-row { cursor: default; }
.uo-drawer-borrowed-count {
    font-family: var(--uo-face-title);
    font-size: 13px;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--uo-accent);
}
.uo-drawer-borrowed-list {
    margin: 6px 0 0;
    font-style: italic;
    font-size: 15px;
    line-height: 1.6;
    color: var(--uo-ink-quiet);
}

.uo-drawer-details { margin-top: 12px; }
.uo-drawer-details > summary {
    font-family: var(--uo-face-machine);
    font-size: 10px;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--uo-accent);
    cursor: pointer;
    padding: 4px 0;
}
.uo-drawer-further {
    margin-top: 18px;
    padding-top: 12px;
    border-top: 1px solid var(--uo-hairline);
}

/* Moved legacy controls: flattened into the drawer's language. Their inline
   tooltips (.info-btn) and ids are untouched. */
.uo-drawer-moved {
    padding: 6px 0 4px;
    font-size: 16px;
    color: var(--uo-ink);
}
.uo-drawer-moved .setting-group,
.uo-drawer-moved .nested-group {
    background: transparent;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    padding: 0;
    margin: 12px 0 0;
}
/* These three selectors are the one place in the drawer where the moved
   content (.setting-group/.nested-group nodes relocated wholesale from the
   deleted legacy sidebars, still real office.css classes elsewhere in the
   app) shares a class name with a still-live office.css rule
   (.setting-group strong/label, .setting-group label input[type=checkbox]).
   A bare-.uo-drawer-moved strip lands at an exact specificity tie for
   strong/label and an outright LOSS for the checkbox (office.css's
   .setting-group label input[type=checkbox] is one element ahead) --
   confirmed by direct specificity comparison, not assumed. Anchored on
   dialog.uo-drawer instead of body.shell-v2: real, already-used selector
   for the drawer element itself (see the base rule above), giving a full
   specificity margin over every known competitor rather than a load-order
   tie. 2026-09-25, unscoping Stage 3. */
dialog.uo-drawer .uo-drawer-moved strong {
    display: block;
    margin: 10px 0 4px;
    font-family: var(--uo-face-machine);
    font-weight: 400;
    font-size: 10px !important;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--uo-ink-quiet) !important;
}
dialog.uo-drawer .uo-drawer-moved label {
    display: block;
    margin: 6px 0;
    line-height: 1.45;
    color: var(--uo-ink);
    font-family: var(--uo-face-prayed);
    font-size: 16px;
    cursor: pointer;
}
dialog.uo-drawer .uo-drawer-moved input[type="checkbox"],
dialog.uo-drawer .uo-drawer-foot input[type="checkbox"] {
    accent-color: #c9a84c;
    margin-right: 8px;
}

/* BCP Only at the foot, a single toggle */
.uo-drawer-foot {
    padding: 18px 36px 26px;
    border-top: 1px solid var(--uo-hairline);
}
.uo-drawer-foot[hidden] { display: none; }
.uo-drawer-bcp-only label {
    display: flex;
    align-items: center;
    justify-content: space-between;
    font-family: var(--uo-face-machine);
    font-size: 11px;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--uo-ink-quiet);
    cursor: pointer;
}
.uo-drawer-bcp-only input[type="checkbox"] {
    order: 2;
    appearance: none;
    -webkit-appearance: none;
    width: 44px;
    height: 22px;
    margin: 0;
    border: 1px solid rgba(201, 168, 76, 0.45);
    border-radius: 11px;
    position: relative;
    cursor: pointer;
}
.uo-drawer-bcp-only input[type="checkbox"]::before {
    content: "";
    position: absolute;
    top: 3px;
    left: 4px;
    width: 14px;
    height: 14px;
    border-radius: 50%;
    background: rgba(201, 168, 76, 0.55);
    transition: left 0.15s;
}
.uo-drawer-bcp-only input[type="checkbox"]:checked::before {
    left: 24px;
    background: #c9a84c;
}

/* CORRECTED 2026-09-25: no such office.css rule was found -- the "~1849"
   citation was stale (that line is an unrelated saint-card rule today). Every
   declaration below is !important anyway, so it wins regardless of whether a
   competitor exists; kept !important as defensive belt-and-suspenders rather
   than removed, since a future office.css change could reintroduce one. */
body.office-active dialog.uo-drawer select.uo-drawer-row-value,
body.office-active dialog.uo-drawer input.uo-drawer-datepick {
    background: transparent !important;
    color: var(--uo-ink) !important;
    border: 0 !important;
    border-radius: 0 !important;
    box-shadow: none !important;
    font-family: var(--uo-face-title) !important;
}
body.office-active dialog.uo-drawer input.uo-drawer-datepick {
    border-bottom: 1px solid var(--uo-hairline) !important;
    font-family: var(--uo-face-machine) !important;
    color: var(--uo-ink-quiet) !important;
}
body.office-active dialog.uo-drawer button {
    border-radius: 0 !important;
    box-shadow: none !important;
}
body.office-active dialog.uo-drawer .uo-drawer-title {
    border: 0 !important;
    padding: 0 !important;
}

@media (max-width: 768px) {
    dialog.uo-drawer { width: 100vw; }
    .uo-drawer-head,
    .uo-drawer-body,
    .uo-drawer-foot { padding-left: 22px; padding-right: 22px; }
}

@media print {
    dialog.uo-drawer { display: none !important; }
}


/* ===========================================================================
   ENTRY & UNIVERSAL SELECTOR SCREENS -- 2026-09-24, per Josh's direct request.

   #tradition-entry ("Where do you pray?") and #mode-selection's own
   #uo-threshold-grid ("Another office") had never been touched by this
   redesign -- Phase 1-5 all stayed "off the office screen nothing changes"
   (see the DEMOLITION comment far above). They still used the pre-redesign
   parchment card system (rounded cards, drop shadows, gold-gradient icon
   circles), so they were the one place in the app that still looked like a
   different product from the office view they lead into -- Josh's own words:
   "it looks very different from everything we've redesigned... very much
   out of place."

   SCOPE NOTE: this deliberately widens that "off the office screen" boundary
   for these two screens ONLY, on Josh's direct instruction. Nothing else
   outside the office screen (splash proper, Book of Needs, Bible Browser,
   admin) is touched.

   Restyled using the SAME flat, hairline-bordered component language
   .uo-drawer-office already established just above (III · How you keep it),
   not a new pattern -- per handoff §7's own governing rule: "no rounded
   cards, no drop shadows on the page, no borders around prayer. Hairlines
   and light do the separating."

   REGROUPED, in index.html: Book of Needs and Bible Browser are tools, not
   prayer traditions, and were previously indistinguishable cards in the same
   5-card grid as Daily Office/Coptic Agpeya/Church of the East -- Josh's own
   words, "the bible browser and book of needs do not answer the 'where do
   you pray' question... it needs to be regrouped." Split into a primary
   tradition grid (.app-mode-grid, now 3 cards) and a visibly smaller,
   secondary "tools" row (.app-mode-tools-row) below a hairline and a quiet
   label, so the two read as different categories at a glance rather than
   five equal choices.

   #tradition-entry keeps its existing family/tradition drill-down logic
   (Western -> Anglican/Catholic, Eastern -> COE/EO/OO, COE -> ACOE/ACE)
   completely untouched -- restyled only. Its own separate "Dark Mode"
   checkbox is hidden here (not deleted -- ?shell=v1 keeps it, unaffected):
   this screen now always renders in the night palette, the same choice
   already made and Josh-approved for #uo-threshold ("a control that visibly
   does nothing is worse than no control" -- 2026-09-22, see that screen's
   own comment above). #splash-bg is correspondingly forced to its dark
   treatment here too, slightly darker again, so the background doesn't
   fight the now-always-dark card sitting on top of it; !important matches
   the mobile breakpoint's own !important rule in css/office.css so this
   still wins there.
   =========================================================================== */

#splash-bg {
    filter: blur(3px) saturate(0.5) brightness(0.34) !important;
    opacity: 0.4 !important;
}

#tradition-entry.app-tradition-entry,
#mode-selection.app-mode-shell {
    background: linear-gradient(180deg, rgba(8, 7, 12, 0.94), rgba(11, 9, 18, 0.90));
    border: 1px solid var(--uo-hairline);
    border-radius: 0;
    box-shadow: none;
    color: var(--uo-ink);
}

#tradition-entry .app-dark-toggle {
    display: none;
}

.app-entry-kicker,
.app-mode-kicker {
    font-family: var(--uo-face-machine);
    color: var(--uo-accent);
}

#tradition-entry h1,
#mode-selection.app-mode-shell h1 {
    font-family: var(--uo-face-title);
    color: var(--uo-ink);
    text-shadow: none;
}

.app-entry-lede,
#mode-selection.app-mode-shell p {
    font-family: var(--uo-face-prayed);
    color: var(--uo-ink-quiet);
}

.app-entry-panel-title {
    font-family: var(--uo-face-machine);
    color: var(--uo-accent);
}

/* Cards: flat, hairline-bordered, no shadow, no rounded corners -- the same
   treatment .uo-drawer-office already uses for "II . Which office". */
.app-entry-family-card,
.app-entry-tradition-card,
#mode-selection .mode-btn.app-mode-card {
    background: transparent;
    border: 1px solid rgba(201, 168, 76, 0.22);
    border-radius: 0;
    box-shadow: none;
    color: var(--uo-ink-quiet);
}
.app-entry-family-card:hover,
.app-entry-family-card:focus-visible,
.app-entry-tradition-card:hover,
.app-entry-tradition-card:focus-visible,
#mode-selection .mode-btn.app-mode-card:hover,
#mode-selection .mode-btn.app-mode-card:focus-visible {
    background: rgba(201, 168, 76, 0.08);
    border-color: rgba(201, 168, 76, 0.6);
    box-shadow: none;
    transform: none;
    color: var(--uo-ink);
}

.app-entry-family-icon,
.app-entry-tradition-code,
.app-mode-icon {
    background: none;
    border: 1px solid rgba(201, 168, 76, 0.4);
    color: var(--uo-accent);
    box-shadow: none;
}

.app-entry-family-card strong,
.app-entry-tradition-card strong,
.app-mode-title {
    font-family: var(--uo-face-title);
    color: var(--uo-ink);
}
.app-entry-family-card small,
.app-entry-tradition-card small,
.app-mode-subtitle {
    font-family: var(--uo-face-prayed);
    color: var(--uo-ink-quiet);
}

.app-entry-tradition-card.is-disabled,
.app-entry-tradition-card:disabled {
    opacity: 0.4;
    filter: none;
}

.app-entry-back {
    border: 1px solid rgba(201, 168, 76, 0.3);
    border-radius: 0;
    background: transparent;
    color: var(--uo-accent);
    font-family: var(--uo-face-prayed);
}
.app-entry-back:hover,
.app-entry-back:focus-visible {
    background: rgba(201, 168, 76, 0.08);
    border-color: rgba(201, 168, 76, 0.6);
    color: var(--uo-ink);
    outline: none;
}

/* The regrouped tools row -- deliberately smaller and unornamented next to
   the tradition grid above it, so the two read as different categories at a
   glance, not five equal choices. */
.app-mode-section-label {
    font-family: var(--uo-face-machine);
    font-size: 10px;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--uo-ink-quiet);
    margin: 0 0 10px;
}
/* Only the label AFTER a tradition grid gets the separating hairline -- the
   first one (right under the <h1>) would otherwise draw a stray line
   directly beneath the title. */
.app-mode-grid + .app-mode-section-label {
    margin-top: 22px;
    padding-top: 18px;
    border-top: 1px solid var(--uo-hairline);
}

.app-mode-tools-row {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
}
.app-mode-tool-btn {
    background: transparent;
    border: 1px solid rgba(201, 168, 76, 0.16);
    border-radius: 0;
    padding: 10px 16px;
    color: var(--uo-ink-quiet);
    font-family: var(--uo-face-prayed);
    font-size: 0.95rem;
    cursor: pointer;
    text-align: left;
}
.app-mode-tool-btn:hover,
.app-mode-tool-btn:focus-visible {
    border-color: rgba(201, 168, 76, 0.5);
    outline: none;
}
.app-mode-tool-btn strong {
    display: block;
    font-family: var(--uo-face-title);
    font-weight: 600;
    font-size: 0.95rem;
    color: var(--uo-ink);
    margin-bottom: 2px;
}
.app-mode-tool-btn:hover strong,
.app-mode-tool-btn:focus-visible strong {
    color: var(--uo-accent);
}
.app-mode-tool-btn small {
    display: block;
    color: var(--uo-ink-quiet);
    font-size: 0.82rem;
}

@media (max-width: 760px) {
    .app-mode-tools-row {
        flex-direction: column;
    }
}

/* ---------------------------------------------------------------------------
   Entry/threshold screens: typography + sizing port (Stage 1 of 6).

   The rules above only ever recolored these screens (font-family/color) --
   they never carried the actual type sizing office.css still supplies
   (font-size/line-height/letter-spacing/margin/text-transform). Ported here
   verbatim from office.css so the old rules can be deleted later (Stage 4)
   without losing sizing. Genuinely unscoped from the start (no body.shell-v2
   prefix) -- shell-v2 is permanent, and this file's own Group 6 rules above
   are being unscoped in the same pass (Stage 6). See
   documentation/entry-screen-unscoping (this session's plan) /
   RESUME_PROJECT_NOTE.md / audit-ledger.html's ui:phase6-retire-old-skin row
   for the full staged account. */

.app-entry-kicker,
.app-mode-kicker {
    font-size: 0.76rem;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    margin-bottom: 8px;
}

#tradition-entry h1 {
    font-size: clamp(2.35rem, 6vw, 5rem);
    font-weight: 400;
    line-height: 0.98;
    letter-spacing: 0.008em;
    margin: 0 0 10px;
}

#mode-selection.app-mode-shell h1 {
    font-size: clamp(2.65rem, 6.5vw, 5.5rem);
    font-weight: 400;
    line-height: 0.96;
    letter-spacing: 0.008em;
    margin: 0 0 10px;
}

.app-entry-lede {
    font-size: clamp(1rem, 1.6vw, 1.24rem);
    line-height: 1.35;
    letter-spacing: 0.025em;
    margin: 0 0 28px;
    max-width: 640px;
}

#mode-selection.app-mode-shell p {
    font-size: clamp(1rem, 1.6vw, 1.28rem);
    line-height: 1.35;
    letter-spacing: 0.025em;
    text-transform: none;
    margin: 0 0 28px;
    max-width: 560px;
}

.app-entry-panel-title {
    font-size: 0.76rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
}

.app-entry-family-card strong,
.app-entry-tradition-card strong {
    display: block;
    font-size: 1.14rem;
    line-height: 1.15;
    font-weight: 600;
}

.app-mode-title {
    display: block;
    font-size: 1.15rem;
    line-height: 1.15;
    font-weight: 600;
}

.app-entry-family-card small,
.app-entry-tradition-card small {
    display: block;
    margin-top: 4px;
    font-size: 0.9rem;
    line-height: 1.25;
}

.app-mode-subtitle {
    display: block;
    font-size: 0.90rem;
    line-height: 1.25;
}

.app-entry-family-card,
.app-entry-tradition-card,
#mode-selection .mode-btn.app-mode-card {
    cursor: pointer;
    text-align: left;
}

#mode-selection .mode-btn.app-mode-card {
    text-transform: none;
    letter-spacing: 0;
}

.app-entry-tradition-card.is-disabled,
.app-entry-tradition-card:disabled {
    cursor: not-allowed;
}

.app-entry-back {
    font-size: 0.84rem;
    padding: 7px 13px;
    cursor: pointer;
}

/* ---------------------------------------------------------------------------
   Entry/threshold screens: card-grid + icon layout port (Stage 2 of 6).

   The second real gap Stage 1's own header comment names: nothing above
   ever rebuilt these screens' actual grid layout, only recolored it. Ported
   verbatim from office.css, with one deliberate, disclosed exception:
   border-radius:18px on the card containers is DROPPED (Group 6's own
   1584-1591-region rule already sets border-radius:0, confirmed live via
   getComputedStyle as this screen's real, current appearance at every
   viewport -- porting 18px would introduce rounded corners that don't
   exist today).

   UPDATED 2026-09-25: the two hairline border-tops below were ported as
   the literal office.css brown (rgba(103,58,31,0.12)) when this stage
   first landed, on the reasoning that var(--uo-hairline) resolves to a
   visibly different gold on this always-night-palette screen and a
   faithful port shouldn't sneak in a color change. Josh reviewed that
   finding and asked for the modernization: these are now
   var(--uo-hairline), matching every other hairline on this screen and
   the governing rule already stated at the top of this section ("no
   rounded cards, no drop shadows on the page, no borders around prayer.
   Hairlines and light do the separating"). Verified live: both grids'
   hairlines now render gold, consistent with the rest of the screen;
   nothing else on the page depended on the old brown value (confirmed by
   grep -- these were its only two occurrences in this file). */

.app-mode-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 12px;
    padding-top: 22px;
    border-top: 1px solid var(--uo-hairline);
}

#mode-selection .mode-btn.app-mode-card {
    display: grid;
    grid-template-columns: 46px minmax(0, 1fr);
    gap: 13px;
    align-items: center;
    width: 100%;
    min-height: 86px;
    margin: 0;
    padding: 16px 18px;
}

.app-mode-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    border-radius: 999px;
    font-family: Georgia, serif;
    font-size: 1.25rem;
}

.app-mode-copy {
    display: flex;
    flex-direction: column;
    gap: 3px;
    min-width: 0;
}

.app-mode-card-admin {
    grid-column: 1 / -1;
}

.app-entry-family-grid,
.app-entry-tradition-panel {
    display: grid;
    gap: 12px;
    padding-top: 22px;
    border-top: 1px solid var(--uo-hairline);
}

.app-entry-family-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
}

.app-entry-tradition-panel {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    margin-top: 18px;
}

.app-entry-panel-title {
    grid-column: 1 / -1;
}

.app-entry-family-card,
.app-entry-tradition-card {
    display: grid;
    align-items: center;
    gap: 13px;
    width: 100%;
    margin: 0;
}

.app-entry-family-card {
    grid-template-columns: 46px minmax(0, 1fr);
    min-height: 112px;
    padding: 18px;
}

.app-entry-tradition-card {
    grid-template-columns: 54px minmax(0, 1fr);
    min-height: 96px;
    padding: 16px 18px;
}

.app-entry-family-icon,
.app-entry-tradition-code {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 999px;
    font-family: Georgia, serif;
}

.app-entry-family-icon {
    width: 44px;
    height: 44px;
    font-size: 1.25rem;
}

/* The Western card shows a Latin cross and the Eastern card an Orthodox cross (three bars, the lowest slanted). */
.app-entry-cross {
    width: 28px;
    height: 28px;
    fill: currentColor;
}

.app-entry-tradition-code {
    width: 50px;
    height: 50px;
    font-size: 0.82rem;
    font-weight: 700;
    letter-spacing: 0.05em;
}

.app-entry-back {
    grid-column: 1 / -1;
    justify-self: start;
}

@media (max-width: 860px) {
    .app-entry-family-grid,
    .app-entry-tradition-panel {
        grid-template-columns: 1fr;
    }
}

@media (max-width: 760px) {
    .app-mode-grid {
        grid-template-columns: 1fr;
    }

    #mode-selection .mode-btn.app-mode-card {
        min-height: 76px;
    }
}

/* ---------------------------------------------------------------------------
   Entry/threshold screens: container sizing + viewport-stabilization port
   (Stage 3 of 6, the highest-stakes of the six -- ported and verified via
   both screenshots AND getComputedStyle spot-checks at 6 viewports before
   anything in office.css was touched).

   office.css sets #tradition-entry's width/max-width/max-height/margin/
   overflow-y/backdrop-filter TWICE: a base rule, then a later "viewport
   stabilization pass" rule with identical selector specificity that wins
   for every property it redeclares, confirmed live via getComputedStyle at
   every tested viewport (e.g. max-height:none at <=760px width, not the
   base rule's calc(100vh-40px)). Only the WINNING (later) values are
   ported below, plus whatever the base rule alone still supplies
   (padding/box-sizing/position/z-index/text-align, never redeclared
   anywhere else). #mode-selection has no such second declaration -- its
   width/max-width/max-height come only from its one base rule.

   border-radius:22px in both screens' <=760px overrides is OMITTED,
   confirmed dead by the same reasoning as Stage 2 (Group 6's unconditional
   border-radius:0 already wins at every viewport, confirmed live). The
   asymmetry that #tradition-entry gets max-height:none at <=760px width
   while #mode-selection keeps max-height:calc(100vh - 24px) is preserved
   deliberately -- that's current, tested behavior, not an inconsistency to
   fix here. */

#tradition-entry.app-tradition-entry {
    padding: clamp(28px, 4.5vw, 58px);
    box-sizing: border-box;
    position: relative;
    z-index: 2;
    text-align: left;
}

#mode-selection.app-mode-shell {
    position: relative;
    width: min(1080px, calc(100vw - 40px));
    max-width: 1080px;
    /* FIXED 2026-09-28, task #15 (Josh: "It's this screen that scrolls..." --
       the "Another office" five-card grid causing the whole PAGE to scroll
       on an ordinary desktop window). Root cause: this cap only ever
       subtracted a flat 40px, never the real space this box's own margin
       (below) and body's own padding (css/office.css's "viewport
       stabilization pass") actually consume outside it. At 1512x900 this
       card's content (756.625px) sat comfortably under the old 860px cap,
       so this box's own overflow-y:auto never engaged -- yet body padding
       (18+26=44px) plus this box's own margin (117+54=171px at 900px
       viewport height) pushed the real total past the viewport by ~72px,
       which body's own overflow-y:auto (a deliberate mobile-safety fallback,
       not touched here) then absorbed as page-level scroll. Corrected to
       subtract the FULL outer budget -- body's padding (with its own
       safe-area-inset terms, kept in sync with that rule) plus this box's
       own margin-top/bottom, spelled out via the identical clamp()
       expressions below -- so this box's own internal overflow-y:auto is
       now what engages whenever content genuinely cannot fit (a normal
       contained scrollbar within the card, not the whole page moving).
       Every breakpoint below that changes this box's own margin-top
       restates this same formula with the matching margin-top value, kept
       immediately next to it for this reason. */
    max-height: calc(100vh - 18px - env(safe-area-inset-top) - 26px - env(safe-area-inset-bottom) - clamp(82px, 13vh, 172px) - clamp(28px, 6vh, 72px));
    overflow-y: auto;
    padding: clamp(28px, 4.5vw, 58px);
    box-sizing: border-box;
    text-align: left;
}

/* The winning (later-cascading) width/max-width/max-height/margin for
   #tradition-entry, from office.css's viewport-stabilization pass. */
#tradition-entry.app-tradition-entry {
    width: min(860px, calc(100vw - 48px));
    max-width: 860px;
    /* FIXED 2026-09-28, task #15, same fix and same reasoning as
       #mode-selection's own max-height just above -- this used to subtract
       a flat 48px, never the real space this box's own margin (right
       below) and body's own padding actually consume outside it. See that
       rule's own long comment for the full explanation. */
    max-height: calc(100vh - 18px - env(safe-area-inset-top) - 26px - env(safe-area-inset-bottom) - clamp(82px, 13vh, 172px) - clamp(28px, 6vh, 72px));
    max-height: calc(100svh - 18px - env(safe-area-inset-top) - 26px - env(safe-area-inset-bottom) - clamp(82px, 13vh, 172px) - clamp(28px, 6vh, 72px));
    max-height: calc(100dvh - 18px - env(safe-area-inset-top) - 26px - env(safe-area-inset-bottom) - clamp(82px, 13vh, 172px) - clamp(28px, 6vh, 72px));
    margin: clamp(82px, 13vh, 172px) auto clamp(28px, 6vh, 72px);
    overflow-y: auto;
    -webkit-backdrop-filter: blur(5px);
    backdrop-filter: blur(5px);
}

#mode-selection.app-mode-shell {
    margin: clamp(82px, 13vh, 172px) auto clamp(28px, 6vh, 72px);
}

@media (min-width: 1100px) and (min-height: 760px) {
    #tradition-entry.app-tradition-entry {
        width: min(900px, calc(100vw - 72px));
        max-width: 900px;
    }
}

@media (max-width: 860px) {
    #tradition-entry.app-tradition-entry,
    #mode-selection.app-mode-shell {
        margin-top: clamp(72px, 11vh, 128px);
    }

    /* Task #15 fix, continued: margin-top just above is narrower at this
       width, so the outer-budget max-height on both boxes (set at the base
       rule above) has to restate that narrower value too, or the wider
       base-rule cap would keep winning and under-count the real budget.
       #tradition-entry only actually uses this in the 761-860px band --
       the next block drops it to max-height:none at <=760px regardless. */
    #tradition-entry.app-tradition-entry,
    #mode-selection.app-mode-shell {
        max-height: calc(100vh - 18px - env(safe-area-inset-top) - 26px - env(safe-area-inset-bottom) - clamp(72px, 11vh, 128px) - clamp(28px, 6vh, 72px));
    }
}

@media (max-width: 760px) {
    #mode-selection.app-mode-shell {
        width: min(100vw - 24px, 560px);
        /* Task #15 fix, continued: same outer-budget formula as the 860px
           block just above (margin-top is still that same narrower clamp
           down here, since <=760px implies <=860px too) -- restated because
           the stale flat calc(100vh - 24px) this used to be would otherwise
           win here (later in the cascade) and undo the corrected budget. */
        max-height: calc(100vh - 18px - env(safe-area-inset-top) - 26px - env(safe-area-inset-bottom) - clamp(72px, 11vh, 128px) - clamp(28px, 6vh, 72px));
        padding: 28px 22px;
    }

    #tradition-entry.app-tradition-entry {
        width: min(100vw - 28px, 560px);
        max-height: none;
        padding: 28px 22px;
    }
}

@media (max-height: 760px) {
    #tradition-entry.app-tradition-entry,
    #mode-selection.app-mode-shell {
        margin-top: 38px;
    }

    /* Task #15 fix, continued: margin-top just above is a flat 38px at
       this height regardless of width, so #mode-selection's outer-budget
       max-height needs to restate that too. #tradition-entry is
       deliberately NOT included here: at width<=760px it already gets
       max-height:none from the width-based block above (own overflow-y:auto
       stays the only safety net there, as it was before this fix, by
       design -- see that block's own comment) -- adding it here would win
       over that "none" on a device matching both queries and silently
       undo it. At width>760px with a short height (a narrow, genuinely
       uncommon combination -- an unusually short desktop window, not a
       normal phone in either orientation), #tradition-entry keeps a
       slightly wider cap than this exact height technically allows; its
       own overflow-y:auto still contains the excess within its own box in
       the vast majority of realistic cases, short of chasing this one
       corner further. */
    #mode-selection.app-mode-shell {
        max-height: calc(100vh - 18px - env(safe-area-inset-top) - 26px - env(safe-area-inset-bottom) - 38px - clamp(28px, 6vh, 72px));
    }
}

/* 2026-10-03: the threshold ("It is time for ...") carried a fixed 92px left / 60px right padding
   from the desktop design, which on a phone left ~240px of text width: the office name ran off
   the right edge and the "Another office" button was cut off (reported from an iPhone). Inline
   styles on the markup, hence !important. */
@media (max-width: 640px) {
    #uo-threshold .uo-threshold-content {
        padding: 0 24px !important;
        width: 100%;
        box-sizing: border-box;
        overflow-y: auto !important;
    }
    #uo-threshold #uo-threshold-office-name {
        font-size: clamp(30px, 9.5vw, 48px) !important;
        overflow-wrap: anywhere;
    }
    #uo-threshold .uo-threshold-actions {
        flex-wrap: wrap;
    }
    #uo-threshold .uo-threshold-actions button {
        flex: 1 1 auto;
        padding-left: 18px !important;
        padding-right: 18px !important;
    }
}

/* ---------------------------------------------------------------------------
   2026-10-05: THE UNIVERSAL OFFICE SELECTOR MUST FIT AN ORDINARY WINDOW.
   Reported by Josh with screenshots from a ~730px-tall Chrome window: the five
   tradition cards, the Tools row and the sponsor line were cut off at a hard
   box edge, with a scroll inside the box. The earlier task-#15 fix only moved
   the scroll from the page into the box; it never made the content small
   enough to fit. This block does that: the top/bottom margins, padding, title
   and grid spacing now scale down with window HEIGHT, and the outer budget
   (max-height) is computed from the SAME two margin variables so the two can
   never drift apart again. The box's own overflow-y:auto stays as the safety
   net for genuinely tiny windows. Keep this block LAST in the file: it must
   win over the older width/height breakpoints above.
   --------------------------------------------------------------------------- */
#mode-selection.app-mode-shell {
    --uo-sel-top: clamp(16px, 4vh, 56px);
    --uo-sel-bottom: clamp(12px, 3vh, 36px);
    margin: var(--uo-sel-top) auto var(--uo-sel-bottom);
    padding: clamp(18px, 3.4vh, 40px) clamp(22px, 3.4vw, 44px);
    max-height: calc(100vh - 18px - env(safe-area-inset-top) - 26px - env(safe-area-inset-bottom) - var(--uo-sel-top) - var(--uo-sel-bottom));
    max-height: calc(100dvh - 18px - env(safe-area-inset-top) - 26px - env(safe-area-inset-bottom) - var(--uo-sel-top) - var(--uo-sel-bottom));
}

#mode-selection.app-mode-shell h1 {
    font-size: clamp(2rem, min(6vw, 8.5vh), 4.4rem);
    margin-bottom: 6px;
}

#mode-selection.app-mode-shell p {
    margin-bottom: clamp(10px, 2vh, 28px);
}

#mode-selection .app-mode-grid {
    gap: clamp(8px, 1.4vh, 12px);
    padding-top: clamp(12px, 2.2vh, 22px);
}

#mode-selection .mode-btn.app-mode-card {
    min-height: 0;
    padding: clamp(10px, 1.8vh, 16px) 18px;
}

#mode-selection .app-mode-grid + .app-mode-section-label {
    margin-top: clamp(12px, 2.2vh, 22px);
    padding-top: clamp(10px, 1.8vh, 18px);
}

#mode-selection .app-mode-tool-btn {
    padding: clamp(6px, 1.2vh, 10px) 16px;
}

/* ---------------------------------------------------------------------------
   2026-10-05: MOBILE AND TABLET PASS (Josh: "this thing needs to be optimized
   for mobile"). Audited with a real browser at iPhone SE / 14 / 15 Pro Max,
   Galaxy S9 / Pixel 7, an Android tablet, iPad mini / Pro 11 and iPad
   landscape. What it found, and what this block does about each:
   - Phones: the header took 171px of a 667px screen (profile icon alone on a
     row, title on a row, tools on a row, "Explore" on a row), leaving about
     400px to read in. Now two short rows (~95px).
   - iPad landscape: the header overflowed -- "Book of Needs" crushed into a
     three-line blob, the day line wrapped to five lines, "Explore other
     Offices" ran off the edge.
   - The white "Book of Needs" pill glared on the dark header.
   - Every paragraph reserved 69px for its drop cap, a fifth of a phone screen.
   - The Universal Office selector scrolled inside its own box on phones.
   - Touch targets under 36px (theme buttons, profile icon, checkboxes).
   Keep this block LAST: it must win over everything above.
   --------------------------------------------------------------------------- */

.uo-ordo > #office-mode-title {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}

.uo-ordo .office-context-action {
    flex: 0 0 auto;
    white-space: nowrap;
    background: transparent;
    color: var(--uo-accent);
    border: 1px solid var(--uo-hairline);
    border-radius: 2px;
    font-family: var(--uo-face-title);
    font-size: 12px;
    letter-spacing: 0.12em;
}

.uo-ordo .office-context-action:hover,
.uo-ordo .office-context-action:focus,
.uo-ordo .office-context-action:focus-visible,
.uo-ordo .office-context-action:active {
    background: transparent;
    border-color: var(--uo-accent);
}

/* 2026-10-06: with "My Parish" beside "Book of Needs", tablet-width screens tighten the two buttons so the
   ordo line still fits without overflowing. */
@media (min-width: 701px) and (max-width: 1000px) {
    .uo-ordo > #office-context-actions { gap: 4px; }
    .uo-ordo .office-context-action {
        padding-left: 7px;
        padding-right: 7px;
        font-size: 11px;
        letter-spacing: 0.06em;
    }
}

/* 2026-10-06 (Josh: "Book of needs should not be backlit like that"): on a phone the
   button stayed filled light grey after a tap (sticky :hover/:focus, plus the browser's
   own tap flash). It is an outline button in every state, in light mode too. */
.uo-ordo .office-context-action {
    -webkit-tap-highlight-color: transparent;
    box-shadow: none;
    background-image: none;
}

@media (max-width: 1100px) {
    .uo-ordo-mark {
        display: none;
    }

    .uo-ordo {
        gap: 12px;
        padding-left: 16px;
        padding-right: 16px;
    }

    .uo-ordo-day {
        min-width: 0;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
        font-size: 15px;
    }
}

@media (max-width: 768px) {
    .uo-ordo {
        gap: 6px 8px;
        padding: 8px 14px;
    }

    .uo-ordo > .app-profile-icon-btn { order: 1; }

    /* May wrap to two lines rather than truncate: "The Episcopal Church" cut to
       "The E..." told the reader nothing about where they are. */
    .uo-ordo > #office-mode-title {
        order: 2;
        flex: 1 1 90px;
        font-size: 14px !important;  /* office.css's small-screen rule is !important too */
        line-height: 1.15 !important;
        letter-spacing: 0.04em;
        white-space: normal;
        overflow: visible;
        text-overflow: clip;
        overflow-wrap: break-word !important;
    }

    .uo-ordo > .uo-theme-control { order: 3; }
    .uo-ordo > #office-context-actions { order: 4; flex: 0 0 auto; }

    .uo-ordo button[onclick*="backToSplash"] {
        order: 5;
        /* inline styles in index.html, hence !important */
        padding: 7px 10px !important;
        font-size: 10px !important;
        letter-spacing: 0.08em !important;
    }
}

/* A phone shows the same prayer in a ~335px column: a slightly smaller cap. Its line-height is
   just under 3.9 / font-size (3.75em at 2.5em) so it still spans exactly two body lines -- see
   office.css's .component-text::first-letter. */
@media (max-width: 600px) {
    .component-text::first-letter {
        font-size: 2.5em;
        line-height: 1.5;
    }
}

/* The selector is a normal page on a phone: the page scrolls, not a box inside it. */
@media (max-width: 760px) {
    #mode-selection.app-mode-shell {
        max-height: none;
        overflow-y: visible;
    }
}

/* Fingers, not pointers. */
@media (pointer: coarse) {
    .uo-theme-button {
        padding: 9px 10px;
        min-height: 36px;
    }

    .uo-ordo .office-context-action {
        min-height: 36px;
        padding: 8px 12px;
    }

    .app-profile-icon-btn {
        width: 40px;
        height: 40px;
    }

    .app-dark-toggle input,
    .app-book-needs-show-all input,
    .app-profile-default-field-checkbox input[type="checkbox"] {
        width: 22px;
        height: 22px;
    }

    .uo-drawer-close {
        min-width: 44px;
        min-height: 44px;
    }
}

/* 2026-10-05 (Josh): gap notes are not shown on phones -- they sat under the prayers and
   took space from them. The cards stay in the page (this is screen-only), so print still
   carries them as footnotes, and the wider layouts still show them. The margin holds
   nothing else now that the overlay cards are gone. */
@media screen and (max-width: 768px) {
    .uo-margin {
        display: none;
    }
}

/* ---------------------------------------------------------------------------
   ADDED 2026-10-25 -- The An Anglican Synaxarium invitation in the margin
   (js/office-ui.js, _synaxariumMarginNote). Drawn like the margin's own notes:
   a gold left bar, a small tracked label, no box and no shadow. Only in the
   Anglican lane (the envelope marks #main-content data-uo-tradition="ANG"),
   never on a phone (where the margin stacks under the page and the note under
   the commemorations already serves) and never in print.
   --------------------------------------------------------------------------- */
.uo-margin .synaxarium-margin { display: none; }
#main-content[data-uo-tradition="ANG"] .uo-margin .synaxarium-margin { display: block; }
.uo-margin .synaxarium-margin {
    max-width: none;
    margin: 0 0 22px;
    padding: 4px 0 4px 12px;
    border-left: 3px solid var(--uo-accent);
    background: none;
    text-align: left;
    font-family: inherit;
    color: var(--uo-ink);
}
.uo-margin .synaxarium-margin .syn-eyebrow {
    margin: 0 0 6px;
    font-family: var(--uo-face-machine);
    font-size: 10px;
    font-weight: 400;
    font-variant: normal;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--uo-ink-quiet);
}
.uo-margin .synaxarium-margin .syn-headline {
    margin: 0 0 4px;
    font-size: 19px;
    line-height: 1.25;
    font-style: italic;
    color: var(--uo-ink);
}
.uo-margin .synaxarium-margin .syn-blurb {
    margin: 0 0 6px;
    font-size: 14px;
    line-height: 1.5;
    color: var(--uo-ink-quiet);
}
.uo-margin .synaxarium-margin .syn-main {
    min-height: 40px;
    font-size: 15px;
    gap: 0.6em;
    color: var(--uo-ink);
}
.uo-margin .synaxarium-margin .syn-play {
    border-color: var(--uo-accent);
    color: var(--uo-accent);
}
.uo-margin .synaxarium-margin .syn-main:hover .syn-play,
.uo-margin .synaxarium-margin.is-playing .syn-play {
    background: var(--uo-accent);
    color: var(--uo-ground);
}
.uo-margin .synaxarium-margin .syn-main:hover .syn-label {
    border-bottom-color: var(--uo-accent);
}
.uo-margin .synaxarium-margin .syn-main:focus-visible {
    outline-color: var(--uo-accent);
}
.uo-margin .synaxarium-margin .syn-meta {
    margin: 2px 0 0;
    font-size: 13px;
    line-height: 1.55;
    text-align: left;
    color: var(--uo-ink-quiet);
}
.uo-margin .synaxarium-margin .syn-meta a:hover,
.uo-margin .synaxarium-margin .syn-meta a:focus-visible {
    color: var(--uo-ink);
    border-bottom-color: var(--uo-accent);
}
.uo-margin .synaxarium-margin .syn-site {
    display: flex;
    flex-wrap: wrap;
    gap: 2px 14px;
    margin: 8px 0 0;
}
.uo-margin .synaxarium-margin .syn-site a {
    font-family: var(--uo-face-machine);
    font-size: 12px;
    letter-spacing: 0.03em;
    color: var(--uo-accent);
    text-decoration: none;
    border-bottom: 1px solid transparent;
    padding: 5px 0;
}
.uo-margin .synaxarium-margin .syn-site a:hover,
.uo-margin .synaxarium-margin .syn-site a:focus-visible {
    border-bottom-color: var(--uo-accent);
}
@media (max-width: 768px) {
    .uo-margin .synaxarium-margin { display: none !important; }
}
@media print {
    .uo-margin .synaxarium-margin { display: none !important; }
}

/* ---------------------------------------------------------------------------
   2026-10-25: THE TRADITION-ENTRY PANEL MUST ALSO FIT AN ORDINARY WINDOW.
   Reported by Josh with a screenshot from a ~590px-tall Chrome window: opening
   "Eastern Christian" showed only Church of the East and the Eastern Orthodox
   Church; the Western Rite and Oriental Orthodox cards were hidden below the
   fold of a short scroll box. The 2026-10-05 block above fixed this for
   #mode-selection only; #tradition-entry kept its fixed 82-172px top margin,
   58px padding and 4.4rem title. Same remedy: margins, padding, title and card
   spacing scale with window HEIGHT, and max-height is computed from the same
   variables. Wide windows only -- at 760px wide and below the earlier
   max-height:none rules for phones stay in charge. Keep this block LAST.
   --------------------------------------------------------------------------- */
@media (min-width: 761px) {
    #tradition-entry.app-tradition-entry {
        --uo-ent-top: clamp(14px, 4vh, 56px);
        --uo-ent-bottom: clamp(10px, 3vh, 36px);
        margin: var(--uo-ent-top) auto var(--uo-ent-bottom);
        padding: clamp(14px, 3vh, 40px) clamp(22px, 3.4vw, 44px);
        max-height: calc(100vh - 18px - env(safe-area-inset-top) - 26px - env(safe-area-inset-bottom) - var(--uo-ent-top) - var(--uo-ent-bottom));
        max-height: calc(100dvh - 18px - env(safe-area-inset-top) - 26px - env(safe-area-inset-bottom) - var(--uo-ent-top) - var(--uo-ent-bottom));
    }
    #tradition-entry.app-tradition-entry h1 {
        font-size: clamp(1.7rem, min(6vw, 7vh), 4.4rem);
        margin-bottom: 4px;
    }
    #tradition-entry .app-entry-lede { margin-bottom: clamp(8px, 1.6vh, 24px); }
    #tradition-entry .app-entry-tradition-panel { gap: clamp(8px, 1.4vh, 14px); }
    #tradition-entry .app-entry-family-card,
    #tradition-entry .app-entry-tradition-card { padding-top: clamp(8px, 1.6vh, 16px); padding-bottom: clamp(8px, 1.6vh, 16px); min-height: 0; }
}


/* ── Phones: nothing clipped, and every control a thumb can hit (2026-10-25, Josh) ──────────────
   Touch targets are 44px (Apple HIG / WCAG 2.5.5) -- the earlier 36px rule above was a first pass. */
@media (pointer: coarse) {
    .uo-theme-button,
    .uo-ordo .office-context-action,
    .uo-drawer-open,
    .uo-drawer-link,
    .app-entry-back,
    .uo-onboarding-skip {
        min-height: 44px;
    }
    .uo-theme-button { min-width: 48px; }
    .app-profile-icon-btn { width: 44px; height: 44px; }
    .uo-drawer-step { min-width: 44px; min-height: 44px; }
    .uo-drawer-row-value { min-height: 44px; }
    .app-entry-back { padding-left: 18px; padding-right: 18px; }
    .uo-drawer-link { display: inline-flex; align-items: center; padding-left: 10px; padding-right: 10px; }
    .uo-drawer-row { min-height: 48px; }
    .uo-drawer-datepick { min-height: 44px; }
    /* The little "i" stays small to the eye; its tappable area grows around it. */
    .uo-explanation-marker { position: relative; }
    .uo-explanation-marker::after { content: ""; position: absolute; inset: -14px; }
}

/* A phone drawer row stacks (label above, value below, full width) so a long value such as the
   Horologion's "New Calendar (Revised Julian Fixed Feasts)" is shown whole instead of clipped. */
@media (max-width: 600px) {
    .uo-drawer-row {
        flex-wrap: wrap;
        row-gap: 0;
        padding: 8px 0;
    }
    .uo-drawer-row-label { font-size: 16px; opacity: 0.8; }
    .uo-drawer-row-value {
        flex: 1 1 100%;
        max-width: 100%;
        text-align: left;
        text-align-last: left;
        text-transform: none;
        letter-spacing: 0.02em;
        font-size: 16px;
        color: var(--gold);
        white-space: normal;
        text-overflow: clip;
    }
}

/* Eastern Orthodox "Commemorated Today": each saint's name stands apart from the life beneath it. */
.uo-saints .uo-saint-entry { margin: 0 0 1.4em; }
.uo-saints .uo-saint-name {
    margin: 0 0 0.35em;
    font-family: "Cinzel", serif;
    font-weight: 600;
    font-size: 1.05em;
    letter-spacing: 0.04em;
    line-height: 1.35;
    color: var(--gold);
    border-top: 1px solid rgba(201, 168, 76, 0.28);
    padding-top: 0.7em;
}
.uo-saints .uo-saint-entry:first-child .uo-saint-name { border-top: 0; padding-top: 0; }
.uo-saints .uo-saint-life { margin: 0; }
.uo-saints .uo-saint-note { margin: 0; font-size: 0.8em; font-style: italic; opacity: 0.75; }

/* Book of Needs checkbox: themed instead of the browser default. */
.app-book-needs-show-all input { accent-color: var(--gold); }

/* The "i" explanation badge: small to the eye but readable -- its text and ring use the full ink
   colour (the quiet one measured 4.3:1 on light). */
.uo-explanation-marker { color: var(--ink); border-color: currentColor; }

/* Tradition emblems (Explore screen and the first-run picker): simple line/fill marks in the accent colour, one per church.
   Episcopal shield, Catholic crossed keys, Church of the East flared cross, Coptic cross, Orthodox three-bar cross. */
.app-mode-icon svg,
.app-entry-tradition-code svg { width: 70%; height: 70%; display: block; }
