/* Base styles */
html {
  scroll-behavior: smooth;
}

body {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  transition: background-color var(--transition-normal), color var(--transition-normal);
}

main {
  flex: 1;
}

/* Header */
/* The header is a .section so it gets main's side padding and shares its edges. */
.site-header {
  padding-block: var(--space-lg);
}

/* The header's bottom padding already separates it from the content. */
.site-header + .section {
  padding-top: 0;
}

.site-header > .container {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  column-gap: var(--space-md);
}

/* Spans the controls' column too, so a long tagline wraps later on a phone. */
.site-header .tagline {
  grid-row: 2;
  grid-column: 2 / -1;
  /* Bulma's .subtitle:not(:last-child) block spacing would add 1.5rem under the header. */
  margin-bottom: 0;
}

/* The 2rem title splits below 336px; 360 leaves room for fonts that render wider. */
@media screen and (max-width: 359px) {
  .site-header .title {
    --bulma-title-size: var(--bulma-size-4);
  }

  .site-header .brand-mark {
    height: calc(var(--bulma-size-4) * 1.125);
  }

  .site-header .tagline {
    grid-column: 1 / -1;
  }
}

.header-controls {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  /* Menus open from the controls' right edge, not their own button's: the preferences menu would leave a 320px screen. */
  position: relative;
}

.header-controls .dropdown {
  position: static;
}

.header-controls .dropdown-menu {
  max-width: calc(100vw - 3rem);
}

/* Bulma reserves 3rem for a trailing icon these menus never show. */
.header-controls .dropdown-item {
  padding-inline-end: 1rem;
}

.header-controls button.dropdown-item {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  border: none;
  cursor: pointer;
  font: inherit;
  font-size: 0.875rem;
}

.header-controls button.dropdown-item svg {
  width: 1rem;
  height: 1rem;
}

/* A 44px target (WCAG 2.5.5). The negative block margin keeps the header row as tall as the old 36px toggle did. */
.header-icon-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  margin-block: -4px;
  padding: 0;
  border: none;
  border-radius: var(--radius-md);
  background: none;
  color: var(--text-primary);
  cursor: pointer;
  transition: background-color var(--transition-fast);
}

.header-icon-button:hover,
.header-icon-button[aria-expanded="true"] {
  background-color: var(--bg-tertiary);
}

.header-icon-button svg {
  width: 20px;
  height: 20px;
}

/* A long display name wraps instead of widening the menu past the screen. */
.menu-signed-in-as {
  white-space: normal;
  overflow-wrap: anywhere;
}

/* Filter section */
.filters {
  margin-bottom: var(--space-lg);
}

/* A country name is several times as wide as its code, so below the tablet breakpoint the exchange and the
   country each take their own line. Sharing one phone-width row truncated "KRAKEN" down to "KRA". 769px is the
   breakpoint the mobile-only/desktop-only pair below already uses. */
.filters .exchange-field {
  flex-wrap: wrap;
  row-gap: var(--space-sm);
}

.filters .exchange-field .control {
  flex: 1 0 100%;
}

/* Bulma's addons row collapses the border between controls with a -1px margin, so a stacked
   control with no neighbour beside it renders 1px wider than the row. The desktop query below
   restores the margin, where there is a neighbour to collapse against again. Same specificity
   as Bulma's rule, won on source order. */
.filters .exchange-field .control:not(:last-child) {
  margin-inline-end: 0;
}

.filters .exchange-field .select,
.filters .exchange-field select {
  width: 100%;
}

/* Bulma squares off the inner corners of an addons row. Stacked, that reads as two half-rounded boxes, so the
   corners come back. Same specificity as Bulma's rule, won on source order. */
.filters .exchange-field .control:first-child:not(:only-child) .select select,
.filters .exchange-field .control:last-child:not(:only-child) .select select {
  border-radius: var(--bulma-radius);
}

@media screen and (min-width: 769px) {
  .filters .exchange-field {
    flex-wrap: nowrap;
  }

  .filters .exchange-field .control {
    flex: 0 1 auto;
    /* A flex item's automatic min-width is its content's min-content size, so a <select> with a long
       country name option cannot shrink below that width and pushes the row past the viewport. */
    min-width: 0;
  }

  .filters .exchange-field .control.is-expanded {
    flex: 1 1 auto;
  }

  .filters .exchange-field .control:not(:last-child) {
    margin-inline-end: -1px;
  }

  .filters .exchange-field .control:first-child:not(:only-child) .select select {
    border-bottom-right-radius: 0;
    border-top-right-radius: 0;
  }

  .filters .exchange-field .control:last-child:not(:only-child) .select select {
    border-bottom-left-radius: 0;
    border-top-left-radius: 0;
  }
}

.top-presets {
  display: flex;
  gap: var(--space-sm);
  flex-wrap: wrap;
}

