﻿/* Copyright (c) 2026 eewzoo.com. SPDX-License-Identifier: MIT */
/* ===========================================================================
   site.css  –  a small amount of custom styling on top of Bootstrap.
   Most styling comes from Bootstrap's classes; we only add what Bootstrap
   doesn't give us (the sidebar layout and a couple of tweaks).
   =========================================================================== */

/* ---------------------------------------------------------------------------
   BUTTONS: TWO TONES, AND RED FOR THE ONES YOU CANNOT UNDO.

   There is no CSS below for this — it is a rule about which Bootstrap classes
   a view may use, and it is written here because this is where somebody looks
   when a button looks wrong.

     btn-primary            SOLID BLUE. The primary action, and there is ONE
                            PER ENCLOSURE — one in the page header, and one in
                            each form or card that can be submitted. Two blues
                            inside the same box means one of them is lying.

                            In a header that is CREATE on a list ("New
                            tenant") and EDIT on a record ("Edit tenant").
                            Edit earns it because that is what you came to the
                            record to do; the rest of that header only
                            navigates, and a header of nothing but grey reads
                            as unfinished. A row-level Edit inside a table
                            stays grey — it is one of many, and blueing them
                            all would make a page of blue. Inside a form it is
                            the commit: Save, Post, Approve, Pay, Invite.
     btn-outline-secondary  GREY OUTLINE. Everything else — Open, Edit, Back,
                            Cancel, Filter, Print, View, Add, Reopen.
     btn-outline-danger     RED OUTLINE. Only what cannot be undone: Delete,
                            Void, Revoke, Write off.
     btn-link               A link that happens to sit among buttons ("+ Add a
                            pet"). Not a tone — a shape.

   NOT ALLOWED: btn-success, btn-info, btn-warning, btn-outline-primary and
   friends. They are why this rule exists — "Approve" was green on one page and
   cyan on the next, "Filter" was styled four ways, and btn-outline-primary had
   no meaning at all, covering Filter, Save, Open, View, Print, Add, Refresh
   and Reopen between them.

   SIZE IS CONTEXT, NOT EMPHASIS: btn-sm inside a table row or a card's inline
   form, the default size in a page header or a form footer. Never py-0 — it
   made a third height, so one page carried buttons at 37px, 30px and 22px.

   A SELECTED FILTER IS A STATE, NOT AN ACTION: .active on an outline button
   (with aria-pressed, or aria-current for a link), never the primary blue.

   THE ONE EXCEPTION: a button sitting ON a solid-coloured bar — the navbar, or
   a card-header bg-primary — uses the LIGHT variant instead. The tones above
   are drawn against the page background; on a blue bar the grey outline
   measured 2.46:1. There are two such buttons in the whole app and each says
   so where it sits.

   Write the classes in this order: btn, btn-sm, then the tone.
   --------------------------------------------------------------------------- */

html, body { min-height: 100vh; }

/* ---------------------------------------------------------------------------
   ...AND THE TWO OUTLINE TONES HAVE TO BE TOLD WHICH THEME THEY ARE IN.

   Bootstrap 5.3 re-scopes its TEXT and BACKGROUND utilities for a dark theme
   but not its OUTLINE BUTTONS: .btn-outline-secondary paints #6c757d and
   .btn-outline-danger #dc3545 whatever theme is loaded. Both were chosen
   against a white page. On the dark page they measure 3.29:1 and 3.41:1,
   under the 4.5:1 a reader needs — which is why the grey buttons looked washed
   out and half-there. It is the same defect the badges had, one layer down:
   a fixed colour on a background that moved.

   So they are re-pointed at the THEME'S OWN tokens. Every Bootswatch dark
   theme (Darkly, Cyborg, Slate, Superhero, Solar, Vapor) then supplies its own
   values and none of them is named here — the only way this keeps working when
   a theme is added.

   These are Bootstrap's own button variables, so hover, focus, active and
   disabled keep working; what changes is the colours they resolve to, not the
   mechanism.

   NOT FIXED HERE, because it is the Bootswatch palettes' own choice and not
   ours: btn-primary is 4.5:1 in the default themes but fails in Cyborg (2.99),
   Solar (3.21) and Superhero (3.40), and several light Bootswatch themes are
   worse still (Minty is ~2.1 across the board). Those are brand colours, and
   overriding them would be redesigning twenty themes rather than fixing a bug.
   --------------------------------------------------------------------------- */
