/*
 * HVS brand, applied over Bootstrap.
 *
 * Loaded after bootstrap.min.css, and it overrides Bootstrap's own custom
 * properties rather than restating its rules. That keeps this file to the
 * decisions and leaves the mechanics where they were written.
 *
 * The palette is the design's (D113): a charcoal sidebar, a white bar and
 * white cards on a near-white page, blue for everything that acts, and the
 * brand red kept for the mark and for what destroys.
 */

:root {
    /* The accent: links, the current entry, primary buttons, selection. */
    --hvs-accent: #3183f6;
    --hvs-accent-hover: #2174e9;
    --hvs-accent-soft: #edf5ff;
    --hvs-accent-text: #226ccf;

    /* The brand red, now only the mark and the destructive actions. */
    --hvs-red: #a33226;
    --hvs-red-hover: #86191c;
    --hvs-red-soft: #fff0ee;

    /* The sidebar, the same in both modes. */
    --hvs-sidebar: #202529;
    --hvs-sidebar-rule: #373e43;
    --hvs-sidebar-hover: #383a43;
    --hvs-sidebar-text: #f5f5f4;

    /* The page and its surfaces. */
    --hvs-page: #f9f9f9;
    --hvs-surface: #ffffff;
    --hvs-line: #e8e8e8;
    --hvs-control-line: #dcdad8;
    --hvs-ink: #0c0a09;
    --hvs-muted: #6b6561;
    --hvs-shadow: 0 1px 2.5px rgba(0, 0, 0, 0.05);

    /* Derived, not given. Dark backgrounds need a lighter tint of the same
     * hue, and a button needs a different one from a link: a button must be
     * visible as a shape against the page, and a link readable as text on
     * it. The method is D32's; the hue is now the accent's. */
    --hvs-accent-dark-mode: #6aa5fa;
    --hvs-accent-dark-mode-hover: #8cbcfb;
    --hvs-link-dark-mode: #8cbcfb;
    --hvs-red-dark-mode: #e8555a;
    --hvs-red-dark-mode-hover: #f08a8d;

    /* No web font. A font from a third party reports every reader's address to
     * it, which is the reasoning that vendored Bootstrap. See D33. */

    /* Bootstrap's own properties, pointed at the palette. */
    --bs-body-bg: var(--hvs-page);
    --bs-body-color: var(--hvs-ink);
    --bs-secondary-color: var(--hvs-muted);
    --bs-border-color: var(--hvs-line);
    --bs-border-radius: 0.5rem;
    --bs-border-radius-sm: 0.375rem;
    --bs-link-color-rgb: 49, 131, 246;
    --bs-link-hover-color-rgb: 33, 116, 233;
}

[data-bs-theme="dark"] {
    --hvs-page: #15191d;
    --hvs-surface: #1f2428;
    --hvs-line: #2f353a;
    --hvs-control-line: #3a4147;
    --hvs-ink: #e9e7e5;
    --hvs-muted: #a9a39e;
    --hvs-accent-soft: #1a2a40;
    --hvs-accent-text: #8cbcfb;
    --hvs-red-soft: #3a1f1d;
    --hvs-shadow: 0 1px 2.5px rgba(0, 0, 0, 0.4);
    --bs-body-bg: var(--hvs-page);
    --bs-body-color: var(--hvs-ink);
    --bs-secondary-color: var(--hvs-muted);
    --bs-border-color: var(--hvs-line);
    --bs-tertiary-bg: var(--hvs-surface);
    --bs-link-color-rgb: 140, 188, 251;
    --bs-link-hover-color-rgb: 106, 165, 250;
}

/*
 * Overriding --bs-primary alone does nothing to buttons.
 *
 * Bootstrap compiles .btn-primary from a Sass variable, so its colours are
 * literals in the stylesheet rather than references to --bs-primary. What each
 * .btn-* class does expose is its own set of custom properties, and those are
 * what this overrides. Discovering that the hard way costs an hour.
 */
