/*
 * Mobile-first. The client's stated workflow is "open dog, press Add Treatment,
 * select type, enter a few details, save" — on a phone, probably one-handed,
 * probably outdoors. Big targets, high contrast, no hover-dependent affordances.
 */

/*
 * Brand colours come from the KURU logo: #422714 (the "kuru" wordmark), #a6977d
 * (the dog outline) and #6e6a3b (the strapline).
 *
 * --brand is a fill behind white text — buttons, recall headers, the avatar
 * disc — and --brand-light is a text colour on the light page background, so
 * both stay dark. The chrome (top bar, sidebar, sign-in) uses the light brown
 * instead; see --chrome-* below for how text stays legible on it.
 */
:root {
    --bg: #f7f6f3;
    --surface: #ffffff;
    --surface-2: #f0ede7;
    --border: #ddd8d0;
    --text: #241d18;
    --text-dim: #6b6259;
    /*
     * One step below --text-dim, for field help text only. It stops at 4.5:1 on
     * --surface-2 — the tightest background it lands on, inside a dropzone —
     * rather than going paler: this is read outdoors on a phone, and a hint that
     * washes out in sunlight is a hint nobody reads.
     */
    --text-faint: #736a60;
    --brand: #422714;
    --brand-light: #6b4425;
    --accent: #a6977d;
    --olive: #6e6a3b;

    /*
     * Chrome — top bar, sidebar, sign-in backdrop — is the logo's light brown,
     * the tone that outlines the dog. It is too pale to carry white text
     * (~2.3:1), so text on it is the dark wordmark brown instead: #422714 on
     * #a6977d is 4.79:1, which clears AA. Everything painted on the chrome
     * therefore uses --chrome-* rather than white.
     *
     * A gradient rather than a flat fill, because a solid block of one tone
     * down a full-height sidebar reads as dead weight.
     */
    --chrome-grad: linear-gradient(165deg, #b9ab93 0%, #a6977d 45%, #8f8068 100%);
    --chrome-text: #422714;
    --chrome-dim: rgba(66, 39, 20, .74);
    --chrome-hover: rgba(66, 39, 20, .10);
    --chrome-active: rgba(66, 39, 20, .18);
    --chrome-rule: rgba(66, 39, 20, .20);

    /*
     * The same light brown as a solid fill, for the surfaces that used to be
     * dark brown blocks: buttons, the FAB, recall group headers. Flat rather
     * than the gradient — a ramp inside a 44px button just looks smudged.
     * Theme-independent: a pale primary button reads well on the dark page too.
     */
    --fill: #a6977d;
    --fill-text: #422714;
    --fill-hover: #94836a;

    --green: #1a7f4b;
    --green-bg: #e3f4ea;
    --amber: #a86611;
    --amber-bg: #fdf0dc;
    --red: #b3261e;
    --red-bg: #fce8e6;
    --blue: #1a5f9e;
    --blue-bg: #e4eff9;
    --grey: #6f675e;
    --grey-bg: #edeae5;
    --purple: #6b3fa0;
    --purple-bg: #f0e9f8;

    --radius: 10px;
    --shadow: 0 1px 2px rgba(0, 0, 0, .06), 0 2px 8px rgba(0, 0, 0, .04);
    --tabbar-h: 60px;
    --topbar-h: 54px;
    --sidebar-w: 232px;
}

/*
 * Three states, not two: an explicit choice stamps data-theme on <html>, and the
 * default ("system") stamps nothing so the media query decides. Every token is
 * defined on bare :root first, so nothing depends on a media query to exist.
 */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        --bg: #18140f;
        --surface: #221c16;
        --surface-2: #2c251d;
        --border: #3b322a;
        --text: #ece7e1;
        --text-dim: #a79c90;
        --text-faint: #968b7f;
        --brand: #5a3a22;
        --brand-light: #c2ae91;
        /* Dark mode keeps dark chrome with light text — a pale bar would glare. */
        --chrome-grad: linear-gradient(165deg, #4a3a29 0%, #3a2c1e 45%, #281c11 100%);
        --chrome-text: #f0e7db;
        --chrome-dim: rgba(240, 231, 219, .78);
        --chrome-hover: rgba(255, 255, 255, .10);
        --chrome-active: rgba(255, 255, 255, .18);
        --chrome-rule: rgba(255, 255, 255, .14);

        --green: #5cc98c;
        --green-bg: #16311f;
        --amber: #e0a850;
        --amber-bg: #33260f;
        --red: #f2867d;
        --red-bg: #3a1815;
        --blue: #6fb0e8;
        --blue-bg: #12253a;
        --grey: #a79c90;
        --grey-bg: #2b2620;
        --purple: #b48ce0;
        --purple-bg: #241a33;
        --shadow: 0 1px 2px rgba(0, 0, 0, .3);
    }
}