/* The grey one is fixed in BOTH themes, not just dark: grey has no identity to
   lose by getting darker, and in light mode the stock #6c757d lands at 4.43:1
   once the button sits on a tinted surface (a card header, a striped row) —
   under the line by a hair. On the theme's own token it is 8.6:1 either way. */
.btn-outline-secondary {
    --bs-btn-color: var(--bs-secondary-color);
    --bs-btn-border-color: var(--bs-secondary-color);
    --bs-btn-hover-color: var(--bs-emphasis-color);
    --bs-btn-hover-bg: var(--bs-secondary-bg);
    --bs-btn-hover-border-color: var(--bs-emphasis-color);
    /* Selected filter chips use .active, so it has to read as chosen rather
       than merely hovered: invert it, the way light mode already does. */
    --bs-btn-active-color: var(--bs-body-bg);
    --bs-btn-active-bg: var(--bs-secondary-color);
    --bs-btn-active-border-color: var(--bs-secondary-color);
    --bs-btn-disabled-color: var(--bs-tertiary-color);
    --bs-btn-disabled-border-color: var(--bs-border-color);
}

/* The red one is fixed in DARK ONLY, and that asymmetry is deliberate. In dark
   it fails outright (3.41:1) and the emphasis token is a lighter red, so it
   both passes and still reads as red. In LIGHT the same token is #58151c — a
   maroon so dark it stops saying "danger", which is the one thing this tone
   exists to say. Bootstrap's stock red there measures 4.53:1 on the page and
   4.28:1 on a tinted surface: a hair under on some rows, and the price of
   keeping it red. #b02a37 (Bootstrap's own red-600) would give 6.5:1 and stay
   red, if the marginal case ever matters more than matching stock. */
[data-bs-theme="dark"] .btn-outline-danger {
    --bs-btn-color: var(--bs-danger-text-emphasis);
    --bs-btn-border-color: var(--bs-danger-text-emphasis);
    --bs-btn-disabled-color: var(--bs-danger-text-emphasis);
    --bs-btn-disabled-border-color: var(--bs-danger-border-subtle);
}

/* The left navigation column. Bootstrap has no built-in sidebar, so we make a
   simple fixed-width one and let the main content fill the rest. */
.sidebar {
    width: 230px;
    min-height: calc(100vh - 56px); /* full height minus the navbar */
    background-color: var(--bs-tertiary-bg);
    flex-shrink: 0;
}

.sidebar .nav-link {
    color: var(--bs-body-color);   /* full-strength text, never muted/grey */
    font-weight: 500;
    border-radius: .375rem;
    margin-bottom: .15rem;
}

/* Hover / focus / current page: a theme-aware primary TINT with emphasized
   primary text. Bootstrap defines --bs-primary-bg-subtle and
   --bs-primary-text-emphasis as a readable PAIR for both light and dark modes,
   so this never becomes white-on-a-pale-colour — which the old rule
   ("primary background + #fff text") did on light themes like Minty/Sandstone. */
.sidebar .nav-link:hover,
.sidebar .nav-link:focus,
.sidebar .nav-link.active {
    background-color: var(--bs-primary-bg-subtle);
    color: var(--bs-primary-text-emphasis);
}

.sidebar .nav-link.active { font-weight: 600; }

.sidebar .nav-link i { width: 1.4rem; display: inline-block; text-align: center; }

/* Collapsible sidebar groups (e.g. the Property section). The child links are
   indented and slightly smaller; a left border shows they belong to the group. */
.sidebar-subnav {
    margin-left: .65rem;
    padding-left: .35rem;
    border-left: 2px solid var(--bs-border-color);
}
.sidebar-subnav .nav-link { font-size: .9rem; }

/* The caret on a group header points down when open and right when collapsed.
   Bootstrap adds the ".collapsed" class to the toggle link while it's closed. */
.sidebar-caret { transition: transform .15s ease; }
.sidebar .nav-link.collapsed .sidebar-caret { transform: rotate(-90deg); }

/* Property-operational sidebar groups are greyed out + inert while the user is in
   the Corporate scope (the company isn't a property — no tenants, leases, etc.).
   Applied conditionally in _Layout.cshtml via the .rm-op-disabled class. */
.rm-op-disabled {
    opacity: .4;
    pointer-events: none;
    cursor: default;
}

/* On small screens the sidebar sits above the content instead of beside it. */
@media (max-width: 768px) {
    .sidebar { width: 100%; min-height: auto; }
}