.btn-primary {
    --bs-btn-bg: var(--hvs-accent);
    --bs-btn-border-color: var(--hvs-accent);
    --bs-btn-hover-bg: var(--hvs-accent-hover);
    --bs-btn-hover-border-color: var(--hvs-accent-hover);
    --bs-btn-active-bg: var(--hvs-accent-hover);
    --bs-btn-active-border-color: var(--hvs-accent-hover);
    --bs-btn-disabled-bg: var(--hvs-accent);
    --bs-btn-disabled-border-color: var(--hvs-accent);
}

/*
 * The outlined primary button, which is the View button on every photograph.
 *
 * .btn-primary above is a separate class with separate literals, so overriding
 * one does nothing for the other, and this one stayed Bootstrap's blue on a
 * page where everything else primary is red.
 */
.btn-outline-primary {
    --bs-btn-color: var(--hvs-accent);
    --bs-btn-border-color: var(--hvs-accent);
    --bs-btn-hover-bg: var(--hvs-accent);
    --bs-btn-hover-border-color: var(--hvs-accent);
    --bs-btn-active-bg: var(--hvs-accent-hover);
    --bs-btn-active-border-color: var(--hvs-accent-hover);
    --bs-btn-disabled-color: var(--hvs-accent);
    --bs-btn-disabled-border-color: var(--hvs-accent);
}

[data-bs-theme="dark"] .btn-outline-primary {
    --bs-btn-color: var(--hvs-accent-dark-mode);
    --bs-btn-border-color: var(--hvs-accent-dark-mode);
    --bs-btn-hover-bg: var(--hvs-accent-dark-mode);
    --bs-btn-hover-border-color: var(--hvs-accent-dark-mode);
    --bs-btn-active-bg: var(--hvs-accent-dark-mode-hover);
    --bs-btn-active-border-color: var(--hvs-accent-dark-mode-hover);
    /* Dark text once filled, for the contrast reason given for .btn-primary. */
    --bs-btn-hover-color: #212529;
    --bs-btn-active-color: #212529;
}

[data-bs-theme="dark"] .btn-primary {
    --bs-btn-bg: var(--hvs-accent-dark-mode);
    --bs-btn-border-color: var(--hvs-accent-dark-mode);
    --bs-btn-hover-bg: var(--hvs-accent-dark-mode-hover);
    --bs-btn-hover-border-color: var(--hvs-accent-dark-mode-hover);
    /* Dark text, because white on #e8555a is 3.57:1 and near-black is 5.88:1.
   * It looks wrong for about a second and it is the readable option. */
    --bs-btn-color: #212529;
    --bs-btn-hover-color: #212529;
    --bs-btn-active-color: #212529;
}

/*
 * The sidebar: charcoal in both modes, the mark at the top, a rule, then the
 * entries with their icons. See D113.
 *
 * The element is Bootstrap's offcanvas-md: a drawer below md, an ordinary box
 * from md up. The width and the sticky height apply from md up, where
 * Bootstrap has stopped styling it; the colour applies in both shapes, so the
 * drawer on a phone is the same sidebar slid in.
 *
 * A fixed width in rem, so it scales with the reader's font size and the
 * longest entry, "Manage Account", does not wrap.
 *
 * The background needs !important because Bootstrap's own rule has it:
 * offcanvas-md is made transparent from md up, on the assumption that it is
 * sitting inside a navbar that has a colour already. Here it is not.
 */
.app-sidebar {
    background-color: var(--hvs-sidebar) !important;
    color: var(--hvs-sidebar-text);
    --bs-offcanvas-bg: var(--hvs-sidebar);
    --bs-offcanvas-color: var(--hvs-sidebar-text);
    --bs-border-color: var(--hvs-sidebar-rule);
}

.app-sidebar .btn-close {
    filter: invert(1) grayscale(100%) brightness(200%);
}

@media (min-width: 768px) {
    .app-sidebar {
        width: 13.5rem;
        flex-shrink: 0;
        position: sticky;
        top: 0;
        height: 100vh;
        overflow-y: auto;
    }
}

.app-nav-rule {
    margin: 0 0 1rem;
    border-top: 1px solid var(--hvs-sidebar-rule);
    opacity: 1;
}

.app-sidebar .nav .app-nav-rule {
    margin: 1.25rem 0 0.75rem;
}

.app-brand img {
    display: block;
    width: 8.5rem;
    height: auto;
}

