@import 'Bookary.Web.Client.bq1nv0l1nz.bundle.scp.css';

/* /Components/Layout/CookieConsent.razor.rz.scp.css */
/*
    THE CONSENT BANNER'S SHEET (T3).

    ── FIXED TO THE BOTTOM, AND NEVER IN THE WAY ──────────────────────────
    `position: fixed` at the bottom of the viewport on a phone, because a
    banner that pushes the page down is a banner that shifts the content a
    reader is already reading — "no layout shift" is satisfied structurally
    here rather than by animating anything. It is an OVERLAY and not a modal:
    no scrim, no scroll lock, the page scrolls and every link in it works
    while the banner is up. z-index 30 sits above the phone's bottom
    navigation bar (.app-nav, 20) so the banner's own buttons can never be
    behind it, and below #blazor-error-ui (1000) — when the connection drops,
    the thing a reader needs to read is the error banner.

    ── WHY NO SAFE-AREA TOKEN ─────────────────────────────────────────────
    A fixed bottom edge on an installed iPhone sits in the home-indicator
    band, and the honest reservation would be `var(--bk-safe-bottom)`.
    SafeAreaTests confines that token to LAYOUTS (its allowlist is a pinned
    test this component may not edit), so the gesture band is cleared with
    plain space instead — --bk-space-6 of bottom padding, which measures
    within 2px of the widest inset in practice. The trade is stated here
    rather than hidden: a very large non-standard inset would put this
    banner's padding short, and the buttons would sit closer to the
    indicator than the layouts do.

    ── THE HAIRLINE ───────────────────────────────────────────────────────
    The banner opens with the title page's hairline: `.bk-rule`'s exact
    declaration — one 1px line in the decorative --bk-border — worn as the
    panel's own top edge, because .bk-rule is a zero-height <hr> and cannot
    be worn by a section. A separator and not a control boundary
    (ColourRuleTests); the panel's own boundary would be --bk-edge and a
    panel floating over the page has none drawn.

    Colours, faces and spacing are `var(--bk-*)` tokens throughout — no new
    colours, no new fonts, and the themes reach this sheet through the tokens
    it already redeclares per theme.
*/

.bk-consent[b-yxwkv8y4qd] {
    position: fixed;
    inset-block-end: 0;
    inset-inline: 0;
    z-index: 30;
    border-block-start: 1px solid var(--bk-border);
    background: var(--bk-surface-raised);
    color: var(--bk-text);
    padding-block: var(--bk-space-3) var(--bk-space-5);
    padding-inline: var(--bk-space-4);
}

/* The copy and the link sit in a measure, not across a desktop's full width.
   No display on the section itself and none here either where `hidden` is
   toggled — an author `display` would outrank the UA's `[hidden]` rule and
   the banner could never be hidden again. Block flow and margins do the
   stacking. */
.bk-consent-copy[b-yxwkv8y4qd] {
    margin: 0;
    max-inline-size: 34rem;
    text-wrap: pretty;
}

/* The privacy link keeps a 44px target that is only as wide as its text —
   the same arrangement as Login.razor's .auth-forgot: a full-width strip
   under the copy is a target a thumb hits by accident. */
.bk-consent-link[b-yxwkv8y4qd] {
    color: var(--bk-accent);
}

.bk-consent-actions[b-yxwkv8y4qd] {
    display: flex;
    flex-wrap: wrap;
    gap: var(--bk-space-2);
    margin-block-start: var(--bk-space-3);
}

/* The three choices and Save wear .bk-button from app.css, which is where
   their 44x44 minimums live — this sheet adds no border or fill to any of
   them, so no rule here can give a control the decorative hairline as its
   only edge (ColourRuleTests). */
.bk-consent-action[b-yxwkv8y4qd] {
    margin: 0;
}

/* THE EXPANDED FIELDSET. `hidden` is toggled by consent.js, so — as on the
   section — no `display` here. min-inline-size: 0 because a fieldset's UA
   minimum is min-content and the note below is what would push a phone
   sideways. */
.bk-consent-choices[b-yxwkv8y4qd] {
    margin: 0;
    margin-block-start: var(--bk-space-3);
    padding: 0;
    border: 0;
    min-inline-size: 0;
}

.bk-consent-legend[b-yxwkv8y4qd] {
    padding: 0;
    margin-block-end: var(--bk-space-2);
    font-family: var(--bk-font-ui);
    font-size: var(--bk-text-sm);
    font-weight: 600;
}

/* The name/why pair stacked inside .bk-check's label, so the whole 44px row
   is the target and the reason travels with its own name. */
.bk-consent-choice[b-yxwkv8y4qd] {
    display: flex;
    flex-direction: column;
    gap: var(--bk-space-1);
    padding-block: var(--bk-space-1);
}

.bk-consent-choice-name[b-yxwkv8y4qd] {
    font-family: var(--bk-font-ui);
    font-size: var(--bk-text-sm);
    font-weight: 600;
}

.bk-consent-choice-why[b-yxwkv8y4qd] {
    color: var(--bk-muted);
    font-family: var(--bk-font-ui);
    font-size: var(--bk-text-xs);
    font-weight: 400;
}

.bk-consent-note[b-yxwkv8y4qd] {
    margin: 0;
    margin-block-start: var(--bk-space-3);
    color: var(--bk-muted);
    font-family: var(--bk-font-ui);
    font-size: var(--bk-text-xs);
}

/* ── ONE ROW FROM 48rem ─────────────────────────────────────────────────────

   The sentence on the left, the three buttons on the right: one band across
   the bottom of the window instead of a stacked block. `:not([hidden])` and
   not the bare class, for the reason the first rule gives: an author
   `display` would outrank the UA's [hidden] rule. The Choose panel spans the
   full width under the row when it is open. */
@media (min-width: 48rem) {
    .bk-consent:not([hidden])[b-yxwkv8y4qd] {
        display: grid;
        grid-template-columns: minmax(0, 1fr) auto;
        align-items: center;
        column-gap: var(--bk-space-5);
        padding-block: var(--bk-space-3);
        padding-inline: var(--bk-space-5);
    }

    .bk-consent-actions[b-yxwkv8y4qd] {
        margin-block-start: 0;
        flex-wrap: nowrap;
    }

    .bk-consent-choices[b-yxwkv8y4qd] {
        grid-column: 1 / -1;
    }
}
/* /Components/Layout/MainLayout.razor.rz.scp.css */
/*
    DESIGN-PLAN §5 rule 1: this file is one of exactly two LAYOUTS allowed to
    name a viewport unit (the other is ImmersiveLayout). It said "exactly two
    places in the codebase" until 2026-08-28, and that was false when it was
    written: a census for phase 2 item (b) counted EIGHT component declarations
    naming one (§2.5 finding 11). Rule 1 now carries the carve-out that makes
    most of them legitimate — an element positioned against the viewport, which
    is `position: fixed` or a modal `<dialog>` in the top layer, is entitled to
    know how big the viewport is, because it has already told the browser its
    box is the screen's box.

    A component that has NOT said so gets nothing: it says `flex: 1` or
    `height: 100%` and takes what its parent gives. The reader once demanded its
    own 100vh inside this shell's 100vh, under a 3.5rem sticky header, and the
    bottom of the book sat below the fold by construction.

    `min-block-size`, not `block-size`: prose pages are as tall as their content
    and the document scrolls them. And `dvh`, not `vh` — `vh` is the LARGEST
    possible viewport on mobile, so a `vh` layout is clipped by the browser's
    URL bar before any other mistake gets a chance.

    The sizing and inset properties in this file are LOGICAL (§9.0, decided
    2026-08-28). They are the same properties under another name in a
    horizontal-tb LTR document, which every one of ours is; what they buy is
    that the day a locale is not, the layouts do not have to be rewritten. The
    four --bk-safe-* tokens are the deliberate exception and say so in app.css.
*/
.shell[b-art46jlujm] {
    min-block-size: 100dvh;
    display: flex;
    flex-direction: column;
}

/*
    DESIGN-PLAN §5A.1 and §5.2 rule 5: only a LAYOUT reserves a safe-area
    inset, for the same reason only a layout names a viewport unit — a
    component cannot know whether its edge is the screen's edge. The rule has
    two halves, and they are enforced differently. env(safe-area-inset-*) is
    written in exactly one place, app.css's token block, which is greppable.
    The var(--bk-safe-*) tokens it publishes are then read by a short
    allowlist: this file, ImmersiveLayout.razor.css, app.css's own page modes,
    and #blazor-error-ui — the last because position: fixed against the
    viewport IS the claim that an element's edge is the screen's edge, which
    is the one thing a component is entitled to know.

    The app installs with display: standalone, so on a notched phone the
    header used to run under the status bar and the Dynamic Island. Measured
    2026-08-27 at 390x844: nothing was reserved.

    The --bk-safe-* tokens this file reads each carry the 0px fallback,
    declared once in app.css, which is where env() itself is written — so a
    browser that has never heard of the function computes a length there
    instead of dropping the declaration here. On a device with no insets
    every value below is zero and the layout is exactly what it was.

    `min-block-size`, NOT `height`: a fixed height plus an inset is the
    arithmetic that put the bottom of the reader below the fold (§2.1) — the
    bar must be able to grow by the inset rather than absorb it.

    `max()` on the horizontal padding, not addition: the inset REPLACES the
    design padding when it is larger, rather than adding to it, so a
    landscape notch does not double the gutter.

    The tokens are PHYSICAL, because the env() functions behind them are —
    there is no logical form of safe-area-inset-left. The rest of this file
    uses logical properties per §9.0, so in an RTL document the two values in
    each padding-inline pair would need swapping. RTL is an explicit non-goal
    (§9); app.css's token block is the single seam where it would be picked
    up, which is most of why the tokens exist.

    None of this works without viewport-fit=cover on the viewport meta in
    App.razor: without it these values are all zero on iOS.
*/
.shell-header[b-art46jlujm] {
    position: sticky;
    inset-block-start: 0;
    z-index: 1;
    display: flex;
    align-items: center;
    gap: 1rem;
    padding-block-start: var(--bk-safe-top);
    padding-inline: max(1rem, var(--bk-safe-left))
                    max(1rem, var(--bk-safe-right));
    min-block-size: calc(3.5rem + var(--bk-safe-top));
    box-sizing: border-box;
    background-color: var(--bk-surface-alt);
    /* The seam between the page and the chrome is a SEPARATOR, not a control's
       boundary, so it wears the hairline (--bk-border) and not the control edge
       (--bk-edge) — the boundary rule of app.css's EDGE AND SHADOW comment,
       applied where the thing being separated is two regions of one page
       rather than a thing you can press. Width stays --bk-edge-width: it is
       the one line weight this design has. */
    border-block-end: var(--bk-edge-width) solid var(--bk-border);
}

/*
    The wordmark is also the way home, so it is a target as well as an identity
    — measured 78 x 23 before phase 2 item (d), which is a link a thumb misses.
    `inline-flex` plus the block minimum rather than padding, because padding
    would push the brand off the header's optical left edge and the header has
    no room to give.
*/
.brand[b-art46jlujm] {
    display: inline-flex;
    align-items: center;
    min-block-size: 2.75rem;
    flex: 0 0 auto;
    font-size: 1.25rem;
    font-weight: 700;
    color: var(--bk-text);
    text-decoration: none;
}

    .brand:hover[b-art46jlujm] {
        color: var(--bk-accent);
    }

/*
    NO max-width AND NO padding HERE, deliberately, since design phase 0. This
    used to be a centred 64rem prose box applied to every page — which is how
    the reader ended up paginating a book against a padded prose column built
    for the Terms page (DESIGN-PLAN §2.1). The containment now belongs to the
    page: its root element declares `.bk-prose` or `.bk-workspace` (app.css),
    and this element is only the flexible track between header and footer.

    `min-block-size: 0` is the flexbox trap's antidote: a flex item refuses to
    shrink below its content by default, so without this, any child that wants
    to FIT the shell rather than grow it can only clip. ShelfPanel learned the
    width half of this lesson; this is the height half, and it was missing.
*/
.shell-content[b-art46jlujm] {
    flex: 1;
    inline-size: 100%;
    min-block-size: 0;
}

/*
    THE FOOTER CLEARS THE BOTTOM BAR (phase 2 item d).

    Below 48rem the navigation is `position: fixed` to the bottom of the
    viewport (app.css, `.app-nav`), so it is out of the flow and would sit on
    top of the last thing on the page — which is this, and which carries the
    Plans, Terms, Privacy and Contact links a signed-out visitor is most
    likely to be reading when they reach the bottom.

    The bar's own height is `--bk-nav-block-size` rather than a number repeated
    here: a footer that cleared 3rem beside a bar that grew to 3.5 is a footer
    with its last line underneath the navigation, and that is a thing you
    discover from a screenshot rather than from a diff. The safe-area inset is
    added ONCE — the bar reserves it inside its own box, and this clears the
    bar's whole box including it.
*/
.shell-footer[b-art46jlujm] {
    padding-block: 1rem calc(1rem + var(--bk-safe-bottom) + var(--bk-nav-block-size));
    padding-inline: max(1.5rem, var(--bk-safe-left))
                    max(1.5rem, var(--bk-safe-right));
    /* The seam over the footer follows the header's rule exactly: a separator
       is the hairline, a control boundary is the edge. */
    border-block-start: var(--bk-edge-width) solid var(--bk-border);
    color: var(--bk-muted);
    font-size: 0.85rem;
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem 1.5rem;
    align-items: baseline;
    justify-content: space-between;
}