/* Dashboard stat cards: make the big number stand out. */
.stat-number { font-size: 2rem; font-weight: 600; line-height: 1; }

/* Settings hub: clickable tiles lift slightly on hover; "coming soon"
   tiles get a dashed border so they read as not-yet-available. */
.setting-card { transition: box-shadow .12s ease, transform .12s ease; }
.setting-card:hover { box-shadow: 0 .25rem .75rem rgba(0,0,0,.1); transform: translateY(-2px); }
.border-dashed { border-style: dashed !important; }

/* ---------------------------------------------------------------------------
   Readable muted text on every theme.
   Bootstrap's default "muted"/"secondary" greys are quite low-contrast: pale
   grey on white and dark grey on black are both hard to read. We re-derive them
   from the ACTIVE theme's own text colour (--bs-body-color) at a strong alpha,
   so secondary text stays legible on light AND dark themes (including Bootswatch
   dark themes like Darkly/Cyborg that keep data-bs-theme="light").
   --------------------------------------------------------------------------- */
[data-bs-theme] {
    --bs-secondary-color: rgba(var(--bs-body-color-rgb), 0.82);
    --bs-tertiary-color:  rgba(var(--bs-body-color-rgb), 0.68);
}
.text-muted,
.text-body-secondary,
.text-secondary {
    color: rgba(var(--bs-body-color-rgb), 0.82) !important;
}
small.text-muted, .small.text-muted { opacity: 1; }

/* ---------------------------------------------------------------------------
   App panels (the <panel> accordion — see TagHelpers/PanelTagHelper.cs).
   These panels REPLACED plain cards whose header was "bg-primary text-white",
   and every other card header in the app still looks like that — so the panel
   headers have to as well, or a page stops matching the rest of the app.

   Bootstrap's own accordion is deliberately quiet (a pale tint when open), so
   we re-point its variables at the ACTIVE THEME's primary colour rather than
   hard-coding blue: the app ships 16 Bootswatch themes (see theme.js) and each
   one carries its own --bs-primary. Scoped to .app-panels — opt-in, so a plain
   Bootstrap accordion used elsewhere keeps its quieter default.

   The chevron is a background-image SVG with its colour baked in, so an open
   AND a closed one both need a white-filled copy to show up on the bar.
   --------------------------------------------------------------------------- */
.app-panels {
    --bs-accordion-btn-bg: var(--bs-primary);
    --bs-accordion-btn-color: #fff;
    --bs-accordion-active-bg: var(--bs-primary);
    --bs-accordion-active-color: #fff;
    --bs-accordion-btn-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23ffffff'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");
    --bs-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23ffffff'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");
    --bs-accordion-btn-focus-box-shadow: 0 0 0 .2rem rgba(var(--bs-primary-rgb), .4);
}

/* An open panel keeps the header square-bottomed against its body, exactly as a
   card header sits on a card body. Bootstrap otherwise rounds it while closed. */
.app-panels .accordion-button:not(.collapsed) {
    box-shadow: none;
}

/* A bar that carries ACTIONS is a flex row, because a <button> cannot nest
   inside the accordion's own <button> and a link that did would collapse the
   panel on its way somewhere else. The toggle takes the space; the actions sit
   at the end, on the same coloured bar, independently clickable.

   The item clips to its own corners so the toggle can square its right edge off
   against the actions without leaving a notch where the radius used to be. */
.app-panels .accordion-item { overflow: hidden; }
.app-panels .accordion-header { display: flex; align-items: stretch; }
.app-panels .accordion-header .accordion-button { flex: 1 1 auto; border-radius: 0; }

.app-panels .panel-actions {
    display: flex;
    flex: 0 0 auto;
    align-items: center;
    gap: .5rem;
    padding: 0 1.25rem 0 .75rem;
    background-color: var(--bs-primary);
}

/* The VALUE on the bar — a balance, a total. Bigger than the heading beside it
   because it is the thing you came to read, and the reason to open the panel at
   all; the note after it says what the number MEANS, so nothing here depends on
   colour alone. Both inherit the bar's own colour rather than naming one, which
   is what keeps them legible across all sixteen themes. */
.app-panels .panel-status { font-size: 1.15rem; font-weight: 600; }
.app-panels .panel-status-note { opacity: .85; font-size: .875rem; }

/* Below ~576px the bar has no room for a heading, a number and two buttons, so
   the actions drop onto their own line rather than squeezing the number out. */