/*
 * The top bar stays put while the page scrolls, like the sidebar from md up,
 * so the account menu and, on a phone, the menu button are never a scroll
 * away. Bootstrap's own sticky level, above a table's row menus, which are
 * positioned fixed at the dropdown level below it; below the dialogs, which
 * are in the browser's top layer whatever this says. An opaque background
 * is what keeps the rows from showing through. See D63.
 */
.app-topbar {
    position: sticky;
    top: 0;
    z-index: 1020;
    background-color: var(--hvs-surface);
    min-height: 4rem;
    box-shadow: var(--hvs-shadow);
}

.app-page-title {
    font-weight: 600;
    font-size: 1rem;
    letter-spacing: -0.02em;
}

/* The account button is the client's name and a chevron, with no box. */
.app-account {
    --bs-btn-color: var(--bs-body-color);
    --bs-btn-hover-color: var(--hvs-accent);
    --bs-btn-active-color: var(--hvs-accent);
    --bs-btn-hover-bg: transparent;
    --bs-btn-active-bg: transparent;
    --bs-btn-border-color: transparent;
    --bs-btn-hover-border-color: transparent;
    --bs-btn-active-border-color: transparent;
    font-weight: 600;
    max-width: 14rem;
}

/*
 * A flex item will not shrink below the width of its content unless told it
 * may. Without this, one wide table makes the whole page scroll sideways on a
 * phone, where table-responsive meant only the table to.
 */
.app-content {
    min-width: 0;
}

/*
 * Left-aligned against the sidebar and capped. Uncapped, a table on a wide
 * monitor puts a filename at one edge of the desk and its Share button at the
 * other.
 */
.app-main {
    max-width: 80rem;
    margin-left: 0;
}

/*
 * Home's cap is wider: a fixed rail and a column that takes the rest
 * (D114), so the 80rem that keeps a table's ends together elsewhere left
 * a margin here. 100rem fills a 1920 screen beside the sidebar and still
 * stops the cards on anything wider.
 */
.app-main-wide {
    max-width: 100rem;
}

/*
 * The entries: pale text on the charcoal, a lighter charcoal under the
 * pointer and behind the current one, as the design draws them. The current
 * entry is a shape rather than a colour, which is what reads on a dark
 * field; aria-current carries the meaning for a screen reader.
 *
 * Bootstrap exposes these as custom properties on .nav and .nav-pills, which
 * is the same mechanism as the buttons above. The same in both modes: the
 * sidebar does not change with the page.
 */
.app-sidebar .nav-pills {
    --bs-nav-link-color: var(--hvs-sidebar-text);
    --bs-nav-link-hover-color: var(--hvs-sidebar-text);
    --bs-nav-pills-link-active-bg: var(--hvs-sidebar-hover);
    --bs-nav-pills-link-active-color: var(--hvs-sidebar-text);
    --bs-nav-link-font-weight: 500;
    --bs-nav-link-font-size: 0.875rem;
    --bs-nav-pills-border-radius: 0.5rem;
}

.app-sidebar .nav-link:hover,
.app-sidebar .nav-link:focus-visible {
    background-color: var(--hvs-sidebar-hover);
}

.app-nav-icon {
    flex: 0 0 auto;
    opacity: 0.92;
}

/*
 * The account button holds a 1.25em icon. Without this the SVG sits on the text
 * baseline rather than in the middle of the button, which reads as a rendering
 * fault.
 */
.app-account svg,
.app-topbar .btn svg {
    vertical-align: -0.2em;
}

/* Wide enough for a filename, and no wider than the bar can spare. */
.app-topbar-search {
    width: 16rem;
    max-width: 40vw;
}

/* Wide enough for an email address, and no wider than a phone. */
.app-topbar .dropdown-menu {
    min-width: 14rem;
    max-width: calc(100vw - 2rem);
}

/*
 * Destructive actions are outlined rather than filled, in the brand red.
 *
 * The accent is blue now, so hue alone would tell them apart; the weight is
 * kept as well, because the action somebody should hesitate over should
 * still be the lighter one on the page, and because the design draws its
 * delete button as red text on a pale red field, which is the same idea.
 */