:root[data-theme="dark"] {
        --bg: #18140f;
        --surface: #221c16;
        --surface-2: #2c251d;
        --border: #3b322a;
        --text: #ece7e1;
        --text-dim: #a79c90;
        --text-faint: #968b7f;
        --brand: #5a3a22;
        --brand-light: #c2ae91;
        /* Dark mode keeps dark chrome with light text — a pale bar would glare. */
        --chrome-grad: linear-gradient(165deg, #4a3a29 0%, #3a2c1e 45%, #281c11 100%);
        --chrome-text: #f0e7db;
        --chrome-dim: rgba(240, 231, 219, .78);
        --chrome-hover: rgba(255, 255, 255, .10);
        --chrome-active: rgba(255, 255, 255, .18);
        --chrome-rule: rgba(255, 255, 255, .14);

        --green: #5cc98c;
        --green-bg: #16311f;
        --amber: #e0a850;
        --amber-bg: #33260f;
        --red: #f2867d;
        --red-bg: #3a1815;
        --blue: #6fb0e8;
        --blue-bg: #12253a;
        --grey: #a79c90;
        --grey-bg: #2b2620;
        --purple: #b48ce0;
        --purple-bg: #241a33;
        --shadow: 0 1px 2px rgba(0, 0, 0, .3);
}


* { box-sizing: border-box; }

body {
    margin: 0;
    padding: 0 0 calc(var(--tabbar-h) + env(safe-area-inset-bottom)) 0;
    background: var(--bg);
    color: var(--text);
    font: 16px/1.5 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
    -webkit-text-size-adjust: 100%;
}

a { color: var(--brand-light); }

/* ---------- top bar ---------- */

.topbar {
    display: flex;
    align-items: center;
    gap: 16px;
    padding: 10px 16px;
    padding-top: calc(10px + env(safe-area-inset-top));
    background: var(--chrome-grad);
    color: var(--chrome-text);
    position: sticky;
    top: 0;
    z-index: 20;
}

.brand { display: flex; align-items: baseline; gap: 8px; text-decoration: none; color: var(--chrome-text); }
.brand-mark { font-weight: 800; letter-spacing: .14em; font-size: 18px; }
.brand-sub { font-size: 12px; opacity: .75; letter-spacing: .06em; text-transform: uppercase; }

/*
 * Page name and trail in the top bar. The name is the one h1 on the page;
 * the crumbs are deliberately small print — orientation, not navigation
 * chrome — and the whole column truncates rather than wrapping the bar.
 */
.titlebar { flex: 1; min-width: 0; display: flex; flex-direction: column; justify-content: center; gap: 1px; }
.titlebar h1 { font-size: 17px; margin: 0; line-height: 1.3; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.crumbs { font-size: 12px; line-height: 1.3; }
.crumbs ol { list-style: none; margin: 0; padding: 0; display: flex; min-width: 0; overflow: hidden; }
.crumbs li { flex: none; white-space: nowrap; }
.crumbs li:last-child { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.crumbs li + li::before { content: "›"; margin: 0 6px; opacity: .5; }
.crumbs a { color: inherit; opacity: .7; text-decoration: none; }
.crumbs a:hover { opacity: 1; text-decoration: underline; }
.crumbs [aria-current="page"] { opacity: .85; font-weight: 600; }

/* On a phone the strapline yields its space to the page name. */
@media (max-width: 479px) {
    .topbar .brand-sub { display: none; }
}

/* Inline SVG, sized and coloured from the element it sits in. */
.icon { display: block; width: 22px; height: 22px; flex: none; }

/*
 * For a label a screen reader needs and the eye does not — a field whose
 * placeholder already says what it is, in a box too tight to spend a line on
 * saying it twice.
 *
 * Clipped rather than `display: none`, which would take it out of the
 * accessibility tree along with the layout and leave the field unnamed.
 */
.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

/* ---------- sidebar (desktop) ---------- */

/*
 * Above 800px the sidebar carries the brand and the destinations, and the top
 * bar stays for the account menu. The bottom tab bar steps aside — which is
 * also what keeps the FAB clear of it on a desktop-width window, where the two
 * used to land in the same corner.
 *
 * The sidebar is fixed, and the body is padded left by its width, so the top
 * bar and the page both start where the sidebar ends.
 */
.sidebar { display: none; }

@media (min-width: 800px) {
    /* The sidebar shows the brand; no need to repeat it in the bar. */
    .topbar .brand { display: none; }

    .sidebar {
        position: fixed;
        top: 0; left: 0; bottom: 0;
        width: var(--sidebar-w);
        display: flex;
        flex-direction: column;
        gap: 4px;
        padding: 16px 12px calc(12px + env(safe-area-inset-bottom));
        background: var(--chrome-grad);
        color: var(--chrome-text);
        z-index: 30;
        isolation: isolate;
    }

    /*
     * Two soft, very low-contrast pools of light over the gradient, so a tall
     * column has some depth without turning into a pattern that competes with
     * the nav labels sitting on top of it.
     */
    .sidebar::before {
        content: "";
        position: absolute;
        inset: 0;
        z-index: -1;
        pointer-events: none;
        background:
            radial-gradient(420px 260px at 110% 4%, rgba(255, 252, 245, .28), transparent 70%),
            radial-gradient(320px 320px at -20% 62%, rgba(110, 106, 59, .14), transparent 70%);
    }

    body { padding: 0 0 32px var(--sidebar-w); }
    .fab { bottom: 24px; }
}

.sidebar-brand {
    display: flex;
    align-items: baseline;
    gap: 8px;
    flex-wrap: wrap;
    padding: 8px 10px 18px;
    text-decoration: none;
    color: var(--chrome-text);
}

/* Only the main list scrolls; the admin footer below it stays put. */
.sidebar-nav { display: flex; flex-direction: column; gap: 2px; flex: 1; overflow-y: auto; }
.sidebar-foot {
    display: flex;
    flex-direction: column;
    gap: 2px;
    flex: none;
    border-top: 1px solid var(--chrome-rule);
    margin-top: 10px;
}
.sidebar-nav a,
.sidebar-foot a {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 12px;
    border-radius: 9px;
    text-decoration: none;
    color: var(--chrome-dim);
    font-size: 14px;
    font-weight: 600;
}
.sidebar-nav a:hover,
.sidebar-foot a:hover { background: var(--chrome-hover); color: var(--chrome-text); }
.sidebar-nav a.active,
.sidebar-foot a.active { background: var(--chrome-active); color: var(--chrome-text); }
.sidebar-nav .icon,
.sidebar-foot .icon { width: 20px; height: 20px; }

.sidebar-label {
    padding: 12px 12px 6px;
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: var(--chrome-dim);
}


/* ---------- bottom tab bar (mobile) ---------- */

.tabbar {
    position: fixed;
    bottom: 0; left: 0; right: 0;
    height: calc(var(--tabbar-h) + env(safe-area-inset-bottom));
    padding-bottom: env(safe-area-inset-bottom);
    display: flex;
    background: var(--surface);
    border-top: 1px solid var(--border);
    z-index: 20;
}
.tabbar a {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    text-decoration: none;
    color: var(--text-dim);
    font-size: 11px;
    font-weight: 500;
}
.tabbar a.active { color: var(--brand-light); }
.tabbar .icon { width: 21px; height: 21px; }

/*
 * Must sit after `.tabbar { display: flex }`, not with the other desktop rules
 * above. A media query adds no specificity, so the later plain rule would win
 * and the bar would stay on screen at desktop widths — which is what used to
 * leave the "+ Add treatment" button stranded behind the Settings tab.
 */
@media (min-width: 800px) {
    .tabbar { display: none; }
}

/* ---------- layout ---------- */

.page { max-width: 1100px; margin: 0 auto; padding: 16px; }

h1 { font-size: 22px; margin: 0 0 4px; letter-spacing: -.01em; }
h2 { font-size: 17px; margin: 26px 0 10px; }
.subtitle { color: var(--text-dim); font-size: 14px; margin: 0 0 18px; }

/*
 * The gap hangs off each card rather than `.card + .card`, because cards are
 * not always siblings — on the reminder settings page a <form> opens between
 * the first card and the rest, so the adjacent-sibling rule never matched and
 * those two sat flush against each other. A trailing margin on the last card
 * is harmless; a missing gap mid-page is not.
 */
.card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    overflow: hidden;
    margin-bottom: 14px;
}
.card-head { padding: 13px 16px; border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 10px; }
.card-head h2 { margin: 0; font-size: 15px; }
.card-body { padding: 16px; }

/* ---------- stat tiles ---------- */

.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 10px; margin-bottom: 18px; }

/* Phones: all four tiles on one row — the glance the page opens with. The
   labels shrink and may wrap inside their tile rather than wrapping the row. */
@media (max-width: 799px) {
    .stats { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 7px; }
    .stat { padding: 10px 6px; text-align: center; }
    .stat-value { font-size: 22px; }
    /* One line per label, all four tiles the same single-line height. "now"
       is dropped from the recall tile below 800px (see the template), so the
       longest label is TO RECALL / NO RECORD and the type can stay readable. */
    .stat-label { font-size: clamp(8px, 2.6vw, 10px); letter-spacing: 0; white-space: nowrap; }
}
.stat {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 13px 15px;
    text-decoration: none;
    color: inherit;
    display: block;
}
.stat-value { font-size: 27px; font-weight: 700; line-height: 1.1; letter-spacing: -.02em; }
.stat-label { font-size: 12px; color: var(--text-dim); text-transform: uppercase; letter-spacing: .05em; margin-top: 3px; }
.stat.red .stat-value { color: var(--red); }
.stat.amber .stat-value { color: var(--amber); }
.stat.blue .stat-value { color: var(--blue); }
.stat.grey .stat-value { color: var(--grey); }

/*
 * A caveat pinned to the tile it qualifies — an unmissable red flag in the
 * corner, with the paragraph it replaced one tap away. The wrap exists so the
 * badge and its popover can anchor to the tile without living inside the <a>.
 */
.stat-wrap { position: relative; }
.stat-wrap .stat { height: 100%; }
.stat-flag {
    position: absolute;
    top: -7px; right: -7px;
    width: 24px; height: 24px;
    border-radius: 50%;
    border: 2px solid var(--bg);
    background: var(--red);
    color: #fff;
    font-size: 14px;
    font-weight: 800;
    line-height: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    z-index: 2;
    padding: 0;
}
.stat-flag:hover { transform: scale(1.08); }
.stat-flag-pop {
    display: none;
    position: absolute;
    top: 20px; right: 0;
    z-index: 40;
    width: min(320px, 86vw);
    /* The palette the inline notices had: amber on amber, not a white card —
       a warning should not dress like the tiles it floats over. */
    background: var(--amber-bg);
    color: var(--amber);
    border: 1px solid color-mix(in srgb, var(--amber) 35%, transparent);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: 12px 14px;
    font-size: 13.5px;
    line-height: 1.45;
}
.stat-flag-pop strong { display: block; margin-bottom: 3px; }
.stat-flag-pop.is-open { display: block; }

/* Phones: a 320px card anchored to a quarter-width tile runs off the left
   edge of the screen. Fixed positioning frees it from the tile horizontally
   (edge to edge, viewport insets) while `top: auto` keeps its static
   position — vertically it still opens just under the tile row. */
@media (max-width: 799px) {
    .stat-flag-pop {
        position: fixed;
        left: 12px;
        right: 12px;
        top: auto;
        width: auto;
    }
}

/* ---------- status pills ---------- */

.pill {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 3px 9px;
    border-radius: 999px;
    font-size: 12px;
    font-weight: 600;
    white-space: nowrap;
    line-height: 1.4;
}
.pill.green  { background: var(--green-bg);  color: var(--green); }
.pill.amber  { background: var(--amber-bg);  color: var(--amber); }
.pill.red    { background: var(--red-bg);    color: var(--red); }
.pill.blue   { background: var(--blue-bg);   color: var(--blue); }
.pill.grey   { background: var(--grey-bg);   color: var(--grey); }
.pill.purple { background: var(--purple-bg); color: var(--purple); }
.pill.muted  { background: transparent;      color: var(--text-dim); }

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

/* ---------- dashboard: cards on mobile, table on desktop ---------- */

.dog-cards { display: grid; gap: 10px; }
.dog-card {
    display: block;
    background: var(--surface);
    border: 1px solid var(--border);
    border-left: 4px solid var(--border);
    border-radius: var(--radius);
    padding: 12px 14px;
    text-decoration: none;
    color: inherit;
    box-shadow: var(--shadow);
}
.dog-card.red { border-left-color: var(--red); }
.dog-card.amber { border-left-color: var(--amber); }
.dog-card.green { border-left-color: var(--green); }
.dog-card.blue { border-left-color: var(--blue); }
.dog-card.grey { border-left-color: var(--grey); }
.dog-card.purple { border-left-color: var(--purple); }

.dog-card-top { display: flex; align-items: center; gap: 10px; margin-bottom: 9px; }
.dog-name { font-weight: 650; font-size: 16px; }
.dog-meta { font-size: 12px; color: var(--text-dim); margin-left: auto; text-align: right; }
.chips { display: flex; flex-wrap: wrap; gap: 5px; }
.chip {
    font-size: 11px;
    padding: 2px 7px;
    border-radius: 6px;
    font-weight: 600;
    background: var(--surface-2);
    color: var(--text-dim);
}
.chip.green  { background: var(--green-bg);  color: var(--green); }
.chip.amber  { background: var(--amber-bg);  color: var(--amber); }
.chip.red    { background: var(--red-bg);    color: var(--red); }
.chip.blue   { background: var(--blue-bg);   color: var(--blue); }
.chip.purple { background: var(--purple-bg); color: var(--purple); }

.table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
table.grid { width: 100%; border-collapse: collapse; font-size: 14px; }
table.grid th {
    text-align: left;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--text-dim);
    padding: 9px 12px;
    border-bottom: 1px solid var(--border);
    white-space: nowrap;
}
table.grid td { padding: 10px 12px; border-bottom: 1px solid var(--border); vertical-align: middle; }
table.grid tr:last-child td { border-bottom: 0; }
table.grid tbody tr:hover { background: var(--surface-2); }
table.grid a { text-decoration: none; color: inherit; font-weight: 600; }

/*
 * Visibility only. These deliberately set no display value for the visible
 * state: both are defined after the component classes they get combined with,
 * so a `display: block` here would win on equal specificity and quietly flatten
 * the component's own layout — which is what stopped `.dog-cards` from being a
 * grid, taking its row gap with it.
 */
.only-desk { display: none; }
@media (min-width: 800px) {
    .only-mobile { display: none; }
    .only-desk { display: revert; }
}

/*
 * The dashboard filter (filter_controller.js) hides rows and containers via
 * the hidden attribute. The component display values above outrank the UA
 * stylesheet's [hidden] rule, so restate it where the filter toggles it.
 */
.dog-card[hidden], .dog-cards[hidden], .only-desk[hidden], table.grid tr[hidden], .btn[hidden] { display: none !important; }

/* ---------- confirm dialog ---------- */

/*
 * The app's own box, in place of window.confirm — which lands top-centre in a
 * system font with the hostname above it, and on a phone in the field reads as
 * though something has gone wrong rather than as the app asking a question.
 *
 * Above everything, the panel and the user menu included: a question is often
 * about the thing covering the page.
 */
.dialog {
    position: fixed;
    inset: 0;
    z-index: 150;
    display: grid;
    place-items: center;
    padding: 20px;
}
.dialog[hidden] { display: none; }

.dialog-backdrop {
    position: absolute;
    inset: 0;
    background: rgb(0 0 0 / .55);
    animation: dialog-fade .12s ease-out;
}

.dialog-card {
    position: relative;
    width: min(26rem, 100%);
    padding: 22px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    animation: dialog-rise .16s cubic-bezier(.32, .72, 0, 1);
}

.dialog-title { margin: 0 0 6px; font-size: 18px; font-weight: 650; }
.dialog-message { margin: 0; color: var(--text-dim); line-height: 1.5; font-size: 14px; }
.dialog-type { margin-top: 16px; }
.dialog-type[hidden] { display: none; }

/*
 * Full-width buttons stacked, not a row of two. This is answered with a thumb,
 * often about something that cannot be undone: two targets a finger's width
 * apart are how you strike out the wrong record. Column-reverse puts the safe
 * one nearest the thumb while reading order keeps it first.
 */
.dialog-actions {
    display: flex;
    flex-direction: column-reverse;
    gap: 10px;
    margin-top: 22px;
}
.dialog-actions .btn { width: 100%; }
.dialog-actions .btn[hidden] { display: none !important; }
.dialog-actions .btn:disabled { opacity: .45; cursor: not-allowed; }

