/*
 * The AMAROV design system, as read from the 22 approved references in
 * docs/design-reference/. That document records the visual language in words;
 * this file is the same statement in CSS, and the two are meant to be checked
 * against each other.
 *
 * Mobile-first: the base rules are the phone, and the two breakpoints add the
 * tablet grid and then the desktop sidebar. The product is used on a phone
 * (docs/PRODUCT_REQUIREMENTS.md section 4), so the phone is not the fallback.
 *
 * No framework, no build step, no remote font (DEC-0037). The Content Security
 * Policy permits no remote origin, so everything here is self-hosted or a
 * system font, and it stays that way.
 */

/* ---------------------------------------------------------------- tokens */
:root {
  /* Surfaces */
  --navy: #0d1b2a;
  --navy-soft: #16293d;
  --bg: #f4f6fb;
  --card: #ffffff;
  --border: #e4e9f2;
  --border-strong: #cfd8e6;

  /* Text */
  --text: #0f172a;
  --muted: #64748b;
  --muted-soft: #94a3b8;
  --on-navy: #e2e8f0;
  --on-navy-muted: #94a3b8;

  /* Status. Amber waits on a person, green is healthy, red is danger,
     blue is informational or automatic. */
  --blue: #0b63e5;
  --blue-dark: #0a4fbb;
  --blue-soft: #eaf1fe;
  --green: #16a34a;
  --green-dark: #128040;
  --green-soft: #e8f7ee;
  --red: #d92d20;
  --red-dark: #b42318;
  --red-soft: #fdeceb;
  --amber: #b45309;
  --amber-soft: #fff4e5;

  /* Shape */
  --r-sm: 8px;
  --r: 12px;
  --r-lg: 16px;
  --r-pill: 999px;

  --shadow: 0 1px 2px rgba(15, 23, 42, .04), 0 4px 12px rgba(15, 23, 42, .05);
  --shadow-lift: 0 6px 24px rgba(15, 23, 42, .10);

  /* Rhythm */
  --gap: 16px;
  --pad: 20px;

  --sidebar: 260px;
  --topbar: 64px;
  --tabbar: 62px;

  --font: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue",
    Arial, "Noto Sans", sans-serif;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
}

*, *::before, *::after { box-sizing: border-box; }

/* `hidden` means hidden, whatever a class further down this file says.
 *
 * The user-agent rule is `[hidden] { display: none }` with **no
 * `!important`**, so any author rule that sets `display` beats it — silently,
 * because nothing in the page reports it and the element simply stays on the
 * screen. Three rules here did exactly that, and every one of them reached an
 * Owner in the first preview session:
 *
 *   * `.field` — the confirmation dialog's reason box is a `.field` that is
 *     hidden unless the action needs a reason, so **the sign-out dialog asked
 *     "are you sure?" over an unexplained empty text box** (`ui.js` —
 *     `confirm()`);
 *   * `.stack` — every connector's setup panel on *Integrációk* is built
 *     hidden and revealed by *Beállítás →*, so all of them were open at once
 *     and the button's label contradicted what was on the screen;
 *   * `.tabbar__badge` — a phone tab's badge is hidden when its count is zero,
 *     so every tab carried an empty amber dot suggesting something was waiting.
 *
 * The alternative to this rule is remembering never to set `display` on any
 * class that is ever hidden, which is not something a reviewer can check.
 * `scripts/check_frontend.py` keeps this declaration here and
 * `tests/browser/test_hidden.py` asserts the behaviour in a browser. */
[hidden] { display: none !important; }

html, body {
  margin: 0;
  padding: 0;
  min-height: 100%;
}

body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: 16px;
  line-height: 1.5;
  -webkit-text-size-adjust: 100%;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 { margin: 0; font-weight: 700; letter-spacing: -0.01em; }
p { margin: 0; }
button, input, textarea, select { font: inherit; color: inherit; }

a { color: var(--blue); text-decoration: none; font-weight: 600; }
a:hover { text-decoration: underline; }

:focus-visible {
  outline: 3px solid var(--blue);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  background: var(--card);
  padding: 12px 16px;
  border-radius: 0 0 var(--r) 0;
}
.skip-link:focus { left: 0; }

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* ---------------------------------------------------------------- boot */
.boot {
  min-height: 100dvh;
  display: grid;
  place-content: center;
  justify-items: center;
  gap: 16px;
  color: var(--muted);
}