.btn-outline-danger {
    --bs-btn-color: var(--hvs-red);
    --bs-btn-border-color: var(--hvs-red);
    --bs-btn-hover-bg: var(--hvs-red);
    --bs-btn-hover-border-color: var(--hvs-red);
    --bs-btn-bg: var(--hvs-red-soft);
}

[data-bs-theme="dark"] .btn-outline-danger {
    --bs-btn-color: var(--hvs-red-dark-mode);
    --bs-btn-border-color: var(--hvs-red-dark-mode);
    --bs-btn-hover-bg: var(--hvs-red-dark-mode);
    --bs-btn-hover-color: #212529;
}

/*
 * A focus ring has to be visible in both modes. Bootstrap's default is derived
 * from the primary colour, and a ring in the page's own blue on a dark page
 * is not a ring.
 */
:focus-visible {
    outline: 2px solid var(--hvs-accent);
    outline-offset: 2px;
}

[data-bs-theme="dark"] :focus-visible {
    outline-color: var(--hvs-accent-dark-mode);
}

/*
 * Page links.
 *
 * Bootstrap compiles these from its own primary colour, as it does buttons,
 * which is a different blue. The same custom-property mechanism as the
 * buttons and the sidebar above.
 *
 * Only the links are coloured. The "Page 2 of 3" item in the middle is marked
 * disabled and keeps Bootstrap's muted colour, which is what says it is not one.
 */
.pagination {
    --bs-pagination-color: var(--hvs-accent);
    --bs-pagination-hover-color: var(--hvs-accent-hover);
    --bs-pagination-focus-color: var(--hvs-accent-hover);
    /* The default focus shadow is a blue glow. The outline rule above already
   * draws a ring, in the right colour, so the glow is removed and not
   * recoloured. */
    --bs-pagination-focus-box-shadow: none;
}

[data-bs-theme="dark"] .pagination {
    --bs-pagination-color: var(--hvs-link-dark-mode);
    --bs-pagination-hover-color: var(--hvs-accent-dark-mode);
    --bs-pagination-focus-color: var(--hvs-accent-dark-mode);
}

/*
 * The column of checkboxes in the photograph list.
 *
 * As narrow as its contents. A table shares spare width between its columns,
 * and without this the checkbox gets a share as wide as the filename's.
 *
 * The checkbox takes the brand colour when ticked, for the reason the buttons
 * do: Bootstrap compiles it blue.
 */
.app-select {
    width: 1%;
    white-space: nowrap;
}

.form-check-input:checked,
.form-check-input[type="checkbox"]:indeterminate {
    background-color: var(--hvs-accent);
    border-color: var(--hvs-accent);
}

[data-bs-theme="dark"] .form-check-input:checked,
[data-bs-theme="dark"] .form-check-input[type="checkbox"]:indeterminate {
    background-color: var(--hvs-accent-dark-mode);
    border-color: var(--hvs-accent-dark-mode);
}

/*
 * The type attribute in the indeterminate selectors above is not decoration.
 * Bootstrap writes its own rule that way, which makes it more specific than a
 * class and a pseudo-class, and a rule without it loses and the dash stays
 * blue.
 *
 * Focus: Bootstrap draws a blue border and a blue glow. The :focus-visible rule
 * further up already draws a ring in the brand colour, so the glow is removed
 * and the border recoloured.
 */
.form-check-input:focus {
    border-color: var(--hvs-accent);
    box-shadow: none;
}

[data-bs-theme="dark"] .form-check-input:focus {
    border-color: var(--hvs-accent-dark-mode);
}

/*
 * A button that looks like a link, which is what Clear is. Bootstrap colours it
 * from --bs-link-color, and the brand's link colour is set on the a element
 * rather than on that property, so it did not reach here.
 */
.btn-link {
    --bs-btn-color: var(--hvs-accent);
    --bs-btn-hover-color: var(--hvs-accent-hover);
    --bs-btn-active-color: var(--hvs-accent-hover);
}

[data-bs-theme="dark"] .btn-link {
    --bs-btn-color: var(--hvs-link-dark-mode);
    --bs-btn-hover-color: var(--hvs-accent-dark-mode);
    --bs-btn-active-color: var(--hvs-accent-dark-mode);
}

