/* =============================================================================
   ATRIUM · 04 · THE PLAN — shell, navigation & global layout
   =============================================================================
   The courtyard plan. Content is the COURT: centred, lit, held at a reading
   measure. Navigation is the COLONNADE: a covered walk at the edge, sitting
   on bare canvas so the court is the brightest thing on screen. The top bar
   is the LINTEL: a quiet beam that only asserts itself once content passes
   beneath it.

   POSTURES — one house, three ways of walking it (not "breakpoints"):

     ENFILADE   < 48em   phone. One room at a time; doorways at the bottom
                         edge, within thumb's reach.
     ADJOINING  48–64em  small tablet. Two spaces may stand open.
     FLOOR PLAN ≥ 64em   desktop. The colonnade seats itself at the side.
     ESTATE     ≥ 100em  ultrawide. The house docks to a centred frame; the
                         room does not stretch to the walls.

   THE ALIGNMENT LINE
   One gutter — --at-mat — is shared by the lintel, the court, the colonnade
   and the colophon, and it widens with the posture. Everything in the
   product therefore starts on the same vertical line at every size. This is
   the single most load-bearing rule in the file.

   CONTRACTS (markup + JS depend on these names — do not rename):
     .v-shell .v-main .v-page-header .v-page-title .v-back
     .v-topbar .v-topbar__inner .v-brand .v-topbar__actions .v-navicon
     .v-rail .v-rail__list .v-rail__link .v-rail--adaptive   (pjax syncChrome)
     .v-tabbar .v-tabbar__item .v-tabbar__compose            (pjax syncChrome)
     .v-topbar-spacer .v-tabbar-spacer  #pjax-root  .pjax-progress (pjax.js)
     .v-footer .v-footer__inner .v-footer__brand .v-footer__links
     .v-footer__meta
   ========================================================================== */

/* =============================================================================
   0 · THE ALIGNMENT LINE
   -----------------------------------------------------------------------------
   The gutter grows with the room. Because the shell's max-width is computed
   from it, the content column and every piece of chrome breathe together.
   ========================================================================== */
:root {
    --at-frame: 96rem;
    /* The width of the vertical scrollbar, measured live. Because the html
       element reserves a stable gutter (below), 100% is always the content
       box and 100vw is always the full viewport, so their difference is the
       gutter — deterministic even when no scrollbar is currently drawn. */
    --at-sbw: calc(100vw - 100%);
}

/* Reserve the scrollbar's gutter always. Two wins: content never jumps
   sideways when navigating between a scrolling and a non-scrolling page
   (the single most common "not-native" tell on the web), and the frame math
   at the Estate tier has an exact number to subtract instead of guessing. */
html { scrollbar-gutter: stable; }

@media (max-width: 47.98em) { :root { --at-mat: var(--at-s3); } }  /* 12 */
@media (min-width: 64em)    { :root { --at-mat: var(--at-s6); } }  /* 24 */

/* =============================================================================
   1 · THE COURT — page container & content widths
   -----------------------------------------------------------------------------
   The measure is fixed: a content column holds its reading width and gives
   everything extra to calm margin. Wide screens get quieter, not wider.
   ========================================================================== */
.v-shell {
    width: 100%;
    max-width: calc(var(--at-measure) + 2 * var(--at-mat));
    margin-inline: auto;
    padding-inline: var(--at-mat);
}

/* Working rooms: tables, market, admin. */
.v-shell--wide { max-width: calc(var(--at-measure-wide) + 2 * var(--at-mat)); }

/* Immersive rooms: the frame itself. */
.v-shell--full { max-width: var(--at-frame); }

.v-main {
    min-width: 0;
    padding-block: var(--at-page-top) var(--at-page-bottom);
}

/* Fixed-chrome clearance. The bars are out of flow; these reserve their
   space so no page needs private padding. Both include their safe areas —
   a notch must not eat the bar's own height. */
.v-topbar-spacer {
    height: calc(var(--at-topbar-h) + env(safe-area-inset-top, 0px));
}

.v-tabbar-spacer {
    height: calc(var(--at-s5));
}

/* --- Responsive grid -------------------------------------------------------
   One grid for every gallery, card wall and tile field. Columns are derived
   from a minimum item width, so the grid reflows without media queries and
   never needs a page-level override.

       <div class="v-grid v-grid--3"> … </div>

   min() guards the narrow end: a 15rem minimum must not overflow a 320px
   phone. */
