/* ==========================================
   PetAware Brand CSS
   Single source of truth for all PetAware apps.
   Reference via: _content/PetAware.Shared.UI/petaware-brand.css
   ========================================== */

/* ── Shadow vocabulary (see DESIGN.md § Elevation & Depth) ──
   The system is flat at rest; these are the only sanctioned shadows, and every one of them is
   a response to state. They live here as custom properties so the vocabulary is something a
   stylesheet can reference rather than something each file re-types from memory — which is how
   the marketing feature card had quietly drifted to its own blur and alpha. */
:root {
    /* Default interactive lift — list items, buttons, diary blocks. */
    --pa-shadow-hover-lift: 0 4px 16px rgba(0, 0, 0, 0.08);
    /* Error-tinted lift on destructive actions. */
    --pa-shadow-danger-hover: 0 2px 8px rgba(234, 67, 53, 0.3);
    /* Marketing feature-card hover only — deliberately deeper than working surfaces. */
    --pa-shadow-feature-hover: 0 20px 60px rgba(0, 0, 0, 0.12);
    /* M3 button elevation on the auth primary action. */
    --pa-shadow-auth-button-hover: 0 1px 3px 1px rgba(0, 0, 0, 0.15), 0 1px 2px 0 rgba(0, 0, 0, 0.3);
    /* Legibility halo for hero type set over the teal gradient. Not elevation. */
    --pa-text-shadow-hero: 0 2px 20px rgba(0, 0, 0, 0.18);

    /* ── Data marks (see DESIGN.md § Colors) ──
       Charted marks use the deep stop of the brand teal, not the primary #4FD0CB. As *text or a
       button fill* the primary is fine, but as a filled shape on white it lands near 1.9:1 against
       the surface — a bar a reader has to hunt for. #3A8F8A clears 3:1 and is already the
       system's own darker teal, so the marks stay the one teal voice while being legible.
       Mirrored in C# by PetAwareTheme.BrandDataMark for chart libraries that take a hex. */
    --pa-chart-mark: #3A8F8A;

    /* Sequential age ramp — one hue at monotonic steps, for quantities that sit along an
       *order* (receivables ageing, overdue tiers). It is deliberately not five categorical hues:
       these buckets are the same measure further along one scale, and a categorical palette would
       claim they are unrelated peers. Step 0 is neutral because "not yet overdue" is the absence
       of the signal, not the mildest amount of it. Every step is always paired with its label. */
    --pa-ramp-age-0: #c7ccd1;
    --pa-ramp-age-1: #f4b5ae;
    --pa-ramp-age-2: #e8776b;
    --pa-ramp-age-3: #c5221f;
    --pa-ramp-age-4: #8c1d18;

    /* The smallest step of the radius ladder, as a property so data marks can reference it. */
    --pa-radius-sm: 4px;

    /* ── Patient-flag hues (see DESIGN.md § Colors, and the ds-flag component) ──
       Reserved for patient status and nothing else — they are the one place the system spends a
       hue that is not teal or neutral, and reusing them as general accents is what would make a
       hazard stripe stop reading as a hazard. Named here because each was written out five times
       between the block rule, the table-cell rule and the hover, and three of those copies had to
       agree on an alpha; the tints are derived rather than re-picked so a change to the hue cannot
       leave a stale wash behind it. */
    --pa-flag-male: #42a5f5;
    --pa-flag-female: #ec407a;
    --pa-flag-hazard: #b71c1c;
    /* Resting wash, and the step it takes under the cursor. */
    --pa-flag-tint: 10%;
    --pa-flag-tint-hover: 18%;

    /* ── The label palette (see DESIGN.md § Colors → The Label Palette) ──
       The product's only *nominal categorical* hues: eight colours a practice picks from to name
       things it has named itself — a department, an appointment type, a rota lane, a calendar
       block. Charts still never get a categorical palette (a quantity is a magnitude or an order,
       and both have a single-hue answer); this is the opposite case, where the categories are
       genuinely unrelated peers because a human said so.

       Chosen for two constraints at once. They must be distinguishable *from each other* at four
       metres on a ward television — which is why there is no second blue and no second green — and
       each must clear 3:1 as a filled shape against both the light surface and the dark one, since
       these are raw hues rather than theme roles and do not swap with the mode. Mid-tones, for the
       same reason --pa-flag-* are: light enough to survive a near-black surface, dark enough to
       survive a near-white one.

       Deliberately not teal: teal is the brand accent under the One Teal Rule, and a department
       that happened to pick it would read as "the important one". Deliberately not #ec407a either
       — that is the reserved female patient flag. --pa-label-pink is a deeper magenta so the two
       cannot be confused where they appear on the same card.

       A colour is never the only thing distinguishing two labels: every surface that paints one
       prints the name beside it. */
    --pa-label-blue: #1a73e8;
    --pa-label-purple: #8430ce;
    --pa-label-pink: #b5006b;
    --pa-label-red: #d0342c;
    --pa-label-orange: #e8710a;
    --pa-label-amber: #b06000;
    --pa-label-green: #1e8e3e;
    --pa-label-grey: #6c737a;

    /* ── Image scrim ──
       The one veil laid over a photograph: the crop dialog's mask, and the hover state that
       puts an icon on an avatar. Neutral black rather than a tinted overlay, because both
       cases sit on an arbitrary photo and a teal wash would recolour the animal. Named here
       for the same reason as the shadows — two files had already picked two alphas. */
    --pa-scrim-image: rgba(0, 0, 0, 0.45);

    /* The bottom navigation bar's height, without the gesture inset it pads itself by. Named
       because six rules across three stylesheets have to clear it — the main content region in
       each mobile head, the thumb-reach FAB, and the bottom sheet raised from the bar — and a
       number they disagree about is a control hidden under another one. PaBottomNav is the one
       that sets it; everything else subtracts it. */
    --pa-bottom-nav-height: 80px;

    /* Breathing room above a sticky region and below the one it docks under. Named because two
       sides have to agree on it — PaStickyHeader offsets itself by it, and anything docking
       beneath composes with it — and a gap the two ends disagree about is an overlap. */
    --pa-sticky-gap: 8px;
}

/* ── Global: Remove card/paper shadows for flat design ── */
.mud-card,
.mud-paper {
    box-shadow: none !important;
}

/* ── Chip sets lay their chips out ──
   MudBlazor 8 renders MudChipSet as a bare div carrying .mud-chipset and ships no rule at all for
   that class, so the set is a plain block box whose chips are inline-flex boxes laid out in line
   boxes. Put that block in a flex row next to another control and it can be squeezed narrower than
   its content, and inline content that does not fit its box overflows it — the chips are then
   drawn on top of whatever sits beside them. Making the set a wrapping flex row of its own keeps
   the chips inside their box at any width. No gap: MudBlazor already gives every chip a 4px
   margin, and adding one here would silently re-space every chip set in the portals. */
.mud-chipset {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    min-width: 0;
}

/* ── Label-palette swatch on a chip ──
   Global rather than scoped because it is used from dialogs, which have no CSS isolation of their
   own. The hue arrives as --pa-department-colour; MudBlazor's filled variant takes over the moment
   the chip is the selected one, so this only has to describe the unselected key state. */
.department-swatch-chip {
    border-color: var(--pa-department-colour);
    color: var(--pa-department-colour);
}

/* -- Smooth scrolling -- */
html {
    scroll-behavior: smooth;
}

/* ── AppShellLayout — content spacing below fixed app bar ── */
.app-shell-main-content.mud-main-content {
    padding-top: 80px;
}
@media (min-width: 960px) {
    .app-shell-main-content.mud-main-content {
        padding-top: 84px;
    }
}