.spinner {
  width: 28px; height: 28px;
  border: 3px solid var(--border-strong);
  border-top-color: var(--blue);
  border-radius: 50%;
  animation: spin .8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

.noscript {
  max-width: 40rem;
  margin: 15vh auto;
  padding: var(--pad);
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
}
.noscript h1 { margin-bottom: 8px; }

/* ================================================================ SIGN IN
   AMAROV_Login_Desktop_v1.png / AMAROV_Login_Mobile_v2.png: a photographic
   panel carrying the company logo on a white card, and a form beside it on a
   desktop, below it on a phone. The photograph is `brand/telep.png`, the
   Owner's own picture of the company's container-lorry fleet, supplied in
   NIGHT-010; before that this panel was the flat brand gradient, which the
   comment here called a stated placeholder rather than a design. */
.signin {
  min-height: 100dvh;
  display: grid;
  grid-template-rows: auto 1fr;
}

/* Three layers, bottom to top: the photograph, the brand gradient over it at
   partial opacity so the panel still reads as this product's colour rather
   than as a picture with words on it, and a scrim that darkens towards the
   bottom edge.

   The scrim is not decoration. The title and the tagline sit at the *bottom*
   of this panel (`align-content: end`), the photograph's own bottom is gravel
   in shade and its top is a bright sky, and a person's phone can be at any
   height in the crop. Without the bottom-up scrim the contrast of white text
   on this image is a property of the photograph, which is a file somebody may
   replace. With it, the text is over ~0.92 alpha of near-black wherever the
   crop lands, so the contrast is a property of the stylesheet.

   `background-color` is under all of it, so the panel is the brand navy — not
   white, and not transparent — while the image is still arriving or if it ever
   404s. `scripts/check_frontend.py` rule 7 fails the build if the file named
   here is not committed, which is the check that was missing the last time
   this panel referred to a photograph that did not exist. */
.signin__brand {
  background-color: #0d1b2a;
  background-image:
    linear-gradient(to top, rgba(6, 15, 25, .92) 0%, rgba(6, 15, 25, .55) 45%, rgba(6, 15, 25, .22) 100%),
    linear-gradient(155deg, rgba(13, 27, 42, .55) 0%, rgba(20, 48, 74, .35) 55%, rgba(27, 69, 104, .32) 100%),
    url("/brand/telep.png");
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  color: #fff;
  padding: 32px var(--pad) 40px;
  display: grid;
  align-content: end;
  gap: 6px;
  min-height: 34vh;
}

/* The white card the logo sits on. The lockup is blue and green artwork on
   white with no transparency of its own, so it needs a light surface under it
   — over the photograph directly it would carry its own white rectangle
   anyway, with a hard edge instead of a rounded one. */
.signin__mark {
  background: #fff;
  border-radius: var(--r);
  padding: 14px 18px;
  justify-self: start;
  margin-bottom: 20px;
  box-shadow: var(--shadow-lift);
  max-width: min(100%, 24rem);
}

.signin__title { font-size: 1.75rem; }
.signin__tagline { color: #cbd5e1; }

.signin__panel {
  background: var(--bg);
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  margin-top: -16px;
  padding: 32px var(--pad) 40px;
  display: grid;
  align-content: start;
  gap: var(--gap);
}

.signin__form { display: grid; gap: var(--gap); max-width: 30rem; width: 100%; }
.signin__heading { font-size: 1.6rem; }
.signin__sub { color: var(--muted); margin-top: 4px; }
.signin__links { display: flex; justify-content: flex-end; }
.signin__password { display: grid; gap: var(--gap); }

/* "or" between the provider button and the password form. A rule with a word
   on it, drawn with borders rather than an image, like everything else here. */
.signin__divider {
  display: flex;
  align-items: center;
  gap: 12px;
  color: var(--muted-soft);
  font-size: .9rem;
}
.signin__divider::before,
.signin__divider::after {
  content: "";
  flex: 1;
  border-top: 1px solid var(--border);
}

.signin__note {
  background: var(--blue-soft);
  border-radius: var(--r);
  padding: 16px;
  max-width: 30rem;
}
.signin__note strong { color: var(--blue-dark); display: block; margin-bottom: 4px; }
.signin__note p { color: var(--muted); font-size: .95rem; }
.signin__footer { color: var(--muted-soft); font-size: .85rem; text-align: center; }

/* ---- the company logo ------------------------------------------------
   `brand/logo.jpg`, the Owner's own lockup. One file drawn at two sizes: the
   login card and the phone top bar.

   `height: auto` with only a width set means the drawn shape comes from the
   file's own aspect ratio, not from a number written here — so a re-cut logo
   replaces this one by dropping in a file, and cannot be stretched by doing
   so. `<img>` also carries the file's intrinsic `width`/`height` as
   attributes, which is what reserves the box before the bytes arrive.

   The caps are limits on *sharpness*, not on taste. The supplied file is
   1000px wide, so 500 CSS pixels is exactly 2x and every cap here — 320 on a
   phone, 420 on the desktop login card, 112 in the phone top bar — stays under
   it, which is the point. The desktop reference draws this lockup at roughly
   670 CSS pixels, which this file cannot fill sharply; that gap, and the one
   file that would close it, are recorded in design-reference/README.md
   section 3. */
.logo {
  display: block;
  width: 100%;
  max-width: 320px;
  height: auto;
}
/* The mobile reference draws the lockup at about 110 CSS pixels here, which at
   this file's ratio is 42px tall inside a 64px bar. Wider would crowd a
   390px-wide row that also has to hold *Kilépés*. */
.topbar__mark .logo { max-width: 112px; }

/* The desktop sidebar's white card. 260px of sidebar less its 16px of padding
   and the card's own 16px leaves 196 CSS pixels, so the cap below is what the
   lockup is actually drawn at — five times under this file's 1000px raster, and
   sharp on any display. Centred, because the card is wider than the cap on no
   screen and equal to it on every one; the rule is there so a re-cut logo with
   a different ratio still sits in the middle of the card rather than against
   its left edge. The height comes from the file's own attributes and
   `height: auto`, so nothing shifts when the image arrives (DEC-0156). */
.sidebar__mark .logo { max-width: 196px; margin: 0 auto; }

/* ================================================================ SHELL */
.shell {
  min-height: 100dvh;
  display: grid;
  grid-template-rows: var(--topbar) 1fr auto;
  /* `minmax(0, 1fr)` rather than the implicit `auto` column. A grid item's
     minimum width is its *min-content* by default, so one child that cannot
     shrink — three KPI tiles with long labels, a header with a long page title
     — widens the column, and with it the whole page. The result is a phone
     that scrolls sideways, which is invisible in a screenshot of the top of a
     screen and immediately obvious to somebody holding one.
     `tests/browser/test_navigation.py` measures it at both reference widths. */
  grid-template-columns: minmax(0, 1fr);
}

/* -- the phone top bar: logo left, sign out right ---------------------- */
.topbar {
  grid-row: 1;
  background: var(--card);
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 0 var(--pad);
  position: sticky;
  top: 0;
  z-index: 20;
  min-width: 0;
}
/* On a phone the top bar is the logo and *Kilépés*, and the screen's own
   heading carries the title (design-reference/README.md section 2). Showing
   both put a third item in a 390px-wide flex row, which is what pushed the
   header past the viewport. The desktop rule below is the mirror image: there
   the header carries the title and `.page__head` is hidden. */
.topbar__title { display: none; }
.topbar__mark { min-width: 0; }
.topbar__who { display: none; color: var(--muted); font-weight: 600; }
.topbar__right { display: flex; align-items: center; gap: 12px; min-width: 0; }

/* -- the desktop sidebar ---------------------------------------------- */
.sidebar { display: none; }

/* -- the content column ----------------------------------------------- */
.main {
  grid-row: 2;
  padding: var(--pad) var(--pad) calc(var(--tabbar) + 28px);
  display: grid;
  gap: var(--gap);
  align-content: start;
}

.page__head { display: grid; gap: 2px; margin-bottom: 4px; }
.page__title { font-size: 1.9rem; }
.page__sub { color: var(--muted); }

/* -- the phone tab bar ------------------------------------------------- */
.tabbar {
  grid-row: 3;
  position: fixed;
  bottom: 0; left: 0; right: 0;
  height: calc(var(--tabbar) + env(safe-area-inset-bottom));
  padding-bottom: env(safe-area-inset-bottom);
  background: var(--card);
  border-top: 1px solid var(--border);
  display: flex;
  z-index: 20;
}
.tabbar__item {
  flex: 1;
  display: grid;
  place-content: center;
  gap: 2px;
  font-size: .8rem;
  font-weight: 600;
  color: var(--muted);
  text-decoration: none;
  position: relative;
}
.tabbar__item:hover { text-decoration: none; }
.tabbar__item[aria-current="page"] { color: var(--blue); }
.tabbar__badge {
  position: absolute;
  top: 6px;
  left: calc(50% + 12px);
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: var(--r-pill);
  background: var(--amber);
  color: #fff;
  font-size: .7rem;
  font-weight: 700;
  display: grid;
  place-content: center;
}

/* -- the "Menü" sheet -------------------------------------------------- */
.sheet {
  position: fixed;
  inset: 0;
  z-index: 40;
  background: rgba(15, 23, 42, .45);
  display: grid;
  align-items: end;
}
.sheet__panel {
  background: var(--card);
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  padding: var(--pad) var(--pad) calc(var(--pad) + env(safe-area-inset-bottom));
  display: grid;
  gap: 4px;
  max-height: 80dvh;
  overflow: auto;
}
.sheet__title { color: var(--muted); font-size: .85rem; text-transform: uppercase;
  letter-spacing: .06em; margin-bottom: 8px; }
.sheet__item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 4px;
  border-bottom: 1px solid var(--border);
  color: var(--text);
  font-weight: 600;
  text-decoration: none;
}
.sheet__item:hover { text-decoration: none; }
.sheet__item[aria-current="page"] { color: var(--blue); }

/* ================================================================ CARDS */
.card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow);
  padding: var(--pad);
  display: grid;
  gap: 12px;
  align-content: start;
}
.card--flush { padding: 0; overflow: hidden; }
/* A last line of defence for every card and panel: a value longer than the
   card breaks rather than widening the page under it. `break-word` rather than
   `anywhere`, because prose should still break between words where it can. */
