/* Shared base for the three web apps (customer, vet, admin).
   Linked after shared.css and before each app's own main.css:

     <link data-trunk rel="css" href="../../crates/ui/style/shared.css" />
     <link data-trunk rel="css" href="../../crates/ui/style/web.css" />
     <link data-trunk rel="css" href="style/main.css" />

   It carries the default (tenant-themable) palette, the surface design
   tokens, and every structural rule the web apps share. An app's main.css
   only keeps its palette overrides and its own components.

   Visual language mirrors the mobile app (apps/app-ui): soft shadow cards
   instead of hard borders, rounder/roomier controls, a calm page
   background. Per-tenant theming stays variable-driven — customer/vet
   inject --primary/--accent via an inline <body> style; admin overrides
   the palette in its main.css. */

:root {
    /* Tenant-themable palette — now BRIDGED onto the Pfotenkur design
       tokens (crates/ui/style/tokens.css). Every component below still
       reads these semantic names, so the whole web UI re-skins to the
       brand without per-rule edits. Per-tenant whitelabel overrides
       --pk-accent at runtime (ui::theme), which re-resolves --primary. */
    --primary:    var(--pk-accent);          /* header, buttons, tabs, focus */
    /* Secondary/data accent = brand green: trend & BCS chart strokes, the
       version banner, button-accent. The kcal progress fill is decoupled and
       reads --pk-accent (teal) directly, so this green is the data-viz tone,
       giving the brand its teal-UI / green-data two-tone. Whitelabel still
       overrides --accent with the practice's own secondary at runtime. */
    --accent:     var(--pk-green-deep);
    --text:       var(--pk-ink);
    --muted:      var(--pk-ink-muted);
    --bg:         var(--pk-white);
    --bg-soft:    var(--pk-paper);
    --border:     var(--pk-border);
    --warn:       var(--pk-amber);
    --danger:     var(--pk-clay);
    /* Focus ring colour — admin overrides to its accent. */
    --focus:      var(--pk-accent);

    /* Surface design tokens — bridged to the brand radii/shadow. */
    --radius-card:    var(--pk-radius-xl);    /* 16px cards */
    --radius-control: var(--pk-radius-md);    /* 10px controls */
    --shadow-card:    var(--pk-shadow-card);
}

* { box-sizing: border-box; }

html, body {
    margin: 0;
    padding: 0;
    background: var(--bg-soft);
}

body {
    font-family: var(--pk-font-body);
    color: var(--text);
    line-height: 1.5;
}

a { color: var(--pk-accent-deep); }

/* ====================== Topbar ====================== */

.topbar {
    background: var(--primary);
    color: var(--pk-accent-on);
    padding: 0.75rem var(--pk-space-6);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    flex-wrap: wrap;
    box-shadow: 0 1px 2px rgba(15, 23, 42, 0.06);
}
/* Ink variant (§01): the platform-admin level signal — a dark --pk-ink bar
   with the context label in brand green. After `.topbar` so it wins the
   background; the lockup + button system are shared with the teal portals. */
.topbar-ink { background: var(--pk-ink); }
.topbar-ink .topbar-context-label { color: var(--pk-green); opacity: 1; }
/* Brand row: the Pfotenkur lockup (mark + wordmark) and, to its right, the
   practice context — separated so they don't run together. */
.topbar .brand {
    font-weight: 600;
    display: inline-flex;
    align-items: center;
    gap: 0.65rem;
}
/* Per-practice logo, shown only under white-label. Capped height. */
.topbar-logo {
    height: 1.75rem;
    width: auto;
    display: block;
    border-radius: 4px;
}
/* White-label practice name (shown beside the practice's own logo). */
.topbar .brand-name {
    font-family: var(--pk-font-display);
    font-weight: var(--pk-weight-bold);
    font-size: var(--pk-text-lg);
    letter-spacing: -0.01em;
}
/* The Pfotenkur lockup: design's inverted (white) horizontal logo as an <img>
   on the accent/ink bar — coin + wordmark in outline paths (#F5FAF5 disc/text,
   teal paws, green leaf), so it's self-contained and CSP-safe (img-src 'self',
   no font @import). */
.topbar-wordmark {
    height: 1.6rem;
    width: auto;
    display: block;
}
/* Practice context to the right of the lockup: a divider, then a smaller
   block. In the vet portal the label ("Praxis-Portal") sits above-or-before
   the practice name; on the customer side just the practice name shows. */
