/* ═══════════════════════════════════════════════
   library.css — the one stylesheet for every page of the `library` app.

   Supersedes main.css (0.6.x public shell). Sections:
     0 fonts → 1 tokens (day) → 2 tokens (night) → 3 base →
     4 header · nav · disclosures · theme pill · account →
     5 landing scene → 6 footer → 7 modal →
     8 public list and detail pages → 8a coming-soon placeholder →
     9 responsive

   Self-contained on purpose: the public side must not pull in the staff
   bundle. Token values are the same ones the Sreda Obitaniya design system
   carries, because that system was authored *from* this file's ancestor —
   keeping them literal here means the public pages need no import chain.
   ═══════════════════════════════════════════════ */

/* ════ 0. Arsenal ══════════════════════════════ */
@font-face {
    font-family: 'Arsenal';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url("fonts/arsenal-400-latin.654e88b7cee2.woff2") format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+20AC, U+2122;
}
@font-face {
    font-family: 'Arsenal';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url("fonts/arsenal-400-cyrillic.b4c9feb05cb7.woff2") format('woff2');
    unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
    font-family: 'Arsenal';
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url("fonts/arsenal-700-latin.91b43c5422cd.woff2") format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+20AC, U+2122;
}
@font-face {
    font-family: 'Arsenal';
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url("fonts/arsenal-700-cyrillic.529460ada88a.woff2") format('woff2');
    unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

/* ════ 1. Tokens — day (the product default) ═══ */
:root {
    --color-bg:          #fffdf0;   /* beige_main */
    --color-surface:     #ffffff;
    --color-surface-alt: #f0f9ff;   /* light_blue_background */
    --color-border:      #d6e5f0;

    --color-primary:     #00548a;   /* blue_logo_accent */
    --color-accent:      #57beff;   /* light_blue_logo_accent */
    --color-accent-dark: #007ac2;

    --color-text:        #001624;   /* dark_blue_for_texts */
    --color-text-muted:  #5b7284;
    --color-text-light:  #ffffff;

    /* Green palette — public "Sign up" / "Login" confirm actions.
       --color-green is the given base #28a745; the other three are the same
       hue stepped in lightness, so every button state stays on one ramp. */
    --color-green:        #28a745;
    --color-green-hover:  #2fbf52;
    --color-green-active: #1e7e34;
    --color-green-ring:   rgba(40,167,69,0.30);
    --color-error:        #d93025;

    --focus-ring:  0 0 0 3px rgba(87,190,255,0.2);
    --hover-tint:  rgba(87,190,255,0.12);
    --active-tint: rgba(87,190,255,0.22);
    --scrim:       rgba(0,22,36,0.55);

    /* Header accents. The bar itself stays navy in both themes, but its hover
       family is theme-split exactly like the scene's: light blue by day, the
       warm yellow at night. Three weights, because the controls on the bar do
       not all react with the same strength and night should not change that —
       only the hue:
         --nav-hot   solid pill fill (nav tabs, the current language)
         --nav-veil  translucent step (language pills, the account button)
         --nav-tint  faint wash inside a dropdown panel

       The night value is #ffe89a, the same warm yellow the rack and shelf
       hotspots use, so one accent runs through the whole night interface.
       Note the design system's own ui.css would give the nav --color-accent
       (#ffeb57, the vivid lemon) instead; the paler scene colour was chosen
       deliberately to match the section images. */
    --nav-hot:        #57beff;
    --nav-hot-ink:    #001624;
    --nav-veil:       rgba(255,255,255,0.25);
    --nav-veil-press: rgba(255,255,255,0.35);
    --nav-veil-ink:   #ffffff;
    --nav-tint:       rgba(87,190,255,0.12);

    /* Scene accents. The resting outline is the same light blue in both
       themes — only the hot (hover/focus) accent flips, because navy is
       invisible against the night plate. Defining it once here is what keeps
       sections 5's rules from needing a [data-theme] twin apiece. */
    --scene-line:      #57beff;
    --scene-line-rest: rgba(87,190,255,0.06);
    --scene-hot:       #00548a;
    --scene-hot-fill:  rgba(0,84,138,0.18);
    --scene-hot-ink:   #ffffff;
    --scene-wash:      transparent;
    /* Hover wash for the scene's own pills and buttons — the counterpart of
       --nav-tint on the header. Theme-split for the same reason: a blue tint
       on the night panel reads cold against the warm accent everywhere else. */
    --scene-tint:      rgba(87,190,255,0.12);

    /* Tones for the brand drawings shown inline (the coming-soon chameleons).
       The drawings are the brand's own line art — ink only, no fills — so they
       take three groups: the line, the eye's pupil and the chevron stripes.
       Day is the brand blue, with the lighter logo blue on the stripes so they
       read as markings rather than as more outline; night is the warm lamp
       yellow of the scene hotspots throughout, so the pair reads as lit, like
       the room, rather than as more type. */
    --mascot-line:     #00548a;
    --mascot-pupil:    #001624;
    --mascot-stripe:   #57beff;

    --font-family: 'Arsenal', Arial, sans-serif;
    --font-size-xs:   14px;
    --font-size-sm:   17px;
    --font-size-base: 19px;
    --font-size-lg:   22px;
    --font-size-xl:   24px;
    --font-size-nav:  21px;
    --font-size-auth: 20px;
    --font-size-key:  18px;
    --font-size-detail-title: 36px;
    --font-size-detail-title-mobile: 28px;
    --letter-spacing-label: 0.5px;

    /* Status ramp. Carried by colour alone, as the design system specifies -
       no icon, no badge - and applied to the RAW machine value as a class. */
    --color-status-ok:   #558a00;
    --color-status-pre:  #00108a;
    --color-status-warn: #8a7a00;
    --color-status-err:  #8a0055;
    --color-status-na:   #9ca3af;

    --space-xs: 4px;
    --space-sm: 8px;
    --space-md: 16px;
    --space-lg: 24px;
    --space-xl: 32px;

    --radius-sm:   4px;
    --radius-md:   8px;
    --radius-lg:   12px;
    --radius-pill: 999px;

    --shadow-sm:    0 1px 3px rgba(0,84,138,0.08);
    --shadow-md:    0 4px 12px rgba(0,84,138,0.12);
    --shadow-pill:  0 4px 12px rgba(0,84,138,0.18);
    --shadow-card:  0 8px 24px rgba(0,84,138,0.16);
    --shadow-panel: 0 8px 24px rgba(0,84,138,0.22);
    --shadow-modal: 0 12px 32px rgba(0,22,36,0.3);

    --header-height: 6.3rem;
    --page-max: 1760px;
}

/* ════ 2. Tokens — night ═══════════════════════
   Authored by reversing day mode: the dark-blue text colour becomes the page,
   the warm beige becomes the type, and the light is a warm yellow. Shadows go
   two-part — a hard umbra edge plus a wide diffuse sub-umbra — because the
   day shadows are blue-tinted and vanish on a dark ground. */
[data-theme="night"] {
    --color-bg:          #001624;
    --color-surface:     #002638;
    --color-surface-alt: #003557;
    --color-border:      #0f4c6e;

    --color-primary:     #57beff;
    --color-accent:      #ffeb57;
    --color-accent-dark: #bde5ff;

    --color-text:        #fffdf0;
    --color-text-muted:  #9db8c9;
    --color-text-light:  #001624;

    --focus-ring:  0 0 0 3px rgba(255,235,87,0.35);
    --hover-tint:  rgba(87,190,255,0.16);
    --active-tint: rgba(87,190,255,0.26);
    --scrim:       rgba(0,10,18,0.72);

    /* The header's hover family goes warm. Same #ffe89a as the scene below,
       so hovering a nav tab and hovering a bookcase read as one interface. */
    --nav-hot:        #ffe89a;
    --nav-hot-ink:    #001624;
    --nav-veil:       rgba(255,232,154,0.30);
    --nav-veil-press: rgba(255,232,154,0.42);
    --nav-veil-ink:   #ffe89a;
    /* Stronger than day's 0.12: the same wash over the dark dropdown surface
       reads far fainter than it does over the white one, so the night value is
       raised to keep the account menu's hover as legible as the nav bar's. */
    --nav-tint:       rgba(255,232,154,0.26);

    /* Warm lamp light — the night scene is lit, not merely darkened. */
    --scene-hot:      #ffe89a;
    --scene-hot-fill: rgba(255,232,154,0.20);
    --scene-hot-ink:  #001624;
    --scene-tint:     rgba(255,232,154,0.26);
    --mascot-line:    #ffe89a;
    --mascot-pupil:   #ffe89a;
    --mascot-stripe:  #ffe89a;

    /* The deep status accents vanish against a dark page, so night uses the
       bright "additional" tones instead - readme.md, Colour. */
    --color-status-ok:   #beff57;
    --color-status-pre:  #576aff;
    --color-status-warn: #ffeb57;
    --color-status-err:  #ff57be;
    --color-status-na:   #6b8496;

    --scene-wash: radial-gradient(58% 86% at 50% 22%,
                                  rgba(255,214,132,0.2),
                                  rgba(14,24,40,0.66) 72%);

    --shadow-sm:    0 1px 0 rgba(0,0,0,0.6), 0 2px 10px rgba(0,0,0,0.45);
    --shadow-md:    0 2px 0 rgba(0,0,0,0.7), 0 10px 28px rgba(0,0,0,0.5);
    --shadow-pill:  0 2px 0 rgba(0,0,0,0.7), 0 10px 28px rgba(0,0,0,0.5);
    --shadow-card:  0 3px 0 rgba(0,0,0,0.7), 0 18px 44px rgba(0,0,0,0.55);
    --shadow-panel: 0 3px 0 rgba(0,0,0,0.7), 0 18px 44px rgba(0,0,0,0.55);
    --shadow-modal: 0 3px 0 rgba(0,0,0,0.8), 0 24px 60px rgba(0,0,0,0.65);
}

/* ════ 3. Base ═════════════════════════════════ */
body {
    font-family: var(--font-family);
    background-color: var(--color-bg);
    color: var(--color-text);
    margin: 0;
    padding: 0;
}

.page-main {
    min-height: 60vh;
}

/* The `hidden` attribute is a UA rule of the lowest possible weight, so ANY
   author `display` declaration silently beats it — and most of the scene's
   toggled panels set `display: flex`. Without this they stay on screen while
   `el.hidden` reports true, which is a bug that reads as correct in script. */
[hidden] {
    display: none !important;
}

/* ════ 4. Header ═══════════════════════════════ */
.header {
    position: sticky;
    top: 0;
    z-index: 100;
    background-color: #00548a;   /* the navy bar does not invert at night */
    color: #ffffff;
    padding: 0;
    border-bottom: 3px solid #57beff;
    box-shadow: var(--shadow-md);
}

/* Same 96% and the same absence of a cap as .scene-main, so the logo sits
   exactly above the room's left edge and the account button above its right.
   Capping this while the room spreads would leave the header floating inside
   the artwork's margins on any wide display. */
.header-container {
    width: 96%;
    margin: 0 auto;
    display: flex;
    align-items: center;
    height: var(--header-height);
    gap: var(--space-sm);
}

.logo {
    display: flex;
    align-items: center;
    /* The header's own gap is 8px; this adds three quarters of it again, so the
       lockup and the language pill read as two things rather than one run. Set
       on the logo rather than on the container's gap, which spaces every other
       control in the bar too. Each breakpoint below keeps the same proportion. */
    margin-right: 6px;
    /* stretch to full header height so the image centers exactly on the
       vertical axis; margin-top offsets the 3px bottom accent border so the
       optical center matches the visible header strip */
    align-self: stretch;
    margin-top: 3px;
    padding: 6px 12px;
    border-radius: var(--radius-pill);
    background-color: transparent;
    flex: none;
    /* The lockup is an <img>, but this <a> still needs a colour: if the image
       ever fails, the alt text renders, and without this it inherits the UA's
       default link blue — near invisible on the navy bar. */
    color: #ffffff;
    font-size: var(--font-size-xl);
    font-weight: 700;
    letter-spacing: 0.5px;
    white-space: nowrap;
    text-decoration: none;
    transition: background-color 0.2s, color 0.2s;
}
.logo:hover {
    background-color: var(--nav-hot);
    color: var(--nav-hot-ink);
}
.logo img {
    height: 4rem;
    width: auto;
    display: block;
}

.nav-toggle {
    display: none;
    background: transparent;
    border: 1px solid rgba(255,255,255,0.4);
    border-radius: var(--radius-md);
    color: #ffffff;
    font-family: inherit;
    font-size: var(--font-size-lg);
    padding: 4px 10px;
    cursor: pointer;
    white-space: nowrap;
}

/* The nav sits hard right; the lang switcher and theme pill hold the left,
   immediately after the logo, so the day/night control keeps the position the
   design gives it and the language menu sits to its left (plan D3). */
.nav-bar {
    display: flex;
    align-items: center;
    gap: var(--space-xs);
    flex: 1;
    flex-wrap: wrap;
    justify-content: flex-end;
}

.nav-bar > a,
.nav-menu-toggle {
    color: rgba(255,255,255,0.85);
    text-decoration: none;
    padding: 6px 12px;
    background-color: transparent;
    border: none;
    border-radius: var(--radius-pill);
    font-family: inherit;
    font-size: var(--font-size-nav);
    font-weight: 500;
    transition: background-color 0.2s, color 0.2s;
    white-space: nowrap;
    cursor: pointer;
}

.nav-bar > a:hover,
.nav-bar > a.active,
.nav-menu-toggle:hover,
.nav-menu.is-open .nav-menu-toggle,
.nav-menu.has-active .nav-menu-toggle {
    background-color: var(--nav-hot);
    color: var(--nav-hot-ink);
}

.nav-bar > a:focus-visible,
.nav-menu-toggle:focus-visible,
.lang-current:focus-visible,
.lang-option:focus-visible,
.theme-option:focus-visible,
.account-toggle:focus-visible {
    outline: 2px solid var(--nav-hot);
    outline-offset: 2px;
}

/* ── 4a. Disclosure menus (language + the "library" section column) ──
   One W3C APG disclosure pattern, two instances. Both ship with the list
   visible and collapse only once JS adds .is-enhanced, so with scripting off
   they degrade to an always-open row of controls that still works rather than
   to a toggle that does nothing. Plan §D5. */
.lang-switcher,
.nav-menu {
    position: relative;
    display: flex;
    align-items: center;
    flex: none;
}

.lang-current,
.lang-option {
    font-family: inherit;
    font-size: var(--font-size-auth);
    font-weight: 500;
    letter-spacing: 0.5px;
    color: rgba(255,255,255,0.85);
    background-color: rgba(255,255,255,0.12);
    border: none;
    border-radius: var(--radius-pill);
    padding: 6px 14px;
    cursor: pointer;
    transition: background-color 0.2s, color 0.2s;
}

.lang-current:hover,
.lang-option:hover {
    background-color: var(--nav-veil);
    color: var(--nav-veil-ink);
}

/* Without JS the toggle cannot do anything, so it is not shown at all. */
.lang-current,
.nav-menu-toggle {
    display: none;
}
.lang-switcher.is-enhanced .lang-current,
.nav-menu.is-enhanced .nav-menu-toggle {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.lang-list,
.nav-menu-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    align-items: center;
    gap: 2px;
}

.lang-switcher.is-enhanced .lang-list,
.nav-menu.is-enhanced .nav-menu-list {
    display: none;
    flex-direction: column;
    align-items: stretch;
    position: absolute;
    top: calc(100% + 6px);
    min-width: 100%;
    z-index: 110;
    background-color: #00548a;
    border: 1px solid #57beff;
    border-radius: var(--radius-md);
    padding: var(--space-xs);
    box-shadow: var(--shadow-md);
}
.lang-switcher.is-enhanced .lang-list { left: 0; }
/* The section column hangs from the right edge: its toggle sits in a nav that
   is itself flush right, so a left-anchored panel would overhang the window. */
.nav-menu.is-enhanced .nav-menu-list { right: 0; }

.lang-switcher.is-enhanced.is-open .lang-list,
.nav-menu.is-enhanced.is-open .nav-menu-list {
    display: flex;
}

.lang-option {
    width: 100%;
    text-align: center;
}

/* The active locale stays in the list rather than being filtered out, so the
   list length never changes and the current choice is always visible. */
.lang-option[aria-current="true"] {
    background-color: var(--nav-hot);
    color: var(--nav-hot-ink);
    font-weight: 700;
}

.nav-menu-list a {
    display: block;
    padding: 8px 14px;
    border-radius: var(--radius-pill);
    color: rgba(255,255,255,0.85);
    font-size: var(--font-size-nav);
    font-weight: 500;
    text-decoration: none;
    white-space: nowrap;
    transition: background-color 0.2s, color 0.2s;
}
.nav-menu-list a:hover,
.nav-menu-list a.active {
    background-color: var(--nav-hot);
    color: var(--nav-hot-ink);
    text-decoration: none;
}

/* The caret only means anything once the list can actually collapse. */
.nav-menu-caret {
    width: 10px;
    height: 10px;
    flex: none;
    transition: transform 0.2s;
}
.nav-menu.is-open .nav-menu-caret {
    transform: rotate(180deg);
}

/* ── 4b. Day/night pill ──
   Two halves inside one 2px white border. The active half is solid white with
   a navy glyph; the inactive half is white at 12% with a white glyph. */
.theme-toggle {
    display: flex;
    align-items: center;
    border: 2px solid #ffffff;
    border-radius: var(--radius-pill);
    overflow: hidden;
    flex: none;
    height: 40px;
}

.theme-option {
    width: 42px;
    height: 100%;
    display: grid;
    place-items: center;
    border: 0;
    padding: 0;
    cursor: pointer;
    background-color: rgba(255,255,255,0.12);
    color: #ffffff;
    transition: background-color 0.2s, color 0.2s;
}
.theme-option + .theme-option {
    border-left: 2px solid #ffffff;
}
.theme-option:hover {
    background-color: var(--nav-veil);
    color: var(--nav-veil-ink);
}
/* Must come after :hover — the two selectors have equal specificity, so the
   pressed half would otherwise lose its solid fill while the pointer is on it. */
.theme-option[aria-pressed="true"] {
    background-color: #ffffff;
    color: #00548a;
}
.theme-option svg {
    display: block;
    stroke: currentColor;
}

/* ── 4c. Account button and its dropdown ── */
.auth-actions {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
    flex: none;
    position: relative;
}

.account-toggle {
    width: 44px;
    height: 44px;
    display: grid;
    place-items: center;
    border-radius: var(--radius-pill);
    border: 2px solid #ffffff;
    background-color: rgba(255,255,255,0.12);
    color: #ffffff;
    cursor: pointer;
    padding: 0;
    transition: background-color 0.2s;
}
/* The portrait glyph is stroke: currentColor, so warming the colour warms the
   drawing too — same treatment the language pill gets. */
.account-toggle:hover  {
    background-color: var(--nav-veil);
    color: var(--nav-veil-ink);
}
.account-toggle:active { background-color: var(--nav-veil-press); }
.account-toggle svg {
    display: block;
    stroke: currentColor;
}

.account-menu {
    position: absolute;
    top: 54px;
    right: 0;
    min-width: 172px;
    background-color: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-card);
    padding: 6px;
    display: none;
    flex-direction: column;
    gap: 2px;
    z-index: 200;
}
.auth-actions.is-open .account-menu {
    display: flex;
}