/*
    THE FOOTER LINKS ARE NAVIGATION, so they take the 44px minimum like every
    other destination — measured 39, 46 and 50 px wide by 16 px tall before
    phase 2 item (d). WCAG 2.5.8 exempts a link sitting INSIDE a sentence,
    where making it 44px would wreck the line it is part of; these are a
    row of their own with a `nav` element and an accessible name around them,
    which is not that case and should not borrow the excuse.
*/
.shell-footer-links[b-art46jlujm] {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem 1rem;
}

.shell-footer-links a[b-art46jlujm] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-block-size: 2.75rem;
    /* "Terms" is 39px of text, so the inline minimum is doing real work here
       rather than restating the block one: 44 x 44 means both axes, and a
       39px-wide target is the axis nobody checks. */
    min-inline-size: 2.75rem;
}

.shell-footer-links a[b-art46jlujm] {
    color: inherit;
}

/*
    DESIGN-PLAN §5.3, mobile-first: the header's unqualified padding and gap are
    now the PHONE's, and this query adds the room a wider screen has. It used to
    be the other way round — a `max-width: 640.98px` block subtracting from a
    desktop default, with the `.98` a Bootstrap-era workaround for the
    off-by-one that only `max-width` has. There are two breakpoints in the whole
    app and this is the first: 48rem (768px), the width at which a tablet in
    portrait, and the four nav links, first fit. 64rem (1024px) is the other,
    and it is the library sidebar's. Custom properties cannot appear in a media
    query's condition, so the names live in this comment rather than as tokens.

    The narrow-viewport step for the CONTENT moved to `.bk-prose` /
    `.bk-workspace` in app.css, with the rest of the containment this element
    gave up in phase 0; app.css inverts its own copy of this query the same way.
*/
@media (min-width: 48rem) {
    .shell-header[b-art46jlujm] {
        padding-inline: max(1.5rem, var(--bk-safe-left))
                        max(1.5rem, var(--bk-safe-right));
        gap: 1.5rem;
    }

    /* No bar at this width — the links are back in the header — so there is
       nothing to clear and the footer returns to its own padding. */
    .shell-footer[b-art46jlujm] {
        padding-block-end: calc(1rem + var(--bk-safe-bottom));
    }
}

/*
    The alpha notice sits after the content, in the flow, so nothing above it
    moves when the island arrives. The wrapper carries the side safe-area
    insets so AlphaNote.razor.css need not. An empty wrapper takes no room.
*/
.shell-alpha[b-art46jlujm] {
    /* Block padding keeps the note's border off the footer's top rule. */
    padding-block: var(--bk-space-4);
    padding-inline: max(var(--bk-space-4), var(--bk-safe-left))
                    max(var(--bk-space-4), var(--bk-safe-right));
}

.shell-alpha:empty[b-art46jlujm] {
    display: none;
}

/*
    APP MODE. Inside the phone apps the page wears html.bk-app (App.razor,
    from the BookaryApp/ user-agent token). The app has its own tab bar and
    an About section on the Account tab, so the website's footer, its alpha
    note and, on the sign-in and register screens, its header give way:
    - The footer's CONTENTS go, the footer stays: its block-end padding is
      what keeps the last row of every page clear of the fixed tab bar.
    - Terms, Privacy, Contact and Feedback move to Account → About Bookary;
      signed out, Privacy and Terms are on /welcome. App Store review needs
      the privacy policy reachable inside the app.
    - The alpha note goes: TestFlight already labels the app a beta.
    - /login and /register lose the header; they carry a Back link to
      /welcome instead (the .auth-back rules in app.css).
    These live here, not in app.css: scoped rules are unlayered and would
    beat anything in app.css's layers.
*/
html.bk-app .shell-footer[b-art46jlujm] {
    border-block-start: none;
}

html.bk-app .shell-footer > *[b-art46jlujm] {
    display: none;
}

html.bk-app .shell-alpha[b-art46jlujm] {
    display: none;
}

html.bk-app .shell:has(.app-nav[data-page-kind="auth"]) .shell-header[b-art46jlujm] {
    display: none;
}

/*
    SHEET MODE (html.bk-sheet, App.razor): a page the phone apps present in
    their own native sheet (the legal pages). The sheet has its own title bar
    and Done, so the site's header (which holds the tab bar), footer and alpha
    note go; the content alone is left, and the footer no longer reserves room
    for a tab bar that is not there.
*/
html.bk-sheet .shell-header[b-art46jlujm],
html.bk-sheet .shell-footer[b-art46jlujm],
html.bk-sheet .shell-alpha[b-art46jlujm] {
    display: none;
}

/* In the sheet nothing else reserves the bottom edge (the footer that did is
   hidden above), so the content keeps its last line clear of the home
   indicator itself. */
html.bk-sheet .shell-content[b-art46jlujm] {
    padding-block-end: var(--bk-safe-bottom);
}
/* /Components/Layout/NavMenu.razor.rz.scp.css */
/*
    ALMOST EVERYTHING THIS FILE USED TO HOLD IS IN app.css NOW (phase 2 item d).

    `.app-nav` and `.nav-link` are laid out by the shell rather than by this
    component: below 48rem the nav is a bar fixed to the bottom of the viewport
    and `.shell-footer` in MainLayout.razor.css has to be padded clear of it,
    and those two files are in different assemblies' scoped stylesheets, which
    cannot see each other. Keeping the rules here would also have meant this
    file reading `var(--bk-safe-*)`, which §5.2 rule 5 confines to a four-file
    allowlist on purpose. app.css explains the arrangement where the arrangement
    is.

    What is left is the one thing that IS this component's: the header's
    session corner (signed in, it holds nothing below 48rem since Phase 1 T5 —
    Sign out lives on Account there), and the ACCOUNT NAME inside it is the only
    part of the header that has no fixed upper bound — an address like
    somebody.with.a.long.name@a-long-domain.example is 24rem of text beside a
    brand and a Sign out button. It was capped at 14rem with an ellipsis, which
    is 224px, and on a 390px phone the header could not afford it: brand plus
    that cap plus Sign out is 372px before any padding. Below 48rem it is not
    shown at all, and nothing is lost — the account name is what the Account
    page opens with, and Account is one tap away in the bar.
*/
.nav-auth[b-jy3wd67w3v] {
    display: flex;
    align-items: center;
    gap: var(--bk-space-1);
    /* `auto` on the inline start, so the corner sits against the far edge with
       the brand at the near one. It used to be `margin-left` and it used to be
       INSIDE the nav being pushed away from the links; now the links are a
       sibling that leaves the flow entirely on a phone, and this pushes against
       the header itself. Logical per §9.0. */
    margin-inline-start: auto;
    min-inline-size: 0;
}
/* /Components/Layout/ReconnectModal.razor.rz.scp.css */
.components-reconnect-first-attempt-visible[b-75vo9hbe2f],
.components-reconnect-repeated-attempt-visible[b-75vo9hbe2f],
.components-reconnect-failed-visible[b-75vo9hbe2f],
.components-pause-visible[b-75vo9hbe2f],
.components-resume-failed-visible[b-75vo9hbe2f],
.components-rejoining-animation[b-75vo9hbe2f] {
    display: none;
}

#components-reconnect-modal.components-reconnect-show .components-reconnect-first-attempt-visible[b-75vo9hbe2f],
#components-reconnect-modal.components-reconnect-show .components-rejoining-animation[b-75vo9hbe2f],
#components-reconnect-modal.components-reconnect-paused .components-pause-visible[b-75vo9hbe2f],
#components-reconnect-modal.components-reconnect-resume-failed .components-resume-failed-visible[b-75vo9hbe2f],
#components-reconnect-modal.components-reconnect-retrying[b-75vo9hbe2f],
#components-reconnect-modal.components-reconnect-retrying .components-reconnect-repeated-attempt-visible[b-75vo9hbe2f],
#components-reconnect-modal.components-reconnect-retrying .components-rejoining-animation[b-75vo9hbe2f],
#components-reconnect-modal.components-reconnect-failed[b-75vo9hbe2f],
#components-reconnect-modal.components-reconnect-failed .components-reconnect-failed-visible[b-75vo9hbe2f] {
    display: block;
}


/* THE BANNER A READER SEES WHEN THE NETWORK IS WORST, WHICH IS WHEN THE PHONE
   IS IN SOMEBODY'S HAND (DESIGN-PLAN §2.5 finding 5).

   This box was 20rem + 2rem of padding on each side + a 1px border = 386px
   under content-box arithmetic, measured 2026-08-27. It fitted a 390px phone
   with 2px to spare and overflowed a 360px one by 26px — the modal that
   appears precisely when a reader cannot fix anything, running off the side of
   the screen. Nothing in this file changed to fix it: the universal
   `box-sizing: border-box` in app.css's base layer did, and 20rem is now 320px
   including its padding on every phone made. It is recorded here because the
   next person to widen this dialog needs to know which arithmetic they are
   working in.

   `20dvh`, not `20vh`, per §5.2 rule 2: `vh` is the tallest the viewport could
   be rather than the height it has, so a 20vh top margin under a URL bar
   pushes the dialog further down than intended on exactly the screen where
   there is least room. A modal <dialog> is in the top layer and positioned
   against the viewport by the user agent, which is what entitles it to name a
   viewport unit at all (§5.2 rule 1's top-layer carve-out); the margin and the
   `auto` centring are logical per §9.0. */
#components-reconnect-modal[b-75vo9hbe2f] {
    background-color: var(--bk-surface);
    color: var(--bk-text);
    inline-size: 20rem;
    margin-block: 20dvh;
    margin-inline: auto;
    padding: 2rem;
    border: var(--bk-edge-width) solid var(--bk-edge);
    border-radius: var(--bk-radius-lg);
    box-shadow: var(--bk-shadow-2);
    opacity: 0;
    transition: display 0.5s allow-discrete, overlay 0.5s allow-discrete;
    animation: components-reconnect-modal-fadeOutOpacity-b-75vo9hbe2f 0.5s both;
    &[open]

{
    animation: components-reconnect-modal-slideUp-b-75vo9hbe2f 1.5s cubic-bezier(.05, .89, .25, 1.02) 0.3s, components-reconnect-modal-fadeInOpacity-b-75vo9hbe2f 0.5s ease-in-out 0.3s;
    animation-fill-mode: both;
}

}

#components-reconnect-modal[b-75vo9hbe2f]::backdrop {
    background-color: rgba(0, 0, 0, 0.4);
    animation: components-reconnect-modal-fadeInOpacity-b-75vo9hbe2f 0.5s ease-in-out;
    opacity: 1;
}

@keyframes components-reconnect-modal-slideUp-b-75vo9hbe2f {
    0% {
        transform: translateY(30px) scale(0.95);
    }

    100% {
        transform: translateY(0);
    }
}

@keyframes components-reconnect-modal-fadeInOpacity-b-75vo9hbe2f {
    0% {
        opacity: 0;
    }

    100% {
        opacity: 1;
    }
}

@keyframes components-reconnect-modal-fadeOutOpacity-b-75vo9hbe2f {
    0% {
        opacity: 1;
    }

    100% {
        opacity: 0;
    }
}

.components-reconnect-container[b-75vo9hbe2f] {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1rem;
}

#components-reconnect-modal p[b-75vo9hbe2f] {
    margin: 0;
    text-align: center;
}

#components-reconnect-modal button[b-75vo9hbe2f] {
    border: 0;
    background-color: var(--bk-accent);
    color: var(--bk-accent-contrast);
    padding: 4px 24px;
    border-radius: var(--bk-radius-sm);
}

    #components-reconnect-modal button:hover[b-75vo9hbe2f] {
        background-color: var(--bk-muted);
    }

.components-rejoining-animation[b-75vo9hbe2f] {
    position: relative;
    width: 80px;
    height: 80px;
}

    .components-rejoining-animation div[b-75vo9hbe2f] {
        position: absolute;
        border: 3px solid var(--bk-accent);
        opacity: 1;
        border-radius: 50%;
        animation: components-rejoining-animation-b-75vo9hbe2f 1.5s cubic-bezier(0, 0.2, 0.8, 1) infinite;
    }

        .components-rejoining-animation div:nth-child(2)[b-75vo9hbe2f] {
            animation-delay: -0.5s;
        }