.card, .panel { overflow-wrap: break-word; }
/* And the line that makes the one above work at all.

   A card is a grid, a panel is a grid, and **a grid item's minimum width is its
   own min-content unless something says otherwise**. So a child that cannot
   shrink — a two-column button row, a key/value line, a segmented control —
   sizes its track to its widest indivisible content and then draws *past the
   card's padding box*, because `.card` has no `overflow: hidden` to clip it.
   The visible result is what the Owner photographed: a button hanging over the
   edge of the card it belongs to, and a panel whose text runs under the card
   beside it.

   `overflow-wrap` cannot fix that on its own: it decides where a line may
   break, and it does not change what a track's minimum width is. This does, for
   every card and every panel in the application at once, which is the point —
   the same defect was in four screens and was fixed in none of them. */
.card > *, .panel > * { min-width: 0; }
/* The heading of a flush card, which keeps its own padding while the rows
   below it run to the edge. A class rather than a style attribute: the
   Content Security Policy is `style-src 'self'`, and a browser blocks an
   inline one outright. */
.card__flush-head { padding: 20px 20px 0; }
.card__title { font-size: 1.15rem; }
.card__sub { color: var(--muted); font-size: .95rem; }
.card__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}
.card__foot { border-top: 1px solid var(--border); padding-top: 12px; }
/* Two pills in the right-hand slot of a card head — a role and a status. They
   wrap onto a second line rather than pushing the heading beside them out of
   the card, which is what `.card__head`'s own `flex-wrap` does one level up. */
