/*
 * Expense Tracker — application styles.
 *
 * Tabler 1.6.1 (vendor/assets/stylesheets/tabler.min.css) does the heavy
 * lifting; this file holds only what it does not provide. To refresh Tabler,
 * see README, "Styling".
 */

/* The app's own blue in place of Tabler's #066fd1. Buttons, links and their
   hover, focus rings and the pale primary shades are all worked out from
   --tblr-primary, in both colour schemes. The selectors are Tabler's own: the
   body carries data-theme as well, and Tabler sets its blue on it again for a
   member on the light scheme, so :root alone would lose there. The same blue
   is the theme colour in app/views/pwa/manifest.json.erb, the layout's
   theme-color meta tag and the icons in public/. */
:root, [data-bs-theme=light], [data-theme=light] {
  --tblr-primary: #3975ed;
  --tblr-primary-rgb: 57, 117, 237;
  /* Tabler ships these three already mixed from its own blue. These are the
     same shades and tints of #3975ed, light scheme first, then dark. */
  --tblr-primary-text-emphasis: light-dark(#172f5f, #88acf4);
  --tblr-primary-bg-subtle: light-dark(#d7e3fb, #0b172f);
  --tblr-primary-border-subtle: light-dark(#b0c8f8, #22468e);
}

/* Money reads better when the digits line up. */
.table, .card-summary { font-variant-numeric: tabular-nums; }

/* Month and year navigation: the current period sits between two buttons. */
.period-nav__current { white-space: nowrap; }

/* The logo in the navbar: a little taller than the 20px menu icons, with a
   stroke of about their weight, nudged off the left edge. */
.navbar-brand-image { height: 1.5rem; padding-left: 5px; }

/* The "filtered to" line above a list. */
.filter { font-size: 0.875rem; }

/* Bank statement import: the drop target is the form itself. Tabler has no
   drop zone of its own. The file input and button underneath it do the same
   job without JavaScript; the Stimulus controller hides them (through the
   hidden attribute, which Tabler's reset honours) once it is in charge. */
.dropzone {
  border: 2px dashed var(--tblr-border-color);
  border-radius: var(--tblr-border-radius-lg);
  padding: 2.5rem 1.5rem;
  text-align: center;
  cursor: pointer;
  transition: border-color 0.15s, background-color 0.15s;
}
.dropzone--over { border-color: var(--tblr-primary); background-color: var(--tblr-primary-lt); }
.dropzone--busy { opacity: 0.6; cursor: progress; }
.dropzone__fallback { display: flex; justify-content: center; align-items: center; gap: 0.5rem; flex-wrap: wrap; }

/* The import preview: one row per shop to be created, each with the form that
   names it, and a line already in the ledger reads dimmer than a new one. */
.shop-list__item { display: flex; flex-wrap: wrap; align-items: start; justify-content: space-between; gap: 1rem; padding: 0.5rem 0; }
.shop-list__item + .shop-list__item { border-top: 1px solid var(--tblr-border-color); }
/* The search narrows the list by hiding rows; the flex display above would
   otherwise win over the hidden attribute. */
.shop-list__item[hidden] { display: none; }
/* The clear button beside the search does that job; the browser's own would
   be a second one inside the field. */
.list-filter__input::-webkit-search-cancel-button { display: none; }

/* Rendered open so the assignment forms are simply on the page without
   JavaScript. The browser's own stylesheet floats a dialog out of the flow,
   which would stack all of them on top of one another; back in flow it is a
   flex item too, so it also needs a full-width basis to wrap onto its own
   line below the row that opens it, rather than squeezing in beside it.
   :not(:modal) keeps this rule off a dialog the modal controller has opened
   with showModal(): an author rule beats the browser's own dialog:modal, so
   without that exclusion the promoted dialog would stay pinned in the flow
   instead of centering itself. */
.shop-list__item > dialog[open]:not(:modal) { position: static; flex-basis: 100%; width: 100%; margin: 0; border: 0; padding: 0; }

/* Once shown with showModal(), the dialog is only a frame; the card inside it
   does the looking. Scoped to :modal so none of this — the width cap above
   all — touches the dialog sitting open and inline on the page without
   JavaScript, which needs to stay full width. */
dialog:modal { max-width: 32rem; width: calc(100% - 2rem); margin: auto; border: 0; padding: 0; background: transparent; }
dialog::backdrop { background: rgba(0, 0, 0, 0.4); }

.row--muted > td { --tblr-table-color-state: var(--tblr-secondary); }

/* A category nobody has chosen yet is the one thing left on the preview still
   asking for a decision, so it is marked as waiting rather than left to read
   like a settled value. Tabler's own warning tokens, so it follows the theme. */
.tag--unassigned { color: var(--tblr-warning); background: var(--tblr-warning-lt); border-color: transparent; }

/* A purchase made with a credit card, in the list of expenses: the card's blue,
   lifted in the dark scheme to stay readable. */
.icon--card { color: light-dark(#0071ce, #4da3ff); }

/* Tabler pins .tag to a 1.5rem height, so a long label wraps onto a second
   line that spills out of the pill. Keep the label on one line and cut the
   overflow with an ellipsis instead. Tabler renders .tag as inline-flex and
   text-overflow does not reach anonymous flex items, so the text sits in a
   .tag__label child (blockified as a flex item) that carries the truncation;
   min-width: 0 lets it shrink below its content width inside the flex row.
   The max-width caps the pill so a long name widens neither pill nor cell,
   and the views put the full name in the title attribute for hover. */
.tag:has(> .tag__label) { max-width: 12rem; }
.tag__label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* A row's category in the list of expenses is a link that filters the list to
   it: it keeps the tag's look, with the text colour instead of the link blue,
   and the border takes the primary colour on hover to show it can be clicked. */
a.tag--filter { color: inherit; text-decoration: none; }
a.tag--filter:hover { border-color: var(--tblr-primary); }

/* Tabler styles .table-sort for list.js's buttons; the sortable headings here
   are links, so they drop the link underline to look the same. */
a.table-sort { text-decoration: none; }

/* On a phone the ledger tables keep only date, name, amount and a "⋯" menu,
   and the amount never wraps, so the desktop cell padding is what would push
   a five-figure amount past the edge. Tighten it below md. */
@media (max-width: 767.98px) {
  .card-table > :not(caption) > * > * { padding-inline: .5rem; }
  .card-table > :not(caption) > * > :first-child { padding-inline-start: 1rem; }
  .card-table > :not(caption) > * > :last-child { padding-inline-end: .75rem; }
}

/* The account switcher under a page title reads as the subtitle it replaces,
   with only the caret to say it opens. */
.bank-account-switch { padding: 0; border: 0; background: none; color: inherit; font: inherit; }

/* Category trend: thirteen bars per category, drawn in plain CSS (no chart
   library). Each bar is a link to that month's expenses; its slot fills the
   column so a thin bar is still easy to hit. The dashed line is the average. */
.trend-chart {
  position: relative;
  display: flex;
  align-items: flex-end;
  gap: 3px;
  height: 7rem;
  border-bottom: 1px solid var(--tblr-border-color);
}
.trend-slot {
  flex: 1 1 0;
  display: flex;
  align-items: flex-end;
  height: 100%;
}
.trend-bar {
  width: 100%;
  min-height: 1px;
  border-radius: 2px 2px 0 0;
  background: var(--tblr-primary);
}
.trend-bar--current { background: var(--tblr-primary-border-subtle); }
.trend-slot:hover .trend-bar, .trend-slot:focus-visible .trend-bar { background: var(--tblr-primary-text-emphasis); }
.trend-average {
  position: absolute;
  left: 0;
  right: 0;
  border-top: 1px dashed var(--tblr-secondary);
  pointer-events: none;
}
.trend-labels {
  display: flex;
  gap: 3px;
  margin-top: 0.25rem;
  font-size: 0.625rem;
  color: var(--tblr-secondary);
}
.trend-labels > span { flex: 1 1 0; text-align: center; overflow: hidden; }

/* The table: each cell shaded by its month against the category's busiest,
   the category column kept in view while the months scroll. */
.trend-table td { background-color: rgba(var(--tblr-primary-rgb), calc(var(--trend-shade, 0) * 0.35)); }
.trend-table th[scope=row] {
  position: sticky;
  left: 0;
  z-index: 1;
  background: var(--tblr-bg-surface);
  white-space: nowrap;
}

/* A loyalty card at the till: the barcode on white whatever the colour
   scheme — a scanner reads dark bars on a light ground — as wide as the
   phone allows, the quiet zone included in the SVG itself. A QR code is
   square and stops growing sooner. */
.loyalty-card-code { background: #fff; padding: 1rem; border-radius: var(--tblr-card-border-radius) var(--tblr-card-border-radius) 0 0; }
.loyalty-card-code svg { display: block; width: 100%; height: auto; max-height: 45vh; }
.loyalty-card-code--square svg { max-width: 20rem; margin: 0 auto; }

/* The number under it, for a cashier to type in when the scan fails. */
.loyalty-card-digits { font-family: var(--tblr-font-monospace); font-size: 1.75rem; letter-spacing: 0.05em; overflow-wrap: anywhere; }

/* The camera while scanning a card: the whole width of a phone. */
.loyalty-card-scanner { width: min(40rem, 100vw - 2rem); padding: 0; border: 0; border-radius: var(--tblr-border-radius-lg); }
.loyalty-card-scanner video { display: block; width: 100%; background: #000; }