.topbar-context {
    display: inline-flex;
    flex-direction: column;
    justify-content: center;
    line-height: 1.15;
    padding-left: 0.65rem;
    border-left: 1px solid rgba(245, 250, 245, 0.3);
}
.topbar-context-label {
    font-family: var(--pk-font-mono);
    font-size: 0.65rem;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    opacity: 0.9;
}
/* Customer (dog owner): the practice name stays full-size, co-equal with the
   wordmark. */
.topbar-context-name {
    font-family: var(--pk-font-display);
    font-weight: var(--pk-weight-bold);
    font-size: var(--pk-text-lg);
    letter-spacing: -0.01em;
}
/* Vet portal: the "Praxis-Portal" label + practice name read as a smaller,
   secondary block next to the wordmark. */
.topbar-context.is-compact .topbar-context-name {
    font-family: var(--pk-font-body);
    font-weight: 600;
    font-size: 0.9rem;
    letter-spacing: 0;
}
/* Customer portal: same compact treatment, but the "Kunden-Portal" label sits
   to the RIGHT of the practice name (a row), not stacked above it. */
.topbar-context.label-after {
    flex-direction: row;
    align-items: baseline;
    gap: 0.45rem;
}

/* Pre-login practice branding above the login card: centred logo + name. */
.login-branding {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 1rem;
}
.login-logo {
    max-height: 4rem;
    max-width: 70%;
    width: auto;
    height: auto;
}
.login-brand-name {
    font-family: var(--pk-font-display);
    font-weight: var(--pk-weight-bold);
    font-size: var(--pk-text-lg);
    margin: 0;
    text-align: center;
}
/* Full-colour Pfotenkur wordmark above the (light) login card. */
.login-wordmark {
    height: 2.75rem;
    width: auto;
    max-width: 80%;
}
/* The nav buttons + locale picker live in this slot; space them so the
   now-opaque ghost buttons don't visually run together. */
.topbar-actions {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.5rem;
}
.topbar a, .topbar form button {
    color: #F5FAF5;
    text-decoration: none;
}
/* Action buttons on the coloured bar — teal product portals OR the ink admin
   bar — use the light-on-dark scale from §01, not the white page fill.
   Primary CTA = solid paper fill (high contrast); secondary/nav buttons +
   selects = a subtle translucent paper tint with a readable hairline. */
