/* _content/ObsidianBinder.Web/Components/Layout/AuthLayout.razor.rz.scp.css */
.auth[b-zcs6o6oe1g] {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    padding: var(--ob-space-4);
}

/* Wide on a monitor, with the form held to a readable column inside it. */
.auth__panel[b-zcs6o6oe1g] {
    width: 100%;
    max-width: 44rem;
    padding: var(--ob-space-7) var(--ob-space-5);
    background-color: var(--ob-surface);
    border: 1px solid var(--ob-border);
    border-radius: var(--ob-radius-lg);
}

.auth__content[b-zcs6o6oe1g] {
    max-width: 36rem;
    margin: 0 auto;
}

.auth__brand[b-zcs6o6oe1g] {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--ob-space-2);
    margin-bottom: var(--ob-space-5);
    font-size: var(--ob-text-xl);
}

@media (max-width: 36rem) {
    .auth[b-zcs6o6oe1g] { padding: var(--ob-space-3); }
    .auth__panel[b-zcs6o6oe1g] { padding: var(--ob-space-5) var(--ob-space-4); }
}

/* The topbar's mark, restated: its rules are scoped to TopBar and cannot reach here, and sharing
   them would mean unscoping them for the sake of one glyph. */
.auth__mark[b-zcs6o6oe1g] {
    width: 20px;
    height: 26px;
    flex-shrink: 0;
    clip-path: polygon(50% 0%, 100% 30%, 82% 100%, 18% 100%, 0% 30%);
    background: linear-gradient(150deg, var(--ob-accent-hover) 0%, var(--ob-accent) 45%, #35236e 100%);
}

.auth__wordmark[b-zcs6o6oe1g] { font-weight: 300; }
.auth__wordmark strong[b-zcs6o6oe1g] { font-weight: 600; }
/* _content/ObsidianBinder.Web/Components/Layout/MainLayout.razor.rz.scp.css */
/*
    Sidebar beside a scrolling main column, both sitting under a sticky top bar. The sidebar is a
    fixed width rather than a collapsible drawer: this is a desktop-first tool — cataloguing a box of
    cards happens at a desk — and the narrow layout below only has to stay usable, not be the
    primary target.
*/

.layout[b-r0wk86i2j6] {
    display: flex;
    align-items: flex-start;
    min-height: calc(100vh - 56px);
}

.layout__main[b-r0wk86i2j6] {
    flex: 1;
    min-width: 0;
}

/*
    Centred in whatever the nav leaves, rather than left-aligned against it. Without the auto
    margins a wide monitor put the whole page in its left two-thirds and left several hundred pixels
    of dead space down the right — 592px of it at 2200px wide.

    The nav stays pinned to the left edge deliberately: centring it along with the content strands
    it in the middle of the screen, away from the edge a sidebar is meant to sit against.
*/
.layout__content[b-r0wk86i2j6] {
    max-width: 1400px;
    margin-inline: auto;
    padding: var(--ob-space-6) var(--ob-space-6) var(--ob-space-7);
}

@media (max-width: 900px) {
    .layout[b-r0wk86i2j6] {
        flex-direction: column;
        align-items: stretch;
    }

    .layout__content[b-r0wk86i2j6] {
        padding: var(--ob-space-5) var(--ob-space-4) var(--ob-space-6);
    }
}
/* _content/ObsidianBinder.Web/Components/Layout/NavMenu.razor.rz.scp.css */
.side[b-6hgpbo1d8z] {
    display: flex;
    flex-direction: column;
    gap: var(--ob-space-5);
    flex-shrink: 0;
    width: 208px;
    padding: var(--ob-space-5) var(--ob-space-3);
    background-color: var(--ob-surface);
    border-right: 1px solid var(--ob-border);
    /* Sticks below the 56px top bar rather than the viewport top. */
    position: sticky;
    top: 56px;
    height: calc(100vh - 56px);
}

.side__group[b-6hgpbo1d8z] {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.side__head[b-6hgpbo1d8z] {
    padding: 0 var(--ob-space-3) var(--ob-space-1);
    color: var(--ob-text-faint);
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.09em;
    text-transform: uppercase;
}

/* ::deep, because NavLink renders its own anchor. Applies to the plain spans too, which is why the
   selector isn't scoped to anchors. */
[b-6hgpbo1d8z] .side__link {
    display: flex;
    align-items: center;
    gap: var(--ob-space-2);
    padding: var(--ob-space-2) var(--ob-space-3);
    border-radius: var(--ob-radius);
    color: var(--ob-text-muted);
    font-size: var(--ob-text-sm);
    font-weight: 500;
    transition: background-color 120ms ease, color 120ms ease;
}

/* A step dimmer than its label, so a column of icons reads as texture rather than a second list.
   The active link's icon comes back with the label. */
[b-6hgpbo1d8z] .side__link .ob-icon {
    color: var(--ob-text-faint);
    transition: color 120ms ease;
}

[b-6hgpbo1d8z] .side__link:hover .ob-icon,
[b-6hgpbo1d8z] .side__link.active .ob-icon {
    color: inherit;
}

[b-6hgpbo1d8z] .side__link:hover {
    background-color: var(--ob-surface-raised);
    color: var(--ob-text);
    text-decoration: none;
}

[b-6hgpbo1d8z] .side__link.active {
    background-color: var(--ob-accent-dim);
    color: var(--ob-text);
}

/* Counts are secondary to the label, and tabular so they don't jitter as they change. The auto
   margin sends them to the right edge now that the link itself packs from the left. */
[b-6hgpbo1d8z] .side__link em {
    margin-left: auto;
    font-style: normal;
    font-size: var(--ob-text-xs);
    color: var(--ob-text-faint);
    font-variant-numeric: tabular-nums;
}

[b-6hgpbo1d8z] .side__link.active em { color: var(--ob-text-muted); }

[b-6hgpbo1d8z] .side__link--planned {
    color: var(--ob-text-faint);
    cursor: not-allowed;
}

[b-6hgpbo1d8z] .side__link--planned:hover {
    background-color: transparent;
    color: var(--ob-text-faint);
}

@media (max-width: 900px) {
    .side[b-6hgpbo1d8z] {
        width: auto;
        height: auto;
        position: static;
        flex-direction: row;
        flex-wrap: wrap;
        gap: var(--ob-space-4);
        border-right: none;
        border-bottom: 1px solid var(--ob-border);
        padding: var(--ob-space-3) var(--ob-space-4);
    }

    .side__group[b-6hgpbo1d8z] {
        flex-direction: row;
        align-items: center;
        flex-wrap: wrap;
    }

    /* The group heading is a vertical-layout device; inline it just crowds the links. */
    .side__head[b-6hgpbo1d8z] { display: none; }
}
/* _content/ObsidianBinder.Web/Components/Layout/TopBar.razor.rz.scp.css */
.topbar[b-ymme2u9ut3] {
    display: flex;
    align-items: center;
    gap: var(--ob-space-6);
    height: 56px;
    padding: 0 var(--ob-space-5);
    background-color: var(--ob-chrome);
    border-bottom: 1px solid var(--ob-border);
    position: sticky;
    top: 0;
    z-index: 20;
}

.topbar__brand[b-ymme2u9ut3] {
    display: flex;
    align-items: center;
    gap: var(--ob-space-3);
    flex-shrink: 0;
    color: var(--ob-text);
    font-size: var(--ob-text-lg);
    letter-spacing: -0.02em;
}

.topbar__brand:hover[b-ymme2u9ut3] {
    text-decoration: none;
    color: var(--ob-text);
}

/* A sheared obsidian shard in two gradients and a clip path — an SVG asset for this would be one
   more thing to keep in sync with the palette. */
.topbar__mark[b-ymme2u9ut3] {
    width: 20px;
    height: 26px;
    flex-shrink: 0;
    clip-path: polygon(50% 0%, 100% 30%, 82% 100%, 18% 100%, 0% 30%);
    background: linear-gradient(150deg, var(--ob-accent-hover) 0%, var(--ob-accent) 45%, #35236e 100%);
}

.topbar__wordmark[b-ymme2u9ut3] { font-weight: 300; }
.topbar__wordmark strong[b-ymme2u9ut3] { font-weight: 600; }

/*
    Stretched to the bar's full height so each item can carry a bottom rule that lands on the bar's
    own border rather than floating above it. Vertically centring the links instead would put the
    rule wherever the text happens to sit.
*/
.topbar__areas[b-ymme2u9ut3] {
    display: flex;
    align-self: stretch;
    gap: var(--ob-space-1);
    min-width: 0;
    overflow-x: auto;
    scrollbar-width: none;
}

.topbar__areas[b-ymme2u9ut3]::-webkit-scrollbar { display: none; }

/* ::deep, because NavLink renders the anchor itself and scoped CSS would never reach it. */
/*
    Available areas sit at full text brightness, not muted. These are the primary navigation of the
    whole app — muting them made them read as secondary, and put them uncomfortably close to the
    planned items below, which are genuinely unavailable. The contrast that matters on this bar is
    available vs. not, so that is the gap worth widening.
*/
[b-ymme2u9ut3] .topbar__area {
    display: flex;
    align-items: center;
    height: 100%;
    padding: 0 var(--ob-space-3);
    color: var(--ob-text);
    font-size: var(--ob-text-sm);
    font-weight: 500;
    white-space: nowrap;
    transition: color 120ms ease, background-color 120ms ease, box-shadow 120ms ease;
}

/* Already at full brightness, so hover is carried by the background alone. */
[b-ymme2u9ut3] .topbar__area:hover {
    background-color: var(--ob-surface-raised);
    color: var(--ob-text);
    text-decoration: none;
}

/*
    Underline rather than a filled pill: the sidebar uses the fill, and using it here too would make
    the two levels read as one. Drawn as an inset shadow on a full-height link, so it sits flush on
    the bar's bottom border.

    Accent on the text as well as the rule — the rule alone is easy to miss when scanning, and the
    two together are what make the current area obvious at a glance.
*/
[b-ymme2u9ut3] .topbar__area.active {
    color: var(--ob-accent-text);
    background-color: transparent;
    box-shadow: inset 0 -2px 0 var(--ob-accent);
}

[b-ymme2u9ut3] .topbar__area.active:hover {
    background-color: var(--ob-surface-raised);
    color: var(--ob-accent-text-hover);
}

[b-ymme2u9ut3] .topbar__area--planned {
    color: var(--ob-text-faint);
    cursor: not-allowed;
}

[b-ymme2u9ut3] .topbar__area--planned:hover {
    background-color: transparent;
    color: var(--ob-text-faint);
}

.topbar__search[b-ymme2u9ut3] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--ob-space-3);
    flex: 1;
    max-width: 380px;
    margin-left: auto;
    padding: var(--ob-space-2) var(--ob-space-3);
    border: 1px solid var(--ob-border);
    border-radius: var(--ob-radius);
    background-color: var(--ob-bg);
    cursor: not-allowed;
}

.topbar__search-hint[b-ymme2u9ut3] {
    color: var(--ob-text-faint);
    font-size: var(--ob-text-sm);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.topbar__key[b-ymme2u9ut3] {
    flex-shrink: 0;
    padding: 1px var(--ob-space-2);
    border: 1px solid var(--ob-border);
    border-radius: var(--ob-radius-sm);
    background-color: var(--ob-surface-raised);
    color: var(--ob-text-faint);
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 10px;
}

@media (max-width: 900px) {
    .topbar[b-ymme2u9ut3] {
        height: auto;
        flex-wrap: wrap;
        gap: var(--ob-space-3);
        padding: var(--ob-space-3) var(--ob-space-4);
    }

    /*
        The bar wraps here, so "full height" no longer means the bar's height and the flush rule
        has nothing to sit against. Back to padded links with the rule directly under the text.
    */
    [b-ymme2u9ut3] .topbar__area {
        height: auto;
        padding: var(--ob-space-2) var(--ob-space-3);
    }

    /* First to go: the areas and brand matter more than a control that does nothing yet. */
    .topbar__search[b-ymme2u9ut3] { display: none; }
}

/* Anchors the menu; the summary is the avatar itself. */
.topbar__account[b-ymme2u9ut3] {
    position: relative;
    margin-left: var(--ob-space-3);
    flex-shrink: 0;
}

.topbar__avatar[b-ymme2u9ut3] {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    background-color: var(--ob-accent);
    background-image: linear-gradient(135deg, var(--ob-accent-hover), var(--ob-accent) 60%);
    color: var(--ob-on-accent);
    font-size: var(--ob-text-sm);
    font-weight: 600;
    cursor: pointer;
    user-select: none;
    /* The circle is the whole affordance — a disclosure triangle beside it reads as a second
       control. */
    list-style: none;
}

.topbar__avatar[b-ymme2u9ut3]::-webkit-details-marker { display: none; }

.topbar__account[open] .topbar__avatar[b-ymme2u9ut3],
.topbar__avatar:hover[b-ymme2u9ut3] {
    box-shadow: 0 0 0 2px var(--ob-bg), 0 0 0 4px var(--ob-accent);
}

.topbar__menu[b-ymme2u9ut3] {
    position: absolute;
    top: calc(100% + var(--ob-space-2));
    right: 0;
    z-index: 30;
    min-width: 220px;
    padding: var(--ob-space-2);
    border: 1px solid var(--ob-border);
    border-radius: var(--ob-radius);
    background-color: var(--ob-surface-raised);
    box-shadow: var(--ob-shadow-lifted);
}

/* Who is signed in — an answer, not an action, so it sits apart from the items below. */
.topbar__menu-user[b-ymme2u9ut3] {
    padding: var(--ob-space-2) var(--ob-space-3);
    margin-bottom: var(--ob-space-2);
    border-bottom: 1px solid var(--ob-border);
    color: var(--ob-text-muted);
    font-size: var(--ob-text-sm);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.topbar__menu-item[b-ymme2u9ut3] {
    display: flex;
    align-items: center;
    gap: var(--ob-space-2);
    width: 100%;
    padding: var(--ob-space-2) var(--ob-space-3);
    border: none;
    border-radius: var(--ob-radius-sm);
    background: none;
    color: var(--ob-text);
    font: inherit;
    font-size: var(--ob-text-sm);
    text-align: left;
    cursor: pointer;
    transition: background-color 120ms ease;
}

.topbar__menu-item:hover[b-ymme2u9ut3] {
    background-color: var(--ob-surface-hover);
}
/* _content/ObsidianBinder.Web/Components/Pages/CardPage.razor.rz.scp.css */
/* Art column beside the detail column; the art sticks so it stays visible while reading down. */
.card-layout[b-lhvcy7o4l4] {
    display: grid;
    grid-template-columns: 260px 1fr;
    gap: var(--ob-space-6);
    align-items: start;
}

.card-layout__art[b-lhvcy7o4l4] {
    position: sticky;
    top: var(--ob-space-6);
}

.card-layout__body[b-lhvcy7o4l4] {
    min-width: 0;
}

.card-layout__body h2[b-lhvcy7o4l4] {
    margin: var(--ob-space-6) 0 var(--ob-space-3);
    font-size: var(--ob-text-lg);
}

/* Overrides the grid tile's hover lift: nothing here is clickable, so it shouldn't invite a click. */
.card-hero:hover[b-lhvcy7o4l4] {
    transform: none;
}

.add-form[b-lhvcy7o4l4] {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: var(--ob-space-3);
    align-items: end;
    padding: var(--ob-space-4);
    background-color: var(--ob-surface);
    border: 1px solid var(--ob-border);
    border-radius: var(--ob-radius-lg);
}

.add-form__wide[b-lhvcy7o4l4] {
    grid-column: span 2;
}

.add-form__qty[b-lhvcy7o4l4] {
    max-width: 6rem;
}

.add-form__submit[b-lhvcy7o4l4] {
    justify-content: center;
}

@media (max-width: 900px) {
    .card-layout[b-lhvcy7o4l4] {
        grid-template-columns: 1fr;
    }

    .card-layout__art[b-lhvcy7o4l4] {
        position: static;
        max-width: 220px;
    }

    .add-form__wide[b-lhvcy7o4l4] {
        grid-column: auto;
    }
}
/* _content/ObsidianBinder.Web/Components/Pages/MyCollection.razor.rz.scp.css */
/* One step up the scale from the shared header meta, which is sized for the dense factual lines
   the other pages put there ("212 sets, newest first"). This one is a standing description of the
   page, so it reads as a subtitle rather than as a caption. Scoped rather than promoted to
   .ob-page-header__meta so the other six headers are left alone. */
.ob-page-header__meta[b-znyui8rogl] {
    font-size: var(--ob-text-base);
}
/* _content/ObsidianBinder.Web/Components/Pages/Privacy.razor.rz.scp.css */
.privacy[b-nv6n4bga5v]  h1 {
    margin: 0 0 var(--ob-space-3);
    font-size: var(--ob-text-xl);
}

.privacy[b-nv6n4bga5v]  h2 {
    margin: var(--ob-space-4) 0 var(--ob-space-2);
    font-size: var(--ob-text-md);
}

.privacy[b-nv6n4bga5v]  p,
.privacy[b-nv6n4bga5v]  li {
    color: var(--ob-text-muted);
    font-size: var(--ob-text-sm);
    line-height: 1.6;
}

.privacy[b-nv6n4bga5v]  ul {
    padding-left: var(--ob-space-4);
}

.privacy[b-nv6n4bga5v]  table {
    width: 100%;
    margin: var(--ob-space-2) 0;
    border-collapse: collapse;
    font-size: var(--ob-text-sm);
}

.privacy[b-nv6n4bga5v]  th,
.privacy[b-nv6n4bga5v]  td {
    padding: var(--ob-space-1) var(--ob-space-2);
    border-bottom: 1px solid var(--ob-border);
    text-align: left;
    vertical-align: top;
    color: var(--ob-text-muted);
}

.privacy[b-nv6n4bga5v]  th {
    color: var(--ob-text);
}
/* _content/ObsidianBinder.Web/Components/Pages/Sets.razor.rz.scp.css */
.ob-set-grid[b-ywssrbzsed] {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    gap: var(--ob-space-3);
    margin: 0;
    padding: 0;
    list-style: none;
}

.ob-set[b-ywssrbzsed] {
    display: grid;
    gap: var(--ob-space-1);
    height: 100%;
    padding: var(--ob-space-4);
    background-color: var(--ob-surface);
    border: 1px solid var(--ob-border);
    border-radius: var(--ob-radius-lg);
    color: var(--ob-text);
    transition: border-color 120ms ease, background-color 120ms ease;
}

.ob-set:hover[b-ywssrbzsed] {
    background-color: var(--ob-surface-raised);
    border-color: var(--ob-accent);
    text-decoration: none;
    color: var(--ob-text);
}

.ob-set__code[b-ywssrbzsed] {
    color: var(--ob-text-faint);
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: var(--ob-text-xs);
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.ob-set__name[b-ywssrbzsed] {
    font-size: var(--ob-text-lg);
    font-weight: 600;
    letter-spacing: -0.015em;
}

.ob-set__meta[b-ywssrbzsed] {
    color: var(--ob-text-muted);
    font-size: var(--ob-text-sm);
}

.ob-set__progress[b-ywssrbzsed] {
    display: flex;
    align-items: center;
    gap: var(--ob-space-3);
    margin-top: var(--ob-space-3);
}

.ob-set__bar[b-ywssrbzsed] {
    flex: 1;
    height: 3px;
    border-radius: 999px;
    background-color: var(--ob-bg);
    overflow: hidden;
}

.ob-set__fill[b-ywssrbzsed] {
    display: block;
    height: 100%;
    border-radius: inherit;
    background-color: var(--ob-accent);
}

.ob-set__count[b-ywssrbzsed] {
    color: var(--ob-text-faint);
    font-size: var(--ob-text-xs);
    font-variant-numeric: tabular-nums;
}
/* _content/ObsidianBinder.Web/Components/Pages/StorageLocations.razor.rz.scp.css */
.new-location[b-glu07srr2s] {
    display: flex;
    gap: var(--ob-space-2);
    max-width: 32rem;
    margin-bottom: var(--ob-space-5);
}
/* _content/ObsidianBinder.Web/Components/Shared/AddCards.razor.rz.scp.css */
.add[b-9ay99mff6g] {
    display: flex;
    flex-direction: column;
    gap: var(--ob-space-3);
    padding: var(--ob-space-4);
    margin-bottom: var(--ob-space-5);
    background-color: var(--ob-surface);
    border: 1px solid var(--ob-border-strong);
    border-radius: var(--ob-radius-lg);
}

.add__bar[b-9ay99mff6g] {
    display: flex;
    align-items: center;
    gap: var(--ob-space-3);
}

/* Deliberately large. This is the one control the whole panel exists for, and it should look like
   somewhere to start typing rather than like one field among several. */
.add__input[b-9ay99mff6g] {
    flex: 1;
    min-width: 0;
    padding: var(--ob-space-3) var(--ob-space-4);
    border: 1px solid var(--ob-border);
    border-radius: var(--ob-radius);
    background-color: var(--ob-bg);
    color: var(--ob-text);
    font: inherit;
    font-size: var(--ob-text-lg);
}

.add__input:focus[b-9ay99mff6g] {
    outline: none;
    border-color: var(--ob-accent);
    box-shadow: 0 0 0 3px var(--ob-accent-dim);
}

.add__input[b-9ay99mff6g]::placeholder { color: var(--ob-text-faint); }

.add__status[b-9ay99mff6g] {
    color: var(--ob-text-faint);
    font-size: var(--ob-text-xs);
    white-space: nowrap;
}

.add__defaults[b-9ay99mff6g] {
    display: flex;
    flex-wrap: wrap;
    gap: var(--ob-space-3);
}

.add__default[b-9ay99mff6g] {
    display: flex;
    flex-direction: column;
    gap: var(--ob-space-1);
    min-width: 8rem;
}

.add__default--qty[b-9ay99mff6g] { min-width: 4.5rem; max-width: 5.5rem; }

.add__hint[b-9ay99mff6g] {
    margin: 0;
    color: var(--ob-text-muted);
    font-size: var(--ob-text-sm);
}

/* ---------------------------------------------------------------- results */

.add__results[b-9ay99mff6g] {
    display: flex;
    flex-direction: column;
    gap: 2px;
    max-height: 22rem;
    overflow-y: auto;
    margin: 0;
    padding: 0;
    list-style: none;
}

.add__hit[b-9ay99mff6g] {
    display: grid;
    grid-template-columns: 28px 1fr auto;
    align-items: center;
    gap: var(--ob-space-3);
    padding: var(--ob-space-2);
    border-radius: var(--ob-radius);
    cursor: pointer;
}

.add__hit:hover[b-9ay99mff6g] { background-color: var(--ob-surface-raised); }

/* The keyboard cursor. A left rule as well as a fill, so which row Enter will act on stays obvious
   even when the mouse is hovering a different one. */
.add__hit--on[b-9ay99mff6g] {
    background-color: var(--ob-surface-hover);
    box-shadow: inset 2px 0 0 var(--ob-accent);
}

.add__thumb[b-9ay99mff6g] {
    aspect-ratio: var(--ob-card-aspect);
    border-radius: 3px;
    overflow: hidden;
    background: repeating-linear-gradient(
        135deg,
        var(--ob-surface-raised) 0 5px,
        var(--ob-surface) 5px 10px
    );
}

.add__thumb img[b-9ay99mff6g] { width: 100%; height: 100%; object-fit: cover; display: block; }

.add__hit-body[b-9ay99mff6g] { display: flex; flex-direction: column; min-width: 0; }

.add__hit-name[b-9ay99mff6g] {
    font-size: var(--ob-text-sm);
    font-weight: 500;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.add__hit-meta[b-9ay99mff6g] {
    color: var(--ob-text-faint);
    font-size: var(--ob-text-xs);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.add__hit-note[b-9ay99mff6g] {
    color: var(--ob-text-faint);
    font-size: var(--ob-text-xs);
    white-space: nowrap;
}

.add__printing[b-9ay99mff6g] { width: auto; min-width: 11rem; font-size: var(--ob-text-xs); }

.add__keys[b-9ay99mff6g] {
    display: flex;
    flex-wrap: wrap;
    gap: var(--ob-space-4);
    color: var(--ob-text-faint);
    font-size: var(--ob-text-xs);
}

.add__keys kbd[b-9ay99mff6g] {
    display: inline-block;
    padding: 1px 5px;
    margin-right: 2px;
    border: 1px solid var(--ob-border);
    border-radius: var(--ob-radius-sm);
    background-color: var(--ob-surface-raised);
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 10px;
}

/* ---------------------------------------------------------------- session log */

.add__log[b-9ay99mff6g] {
    display: flex;
    flex-direction: column;
    gap: var(--ob-space-1);
    padding-top: var(--ob-space-3);
    border-top: 1px solid var(--ob-border);
}

.add__log-head[b-9ay99mff6g] {
    display: flex;
    justify-content: space-between;
    gap: var(--ob-space-3);
    margin-bottom: var(--ob-space-1);
    color: var(--ob-text-faint);
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.09em;
    text-transform: uppercase;
}

.add__log-total[b-9ay99mff6g] { letter-spacing: 0.02em; text-transform: none; font-weight: 500; }

.add__log-row[b-9ay99mff6g] {
    display: grid;
    grid-template-columns: auto 1fr auto auto auto;
    align-items: center;
    gap: var(--ob-space-3);
    padding: var(--ob-space-2);
    border-radius: var(--ob-radius);
    background-color: var(--ob-bg);
    font-size: var(--ob-text-sm);
}

.add__log-tick[b-9ay99mff6g] { color: var(--ob-positive); }

.add__log-name[b-9ay99mff6g] {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.add__log-meta[b-9ay99mff6g] {
    color: var(--ob-text-faint);
    font-size: var(--ob-text-xs);
    white-space: nowrap;
}

@media (max-width: 640px) {
    .add__log-row[b-9ay99mff6g] {
        grid-template-columns: auto 1fr auto;
        row-gap: var(--ob-space-2);
    }
}
/* _content/ObsidianBinder.Web/Components/Shared/AddCardsMenu.razor.rz.scp.css */
.menu[b-9fjzjtbm6g] {
    position: relative;
    display: inline-block;
}

.menu__split[b-9fjzjtbm6g] {
    display: inline-flex;
}

/*
    Deliberately larger and squarer than the buttons around it — size is what marks the page's
    primary action here. The halves share one silhouette: outer corners rounded, the seam square,
    and the divider is the toggle's left border so it runs the full height of the fill.
*/
.menu__main[b-9fjzjtbm6g],
.menu__toggle[b-9fjzjtbm6g] {
    padding: var(--ob-space-3) var(--ob-space-4);
    font-size: var(--ob-text-base);
}

.menu__main[b-9fjzjtbm6g] {
    border-radius: var(--ob-radius-sm) 0 0 var(--ob-radius-sm);
}

.menu__toggle[b-9fjzjtbm6g] {
    padding-inline: var(--ob-space-3);
    border-left: 1px solid #ffffff2e;
    border-radius: 0 var(--ob-radius-sm) var(--ob-radius-sm) 0;
}

.menu__toggle svg[b-9fjzjtbm6g] {
    display: block;
    width: 16px;
    height: 16px;
}

/*
    Sits under the panel and over everything else, so a click anywhere outside closes the menu.
    Transparent on purpose — this is a small menu, not a modal, and dimming the page behind it would
    overstate what is happening.
*/
.menu__backdrop[b-9fjzjtbm6g] {
    position: fixed;
    inset: 0;
    z-index: 30;
}

.menu__panel[b-9fjzjtbm6g] {
    position: absolute;
    top: calc(100% + var(--ob-space-2));
    right: 0;
    z-index: 31;
    width: 19rem;
    padding: var(--ob-space-2);
    background-color: var(--ob-surface-raised);
    border: 1px solid var(--ob-border-strong);
    border-radius: var(--ob-radius-lg);
    box-shadow: var(--ob-shadow-lifted);
}

.menu__panel:focus[b-9fjzjtbm6g] { outline: none; }

.menu__head[b-9fjzjtbm6g] {
    padding: var(--ob-space-2) var(--ob-space-2) var(--ob-space-1);
    color: var(--ob-text-faint);
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.09em;
    text-transform: uppercase;
}

.menu__head--second[b-9fjzjtbm6g] {
    margin-top: var(--ob-space-2);
    padding-top: var(--ob-space-3);
    border-top: 1px solid var(--ob-border);
}

.menu__item[b-9fjzjtbm6g] {
    display: flex;
    align-items: flex-start;
    gap: var(--ob-space-3);
    width: 100%;
    padding: var(--ob-space-2);
    border: none;
    border-radius: var(--ob-radius);
    background: none;
    color: var(--ob-text);
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.menu__item:hover:not(:disabled)[b-9fjzjtbm6g] {
    background-color: var(--ob-surface-hover);
    text-decoration: none;
    color: var(--ob-text);
}

/*
    Dimmed rather than hidden, and not-allowed rather than pointer: the option is real and coming,
    it just isn't here. The tooltip carries why.
*/
.menu__item:disabled[b-9fjzjtbm6g] {
    color: var(--ob-text-faint);
    cursor: not-allowed;
}

.menu__icon[b-9fjzjtbm6g] {
    flex-shrink: 0;
    width: 16px;
    height: 16px;
    margin-top: 2px;
    color: var(--ob-text-muted);
}

.menu__item:disabled .menu__icon[b-9fjzjtbm6g] { color: var(--ob-text-faint); }

.menu__icon svg[b-9fjzjtbm6g] { width: 100%; height: 100%; display: block; }

.menu__text[b-9fjzjtbm6g] {
    display: flex;
    flex-direction: column;
    gap: 1px;
    min-width: 0;
}

.menu__label[b-9fjzjtbm6g] {
    font-size: var(--ob-text-sm);
    font-weight: 500;
}

.menu__hint[b-9fjzjtbm6g] {
    color: var(--ob-text-faint);
    font-size: var(--ob-text-xs);
    line-height: 1.4;
}

@media (max-width: 640px) {
    .menu__panel[b-9fjzjtbm6g] {
        width: min(19rem, calc(100vw - var(--ob-space-6)));
    }
}
/* _content/ObsidianBinder.Web/Components/Shared/CardDetailPanel.razor.rz.scp.css */
/*
    The same lane-and-sticky arithmetic as FilterPanel, on the other side of the results: the rail
    is exactly the grid's forfeited columns wide (--fp-lane, computed by .work__body), parks below
    the sticky filter bar, and scrolls internally rather than running off the bottom. The two rails
    are never open together — they would take their columns from the same lane.
*/
.detail[b-ocandst7x0] {
    flex: 0 0 var(--fp-lane, 16rem);
    align-self: flex-start;
    position: sticky;
    top: calc(56px + var(--ob-filters-h, 4rem) + var(--ob-space-3));
    display: flex;
    flex-direction: column;
    gap: var(--ob-space-3);
    max-height: calc(100vh - 56px - var(--ob-filters-h, 4rem) - var(--ob-space-5));
    overflow-y: auto;
    overscroll-behavior: contain;
    padding: var(--ob-space-3);
    background-color: var(--ob-surface);
    /* The grid-facing edge wears the same near-white as the opened tile's ring — the two ends of
       one relationship share a colour, and it is deliberately not the accent: violet belongs to
       selection, white to the rail (see .ob-card--expanded). */
    border: 1px solid var(--ob-border-strong);
    border-left: 3px solid color-mix(in srgb, var(--ob-text) 92%, transparent);
    border-radius: var(--ob-radius-lg);
    animation: detail-in-b-ocandst7x0 160ms ease;
}

/* Mount only, like the expander before it — Blazor removes the node on close, so there is nothing
   to animate out of. Retargeting replaces content inside a mounted node and plays nothing, which
   is the difference between switching cards and strobing. */
@keyframes detail-in-b-ocandst7x0 {
    from { opacity: 0; transform: translateX(8px); }
    to { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
    .detail[b-ocandst7x0] { animation: none; }
}

.detail__head[b-ocandst7x0] {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--ob-space-2);
}

.detail__title[b-ocandst7x0] {
    margin: 0;
    color: var(--ob-text);
    font-size: var(--ob-text-lg);
    font-weight: 600;
}

.detail__close[b-ocandst7x0] {
    display: inline-flex;
    align-items: center;
    padding: var(--ob-space-1);
    border: none;
    background: none;
    color: var(--ob-text-faint);
    font: inherit;
    cursor: pointer;
}

.detail__close:hover[b-ocandst7x0] { color: var(--ob-text); }

/* The tile's frame again — same aspect, same placeholder treatment, so a missing image still
   reads as an empty binder slot rather than a fault. Capped rather than rail-width: full width
   is ~415px tall at the card aspect, which made the rail a poster of the artwork the grid
   already shows and pushed the holdings below the fold. */
.detail__frame[b-ocandst7x0] {
    position: relative;
    width: min(100%, 11rem);
    margin-inline: auto;
    aspect-ratio: var(--ob-card-aspect);
    border-radius: var(--ob-radius-sm);
    overflow: hidden;
    background-color: var(--ob-surface-raised);
}

.detail__image[b-ocandst7x0] {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.detail__placeholder[b-ocandst7x0] {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 100%;
    color: var(--ob-text-faint);
    font-size: var(--ob-text-xl);
    font-variant-numeric: tabular-nums;
}

.detail__facts[b-ocandst7x0] {
    display: flex;
    flex-direction: column;
    gap: var(--ob-space-1);
}

.detail__set[b-ocandst7x0] {
    color: var(--ob-text);
    font-size: var(--ob-text-sm);
}

.detail__meta[b-ocandst7x0] {
    color: var(--ob-text-muted);
    font-size: var(--ob-text-xs);
    font-variant-numeric: tabular-nums;
}

.detail__entries[b-ocandst7x0] {
    display: flex;
    flex-direction: column;
    margin: 0;
    padding: 0;
    list-style: none;
}

.detail__entry[b-ocandst7x0] {
    display: flex;
    flex-direction: column;
    gap: var(--ob-space-1);
    padding: var(--ob-space-2) 0;
    border-top: 1px solid var(--ob-border);
}

.detail__entry-line[b-ocandst7x0] {
    display: flex;
    align-items: center;
    gap: var(--ob-space-2);
}

.detail__printing[b-ocandst7x0] {
    flex: 1;
    min-width: 0;
    color: var(--ob-text);
    font-size: var(--ob-text-sm);
}

.detail__qty[b-ocandst7x0] {
    color: var(--ob-text-muted);
    font-size: var(--ob-text-sm);
    font-variant-numeric: tabular-nums;
}

.detail__where[b-ocandst7x0] {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    color: var(--ob-text-muted);
    font-size: var(--ob-text-xs);
    text-overflow: ellipsis;
    white-space: nowrap;
}

.detail__view[b-ocandst7x0] { align-self: flex-start; }

/*
    Below the rail breakpoint it floats over the results, exactly as the filter panel does — same
    positioning context (.work__body turns relative there), same z-index rung, same full-bleed
    step at 640px.
*/
@media (max-width: 1100px) {
    .detail[b-ocandst7x0] {
        flex: none;
        position: absolute;
        z-index: 30;
        top: 0;
        right: 0;
        width: 19rem;
        background-color: var(--ob-surface-raised);
        border-radius: var(--ob-radius);
        box-shadow: var(--ob-shadow-lifted);
    }
}

@media (max-width: 640px) {
    .detail[b-ocandst7x0] {
        left: 0;
        right: 0;
        width: auto;
    }
}
/* _content/ObsidianBinder.Web/Components/Shared/CollectionList.razor.rz.scp.css */
/*
    The filter panel sits beside the results rather than over them. The reason is causal rather than
    cosmetic: ticking a set is meant to rewrite the grid, and until now the grid it rewrote was
    underneath the panel doing the rewriting.

    The bar above stays full width — halving the search box to make room for a rail would trade one
    problem for another.
*/
/*
    The rail and the grid are sized from one pair of numbers, and that is the whole trick to the
    cards not moving when it opens.

    Work in whole columns rather than pixels. If the rail's lane is exactly k column pitches wide,
    then opening it removes exactly k columns and every card that stays on screen keeps both its
    size and its position — the grid either side of the change is the same grid with fewer columns.
    Get the lane width wrong by even a few pixels and every card shifts and resizes slightly, which
    is what a filter panel was doing before this.

    Pitch is (width + gap) / columns, so the lane is k pitches less the gap that sits beside it:

        lane = k × (100% + gap) / cols − gap

    100% resolves against .work__body, whose width does not change when the rail opens — it is the
    closed-state width, which is exactly the reference the arithmetic needs.

    The consequence, accepted deliberately: the rail is not a fixed width. Pitch grows with the
    viewport, so a fixed rail could only be a whole number of columns at one screen size. It lands
    between 236px and 322px across the sizes and ranges below, which is a reasonable rail either
    way.
*/
.work__body[b-ae7p9k6g7a] {
    display: flex;
    align-items: flex-start;
    gap: var(--ob-space-4);

    /* Medium. Columns are counted rather than derived from a card width, so 1fr can stretch them to
       fill the row exactly — no slack at either edge, which a fixed width always leaves. */
    --cols: 6;
    --cols-open: 4;

    --fp-lane: calc(
        (var(--cols) - var(--cols-open)) * (100% + var(--ob-space-4)) / var(--cols)
        - var(--ob-space-4));
}

.work__body[data-size="small"][b-ae7p9k6g7a] { --cols: 8; --cols-open: 6; }

.work__body[data-size="large"][b-ae7p9k6g7a] { --cols: 4; --cols-open: 3; }

/* Wider than the content cap, so the column counts go up rather than the cards getting huge. Above
   this the content stops growing and one set of numbers holds all the way out. */
@media (min-width: 1501px) {
    .work__body[b-ae7p9k6g7a] { --cols: 8; --cols-open: 6; }
    .work__body[data-size="small"][b-ae7p9k6g7a] { --cols: 10; --cols-open: 8; }
    .work__body[data-size="large"][b-ae7p9k6g7a] { --cols: 5; --cols-open: 4; }
}

/*
    minmax(0, 1fr) rather than a bare 1fr, and it is load-bearing at the small size. A bare 1fr is
    minmax(auto, 1fr), whose floor is the tile's min-content width — so once the share per column
    drops below that (110px against a 113px floor at eight columns), the columns stop shrinking, the
    row overflows, and the pitch no longer matches the one the rail was sized against. Zero as the
    floor lets the fraction actually be a fraction.
*/
.ob-card-grid--counted[b-ae7p9k6g7a] {
    grid-template-columns: repeat(var(--cols), minmax(0, 1fr));
}

/* One lane, whichever panel is in it — so swapping filters for a card costs the grid nothing and
   only opening or closing the lane itself changes the column count. */
.work__body--open .ob-card-grid--counted[b-ae7p9k6g7a] {
    grid-template-columns: repeat(var(--cols-open), minmax(0, 1fr));
}

/*
    min-width: 0 is load-bearing, not defensive. A flex item's automatic minimum size is its
    content, and .ob-table carries min-width: 40rem — without this the table would shove the rail
    off the right-hand edge instead of scrolling inside .ob-table-scroll, which is exactly the view
    the Sell page will open on.
*/
.work__results[b-ae7p9k6g7a] {
    flex: 1;
    min-width: 0;
}

.work__scrim[b-ae7p9k6g7a] {
    position: fixed;
    inset: 0;
    z-index: 20;
    background-color: rgb(0 0 0 / 0.45);
}

/*
    1100px is derived rather than picked: the nav rail is 208px and the layout stacks it at 900px,
    so a filter rail needs roughly those two added together before what is left still reads as a
    grid. Under it the panel floats again and .work__body becomes its positioning context.
*/
@media (max-width: 1100px) {
    .work__body[b-ae7p9k6g7a] {
        display: block;
        position: relative;
    }

    /* The panels float down here, so they take no width from the grid and there are no columns to
       lose. Nothing needs to line up, and a fixed count would be wrong across this whole range, so
       the grid goes back to fitting as many as will fit. */
    .ob-card-grid--counted[b-ae7p9k6g7a],
    .work__body--open .ob-card-grid--counted[b-ae7p9k6g7a] {
        grid-template-columns: repeat(auto-fill, minmax(var(--ob-card-w), 1fr));
    }
}

/* Clicking away closes a floating panel; it must not close a rail, or clicking a card would put
   the filters away every time. */
@media (min-width: 1101px) {
    .work__scrim[b-ae7p9k6g7a] { display: none; }
}

/*
    Sticky under the top bar, like the rail, so search, sort and the view toggle stay reachable
    without scrolling back up a long grid. It already carries an opaque background, so the cards
    pass behind it rather than through it.

    z-index because it comes before .work__body in the document: without it the rail, which is also
    positioned, would paint over the top of it. It stays below the scrim (20) and the floating panel
    (30), so the narrow layout still covers it as it should.
*/
/*
    How tall the sticky bar is, which the rail reads to know where to park beneath it.

    Built from the parts rather than measured, which is only possible because the pill row cannot
    wrap: one row of controls, and optionally one row of pills with a gap above it. Two heights, and
    the component says which applies.
*/
.work[b-ae7p9k6g7a] {
    --ob-filters-h: 4rem;
}

.work--pills[b-ae7p9k6g7a] {
    --ob-filters-h: calc(4rem + var(--ob-space-2) + 1.5rem);
}

@media (min-width: 641px) {
    .filters[b-ae7p9k6g7a] {
        position: sticky;
        top: 56px;
        z-index: 10;
    }
}

/*
    Takes the filter controls' place while a selection is live, so it inherits the bar's height and
    the rail's offset arithmetic keeps holding — the reason it replaces rather than stacks.
*/
.bulk[b-ae7p9k6g7a] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--ob-space-2);
    width: 100%;
    min-height: 2.25rem;
}

.bulk__link[b-ae7p9k6g7a] {
    padding: 0 var(--ob-space-1);
    border: none;
    background: none;
    color: var(--ob-text-faint);
    font: inherit;
    font-size: var(--ob-text-xs);
    text-decoration: underline;
    cursor: pointer;
}

.bulk__link:hover[b-ae7p9k6g7a] { color: var(--ob-text); }

/* Divides "what is selected" from "what to do with it" without a heading for either. */
.bulk__sep[b-ae7p9k6g7a] {
    width: 1px;
    height: 1.25rem;
    background-color: var(--ob-border);
}

.bulk__field[b-ae7p9k6g7a] {
    display: flex;
    align-items: center;
    gap: var(--ob-space-2);
}

.bulk__label[b-ae7p9k6g7a] {
    color: var(--ob-text-faint);
    font-size: var(--ob-text-xs);
    white-space: nowrap;
}

.bulk__field select[b-ae7p9k6g7a] { min-width: 8rem; }

/* Pushed to the right, away from the two reversible controls, so Remove is not the thing your
   hand is already near. */
.bulk__confirm[b-ae7p9k6g7a] {
    margin-left: auto;
    color: var(--ob-text);
    font-size: var(--ob-text-sm);
}

.bulk__confirm + .ob-button[b-ae7p9k6g7a] { margin-left: 0; }

.bulk .ob-button--danger:not(.bulk__confirm + .ob-button)[b-ae7p9k6g7a] { margin-left: auto; }

/* Top-aligned, not centred: the search grows downward as pills are added, and centring would drag
   the count and the view toggle down the row with it every time a filter is applied. */
.filters[b-ae7p9k6g7a] {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: var(--ob-space-2);
    padding: var(--ob-space-3);
    margin-bottom: var(--ob-space-5);
    background-color: var(--ob-surface);
    border: 1px solid var(--ob-border);
    border-radius: var(--ob-radius-lg);
}

/* All hold themselves against the height of the input row, so they line up with it rather than
   with the top of a search box that may be two rows tall. */
.filters__view[b-ae7p9k6g7a],
.filters__sort[b-ae7p9k6g7a],
.filters__toggle[b-ae7p9k6g7a] {
    min-height: 2.25rem;
}

/* Pushed to the end of the row, and kept together when the row wraps — split across two lines they
   would read as two unrelated controls rather than one pair. */
.filters__views[b-ae7p9k6g7a] {
    display: flex;
    align-items: center;
    gap: var(--ob-space-2);
    margin-left: auto;
}

/* Moved here from FilterPanel along with the button itself, unchanged. */
.filters__toggle[b-ae7p9k6g7a] {
    display: inline-flex;
    align-items: center;
    gap: var(--ob-space-2);
    padding: var(--ob-space-1) var(--ob-space-3);
    background-color: var(--ob-bg);
    border: 1px solid var(--ob-border);
    border-radius: var(--ob-radius);
    color: var(--ob-text-muted);
    font: inherit;
    font-size: var(--ob-text-sm);
    cursor: pointer;
    transition: border-color 120ms ease, color 120ms ease;
}

.filters__toggle:hover[b-ae7p9k6g7a] { color: var(--ob-text); }

.filters__toggle--on[b-ae7p9k6g7a] {
    border-color: var(--ob-accent);
    color: var(--ob-text);
}

/* The count is the only thing telling you a closed panel is still filtering, so it carries the
   accent rather than sitting quietly in muted grey. */
.filters__badge[b-ae7p9k6g7a] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.25rem;
    height: 1.25rem;
    padding: 0 4px;
    background-color: var(--ob-accent);
    border-radius: 999px;
    color: #fff;
    font-size: var(--ob-text-xs);
    font-variant-numeric: tabular-nums;
}

.filters__sort[b-ae7p9k6g7a] {
    display: flex;
    align-items: center;
    gap: var(--ob-space-2);
}

.filters__sort-name[b-ae7p9k6g7a] {
    display: inline-flex;
    align-items: center;
    gap: var(--ob-space-1);
    color: var(--ob-text-faint);
    font-size: var(--ob-text-xs);
}

/* ::deep because Icon renders its own root element, which carries its own scope rather than this
   component's — a plain .ob-icon selector in here would never match. The icons in the view toggle
   and the pill inherit their size from the button's font-size and need no rule at all; these two
   sit against smaller text than the em scale alone flatters. */
.filters__sort-name[b-ae7p9k6g7a]  .ob-icon {
    width: 1em;
    height: 1em;
}

.filters__sort select[b-ae7p9k6g7a] {
    width: auto;
    min-width: 7rem;
}

/*
    The words beside the Sort and Size icons, dropped once the bar gets tight. Space bought rather
    than saved: the grouping control has to fit on the same row, because the rail's sticky offset is
    computed from the bar being one of two known heights and a wrapped bar misparks it silently
    rather than looking broken. The icons carry the meaning on their own, and both controls show
    their current value in the select beside them regardless.
*/
/*
    Everything the bar can give up before it wraps, and it has to give up enough: from 1101px the
    filter panel is a rail whose sticky offset is computed from the bar being one of two known
    heights, so a wrapped bar misparks it silently rather than looking broken. Below 1101 the panel
    floats and nothing depends on the height, which is why this only has to hold down to there.

    In order of what is lost: the words beside the Sort and Size icons (the select beside each still
    shows its value), then the selects' own slack, then the search box's floor. Nothing is hidden
    that is the only statement of something.
*/
@media (max-width: 1400px) {
    .filters__word[b-ae7p9k6g7a] { display: none; }

    .filters__sort select[b-ae7p9k6g7a] { min-width: 5.5rem; }

    /* Column gap only. The row gap is what sits between the controls and the pill row, and
       --ob-filters-h is built from it — narrowing that would move the rail without anything saying
       so. The last thing needed to survive the widest case: an active filter puts a count badge on
       the Filters button, and those 28px are what tips 1101px over. */
    .filters[b-ae7p9k6g7a] { column-gap: var(--ob-space-1); }
}

/*
    The row between the filter bar and the results: what is shown on the left, how it is ordered on
    the right, both aligned to the edges of the grid underneath so they read as its header rather
    than as more filter controls.
*/
.results-head[b-ae7p9k6g7a] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--ob-space-2) var(--ob-space-3);
    margin-bottom: var(--ob-space-3);
    min-height: 2.25rem;
}

.results-head__counts[b-ae7p9k6g7a] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--ob-space-3);
}

/* Right edge of the grid. Auto rather than space-between so the sort stays put when the selected
   count appears and disappears beside the shown count. */
.results-head .filters__sort[b-ae7p9k6g7a] {
    margin-left: auto;
}

.results-head__all[b-ae7p9k6g7a] {
    display: inline-flex;
    align-items: center;
    gap: var(--ob-space-2);
    color: var(--ob-text-muted);
    font-size: var(--ob-text-xs);
    cursor: pointer;
}

.results-head__all:hover[b-ae7p9k6g7a] { color: var(--ob-text); }

.results-head__all input[b-ae7p9k6g7a] {
    margin: 0;
    cursor: pointer;
}

.results-head__count[b-ae7p9k6g7a] {
    color: var(--ob-text-faint);
    font-size: var(--ob-text-xs);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* Divided by a middot rather than parentheses: the two numbers count different things and neither
   is a footnote to the other. */
.results-head__held[b-ae7p9k6g7a]::before {
    content: "·";
    margin: 0 var(--ob-space-2);
}

/* Accent-coloured because it is the only thing here that is true only sometimes — a selection is a
   state you are in, and it has to be findable without reading the row. */
.results-head__selected[b-ae7p9k6g7a] {
    color: var(--ob-accent-text);
    font-size: var(--ob-text-xs);
    font-variant-numeric: tabular-nums;
    font-weight: 500;
    white-space: nowrap;
}

/* Its own full-width row below the controls, wrapping freely. The point of keeping the pills out
   of the search box is that however many there are, and however wide they get, the input above
   never changes size. */
/*
    One line, scrolling sideways rather than wrapping, and that is a layout constraint rather than a
    stylistic preference.

    The rail parks below this bar, so it needs the bar's height — and CSS cannot read one element's
    height into another element's sticky offset. While these pills wrapped, the bar's height depended
    on how many rows they took, which depends on their text and the viewport, so nothing could know
    it ahead of time and the offset had to be guessed. Guessed high it left a gap; guessed low it
    slid the rail under the bar.

    Held to one line, the bar has exactly two heights — with pills and without — and the component
    already knows which. The offset becomes arithmetic instead of an estimate.
*/
.filters__active[b-ae7p9k6g7a] {
    display: flex;
    flex-wrap: nowrap;
    align-items: center;
    gap: var(--ob-space-2);
    width: 100%;
    min-width: 0;
    height: 1.5rem;
}

/* The scrolling part. Clear all is its sibling rather than its last child, so it stays put however
   many pills there are. */
.filters__pills[b-ae7p9k6g7a] {
    display: flex;
    flex: 1;
    align-items: center;
    gap: var(--ob-space-1);
    min-width: 0;
    height: 100%;
    overflow-x: auto;
    scrollbar-width: none;
}

.filters__pills[b-ae7p9k6g7a]::-webkit-scrollbar { display: none; }

/* flex: none so a long pill scrolls out of the row rather than being squeezed thinner than its
   text — the row is a scroller now, not a shrinker. */
.filters__pill[b-ae7p9k6g7a] {
    display: inline-flex;
    flex: none;
    align-items: center;
    gap: var(--ob-space-1);
    padding: 2px var(--ob-space-1) 2px var(--ob-space-2);
    background-color: color-mix(in srgb, var(--ob-accent) 22%, transparent);
    border: 1px solid color-mix(in srgb, var(--ob-accent) 45%, transparent);
    border-radius: var(--ob-radius-sm);
    color: var(--ob-text);
    font-size: var(--ob-text-xs);
    white-space: nowrap;
}

.filters__pill-x[b-ae7p9k6g7a] {
    display: inline-flex;
    align-items: center;
    padding: 0 2px;
    border: none;
    background: none;
    color: inherit;
    font: inherit;
    font-size: var(--ob-text-sm);
    line-height: 1;
    opacity: 0.65;
    cursor: pointer;
}

.filters__pill-x:hover[b-ae7p9k6g7a] { opacity: 1; }

/* Sits after the pills rather than pinned opposite them: it is the last item in the list of things
   you can remove, not a separate control. */
.filters__clear[b-ae7p9k6g7a] {
    flex: none;
    padding: 0 var(--ob-space-1);
    border: none;
    background: none;
    color: var(--ob-text-faint);
    font: inherit;
    font-size: var(--ob-text-xs);
    text-decoration: underline;
    cursor: pointer;
}

.filters__clear:hover[b-ae7p9k6g7a] { color: var(--ob-text); }

@media (max-width: 640px) {
    /* The toggles keep the end of their own row instead of taking a full-width one — they are two
       small squares, and stretching them would make the narrowest screen the loudest. */
    .filters__views[b-ae7p9k6g7a] {
        order: 1;
    }

    .filters__toggle[b-ae7p9k6g7a] {
        flex: 1 1 100%;
        justify-content: center;
    }
}
/* _content/ObsidianBinder.Web/Components/Shared/CollectionStats.razor.rz.scp.css */
.stats[b-x33kmwvd0r] {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--ob-space-4);
    margin-bottom: var(--ob-space-5);
}

@media (max-width: 900px) {
    .stats[b-x33kmwvd0r] {
        grid-template-columns: 1fr;
    }
}

.stat[b-x33kmwvd0r] {
    display: flex;
    flex-direction: column;
    gap: var(--ob-space-3);
    padding: var(--ob-space-4) var(--ob-space-5);
}

.stat__head[b-x33kmwvd0r] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--ob-space-2);
}

