/* ============================================
   @landlord/ui-shared — Component Library
   ============================================
   Portal-agnostic component class definitions extracted from
   landlord-portal main.css (UI Visual Refresh Design Plan §7).

   Contents:
     - Numeric utilities (.num/.money/.money--*)
     - Buttons (.btn family)
     - Form Elements (.form-group, .form-row, .form-actions, .duration-hint)
     - Cards (.card/.card-header/.card-body)
     - KPI Card (.kpi-grid/.kpi/.kpi__* — plan §5.1)
     - Cash-Flow Chart (.cash-flow-chart — plan §2.5)
     - Skeleton Loaders (.skeleton* — plan §5.3)
     - Data Tables (.data-table* — plan §5.2)
     - Badges & Status (.badge*)
     - Empty & Loading States (.empty-state/.loading-state/.error-state)

   Build-time copied into every portal's static/css/_shared/ via
   `npm run sync`. MUST be linked after tokens.css + fonts.css but
   before the portal's main.css so portal-specific context overrides
   (e.g. .hero .btn-primary) win the cascade.
   ============================================ */

/* ============================================
   Numeric Utilities — Fira Mono with tabular lining figures so
   money/counts align in columns. Apply .num or .money to any figure.
   ============================================ */
.num, .money, .data-table td.num, .kpi__value, .stat-value {
    font-family: var(--font-family-numeric);
    font-variant-numeric: tabular-nums lining-nums;
    font-feature-settings: var(--font-feature-numeric);
}

/* Sign-color modifiers for .money (plan §2.2). Token-driven so each has a
   dark-mode counterpart via the --color-money-* aliases. Apply explicitly
   when business semantics invert the raw sign (e.g. a tenant statement
   where a positive balance means "owes"). */
.money--positive { color: var(--color-money-positive); }
.money--negative { color: var(--color-money-negative); }
.money--neutral  { color: var(--color-money-neutral); }

/* ============================================
   Buttons
   ============================================ */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--spacing-sm);
    padding: 0.625rem 1.25rem;
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    text-align: center;
    border-radius: var(--border-radius);
    border: 1px solid transparent;
    cursor: pointer;
    transition: all var(--transition-fast);
    text-decoration: none;
    line-height: 1.5;
    white-space: nowrap;
}

.btn:focus-visible {
    box-shadow: var(--focus-ring);
}

.btn:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}

.btn-primary {
    background-color: var(--color-primary);
    color: white;
    border-color: var(--color-primary);
}

.btn-primary:hover:not(:disabled) {
    background-color: var(--color-primary-dark);
    border-color: var(--color-primary-dark);
    color: white;
}

.btn-primary:active:not(:disabled) {
    background-color: var(--color-primary-dark);
    transform: translateY(1px);
}

.btn-secondary {
    background-color: var(--color-background);
    color: var(--color-text-secondary);
    border-color: var(--color-border);
}

.btn-secondary:hover:not(:disabled) {
    background-color: var(--color-background-alt);
    border-color: var(--color-border);
    color: var(--color-text);
}

.btn-outline {
    background-color: transparent;
    color: var(--color-primary);
    border-color: var(--color-primary);
}

.btn-outline:hover:not(:disabled) {
    background-color: var(--color-primary);
    color: white;
}

.btn-ghost {
    background-color: transparent;
    color: var(--color-text-secondary);
    border-color: transparent;
}

.btn-ghost:hover:not(:disabled) {
    background-color: var(--color-background-alt);
    color: var(--color-text);
}

/* Button sizes */
.btn-xs {
    padding: 0.25rem 0.5rem;
    font-size: 0.75rem;
    line-height: 1.25;
}

.btn-sm {
    padding: 0.25rem 0.75rem;
    font-size: var(--font-size-xs);
    line-height: 1.25;
}

.btn-lg {
    padding: 0.75rem 1.5rem;
    font-size: var(--font-size-base);
}

.btn-large {
    padding: 0.875rem 2rem;
    font-size: var(--font-size-lg);
}

/* Full width button */
.btn-block {
    width: 100%;
}

/* ============================================
   Form Elements
   ============================================ */

.form-group {
    margin-bottom: var(--spacing-lg);
}

.form-group label {
    display: block;
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    color: var(--color-text);
    margin-bottom: var(--spacing-xs);
}

.form-group input,
.form-group select,
.form-group textarea {
    width: 100%;
    padding: 0.625rem 0.875rem;
    font-size: var(--font-size-base);
    font-family: inherit;
    color: var(--color-text);
    background-color: var(--color-background);
    border: 1px solid var(--color-border);
    border-radius: var(--border-radius);
    transition: all var(--transition-fast);
    line-height: var(--line-height-normal);
}

.form-group input:hover,
.form-group select:hover,
.form-group textarea:hover {
    border-color: var(--color-text-muted);
}

.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
    outline: none;
    border-color: var(--color-primary);
    box-shadow: 0 0 0 3px rgba(30, 58, 95, 0.1);
}