.topbar .button-primary {
    background: #F5FAF5;
    color: var(--pk-accent-deep);
    border-color: transparent;
}
.topbar .button-primary:hover { background: #fff; }
.topbar .button-secondary {
    background: rgba(245, 250, 245, 0.14);
    color: #F5FAF5;
    border-color: rgba(245, 250, 245, 0.25);
}
.topbar .button-secondary:hover { background: rgba(245, 250, 245, 0.24); }
/* §C3: a ghost button on the ink/teal bar (e.g. admin "Abmelden") reads as a
   quiet transparent OUTLINE — not a translucent grey box that looks foreign on
   the dark header. Fills only on hover. */
.topbar .button-ghost {
    background: transparent;
    color: #F5FAF5;
    border-color: rgba(245, 250, 245, 0.25);
}
.topbar .button-ghost:hover { background: rgba(245, 250, 245, 0.14); }
/* Locale/practice selects share the secondary treatment (overrides the
   white-on-dark default from shared.css with the §01 paper tint). */
.topbar .locale-picker select,
.topbar .practice-switcher select {
    /* The dense topbar selects keep their compact NATIVE arrow — the central
       flat/chevron look is for content selects. `appearance:auto` undoes the
       global `select` rule; `background-image:none` cancels its chevron (which
       would otherwise tile, since shared.css's shorthand resets the repeat);
       `min-height:0` opts out of the 2.5rem field floor so they stay compact
       (sized by shared.css's padding) instead of growing to button height. */
    -webkit-appearance: auto;
    appearance: auto;
    min-height: 0;
    background-color: rgba(245, 250, 245, 0.14);
    background-image: none;
    color: #F5FAF5;
    border-color: rgba(245, 250, 245, 0.25);
}
/* Nav-links (§01): quiet light text, no fill; the active route gets a
   translucent pill. Distinct from the secondary/ghost buttons above. */
.topbar .topbar-navlink {
    display: inline-flex;
    align-items: center;
    min-height: 2.5rem;
    padding: 0.4rem 0.85rem;
    border-radius: var(--pk-radius-md);
    color: #D7ECDC;
    font-weight: 600;
    text-decoration: none;
}
.topbar .topbar-navlink:hover { background: rgba(245, 250, 245, 0.10); color: #F5FAF5; }
.topbar .topbar-navlink.is-active {
    background: rgba(245, 250, 245, 0.16);
    color: #F5FAF5;
}

main {
    max-width: 720px;
    margin: 1.5rem auto;
    padding: 0 1rem;
}

h1, h2, h3 {
    font-family: var(--pk-font-display);
    color: var(--pk-ink);
    line-height: 1.2;
    margin: 1.5rem 0 0.75rem;
}
h1 { font-size: 1.75rem; font-weight: var(--pk-weight-bold); }    /* pet-name (README §10) */
h2 { font-size: var(--pk-h3); font-weight: var(--pk-weight-bold); } /* card titles (README §2) */
h3 { font-weight: var(--pk-weight-semibold); }

/* ====================== Card / rows ====================== */

.card {
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-card);
    padding: var(--pk-space-6);
    margin: 1rem 0;
    box-shadow: var(--shadow-card);
}

.card-row {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 1rem;
}
.card-row + .card-row { margin-top: 0.5rem; }
.label { color: var(--pk-ink-soft); font-size: 0.875rem; }
/* Measured values are monospaced for that clinical, tabular feel (README
   §2/§3). No nowrap here: .value also carries text fallbacks ("not enough
   measurements") that must be free to wrap on narrow screens. */
.value {
    font-family: var(--pk-font-mono);
    font-weight: 600;
}
/* The highlighted figure (e.g. "Remaining 1172 kcal") leans on the accent. */
.value-large {
    font-family: var(--pk-font-mono);
    font-size: 1.5rem;
    font-weight: 600;
    color: var(--pk-accent-deep);
    white-space: nowrap;
}
/* .value-text / .value-note live in shared.css (also reached by app-ui, which
   doesn't load web.css) — see the §00 #4 mono-for-prose fix. */

.muted { color: var(--muted); }
.warn  { color: var(--warn); }
.danger { color: var(--danger); }

/* ====================== Budget bar ====================== */

.budget-bar {
    height: 0.5rem;
    background: var(--pk-accent-soft);
    border-radius: var(--pk-radius-pill);
    overflow: hidden;
    margin-top: 0.5rem;
}
.budget-bar-fill {
    height: 100%;
    background: var(--pk-accent);     /* progress fill = brand accent (README §4) */
    border-radius: var(--pk-radius-pill);
    transition: width 200ms;
}
.budget-bar-fill.over { background: var(--warn); }

/* ====================== Control grouping ======================
   CONVENTION: interactive controls (buttons, inputs, selects) must never
   visually touch. Always group adjacent controls in a flex container with
   `gap` — never place two controls as bare siblings. Use one of:
     .action-row  (shared.css) — a row of buttons
     .field-row   (below)      — label + input + optional action button
     .log-tabs / .pager        — purpose-built groups
   The shared form layout already spaces stacked fields via `form { gap }`.
*/

/* Label + control (+ action) on one line, evenly spaced and wrapping. */
.field-row {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    flex-wrap: wrap;
    margin: 0.25rem 0;
}

/* Label text stacked above its control (feeding picker search box). */
.field-stack {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
}
/* Full-width, left-aligned list buttons (larder + catalog picker results),
   stacked so adjacent options never touch — Spec: controls never touch. */
.is-block {
    display: block;
    width: 100%;
    text-align: left;
    margin: 0.25rem 0;
}
/* A compact standalone input — the central §A1 rule gives it the field look;
   this only sets the narrow width. */
.field-input { width: 6rem; }
/* The picker search box opts into full width via .is-block, but plain
   `.is-block` loses to the later, equal-specificity `.field-input { width:6rem }`.
   This combined selector wins so the feeding search field spans the row. */
.field-input.is-block { width: 100%; }

/* (Re-attach code + inline entry-edit inputs in .field-row sit outside a
   <form> but are covered by the central §A1 rule above — no separate rule.) */

/* Layout-only: the brand field look (border, white fill, chevron, focus ring)
   now comes from the central `select` rule below — `.field-select` just widens
   a standalone picker to a sensible size and spaces it from the row beneath
   (controls never touch; it sits outside <form>, so no form gap spaces it). */
.field-select {
    width: 16rem;
    max-width: 100%;
    margin-bottom: var(--pk-space-4);
}

/* ====================== Forms ====================== */

form { display: flex; flex-direction: column; gap: 0.75rem; }
form label {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    font-size: 0.875rem;
    font-weight: var(--pk-weight-semibold);   /* §02: field labels in body 600 */
    color: var(--muted);
}
/* A styled file-picker trigger (shared `.upload-trigger`) used inside a <form>
   must keep reading as a button, not collapse into the column field-label look
   above. Higher specificity than `form label` so it wins. */
form label.upload-trigger {
    display: inline-flex;
    flex-direction: row;
    align-items: center;
    color: var(--pk-ink);
    font-size: 1rem;
}
/* §A1 — CENTRAL field styling. Every text input, search box and textarea gets
   the same readable contrast (border #BCCFBC on #fff, teal 1.5px focus border +
   ring, #8A9A8E placeholder), in a <form> or not — solved ONCE here, not per
   field. Selects: this rule + `.field-select` (standalone). Checkboxes/radios
   and the dark topbar selects keep their own look via more specific rules. */
input[type="text"], input[type="search"], input[type="email"], input[type="number"],
input[type="password"], input[type="tel"], input[type="url"], input[type="date"],
input[type="datetime-local"], input[type="time"], input[type="month"],
input:not([type]), textarea, select {
    /* Match the button box height (min-height 2.5rem) so a field and a button
       on the same row line up; the trimmed vertical padding keeps the natural
       height under 2.5rem, and single-line inputs/selects center their text. */
    min-height: 2.5rem;
    padding: 0.5rem 0.85rem;
    border: 1px solid #BCCFBC;
    border-radius: var(--radius-control);
    background: #fff;
    color: inherit;
    font-size: 1rem;
    font-family: inherit;
    -webkit-appearance: none;
            appearance: none;
}
/* Textareas are multi-line: 2.5rem is just a floor, and they keep generous
   padding rather than the single-line trim above. */
textarea { min-height: 5rem; padding: 0.6rem 0.85rem; }
input[type="text"]:focus, input[type="search"]:focus, input[type="email"]:focus,
input[type="number"]:focus, input[type="password"]:focus, input[type="tel"]:focus,
input[type="url"]:focus, input[type="date"]:focus, input[type="datetime-local"]:focus,
input[type="time"]:focus, input[type="month"]:focus, input:not([type]):focus,
textarea:focus, select:focus {
    outline: none;
    border: 1.5px solid var(--pk-accent);
    box-shadow: 0 0 0 3px rgba(25, 135, 140, 0.15);
}
input::placeholder, textarea::placeholder { color: #8A9A8E; }
/* Every styled select (the rule above covers all of them now — in-form AND
   standalone pickers like the vet member-role select). `appearance: none`
   strips the native arrow, so draw a chevron and leave room for it. */
select {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none' stroke='%2346544A' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 0.8rem center;
    padding-right: 2.2rem;
}
/* The pager "per page" select is a compact utility control sized by shared.css;
   keep its native arrow (the global appearance:none above would strip it, and
   the chevron/right-padding don't suit its small footprint). */
.pager-size {
    -webkit-appearance: auto;
    appearance: auto;
    padding-right: 0.6rem;
    background-image: none;
}
/* Checkboxes/radios must keep their native rendering — the blanket
   `appearance: none` above otherwise strips the check/fill so the control
   looks empty even when on. Restore native look + theme the fill. */
form input[type="checkbox"], form input[type="radio"] {
    -webkit-appearance: auto;
    appearance: auto;
    width: 1.1rem;
    height: 1.1rem;
    padding: 0;
    margin: 0;
    flex: none;
    accent-color: var(--primary);
}

/* Collapsible advanced-options block inside forms. */
.advanced-options {
    border: 1px solid var(--border);
    border-radius: var(--radius-control);
    padding: 0.5rem 0.75rem;
}
.advanced-options > summary {
    cursor: pointer;
    font-size: 0.875rem;
    color: var(--muted);
    font-weight: 600;
}
.advanced-options[open] > summary { margin-bottom: 0.5rem; }

/* ====================== Buttons ====================== */

.button-primary,
.button-accent,
.button-secondary,
.button-ghost,
.button-ink,
.button-danger {
    appearance: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
    min-height: 2.5rem;
    padding: 0.6rem 1.1rem;
    border: 1px solid transparent;
    border-radius: var(--radius-control);
    font-size: 1rem;
    font-weight: 600;
    cursor: pointer;
    text-decoration: none;
}
.button-primary { background: var(--pk-accent); color: var(--pk-accent-on); }
.button-primary:hover { background: var(--pk-accent-deep); }
.button-accent  { background: var(--accent);  color: var(--pk-accent-on); }
/* Secondary action (TOTP setup, export links, scan entry): a soft accent
   fill (README §7), readable without an outline. */
.button-secondary {
    background: var(--pk-accent-soft);
    color: var(--pk-accent-deep);
    border-color: transparent;
}
.button-secondary:hover { background: color-mix(in srgb, var(--pk-accent-soft) 80%, var(--pk-accent)); }
.button-ghost   { background: var(--bg); color: var(--pk-ink-soft); border-color: var(--pk-border-input); }
.button-ghost:hover { background: var(--pk-surface-2); }
/* Strong "create" CTA on light surfaces (e.g. "Neue Praxis", "+ Tier per
   Code") — the ink fill (§02) reads as the platform's primary action,
   distinct from the accent-keyed .button-primary. */
.button-ink     { background: var(--pk-ink); color: #fff; }
.button-ink:hover { background: #000; }
/* Destructive = clay OUTLINE, not solid red (§02): present but visually
   restrained until deliberately chosen. */
.button-danger  { background: transparent; color: var(--pk-clay); border-color: var(--pk-clay-border); }
.button-danger:hover { background: var(--pk-clay-bg); }
.button-primary:active,
.button-accent:active,
.button-secondary:active,
.button-ghost:active,
.button-ink:active,
.button-danger:active { transform: translateY(1px); }
.button-primary:disabled,
.button-accent:disabled,
.button-secondary:disabled,
.button-ghost:disabled,
.button-ink:disabled,
.button-danger:disabled {
    opacity: 0.6;
    cursor: not-allowed;
    transform: none;
}
/* §C4: a disabled primary reads as a clearly muted green (not a dimmed teal),
   so "active = full teal" vs "disabled" is unambiguous. */
.button-primary:disabled {
    background: #A9CDB6;
    color: #F5FAF5;
    opacity: 1;
}

/* ====================== Impersonation banner ====================== */

.impersonation-banner {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.75rem;
    padding: 0.5rem 1rem;
    background: #b45309;
    color: #fff;
    font-size: 0.9rem;
    font-weight: 600;
}
.impersonation-banner .button-ghost {
    background: rgba(255, 255, 255, 0.15);
    color: #fff;
    border-color: rgba(255, 255, 255, 0.45);
    min-height: 2rem;
    padding: 0.3rem 0.8rem;
}

/* ====================== Pager (numbered pages) ====================== */

/* ====================== Recommendation row (vet budget aid) ====================== */

.recommendation-row {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.6rem;
    margin: 0.25rem 0 0.5rem;
}
.recommendation-row .button-ghost {
    min-height: 2rem;
    padding: 0.3rem 0.8rem;
}

/* ====================== Correctable entry rows ====================== */

.entry-row {
    list-style: none;
    padding: 0.5rem 0;
    border-bottom: 1px solid var(--border);
}
.entry-row:last-child { border-bottom: none; }
.entry-action {
    min-height: 1.9rem;
    padding: 0.25rem 0.7rem;
    font-size: 0.85rem;
}
.entry-label { width: auto; flex: 1 1 8rem; min-width: 6rem; }
/* `.hidden` (shared.css) must beat the `display:flex` of these rows when
   the edit/display halves toggle — raise specificity so it wins. */
.entry-row .field-row.hidden,
.entry-row .action-row.hidden { display: none; }

/* Collapsible history entry: summary row (date + value + disclosure chevron),
   body revealed on expand (edit controls + photos). */
.entry-summary {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    cursor: pointer;
    list-style: none;
}
.entry-summary::-webkit-details-marker { display: none; }
.entry-summary::before {
    content: "▸";
    color: var(--muted);
    font-size: 0.8em;
    flex: 0 0 auto;
    transition: transform 0.15s ease;
}
.entry-details[open] > .entry-summary::before { transform: rotate(90deg); }
.entry-summary .value { margin-left: auto; }
.entry-body {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    padding: 0.5rem 0 0.25rem 1.4rem;
}

/* ====================== Lists ====================== */

.pet-list a {
    text-decoration: none;
    color: var(--text);
    display: block;
    padding: 0.75rem 0;
    border-bottom: 1px solid var(--border);
}
.pet-list a:hover { background: var(--bg-soft); }

/* ---- Generic status pill (§02): body text + 6px colour dot ----
   Feature-specific pills (.patient-state, .job-status, …) share this shape;
   new call sites use these generic variants. */
.pill {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 0.75rem;
    font-weight: 600;
    padding: 0.2rem 0.6rem;
    border-radius: var(--pk-radius-pill);
    white-space: nowrap;
}
.pill::before {
    content: "";
    width: 6px; height: 6px;
    border-radius: 50%;
    background: currentColor;
    flex: 0 0 auto;
}
.pill-ok      { background: var(--pk-pill-ok-bg);      color: var(--pk-pill-ok-text); }
.pill-warn    { background: var(--pk-pill-warn-bg);    color: var(--pk-pill-warn-text); }
.pill-danger  { background: var(--pk-pill-danger-bg);  color: var(--pk-pill-danger-text); }
.pill-info    { background: var(--pk-pill-info-bg);    color: var(--pk-pill-info-text); }
.pill-neutral { background: var(--pk-pill-neutral-bg); color: var(--pk-pill-neutral-text); }

/* ---- Entity list rows (§03) ----
   One card wraps a list of these: avatar tile + name + meta + status pill.
   Used by the patient list, pet list and other entity collections. */
.entity-list { list-style: none; margin: 0; padding: 0; }
.entity-row {
    display: flex;
    align-items: center;
    gap: var(--pk-space-3);
    padding: 0.7rem 0;
    border-bottom: 1px solid var(--border);
    text-decoration: none;
    color: inherit;
}
.entity-list > li:last-child .entity-row,
.entity-row:last-child { border-bottom: none; }
a.entity-row:hover { background: var(--bg-soft); }
/* The icon/initial tile for non-pet list rows (personnel, alerts, codes,
   foods). Matches the shared ui::Avatar list size (§A3: 38px lists) so every
   list-row tile — pet emoji or person initial — is the one design size. */
.entity-avatar {
    flex: 0 0 auto;
    width: 38px; height: 38px;
    border-radius: 11px;   /* match the shared Avatar 38px tile (design radius) */
    background: var(--pk-surface);
    color: var(--pk-accent-deep);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-family: var(--pk-font-display);
    font-weight: 700;
    font-size: 1.05rem;
    text-transform: uppercase;
}
/* Missing-consent / attention rows get a clay tile (§03). */
.entity-avatar.is-missing { background: var(--pk-clay-bg); color: var(--pk-clay-text); }
.entity-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.entity-name { font-weight: 600; }
.entity-meta { font-size: 0.85rem; color: var(--muted); }
.entity-aside { flex: 0 0 auto; margin-left: 0.5rem; }
/* Dimmed row (e.g. an acknowledged alert). */
.entity-row.is-acknowledged { opacity: 0.55; }

/* ---- Avatar tile (§A3) ----
   The `.avatar` + per-size `.avatar-38/52/64` rules now live in shared.css
   (loaded by every app incl. app-ui) so the mobile app renders the same tile.
   Only the `.pet-head`-scoped phone override stays here (web pet-detail). */

/* ---- "Meine Tiere" grid (§B2) ----
   Compact pet cards instead of one wide near-empty row: avatar + name +
   practice + status pill + chevron, plus a dashed "add a pet" card. One pet →
   a single narrower column (not full-bleed). */
.pet-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    gap: var(--pk-space-4);
    margin: 1rem 0;
}
.pet-grid.is-single { grid-template-columns: minmax(0, 520px); }
.pet-grid-card {
    display: flex;
    align-items: center;
    gap: var(--pk-space-3);
    padding: var(--pk-space-4);
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-card);
    box-shadow: var(--shadow-card);
    text-decoration: none;
    color: inherit;
}
.pet-grid-card:hover { border-color: var(--pk-accent); }
.pet-grid-body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.pet-grid-name { font-family: var(--pk-font-display); font-weight: 700; }
.pet-grid-practice { font-family: var(--pk-font-mono); font-size: 0.75rem; color: var(--pk-ink-muted); }
.pet-grid-card .pill { align-self: flex-start; margin-top: 2px; }
.pet-grid-chevron { flex: 0 0 auto; color: var(--pk-ink-faint); font-size: 1.4rem; line-height: 1; }
/* The "add a pet" affordance: a dashed, quiet card laid out like a pet card —
   a "+" tile + title + subtitle, left-aligned. */
.pet-grid-card.is-add {
    border-style: dashed;
    border-color: var(--pk-border-input);
    background: transparent;
    box-shadow: none;
}
.pet-grid-card.is-add:hover { background: var(--pk-surface); border-color: var(--pk-accent); }
.pet-grid-card.is-add .pet-grid-name { color: var(--pk-accent-deep); }
.pet-grid-add-icon {
    flex: 0 0 auto;
    width: 52px; height: 52px;
    border-radius: 15px;
    background: var(--pk-surface);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-family: var(--pk-font-display);
    font-weight: 700;
    font-size: 1.6rem;
    line-height: 1;
    color: var(--pk-accent-deep);
}

/* ---- Dashboard metric cards (§B1) ----
   Platform-admin landing: real counts as entry points. Each card is LABEL
   (uppercase mono) → VALUE (big mono) → an optional action link, four across.
   Cards that map to a section are links (hover border); pure metrics (pets,
   codes — no admin section) are plain. Offene Jobs goes amber when > 0. */
.metric-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: var(--pk-space-3);
    margin: 1.5rem 0;
}
.metric-card {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    padding: var(--pk-space-4);
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-card);
    box-shadow: var(--shadow-card);
    text-decoration: none;
    color: inherit;
}
a.metric-card { transition: border-color 0.12s ease, box-shadow 0.12s ease; }
a.metric-card:hover { border-color: var(--pk-accent); box-shadow: var(--pk-shadow-card), 0 0 0 1px var(--pk-accent); }
a.metric-card:hover .metric-arrow { transform: translateX(3px); }
.metric-arrow {
    margin-top: 0.1rem;
    font-size: 1rem;
    line-height: 1;
    color: var(--pk-accent-deep);
    transition: transform 0.12s ease;
}
.metric-label {
    font-family: var(--pk-font-mono);
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--pk-ink-muted);
}
.metric-value {
    font-family: var(--pk-font-mono);
    font-weight: 600;
    font-size: 2rem;
    line-height: 1;
    color: var(--pk-ink);
}
/* Offene Jobs amber when > 0 — the card flags attention (design). */
.metric-card.is-warn { border-color: var(--pk-amber-border); }
.metric-card.is-warn .metric-label,
.metric-card.is-warn .metric-arrow { color: var(--pk-amber-text); }