.stat__label[b-x33kmwvd0r] {
    color: var(--ob-text-muted);
    font-size: var(--ob-text-sm);
    font-weight: 500;
}

.stat__body[b-x33kmwvd0r] {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--ob-space-4);
}

.stat__value[b-x33kmwvd0r] {
    display: block;
    font-size: var(--ob-text-xl);
    font-weight: 600;
    line-height: 1.2;
}

/* The movement is green, the window it was measured over is not — one is a judgement, the other
   a caption. */
.stat__delta[b-x33kmwvd0r] {
    display: block;
    margin-top: var(--ob-space-1);
    color: var(--ob-positive);
    font-size: var(--ob-text-xs);
}

.stat__delta-period[b-x33kmwvd0r],
.stat__sub[b-x33kmwvd0r] {
    color: var(--ob-text-faint);
    font-size: var(--ob-text-xs);
}

.stat__sub[b-x33kmwvd0r] {
    display: block;
    margin-top: var(--ob-space-1);
}

.stat__spark[b-x33kmwvd0r],
.stat__icon[b-x33kmwvd0r],
.stat__ring[b-x33kmwvd0r] {
    flex-shrink: 0;
}
/* _content/ObsidianBinder.Web/Components/Shared/EntryHistory.razor.rz.scp.css */
.history[b-955iskdkn6] {
    display: flex;
    flex-direction: column;
    gap: var(--ob-space-1);
}