.account-menu a {
    display: block;
    padding: 9px 12px;
    border-radius: var(--radius-md);
    font-size: var(--font-size-sm);
    color: var(--color-text);
    text-decoration: none;
    transition: background-color 0.2s;
}
.account-menu a:hover {
    background-color: var(--nav-tint);
    text-decoration: none;
}

/* ════ 5. Landing scene ════════════════════════ */
/* The room is the hero: it spreads to the display edges, leaving only a 2%
   gutter each side, and takes no width cap at all — on a wide monitor it keeps
   growing rather than centring in a 1760px column. */
.scene-main {
    width: 96%;
    margin: 0 auto;
    padding: 10px 0 16px;
}

/* The stage owns the panorama's aspect ratio. Every percentage hotspot below
   is measured against this box, so it must match the plate exactly or the
   whole coordinate map drifts. */
/* Width is what the window sets; height simply follows the panorama's own
   ratio and is allowed to run past the bottom of the viewport. The ratio is
   never negotiable — every rack polygon and shelf rect below is a percentage
   of this box, so distorting it moves every hotspot off the artwork with
   nothing reporting an error. */
.scene-stage {
    position: relative;
    width: 100%;
    aspect-ratio: 1864 / 1374;
    border-radius: var(--radius-lg);
    overflow: hidden;
    background-color: var(--color-surface);
    box-shadow: var(--shadow-card);
}