.v-grid {
    --grid-min: 15rem;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(var(--grid-min), 100%), 1fr));
    gap: var(--at-s4);
}

.v-grid--tight { gap: var(--at-s2); }
.v-grid--loose { gap: var(--at-s6); }

/* Intent, not column counts: the number is the widest comfortable column. */
.v-grid--2 { --grid-min: 18rem; }
.v-grid--3 { --grid-min: 13rem; }
.v-grid--4 { --grid-min: 10rem; }
.v-grid--gallery { --grid-min: 7.5rem; gap: var(--at-s2); }

/* Horizontal wrap for chips, tags, stats. */
.v-cluster {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--at-s2);
}

/* A single-line row that scrolls instead of wrapping (filter chips, stat
   rows). Replaces the hand-rolled overflow/scrollbar-hiding inline styles
   the pages used to carry. */
.v-scroller {
    display: flex;
    align-items: center;
    gap: var(--at-s2);
    overflow-x: auto;
    scrollbar-width: none;
    scroll-snap-type: x proximity;
    overscroll-behavior-inline: contain;
    -webkit-overflow-scrolling: touch;
}

.v-scroller::-webkit-scrollbar { display: none; }

.v-scroller > * {
    flex: 0 0 auto;
    scroll-snap-align: start;
}

/* Let a scroller run to the room's edges while its items stay on the line. */
.v-scroller--bleed {
    margin-inline: calc(var(--at-mat) * -1);
    padding-inline: var(--at-mat);
}

/* Full-bleed exception — immersive media only. */
.v-bleed {
    margin-inline: calc(var(--at-mat) * -1);
    border-radius: 0;
}

/* =============================================================================
   2 · PAGE HEADER
   ========================================================================== */
.v-page-header {
    display: flex;
    align-items: center;
    gap: var(--at-s3);
    min-height: var(--at-control-h);
    margin-bottom: var(--at-s6);
}

.v-page-title {
    margin: 0;
    /* A room announces itself in the Inscription. */
    font-family: var(--at-font-inscription);
    font-size: var(--at-text-2xl);
    font-weight: var(--at-weight-display);
    letter-spacing: var(--at-tracking-display);
    line-height: var(--at-leading-tight);
}

/* The way back out. Sits on the alignment line, hanging into the gutter so
   the title — not the chevron — starts the column. */
.v-back {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: var(--at-control-h-sm);
    height: var(--at-control-h-sm);
    margin-inline-start: calc(var(--at-s2) * -1);
    border-radius: var(--at-r-pill);
    color: var(--at-ink-secondary);
    font-size: var(--at-icon-md);
    transition: background-color var(--at-t-fast) var(--at-ease-out),
                color var(--at-t-fast) var(--at-ease-out);
}

.v-back:hover {
    background-color: var(--at-surface-hover);
    color: var(--at-ink);
}

.v-back:active { transform: scale(0.92); }

/* =============================================================================
   3 · THE LINTEL — top bar
   -----------------------------------------------------------------------------
   At the very top of a page the lintel steps back and lets the room read
   taller: the eye meets content, not chrome. The moment content passes
   beneath it, it takes its surface and its mortar joint.

   FAIL-SAFE BY CONSTRUCTION. The bar is solid in CSS; the shell script adds
   .at-top only once an IntersectionObserver confirms the page is genuinely
   at the top. If the script never runs, or the observer is unsupported, the
   bar simply stays solid — content can never slide under an invisible bar.
   (A scroll-linked animation was the tidier idea, but `scroll()` timelines
   report as supported and then sit inactive in real engines, and the
   failure mode there is exactly the one we refuse to ship.)
   ========================================================================== */
.v-topbar {
    position: fixed;
    inset-inline: 0;
    top: 0;
    z-index: var(--at-z-nav);
    /* The safe area is added to the height, never subtracted from it. */
    height: calc(var(--at-topbar-h) + env(safe-area-inset-top, 0px));
    padding-top: env(safe-area-inset-top, 0px);
    /* ALWAYS SOLID. The bar keeps its surface and mortar joint at every scroll
       position — an app's title bar never dissolves into the content beneath
       it. (It used to go transparent at the very top via .at-top; that read as
       a web page and let banners bleed through the chrome.) */
    background-color: var(--at-surface);
    border-bottom: 1px solid var(--at-hairline);
}