/* ---- Empty states (§03) ----
   Centred icon in a surface circle + one sentence + a primary CTA, instead of
   a bare muted line. (app-ui has its own .empty-state in its main.css.) */
.empty-state { text-align: center; padding: 2rem 1rem; }
.empty-state-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 3.5rem; height: 3.5rem;
    border-radius: var(--pk-radius-pill);
    background: var(--pk-surface);
    font-size: 1.6rem;
    margin-bottom: 0.75rem;
}
.empty-state-text { margin: 0 auto 1rem; color: var(--pk-ink-soft); max-width: 28rem; }

/* ---- Pet-detail header (§03): species tile + name + practice + pill ---- */
.pet-head {
    display: flex;
    align-items: center;
    gap: var(--pk-space-4);
    margin: 1.5rem 0 0.75rem;
}
/* The species tile is now the shared ui::Avatar (size 64); only the text
   block + name + practice line remain bespoke here. */
.pet-head-body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 0.1rem; }
.pet-head-name { margin: 0; }
.pet-head-practice { font-family: var(--pk-font-mono); font-size: 0.8rem; color: var(--pk-ink-muted); }
/* On phones the detail-header avatar steps down 64 → 52px (Redesign v3 R3),
   mirroring the .avatar-52 metrics. The only layout media query in the app. */