.scene-view {
    position: absolute;
    inset: 0;
    transition: opacity 0.28s ease;
}
.scene-view.is-fading {
    opacity: 0;
}

.scene-room,
.scene-rack {
    position: absolute;
    inset: 0;
}
.scene-rack {
    display: grid;
    place-items: center;
    /* Top clears the two floating chrome rows; right reserves the section
       card's gutter. Without the reservation the centred rack runs under the
       card and swallows the hover target on the two lowest shelves. */
    padding: 180px 46% 24px 4%;
    background-color: var(--color-surface-alt);
}
.scene-pano {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    display: block;
}

.scene-blur {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    display: block;
    object-fit: cover;
    filter: blur(12px);
    opacity: 0.5;
    transform: scale(1.04);
}

/* One stretched overlay keeps the traced polygons registered at any width. */
.rack-map {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    overflow: visible;
}

.rack-frame {
    fill: var(--scene-line-rest);
    stroke: var(--scene-line);
    stroke-width: 4;
    stroke-linejoin: round;
    cursor: pointer;
    pointer-events: all;   /* hittable through a near-transparent fill */
    transition: stroke 0.2s, stroke-width 0.2s, fill 0.2s;
}
.rack-frame:hover,
.rack-frame:focus {
    stroke: var(--scene-hot);
    stroke-width: 9;
    fill: var(--scene-hot-fill);
    outline: none;
}

.rack-frame-box {
    position: relative;
    height: 100%;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    overflow: hidden;
    background-color: var(--color-surface);
    box-shadow: var(--shadow-card);
}
.rack-plate {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    display: block;
}
.rack-night-wash {
    position: absolute;
    inset: 0;
    pointer-events: none;
    background: var(--scene-wash);
}

.shelf-frame {
    position: absolute;
    border: 0;
    border-radius: var(--radius-sm);
    padding: 0;
    cursor: pointer;
    background-color: var(--scene-line-rest);
    box-shadow: inset 0 0 0 3px var(--scene-line);
    transition: background-color 0.2s, box-shadow 0.2s;
}
.shelf-frame:hover,
.shelf-frame:focus {
    box-shadow: inset 0 0 0 5px var(--scene-hot);
    background-color: var(--scene-hot-fill);
    outline: none;
}

