Files
beer_data/src/flask/static/css/app.css
T
hermes 2600622af8 fix(ui): dimmed backdrop and a bigger close button on the advice panel
The panel read as floating in the page rather than sitting over it, and
the close button was a 26px target - too small to hit reliably on a
phone.

The backdrop is a large spread shadow rather than a pseudo element,
which would paint behind the panel's own background.

Also: Escape closes the panel, clicking away closes it, and the button
is a real button so it works with a keyboard.
2026-11-09 21:14:33 +10:00

1020 lines
28 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; }
/* On a narrow screen the tables reflow into stacked cards.
A seven-column table does not fit a phone. It was left to scroll
sideways inside its wrapper, which meant the columns on the right were
off the screen and the brewer had to pan the table to see them.
Each row becomes a card: the header cell becomes a label above its
value, and the cells stack. */
@media (max-width: 720px) {
/* The header wraps onto two rows on a phone: the brand and the
sign-out on the first, the nav tabs on the second where they get
the full width and scroll sideways if they still do not fit.
They used to sit on one row with the brand and the sign-out, and
the last tab was cut off mid-word. */
.topbar__inner {
flex-wrap: wrap;
row-gap: .5rem;
}
.brand {
order: 1;
}
.topbar__logout {
order: 2;
margin-left: auto;
}
.nav {
order: 3;
flex: 1 0 100%;
overflow-x: auto;
scrollbar-width: none;
-webkit-overflow-scrolling: touch;
/* A fade at the right edge so a clipped tab reads as
scrollable rather than broken. */
-webkit-mask-image: linear-gradient(to right, #000 calc(100% - 1.5rem), transparent 100%);
mask-image: linear-gradient(to right, #000 calc(100% - 1.5rem), transparent 100%);
}
.nav::-webkit-scrollbar {
display: none;
}
.nav__link {
flex-shrink: 0;
}
/* Tighter tabs, so all four fit on a phone without scrolling.
They still scroll if they have to. */
.nav__link {
padding: .35rem .55rem;
font-size: .8rem;
letter-spacing: .01em;
}
.table-wrap {
overflow-x: visible;
}
table.data,
table.data tbody,
table.data tr,
table.data td {
display: block;
width: auto;
}
table.data thead {
/* HIDDEN in the stacked layout. Each cell shows its column
name as a label, so the header row would be a duplicate - and
it was 230px of the first card. */
display: none;
}
table.data tr {
border: 1px solid var(--border);
border-radius: 8px;
margin-bottom: .75rem;
padding: .25rem .1rem;
background: var(--bg-raised-2);
}
table.data tbody tr:hover {
background: var(--bg-raised-2);
}
table.data th {
display: block;
border-bottom: 1px solid var(--border);
}
table.data td {
display: grid;
grid-template-columns: 8.5rem 1fr;
gap: .5rem;
align-items: baseline;
padding: .45rem .7rem;
border-bottom: 1px solid rgba(255, 255, 255, .04);
text-align: left;
}
table.data td::before {
/* The column heading, shown as the row's label. */
content: attr(data-label);
font-size: .65rem;
font-weight: 650;
letter-spacing: .07em;
text-transform: uppercase;
color: var(--text-faint);
}
table.data td.num {
text-align: left;
}
table.data td.right,
table.data td:empty {
grid-template-columns: 1fr;
}
table.data td.right::before {
content: none;
}
/* A cell with nothing in it must not leave a gap. */
table.data td:empty {
display: none;
}
}
/* ----------------------------------------------------------------- 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; }
/* The three stats sit side by side, so their captions line up. One
caption ("needs 2nd reading") is two lines and the others are one,
which left the baselines ragged. */
.brew-card__stat {
display: flex;
flex-direction: column;
}
.brew-card__stat .stat__label { font-size: .68rem; }
.brew-card__stat .stat__value { font-size: 1.3rem; }
.brew-card__stat .stat__meta { margin-top: auto; }
.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; }
/* ------------------------------------------------------------- advice */
/* The LLM's advice. It sits next to the brewer's own comment, never
replaces it, and is visually distinct so the two are never confused. */
.advice {
display: block;
margin-top: .35rem;
padding: .5rem .65rem;
border-left: 2px solid var(--amber);
border-radius: 0 4px 4px 0;
background: var(--amber-dim);
color: var(--text);
font-size: .8125rem;
line-height: 1.5;
}
.advice__label {
display: block;
margin-bottom: .15rem;
font-size: .625rem;
font-weight: 600;
letter-spacing: .08em;
text-transform: uppercase;
color: var(--amber-text);
}
/* The advice on the active-beers card and the run detail page - the
full text, not the clamped table cell. */
.advice--reading {
margin-top: .5rem;
}
/* Advice that could not be generated - it says why. */
.advice--failed {
border-left-color: var(--danger);
background: var(--danger-dim);
color: var(--text-muted);
}
.advice--failed .advice__label {
color: var(--danger);
}
/* The advice in the table is a compact trigger that pops the full text
out on hover, focus or tap.
It used to be the text itself, clamped to four lines. The advice runs
to several hundred characters, and a cell that wide pushes the table
past the width of a phone, so the column ended up clipped off the
right edge and unreadable without sideways scrolling. */
.advice-cell {
position: relative;
width: 1%;
white-space: nowrap;
}
.advice-trigger {
display: inline-flex;
align-items: center;
gap: .35rem;
padding: .25rem .55rem;
border: 1px solid var(--amber);
border-radius: 999px;
background: var(--amber-dim);
color: var(--amber-text);
font: inherit;
font-size: .75rem;
line-height: 1.4;
cursor: pointer;
white-space: nowrap;
}
.advice-trigger:hover,
.advice-trigger:focus-visible {
background: var(--amber);
color: var(--bg);
}
.advice-trigger--failed {
border-color: var(--danger);
background: var(--danger-dim);
color: var(--danger);
}
.advice-trigger__icon {
font-size: .85em;
}
/* The pop-out.
It is `position: fixed` and anchored to the trigger by the script,
not absolutely positioned inside the cell. The table is a scroll
container, and an absolutely positioned panel inside it was clipped
by the container's own overflow - the advice was sliced in half by
the card's edge and the bottom border was cut off.
Fixed positioning takes it out of the table entirely, so it floats
above the page and is never cut by a scroll container. */
.advice-pop {
position: fixed;
z-index: 200;
display: none;
width: min(30rem, 92vw);
max-height: 70vh;
overflow-y: auto;
padding: .8rem .9rem;
border: 1px solid var(--amber);
border-radius: 8px;
background: var(--bg-raised);
box-shadow: 0 12px 32px rgba(0, 0, 0, .55);
text-align: left;
white-space: normal;
}
.advice-pop__head {
display: flex;
align-items: center;
justify-content: space-between;
gap: .5rem;
margin-bottom: .4rem;
}
.advice-pop__head .advice__label {
margin-bottom: 0;
}
.advice-pop__close {
border: 0;
background: none;
color: var(--text-muted);
font-size: 1.1rem;
line-height: 1;
cursor: pointer;
/* A 44px touch target, which is the smallest that is comfortable
on a phone. The negative margin pulls the panel's padding back
in so the button does not push the layout around, but the
padding is still what makes it easy to hit. */
padding: .9rem;
margin: -.9rem -.9rem -.9rem 0;
}
.advice-pop__close:hover {
color: var(--text);
}
.advice-pop__text {
margin: 0;
font-size: .8125rem;
line-height: 1.55;
color: var(--text);
}
/* The script opens and closes the panel, and places it. The CSS only
decides whether it is shown. */
.advice-pop.is-open {
display: block;
}
/* On a phone there is no hover, so the trigger is a button the script
toggles, and the panel is pinned to the middle of the screen so it is
readable whatever the table's width. */
@media (max-width: 720px) {
.advice-cell {
width: auto;
}
/* A dimmed backdrop, so the panel reads as sitting over the page
rather than floating in it.
A large spread shadow is used rather than a pseudo element, which
would paint behind the panel's own background. */
.advice-pop {
box-shadow: 0 0 0 100vmax rgba(0, 0, 0, .55),
0 12px 32px rgba(0, 0, 0, .55);
}
/* On a phone the panel is centred on the screen rather than
anchored to the trigger - there is not the room to float it
beside the cell, and the trigger may be near the edge. */
.advice-pop {
top: 50% !important;
left: 50% !important;
transform: translate(-50%, -50%);
}
}