.history__toggle[b-955iskdkn6] {
    display: inline-flex;
    align-items: center;
    gap: var(--ob-space-1);
    align-self: flex-start;
    padding: 0;
    border: none;
    background: none;
    color: var(--ob-text-faint);
    font: inherit;
    font-size: var(--ob-text-xs);
    cursor: pointer;
}

.history__toggle:hover[b-955iskdkn6] { color: var(--ob-text); }

.history__toggle[b-955iskdkn6]  svg {
    transition: transform 120ms ease;
}

.history__toggle[aria-expanded="true"][b-955iskdkn6]  svg {
    transform: rotate(180deg);
}

.history__empty[b-955iskdkn6] {
    color: var(--ob-text-faint);
    font-size: var(--ob-text-xs);
}

.history__list[b-955iskdkn6] {
    display: flex;
    flex-direction: column;
    gap: var(--ob-space-1);
    margin: 0;
    padding: 0 0 0 var(--ob-space-2);
    list-style: none;
    border-left: 1px solid var(--ob-border);
}

.history__item[b-955iskdkn6] {
    display: flex;
    flex-direction: column;
}

.history__when[b-955iskdkn6] {
    color: var(--ob-text-faint);
    font-size: var(--ob-text-xs);
    font-variant-numeric: tabular-nums;
}