/* ── 5a. The floating chrome layer ──
   Sits over the scene and passes clicks through; only its interactive
   children take pointer events back, so the racks stay reachable underneath. */
.scene-chrome {
    position: absolute;
    inset: 0;
    pointer-events: none;
    display: flex;
    flex-direction: column;
    gap: 12px;
    padding: 16px 20px;
}

.search-bar {
    display: flex;
    align-items: stretch;
    width: min(720px, 100%);
    margin: 0 auto;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background-color: var(--color-surface);
    box-shadow: var(--shadow-panel);
    overflow: hidden;
    pointer-events: auto;
}

.search-input {
    flex: 1;
    min-width: 0;
    font-family: inherit;
    font-size: var(--font-size-sm);
    height: 44px;
    padding: 0 14px;
    border: 0;
    border-radius: 0;
    background-color: transparent;
    color: var(--color-text);
}
.search-input:focus {
    outline: none;
}
.search-input::placeholder {
    color: var(--color-text-muted);
}

.search-submit {
    flex: none;
    height: 44px;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 0 18px;
    border: 0;
    border-left: 1px solid var(--color-primary);
    border-radius: 0;
    background-color: var(--color-primary);
    color: var(--color-text-light);
    font-family: inherit;
    font-size: var(--font-size-sm);
    cursor: pointer;
    transition: background-color 0.2s, color 0.2s, box-shadow 0.2s;
}
.search-submit:hover {
    background-color: var(--scene-hot);
    color: var(--scene-hot-ink);
}
.search-submit:focus-visible {
    outline: none;
    box-shadow: var(--focus-ring);
}
.search-submit img {
    height: 34px;
    width: auto;
    /* the glyph ships white-on-navy; invert it when the fill turns pale */
    filter: brightness(0) invert(1);
    transition: filter 0.2s;
}
.search-submit:hover img {
    filter: none;
}

.scene-labels {
    width: min(720px, 100%);
    margin: -4px auto 0;
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-md);
}

.overlay-label {
    margin: 0;
    padding: 3px 14px 3px 8px;
    border-radius: var(--radius-pill);
    font-size: var(--font-size-xs);
    letter-spacing: 0.5px;
    text-transform: uppercase;
    color: var(--scene-line);
    text-decoration: none;
    pointer-events: auto;
    transition: color 0.2s;
}
a.overlay-label {
    padding: 3px 8px 3px 14px;
}
.overlay-label:hover {
    color: var(--scene-hot);
    text-decoration: none;
}

.scene-crumbs {
    display: flex;
    align-items: center;
    gap: 10px;
    align-self: flex-start;
    font-size: var(--font-size-base);
    padding: 7px 14px;
    border-radius: var(--radius-pill);
    background-color: var(--color-surface);
    box-shadow: var(--shadow-pill);
    pointer-events: auto;
}
.scene-crumbs .sep { color: var(--color-text-muted); }
.scene-crumbs strong { font-weight: 700; }

/* "Back to the room" in the breadcrumb is a real <a>, and nothing in this file
   styles bare links, so it was rendering in the UA's default link blue
   (#0000EE) in both themes — unreadable on the night panel. It takes the same
   ink as the section name beside it, and the same hot accent on hover that
   every other interactive thing in the scene uses. */
.scene-crumbs a {
    color: var(--color-text);
    text-decoration: none;
    transition: color 0.2s;
}
.scene-crumbs a:hover,
.scene-crumbs a:focus-visible {
    color: var(--scene-hot);
    text-decoration: none;
}

/* Anchored to the stage, not stacked in the chrome's flex column: the phone
   layout (§9) takes the card out of the overlay entirely and puts it under the
   plate, which is only possible for a child of the stage. right/bottom repeat
   the chrome's own 20px/16px padding, so the desktop position is unchanged. */
.section-card-wrap {
    position: absolute;
    right: 20px;
    bottom: 16px;
    width: 42%;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 12px;
}

.section-card {
    display: flex;
    flex-direction: column;
    gap: 12px;
    padding: var(--space-md) 18px;
    border-radius: var(--radius-lg);
    background-color: var(--color-surface);
    box-shadow: var(--shadow-panel);
    pointer-events: auto;
    width: 100%;
}
.section-card .eyebrow {
    margin: 0 0 4px;
    font-size: var(--font-size-xs);
    letter-spacing: 0.5px;
    text-transform: uppercase;
    color: var(--color-text-muted);
}
.section-card h1 {
    margin: 0;
    font-size: var(--font-size-xl);
    font-weight: 700;
}
.section-card .meta {
    margin: 4px 0 0;
    font-size: var(--font-size-sm);
    color: var(--color-text-muted);
}

.shelf-pills {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-sm);
}
.shelf-pill {
    padding: 6px 14px;
    border-radius: var(--radius-pill);
    border: 1px solid var(--color-border);
    background-color: var(--color-surface);
    color: var(--color-text);
    font-family: inherit;
    font-size: var(--font-size-sm);
    text-decoration: none;
    cursor: pointer;
    transition: background-color 0.2s;
}
.shelf-pill:hover {
    background-color: var(--scene-tint);
    color: var(--scene-hot);
    text-decoration: none;
}

/* Same ink as the section card's own text and the shelf pills, not the old
   --color-primary, so the two "Back to the room" controls and everything
   around them read as one set; hover takes the scene's hot accent in whichever
   theme is active. */
.scene-back {
    padding: 9px 18px;
    border-radius: var(--radius-md);
    border: 1px solid var(--color-border);
    background-color: var(--color-surface);
    color: var(--color-text);
    font-family: inherit;
    font-size: var(--font-size-sm);
    cursor: pointer;
    pointer-events: auto;
    box-shadow: var(--shadow-pill);
    transition: background-color 0.2s, color 0.2s, border-color 0.2s;
}
.scene-back:hover {
    background-color: var(--scene-tint);
    color: var(--scene-hot);
    border-color: var(--scene-hot);
}

/* Hover read-out for a rack or a shelf. Positioned from script, because the
   anchor is a traced polygon rather than a laid-out box. */
.scene-tip {
    position: absolute;
    display: flex;
    gap: var(--space-sm);
    align-items: baseline;
    white-space: nowrap;
    background-color: var(--color-surface);
    color: var(--color-text);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    padding: 7px 12px;
    font-size: var(--font-size-sm);
    line-height: 1.25;
    width: max-content;
    max-width: 80%;
    pointer-events: none;
    box-shadow: var(--shadow-panel);
    z-index: 50;
}
.scene-tip strong { font-weight: 700; }
.scene-tip span   { opacity: 0.75; }


/* ════ 6. Footer ═══════════════════════════════ */
.site-footer {
    background-color: var(--color-primary);
    color: var(--color-text-light);
    border-top: 3px solid var(--color-accent);
    padding: var(--space-xl) 4%;
    display: grid;
    grid-template-columns: 1.2fr 1fr 1fr;
    gap: var(--space-xl);
}
/* At night the navy fill would sit a shade off the page ground, so the footer
   takes the raised surface instead and its type flips with it. */
[data-theme="night"] .site-footer {
    background-color: var(--color-surface-alt);
    color: var(--color-text);
}

.footer-col {
    display: flex;
    flex-direction: column;
    gap: var(--space-md);
    font-size: var(--font-size-nav);
    line-height: 1.5;
}

.footer-head {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 10px;
    font-size: var(--font-size-nav);
    font-weight: 700;
    color: var(--color-accent);
    text-transform: uppercase;
    letter-spacing: 0.5px;
}
[data-theme="night"] .footer-head {
    color: var(--color-accent-dark);
}

.footer-copy {
    margin: 0;
    color: rgba(255,255,255,0.85);
}
[data-theme="night"] .footer-copy {
    color: var(--color-text);
}

.footer-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: var(--space-sm);
}