/* -- Brand gradient text effect -- */
.gradient-text {
    background: linear-gradient(135deg, #4FD0CB 0%, #3A8F8A 100%);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
}

/* -- Glass morphism effect -- */
.glass-effect {
    background: rgba(255, 255, 255, 0.8);
    backdrop-filter: blur(20px) saturate(180%);
    -webkit-backdrop-filter: blur(20px) saturate(180%);
    border: 1px solid rgba(255, 255, 255, 0.2);
}

/* -- Keyframe Animations -- */
@keyframes slideUp {
    from { opacity: 0; transform: translateY(40px); }
    to   { opacity: 1; transform: translateY(0); }
}

@keyframes fadeIn {
    from { opacity: 0; transform: translateY(20px); }
    to   { opacity: 1; transform: translateY(0); }
}

@keyframes float {
    0%, 100% { transform: translateY(0); }
    50%      { transform: translateY(-20px); }
}

@keyframes pulse {
    0%, 100% { transform: scale(1);    box-shadow: 0 0 0 0 rgba(79,208,203,0.7); }
    50%      { transform: scale(1.05); box-shadow: 0 0 0 20px rgba(79,208,203,0); }
}

@keyframes backgroundShift {
    0%, 100% { transform: scale(1) rotate(0deg); opacity: 1; }
    50%      { transform: scale(1.1) rotate(5deg); opacity: 0.8; }
}

/* ── Data reveal ──
   The one authored moment on a reporting surface: a generated result arrives as a single wave
   travelling down and to the right — tiles rise, bars grow out of the baseline they are measured
   from, plotted charts wipe in along their own time axis. It is one choreography, not five
   effects; nothing here loops, and nothing moves after the data has landed.

   Everything eases on the same exponential curve so a bar and a tile that start together feel
   like one gesture. Stagger is carried by --pa-reveal-index on the element itself, so a list
   sets its own rhythm without the stylesheet knowing how long the list is. */
@keyframes paDataRise {
    from { opacity: 0; transform: translateY(10px); }
    to   { opacity: 1; transform: none; }
}

@keyframes paBarGrow {
    from { transform: scaleX(0); }
    to   { transform: scaleX(1); }
}

@keyframes paPlotWipe {
    from { clip-path: inset(0 100% 0 0); }
    to   { clip-path: inset(0 0 0 0); }
}

.pa-reveal      { animation: paDataRise 420ms cubic-bezier(0.16, 1, 0.3, 1) both; }
.pa-reveal-plot { animation: paPlotWipe 720ms cubic-bezier(0.16, 1, 0.3, 1) both; }
.pa-reveal-step { animation-delay: calc(var(--pa-reveal-index, 0) * 55ms); }

@media (prefers-reduced-motion: reduce) {
    .pa-reveal,
    .pa-reveal-plot {
        animation: none;
    }
}

/* -- Animation utility classes -- */
.animate-slide-up { animation: slideUp 0.8s cubic-bezier(0.4,0,0.2,1) forwards; }
.animate-fade-in  { animation: fadeIn  0.8s cubic-bezier(0.4,0,0.2,1) forwards; }
.floating          { animation: float   4s   ease-in-out infinite; }
.pulsing           { animation: pulse   3s   ease-in-out infinite; }
.delay-1 { animation-delay: 0.2s; }
.delay-2 { animation-delay: 0.4s; }
.delay-3 { animation-delay: 0.6s; }

/* -- Custom scrollbar -- */
::-webkit-scrollbar { width: 10px; }
::-webkit-scrollbar-track { background: var(--mud-palette-background); }
::-webkit-scrollbar-thumb {
    background: linear-gradient(135deg, #4FD0CB 0%, #3A8F8A 100%);
    border-radius: 5px;
}
::-webkit-scrollbar-thumb:hover {
    background: linear-gradient(135deg, #3A8F8A 0%, #4FD0CB 100%);
}

/* -- Dashboard gradient header -- */
.dashboard-header {
    background: linear-gradient(135deg, #4FD0CB 0%, #3A8F8A 100%);
    color: white;
    border-radius: 24px;
    position: relative;
    overflow: hidden;
}

.dashboard-header::before {
    content: '';
    position: absolute;
    inset: 0;
    background-image:
        radial-gradient(circle at 10% 20%, rgba(255,255,255,0.15) 0%, transparent 20%),
        radial-gradient(circle at 90% 80%, rgba(255,255,255,0.12) 0%, transparent 25%);
    z-index: 1;
}

.dashboard-header > * {
    position: relative;
    z-index: 2;
}

/* -- Landing layout (shared across projects) -- */
.landing-layout,
.page {
    position: relative;
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    margin: 0;
    padding: 0;
}

.landing-layout > main,
.page > main {
    flex: 1;
    width: 100%;
}

/* -- Gradient app bars -- */
.gradient-appbar {
    background: linear-gradient(135deg, #4FD0CB 0%, #3A8F8A 100%) !important;
}

.admin-appbar {
    background: linear-gradient(135deg, #1a237e 0%, #283593 100%) !important;
}

/* -- Main content area -- */
.main-content {
    background: #f8f9fa;
    min-height: 100vh;
}


/* -- Admin landing page -- */
.landing-page {
    min-height: 100vh;
}

.hero-section {
    background: linear-gradient(135deg, #1a237e 0%, #283593 50%, #3949ab 100%);
    min-height: 100vh;
    display: flex;
    align-items: center;
}

/* -- Inline code styling -- */
.inline-code {
    background: #E6FAF9;
    padding: 2px 6px;
    border-radius: 4px;
    font-family: 'Consolas', monospace;
    font-size: 0.9em;
    color: #3AB0A8;
}

/* -- Hero animated background (Landing Home) -- */
.hero-animated-bg {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    overflow: hidden;
    pointer-events: none;
    z-index: 1;
}

.shape {
    position: absolute;
}

.shape-circle {
    border-radius: 50%;
    background: linear-gradient(135deg, rgba(255,255,255,0.25), rgba(255,255,255,0.1));
    border: 1px solid rgba(255,255,255,0.2);
}

.shape-1 {
    width: 300px;
    height: 300px;
    top: -100px;
    right: -50px;
    opacity: 0.4;
    animation: float-1 20s ease-in-out infinite;
}

.shape-2 {
    width: 200px;
    height: 200px;
    bottom: 10%;
    left: 5%;
    opacity: 0.35;
    animation: float-2 25s ease-in-out infinite;
}

.shape-3 {
    width: 150px;
    height: 150px;
    top: 40%;
    right: 15%;
    opacity: 0.3;
    animation: float-3 18s ease-in-out infinite;
}

.shape-square {
    background: linear-gradient(45deg, rgba(255,255,255,0.2), rgba(255,255,255,0.08));
    border: 1px solid rgba(255,255,255,0.15);
    border-radius: 15px;
    transform: rotate(45deg);
}

.shape-4 {
    width: 120px;
    height: 120px;
    top: 20%;
    left: 10%;
    opacity: 0.35;
    animation: float-rotate-1 22s ease-in-out infinite;
}

.shape-5 {
    width: 90px;
    height: 90px;
    bottom: 20%;
    right: 10%;
    opacity: 0.3;
    animation: float-rotate-2 28s ease-in-out infinite;
}

.shape-triangle {
    width: 0;
    height: 0;
    border-left: 60px solid transparent;
    border-right: 60px solid transparent;
    border-bottom: 100px solid rgba(255,255,255,0.15);
    background: transparent;
}

.shape-6 {
    top: 60%;
    left: 20%;
    opacity: 0.4;
    animation: float-rotate-1 30s ease-in-out infinite;
}

.shape-7 {
    top: 15%;
    right: 25%;
    opacity: 0.35;
    animation: float-rotate-2 24s ease-in-out infinite reverse;
}

.shape-blob {
    border-radius: 50% 40% 60% 50%;
    background: radial-gradient(ellipse at center, rgba(79,208,203,0.3) 0%, rgba(79,208,203,0.1) 50%, transparent 70%);
}

.shape-8 {
    width: 500px;
    height: 500px;
    top: -200px;
    left: -150px;
    opacity: 0.5;
    animation: morph-1 30s ease-in-out infinite;
}

.shape-9 {
    width: 450px;
    height: 450px;
    bottom: -150px;
    right: -100px;
    opacity: 0.45;
    animation: morph-2 25s ease-in-out infinite;
}

.shape-wave {
    position: absolute;
    bottom: 0;
    left: 0;
    width: 100%;
    height: 200px;
    opacity: 1;
}

.shape-wave path {
    fill: rgba(255,255,255,0.08);
}

@keyframes float-1 {
    0%, 100% { transform: translate(0, 0) scale(1); }
    25%      { transform: translate(-30px, 40px) scale(1.05); }
    50%      { transform: translate(20px, -20px) scale(0.95); }
    75%      { transform: translate(-20px, -30px) scale(1.02); }
}

@keyframes float-2 {
    0%, 100% { transform: translate(0, 0) scale(1); }
    33%      { transform: translate(40px, -30px) scale(1.08); }
    66%      { transform: translate(-30px, 20px) scale(0.92); }
}

@keyframes float-3 {
    0%, 100% { transform: translate(0, 0); }
    50%      { transform: translate(-40px, 40px); }
}

@keyframes float-rotate-1 {
    0%, 100% { transform: rotate(45deg) translate(0, 0); }
    25%      { transform: rotate(55deg) translate(20px, -20px); }
    50%      { transform: rotate(35deg) translate(-10px, 30px); }
    75%      { transform: rotate(50deg) translate(-30px, -10px); }
}

@keyframes float-rotate-2 {
    0%, 100% { transform: rotate(45deg) translate(0, 0); }
    33%      { transform: rotate(60deg) translate(-20px, 20px); }
    66%      { transform: rotate(30deg) translate(30px, -30px); }
}

@keyframes morph-1 {
    0%, 100% { border-radius: 50% 40% 60% 50%; transform: rotate(0deg) scale(1); }
    25%      { border-radius: 40% 60% 50% 50%; transform: rotate(90deg) scale(1.1); }
    50%      { border-radius: 60% 50% 40% 60%; transform: rotate(180deg) scale(0.9); }
    75%      { border-radius: 50% 50% 60% 40%; transform: rotate(270deg) scale(1.05); }
}

@keyframes morph-2 {
    0%, 100% { border-radius: 60% 40% 50% 60%; transform: rotate(0deg); }
    50%      { border-radius: 40% 60% 60% 40%; transform: rotate(-180deg); }
}

@keyframes celebrationPop {
    from { opacity: 0; transform: scale(0.85); }
    to   { opacity: 1; transform: scale(1); }
}

@keyframes celebrationBounce {
    0%   { transform: translateY(20px) scale(0.5); opacity: 0; }
    50%  { transform: translateY(-10px) scale(1.1); }
    100% { transform: translateY(0) scale(1); opacity: 1; }
}

@keyframes slideInUp {
    from { opacity: 0; transform: translateY(20px); }
    to   { opacity: 1; transform: translateY(0); }
}

/* Reduce motion for accessibility */
@media (prefers-reduced-motion: reduce) {
    .shape {
        animation: none !important;
    }
}

/* Responsive hero shapes */
@media (max-width: 768px) {
    .shape-1 { width: 200px; height: 200px; }
    .shape-2 { width: 150px; height: 150px; }
    .shape-3 { width: 100px; height: 100px; }
    .shape-8, .shape-9 { width: 300px; height: 300px; }
}

/* ==============================================
   Auth Pages — Google-inspired sign-in / register
   ============================================== */

/* Full-page background */
.auth-layout-bg {
    min-height: 100vh;
    background-color: #f0f4f9;
}

/* Vertical flex wrapper that centres the card and pins the footer */
.auth-layout-wrapper {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    padding: 24px 16px;
    box-sizing: border-box;
}

/* Footer row at bottom of auth pages */
.auth-layout-footer {
    width: 100%;
    max-width: 960px;
    padding: 16px 0 8px;
    margin-top: auto;
}

/* -- Auth card (the white rounded box) -- */
.auth-card {
    width: 100%;
    max-width: 450px;
    border-radius: 28px !important;
    border: 1px solid #dadce0 !important;
    background-color: #fff !important;
    padding: 48px 40px 36px !important;
    box-sizing: border-box;
    margin: 16px auto;
}

.auth-card-inner {
    width: 100%;
}

/* -- Typography -- */
.auth-title {
    font-weight: 400 !important;
    color: #1f1f1f !important;
    margin-bottom: 4px !important;
    line-height: 1.35 !important;
}

.auth-subtitle {
    color: #5f6368 !important;
    margin-bottom: 0 !important;
}

/* -- Links -- */
.auth-forgot-link {
    display: inline-block;
    font-weight: 500 !important;
}

.auth-link {
    font-weight: 500 !important;
}

/* -- Primary action button (Next / Continue / Sign in) -- */
.auth-next-btn {
    border-radius: 20px !important;
    height: 40px !important;
    min-width: 90px !important;
    padding-left: 24px !important;
    padding-right: 24px !important;
    text-transform: none !important;
    font-weight: 500 !important;
    font-size: 0.875rem !important;
    letter-spacing: 0.0107em !important;
    box-shadow: none !important;
}

.auth-next-btn:hover {
    box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.15),
                0 1px 2px 0 rgba(0, 0, 0, 0.3) !important;
}

/* Sign-in / register / reset host for the MAUI heads.
   The web apps get their centring from AuthPageLayout, which the phones do not use — they have
   no room for its footer and their layouts own the theme and safe-area plumbing. This is that
   layout's one job, defined once so the owner app and the vet app cannot drift apart.

   A card is a web idea: a sheet of paper laid on a desk, with the desk showing around it. A phone
   has no desk — the app *is* the screen — so on the handsets the card stops being a card. One
   surface edge to edge, the mark at the top where a phone app puts its identity, and the fields
   centred in what is left so they land under the thumb rather than under the fold.
   overflow-y is auto, not hidden: the register form is taller than a phone. */
.pa-mobile-auth-content {
    display: flex;
    align-items: stretch;
    justify-content: center;
    min-height: 100vh;
    min-height: 100dvh;
    overflow-y: auto;
    background-color: var(--mud-palette-surface);
    padding-top: var(--safe-area-top, env(safe-area-inset-top, 0px));
    padding-bottom: var(--safe-area-bottom, env(safe-area-inset-bottom, 0px));
    box-sizing: border-box;
}

.pa-mobile-auth-content .auth-card {
    display: flex;
    background-color: transparent !important;
    border: none !important;
    border-radius: 0 !important;
    box-shadow: none !important;
    margin: 0;
    padding: 32px 24px calc(24px + var(--safe-area-bottom, env(safe-area-inset-bottom, 0px))) !important;
}

.pa-mobile-auth-content .auth-card-inner {
    flex: 1 1 auto;
    justify-content: flex-start;
}

/* auto on both sides is what centres the form in the space the header does not use, without
   claiming a height it might not have: a taller form (register) simply pushes the margins to
   nothing and scrolls, rather than being centred off the top of the screen. */
.pa-mobile-auth-content .auth-full-width {
    margin-top: auto !important;
    margin-bottom: auto !important;
}

/* The card's own colours are pinned to the web's white paper. On a phone the surface underneath
   is the theme's, and it is dark half the time. */
.pa-mobile-auth-content .auth-title {
    color: var(--mud-palette-text-primary) !important;
}

.pa-mobile-auth-content .auth-subtitle {
    color: var(--mud-palette-text-secondary) !important;
}

/* -- Responsive: tighten card on small screens -- */
@media (max-width: 480px) {
    .auth-card {
        padding: 32px 24px 24px !important;
        border-radius: 0 !important;
        border: none !important;
        max-width: 100%;
        margin: 0 auto;
    }

    .auth-layout-wrapper {
        padding: 0;
        justify-content: flex-start;
    }

    .auth-layout-footer {
        padding: 16px;
    }
}

/* ═══════════════════════════════════════════════════
   AI Markdown Content (.ai-md-content)
   Used by all LLM panels to render Markdig HTML.
   ═══════════════════════════════════════════════════ */
.ai-md-content {
    font-size: 0.875rem;
    line-height: 1.65;
    word-wrap: break-word;
    overflow-wrap: break-word;
}

.ai-md-content p {
    margin: 0 0 0.5em 0;
}

.ai-md-content p:last-child {
    margin-bottom: 0;
}

.ai-md-content h1,
.ai-md-content h2,
.ai-md-content h3,
.ai-md-content h4 {
    margin: 0.8em 0 0.4em 0;
    font-weight: 600;
    line-height: 1.3;
}

.ai-md-content h1 { font-size: 1.25rem; }
.ai-md-content h2 { font-size: 1.1rem; }
.ai-md-content h3 { font-size: 1rem; }
.ai-md-content h4 { font-size: 0.9rem; }

.ai-md-content ul,
.ai-md-content ol {
    margin: 0.4em 0;
    padding-left: 1.5em;
}

.ai-md-content li {
    margin-bottom: 0.25em;
}

.ai-md-content li::marker {
    color: var(--mud-palette-text-secondary);
}

.ai-md-content code {
    font-family: 'Cascadia Code', 'Fira Code', Consolas, monospace;
    font-size: 0.82em;
    background: var(--mud-palette-background-grey);
    padding: 1px 5px;
    border-radius: 4px;
}

.ai-md-content pre {
    background: var(--mud-palette-background-grey);
    border-radius: 8px;
    padding: 12px 16px;
    margin: 0.5em 0;
    overflow-x: auto;
    scrollbar-width: thin;
}

.ai-md-content pre code {
    background: transparent;
    padding: 0;
    font-size: 0.82em;
    line-height: 1.5;
}

.ai-md-content blockquote {
    border-left: 3px solid var(--mud-palette-primary);
    margin: 0.5em 0;
    padding: 0.25em 0.75em;
    color: var(--mud-palette-text-secondary);
}

.ai-md-content table {
    border-collapse: collapse;
    width: 100%;
    margin: 0.5em 0;
    font-size: 0.82em;
}

.ai-md-content th,
.ai-md-content td {
    border: 1px solid var(--mud-palette-lines-default);
    padding: 6px 10px;
    text-align: left;
}

.ai-md-content th {
    background: var(--mud-palette-background-grey);
    font-weight: 600;
}

.ai-md-content hr {
    border: none;
    border-top: 1px solid var(--mud-palette-lines-default);
    margin: 0.75em 0;
}

.ai-md-content strong {
    font-weight: 600;
}

.ai-md-content a {
    color: var(--mud-palette-primary);
    text-decoration: none;
}

/* ═══════════════════════════════════════════════════
   Formatted Text Field (.pa-formatted-text-field)
   RadzenHtmlEditor wrapper, styled to sit alongside
   MudBlazor outlined fields.
   ═══════════════════════════════════════════════════ */
.pa-formatted-text-field {
    margin-bottom: 4px;
}

.pa-formatted-text-field__label {
    font-size: 0.75rem;
    color: var(--mud-palette-text-secondary);
    margin-bottom: 4px;
}

.pa-formatted-text-field__editor {
    /* Re-point Radzen's theme variables at MudBlazor's — these already track light/dark mode
       reactively, so the editor follows the app's palette instead of Radzen's Material demo colors.
       Theme RadzenHtmlEditor through ITS OWN variables (border/radius/background) rather than
       boxing it in a second wrapper — it already draws its own chrome via --rz-editor-*, and
       doubling up on border/radius/overflow is what caused the corner mismatch below. */
    --rz-primary: var(--mud-palette-primary);
    --rz-primary-light: var(--mud-palette-primary-lighten);
    --rz-primary-lighter: var(--mud-palette-primary-lighten);
    --rz-primary-dark: var(--mud-palette-primary-darken);
    --rz-on-primary: var(--mud-palette-primary-text);
    --rz-secondary: var(--mud-palette-secondary);
    --rz-secondary-light: var(--mud-palette-secondary-lighten);
    --rz-secondary-dark: var(--mud-palette-secondary-darken);
    --rz-on-secondary: var(--mud-palette-secondary-text);
    --rz-danger: var(--mud-palette-error);
    --rz-danger-light: var(--mud-palette-error-lighten);
    --rz-danger-dark: var(--mud-palette-error-darken);
    --rz-warning: var(--mud-palette-warning);
    --rz-success: var(--mud-palette-success);
    --rz-info: var(--mud-palette-info);
    --rz-base: var(--mud-palette-background-gray);
    --rz-base-50: var(--mud-palette-background);
    --rz-base-100: var(--mud-palette-background-gray);
    --rz-base-200: var(--mud-palette-background-gray);
    --rz-base-300: var(--mud-palette-lines-inputs, var(--mud-palette-lines-default));
    --rz-base-lighter: var(--mud-palette-surface);
    --rz-base-light: var(--mud-palette-surface);
    --rz-text-color: var(--mud-palette-text-primary);
    --rz-text-secondary-color: var(--mud-palette-text-secondary);
    --rz-text-title-color: var(--mud-palette-text-primary);
    --rz-text-font-family: inherit;
    /* RadzenHtmlEditor's .rz-html-editor rule reads --rz-editor-border-radius and
       --rz-editor-focus-outline directly — NOT --rz-border-radius/--rz-primary. Those two are
       pre-resolved once at Radzen's :root (from its own unthemed defaults) before this override
       ever runs, and CSS custom-property inheritance passes down the *resolved* value, not the
       var() formula — so redefining --rz-border-radius/--rz-primary here has no effect on them.
       Set the actual consumed variables directly instead. */
    --rz-editor-border-radius: var(--mud-default-borderradius);
    --rz-editor-border: 1px solid var(--mud-palette-lines-inputs, var(--mud-palette-lines-default));
    --rz-editor-focus-outline: 2px solid var(--mud-palette-primary);
    --rz-editor-focus-outline-offset: -2px;
}

.pa-formatted-text-field__editor--error {
    --rz-editor-border: 1px solid var(--mud-palette-error);
}

/* RadzenHtmlEditor hard-codes overflow:hidden on its root, which clips its own toolbar
   dropdowns (font/color/table pickers — position:absolute children, not portaled to <body>)
   whenever the editor is short, e.g. inside a compact MudDialog. The content area already
   scrolls independently via .rz-html-editor-content{overflow:auto}, so it's safe to let the
   outer box stop clipping; give the toolbar/content their own corner radii so the rounded
   look survives without relying on overflow:hidden to clip square corners into shape. */
.pa-formatted-text-field__editor .rz-html-editor {
    overflow: visible;
}

/* Four buttons, so the toolbar stops being a ribbon and becomes part of the field. Radzen styles
   it as a filled bar with its own padding, which is what made a three-sentence note box look like
   a word processor; matching it to the content area and thinning it to a hairline leaves the
   buttons reading as controls on the field rather than a strip bolted above it. */
.pa-formatted-text-field__editor .rz-html-editor-toolbar {
    border-top-left-radius: var(--mud-default-borderradius);
    border-top-right-radius: var(--mud-default-borderradius);
    background: transparent;
    border-bottom: 1px solid var(--mud-palette-lines-default);
    padding: 2px 4px;
    gap: 0;
}

.pa-formatted-text-field__editor .rz-html-editor-content,
.pa-formatted-text-field__editor .rz-html-editor-source {
    border-bottom-left-radius: var(--mud-default-borderradius);
    border-bottom-right-radius: var(--mud-default-borderradius);
    min-height: var(--pa-editor-content-min-height, 96px);
    max-height: var(--pa-editor-content-max-height, none);
    overflow-y: auto;
}

.pa-formatted-text-field__helper,
.pa-formatted-text-field__error {
    font-size: 0.75rem;
    margin-top: 4px;
}

.pa-formatted-text-field__helper {
    color: var(--mud-palette-text-secondary);
}

.pa-formatted-text-field__error {
    color: var(--mud-palette-error);
}

/* ═══════════════════════════════════════════════════
   Shared Nav Menu Styles
   Used by nav menus across all apps.
   ═══════════════════════════════════════════════════ */

/* Root class on MudNavMenu itself. Global (not scoped .razor.css) because MudBlazor's
   mini-drawer CSS requires .mud-navmenu to be a DIRECT child of .mud-drawer-content
   (`.mud-drawer--closed.mud-drawer-mini > .mud-drawer-content > .mud-navmenu ...`),
   so the nav menu cannot be wrapped in a scope-carrying element. */
.nav-menu-root,
.nav-menu-shell {
    height: 100vh;
    display: flex;
    flex-direction: column;
    overflow-x: hidden;
}

.nav-section-header {
    margin-top: 0.5rem;
    transition: all 0.3s ease;
}

.nav-section-text {
    letter-spacing: 0.05em;
}

.nav-link-enhanced {
    margin: 2px 8px;
    border-radius: 8px !important;
    transition: all 0.2s ease-in-out !important;
}

.nav-link-enhanced:hover {
    background-color: rgba(79, 208, 203, 0.08) !important;
    transform: translateX(4px);
}

.nav-link-nested {
    margin: 2px 8px;
    border-radius: 8px !important;
    font-size: 0.875rem;
}

.support-link {
    opacity: 0.9;
    font-weight: 500;
}

.support-link:hover {
    opacity: 1;
    background-color: rgba(79, 208, 203, 0.12) !important;
}

/* Developer-only nav entries, marked as "not part of the product". The warning palette rather
   than a hand-picked orange: it is the theme's own "proceed knowingly" colour and it follows
   light/dark mode, which a literal hex does not. */
.dev-tool {
    border-left: 3px solid var(--mud-palette-warning);
    background-color: color-mix(in srgb, var(--mud-palette-warning) 10%, transparent) !important;
    opacity: 0.8;
}

.nav-footer {
    border-top: 1px solid rgba(0, 0, 0, 0.12);
    background: rgba(0, 0, 0, 0.04);
    transition: all 0.3s ease;
}

.nav-primary-section {
    overflow-y: auto;
    overflow-x: hidden;
    scrollbar-width: thin;
}

.nav-bottom-section {
    flex-shrink: 0;
}

.nav-primary-section::-webkit-scrollbar { width: 4px; }
.nav-primary-section::-webkit-scrollbar-track { background: transparent; }
.nav-primary-section::-webkit-scrollbar-thumb {
    background: rgba(0, 0, 0, 0.3);
    border-radius: 4px;
}
.nav-primary-section::-webkit-scrollbar-thumb:hover {
    background: rgba(0, 0, 0, 0.5);
}

/* ── Mobile Nav Header (gradient banner in drawer) ── */
.mobile-nav-header {
    position: relative;
}

.gradient-bg-animation {
    position: absolute;
    top: 0; left: 0; right: 0; bottom: 0;
    overflow: hidden;
}

.gradient-orb {
    position: absolute;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.1);
    backdrop-filter: blur(10px);
    animation: orbFloat 6s ease-in-out infinite;
}

.orb-1 { width: 120px; height: 120px; top: -60px; left: -60px; animation-delay: 0s; }
.orb-2 { width: 80px; height: 80px; top: 20%; right: -40px; animation-delay: 2s; }
.orb-3 { width: 60px; height: 60px; bottom: -30px; left: 20%; animation-delay: 4s; }

@keyframes orbFloat {
    0%, 100% { transform: translateY(0px) rotate(0deg); opacity: 0.7; }
    50%      { transform: translateY(-20px) rotate(180deg); opacity: 1; }
}

.animate-slide-down {
    animation: slideDown 0.8s ease-out;
}

@keyframes slideDown {
    from { opacity: 0; transform: translateY(-30px); }
    to   { opacity: 1; transform: translateY(0); }
}

@media (max-width: 600px) {
    .mobile-nav-header .mud-container {
        padding: 16px 12px;
    }
}

/* ═══════════════════════════════════════════════════
   Feature Cards (used by landing pages)
   ═══════════════════════════════════════════════════ */
.feature-card {
    height: 100%;
    border: 2px solid rgba(0, 0, 0, 0.08);
    border-radius: 16px;
    transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}

.feature-card:hover {
    transform: translateY(-8px) scale(1.02);
    box-shadow: 0 20px 60px rgba(0, 0, 0, 0.12) !important;
    border-color: #4FD0CB !important;
}

@media (max-width: 960px) {
    .feature-card:hover {
        transform: translateY(-4px) scale(1.01);
    }
}

/* ═══════════════════════════════════════════════════
   Page Headers & Stats Cards
   ═══════════════════════════════════════════════════ */
.page-header {
    background: linear-gradient(135deg, #4FD0CB 0%, #3A8F8A 100%);
    color: white;
    padding: 48px 0;
    border-radius: 0 0 24px 24px;
    margin-bottom: 32px;
}

.stats-card {
    background: linear-gradient(135deg, #4FD0CB 0%, #3A8F8A 100%);
    color: white;
    border-radius: 16px;
    padding: 24px;
    transition: all 0.3s ease;
}

.stats-card:hover {
    transform: translateY(-4px);
    box-shadow: 0 16px 48px rgba(0, 0, 0, 0.16);
}

/* ═══════════════════════════════════════════════════
   Icon buttons are circles — in every variant
   ═══════════════════════════════════════════════════
   MudBlazor rounds the plain icon button to 50%, but the moment one is given a Variant it also
   picks up .mud-button, whose rule pulls the radius back to the rectangular default. That is why
   every outlined/filled icon action — the bar at the top of the patient profile, the row actions
   in the tables, the same buttons in both mobile heads — rendered as a squircle next to the round
   ones. The padding MudBlazor gives them is already symmetric, so the radius is the whole fix.

   Button groups are excluded on purpose: a segmented group is meant to read as one control with
   square internal edges, not as a row of separate circles. */
.mud-icon-button.mud-button {
    border-radius: 50%;
}

.mud-button-group-root .mud-icon-button.mud-button {
    border-radius: var(--mud-default-borderradius);
}

/* ═══════════════════════════════════════════════════
   Cursor management (better UX across all apps)
   ═══════════════════════════════════════════════════ */
button,
.mud-button,
.mud-icon-button,
.mud-nav-link,
.mud-menu-item,
.mud-list-item,
.mud-table-row {
    cursor: pointer !important;
}

input:not([type="button"]):not([type="submit"]):not([type="reset"]),
textarea,
select,
.mud-input,
.mud-select,
.mud-textarea {
    cursor: text !important;
}

input[type="checkbox"],
input[type="radio"],
.mud-checkbox,
.mud-radio {
    cursor: pointer !important;
}

button:disabled,
.mud-button:disabled,
.mud-button.mud-disabled,
input:disabled,
select:disabled,
textarea:disabled,
.mud-disabled {
    cursor: not-allowed !important;
}

/* ══════════════════════════════════════════════
   PetAware Button Library (Pa*Button components)
   ══════════════════════════════════════════════ */

.pa-btn {
    border-radius: 12px !important;
    text-transform: none !important;
    font-weight: 500 !important;
    letter-spacing: 0.01em !important;
    transition: box-shadow 0.2s ease, transform 0.15s ease !important;
}

.pa-btn:hover:not(.mud-disabled) {
    transform: translateY(-1px);
}

.pa-btn:active:not(.mud-disabled) {
    transform: translateY(0);
}

.pa-icon-btn {
    border-radius: 12px !important;
    transition: background-color 0.2s ease !important;
}

.pa-btn-danger:hover:not(.mud-disabled) {
    box-shadow: 0 2px 8px rgba(234, 67, 53, 0.3) !important;
}

/* ══════════════════════════════════════════════
   Md3 List Item Cards (Event & Medication)
   ══════════════════════════════════════════════ */

.md3-event-item,
.md3-medication-item {
    cursor: pointer;
    border-radius: 12px !important;
    transition: box-shadow 0.2s ease, transform 0.15s ease;
}

.md3-event-item:hover,
.md3-medication-item:hover {
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
    transform: translateY(-1px);
}

.md3-event-avatar,
.md3-medication-avatar {
    border-radius: 12px !important;
    flex-shrink: 0;
}

.mud-text-weight-bold {
    font-weight: 600;
}

/* ══════════════════════════════════════════════
   Messaging panel (shared MessagingPanel component)
   Global (not scoped .razor.css) because these classes sit on MudBlazor
   components, which never receive the CSS-isolation scope attribute.
   ══════════════════════════════════════════════ */

/* Fill the available viewport height so the chat behaves like a messaging app rather than a
   short card. The offset leaves room for the app bar + page heading above the panel. */
.messaging-root {
    height: calc(100dvh - 200px);
    min-height: 460px;
}

.messaging-threads {
    flex: 0 0 340px;
    max-width: 340px;
    min-height: 0;
    overflow: hidden;
}

.messaging-thread-list {
    min-height: 0;
    overflow-y: auto;
}

/* MudTextField's root (.mud-input-control) is flex:1 1 auto by default, which makes the search
   field and composer greedily absorb the panes' free space (pushing the thread list to the middle
   and inflating the composer row). Pin them to their content height; the composer keeps its
   horizontal stretch via the flex-grow-1 utility (!important) on the component. */
.messaging-threads .mud-input-control,
.messaging-conversation .mud-input-control {
    flex: 0 0 auto;
}

/* Lets the thread title/preview column shrink so its text can truncate. */
.messaging-thread-text {
    min-width: 0;
}

.messaging-truncate {
    min-width: 0; /* flex items refuse to shrink below content width without this */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.messaging-conversation {
    flex: 1 1 auto;
    min-width: 0;
    min-height: 0;
    overflow: hidden;
}

/* The scrollable message area grows to fill the pane. The MudStack is reversed (column-reverse,
   newest message first in markup) so the view stays pinned to the newest message with no JS. */
.messaging-messages {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    padding: 8px 12px;
}

/* Preserve authored line breaks inside chat bubbles. */
.messaging-messages .mud-chat-bubble {
    white-space: pre-wrap;
    word-break: break-word;
}

/* The "send from which of our mailboxes" picker sits inline with the channel toggle, so it is
   sized to its content rather than stretching the composer's control row. */
.messaging-from-picker {
    max-width: 260px;
}

/* ── Inbound mail (the practice's inbox) ───────────────────────────── */

/* Plain-text email bodies carry meaning in their line breaks — quoted replies, addresses,
   signature blocks. Collapsing that whitespace turns a legible letter into one run-on paragraph. */
.inbound-mail-plaintext {
    white-space: pre-wrap;
    word-break: break-word;
}

/* Sanitised HTML from an outside sender. It has been stripped of scripts, styles, remote content
   and inline positioning on the way in, but its own markup can still be arbitrarily wide — so it
   scrolls inside its own panel rather than stretching the dialog. */
.inbound-mail-body {
    max-height: 420px;
    overflow: auto;
}

.inbound-mail-body table {
    max-width: 100%;
}

/* Queue rows open the message, so they have to look like they will. */
.inbox-row {
    cursor: pointer;
    transition: background-color 120ms ease;
}

.inbox-row:hover {
    background-color: var(--mud-palette-action-default-hover);
}

/* A port number is at most five digits, so the field is sized to its content rather than
   stretching to match the server name beside it. */
.mailbox-port-field {
    max-width: 120px;
}

/* The placeholder bubbles stand where real ones will be, so they carry the same corner. */
.messaging-skeleton-bubble {
    border-radius: var(--mud-default-borderradius);
}

/* Hidden on desktop; the conversation header shows it only in the single-pane mobile layout below. */
.messaging-back-btn {
    display: none;
}

/* On narrow screens, behave like a standard messaging app: one pane at a time (thread list, or the
   open conversation with a back button) instead of squeezing both panes side by side. */
@media (max-width: 700px) {
    .messaging-root {
        height: calc(100dvh - 150px);
    }

    .messaging-threads {
        flex: 1 1 auto;
        max-width: none;
    }

    /* !important required: both panes carry MudBlazor's d-flex utility (display:flex !important) */
    .messaging-conversation {
        display: none !important;
    }

    .messaging-back-btn {
        display: inline-flex;
    }

    .messaging-root.has-selection .messaging-threads {
        display: none !important;
    }

    .messaging-root.has-selection .messaging-conversation {
        display: flex !important;
        flex: 1 1 auto;
    }

    /* The split panel's own collapse rule hides the conversation on a narrow viewport, which is
       right when both panes share one route and wrong when the conversation *is* the route. */
    .messaging-root-thread-only .messaging-conversation {
        display: flex !important;
        flex: 1 1 auto;
    }
}

/* ── One pane per route (the mobile heads) ──
   The split panel is sized to sit under a page heading. Each of these owns its whole screen
   instead, so it takes the viewport minus the shell's chrome: the app bar and the status bar
   above, and — for the list, which keeps the bottom bar — the navigation bar and gesture inset
   below. The conversation hides the bottom bar so its composer owns that edge, which is why it
   subtracts only the safe-area inset. */
.messaging-root-list-only,
.messaging-root-thread-only {
    height: auto;
    min-height: 0;
    flex: 1 1 auto;
}

.messaging-root-list-only {
    height: calc(100dvh - 48px - var(--safe-area-top, env(safe-area-inset-top, 0px))
                 - 64px - var(--safe-area-bottom, env(safe-area-inset-bottom, 0px)));
}

/* The conversation has no app bar above it either — its own header is the screen's header — so it
   takes the viewport minus only the gesture inset the host has already padded for. */
.messaging-root-thread-only {
    height: calc(100dvh - var(--safe-area-bottom, env(safe-area-inset-bottom, 0px)));
}

/* Standing in for an app bar means looking like one: past the status bar, on the bar's own surface,
   and staying put while the messages scroll under it. */
.messaging-root-thread-only .messaging-conversation-header {
    position: sticky;
    top: 0;
    z-index: 2;
    background-color: var(--mud-palette-surface);
    padding-top: calc(8px + var(--safe-area-top, env(safe-area-inset-top, 0px))) !important;
}

/* Rows reach the screen edge, as they do in every chat app; the card border that reads as a
   panel on a desktop reads as a box drawn around the whole screen on a phone. */
.messaging-root-list-only .messaging-threads,
.messaging-root-thread-only .messaging-conversation {
    border: none;
    max-width: none;
}


/* ── Patient flag coloring (feedback round 2) ─────────────────────────────
   Gender tints the patient overview panel/row: blue = male, pink = female.
   A behavioural (caution) alert overrides gender with a deep-red hazard-stripe
   highlight — a pattern, not just a hue, so it can never be confused with the
   flat pink female tint. Works on block containers (cards/papers) and table
   rows alike; on <tr> the left accent is drawn on the first cell since borders
   on rows are swallowed by table border-collapse. Map flags via PetFlagCss.For(). */
.pa-flag-male,
tr.pa-flag-male > td:first-child {
    border-left: 4px solid var(--pa-flag-male);
}

.pa-flag-female,
tr.pa-flag-female > td:first-child {
    border-left: 4px solid var(--pa-flag-female);
}

.pa-flag-caution,
tr.pa-flag-caution > td:first-child {
    border-left: 5px solid var(--pa-flag-hazard);
}

.pa-flag-male {
    background-color: color-mix(in srgb, var(--pa-flag-male) var(--pa-flag-tint), transparent);
}

.pa-flag-female {
    background-color: color-mix(in srgb, var(--pa-flag-female) var(--pa-flag-tint), transparent);
}

.pa-flag-caution {
    background-image: repeating-linear-gradient(
        45deg,
        color-mix(in srgb, var(--pa-flag-hazard) 16%, transparent) 0 10px,
        color-mix(in srgb, var(--pa-flag-hazard) 4%, transparent) 10px 20px
    );
}

/* Row hover in MudTable applies its own background; keep the tint visible. */
tr.pa-flag-male.mud-table-row:hover,
tr.pa-flag-male:hover {
    background-color: color-mix(in srgb, var(--pa-flag-male) var(--pa-flag-tint-hover), transparent);
}

tr.pa-flag-female.mud-table-row:hover,
tr.pa-flag-female:hover {
    background-color: color-mix(in srgb, var(--pa-flag-female) var(--pa-flag-tint-hover), transparent);
}

tr.pa-flag-caution.mud-table-row:hover,
tr.pa-flag-caution:hover {
    background-image: repeating-linear-gradient(
        45deg,
        color-mix(in srgb, var(--pa-flag-hazard) 24%, transparent) 0 10px,
        color-mix(in srgb, var(--pa-flag-hazard) 8%, transparent) 10px 20px
    );
}

/* ── Vet Portal notification bell (popover content renders outside component scope) ── */

.notification-menu-list {
    min-width: 340px;
    max-width: 420px;
}

.notification-item-text {
    min-width: 0;
    white-space: normal;
}

.notification-title-unread {
    font-weight: 600;
}

/* ── Emergency accent: a notification in the bell, and a row in a list ──
   The same hazard hue as a caution flag, because they are the same message. It carries no stripe:
   an emergency row is read in a list of rows, where the tint plus the accent is enough, and the
   pattern is what a caution flag needs to out-shout a gender tint sitting underneath it. */
.notification-item-emergency,
tr.pa-flag-emergency,
.pa-flag-emergency {
    background-color: color-mix(in srgb, var(--pa-flag-hazard) var(--pa-flag-tint), transparent);
    border-left: 3px solid var(--pa-flag-hazard);
}

tr.pa-flag-emergency.mud-table-row:hover,
tr.pa-flag-emergency:hover {
    background-color: color-mix(in srgb, var(--pa-flag-hazard) var(--pa-flag-tint-hover), transparent);
}

/* ── Inventory CSV import preview (dialog content) ── */

.import-preview-table {
    overflow-x: auto;
}

/* ── Per-day working hours editor (Settings > Profile) ── */

.per-day-switch {
    min-width: 90px;
}

.override-date-text {
    min-width: 140px;
}

.rotation-weeks-select {
    max-width: 260px;
}

/* ── Signature preview (Settings > Prescriptions) ── */

.signature-preview {
    max-width: 420px;
}

.signature-preview-image {
    max-height: 60px;
    max-width: 100%;
}

.signature-preview-text {
    font-style: italic;
    color: var(--mud-palette-primary);
}

/* ── Clinical note templates inside a written note ──
   Marks text a note template contributed automatically so it never reads as an observation the
   vet actually made — it is confirmed or dropped before the record is saved. The heading class
   marks a template's own subsection heading, so it stays distinguishable from an element's label
   wherever the finished note is read back. */
.soap-default-text {
    font-style: italic;
    color: var(--mud-palette-text-secondary);
}

.soap-note-heading {
    margin-top: 12px;
    color: var(--mud-palette-primary);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    font-size: 0.8125rem;
}

.soap-note-heading:first-child {
    margin-top: 0;
}

/* ==========================================================================
   Global command palette (PaCommandPalette, Ctrl+K)
   ==========================================================================
   Lives here rather than in PaCommandPalette.razor.css for the same reason as the notification
   bell above: every surface that carries the palette's shape — the overlay, the panel, the chip
   row, the group headers, the footer — is rendered by a MudBlazor component, and Blazor's CSS
   isolation only stamps its scope attribute onto elements the component itself emits. A scoped
   `.pa-palette { … }` therefore compiles to `.pa-palette[b-xxxx]`, which matches nothing, and
   the panel silently loses its width, its height ceiling and its padding. Global is not a
   shortcut here; it is the only selector that can reach the markup.
   Every value below is a design-system token or a step on the 4px rhythm — see DESIGN.md. */

.pa-palette {
    /* One gutter, named once. The input, the chips, the rows, the group headers and the footer
       all indent to the same line; a palette where the search box and the results it produces
       sit on two different left edges reads as two stacked widgets. */
    --pa-palette-gutter: 16px;
    --pa-palette-gutter-tight: 8px;
    /* 12px — the ladder's list-item step (DESIGN.md § Shape), one below the 16px container. */
    --pa-palette-row-radius: 12px;

    /* ── Width ──
       A floor as well as a ceiling. Before the first keystroke the panel holds only a text field
       and a hint, and without the floor it opens as a narrow slot that then jerks out to full
       width on the first result — the panel appears to be still loading when it is simply empty. */
    min-width: min(26rem, 100%);
    width: min(680px, 100%);
    max-width: 100%;

    /* ── Height ──
       The floor keeps the empty, "keep typing", no-matches and results states the same size, so
       the panel does not resize under the cursor as the user types through all four. The ceiling
       is two limits because they fail in opposite directions: 100% keeps the panel inside the
       overlay's padded content box, and the rem keeps it from becoming an absurd column of rows on
       a 4K monitor turned portrait. The floor is itself capped by the parent so it can never exceed
       the ceiling on a short view. */
    min-height: min(24rem, 100%);
    max-height: min(100%, 34rem);

    display: flex;
    flex-direction: column;
    box-sizing: border-box;
    /* Anything past the ceiling scrolls in the results pane (below), never by growing the panel
       or spilling out of the overlay. */
    overflow: hidden;
    border-radius: var(--mud-default-borderradius);
    background-color: var(--mud-palette-surface);
    /* The global flat-surface rule strips box-shadow from every .mud-paper, which is right for
       working surfaces and wrong for this one: a transient overlay is above the plane by
       definition and takes MudBlazor's native elevation (DESIGN.md § Elevation & Depth). The
       !important is answering the global rule's own !important, not raising specificity for
       its own sake. */
    box-shadow: var(--mud-elevation-12) !important;
}

/* The palette docks near the top of the viewport rather than centring, because it grows
   downwards as results arrive — a vertically centred panel would shift the row under the
   cursor every time the result count changed. The horizontal padding is what guarantees the
   panel remains contained even when the palette is hosted in a constrained overlay view. */
.pa-palette-overlay .mud-overlay-content {
    display: flex;
    align-items: flex-start;
    justify-content: center;
    width: 100%;
    height: 100%;
    box-sizing: border-box;
    padding: 10vh 16px 16px;
    overflow: hidden;
}

.pa-palette-stack {
    display: flex;
    flex-direction: column;
    /* min-height:0 is what lets the results pane, not the panel, be the thing that scrolls. */
    min-height: 0;
    flex: 1 1 auto;
}

.pa-palette-input {
    padding: var(--pa-palette-gutter-tight) var(--pa-palette-gutter) 0;
}

/* Reserved whether or not a search is running, so starting one does not shove the whole result
   list down by the height of the bar. */
.pa-palette-progress {
    height: 4px;
    flex: 0 0 auto;
    margin-top: var(--pa-palette-gutter-tight);
}

/* The entity tabs. A single non-wrapping row that scrolls sideways rather than a wrapping chip
   cloud: these are the way in, and a control whose height changes as the practice's record types
   change would move the search box under the user's cursor. */
.pa-palette-tabs {
    display: flex;
    flex: 0 0 auto;
    gap: 6px;
    align-items: center;
    padding: var(--pa-palette-gutter-tight) var(--pa-palette-gutter);
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scrollbar-width: thin;
}

.pa-palette-tabs .pa-palette-tab {
    flex: 0 0 auto;
    margin: 0;
}

.pa-palette-results {
    overflow-y: auto;
    overscroll-behavior: contain;
    flex: 1 1 auto;
    /* A floor tall enough for the empty and error states, so the panel does not resize as the
       user types through "no query → too short → no matches → results". */
    min-height: 13rem;
    padding: var(--pa-palette-gutter-tight) var(--pa-palette-gutter-tight);
}

/* Set from JS while the list scrolls itself under a stationary cursor: the row that slides
   beneath the pointer is not a row the user pointed at. Released on the next real mouse move. */
.pa-palette-results.pa-palette-no-pointer {
    pointer-events: none;
}

.pa-palette-group-header {
    /* Indents to the row text, not to the row box: the rows carry their own 8px inset from the
       results padding, so the header aligns with the label rather than with the hover shape. */
    padding: 12px var(--pa-palette-gutter-tight) 4px;
    color: var(--mud-palette-text-secondary);
}

.pa-palette-group-action {
    /* A secondary action sitting in a caption-sized row; the default button padding would make
       the header taller than the rows it labels. */
    padding: 0 var(--pa-palette-gutter-tight);
    min-width: 0;
}

.pa-palette-state {
    padding: 32px 24px;
}

/* Guidance copy wraps rather than truncating — it is prose, and it is the only thing on screen. */
.pa-palette-state-hint,
.pa-palette-state-title {
    max-width: 34ch;
    text-wrap: pretty;
}

/* The query is echoed back inside a heading with one line to work with, and it can arrive as a
   single 120-character token with no break opportunity in it. */
.pa-palette-state-title {
    overflow-wrap: anywhere;
}

.pa-palette-inline-hint {
    display: block;
    padding: 12px var(--pa-palette-gutter-tight) 4px;
}

.pa-palette-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 8px 12px;
    border-radius: var(--pa-palette-row-radius);
    cursor: pointer;
    transition: background-color 90ms linear;
}

@media (prefers-reduced-motion: reduce) {
    .pa-palette-row {
        transition: none;
    }
}

/* Selection is driven by the keyboard as much as the mouse, so it is a real state rather
   than :hover — the highlighted row must follow the arrow keys with the pointer nowhere near.
   The teal tint is the same one the nav uses for an active row, rather than the fainter neutral
   action-hover: on a glare-lit front-desk monitor a grey wash at 4% is not a visible state. A
   left accent border is deliberately *not* used here — in a list of patients it would be read as
   a patient flag, and those hues and edges mean something clinical. */
.pa-palette-row-selected {
    background-color: var(--mud-palette-primary-hover);
}

@media (forced-colors: active) {
    .pa-palette-row-selected {
        forced-color-adjust: none;
        background-color: Highlight;
        color: HighlightText;
    }
}

.pa-palette-row-icon {
    flex: 0 0 auto;
}

.pa-palette-row-text {
    flex: 1 1 auto;
    min-width: 0;
}

/* Both identity lines truncate to one line each. A 90-character imported item name, or a
   subtitle concatenating owner and phone, would otherwise reflow the row to three lines and
   destroy the even rhythm that makes the list scannable in the first place. */
.pa-palette-row-title,
.pa-palette-row-subtitle {
    display: block;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* The trailing detail is a price, a balance or a status — it earns its width but never at the
   expense of the name, which is what the user is actually reading. */
.pa-palette-row-detail {
    flex: 0 1 auto;
    max-width: 35%;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    text-align: end;
}

.pa-palette-footer {
    padding: var(--pa-palette-gutter-tight) var(--pa-palette-gutter);
}

.pa-palette-count {
    white-space: nowrap;
}

/* Announced, never drawn. Clipped rather than display:none, which removes it from the
   accessibility tree along with everything it was supposed to say. */
.pa-palette-live {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

/* On a phone or a hand-held tablet the palette is the screen. Docking a 680px card into a 390px
   viewport leaves a strip of dimmed page that does nothing but shrink the result list, and the
   keyboard hints name keys the device does not have. */
@media (max-width: 599.98px) {
    .pa-palette-overlay .mud-overlay-content {
        padding: 0;
    }

    .pa-palette {
        min-width: 0;
        width: 100%;
        max-width: 100%;
        height: 100%;
        min-height: 0;
        max-height: 100%;
        border-radius: 0;
    }

    .pa-palette-hint {
        display: none;
    }

    /* A touch target, not a pointer target. */
    .pa-palette-row {
        padding: 12px;
        min-height: 48px;
    }
}

/* Short viewports (a laptop with the diary open, or a landscape phone) get the vertical space
   back from the padding rather than from the result list. */
@media (max-height: 560px) and (min-width: 600px) {
    .pa-palette-overlay .mud-overlay-content {
        padding: 2vh 16px 16px;
    }

    .pa-palette {
        min-height: 0;
        max-height: 100%;
    }

    .pa-palette-results {
        min-height: 8rem;
    }
}

/* ── Rows that open a record ──────────────────────────────────────────────
   The affordance that pairs with RecordLink: where a table row opens the record its
   name links to, the whole row has to look clickable. Cells that carry their own
   controls stop the click and keep the default cursor, so they must not look like
   they open anything either. Shared rather than page-scoped because "clicking a row
   opens that record" is a portal-wide convention, not one table's behaviour. */
.pa-record-row {
    cursor: pointer;
}

.pa-record-row .pa-row-cell-controls {
    cursor: auto;
}

/* ── The resuscitation mark ───────────────────────────────────────────────
   A chip next to a name invites a press. Where there is a field behind it the press works
   and the chip says so; where there isn't — a list, the wall board — it has to stop looking
   like a control, or the reader who tries it learns the mark is decoration. */
/* cursor only — not pointer-events: none. The tooltip spelling out "Do not resuscitate" is the
   whole point of the mark on a list, and killing pointer events would take it with the press. */
.pa-resus-mark-inert {
    cursor: default;
}

.pa-resus-mark-actionable {
    cursor: pointer;
}

/* ══════════════════════════════════════════════
   MOBILE SHELL — touch devices only
   Scoped to a coarse pointer rather than a viewport width: these are corrections for a hand
   holding a phone, not for a narrow browser window, and a resized desktop window should not
   start growing thumb-reach offsets and bottom-sheet animations.
   ══════════════════════════════════════════════ */
@media (hover: none) and (pointer: coarse) {

    /* The screen's primary action, in the arc a thumb sweeps without the hand shifting grip.
       Clears the bottom navigation bar and, under it, whatever the gesture handle took — the
       same measured inset the nav bar itself uses, because Android's WebView reports
       env(safe-area-inset-bottom) as 0px however the window is configured. */
    .pa-mobile-fab {
        position: fixed;
        right: 16px;
        bottom: calc(var(--pa-bottom-nav-height) + 16px + var(--safe-area-bottom, env(safe-area-inset-bottom, 0px)));
        z-index: 1050;
    }

    /* ── The sheet clears the bar that opened it ──
       A bottom sheet raised from the bottom bar would otherwise render on top of that bar, so the
       control you just pressed disappears under the thing it opened, and the row nearest the
       bottom sits exactly where your thumb already is. Lifting it clear of the bar's whole
       footprint keeps "More" visible and puts a gap between the finger and the first choice. */
    .mud-dialog-container.mud-dialog-position-bottomcenter .mud-dialog {
        margin-bottom: calc(var(--pa-bottom-nav-height) + 8px + var(--safe-area-bottom, env(safe-area-inset-bottom, 0px)));
    }

    /* A dialog positioned at the bottom of a phone screen is a bottom sheet, and a sheet that
       simply appears reads as a rendering glitch rather than as something arriving. MudBlazor's
       own transition classes are the hook — overriding them keeps the close half working, which
       is the half a hand-rolled animation cannot do: Blazor removes the node on close, so there
       is nothing left to animate unless the library's own exit transition is what is running. */
    .mud-dialog-container .mud-dialog {
        animation: pa-sheet-in 220ms cubic-bezier(0, 0, 0.2, 1);
    }

    .mud-dialog-container.mud-dialog-position-bottomcenter .mud-dialog {
        animation: pa-sheet-up 240ms cubic-bezier(0, 0, 0.2, 1);
    }

    /* The scrim arriving with the sheet rather than snapping on ahead of it. */
    .mud-overlay .mud-overlay-scrim {
        animation: pa-scrim-in 200ms ease-out;
    }

    @keyframes pa-sheet-up {
        from { opacity: 0; transform: translateY(24px); }
        to { opacity: 1; transform: translateY(0); }
    }

    @keyframes pa-sheet-in {
        from { opacity: 0; transform: scale(0.96); }
        to { opacity: 1; transform: scale(1); }
    }

    @keyframes pa-scrim-in {
        from { opacity: 0; }
        to { opacity: 1; }
    }

    /* MudBlazor's timeline reserves a wide gutter for the alternating layout it cannot use on a
       phone. Pulling the rail in gives the row back the width it needs for a patient's name. */
    .day-timeline .mud-timeline-item {
        padding-bottom: 8px;
    }

    @media (prefers-reduced-motion: reduce) {
        .mud-dialog-container .mud-dialog,
        .mud-dialog-container.mud-dialog-position-bottomcenter .mud-dialog,
        .mud-overlay .mud-overlay-scrim {
            animation: none;
        }
    }
}

/* ===== Error boundary technical details (debug builds) =====

   A stack trace is only useful if it is readable as a stack trace: fixed-pitch, wrapped rather than
   clipped, and scrollable so a deep trace cannot push the "Try again" button off the screen it is
   meant to rescue. Never rendered in a release build — see PaErrorBoundary.ShowDetails. */
.pa-error-details {
    width: 100%;
    max-width: 720px;
}

.pa-error-stack {
    display: block;
    font-family: ui-monospace, 'Cascadia Mono', 'Consolas', monospace;
    white-space: pre-wrap;
    word-break: break-word;
    max-height: 40vh;
    overflow-y: auto;
    color: var(--mud-palette-text-secondary);
}

/* ══════════════════════════════════════════════
   RECORD HISTORY SIDE SHEET — PaRecordHistorySheet
   Global rather than a .razor.css because everything sized here is markup MudBlazor emits, which
   CSS isolation's scope attribute never reaches — and because the container is rendered by
   MudDialogProvider, outside the component's own tree entirely.

   The panel is a dialog, not a MudDrawer, so that a page's stacking context cannot capture it.
   DialogPosition.Custom is the one position MudBlazor ships no rules for, which is why the sheet
   can be flush to the edge and full height instead of a floating card with a 32px margin.
   ══════════════════════════════════════════════ */

/* :has() keeps this off every other Custom-positioned dialog — the position is a blank canvas,
   not a reservation. */
.mud-dialog-container.mud-dialog-custom:has(> .pa-history-sheet) {
    align-items: stretch;
    justify-content: flex-end;
    padding: 0;
}

.mud-dialog.pa-history-sheet {
    width: 440px;
    max-width: 100vw;
    height: 100%;
    max-height: 100%;
    margin: 0;
    border-radius: 0;
    /* The body is the scroller. A sheet that scrolls as a whole takes its own header with it. */
    overflow: hidden;
    animation: pa-history-sheet-in 200ms cubic-bezier(0, 0, 0.2, 1) both;
}

/* On a narrow window the record behind it is unusable anyway, so the sheet takes the screen. */
@media (max-width: 599.98px) {
    .mud-dialog.pa-history-sheet {
        width: 100vw;
    }
}

@keyframes pa-history-sheet-in {
    from { transform: translateX(100%); }
    to { transform: translateX(0); }
}

@media (prefers-reduced-motion: reduce) {
    .mud-dialog.pa-history-sheet {
        animation: none;
    }
}

.pa-history-sheet .mud-dialog-title {
    flex: 0 0 auto;
    padding: 12px 8px 12px 20px;
    border-bottom: 1px solid var(--mud-palette-lines-default);
}

.pa-history-sheet .pa-history-sheet__title {
    width: 100%;
}

/* The two halves of the header row. The title takes the slack and gives it back by truncating;
   the actions never shrink, so Close sits at the sheet's edge whatever the record is called. */
.pa-history-sheet .pa-history-sheet__title-text {
    flex: 1 1 auto;
    min-width: 0;
}

/* The caption is wrapped in a tooltip so the full name is still readable once it is truncated;
   the tooltip's own inline-block wrapper has to be allowed to shrink or nothing truncates. */
.pa-history-sheet .pa-history-sheet__title-text .mud-tooltip-root {
    display: block;
    min-width: 0;
}

.pa-history-sheet .pa-history-sheet__title-text .mud-typography {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.pa-history-sheet .pa-history-sheet__actions {
    flex: 0 0 auto;
}

.pa-history-sheet .mud-dialog-content {
    padding: 0;
}

.pa-history-sheet .pa-history-sheet__body {
    padding: 1rem 1rem 2rem;
}

.pa-history-sheet .pa-history-sheet__state {
    min-height: 12rem;
    padding: 2rem 1rem;
}

.pa-history-sheet .pa-history-sheet__more {
    padding-bottom: 1.5rem;
}