.history__what[b-955iskdkn6] {
    color: var(--ob-text-muted);
    font-size: var(--ob-text-xs);
}
/* _content/ObsidianBinder.Web/Components/Shared/FacetChecklist.razor.rz.scp.css */
.fc[b-99shm307k2] {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.fc__head[b-99shm307k2] {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--ob-space-2);
    margin-bottom: var(--ob-space-1);
}

.fc__name[b-99shm307k2] {
    color: var(--ob-text-muted);
    font-size: var(--ob-text-xs);
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.fc__selected[b-99shm307k2] {
    color: var(--ob-accent);
    font-size: var(--ob-text-xs);
}

/* The whole row is the target, not just the box — these are small and often reached for on a
   phone, where a 13px checkbox is a miss waiting to happen. */
.fc__option[b-99shm307k2] {
    display: flex;
    align-items: center;
    gap: var(--ob-space-2);
    padding: 3px var(--ob-space-1);
    border-radius: var(--ob-radius-sm);
    font-size: var(--ob-text-sm);
    cursor: pointer;
}

.fc__option:hover[b-99shm307k2] { background-color: var(--ob-surface); }

.fc__box[b-99shm307k2] {
    cursor: pointer;
}

/* Truncates rather than wraps: a long set name should not push its count onto a second line and
   make the column of numbers ragged. */
.fc__value[b-99shm307k2] {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--ob-text);
}