.footer-link {
    display: flex;
    align-items: center;
    gap: 12px;
    font-size: var(--font-size-nav);
    color: rgba(255,255,255,0.85);
    text-decoration: none;
    transition: color 0.2s;
}
.footer-link:hover {
    color: var(--color-accent);
    text-decoration: none;
}
[data-theme="night"] .footer-link {
    color: var(--color-text);
}
[data-theme="night"] .footer-link:hover {
    color: var(--color-accent);
}

.footer-icon {
    display: block;
    flex: none;
}

.footer-legal {
    margin: var(--space-lg) 0 0;
    font-size: var(--font-size-lg);
    color: rgba(255,255,255,0.85);
}
[data-theme="night"] .footer-legal {
    color: var(--color-text);
}

/* ════ 7. Not-found modal ══════════════════════ */
.scene-dialog {
    border: 0;
    padding: 0;
    background: transparent;
    color: var(--color-text);
}
.scene-dialog::backdrop {
    background-color: var(--scrim);
}
.scene-dialog-box {
    background-color: var(--color-surface);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-modal);
    padding: 28px var(--space-xl);
    max-width: 420px;
}
.scene-dialog-box h3 {
    margin: 0 0 10px;
    font-size: var(--font-size-lg);
    font-weight: 700;
}
.scene-dialog-box p {
    margin: 0 0 20px;
    font-size: var(--font-size-base);
}
.scene-dialog-close {
    padding: 9px 18px;
    border-radius: var(--radius-md);
    border: 1px solid var(--color-primary);
    background-color: var(--color-primary);
    color: var(--color-text-light);
    font-family: inherit;
    font-size: var(--font-size-sm);
    cursor: pointer;
    transition: filter 0.2s;
}
.scene-dialog-close:hover {
    filter: brightness(1.12);
}

/* ════ 8. Public list and detail pages ═════════
   The shelf, book, author and category pages. These are the public
   counterparts of the staff console's list/detail shapes -- deliberately
   re-expressed here rather than importing staff.css, because the public side
   must not ship the staff bundle (PUBLIC_PAGES_REDESIGN_PLAN.md D2).

   Everything reads from the day/night tokens in sections 1-2, so these pages
   follow the theme with no [data-theme] rules of their own. */

.public-page {
    width: 96%;
    margin: 0 auto;
    padding: var(--space-lg) 0 var(--space-xl);
}

/* ── Breadcrumbs ── */
.crumbs {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
    margin-bottom: var(--space-md);
    font-size: var(--font-size-sm);
    color: var(--color-text-muted);
}
.crumbs a {
    color: var(--color-primary);
    text-decoration: none;
    transition: color 0.2s;
}
.crumbs a:hover {
    color: var(--scene-hot);
}
.crumbs .sep {
    color: var(--color-text-muted);
}
.crumbs-current {
    font-weight: 700;
    color: var(--color-text);
}

/* ── The detail band above a result list ── */
.detail-band {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--space-lg);
    padding: var(--space-lg) var(--space-xl);
    margin-bottom: var(--space-lg);
    background-color: var(--color-surface);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-card);
}
.eyebrow {
    margin: 0 0 var(--space-xs);
    font-size: var(--font-size-xs);
    font-weight: 700;
    letter-spacing: var(--letter-spacing-label);
    text-transform: uppercase;
    color: var(--color-text-muted);
}
.detail-title {
    margin: 0;
    font-size: var(--font-size-detail-title);
    font-weight: 700;
    line-height: 1.2;
    color: var(--color-primary);
}
/* Definition list rather than a table: these are label/value pairs, and a dl
   says that in markup without needing a row per fact. */
.detail-facts {
    display: grid;
    grid-template-columns: auto auto;
    gap: 2px var(--space-md);
    margin: 0;
    font-size: var(--font-size-sm);
}
.detail-facts dt {
    font-weight: 700;
    color: var(--color-text-muted);
}
.detail-facts dd {
    margin: 0;
    color: var(--color-text);
    font-variant-numeric: tabular-nums;
}

/* ── Search console ── */
.console {
    display: flex;
    flex-direction: column;
    gap: var(--space-md);
    padding: var(--space-lg) var(--space-xl);
    margin-bottom: var(--space-lg);
    background-color: var(--color-surface);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-card);
}
.console-searchbar {
    display: flex;
    align-items: flex-end;
    gap: var(--space-sm);
}
.console-item {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-width: 0;
}
.console-submit {
    flex: none;
}
.console label,
.sort-label {
    margin-bottom: var(--space-xs);
    font-size: var(--font-size-xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: var(--letter-spacing-label);
    color: var(--color-text-muted);
}
.console .search-input,
.filter-select {
    width: 100%;
    padding: 9px 12px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background-color: var(--color-surface);
    color: var(--color-text);
    font-family: inherit;
    font-size: var(--font-size-sm);
    box-sizing: border-box;
    transition: border-color 0.2s, box-shadow 0.2s;
}
.console .search-input:focus,
.filter-select:focus {
    outline: none;
    border-color: var(--color-accent);
    box-shadow: var(--focus-ring);
}
.search-button {
    padding: 9px 20px;
    border: none;
    border-radius: var(--radius-md);
    background-color: var(--color-primary);
    color: var(--color-text-light);
    font-family: inherit;
    font-size: var(--font-size-sm);
    font-weight: 700;
    white-space: nowrap;
    cursor: pointer;
    transition: background-color 0.2s;
}
.search-button:hover {
    background-color: var(--color-accent-dark);
}
.filter-container {
    display: flex;
    gap: var(--space-lg);
    flex-wrap: wrap;
}
.filter-container .console-item {
    flex: 1 1 45%;
    min-width: 220px;
}

/* ── Sort toolbar ── */
.results-toolbar {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
    padding: var(--space-sm) var(--space-lg);
    border-bottom: 1px solid var(--color-border);
    flex-wrap: wrap;
}
.sort-label {
    margin-bottom: 0;
}
.sort-button {
    padding: 5px 14px;
    background-color: transparent;
    color: var(--color-primary);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-pill);
    font-family: inherit;
    font-size: var(--font-size-sm);
    white-space: nowrap;
    cursor: pointer;
    transition: background-color 0.2s, color 0.2s, border-color 0.2s;
}
.sort-button:hover {
    border-color: var(--color-accent);
    background-color: var(--scene-tint);
}
/* Direction arrows are CSS-drawn triangles, not glyphs: the unicode arrows fall
   outside every unicode-range Arsenal declares, so they would drop to a random
   system font and render inconsistently. */
.sort-button.asc::after,
.sort-button.desc::after {
    content: "";
    display: inline-block;
    width: 0;
    height: 0;
    margin-left: 7px;
    vertical-align: 0.05em;
    border-left: 0.3em solid transparent;
    border-right: 0.3em solid transparent;
}
.sort-button.asc::after  { border-bottom: 0.36em solid currentColor; }
.sort-button.desc::after { border-top: 0.36em solid currentColor; }
.sort-button.asc,
.sort-button.desc {
    background-color: var(--color-accent);
    border-color: var(--color-accent);
    color: var(--color-text);
    font-weight: 700;
}
.results-count {
    margin-left: auto;
    font-size: var(--font-size-sm);
    color: var(--color-text-muted);
    font-variant-numeric: tabular-nums;
}

/* ── Result rows ── */
.results {
    background-color: var(--color-surface);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-card);
    overflow: hidden;
}
.table-bar {
    width: 100%;
    border-collapse: collapse;
}
.table-bar > tbody > tr {
    display: grid;
    grid-template-columns: 128px 2fr 3fr;
    gap: var(--space-md);
    align-items: center;
    padding: var(--space-sm) var(--space-lg);
    border-bottom: 1px solid var(--color-border);
    cursor: pointer;
    transition: background-color 0.2s;
}
.table-bar > tbody > tr:nth-child(even) {
    background-color: var(--color-surface-alt);
}
.table-bar > tbody > tr:hover {
    background-color: var(--scene-tint);
}
.table-bar > tbody > tr:last-child {
    border-bottom: 0;
}
.table-bar td {
    padding: var(--space-sm);
    vertical-align: middle;
}