@media (max-width: 575.98px) {
    .app-panels .accordion-header { flex-wrap: wrap; }
    .app-panels .panel-actions {
        flex: 1 0 100%;
        justify-content: flex-end;
        padding: 0 1rem .75rem 1rem;
    }
}

/* Dashboard stat cards that lead somewhere. The lift on hover is the only hint
   that the card is clickable, and :focus-within gives keyboard users the same
   hint when the stretched link inside takes focus. */
.stat-link-card { transition: box-shadow .12s ease, transform .12s ease; }
.stat-link-card:hover { box-shadow: 0 .25rem .75rem rgba(0,0,0,.1); transform: translateY(-2px); }
.stat-link-card:focus-within { box-shadow: 0 0 0 .2rem rgba(var(--bs-primary-rgb), .4); }

/* The small grey label above a dashboard stat. Shared by the plain <div> version
   and the <a> version so a card looks identical whether or not it links. */
.stat-heading {
    display: block;
    font-size: .875rem;
    text-transform: uppercase;
    text-decoration: none;
    color: rgba(var(--bs-body-color-rgb), .82);
}
a.stat-heading:hover { text-decoration: underline; }

/* The "+ Add a …" disclosure at the top of a satellite card (see
   TagHelpers/SatelliteCardTagHelper.cs). A <summary> is a button in everything
   but appearance, so it needs to say so with the pointer. */
.satellite-add > summary { cursor: pointer; }

/* An empty validation summary must not render as an empty red box. The
   asp-validation-summary tag helper marks the div .validation-summary-valid
   when there is nothing to report, which is the hook for hiding it — eight
   pages used to do this with an inline style computed in Razor, and every page
   that forgot showed a bare red bar above a perfectly good form. */
.validation-summary-valid { display: none; }

/* Text a person typed keeps the line breaks they typed — notes, addresses, the
   body of a logged call. Thirteen places said this with an inline style, in two
   spellings. */
.pre-wrap { white-space: pre-wrap; }

/* A small round picture of a person, pet or vehicle — the primary photo in its
   media gallery. Size it with --avatar-size on the element. The "-empty" variant
   is the placeholder (an icon on a tinted circle) when there is no photo yet. */
.media-avatar {
    width: var(--avatar-size, 40px); height: var(--avatar-size, 40px);
    border-radius: 50%; object-fit: cover; flex-shrink: 0;
    border: 1px solid var(--bs-border-color);
}
.media-avatar-empty {
    display: inline-flex; align-items: center; justify-content: center;
    background: var(--bs-secondary-bg); color: var(--bs-secondary-color);
    font-size: calc(var(--avatar-size, 40px) * .5);
}

/* _PictureCircle: the picture is the upload button. A small camera badge sits on
   its lower-right edge so the circle reads as clickable before it is hovered. */
.picture-circle { display: inline-block; margin: 0; }
.picture-circle-label { position: relative; display: inline-block; cursor: pointer; line-height: 0; }
.picture-circle-label:hover .media-avatar,
.picture-circle-label:has(+ input:focus-visible) .media-avatar { outline: 2px solid var(--bs-primary); outline-offset: 2px; }
.picture-circle-badge {
    position: absolute; right: -2px; bottom: -2px;
    width: calc(var(--avatar-size, 40px) * .42); height: calc(var(--avatar-size, 40px) * .42);
    min-width: 18px; min-height: 18px;
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: 50%; background: var(--bs-primary); color: #fff;
    border: 2px solid var(--bs-body-bg);
    font-size: max(10px, calc(var(--avatar-size, 40px) * .2));
}

/* The bill editor's Bill / Credit switch (Pages/Accounting/BillEdit). A plain
   Bootstrap switch is grey off and blue on, which says nothing about which side
   you are on. Here: RED while it is a bill (money going out), GREEN once it is a
   credit (money coming back), with the matching word bold in the same colour.
   Pure CSS — :has() reads the checkbox — so it follows the switch with no script,
   and the theme's own danger/success colours keep it right in dark mode. */
.bill-credit-switch .form-check-input {
    background-color: var(--bs-danger);
    border-color: var(--bs-danger);
    /* the knob: white on red (Bootstrap draws it grey when unchecked) */
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e");
}
.bill-credit-switch .form-check-input:checked {
    background-color: var(--bs-success);
    border-color: var(--bs-success);
}
.bill-credit-switch .form-check-input:focus {
    box-shadow: 0 0 0 .25rem rgba(var(--bs-danger-rgb), .25);
}
.bill-credit-switch .form-check-input:checked:focus {
    box-shadow: 0 0 0 .25rem rgba(var(--bs-success-rgb), .25);
}
.bill-credit-switch:has(.form-check-input:not(:checked)) #billTypeBill { color: var(--bs-danger); font-weight: 600; }
.bill-credit-switch:has(.form-check-input:checked) #billTypeCredit { color: var(--bs-success); font-weight: 600; }