.card__pills { display: flex; gap: 8px; flex-wrap: wrap; justify-content: flex-end; }

/* Tinted panels carry meaning, not decoration. */
.panel { border-radius: var(--r); padding: 16px; display: grid; gap: 6px; }
.panel--info { background: var(--blue-soft); }
.panel--info strong { color: var(--blue-dark); }
.panel--ok { background: var(--green-soft); }
.panel--ok strong { color: var(--green-dark); }
.panel--warn { background: var(--amber-soft); }
.panel--warn strong { color: var(--amber); }
.panel--danger { background: var(--red-soft); }
.panel--danger strong { color: var(--red-dark); }
.panel p { color: var(--muted); }
.panel--danger p, .panel--warn p { color: #52525b; }

/* ================================================================ KPI */
/* Mobile-first, and `auto-fit` rather than a fixed three: `repeat(3, 1fr)` on a
   phone means each column is at least its own min-content, so a label like
   *Elfogadott megrendelés (30 nap)* makes the row — and the page under it —
   wider than the screen. Two tiles fit a phone; the tablet and desktop rules
   below ask for more. */
.kpis {
  display: grid;
  gap: var(--gap);
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}
.kpi {
  min-width: 0;
  /* A tile is 152px wide on a phone and its note carries a document name and a
     section reference. `anywhere` is the only value that lets a token longer
     than the tile break rather than push the page sideways. */
  overflow-wrap: anywhere;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow);
  padding: 16px;
  display: grid;
  gap: 4px;
  align-content: start;
}
.kpi__label { color: var(--muted); font-size: .85rem; }
.kpi__value { font-size: 1.75rem; font-weight: 700; letter-spacing: -0.02em; }
.kpi__value--amber { color: var(--amber); }
.kpi__value--green { color: var(--green); }
.kpi__value--red { color: var(--red); }
.kpi__value--blue { color: var(--blue-dark); }
.kpi__value--unknown { color: var(--muted-soft); font-size: 1.15rem; font-weight: 600; }
.kpi__note { color: var(--muted); font-size: .8rem; }

/* The hero spend card from AMAROV_Marketing_Director_Mobile_v2.png. */
.hero__value { font-size: 2.6rem; font-weight: 800; letter-spacing: -0.03em; }
/* The same slot when there is no number in it.

   2.6rem display type is for a figure. *nincs felhatalmazás* set in it was the
   largest thing on the dashboard, said less than the sentence under it, and —
   being one long Hungarian word — was the widest indivisible thing in its card
   at every width (DEC-0160). At a phrase's own size it stops dominating the
   screen and stops being the thing that decides how wide the card must be.

   `anywhere` rather than the card's `break-word`: `break-word` breaks a word
   that does not fit but does **not** reduce the element's min-content width, so
   a grid track can still be sized to *felhatalmazás* and draw past the card.
   This is the value that makes the minimum a character rather than a word. */
.hero__value--unknown {
  font-size: 1.35rem;
  font-weight: 700;
  letter-spacing: normal;
  overflow-wrap: anywhere;
}
.hero__of { color: var(--muted); }

.meter {
  height: 12px;
  border-radius: var(--r-pill);
  background: #e6e9ef;
  overflow: hidden;
}
.meter__fill { height: 100%; background: var(--green); border-radius: var(--r-pill); }
.meter__fill--warn { background: var(--amber); }
.meter__fill--over { background: var(--red); }
.meter-row { display: flex; align-items: center; gap: 12px; }
.meter-row .meter { flex: 1; }
.meter-row__pct { font-weight: 700; color: var(--green-dark); font-size: .95rem; }