@keyframes components-rejoining-animation-b-75vo9hbe2f {
    0% {
        top: 40px;
        left: 40px;
        width: 0;
        height: 0;
        opacity: 0;
    }

    4.9% {
        top: 40px;
        left: 40px;
        width: 0;
        height: 0;
        opacity: 0;
    }

    5% {
        top: 40px;
        left: 40px;
        width: 0;
        height: 0;
        opacity: 1;
    }

    100% {
        top: 0px;
        left: 0px;
        width: 80px;
        height: 80px;
        opacity: 0;
    }
}
/* /Components/Pages/Home.razor.rz.scp.css */
/*
    ══════════════════════════════════════════════════════════════════════════
    THE FRONT DOOR (DESIGN-PLAN §6, phase 4; re-dressed by phase 3)

    Scoped, and that is the point: this is the only page in the product shaped
    like a landing page, so none of it belongs in app.css. Nothing here declares
    a new token — every colour, space, radius, duration and type step is a
    `--bk-*` read from the token block, so the page follows light, dark and
    sepia without knowing they exist.

    ── THE THREE RULES THIS FILE IS WRITTEN UNDER ────────────────────────────

    1. MOBILE-FIRST. Every unqualified rule below is the PHONE. Width is added
       by `@container`, never subtracted by `max-width`.

    2. CONTAINER QUERIES, NOT MEDIA QUERIES (§5.5). The question this page keeps
       asking is "how wide is my column", and above 90rem of viewport that stops
       being the same question as "how wide is the screen" — `.bk-workspace`
       caps the column at 90rem, so on a 2560px monitor a viewport query would
       report 160rem for a column that is 90. The container is declared on the
       page root and named, so a nested container added later cannot silently
       capture these queries. ONE EXCEPTION: the hero's two-column split is a
       viewport @media (min-width: 64rem) query — see its comment below.

    3. NO `var(--bk-safe-*)`, NO `env()`, NO VIEWPORT UNITS. §5.2 rule 5 and
       rule 1 confine all three to the layouts, and SafeAreaTests enforces the
       first by reading this directory. The page mode's padding already carries
       the horizontal insets and MainLayout's footer already clears the bottom
       bar, so there is nothing left here to reserve.

    ── WHY THE BREAKPOINTS ARE THE APP'S TWO AND NOT NEW ONES ────────────────

    48rem and 64rem, the two §5.3 names. The hero's two-column split wants to
    change at the same width the nav does, and inventing a third number for a
    landing page would mean the product reflows twice on the way from a phone
    to a laptop for no reason a reader could see.
*/

.home[b-9p51uu9pa6] {
    /* Named, per rule 2 above. */
    container: home / inline-size;

    display: flex;
    flex-direction: column;
    /* The band gap IS the page rhythm: bands do not carry their own outer
       margins, so no two adjacent margins can collapse into an unintended
       gap and every seam on the page is this one number. */
    gap: var(--bk-space-6);
    padding-block-end: var(--bk-space-6);
}

@container home (min-width: 48rem) {
    .home[b-9p51uu9pa6] {
        gap: var(--bk-space-8);
    }
}

/* ── TYPE ──────────────────────────────────────────────────────────────────

   Literata (--bk-font-reading) for every heading on this page, and the UI face
   for everything else. §4.1 wants the product to feel like paper; the landing
   page is where a stranger forms that impression, and the headings are the
   only place a serif can carry it without costing legibility at small sizes.

   THE h1 IS DISPLAY TYPE, scaled by a clamp that runs on CONTAINER units
   (cqw), not viewport ones — rule 3 above bans viewport units and the
   container is the column this headline lives in anyway. THE FLOOR IS 2.5rem
   (40px) and the step follows 11cqw, and both numbers are set by the script
   line under the headline: "ready wherever you read." sets about 10.1em wide
   in traced strokes plus its three 0.22em gaps, at 0.88 of the headline's size, so the
   whole line fits ONE ROW inside a 390px phone's 358px column exactly when
   the headline is 40px — measured, not estimated, and the reason this floor
   is 40px rather than the 44px the earlier display scale wore. Past the
   floor the step is 11cqw and not more because 11.16cqw is the widest step at
   which the script line still fits one row at every width the clamp spans;
   the cap (5.75rem, 92px) is the title page's size and is reached only where
   the headline has the full column to itself — below the 64rem split. LEADING
   IS 1.0 — at display sizes --bk-leading-tight's 1.2 is a paragraph's
   leading, and the close-set lines are what make the script line read as the
   sentence's end rather than as a subhead. */
.home-h1[b-9p51uu9pa6] {
    margin: 0;
    font-family: var(--bk-font-reading);
    font-size: clamp(2.5rem, 11cqw, 5.75rem);
    font-weight: 600;
    line-height: 1;
    letter-spacing: -0.02em;
    text-wrap: balance;
}

/* THE HEADLINE'S SECOND LINE, set apart rather than flowed: its own line, in
   the accent, regular weight. `display: block` makes the break the
   designer's and not the line breaker's; the words above it are unchanged.
   NOT ITALIC — the line is written in a traced hand (HomeInkLine) and italic
   Literata under it would be a second handwriting competing with the first.
   The words themselves live in a visually hidden span beside the strokes, so
   the sentence stays a sentence for a crawler and a screen reader. */
.home-h1-em[b-9p51uu9pa6] {
    display: block;
    font-style: normal;
    font-weight: 400;
    color: var(--bk-accent);
}

/* THE SCRIPT'S SIZE, in the one unit the strokes are drawn in: the SVG words
   carry their widths in em (9.42em of strokes plus three word spaces =
   10.14em), so scaling the wrapper's font-size scales the whole line. 0.88em
   of the headline is the ratio at which the line sets on ONE row at 390px
   (see the clamp above for the arithmetic). */
.home-h1-ink[b-9p51uu9pa6] {
    font-size: 0.88em;
    /* ROWS AS FLEX LINES, not line boxes: as inline content each word sat on
       the headline's strut and its full stroke box, so a wrapped second row
       ("you read." in the wide pitch column) fell further from the first than
       the headline's own lines are apart. As flex items the rows stack on
       the words' boxes alone, which HomeInkLine trims to the strokes' body;
       the gap stands in for the word space flex drops. */
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    /* 0.22em and not a full 0.25em word space: at 390px the one-row line
       otherwise fits its 358px column with a fraction of a pixel to spare,
       which one engine's rounding would break onto two rows. */
    column-gap: 0.22em;
    /* Clearance under the set line above: the script's tall ascenders (the
       d, the h) would otherwise touch "you own,"'s descending comma. */
    padding-block-start: 0.15em;
}

.home-h2[b-9p51uu9pa6] {
    margin: 0;
    font-family: var(--bk-font-reading);
    font-size: var(--bk-text-lg);
    font-weight: 700;
    line-height: var(--bk-leading-tight);
    text-wrap: balance;
}

.home-lede[b-9p51uu9pa6] {
    margin: 0;
    font-size: var(--bk-text-base);
    line-height: var(--bk-leading-reading);
    color: var(--bk-muted);
    /* The lede is the one paragraph on the page a visitor certainly reads, so
       it gets a measure of its own rather than the workspace's full width —
       §7's 60-70 characters, applied where it matters most. */
    max-inline-size: 34rem;
    text-wrap: pretty;
}

.home-lede-sm[b-9p51uu9pa6] {
    font-size: var(--bk-text-sm);
}

/* Small caps label above a heading — REAL small caps (`all-small-caps`, the
   title page's eyebrow) rather than the uppercase stand-in, letter-spaced
   because small caps at 0.8rem without tracking set tight and read as an
   abbreviation. */
.home-eyebrow[b-9p51uu9pa6] {
    font-size: var(--bk-text-xs);
    font-variant: all-small-caps;
    letter-spacing: 0.14em;
    color: var(--bk-muted);
}

.home-alpha[b-9p51uu9pa6] {
    font-weight: 700;
    color: var(--bk-accent);
}

/* The ONE fine line under the CTA (review #1) — the sentence that prices the
   button and says the product is still alpha. It is fine print at the volume
   of fine print; the numbers in it come from the `plans` row (Home.razor). */
.home-fine[b-9p51uu9pa6] {
    margin: 0;
    font-size: var(--bk-text-sm);
    line-height: var(--bk-leading-normal);
    color: var(--bk-muted);
    max-inline-size: 34rem;
    text-wrap: pretty;
}

@container home (min-width: 48rem) {
    .home-h2[b-9p51uu9pa6] {
        font-size: var(--bk-text-xl);
    }

    .home-lede[b-9p51uu9pa6] {
        font-size: var(--bk-text-md);
    }

    .home-lede-sm[b-9p51uu9pa6] {
        font-size: var(--bk-text-base);
    }
}

/* ── HERO ─────────────────────────────────────────────────────────────────

   THE TITLE PAGE'S OPENING. A double rule, the eyebrow, the headline with its
   script line — and then the DRAWING directly under the headline, with the
   argument (lede, CTA, fine line) after it. Below 64rem that is simply the
   markup's order, because 390px is where this page is designed and the
   product is the second thing a phone-sized visitor should meet. FROM 64rem
   the same markup re-flows into the approved split: a 1fr/2fr grid — the
   pitch (eyebrow, headline, lede, CTA, fine line) in the narrow column, the
   drawing in the two-thirds beside it. The headline sits in the pitch rather
   than across the top: at 6cqw in a third-width column it is the title
   page's ornament at the drawing's shoulder, and the width goes to the
   drawing, which is what is being sold.

   The copy stays left-aligned on a phone and left-aligned on a desktop. The
   wide mockup centres it; centring was dropped on measurement rather than on
   taste, because the h1 and the lede have different natural measures (a
   balanced two-line display block over a 30ch paragraph), and centred, the two
   ragged edges make a shape that pulls the eye away from the button
   underneath them both.

   The gap is the title page's tight rhythm — the rule sits on the opening's
   top edge (no padding-block-start here; the workspace's own gutter is the
   air above it) and the eyebrow, headline and split stack close under it. */
.home-hero[b-9p51uu9pa6] {
    display: flex;
    flex-direction: column;
    gap: var(--bk-space-4);
}

/* THE DOUBLE RULE: two 1px ink lines 3px apart. `border-block` on a 5px box
   under the global border-box reset is exactly that — 1 + 3 + 1 — and the ink
   is the text colour because a title page's rules are printed in the same ink
   as its type. */
.home-hero-rule[b-9p51uu9pa6] {
    block-size: 5px;
    border-block: 1px solid var(--bk-text);
}

/* A VIEWPORT query, not the `home` container: the container is the workspace's
   content box (1.5rem gutters each side), so a 64rem container query only split
   at ~1072px and a 1024px laptop stayed stacked. The plan's breakpoint is the
   64rem SCREEN (§5.3's desk), so this one rule asks the viewport. The split is
   1fr 2fr — the pitch's column and the drawing's — because the drawing is the
   product and the pitch is its argument; the drawing scaled up to two-thirds
   is the visual focus the approved prototype asks for.

   PLACEMENT IS EXPLICIT RATHER THAN AUTO-PLACED: the markup is in the phone's
   order (rule, eyebrow, headline, DRAWING, copy) and the grid moves the
   drawing beside the whole pitch — rows 2 to 4 of the first column — while
   every pitch element keeps its place. The rule spans both columns, as a
   title page's rule is the width of the page (review round 1: auto-placed, it
   stopped at the first column and read as a stray line under the header).
   The split's own rule sits below the lede's and the copy's, where source
   order can do its job on the equal-specificity overrides it carries. */

/* THE LEDE, set as book type. The reading face at 20px — the mockup's 20px
   lead mapped onto the token scale's --bk-text-md — and 21px where there is
   room, in the ink rather than the muted grey: this is the opening paragraph
   of the argument, not a caption. From 48rem it takes a 30ch measure of its
   own (§7's 60–70 characters where they matter most). */
.home-hero-lede[b-9p51uu9pa6] {
    font-family: var(--bk-font-reading);
    font-size: 1.25rem;
    line-height: 1.45;
    color: var(--bk-text);
}

/* THE DROP CAP — the title page's decorated initial, in the accent at 3em.
   Floated and padded inline-end (and nudged off the cap line) rather than
   margin-ed, because the mockup's proportions are the point: the paragraph's
   first three lines wrap around it and the lede opens the way a chapter
   does. */
.home-hero-lede[b-9p51uu9pa6]::first-letter {
    float: left;
    font-size: 3em;
    line-height: 0.85;
    font-weight: 600;
    color: var(--bk-accent);
    padding-block-start: 5px;
    padding-inline-end: 8px;
}

/* The pitch's block — the lede, the CTA and the fine line. It keeps the tight
   stack the one-column hero had (gap 3), and below 64rem it sits under the
   drawing where the phone's reading order wants it. */
.home-hero-copy[b-9p51uu9pa6] {
    display: flex;
    flex-direction: column;
    gap: var(--bk-space-3);
}

/* The drawing's block. `min-inline-size: 0` because it is a flex child and
   then a grid child, and the drawn phone's text must not be what sets the
   column's width (the flexbox trap HorizontalOverflowTests measures). */
.home-hero-preview[b-9p51uu9pa6] {
    display: flex;
    flex-direction: column;
    gap: var(--bk-space-4);
    min-inline-size: 0;
}