.image-column {
    display: flex;
    justify-content: center;
    align-items: center;
}
.image-field {
    width: 100%;
    max-width: 104px;
    height: auto;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    object-fit: cover;
    box-shadow: var(--shadow-sm);
}

.info-column {
    display: flex;
    flex-direction: column;
    gap: var(--space-xs);
    min-width: 0;
}
.info-header {
    font-size: var(--font-size-lg);
    font-weight: 700;
    line-height: 1.3;
    color: var(--color-primary);
}
.info-table {
    width: 100%;
    border-collapse: collapse;
}
.info-table tr {
    display: grid;
    grid-template-columns: 96px 1fr;
}
.info-table td {
    padding: 2px 4px;
}
.info-key {
    font-size: var(--font-size-key);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.3px;
    color: var(--color-text-muted);
}
.info-value {
    font-size: var(--font-size-sm);
    color: var(--color-text);
}

/* Status colours. The class is the RAW machine value, never the translated
   label -- translating it would rename the class and lose the colour. */
.in_library  { color: var(--color-status-ok); font-weight: 700; }
.subscribed  { color: var(--color-status-err); font-weight: 700; }
.unavailable { color: var(--color-status-na); font-weight: 700; }
.pre_ordered { color: var(--color-status-pre); font-weight: 700; }
.coming_soon { color: var(--color-status-warn); font-weight: 700; }

.tag-column {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-xs);
    align-content: flex-start;
}
.tag-field {
    display: inline-block;
    padding: 3px 10px;
    border-radius: var(--radius-pill);
    background-color: var(--color-primary);
    color: var(--color-text-light);
    font-size: var(--font-size-key);
    letter-spacing: 0.2px;
    text-decoration: none;
}
a.tag-field:hover {
    background-color: var(--color-accent-dark);
    color: var(--color-text-light);
}

.results-empty {
    display: block;
    padding: var(--space-xl) var(--space-lg);
    text-align: center;
    color: var(--color-text-muted);
    font-size: var(--font-size-base);
}

/* ── Detail pages ── */
.detail-container {
    background-color: var(--color-surface);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-card);
    padding: var(--space-lg) var(--space-xl) var(--space-xl);
}
.detail-container .detail-title {
    margin: 0 0 var(--space-lg);
}
/* image | facts | availability. The third column is narrower on purpose: it is
   where client action buttons will go, so it is left with room rather than
   filled to match its neighbours (plan section 8.1). */
.detail-columns {
    display: grid;
    grid-template-columns: 1fr 1.3fr 1.1fr;
    gap: var(--space-xl);
    align-items: start;
}
.detail-col {
    min-width: 0;
}
.detail-heading {
    margin: var(--space-md) 0 var(--space-sm);
    font-size: var(--font-size-lg);
    font-weight: 700;
    color: var(--color-text);
    border-bottom: 2px solid var(--color-accent);
    padding-bottom: var(--space-xs);
}
.detail-col > .detail-heading:first-child {
    margin-top: 0;
}
.detail-cover {
    display: flex;
    justify-content: center;
}
.detail-cover .image-field {
    max-width: 240px;
    width: 100%;
}

.detail-props {
    width: 100%;
    border-collapse: collapse;
}
.detail-props .props-row:nth-child(odd) {
    background-color: var(--color-surface-alt);
}
.detail-props th,
.detail-props td {
    border: 0;
    padding: 7.5px 10px;
    text-align: left;
    vertical-align: top;
    font-size: var(--font-size-auth);
    color: var(--color-text);
}
.props-label {
    width: 42%;
    min-width: 120px;
    font-weight: 700;
    color: var(--color-text-muted);
}
.props-value {
    width: 58%;
    font-weight: 500;
}
.props-link {
    color: var(--color-primary);
    text-decoration: none;
    font-weight: 600;
    border-bottom: 1px solid var(--color-border);
    transition: color 0.2s, border-color 0.2s;
}
.props-link:hover {
    color: var(--scene-hot);
    border-bottom-color: var(--scene-hot);
    text-decoration: none;
}

.detail-tags {
    display: flex;
    flex-wrap: wrap;
    align-content: flex-start;
    gap: var(--space-sm);
    padding: var(--space-sm);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background-color: var(--color-surface-alt);
}
.detail-tags .tag-field {
    font-size: var(--font-size-nav);
}

/* The prose band: full width beneath the columns, because these fields are
   unbounded text and a props cell in a 1.1fr column would wrap 4-5 lines to
   roughly ten and drag its column past both neighbours. */
.detail-prose {
    margin-top: var(--space-xl);
}
.detail-prose p {
    margin: 0;
    max-width: 68ch;
    font-size: var(--font-size-base);
    line-height: 1.6;
    color: var(--color-text);
}

/* ── Pagination ── */
.pagination {
    display: flex;
    justify-content: center;
    align-items: center;
    padding: var(--space-md);
    border-top: 1px solid var(--color-border);
}
.step-links {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
    flex-wrap: wrap;
    justify-content: center;
}
.submit-button {
    padding: 7px 16px;
    background-color: var(--color-primary);
    color: var(--color-text-light);
    border: none;
    border-radius: var(--radius-md);
    cursor: pointer;
    font-size: var(--font-size-sm);
    font-family: inherit;
    font-weight: 500;
    text-decoration: none;
    transition: background-color 0.2s;
}
.submit-button:hover {
    background-color: var(--color-accent-dark);
    color: var(--color-text-light);
    text-decoration: none;
}
.current {
    font-size: var(--font-size-sm);
    color: var(--color-text-muted);
    padding: 0 var(--space-sm);
    font-variant-numeric: tabular-nums;
}

/* ════ 8a. Coming-soon placeholder ════════════
   One message, one way back, and the mascot on either side (library/
   coming_soon.html). Everything reads from the tokens, so night mode needs no
   rules of its own here.

   The mascots must match the box's HEIGHT, with their width following from
   the drawing's ratio. Neither flex nor grid can size an in-flow item that way
   without a size loop, so each drawing is absolutely positioned inside its
   grid cell: top/bottom 0 makes its height the row's height, which the box
   alone sets, and the SVG's viewBox ratio then gives the width. The side
   columns reserve at least --soon-side-min for them; §9 retunes the box cap,
   the side minimum and the title size at each breakpoint so the drawing never
   outgrows its cell. Those values are measured, not estimated: the drawing is
   1.095 of the box's height wide, and a title line adds a fixed step to that
   height (at 22px: one line 134px, two 161px, three 187px), so each band
   keeps every locale at a line count whose mascot fits its side column
   (COMING_SOON_PAGE_PLAN.md §5). */
.soon-main {
    width: 96%;
    margin: 0 auto;
    padding: clamp(32px, 10vh, 120px) 0 clamp(48px, 14vh, 160px);
}

.soon-stage {
    --soon-box-max:  34rem;
    --soon-side-min: 15.5rem;
    /* Tight on purpose: the pair should almost touch the wardrobe with their
       paws. The drawings carry 4 units of their own padding, so a 4px column
       gap puts the outstretched arm within a hair of the frame. */
    --soon-gap:      4px;
    display: grid;
    grid-template-columns:
        minmax(var(--soon-side-min), 1fr)
        minmax(0, var(--soon-box-max))
        minmax(var(--soon-side-min), 1fr);
    column-gap: var(--soon-gap);
}