/* ================================================================ PILLS */
.pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 12px;
  border-radius: var(--r-pill);
  font-size: .8rem;
  font-weight: 700;
  letter-spacing: .01em;
  /* A pill may wrap, and that is a change from `white-space: nowrap`.

     `Jóváhagyáshoz kötött` beside a row title was three pixels wider than the
     row it sits in at 320px — and a pill that may never wrap is a pill that
     decides how wide the page is: `nowrap` leaves no soft wrap opportunity at
     all, so `overflow-wrap` cannot help, the flex parent sizes the track to the
     whole phrase, and the text beside it is what gets pushed out (DEC-0160).

     It does not wrap when it does not have to: an `inline-flex` item takes its
     max-content width wherever there is room, so a two-word status pill stays
     on one line at every width where one line fits. `anywhere` is the last
     resort under that, for a single status word longer than the space it has —
     `break-word` would not shrink the element's min-content width and so would
     not stop the overflow. */
  white-space: normal;
  min-width: 0;
  max-width: 100%;
  overflow-wrap: anywhere;
}
.pill--amber { background: var(--amber-soft); color: var(--amber); }
.pill--green { background: var(--green-soft); color: var(--green-dark); }
.pill--red { background: var(--red-soft); color: var(--red-dark); }
.pill--blue { background: var(--blue-soft); color: var(--blue-dark); }
.pill--grey { background: #eef1f6; color: var(--muted); }

.dot { width: 9px; height: 9px; border-radius: 50%; flex: none; }
.dot--amber { background: var(--amber); }
.dot--green { background: var(--green); }
.dot--red { background: var(--red); }
.dot--blue { background: var(--blue); }
.dot--grey { background: var(--muted-soft); }

/* ================================================================ BUTTONS */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 46px;
  /* Vertical padding rather than a fixed height, because the label may now
     wrap: `min-height` keeps a one-line button exactly the size it was, and a
     two-line one grows downwards instead of overflowing sideways. */
  padding: 8px 20px;
  /* `nowrap` is the browser's default for a flex item's text and it is what
     made a long label wider than its column. The label wraps and stays
     centred; nothing about a short one changes. */
  white-space: normal;
  text-align: center;
  overflow-wrap: break-word;
  min-width: 0;
  border: 1px solid transparent;
  border-radius: var(--r);
  background: var(--blue);
  color: #fff;
  font-weight: 700;
  cursor: pointer;
  text-decoration: none;
  transition: background .12s ease, border-color .12s ease;
}
.btn:hover { background: var(--blue-dark); text-decoration: none; }
.btn:disabled { opacity: .55; cursor: not-allowed; }
.btn--block { width: 100%; }
.btn--ghost { background: transparent; color: var(--text); border-color: var(--border-strong); }
.btn--ghost:hover { background: #f1f4f9; }
.btn--outline { background: transparent; color: var(--blue); border-color: var(--blue); }
.btn--outline:hover { background: var(--blue-soft); }
.btn--approve { background: var(--green); }
.btn--approve:hover { background: var(--green-dark); }
.btn--reject { background: transparent; color: var(--red); border-color: var(--red); }
.btn--reject:hover { background: var(--red-soft); }
.btn--danger { background: var(--red); }
.btn--danger:hover { background: var(--red-dark); }
/* The way out of the application on a phone, so it keeps `.btn`'s 46px height
   (docs/OWNER_SURFACE.md section 9.4) rather than the 40px it had. The height
   was collapsed along with the padding when the phone top bar was made to fit
   at 390px (CHANGELOG, NIGHT-006), and only the *padding* was doing that work:
   a taller button is no wider, and the top bar is 64px. 40px is below the
   44px minimum `tests/browser/test_accessibility.py` holds every other control
   to. */
.btn--signout {
  background: transparent;
  color: var(--red);
  border-color: transparent;
  font-weight: 700;
  padding: 0 6px;
}
.btn--signout:hover { background: var(--red-soft); }
.btn--small { min-height: 38px; padding: 0 14px; font-size: .9rem; }
.btn--link {
  background: none;
  border: none;
  color: var(--blue);
  padding: 0;
  min-height: 0;
  font-weight: 700;
}
.btn--link:hover { background: none; text-decoration: underline; }

/* A row of buttons that fits whatever it is in.

   It was `1fr 1fr` at every width, which on a phone is two columns each half of
   a ~330px card — narrower than *Heti összefoglaló* is, so the button overhung
   the card rather than the column shrinking to hold it. `auto-fit` with a
   `min()` floor is the same two columns wherever two fit and one column where
   they do not, with no breakpoint to keep in step with the card's padding. */
.actions {
  display: grid;
  gap: 10px;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 11rem), 1fr));
}
.actions > * { min-width: 0; }
.actions--single { grid-template-columns: 1fr; }
.actions--end { display: flex; justify-content: flex-end; gap: 10px; flex-wrap: wrap; }
.actions--end > * { min-width: 0; }