/* THE SPLIT AND ITS PLACEMENT (64rem). A VIEWPORT query, not the `home`
   container: the container is the workspace's content box (1.5rem gutters each
   side), so a 64rem container query only split at ~1072px and a 1024px laptop
   stayed stacked. The plan's breakpoint is the 64rem SCREEN (§5.3's desk), so
   this one rule asks the viewport. 1fr 2fr — the pitch's column and the
   drawing's — because the drawing is the product and the pitch is its
   argument: the drawing scaled up to two-thirds is the visual focus the
   approved prototype asks for, and the headline's clamp leans back inside the
   narrow column so the two do not shout over each other. */
@media (min-width: 64rem) {
    .home-hero[b-9p51uu9pa6] {
        display: grid;
        grid-template-columns: minmax(0, 1fr) minmax(0, 2fr);
        align-items: start;
        column-gap: var(--bk-space-8);
        row-gap: var(--bk-space-4);
    }

    .home-hero .home-hero-rule[b-9p51uu9pa6] {
        grid-column: 1 / -1;
        grid-row: 1;
    }

    .home-hero .home-eyebrow[b-9p51uu9pa6] {
        grid-column: 1;
        grid-row: 2;
    }

    .home-hero .home-h1[b-9p51uu9pa6] {
        grid-column: 1;
        grid-row: 3;
        /* The display scale leans back where the drawing leans in: in the
           pitch's third-width column the 92px cap set four ragged one-word
           rows and out-shouted the product beside it, so the split's step is
           6cqw capped at 4.5rem — still above the floor at every width this
           query covers, and enough that "Every book" sets on one line of the
           column at 1280. */
        font-size: clamp(2.5rem, 6cqw, 4.5rem);
    }

    .home-hero .home-hero-copy[b-9p51uu9pa6] {
        grid-column: 1;
        grid-row: 4;
    }

    .home-hero .home-hero-preview[b-9p51uu9pa6] {
        grid-column: 2;
        grid-row: 2 / 5;
    }

    .home-hero-lede[b-9p51uu9pa6] {
        font-size: 1.3125rem;
        max-inline-size: 30ch;
    }
}

.home-cta-row[b-9p51uu9pa6] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--bk-space-3);
    margin-block-start: var(--bk-space-2);
}

/* The primitive carries the 44px minimum, the colour and the focus ring
   (§4.5); this adds only what a landing page's primary action needs on top —
   it is the one control on the screen, so it is allowed the larger step. */
.home-cta[b-9p51uu9pa6] {
    font-size: var(--bk-text-base);
    font-weight: 600;
    padding-inline: var(--bk-space-5);
}

.home-cta-aside[b-9p51uu9pa6] {
    font-size: var(--bk-text-sm);
    color: var(--bk-muted);
}

/* An anchor in that slot is a real target, not a sentence fragment: it is a
   row of its own beside a button, so WCAG 2.5.8's inline-link exemption does
   not apply to it (the same argument MainLayout makes for the footer links). */
a.home-cta-aside[b-9p51uu9pa6] {
    display: inline-flex;
    align-items: center;
    min-block-size: 2.75rem;
    color: var(--bk-accent);
}

/* ── THE PREVIEW ──────────────────────────────────────────────────────────

   THE PRODUCT, DRAWN IN MARKUP AND TOKENS — no images, no url(), nothing to
   fetch and nothing to keep in sync with a screenshot pipeline. Home.razor
   explains the argument the drawing makes; this file only lays it out: the
   laptop takes the flexible width on a stage of its own, the phone lies on
   its bottom-right corner at a proportional width, and both grow DOWNWARD
   with their text rather than cropping it.

   The visually hidden sentence is hidden the way .home-table-caption used to
   be — `clip-path` rather than `display: none`, so it stays in the
   accessibility tree as the drawing's text alternative. */