/* .at-top is retained as an inert hook (the sentinel/observer still set it) but
   no longer changes the bar's surface — the header is solid regardless. */

/* The observer's mark. It sits at the document's top edge and scrolls away
   with the page; it paints nothing. */
.v-topbar-sentinel {
    position: absolute;
    top: 0;
    inset-inline-start: 0;
    width: 1px;
    height: 2rem;
    pointer-events: none;
    visibility: hidden;
}

.v-topbar__inner {
    display: flex;
    align-items: center;
    gap: var(--at-s2);
    height: 100%;
    max-width: var(--at-frame);
    margin-inline: auto;
    padding-inline: var(--at-mat);
}

.v-brand {
    display: inline-flex;
    align-items: center;
    gap: var(--at-s2);
    flex: 0 0 auto;
    color: var(--at-ink);
    font-family: var(--at-font-inscription);
    font-size: var(--at-text-lg);
    font-weight: var(--at-weight-display);
    letter-spacing: var(--at-tracking-display);
    -webkit-tap-highlight-color: transparent;
    padding-right: var(--at-s5);
}

.v-brand:hover { color: var(--at-ink); }
.v-brand img { height: 1.5rem; width: auto; display: block; }

.v-topbar__actions {
    display: flex;
    align-items: center;
    gap: var(--at-s1);
    margin-inline-start: auto;
    flex: 0 0 auto;
}

/* Icon action in the bar — may carry a dot or a count. */
.v-navicon {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: var(--at-control-h);
    height: var(--at-control-h);
    border: 0;
    border-radius: var(--at-r-pill);
    background: transparent;
    color: var(--at-ink-secondary);
    font-size: var(--at-icon-md);
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    transition: background-color var(--at-t-fast) var(--at-ease-out),
                color var(--at-t-fast) var(--at-ease-out),
                transform var(--at-t-instant) var(--at-ease-spring);
}

.v-navicon:hover {
    background-color: var(--at-surface-hover);
    color: var(--at-ink);
}

.v-navicon.is-active { color: var(--at-accent); }
.v-navicon:active { transform: scale(0.92); }

.v-navicon .v-dot   { position: absolute; top: 9px; inset-inline-end: 9px; }
.v-navicon .v-count { position: absolute; top: 3px; inset-inline-end: 3px; }

/* =============================================================================
   4 · THE COLONNADE — desktop rail
   -----------------------------------------------------------------------------
   The walk sits on bare canvas with no border and no surface of its own, so
   the content column reads as the lit court beside it. Hierarchy by
   luminance, exactly as the tokens require — a bordered box would be the
   lazy answer.
   ========================================================================== */
.v-rail {
    display: none;
    position: fixed;
    top: calc(var(--at-topbar-h) + env(safe-area-inset-top, 0px));
    bottom: 0;
    inset-inline-start: 0;
    z-index: var(--at-z-rail);
    width: var(--at-rail-w);
    padding: var(--at-s5) var(--at-s3) var(--at-s6);
    overflow-y: auto;
    overscroll-behavior: contain;
    /* Reserve the scrollbar so hovering the rail never shifts its links. */
    scrollbar-gutter: stable;
}

.v-rail__list {
    display: flex;
    flex-direction: column;
    gap: var(--at-s0);
    margin: 0;
    padding: 0;
    list-style: none;
}

/* Section key inside the walk. */
.v-rail__label {
    margin: var(--at-s5) var(--at-s3) var(--at-s1);
    font-size: var(--at-text-xs);
    font-weight: var(--at-weight-semibold);
    letter-spacing: var(--at-tracking-caps);
    text-transform: uppercase;
    color: var(--at-ink-faint);
}

.v-rail__sep {
    height: 1px;
    margin: var(--at-s3) var(--at-s3);
    background: var(--at-hairline);
    list-style: none;
}