/*
 * The confirmation dialog.
 *
 * A native dialog element, styled as a card. Bootstrap's own custom properties
 * for the surface, so it follows the colour mode without a rule of its own.
 * The backdrop is the element's own pseudo-element, which needs no markup.
 *
 * max-width in a viewport unit as well as rem, so a phone gets a margin. The
 * browser centres a modal dialog by itself.
 */
.app-dialog {
    border: 1px solid var(--bs-border-color);
    border-radius: var(--bs-border-radius-lg);
    background-color: var(--bs-body-bg);
    color: var(--bs-body-color);
    padding: 1.5rem;
    width: min(32rem, calc(100vw - 2rem));
    box-shadow: var(--bs-box-shadow-lg);
}

.app-dialog::backdrop {
    background-color: rgba(0, 0, 0, 0.5);
}

/*
 * The gallery.
 *
 * A native dialog that fills the viewport, dark whatever the colour mode,
 * because a photograph is looked at against black. The image is centred and
 * fitted; the buttons float over it at the edges. Outlined light buttons, for
 * the one place in the product whose background is always dark.
 */
.app-gallery {
    border: 0;
    padding: 0;
    width: 100vw;
    height: 100vh;
    max-width: none;
    max-height: none;
    background-color: #111;
    color: #eee;
}

.app-gallery::backdrop {
    background-color: rgba(0, 0, 0, 0.9);
}

.app-gallery-figure {
    display: flex;
    flex-direction: column;
    height: 100%;
}

/* The stage is the room the photograph has: it fills what the caption
 * leaves, and clips the photograph when it is zoomed past its edges. The
 * photograph is laid out to fit, and gallery.js moves and scales it with a
 * transform from the top left corner, so the arithmetic in the script is
 * the same for every screen. See D110. */
.app-gallery-stage {
    flex: 1 1 auto;
    min-height: 0;
    position: relative;
    overflow: hidden;
    touch-action: none;
    cursor: zoom-in;
}

.app-gallery-stage.app-gallery-zoomed {
    cursor: grab;
}

.app-gallery-stage.app-gallery-panning {
    cursor: grabbing;
}

.app-gallery-image {
    width: 100%;
    height: 100%;
    object-fit: contain;
    transform-origin: 0 0;
    user-select: none;
    transition: opacity 0.15s ease;
}

.app-gallery-loading {
    opacity: 0.3;
}

.app-gallery-caption {
    padding: 0.75rem 1rem;
    background-color: #1a1a1a;
}

.app-gallery-close {
    position: absolute;
    top: 0.75rem;
    right: 0.75rem;
    z-index: 1;
}

.app-gallery-step {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    z-index: 1;
}

.app-gallery-previous {
    left: 0.75rem;
}

.app-gallery-next {
    right: 0.75rem;
}

/* A disabled step at either end fades rather than vanishing, so the layout
 * holds and the eye learns where the buttons are. */
.app-gallery-step:disabled {
    opacity: 0.25;
}

/* The caption's dots are the one include, drawn light here because the
 * caption is the one place in the product whose background is always dark. */
.app-gallery .app-more {
    --bs-btn-color: #eee;
    --bs-btn-border-color: #eee;
    --bs-btn-hover-bg: #eee;
    --bs-btn-hover-color: #111;
    --bs-btn-hover-border-color: #eee;
}

/* The QR code an authenticator app scans, drawn as SVG by the view. Sized
   here so the SVG's own dimensions do not decide. See D77. */
.app-qr svg {
    width: 12rem;
    height: 12rem;
    background: #fff;
    padding: .5rem;
    border: 1px solid var(--bs-border-color);
    border-radius: var(--bs-border-radius);
}

/*
 * Lists on a phone. See D84.
 *
 * A column marked app-md is secondary: shown from the md breakpoint up, hidden
 * below it, so a phone shows a row's name and its actions and nothing that
 * needs a wider screen to read. The breakpoint is Bootstrap's own, the same
 * one the sidebar folds at, so a page changes shape once rather than twice.
 *
 * A value marked app-truncate is cut with an ellipsis instead of wrapping or
 * pushing the table wider than the screen. The full value is in the title
 * attribute, and in the checkbox's label where there is one, so nothing is
 * lost to the cut. Narrower on a phone, where the name shares the row with
 * the camera and the actions.
 */
