/* One column, Telegram's colours, nothing loaded from anywhere else.
 *
 * Every colour is a Telegram theme variable with a fallback, because the same page is
 * opened two ways: inside the chat, where tg.js has set the variables from what Telegram
 * handed the window, and in a desktop browser, where nothing sets them and the fallback
 * is the whole palette. The fallbacks follow the system's light or dark setting so the
 * desktop case is not permanently white.
 *
 * No web fonts. The system stack is what the rest of the phone uses, it loads instantly,
 * and it is one fewer thing fetched from outside (ADR 0022).
 */

:root {
  color-scheme: light dark;

  --bg: var(--tg-theme-bg-color, light-dark(#ffffff, #17212b));
  --fg: var(--tg-theme-text-color, light-dark(#000000, #ffffff));
  --hint: var(--tg-theme-hint-color, light-dark(#707579, #708499));
  --link: var(--tg-theme-link-color, light-dark(#3390ec, #6ab3f3));
  --accent: var(--tg-theme-button-color, light-dark(#3390ec, #5288c1));
  --on-accent: var(--tg-theme-button-text-color, #ffffff);
  --panel: var(--tg-theme-secondary-bg-color, light-dark(#f4f4f5, #232e3c));

  --gap: 12px;
  --radius: 12px;
  --tabs-height: 56px;
}

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  padding: 0;
}

body {
  background: var(--bg);
  color: var(--fg);
  font: 16px/1.45 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  /* Telegram's window has a home indicator and a notch to keep clear of. */
  padding: env(safe-area-inset-top) env(safe-area-inset-right) 0 env(safe-area-inset-left);
}

/* Room for the bottom bar, which is fixed and would otherwise cover the last row of
 * whatever is being read — on the day screen, the most recent thing eaten. */
body.framed main {
  padding-bottom: calc(var(--tabs-height) + env(safe-area-inset-bottom) + var(--gap));
}

main {
  max-width: 640px;
  margin: 0 auto;
  padding: var(--gap);
}

h1 {
  font-size: 20px;
  font-weight: 600;
  margin: 0 0 var(--gap);
}

a {
  color: var(--link);
}

p {
  margin: 0 0 var(--gap);
}

.hint {
  color: var(--hint);
  font-size: 14px;
}

.mono {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 13px;
  word-break: break-word;
}

/* The door and the refusals: one short message, vertically out of the way of the
 * keyboard, readable without scrolling on the shortest phone. */
.door {
  min-height: 60vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  text-align: center;
  gap: 4px;
}

/* ------------------------------------------------------------------ the bottom bar */

nav.tabs {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  height: calc(var(--tabs-height) + env(safe-area-inset-bottom));
  padding-bottom: env(safe-area-inset-bottom);
  display: flex;
  background: var(--panel);
  border-top: 1px solid color-mix(in srgb, var(--hint) 25%, transparent);
}

nav.tabs a {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  font-size: 11px;
  text-decoration: none;
  color: var(--hint);
  /* Comfortably past the 44px minimum: this bar is tapped with a thumb, one-handed,
   * and a mis-tap here navigates away from a half-finished correction. */
  min-height: 44px;
}

nav.tabs a.on {
  color: var(--accent);
}

nav.tabs .ico {
  font-size: 18px;
  line-height: 1;
}

/* ---------------------------------------------------------- the confirmation screen */

/* The message the parse was read from, shown above the parse because the screen exists
 * to compare the two. Quoted and set apart so it never reads as something the assistant
 * decided. */
.said {
  font-size: 15px;
  padding: 8px 12px;
  border-radius: var(--radius);
  background: var(--panel);
}

ul.items,
ul.lines,
ul.results,
ul.written,
ul.failed {
  list-style: none;
  margin: 0 0 var(--gap);
  padding: 0;
}

.card {
  background: var(--panel);
  border-radius: var(--radius);
  padding: var(--gap);
  margin-bottom: var(--gap);
}

.card-head {
  display: flex;
  justify-content: space-between;
  font-size: 13px;
  color: var(--hint);
  margin-bottom: 8px;
}

.card-head .kind {
  font-weight: 600;
  color: var(--fg);
}

.card-foot {
  display: flex;
  gap: var(--gap);
  align-items: center;
  margin-top: 8px;
}

/* A whole food line is one tap target. The plan asks for correction to be one gesture
 * away from every number on the screen, and a link around only the name would make the
 * weight — the thing most often wrong — the one part that is not tappable. */
.line a {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 2px 8px;
  padding: 8px 0;
  text-decoration: none;
  color: inherit;
  border-top: 1px solid color-mix(in srgb, var(--hint) 20%, transparent);
}

.line:first-child a {
  border-top: none;
}

.line .name {
  font-weight: 500;
}

.line .amount {
  text-align: right;
  white-space: nowrap;
}

.line .energy,
.line .macros,
.line .count,
.line .brand {
  font-size: 13px;
  color: var(--hint);
}

.line .macros {
  text-align: right;
}

.one {
  margin: 0;
}

.note {
  font-size: 14px;
  color: var(--hint);
  margin: 8px 0 0;
}

/* «Похоже на дубль». Amber rather than red: it is a question, never a refusal — eating
 * the same thing twice in a day is ordinary and looks identical from here. */
.badge {
  display: inline-block;
  font-size: 12px;
  padding: 2px 8px;
  border-radius: 999px;
  background: color-mix(in srgb, #e8a33d 30%, transparent);
  color: var(--fg);
}

/* ------------------------------------------------------------------- the day screen */

/* The totals. Big enough to be read at arm's length, because on most openings this is
 * the only thing the owner came to look at. */
.bar {
  background: var(--panel);
  border-radius: var(--radius);
  padding: var(--gap);
  margin-bottom: var(--gap);
  text-align: center;
}

.bar .total {
  font-size: 26px;
  font-weight: 600;
  margin: 0;
}

.bar .macros {
  color: var(--hint);
  font-size: 14px;
  margin: 4px 0 0;
}

.bar .hint {
  margin: 4px 0 0;
}

.dates {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: var(--gap);
}

.dates input[type="date"] {
  flex: 1;
  min-width: 0;
}

.dates .step {
  min-width: 44px;
  min-height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  text-decoration: none;
  border-radius: 8px;
  background: var(--panel);
}

.dates .step.disabled {
  color: var(--hint);
  opacity: 0.5;
}

/* An unconfirmed draft. Pale rather than merely labelled: it has to be legible as "not
 * counted" at a glance, from a screen the owner is scrolling past. */
.card.waiting a {
  display: block;
  text-decoration: none;
  color: var(--hint);
}

.card.waiting .kind {
  display: block;
  font-size: 13px;
}

.card.waiting .name {
  display: block;
  color: var(--fg);
  opacity: 0.7;
}

/* A row nobody has agreed to yet, and the one tap that agrees to it (ADR 0026). The mark
 * borrows the amber already used for a message waiting on the owner, because it is the
 * same state: something is owed an answer. «Верно» is quiet rather than prominent — it
 * appears on a handful of rows and must not out-shout the entry itself. */
form.ok {
  margin: 0;
}

button.quiet.ok {
  color: #1a7f37;
}

/* Deletion, compact, inside a card that already has a border of its own. */
details.rm {
  background: none;
  padding: 0;
  margin: 0;
}

details.rm summary {
  color: var(--hint);
  font-size: 14px;
  padding: 10px 0;
}

details[open].rm summary {
  margin-bottom: 8px;
}

button.danger {
  background: #d1453b;
}

fieldset.scopes {
  border: none;
  padding: 0;
  margin: 0 0 var(--gap);
}

fieldset.scopes legend {
  padding: 0;
  font-size: 13px;
  color: var(--hint);
  margin-bottom: 4px;
}

/* A radio is a control the size of a word, not a full-width box like every other input
 * on these pages, so it opts out of the rule that makes weights easy to tap. */
label.radio {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0 8px;
  align-items: baseline;
  margin-bottom: 8px;
}

label.radio input {
  width: auto;
  min-height: 0;
  grid-row: span 2;
  align-self: center;
}

label.radio .hint {
  grid-column: 2;
}

/* ------------------------------------------------------------------ the week screen */

.dates .span {
  flex: 1;
  text-align: center;
  font-size: 14px;
  color: var(--hint);
}

/* One line per day, and the line is the tap target for the whole day behind it. */
.lines.week .when {
  font-weight: 500;
}

/* Which numbers came from the nightly summary rather than from the log. Quiet, because
 * it is a caveat on a figure and not a figure of its own. */
.lines.week .rolled {
  font-weight: 400;
  font-size: 12px;
  color: var(--hint);
}

.lines.week .figures {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 2px 10px;
  font-size: 14px;
  color: var(--hint);
}

/* The trend. Stretched to the width of the column rather than drawn to scale: what is
 * being read is the shape, and the numbers that make it are written underneath. */
svg.trend {
  display: block;
  width: 100%;
  height: 120px;
  margin-bottom: var(--gap);
  color: var(--accent);
}

.chip.on {
  background: var(--accent);
  color: var(--on-accent);
}

/* A row that is not a link: same shape as `.line a`, without the tap target, for lists
 * where there is nothing underneath to open. */
.line.flat {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 2px 8px;
  padding: 8px 0;
  border-top: 1px solid color-mix(in srgb, var(--hint) 20%, transparent);
}

.line.flat:first-child {
  border-top: none;
}

/* ------------------------------------------------------------ the catalogue screen */

h2 {
  font-size: 15px;
  font-weight: 600;
  color: var(--hint);
  margin: var(--gap) 0 4px;
}

/* The arithmetic verdict, in the same slot the day screen puts its totals — it is the
 * number the card is judged by. Tinted rather than shouted: it is a discrepancy to look
 * into, and a red panel on a page of boxes reads as a failure to save. */
.bar.wrong {
  background: color-mix(in srgb, #d1453b 18%, var(--panel));
}

.bar .checks {
  list-style: none;
  margin: 8px 0 0;
  padding: 0;
  font-size: 14px;
  text-align: left;
}

.bar .checks li.bad {
  color: #d1453b;
}

/* A portion is a name and a weight side by side, then its two buttons. */
.line.flat form {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
}

.line.flat form .pair {
  flex: 1 1 60%;
}

/* ------------------------------------------------------------ the messages screen */

/* The state of one message, in the corner of its card. Coloured only where the colour
 * means something: waiting on the owner, or failed. «записано» is the ordinary outcome
 * and a green tick on nine cards out of ten is a page nobody reads.
 *
 * `.mark.waiting` is shared with the day screen, where it marks a row nobody has agreed
 * to yet. One amber for one meaning — something is owed an answer — on purpose. */
.card-head .mark.waiting {
  color: #e8a33d;
}

.card-head .mark.bad {
  color: #d1453b;
}

/* What a message became: the kind, then the row, then — only when it is true — how long
 * after the message the row appeared. */
.written .became {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0 8px;
  font-size: 14px;
}

.written .kind {
  color: var(--hint);
  font-size: 13px;
}

.written .late {
  grid-column: 2;
  font-size: 13px;
  color: #e8a33d;
}

/* What the assistant said, indented and quieter than what the owner said, so the two
 * directions are tellable apart while scrolling past without reading either. */
.card.out {
  margin-left: var(--gap);
  background: none;
  border-left: 2px solid color-mix(in srgb, var(--hint) 30%, transparent);
  border-radius: 0;
  padding: 8px 0 8px var(--gap);
  color: var(--hint);
  font-size: 15px;
}

/* A date heading over the timeline, and the tap target for that whole day. */
h2 a {
  text-decoration: none;
}

/* ---------------------------------------------------------------- forms and buttons */

label {
  display: block;
  margin-bottom: var(--gap);
}

.lbl {
  display: block;
  font-size: 13px;
  color: var(--hint);
  margin-bottom: 4px;
}

input,
select,
textarea {
  width: 100%;
  font: inherit;
  color: var(--fg);
  background: var(--bg);
  border: 1px solid color-mix(in srgb, var(--hint) 40%, transparent);
  border-radius: 8px;
  /* 16px keeps iOS from zooming the page when a box is focused, which on a form this
   * long leaves the owner scrolled somewhere they did not ask to be. */
  font-size: 16px;
  padding: 10px;
  min-height: 44px;
}

.pair {
  display: flex;
  gap: 8px;
  align-items: center;
}

.pair .unit {
  color: var(--hint);
}

button {
  font: inherit;
  font-size: 16px;
  min-height: 44px;
  padding: 10px 16px;
  border: none;
  border-radius: 8px;
  background: var(--accent);
  color: var(--on-accent);
  cursor: pointer;
}

/* «Всё верно» — the plan asks for it large and always in sight. */
.big {
  display: block;
  width: 100%;
  text-align: center;
  font-weight: 600;
  text-decoration: none;
  background: var(--accent);
  color: var(--on-accent);
  border-radius: var(--radius);
  padding: 14px;
}

/* Everything that is not the main move: readable, reachable, and visibly not the button
 * being offered. «Отклонить» throws away what was said, so it must not be next to
 * «Всё верно» in weight as well as in position. */
.quiet {
  background: none;
  color: var(--hint);
  padding: 10px 0;
}

.actions {
  position: sticky;
  bottom: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: var(--gap) 0 calc(var(--gap) + env(safe-area-inset-bottom));
  background: linear-gradient(to top, var(--bg) 70%, transparent);
}

.actions form {
  width: 100%;
}

.chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: var(--gap);
}

.chip {
  min-height: 36px;
  padding: 6px 12px;
  font-size: 14px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--accent) 18%, transparent);
  color: var(--fg);
}

details {
  background: var(--panel);
  border-radius: var(--radius);
  padding: var(--gap);
  margin-bottom: var(--gap);
}

summary {
  cursor: pointer;
  min-height: 24px;
}

details[open] summary {
  margin-bottom: var(--gap);
}

/* A search result is a button, because choosing it is the whole point of showing it. */
.result {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 2px 8px;
  width: 100%;
  text-align: left;
  background: var(--panel);
  color: var(--fg);
  margin-bottom: 8px;
}

.search form {
  display: flex;
  gap: 8px;
  margin-bottom: var(--gap);
}

.search form button {
  flex: none;
}

.written li,
.failed li {
  padding: 6px 0;
}

.failed li {
  color: #d1453b;
}

/* The scanner. Wide and short, because a barcode is: a 4:3 preview of the same width
 * puts the packet further away to fit it in, and the bars are what need the pixels. Black
 * while empty, so a camera that never opens looks like a camera that never opened. */
.scanner video {
  width: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  background: #000;
  border-radius: var(--radius);
}

.scanner p {
  margin: 8px 0 0;
  min-height: 1.2em; /* holds its place, so the layout does not jump on the first word */
}