/* ================================================================ FORMS */
.field { display: grid; gap: 6px; }
.field__label { font-weight: 700; font-size: .95rem; }
.field__hint { color: var(--muted); font-size: .85rem; }
.field__error { color: var(--red-dark); font-size: .85rem; font-weight: 600; }

.input {
  width: 100%;
  min-height: 50px;
  padding: 12px 14px;
  border: 1px solid var(--border-strong);
  border-radius: var(--r);
  background: var(--card);
  color: var(--text);
}
.input:focus { border-color: var(--blue); }
.input[aria-invalid="true"] { border-color: var(--red); }
textarea.input { min-height: 90px; resize: vertical; }

.form-grid { display: grid; gap: var(--gap); }
.form-grid > *, .rows > *, .stack > * { min-width: 0; }

/* Segmented control — the autonomy chooser in AMAROV_Settings_*.png. */
.segments { display: grid; gap: 10px; }
.segments > * { min-width: 0; }
.segment {
  min-height: 48px;
  padding: 10px 14px;
  white-space: normal;
  overflow-wrap: break-word;
  border: 1px solid var(--blue);
  border-radius: var(--r);
  background: transparent;
  color: var(--blue);
  font-weight: 700;
  cursor: pointer;
}
.segment[aria-pressed="true"] { background: var(--blue); color: #fff; }
.segment:disabled { opacity: .5; cursor: not-allowed; }

/* Sub-navigation chips — the Settings sections on a phone. */
.chips { display: flex; gap: 8px; overflow-x: auto; padding-bottom: 4px; }
.chip {
  flex: none;
  padding: 9px 16px;
  border-radius: var(--r-pill);
  border: 1px solid var(--border-strong);
  background: var(--card);
  color: var(--text);
  font-weight: 600;
  cursor: pointer;
  text-decoration: none;
}
.chip[aria-current="true"] { background: var(--blue); border-color: var(--blue); color: #fff; }

/* ================================================================ LISTS */
.rows { display: grid; }
.row {
  display: grid;
  gap: 8px;
  padding: 16px var(--pad);
  border-bottom: 1px solid var(--border);
}
.row:last-child { border-bottom: none; }
.row__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  min-width: 0;
}
/* And its children, for the same reason `.card > *` exists one section up: a
   flex item's minimum width is its own min-content unless something says
   otherwise, so a badge that cannot shrink sizes the row and draws past it. */
.row__head > * { min-width: 0; }
/* `anywhere`, because the strings that land in a row title are not prose: a
   browser's user-agent string, an identifier, a URL. One of them is enough to
   make a phone scroll sideways, and it is the row nobody thought about. */
.row__title { font-weight: 700; min-width: 0; overflow-wrap: anywhere; }
.row__meta { color: var(--muted); font-size: .9rem; overflow-wrap: anywhere; }
.row__value { font-weight: 700; white-space: nowrap; }

.pairs { display: grid; gap: 10px; }
/* `wrap`, and both children allowed to shrink. A key and a value that together
   are wider than the card used to draw past its edge — the *Autonómia állapot*
   and mandate cards, where the value is a sentence rather than a number. They
   now sit on two lines, which is what a person reading a narrow card expects
   anyway. */
.pair {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 4px 16px;
  flex-wrap: wrap;
}
.pair__key { color: var(--muted); min-width: 0; overflow-wrap: anywhere; }
.pair__value { font-weight: 600; text-align: right; min-width: 0; overflow-wrap: anywhere; }
.pair__value--mono { font-family: var(--mono); font-size: .85rem; word-break: break-all; }

/* A configuration name with the sentence saying what it is for underneath it.
   `.pairs` is a single-column grid, so the wrapper is one grid item and the
   two lines inside it stay together — which is the point: a purpose sentence
   that wrapped to the next name would attach itself to the wrong one. */
.pair-group { display: grid; gap: 2px; }

.stack { display: grid; gap: var(--gap); }
.stack--tight { gap: 10px; }

/* `anywhere` rather than the cards' `break-word`, and the difference is the
   whole reason this line exists: `break-word` breaks a long word when it
   reaches the edge of its line and **does not shrink the element's min-content
   width**, so a list item holding `MD_GOOGLE_ADS_DEVELOPER_TOKEN` widened the
   grid track, the card, and the page under it by 3px on a 360px phone.
   `anywhere` counts in intrinsic sizing, which is what stops that — and it is
   what `.row__meta` already uses, for the same names in the same screen. */
.bullets {
  margin: 0;
  padding-left: 20px;
  color: var(--muted);
  display: grid;
  gap: 8px;
  overflow-wrap: anywhere;
}

/* What an Owner actually wrote, quoted back to them, and what the system made
   of it. Two visually distinct things on purpose: the quote is evidence and
   must never be mistaken for the reading, which is a machine's opinion. */
.quote {
  margin: 0;
  padding: 12px 16px;
  border-left: 3px solid var(--blue);
  background: var(--blue-soft);
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
}
.reading { margin: 0; color: var(--muted); }

/* A disclosure whose contents cost a request, so it is closed until asked. */
.disclosure > summary {
  cursor: pointer;
  color: var(--blue);
  font-weight: 600;
  padding: 8px 0;
}
.disclosure[open] > summary { margin-bottom: 8px; }

/* ================================================================ STATES
   Explicit loading, error, empty, and unknown states. A blank card and a card
   that is still loading must never look the same, and "no data" must never
   look like "zero". */
.state {
  display: grid;
  gap: 8px;
  justify-items: center;
  text-align: center;
  padding: 32px 20px;
  color: var(--muted);
}
.state__title { font-weight: 700; color: var(--text); }
.state--error { color: var(--red-dark); }
.state--error .state__title { color: var(--red-dark); }

.unknown { color: var(--muted-soft); font-style: italic; }

.skeleton {
  background: linear-gradient(90deg, #eef1f6 25%, #e4e9f2 37%, #eef1f6 63%);
  background-size: 400% 100%;
  animation: shimmer 1.4s ease infinite;
  border-radius: var(--r-sm);
  height: 1em;
}
@keyframes shimmer { 0% { background-position: 100% 0; } 100% { background-position: 0 0; } }

.offline-banner {
  background: var(--amber-soft);
  color: var(--amber);
  font-weight: 700;
  text-align: center;
  padding: 10px var(--pad);
}

/* ================================================================ TOASTS */
.toasts {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  bottom: calc(var(--tabbar) + 16px + env(safe-area-inset-bottom));
  z-index: 60;
  display: grid;
  gap: 8px;
  width: min(30rem, calc(100vw - 32px));
}
.toast {
  border-radius: var(--r);
  padding: 14px 16px;
  box-shadow: var(--shadow-lift);
  font-weight: 600;
  background: var(--navy);
  color: #fff;
}
.toast--ok { background: var(--green-dark); }
.toast--error { background: var(--red-dark); }

/* ================================================================ DIALOG */
.confirm {
  border: none;
  border-radius: var(--r-lg);
  padding: 0;
  width: min(32rem, calc(100vw - 32px));
  box-shadow: var(--shadow-lift);
}
.confirm::backdrop { background: rgba(15, 23, 42, .5); }
.confirm__form { padding: var(--pad); display: grid; gap: var(--gap); }
.confirm__title { font-size: 1.25rem; }
.confirm__body { color: var(--muted); }
.confirm__state { border-radius: var(--r); padding: 12px 14px; font-weight: 600; }
.confirm__actions { display: flex; justify-content: flex-end; gap: 10px; }

/* ================================================================ TABLES
   A table on a desktop; the same data as stacked rows on a phone, because a
   horizontally scrolling table on a phone is a table nobody reads. */
.table { width: 100%; border-collapse: collapse; display: none; }
.table th, .table td { padding: 14px var(--pad); text-align: left; }
.table th {
  font-size: .85rem;
  color: var(--muted);
  font-weight: 700;
  border-bottom: 1px solid var(--border);
}
.table td { border-bottom: 1px solid var(--border); }
.table tr:last-child td { border-bottom: none; }
.table td.num, .table th.num { text-align: right; }

/* ================================================================ TABLET */
@media (min-width: 640px) {
  .main { padding: 24px 28px calc(var(--tabbar) + 32px); }
  .kpis--three { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .grid-2 { display: grid; gap: var(--gap); grid-template-columns: 1fr 1fr; }
  .signin {
    grid-template-rows: none;
    grid-template-columns: 1fr 1fr;
    min-height: 100dvh;
  }
  .signin__brand { min-height: auto; padding: 40px; }
  /* Half the viewport wide from here, so the card has room for the lockup at
     the largest size the supplied file stays sharp at. */
  .signin__mark { max-width: min(100%, 30rem); }
  .signin__mark .logo { max-width: 420px; }
  .signin__panel {
    margin: 0;
    border-radius: 0;
    padding: 40px;
    align-content: center;
    justify-items: center;
  }
  .actions--wide { grid-template-columns: auto auto; justify-content: end; }
}

/* ================================================================ DESKTOP
   AMAROV_Marketing_Director_UI_v2.png: a fixed dark sidebar with the logo card
   at the top, and a white header bar carrying the page title, the signed-in
   Owner, and the sign-out button. */
@media (min-width: 1024px) {
  .shell {
    grid-template-columns: var(--sidebar) minmax(0, 1fr);
    grid-template-rows: var(--topbar) 1fr;
  }

  .sidebar {
    display: grid;
    grid-row: 1 / -1;
    grid-column: 1;
    background: var(--navy);
    padding: 20px 16px;
    gap: 6px;
    align-content: start;
    position: sticky;
    top: 0;
    height: 100dvh;
    overflow-y: auto;
  }
  .sidebar__mark {
    background: #fff;
    border-radius: var(--r);
    padding: 14px 16px;
    margin-bottom: 18px;
    text-align: center;
  }
  .sidebar__link {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding: 12px 16px;
    border-radius: var(--r);
    color: var(--on-navy);
    font-weight: 600;
    text-decoration: none;
  }
  .sidebar__link:hover { background: var(--navy-soft); text-decoration: none; }
  .sidebar__link[aria-current="page"] { background: var(--blue); color: #fff; }
  .sidebar__badge {
    min-width: 22px;
    padding: 1px 7px;
    border-radius: var(--r-pill);
    background: var(--amber);
    color: #fff;
    font-size: .78rem;
    font-weight: 700;
    text-align: center;
  }
  .sidebar__link[aria-current="page"] .sidebar__badge { background: #fff; color: var(--blue-dark); }

  .topbar { grid-column: 2; }
  .topbar__mark { display: none; }
  .topbar__title {
    display: block;
    font-size: 1.35rem;
    font-weight: 700;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .topbar__who { display: block; }

  .main {
    grid-column: 2;
    padding: 24px 32px 48px;
    max-width: 1400px;
  }
  .page__head { display: none; }

  .tabbar { display: none; }
  .toasts { bottom: 24px; left: auto; right: 24px; transform: none; }

  .kpis { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .kpis--three { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .grid-2 { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .grid-side { display: grid; gap: var(--gap); grid-template-columns: 2fr 1fr; }
  .grid-settings { display: grid; gap: var(--gap); grid-template-columns: 240px 1fr; align-items: start; }

  .chips { display: none; }
  .sidenav { display: grid; gap: 4px; }
  .sidenav__item {
    text-align: left;
    padding: 12px 16px;
    border-radius: var(--r);
    border: none;
    background: transparent;
    color: var(--text);
    font-weight: 600;
    cursor: pointer;
  }
  .sidenav__item[aria-current="true"] { background: var(--blue-soft); color: var(--blue-dark); }

  .table { display: table; }
  .table + .rows { display: none; }

  .row--wide {
    grid-template-columns: 1fr auto auto;
    align-items: center;
    gap: 20px;
  }
  /* Content-sized and right-aligned, as the references draw it — but with a
     `minmax(0, …)` floor, so a long Hungarian label shrinks the label rather
     than the card. `auto` alone is min-content, which is what let a button out
     of its card on the narrower two-column layouts. */
  .actions { grid-template-columns: repeat(2, minmax(0, auto)); justify-content: end; }
  .actions--single { grid-template-columns: minmax(0, auto); }
}

/* ================================================== THE ASSET LIBRARY
   Marketing anyagok. Two things here are load-bearing rather than decorative.

   The drop zone is a large target with a visible focus and hover state, because
   on a phone it is not a drop zone at all — it is the area around the button
   that opens the picker, and it has to read as one thing.

   The grid is `auto-fill` rather than a fixed column count, so the same markup
   is one column on a 360px phone and four on a desktop without a media query
   per breakpoint. A thumbnail has a fixed aspect ratio so a tall photograph and
   a wide one do not make the row jump. */
.dropzone {
  display: grid;
  gap: 12px;
  justify-items: center;
  padding: 28px var(--pad);
  border: 2px dashed var(--border-strong);
  border-radius: var(--r);
  background: var(--bg);
  text-align: center;
}
.dropzone[data-over="true"] { border-color: var(--blue); background: var(--blue-soft); }
.dropzone__label { color: var(--muted); font-weight: 600; }
.dropzone .field { width: 100%; max-width: 22rem; }

/* The language toggle. Deliberately NOT `.chips`: that class is the phone's
   form of a section list and is hidden on a desktop, where the side navigation
   replaces it — and a language toggle has no desktop counterpart to replace it
   with. */
.lang-toggle { display: flex; gap: 8px; margin-bottom: var(--gap); }

.quota { margin: 4px 0 12px; }

.asset-grid {
  display: grid;
  gap: var(--gap);
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
}

.asset-card {
  display: grid;
  grid-template-rows: auto 1fr;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--r);
  box-shadow: var(--shadow);
  overflow: hidden;
}
.asset-card__thumb {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  background: var(--bg);
  display: block;
}
.asset-card__thumb--none {
  display: grid;
  place-items: center;
  color: var(--muted-soft);
  font-family: var(--mono);
  font-size: .85rem;
}
.asset-card__thumb--audio { aspect-ratio: auto; padding: 12px; }
.asset-card__body { display: grid; gap: 10px; padding: 16px; align-content: start; }
.asset-card__title { font-size: 1.05rem; overflow-wrap: anywhere; }
.actions--assets {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 6rem), 1fr));
  gap: 8px;
}
.actions--assets > * { min-width: 0; }

/* ================================================================ MOTION */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
}

/* ================================================================ PRINT
   The audit trail and the weekly brief are the two things anybody prints. */
@media print {
  .sidebar, .tabbar, .topbar, .toasts, .actions { display: none !important; }
  .main { padding: 0; }
  .card { box-shadow: none; break-inside: avoid; }
}