.home-sr-only[b-9p51uu9pa6] {
    position: absolute;
    inline-size: 1px;
    block-size: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

/* THE STAGE THE TWO DEVICES LIE ON — a two-column grid whose two items share
   ONE ROW on purpose: the laptop window takes its 86% across both columns,
   the phone stands in the second column, and the two overlap because their
   grid areas overlap. THE PHONE'S TOP EDGE IS THE DRAWING'S ONE GEOMETRIC
   RULE (Home.razor spells it out): it begins below the "Continue reading"
   row — the calibrated margin below, the row's own height at that breakpoint —
   so it can never clip "Pride and Prejudice", and the shelf row is held to
   68% (wide) / 52% (phone) of the window so it can never hide a cover. The
   row's height is the PHONE's (it is the taller item), so the stage grows
   with the phone and the window keeps its natural height: the drawing is a
   window with a phone standing in front of its lower right, which is what
   this picture has always drawn. NOTHING IS LIFTED BY A SHADOW (§4.0: flat
   furniture is this page's rule) — the phone's ink bezel is what separates it
   from the window behind it. */
.home-preview[b-9p51uu9pa6] {
    display: grid;
    grid-template-columns: minmax(0, 54%) minmax(0, 46%);
    align-items: start;
}

.home-preview-window[b-9p51uu9pa6] {
    grid-column: 1 / -1;
    grid-row: 1;
    inline-size: 86%;
    justify-self: start;
    display: flex;
    flex-direction: column;
    border: var(--bk-edge-width) solid var(--bk-edge);
    border-radius: var(--bk-radius-lg);
    background: var(--bk-surface-raised);
    overflow: hidden;
}

/* The laptop's chrome: three dots on a tinted bar. It says "window" in a few
   pixels of height and carries no information, so it is dots and not a drawn
   tab strip with an address nobody can read. */
.home-preview-bar[b-9p51uu9pa6] {
    display: flex;
    align-items: center;
    gap: 0.3rem;
    padding: var(--bk-space-2) var(--bk-space-3);
    background: var(--bk-surface-alt);
    border-block-end: 1px solid var(--bk-border);
}

.home-preview-bar span[b-9p51uu9pa6] {
    inline-size: 6px;
    block-size: 6px;
    border-radius: var(--bk-radius-full);
    background: var(--bk-border-strong);
}

.home-preview-screen[b-9p51uu9pa6] {
    display: flex;
    flex-direction: column;
    gap: var(--bk-space-3);
    padding: var(--bk-space-3);
}

/* The window's two section titles — "Continue reading" and "Your library" —
   in the reading face, at the drawing's enlarged scale: this text is TEXTURE
   only (the art is aria-hidden) but it is the texture of a real app, and at
   the old 16px it read as a wireframe beside covers twice its size. */
.home-preview-h[b-9p51uu9pa6] {
    margin: 0 0 var(--bk-space-2);
    font-family: var(--bk-font-reading);
    font-size: 1.125rem;
    font-weight: 700;
    line-height: var(--bk-leading-tight);
    color: var(--bk-text);
}

/* The "Continue reading" row — the drawing of the card the signed-in page
   really renders. Tinted rather than outlined so the row reads as one object
   inside the window. */
.home-preview-row[b-9p51uu9pa6] {
    display: flex;
    align-items: center;
    gap: var(--bk-space-2);
    padding: var(--bk-space-2);
    border-radius: var(--bk-radius-sm);
    background: var(--bk-surface-alt);
}

.home-preview-cover[b-9p51uu9pa6] {
    flex: 0 0 auto;
    inline-size: 2.75rem;
}

.home-preview-rowbody[b-9p51uu9pa6] {
    flex: 1 1 auto;
    min-inline-size: 0;
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
}

.home-preview-title[b-9p51uu9pa6] {
    font-family: var(--bk-font-reading);
    font-size: 0.9375rem;
    font-weight: 600;
    line-height: var(--bk-leading-tight);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.home-preview-meta[b-9p51uu9pa6] {
    font-size: 0.75rem;
    line-height: var(--bk-leading-normal);
    color: var(--bk-muted);
}

/* THE SHELF under the row: THREE cloth bindings on a phone and four from
   48rem — `repeat(n, minmax(0, 1fr))`, so the plates share the row's width
   whatever it is. THE ROW'S WIDTH IS THE PHONE-CLEARANCE MEASUREMENT: 57% of
   the screen's content box here, which the window's padding turns into about
   52% of the WINDOW on a phone, and the 48rem block's 71% into about 68% on a
   wide screen — the two fractions the drawing is checked against, because the
   drawn phone stands at the right and a plate under its bezel is a plate
   nobody sees. The fourth is HIDDEN below 48rem rather than not rendered:
   this page is static SSR and cannot know the container's width on the
   server, and the container query below is what reveals it. The gap is the
   half-step (space-1): every pixel not spent on gaps is a pixel the stamped
   title gets, and Literata is wide. */
.home-preview-grid[b-9p51uu9pa6] {
    display: grid;
    inline-size: 57%;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--bk-space-1);
}

.home-preview-grid > :nth-child(n+4)[b-9p51uu9pa6] {
    display: none;
}

/* THE SHELF COVERS' STAMPED TITLES (review #2): NEVER BROKEN MID-WORD — no
   `hyphens: auto`, no `word-break` — and fitted instead. The titles are short
   on purpose and the SIZE is the one a measurement says fits the longest
   single word in the plates it serves: the phone's row shows the three short
   titles ("Prejudice" wraps at its own space, "Dracula" sets whole), and the
   wide row's fourth plate is the only one carrying "Middlemarch", sized in
   the 48rem block below to sit whole between that plate's edges. Where a
   title is two words it wraps at the SPACE, which is where a cover breaks
   one. The clamp is a safety net whose ellipsis falls at a line end — never a
   mid-word cut — and the fallback keeps a 4px spine band beside the title
   (the spine is what makes the plate read as a binding), narrower than the
   library's because the small plate cannot afford it. */
.home-preview-grid .home-cover-fallback[b-9p51uu9pa6] {
    padding: 0.15rem;
    padding-inline-start: calc(0.15rem + 4px);
}

.home-preview-grid .home-cover-fallback[b-9p51uu9pa6]::before,
.home-preview-grid .home-cover-fallback[b-9p51uu9pa6]::after {
    inline-size: 4px;
}

.home-preview-grid .home-cover-fallback[b-9p51uu9pa6]::after {
    inset-block-start: 8px;
    box-shadow: 0 4px 0 var(--bk-highlight);
}

.home-preview-grid .home-cover-title[b-9p51uu9pa6] {
    font-size: 0.4rem;
    line-height: 1.15;
    padding-block: var(--bk-space-1);
    white-space: normal;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    overflow: hidden;
}

/* THE COMIC PLATE: three flat colour panels of the tokens the other plates
   already wear — brass, cloth blue, cloth oxblood, fills only — on the ink
   ground a comic page is printed against, with its title stamped under them
   the way every other cover stamps one. No spine band and no gilt bands on
   this one (a comic page is not a cloth binding), and still nothing to
   fetch. */
.home-cover-comic[b-9p51uu9pa6] {
    background: var(--bk-shadow-ink);
}

.home-cover-comic .home-cover-fallback[b-9p51uu9pa6] {
    flex-direction: column;
    align-items: stretch;
    justify-content: flex-end;
    padding-inline-start: 0.25rem;
    box-shadow: none;
}

.home-cover-comic .home-cover-fallback[b-9p51uu9pa6]::before,
.home-cover-comic .home-cover-fallback[b-9p51uu9pa6]::after {
    display: none;
}

.home-comic-face[b-9p51uu9pa6] {
    flex: 1 1 auto;
    min-block-size: 0;
    display: grid;
    grid-template-columns: 1fr 1fr;
    grid-template-rows: 1fr 1fr;
    gap: 3px;
    margin-block-end: var(--bk-space-1);
}

.home-comic-panel[b-9p51uu9pa6] {
    background: var(--bk-cloth-3);
}

.home-comic-panel:first-child[b-9p51uu9pa6] {
    background: var(--bk-highlight);
}

.home-comic-panel:nth-child(2)[b-9p51uu9pa6] {
    background: var(--bk-cloth-1);
}

.home-comic-panel:last-child[b-9p51uu9pa6] {
    grid-column: 1 / -1;
}

/* THE PHONE: a bezel in ink, the mockup's ~26px radius, standing in the
   stage's second column where its area overlaps the window's. Its WIDTH is
   PROPORTIONAL (the column's own 46%) with a fixed ceiling (14.5rem), so it
   stays a phone at every width instead of growing into a second window, and
   its HEIGHT is the device's own proportion (10:21) — the frame is a VIEWPORT
   now, not a text-sized box, because the page inside it turns (below). The
   `overflow: hidden` clip is the BEZEL'S and not the page's: the rounded
   corners clip at the frame the way a real screen clips, and the page rule
   below keeps its readable text and no clip or clamp of its own, which is
   what HomeCopyTests pins. The page's padding keeps its text off the curved
   corners. */
.home-preview-phone[b-9p51uu9pa6] {
    grid-column: 2;
    grid-row: 1;
    justify-self: end;
    align-self: start;
    /* THE CALIBRATED MARGIN, and the drawing's one geometric rule (Home.razor
       spells it out): the "Continue reading" row's bottom edge at this
       breakpoint plus the row's own gap, so the phone's top edge sits below
       the row and cannot clip "Pride and Prejudice" however the columns fall.
       The row is fixed furniture — its cover and its text are sized in rem,
       and the row title never wraps — so its height does not move with the
       viewport and one number per breakpoint block pins the phone's top.
       Measured in the render check at 390px and at the wide measure. */
    margin-block-start: 10.25rem;
    inline-size: 100%;
    max-inline-size: 14.5rem;
    /* SHORTER ON A PHONE: at 10/21 the frame ran ~350px tall beside a
       laptop half its height and left a long blank strip under the window.
       10/16 cuts that strip by about 80px (the frame still runs ~125px past
       the window's foot, which is the overlap the drawing wants); the wider
       stage below
       restores the real phone's proportion. */
    aspect-ratio: 10 / 16;
    display: flex;
    border: 7px solid var(--bk-shadow-ink);
    border-radius: 26px;
    background: var(--bk-surface);
    overflow: hidden;
}

/* THE PHONE'S PAGE IS THE BOOK'S OWN PAGE — real text, the public-domain
   opening paragraphs of Pride and Prejudice — and it stays readable before it
   stays picture-perfect. The reading face at 15px (0.9375rem) — THE FLOOR
   THIS PAGE'S REVIEW SET, AND IT HOLDS AT EVERY WIDTH (HomeCopyTests pins it
   on this very rule): the step-down that used to live below 48rem left the
   phone width's page at 9px, text a reader cannot read. THE TYPE DOES NOT
   SHRINK TO FIT THE FRAME ANY MORE — the page TURNS inside it instead (the
   wrapper below), which is why this rule carries no clamp and no hidden
   overflow of its own: the clip is the bezel's job above. Line-height 1.5
   rather than the excerpt's 1.6: this is a page of a BOOK, set tight like
   one. */
.home-preview-page[b-9p51uu9pa6] {
    flex: 1 1 auto;
    padding: var(--bk-space-2);
    font-family: var(--bk-font-reading);
    font-size: 0.9375rem;
    line-height: 1.5;
    color: var(--bk-muted);
}

.home-preview-page p[b-9p51uu9pa6] {
    margin: 0;
}

/* A BOOK PAGE INDENTS ITS PARAGRAPHS instead of spacing them: the chapter
   head opens the page, the drop-cap paragraph opens the chapter, and every
   paragraph after it starts with the indent a printed page sets. */
.home-preview-page-move p:nth-child(n+3)[b-9p51uu9pa6] {
    text-indent: 1.2em;
}

/* THE PAGE TURNS — the drawing's one moving part after the quill line in the
   headline. The inner wrapper drifts up about 46% of the text over fourteen
   seconds and back again, ease-in-out both ways, forever, starting after a
   second: slow enough to read against and never the thing a visitor watches.
   CSS only, like every other pixel this page is allowed to move. */
@keyframes home-page-turn-b-9p51uu9pa6 {
    from {
        transform: translateY(0);
    }

    to {
        transform: translateY(-46%);
    }
}

.home-preview-page-move[b-9p51uu9pa6] {
    animation: home-page-turn-b-9p51uu9pa6 14s ease-in-out 1s infinite alternate;
}

/* LESS MOTION MEANS NO MOTION: the drift stops and the page rests at its top
   — the finished state is the state that needs no animation to be true. */
@media (prefers-reduced-motion: reduce) {
    .home-preview-page-move[b-9p51uu9pa6] {
        animation: none;
    }
}

/* "Chapter I." — the chapter the page opens, set as a chapter head: centred,
   small caps, tracked, in the ink the heading of a page wears. */
.home-preview-chapter[b-9p51uu9pa6] {
    margin-block-end: var(--bk-space-2);
    font-variant: all-small-caps;
    letter-spacing: 0.12em;
    text-align: center;
    color: var(--bk-text);
}

/* THE PAGE'S DROP CAP — the same decorated initial the hero's lede opens
   with, and for the same reason: the picture is a book page, and a book page
   opens its first chapter with one. */
.home-preview-lead[b-9p51uu9pa6]::first-letter {
    float: left;
    font-size: 3em;
    line-height: 0.85;
    font-weight: 600;
    color: var(--bk-accent);
    padding-block-start: 3px;
    padding-inline-end: 5px;
}

@container home (min-width: 48rem) {
    /* THE DRAWING SCALES UP WITH ITS COLUMN — larger covers, larger laptop
       text, a larger phone — because from here it is the visual focus and the
       pitch beside it is the caption. */
    .home-preview-cover[b-9p51uu9pa6] {
        inline-size: 3.75rem;
    }

    .home-preview-h[b-9p51uu9pa6] {
        font-size: 1.5rem;
    }

    .home-preview-title[b-9p51uu9pa6] {
        font-size: 1.1875rem;
    }

    .home-preview-meta[b-9p51uu9pa6] {
        font-size: 0.875rem;
    }

    /* FOUR PLATES, and the row widens to the fraction of the window the phone
       clearance allows — about 68% (the grid's comment above). */
    .home-preview-grid[b-9p51uu9pa6] {
        inline-size: 71%;
        grid-template-columns: repeat(4, minmax(0, 1fr));
    }

    .home-preview-grid > :nth-child(n+4)[b-9p51uu9pa6] {
        display: block;
    }

    .home-preview-grid .home-cover-title[b-9p51uu9pa6] {
        /* 0.65rem: the wide row's plate is ~100px wide at the narrowest
           measure this block covers (768px), and "Middlemarch" sets 78px at
           10.4px — whole between the plate's edges, with air left over. */
        font-size: 0.65rem;
    }

    /* THE PHONE'S TOP EDGE, recalibrated for this block's taller row — its
       cover wears the 3.75rem step here — same rule as the margin above. */
    .home-preview-phone[b-9p51uu9pa6] {
        margin-block-start: 12.25rem;
        aspect-ratio: 10 / 21;
    }
}

/* ── THE COVER PLATE: CLOTH, WITH A REAL COVER LAID OVER ──────────────────

   The library's genuinely good idea, reused here twice (the preview's covers
   and the "Recently added" strip): a book with no cover is still recognisably
   a book — a quiet plate bound in one of five cloths chosen from the book's
   id, with the title stamped in foil. When a cover image exists it is laid
   over the top rather than swapped in, so a cover that 404s or is still being
   derived degrades to the plate instead of to a broken-image glyph: the cover
   is a background image on a span (Home.razor says why, not an <img>), a
   failed background paints nothing, and the plate is ALWAYS painted
   underneath.

   The plate is CLOTH — one of the five fixed --bk-cloth-1…5 material tokens,
   the same in every theme — chosen through the data-cloth attribute rather
   than an inline colour: the five cloths are a closed set the token layer
   owns, so the pick is a number and the colour stays a token. */
.home-cover[b-9p51uu9pa6] {
    position: relative;
    display: block;
    aspect-ratio: 2 / 3;
    overflow: hidden;
    border: var(--bk-edge-width) solid var(--bk-edge);
    border-radius: var(--bk-radius-sm);
    background-color: var(--bk-cloth-1);
}

/* THE FIVE CLOTHS, one rule per binding. MATERIALS, not theme roles:
   --bk-cloth-1…5 are fills and decoration only (plates, spines) and never
   text or icon ink. The base rule above is cloth 1 for anything with no
   data-cloth. */
.home-cover[data-cloth="2"][b-9p51uu9pa6] {
    background-color: var(--bk-cloth-2);
}

.home-cover[data-cloth="3"][b-9p51uu9pa6] {
    background-color: var(--bk-cloth-3);
}

.home-cover[data-cloth="4"][b-9p51uu9pa6] {
    background-color: var(--bk-cloth-4);
}

.home-cover[data-cloth="5"][b-9p51uu9pa6] {
    background-color: var(--bk-cloth-5);
}

/* The typeset fallback: the title centred on the plate, FOIL LETTERING ON
   CLOTH — the one place --bk-foil may be lettering, since the plate is a
   material and not a surface. Set in the reading face at 700, ruled above and
   below in brass (see .home-cover-title), with a thin inset frame so it reads
   as a deliberate cover design. Never a broken-image glyph — when a cover
   fails, THIS is what the user sees. */
.home-cover-fallback[b-9p51uu9pa6] {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0.2rem;
    /* The spine band below takes 6px of the inline-start edge, so the title
       keeps its air BESIDE it. The padding is 0.2rem and the band 6px because
       a measured stamped title needs every point of the rest: "Middlemarch"
       sets 59px in Literata at 8px and the 390px phone's plate gives the
       title 69px with this geometry, 53px with the old one. */
    padding-inline-start: calc(0.2rem + 6px);
    box-shadow: inset 0 0 0 3px color-mix(in srgb, var(--bk-foil) 25%, transparent);
    color: var(--bk-foil);
    font-family: var(--bk-font-reading);
    font-size: 0.75rem;
    font-weight: 700;
    line-height: 1.25;
    text-align: center;
    overflow: hidden;
}

/* THE BRASS RULES ABOVE AND BELOW THE TITLE (#159's one brass, beside the
   spine bands). Decoration only — --bk-highlight never carries meaning and
   never sets a glyph here (ColourRuleTests pins that); it frames the foil
   lettering the way a stamped cover is framed. */
/* THE STAMPED TITLES ON SMALL COVERS — the fit rule the review asked for
   after "The Broken Cartographe(r)" was seen live in the 4-up strip at 390px
   (review #10): a title wider than the plate overflowed it and was CUT by the
   fallback's overflow, and the hyphenation that unclipped it broke words the
   way no cover ever stamps one. THE PLATE NOW CARRIES A SHORT FORM: one line
   at a size that fits — 0.5rem, the size at which "Middlemarch" (59px in
   Literata at 8px) sits inside a 390px phone's plate between its rules, with
   room to spare at 360px too — ellipsised inside the plate (the review allows
   exactly that), never cut and never crossing the brass rules that frame it.
   THE FULL TITLE STAYS IN THE CAPTION BELOW the cover; that is where a reader
   reads it. `min-inline-size: 0` and a 100% ceiling because the stamp is a
   flex item in the fallback: without them its content width pushes past the
   plate and the ellipsis never appears. */
.home-cover-title[b-9p51uu9pa6] {
    border-block: 1px solid var(--bk-highlight);
    padding-block: var(--bk-space-2);
    font-size: 0.5rem;
    max-inline-size: 100%;
    min-inline-size: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* THE SPINE BAND, drawn the way the library card draws it: a 6px stripe down
   the inline-start edge that is a DARKER BAND OF WHICHEVER CLOTH lies under it
   — --bk-shadow-ink at reduced opacity, because the edge turns LIGHT in the
   dark themes and the cloth does not change with the theme. 6px rather than
   the library card's 9px: these plates are 60–135px wide and the band is
   paid for out of the title's width (see the fallback above). */
.home-cover-fallback[b-9p51uu9pa6]::before {
    content: "";
    position: absolute;
    inset-block: 0;
    inset-inline-start: 0;
    inline-size: 6px;
    background: var(--bk-shadow-ink);
    opacity: 0.3;
}

/* Two bands from one pseudo-element: the block itself is the band at 10px,
   and its unblurred shadow — same size, dropped 5px — is the band at 15px. */
.home-cover-fallback[b-9p51uu9pa6]::after {
    content: "";
    position: absolute;
    inset-block-start: 10px;
    inset-inline-start: 0;
    inline-size: 6px;
    block-size: 2px;
    background: var(--bk-highlight);
    box-shadow: 0 5px 0 var(--bk-highlight);
}

/* The real cover sits over the fallback as a BACKGROUND IMAGE on this span
   (Home.razor says why it is not an <img>): when its bytes arrive it covers
   the plate completely; before then, and after a 404, it paints nothing and
   the plate with its stamped title simply remains. */
.home-cover-image[b-9p51uu9pa6] {
    position: absolute;
    inset: 0;
    background-position: center;
    background-size: cover;
    background-repeat: no-repeat;
}

/* ── THE COLOPHON ─────────────────────────────────────────────────────────

   The strip a title page prints under its opening: a rule, four cells, and
   the book's particulars. On a phone it sets 2×2 — the mockup's proportion,
   with the vertical rules between the columns and one across the middle —
   and from 48rem all four stand in one row divided by vertical rules, with
   the same top and bottom rule at every width. Labels small caps, values in
   the reading face: the colophon a book actually prints. */
.home-colophon[b-9p51uu9pa6] {
    display: grid;
    grid-template-columns: 1fr 1fr;
    border-block: 1px solid var(--bk-border);
    font-family: var(--bk-font-reading);
}

.home-colophon-cell[b-9p51uu9pa6] {
    display: flex;
    flex-direction: column;
    gap: var(--bk-space-1);
    padding: var(--bk-space-4) var(--bk-space-3);
    border-inline-end: 1px solid var(--bk-border);
    border-block-end: 1px solid var(--bk-border);
}

/* The 2×2: no right rule on the second column, no bottom rule on the second
   row — the strip's own top and bottom rules close the box. */
.home-colophon-cell:nth-child(2n)[b-9p51uu9pa6] {
    border-inline-end: 0;
}

.home-colophon-cell:nth-child(n+3)[b-9p51uu9pa6] {
    border-block-end: 0;
}

.home-colophon-label[b-9p51uu9pa6] {
    font-variant: all-small-caps;
    letter-spacing: 0.14em;
    font-size: var(--bk-text-xs);
    color: var(--bk-muted);
}

.home-colophon-value[b-9p51uu9pa6] {
    /* --bk-text-sm and not the base step: the format list is the widest value
       in the strip and Literata is wide — at 16px "EPUB · PDF · MOBI · AZW3 ·
       CBZ · CBR" is 365px against a 274px cell and breaks its last name onto
       a line of its own. At this step the two long values set as two balanced
       lines instead, which reads as set type rather than as an overflow. */
    font-size: var(--bk-text-sm);
    line-height: var(--bk-leading-normal);
    color: var(--bk-text);
    text-wrap: balance;
}

/* "No. 1" and "MMXXVI" wear the display size — the two particulars a title
   page sets large in its colophon, at the mockup's 20px. */
.home-colophon-display[b-9p51uu9pa6] {
    font-size: var(--bk-text-md);
    font-weight: 600;
    line-height: var(--bk-leading-tight);
    color: var(--bk-text);
}

@container home (min-width: 48rem) {
    .home-colophon[b-9p51uu9pa6] {
        grid-template-columns: repeat(4, 1fr);
    }

    .home-colophon-cell[b-9p51uu9pa6] {
        padding: var(--bk-space-5) var(--bk-space-4);
        border-block-end: 0;
        border-inline-end: 1px solid var(--bk-border);
    }

    .home-colophon-cell:nth-child(2n)[b-9p51uu9pa6] {
        border-inline-end: 1px solid var(--bk-border);
    }

    .home-colophon-cell:last-child[b-9p51uu9pa6] {
        border-inline-end: 0;
    }
}

/* ── HOW IT WORKS ─────────────────────────────────────────────────────────

   Three steps, one column on a phone and three across above 48rem. A real
   <ol>, so the order is the list's and not three hand-set glyphs a screen
   reader reads as body text. No marker is ever PAINTED, though — .home-step
   below has no list-style and a grid/flex item has no marker box — so this
   list carries no list-style and no ::marker rule; the icon is decoration
   beside the heading, aria-hidden next to text that says the same thing. */
.home-steps[b-9p51uu9pa6] {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--bk-space-5);
    margin: 0;
    padding: 0;
}

/* ON A PHONE THE STEP IS A GRID, and that is the review's density fix (#4):
   the icon sits BESIDE its title on one line instead of on a line of its own,
   the note takes the row under them both, and every gap between the three is
   HALVED from the 0.5rem the stacked step wore. The grid does it without a
   wrapper element around icon and title; the 48rem block below returns the
   step to the unchanged stacked column where there is room for it. */
.home-step[b-9p51uu9pa6] {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    align-items: center;
    gap: var(--bk-space-1);
    padding-block-start: var(--bk-space-3);
    border-block-start: 1px solid var(--bk-border);
}

.home-step-icon[b-9p51uu9pa6] {
    grid-column: 1;
    inline-size: 1.75rem;
    block-size: 1.75rem;
    color: var(--bk-accent);
}

.home-step-title[b-9p51uu9pa6] {
    grid-column: 2;
    font-family: var(--bk-font-reading);
    font-size: var(--bk-text-base);
    font-weight: 600;
    line-height: var(--bk-leading-tight);
}

.home-step-note[b-9p51uu9pa6] {
    grid-column: 1 / -1;
    margin: 0;
    font-size: var(--bk-text-sm);
    line-height: var(--bk-leading-reading);
    color: var(--bk-muted);
    text-wrap: pretty;
}

@container home (min-width: 48rem) {
    .home-steps[b-9p51uu9pa6] {
        grid-template-columns: repeat(3, minmax(0, 1fr));
        gap: var(--bk-space-6);
    }

    .home-step[b-9p51uu9pa6] {
        display: flex;
        flex-direction: column;
        /* The phone grid above centres icon and title on one line; the
           stacked column must not inherit that and centre them across the
           step, so it aligns to the step's start like the original. */
        align-items: flex-start;
        gap: var(--bk-space-2);
    }

    .home-step-title[b-9p51uu9pa6] {
        font-size: var(--bk-text-md);
    }

    .home-step-note[b-9p51uu9pa6] {
        font-size: var(--bk-text-base);
    }
}

/* ── YOUR BOOKS STAY YOURS ────────────────────────────────────────────────

   Three promises, each an OPEN COLUMN that opens with the .bk-rule hairline
   the shelf block above set the pattern for — so a promise is a ruled entry,
   not a card: no box, no tint, and no accent rule of its own (the words are
   the emphasis). The promise link is a row of its own at the foot of the
   promise, so it gets the 44px target rather than the inline-link
   exemption. */
.home-promises[b-9p51uu9pa6] {
    display: grid;
    grid-template-columns: 1fr;
    column-gap: var(--bk-space-6);
    margin: var(--bk-space-2) 0 0;
    padding: 0;
    list-style: none;
}

.home-promise[b-9p51uu9pa6] {
    display: flex;
    flex-direction: column;
    gap: var(--bk-space-2);
    padding-block-end: var(--bk-space-5);
}

.home-promise > .bk-rule[b-9p51uu9pa6] {
    /* The hairline's own margin plus the column's gap lands on the shelf
       block's rule-to-title rhythm (space-3). */
    margin-block-end: var(--bk-space-1);
}

.home-promise-title[b-9p51uu9pa6] {
    font-family: var(--bk-font-reading);
    font-size: var(--bk-text-base);
    font-weight: 600;
    line-height: var(--bk-leading-tight);
}

.home-promise-note[b-9p51uu9pa6] {
    margin: 0;
    font-size: var(--bk-text-sm);
    line-height: var(--bk-leading-reading);
    color: var(--bk-muted);
    text-wrap: pretty;
}

/* 44px BOTH WAYS: min-block-size alone left "Terms" a 30px-wide target, and
   the inline-link exemption does not cover this link — it is a row of its own
   at the foot of the promise, not a phrase inside one. The padding-inline
   widens the smallest word past 2.75rem, and the minimum is what a one-word
   link like "Privacy" falls back to. */
.home-promise-link[b-9p51uu9pa6] {
    display: inline-flex;
    align-items: center;
    align-self: flex-start;
    min-block-size: 2.75rem;
    min-inline-size: 2.75rem;
    padding-inline: var(--bk-space-1);
    font-size: var(--bk-text-sm);
    font-weight: 600;
    color: var(--bk-accent);
}

@container home (min-width: 48rem) {
    .home-promises[b-9p51uu9pa6] {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }

    .home-promise-title[b-9p51uu9pa6] {
        font-size: var(--bk-text-md);
    }

    .home-promise-note[b-9p51uu9pa6] {
        font-size: var(--bk-text-base);
    }
}

/* ── THE PLAN LEDGER ──────────────────────────────────────────────────────

   Two OPEN COLUMNS — Free and Pro — laid out as a ledger: the plan name heads
   its column and every fact is a ruled row under it, the hairline being the
   separator and nothing more. No panel and no tint around a plan (the boxes
   this replaces said "product tier" louder than the numbers did), one column
   on a phone and two across above 48rem: the teaser for /pricing, which owns
   the full comparison table. Every number in them is rendered by Home.razor's
   helpers from the `plans` row. */
.home-plan-grid[b-9p51uu9pa6] {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--bk-space-4);
}