/* Tabular so the counts form a readable column, which is what makes the ordering legible. */
.fc__count[b-99shm307k2] {
    color: var(--ob-text-faint);
    font-size: var(--ob-text-xs);
    font-variant-numeric: tabular-nums;
}

.fc__more[b-99shm307k2] {
    align-self: flex-start;
    padding: 2px var(--ob-space-1);
    border: none;
    background: none;
    color: var(--ob-text-muted);
    font: inherit;
    font-size: var(--ob-text-xs);
    text-decoration: underline;
    cursor: pointer;
}

.fc__more:hover[b-99shm307k2] { color: var(--ob-text); }
/* _content/ObsidianBinder.Web/Components/Shared/FilterBar.razor.rz.scp.css */
/*
    Grows into whatever the other controls leave, but asks for little up front. The basis is what
    decides when the bar wraps — at 22rem the row ran out of room once the card size control joined
    it and pushed the view toggle onto a line of its own. Asking for 15rem and growing gets the same
    width in practice while keeping everything on one line a good deal longer.
*/
.fb[b-wnzk44lgvy] {
    flex: 1 1 15rem;
    min-width: 12rem;
}

/* The search box's floor, lowered for the range where the bar is most crowded and must not wrap
   (see the note in CollectionList's stylesheet). It is the one control that can shrink and still
   work — a narrow box you type into is usable in a way that half a segmented control is not. */