.soon-side {
    position: relative;
}
.soon-mascot {
    position: absolute;
    top: 0;
    bottom: 0;
    height: 100%;
    width: auto;
    /* Safety net, not layout: an unmeasured width or a longer translation can
       make the box taller than its band allows. Capped, the drawing shrinks
       inside its own viewBox instead of pushing the page sideways. */
    max-width: 100%;
    display: block;
}
.soon-side-left .soon-mascot  { right: 0; }
/* No transform: the two sides are the reference's own pair, drawn facing each
   other, so each side gets the figure that already looks inward. */
.soon-side-right .soon-mascot { left: 0; }

/* The <symbol> both mascots <use>. Not display:none — some engines refuse to
   render a <use> whose symbol sits in an undisplayed subtree. */
.soon-sprite {
    position: absolute;
    width: 0;
    height: 0;
    overflow: hidden;
}

/* The wardrobe around the message (library/_wardrobe.svg). Its padding is what
   reserves the frame's thickness, so the pieces never overlap the message, and
   the mascots beside it take the FRAME's height — the furniture is what the eye
   reads as the box now. Ink follows the mascots. */
.soon-frame {
    position: relative;
    padding: 26px 22px 30px;
    color: var(--mascot-line);
}
/* Each piece is stretched along one axis only, and sized explicitly: an
   absolutely positioned <svg> with a viewBox otherwise falls back to its
   intrinsic size and refuses to fill the edge it is pinned to.

   The joins are crossings, not meetings. A rail's end sits at a FRACTION of
   the frame's width (it is stretched), while a panel's line sits at a FIXED
   offset inside a 22px piece, so two ends can never be made to land on each
   other at every width — the first build left a notch at all four corners.
   Instead the rails run between the panels' outer lines (inset 4px, where
   those lines are) and the panels run from the cornice's upper rail down to
   the plinth's lower one, so each rail CROSSES both panels and the corners
   close at every size. The panel span is not the frame's full height: the
   upper rail sits 5px into the cornice piece and the lower one 18px up from
   the bottom, and a panel drawn past either left a 5px spike above the
   cornice. */
.soon-part {
    position: absolute;
    display: block;
    pointer-events: none;
}
.soon-cornice {
    left: 4px;
    top: 0;
    width: calc(100% - 8px);
    height: 26px;
}
.soon-panel-left,
.soon-panel-right {
    /* top rail to the plinth's lower rail, which the feet then hang from */
    top: 5px;
    height: calc(100% - 23px);
    width: 22px;
}
.soon-panel-left  { left: 0; }
.soon-panel-right { right: 0; }
.soon-plinth {
    left: 4px;
    bottom: 12px;
    width: calc(100% - 8px);
    height: 18px;
}
.soon-foot-left,
.soon-foot-right {
    bottom: 0;
    width: 26px;
    height: 20px;
}
.soon-foot-left  { left: 16px; }
/* Mirrored so the pair leans outward, the way the assembled drawing does. */
.soon-foot-right {
    right: 16px;
    transform: scaleX(-1);
}

.soon-box {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-lg);
    padding: var(--space-xl);
    /* The page's own ground, not a raised surface: the wardrobe is what frames
       the message now, so a second card inside it read as a box in a box. */
    background-color: var(--color-bg);
    border-radius: var(--radius-lg);
    text-align: center;
}
.soon-title {
    margin: 0;
    font-size: var(--font-size-detail-title);
    font-weight: 700;
    line-height: 1.2;
    /* The scene's hot accent, not the primary: it is already navy by day and the
       warm lamp yellow #ffe89a at night, so the message lights up in the same
       colour as the chameleons beside it without a [data-theme] rule of its own. */
    color: var(--scene-hot);
    text-wrap: balance;
    /* Arsenal, stated rather than inherited: this is the page's one line of
       display type, and it should not follow a change to the body font. */
    font-family: var(--font-family);
    /* The footer label face — .footer-head's uppercase and tracking on the
       same Arsenal 700. Its light-blue colour is not carried over: #57beff on
       the white card is about 2.1:1, well under legible contrast. */
    text-transform: uppercase;
    letter-spacing: var(--letter-spacing-label);
}

/* The page's only action, so it takes the filled primary treatment — navy by
   day, light blue with dark ink by night, both from the tokens. */
.soon-back {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 10px 22px;
    border-radius: var(--radius-md);
    background-color: var(--color-primary);
    color: var(--color-text-light);
    font-size: var(--font-size-base);
    font-weight: 700;
    text-decoration: none;
    box-shadow: var(--shadow-sm);
    transition: filter 0.2s, box-shadow 0.2s;
}
/* Hover takes the same hot accent, with the ink that goes on it: by day that is
   the navy the button already wears, so only the brightness step shows; at night
   the button turns the lamp yellow with dark ink. */
.soon-back:hover {
    background-color: var(--scene-hot);
    color: var(--scene-hot-ink);
    filter: brightness(1.12);
    box-shadow: var(--shadow-pill);
    text-decoration: none;
}
.soon-back:focus-visible {
    outline: none;
    box-shadow: var(--focus-ring);
}

/* ════ 9. Responsive ═══════════════════════════ */
@media (max-width: 1200px) {
    .scene-rack {
        padding: 150px 40% 20px 3%;
    }
    .section-card-wrap {
        width: 36%;
    }
    .soon-stage {
        --soon-box-max:  28rem;
        --soon-side-min: 14rem;
    }
    .soon-title {
        font-size: var(--font-size-detail-title-mobile);
    }
}

@media (max-width: 1024px) {
    .detail-columns {
        grid-template-columns: 1fr 1.3fr;
    }
    /* Availability drops to a full-width row of its own rather than being
       squeezed; it is the column a visitor actually came for. */
    .detail-col:last-child {
        grid-column: 1 / -1;
    }
    .header-container {
        gap: var(--space-xs);
    }
    .logo {
        margin-right: 3px;
    }
    .nav-bar > a,
    .nav-menu-toggle {
        padding: 6px 10px;
        font-size: var(--font-size-sm);
    }
    .logo img {
        height: 3.4rem;
    }
    .soon-stage {
        --soon-side-min: 12rem;
    }
    .soon-box {
        gap: var(--space-md);
        padding: var(--space-lg) var(--space-md);
    }
    .soon-title {
        font-size: var(--font-size-base);
    }
}