@media (max-width: 640px) {
    .pet-head .avatar-64 { width: 52px; height: 52px; border-radius: 15px; }
    .pet-head .avatar-64 .avatar-emoji { font-size: 27px; }
    .pet-head .avatar-64 .avatar-initial { font-size: 23px; }
}

/* ====================== Alerts ====================== */

.alert {
    background: var(--pk-amber-bg);
    color: var(--pk-amber-text);
    border-left: 4px solid var(--pk-amber);
    padding: 14px 16px;
    border-radius: var(--pk-radius-lg);
}
.alert.danger {
    background: var(--pk-clay-bg);
    color: var(--pk-clay-text);
    border-left-color: var(--pk-clay);
}

/* ====================== Monospace token ====================== */

.code-token {
    font-family: var(--pk-font-mono);
    font-weight: 600;
}

/* ====================== Lightbox ====================== */

.lightbox-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(15, 23, 42, 0.85);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1.5rem;
    z-index: 1000;
}
.lightbox {
    background: var(--bg);
    border-radius: var(--radius-card);
    overflow: hidden;
    display: flex;
    flex-direction: column;
    max-width: min(90vw, 1024px);
    max-height: 90vh;
}
.lightbox-stage {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    background: #000;
    min-height: 50vh;
}
.lightbox-img {
    display: block;
    max-width: 100%;
    max-height: calc(90vh - 70px);
    object-fit: contain;
}
.lightbox-nav {
    appearance: none;
    background: rgba(255, 255, 255, 0.15);
    color: #fff;
    border: 0;
    font-size: 2rem;
    line-height: 1;
    width: 2.5rem;
    height: 4rem;
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    cursor: pointer;
    user-select: none;
}
.lightbox-nav:hover:not(:disabled) { background: rgba(255, 255, 255, 0.3); }
.lightbox-nav:disabled { opacity: 0.2; cursor: not-allowed; }
.lightbox-nav-prev { left: 0.5rem; border-radius: 0 0.5rem 0.5rem 0; }
.lightbox-nav-next { right: 0.5rem; border-radius: 0.5rem 0 0 0.5rem; }
.lightbox-actions {
    display: flex;
    gap: 0.5rem;
    padding: 0.75rem 1rem;
    border-top: 1px solid var(--border);
    align-items: center;
    justify-content: flex-end;
}
.lightbox-actions .muted { margin-right: auto; }
.lightbox-meta {
    margin: 0;
    padding: 0.5rem 1rem 0;
    text-align: center;
    font-size: 0.85rem;
}