@media (max-width: 1400px) {
    .fb[b-wnzk44lgvy] {
        flex-basis: 7rem;
        min-width: 6rem;
    }
}

/* The field and its menu are one positioned unit, so the dropdown anchors to the box rather than
   to the pill row that may or may not be below it. */
.fb__field[b-wnzk44lgvy] {
    position: relative;
}

.fb__box[b-wnzk44lgvy] {
    display: flex;
    align-items: center;
    padding: var(--ob-space-1) var(--ob-space-2);
    min-height: 2.25rem;
    background-color: var(--ob-bg);
    border: 1px solid var(--ob-border);
    border-radius: var(--ob-radius);
    cursor: text;
    transition: border-color 120ms ease;
}

.fb__box:focus-within[b-wnzk44lgvy] {
    border-color: var(--ob-accent);
}

/* Rounded off from the menu below when it's open, so the two read as one surface. */
.fb__field--open .fb__box[b-wnzk44lgvy] {
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
}

/* ::deep because Icon renders its own root, which carries its own scope rather than this
   component's. Brightens with the box it sits in, so the glyph doesn't stay dim while the border
   goes accent on focus and leave the box looking half-awake. */
.fb__box[b-wnzk44lgvy]  .fb__icon {
    margin-right: var(--ob-space-2);
    transition: color 120ms ease;
}

