/* Portfolio Tracker — Complete dark theme over Bootstrap 5 */

:root {
    --bg-dark: #0d1117;
    --bg-card: #161b22;
    --bg-input: #21262d;
    --border-color: #30363d;
    --text-primary: #e6edf3;
    --text-secondary: #8b949e;
    --accent-green: #3fb950;
    --accent-red: #f85149;
    --accent-blue: #58a6ff;
    --accent-yellow: #d29922;
}

/* ── Base ── */

html.dark-mode,
body.dark-mode {
    background-color: var(--bg-dark) !important;
    color: var(--text-primary) !important;
}

body.dark-mode a:not(.btn):not(.nav-link) {
    color: var(--accent-blue);
}

body.dark-mode a:hover:not(.btn):not(.nav-link) {
    color: #79c0ff;
}

/* ── Navbar ── */

body.dark-mode .navbar {
    background-color: var(--bg-card) !important;
    border-bottom: 1px solid var(--border-color);
}

body.dark-mode .navbar .nav-link {
    color: var(--text-secondary) !important;
}

body.dark-mode .navbar .nav-link:hover {
    color: var(--text-primary) !important;
}

body.dark-mode .navbar-brand {
    color: var(--text-primary) !important;
}

/* ── Cards ── */

body.dark-mode .card {
    background-color: var(--bg-card);
    border: 1px solid var(--border-color);
    color: var(--text-primary);
}

body.dark-mode .card-header {
    background-color: rgba(255, 255, 255, 0.03);
    border-bottom: 1px solid var(--border-color);
    color: var(--text-primary);
}

body.dark-mode .card-body {
    color: var(--text-primary);
}

body.dark-mode .card-title,
body.dark-mode .card-subtitle {
    color: var(--text-primary);
}

body.dark-mode .card-text {
    color: var(--text-primary);
}

/* ── Forms ── */

body.dark-mode .form-control,
body.dark-mode .form-select {
    background-color: var(--bg-input) !important;
    border: 1px solid var(--border-color) !important;
    color: var(--text-primary) !important;
}

body.dark-mode .form-control:focus,
body.dark-mode .form-select:focus {
    background-color: var(--bg-input) !important;
    color: var(--text-primary) !important;
    border-color: var(--accent-blue) !important;
    box-shadow: 0 0 0 0.2rem rgba(88, 166, 255, 0.25) !important;
}

body.dark-mode .form-control::placeholder {
    color: var(--text-secondary) !important;
}

body.dark-mode .form-label {
    color: var(--text-primary) !important;
}

body.dark-mode .form-check-label {
    color: var(--text-primary);
}

body.dark-mode .form-check-input {
    background-color: var(--bg-input);
    border-color: var(--border-color);
}

body.dark-mode .form-text {
    color: var(--text-secondary);
}

body.dark-mode .input-group-text {
    background-color: var(--bg-input);
    border-color: var(--border-color);
    color: var(--text-primary);
}

/* ── Tables ── */

body.dark-mode .table {
    color: var(--text-primary) !important;
    --bs-table-bg: transparent;
    --bs-table-hover-bg: rgba(88, 166, 255, 0.05);
    --bs-table-striped-bg: rgba(255, 255, 255, 0.02);
}

body.dark-mode .table > :not(caption) > * > * {
    border-bottom-color: var(--border-color);
    background-color: transparent;
    color: var(--text-primary);
}

body.dark-mode .table thead th {
    border-bottom-color: var(--border-color);
    color: var(--text-secondary);
    font-weight: 600;
}

body.dark-mode .table-hover tbody tr:hover {
    background-color: rgba(88, 166, 255, 0.05) !important;
}

body.dark-mode .table-striped > tbody > tr:nth-of-type(odd) > * {
    background-color: rgba(255, 255, 255, 0.02);
}

/* ── Buttons ── */

body.dark-mode .btn-primary {
    background-color: var(--accent-blue);
    border-color: var(--accent-blue);
}

body.dark-mode .btn-outline-secondary {
    border-color: var(--border-color);
    color: var(--text-primary);
}

body.dark-mode .btn-outline-secondary:hover {
    background-color: var(--border-color);
    color: var(--text-primary);
}

body.dark-mode .btn-close {
    filter: invert(0.8);
}

body.dark-mode .btn-close-white {
    filter: invert(1);
}

/* ── Modals ── */