.home-plan[b-9p51uu9pa6] {
    display: flex;
    flex-direction: column;
    gap: var(--bk-space-3);
}

.home-plan-name[b-9p51uu9pa6] {
    margin: 0;
    font-family: var(--bk-font-reading);
    font-size: var(--bk-text-md);
    font-weight: 700;
    line-height: var(--bk-leading-tight);
}

.home-plan-name-pro[b-9p51uu9pa6] {
    color: var(--bk-accent);
}

/* A real <dl>: each value is announced with the label that names it, which is
   the same argument the old comparison table made for being a <table>. Every
   row opens with the hairline — with no panel around the plan, the ledger's
   rules are the only separators there are. */
.home-plan-facts[b-9p51uu9pa6] {
    display: flex;
    flex-direction: column;
    margin: 0;
}

.home-plan-fact[b-9p51uu9pa6] {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--bk-space-3);
    padding-block: var(--bk-space-2);
    border-block-start: 1px solid var(--bk-border);
}

.home-plan-fact dt[b-9p51uu9pa6] {
    font-size: var(--bk-text-xs);
    color: var(--bk-muted);
}

.home-plan-fact dd[b-9p51uu9pa6] {
    margin: 0;
    font-size: var(--bk-text-sm);
    text-align: end;
}

/* The price row wears the reading face at heading size: on a plan card the
   number is the headline. */
.home-plan-fact-price dd[b-9p51uu9pa6] {
    font-family: var(--bk-font-reading);
    font-size: var(--bk-text-lg);
    font-weight: 700;
    line-height: var(--bk-leading-tight);
}

.home-plan-price-pro[b-9p51uu9pa6] {
    color: var(--bk-accent);
}

/* "Coming soon" IS A QUIET LABEL (review #6), not the price wearing the
   price's size and colour: back to the row's own muted, normal-sized value.
   The price rule above is for a number a reader is asked to weigh, and here
   there is no number — only the catalogue's answer that the plan is not for
   sale yet. Still _pro.IsComingSoon-driven (Home.razor): the day the row
   carries a price id this class is not applied and the cell turns back into
   a number in the price's own clothes. */
.home-plan-fact-price dd.home-plan-soon[b-9p51uu9pa6] {
    font-family: var(--bk-font-ui);
    font-size: var(--bk-text-sm);
    font-weight: 400;
    color: var(--bk-muted);
}

@container home (min-width: 48rem) {
    .home-plan-grid[b-9p51uu9pa6] {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: var(--bk-space-6);
    }
}

/* ── BANDS ────────────────────────────────────────────────────────────────

   The mockup's full-bleed stripes, as plain stacked bands. The tinted panel
   this band used to wear is gone with the cards inside it: the title-page
   style below the hero is ruled and open — a label, a serif title and
   hairline-separated entries — and a tinted box around any of it would put
   the very furniture back that the rules exist to replace. */
.home-band[b-9p51uu9pa6] {
    display: flex;
    flex-direction: column;
    gap: var(--bk-space-4);
}

.home-note[b-9p51uu9pa6] {
    margin: 0;
    border-inline-start: 3px solid var(--bk-border);
    padding: var(--bk-space-3) var(--bk-space-4);
    font-size: var(--bk-text-sm);
    line-height: var(--bk-leading-reading);
    color: var(--bk-muted);
}

/* ── THE CARD ─────────────────────────────────────────────────────────────

   The signed-in reader's real "Continue reading" card. FLAT, like every static
   card on this page (§4.0). The hard offset shadow is an affordance — it says
   "this picks up" — and it stays on the one control that does, the filled CTA
   and the real Resume button, both of which carry it from the
   .bk-button-primary primitive in app.css. A raised card beside raised
   buttons is two affordances saying different things. */
.home-card[b-9p51uu9pa6] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--bk-space-3);
    padding: var(--bk-space-3);
    border: var(--bk-edge-width) solid var(--bk-edge);
    border-radius: var(--bk-radius-lg);
    background: var(--bk-surface-raised);
}

.home-card-body[b-9p51uu9pa6] {
    flex: 1 1 10rem;
    min-inline-size: 0;
    display: flex;
    flex-direction: column;
    gap: var(--bk-space-1);
}

.home-card-title[b-9p51uu9pa6] {
    font-family: var(--bk-font-reading);
    font-size: var(--bk-text-md);
    font-weight: 600;
    line-height: var(--bk-leading-tight);
}

.home-card-meta[b-9p51uu9pa6] {
    font-size: var(--bk-text-sm);
    color: var(--bk-muted);
}

/* Full width on a phone, so Resume is a full-width target under the thumb
   rather than a button squeezed beside a title; `flex: 0 0 auto` at the width
   where the row has room for it. */
.home-card-action[b-9p51uu9pa6] {
    flex: 1 0 100%;
    font-weight: 600;
}

.home-progress[b-9p51uu9pa6] {
    margin-block-start: var(--bk-space-1);
    block-size: 4px;
    border-radius: var(--bk-radius-sm);
    background: var(--bk-surface-alt);
    overflow: hidden;
}

/* THE PREVIEW'S TRACK SHOWS, unlike the rule above it in the row it sits in:
   the preview's "Continue reading" row is painted --bk-surface-alt, and a
   track in the row's own colour was invisible — the 38% fill read as a
   floating bar with no rail under it. --bk-border-strong is the rail. The
   real card above keeps the track the fill's contrast budget below was
   measured against; in the drawing the percentage is in text beside the bar
   and the art is aria-hidden, so the drawing owes the rail only its
   visibility. */
.home-preview .home-progress[b-9p51uu9pa6] {
    background: var(--bk-border-strong);
}

/* The fill is the ACCENT, not brass. #159 tried --bk-highlight here first and
   it failed its own contrast budget: 2.1-2.5:1 against the track in the light
   and sepia themes, where the accent measures 5.0-7.8:1. This bar has its
   percentage in text above it, but BookCard's twin meter has no number beside
   it — there the bar IS the carrier and owes 1.4.11's 3:1 — and the two
   meters stay one colour. The pixel canvas draws progress in oxblood for the
   same reason, and keeps brass for the spine bands. */