.fb__box:focus-within[b-wnzk44lgvy]  .fb__icon {
    color: var(--ob-text-muted);
}

/* Full width and fixed, never sharing the row with anything. */
.fb__input[b-wnzk44lgvy] {
    flex: 1 1 auto;
    width: 100%;
    min-width: 0;
    padding: var(--ob-space-1) 0;
    border: none;
    background: none;
    color: var(--ob-text);
    font: inherit;
    font-size: var(--ob-text-sm);
    outline: none;
}

.fb__input[b-wnzk44lgvy]::placeholder { color: var(--ob-text-faint); }

.fb__menu[b-wnzk44lgvy] {
    position: absolute;
    z-index: 20;
    top: 100%;
    left: 0;
    right: 0;
    margin: 0;
    padding: var(--ob-space-1);
    list-style: none;
    background-color: var(--ob-surface-raised);
    border: 1px solid var(--ob-accent);
    border-top: none;
    border-radius: 0 0 var(--ob-radius) var(--ob-radius);
    box-shadow: var(--ob-shadow, 0 8px 24px rgb(0 0 0 / 0.4));
    max-height: 16rem;
    overflow-y: auto;
}

.fb__option[b-wnzk44lgvy] {
    display: flex;
    align-items: baseline;
    gap: var(--ob-space-2);
    padding: var(--ob-space-1) var(--ob-space-2);
    border-radius: var(--ob-radius-sm);
    font-size: var(--ob-text-sm);
    cursor: pointer;
}

.fb__option--on[b-wnzk44lgvy] { background-color: var(--ob-surface); }