/* ====================== Log-/metric tabs ====================== */
/* Emitted by the shared HistoryRangeChart and the customer "Eintragen"
   tabs; lived in web-customer/main.css before, leaving the vet history
   tabs unstyled. */
.log-heading { margin-bottom: 0.5rem; }
/* Segmented control (README §5): one tinted pill-track holding the tabs.
   A small inner gap keeps segments from touching (controls-never-touch). */
.log-tabs {
    display: inline-flex;
    flex-wrap: wrap;
    gap: var(--pk-space-1);
    margin: 0 0 1rem;
    padding: 5px;
    background: var(--pk-surface-2);
    border-radius: var(--pk-radius-lg);
}
/* Each tab sheds the ghost button's own chrome so the track reads as one
   control; inactive = quiet ink, active = accent fill. */
.log-tabs .button-ghost {
    background: transparent;
    border-color: transparent;
    color: var(--pk-ink-soft);
    min-height: 2.1rem;
    padding: 0.4rem 0.9rem;
    border-radius: var(--pk-radius-sm);
}
.log-tabs .button-ghost:hover {
    background: color-mix(in srgb, var(--pk-surface-2) 55%, var(--pk-white));
}
.log-tabs .button-ghost.log-tab-active,
.log-tabs .button-ghost.log-tab-active:hover {
    background: var(--pk-accent);
    color: var(--pk-accent-on);
    border-color: transparent;
}

/* Grouped form fields (RedeemForm, admin tenant creation). Referenced
   in markup but never defined. */
.field-group {
    border: 1px solid var(--border);
    border-radius: var(--radius-control);
    padding: 0.75rem 1rem 1rem;
    margin: 0 0 1rem;
}
.field-group > legend {
    padding: 0 0.4rem;
    font-weight: 600;
    color: var(--muted, #6b7280);
}