.v-rail__link {
    position: relative;
    display: flex;
    align-items: center;
    gap: var(--at-s3);
    min-height: 2.375rem;
    padding: var(--at-s2) var(--at-s3);
    border-radius: var(--at-r-md);
    color: var(--at-ink-secondary);
    /* Chrome speaks a size quieter than content. */
    font-size: var(--at-text-sm);
    font-weight: var(--at-weight-medium);
    text-decoration: none;
    -webkit-tap-highlight-color: transparent;
    transition: background-color var(--at-t-fast) var(--at-ease-out),
                color var(--at-t-fast) var(--at-ease-out);
}

.v-rail__link:hover {
    background-color: var(--at-surface-hover);
    color: var(--at-ink);
}

.v-rail__link:active { background-color: var(--at-surface-sunken); }

/* The icon column is fixed so every label starts on one line. */
.v-rail__link .v-icon {
    width: 1.25rem;
    flex: 0 0 auto;
    font-size: var(--at-icon-md);
    color: inherit;
}

/* Current place: surface + weight + Porphyry icon. Three signals, never
   colour alone. */
.v-rail__link[aria-current="page"],
.v-rail__link.is-active {
    background-color: var(--at-surface);
    color: var(--at-ink);
    font-weight: var(--at-weight-semibold);
    box-shadow: var(--at-edge), var(--at-shadow-xs);
}

.v-rail__link[aria-current="page"] .v-icon,
.v-rail__link.is-active .v-icon { color: var(--at-accent); }

.v-rail__link .v-count { margin-inline-start: auto; }

/* Anything the walk carries at its foot (account, version, quiet actions). */
.v-rail__foot {
    margin-top: auto;
    padding-top: var(--at-s5);
}

/* =============================================================================
   5 · THE THRESHOLDS — mobile tab bar
   -----------------------------------------------------------------------------
   Doorways at the bottom edge. Solid plaster, never glass; the home
   indicator gets its own room.
   ========================================================================== */
.v-tabbar {
    position: fixed;
    inset-inline: 0;
    bottom: 0;
    z-index: var(--at-z-nav);
    display: flex;
    align-items: stretch;
    height: calc(var(--at-tabbar-h) + env(safe-area-inset-bottom, 0px));
    padding-bottom: env(safe-area-inset-bottom, 0px);
    background-color: var(--at-surface);
    border-top: 1px solid var(--at-hairline);
}

.v-tabbar__item {
    position: relative;
    display: flex;
    flex: 1 1 0;
    min-width: 0;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    border: 0;
    background: transparent;
    color: var(--at-ink-muted);
    font-size: 0.625rem;
    font-weight: var(--at-weight-medium);
    letter-spacing: var(--at-tracking-meta);
    text-decoration: none;
    -webkit-tap-highlight-color: transparent;
    transition: color var(--at-t-base) var(--at-ease-out);
}