.top-presets .button {
  min-width: 50px;
}

/* Results */
.allocation-card {
  margin-bottom: var(--space-md);
  transition: transform var(--transition-fast), box-shadow var(--transition-fast);
}

.allocation-card:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-md);
}

.allocation-card .rank {
  font-size: 1.25rem;
  font-weight: bold;
  color: var(--accent);
}

.allocation-card .coin-name {
  font-weight: 600;
}

/* Desktop table */
.allocation-table {
  width: 100%;
}

.allocation-table th {
  white-space: nowrap;
}

/* Filtered coins keep their place in the ranking but read as context, not as holdings. Draining the colour
   reads as "unavailable" without touching contrast: the grayscale matrix is the luminance one, so every pair
   inside keeps its ratio. Fading the card instead composited its tag down to 2.75:1. */
.allocation-table .filtered-row {
  filter: grayscale(1);
  background-color: var(--bg-tertiary);
  font-size: 0.875rem;
}

.allocation-card.filtered-row {
  filter: grayscale(1);
  font-size: 0.875rem;
}

/* Bulma sets display on table rows and cards, which would beat the user agent's [hidden] rule. */
.allocation-table tbody tr[hidden],
.allocation-card[hidden],
.filter-run[hidden] {
  display: none;
}

.filter-toggle {
  margin-top: var(--space-md);
}

/* The marker for a run of filtered coins is a seam in the ranking, not another row of data: no grayscale, just
   the same muted colour a filtered row's own text already uses. */
.filter-run {
  text-align: center;
  color: var(--text-secondary);
}

.filter-run-toggle {
  background: none;
  border: none;
  color: inherit;
}

/* Not a Bulma .tag: .tag.is-light resolves its colour through --bulma-light-light-invert-l,
   which Bulma 1.0.0 references but never defines, so the text falls back to inherited. */
.filter-reason {
  display: inline-flex;
  align-items: center;
  height: 2em;
  padding: 0 0.75em;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  background-color: var(--bg-tertiary);
  color: var(--text-primary);
  font-size: var(--bulma-size-small);
  white-space: nowrap;
}

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

/* Responsive */
.mobile-only {
  display: block;
}

.desktop-only {
  display: none;
}

@media screen and (min-width: 769px) {
  .mobile-only {
    display: none;
  }

  .desktop-only {
    display: block;
  }
}

/* Summary section */
.summary-stat {
  text-align: center;
}

.summary-stat .value {
  font-size: 1.5rem;
  font-weight: bold;
  color: var(--accent);
}

/* Two classes deep, so it outranks .is-gain and .is-loss: without these the portfolio's headline
   total return renders in the accent colour whichever way the portfolio went. */
.summary-stat .value.is-gain {
  color: var(--gain);
}

.summary-stat .value.is-loss {
  color: var(--loss);
}

.summary-stat .heading {
  font-size: 0.875rem;
  color: var(--text-secondary);
}

/* Freshness indicator */
.freshness {
  font-size: 0.875rem;
  display: flex;
  align-items: center;
  gap: var(--space-xs);
}

.freshness.fresh {
  color: var(--success);
}

.freshness.stale {
  color: var(--warning);
}

.is-gain {
  color: var(--gain);
}

.is-loss {
  color: var(--loss);
}

/* No results state */
.no-results {
  text-align: center;
  padding: var(--space-xl);
  color: var(--text-secondary);
}

.no-results svg {
  width: 64px;
  height: 64px;
  margin-bottom: var(--space-md);
  opacity: 0.5;
}

/* Logo */

/* Matches the h1 beside it, whose line box is Bulma's title size at line-height 1.125. */
.brand-mark {
    display: block;
    height: calc(var(--bulma-size-3) * 1.125);
    width: auto;
}

.brand-footer {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-sm);
}

.brand-footer .brand-mark {
    height: 24px;
}

/* Bulma's link blue reaches 3.4:1 on the light footer; the footer's own grey with an underline reads as a link. */
.legal-links a {
    color: var(--text-secondary);
    text-decoration: underline;
}

/* Brand components. Bulma's variables only take flat colours, so the gradients live here. */
.title,
.subtitle,
.summary-stat .value,
.allocation-card .rank {
    font-family: var(--hf-font-heading);
}

.title {
    letter-spacing: 0.01em;
}

.tagline {
    font-family: var(--hf-font-body);
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--text-secondary);
    font-size: 0.75rem;
}

.tagline.is-hero {
    letter-spacing: 0.22em;
    font-size: 0.8125rem;
}

.button.is-primary {
    background-image: linear-gradient(135deg, var(--hf-blue) 0%, var(--hf-mint) 100%);
    border: none;
    color: var(--hf-night);
    font-weight: 600;
    transition: box-shadow var(--transition-fast), transform var(--transition-fast);
}