@media (max-width: 767.98px) {
    .app-md {
        display: none;
    }
}

.app-truncate {
    display: block;
    max-width: 22rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

@media (max-width: 767.98px) {
    .app-truncate {
        max-width: 11rem;
    }
}

/*
 * The per-row actions menu: View stays a button, everything else is behind
 * one toggle, so a row is two controls wide on any screen. A destructive
 * item inside the menu is a form whose button is a menu item, and ui.js
 * confirms it as it confirms any form.
 */
.app-actions .dropdown-item.text-danger:hover,
.app-actions .dropdown-item.text-danger:focus {
    color: var(--bs-danger) !important;
}

.app-actions form {
    display: contents;
}

/*
 * A visible button that is a form's submit, first in a joined group (Restore
 * on the Bin). Bootstrap squares the inner corners of .btn-group > .btn, and
 * this button is a form's child rather than the group's, so the same is done
 * here. display: contents above already makes it sit in the group's row.
 */
.btn-group.app-actions > form:not(:last-child) > .btn {
    border-top-right-radius: 0;
    border-bottom-right-radius: 0;
}

/*
 * The dots button: as wide as its icon, the icon on the text line, and no
 * caret. It keeps Bootstrap's dropdown-toggle class because that is what
 * keeps the last button of a group rounded when a menu list follows it.
 */
.app-more {
    padding-left: 0.4rem;
    padding-right: 0.4rem;
}

.app-more::after {
    display: none;
}

.app-more svg {
    vertical-align: -0.125em;
}

/*
 * The grid of tiles, the other shape a list of photographs takes. See D87.
 *
 * As many columns as fit at about 160px each, so a phone shows two and a
 * desktop five or six, with no breakpoint to maintain. A tile is the
 * thumbnail in a 4:3 frame, cropped to fill, and a foot with the checkbox,
 * the name and camera, and the dots. A selected tile takes the same class a
 * selected row does, drawn here as a ring in the brand colour.
 */
.app-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
    gap: 0.75rem;
    /* The same space beneath as a table leaves, so the pagination and the
       footer line sit off the last row of tiles as they sit off a table. */
    margin-bottom: 1rem;
}

.app-tile {
    border: 1px solid var(--bs-border-color);
    border-radius: var(--bs-border-radius);
    background: var(--hvs-surface);
    box-shadow: var(--hvs-shadow);
    overflow: hidden;
}

.app-tile.table-active {
    box-shadow: 0 0 0 2px var(--hvs-accent);
}

[data-bs-theme="dark"] .app-tile.table-active {
    box-shadow: 0 0 0 2px var(--hvs-accent-dark-mode);
}

.app-tile-image {
    display: block;
    aspect-ratio: 4 / 3;
    background: var(--bs-tertiary-bg);
}

.app-tile-image img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.app-tile-foot {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem;
}

.app-tile-text {
    flex: 1 1 auto;
    min-width: 0;
}

.app-tile-text .app-truncate {
    max-width: 100%;
}

/*
 * Surfaces, as the design draws them: white, a hairline, soft corners, the
 * faintest shadow. Every table on a signed-in page sits in a table-responsive
 * wrapper, so the wrapper is the card and no template changes for it. The
 * table's own last rule is dropped so the card's edge is the last line. See
 * D113.
 */
.card {
    --bs-card-bg: var(--hvs-surface);
    --bs-card-border-color: var(--hvs-line);
    --bs-card-border-radius: 0.5rem;
    box-shadow: var(--hvs-shadow);
}

.app-main .table-responsive {
    background-color: var(--hvs-surface);
    border: 1px solid var(--hvs-line);
    border-radius: 0.5rem;
    box-shadow: var(--hvs-shadow);
    margin-bottom: 1rem;
}

.app-main .table {
    --bs-table-bg: transparent;
    --bs-table-border-color: var(--hvs-line);
    --bs-table-hover-bg: var(--hvs-accent-soft);
    --bs-table-active-bg: var(--hvs-accent-soft);
    --bs-table-color: var(--bs-body-color);
    --bs-table-hover-color: var(--bs-body-color);
    --bs-table-active-color: var(--bs-body-color);
    margin-bottom: 0;
    font-size: 0.875rem;
}