@keyframes dialog-fade {
    from { opacity: 0; }
    to { opacity: 1; }
}
@keyframes dialog-rise {
    from { opacity: 0; transform: translateY(8px) scale(.98); }
    to { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
    .dialog-backdrop, .dialog-card { animation: none; }
}

/* The page behind stays put while the question is up. */
body.has-dialog { overflow: hidden; }

/* ---------- off-canvas panel (right slide-in) ---------- */

/*
 * z-order: the tabbar sits at 20 and the topbar below that; the backdrop and
 * panel go above both, under nothing but the user menu dropdown (80).
 */
.offcanvas-backdrop {
    position: fixed;
    inset: 0;
    z-index: 70;
    background: rgb(0 0 0 / .38);
    opacity: 0;
    visibility: hidden;
    transition: opacity .2s ease, visibility .2s ease;
}
.offcanvas-backdrop.is-open { opacity: 1; visibility: visible; }

.offcanvas {
    position: fixed;
    inset: 0 0 0 auto;
    z-index: 71;
    display: flex;
    flex-direction: column;
    width: min(var(--offcanvas-w, 30rem), 100%);
    background: var(--surface);
    border-left: 1px solid var(--border);
    box-shadow: var(--shadow);
    visibility: hidden;
    /* Only while it is shut. An element with a transform becomes the containing
       block for its position:fixed children — leave one on the open state and
       anything inside that pins itself to the viewport lands in the wrong
       place. */
    transform: translateX(100%);
    transition: transform .24s ease, visibility .24s ease;
}
.offcanvas.is-open { visibility: visible; transform: none; }

.offcanvas-head {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 14px 18px;
    border-bottom: 1px solid var(--border);
}
.offcanvas-head h2 { margin: 0; font-size: 17px; flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.offcanvas-close {
    border: 0;
    background: none;
    color: var(--text-dim);
    font-size: 17px;
    line-height: 1;
    padding: 8px;
    margin: -8px -8px -8px 0;
    cursor: pointer;
    border-radius: var(--radius);
}
.offcanvas-close:hover { background: var(--surface-2); color: var(--text); }

.offcanvas-print {
    border: 0;
    background: none;
    color: var(--text-dim);
    padding: 8px;
    margin: -8px 0;
    cursor: pointer;
    border-radius: var(--radius);
    display: flex;
}
.offcanvas-print:hover { background: var(--surface-2); color: var(--text); }
.offcanvas-print .icon { width: 19px; height: 19px; }
/* display:flex above outranks the UA [hidden] rule — same trap as the filter
   button; the controller only reveals this for printable content. */
.offcanvas-print[hidden] { display: none !important; }

.offcanvas-body { flex: 1 1 auto; overflow-y: auto; min-height: 0; padding: 16px 18px; }

.offcanvas-foot {
    display: flex;
    justify-content: center;
    padding: 12px 18px calc(12px + env(safe-area-inset-bottom));
    border-top: 1px solid var(--border);
    font-size: 13px;
}

/* The page behind stays put while the panel is over it. */
body.has-offcanvas { overflow: hidden; }

/*
 * A panel open over a page is what somebody meant to put on paper. The page
 * behind it is not, and printing both is how you get a record on page two. So
 * the panel stops being a slide-in and becomes the page: static, full width,
 * no chrome, nothing clipped by the scroller it lives in on screen.
 */
/* On paper and only on paper — the printed copy's own heading. */
.only-print { display: none; }

@media print {
    .no-print { display: none !important; }
    .only-print { display: revert; }

    body.has-offcanvas { overflow: visible; padding: 0; }
    body.has-offcanvas .topbar,
    body.has-offcanvas .sidebar,
    body.has-offcanvas .tabbar,
    body.has-offcanvas .page,
    body.has-offcanvas .offcanvas-backdrop,
    body.has-offcanvas .offcanvas-head,
    body.has-offcanvas .offcanvas-foot { display: none !important; }
    body.has-offcanvas .offcanvas {
        position: static;
        width: 100%;
        border: 0;
        box-shadow: none;
        transform: none;
        visibility: visible;
    }
    body.has-offcanvas .offcanvas-body { overflow: visible; }
}

/* The shape of what is on its way, held for as long as the network takes. */
.panel-skeleton { display: flex; flex-direction: column; gap: 12px; }
.skeleton-bar {
    display: block;
    height: 12px;
    border-radius: 999px;
    background: linear-gradient(90deg, var(--surface-2) 25%, var(--border) 50%, var(--surface-2) 75%);
    background-size: 240% 100%;
    animation: skeleton-sweep 1.3s ease-in-out infinite;
}
@keyframes skeleton-sweep {
    from { background-position: 120% 0; }
    to { background-position: -120% 0; }
}
@media (prefers-reduced-motion: reduce) {
    .skeleton-bar { animation: none; }
    .offcanvas, .offcanvas-backdrop { transition: none; }
}

/* ---------- recall board ---------- */

.recall-group { margin-bottom: 16px; }
.recall-head {
    display: flex;
    align-items: baseline;
    gap: 10px;
    padding: 11px 15px;
    background: var(--fill);
    color: var(--fill-text);
    border-radius: var(--radius) var(--radius) 0 0;
}
.recall-head h3 { margin: 0; font-size: 15px; }
.recall-lead { font-size: 12px; opacity: .8; margin-left: auto; }
.recall-body { background: var(--surface); border: 1px solid var(--border); border-top: 0; border-radius: 0 0 var(--radius) var(--radius); }
.recall-row { display: flex; align-items: center; gap: 12px; padding: 12px 15px; border-bottom: 1px solid var(--border); flex-wrap: wrap; }
.recall-row:last-child { border-bottom: 0; }

/* Already arranged: stays on the board as a receipt, but reads as settled. */
.recall-row.arranged { opacity: .72; }
.recall-row.arranged .recall-when { color: var(--blue); }
.recall-row.arranged .recall-when a { color: inherit; text-decoration: none; }
.recall-row.arranged .recall-when a:hover { text-decoration: underline; }
.recall-dog { font-weight: 650; min-width: 90px; text-decoration: none; color: inherit; }
.recall-when { font-size: 13px; color: var(--text-dim); }
.recall-action { margin-left: auto; display: flex; gap: 6px; }
.recall-urgent { color: var(--red); font-weight: 650; }
.recall-note {
    padding: 9px 15px;
    background: var(--surface-2);
    font-size: 12px;
    color: var(--text-dim);
    border-top: 1px solid var(--border);
}

/* ---------- forms ---------- */

.form-row { margin-bottom: 15px; }
label { display: block; font-size: 13px; font-weight: 600; margin-bottom: 5px; color: var(--text); }
/* A label with a secondary action beside it, e.g. "Vaccines & products" next to Product. */
.form-row-label-line { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.form-row-label-line label { margin-bottom: 5px; }
.label-link { font-size: 12px; font-weight: 600; white-space: nowrap; }
/*
 * .hint is written by hand in templates; .help-text is what Symfony's `help`
 * option emits. They say the same kind of thing, so they get the same styling —
 * without the second selector, help text on any form_widget()-rendered page
 * (dog edit, treatments, admin) falls back to full-size body copy.
 */
.hint, .help-text {
    font-size: 12px;
    line-height: 1.45;
    color: var(--text-faint);
    font-weight: 400;
    margin-top: 4px;
}

input[type=text], input[type=date], input[type=email], input[type=password],
input[type=number], input[type=search], select, textarea {
    width: 100%;
    padding: 11px 12px;
    font-size: 16px; /* 16px stops iOS zooming on focus */
    font-family: inherit;
    color: var(--text);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 8px;
    -webkit-appearance: none;
    appearance: none;
}
select {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8'%3E%3Cpath fill='%236b7280' d='M1 1l5 5 5-5'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 12px center;
    padding-right: 34px;
}
input:focus, select:focus, textarea:focus { outline: 2px solid var(--brand-light); outline-offset: 1px; border-color: transparent; }
textarea { min-height: 80px; resize: vertical; }

/* A read-only field has to look unavailable, not just refuse to take focus —
   the profile page shows the sign-in email this way. */
input:disabled, select:disabled, textarea:disabled {
    background: var(--surface-2);
    color: var(--text-dim);
    cursor: not-allowed;
}

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 11px 18px;
    font-size: 15px;
    font-weight: 600;
    font-family: inherit;
    border: 1px solid transparent;
    border-radius: 8px;
    background: var(--fill);
    color: var(--fill-text);
    text-decoration: none;
    cursor: pointer;
    min-height: 44px; /* thumb-sized */
}
.btn:hover { background: var(--fill-hover); }
.btn.secondary { background: var(--surface); color: var(--text); border-color: var(--border); }
.btn.secondary:hover { background: var(--surface-2); }
.btn.small { padding: 6px 12px; font-size: 13px; min-height: 34px; }
.btn.danger { background: var(--red); }
/* Same gradient as the DEV badge — for the one button in the app that
   removes a record outright rather than voiding or striking it through. */
.btn.danger-strong {
    background: linear-gradient(90deg, #f97316, #dc2626);
    color: #fff;
    border-color: transparent;
}
.btn.danger-strong:hover { background: linear-gradient(90deg, #ea6a0f, #c81e1e); }
.btn.block { width: 100%; }
.btn-row { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 18px; }

/*
 * Every label sits beside its control rather than stacked above it, so all the
 * text in the row — field labels, the search text, "Include retired", the
 * button — shares one vertical centre line.
 *
 * This is why the cells are laid out as rows and everything is centred rather
 * than bottom-aligned. Stacking the labels put them at the top of their cell,
 * where no amount of aligning the controls could bring them onto that line.
 *
 * The controls still declare a height: a bare <input> is a few pixels shorter
 * than .kselect-button (which is display:flex, so block-level and exactly its
 * min-height), and unequal control heights make the row's baseline wander as
 * cells wrap.
 */
.filters { display: flex; gap: 6px 10px; flex-wrap: wrap; align-items: center; margin-bottom: 12px; }
.filters .form-row { display: flex; align-items: center; gap: 6px; margin-bottom: 0; flex: 1 1 200px; }
.filters .form-row label {
    flex: none;
    margin-bottom: 0;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--text-dim);
    white-space: nowrap;
}
/* `select` covers the moment before the listbox controller has replaced it, and
   the case where its script never runs at all. */
.filters .form-row > input,
.filters .form-row > select,
.filters .form-row > .kselect { flex: 1; min-width: 0; }
/*
 * Compact controls, 38px rather than the 46px a form field gets elsewhere. This
 * is a filter bar above the thing you came to look at, and on a phone it was
 * costing four rows before the first dog appeared.
 *
 * The height comes off the padding, not the type: 16px is what stops iOS zooming
 * the page when the search field takes focus, so it stays. 38px matches the
 * inline controls in table.grid, which is the other place this app shrinks a
 * field to fit more on screen.
 */
.filters .form-row > input,
.filters .form-row > select,
.filters .kselect-button { min-height: 38px; padding: 8px 10px; }
.filters .form-row > select { padding-right: 28px; background-position: right 9px center; }
/* Beats `.filters .form-row`'s own flex, which is more specific than a lone class. */
.filters .form-row.filters-check { flex: 0 0 auto; }
.filters .btn { min-height: 38px; padding: 8px 14px; font-size: 14px; }

/*
 * Two to a line on a phone. The 200px basis above cannot fit a pair into a
 * screen narrower than about 440px, so every filter took a line to itself —
 * seven of them on the audit trail before the first entry appeared. Half and
 * half instead, which is shorter than what it replaces even with the label
 * moved above its control; beside it there is nothing left for a select to sit
 * in at 160px.
 *
 * Search keeps the whole width: it is the one field here that is typed into
 * rather than picked from, and it holds a sentence rather than a word.
 */
@media (max-width: 479px) {
    .filters .form-row {
        flex: 1 1 calc(50% - 5px);
        flex-direction: column;
        align-items: stretch;
        gap: 3px;
    }
    .filters .form-row:has(> input[type="search"]) { flex-basis: 100%; }
    /* Last on its own line, and worth the full width as a target. */
    .filters > .btn { flex: 1 1 100%; }
}

/*
 * The fold. Even two to a line, seven controls and six chips were most of the
 * first screen on a phone, and the entries — the thing anybody came for —
 * started below them. So the filter folds itself away and says in words what it
 * is doing, which a filter you cannot see has to.
 *
 * <details> rather than a controller: the browser already does this, it works
 * with the script off, and it is a real disclosure to a screen reader rather
 * than a div pretending to be one.
 *
 * Card-like by hand rather than `.card`, which sets `overflow: hidden` — that
 * would clip the listbox the moment one of the selects inside was opened.
 */
.filter-fold {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    margin-bottom: 12px;
}

.filter-summary {
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 11px 13px;
    cursor: pointer;
    user-select: none;
    -webkit-user-select: none;
    /* The default triangle goes; the caret sits at the far end, where the thumb
       reaching for it is not covering the words it is about. */
    list-style: none;
    border-radius: calc(var(--radius) - 1px);
}
.filter-summary::-webkit-details-marker { display: none; }
.filter-summary:hover { background: var(--surface-2); }
.filter-summary-mark { flex: none; font-size: 15px; color: var(--text-dim); }
.filter-summary-text {
    flex: 1;
    min-width: 0;
    font-size: 13px;
    /* One line: the summary is the thing standing in for the filter, and a
       summary that grows to three lines is not saving anybody the room. */
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.filter-summary-text strong { font-weight: 650; }
.filter-summary-text .subtle { color: var(--text-dim); }
.filter-caret { flex: none; color: var(--text-dim); line-height: 1; transition: transform .15s ease; }
.filter-fold[open] .filter-caret { transform: rotate(180deg); }
/* Open, the summary is the card's own heading and wants the line every other
   card head has — and square corners where that line now runs. */
.filter-fold[open] .filter-summary {
    border-bottom: 1px solid var(--border);
    border-radius: calc(var(--radius) - 1px) calc(var(--radius) - 1px) 0 0;
}
.filter-fold-body { padding: 13px; }
.filter-fold-body .audit-chips { margin-bottom: 0; }

@media (prefers-reduced-motion: reduce) {
    .filter-caret { transition: none; }
}

@media (min-width: 800px) {
    /*
     * Nothing to fold away up here — the filter was never the thing in the way —
     * so the summary goes, the card goes with it, and the controls sit on the
     * page exactly as they did before there was a fold.
     */
    .filter-summary { display: none; }
    .filter-fold { background: none; border: 0; box-shadow: none; margin-bottom: 0; }
    .filter-fold-body { padding: 0; }
    /* Without the card's padding to close the gap, the chips want their own
       margin back or the entries butt straight up against them. */
    .filter-fold-body .audit-chips { margin-bottom: 12px; }

    /*
     * And a closed <details> has to actually give up its contents. Styling the
     * child used to be enough, because `display` is how the browser hid them;
     * Chrome now hides them with content-visibility on ::details-content, which
     * no author rule on a child can reach — so a desktop arriving on an
     * unfiltered page would find a filter laid out inside a box 0px tall.
     */
    .filter-fold::details-content { content-visibility: visible; block-size: auto; }
}

/* The count on the left, the page's one real action on the right — the line
   the Dogs page opens with. */
.page-actions { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 12px; }
.page-actions .subtitle { margin-bottom: 0; }

/*
 * A checkbox and its text on one line, for the places where the stacked
 * label-above-control layout makes no sense. The second selector only exists to
 * match the specificity of `.filters .form-row label`, which would otherwise
 * impose the uppercase field-label styling on it.
 */
.check-inline,
.filters .form-row label.check-inline {
    display: flex;
    align-items: center;
    gap: 7px;
    margin: 0;
    font-size: 14px;
    font-weight: 400;
    text-transform: none;
    letter-spacing: 0;
    color: var(--text);
    cursor: pointer;
}
.check-inline input[type=checkbox] {
    width: 18px; height: 18px;
    flex: none;
    margin: 0;
    accent-color: var(--brand-light);
    cursor: pointer;
}

/* ---------- dog page ---------- */

.dog-header { display: flex; gap: 16px; align-items: flex-start; margin-bottom: 8px; flex-wrap: wrap; }
.dog-photo {
    width: 104px; height: 104px; border-radius: 12px; object-fit: cover;
    background: var(--surface-2); flex: none;
    display: flex; align-items: center; justify-content: center; font-size: 40px;
}
/*
 * The thumbnail is also the upload control. The overlay is a small corner
 * badge rather than a full-tile tint so it stays visible on touch (there is
 * no hover to discover it with) without permanently covering the photo.
 */
.photo-tile { position: relative; flex: none; border-radius: 12px; display: block; }
.photo-tile > a { display: block; border-radius: 12px; overflow: hidden; }
.photo-tile > a:focus-visible,
a.photo-tile:focus-visible { outline: 2px solid var(--brand-light); outline-offset: 2px; }
.photo-tile-overlay {
    position: absolute;
    left: -4px;
    bottom: -4px;
    width: 28px;
    height: 28px;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 2px solid var(--surface);
    border-radius: 50%;
    background: var(--brand);
    color: #fff;
    cursor: pointer;
    box-shadow: 0 1px 3px rgba(0, 0, 0, .35);
}
.photo-tile-overlay .icon { width: 15px; height: 15px; }
.photo-tile-overlay:hover { background: var(--brand-light); }
.photo-tile-overlay:focus-visible { outline: 2px solid var(--brand-light); outline-offset: 2px; }

.dog-facts { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 10px 18px; margin-top: 6px; }
.fact-label { font-size: 11px; text-transform: uppercase; letter-spacing: .05em; color: var(--text-dim); }
.fact-value { font-size: 14px; font-weight: 500; }
.fact-value.missing { color: var(--text-dim); font-style: italic; font-weight: 400; }

.status-strip { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 9px; margin: 16px 0; }
.status-tile { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 11px 13px; }
.status-tile .type { font-size: 11px; text-transform: uppercase; letter-spacing: .05em; color: var(--text-dim); margin-bottom: 5px; }
.status-tile .when { font-size: 12px; color: var(--text-dim); margin-top: 5px; }

/* An open plan on the tile — the answer to "I planned it, where did it go?". */
.status-tile .status-plan { font-size: 12px; color: var(--blue); margin-top: 6px; padding-top: 6px; border-top: 1px dashed var(--border); }
.status-tile .status-plan .status-plan-notes { color: var(--text-dim); margin-top: 2px; }
.status-tile .status-plan form { margin-top: 6px; }

.timeline { list-style: none; margin: 0; padding: 0; }
.timeline li { display: flex; gap: 12px; padding: 12px 0; border-bottom: 1px solid var(--border); }
.timeline li:last-child { border-bottom: 0; }
.timeline .tl-action { margin-left: auto; align-self: center; }
.timeline .tl-date { font-size: 13px; color: var(--text-dim); min-width: 84px; flex: none; font-variant-numeric: tabular-nums; }
.timeline .tl-main { flex: 1; min-width: 0; }
/* The row's title opens the record; it reads as text until asked. */
.timeline .tl-link { color: inherit; text-decoration: none; }
.timeline .tl-link:hover { text-decoration: underline; }
.timeline .tl-title { font-weight: 600; font-size: 14px; }
.timeline .tl-sub { font-size: 13px; color: var(--text-dim); margin-top: 2px; }
.timeline .voided { text-decoration: line-through; opacity: .55; }

.fab {
    position: fixed;
    right: 16px;
    bottom: calc(var(--tabbar-h) + 16px + env(safe-area-inset-bottom));
    z-index: 15;
    box-shadow: 0 4px 14px rgba(0, 0, 0, .22);
    border-radius: 999px;
    padding: 14px 22px;
}
@media (min-width: 800px) { .fab { bottom: 24px; } }

/* ---------- misc ---------- */

.flash { padding: 12px 15px; border-radius: var(--radius); margin-bottom: 14px; font-size: 14px; font-weight: 500; }
.flash-success { background: var(--green-bg); color: var(--green); }
.flash-error { background: var(--red-bg); color: var(--red); }
.flash-warning { background: var(--amber-bg); color: var(--amber); }
.flash-info { background: var(--blue-bg); color: var(--blue); }

.empty { text-align: center; padding: 40px 20px; color: var(--text-dim); }
.empty .big { font-size: 40px; margin-bottom: 10px; }

.notice {
    background: var(--amber-bg);
    color: var(--amber);
    border-radius: var(--radius);
    padding: 12px 15px;
    font-size: 13px;
    margin-bottom: 14px;
    line-height: 1.5;
}
.notice strong { display: block; margin-bottom: 3px; }

.muted { color: var(--text-dim); }
.nowrap { white-space: nowrap; }
.right { text-align: right; }
.tabular { font-variant-numeric: tabular-nums; }

/* ---------- login ---------- */

/*
 * Sign-in is the one screen with no nav on it, so it carries the brand itself:
 * the gradient fills the viewport and the card floats on top. The top bar is
 * hidden here — it would only repeat the wordmark already on the logo.
 *
 * padding is zeroed because the desktop rule reserves room on the left for a
 * sidebar that this page never renders, which would push the card off centre.
 *
 * Centring is `margin: auto` on the flex item rather than `align-items: center`
 * so that a card taller than the viewport — a short window, or an error message
 * pushing the form down — overflows downward and stays scrollable instead of
 * having its top cut off.
 */
.is-login {
    background: var(--chrome-grad);
    min-height: 100dvh;
    padding: 0;
    display: flex;
}
.is-login .topbar { display: none; }
.is-login .page { margin: auto; }

.login-wrap { width: 100%; max-width: 380px; margin: 0 auto; }
.login-wrap .card-body { padding: 24px; }
.login-title { text-align: center; margin-bottom: 6px; }
.login-sub { text-align: center; color: var(--text-dim); font-size: 13px; margin-bottom: 22px; }


/* ---------- photo gallery ---------- */

/*
 * auto-fill rather than auto-fit: with one photo, auto-fit would stretch it
 * across the whole row. This keeps every tile the same size whatever the count.
 */
.gallery {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(104px, 1fr));
    gap: 8px;
}

.gallery-item { position: relative; margin: 0; }
.gallery-item a { display: block; border-radius: var(--radius); overflow: hidden; }
.gallery-item img {
    display: block;
    width: 100%;
    aspect-ratio: 1;
    object-fit: cover;
    background: var(--surface-2);
}
.gallery-item a:focus-visible { outline: 2px solid var(--brand-light); outline-offset: 2px; }

.gallery-remove {
    position: absolute;
    top: 5px; right: 5px;
    width: 26px; height: 26px;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 0;
    border-radius: 50%;
    background: rgba(0, 0, 0, .55);
    color: #fff;
    font-size: 17px;
    line-height: 1;
    cursor: pointer;
    opacity: 1;
}
@media (hover: hover) {
    .gallery-remove { opacity: 0; transition: opacity .12s ease; }
    .gallery-item:hover .gallery-remove,
    .gallery-remove:focus-visible { opacity: 1; }
}
.gallery-remove:hover { background: var(--red); }

/* ---------- file drop zone ---------- */

.dropzone {
    border: 2px dashed var(--border);
    border-radius: var(--radius);
    background: var(--surface-2);
    padding: 22px 16px;
    text-align: center;
    cursor: pointer;
    transition: border-color .12s ease, background .12s ease;
}
.dropzone:hover { border-color: var(--accent); }
.dropzone.is-over { border-color: var(--brand-light); background: var(--surface); }

.dropzone-empty .icon { width: 30px; height: 30px; margin: 0 auto 8px; color: var(--text-dim); }
.dropzone-empty p { margin: 0; font-size: 14px; }
.dropzone-empty .hint { margin-top: 3px; font-size: 12px; color: var(--text-faint); }

.dropzone-file { display: flex; align-items: center; gap: 12px; text-align: left; }
.dropzone-preview {
    width: 48px; height: 48px; flex: none;
    border-radius: 8px;
    object-fit: cover;
    background: var(--surface);
}
.dropzone-meta { flex: 1; min-width: 0; }
.dropzone-meta strong { display: block; font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dropzone-meta span { font-size: 12px; color: var(--text-dim); }
.dropzone-meta span.is-error { color: var(--red); font-weight: 600; }

/* The dog edit form's photograph field — see vich_image_widget in form/kuru_theme.html.twig. */
.photo-field-current {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px;
    margin-bottom: 10px;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--radius);
}
.photo-field-thumb {
    width: 56px; height: 56px; flex: none;
    border-radius: 8px;
    object-fit: cover;
    background: var(--surface);
}
.photo-field-meta { flex: 1; min-width: 0; }
.photo-field-meta strong { display: block; font-size: 14px; }
.photo-field-delete {
    display: flex;
    align-items: center;
    gap: 7px;
    margin: 4px 0 0;
    font-size: 13px;
    font-weight: 400;
    color: var(--text-dim);
    cursor: pointer;
}
.photo-field-delete input[type=checkbox] {
    width: 17px; height: 17px;
    flex: none;
    margin: 0;
    accent-color: var(--brand-light);
    cursor: pointer;
}

/* ---------- modal ---------- */

.modal {
    position: fixed;
    inset: 0;
    z-index: 80;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 16px;
    background: rgba(0, 0, 0, .6);
}
.modal[hidden] { display: none; }

.modal-card {
    width: 100%;
    max-width: 460px;
    max-height: calc(100dvh - 32px);
    display: flex;
    flex-direction: column;
    background: var(--surface);
    color: var(--text);
    border-radius: 14px;
    box-shadow: 0 18px 50px rgba(0, 0, 0, .35);
    overflow: hidden;
}
.modal-head { padding: 16px 18px 12px; border-bottom: 1px solid var(--border); }
.modal-head h2 { margin: 0 0 2px; font-size: 16px; }
.modal-head .subtitle { margin: 0; font-size: 13px; }
.modal-body { padding: 16px 18px; overflow-y: auto; }
.modal-foot {
    padding: 12px 18px;
    border-top: 1px solid var(--border);
    background: var(--surface-2);
    display: flex;
    justify-content: flex-end;
    gap: 10px;
}

/* ---------- square cropper ---------- */

/*
 * Cropper.js needs a block-level image with a bounded height to size its
 * container against. The crop box is left square — the tile it feeds is a
 * rounded square, so a circular mask would discard corners that do get shown.
 */
.cropper-stage {
    height: 300px;
    background: var(--surface-2);
    border-radius: var(--radius);
    overflow: hidden;
}
.cropper-stage img { display: block; max-width: 100%; }
.cropper-view-box { outline: 0; box-shadow: 0 0 0 1px #fff, 0 0 0 9999px rgba(0, 0, 0, .5); }

.cropper-tools { display: flex; gap: 8px; justify-content: center; flex-wrap: wrap; margin-top: 12px; }
.cropper-tools .btn { min-width: 44px; }

/* ---------- avatar menu ---------- */

.usermenu { position: relative; margin-left: auto; }

.usermenu-button {
    display: flex;
    align-items: center;
    gap: 9px;
    background: none;
    border: 0;
    padding: 3px;
    border-radius: 999px;
    cursor: pointer;
    color: var(--chrome-text);
    font-family: inherit;
}
.usermenu-button:hover { background: var(--chrome-hover); }

.avatar {
    width: 34px; height: 34px;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 13px;
    font-weight: 700;
    letter-spacing: .02em;
    background: var(--brand);
    color: #fff;
    flex: none;
    text-transform: uppercase;
}
.avatar.lg { width: 52px; height: 52px; font-size: 18px; }

.usermenu-name { display: none; font-size: 14px; font-weight: 600; }
.usermenu-caret { opacity: .7; }
@media (min-width: 800px) { .usermenu-name { display: inline; } }

.usermenu-panel {
    display: none;
    position: absolute;
    right: 0;
    top: calc(100% + 8px);
    width: 260px;
    background: var(--surface);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: 12px;
    box-shadow: 0 10px 30px rgba(0, 0, 0, .18);
    padding: 6px;
    z-index: 60;
}
.usermenu-panel.is-open { display: block; }

.usermenu-head { padding: 10px 11px 12px; display: flex; gap: 11px; align-items: center; }
.usermenu-head .avatar { background: var(--brand); box-shadow: none; }
.usermenu-head .who { min-width: 0; }
.usermenu-head .who strong { display: block; font-size: 14px; }
.usermenu-head .who span { display: block; font-size: 12px; color: var(--text-dim); overflow: hidden; text-overflow: ellipsis; }

.usermenu-sep { height: 1px; background: var(--border); margin: 4px 0; }

.usermenu-item {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 9px 11px;
    border-radius: 8px;
    text-decoration: none;
    color: var(--text);
    font-size: 14px;
    font-weight: 500;
    background: none;
    border: 0;
    font-family: inherit;
    cursor: pointer;
    text-align: left;
}
.usermenu-item:hover { background: var(--surface-2); }
.usermenu-item .icon { width: 18px; height: 18px; color: var(--text-dim); }
.usermenu-item.danger:hover { color: var(--red); }
.usermenu-item.danger:hover .icon { color: var(--red); }

.usermenu-label {
    padding: 8px 11px 4px;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: var(--text-dim);
    font-weight: 600;
}

.theme-switch { display: flex; gap: 4px; padding: 2px 7px 8px; }
.theme-switch button {
    flex: 1;
    padding: 7px 4px;
    font-size: 12px;
    font-weight: 600;
    font-family: inherit;
    color: var(--text-dim);
    background: var(--surface-2);
    border: 1px solid transparent;
    border-radius: 7px;
    cursor: pointer;
}
.theme-switch button:hover { color: var(--text); }
.theme-switch button.is-active {
    background: var(--surface);
    color: var(--brand-light);
    border-color: var(--border);
}

/* The same control on a card body rather than inside the dropdown: no side
   padding faking the menu inset, and big enough to hit with a thumb. */
.theme-switch.boxed { padding: 0; gap: 6px; max-width: 340px; }
.theme-switch.boxed button { padding: 11px 6px; font-size: 13px; }

/* ---------- custom select ---------- */

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

.kselect { position: relative; }

.kselect-button {
    width: 100%;
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 11px 12px;
    font-size: 16px;
    font-family: inherit;
    text-align: left;
    color: var(--text);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 8px;
    cursor: pointer;
    min-height: 46px;
}
.kselect-button:hover { border-color: var(--text-dim); }
.kselect-button:focus-visible { outline: 2px solid var(--brand-light); outline-offset: 1px; }
.kselect-button:disabled { opacity: .55; cursor: not-allowed; }

.kselect-label { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.kselect-label.is-placeholder { color: var(--text-dim); }
.kselect-caret { display: flex; color: var(--text-dim); flex: none; }

.kselect-menu {
    display: none;
    position: absolute;
    left: 0; right: 0;
    top: calc(100% + 4px);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 10px;
    box-shadow: 0 10px 30px rgba(0, 0, 0, .18);
    z-index: 50;
    padding: 5px;
    max-height: 320px;
    display: none;
    flex-direction: column;
}
.kselect-menu.is-open { display: flex; }

.kselect-search { padding: 3px 3px 6px; }
.kselect-search input { padding: 8px 10px; font-size: 14px; }
.kselect-search[hidden] { display: none; }

.kselect-list { overflow-y: auto; -webkit-overflow-scrolling: touch; }

.kselect-option {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 9px 10px;
    border-radius: 7px;
    font-size: 15px;
    cursor: pointer;
}
.kselect-option > span:first-child { flex: 1; }
.kselect-option[hidden] { display: none; }
.kselect-option.is-highlighted { background: var(--surface-2); }
.kselect-option.is-selected { font-weight: 650; color: var(--brand-light); }
.kselect-option.is-disabled { opacity: .5; cursor: not-allowed; }
.kselect-tick { visibility: hidden; display: flex; color: var(--brand-light); }
.kselect-option.is-selected .kselect-tick { visibility: visible; }

.kselect-group {
    padding: 9px 10px 4px;
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--text-dim);
}
.kselect-group[hidden] { display: none; }

.kselect-empty { padding: 12px 10px; font-size: 14px; color: var(--text-dim); text-align: center; }
.kselect-empty[hidden] { display: none; }

/* Inside a table cell the control should not fight the column width. */
table.grid .kselect-button { min-height: 38px; padding: 7px 10px; font-size: 14px; }

/* Editable cells: same compact sizing as the listbox above, so a row of mixed
   controls keeps the table's line height rather than the form's. */
table.grid input[type=text],
table.grid input[type=number] {
    min-height: 38px;
    min-width: 130px;
    padding: 7px 10px;
    font-size: 14px;
}
table.grid .grid-num { width: 92px; min-width: 0; }
table.grid .grid-check {
    width: 18px; height: 18px;
    accent-color: var(--brand-light);
    cursor: pointer;
}
table.grid .grid-actions { text-align: right; white-space: nowrap; }
.grid-inuse { font-size: 12px; }

/* Present to screen readers, absent to everything else — for a column that
   needs a name but no visible heading. */
.visually-hidden {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
}

/* ---------- settings lists ---------- */

.rowlist { display: flex; flex-direction: column; }
.rowlist-item {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 16px;
    border-bottom: 1px solid var(--border);
    flex-wrap: wrap;
}
.rowlist-item:last-child { border-bottom: 0; }
.rowlist-main { flex: 1; min-width: 160px; }
.rowlist-main strong { font-size: 15px; }
.rowlist-sub { font-size: 12px; color: var(--text-dim); margin-top: 2px; }
.rowlist-actions { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }

.inline-form { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.inline-form .kselect { min-width: 190px; }

/* ---------- profile page ---------- */

.profile-layout { display: grid; gap: 0 20px; align-items: start; }

@media (min-width: 900px) {
    .profile-layout { grid-template-columns: 272px minmax(0, 1fr); }
    /*
     * Sticks below the top bar, which is itself sticky at 0. `align-items:
     * start` on the grid is what keeps the aside from stretching to the full
     * row height — a stretched item has nothing to slide within and would
     * never stick.
     */
    .profile-aside { position: sticky; top: calc(var(--topbar-h) + 16px); }
}

/* Sections are jumped to from the nav, so their heading has to clear the
   sticky top bar rather than hiding underneath it. */
.profile-main .card { scroll-margin-top: calc(var(--topbar-h) + 16px); }

/* Appearance sits outside the form (nothing to save), so it needs the gap the
   cards give each other. */
.profile-main > form { margin-bottom: 18px; }

.profile-id {
    background: var(--chrome-grad);
    color: var(--chrome-text);
    padding: 24px 16px 18px;
    text-align: center;
}
.profile-id h2 { font-size: 19px; margin: 12px 0 2px; }
.profile-email {
    margin: 0;
    font-size: 13px;
    color: var(--chrome-dim);
    overflow-wrap: anywhere;
}
.profile-badges { display: flex; flex-wrap: wrap; gap: 6px; justify-content: center; margin-top: 12px; }

/* A pill tinted from the chrome tokens instead of .pill's fixed status colours,
   so it stays legible on the brown in both themes. */
.chip {
    display: inline-flex;
    align-items: center;
    padding: 3px 10px;
    border-radius: 999px;
    font-size: 12px;
    font-weight: 600;
    background: var(--chrome-active);
    color: var(--chrome-text);
}

.avatar.xl {
    width: 76px; height: 76px;
    font-size: 27px;
    margin: 0 auto;
    display: flex;
    box-shadow: 0 0 0 4px rgba(255, 255, 255, .35);
}

.profile-nav { display: flex; flex-direction: column; padding: 8px; gap: 2px; }
.profile-nav a {
    display: flex;
    align-items: center;
    gap: 11px;
    padding: 9px 11px;
    border-radius: 9px;
    text-decoration: none;
    color: var(--text-dim);
    font-size: 14px;
    font-weight: 600;
}
.profile-nav a:hover { background: var(--surface-2); color: var(--text); }
.profile-nav a.active { background: var(--surface-2); color: var(--brand-light); }
.profile-nav .icon { width: 18px; height: 18px; }

.toggle-row {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 12px 14px;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    cursor: pointer;
    font-weight: 400;
    margin: 0;
}
.toggle-row:hover { border-color: var(--text-dim); }
.toggle-main { flex: 1; min-width: 0; }
.toggle-main strong { display: block; font-size: 14px; font-weight: 600; }
.toggle-main span { display: block; font-size: 12px; color: var(--text-dim); margin-top: 2px; }
.toggle-row input[type=checkbox] {
    width: 20px; height: 20px;
    flex: none;
    accent-color: var(--brand-light);
    cursor: pointer;
}

/* The client's logo is dark-on-white, so it needs a light plate of its own
   rather than sitting on the page background in dark mode. */
.login-logo {
    display: block;
    width: 190px;
    max-width: 70%;
    height: auto;
    margin: 0 auto 10px;
    background: #fff;
    border-radius: 8px;
    padding: 8px 10px;
}

/* ---------- dashboard: wide canvas, dog meta, rail widgets ---------- */

/*
 * The dashboard earns a wider canvas than the reading-width pages: the dog
 * list keeps its column and the slack that used to be empty margin becomes
 * a rail of kennel-wide widgets. Forms and detail pages stay at 1100px on
 * purpose — width is only a gift to pages with something to put in it.
 */
.page-wide { max-width: 1560px; }

.dash-layout { display: grid; gap: 20px; align-items: start; }
.dash-rail { display: grid; gap: 14px; }

/* Phones: the kennel-wide cards come first and the register below them — the
   summary is what a phone opens for; the list is what you scroll into. */
@media (max-width: 799px) {
    .dash-rail { order: -1; }
}

@media (min-width: 700px) and (max-width: 1149px) {
    /* Middle widths: the widgets sit shoulder to shoulder below the list. */
    .dash-rail { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
}
@media (min-width: 1150px) {
    .dash-layout { grid-template-columns: minmax(0, 1fr) 330px; }
}

/* The line under a dog's name: age · sex · breed. */
.dog-sub { font-size: 12.5px; color: var(--text-dim); margin-top: 1px; }
.dog-card .dog-sub { margin: 2px 0 6px 17px; }

.rail-card { padding: 14px 16px; margin-bottom: 0; }
.rail-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin-bottom: 8px; }
.rail-head h2 { margin: 0; font-size: 12px; text-transform: uppercase; letter-spacing: .08em; color: var(--text-dim); }
.rail-more { font-size: 12.5px; white-space: nowrap; }

.rail-row { display: flex; align-items: center; gap: 8px; padding: 4px 0; font-size: 14px; min-width: 0; }
.rail-row a { color: inherit; text-decoration: none; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rail-row a:hover { text-decoration: underline; }
.rail-dim { color: var(--text-dim); font-size: 12.5px; flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rail-when { font-size: 12.5px; color: var(--amber); white-space: nowrap; font-variant-numeric: tabular-nums; }
.rail-empty { color: var(--text-dim); font-size: 13.5px; margin: 4px 0 0; }

.journey { padding: 8px 0 4px; border-top: 1px solid var(--border); }
.journey:first-of-type { border-top: 0; padding-top: 2px; }
.journey-loc { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; font-weight: 700; font-size: 13.5px; }
.journey-n { color: var(--text-dim); font-weight: 400; font-size: 12px; white-space: nowrap; }

.date-chip { font-size: 11.5px; font-weight: 700; padding: 2px 8px; border-radius: 999px; white-space: nowrap; font-variant-numeric: tabular-nums; flex: none; }
.date-chip.red { color: var(--red); background: var(--red-bg); }
.date-chip.amber { color: var(--amber); background: var(--amber-bg); }
.date-chip.green { color: var(--green); background: var(--green-bg); }
.date-chip.blue { color: var(--blue); background: var(--blue-bg); }
.date-chip.grey { color: var(--grey); background: var(--grey-bg); }
.date-chip.purple { color: var(--purple); background: var(--purple-bg); }

.seg-bar { display: flex; gap: 2px; height: 10px; border-radius: 999px; overflow: hidden; margin: 4px 0 10px; }
.seg { min-width: 6px; }
.seg.green { background: var(--green); }
.seg.blue { background: var(--blue); }
.seg.amber { background: var(--amber); }
.seg.red { background: var(--red); }
.seg.grey { background: var(--grey); }
.seg.purple { background: var(--purple); }

.seg-legend { display: flex; flex-wrap: wrap; gap: 4px 12px; font-size: 12.5px; color: var(--text-dim); margin-bottom: 10px; }
.seg-legend .dot { width: 8px; height: 8px; }

.kennel-facts { display: grid; gap: 4px; font-size: 13.5px; color: var(--text-dim); border-top: 1px solid var(--border); padding-top: 10px; }
.kennel-facts strong { color: var(--text); }

/* ---------- user guide ---------- */

/*
 * The guide borrows the profile page's shape — a sticky contents column beside
 * a reading column — rather than inventing a documentation shell of its own.
 * The manual is part of the app, and it should not feel like a different site.
 */
.help-layout { display: grid; gap: 0 22px; align-items: start; }

@media (min-width: 960px) {
    .help-layout { grid-template-columns: 268px minmax(0, 1fr); }
    .help-aside { position: sticky; top: calc(var(--topbar-h) + 16px); }
}

.help-contents { padding: 14px; }
.help-search input { width: 100%; margin-bottom: 10px; }

/* Its own scroller on tall screens: the contents is longer than most pages,
   and a sticky column that overflows the viewport can never reach its end. */
.help-nav { display: block; }
@media (min-width: 960px) {
    .help-nav { max-height: calc(100vh - var(--topbar-h) - 150px); overflow-y: auto; }
}

.help-section + .help-section { margin-top: 12px; }
.help-section-head {
    font-size: 11.5px;
    text-transform: uppercase;
    letter-spacing: .08em;
    color: var(--text-dim);
    padding: 6px 8px 4px;
    display: flex;
    align-items: center;
    gap: 6px;
}
.help-tag {
    font-size: 9.5px;
    letter-spacing: .06em;
    padding: 1px 6px;
    border-radius: 999px;
    color: var(--purple);
    background: var(--purple-bg);
}

.help-link {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 9px;
    border-radius: 7px;
    font-size: 13.5px;
    color: var(--text);
    text-decoration: none;
}
.help-link:hover { background: var(--grey-bg); }
.help-link.active { background: var(--blue-bg); color: var(--blue); font-weight: 600; }
.help-link svg { width: 15px; height: 15px; flex: none; }
/* Beats the UA rule the way .btn[hidden] has to — display:flex is the more
   specific claim, so filtering the contents would otherwise change nothing. */
.help-link[hidden], .help-section[hidden] { display: none !important; }

.help-nomatch { font-size: 13px; color: var(--text-dim); padding: 8px 9px 0; margin: 0; }
.help-pdf { margin-top: 12px; width: 100%; justify-content: center; }

/*
 * Below 960px the contents column has nowhere to sit beside the article, so
 * it used to just stack above it — thirty-odd links and a search box before
 * a reader ever reached the page they came for. It now opens as a drawer
 * instead, the same shape as the off-canvas panel, but self-contained: the
 * contents is already on the page, so this only ever shows or hides it —
 * nothing is fetched.
 */
.help-toggle { display: inline-flex; margin-bottom: 14px; }
.help-close { display: none; }

@media (min-width: 960px) {
    .help-toggle { display: none; }
}

.help-aside-backdrop {
    position: fixed;
    inset: 0;
    z-index: 70;
    background: rgb(0 0 0 / .38);
    opacity: 0;
    visibility: hidden;
    transition: opacity .2s ease, visibility .2s ease;
}
.help-aside-backdrop.is-open { opacity: 1; visibility: visible; }

@media (max-width: 959.98px) {
    .help-aside {
        position: fixed;
        inset: 0 0 0 auto;
        z-index: 71;
        width: min(320px, 86vw);
        overflow-y: auto;
        visibility: hidden;
        transform: translateX(100%);
        background: var(--surface);
        box-shadow: var(--shadow);
        transition: transform .24s ease, visibility .24s ease;
    }
    .help-aside.is-open { visibility: visible; transform: none; }

    /* The drawer itself scrolls now, so the nested scroller from the desktop
       column would just double up. */
    .help-nav { max-height: none; }

    .help-contents { position: relative; padding-top: 46px; }
    .help-close {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        position: absolute;
        top: 8px;
        right: 8px;
        width: 30px;
        height: 30px;
        border: 0;
        background: none;
        color: var(--text-dim);
        font-size: 16px;
        cursor: pointer;
        border-radius: var(--radius);
    }
    .help-close:hover { background: var(--surface-2); color: var(--text); }
}

/* The page behind stays put while the drawer is over it. */
body.has-help-nav { overflow: hidden; }

/* The list of headings on this page. Above the article on a phone, where it
   is a summary of what follows; still above it on desktop, where it is short. */
.help-toc { padding: 12px 16px; }
.help-toc-head {
    font-size: 11.5px;
    text-transform: uppercase;
    letter-spacing: .08em;
    color: var(--text-dim);
    margin-bottom: 6px;
}
.help-toc a { display: block; padding: 3px 0; font-size: 13.5px; color: var(--text-dim); text-decoration: none; }
.help-toc a:hover { color: var(--text); text-decoration: underline; }
.help-toc a.lvl-3 { padding-left: 14px; font-size: 13px; }

/* Headings are jumped to from the contents above, so they have to clear the
   sticky top bar rather than land underneath it. */
.help-doc :is(h2, h3) { scroll-margin-top: calc(var(--topbar-h) + 16px); }

/* ---- the rendered Markdown ---- */

.prose { font-size: 15px; line-height: 1.65; max-width: 74ch; }
.prose > :first-child { margin-top: 0; }
.prose > :last-child { margin-bottom: 0; }
.prose p { margin: 0 0 14px; }
.prose h2 {
    font-size: 18px;
    margin: 30px 0 10px;
    padding-top: 18px;
    border-top: 1px solid var(--border);
}
.prose h2:first-child { margin-top: 0; padding-top: 0; border-top: 0; }
.prose h3 { font-size: 15px; margin: 22px 0 8px; }
.prose h4 { font-size: 14px; margin: 18px 0 6px; color: var(--text-dim); }
.prose ul, .prose ol { margin: 0 0 14px; padding-left: 22px; }
.prose li { margin-bottom: 5px; }
.prose li > ul, .prose li > ol { margin: 5px 0 0; }
.prose a { color: var(--blue); }
.prose strong { font-weight: 700; }
.prose code {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: .88em;
    background: var(--grey-bg);
    border-radius: 5px;
    padding: 1px 5px;
}
.prose pre {
    background: var(--grey-bg);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 12px 14px;
    overflow-x: auto;
    margin: 0 0 14px;
}
.prose pre code { background: none; padding: 0; }

/* A quotation is how the guide says "this is a rule, not a suggestion". */
.prose blockquote {
    margin: 0 0 14px;
    padding: 10px 14px;
    border-left: 3px solid var(--accent);
    background: var(--grey-bg);
    border-radius: 0 var(--radius) var(--radius) 0;
    color: var(--text-dim);
}
.prose blockquote p:last-child { margin-bottom: 0; }

.prose table { border-collapse: collapse; width: 100%; margin: 0 0 16px; font-size: 14px; }
.prose th, .prose td { text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--border); vertical-align: top; }
.prose th { font-size: 12.5px; text-transform: uppercase; letter-spacing: .04em; color: var(--text-dim); }
.prose tr:last-child td { border-bottom: 0; }

/* Tables in a manual are mostly reference grids that will not narrow politely. */
.prose > table { display: block; overflow-x: auto; }

.prose hr { border: 0; border-top: 1px solid var(--border); margin: 24px 0; }

/* ---- previous / next ---- */

.help-pager { display: grid; gap: 12px; grid-template-columns: 1fr 1fr; margin: 4px 0 14px; }
.help-pager-link {
    display: block;
    padding: 12px 14px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    text-decoration: none;
    color: var(--text);
    min-width: 0;
}
.help-pager-link:hover { border-color: var(--accent); }
.help-pager-link.next { text-align: right; }
.help-pager-dir { display: block; font-size: 12px; color: var(--text-dim); }
.help-pager-label { display: block; font-weight: 600; font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.help-more h2 { margin: 0 0 8px; font-size: 15px; }
.help-more ul { margin: 0; padding-left: 20px; font-size: 14px; color: var(--text-dim); }
.help-more li { margin-bottom: 6px; }

/* ---------- audit trail ---------- */

/*
 * A list rather than a table. The trail's five facts do not line up into
 * columns that mean anything on a phone — "who" is a name, "what" is a sentence
 * and "detail" is a bag of whatever mattered — so each entry is a block that
 * reads top to bottom, the same shape at every width.
 */
.audit-row { align-items: flex-start; gap: 10px; }
.audit-mark { font-size: 18px; line-height: 1.2; flex: 0 0 auto; }
.audit-what { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.audit-what .pill { text-decoration: none; }
.audit-subject { font-size: 15px; min-width: 0; overflow-wrap: anywhere; }

/*
 * The detail map. Wrapped as separate spans rather than one string so a line
 * with six keys breaks between them instead of mid-value, and the key stands
 * out enough to be skipped when it is the value you are hunting for.
 */
.audit-detail {
    display: flex;
    flex-wrap: wrap;
    gap: 2px 12px;
    margin-top: 4px;
    font-size: 12px;
    color: var(--text-dim);
    overflow-wrap: anywhere;
}
.audit-detail b { font-weight: 600; color: var(--text); }
.audit-from { white-space: nowrap; }

.audit-chips { margin-bottom: 12px; }
.audit-chips .chip { text-decoration: none; padding: 5px 10px; font-size: 12px; }
/* The chosen category. Reads as pressed rather than merely coloured, which is
   what tells you the page below is narrowed. */
.audit-chips .chip.on { background: var(--brand); color: #fff; }

.pager { display: flex; gap: 12px; justify-content: center; align-items: center; margin-top: 14px; }
.pager .subtitle { margin: 0; }

/* ---------- the bell ---------- */

/*
 * Sits to the left of the account menu and takes the auto margin off it: two
 * elements both pushing themselves right would split the gap between them and
 * leave the bell stranded in the middle of the bar.
 */
.notifs { position: relative; margin-left: auto; }
.notifs + .usermenu { margin-left: 0; }

.notifs-button {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 38px;
    border-radius: 999px;
    color: var(--chrome-text);
    text-decoration: none;
}
.notifs-button:hover { background: var(--chrome-hover); }

/*
 * The count rides on the bell rather than beside it: the bar is tight on a
 * phone, and a number that moves the icon when it appears reads as a different
 * button rather than the same one with news.
 */
.notifs-badge {
    position: absolute;
    top: 2px;
    right: 0;
    min-width: 17px;
    height: 17px;
    padding: 0 4px;
    border-radius: 999px;
    background: var(--red);
    color: #fff;
    font-size: 11px;
    font-weight: 700;
    line-height: 17px;
    text-align: center;
    /* A ring in the bar's own tone, so the disc reads as sitting on the chrome
       rather than floating over whatever scrolls past underneath. */
    box-shadow: 0 0 0 2px var(--fill);
}
/* The attribute alone loses to the flex/display rules above it. */
.notifs-badge[hidden] { display: none; }

.notifs-panel {
    display: none;
    position: absolute;
    right: 0;
    top: calc(100% + 8px);
    width: min(360px, calc(100vw - 24px));
    background: var(--surface);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: 12px;
    box-shadow: 0 10px 30px rgba(0, 0, 0, .18);
    z-index: 60;
    overflow: hidden;
}
.notifs-panel.is-open { display: block; }

.notifs-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding: 11px 13px;
    border-bottom: 1px solid var(--border);
}
.notifs-head strong { font-size: 14px; }

/* A button that has to read as a link, because what it does is not a form. */
.linkish {
    background: none;
    border: 0;
    padding: 0;
    font: inherit;
    font-size: 12px;
    color: var(--accent);
    cursor: pointer;
    text-decoration: underline;
}

/* Tall enough to show four or five, short enough to never fill a phone. */
.notifs-list { max-height: min(60vh, 420px); overflow-y: auto; }
.notifs-list .panel-skeleton { padding: 14px; }

.notifs-foot {
    display: block;
    padding: 10px 13px;
    border-top: 1px solid var(--border);
    font-size: 13px;
    font-weight: 600;
    text-align: center;
    color: var(--accent);
    text-decoration: none;
}
.notifs-foot:hover { background: var(--surface-2); }

/*
 * The bell is not the last thing in the bar — the account menu sits to its
 * right — so a panel hung off the bell's right edge starts a panel-width to the
 * left of the bell, which on a phone is off the side of the screen: the first
 * 40-50px of it were being cropped by the left edge.
 *
 * Below the phone breakpoint it stops hanging off the bell and spans the bar
 * instead. Taking position off .notifs hands the containing block to .topbar,
 * which is sticky and therefore positioned, so `top: 100%` lands on the bar's
 * bottom edge with its safe-area padding already counted in, and the two insets
 * hold the panel inside the viewport whatever else is in the bar.
 */
@media (max-width: 479px) {
    .notifs { position: static; }
    .notifs-panel {
        top: calc(100% + 8px);
        left: 12px;
        right: 12px;
        width: auto;
    }
}

/* ---------- one notification ---------- */

.notice-list { display: flex; flex-direction: column; }
.notice {
    display: flex;
    align-items: flex-start;
    gap: 11px;
    padding: 11px 13px;
    border-bottom: 1px solid var(--border);
    text-decoration: none;
    color: inherit;
}
.notice:last-child { border-bottom: 0; }
.notice:hover { background: var(--surface-2); }
/* Unread carries a tinted ground and a coloured edge; read is plain. The row
   is the same shape either way, so a list of both still scans as one list. */
.notice.unread { background: var(--surface-2); box-shadow: inset 3px 0 0 var(--accent); }

.notice-mark {
    flex: none;
    width: 30px;
    height: 30px;
    border-radius: 9px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 15px;
    background: var(--grey-bg);
}
.notice-mark.red    { background: var(--red-bg); }
.notice-mark.amber  { background: var(--amber-bg); }
.notice-mark.green  { background: var(--green-bg); }
.notice-mark.blue   { background: var(--blue-bg); }
.notice-mark.purple { background: var(--purple-bg); }

.notice-main { min-width: 0; flex: 1; }
.notice-title { display: block; font-size: 14px; font-weight: 600; line-height: 1.35; }
.notice.unread .notice-title { font-weight: 700; }
.notice-body { display: block; font-size: 13px; color: var(--text-dim); margin-top: 2px; }
.notice-when { display: block; font-size: 11px; color: var(--text-dim); margin-top: 3px; }

.notice-dot { flex: none; width: 8px; height: 8px; border-radius: 50%; background: var(--accent); margin-top: 10px; }

.notice-empty { padding: 26px 18px; text-align: center; color: var(--text-dim); font-size: 13px; }
.notice-empty span { font-size: 24px; display: block; margin-bottom: 6px; }
.notice-empty p { margin: 0; }

/* ---------- the assistant ---------- */

/*
 * Shares the panel's backdrop and head, and adds what a conversation needs:
 * a thread that scrolls on its own and a composer pinned under it, so the box
 * you type in never walks off the bottom of a phone as the answers grow.
 */
.assistant-panel {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    width: min(460px, 100%);
    display: flex;
    flex-direction: column;
    background: var(--bg);
    border-left: 1px solid var(--border);
    box-shadow: -10px 0 40px rgba(0, 0, 0, .18);
    z-index: 70;
    transform: translateX(100%);
    transition: transform .18s ease;
    padding-bottom: env(safe-area-inset-bottom);
}
.assistant-panel.is-open { transform: none; }
.assistant-panel .offcanvas-head { flex: none; }

@media (prefers-reduced-motion: reduce) {
    .assistant-panel { transition: none; }
}

.chat {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    padding: 14px;
    display: flex;
    flex-direction: column;
    gap: 10px;
}

/* The page version: the same thread and composer inside a card, with a height
   that leaves the top bar and the page's own footnote visible. */
.chat-page { display: flex; flex-direction: column; padding: 0; height: min(70vh, 640px); }
.chat-page .chat { padding: 16px; }

.chat-turn { display: flex; flex-direction: column; gap: 5px; max-width: 88%; }
.chat-turn.person { align-self: flex-end; align-items: flex-end; }
.chat-turn.assistant { align-self: flex-start; }

.chat-bubble {
    padding: 10px 13px;
    border-radius: 14px;
    font-size: 14px;
    line-height: 1.5;
    background: var(--surface);
    border: 1px solid var(--border);
    overflow-wrap: anywhere;
}
/* The person's own words in the app's fill, the way a sent message reads
   everywhere else. The assistant stays on plain surface: it is answering, not
   announcing. */
.chat-turn.person .chat-bubble { background: var(--fill); color: var(--fill-text); border-color: transparent; }
.chat-problem { background: var(--red-bg); border-color: transparent; color: var(--red); }

.chat-links { display: flex; flex-wrap: wrap; gap: 5px; }
.chat-links .chip { text-decoration: none; padding: 4px 9px; font-size: 12px; }

/* Three dots while the answer is being worked out. A question that takes ten
   seconds needs to look like it is being answered, not like it was lost. */
.chat-thinking { display: flex; gap: 5px; align-items: center; }
.chat-thinking span {
    width: 6px; height: 6px;
    border-radius: 50%;
    background: var(--text-dim);
    animation: chat-blink 1.2s infinite ease-in-out;
}
.chat-thinking span:nth-child(2) { animation-delay: .2s; }
.chat-thinking span:nth-child(3) { animation-delay: .4s; }
@keyframes chat-blink { 0%, 80%, 100% { opacity: .25; } 40% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
    .chat-thinking span { animation: none; opacity: .55; }
}

.chat-empty { margin: auto; text-align: center; color: var(--text-dim); font-size: 14px; padding: 10px; }
.chat-empty .big { font-size: 30px; margin-bottom: 6px; }
.chat-empty p { margin: 0 0 12px; }
.chat-starters { display: flex; flex-direction: column; gap: 6px; }
.chat-starters .chip {
    border: 1px solid var(--border);
    background: var(--surface);
    cursor: pointer;
    font: inherit;
    font-size: 12px;
    padding: 8px 10px;
    text-align: left;
    color: var(--text);
}
.chat-starters .chip:hover { border-color: var(--accent); }

/*
 * The starters, kept above the box rather than only in an empty thread. The
 * question worth asking is usually the one you think of three answers in, by
 * which point the empty state — and its four suggestions — are scrolled off the
 * top and gone.
 *
 * One line that scrolls sideways, so a fifth starter costs no height in a panel
 * whose height is the whole point. Short labels, with the question each one
 * actually asks on data-question.
 */
.chat-suggests {
    flex: none;
    display: flex;
    gap: 6px;
    padding: 8px 12px;
    border-top: 1px solid var(--border);
    background: var(--surface);
    overflow-x: auto;
    scrollbar-width: thin;
    -webkit-overflow-scrolling: touch;
}
.chat-suggests .chip {
    flex: none;
    white-space: nowrap;
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--text);
    font: inherit;
    font-size: 12px;
    padding: 6px 11px;
    cursor: pointer;
}
.chat-suggests .chip:hover { border-color: var(--accent); }
.chat-suggests .chip:disabled { opacity: .45; cursor: default; }

.chat-composer {
    flex: none;
    display: flex;
    align-items: flex-end;
    gap: 8px;
    padding: 10px 12px;
    border-top: 1px solid var(--border);
    background: var(--surface);
    /* So the attached photograph can take a line of its own above the box
       without the composer needing a wrapper, and without either place it is
       used having to know it might. */
    flex-wrap: wrap;
}

/*
 * A photograph waiting to be sent. First child and full width, so it sits on
 * its own line above the box rather than squeezing it.
 *
 * Shown at thumbnail size: this is here to answer "did I get the shot", which
 * takes a glance, not a study.
 */
.chat-photo {
    flex: 1 1 100%;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 6px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--surface-2);
}
.chat-photo[hidden] { display: none; }
.chat-photo img {
    width: 46px;
    height: 46px;
    flex: none;
    object-fit: cover;
    border-radius: 7px;
    background: var(--grey-bg);
}
.chat-photo-note { flex: 1; min-width: 0; font-size: 13px; color: var(--text-dim); }
.chat-photo-drop {
    flex: none;
    width: 30px;
    height: 30px;
    border: 0;
    border-radius: 999px;
    background: none;
    color: var(--text-dim);
    font-size: 15px;
    cursor: pointer;
}
.chat-photo-drop:hover { background: var(--surface); color: var(--text); }

/* On a turn that carried one: the icon at text size, saying the assistant can
   still see the picture. */
.chat-photo-mark {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--text-dim);
}
.chat-photo-mark .icon { width: 14px; height: 14px; }
.chat-composer textarea {
    flex: 1;
    min-width: 0;
    resize: none;
    /* 16px: anything smaller and iOS zooms the whole page on focus. */
    font: inherit;
    font-size: 16px;
    line-height: 1.4;
    padding: 9px 11px;
    border: 1px solid var(--border);
    border-radius: 12px;
    background: var(--bg);
    color: var(--text);
    max-height: 160px;
}
.chat-composer textarea:focus { outline: 2px solid var(--accent); outline-offset: 1px; }

.chat-cam, .chat-mic, .chat-send {
    flex: none;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 42px;
    height: 42px;
    border-radius: 12px;
    border: 1px solid var(--border);
    background: var(--bg);
    color: var(--text);
    cursor: pointer;
}
.chat-send { background: var(--fill); color: var(--fill-text); border-color: transparent; }
.chat-send:disabled { opacity: .5; cursor: default; }
.chat-mic[hidden] { display: none; }
/* Listening: red, and pulsing, because the one thing worse than a microphone
   that will not start is one nobody notices is still running. */
.chat-mic.is-listening { background: var(--red-bg); color: var(--red); border-color: transparent; animation: chat-pulse 1.4s infinite; }
@keyframes chat-pulse { 50% { opacity: .55; } }
@media (prefers-reduced-motion: reduce) {
    .chat-mic.is-listening { animation: none; }
}

/*
 * The AI assistant's way in: a tab docked to the right edge of the page, rather
 * than a sixth thing in a top bar that already carries the brand, the page
 * name, the trail, the bell and the account.
 *
 * A tab rather than a floating disc because the edge is dead space — a disc in
 * the corner is over the page, and over a phone the page is all there is. It
 * reads bottom-to-top, rounded on the left only, so it looks like part of the
 * edge it is attached to.
 *
 * Semi-transparent at rest: it still sits above the page, and a solid slab
 * parked over a treatment date is worse than no shortcut at all. Opaque the
 * moment it is hovered, focused, pressed or dragged. Above the page, below the
 * bars, and well below the panel it opens.
 *
 * Vertically centred to start with, and draggable up and down from there.
 * Centring is done with auto margins rather than a translate so that the drag
 * has only `top` to set: a transform left in place would put the tab half its
 * own height away from the finger that moved it.
 */
.assistant-launcher {
    position: fixed;
    right: 0;
    top: 0;
    bottom: 0;
    height: fit-content;
    margin: auto 0;
    z-index: 16;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 7px;
    padding: 11px 4px 11px 5px;
    border-radius: 11px 0 0 11px;
    background: var(--fill);
    color: var(--fill-text);
    box-shadow: -2px 2px 12px rgba(0, 0, 0, .22);
    text-decoration: none;
    opacity: .62;
    /* Only the fade is animated: the one thing this must not do is drift away
       from the thumb already reaching for it. */
    transition: opacity .18s ease;
    /* The drag owns the gesture. Without this a finger starting on the tab
       scrolls the page and the tab never moves. */
    touch-action: none;
    -webkit-user-select: none;
    user-select: none;
}

/* Bottom-to-top, which is the way round a tab on the right-hand edge reads. */
.assistant-launcher-label {
    writing-mode: vertical-rl;
    transform: rotate(180deg);
    white-space: nowrap;
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .09em;
}

.assistant-launcher:hover,
.assistant-launcher:active,
.assistant-launcher:focus-visible,
.assistant-launcher.is-dragging { opacity: 1; }
.assistant-launcher:focus-visible { outline: 2px solid var(--brand-light); outline-offset: 2px; }
.assistant-launcher.is-dragging { box-shadow: -3px 4px 18px rgba(0, 0, 0, .30); cursor: grabbing; }
.assistant-launcher .icon { width: 18px; height: 18px; }

@media (prefers-reduced-motion: reduce) {
    .assistant-launcher { transition: none; }
}

/* ---------- reading a certificate ---------- */

.scan {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
    padding: 12px 14px;
    margin-bottom: 14px;
    background: var(--surface-2);
    border: 1px dashed var(--border);
    border-radius: var(--radius);
}
.scan-text { flex: 1; min-width: 180px; font-size: 13px; color: var(--text-dim); }
.scan-text strong { display: block; color: var(--text); font-size: 14px; }
.scan-note { flex-basis: 100%; font-size: 13px; margin: 0; }
.scan-note.bad { color: var(--red); }
.scan-note.good { color: var(--green); }

/* ---------- dev environment badge ---------- */

/*
 * Says out loud that this is not app.kuru-dogs.org. The two instances are
 * identical to look at, and the whole point of a staging copy is that people
 * try things on it — so the one thing it must never do is let somebody believe
 * a change they just made is live, or that a dog they just struck out is gone
 * from the real register.
 *
 * Bottom left, because .fab already owns the other corner and a warning that
 * covers "Add treatment" is worse than no warning at all. Never interactive:
 * pointer-events are off so it cannot swallow a tap meant for what is beneath.
 */
.devbadge {
    position: fixed;
    left: 16px;
    bottom: calc(var(--tabbar-h) + 16px + env(safe-area-inset-bottom));
    z-index: 60;
    pointer-events: none;
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 6px 12px 6px 10px;
    border-radius: 999px;
    background: linear-gradient(90deg, #f97316, #dc2626);
    color: #fff;
    font-size: 11px;
    font-weight: 800;
    letter-spacing: .1em;
    box-shadow: 0 4px 14px rgba(0, 0, 0, .28);
}

.devbadge .icon {
    width: 15px;
    height: 15px;
    animation: devbadge-pulse 2.4s ease-in-out infinite;
}

@keyframes devbadge-pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: .45; }
}

/* No tabbar to clear at desktop widths. */
@media (min-width: 800px) {
    .devbadge { bottom: 24px; }
}

@media (prefers-reduced-motion: reduce) {
    .devbadge .icon { animation: none; }
}

/* ---------- offline: reconnecting banner ---------- */

.net-banner {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 90;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: calc(8px + env(safe-area-inset-top)) 16px 8px;
    background: var(--amber);
    color: #1c1200;
    font-size: 13px;
    font-weight: 600;
    text-align: center;
    opacity: 0;
    transform: translateY(-100%);
    transition: opacity .25s ease, transform .25s ease;
}
.net-banner.is-visible { opacity: 1; transform: translateY(0); }

.net-banner-spinner {
    width: 13px;
    height: 13px;
    border-radius: 999px;
    border: 2px solid rgba(28, 18, 0, .35);
    border-top-color: #1c1200;
    animation: net-banner-spin .8s linear infinite;
}

@keyframes net-banner-spin {
    to { transform: rotate(360deg); }
}

@media (prefers-reduced-motion: reduce) {
    .net-banner { transition: none; }
    .net-banner-spinner { animation: none; }
}

/* ---------- offline: treatment sync pill ---------- */

/*
 * Same bones as .notifs — a button that grows a panel — because the two sit
 * side by side in the bar and ought to read as the same kind of control. It
 * differs from the bell in one respect: it is hidden by default and stays
 * that way whenever there is nothing to say, which given how rarely a
 * handler is actually offline is most of the time.
 */
.sync-wrap { position: relative; }

.sync-pill {
    display: flex;
    align-items: center;
    gap: 6px;
    height: 34px;
    padding: 0 12px 0 10px;
    border: 0;
    border-radius: 999px;
    background: var(--chrome-hover);
    color: var(--chrome-text);
    font: inherit;
    font-size: 12px;
    font-weight: 700;
    cursor: pointer;
    white-space: nowrap;
}
.sync-pill:hover { background: var(--chrome-active); }
.sync-pill[hidden] { display: none; }
.sync-pill .icon { width: 17px; height: 17px; }

.sync-pill-offline { background: var(--amber-bg); color: var(--amber); }
.sync-pill-attention { background: var(--red-bg); color: var(--red); }

.sync-pill-sending { background: var(--blue-bg); color: var(--blue); }
.sync-pill-sending .icon { animation: sync-pulse 1.4s ease-in-out infinite; }

@keyframes sync-pulse {
    0%, 100% { transform: rotate(0deg); }
    50% { transform: rotate(180deg); }
}
@media (prefers-reduced-motion: reduce) {
    .sync-pill-sending .icon { animation: none; }
}

.sync-panel {
    display: none;
    position: absolute;
    right: 0;
    top: calc(100% + 8px);
    width: min(340px, calc(100vw - 24px));
    background: var(--surface);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: 12px;
    box-shadow: 0 10px 30px rgba(0, 0, 0, .18);
    z-index: 60;
    overflow: hidden;
}
.sync-panel.is-open { display: block; }

.sync-panel-head {
    padding: 11px 13px;
    border-bottom: 1px solid var(--border);
    font-size: 14px;
}

.sync-panel-list { max-height: min(60vh, 420px); overflow-y: auto; }

.sync-panel-status {
    margin: 0;
    padding: 14px 13px;
    color: var(--text-dim);
    font-size: 13px;
}

.sync-row {
    padding: 11px 13px;
    border-bottom: 1px solid var(--border);
}
.sync-row:last-child { border-bottom: 0; }
.sync-row-summary { margin: 0; font-weight: 600; font-size: 13px; }
.sync-row-meta { margin: 2px 0 0; font-size: 12px; color: var(--text-dim); }
.sync-row-error { margin: 4px 0 0; font-size: 12px; color: var(--red); }
.sync-row-actions { display: flex; gap: 8px; margin-top: 8px; }

@media (max-width: 479px) {
    .sync-wrap { position: static; }
    .sync-panel {
        top: calc(100% + 8px);
        left: 12px;
        right: 12px;
        width: auto;
    }
}