body.dark-mode .modal-content {
    background-color: var(--bg-card);
    border: 1px solid var(--border-color);
    color: var(--text-primary);
}

body.dark-mode .modal-header {
    border-bottom-color: var(--border-color);
    color: var(--text-primary);
}

body.dark-mode .modal-title {
    color: var(--text-primary);
}

body.dark-mode .modal-body {
    color: var(--text-primary);
}

body.dark-mode .modal-footer {
    border-top-color: var(--border-color);
}

/* ── Alerts ── */

body.dark-mode .alert {
    background-color: var(--bg-card);
    border-color: var(--border-color);
    color: var(--text-primary);
}

body.dark-mode .alert-success {
    border-left: 3px solid var(--accent-green);
}

body.dark-mode .alert-danger {
    border-left: 3px solid var(--accent-red);
}

body.dark-mode .alert-warning {
    border-left: 3px solid var(--accent-yellow);
}

body.dark-mode .alert-info {
    border-left: 3px solid var(--accent-blue);
}

/* ── Badges ── */

body.dark-mode .badge {
    color: var(--text-primary);
}

body.dark-mode .bg-secondary {
    background-color: var(--border-color) !important;
}

body.dark-mode .bg-success {
    background-color: rgba(63, 185, 80, 0.2) !important;
    color: var(--accent-green) !important;
}

body.dark-mode .bg-danger {
    background-color: rgba(248, 81, 73, 0.2) !important;
    color: var(--accent-red) !important;
}

/* ── Text utilities ── */

body.dark-mode .text-muted {
    color: var(--text-secondary) !important;
}

body.dark-mode .text-body {
    color: var(--text-primary) !important;
}

body.dark-mode .text-dark {
    color: var(--text-primary) !important;
}

body.dark-mode .text-light {
    color: var(--text-primary) !important;
}

body.dark-mode .text-white {
    color: var(--text-primary) !important;
}

body.dark-mode .text-black {
    color: var(--text-primary) !important;
}

body.dark-mode h1,
body.dark-mode h2,
body.dark-mode h3,
body.dark-mode h4,
body.dark-mode h5,
body.dark-mode h6 {
    color: var(--text-primary);
}

body.dark-mode p,
body.dark-mode li,
body.dark-mode span:not(.badge-internal) {
    color: var(--text-primary);
}

body.dark-mode strong {
    color: var(--text-primary);
}

body.dark-mode small {
    color: var(--text-secondary);
}

/* ── Misc Bootstrap components ── */

body.dark-mode .list-group-item {
    background-color: var(--bg-card);
    border-color: var(--border-color);
    color: var(--text-primary);
}

body.dark-mode .dropdown-menu {
    background-color: var(--bg-card);
    border-color: var(--border-color);
}

body.dark-mode .dropdown-item {
    color: var(--text-primary);
}

body.dark-mode .dropdown-item:hover {
    background-color: var(--bg-input);
    color: var(--text-primary);
}

body.dark-mode .progress {
    background-color: var(--bg-input);
}

body.dark-mode .page-link {
    background-color: var(--bg-card);
    border-color: var(--border-color);
    color: var(--text-primary);
}

body.dark-mode .page-item.active .page-link {
    background-color: var(--accent-blue);
    border-color: var(--accent-blue);
}

body.dark-mode .nav-tabs .nav-link {
    color: var(--text-secondary);
    border-color: transparent;
}

body.dark-mode .nav-tabs .nav-link:hover,
body.dark-mode .nav-tabs .nav-link.active {
    color: var(--text-primary);
    border-color: var(--border-color);
    border-bottom-color: transparent;
}

/* ── Custom components ── */

.text-green {
    color: var(--accent-green) !important;
}

.text-red {
    color: var(--accent-red) !important;
}

.summary-card .card {
    border-left: 3px solid var(--accent-blue);
}

.summary-card .card-title {
    font-size: 0.85rem;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--text-secondary);
}

.summary-card .card-value {
    font-size: 1.5rem;
    font-weight: 700;
    color: var(--text-primary);
}

.badge-green {
    background-color: rgba(63, 185, 80, 0.15);
    color: var(--accent-green);
}

.badge-red {
    background-color: rgba(248, 81, 73, 0.15);
    color: var(--accent-red);
}

.base-currency {
    font-size: 0.8rem;
    font-weight: 500;
    color: var(--text-secondary) !important;
    letter-spacing: 0.5px;
    vertical-align: middle;
}