.app-main .table > :not(caption) > * > * {
    padding: 0.7rem 0.75rem;
}

.app-main .table > :not(caption) > tr:first-child > th,
.app-main .table > thead > tr > th {
    font-weight: 600;
    white-space: nowrap;
}

.app-main .table > thead > tr > th:first-child,
.app-main .table > tbody > tr > td:first-child {
    padding-left: 1.125rem;
}

.app-main .table > tbody > tr:last-child > td {
    border-bottom: 0;
}

/*
 * Controls: the design's corner and hairline, and the accent on focus in place
 * of Bootstrap's blue glow, which was a different blue.
 */
.form-control,
.form-select {
    --bs-border-color: var(--hvs-control-line);
    border-radius: 0.375rem;
    background-color: var(--hvs-surface);
}

.form-control:focus,
.form-select:focus {
    border-color: var(--hvs-accent);
    box-shadow: 0 0 0 0.2rem rgba(49, 131, 246, 0.2);
}

.btn {
    --bs-btn-border-radius: 0.375rem;
    --bs-btn-font-weight: 500;
}

.btn-outline-secondary {
    --bs-btn-color: var(--bs-body-color);
    --bs-btn-border-color: var(--hvs-control-line);
    --bs-btn-bg: var(--hvs-surface);
    --bs-btn-hover-bg: var(--hvs-accent-soft);
    --bs-btn-hover-color: var(--hvs-accent-text);
    --bs-btn-hover-border-color: var(--hvs-control-line);
    --bs-btn-active-bg: var(--hvs-accent-soft);
    --bs-btn-active-color: var(--hvs-accent-text);
    --bs-btn-active-border-color: var(--hvs-accent);
}

.badge.text-bg-secondary,
.badge.bg-secondary {
    background-color: var(--hvs-accent-soft) !important;
    color: var(--hvs-accent-text) !important;
    font-weight: 500;
}

.dropdown-menu {
    --bs-dropdown-bg: var(--hvs-surface);
    --bs-dropdown-border-color: var(--hvs-line);
    --bs-dropdown-border-radius: 0.5rem;
    --bs-dropdown-link-active-bg: var(--hvs-accent-soft);
    --bs-dropdown-link-active-color: var(--hvs-accent-text);
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.12);
}

.alert {
    --bs-alert-border-radius: 0.5rem;
}

h1, .h1, h2, .h2, h3, .h3 {
    letter-spacing: -0.02em;
}

/*
 * Page headings at the design's size: the bar already names the section, so
 * the heading on the page is a title, not a banner.
 */
.app-main h1 {
    font-size: 1.5rem;
    font-weight: 600;
}

/*
 * Home (D114): the design's dashboard. A main column and a rail of
 * figures, side by side from lg up and stacked below. The share cards
 * scroll sideways in their own strip; the recent images table is the card
 * itself, so its wrapper drops the card frame the rule above gives tables.
 */
.app-home {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 1.5rem;
}

@media (min-width: 992px) {
    .app-home {
        grid-template-columns: minmax(0, 1fr) 15rem;
    }
}

.app-home-main {
    display: grid;
    gap: 1.5rem;
    min-width: 0;
    /* Each card as tall as its contents, not stretched to share the
       rail's height: an empty shares card is one sentence tall. */
    align-content: start;
}

.app-home-card > .card-body {
    flex: 0 0 auto;
}

.app-main .app-home-table {
    border: 0;
    border-radius: 0 0 0.5rem 0.5rem;
    box-shadow: none;
    margin-bottom: 0;
}

.app-share-cards {
    display: flex;
    gap: 0.75rem;
    overflow-x: auto;
    padding-bottom: 0.4rem;
    scrollbar-width: thin;
    scrollbar-color: var(--hvs-control-line) transparent;
}

.app-share-card {
    flex: 1 0 12rem;
    min-width: 12rem;
    max-width: 16rem;
    padding: 0.75rem;
    border: 1px solid var(--hvs-line);
    border-radius: 0.45rem;
    background: var(--hvs-page);
    font-size: 0.8125rem;
}