/* The prefix is shown on every row rather than only where it's ambiguous: seeing "set" beside
   Base Set four times is how the syntax gets learned without a help link. */
.fb__option-kind[b-wnzk44lgvy] {
    min-width: 4.5rem;
    color: var(--ob-text-faint);
    font-size: var(--ob-text-xs);
    font-variant: small-caps;
}

.fb__option-value[b-wnzk44lgvy] { color: var(--ob-text); }

@media (max-width: 640px) {
    .fb[b-wnzk44lgvy] {
        flex: 1 1 100%;
    }
}
/* _content/ObsidianBinder.Web/Components/Shared/FilterPanel.razor.rz.scp.css */
/*
    A column beside the results, sticky under the 56px top bar so it stays reachable while the grid
    scrolls past it.

    max-height and overflow-y are the fix for a real bug rather than polish: the facet checklists
    grow with the number of distinct sets and locations, so the panel had no bound and ran off the
    bottom of the screen, taking the Quantity inputs with it. Any *fixed* height would be a height
    some collection eventually outgrows, so it is measured against the viewport instead — and it has
    to survive into the floating fallback below, which is where it was worst.
*/
.fp[b-0qm72ky216] {
    /* An exact number of the grid's column pitches, computed by the host — see .work__body. Any
       other width and opening this would resize and shift every card that stayed on screen. */
    flex: 0 0 var(--fp-lane, 16rem);
    align-self: flex-start;
    /* Below the sticky filter bar rather than the top bar alone, or it would slide underneath it.
       --ob-filters-h is the bar's height, declared by the host — CSS cannot measure it. */
    position: sticky;
    top: calc(56px + var(--ob-filters-h, 4rem) + var(--ob-space-3));
    max-height: calc(100vh - 56px - var(--ob-filters-h, 4rem) - var(--ob-space-5));
    overflow-y: auto;
    /* Reaching the end of the facet list should not start scrolling the results behind it. */
    overscroll-behavior: contain;
    padding: var(--ob-space-3);
    /* surface, not surface-raised: it sits in the page now, a sibling of the filter bar above it,
       rather than something floating over the content. */
    background-color: var(--ob-surface);
    border: 1px solid var(--ob-border);
    border-radius: var(--ob-radius-lg);
}

/* Explicit, because `hidden` is only a default of display:none and every rule above would beat it. */
.fp[hidden][b-0qm72ky216] { display: none; }

/* The toggle button is inches away in this layout, so a second control saying the same thing is
   noise. It comes back when the panel floats and the button may be scrolled away. */
.fp__head[b-0qm72ky216] { display: none; }

.fp__title[b-0qm72ky216] {
    color: var(--ob-text-muted);
    font-size: var(--ob-text-xs);
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.fp__close[b-0qm72ky216] {
    display: inline-flex;
    align-items: center;
    padding: var(--ob-space-1);
    border: none;
    background: none;
    color: var(--ob-text-faint);
    font: inherit;
    cursor: pointer;
}

.fp__close:hover[b-0qm72ky216] { color: var(--ob-text); }

.fp__group[b-0qm72ky216] {
    display: flex;
    flex-direction: column;
    gap: var(--ob-space-2);
    padding-bottom: var(--ob-space-3);
    margin-bottom: var(--ob-space-3);
    border-bottom: 1px solid var(--ob-border);
}

.fp__group:last-child[b-0qm72ky216] {
    padding-bottom: 0;
    margin-bottom: 0;
    border-bottom: none;
}

.fp__label[b-0qm72ky216] {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--ob-space-2);
    color: var(--ob-text-muted);
    font-size: var(--ob-text-xs);
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.fp__pair[b-0qm72ky216] {
    display: flex;
    align-items: center;
    gap: var(--ob-space-2);
}

.fp__pair input[b-0qm72ky216] { flex: 1; min-width: 0; }

.fp__dash[b-0qm72ky216] {
    color: var(--ob-text-faint);
    font-size: var(--ob-text-xs);
}

.fp__group--planned[b-0qm72ky216] {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
}

.fp__group--planned .fp__label[b-0qm72ky216] { width: 100%; }

/* Same treatment as the planned areas in the top bar: visible, obviously inert, and carrying the
   reason in a tooltip. */
.fp__planned[b-0qm72ky216] {
    padding: 2px var(--ob-space-2);
    border: 1px dashed var(--ob-border);
    border-radius: var(--ob-radius-sm);
    color: var(--ob-text-faint);
    font-size: var(--ob-text-xs);
    cursor: help;
}

/*
    Below the rail breakpoint there is no width to give it, so it goes back to floating over the
    results. That is the fallback rather than the design — but it keeps the max-height and the
    internal scroll from above, which is the half of the old behaviour that was simply broken.

    Positioned against .work__body, which turns position:relative at the same breakpoint.
*/
@media (max-width: 1100px) {
    .fp[b-0qm72ky216] {
        /* Floating, so it owns no column and the lane arithmetic above does not apply. */
        flex: none;
        position: absolute;
        z-index: 30;
        top: 0;
        right: 0;
        width: 17rem;
        background-color: var(--ob-surface-raised);
        border-radius: var(--ob-radius);
        box-shadow: var(--ob-shadow-lifted);
    }

    /* Sticky, so the way out stays put while the facets scroll under it. The negative margins pull
       it out to the panel's edges against the padding on .fp. */
    .fp__head[b-0qm72ky216] {
        display: flex;
        align-items: center;
        justify-content: space-between;
        position: sticky;
        top: 0;
        z-index: 1;
        margin:
            calc(var(--ob-space-3) * -1)
            calc(var(--ob-space-3) * -1)
            var(--ob-space-2);
        padding: var(--ob-space-2) var(--ob-space-3);
        background-color: var(--ob-surface-raised);
        border-bottom: 1px solid var(--ob-border);
    }
}

@media (max-width: 640px) {
    .fp[b-0qm72ky216] {
        left: 0;
        right: 0;
        width: auto;
    }
}
/* _content/ObsidianBinder.Web/Components/Shared/RangeSlider.razor.rz.scp.css */
.rs[b-aoptauelti] {
    display: flex;
    flex-direction: column;
    gap: var(--ob-space-1);
}

.rs__head[b-aoptauelti] {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--ob-space-2);
}

.rs__name[b-aoptauelti] {
    color: var(--ob-text-muted);
    font-size: var(--ob-text-xs);
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.rs__summary[b-aoptauelti] {
    color: var(--ob-text);
    font-size: var(--ob-text-xs);
}

/* Both inputs occupy the same box, so the two thumbs share one track. */
.rs__track[b-aoptauelti] {
    position: relative;
    height: 1.25rem;
}

.rs__rail[b-aoptauelti],
.rs__fill[b-aoptauelti] {
    position: absolute;
    top: 50%;
    height: 3px;
    border-radius: 999px;
    transform: translateY(-50%);
    pointer-events: none;
}

.rs__rail[b-aoptauelti] {
    left: 0;
    right: 0;
    background-color: var(--ob-border);
}

.rs__fill[b-aoptauelti] { background-color: var(--ob-accent); }

/*
    The whole trick. The inputs are stacked and made invisible apart from their thumbs, and the
    input itself ignores the pointer so a click always reaches the track beneath — otherwise the
    input on top would swallow every drag aimed at the other handle. Pointer events are handed
    back to the thumbs alone, which is what makes both grabbable.
*/
.rs__input[b-aoptauelti] {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    background: none;
    appearance: none;
    -webkit-appearance: none;
    pointer-events: none;
}

.rs__input:focus[b-aoptauelti] { outline: none; }

.rs__input[b-aoptauelti]::-webkit-slider-runnable-track {
    height: 100%;
    background: none;
}

.rs__input[b-aoptauelti]::-moz-range-track {
    height: 100%;
    background: none;
}

.rs__input[b-aoptauelti]::-webkit-slider-thumb {
    appearance: none;
    -webkit-appearance: none;
    width: 0.85rem;
    height: 0.85rem;
    border: 2px solid var(--ob-bg);
    border-radius: 50%;
    background-color: var(--ob-accent);
    cursor: grab;
    pointer-events: auto;
}

.rs__input[b-aoptauelti]::-moz-range-thumb {
    width: 0.85rem;
    height: 0.85rem;
    border: 2px solid var(--ob-bg);
    border-radius: 50%;
    background-color: var(--ob-accent);
    cursor: grab;
    pointer-events: auto;
}

/* Keyboard users get no thumb highlight from the browser once appearance is stripped, so the
   focused handle has to say so itself. */
.rs__input:focus-visible[b-aoptauelti]::-webkit-slider-thumb {
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--ob-accent) 45%, transparent);
}

.rs__input:focus-visible[b-aoptauelti]::-moz-range-thumb {
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--ob-accent) 45%, transparent);
}

.rs__ends[b-aoptauelti] {
    display: flex;
    justify-content: space-between;
    color: var(--ob-text-faint);
    font-size: var(--ob-text-xs);
}