.button.is-primary:hover {
    box-shadow: var(--hf-shadow);
    transform: translateY(-1px);
}

.box,
.card {
    border: 1px solid var(--border-color);
}

/* The one pair that fails AA on a card, so it never carries text — only a 2px ring. */
:focus-visible {
    outline: 2px solid var(--hf-blue);
    outline-offset: 2px;
}

/* Portfolio pages */
.save-portfolio {
    display: flex;
    gap: var(--space-sm);
    flex-wrap: wrap;
}

.portfolio-row .portfolio-link {
    color: var(--accent);
    display: block;
}

/* Transparent on purpose: the badge sits on the card surface the rest of the page's gain and loss
   text was measured against, rather than on --bg-tertiary, where --loss falls under 4.5:1 in dark. */
.drift-badge {
    background-color: transparent;
    border: 1px solid var(--border-color);
    color: var(--text-primary);
    white-space: nowrap;
}

.drift-badge.urgency-BALANCED {
    color: var(--gain);
}

.drift-badge.urgency-DUE {
    color: var(--warning);
}

.drift-badge.urgency-OVERDUE {
    color: var(--loss);
}

/* The width a quantity or a price stays readable at, and the basis the stacked layout grows from. */
.holdings-table input,
.rebalance-table input {
    min-width: 7rem;
}

.drop > summary {
    cursor: pointer;
    color: var(--text-secondary);
    list-style: none;
}

.drop > summary::-webkit-details-marker {
    display: none;
}

.drop-confirm {
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    padding: var(--space-sm);
    margin-top: var(--space-xs);
    /* 16rem keeps the warning readable in a narrow last column; a phone has less than that to give. */
    min-width: min(16rem, 100%);
    text-align: left;
    white-space: normal;
}

.adjustment {
    display: block;
    white-space: nowrap;
}

/* A stacked table is the one markup tree restyled: below the tablet breakpoint each row becomes a
   labelled block, and the header row that named the columns is replaced by a label per cell. The
   read-only history follows the two editable tables so all three read the same way.

   769px is the breakpoint the rest of this file crosses at min-width. It is written the other way
   round here because the desktop side would otherwise have to undo seven display values. */
@media screen and (max-width: 768px) {
    .stacked-table,
    .stacked-table tbody,
    .stacked-table tr,
    .stacked-table td {
        display: block;
    }

    .stacked-table thead {
        display: none;
    }

    .stacked-table tr {
        border: 1px solid var(--border-color);
        border-radius: var(--radius-md);
        padding: var(--space-sm);
    }

    .stacked-table tr:not(:last-child) {
        margin-bottom: var(--space-md);
    }

    /* Label left, figure right, wrapping rather than widening the row when a translated label and a
       7rem input cannot share a line. */
    .stacked-table td {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        justify-content: space-between;
        gap: var(--space-xs) var(--space-sm);
        border-bottom: none;
        padding: var(--space-xs) 0;
    }

    .stacked-table td[data-label]::before {
        content: attr(data-label);
        color: var(--text-secondary);
        font-size: 0.875rem;
    }

    /* What the row is about, which needs no label and reads as the block's heading. */
    .stacked-table td.stacked-heading {
        display: block;
        margin-bottom: var(--space-xs);
        padding-bottom: var(--space-sm);
        border-bottom: 1px solid var(--border-color);
    }

    /* A cell holding a control or a list of them, where a label beside the value leaves neither room. */
    .stacked-table td.stacked-block {
        display: block;
    }

    .stacked-table td.stacked-block[data-label]::before {
        display: block;
        margin-bottom: var(--space-xs);
    }

    .stacked-table .input {
        flex: 1 1 7rem;
    }

    /* A rejected figure is explained under its input, not squeezed in beside it. */
    .stacked-table .help {
        flex: 1 0 100%;
    }

    /* 44px, the size the header's own buttons already meet, rather than the 24px WCAG 2.5.8 floor. */
    .portfolio-form .input,
    .portfolio-form .button,
    .portfolio-form .select,
    .portfolio-form .select select,
    .portfolio-form .drop > summary {
        min-height: 44px;
    }

    /* Bulma fixes the wrapper at 2.5em, which would clip the taller select inside it. */
    .portfolio-form .select:not(.is-multiple) {
        height: auto;
    }

    /* Bulma pins a grouped control at flex-shrink 0, so the amount field keeps the width of its
       20-character default and leaves the panel. Each one already wraps onto a line of its own here. */
    .portfolio-form .field.is-grouped > .control {
        flex-shrink: 1;
    }

    /* The drop control is a line of text on a wide screen, where a column of its own says what it
       is. On a block it needs an edge to read as a target rather than as a stray word. */
    .portfolio-form .drop > summary {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        min-width: 44px;
        padding-inline: var(--space-md);
        border: 1px solid var(--border-color);
        border-radius: var(--radius-md);
    }
}