.home-progress-fill[b-9p51uu9pa6] {
    display: block;
    block-size: 100%;
    background: var(--bk-accent);
}

@container home (min-width: 34rem) {
    .home-card[b-9p51uu9pa6] {
        flex-wrap: nowrap;
        padding: var(--bk-space-4);
        gap: var(--bk-space-4);
    }

    .home-card-action[b-9p51uu9pa6] {
        flex: 0 0 auto;
    }
}

/* ── THE BOOK PLATE ───────────────────────────────────────────────────────

   The card's typeset fallback, which is the library's genuinely good idea: a
   book with no cover is still recognisably a book, set in the reading face
   with an oxblood spine. When a cover image exists it is laid over the top
   rather than swapped in, so a cover that 404s or is still being derived
   degrades to the plate instead of to a broken-image glyph. */
.home-plate[b-9p51uu9pa6] {
    position: relative;
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: var(--bk-space-1);
    inline-size: 3.875rem;
    aspect-ratio: 2 / 3;
    padding: var(--bk-space-2) 0.4rem;
    border: var(--bk-edge-width) solid var(--bk-edge);
    border-inline-start: 3px solid var(--bk-accent);
    border-radius: var(--bk-radius-sm);
    background: var(--bk-surface-alt);
    overflow: hidden;
}

/* 6rem AT EVERY WIDTH, and the number was measured rather than chosen. The
   plate has to hold an ordinary one-word title without breaking it: at 5rem the
   content box is 62px and "Middlemarch" sets to 64, which broke to
   "Middlemarc / h" and read as a rendering bug rather than as a cover. 6rem
   gives 79px of content, and the card still has 218px for its text on a 390px
   phone. A title longer than that is clamped and ellipsised, which is the
   honest treatment; a title one character over is not. */
.home-plate-lg[b-9p51uu9pa6] {
    inline-size: 6rem;
}

.home-plate-title[b-9p51uu9pa6] {
    font-family: var(--bk-font-reading);
    font-size: 0.62rem;
    font-weight: 600;
    line-height: var(--bk-leading-tight);
    color: var(--bk-text);
    /* Three lines and then an ellipsis: a long title must not push the author
       out of a 3:2 plate, and clamping is the only thing that holds the plate's
       aspect ratio against arbitrary user data.

       AND THE CLAMP ALONE DOES NOT HELP A SINGLE LONG WORD: measured at 1280,
       "Middlemarch" rendered as "Middlemarc" in a 6rem plate, because there was
       no break opportunity in it to take. `hyphens: auto` rather than
       `overflow-wrap: anywhere`, which was tried first and broke it as
       "Middlema / rch" — a hyphenation dictionary breaks at syllables and shows
       the hyphen, which is what a book spine does. It needs the document
       language, and App.razor's <html lang="en"> supplies it; `break-word` is
       the floor under it for a title with no syllables to break, which is
       usually a URL or a scanning artefact. */
    overflow-wrap: break-word;
    hyphens: auto;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
    overflow: hidden;
}