.perf-head {
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--text-secondary) !important;
}

.perf-value {
    font-size: 1.35rem;
    font-weight: 700;
}

.perf-note {
    font-size: 0.75rem;
    color: var(--text-secondary) !important;
}

.perf-breakdown .col {
    min-width: 8rem;
}

.perf-breakdown .col > div:nth-child(2) {
    font-size: 1.05rem;
    font-weight: 600;
}

.summary-card .card-note {
    font-size: 0.75rem;
    color: var(--text-secondary) !important;
    margin-top: 0.15rem;
}

.badge-amber {
    background-color: rgba(210, 153, 34, 0.15);
    color: var(--accent-yellow);
}

/* The base currency is a currency like any other in that column, so it gets a
   badge too - only a quieter one, since it is the unconverted case. */
.badge-base {
    background-color: rgba(139, 148, 158, 0.15);
    color: var(--text-secondary) !important;
}

/* The base-currency tag on a money column header. */
.col-ccy {
    font-size: 0.7rem;
    font-weight: 400;
    color: var(--text-secondary);
    opacity: 0.75;
}

/* ── Quote freshness ──
   Three states, and they say three different things:

     grey   the quote is inside its 24h hold - the ordinary case, not a warning
     amber  the hold lapsed and the refresh failed, so this is the last price
            that arrived and it is older than it should be
     red    there is no price at all (.badge-red "Sin datos")

   The middle one is keyed on when a provider last answered, not on the market
   time of the quote: a Friday close is three days old by Sunday with nothing
   having gone wrong, and colouring off that would turn the table amber every
   weekend until nobody read the colour.

   A holding Yahoo could not price is not a holding worth nothing, so it is
   dimmed and labelled rather than shown at $0.00 alongside real figures.

   The !important is not decoration: the dark theme colours every td and every
   span from `body.dark-mode ...` selectors that outrank a two-class rule, which
   is the same reason .text-green above carries one. */

tr.row-unpriced td {
    color: var(--text-secondary) !important;
}

tr.row-unpriced td:first-child {
    box-shadow: inset 2px 0 0 var(--text-secondary);
}

tr.row-stale td:first-child {
    box-shadow: inset 2px 0 0 var(--accent-yellow);
}

.price-age {
    font-size: 0.8rem;
    color: var(--text-secondary) !important;
    white-space: nowrap;
}

.price-age.is-stale {
    color: var(--accent-yellow) !important;
}

/* ── Sortable table headers ── */

th.sortable {
    cursor: pointer;
    user-select: none;
    white-space: nowrap;
}

th.sortable:hover {
    color: var(--accent-blue);
}

th.sortable::after {
    content: "↕";          /* up-down arrow, so the column reads as sortable */
    opacity: 0.25;
    margin-left: 0.35rem;
    font-size: 0.85em;
}

th.sortable[aria-sort="ascending"]::after {
    content: "↑";
    opacity: 1;
    color: var(--accent-blue);
}

th.sortable[aria-sort="descending"]::after {
    content: "↓";
    opacity: 1;
    color: var(--accent-blue);
}

.login-card {
    max-width: 400px;
    margin: 10vh auto;
}

/* Toast */
.toast-container {
    position: fixed;
    top: 1rem;
    right: 1rem;
    z-index: 9999;
}

/* Chart
   Chart.js with maintainAspectRatio:false takes the canvas size from the
   canvas's parent. If that parent's own height is decided by its content,
   then the canvas is measuring a box it is itself inside of, and every
   resize feeds the canvas's new height back in as the box's height: the
   chart grows without bound. Both symptoms of that were visible - the
   reports canvas rendered at 700px because its card also held a long
   table, and the retirement one crept downward on resize.

   So the canvas gets a wrapper that owns its height outright. Nothing
   drawn inside can change it, which is what breaks the loop. The height
   attribute on the canvas is deliberately absent: Chart.js overwrites it
   on the first frame, so it only ever described the chart before it was
   drawn. */
.chart-box {
    position: relative;
    height: var(--chart-height, 320px);
}

.chart-box > canvas {
    display: block;
    width: 100%;
    height: 100%;
}

@media (max-width: 767.98px) {
    .chart-box {
        height: 240px;
    }
}

.chart-empty {
    min-height: 300px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.35rem;
    text-align: center;
    color: var(--text-secondary) !important;
}