.form-group input::placeholder,
.form-group textarea::placeholder {
    color: var(--color-text-muted);
}

.form-group small {
    display: block;
    margin-top: var(--spacing-xs);
    font-size: var(--font-size-xs);
    color: var(--color-text-light);
}

.duration-hint {
    font-variant-numeric: tabular-nums;
    height: 1em;
}

.form-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: var(--spacing-md);
}

.form-actions {
    display: flex;
    justify-content: flex-end;
    gap: var(--spacing-md);
    margin-top: var(--spacing-xl);
    padding-top: var(--spacing-lg);
    border-top: 1px solid var(--color-border);
}

/* ============================================
   Cards
   ============================================ */

.card {
    background: var(--color-background);
    /* Elevation audit (plan §2.3): Tier 1 resting card uses shadow for
       separation — border removed so border+shadow don't compete. */
    border-radius: var(--border-radius-lg);
    box-shadow: var(--box-shadow);
    overflow: hidden;
}

.card-header {
    padding: var(--spacing-md) var(--spacing-lg);
    background-color: var(--color-background-alt);
    border-bottom: 1px solid var(--color-border);
}

.card-header h3 {
    margin: 0;
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-semibold);
}

.card-body {
    padding: var(--spacing-lg);
}

/* ============================================
   KPI Card (plan §5.1) — hero metric + delta + trend arrow.
   Replaces .stat-tile on the dashboard finance card.
   ============================================ */

.kpi-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: var(--spacing-lg);
}

.kpi {
    /* Elevation: Tier 1 card — shadow only, no competing border (plan §2.3). */
    position: relative;
    padding: var(--spacing-lg);
    background: var(--color-background);
    border-radius: var(--border-radius-lg);
    box-shadow: var(--box-shadow);
    display: flex;
    flex-direction: column;
    gap: var(--spacing-xs);
}

.kpi__label {
    font-size: var(--font-size-xs);
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--color-text-light);
}

.kpi__value {
    /* Font family + tabular figures applied via the .num/.money/.kpi__value
       utility selector above (Phase 0). */
    font-size: var(--font-size-3xl);
    font-weight: var(--font-weight-bold);
    color: var(--color-text);
    line-height: 1.1;
}

.kpi__delta {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
}

.kpi__delta .num { font-weight: var(--font-weight-semibold); }

.kpi__delta-note {
    color: var(--color-text-muted);
    font-weight: var(--font-weight-normal);
}

.kpi__delta--positive { color: var(--color-trend-up); }
.kpi__delta--negative { color: var(--color-trend-down); }
.kpi__delta--neutral  { color: var(--color-trend-flat); }

.kpi__spark {
    width: 100%;
    height: var(--spacing-xl);
    margin-top: var(--spacing-xs);
    display: block;
}

.kpi--feature { border-left: 4px solid var(--color-primary); }

/* ============================================
   Cash-Flow Chart (plan §2.5, Phase 4 data-viz)
   — token-driven; no hardcoded chart palette.
   ============================================ */

.cash-flow-chart {
    width: 100%;
    height: auto;
    display: block;
    overflow: visible;
}

.cash-flow-chart text {
    font-family: var(--font-family-mono);
    font-variant-numeric: var(--font-feature-numeric);
}

@media (prefers-reduced-motion: reduce) {
    .cash-flow-chart,
    .kpi__spark {
        transition: none;
        animation: none;
    }
}


/* ============================================
   Skeleton Loaders (plan §5.3) — shimmer blocks
   matching the shape of eventual content.
   ============================================ */

.skeleton {
    position: relative;
    overflow: hidden;
    background: var(--color-background-subtle);
    border-radius: var(--border-radius-sm);
}

.skeleton::after {
    content: "";
    position: absolute;
    inset: 0;
    transform: translateX(-100%);
    /* Shimmer overlay — semi-transparent white sweep (dark-mode variant is
       Phase 4b; this light-mode value mirrors the Visual Style Guide). */
    background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.6), transparent);
    animation: skeleton-shimmer 1.4s infinite;
}

.skeleton-text  { height: 1em; margin-bottom: var(--spacing-xs); }
.skeleton-line  { height: 0.75em; }
.skeleton-block { height: var(--spacing-3xl); }
.skeleton-list  { display: flex; flex-direction: column; gap: var(--spacing-sm); }

@keyframes skeleton-shimmer { 100% { transform: translateX(100%); } }

/* Reduced motion: skeleton becomes a static muted block (brand/a11y). */
@media (prefers-reduced-motion: reduce) {
    .skeleton::after { animation: none; transform: none; background: none; }
}

/* ============================================
   Data Tables (plan §5.2) — semantic <table> component with right-aligned
   numerics, row hover, sticky-header and compact-density variants.
   HTML contract: <table class="data-table"><thead><tr><th scope="col">…
   Numeric columns: <th scope="col" class="num"> / <td class="num">.
   ============================================ */

/* Scroll wrapper — provides horizontal overflow on narrow viewports and
   a bordered, rounded surface around the table. */