.v-tabbar__item > span {
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.v-tabbar__item .v-icon {
    font-size: var(--at-icon-lg);
    transition: transform var(--at-t-base) var(--at-ease-spring);
}

.v-tabbar__item:hover { color: var(--at-ink-secondary); }

.v-tabbar__item[aria-current="page"],
.v-tabbar__item.is-active { color: var(--at-accent); }

.v-tabbar__item.is-active .v-icon,
.v-tabbar__item[aria-current="page"] .v-icon { transform: translateY(-1px); }

.v-tabbar__item:active .v-icon {
    transform: scale(0.88);
    transition-duration: var(--at-t-instant);
}

/* The current threshold is marked above the icon — never colour alone. */
.v-tabbar__item.is-active::before,
.v-tabbar__item[aria-current="page"]::before {
    content: "";
    position: absolute;
    top: 0;
    left: 50%;
    width: 1.25rem;
    height: 2px;
    border-radius: 0 0 var(--at-r-pill) var(--at-r-pill);
    background-color: var(--at-accent);
    transform: translateX(-50%);
}

.v-tabbar__item .v-count {
    position: absolute;
    top: 4px;
    left: 55%;
}

/* The one primary act, always within reach. */
.v-tabbar__compose {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 2.625rem;
    height: 2.625rem;
    border-radius: var(--at-r-pill);
    background-color: var(--at-accent);
    color: var(--at-accent-contrast);
    font-size: var(--at-icon-md);
    box-shadow: var(--at-shadow-md);
    transition: transform var(--at-t-fast) var(--at-ease-spring),
                background-color var(--at-t-fast) var(--at-ease-out);
}

.v-tabbar__item:active .v-tabbar__compose { transform: scale(0.92); }

/* =============================================================================
   6 · LOADING STATES
   -----------------------------------------------------------------------------
   One progress element for the whole product: the filament pjax.js creates
   as .pjax-progress. It crosses the top of the frame with a soft leading
   glow, then retires. (.v-navprogress was a second, unused definition of
   the same thing and is gone — the class JS actually emits is the one the
   system styles.)
   ========================================================================== */
.pjax-progress {
    position: fixed;
    top: 0;
    inset-inline-start: 0;
    z-index: calc(var(--at-z-toast) + 1);
    height: 2px;
    width: 0;
    background: var(--at-accent);
    box-shadow: 0 0 0.75rem var(--at-accent);
    opacity: 0;
    pointer-events: none;
    transition: width var(--at-t-base) var(--at-ease-out),
                opacity var(--at-t-fast) var(--at-ease-out);
}

/* Creeps toward — never reaches — the far wall while the room loads. */
.pjax-progress.is-active {
    opacity: 1;
    width: 72%;
    transition: width 1.4s cubic-bezier(0.1, 0.7, 0.25, 1),
                opacity var(--at-t-instant) var(--at-ease-out);
}

.pjax-progress.is-done {
    opacity: 0;
    width: 100%;
    transition: width var(--at-t-fast) var(--at-ease-out),
                opacity var(--at-t-slow) var(--at-ease-out) 80ms;
}

/* Inline spinner for regions that fetch their own content. Shares the
   v-spin keyframe already defined by the button's loading state. */
.v-spinner {
    display: inline-block;
    width: 1.25rem;
    height: 1.25rem;
    border: 2px solid var(--at-hairline-strong);
    border-top-color: var(--at-accent);
    border-radius: 50%;
    animation: v-spin 0.7s linear infinite;
}

.v-spinner--sm { width: 0.875rem; height: 0.875rem; border-width: 1.5px; }

/* A region waiting on data dims and stops taking input; it never collapses,
   so the page cannot jump when the content lands. */
[aria-busy="true"] {
    opacity: 0.6;
    pointer-events: none;
    transition: opacity var(--at-t-base) var(--at-ease-out);
}

/* =============================================================================
   7 · PAGE ARRIVAL
   -----------------------------------------------------------------------------
   The room stays still; the page comes into the light. #pjax-root is the
   swap target, so it is the thing that lights.
   ========================================================================== */
#pjax-root,
.v-page-enter {
    animation: v-rise var(--at-t-slower) var(--at-ease-out);
}

/* During an in-app swap the page lights as one — per-card staggering on top
   of it reads as a double bounce. */
#pjax-root.is-swapping .v-card,
.v-page-enter.is-swapping .v-card { animation: none; }

/* =============================================================================
   8 · THE COLOPHON — global footer
   -----------------------------------------------------------------------------
   The end of the column, not a second navigation. It holds the reading
   measure and the alignment line, set well below the last content.
   ========================================================================== */
.v-footer {
    max-width: calc(var(--at-measure) + 2 * var(--at-mat));
    margin: var(--at-s10) auto 0;
    padding: var(--at-s6) var(--at-mat) var(--at-s9);
    /* border-top: 1px solid var(--at-hairline); */
}

.v-footer__inner {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--at-s2) var(--at-s5);
}

.v-footer__brand {
    font-family: var(--at-font-inscription);
    font-size: var(--at-text-md);
    font-weight: var(--at-weight-display);
    letter-spacing: var(--at-tracking-display);
    color: var(--at-ink-secondary);
}

.v-footer__links {
    display: flex;
    flex-wrap: wrap;
    gap: var(--at-s2) var(--at-s4);
}

.v-footer__links a {
    font-size: var(--at-text-xs);
    letter-spacing: var(--at-tracking-meta);
    color: var(--at-ink-muted);
    transition: color var(--at-t-fast) var(--at-ease-out);
}

.v-footer__links a:hover { color: var(--at-ink); }

.v-footer__meta {
    margin-inline-start: auto;
    font-size: var(--at-text-xs);
    color: var(--at-ink-faint);
    font-variant-numeric: tabular-nums;
}

/* =============================================================================
   9 · POSTURES
   ========================================================================== */