.chart-empty.d-none {
    display: none !important;
}

/* Responsive tables */
.table-responsive-custom {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

@media (max-width: 768px) {
    .summary-card .card-value {
        font-size: 1.2rem;
    }
}

/* ── Retirement plan ──
   The projection table mixes facts and assumptions in one column of figures, so
   each row says which it is. A left bar rather than a background: the table
   already stripes on hover and a filled row would fight it, and the bar reads at
   a glance down six hundred rows.

     amber  a real balance fixed this month - a statement's closing NAV, or
            today's live portfolio
     blue   the current month
     dim    everything ahead of today, which is a model's opinion and is not
            shown in the same weight as the figures behind it

   The !important carries the same reason as the price rules above: the dark
   theme colours every td from a selector that outranks a two-class rule. */

/* The two marks coincide on the current month, whose balance is normally a real
   one, so they are split across two properties rather than both claiming the
   bar: "when" is the tint, "is this a fact" is the bar. The :not() keeps a
   current month with no known balance from losing its mark entirely. */

tr.plan-known td:first-child {
    box-shadow: inset 2px 0 0 var(--accent-yellow);
}

/* The !important is the same one .text-green and the .price-age rules carry:
   `body.dark-mode .table > :not(caption) > * > *` paints every cell transparent
   and outranks a two-class rule. It also outranks the table's own hover, so the
   hover state is restated here rather than silently lost on this one row. */
tr.plan-now > td {
    background-color: rgba(88, 166, 255, 0.07) !important;
}

tr.plan-now:hover > td {
    background-color: rgba(88, 166, 255, 0.13) !important;
}

tr.plan-now:not(.plan-known) td:first-child {
    box-shadow: inset 2px 0 0 var(--accent-blue);
}

tr.plan-estimate td {
    color: var(--text-secondary) !important;
}

/* The totals stay legible even in an estimated row: they are the column anyone
   actually reads down, and dimming those too makes the whole table one grey. */
tr.plan-estimate td.fw-bold {
    color: var(--text-primary) !important;
}

.plan-dot {
    display: inline-block;
    width: 0.55rem;
    height: 0.55rem;
    border-radius: 2px;
    margin-right: 0.2rem;
    vertical-align: baseline;
}

.plan-dot-known { background-color: var(--accent-yellow); }
.plan-dot-now   { background-color: var(--accent-blue); }
.plan-dot-est   { background-color: var(--text-secondary); }

/* Six hundred monthly rows do not belong in one page scroll, and the yearly view
   is fifty - so the pane is capped and scrolls itself, and renderTable() brings
   the current period into view inside it. */
.plan-table-scroll {
    max-height: 26rem;
    overflow-y: auto;
}

/* The !important again, and here it is not cosmetic: the dark theme paints every
   cell transparent, so without it the rows scroll *through* the pinned header
   and both are unreadable. */
.plan-table-scroll thead th {
    position: sticky;
    top: 0;
    z-index: 1;
    background-color: var(--bg-card) !important;
    box-shadow: inset 0 -1px 0 var(--border-color);
}

/* Daily reports
   The summary is stored as preformatted text - it draws its own table with box
   characters - so it is shown in a monospace block that keeps that alignment,
   scrolling sideways rather than wrapping. */
.daily-report + .daily-report {
    margin-top: 0.5rem;
}

.daily-report-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    width: 100%;
    padding: 0.5rem 0.75rem;
    border: 1px solid var(--border-color);
    border-radius: 6px;
    background-color: var(--bg-input);
    color: var(--text-primary);
    text-align: left;
}

.daily-report-head:hover {
    border-color: var(--accent-blue);
}

.daily-report-head::after {
    content: "▾";
    color: var(--text-secondary);
    transition: transform 0.15s ease;
}

.daily-report-head[aria-expanded="true"]::after {
    transform: rotate(180deg);
}

.daily-report-date {
    font-variant-numeric: tabular-nums;
}

.daily-report-figures {
    display: flex;
    gap: 1rem;
    margin-left: auto;
    font-variant-numeric: tabular-nums;
}

.daily-report-body {
    margin: 0.5rem 0 0;
    padding: 0.75rem;
    border: 1px solid var(--border-color);
    border-radius: 6px;
    background-color: var(--bg-card);
    color: var(--text-secondary);
    font-size: 0.8rem;
    line-height: 1.45;
    overflow-x: auto;
}