.data-table-wrap {
    overflow-x: auto;
    border: 1px solid var(--color-border);
    border-radius: var(--border-radius-lg);
    background: var(--color-background);
    -webkit-overflow-scrolling: touch;
}

.data-table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--font-size-sm);
}

/* Header row — small uppercase labels underlined with --color-border-strong. */
.data-table thead th {
    font-size: var(--font-size-xs);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    font-weight: var(--font-weight-semibold);
    color: var(--color-text-muted);
    text-align: left;
    padding: var(--spacing-sm) var(--spacing-md);
    border-bottom: 2px solid var(--color-border-strong);
    white-space: nowrap;
    vertical-align: bottom;
}

/* Body rows — 1px --color-border-light divider; hover → --color-background-alt. */
.data-table tbody td {
    padding: var(--spacing-sm) var(--spacing-md);
    border-bottom: 1px solid var(--color-border-light);
    color: var(--color-text-secondary);
    vertical-align: middle;
}

.data-table tbody tr:last-child td {
    border-bottom: none;
}

.data-table tbody tr:hover td {
    background: var(--color-background-alt);
}

/* Numeric columns — layout/alignment only. Font rules (Fira Mono, tabular
   lining figures) are in the Phase 0 .num/.money utility block; don't
   duplicate them here. */
.data-table th.num,
.data-table td.num {
    text-align: right;
    white-space: nowrap;
}

/* Sortable header affordance — cursor + hover colour shift. The sort icon
   (a Material Symbols span inside the <th>) toggles via .sort-asc/.sort-desc
   classes applied by the page JS. */
.data-table th.sortable {
    cursor: pointer;
    user-select: none;
}

.data-table th.sortable:hover {
    color: var(--color-text);
}

.data-table th.sortable .sort-icon {
    font-size: var(--font-size-sm);
    opacity: 0.4;
    margin-left: var(--spacing-xs);
    vertical-align: middle;
}

.data-table th.sortable.sort-asc .sort-icon,
.data-table th.sortable.sort-desc .sort-icon {
    opacity: 1;
}

/* Striped variant — subtle alternating row background. */
.data-table--striped tbody tr:nth-child(even) td {
    background: var(--color-background-alt);
}

/* Sticky header — pins <thead> inside a scroll container so rows scroll
   under it. Requires the table to be in a scrollable ancestor. */
.data-table--sticky thead th {
    position: sticky;
    top: 0;
    background: var(--color-background);
    z-index: 1;
}

/* Compact density — tighter padding for dense financial views (plan §2.6).
   Apply only to genuinely dense tables; keep summary/comfortable tables
   at default density. */
.data-table--compact thead th,
.data-table--compact tbody td {
    padding: var(--spacing-xs) var(--spacing-sm);
    line-height: var(--line-height-normal);
}

/* ============================================
   Badges & Status
   ============================================ */

.badge {
    display: inline-flex;
    align-items: center;
    padding: 0.25rem 0.625rem;
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-medium);
    text-transform: uppercase;
    letter-spacing: 0.025em;
    border-radius: var(--border-radius-sm);
    white-space: nowrap;
}

.badge-success {
    background-color: var(--color-success-light);
    color: var(--color-success);
}

.badge-warning {
    background-color: var(--color-warning-light);
    color: var(--color-warning);
}

.badge-error {
    background-color: var(--color-error-light);
    color: var(--color-error);
}

.badge-neutral {
    background-color: var(--color-background-subtle);
    color: var(--color-text-light);
}

/* ============================================
   Empty & Loading States
   ============================================ */

.empty-state {
    text-align: center;
    padding: var(--spacing-2xl);
    color: var(--color-text-light);
}

.empty-state .empty-icon {
    font-size: 3rem;
    margin-bottom: var(--spacing-md);
    opacity: 0.5;
}

.empty-state h3 {
    color: var(--color-text);
    margin-bottom: var(--spacing-sm);
}

.empty-state p {
    color: var(--color-text-light);
    margin-bottom: var(--spacing-lg);
}

.loading-state {
    text-align: center;
    padding: var(--spacing-xl);
    color: var(--color-text-light);
}

.error-state {
    text-align: center;
    padding: var(--spacing-xl);
    color: var(--color-error);
}

/* ── Inline field validation ─────────────────────────────────────────────
   Paired with core/validation.js: a page toggles .input-error on the field
   and shows the matching .input-error-msg (hidden until then). Lived as
   copies inside individual page templates, one of them with a hard-coded
   #ef4444 (3.76:1 on white - below AA), and the add-tenant page had neither
   the elements nor the styles, so a rejected phone number did nothing
   visible at all (2026-09-26). One definition, token-driven, both themes.
   The message text carries the meaning; colour is not the only signal. */
.input-error {
    border-color: var(--color-error) !important;
}

.input-error-msg {
    color: var(--color-error-text);
    font-size: var(--font-size-xs);
    margin-top: var(--spacing-xs);
    display: none;
}
