Review finding, and it was right. The PR claimed that runs with impossible readings were "excluded from the averages". That was only true of the attenuation average. Run 12's bad final gravity was still poisoning three other statistics: - **Brew time** - its 56 days were folded into the average, making a typical brew look like 13.7 days when it is 10.9. - **Time to 1.010** - its 56 days did the same to that figure (8.4d). - **The history page's ABV** - an 18.64% ABV, which made run 12 the "strongest" beer in the history and pulled the average up. - **The fermentation curve** - its curve plunges from 1.042 to 0.900, which dragged the median line down and stretched the min-max envelope to cover a gravity below water. One bad reading was reaching four different statistics. What changed ------------ - The suspect runs are computed once and excluded from every aggregate: the attenuation, brew-time and time-to-1.010 averages, the fastest and slowest brews, the history page's ABV average, and its strongest and lightest. - `/api/runs` now carries a `suspect` flag, and the charts skip the flagged runs - the attenuation chart and the fermentation curve. - The suspect run stays in the table, with a "not counted" chip and a tinted row, so it can be corrected. It is excluded from the figures, not hidden. The card header says "16 brews (15 counted)" and the prose says how many brews the figures are based on. Tests ----- 133 (up from 127). New coverage: the suspect run is excluded from the brew time and time-to-1.010 averages, it is still listed for review, it is flagged in the API, it is not the strongest brew in the history, and the charts filter it out. The `logged_in` fixture moved to conftest.py, where both test modules can use it.
692 lines
20 KiB
CSS
692 lines
20 KiB
CSS
/* ==========================================================================
|
|
Brew Log — design system
|
|
Beer-house palette: warm amber/copper on near-black, with a condensed
|
|
display face (Anton) for headings and Inter for everything else.
|
|
|
|
Palette taken from the brewery-128 template:
|
|
--primary #be7f39 (amber / copper)
|
|
--primary-dark #0e0e0e
|
|
--black-primary #212024
|
|
--black-secondary #161616
|
|
--gray #e9e9e9
|
|
--dark-gray #afafaf
|
|
--paragraph-gray #535353
|
|
========================================================================== */
|
|
|
|
/* ------------------------------------------------------------------ fonts */
|
|
|
|
@font-face {
|
|
font-family: 'Anton';
|
|
font-style: normal;
|
|
font-weight: 400;
|
|
font-display: swap;
|
|
src: url('../fonts/anton-latin.woff2') format('woff2');
|
|
}
|
|
|
|
@font-face {
|
|
font-family: 'Inter';
|
|
font-style: normal;
|
|
font-weight: 100 900;
|
|
font-display: swap;
|
|
src: url('../fonts/inter-latin-400.woff2') format('woff2');
|
|
}
|
|
|
|
/* ----------------------------------------------------------------- tokens */
|
|
|
|
:root {
|
|
/* surfaces */
|
|
--bg: #121110;
|
|
--bg-raised: #1c1a19;
|
|
--bg-raised-2: #242120;
|
|
--bg-input: #161514;
|
|
|
|
/* lines */
|
|
--border: #332e2a;
|
|
--border-strong: #4a4239;
|
|
|
|
/* text */
|
|
--text: #f2ede8;
|
|
--text-muted: #afafaf;
|
|
--text-faint: #7d746c;
|
|
|
|
/* brand */
|
|
--amber: #be7f39;
|
|
--amber-bright: #dda05c;
|
|
--amber-deep: #8f5f28;
|
|
--amber-dim: rgba(190, 127, 57, 0.14);
|
|
--amber-text: #e2b47c;
|
|
|
|
/* foam / highlights */
|
|
--foam: #f7f3ee;
|
|
--cream: #eef4f8;
|
|
|
|
/* status */
|
|
--warn: #e8a33d;
|
|
--warn-dim: rgba(232, 163, 61, 0.14);
|
|
--danger: #d9534f;
|
|
--danger-dim: rgba(217, 83, 79, 0.14);
|
|
--success: #7db902;
|
|
--success-dim: rgba(125, 185, 2, 0.14);
|
|
|
|
--radius: 10px;
|
|
--radius-sm: 6px;
|
|
--radius-lg: 16px;
|
|
|
|
--shadow: 0 1px 2px rgba(0, 0, 0, .45), 0 10px 30px rgba(0, 0, 0, .35);
|
|
|
|
--font: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
--display: 'Anton', 'Arial Narrow', Impact, sans-serif;
|
|
--mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
|
|
}
|
|
|
|
* { box-sizing: border-box; }
|
|
|
|
html, body {
|
|
margin: 0;
|
|
padding: 0;
|
|
background: var(--bg);
|
|
color: var(--text);
|
|
font-family: var(--font);
|
|
font-size: 15px;
|
|
line-height: 1.55;
|
|
-webkit-font-smoothing: antialiased;
|
|
}
|
|
|
|
a { color: var(--amber-text); text-decoration: none; }
|
|
a:hover { text-decoration: underline; }
|
|
|
|
h1, h2, h3, h4 { margin: 0 0 .5rem; line-height: 1.15; font-weight: 650; }
|
|
h1 { font-size: 1.65rem; }
|
|
h2 { font-size: 1.25rem; }
|
|
h3 { font-size: 1.02rem; }
|
|
|
|
p { margin: 0 0 .75rem; }
|
|
|
|
/* Display type: the condensed face, used sparingly for headings and figures. */
|
|
.display {
|
|
font-family: var(--display);
|
|
font-weight: 400;
|
|
letter-spacing: .01em;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------- topbar */
|
|
|
|
.topbar {
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 50;
|
|
background: rgba(18, 17, 16, .94);
|
|
backdrop-filter: blur(8px);
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
|
|
.topbar__inner {
|
|
max-width: 1240px;
|
|
margin: 0 auto;
|
|
padding: .6rem 1.25rem;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 1.5rem;
|
|
}
|
|
|
|
.brand {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: .55rem;
|
|
color: var(--text);
|
|
text-decoration: none;
|
|
}
|
|
.brand:hover { text-decoration: none; }
|
|
|
|
.brand__mark {
|
|
font-size: 1.4rem;
|
|
line-height: 1;
|
|
filter: drop-shadow(0 0 10px rgba(190, 127, 57, .35));
|
|
}
|
|
|
|
.brand__text {
|
|
font-family: var(--display);
|
|
font-size: 1.22rem;
|
|
letter-spacing: .04em;
|
|
text-transform: uppercase;
|
|
color: var(--amber-bright);
|
|
}
|
|
|
|
.nav { display: flex; gap: .25rem; flex: 1; }
|
|
|
|
.nav__link {
|
|
padding: .4rem .8rem;
|
|
border-radius: var(--radius-sm);
|
|
color: var(--text-muted);
|
|
font-size: .92rem;
|
|
font-weight: 500;
|
|
text-decoration: none;
|
|
transition: background .12s ease, color .12s ease;
|
|
}
|
|
.nav__link:hover { background: var(--bg-raised-2); color: var(--text); text-decoration: none; }
|
|
.nav__link.is-active {
|
|
background: var(--amber-dim);
|
|
color: var(--amber-text);
|
|
box-shadow: inset 0 -2px 0 var(--amber);
|
|
}
|
|
|
|
.topbar__logout { display: flex; align-items: center; gap: .6rem; }
|
|
.topbar__user {
|
|
color: var(--text-muted);
|
|
font-size: .85rem;
|
|
font-family: var(--mono);
|
|
padding: .15rem .5rem;
|
|
border: 1px solid var(--border);
|
|
border-radius: 999px;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------ page */
|
|
|
|
.page {
|
|
max-width: 1240px;
|
|
margin: 0 auto;
|
|
padding: 1.5rem 1.25rem 4rem;
|
|
}
|
|
.page--narrow { max-width: 460px; }
|
|
|
|
.page__head {
|
|
display: flex;
|
|
align-items: flex-end;
|
|
justify-content: space-between;
|
|
gap: 1rem;
|
|
flex-wrap: wrap;
|
|
margin-bottom: 1.25rem;
|
|
}
|
|
.page__title {
|
|
margin: 0;
|
|
font-family: var(--display);
|
|
font-weight: 400;
|
|
font-size: 1.85rem;
|
|
letter-spacing: .02em;
|
|
text-transform: uppercase;
|
|
color: var(--text);
|
|
}
|
|
.page__sub { color: var(--text-muted); font-size: .9rem; margin: .15rem 0 0; }
|
|
|
|
/* ----------------------------------------------------------------- cards */
|
|
|
|
.card {
|
|
background: var(--bg-raised);
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius);
|
|
box-shadow: var(--shadow);
|
|
position: relative;
|
|
}
|
|
|
|
/* A thin amber seam along the top of each card — the "label on the barrel" touch. */
|
|
.card::before {
|
|
content: '';
|
|
position: absolute;
|
|
inset: 0 0 auto 0;
|
|
height: 2px;
|
|
border-radius: var(--radius) var(--radius) 0 0;
|
|
background: linear-gradient(90deg, var(--amber-deep), var(--amber) 40%, transparent 85%);
|
|
opacity: .8;
|
|
}
|
|
|
|
.card__head {
|
|
padding: .9rem 1.1rem;
|
|
border-bottom: 1px solid var(--border);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 1rem;
|
|
flex-wrap: wrap;
|
|
}
|
|
.card__title {
|
|
margin: 0;
|
|
font-size: 1rem;
|
|
font-weight: 650;
|
|
letter-spacing: .01em;
|
|
}
|
|
.card__hint { color: var(--text-faint); font-size: .82rem; }
|
|
.card__body { padding: 1.1rem; }
|
|
.card__body--flush { padding: 0; }
|
|
|
|
.grid { display: grid; gap: 1rem; }
|
|
.grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
.grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
|
/* The active-beer cards: fill the row whatever the number of brews. */
|
|
.grid--cards { grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); }
|
|
.grid--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
|
|
.grid--sidebar { grid-template-columns: minmax(0, 380px) minmax(0, 1fr); align-items: start; }
|
|
|
|
@media (max-width: 1000px) {
|
|
.grid--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
.grid--3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
.grid--sidebar { grid-template-columns: minmax(0, 1fr); }
|
|
}
|
|
@media (max-width: 640px) {
|
|
.grid--2, .grid--3, .grid--4 { grid-template-columns: minmax(0, 1fr); }
|
|
.topbar__inner { flex-wrap: wrap; gap: .75rem; }
|
|
.nav { order: 3; width: 100%; overflow-x: auto; }
|
|
.page__title { font-size: 1.5rem; }
|
|
}
|
|
|
|
/* ----------------------------------------------------------------- stats */
|
|
|
|
.stat {
|
|
background: var(--bg-raised);
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius);
|
|
padding: .85rem 1rem;
|
|
}
|
|
.stat__label {
|
|
color: var(--text-faint);
|
|
font-size: .72rem;
|
|
text-transform: uppercase;
|
|
letter-spacing: .09em;
|
|
font-weight: 600;
|
|
}
|
|
.stat__value {
|
|
font-family: var(--display);
|
|
font-weight: 400;
|
|
font-size: 1.75rem;
|
|
letter-spacing: .02em;
|
|
line-height: 1.2;
|
|
color: var(--amber-bright);
|
|
}
|
|
.stat__meta { color: var(--text-muted); font-size: .83rem; }
|
|
|
|
/* ----------------------------------------------------------------- forms */
|
|
|
|
.field { margin-bottom: .9rem; }
|
|
.field__label {
|
|
display: block;
|
|
font-size: .74rem;
|
|
font-weight: 600;
|
|
text-transform: uppercase;
|
|
letter-spacing: .09em;
|
|
color: var(--text-muted);
|
|
margin-bottom: .3rem;
|
|
}
|
|
.field__hint { color: var(--text-muted); font-size: .8rem; margin-top: .25rem; }
|
|
.field__error { color: var(--danger); font-size: .8rem; margin-top: .25rem; }
|
|
|
|
input[type="text"], input[type="password"], input[type="number"],
|
|
input[type="date"], input[type="email"], textarea, select {
|
|
width: 100%;
|
|
padding: .5rem .65rem;
|
|
background: var(--bg-input);
|
|
border: 1px solid var(--border-strong);
|
|
border-radius: var(--radius-sm);
|
|
color: var(--text);
|
|
font-family: inherit;
|
|
font-size: .92rem;
|
|
transition: border-color .12s ease, box-shadow .12s ease;
|
|
}
|
|
input:focus, textarea:focus, select:focus {
|
|
outline: none;
|
|
border-color: var(--amber);
|
|
box-shadow: 0 0 0 3px var(--amber-dim);
|
|
}
|
|
input::placeholder, textarea::placeholder { color: var(--text-faint); }
|
|
textarea { resize: vertical; min-height: 5rem; }
|
|
|
|
.checkbox {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: .55rem;
|
|
font-size: .9rem;
|
|
color: var(--text);
|
|
cursor: pointer;
|
|
}
|
|
|
|
/* The native checkbox is a white square on a near-black page — replace it. */
|
|
.checkbox input[type="checkbox"] {
|
|
appearance: none;
|
|
-webkit-appearance: none;
|
|
width: 1.05rem;
|
|
height: 1.05rem;
|
|
margin: 0;
|
|
flex: 0 0 auto;
|
|
border: 1px solid var(--border-strong);
|
|
border-radius: 4px;
|
|
background: var(--bg-input);
|
|
cursor: pointer;
|
|
position: relative;
|
|
transition: background .12s ease, border-color .12s ease;
|
|
}
|
|
.checkbox input[type="checkbox"]:hover { border-color: var(--amber); }
|
|
.checkbox input[type="checkbox"]:checked {
|
|
background: var(--amber);
|
|
border-color: var(--amber);
|
|
}
|
|
.checkbox input[type="checkbox"]:checked::after {
|
|
content: '';
|
|
position: absolute;
|
|
left: .32rem;
|
|
top: .07rem;
|
|
width: .28rem;
|
|
height: .55rem;
|
|
border: solid #17110a;
|
|
border-width: 0 2px 2px 0;
|
|
transform: rotate(43deg);
|
|
}
|
|
.checkbox input[type="checkbox"]:focus-visible {
|
|
outline: none;
|
|
box-shadow: 0 0 0 3px var(--amber-dim);
|
|
}
|
|
|
|
/* Hide the native number spinners — the light spinner widgets jar badly
|
|
against the dark theme. */
|
|
input[type="number"]::-webkit-outer-spin-button,
|
|
input[type="number"]::-webkit-inner-spin-button {
|
|
-webkit-appearance: none;
|
|
margin: 0;
|
|
}
|
|
input[type="number"] { -moz-appearance: textfield; }
|
|
|
|
/* The native date picker's calendar icon is near-black on near-black. */
|
|
input[type="date"]::-webkit-calendar-picker-indicator {
|
|
filter: invert(0.75) sepia(1) saturate(3) hue-rotate(350deg) brightness(1.1);
|
|
cursor: pointer;
|
|
opacity: .7;
|
|
}
|
|
input[type="date"]::-webkit-calendar-picker-indicator:hover { opacity: 1; }
|
|
|
|
/* Dark scrollbars to match the page. */
|
|
* {
|
|
scrollbar-width: thin;
|
|
scrollbar-color: var(--border-strong) var(--bg);
|
|
}
|
|
::-webkit-scrollbar { width: 10px; height: 10px; }
|
|
::-webkit-scrollbar-track { background: var(--bg); }
|
|
::-webkit-scrollbar-thumb {
|
|
background: var(--border-strong);
|
|
border-radius: 6px;
|
|
border: 2px solid var(--bg);
|
|
}
|
|
::-webkit-scrollbar-thumb:hover { background: var(--amber-deep); }
|
|
|
|
/* A figure that should read as a number, not as body text. */
|
|
.strong { font-weight: 650; color: var(--text); }
|
|
|
|
.field-row { display: flex; gap: .75rem; }
|
|
.field-row > * { flex: 1; }
|
|
|
|
/* --------------------------------------------------------------- buttons */
|
|
|
|
.btn {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: .4rem;
|
|
padding: .5rem .95rem;
|
|
border: 1px solid transparent;
|
|
border-radius: var(--radius-sm);
|
|
background: var(--amber);
|
|
color: #17110a;
|
|
font-family: inherit;
|
|
font-size: .85rem;
|
|
font-weight: 650;
|
|
letter-spacing: .01em;
|
|
cursor: pointer;
|
|
text-decoration: none;
|
|
transition: filter .12s ease, background .12s ease, border-color .12s ease;
|
|
}
|
|
.btn:hover { filter: brightness(1.12); text-decoration: none; }
|
|
.btn:disabled { opacity: .5; cursor: not-allowed; }
|
|
|
|
.btn--ghost {
|
|
background: transparent;
|
|
border-color: var(--border-strong);
|
|
color: var(--text-muted);
|
|
}
|
|
.btn--ghost:hover { background: var(--bg-raised-2); color: var(--text); }
|
|
|
|
.btn--danger { background: transparent; border-color: var(--danger); color: var(--danger); }
|
|
.btn--danger:hover { background: var(--danger-dim); }
|
|
|
|
.btn--sm { padding: .3rem .6rem; font-size: .78rem; }
|
|
.btn--block { width: 100%; }
|
|
|
|
.btn-row { display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; }
|
|
|
|
/* ---------------------------------------------------------------- tables */
|
|
|
|
.table-wrap { overflow-x: auto; }
|
|
|
|
table.data {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
font-size: .88rem;
|
|
}
|
|
table.data th {
|
|
text-align: left;
|
|
padding: .55rem .8rem;
|
|
color: var(--text-faint);
|
|
font-size: .7rem;
|
|
font-weight: 650;
|
|
text-transform: uppercase;
|
|
letter-spacing: .08em;
|
|
border-bottom: 1px solid var(--border);
|
|
background: var(--bg-raised-2);
|
|
white-space: nowrap;
|
|
}
|
|
table.data td {
|
|
padding: .6rem .8rem;
|
|
border-bottom: 1px solid var(--border);
|
|
vertical-align: top;
|
|
}
|
|
table.data tr:last-child td { border-bottom: none; }
|
|
table.data tbody tr:hover { background: var(--bg-raised-2); }
|
|
table.data .num { font-family: var(--mono); text-align: right; white-space: nowrap; }
|
|
table.data .comment { color: var(--text-muted); font-size: .84rem; }
|
|
|
|
/* ----------------------------------------------------------------- chips */
|
|
|
|
.chip {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: .3rem;
|
|
padding: .12rem .5rem;
|
|
border-radius: 999px;
|
|
font-size: .74rem;
|
|
font-weight: 600;
|
|
letter-spacing: .02em;
|
|
white-space: nowrap;
|
|
}
|
|
.chip--active { background: var(--amber-dim); color: var(--amber-text); }
|
|
.chip--done { background: rgba(175, 175, 175, .13); color: var(--text-muted); }
|
|
.chip--warn { background: var(--warn-dim); color: var(--warn); }
|
|
.chip--danger { background: var(--danger-dim); color: var(--danger); }
|
|
.chip--good { background: var(--success-dim); color: var(--success); }
|
|
/* A run whose readings are impossible: shown in the table so it can be
|
|
corrected, but marked as not counted in the averages. */
|
|
.data tbody tr.is-suspect td {
|
|
color: var(--text-muted);
|
|
background: rgba(232, 163, 61, .06);
|
|
}
|
|
|
|
.chip--tap {
|
|
background: var(--bg-raised-2);
|
|
border: 1px solid var(--amber-deep);
|
|
color: var(--amber-text);
|
|
font-family: var(--mono);
|
|
}
|
|
|
|
/* --------------------------------------------------------------- flashes */
|
|
|
|
.flashes { display: flex; flex-direction: column; gap: .5rem; margin-bottom: 1rem; }
|
|
|
|
.flash {
|
|
padding: .6rem .9rem;
|
|
border-radius: var(--radius-sm);
|
|
border: 1px solid var(--border);
|
|
background: var(--bg-raised);
|
|
font-size: .9rem;
|
|
}
|
|
.flash--success { border-color: rgba(125, 185, 2, .45); background: var(--success-dim); color: #c3e08a; }
|
|
.flash--error { border-color: rgba(217, 83, 79, .45); background: var(--danger-dim); color: #f0a8a6; }
|
|
.flash--warning { border-color: rgba(232, 163, 61, .45); background: var(--warn-dim); color: #f2c880; }
|
|
.flash--info { border-color: rgba(190, 127, 57, .45); background: var(--amber-dim); color: var(--amber-text); }
|
|
|
|
/* ---------------------------------------------------------------- login */
|
|
|
|
.login {
|
|
min-height: 100vh;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 2rem 1rem;
|
|
background:
|
|
radial-gradient(900px 480px at 50% -10%, rgba(190, 127, 57, .13), transparent 62%),
|
|
var(--bg);
|
|
}
|
|
.login__card {
|
|
width: 100%;
|
|
max-width: 380px;
|
|
background: var(--bg-raised);
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius-lg);
|
|
padding: 2rem;
|
|
box-shadow: var(--shadow);
|
|
position: relative;
|
|
}
|
|
.login__card::before {
|
|
content: '';
|
|
position: absolute;
|
|
inset: 0 0 auto 0;
|
|
height: 3px;
|
|
border-radius: var(--radius-lg) var(--radius-lg) 0 0;
|
|
background: linear-gradient(90deg, var(--amber-deep), var(--amber-bright), var(--amber-deep));
|
|
}
|
|
.login__mark { font-size: 2.4rem; line-height: 1; }
|
|
.login__title {
|
|
margin: .6rem 0 .15rem;
|
|
font-family: var(--display);
|
|
font-weight: 400;
|
|
font-size: 1.9rem;
|
|
letter-spacing: .03em;
|
|
text-transform: uppercase;
|
|
color: var(--amber-bright);
|
|
}
|
|
.login__sub { color: var(--text-muted); font-size: .88rem; margin-bottom: 1.4rem; }
|
|
|
|
/* ----------------------------------------------------------- active card */
|
|
|
|
.brew-card {
|
|
background: var(--bg-raised);
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius);
|
|
padding: 1rem 1.1rem;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: .75rem;
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
.brew-card::before {
|
|
content: '';
|
|
position: absolute;
|
|
inset: 0 0 auto 0;
|
|
height: 2px;
|
|
background: linear-gradient(90deg, var(--amber-deep), var(--amber) 45%, transparent 90%);
|
|
opacity: .75;
|
|
}
|
|
.brew-card__head { display: flex; align-items: flex-start; justify-content: space-between; gap: .75rem; }
|
|
.brew-card__name { font-weight: 650; font-size: 1.02rem; margin: 0; }
|
|
.brew-card__meta { color: var(--text-muted); font-size: .82rem; font-family: var(--mono); }
|
|
.brew-card__stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: .6rem; }
|
|
.brew-card__stat .stat__label { font-size: .68rem; }
|
|
.brew-card__stat .stat__value { font-size: 1.3rem; }
|
|
.brew-card__spark { height: 46px; }
|
|
|
|
/* The "start a new beer" tile — fills the last grid slot and gives the
|
|
page a way to begin a brew without hunting for the form. */
|
|
.brew-card--new {
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: .6rem;
|
|
text-align: center;
|
|
text-decoration: none;
|
|
color: var(--text-muted);
|
|
border-style: dashed;
|
|
border-color: var(--border-strong);
|
|
min-height: 180px;
|
|
}
|
|
.brew-card--new:hover {
|
|
border-color: var(--amber);
|
|
color: var(--text);
|
|
text-decoration: none;
|
|
}
|
|
.brew-card--new::before { display: none; }
|
|
.brew-card__new-mark {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 2.4rem;
|
|
height: 2.4rem;
|
|
border: 1px solid var(--amber);
|
|
border-radius: 50%;
|
|
font-size: 1.35rem;
|
|
line-height: 1;
|
|
color: var(--amber);
|
|
}
|
|
.brew-card__new-text { display: flex; flex-direction: column; gap: .15rem; }
|
|
.brew-card__new-text strong { font-size: .95rem; }
|
|
.brew-card__spark--empty {
|
|
display: flex;
|
|
align-items: center;
|
|
font-style: italic;
|
|
padding: .35rem 0;
|
|
}
|
|
|
|
/* --------------------------------------------------------------- helpers */
|
|
|
|
.muted { color: var(--text-muted); }
|
|
.faint { color: var(--text-faint); }
|
|
.mono { font-family: var(--mono); }
|
|
.nowrap { white-space: nowrap; }
|
|
.small { font-size: .84rem; }
|
|
.tiny { font-size: .76rem; }
|
|
.center { text-align: center; }
|
|
.right { text-align: right; }
|
|
.stack > * + * { margin-top: .75rem; }
|
|
.mt-0 { margin-top: 0; }
|
|
.mb-0 { margin-bottom: 0; }
|
|
.row { display: flex; gap: .75rem; align-items: center; flex-wrap: wrap; }
|
|
.row--between { justify-content: space-between; }
|
|
.hidden { display: none !important; }
|
|
|
|
/* Spacing utilities. These exist so the templates carry no inline
|
|
`style=` attributes, which a strict Content-Security-Policy
|
|
(`style-src 'self'`) would block. */
|
|
.mb-lg { margin-bottom: 1.25rem; }
|
|
.mt-lg { margin-top: 2rem; }
|
|
|
|
.empty {
|
|
padding: 2.5rem 1rem;
|
|
text-align: center;
|
|
color: var(--text-faint);
|
|
}
|
|
.empty__icon { font-size: 2rem; display: block; margin-bottom: .5rem; opacity: .7; }
|
|
|
|
/* --------------------------------------------------------------- reading */
|
|
|
|
.reading-list { list-style: none; margin: 0; padding: 0; }
|
|
.reading-list__item {
|
|
display: flex;
|
|
gap: .75rem;
|
|
align-items: flex-start;
|
|
padding: .6rem 0;
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
.reading-list__item:last-child { border-bottom: none; }
|
|
.reading-list__date {
|
|
font-family: var(--mono);
|
|
font-size: .82rem;
|
|
color: var(--text-muted);
|
|
flex: 0 0 5.5rem;
|
|
}
|
|
.reading-list__sg { font-family: var(--mono); font-weight: 650; flex: 0 0 4rem; }
|
|
.reading-list__body { flex: 1; min-width: 0; }
|
|
.reading-list__comment { color: var(--text-muted); font-size: .84rem; }
|