/* ---------------------------------------------------------------------------
   .table-light follows the ACTIVE theme.
   Bootstrap hard-codes it to a fixed pale grey with its own border colour, so on
   a Bootswatch or dark theme a table header/footer (e.g. the bill's action bar
   under its lines) sat in a grey band that belonged to no theme. Re-pointed at the
   theme's own tertiary background and border, it now reads as part of the card on
   every theme — one rule for all ~30 pages that use it.
   --------------------------------------------------------------------------- */
.table-light {
    --bs-table-color: var(--bs-body-color);
    --bs-table-bg: var(--bs-tertiary-bg);
    --bs-table-border-color: var(--bs-border-color);
    --bs-table-striped-bg: var(--bs-secondary-bg);
    --bs-table-striped-color: var(--bs-body-color);
    --bs-table-active-bg: var(--bs-secondary-bg);
    --bs-table-active-color: var(--bs-body-color);
    --bs-table-hover-bg: var(--bs-secondary-bg);
    --bs-table-hover-color: var(--bs-body-color);
    color: var(--bs-table-color);
    border-color: var(--bs-table-border-color);
}

/* ---------------------------------------------------------------------------
   A card whose header folds its body (.card-fold-toggle) — e.g. the bill's
   History. The button fills the header and inherits the header's own colours and
   padding, so the card looks exactly like its neighbours on every theme; only a
   chevron says it folds.
   --------------------------------------------------------------------------- */
.card-fold-toggle {
    display: flex;
    align-items: center;
    gap: .5rem;
    width: 100%;
    min-width: 0;
    padding: var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x);
    /* As tall as a header holding .btn-sm buttons (Documents, beside it), so the
       two headers line up. */
    min-height: calc(2 * var(--bs-card-cap-padding-y) + 1.5em * .875 + .5rem + 2px);
    background: transparent;
    border: 0;
    color: inherit;
    font: inherit;
    text-align: start;
}
.card-fold-toggle:focus-visible {
    outline: 0;
    box-shadow: inset 0 0 0 .2rem rgba(var(--bs-primary-rgb), .4);
}
.card-fold-chevron { transition: transform .2s ease; }
.card-fold-toggle:not(.collapsed) .card-fold-chevron { transform: rotate(180deg); }
@media (prefers-reduced-motion: reduce) { .card-fold-chevron { transition: none; } }

/* A grid box widened by grow-on-focus.js floats over its neighbours: a shadow
   says it is on top, and the page background behind it keeps the cells it
   covers from showing through a transparent theme. */
.grown-on-focus { box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .18); background-color: var(--bs-body-bg); border-radius: var(--bs-border-radius-sm); }

/* A CLOSED (no longer managed) property's row on the Properties list — kept for
   its history, greyed so it never reads as a live building. A class on the <tr>
   alone does nothing: Bootstrap paints every table CELL with its own colour, so
   the rule has to reach the cells. Tertiary, not secondary — this app lifts
   secondary to 0.82 for readability, which is too close to body text to say
   "inactive". */
.table > tbody > tr.row-closed > * { color: var(--bs-tertiary-color); }

/* ---------------------------------------------------------------------------
   Brand mark (wwwroot/img/logo-mark.svg) — the Apartment Elink logo tile shown
   beside the product name in the navbar and, larger, on the sign-in card. The
   tile carries its own colours, so it reads on any Bootswatch theme.
   --------------------------------------------------------------------------- */
.brand-mark { width: 1.75rem; height: 1.75rem; vertical-align: -0.45rem; }
.brand-mark-lg { width: 3.5rem; height: 3.5rem; }

/* ---------------------------------------------------------------------------
   Site-wide search (Pages/Search, the search window _SearchModal +
   js/site-search.js). Each group shows four rows; once "Show more" has
   fetched the rest, the group scrolls INSIDE itself, so one big group never
   pushes the others out of reach.
   --------------------------------------------------------------------------- */
.search-scroll { max-height: 24rem; overflow-y: auto; }
.search-group .card-header .badge { font-weight: 600; }

/* In-app help styles live in help.css (the portals load it too). */