@media (max-width: 768px) {
    /* The nav is absolutely positioned at this width, so it is out of flow and
       the header's free space has to go somewhere else. It is shared out by
       flex-grow, 1 : 1 : 3, between two empty spacers and the account block:
         logo · lang ‹1› theme ‹1› menu ‹3› account
       so the gaps inside the lang / theme / menu group are always a third of
       the gap before the portrait, at every width and in every language, and
       the group still reads as a group. Before this the group had only the
       header's 2-4px gap and all the free space went to the account's auto
       margin. The spacers are capped at 24px — past that the rest goes to the
       account gap — and keep the old gap as their minimum, so no narrow width
       gets any wider than it was. The header's own gap is zeroed so the
       spacers are the only spacing between these controls.
       The account block grows from its content size (flex-basis auto), so
       what it gains is exactly the visible gap; its button sits at the right
       edge and its menu is anchored right: 0, so neither moves. */
    .header-container {
        flex-wrap: nowrap;
        gap: 0;
        --header-spacer-min: var(--space-xs);
    }
    .header-container::before,
    .header-container::after {
        content: "";
        flex: 1 1 0;
        min-width: var(--header-spacer-min);
        max-width: 24px;
    }
    .logo { margin-right: var(--header-spacer-min); }
    .header-container > .lang-switcher { order: 1; }
    .header-container::before { order: 2; }
    .theme-toggle { order: 3; }
    .header-container::after { order: 4; }
    .nav-toggle {
        display: block;
        order: 5;
    }
    .auth-actions {
        order: 6;
        flex: 3 1 auto;
        justify-content: flex-end;
        margin-left: var(--header-spacer-min);
    }
    .nav-bar {
        display: none;
        position: absolute;
        top: var(--header-height);
        left: 0;
        right: 0;
        flex-direction: column;
        align-items: stretch;
        background-color: #00548a;
        border-bottom: 3px solid #57beff;
        box-shadow: var(--shadow-md);
        padding: var(--space-sm) 4%;
        gap: 2px;
        z-index: 101;
    }
    .header.nav-open .nav-bar {
        display: flex;
    }
    .nav-bar > a,
    .nav-menu-toggle {
        padding: 10px 12px;
        border-radius: var(--radius-md);
        font-size: var(--font-size-nav);
    }
    /* The nav is already an absolutely-positioned column here, so there is
       nothing to overlay — the section list flows inline instead of floating. */
    .nav-menu {
        display: block;
        width: 100%;
    }
    .nav-menu.is-enhanced .nav-menu-list {
        position: static;
        background-color: transparent;
        border: none;
        box-shadow: none;
        padding: 0 0 0 var(--space-md);
        margin-top: 2px;
    }
    .logo img {
        height: 3rem;
    }

    /* Result rows drop the third column and put the tags under the info,
       the same regrouping the staff list pages use at this width. */
    .table-bar > tbody > tr {
        grid-template-columns: 96px 1fr;
        grid-template-areas: "image info" "tags tags";
        padding: var(--space-sm) var(--space-md);
    }
    .image-column { grid-area: image; }
    .info-column  { grid-area: info; }
    .tag-column   { grid-area: tags; }
    .image-field  { max-width: 84px; }
    .info-header  { font-size: var(--font-size-base); }
    .detail-columns {
        grid-template-columns: 1fr;
        gap: var(--space-lg);
    }
    .detail-container {
        padding: var(--space-md);
    }
    .detail-band {
        flex-direction: column;
        align-items: flex-start;
        padding: var(--space-md);
    }
    .detail-title { font-size: var(--font-size-detail-title-mobile); }
    .public-page  { padding: var(--space-md) 0 var(--space-lg); }

    /* The rack view's right gutter cannot survive a phone-width frame. Beside
       the plate there is no room for the card, and on top of it the card
       covered the plate almost completely — so at this width the stage stops
       being a fixed-ratio frame in rack view and simply stacks: plate first,
       card underneath, nothing overlapping.

       The chrome layer keeps its own absolute placement, so the search bar and
       the top-left breadcrumb stay exactly where they were, floating over the
       plate. Only the card leaves the overlay. */
    .scene-stage.is-rack {
        aspect-ratio: auto;
    }
    .scene-stage.is-rack .scene-view,
    .scene-stage.is-rack .scene-rack {
        position: relative;
        inset: auto;
    }
    .scene-rack {
        /* top still clears the floating search bar and labels; the right-hand
           reservation is gone with the card. */
        padding: 96px 3% 16px;
    }
    /* Sized by height rather than width: a plate is roughly 1:2, so letting the
       width lead would make a 340px-wide phone render a 700px-tall rack. */
    .scene-stage.is-rack .rack-frame-box {
        height: 58vh;
        max-height: 520px;
    }
    .section-card-wrap {
        position: static;
        width: auto;
        align-items: stretch;
        padding: 12px 3% 16px;
    }
    .search-submit span {
        display: none;
    }
    /* The two overlay labels share one 720px row and collide well before this
       width. The hint is the one to drop: it tells the visitor to hover, and
       the devices in this range mostly cannot. */
    .scene-labels {
        justify-content: flex-end;
    }
    [data-scene-hint] {
        display: none;
    }
    .site-footer {
        grid-template-columns: 1fr;
        gap: var(--space-lg);
    }
    .footer-legal {
        margin-top: var(--space-sm);
    }
    .soon-stage {
        --soon-side-min: 10rem;
        --soon-gap:      4px;
    }
    .soon-frame {
        padding: 22px 16px 26px;
    }
    .soon-box {
        padding: var(--space-md) 12px;
    }
    .soon-title {
        font-size: var(--font-size-sm);
    }
    .soon-back {
        padding: 8px 14px;
        font-size: var(--font-size-sm);
    }
}

/* Coming-soon placeholder, compact. Set by the page's own geometry rather than
   by a shared breakpoint: a box-high drawing is ~0.86 of the box's height wide,
   and below this width the box wraps its message so far that the two drawings
   no longer fit beside it. Measured, not guessed: the wardrobe makes the
   frame ~56px taller than the bare box, so at 601px Russian wraps to three
   lines and the drawing wants 165px where its column holds 160px; 641px fits
   with room to spare. The box keeps the
   width, being the message, and the mascots step down to a fixed share of the
   screen, centred on the box's middle instead of spanning its full height. */
@media (max-width: 640px) {
    .soon-stage {
        grid-template-columns: 17vw minmax(0, 1fr) 17vw;
        column-gap: 4px;
    }
    .soon-side {
        display: flex;
        align-items: center;
    }
    .soon-mascot {
        position: static;
        width: 100%;
        height: auto;
    }
}

/* Phone header. Six children — logo, language, day/night, the nav toggle and
   the account button — sum to about 392px at the default sizes, which does not
   fit a 390px screen, let alone a 360px one, and the account button was
   pushing a horizontal scrollbar onto the page. Everything steps down together
   rather than letting the last control clip off the right edge. */
@media (min-width: 521px) and (max-width: 768px) {
    .logo {
        margin-right: calc(var(--header-spacer-min) + 3px);
    }
}

@media (max-width: 480px) {
    .header-container {
        --header-spacer-min: 2px;
        height: 5rem;
    }
    .logo {
        padding: 4px 4px;
        margin-right: calc(var(--header-spacer-min) + 1.5px);
    }
    .logo img {
        height: 1.9rem;
    }
    .theme-toggle {
        height: 32px;
    }
    .theme-option {
        width: 30px;
    }
    .theme-option svg {
        width: 16px;
        height: 16px;
    }
    .account-toggle {
        width: 32px;
        height: 32px;
    }
    .account-toggle svg {
        width: 19px;
        height: 19px;
    }
    .lang-current,
    .lang-option {
        padding: 5px 10px;
        font-size: var(--font-size-xs);
    }
    .nav-toggle {
        font-size: var(--font-size-sm);
        padding: 4px 8px;
    }
    .nav-bar {
        top: 5rem;
    }
    .scene-chrome {
        padding: 10px 12px;
        gap: var(--space-sm);
    }
    .search-input {
        font-size: var(--font-size-xs);
    }
    .overlay-label {
        font-size: 12px;
    }
    .soon-frame {
        padding: 20px 14px 24px;
    }
    .soon-box {
        padding: var(--space-md) var(--space-sm);
    }
}

/* Very narrow phones (320px is still the floor worth supporting). The header's
   five controls do not fit even at the 480px sizes, and the logo is both the
   widest item and the one that loses least by shrinking. */
@media (max-width: 380px) {
    .logo img {
        height: 1.5rem;
    }
    .nav-toggle {
        font-size: var(--font-size-xs);
        padding: 4px 6px;
    }
}

/* Same reasoning as the 768px rule, but keyed off the pointer rather than the
   width: a wide touch screen cannot hover either. */
@media (hover: none) {
    .scene-labels {
        justify-content: flex-end;
    }
    [data-scene-hint] {
        display: none;
    }
}

/* Motion is decoration here: the fade and the caret spin carry no
   information the markup does not already state. */
@media (prefers-reduced-motion: reduce) {
    .scene-view,
    .rack-frame,
    .shelf-frame,
    .nav-menu-caret,
    .search-submit,
    .search-submit img,
    .soon-back {
        transition: none;
    }
}