.home-plate-author[b-9p51uu9pa6] {
    font-size: 0.5rem;
    line-height: var(--bk-leading-tight);
    color: var(--bk-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.home-plate-image[b-9p51uu9pa6] {
    position: absolute;
    inset: 0;
    background-position: center;
    background-size: cover;
    background-repeat: no-repeat;
}

@container home (min-width: 48rem) {
    .home-plate-lg .home-plate-title[b-9p51uu9pa6] {
        /* 0.7rem, not 0.8: at 0.8rem "Middlemarch" sets to 82px inside the
           79px content box the 6rem plate above gives, and broke. The plate
           does not get wider again to fix it — the type does, because past
           6rem the plate starts eating the card's text column on a phone. */
        font-size: 0.7rem;
    }

    .home-plate-lg .home-plate-author[b-9p51uu9pa6] {
        font-size: 0.65rem;
    }
}

/* ── SIGNED IN ────────────────────────────────────────────────────────────

   The heading is smaller than the landing page's h1 and that is deliberate:
   "Continue reading" is a label over the one thing on the screen, not a pitch.
   §6's own words — a reading app's home is the book you are in the middle of —
   and the book should be the largest thing on it. */
.home-continue[b-9p51uu9pa6] {
    display: flex;
    flex-direction: column;
    gap: var(--bk-space-4);
    padding-block-start: var(--bk-space-4);
}

.home-continue-heading[b-9p51uu9pa6] {
    margin: 0;
    font-family: var(--bk-font-reading);
    font-size: var(--bk-text-md);
    font-weight: 700;
    line-height: var(--bk-leading-tight);
}

.home-recent-heading[b-9p51uu9pa6] {
    margin-block-start: var(--bk-space-3);
    font-size: var(--bk-text-md);
}

.home-recent[b-9p51uu9pa6] {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: var(--bk-space-2);
    margin: 0;
    padding: 0;
    list-style: none;
}

/* Four on a phone even when the strip loaded more: the page renders up to
   eight so the wide layout has covers to fill with, and the phone keeps the
   row it was proportioned for. Hidden rather than not rendered, because this
   page is static SSR and cannot know the container's width on the server —
   the container query below is what reveals the rest. */
.home-recent li:nth-child(n+5)[b-9p51uu9pa6] {
    display: none;
}

/* Cover above, WORDS BELOW. The strip used to set its title inside the plate
   at 0.62rem and its author at 0.5rem — texture, not text. The title below is
   the CAPTION (review #10: "the full title stays in the caption below") and
   it is 0.875rem with two lines before the ellipsis — larger than the
   author's 0.8125rem, which is what a title over an author looks like — and
   both set in the same readable text the rest of the page wears. THE
   NEVER-BROKEN-MID-WORD / FITS-BETWEEN-THE-RULES RULE IS THE PLATE STAMP'S
   (.home-cover-title above), not this caption's: the stamp is short form
   inside a small plate, while the caption keeps the FULL title and wraps it
   at spaces with hyphens and break-word as its floor for the single long
   word. The foil title on the cover is decoration beside them (aria-hidden
   in the markup). */
.home-recent-link[b-9p51uu9pa6] {
    display: flex;
    flex-direction: column;
    gap: var(--bk-space-1);
    min-inline-size: 0;
    color: var(--bk-text);
    text-decoration: none;
}

.home-recent-link:focus-visible[b-9p51uu9pa6] {
    outline: 2px solid var(--bk-focus);
    outline-offset: 2px;
}

.home-recent-link:hover .home-cover[b-9p51uu9pa6] {
    border-color: var(--bk-accent);
}

.home-recent-cover[b-9p51uu9pa6] {
    inline-size: 100%;
}

.home-recent-title[b-9p51uu9pa6] {
    font-family: var(--bk-font-reading);
    /* 0.875rem, restored after the fix round dropped it to 0.625rem to fit
       "Cartographer" unwrapped at 360px: the fit rule that needed the
       shrink is the PLATE STAMP's (review #10, .home-cover-title above) and
       it works alone — the caption keeps the full title at its readable size
       and wraps it, ellipsis after two lines. */
    font-size: 0.875rem;
    font-weight: 600;
    line-height: var(--bk-leading-tight);
    /* A title over two lines is a title; a third is a paragraph squeezing the
       author out of the cell. The clamp needs `hyphens` and `break-word`
       beside it for the single long word, the same pair .home-plate-title
       argues for above. */
    overflow-wrap: break-word;
    hyphens: auto;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    overflow: hidden;
}

.home-recent-author[b-9p51uu9pa6] {
    font-size: 0.8125rem;
    line-height: var(--bk-leading-tight);
    color: var(--bk-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

@container home (min-width: 48rem) {
    .home-recent[b-9p51uu9pa6] {
        /* auto-fill off a 7.5rem floor — the plate that shipped at this
           width, not the 6rem that only holds a one-word title and reads
           cramped under cover art. NOT a fixed count. The repeat(4, …) this
           replaces was the phone's proportion worn at every width, and at
           1280px it left the strip a third of the container with empty paper
           under it (#156); a 6rem floor made the same mistake from the other
           side — eleven tracks in the ~76rem container, eight covers at
           ~110px and three empty. At 7.5rem the container holds nine tracks,
           so the eight covers render at ~135px and fill the width the strip
           was meant to fill. auto-fill rather than auto-fit so a short library
           keeps the same plate size instead of stretching three covers across
           a laptop; the empty tracks absorb the leftover width. */
        grid-template-columns: repeat(auto-fill, minmax(7.5rem, 1fr));
        gap: var(--bk-space-4);
    }

    .home-recent li:nth-child(n+5)[b-9p51uu9pa6] {
        display: list-item;
    }

    .home-recent-title[b-9p51uu9pa6] {
        font-size: 1rem;
    }

    .home-recent-author[b-9p51uu9pa6] {
        font-size: 0.875rem;
    }
}
/* /Components/Pages/HomeFeatures.razor.rz.scp.css */
/* ── WHAT'S ON THE SHELF ──────────────────────────────────────────────────

   Six claims separated by hairlines, not cards: every cell opens with the
   .bk-rule primitive from app.css, so the line between two features is the
   same line every other band on the page draws, and no cell grows a box
   around it. MOBILE FIRST — the unqualified values are the 390px phone's —
   and the grid widens through §5.3's two named breakpoints only: two
   columns from 48rem, three from 64rem. All ink and spacing are tokens; the
   plate below is a drawing made of tokens, not an image. */

.home-features-title[b-mj62yi2in8] {
    margin: var(--bk-space-4) 0 0;
    font-family: var(--bk-font-reading);
    font-size: var(--bk-text-lg);
    font-weight: 700;
    line-height: var(--bk-leading-tight);
    text-wrap: balance;
}

.home-features-grid[b-mj62yi2in8] {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    column-gap: var(--bk-space-6);
    margin: var(--bk-space-5) 0 0;
    padding: 0;
    list-style: none;
}

/* The cell is a ruled entry: hairline, serif title, one sentence — the
   hairline IS the separator, so no cell grows a border of its own and the
   last row is closed by the band that follows rather than a rule under it. */
.home-features-cell[b-mj62yi2in8] {
    display: flex;
    flex-direction: column;
    padding-block-end: var(--bk-space-5);
}

.home-features-cell > .bk-rule[b-mj62yi2in8] {
    margin-block-end: var(--bk-space-3);
}

.home-features-cell-title[b-mj62yi2in8] {
    margin: 0 0 var(--bk-space-2);
    font-family: var(--bk-font-reading);
    font-size: var(--bk-text-base);
    font-weight: 600;
    line-height: var(--bk-leading-tight);
}

.home-features-cell-note[b-mj62yi2in8] {
    margin: 0;
    font-size: var(--bk-text-sm);
    line-height: var(--bk-leading-reading);
    color: var(--bk-muted);
    text-wrap: pretty;
}

/* ── THE COMICS CELL ──────────────────────────────────────────────────────

   First and full width at every width (grid-column below), because comics
   on the same shelf as books is the claim this section leads with and it
   earns a row of its own —
   with its plate, and an accent seam where every other cell has a plain one.
   The accent rule is decoration beside the words, never a substitute for
   them (ColourRuleTests: nothing here paints a glyph in --bk-highlight). */
.home-features-comics[b-mj62yi2in8] {
    grid-column: 1 / -1;
}

.home-features-comics > .bk-rule[b-mj62yi2in8] {
    border-block-start: 2px solid var(--bk-accent);
}

.home-features-comics-body[b-mj62yi2in8] {
    display: flex;
    align-items: flex-start;
    gap: var(--bk-space-4);
}

.home-features-comics-text[b-mj62yi2in8] {
    min-inline-size: 0;
}

/* The full-width cell's sentence could run the whole workspace, so it takes
   the lede's measure instead (§7's 60-70 characters). */
.home-features-comics .home-features-cell-note[b-mj62yi2in8] {
    max-inline-size: 34rem;
}

/* THE COMIC PLATE — the home-cover-comic idea rebuilt for this block with
   its own classes: a small book-shaped plate of the ink ground, three flat
   colour panels of tokens the cover plates already wear, and the title
   stamped under them the way every cover stamps one. It is a DRAWING in
   markup and tokens — nothing here is fetched — and it is aria-hidden in the
   markup beside the sentence that says the same thing. */
.home-features-plate[b-mj62yi2in8] {
    flex: 0 0 auto;
    inline-size: 5rem;
    aspect-ratio: 2 / 3;
    display: flex;
    flex-direction: column;
    padding: 0.25rem;
    background: var(--bk-shadow-ink);
}

.home-features-plate-face[b-mj62yi2in8] {
    flex: 1 1 auto;
    min-block-size: 0;
    display: grid;
    grid-template-columns: 1fr 1fr;
    grid-template-rows: 1fr 1fr;
    gap: 3px;
    margin-block-end: var(--bk-space-1);
}

.home-features-plate-frame[b-mj62yi2in8] {
    background: var(--bk-cloth-3);
}

.home-features-plate-frame:first-child[b-mj62yi2in8] {
    background: var(--bk-highlight);
}

.home-features-plate-frame:nth-child(2)[b-mj62yi2in8] {
    background: var(--bk-cloth-1);
}

.home-features-plate-frame:last-child[b-mj62yi2in8] {
    grid-column: 1 / -1;
}

/* FOIL LETTERING ON THE PLATE — the one place --bk-foil sets type, because
   the plate is a material and not a surface. Brass rules above and below the
   stamp frame it the way a cover is framed; the short form and the ellipsis
   are Home's cover-title fit rule, so a title is never broken mid-word. */
.home-features-plate-title[b-mj62yi2in8] {
    border-block: 1px solid var(--bk-highlight);
    padding-block: var(--bk-space-1);
    color: var(--bk-foil);
    font-family: var(--bk-font-reading);
    font-size: 0.5rem;
    font-weight: 700;
    line-height: 1.15;
    text-align: center;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

@media (min-width: 48rem) {
    .home-features-grid[b-mj62yi2in8] {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    /* NO ORPHAN: comics spans the first row, leaving five cells — 2 + 2 + 1
       in two columns — so the last runs the full width rather than sit
       alone in half a row. */
    .home-features-cell:last-child[b-mj62yi2in8] {
        grid-column: 1 / -1;
    }

    .home-features-title[b-mj62yi2in8] {
        font-size: var(--bk-text-xl);
    }

    .home-features-cell-title[b-mj62yi2in8] {
        font-size: var(--bk-text-md);
    }

    .home-features-cell-note[b-mj62yi2in8] {
        font-size: var(--bk-text-base);
    }
}

@media (min-width: 64rem) {
    .home-features-grid[b-mj62yi2in8] {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }

    /* Three columns hold the five cells under comics as 3 + 2; the last
       takes the two slots left so the row closes flush. */
    .home-features-cell:last-child[b-mj62yi2in8] {
        grid-column: span 2;
    }
}
/* /Components/Pages/HomeInkLine.razor.rz.scp.css */
@keyframes home-ink-draw-b-eqbfwkns86{0%{opacity:1}100%{opacity:1;stroke-dashoffset:0}}
.home-ink[b-eqbfwkns86]{overflow:visible}
/* The word boxes carry the script's full ascender-to-descender height, so two
   wrapped rows (the wide pitch column sets "ready wherever / you read.") sit
   further apart than the headline's own lines. Pulling each box in by its
   empty ascender and descender room closes that gap; strokes still paint
   outside it (overflow: visible). */
.home-ink[b-eqbfwkns86]{margin-block:-0.1em}
.home-ink path[b-eqbfwkns86]{fill:none;stroke:currentColor;stroke-linecap:round;stroke-linejoin:round;opacity:0;animation-name:home-ink-draw-b-eqbfwkns86;animation-timing-function:cubic-bezier(.35,.05,.6,1);animation-fill-mode:forwards}
@media (prefers-reduced-motion: reduce){.home-ink path[b-eqbfwkns86]{animation:none;opacity:1;stroke-dashoffset:0 !important}}
/* /Components/Pages/HomeReaders.razor.rz.scp.css */
/*
    ══════════════════════════════════════════════════════════════════════════
    "BUILT WITH ITS FIRST READERS" — the honest social-proof band's sheet.

    Scoped like Home.razor.css, and under the same three rules. Nothing here
    declares a token: every colour, space and type step is a `--bk-*` read
    from the token block, so the band follows light, dark and sepia without
    knowing they exist. The unqualified rules below are the PHONE at 390px,
    and no width is ever SUBTRACTED — there is no `max-width` query here and
    BaseLayerTests would fail one. No `--bk-safe-*`, no `env()` and no
    viewport unit either (§5.2 rules 5 and 1; SafeAreaTests).

    THIS FILE OWNS LITTLE ON PURPOSE. The band wears the title-page
    primitives out of app.css — .bk-section-label and .bk-page-title,
    both pinned by TitlePagePrimitiveTests — so its type and its label
    voice are not re-declared here and cannot drift from the
    pages that wear them. What is left is what the primitives do not know:
    the seam under the label, the link's own target size, and the quotes slot
    that renders nothing today.
*/

/* THE SEAM UNDER THE LABEL. .bk-section-label carries its hairline but no
   block margin of its own — app.css leaves the title stack's rhythm to the
   wearer and says so — so the gap to the heading is set here rather than
   with a flex gap on the section: the heading's own block margin (the
   primitive's space-4) then keeps its single job below it, and no two seams
   can add up to one unintended one. */
.home-readers > .bk-section-label[b-2nnb4bm73f] {
    margin-block-end: var(--bk-space-3);
}

/* THE PARAGRAPH: the lede's book type and measure without its drop cap
   (see the Razor comment). */
.home-readers-text[b-2nnb4bm73f] {
    margin: 0;
    font-family: var(--bk-font-reading);
    font-size: var(--bk-text-md);
    line-height: 1.45;
    color: var(--bk-text);
    max-inline-size: 34rem;
    text-wrap: pretty;
}

/* THE LINK, AND WHY IT IS STILL NOT A BUTTON. It is a row of its own and not
   a sentence fragment inside prose, so WCAG 2.5.8's inline-link exemption
   does not cover it — the same argument Home.razor.css makes for
   a.home-cta-aside, and the same 44px target with it. The accent is the
   global `a` rule's (app.css); only the target, the face and the size are
   set here. */
.home-readers-link-row[b-2nnb4bm73f] {
    margin: var(--bk-space-2) 0 0;
}

a.home-readers-link[b-2nnb4bm73f] {
    display: inline-flex;
    align-items: center;
    min-block-size: 2.75rem;
    font-family: var(--bk-font-ui);
    font-size: var(--bk-text-sm);
}

/* ── THE QUOTES SLOT ────────────────────────────────────────────────────────

   None of this renders today: HomeReaders.razor's list is empty until a real
   tester's words arrive with their permission. It is laid out anyway so the
   first real quote lands on a finished shape rather than on a redesign — one
   figure per quote, each opening with the separator hairline. --bk-border
   because the hairline is the separator (ColourRuleTests): a quote is
   emphasis, not a control boundary. */
.home-readers-quotes[b-2nnb4bm73f] {
    margin: var(--bk-space-5) 0 0;
    padding: 0;
    list-style: none;
}

.home-readers-quote[b-2nnb4bm73f] {
    display: flex;
    flex-direction: column;
    gap: var(--bk-space-2);
    /* The UA's figure and blockquote margins are document margins; this is a
       band's list, and its rhythm is the padding below. */
    margin: 0;
    padding-block: var(--bk-space-4);
    border-block-start: 1px solid var(--bk-border);
}

.home-readers-quote-text[b-2nnb4bm73f] {
    margin: 0;
    font-family: var(--bk-font-reading);
    font-size: var(--bk-text-base);
    line-height: var(--bk-leading-reading);
    color: var(--bk-text);
    text-wrap: pretty;
}

/* The attribution is a sentence ("Ada, alpha tester"), not a label — so it
   takes the fine-line voice (.home-fine in Home.razor.css) and NOT the label
   voice's small caps: app.css keeps that voice in ONE selector list, and a
   copy of its declarations here is exactly the drift the list exists to
   stop. */
.home-readers-quote-who[b-2nnb4bm73f] {
    margin: 0;
    font-family: var(--bk-font-ui);
    font-size: var(--bk-text-sm);
    line-height: var(--bk-leading-normal);
    color: var(--bk-muted);
}
/* /Components/Pages/Welcome.razor.rz.scp.css */
/* Under ImmersiveLayout, whose surface is exactly the screen and never
   scrolls; the welcome screen is its own scrolling region, so a phone in
   landscape can still reach both buttons. The safe-area edges come from
   .bk-standalone-edges in app.css (only layouts and app.css read them). */
.welcome[b-a7pevw7twh] {
    display: flex;
    flex-direction: column;
    box-sizing: border-box;
    block-size: 100%;
    overflow-y: auto;
    color: var(--bk-text);
    text-align: center;
}

.welcome-middle[b-a7pevw7twh] {
    display: flex;
    flex: 1;
    flex-direction: column;
    align-items: center;
    justify-content: center;
}

.welcome-shelf[b-a7pevw7twh] {
    display: flex;
    flex-direction: column;
    align-items: center;
    margin-block-end: 2.5rem;
}

.welcome-books[b-a7pevw7twh] {
    display: flex;
    align-items: flex-end;
    gap: 3px;
}

.welcome-book[b-a7pevw7twh] {
    position: relative;
    border-radius: 2px 2px 0 0;
}

.welcome-book span[b-a7pevw7twh] {
    position: absolute;
    inset-inline: 3px;
    block-size: 2px;
    background: var(--bk-foil);
    opacity: 0.8;
}

.welcome-book-one[b-a7pevw7twh] {
    inline-size: 22px;
    block-size: 96px;
    background: var(--bk-cloth-1);
}

.welcome-book-one span:first-child[b-a7pevw7twh] { inset-block-start: 12px; }
.welcome-book-one span:last-child[b-a7pevw7twh] { inset-block-end: 12px; }

.welcome-book-two[b-a7pevw7twh] {
    inline-size: 17px;
    block-size: 80px;
    background: var(--bk-cloth-2);
}

.welcome-book-two span[b-a7pevw7twh] { inset-block-start: 10px; }

.welcome-book-three[b-a7pevw7twh] {
    inline-size: 26px;
    block-size: 106px;
    background: var(--bk-cloth-3);
}

.welcome-book-three span:first-child[b-a7pevw7twh] { inset-block-start: 14px; }
.welcome-book-three span:last-child[b-a7pevw7twh] { inset-block-start: 19px; block-size: 1px; }

.welcome-book-four[b-a7pevw7twh] {
    inline-size: 19px;
    block-size: 88px;
    background: var(--bk-cloth-4);
}

.welcome-book-four span[b-a7pevw7twh] { inset-block-end: 14px; }

.welcome-book-five[b-a7pevw7twh] {
    inline-size: 15px;
    block-size: 72px;
    background: var(--bk-cloth-5);
}

.welcome-shelf-line[b-a7pevw7twh] {
    inline-size: 148px;
    block-size: 4px;
    border-radius: 1px;
    background: var(--bk-edge);
}

.welcome .bk-title-rule[b-a7pevw7twh] {
    inline-size: 48px;
    margin-block-end: 1rem;
}

.welcome h1[b-a7pevw7twh] {
    margin: 0 0 0.75rem;
    font-family: var(--bk-font-reading);
    font-size: 3rem;
    font-weight: 700;
    line-height: 1;
    letter-spacing: -0.02em;
    animation: welcome-wordmark-b-a7pevw7twh 0.6s cubic-bezier(.35,.05,.6,1) both;
}

@keyframes welcome-wordmark-b-a7pevw7twh {
    /* Negative insets on every side but the swept one: the clip must not crop what hangs
       outside the letters' box, the y's descender above all. */
    from {
        clip-path: inset(-0.3em 100% -0.4em -0.1em);
        opacity: 0.35;
    }

    to {
        clip-path: inset(-0.3em -0.1em -0.4em -0.1em);
        opacity: 1;
    }
}

.welcome-lede[b-a7pevw7twh] {
    max-inline-size: 20rem;
    margin: 0;
    font-family: var(--bk-font-reading);
    font-size: var(--bk-text-md);
    line-height: 1.4;
    text-wrap: balance;
}

.welcome-ink-line[b-a7pevw7twh] {
    display: block;
    font-style: normal;
}

/* As Home.razor.css's .home-h1-ink: the words are separate SVGs laid out
   as wrapping flex rows, so the gap stands in for the word space flex drops,
   and the top padding keeps the tall ascenders off the line above. */
.welcome-ink[b-a7pevw7twh] {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-items: flex-end;
    column-gap: 0.22em;
    padding-block-start: 0.15em;
    color: var(--bk-accent);
    font-size: 1.6em;
}

.welcome-sr-only[b-a7pevw7twh] {
    position: absolute;
    inline-size: 1px;
    block-size: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

.welcome-actions[b-a7pevw7twh] {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    padding-block-start: 1.5rem;
}

.welcome-actions > .bk-button[b-a7pevw7twh] {
    inline-size: 100%;
    min-block-size: 3rem;
}

.welcome-legal[b-a7pevw7twh] {
    display: flex;
    justify-content: center;
    gap: 0.5rem;
    margin-block-start: 0.25rem;
}

.welcome-legal a[b-a7pevw7twh] {
    display: inline-flex;
    align-items: center;
    min-block-size: 2.75rem;
    padding-inline: 0.5rem;
    color: var(--bk-muted);
    font-size: var(--bk-text-xs);
    font-variant: all-small-caps;
    letter-spacing: 0.14em;
    text-decoration: none;
}

.welcome-legal a:hover[b-a7pevw7twh] {
    color: var(--bk-text);
}

@media (min-width: 48rem) {
    .welcome > *[b-a7pevw7twh] {
        inline-size: 100%;
        max-inline-size: 22.5rem;
        margin-inline: auto;
    }
}

@media (max-height: 30rem) {
    .welcome-shelf[b-a7pevw7twh] {
        display: none;
    }
}

@media (prefers-reduced-motion: reduce) {
    .welcome h1[b-a7pevw7twh] {
        animation: none;
        clip-path: none;
        opacity: 1;
    }
}