/*
 * The stack: the first thumbnails fanned behind one another, and the count
 * as a link over them. position relative so the link can sit on the
 * pictures; the pictures are decoration, the link is the control.
 */
.app-share-stack {
    position: relative;
    height: 4.5rem;
    margin: 0.5rem 0;
}

.app-share-stack img {
    position: absolute;
    top: 0;
    width: 5.5rem;
    height: 3.75rem;
    object-fit: cover;
    border-radius: 0.3rem;
    border: 1px solid var(--hvs-line);
    background: var(--hvs-line);
}

.app-share-stack img:nth-child(1) { left: 0; z-index: 3; }
.app-share-stack img:nth-child(2) { left: 1.25rem; top: 0.35rem; z-index: 2; opacity: 0.9; }
.app-share-stack img:nth-child(3) { left: 2.5rem; top: 0.7rem; z-index: 1; opacity: 0.8; }

.app-share-stack-link {
    position: absolute;
    right: 0;
    bottom: 0;
    z-index: 4;
    padding: 0.25rem 0.5rem;
    border-radius: 0.3rem;
    background: var(--hvs-surface);
    border: 1px solid var(--hvs-line);
    font-size: 0.75rem;
    font-weight: 500;
    text-decoration: none;
}

/* The rail: a card of four figures, each with its icon, number and line. */
.app-stats {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    padding: 0.75rem;
    align-self: start;
}

@media (min-width: 576px) and (max-width: 991.98px) {
    .app-stats {
        display: grid;
        grid-template-columns: 1fr 1fr;
    }
}

.app-stat {
    padding: 0.75rem;
    border: 1px solid var(--hvs-line);
    border-radius: 0.45rem;
    background: var(--hvs-page);
}

.app-stat-heading {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 0.5rem;
}

.app-stat-heading > div {
    display: flex;
    flex-direction: column;
    line-height: 1.2;
}

.app-stat-heading span {
    font-size: 0.75rem;
    font-weight: 600;
}

.app-stat-heading small {
    font-size: 0.6875rem;
    color: var(--hvs-muted);
}

.app-stat-icon {
    display: grid;
    place-items: center;
    width: 1.75rem;
    height: 1.75rem;
    border-radius: 0.4rem;
    background: var(--hvs-accent-soft);
    color: var(--hvs-accent-text);
}

.app-stat-value {
    display: flex;
    align-items: baseline;
    gap: 0.35rem;
}

.app-stat-value strong {
    font-size: 1.5rem;
    font-weight: 600;
    letter-spacing: -0.02em;
}

.app-stat-value span {
    font-size: 0.75rem;
    color: var(--hvs-muted);
}

.app-stat-foot {
    display: flex;
    align-items: center;
    gap: 0.35rem;
    margin-top: 0.4rem;
    font-size: 0.6875rem;
    color: var(--hvs-muted);
}

.app-stat-foot i {
    width: 0.45rem;
    height: 0.45rem;
    border-radius: 50%;
    background: var(--hvs-muted);
}

.app-stat-healthy i { background: #2e9e5b; }
.app-stat-warn i { background: #d9822b; }

/* Eight bars, the tallest full height, drawn from the counts. */
.app-mini-bars {
    display: flex;
    align-items: flex-end;
    gap: 0.2rem;
    height: 1.75rem;
    margin-top: 0.5rem;
}

.app-mini-bars i {
    flex: 1 1 0;
    min-height: 2px;
    border-radius: 2px 2px 0 0;
    background: var(--hvs-accent);
    opacity: 0.75;
}

.app-mini-bars i:last-child {
    opacity: 1;
}

.app-storage-track {
    height: 0.4rem;
    margin-top: 0.5rem;
    border-radius: 999px;
    background: var(--hvs-line);
    overflow: hidden;
}

.app-storage-track i {
    display: block;
    height: 100%;
    border-radius: 999px;
    background: var(--hvs-accent);
}

.app-storage-caption {
    display: flex;
    justify-content: space-between;
    margin-top: 0.3rem;
    font-size: 0.6875rem;
    color: var(--hvs-muted);
}

.app-activity-list li + li {
    border-top: 1px solid var(--hvs-line);
}