/* ENFILADE (< 48em) — thresholds on, colonnade off. */
@media (max-width: 47.98em) {
    .v-main { padding-block: var(--at-page-top-mobile) var(--at-page-bottom-mobile); }
    .v-page-header { margin-bottom: var(--at-s5); }
    .v-footer { margin-top: var(--at-s5); padding-bottom: var(--at-s11); }
    .v-footer__inner { flex-direction: column; align-items: flex-start; }
    .v-footer__meta { margin-inline-start: 0; }
}

/* ADJOINING & beyond (≥ 48em) — the thresholds retire. */
@media (min-width: 48em) {
    .v-tabbar,
    .v-tabbar-spacer { display: none; }
}

/* ADJOINING split: list-and-detail. The host page marks the two panes. */
@media (min-width: 48em) and (max-width: 63.98em) {
    .v-split {
        display: grid;
        grid-template-columns: minmax(16rem, 22rem) minmax(0, 1fr);
        height: calc(100dvh - var(--at-topbar-h));
    }

    .v-split__list {
        border-inline-end: 1px solid var(--at-hairline);
        overflow-y: auto;
        overscroll-behavior: contain;
    }

    .v-split__detail { overflow-y: auto; overscroll-behavior: contain; }
}

/* FLOOR PLAN (≥ 64em) — the colonnade seats itself; the body cedes its
   width. The rail is fixed, so pages need no markup change to sit beside it. */
@media (min-width: 64em) {
    .v-rail { display: flex; flex-direction: column; }

    body { padding-inline-start: var(--at-rail-w); }

    /* The alignment line pays off here with no extra rule: the gutter is
       24px, and the colonnade's own inset (12) plus its link inset (12) is
       also 24 — so the brand and every rail label start on one line. */

    /* An optional inspector column for pages that declare one. The main
       column keeps the reading measure; the aside takes what is left. */
    .v-with-inspector {
        display: grid;
        grid-template-columns: minmax(0, 1fr) 19rem;
        gap: var(--at-s7);
        align-items: start;
    }

    .v-inspector {
        position: sticky;
        top: calc(var(--at-topbar-h) + var(--at-s4));
        align-self: start;
        max-height: calc(100dvh - var(--at-topbar-h) - var(--at-s8));
        overflow-y: auto;
    }
}

/* Below the Floor Plan there is no room for a second column, so the aside
   folds above the content it introduces (suggestions belong near the top of
   a feed, not buried under fifty posts). */
@media (max-width: 63.98em) {
    .v-with-inspector { display: flex; flex-direction: column; }
    .v-with-inspector > .v-inspector { order: -1; }
}

/* ESTATE (≥ 100em) — the house docks to a centred frame.
   -----------------------------------------------------------------------------
   The frame's side margin is measured in the CONTENT box, exactly like the
   `margin-inline: auto` that centres .v-shell and .v-topbar__inner — so the
   fixed rail, the lintel and the content column all start on one vertical
   line. The earlier `(100vw - frame)/2` measured in the full viewport and
   left the rail half a scrollbar (5px) off the content it introduces. */
@media (min-width: 100em) {
    :root { --at-frame-margin: max(0px, (100vw - var(--at-sbw) - var(--at-frame)) / 2); }

    .v-rail { inset-inline-start: var(--at-frame-margin); }

    body {
        padding-inline-start: calc(var(--at-frame-margin) + var(--at-rail-w));
        padding-inline-end: var(--at-frame-margin);
    }

    .v-topbar__inner { padding-inline: var(--at-s6); }
}

/* Rooms without a colonnade (auth, admin login) never reserve its gutter. */
@media (min-width: 64em) {
    body:not(:has(.v-rail)) { padding-inline-start: 0; }
}

@media (min-width: 100em) {
    body:not(:has(.v-rail)) { padding-inline: 0; }
}

/* =============================================================================
   10 · PRINT — the chrome retires; the content remains.
   ========================================================================== */
@media print {
    .v-topbar, .v-tabbar, .v-rail, .v-toast, .pjax-progress,
    .v-topbar-spacer, .v-tabbar-spacer { display: none !important; }

    body { padding: 0 !important; background: #fff; }
    .v-shell, .v-shell--wide { max-width: none; padding-inline: 0; }
}
