/* ==========================================================================
   Hikma admin portal - the ONE stylesheet for every admin screen.
   (App.razor links it once, so no page loads its own CSS and there are no scoped
   .razor.css files. Per-company brand colours arrive as CSS variables injected by
   App.razor and apply everywhere.) To change something sitewide, change the shared
   class - never an inline style or a page-specific copy. The families:

     Sidebar ......... .admin-sidebar (+ __brand, __brand-mark, __brand-name, __toggle), .admin-nav; collapsed to a rail by
                       html[data-sidebar="collapsed"] (SidebarToggle / SidebarPreload / admin.js)
     List head ....... .list-head (+ __title): a list's title left, its search / filters grouped right (Kitchen Operations, Orders)
     Header .......... .admin-header (+ __titles, __icon), .user-menu (the signed-in person's menu: + __trigger, __name, __chevron,
                       __panel, __who, __section, __label, __item, __item--danger, __switcher), .user-avatar
     Stat strip ...... .stat-strip > .stat-strip__item (+ __icon, __text, __label, __value, __sub; --accent | --attention |
                       --warn | --bad | --link | --active) - every dashboard's headline figures, via StatStrip/StatStripItem
     Kitchen list .... .ops-list, .ops-card (+ __main, __media, __body, __side, __reveal), .ops-slot,
                       .ops-pill, .ops-plan, .ops-eta, .ops-menu, .ops-stage(s), .ops-batchrow
     Page chrome ..... .kitchen-page (page column), .page-header (title row),
                       .page-toast / .page-toast--error (saved / error notices)
     Lists ........... .catalog-section (the white card) + .catalog-table(-wrap)  [catalog
                       screens, Users, Roles]  and  .orders-table  [Orders, Catering,
                       Reviews]. Two table families: a general table fix may need both.
                       .filter-bar + .filter-pill (the shared search / status / date pills), .search-filter-bar
                       (a catalog list's filter bar: the shared search pill + Inactive / Deleted chips), .col-actions
     Forms ........... .meal-form > .form-card (fieldset + legend), .form-field, .tf,
                       .lbl, .meal-form__actions, .detail-accordion (multi-section pages)
     Buttons ......... .btn + .btn--cta | --primary-action | --outline | --danger | --sm
     Pickers/grids ... .perm-group/.perm-grid, .badge-chip, .catalog-picker, .component-*
     Settings ........ .settings-group > .setting (+ __head, __badges, __control, __unit, __code, __access, __actions; --saved) -
                       the generic settings editor (SettingsEditor), on the Platform portal and the client Settings page
     Utilities ....... .u-mt-sm / .u-mt-md, at the END of this file so they win
   The only inline style left in markup is display:none on filter-hidden table rows
   (admin.js toggles those). The customer web app has its own app.css; the two share a
   colour palette kept in step by hand.
   ========================================================================== */

:root {
    /* ---- Page frame: the SAME minimal spacing on every admin screen in both portals. Top / side padding of the content area (the
       header uses the side value too, so their edges line up) and the gap between the blocks of a page. Change here only. ---- */
    --page-pad-y: 6px;
    --page-pad-x: 8px;
    --page-gap: 6px;
    /* The inner side padding of a summary card (.stat-strip__item). A section title above a list uses it as its left inset, so the title's
       text lines up with the content of the cards above it instead of sitting on the cards' outer edge. */
    --card-pad-x: .9rem;

    /* ---- Hikma brand palette (the six-ingredient palette) — same tokens as the
       customer Web app's app.css, kept in sync manually since the two projects
       don't share a CSS file. ---- */
    --coriander:      #8A9A5B;
    --coriander-rgb:  138, 154, 91;
    --coriander-deep: #6C7847;
    --cardamom:       #8A9A5B;
    --turmeric:       #D9A441;
    --turmeric-deep:  #C08A2E;
    --turmeric-soft:  #FBF0DC;
    --cinnamon:       #8B5E3C;
    --cinnamon-soft:  #F1E5D8;
    --ivory:          #FAF8F3;
    --iron:           #24302B;

    --teal:        var(--coriander);
    --teal-deep:   var(--coriander-deep);
    --teal-soft:   var(--cardamom);
    --ink-heading: var(--iron);
    --ink:         #3A4A43;
    --ink-soft:    #6B776F;
    --line:        #E4DDCF;
    --line-input:  #D6CDBC;
    --wash:        #F1EEE4;
    /* Type scale for new screens: five sizes (the portal's older rules still carry ~30 one-off sizes). */
    --fs-xs: .75rem;    /* captions, small labels, tags */
    --fs-sm: .88rem;    /* body text, buttons, form fields */
    --fs-base: 1rem;    /* names, emphasised values */
    --fs-lg: 1.15rem;   /* card / section titles */
    --fs-xl: 1.7rem;    /* one big number (serves) */

    --cta:      var(--turmeric);
    --cta-deep: var(--turmeric-deep);
    --cta-soft: var(--turmeric-soft);

    --success:      var(--coriander);
    --success-soft: #E3ECE7;
    --error:        #B3261E;
    --error-soft:   #FBEAE5;
    --error-border: #E08A80;
    --warning-text: #8A6425;
    --info:         #5F6B3D;
    --info-soft:    #EEF0E2;

    --serif: 'Fraunces', Georgia, 'Times New Roman', serif;
    --sans: 'Plus Jakarta Sans', -apple-system, 'Segoe UI', Roboto, sans-serif;
}

* { box-sizing: border-box; }
body { margin: 0; font-family: var(--sans); color: var(--ink); background: var(--ivory); }
h1 { font-family: var(--serif); font-weight: 700; font-size: 1.4rem; color: var(--ink-heading); margin: 0 0 .2rem; }
h2, h3, h4, h5, h6 { font-family: var(--serif); font-weight: 600; color: var(--ink-heading); }

.lbl { display: block; font-size: .82rem; color: var(--ink-soft); margin: .8rem 0 .3rem; }
.tf { width: 100%; border: 1.5px solid #D6CDBC; border-radius: 9px; padding: .72rem .8rem; font: inherit; background: #fff; }
.tf:focus { outline: none; border-color: var(--teal); }

/* text-decoration/text-transform reset here (not just on .btn--cta) because a handful of
   these are <a> tags styled as buttons (the "+ Create X" links below, Reviews' "Export"/
   "Generate Sticker") — a real <button> never picks up the browser's default link
   underline, but an anchor does unless something clears it. */
.btn { font-family: var(--sans); font-weight: 600; border-radius: 999px; cursor: pointer; padding: .8rem 1.6rem; font-size: .95rem; border: 1.5px solid var(--teal); background: #fff; color: var(--ink); text-decoration: none; text-transform: none; }
.btn--cta { background: var(--teal); color: #fff; }
.btn--cta:hover { background: var(--teal-deep); }
.btn--block { width: 100%; margin-top: 1rem; }
/* "+ Create X" entry points (Meals/Menu Items/Modifier Groups/Tags/Categories catalog
   list pages) — layered on top of .btn--cta rather than changing it everywhere, since
   .btn--cta's white-on-teal default doesn't clear WCAG AA contrast at this font size
   (~3:1); .teal-deep (already used as .btn--cta's hover shade) does, at ~4.8:1. */
.btn--create { background: var(--teal-deep); }
.btn--create:hover { filter: brightness(.92); }
.btn-link { background: none; border: none; color: var(--teal-deep); font: inherit; cursor: pointer; padding: 0; text-decoration: underline; }
.btn:disabled { opacity: .5; cursor: not-allowed; }

.login-shell { min-height: 100vh; display: grid; grid-template-columns: 1fr 1fr; }
@media (max-width: 860px) { .login-shell { grid-template-columns: 1fr; } }

/* Left panel — no photo asset wired in yet, so this is a brand-colour gradient standing
   in for one; swap in a background-image on .login-hero (same rule) whenever a real
   photo's ready, everything else here is unaffected. */
.login-hero {
    display: none;
    flex-direction: column;
    justify-content: space-between;
    padding: 3rem;
    background: radial-gradient(circle at 15% 15%, rgba(217, 164, 65, .35), transparent 45%),
                linear-gradient(160deg, var(--iron) 0%, #1a2420 55%, var(--cinnamon) 140%);
    color: #fff;
    position: relative;
    overflow: hidden;
}
@media (min-width: 861px) { .login-hero { display: flex; } }
.login-hero::after {
    content: ""; position: absolute; inset: 0; pointer-events: none;
    background: radial-gradient(circle at 85% 90%, rgba(255,255,255,.06), transparent 40%);
}
.login-hero__brand { display: flex; align-items: center; gap: .7rem; }
.login-hero__mark { display: inline-flex; align-items: center; justify-content: center; width: 46px; height: 46px; border-radius: 50%; background: rgba(255,255,255,.12); color: var(--turmeric); flex: none; }
.login-hero__name { font-family: var(--serif); font-size: 2rem; margin: 0; line-height: 1.1; }
.login-hero__name-primary { color: #fff; font-weight: 700; }
.login-hero__name-accent { color: var(--turmeric); font-weight: 500; display: block; }
.login-hero__tagline { font-size: 1.05rem; color: rgba(255,255,255,.85); line-height: 1.5; margin: 0; position: relative; z-index: 1; }

.login-panel { display: flex; align-items: center; justify-content: center; padding: 2rem 1.5rem; background: var(--ivory); }
.login-panel__inner { width: 100%; max-width: 380px; }
.login-eyebrow { font-size: .74rem; letter-spacing: 1.5px; text-transform: uppercase; color: var(--teal-deep); font-weight: 700; }
.login-title { font-family: var(--serif); font-size: 2.1rem; margin: .35rem 0 .5rem; color: var(--ink-heading); }
.login-subtitle { color: var(--ink-soft); margin: 0 0 1.6rem; line-height: 1.5; }

.login-field { position: relative; }
.login-field__icon { position: absolute; left: .85rem; top: 50%; transform: translateY(-50%); color: var(--ink-soft); display: flex; pointer-events: none; }
.login-field__input { padding-left: 2.6rem; }
.login-submit { display: flex; align-items: center; justify-content: center; gap: .5rem; }
.login-submit__arrow { font-size: 1.05rem; }

.login-error { color: #b3261e; background: #FBEAE5; border-radius: 8px; padding: .6rem .8rem; font-size: .88rem; margin: 0 0 1rem; }
.login-info { color: #8a6d1f; background: #FBF3E0; border-radius: 8px; padding: .6rem .8rem; font-size: .88rem; margin: 0 0 1rem; }
.login-resend { margin-top: .9rem; text-align: center; }

.admin-shell { min-height: 100vh; display: flex; }

.admin-sidebar { width: 240px; flex: none; background: #24302B; color: #fff; display: flex; flex-direction: column; padding: 1.5rem 0; }
.admin-sidebar__brand { display: flex; align-items: center; gap: .6rem; padding: 0 1.25rem 1.5rem; }
.admin-sidebar__brand-mark { display: flex; align-items: center; justify-content: center; width: 34px; height: 34px; border-radius: 9px; background: var(--teal); color: #fff; font-weight: 800; font-size: .85rem; }
.admin-sidebar__brand-name { font-weight: 700; }
.admin-nav { display: flex; flex-direction: column; }

/* Collapsible sidebar (both portals). SidebarToggle is the button in the brand row; the state is html[data-sidebar="collapsed"], applied by
   SidebarPreload before paint and flipped by admin.js. Collapsed = a slim rail with just the brand mark and the button (the nav has no icons
   to show on its own, so it is hidden whole). */
.admin-sidebar { transition: width .18s ease; }
@media (prefers-reduced-motion: reduce) { .admin-sidebar { transition: none; } }
.admin-sidebar__brand-mark { flex: none; }
.admin-sidebar__brand-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.admin-sidebar__toggle { margin-left: auto; flex: none; display: grid; place-items: center; width: 30px; height: 30px; padding: 0; border: none; border-radius: 8px; background: transparent; color: #C7C2B5; cursor: pointer; }
.admin-sidebar__toggle:hover { background: rgba(255,255,255,.1); color: #fff; }
.admin-sidebar__toggle:focus-visible { outline: 2px solid var(--turmeric); outline-offset: 2px; }
.admin-sidebar__toggle-open { display: none; }
html[data-sidebar="collapsed"] .admin-sidebar { width: 64px; }
html[data-sidebar="collapsed"] .admin-sidebar__brand { flex-direction: column; justify-content: center; gap: .85rem; padding: 0 0 1.25rem; }
html[data-sidebar="collapsed"] .admin-sidebar__brand-name, html[data-sidebar="collapsed"] .admin-nav { display: none; }
html[data-sidebar="collapsed"] .admin-sidebar__toggle { margin-left: 0; }
html[data-sidebar="collapsed"] .admin-sidebar__toggle-close { display: none; }
html[data-sidebar="collapsed"] .admin-sidebar__toggle-open { display: block; }
.admin-nav__item { display: block; padding: .7rem 1.25rem; color: #C7C2B5; text-decoration: none; font-size: .92rem; font-weight: 500; border-left: 3px solid transparent; cursor: pointer; }
.admin-nav__item:hover { background: rgba(255,255,255,.05); }
.admin-nav__item--active { background: rgba(47,111,94,.12); border-left-color: var(--teal); color: #fff; font-weight: 700; }
.admin-nav__item--muted { color: #6B665A; cursor: default; }
.admin-nav__item--muted:hover { background: none; }

.admin-nav__group { display: flex; flex-direction: column; }
.admin-nav__group-label { display: flex; align-items: center; justify-content: space-between; padding: .7rem 1.25rem .3rem; color: #C7C2B5; font-size: .92rem; font-weight: 500; }
.admin-nav__item--sub { padding-left: 2.15rem; font-size: .86rem; display: flex; align-items: center; gap: .5rem; }
.admin-nav__item--sub::before { content: ''; width: 4px; height: 4px; border-radius: 50%; background: currentColor; opacity: .6; flex: none; }

.admin-content { flex: 1; min-width: 0; }
.admin-header { display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding: 1.25rem var(--page-pad-x); background: #fff; border-bottom: 1px solid var(--line); }
.admin-header__title { margin: 0; }
/* Detail/create pages now put the breadcrumb (.admin-header__subtitle) BEFORE the <h1>;
   list pages still put a plain descriptive subtitle AFTER their <h2>/<h1> — both orders
   coexist, so the gap between them is driven by sibling combinators rather than a fixed
   margin on either element alone, since which one is "first" differs by page. */
.admin-header__subtitle { margin: 0; color: var(--ink-soft); font-size: .88rem; }
h1 + .admin-header__subtitle, h2 + .admin-header__subtitle { margin-top: .2rem; }
.admin-header__subtitle + h1 { margin-top: .3rem; }
.admin-header__actions { display: flex; align-items: center; gap: 1rem; }
.admin-header__titles--with-icon { display: flex; align-items: center; gap: .85rem; }
.admin-header__icon { display: grid; place-items: center; color: var(--ink-heading); flex: none; }
.user-avatar { width: 36px; height: 36px; border-radius: 50%; object-fit: cover; background: var(--wash); flex: none; }

/* The signed-in person's menu (UserMenu.razor), shared by both portals: a <details> whose summary is the avatar + name and whose panel holds
   who they are, the portal's own entries and Log out. */
.user-menu { position: relative; }
.user-menu__trigger { display: inline-flex; align-items: center; gap: .55rem; padding: .25rem .55rem .25rem .3rem; border: 1px solid transparent; border-radius: 999px; list-style: none; cursor: pointer; color: var(--ink-heading); font-size: var(--fs-sm); font-weight: 600; }
.user-menu__trigger::-webkit-details-marker { display: none; }
.user-menu__trigger:hover, .user-menu[open] > .user-menu__trigger { background: var(--wash); border-color: var(--line); }
.user-menu__trigger:focus-visible { outline: 2px solid var(--teal); outline-offset: 2px; }
.user-menu__chevron { color: var(--ink-soft); transition: transform .15s ease; }
.user-menu[open] > .user-menu__trigger .user-menu__chevron { transform: rotate(180deg); }
.user-menu__panel { position: absolute; right: 0; top: calc(100% + 6px); z-index: 40; min-width: 250px; padding: .35rem; background: #fff; border: 1px solid var(--line); border-radius: 12px; box-shadow: 0 10px 28px rgba(0,0,0,.14); }
.user-menu__who { display: flex; flex-direction: column; gap: .1rem; padding: .55rem .7rem .65rem; border-bottom: 1px solid var(--line); margin-bottom: .3rem; }
.user-menu__who-name { font-weight: 700; color: var(--ink-heading); }
.user-menu__who-email { font-size: var(--fs-xs); color: var(--ink-soft); overflow-wrap: anywhere; }
.user-menu__section { margin: 0; padding: .25rem 0; }
.user-menu__section + .user-menu__section { border-top: 1px solid var(--line); margin-top: .25rem; padding-top: .35rem; }
.user-menu__label { display: block; padding: 0 .7rem .25rem; font-size: var(--fs-xs); font-weight: 600; color: var(--ink-soft); text-transform: uppercase; letter-spacing: .04em; }
.user-menu__switcher select { display: block; width: calc(100% - 1.4rem); margin: 0 .7rem; font: inherit; padding: .4rem .6rem; border: 1px solid var(--line); border-radius: 8px; background: #fff; color: var(--ink); }
.user-menu__item { display: flex; align-items: center; gap: .6rem; width: 100%; padding: .55rem .7rem; border: none; border-radius: 8px; background: none; text-align: left; font: inherit; font-size: var(--fs-sm); color: var(--ink); cursor: pointer; }
.user-menu__item:hover, .user-menu__item:focus-visible { background: var(--wash); }
.user-menu__item--danger { color: var(--error); font-weight: 600; }
.user-menu__item--danger:hover, .user-menu__item--danger:focus-visible { background: var(--error-soft); }
@media (max-width: 720px) { .user-menu__name, .user-menu__chevron { display: none; } }
/* 1180px was cramping the catalog tables (Associations accordion etc.) once rows grew
   enough action buttons to wrap/overlap — widened sitewide. .meal-form and .save-bar
   below carry the same number on purpose (both are full-width children of .admin-main,
   not independently narrower) so a future width change only needs updating in sync here. */
/* Compact page frame: the smallest comfortable gap between the header and the first thing on the page (top) and the sidebar and
   the content (sides); the header uses the same side padding so their edges line up. */
.admin-main { max-width: 1440px; margin: 0; padding: var(--page-pad-y) var(--page-pad-x); }

/* Printing an order sticker (see OrderSticker.razor) should show just the sticker —
   no sidebar, no header, no page chrome. First printable surface in this app. */
@media print {
    .admin-sidebar, .admin-header { display: none; }
    .admin-content, .admin-main { max-width: none; margin: 0; padding: 0; }
    .sticker__print { display: none; }
}

.sticker-page { display: flex; flex-direction: column; align-items: center; gap: 1.5rem; padding: 2rem 0; }
.sticker { width: 60mm; padding: 4mm; border: 1px dashed var(--line); border-radius: 8px; display: flex; flex-direction: column; align-items: center; gap: 3mm; text-align: center; }
.sticker__order { font-weight: 700; font-size: 1rem; }
.sticker__qr { width: 42mm; height: 42mm; }
.sticker__caption { font-size: .78rem; color: var(--ink-soft); }
.sticker__meal { font-size: .8rem; color: var(--ink); }
.sticker__url { font-size: .72rem; color: var(--ink-soft); word-break: break-all; max-width: 320px; text-align: center; }

@media print {
    .sticker { border: none; }
    .sticker__url { display: none; }
}

.kitchen-page, .orders-page, .dashboard-page, .reviews-page { display: flex; flex-direction: column; gap: var(--page-gap); }
/* The flex gap above already spaces a page's direct children, so a header / toast /
   accordion that ALSO carries its own bottom margin was double-spaced — ~2.7rem between
   the title, the "created" notice and the first card instead of 1.5rem. Direct children
   only: a header or accordion nested inside another wrapper keeps its own margin. */
/* Create screens wrap their accordions in one <form class="meal-create-form"> (so a single
   submit posts everything); lay it out like the Edit screen's stack of accordions. */
.meal-create-form { display: flex; flex-direction: column; gap: var(--page-gap); }
.meal-create-form > .detail-accordion { margin-bottom: 0; }
.kitchen-page > .page-header,
.kitchen-page > .page-toast,
.kitchen-page > .detail-accordion { margin-bottom: 0; }

/* ---- Stat strip: the headline figures of every dashboard (Kitchen, Dashboard, Orders, Catering, Meal Scheduling,
   Reviews) as ONE band of compact items with a clear hairline between every item, across and down. The band's own
   background shows through a 1px gap between white items, so the dividers are always right - however the items wrap -
   and a short last row simply stretches. --cols is how many items sit on a row: all of them on a wide screen (4 from
   1000px, 5-6 from 1280px), three on a medium one, two on a phone. ---- */
.stat-strip { --cols: 3; display: flex; flex-wrap: wrap; gap: 1px; background: var(--line); border: 1px solid var(--line); border-radius: 12px; overflow: hidden; }
.stat-strip__item { flex: 1 1 calc(100% / var(--cols) - 2px); display: flex; align-items: center; gap: .6rem; min-width: 0; padding: .6rem var(--card-pad-x); background: #fff; color: inherit; text-decoration: none; }
@media (min-width: 1000px) {
    .stat-strip:has(> :nth-child(4):last-child) { --cols: 4; }
}
@media (min-width: 1280px) {
    .stat-strip:has(> :nth-child(5):last-child) { --cols: 5; }
    .stat-strip:has(> :nth-child(6):last-child) { --cols: 6; }
    .stat-strip:has(> .stat-strip__item--actions:nth-child(5):last-child) { --cols: 6; }
}
@media (max-width: 640px) {
    .stat-strip { --cols: 2; }
}
.stat-strip__icon { display: grid; place-items: center; width: 34px; height: 34px; border-radius: 10px; background: var(--wash); color: var(--ink-heading); flex: none; }
/* Label on the first line; the figure and its small note share the second line when the note fits beside it, and the note drops to
   a line of its own (never truncated) when it does not - so a wide strip is two lines tall and a narrow one still shows everything. */
.stat-strip__text { display: flex; flex-wrap: wrap; column-gap: .45rem; align-items: baseline; min-width: 0; }
.stat-strip__label { flex: 1 0 100%; font-size: var(--fs-sm); line-height: 1.25; color: var(--ink-soft); }
.stat-strip__value { flex: none; font-size: var(--fs-lg); line-height: 1.1; color: var(--ink-heading); }
.stat-strip__sub { flex: 1 1 auto; min-width: 0; font-size: var(--fs-xs); line-height: 1.25; color: var(--ink-soft); }
.stat-strip__item--accent { background: #EEF3E6; }
.stat-strip__item--accent .stat-strip__icon { background: transparent; color: var(--teal-deep); }
.stat-strip__item--accent .stat-strip__sub { color: var(--teal-deep); }
.stat-strip__item--attention { background: var(--cta-soft); }
.stat-strip__item--attention .stat-strip__value { color: var(--cta-deep); }
.stat-strip__item--warn .stat-strip__icon { background: #FBF0DC; color: #8A6425; }
.stat-strip__item--bad .stat-strip__icon { background: #FBEAE5; color: #b3261e; }
.stat-strip__item--link { transition: background-color .15s ease; }
.stat-strip__item--link:hover { background: var(--wash); }
.stat-strip__item--attention.stat-strip__item--link:hover { background: var(--cta-soft); }
.stat-strip__item--active { box-shadow: inset 0 0 0 2px var(--teal-deep); }
.stat-strip__item--actions { flex-direction: column; align-items: flex-start; justify-content: center; gap: .5rem; flex-grow: 2; flex-basis: calc(200% / var(--cols) - 2px); }
/* The label is a row-wise flex item elsewhere (flex: 1 0 100% = a full line); in this column it would take the cell's whole height and push the buttons out. */
.stat-strip__item--actions .stat-strip__label { flex: none; }
/* The buttons sit side by side at their own width, and wrap onto a second line when the cell is narrow. */
.stat-strip__buttons { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem .75rem; }
.stat-strip__item--actions form { margin: 0; }
.stat-strip__item--actions .btn { margin: 0; padding: .5rem 1rem; font-size: .82rem; white-space: nowrap; }

/* ---- Kitchen Operations list: one card per meal (its current batch), with a reveal for the rest ---- */
.ops-list { display: flex; flex-direction: column; gap: .75rem; }
/* ---- List head, shared by Kitchen Operations and Orders: the list's title on the left, the search and filters grouped on the right ---- */
.list-head { --controls-basis: 47rem; display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: .75rem 1.25rem; }
/* A list with more controls (Orders: a date RANGE and a fulfilment filter) needs more room before the controls drop under the title. */
.list-head--many-controls { --controls-basis: 63rem; }
/* The single action bar above the list: the title on the left; the search and the status filter grouped together on the right. */
/* Inset by the cards' inner padding (+1px for the strip's border) so "Scheduled Meals" starts where the content of the summary cards above it starts. */
/* Smaller than the page title (h1, 1.4rem) above it: a bare <h2> falls back to the browser default of 1.5rem, which made it BIGGER. */
.list-head__title { margin: 0; padding-left: calc(var(--card-pad-x) + 1px); font-size: var(--fs-lg); }
/* The day(s) the list is showing, in brackets after the title: "(Today)", "(22 Oct 2026)", "(22 - 24 Oct 2026)". Quieter than the title. */
.list-head__scope { font-family: var(--sans); font-size: var(--fs-sm); font-weight: 500; color: var(--ink-soft); margin-left: .2rem; }
/* The controls stay compact and together at the right edge, a .75rem apart. The bar asks for --controls-basis (the room its controls need
   at their SMALLEST) - not what they would take at full size - so it stays beside the title until that really does not fit; only then does
   the whole bar drop to a row of its own under it (still right-aligned). */
.list-head > .filter-bar { flex: 1 1 var(--controls-basis); margin-left: auto; justify-content: flex-end; gap: .75rem; }
/* Only the SEARCH is flexible: it starts at its smallest comfortable size (12.5rem) and grows to at most 18rem (288px - never stretching across
   the free space), so a crowded row squeezes it instead of wrapping. The dropdowns are 9rem (their labels are short) and the date range keeps
   its full 19rem: a date input squeezed below its natural width cuts the year off. */
.list-head .filter-pill--search { flex: 1 1 12.5rem; max-width: 18rem; }
.list-head .filter-pill--select { flex: 0 0 9rem; }
.list-head .filter-pill--date { flex: 0 0 19rem; }
.ops-list__empty { margin: 0; padding: .85rem 1.25rem; text-align: center; color: var(--ink-soft); background: #fff; border: 1px dashed var(--line); border-radius: 12px; }
/* The "no meals scheduled" card: an icon, a line of what is missing, a hint, and the button that fixes it - stacked and centred. */
.ops-empty { display: flex; flex-direction: column; align-items: center; gap: .4rem; padding: 2rem 1.25rem; }
.ops-empty__icon { color: var(--ink-soft); line-height: 0; }
.ops-empty__title { margin: 0; font-size: var(--fs-lg); font-weight: 600; color: var(--ink); }
.ops-empty__hint { margin: 0 0 .6rem; }

.ops-card { background: #fff; border: 1px solid var(--line); border-radius: 14px; overflow: hidden; }
.ops-card__main { display: grid; grid-template-columns: 130px minmax(0, 1fr) minmax(310px, 380px); gap: 1.25rem; padding: 1.1rem; align-items: start; }
.ops-card__media { position: relative; }
.ops-card__media img { display: block; width: 130px; height: 120px; object-fit: cover; border-radius: 12px; }
.ops-card__batchbadge { position: absolute; top: .45rem; right: -.35rem; padding: .18rem .6rem; border-radius: 7px; background: var(--teal-deep); color: #fff; font-size: var(--fs-xs); font-weight: 600; box-shadow: 0 1px 4px rgba(0,0,0,.2); }
.ops-card__serves { display: flex; align-items: baseline; justify-content: center; gap: .35rem; margin: .55rem 0 0; color: var(--ink-soft); }
.ops-card__serves strong { font-size: var(--fs-xl); line-height: 1; color: var(--ink-heading); }
.ops-card__serves span { font-size: var(--fs-base); }
.ops-card__name { margin: 0 0 .35rem; font-size: var(--fs-lg); color: var(--ink-heading); }
.ops-card__meta { display: flex; flex-wrap: wrap; align-items: center; gap: .3rem 1.1rem; margin: 0 0 .85rem; color: var(--ink-soft); font-size: var(--fs-sm); }
.ops-card__metaitem { display: inline-flex; align-items: center; gap: .35rem; }

/* The menu items: a bordered box, one column per slot, thin dividers between columns */
.ops-menubox { border: 1px solid var(--line); border-radius: 12px; padding: .7rem .9rem .9rem; background: #fff; }
.ops-menubox__title { margin: 0 0 .6rem; font-size: var(--fs-sm); font-weight: 700; color: var(--ink-heading); }
.ops-menubox__title span { font-weight: 400; color: var(--ink-soft); }
.ops-menubox__slots { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); }
.ops-slot { padding: 0 1rem; border-left: 1px solid var(--line); }
.ops-slot:first-child { padding-left: 0; border-left: none; }
.ops-slot:last-child { padding-right: 0; }
.ops-slot__title { margin: 0 0 .5rem; font-size: var(--fs-sm); font-weight: 700; color: var(--ink-heading); }
.ops-slot__title span { font-weight: 400; color: var(--ink-soft); font-size: var(--fs-sm); }
.ops-slot__item { display: flex; align-items: center; gap: .65rem; margin: 0 0 .55rem; }
.ops-slot__item:last-child { margin-bottom: 0; }
.ops-slot__thumb { width: 46px; height: 46px; border-radius: 10px; object-fit: cover; flex: none; }
.ops-slot__thumb--none { background: var(--wash); }
.ops-slot__text { display: flex; flex-direction: column; min-width: 0; }
.ops-slot__name { font-size: var(--fs-sm); color: var(--ink-heading); }
.ops-slot__tag { font-size: var(--fs-xs); color: var(--ink-soft); }

.ops-card__side { display: flex; flex-direction: column; gap: .8rem; }
.ops-card__status { display: flex; align-items: center; justify-content: space-between; gap: .6rem; }
.ops-card__action { display: inline-flex; align-items: center; gap: .5rem; padding: .6rem 1.4rem; border-radius: 999px; font-weight: 600; }
.ops-pill { display: inline-block; padding: .4rem 1rem; border-radius: 999px; font-size: var(--fs-sm); font-weight: 600; background: var(--wash); color: var(--ink-soft); }
.ops-pill--scheduled { background: #E8F0F8; color: #35618F; }
.ops-pill--preparing { background: var(--turmeric-soft); color: var(--turmeric-deep); }
.ops-pill--cooking { background: #FBE7D5; color: #B5601F; }
.ops-pill--ready { background: #E3ECE7; color: var(--teal-deep); }

/* The plan box: editable start + cooking time on the first row, the two ETAs below a divider */
.ops-plan { padding: .9rem 1rem; background: #fff; border: 1px solid var(--line); border-radius: 12px; }
.ops-plan__form { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) auto; gap: .5rem .9rem; align-items: end; }
.ops-plan__cell { display: flex; flex-direction: column; gap: .2rem; min-width: 0; }
.ops-plan__label { margin: 0; font-size: var(--fs-xs); color: var(--ink-soft); }
.ops-plan__form .tf { padding: .4rem .5rem; font-size: var(--fs-sm); }
.ops-plan__read, .ops-eta { display: grid; grid-template-columns: 1fr 1fr; gap: .5rem .9rem; margin: 0; }
.ops-plan__read dd, .ops-eta dd { margin: 0; font-weight: 600; color: var(--ink-heading); font-size: var(--fs-base); }
.ops-eta { margin-top: .8rem; padding-top: .8rem; border-top: 1px solid var(--line); }
.ops-eta__shift { margin: .55rem 0 0; font-size: var(--fs-xs); font-weight: 600; }
.ops-eta__shift--later { color: #B3261E; }
.ops-eta__shift--earlier { color: var(--teal-deep); }
.ops-menu { position: relative; }
.ops-menu__button { list-style: none; cursor: pointer; padding: .2rem .55rem; border-radius: 8px; font-size: var(--fs-lg); line-height: 1; color: var(--ink-soft); }
.ops-menu__button::-webkit-details-marker { display: none; }
.ops-menu__button:hover { background: var(--wash); }
.ops-menu__list { position: absolute; right: 0; z-index: 20; min-width: 190px; padding: .35rem; background: #fff; border: 1px solid var(--line); border-radius: 10px; box-shadow: 0 10px 28px rgba(0,0,0,.14); }
.ops-menu__item { display: block; width: 100%; padding: .5rem .7rem; border: none; border-radius: 8px; background: none; text-align: left; font: inherit; font-size: var(--fs-sm); color: var(--ink); cursor: pointer; list-style: none; }
.ops-menu__item:hover { background: var(--wash); }
.ops-menu__item--danger { color: #B3261E; }

.ops-card__reveal { border-top: 1px solid var(--line); background: #F7F8F2; }
/* The whole bar is the control (a <summary> spans the card's width): a roomy target for a cook's thumb, tinted when pointed at, with a focus ring for the keyboard. */
.ops-card__reveal > summary { display: flex; align-items: center; justify-content: flex-start; gap: .7rem; min-height: 3rem; padding: .5rem 1.25rem; cursor: pointer; list-style: none; user-select: none; transition: background-color .15s; }
.ops-card__reveal > summary:hover { background: #EEF3E6; }
.ops-card__reveal > summary:focus-visible { outline: 2px solid var(--teal-deep); outline-offset: -2px; }
.ops-card__reveal > summary::-webkit-details-marker { display: none; }
/* The expand / collapse cue: an outlined round button with a chevron that flips when the card is open. It reads as a control at rest (green outline) and fills
   when the bar is pointed at. */
.ops-card__toggle { flex: none; display: grid; place-items: center; width: 34px; height: 34px; border-radius: 50%; background: #fff; border: 2px solid var(--teal-soft); color: var(--teal-deep); transition: background .15s, border-color .15s; }
.ops-card__reveal > summary:hover .ops-card__toggle { background: #DDE7CC; border-color: var(--teal-deep); }
/* The words beside the chevron: what the card reveals, as "Show ..." while it is closed and "Hide ..." once it is open. */
.ops-card__togglelabel { font-size: .9rem; font-weight: 600; color: var(--teal-deep); }
.ops-card__togglesteps { font-size: .85rem; color: var(--ink-soft); }
.ops-card__reveal:not([open]) .ops-card__togglelabel--hide, .ops-card__reveal[open] .ops-card__togglelabel--show { display: none; }
.ops-card__toggle svg { transition: transform .2s; }
.ops-card__reveal[open] .ops-card__toggle svg { transform: rotate(180deg); }


.ops-reveal { display: grid; gap: 1.1rem; padding: 0 1.1rem 1.1rem; }
.ops-reveal h4 { margin: 0 0 .5rem; font-size: var(--fs-base); }   /* serif + weight come from the global h2-h6 rule */
.ops-reveal h4 small { font-weight: 400; color: var(--ink-soft); margin-left: .4rem; }
/* Cooking Progress: the stage stepper (left) and the current step (right) */
.ops-progress { display: grid; grid-template-columns: minmax(0, 1fr) minmax(240px, 340px); grid-template-areas: "title side" "steps card"; gap: .5rem 1.5rem; align-items: start; }
.ops-progress__heading { grid-area: title; margin: 0; }
.ops-progress__heading--side { grid-area: side; }
.ops-progress__steps { grid-area: steps; }
.ops-currentstep { grid-area: card; }
.ops-steps { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); margin: 0; padding: 0; list-style: none; }
.ops-step { position: relative; display: flex; flex-direction: column; align-items: center; gap: .15rem; text-align: center; }
.ops-step::before { content: ''; position: absolute; top: 20px; right: 50%; width: 100%; height: 2px; background: var(--line); }
.ops-step:first-child::before { display: none; }
.ops-step--done::before, .ops-step--current::before { background: var(--teal-deep); }
.ops-step__dot { position: relative; z-index: 1; display: grid; place-items: center; width: 40px; height: 40px; border-radius: 50%; background: var(--wash); color: var(--ink-soft); margin-bottom: .35rem; }
.ops-step--done .ops-step__dot { background: var(--teal-deep); color: #fff; }
.ops-step--current .ops-step__dot { background: #DDE9CC; }
.ops-step--current .ops-step__dot { color: var(--teal-deep); box-shadow: 0 0 0 3px var(--teal-deep); }
.ops-step__label { font-size: var(--fs-sm); font-weight: 600; color: var(--ink-heading); }
.ops-step--pending .ops-step__label { font-weight: 400; color: var(--ink-soft); }
.ops-step--pending .ops-step__dot { background: var(--wash); color: #A3ADA7; }
.ops-step--current .ops-step__label { color: var(--teal-deep); }
.ops-step__time { font-size: var(--fs-sm); color: var(--ink-soft); }
.ops-currentstep__card { display: flex; align-items: center; gap: .8rem; padding: .9rem 1rem; background: #EEF3E6; border-radius: 12px; }
.ops-currentstep__icon { display: grid; place-items: center; flex: none; color: var(--teal-deep); }
.ops-currentstep__card div { display: flex; flex-direction: column; gap: .15rem; }
.ops-currentstep__card span:not(.ops-currentstep__icon) { font-size: var(--fs-sm); color: var(--ink-soft); }

/* Photos & Updates: pictures + upload (left), the note and media controls (right), checkbox and Save along the foot */
.ops-updates__form { display: grid; grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr); gap: 1rem 1.5rem; }
.ops-updates__photos { min-width: 0; }
.ops-photos { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: .85rem; margin-bottom: .85rem; }
.ops-photos:empty { display: none; }
.ops-photo { margin: 0; }
.ops-photo__frame { position: relative; }
.ops-photo__frame img { display: block; width: 100%; height: 105px; object-fit: cover; border-radius: 10px; }
.ops-photo__badge { position: absolute; left: .4rem; bottom: .4rem; padding: .1rem .5rem; border-radius: 6px; background: var(--teal-deep); color: #fff; font-size: var(--fs-xs); font-weight: 600; }
.ops-photo__badge--staff { background: #6B776F; }
.ops-photo__caption { margin-top: .3rem; font-size: var(--fs-sm); color: var(--ink); }
.ops-photo__time { display: block; margin-top: .15rem; padding-bottom: .25rem; border-bottom: 1px solid var(--line); font-size: var(--fs-xs); color: var(--ink-soft); }
.ops-updates__photos .catalog-dropzone { padding: 1.1rem; border: 2px dashed var(--line-input); border-radius: 12px; background: #fff; text-align: center; }
.ops-updates__note { display: flex; flex-direction: column; gap: .35rem; min-width: 0; }
.ops-note__label { font-size: var(--fs-sm); font-weight: 600; color: var(--ink-heading); }
.ops-note__label span { font-weight: 400; color: var(--ink-soft); }
.ops-updates__note textarea { resize: vertical; }
.ops-note__count { align-self: flex-end; font-size: var(--fs-xs); color: var(--ink-soft); }
.ops-media__title { margin: .5rem 0 .2rem; font-size: var(--fs-sm); font-weight: 700; color: var(--ink-heading); }
.ops-media__buttons { display: flex; flex-wrap: wrap; gap: .6rem; }
.ops-media__btn { display: inline-flex; align-items: center; gap: .4rem; flex: 1 1 auto; justify-content: center; padding: .55rem .7rem; white-space: nowrap; }
.ops-updates__foot { grid-column: 1 / -1; display: flex; align-items: center; justify-content: flex-end; gap: 1rem; }
.ops-updates__save { display: inline-flex; align-items: center; gap: .5rem; border-radius: 999px; }
.ops-updates__none { margin: 0; color: var(--ink-soft); font-size: var(--fs-sm); }
.ops-reveal__actual { margin: .6rem 0 0; font-size: var(--fs-sm); color: var(--ink-soft); }
.ops-batches { display: flex; flex-direction: column; gap: .5rem; }
.ops-batchrow { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem 1rem; padding: .6rem .8rem; background: #fff; border: 1px solid var(--line); border-radius: 10px; font-size: var(--fs-sm); }
.ops-batchrow__id small { color: var(--ink-soft); margin-left: .35rem; }
.ops-batchrow__times { color: var(--ink-soft); flex: 1; }
@media (max-width: 1100px) {
    .ops-card__main { grid-template-columns: 120px minmax(0, 1fr); }
    .ops-card__media img { width: 120px; height: 100px; }
    .ops-card__side { grid-column: 1 / -1; }
}
@media (max-width: 820px) {
    .ops-progress { grid-template-columns: 1fr; grid-template-areas: "title" "steps" "side" "card"; }
    .ops-updates__form { grid-template-columns: 1fr; }
}
@media (max-width: 640px) {
    .ops-card__main { grid-template-columns: 1fr; }
    .ops-card__media img { width: 100%; height: 150px; }
    .ops-menubox__slots { grid-auto-flow: row; gap: .9rem; }
    .ops-slot, .ops-slot:first-child, .ops-slot:last-child { padding: 0; border-left: none; }
}



.date-tabs { display: flex; gap: .5rem; flex-wrap: wrap; }
.date-tab { display: block; padding: .5rem 1rem; border-radius: 10px; background: #fff; border: 1px solid var(--line); color: var(--ink); text-decoration: none; font-size: .85rem; font-weight: 600; }
.date-tab--active { background: var(--teal); border-color: var(--teal); color: #fff; }

.orders-section { background: #fff; border: 1px solid var(--line); border-radius: 16px; padding: 1.25rem; }
.orders-section h2 { font-size: 1rem; margin: 0 0 1rem; }
.orders-table-wrap { overflow-x: auto; }
.orders-table { width: 100%; border-collapse: collapse; font-size: .88rem; }
.orders-table thead tr { background: var(--wash); }
.orders-table th { text-align: left; font-size: .72rem; color: var(--ink-soft); text-transform: uppercase; letter-spacing: .04em; font-weight: 700; padding: .7rem .85rem; border-bottom: 1px solid var(--line); white-space: nowrap; }
.orders-table td { padding: .75rem .85rem; border-bottom: 1px solid var(--line); vertical-align: middle; }
.orders-table tbody tr:last-child td { border-bottom: none; }
.orders-empty { color: var(--ink-soft); margin: 0; }

/* Each filter control is its own pill sitting directly on the page background (not one
   bounding card) — selects/dates auto-submit via this.form.requestSubmit() (same pattern
   MainLayout's company switcher already uses), search submits on Enter, so there's no
   separate Filter button. Clear filters only appears once something is actually set. */
/* The ONE filter / action bar of every list screen (Orders, Catering, Kitchen Operations, and every catalog list via
   SearchFilterBar): one compact row directly above the data, the search and status pills close together, and any
   result count riding at the right end of the same row instead of on a line of its own. */
.filter-bar { display: flex; align-items: center; justify-content: flex-start; gap: .5rem; flex-wrap: wrap; }
.filter-pill {
    display: flex; align-items: center; gap: .5rem; height: 2.4rem;
    border: 1.5px solid #D6CDBC; border-radius: 10px; background: #fff; padding: 0 .9rem;
}
.filter-pill--search { flex: 0 1 22rem; min-width: 200px; }
@media (pointer: coarse) { .filter-pill { height: 2.85rem; } }
.filter-pill--select { flex: 0 0 10rem; padding: 0 .5rem 0 .9rem; }
.filter-pill--date { flex: 0 1 19rem; min-width: 240px; color: var(--ink-soft); }
.filter-pill--day { flex: 0 0 11rem; }   /* one day (Kitchen Operations), where --date is a from - to range (Orders, Catering) */
.filter-pill__icon { color: var(--ink-soft); flex: none; }
.filter-pill__input {
    border: none; outline: none; font: inherit; width: 100%; background: transparent; color: var(--ink);
}
.filter-pill__input::placeholder { color: var(--ink-soft); }
.filter-pill__select { border: none; outline: none; font: inherit; background: transparent; color: var(--ink); width: 100%; cursor: pointer; }
.filter-pill__date { border: none; outline: none; font: inherit; background: transparent; color: var(--ink); flex: 1 1 auto; min-width: 0; }
.filter-pill__dash { flex: none; }

/* Command-palette-style search dropdown — generic, reusable across the app: any
   [data-role="cmdsearch"] wrapper (just needs position:relative, which this selector
   provides) around a [data-role="cmdsearch-input"] + a [data-role="cmdsearch-panel"]
   gets the same "instant floating card on focus, showing recent searches + a quick hint
   of what's searchable" behavior — see admin.js's own comment on the handlers for the
   JS half. Same floating-card shadow as .orders-action-form (this app's other
   absolutely-positioned reveal panel), for visual consistency. */
[data-role="cmdsearch"] { position: relative; }
.cmdsearch__panel {
    position: absolute; top: calc(100% + .4rem); left: 0; right: 0; z-index: 20;
    background: #fff; border: 1px solid var(--line); border-radius: 12px;
    box-shadow: 0 18px 40px -18px rgba(0,0,0,.35); padding: .6rem; max-height: 320px; overflow-y: auto;
}
.cmdsearch__section + .cmdsearch__section { margin-top: .6rem; padding-top: .6rem; border-top: 1px solid var(--line); }
.cmdsearch__sectiontitle { display: block; font-size: .7rem; font-weight: 700; color: var(--ink-soft); text-transform: uppercase; letter-spacing: .02em; padding: .2rem .5rem .4rem; }
.cmdsearch__row {
    display: flex; align-items: center; gap: .55rem; width: 100%; text-align: left;
    background: none; border: none; border-radius: 8px; padding: .5rem .55rem; font: inherit; color: var(--ink); cursor: pointer;
}
.cmdsearch__row:hover { background: var(--wash); }
.cmdsearch__rowicon { flex: none; color: var(--ink-soft); }
.cmdsearch__chips { display: flex; flex-wrap: wrap; gap: .4rem; padding: 0 .5rem; }
.cmdsearch__chip { display: inline-block; padding: .25rem .6rem; border-radius: 999px; background: var(--wash); color: var(--ink-soft); font-size: .76rem; }
.cmdsearch__empty { margin: 0; padding: .6rem .5rem; color: var(--ink-soft); font-size: .82rem; }

/* Multi-select filter (MultiSelectFilter.razor) — a trigger that fills its toolbar pill
   and opens a floating searchable checkbox panel; same card look as .cmdsearch__panel. */
.filter-pill--multi { padding: 0; }
.msel { position: relative; align-self: stretch; width: 100%; }
.msel__trigger {
    display: flex; align-items: center; justify-content: space-between; gap: .5rem; width: 100%; height: 100%;
    background: none; border: none; padding: 0 .6rem 0 .9rem; font: inherit; color: var(--ink); cursor: pointer; text-align: left;
}
.msel__label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.msel__chevron { flex: none; color: var(--ink-soft); }
.msel__panel {
    position: absolute; top: calc(100% + .4rem); left: 0; min-width: 270px; max-width: calc(100vw - 1rem); z-index: 20;
    background: #fff; border: 1px solid var(--line); border-radius: 12px;
    box-shadow: 0 18px 40px -18px rgba(0,0,0,.35); padding: .6rem;
}
/* admin.js adds --end when opening the panel would push it past the right edge of the screen (a status filter at the end of a bar): the panel
   then lines up with the filter's right edge and opens leftwards. */
.msel__panel--end { left: auto; right: 0; }
.msel__search { width: 100%; box-sizing: border-box; border: 1.5px solid #D6CDBC; border-radius: 8px; padding: .5rem .65rem; font: inherit; margin-bottom: .5rem; }
.msel__search:focus { outline: none; border-color: var(--teal); }
.msel__list { max-height: 300px; overflow-y: auto; }
.msel__group + .msel__group { margin-top: .4rem; padding-top: .4rem; border-top: 1px solid var(--line); }
.msel__grouptitle { display: block; font-size: .7rem; font-weight: 700; color: var(--ink-soft); text-transform: uppercase; letter-spacing: .02em; padding: .2rem .5rem; }
.msel__option { display: flex; align-items: center; gap: .55rem; padding: .4rem .5rem; border-radius: 8px; cursor: pointer; font-size: .88rem; }
.msel__option[hidden], .msel__group[hidden] { display: none; }
.msel__option:hover { background: var(--wash); }
.msel__option input { accent-color: var(--teal); }

/* Active filter chips — each is a link to the same page minus that one value. */
.filter-chips { display: flex; flex-wrap: wrap; gap: .4rem; margin-top: -.75rem; }
.filter-chip {
    display: inline-flex; align-items: center; gap: .45rem; padding: .3rem .7rem; border-radius: 999px;
    background: #fff; border: 1px solid var(--teal); color: var(--teal-deep); font-size: .8rem; font-weight: 600; text-decoration: none;
}
.filter-chip:hover { background: var(--wash); }
.filter-chip__x { font-size: .65rem; color: var(--ink-soft); }

.filter-bar__clear { align-self: center; color: var(--teal-deep); font-size: .85rem; font-weight: 600; text-decoration: none; margin-left: .25rem; }
.filter-bar__clear:hover { text-decoration: underline; }

/* "Today" — resets just the date range (keeps search/status/fulfilment), the one-click
   fix for "the range picker needs both fields edited correctly to get back to today".
   Only rendered when the range isn't already today, so it's never a no-op click. */
.filter-bar__today {
    align-self: center; display: inline-flex; align-items: center; height: 2.85rem; padding: 0 1rem;
    border: 1.5px solid var(--teal); border-radius: 10px; color: var(--teal-deep); font-size: .85rem;
    font-weight: 600; text-decoration: none; white-space: nowrap;
}
.filter-bar__today:hover { background: var(--wash); }

/* Sortable column headers — a plain link so clicking reloads with ?sort=&dir=, same
   query-string-driven pattern every other list/filter control in this admin uses. */
.orders-table__sort { color: inherit; text-decoration: none; cursor: pointer; }
.orders-table__sort:hover { color: var(--teal-deep); }
.orders-table__chevroncol { width: 2.25rem; padding-left: .5rem !important; padding-right: 0 !important; }

/* The chevron lives in its own slim column now (not glued to Order#) as a small round
   chip — plain gray at rest, filled teal-soft while this row's detail panel is open. */
.orders-table__chevronbtn {
    display: grid; place-items: center; width: 26px; height: 26px; border-radius: 8px;
    background: var(--wash); cursor: pointer; transition: background .15s ease;
}
.orders-table__row:has(.row-toggle:checked) .orders-table__chevronbtn { background: var(--teal-deep); }
.orders-table__chevronbtn .row-chevron { color: var(--ink-soft); }
.orders-table__row:has(.row-toggle:checked) .orders-table__chevronbtn .row-chevron { color: #fff; }

/* Icon + two-line stack (Customer, Fulfilment, Date) — ONE icon to the left, vertically
   spanning both lines, so line 1 and line 2 start at the same x position instead of line
   1 alone being pushed right by an inline icon. Compound selector (not just
   .orders-table__iconrow) so this reliably beats .row-cell's own "display: block" below
   regardless of which rule the cascade sees last. */
.row-cell.orders-table__iconrow { display: flex; align-items: flex-start; gap: .5rem; }
.orders-table__rowicon { flex: none; color: var(--ink-soft); margin-top: .15rem; }
.orders-table__stack { display: flex; flex-direction: column; align-items: flex-start; gap: .15rem; }
.orders-table__primary { font-weight: 600; color: var(--ink-heading); }
.orders-table__primary--link { color: var(--teal-deep); }
.orders-table__sub { font-size: .78rem; color: var(--ink-soft); font-weight: 400; }
/* Customer cell: person icon + name on row 1, phone icon + number on row 2 — a two-column
   grid (not a stack with an inline icon) so BOTH icons share the left column and both
   text lines start at the same x. */
.row-cell.orders-table__iconrow--grid {
    display: grid; grid-template-columns: auto 1fr; column-gap: .5rem; row-gap: .15rem; align-items: center;
}
.orders-table__iconrow--grid .orders-table__rowicon { margin-top: 0; display: grid; place-items: center; width: 14px; }

.order-action-form { margin: 0; }
.orders-table__actions { min-width: 230px; }
/* Catering rows have 11 columns and only ever Accept/Decline or one next-step button, so
   the Actions column doesn't need the Orders screen's full reserve (its buttons wrap). */
.orders-table--catering .orders-table__actions { min-width: 0; }
.orders-actions-cell { display: flex; align-items: center; flex-wrap: wrap; gap: .5rem; }
.btn--sm { display: inline-flex; align-items: center; justify-content: center; gap: .4rem; padding: .4rem .85rem; font-size: .8rem; white-space: nowrap; }
.orders-table__noaction { color: var(--ink-soft); }
.btn--danger { border-color: #E08A80; color: #b3261e; }
.btn--danger:hover { background: #fbe9e7; }
/* A destructive action that should not shout: no border until it is pointed at, the red only in the text and icon. */
.btn--ghost-danger { border-color: transparent; background: transparent; color: #b3261e; }
.btn--ghost-danger:hover:not(:disabled) { background: #fbe9e7; border-color: #E08A80; }
.btn--ghost-danger:disabled { opacity: .5; cursor: not-allowed; }

/* ---- Actions column: alignment + button hierarchy (every table) ----
   The Actions header and its cells are explicitly right-aligned (col-actions on both),
   so the right edge of every table has one clean boundary however the text columns
   stretch. Inside it, three levels of weight:
     primary    — the row's main action on catalog lists (Edit, Edit Rules): a clean
                  icon + text button on a soft green tint, no border (.btn--primary-action);
     workflow   — the order screens' forward step, text only (Acknowledge, Process, Accept,
                  Pickup, Complete...): the one solid .btn--cta, so every order row looks alike;
     secondary / destructive (Archive, More, Delete, Decline, Cancel, Delivery Failed):
                  a soft outline — hairline border, transparent fill, muted text — the
                  destructive ones tinted red. Hover fills them lightly.
   Scoped to .col-actions so the bulk-action toolbars and form buttons elsewhere keep
   their own look. */
/* "table" in the selector on purpose: it must out-rank the tables' own `th { text-align: left }` rules further down this file. */
table th.col-actions, table td.col-actions { text-align: right; }
.col-actions .meal-row__actions,
.col-actions .orders-actions-cell,
.col-actions .catalog-table__actioncell { justify-content: flex-end; }

.btn--sm.btn--primary-action {
    border-color: transparent; background: rgba(var(--coriander-rgb), .16); color: var(--teal-deep);
}
.btn--sm.btn--primary-action:hover { background: rgba(var(--coriander-rgb), .28); }

.col-actions .btn--sm:not(.btn--cta):not(.btn--primary-action):not(.btn--danger) {
    border-color: var(--line-input); background: transparent; color: var(--ink-soft);
}
.col-actions .btn--sm:not(.btn--cta):not(.btn--primary-action):not(.btn--danger):hover {
    background: var(--wash); color: var(--ink);
}
.col-actions .btn--sm.btn--danger { border-color: #EBCFCA; background: transparent; color: #b3261e; }
.col-actions .btn--sm.btn--danger:hover { border-color: #E08A80; background: #fbe9e7; }

/* Opt-out for a table too narrow to benefit from the right edge (Menu Categories has just a
   name column and Actions — right-aligning would strand the buttons far from the name).
   Keeps the button hierarchy above, drops only the alignment. */
table th.col-actions--left, table td.col-actions--left { text-align: left; }
.col-actions--left .meal-row__actions { justify-content: flex-start; }

/* Compact icon-only action button (Generate Sticker, now merged into the Actions
   column instead of its own separate QR Code column). */
.orders-icon-btn {
    display: grid; place-items: center; width: 30px; height: 30px; border-radius: 8px;
    border: 1px solid var(--line); background: #fff; color: var(--ink-soft); flex: none;
}
.orders-icon-btn:hover { color: var(--teal-deep); border-color: var(--teal-soft); background: var(--wash); }

/* Catalog row-action icons (Archive / Restore / Delete / Undo — see AdminActionIcons.cs).
   Sized here rather than via width/height on the SVG so the pointer:coarse block at the
   bottom of this file can scale every one of them for touch in one place. */
.act-ic { width: 15px; height: 15px; flex: none; }
/* Archive toggle: swap the box icon for the counter-clockwise arrow while the row is
   staged for archive. Driven purely by the <tr> class admin.js toggles — no JS icon swap. */
.rdt__ic { display: inline-flex; }
.rdt__ic--pending { display: none; }
.catalog-table__row--pending-delete .rdt__ic--idle { display: none; }
.catalog-table__row--pending-delete .rdt__ic--pending { display: inline-flex; }
/* Same idea for the Restore toggle on an archived row (see RestoreToggleButton). */
.rrt__ic { display: inline-flex; }
.rrt__ic--pending { display: none; }
.catalog-table__row--pending-restore .rrt__ic--idle { display: none; }
.catalog-table__row--pending-restore .rrt__ic--pending { display: inline-flex; }

/* ARRIVED_ONSITE's two-way action: "Deliver" plus a reveal panel for "Delivery
   Failed" so the reason is required before that click can submit. Same
   details/summary reveal idiom as Meals.razor's edit-availability panel. */
.orders-action-row { display: flex; align-items: center; gap: .3rem; position: relative; }
.orders-action-details { position: relative; }
.orders-action-summary { list-style: none; cursor: pointer; }
.orders-action-summary::-webkit-details-marker { display: none; }
.orders-action-form {
    position: absolute; right: 0; top: calc(100% + .4rem); z-index: 5; width: 220px;
    background: #fff; border: 1px solid var(--line); border-radius: 12px; box-shadow: 0 18px 40px -18px rgba(0,0,0,.35);
    padding: .8rem; margin: 0;
}
.orders-action-form .lbl { margin-top: 0; }

/* Expandable order row — a hidden checkbox + <label for> pair, no JS needed.
   Clicking anywhere in the row (except the actions cell) toggles the detail row below it. */
.orders-table__row:hover { background: var(--wash); }
.orders-table__chevron { width: 1.5rem; }
.row-cell { display: block; cursor: pointer; }
.row-chevron { font-size: .75rem; color: var(--ink-soft); transition: transform .15s ease; }
.orders-table__row:has(.row-toggle:checked) .row-chevron { transform: rotate(90deg); }
.orders-table__detail { display: none; }
.orders-table__row:has(.row-toggle:checked) + .orders-table__detail { display: table-row; }
.orders-table__detail > td { padding: 0 .6rem .75rem; border-bottom: 1px solid var(--line); }

/* Expanded order detail panel — "Items in this order" on the left, each item's own
   selections nested directly underneath it (not pooled into a separate section);
   Delivery address / Special requests stacked in a column on the right.
   CSS Grid (not flex) so the two columns' CONTENT boxes start on the same line
   regardless of font metrics: row 1 holds only the "Items in this order" heading (the
   meta column has no heading of its own, so its row-1 cell is simply empty) — row 1's
   height is still set by that heading, which pushes row 2 down by the same amount in
   BOTH columns, so the first item card and the first meta box always start level. */
.order-detail { background: var(--wash); border-radius: 12px; padding: 1rem 1.1rem; display: flex; flex-direction: column; gap: 1rem; }
.order-detail__row {
    display: grid; grid-template-columns: minmax(280px, 2fr) minmax(200px, 1fr);
    grid-template-rows: auto 1fr; column-gap: 1.5rem; row-gap: .6rem;
}
.order-detail__heading { font-size: .8rem; font-weight: 700; color: var(--ink-heading); margin: 0; }
.order-detail__items-heading { grid-column: 1; grid-row: 1; }

.order-detail__items { grid-column: 1; grid-row: 2; }
.order-detail__meta-col { grid-column: 2; grid-row: 2; display: flex; flex-direction: column; gap: .75rem; }

/* One item = one card. Top row: quantity badge | name | price, all top-aligned (the
   kitchen reads the badge first — how many to make/pack — so it's a dark, heavy pill on
   the far left edge rather than "2 × $x" buried in grey sub-text). The item's own "what's
   included" chips sit on a second row underneath, lined up with the name (not the badge)
   and divided off, so they read as part of this item. */
.order-detail__itemcard {
    background: #fff; border: 1px solid var(--line); border-radius: 10px; padding: .7rem .8rem;
    display: grid; align-items: start; column-gap: .85rem;
    grid-template-columns: auto 1fr auto;
    grid-template-areas: "qty info price" ". chips chips";
}
.order-detail__itemcard + .order-detail__itemcard { margin-top: .5rem; }
.order-detail__qty {
    grid-area: qty; display: inline-flex; align-items: center; justify-content: center;
    min-width: 3rem; height: 2.4rem; padding: 0 .85rem; border-radius: 999px;
    background: var(--teal-deep); color: #fff; font-size: 1.3rem; font-weight: 800; line-height: 1;
    font-variant-numeric: tabular-nums;
}
.order-detail__iteminfo { grid-area: info; display: flex; flex-direction: column; gap: .15rem; min-width: 0; }
.order-detail__itemname { font-weight: 600; color: var(--ink-heading); font-size: .95rem; line-height: 1.3; }
.order-detail__itemsub { font-size: .78rem; color: var(--ink-soft); }
.order-detail__itemprice { grid-area: price; font-weight: 700; color: var(--ink-heading); font-size: .95rem; line-height: 1.3; }

.order-detail__selectiongrid {
    grid-area: chips; display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: .5rem;
    margin-top: .6rem; padding-top: .6rem; border-top: 1px dashed var(--line);
}
.order-detail__selection { display: flex; align-items: center; gap: .5rem; background: var(--wash); border: 1px solid var(--line); border-radius: 10px; padding: .5rem .6rem; }
.order-detail__selectionicon { display: grid; place-items: center; width: 28px; height: 28px; border-radius: 8px; background: #fff; color: var(--teal-deep); flex: none; }
.order-detail__selectionimg { width: 28px; height: 28px; border-radius: 8px; object-fit: cover; flex: none; }
.order-detail__selectiontext { display: flex; flex-direction: column; gap: .05rem; min-width: 0; }
.order-detail__selectionlabel { display: block; font-size: .7rem; color: var(--ink-soft); text-transform: uppercase; letter-spacing: .02em; }
.order-detail__selectionvalue { display: block; font-size: .85rem; font-weight: 600; color: var(--ink-heading); }

.order-detail__meta { display: flex; align-items: flex-start; gap: .6rem; background: #fff; border: 1px solid var(--line); border-radius: 10px; padding: .7rem .8rem; }
.order-detail__meta svg { flex: none; margin-top: .15rem; color: var(--teal-deep); }
.order-detail__metalabel { display: block; font-size: .7rem; text-transform: uppercase; letter-spacing: .02em; color: var(--ink-soft); margin-bottom: .15rem; }
.order-detail__metavalue { display: block; font-size: .85rem; color: var(--ink-heading); font-weight: 500; }

.order-items-table { width: 100%; border-collapse: collapse; font-size: .85rem; background: var(--wash); border-radius: 10px; overflow: hidden; }
.order-items-table th { text-align: left; font-size: .74rem; color: var(--ink-soft); text-transform: uppercase; letter-spacing: .02em; padding: .45rem .75rem; }
.order-items-table td { padding: .4rem .75rem; }

.status-pill { display: inline-block; padding: .25rem .65rem; border-radius: 999px; font-size: .76rem; font-weight: 600; background: var(--wash); color: var(--ink); }
.status-pill--delivered, .status-pill--completed { background: #E3ECE7; color: #8A9A5B; }
.status-pill--cancelled, .status-pill--declined { background: #FBEAE5; color: #b3261e; }
.status-pill--preparing, .status-pill--confirmed, .status-pill--accepted { background: #EEF0E2; color: #5F6B3D; }
.status-pill--active { background: #E3ECE7; color: #8A9A5B; }
.status-pill--inactive { background: var(--wash); color: var(--ink-soft); }
.status-pill--deleted { background: #FBEAE5; color: #b3261e; }
.tag-chip { background: var(--wash); color: var(--teal-deep); font-size: .78rem; font-weight: 600; padding: .25rem .6rem; border-radius: 999px; }
@keyframes progress-pulse {
    0%, 100% { box-shadow: 0 0 0 4px rgba(217, 164, 65, .18); }
    50%      { box-shadow: 0 0 0 8px rgba(217, 164, 65, .08); }
}

.journey-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: .8rem; }
.journey-photo { margin: 0; }
.journey-photo img { width: 100%; aspect-ratio: 1; object-fit: cover; border-radius: 10px; }
.journey-photo figcaption { font-size: .78rem; color: var(--ink-soft); margin-top: .3rem; text-align: center; }
.journey-done { color: var(--ink-soft); margin: 0; }
.update-panel__check { display: flex; align-items: center; gap: .5rem; font-size: .88rem; color: var(--ink); margin: .9rem 0 1.1rem; cursor: pointer; }
.update-panel__check input[type="checkbox"] { width: 16px; height: 16px; accent-color: var(--teal); }

/* ---------- Weekly Meal Planning ---------- */
.page-toast { background: #E3ECE7; color: #8A9A5B; border-radius: 8px; padding: .6rem .9rem; font-size: .85rem; margin: 0 0 1rem; }
.page-toast--error { background: var(--error-soft); color: var(--error); }

.page-header { display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem; flex-wrap: wrap; margin-bottom: 1.25rem; }
.page-header__actions { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; margin-left: auto; }

/* ---- recipes and ingredients ---- */
/* The recipe's ingredient lines: one small card per line (the main fields, then the secondary ones), so it reads at any width. */
.recipe-lines { display: flex; flex-direction: column; gap: .6rem; margin-bottom: .75rem; }
.recipe-line { border: 1px solid var(--line); border-radius: 10px; padding: .65rem .75rem; display: flex; flex-direction: column; gap: .5rem; }
.recipe-line[hidden], .recipe-step[hidden], .recipe-field[hidden] { display: none; }
.recipe-line__row { display: flex; flex-wrap: wrap; align-items: flex-end; gap: .5rem .75rem; }
.recipe-line__row--secondary .tf { font-size: .8rem; }
.recipe-field { display: flex; flex-direction: column; gap: .2rem; min-width: 0; }
.recipe-field .lbl { margin: 0; font-size: .72rem; }
.recipe-field .tf { padding: .35rem .5rem; font-size: .85rem; min-width: 0; }
.recipe-field--grow { flex: 1 1 12rem; }
.recipe-field--medium { flex: 0 1 10rem; }
.recipe-field--number { width: 5.5rem; }
.recipe-field--unit { width: 5rem; }
.recipe-field--check { flex-direction: row; align-items: center; gap: .35rem; font-size: .82rem; padding-bottom: .4rem; }
/* A step: its number, then the text with its minutes / temperature / equipment underneath. */
.recipe-step { display: flex; gap: .6rem; margin-bottom: .75rem; }
.recipe-step__body { flex: 1; display: flex; flex-direction: column; gap: .4rem; min-width: 0; }
.recipe-step__number { flex: none; width: 1.6rem; height: 1.6rem; margin-top: .3rem; border-radius: 50%; background: var(--line); color: var(--ink-soft); font-size: .78rem; font-weight: 600; display: flex; align-items: center; justify-content: center; }
/* A batch's ingredient plan: a card-like row per dish, and the "used" box with its unit. */
.batch-dishes { display: flex; flex-direction: column; gap: .6rem; }
.batch-dish { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: .5rem 1rem; border: 1px solid var(--line); border-radius: 10px; padding: .65rem .75rem; }
.batch-dish__name { display: flex; flex-direction: column; gap: .15rem; min-width: 0; flex: 1 1 14rem; }
.batch-dish__note { font-size: .8rem; color: var(--ink-soft); }
.batch-dish__note--warn { color: var(--error); }
.batch-usage { display: flex; gap: .4rem; }
/* A colour picker among the form fields (Add licensee). */
.colour-input { width: 5rem; height: 2.4rem; padding: .15rem; cursor: pointer; }
.batch-usage .tf { padding: .3rem .45rem; font-size: .85rem; min-width: 0; }
.batch-usage input.tf { width: 8.5rem; }
.batch-usage select.tf { width: 5rem; }
.prep-batches { margin: 0 0 .5rem; padding-left: 1.2rem; }
/* Printing a prep list or a batch's ingredients: only the content, none of the buttons and notices. */
@media print {
    .no-print { display: none; }
}
/* The starter-ingredient picker: a search, then a group of tiles per category, then a sticky footer with the count and the actions. A tile is a label around
   a visually hidden checkbox (still focusable and posted); the selected look comes from :has(:checked), so the page works without the script. */
.starter-picker__bar { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: .75rem 1.5rem; margin-bottom: 1.25rem; }
.starter-picker__search { flex: 1 1 18rem; max-width: 26rem; }
.starter-picker__all { margin-left: auto; font-weight: 600; color: var(--ink-heading); cursor: pointer; }
.starter-group { margin-bottom: 1.5rem; }
.starter-group[hidden], .starter-card[hidden], .starter-picker__none[hidden] { display: none; }
.starter-group__head { display: flex; align-items: center; justify-content: space-between; gap: .75rem; margin-bottom: .6rem; }
.starter-group__title { margin: 0; font-size: var(--fs-lg); }
.starter-group__select { font-weight: 600; color: var(--ink-heading); cursor: pointer; }
.starter-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr)); gap: .6rem; }
.starter-card { position: relative; display: flex; align-items: center; gap: .65rem; padding: .7rem .85rem; background: #fff; border: 1.5px solid var(--line); border-radius: 12px; cursor: pointer; transition: border-color .15s ease, background-color .15s ease; }
.starter-card:hover { border-color: var(--teal-soft); }
.starter-card__check { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; }
.starter-card:has(.starter-card__check:focus-visible) { outline: 2px solid var(--teal-deep); outline-offset: 2px; }
.starter-card__tick { flex: none; display: grid; place-items: center; width: 1.4rem; height: 1.4rem; border-radius: 50%; border: 1.5px solid var(--line); background: #fff; color: transparent; }
.starter-card__name { flex: 1 1 auto; min-width: 0; font-weight: 600; color: var(--ink-heading); }
.starter-card__density { flex: none; padding: .15rem .55rem; border-radius: 999px; background: var(--wash); color: var(--ink-soft); font-size: var(--fs-xs); white-space: nowrap; }
.starter-card:has(.starter-card__check:checked) { border-color: var(--teal-deep); background: #EEF3E6; }
.starter-card:has(.starter-card__check:checked) .starter-card__tick { background: var(--teal-deep); border-color: var(--teal-deep); color: #fff; }
.starter-picker__none { margin: 1rem 0; color: var(--ink-soft); }
.starter-footer { position: sticky; bottom: 0; z-index: 30; display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: .75rem 1rem; margin-top: .5rem; padding: 1rem 1.4rem; background: #fff; border: 1px solid var(--line); border-radius: 14px; box-shadow: 0 -6px 20px rgba(20,24,20,.1); }
.starter-footer__count { font-weight: 600; color: var(--ink-heading); }
.starter-footer__actions { display: flex; align-items: center; gap: .75rem; margin-left: auto; }
.meals-weeknav-wrap { display: flex; flex-direction: column; align-items: flex-end; gap: .4rem; }
.meals-weeknav { display: flex; align-items: center; background: #fff; border: 1px solid var(--line); border-radius: 12px; padding: .35rem; flex: none; }
.meals-backtoday { color: var(--teal-deep); font-weight: 600; text-decoration: underline; font-size: .8rem; }

/* The date range display doubles as the "jump to a date" trigger — one unified control
   instead of a separate always-visible date-input row next to it. Same anchored-popover
   pattern as .meal-row__editform (Edit serves/availability). */
.meals-weeknav__datepick { position: relative; }
.meals-weeknav__datepick summary { list-style: none; cursor: pointer; }
.meals-weeknav__datepick summary::-webkit-details-marker { display: none; }
.meals-weeknav__datepickform {
    position: absolute; left: 50%; transform: translateX(-50%); top: calc(100% + .4rem); z-index: 5; width: 200px;
    background: #fff; border: 1px solid var(--line); border-radius: 12px; box-shadow: 0 18px 40px -18px rgba(0,0,0,.35);
    padding: .8rem;
}
.meals-weeknav__datepickform .lbl { margin-top: 0; }
.meals-weeknav__btn { display: grid; place-items: center; width: 30px; height: 30px; border-radius: 8px; color: var(--ink-soft); text-decoration: none; flex: none; }
.meals-weeknav__btn:hover { background: var(--wash); color: var(--ink); }
.meals-weeknav__divider { width: 1px; height: 18px; background: var(--line); margin: 0 .4rem; flex: none; }
.meals-weeknav__range { display: inline-flex; align-items: center; gap: .5rem; font-size: .88rem; font-weight: 600; color: var(--ink-heading); white-space: nowrap; padding: 0 .4rem; }
.meals-weeknav__range svg { color: var(--ink-soft); flex: none; }


/* Tab bar (one pill per day of the currently-viewed 7-day window) + a single focused-day
   panel below it, replacing a 7-cards-in-a-row grid — a short date label per tab fits far
   more comfortably in a row than a full card ever did, and only one day's detail needs to
   render at a time. Wraps on narrow viewports rather than needing horizontal scroll. */
/* The row under the summary: the seven day tabs on the left, the week navigator on the right (stacking under the tabs on a narrow screen). */
.meals-dayrow { display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; gap: .75rem 1rem; margin-bottom: 1rem; }
.meals-daytabs { display: flex; flex-wrap: wrap; gap: .5rem; flex: 1 1 auto; }
.meals-daytabs__tab {
    display: flex; align-items: center; gap: .4rem; padding: .55rem .9rem; border-radius: 999px;
    background: #fff; border: 1.5px solid var(--line); color: var(--ink); font-size: .84rem; font-weight: 600;
    text-decoration: none; white-space: nowrap;
}
.meals-daytabs__tab:hover { border-color: var(--teal-soft); }
.meals-daytabs__tab.is-selected { background: var(--teal); border-color: var(--teal); color: #fff; }
.meals-daytabs__tab.is-today:not(.is-selected) { border-color: var(--teal); color: var(--teal-deep); }
.meals-daytabs__tab.is-past:not(.is-selected) { color: var(--ink-soft); }
.meals-daytabs__badge {
    font-size: .66rem; font-weight: 700; text-transform: uppercase; letter-spacing: .02em;
    padding: .1rem .4rem; border-radius: 999px; background: var(--teal); color: #fff;
}
.meals-daytabs__tab.is-selected .meals-daytabs__badge { background: rgba(255,255,255,.25); }

.meal-day-focus {
    background: #fff; border: 1px solid var(--line); border-radius: 14px; padding: 1.25rem;
    display: flex; flex-direction: column; gap: .7rem;
}
.meal-day-focus--today { border-color: var(--teal); box-shadow: 0 0 0 1px var(--teal); }
.meal-day-focus--past { background: var(--wash); }
.meal-day-focus--past .meal-day__date { color: var(--ink-soft); }

.meal-day__head { display: flex; align-items: center; gap: .5rem; }
.meal-day__date { font-size: 1rem; font-weight: 700; color: var(--ink-heading); }
.meal-day__todaybadge {
    font-size: .68rem; font-weight: 700; text-transform: uppercase; letter-spacing: .02em;
    padding: .15rem .5rem; border-radius: 999px; background: var(--teal); color: #fff;
}

.meal-row--readonly { background: transparent; border: 1px solid var(--line); }

.meal-row { display: flex; align-items: flex-start; justify-content: space-between; gap: .6rem; background: var(--wash); border-radius: 10px; padding: .6rem .7rem; }
.meal-row__main { display: flex; align-items: flex-start; gap: .55rem; min-width: 0; }
.meal-row__dot { width: 8px; height: 8px; border-radius: 50%; background: var(--teal); margin-top: .45rem; flex: none; }
.meal-row__name { font-size: .88rem; font-weight: 600; color: var(--ink-heading); margin: 0; }
.meal-row__serves { font-size: .78rem; color: var(--ink-soft); margin: .1rem 0 0; }
.meal-row__actions { display: flex; align-items: center; gap: .3rem; flex: none; position: relative; }
.meal-row__iconbtn { display: grid; place-items: center; width: 26px; height: 26px; border-radius: 7px; background: #fff; border: 1px solid var(--line); color: var(--ink-soft); cursor: pointer; list-style: none; }
.meal-row__iconbtn::-webkit-details-marker { display: none; }
.meal-row__iconbtn:hover { border-color: var(--teal); color: var(--teal-deep); }
.meal-row__iconbtn--danger:hover { border-color: #E08A80; color: #b3261e; }

.meal-row__editdetails { position: relative; }
.meal-row__editform {
    position: absolute; right: 0; top: calc(100% + .4rem); z-index: 5; width: 200px;
    background: #fff; border: 1px solid var(--line); border-radius: 12px; box-shadow: 0 18px 40px -18px rgba(0,0,0,.35);
    padding: .8rem;
}
.meal-row__editform .lbl { margin-top: 0; }
.meal-row__soldnote { font-size: .74rem; color: var(--ink-soft); margin: .3rem 0 0; }

.meal-day__adddetails summary { list-style: none; cursor: pointer; }
.meal-day__adddetails summary::-webkit-details-marker { display: none; }
.meal-day__addbtn {
    display: block; text-align: center; padding: .55rem; border-radius: 9px;
    border: 1.5px dashed var(--teal-soft); color: var(--teal-deep); font-size: .84rem; font-weight: 600;
}
.meal-day__addbtn:hover { background: var(--wash); }
.meal-day__addform select.tf, .meal-day__addform input.tf { margin-bottom: 0; }

/* Generic "<details> as a centered modal" mechanism — any <details class="modal-details">
   containing a .modal-backdrop + a .modal-panel opens as a dimmed, centered overlay
   instead of expanding inline in whatever card/row it's declared in. Originally built
   just for "+ Add Meal" (an inline form used to stretch that day's card far taller than
   its row neighbours); now shared by the sitewide confirm dialog (admin.js) too, so both
   get the same look with one set of rules. The dimmed .modal-backdrop is a real,
   always-present sibling element, only shown (and only fixed-positioned, covering the
   viewport) while the parent <details> is [open] — no JS needed to open it, matching
   every other disclosure on this page; admin.js just wires up closing it again (backdrop
   click / .modal-close / Escape), since <details> has no native equivalent to <dialog>'s
   close-on-Escape/backdrop-click. */
/* The trigger is a <summary>, which browsers give a native disclosure marker (a ▶
   triangle) by default — every other <summary>-as-button in this app suppresses it via
   its own specific class's ::-webkit-details-marker rule; this covers the trigger at the
   .modal-details level instead so any future modal (styled as .btn or anything else)
   doesn't need to remember to do that itself. */
.modal-details > summary { list-style: none; }
.modal-details > summary::-webkit-details-marker { display: none; }
.modal-details .modal-backdrop { display: none; }
.modal-details[open] .modal-backdrop {
    display: block; position: fixed; inset: 0; background: rgba(20,24,20,.5); z-index: 60;
}
.modal-details[open] .modal-panel {
    position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 61;
    width: min(420px, 92vw); max-height: 85vh; overflow-y: auto;
    background: #fff; border-radius: 16px; box-shadow: 0 30px 70px -20px rgba(0,0,0,.45);
    padding: 1.25rem; margin: 0;
}
.modal-panel__head { display: flex; align-items: center; justify-content: space-between; gap: .5rem; margin-bottom: .3rem; }
.modal-panel__title { font-size: 1rem; font-weight: 700; color: var(--ink-heading); margin: 0; }
.modal-panel__x { background: none; border: none; font-size: 1.4rem; line-height: 1; color: var(--ink-soft); cursor: pointer; padding: 0 .2rem; }
.modal-panel__x:hover { color: var(--ink); }
/* The Cancel / submit row at the foot of a modal form needs air above it: the last field sits
   directly on top of it otherwise, and the buttons overlap that field's border. */
.modal-panel .meal-form__actions { margin-top: 1.4rem; }

/* Divides a modal into named sections — e.g. the Meals "Edit" modal's Total Serves
   (top) vs. Availability & Prebooking Times (bottom), merged from two separate
   icon-triggered popovers into one modal so both live in one obvious place instead of
   requiring the admin to guess which icon holds which field. */
.modal-panel__section {
    font-size: .78rem; font-weight: 700; text-transform: uppercase; letter-spacing: .03em;
    color: var(--ink-soft); margin: 1.1rem 0 .5rem; padding-top: 1rem; border-top: 1px solid var(--line);
}

/* Same divider idea as .modal-panel__section, but for a section header living directly on
   a full-size page (ModifierGroupDetail.razor's Selection Rules accordion) rather than a
   compact modal — .78rem/--ink-soft reads fine packed into a small modal-panel but washed
   out at normal page scale, so this is bigger and uses the darker heading color instead. */
.form-card__section {
    font-size: .92rem; font-weight: 700; text-transform: uppercase; letter-spacing: .03em;
    color: var(--ink-heading); margin: 1.3rem 0 .6rem; padding-top: 1.1rem; border-top: 1px solid var(--line);
}

/* "Apply Modifier Group Changes" modal's Affected Scope readout (ModifierGroupDetail.razor) —
   plain, un-bulleted rows matching this app's minimal list styling elsewhere (nothing here
   already resets bare <ul>, so it needs its own). */
.assoc-impact-list { list-style: none; margin: 0 0 1rem; padding: 0; font-size: .85rem; color: var(--ink); }
.assoc-impact-list li { padding: .3rem 0; border-bottom: 1px solid var(--line); }
.assoc-impact-list li:last-child { border-bottom: none; }

/* Once the "+ Add Meal" modal is open, the empty-state graphic underneath adds nothing
   (it's fully covered by the backdrop anyway) — hidden outright as a defensive
   belt-and-braces, not just relying on the backdrop's z-index. */
.meal-day__adddetails[open] ~ .meal-day__empty { display: none; }

/* Sitewide confirm dialog (replaces the browser's native confirm(), which can't be
   themed) — built once by admin.js and appended to <body>, reusing .modal-details/
   .modal-panel above for the overlay itself. */
.confirm-dialog__message { font-size: .92rem; color: var(--ink); margin: 0 0 1rem; }

.meal-day__empty { text-align: center; color: var(--ink-soft); padding: 1rem .5rem; }
.meal-day__empty svg { color: #C3CBC5; margin-bottom: .4rem; }
.meal-day__empty p { font-size: .85rem; font-weight: 600; color: var(--ink); margin: 0 0 .2rem; }
.meal-day__empty span { font-size: .76rem; }

.meal-day__footer { display: flex; align-items: center; justify-content: space-between; margin-top: auto; padding-top: .4rem; }

.meal-row__editform--wide { width: 260px; }

.meal-row__check { display: flex; align-items: center; gap: .5rem; font-size: .84rem; color: var(--ink); margin: .5rem 0; cursor: pointer; }
.meal-row__check input[type="checkbox"], .meal-row__check input[type="radio"] { width: 15px; height: 15px; accent-color: var(--teal); flex: none; }
.meal-row__hint { font-size: .74rem; color: var(--ink-soft); margin: .3rem 0 .6rem; }
.meal-day__addform .meal-row__check { margin: .6rem 0; }
.meal-day__addform .meal-row__hint { margin: .3rem 0 .8rem; }
.meal-day__totalserves { font-size: .8rem; font-weight: 600; color: var(--ink-heading); }
.meal-row__inlineform { display: inline; margin: 0; }

/* ---------- Consolidated Create/Edit Meal + Create Menu Item pages ---------- */
.meal-form { display: flex; flex-direction: column; gap: 1.25rem; max-width: 1440px; }
.meal-form__actions { display: flex; align-items: center; justify-content: flex-end; gap: 1.1rem; }

/* Sticky Save Changes / Cancel Changes bar — replaces every accordion's own local Save
   button. position: sticky (not fixed) keeps it inside .admin-main's own content column,
   so it never overlaps .admin-sidebar and needs no hardcoded left offset. */
.save-bar {
    position: sticky; bottom: 0; z-index: 30;
    display: flex; align-items: center; justify-content: space-between; gap: 1rem;
    margin-top: .5rem; padding: 1rem 1.4rem;
    background: #fff; border: 1px solid var(--line); border-radius: 14px;
    box-shadow: 0 -6px 20px rgba(20,24,20,.1);
    max-width: 1440px;
}
.save-bar__status { font-size: .85rem; color: var(--ink-soft); }
.save-bar__actions { display: flex; align-items: center; gap: .75rem; margin-left: auto; }
.save-bar__actions form { margin: 0; }

/* min-width:0 — a <fieldset> defaults to `min-width: min-content`, which refuses to
   shrink below the intrinsic width of its widest child (a wide catalog table, a
   component-search input). On a narrow viewport that pushed the whole page into a
   horizontal scroll instead of letting the table scroll inside its own
   .catalog-table-wrap. Every .form-card in this app is a fieldset. */
.form-card { background: #fff; border: 1px solid var(--line); border-radius: 14px; padding: 1.25rem 1.4rem; min-width: 0; }
.form-card legend { font-size: 1.05rem; font-weight: 700; color: var(--ink-heading); padding: 0; }
.form-card__hint { font-size: .82rem; color: var(--ink-soft); margin: 0 0 1rem; }
.form-card__count { font-size: .8rem; color: var(--ink-soft); margin: .6rem 0 0; }
.form-required { color: #b3261e; }

.form-row { display: flex; gap: 1.25rem; }
.form-row .form-field { flex: 1; min-width: 0; }
.form-field--grow { flex: 2; }
/* Outside a .form-row, .tf's own width:100% otherwise stretches a lone dropdown across the
   whole fieldset — used by Price Rules' "Vary prices by" driver-group picker. */
.form-field--narrow { max-width: 280px; }

.catalog-form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1.25rem; align-items: start; }
.catalog-form-col { display: flex; flex-direction: column; gap: 1.25rem; }
@media (max-width: 900px) { .catalog-form-grid { grid-template-columns: 1fr; } }

/* ---------- View/Edit detail pages (Meal, Menu Item) ---------- */
/* Sections are <details> accordions rather than anchor-linked sub-nav tabs — Blazor's
   enhanced navigation intercepts in-page link clicks and doesn't reliably scroll to
   same-page #fragments, so anchor tabs silently did nothing. Accordions need no
   navigation at all: clicking the summary just toggles the native <details> element. */
.detail-accordion { background: #fff; border: 1px solid var(--line); border-radius: 14px; margin-bottom: 1.25rem; }
.detail-accordion:last-child { margin-bottom: 0; }
.detail-accordion__summary {
    list-style: none; cursor: pointer; padding: 1.1rem 1.4rem;
    font-size: 1.05rem; font-weight: 700; color: var(--ink-heading);
    display: flex; align-items: center; justify-content: space-between;
}
.detail-accordion__summary::-webkit-details-marker { display: none; }
.detail-accordion__summary:hover { color: var(--teal-deep); }
.detail-accordion__summary::after { content: '▸'; font-size: .8rem; color: var(--ink-soft); margin-left: .75rem; flex: none; }
.detail-accordion[open] > .detail-accordion__summary::after { content: '▾'; }
.detail-accordion > .meal-form { padding: 0 1.4rem 1.4rem; }
.detail-accordion .form-card { border: none; padding: 0; border-radius: 0; }
/* Some accordion bodies are a bare <fieldset class="form-card"> (Modifier Group page's
   Modifiers / Price Rules / Associations; Menu Item's Modifier Groups / Meal Upgrades)
   instead of the .meal-form-wrapped kind. Without this they'd have zero inset — content
   flush against the card edge and misaligned with the summary above and the .meal-form
   accordions elsewhere on the page. Matches .meal-form's inset, plus a little top room
   under the summary (there's no divider line between them). */
.detail-accordion > .form-card { padding: 1.1rem 1.4rem 1.4rem; margin: 0; }
.detail-accordion > .form-card > :first-child { margin-top: 0; }

/* Vertical rhythm inside an accordion body — a small fixed set of steps, replacing the
   scattered inline style="margin-top:…" the markup used to carry. */

/* A search-and-attach box needs clear space below it before the table it feeds. */
.form-card .component-search { margin-bottom: 1.1rem; }
/* A secondary hint that introduces a second block below a table (e.g. the Price Rules
   reference list under the matrix) needs clear separation above it. */
.form-card__hint--divider { margin-top: 1.75rem; padding-top: 1.25rem; border-top: 1px solid var(--line); }

.catalog-form-cancel { color: var(--ink-soft); font-size: .88rem; font-weight: 600; text-decoration: underline; margin-right: auto; }

/* toggle switch (Status: Active/Inactive) */
.toggle-switch { display: flex; align-items: center; gap: .6rem; cursor: pointer; padding-top: .35rem; }
.toggle-switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.toggle-switch__track { position: relative; width: 40px; height: 22px; border-radius: 999px; background: #D6CDBC; transition: background .15s ease; flex: none; }
.toggle-switch__track::after { content: ''; position: absolute; top: 2px; left: 2px; width: 18px; height: 18px; border-radius: 50%; background: #fff; transition: transform .15s ease; }
.toggle-switch input:checked + .toggle-switch__track { background: var(--teal); }
.toggle-switch input:checked + .toggle-switch__track::after { transform: translateX(18px); }
.toggle-switch__label { font-size: .88rem; font-weight: 600; color: var(--ink); }

/* Include Menu Items checklist with thumbnails */
.catalog-picker { display: flex; flex-direction: column; gap: .3rem; max-height: 360px; overflow-y: auto; margin-top: .6rem; }
.catalog-picker__row { display: flex; align-items: center; gap: .6rem; padding: .5rem .4rem; border-radius: 8px; cursor: pointer; }
.catalog-picker__row:hover { background: var(--wash); }
.catalog-picker__row input[type="checkbox"] { width: 16px; height: 16px; accent-color: var(--teal); flex: none; }
.catalog-picker__thumb { width: 36px; height: 36px; border-radius: 7px; object-fit: cover; flex: none; }
.catalog-picker__thumb--empty { background: var(--wash); }
.catalog-picker__name { flex: 1; min-width: 0; font-size: .86rem; font-weight: 600; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Meal Components accordion redesign — card-based, single unified list (Simple Swap and
   Mix and Match components interleaved, sharing one drag-reorder sequence; neither mode
   carries a badge — Simple Swap items come from the search-to-add bar, Mix and Match
   slots from the "Add a Mix & Match Slot" modal, distinct enough by their card layout
   alone), replacing the flat .catalog-picker row list above for the edit-mode view
   (create-mode's flat checklist still uses .catalog-picker unchanged). */
/* Management > Roles: the permission tick grid. One bordered group per area (Orders, Catering,
   ...), its options laid out in columns that wrap with the width. */
.perm-group { border: 1px solid var(--line); border-radius: 12px; padding: .8rem 1rem; margin-top: .85rem; background: #fff; }
.perm-group__title { margin: 0 0 .6rem; font-weight: 700; color: var(--ink-heading); font-size: .92rem; }
.perm-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: .6rem 1.2rem; }
.perm-option { display: flex; align-items: flex-start; gap: .55rem; font-size: .9rem; color: var(--ink); cursor: pointer; }
.perm-option input { margin-top: .2rem; flex: none; }
.perm-option small { display: block; color: var(--ink-soft); font-size: .75rem; }
.perm-option input:disabled + span { color: var(--ink-soft); }

/* "Click a button, a panel expands in place below it" — a plain <details> whose <summary> is
   styled as a button (e.g. Add Meal Upgrade). The non-modal sibling of .modal-details. */
.reveal-details > summary { display: inline-block; list-style: none; cursor: pointer; }
.reveal-details > summary::-webkit-details-marker { display: none; }
.reveal-details[open] > summary { background: var(--wash); }
.reveal-details__panel { margin-top: 1.1rem; }
.component-section__header { display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem; margin-bottom: .3rem; }

.component-grid { display: flex; flex-direction: column; gap: .6rem; margin-top: .8rem; }
.component-card { background: #fff; border: 1px solid var(--line); border-radius: 12px; padding: .7rem .8rem; }
.component-card[draggable="true"] { cursor: grab; }
.component-card--choice { background: var(--wash-soft, #FAF7EF); }

.component-card__row { display: flex; align-items: center; gap: .7rem; }
.component-card__handle { flex: none; color: var(--ink-soft); cursor: grab; display: grid; place-items: center; }
.component-card__check { width: 17px; height: 17px; accent-color: var(--teal); flex: none; }
.component-card__thumb { width: 40px; height: 40px; border-radius: 8px; object-fit: cover; flex: none; }
.component-card__thumb--empty { background: var(--wash); }
.component-card__body { flex: 1; min-width: 0; }
.component-card__name { font-size: .88rem; font-weight: 600; color: var(--ink-heading); margin: 0; display: flex; align-items: center; gap: .4rem; }
.component-card__sub { font-size: .76rem; color: var(--ink-soft); margin: .1rem 0 0; }
.component-card__price { font-size: .84rem; font-weight: 600; color: var(--ink); flex: none; }

/* Mix and Match slot's title — reads as plain text while readonly (collapsed), gets a
   visible border/background once the pencil toggle clears readonly (expanded), rather than
   swapping between a separate <p> and <input>. */
.component-card__nameinput {
    font: inherit; font-size: .88rem; font-weight: 600; color: var(--ink-heading);
    border: 1.5px solid transparent; background: none; border-radius: 6px;
    padding: .1rem .3rem; margin: 0 0 0 -.3rem; width: 100%; max-width: 320px;
}
.component-card__nameinput:not([readonly]) { border-color: #D6CDBC; background: #fff; cursor: text; }
.component-card__nameinput[readonly] { cursor: pointer; }

/* Pencil icon's in-place edit panel — a normal block-level sibling of .component-card__row
   (not an absolutely-positioned popover), so opening it pushes the sync pill/choice list
   down naturally. No Save button in here — see the form's data-role="section-save-form". */
.component-card__editpanel { margin: .6rem 0 0 2.7rem; }
.component-card__editrow { display: flex; flex-wrap: wrap; gap: .7rem; }
.component-card__editrow .form-field { min-width: 140px; }
.component-card__editwarning {
    font-size: .78rem; color: #7A5B12; background: #FCF3D9; border-radius: 8px;
    padding: .5rem .7rem; margin: .5rem 0 0;
}

/* "Search to add" bar — a floating dropdown replacing what used to be a permanent grid
   card per unused menu item. Every candidate (NotIncludedItems()) is already rendered as
   a hidden .component-search__result; admin.js just toggles which ones match as the admin
   types and shows/hides the whole box, so there's no extra round trip to filter. */
.component-search { position: relative; margin-top: .8rem; }
.component-search__inputwrap { position: relative; }
.component-search__icon { position: absolute; left: .8rem; top: 50%; transform: translateY(-50%); color: var(--ink-soft); pointer-events: none; }
.component-search__inputwrap input.tf { padding-left: 2.5rem; }
/* z-index above .save-bar (30) — on pages with the sticky Save Changes bar, an open
   results dropdown would otherwise render underneath it (technically open, invisibly so)
   whenever the search box sits low enough on the page for the two to overlap. */
.component-search__results { display: none; position: absolute; top: calc(100% + .3rem); left: 0; right: 0; max-height: 320px; overflow-y: auto; background: #fff; border: 1px solid var(--line); border-radius: 10px; box-shadow: 0 10px 28px rgba(20,24,20,.14); z-index: 40; }
.component-search__results--open { display: block; }
.component-search__result { display: flex; align-items: center; gap: .6rem; width: 100%; padding: .55rem .7rem; border: none; border-bottom: 1px solid var(--line); background: none; text-align: left; cursor: pointer; font: inherit; }
.component-search__result:last-child { border-bottom: none; }
.component-search__result:hover { background: var(--wash); }
.component-search__thumb { width: 34px; height: 34px; border-radius: 7px; object-fit: cover; flex: none; }
.component-search__thumb--empty { background: var(--wash); }
.component-search__name { flex: 1; min-width: 0; font-size: .85rem; font-weight: 600; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.component-search__price { flex: none; font-size: .82rem; font-weight: 600; color: var(--ink-soft); }
/* "already covered via {Category}" hint on a Specific Items search result — see the
   Associations accordion's smart-prompt in ModifierGroupDetail.razor / stageComponentSearchPick. */
.component-search__hint { flex: none; font-size: .7rem; color: var(--ink-soft); white-space: nowrap; }

.component-card__alts { display: flex; flex-direction: column; gap: .3rem; margin: .6rem 0 0 2.7rem; }

/* Alternatives (Meal Components) / options (Customer Choices) — indented, in-flow rows;
   deliberately NOT built on .meal-row__editdetails/.meal-row__editform (those float as an
   absolutely-positioned overlay), so editing an alternative's price delta or adding a new
   one renders directly in the card, pushing the rest of the content down like any other
   block, rather than popping out over it. */
.component-card__altrow { display: flex; align-items: center; gap: .4rem; }
.component-card__altedit { display: flex; align-items: center; gap: .5rem; flex: 1; min-width: 0; padding: .35rem .5rem; border-radius: 8px; background: var(--wash); font-size: .82rem; }
.component-card__altedit--add { background: none; border: 1px dashed var(--line); margin-top: .3rem; }
.component-card__alt-swap { flex: none; color: var(--ink-soft); }
.component-card__alt-label { flex: 1; min-width: 0; color: var(--ink); font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.component-card__altselect { flex: 1; min-width: 0; border: 1.5px solid #D6CDBC; border-radius: 6px; padding: .3rem .4rem; font: inherit; font-size: .8rem; background: #fff; }
/* The price difference and the portion of a serve sit side by side on every meal-option form: the same small labelled number box. */
.component-card__deltafield, .component-card__portionfield { display: flex; align-items: center; gap: .25rem; flex: none; font-size: .78rem; color: var(--ink-soft); }
.component-card__deltafield input, .component-card__portionfield input { width: 4.2rem; border: 1.5px solid #D6CDBC; border-radius: 6px; padding: .25rem .4rem; font: inherit; font-size: .8rem; background: #fff; }
.component-card__defaultcheck { display: flex; align-items: center; gap: .25rem; flex: none; font-size: .76rem; color: var(--ink-soft); white-space: nowrap; }
.component-card__defaultcheck input { width: 14px; height: 14px; accent-color: var(--teal); }

/* Marks a row with a pending, not-yet-committed edit while in draft mode, until Save Changes
   or a reload. */
.component-card__altrow--dirty .component-card__altedit { background: #FCF3D9; }

.component-card__addalt { display: block; margin: .5rem 0 0 2.7rem; }
.component-card__addalt-btn { cursor: pointer; font-size: .8rem; font-weight: 600; color: var(--teal-deep); list-style: none; }
.component-card__addalt-btn::-webkit-details-marker { display: none; }
.component-card__addalt-btn:hover { text-decoration: underline; }

.component-card__choiceicon { flex: none; width: 32px; height: 32px; border-radius: 8px; background: var(--wash); color: var(--teal-deep); display: grid; place-items: center; }
/* Left-indented to 2.7rem, matching .component-card__alts/.component-card__addalt on
   Simple Swap cards — otherwise the Mix and Match card's own content (sync pill, choice
   list, "+ Add option") sits flush with the card edge while the Simple Swap card's
   equivalent content is indented under the thumbnail column, making the two card types
   look inconsistently aligned side by side. */
.component-card__syncpill { display: inline-block; font-size: .76rem; font-weight: 600; color: #5F6B3D; background: #E3ECE7; border-radius: 999px; padding: .2rem .65rem; margin: .5rem 0 0 2.7rem; }
.component-card__choicelist { display: flex; flex-direction: column; gap: .3rem; margin: .6rem 0 0 2.7rem; }
.component-card__choicebullet { width: 11px; height: 11px; border-radius: 50%; flex: none; border: 1.5px solid var(--teal); }
.component-card__choicebullet--on { background: var(--teal); }

/* Meal Highlights badge picker */
.badge-picker { display: flex; flex-wrap: wrap; gap: .6rem; margin-top: .6rem; }
.badge-chip { display: inline-flex; align-items: center; gap: .45rem; padding: .55rem .9rem; border: 1.5px solid var(--line); border-radius: 10px; font-size: .84rem; font-weight: 600; color: var(--ink); cursor: pointer; }
.badge-chip input { position: absolute; opacity: 0; width: 0; height: 0; }
.badge-chip:has(input:checked) { border-color: var(--teal); background: #E3ECE7; color: var(--teal-deep); }
.badge-chip__ic { display: grid; place-items: center; flex: none; color: var(--teal-deep); }

.catalog-badge { display: inline-block; background: var(--wash); color: var(--ink-soft); font-size: .72rem; font-weight: 600; border-radius: 999px; padding: .15rem .55rem; margin-left: .4rem; }
.catalog-price { font-size: .78rem; color: var(--ink-soft); margin-left: auto; }

.catalog-dropzone {
    display: flex; flex-direction: column; align-items: center; gap: .25rem;
    border: 1.5px dashed var(--teal-soft); border-radius: 12px; padding: 1.6rem 1.2rem; text-align: center;
    color: var(--ink-soft); font-size: .84rem; cursor: pointer; margin-top: .3rem;
}
.catalog-dropzone strong { color: var(--ink-heading); font-size: .92rem; }
.catalog-dropzone:hover, .catalog-dropzone:focus-visible { border-color: var(--teal); background: var(--wash); outline: none; }
.catalog-dropzone.is-dragover { background: var(--wash); border-color: var(--teal); }
/* The native "Choose files / N files" control is hidden: the dropzone is the control, and the
   list below is the (accumulated) selection — a native input would only ever show the last pick. */
.catalog-file-input { display: none; }

/* Vertical image list — saved photos and photos waiting to be saved share one row design:
   big enough thumbnail to judge and remove at a glance, labelled buttons instead of tiny icons. */
.catalog-media-list { display: flex; flex-direction: column; gap: .75rem; margin-top: .9rem; }
.catalog-media { display: flex; align-items: center; gap: .9rem; padding: .65rem .85rem .65rem .6rem; border: 1px solid var(--line); border-radius: 14px; background: #fff; cursor: grab; }
.catalog-media:active { cursor: grabbing; }
.catalog-media.is-primary { border-color: var(--teal-soft); box-shadow: 0 0 0 1px var(--teal-soft); }
.catalog-media__handle { flex: none; display: grid; place-items: center; width: 22px; color: var(--ink-soft); cursor: grab; }
.catalog-media__num { flex: none; width: 26px; height: 26px; border-radius: 50%; background: var(--wash); color: var(--ink); font-size: .78rem; font-weight: 700; display: grid; place-items: center; }
.catalog-media__num:empty { visibility: hidden; }
.catalog-media__thumb { flex: none; width: 176px; height: 121px; object-fit: cover; border-radius: 10px; background: var(--wash); display: block; }
.catalog-media__body { flex: 1; min-width: 0; display: flex; flex-direction: column; align-items: flex-start; gap: .3rem; }
.catalog-media__name { font-size: .88rem; font-weight: 600; color: var(--ink-heading); max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.catalog-media__meta { font-size: .76rem; color: var(--ink-soft); }
.catalog-media__badge { display: inline-block; background: var(--teal); color: #fff; font-size: .7rem; font-weight: 700; letter-spacing: .3px; border-radius: 999px; padding: .2rem .65rem; }
.catalog-media__badge[hidden] { display: none; }
.catalog-media__badge--new { background: var(--wash); color: var(--ink-soft); border: 1px solid var(--line); }
.catalog-media__actions { flex: none; display: flex; align-items: center; gap: .5rem; }
.catalog-media__btn { position: relative; display: inline-flex; align-items: center; justify-content: center; gap: .35rem; min-height: 38px; padding: .45rem 1rem; border: 1.5px solid var(--line-input); border-radius: 999px; background: #fff; color: var(--ink); font: inherit; font-size: .82rem; font-weight: 600; cursor: pointer; white-space: nowrap; transition: background .12s ease, border-color .12s ease; }
.catalog-media__btn:hover { background: var(--wash); border-color: var(--teal-soft); }
.catalog-media__btn--danger { color: #b3261e; border-color: #e6c3bf; }
.catalog-media__btn--danger:hover { background: #fbeae5; border-color: #d9a29c; }
.catalog-media__btn input { position: absolute; opacity: 0; width: 0; height: 0; }
.catalog-media__btn:has(input:focus-visible) { outline: 2px solid var(--teal); outline-offset: 2px; }
.catalog-media.is-primary [data-action="make-primary"] { display: none; }
/* A saved image ticked for removal: dimmed and dashed, button flips to "Undo remove". */
.catalog-media__undo-label { display: none; }
.catalog-media:has(input[name="removeImageIds"]:checked) { opacity: .6; border-style: dashed; background: #fdf7f6; }
.catalog-media:has(input[name="removeImageIds"]:checked) .catalog-media__thumb { filter: grayscale(1); }
.catalog-media:has(input[name="removeImageIds"]:checked) .catalog-media__remove-label { display: none; }
.catalog-media:has(input[name="removeImageIds"]:checked) .catalog-media__undo-label { display: inline; }
.catalog-media:has(input[name="removeImageIds"]:checked) [data-action="make-primary"] { display: none; }
@media (max-width: 640px) {
    .catalog-media { flex-wrap: wrap; }
    .catalog-media__thumb { width: 100%; height: auto; aspect-ratio: 16 / 11; }
    .catalog-media__body { flex-basis: 100%; }
    .catalog-media__actions { width: 100%; }
    .catalog-media__actions .catalog-media__btn { flex: 1; }
}

/* Catalog list tables (Meals / Menu Items) */
.catalog-section { background: #fff; border: 1px solid var(--line); border-radius: 16px; padding: 1.25rem; margin-bottom: 1.5rem; }
.catalog-section h2 { font-size: 1.6rem; font-weight: 700; margin: 0; color: var(--ink-heading); }
.catalog-table-wrap { overflow-x: auto; }
.catalog-table { width: 100%; border-collapse: collapse; font-size: .86rem; }
.catalog-table th { text-align: left; font-size: .74rem; color: var(--ink-soft); text-transform: uppercase; letter-spacing: .02em; padding: .5rem .6rem; border-bottom: 1px solid var(--line); white-space: nowrap; }
.catalog-table td { padding: .65rem .6rem; border-bottom: 1px solid var(--line); vertical-align: middle; }
.catalog-table tbody tr:last-child td { border-bottom: none; }

.catalog-table__namecell { display: flex; align-items: center; gap: .65rem; min-width: 200px; text-decoration: none; }
.catalog-table__namecell:hover .catalog-table__name, a.catalog-table__name:hover { color: var(--teal-deep); text-decoration: underline; }
.catalog-table__thumb { width: 44px; height: 44px; object-fit: cover; border-radius: 8px; flex: none; }
/* Faint image-glyph placeholder for items with no photo yet — same muted tone as
   .meal-day__empty svg, background-image rather than a markup child so both
   CatalogMeals.razor and CatalogMenuItems.razor get it for free from this one class. */
.catalog-table__thumb--empty {
    background-color: var(--wash);
    background-repeat: no-repeat;
    background-position: center;
    background-size: 20px 20px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23C3CBC5' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='3' width='18' height='18' rx='3'/%3E%3Ccircle cx='8.5' cy='8.5' r='1.5'/%3E%3Cpath d='M21 15l-5-5L5 21'/%3E%3C/svg%3E");
}
.catalog-table__name { font-size: .88rem; font-weight: 600; color: var(--ink-heading); margin: 0; text-decoration: none; }

.catalog-table__stack { display: flex; align-items: center; }
.catalog-table__stack img { width: 32px; height: 32px; object-fit: cover; border-radius: 6px; border: 2px solid #fff; box-shadow: 0 0 0 1px var(--line); margin-left: -8px; }
.catalog-table__stack img:first-child { margin-left: 0; }
.catalog-table__stackmore { display: grid; place-items: center; width: 32px; height: 32px; border-radius: 6px; background: var(--wash); color: var(--ink-soft); font-size: .68rem; font-weight: 700; border: 2px solid #fff; box-shadow: 0 0 0 1px var(--line); margin-left: -8px; }
.catalog-table__stackmore--empty { margin-left: 0; font-weight: 500; }

.catalog-table__footer { display: flex; align-items: center; justify-content: space-between; padding-top: .8rem; font-size: .8rem; color: var(--ink-soft); }

/* Modifiers table — drag handle + inline "ghost" creation row (admin.js initModifierDrag),
   replacing a manual Sort Order column and a separate add-modifier form below the table. */
.catalog-table tr[draggable="true"] { cursor: grab; }
.catalog-table__draghandle { width: 24px; color: var(--ink-soft); }
.catalog-table__addrow td { background: var(--wash); }
.catalog-table__addinput { width: 100%; border: none; background: none; font: inherit; font-style: italic; color: var(--ink-soft); padding: .3rem; border-radius: 6px; }
.catalog-table__addinput::placeholder { color: var(--ink-soft); font-style: italic; }
.catalog-table__addinput:focus { outline: none; font-style: normal; color: var(--ink); background: #fff; box-shadow: 0 0 0 1.5px var(--teal); }
.catalog-table__addinput--num { max-width: 5.5rem; }

/* Modifiers / Price Rules tables' inline pencil-edit — same "readonly until expanded"
   idea as .component-card__nameinput (Meal page), just narrower for a number cell. */
.catalog-table__numinput {
    font: inherit; font-size: .85rem; color: var(--ink);
    border: 1.5px solid transparent; background: none; border-radius: 6px;
    padding: .15rem .3rem; width: 5.5rem;
}
.catalog-table__numinput:not([readonly]) { border-color: #D6CDBC; background: #fff; }
/* :invalid reflects setCustomValidity() from the Default Modifier Groups Min/Max
   validation in admin.js — overrides the browser's own default invalid-outline styling. */
.catalog-table__numinput:invalid:not([readonly]) { border-color: var(--error-border); background: var(--error-soft); }
.catalog-table__hint { font-size: .72rem; color: var(--ink-soft); margin-left: .3rem; }

/* Action icons (pencil/trash) placed side by side in a table cell — <details>/<form>
   default to block-level and stack vertically otherwise. */
.catalog-table__actioncell { display: flex; align-items: center; gap: .4rem; }

/* Generic "modified locally, not yet saved" indicator — admin.js adds this class to any
   touched text/number/select/textarea field inside a tracked section-save-form (Modifier
   Group Overview/Modifiers/Price Rules, and anywhere else the same draft pattern is used).
   A deliberately non-brand blue: this is a system/state signal, not themed UI, so it should
   read the same regardless of which company's accent colors are active. */
.field--dirty { border-color: #3b82f6 !important; box-shadow: 0 0 0 2px rgba(59, 130, 246, .15); }

/* Modifiers table / Price Rules reference list rows marked pending-delete via the trash/undo
   toggle (data-role="row-delete-toggle" in admin.js) — dimmed + struck through to signal
   "this will be gone once you save," fully reversible via Undo until then. Fields go inert
   (readOnly, no pointer events) since editing a row you're about to delete makes no sense. */
.catalog-table__row--pending-delete { opacity: .5; }
.catalog-table__row--pending-delete input,
.catalog-table__row--pending-delete td:not(.catalog-table__actioncell) { text-decoration: line-through; }
.catalog-table__row--pending-delete input { pointer-events: none; }
.catalog-table__row--pending-delete .catalog-table__actioncell,
.catalog-table__row--pending-delete .catalog-table__actioncell input { pointer-events: auto; text-decoration: none; }

/* A modifier queued via the Modifiers accordion's "Add" button but not yet committed —
   staged client-side, written by the next Save Changes batch, dropped by Cancel Changes.
   Teal left rail + wash fill mark it as a positive pending addition (vs pending-delete's
   dimmed strikethrough). */
.catalog-table__row--pending-new td,
.catalog-table__row--pending-restore td { background: var(--wash); box-shadow: inset 0 1px 0 var(--line); }
.catalog-table__row--pending-new td:first-child,
.catalog-table__row--pending-restore td:first-child { box-shadow: inset 3px 0 0 var(--teal), inset 0 1px 0 var(--line); }
.catalog-table__pendingname { font-weight: 600; color: var(--ink); }
.catalog-table__pendingprice { font-variant-numeric: tabular-nums; color: var(--ink-soft); }
/* A row staged for restore is shown even while "Show Inactive" is off (admin.js flips its
   data-role); drop the inline display:none the server rendered on it so it's visible. */
.catalog-table__row--pending-restore { display: table-row !important; }

/* A row whose own IsActive is still true in the DB, but is treated as inactive here
   because its PARENT is inactive (a modifier under an inactive group; a price rule
   targeting an inactive modifier or belonging to an inactive group) — distinct from
   --pending-delete (no strikethrough: nothing is being removed, DB state is untouched) and
   from a genuinely-inactive row (no Restore/Delete Permanently — there's nothing on this
   row itself to restore; fixing the parent is what unlocks it). */
.catalog-table__row--inherited-inactive { opacity: .5; }

/* Row-delete-toggle button — trash (default) vs. undo (once pending-delete) states share one
   button/icon slot (admin.js toggles which <svg> is hidden); undo gets an accent color so it
   doesn't read as "delete again." Explicit svg[hidden] override: the bare HTML `hidden`
   attribute is NOT reliably respected on <svg> root elements — SVG's own UA-stylesheet
   default (effectively `display: inline`) wins over the browser's generic `[hidden] {
   display: none }` rule for this element type, so without this both icons render stacked
   on top of each other regardless of which one JS marks hidden. */
[data-role="row-delete-toggle"] svg[hidden] { display: none; }

/* Default Modifier Groups list — the real Single/Multiple + Min/Max controls expand on pencil
   click; margin-top gives that expansion breathing room instead of crowding the row above. */

.catalog-table__rules { display: flex; flex-wrap: wrap; align-items: center; gap: .8rem; margin-top: .6rem; }
.catalog-table__rules .meal-row__check { margin: 0; white-space: nowrap; }
.catalog-table__ruleslabel { display: flex; align-items: center; gap: .35rem; font-size: .8rem; color: var(--ink-soft); white-space: nowrap; }
/* flex-basis: 100% forces it onto its own line below Single/Multiple/Min/Max rather than
   squeezing in beside them, however wide the row wraps. */
.catalog-table__ruleserror { flex-basis: 100%; font-size: .78rem; color: var(--error); background: var(--error-soft); border-radius: 6px; padding: .35rem .6rem; margin-top: .2rem; }
/* Same rules editor, now living in its own full-width row (see ModifierGroupDetail.razor's
   Associations accordion) instead of squeezed into a table cell — a bit more breathing room. */
.catalog-table__rules--row { padding: .5rem .2rem; }
/* Modifier Group Associations accordion: Scope Sub-Tabs (Categories / Specific Items /
   Collections) — a plain underline-style segmented control, not full "tab panel" chrome,
   since it sits inside an already-bordered form-card. */
.assoc-tabs { display: flex; gap: 1.1rem; margin: .9rem 0 1.1rem; border-bottom: 1.5px solid var(--line); }
.assoc-tabs__tab {
    font: inherit; font-size: .85rem; font-weight: 600; color: var(--ink-soft);
    background: none; border: none; border-bottom: 2px solid transparent;
    padding: 0 .1rem .6rem; margin-bottom: -1.5px; cursor: pointer;
}
.assoc-tabs__tab:hover { color: var(--ink); }
.assoc-tabs__tab--active { color: var(--teal-deep); border-bottom-color: var(--teal); }

/* Inheritance-source badges — Categories tab's summary column and Specific Items tab's
   "Inheritance Source" column both use these three (plus the existing status-pill--pending
   for "not yet synced", which isn't an inheritance state so stays a plain pill). */
.assoc-badge { display: inline-flex; align-items: center; padding: .2rem .55rem; border-radius: 999px; font-size: .72rem; font-weight: 600; margin: 0 .3rem .2rem 0; white-space: nowrap; }
.assoc-badge--inherited { background: #E3ECE7; color: #5F6B3D; }
.assoc-badge--direct { background: #E7EEF7; color: #35618F; }
.assoc-badge--excluded { background: #FBEAE5; color: #b3261e; }
.assoc-badge--pending { background: #FBF3E0; color: #8a6d1f; }

/* Specific Items tab's "Inheritance Type" column — its own 3-state pill set (deliberately
   distinct colors from .assoc-badge--inherited/--direct above, which mean something
   different: aggregate per-category counts on the Categories tab, not one item's own
   status) — Excluded reuses .assoc-badge--excluded above (same meaning, same red,
   everywhere it appears). */
.assoc-badge--inherited-type { background: #E9EEF3; color: #4A5B6E; }
.assoc-badge--custom-type { background: #F1E8F7; color: #7A4EA3; }

/* Categories tab's per-category item tree — a chevron toggle (leading table column) reveals
   every item in that category with its own inheritance/exclusion status, so "why does this
   item have the group" never requires leaving the row. */
.catalog-table__treetoggle { width: 2rem; text-align: center; }
.assoc-tree-toggle { display: inline-flex; align-items: center; justify-content: center; width: 1.6rem; height: 1.6rem; border: none; background: none; color: var(--ink-soft); cursor: pointer; border-radius: 6px; padding: 0; }
.assoc-tree-toggle:hover { background: var(--wash); color: var(--ink); }
.assoc-tree-toggle__chevron { transition: transform .15s ease; }
.assoc-tree-toggle[aria-expanded="true"] .assoc-tree-toggle__chevron { transform: rotate(90deg); }
.assoc-tree { display: flex; flex-direction: column; gap: .5rem; padding: .3rem .2rem .3rem 1.4rem; border-left: 2px solid var(--line); margin-left: .7rem; }
.assoc-tree__row { display: flex; align-items: center; flex-wrap: wrap; gap: .5rem; font-size: .82rem; }
.assoc-tree__mark { flex: none; width: 1.1rem; text-align: center; }
.assoc-tree__name { flex: none; font-weight: 600; color: var(--ink); min-width: 9rem; }
.assoc-tree__row--excluded .assoc-tree__name { color: var(--ink-soft); }
.assoc-tree__status { color: var(--ink-soft); }
.assoc-tree__status--pending { color: #8a6d1f; }

/* Categories tab's own "Selection Rules" column — a dedicated badge instead of muted text
   squeezed under the category name, so it's scannable at a glance across every row. */
.assoc-rules-badge { display: inline-block; padding: .2rem .6rem; border-radius: 8px; background: var(--wash); border: 1px solid var(--line); font-size: .78rem; font-weight: 600; color: var(--ink); white-space: nowrap; }

/* Merged drawer under a Categories-tab row (ModifierGroupDetail.razor) — opened by either
   the leading chevron or the row's "Edit Rules" button (same data-target, generic
   component-edit-toggle), holding the category's own rules editor above the item tree
   instead of the rules editor living in a separate modal. */
.assoc-drawer { display: flex; flex-direction: column; gap: .6rem; }

/* The drawer's two independently collapsible sections ("Category Selection Rules
   Override" / "Linked Items") — each a bordered box with its own toggle bar, reusing
   .assoc-tree-toggle/.assoc-tree-toggle__chevron's existing rotate-on-expand rule (the
   toggle button carries both classes) rather than a second copy of it. The width/padding
   overrides below win over .assoc-tree-toggle's own (a small square icon button) since
   they're declared later at equal specificity. */
.assoc-subpanel { border: 1px solid var(--line); border-radius: 10px; overflow: hidden; }
.assoc-subpanel__toggle {
    display: flex; align-items: center; justify-content: flex-start; gap: .5rem; width: 100%; height: auto;
    text-align: left; font: inherit; font-size: .8rem; font-weight: 700;
    color: var(--ink-heading); background: var(--wash);
    border: none; border-radius: 0; cursor: pointer; padding: .6rem .9rem;
}
.assoc-subpanel__toggle:hover { background: var(--line); }
.assoc-subpanel__body { padding: .9rem; }

/* Row-level "..." overflow menu (Categories tab's Actions column) — a plain <details>,
   closed by clicking outside it (admin.js, delegated on document) or Escape. Not built on
   the .modal-details mechanism above: that's a full-screen centered dialog with a dark
   backdrop, this is a small anchored dropdown right under its own trigger. */
.row-menu { position: relative; display: inline-flex; }
.row-menu > summary { list-style: none; cursor: pointer; }
.row-menu > summary::-webkit-details-marker { display: none; }
.row-menu__panel { display: none; }
.row-menu[open] .row-menu__panel {
    display: flex; flex-direction: column; gap: .3rem;
    position: absolute; top: calc(100% + .3rem); right: 0; z-index: 30;
    background: #fff; border: 1px solid var(--line); border-radius: 10px;
    box-shadow: 0 10px 28px rgba(20,24,20,.14); padding: .4rem; min-width: 12rem;
}
.row-menu__panel .btn { width: 100%; justify-content: flex-start; }

/* In-page Meals / Menu Items tab pair — CatalogMeals.razor and CatalogMenuItems.razor
   are now separate routes, this keeps the two visually paired the way the single
   two-section Catalog page used to. */
.admin-subnav { display: flex; gap: .4rem; margin-bottom: 1.1rem; border-bottom: 1px solid var(--line); }
.admin-subnav__item { padding: .55rem .2rem; margin-right: 1.1rem; font-size: .92rem; font-weight: 600; color: var(--ink-soft); text-decoration: none; border-bottom: 2px solid transparent; }
.admin-subnav__item:hover { color: var(--ink); }
.admin-subnav__item--active { color: var(--teal-deep); border-bottom-color: var(--teal); }

/* Catalog list pages' search + category filter bar. SearchFilterBar's root element is
   taller than just its search box (it also carries the "Showing N of M" summary line
   below), so align-items: flex-start keeps the category <select> pinned to the top at its
   own natural height instead of the default stretch blowing it up to match. */
.catalog-filter { display: flex; align-items: flex-start; gap: .6rem; margin-bottom: .9rem; }

.catalog-filter__category { flex: 0 1 220px; width: auto; }

/* Row-checkbox bulk-action toolbar, shown once ≥1 row is checked. */
.catalog-table__checkcol, .catalog-table td:first-child { width: 34px; }
.catalog-table input[type="checkbox"] { width: 15px; height: 15px; accent-color: var(--teal); cursor: pointer; }
.catalog-bulk { display: none; align-items: center; gap: .75rem; background: var(--wash); border-radius: 10px; padding: .6rem .9rem; margin-bottom: .75rem; }
.catalog-bulk--visible { display: flex; }
.catalog-bulk__count { font-size: .82rem; font-weight: 600; color: var(--ink); margin-right: auto; }
.catalog-bulk__form { display: flex; align-items: center; gap: .5rem; margin: 0; }
.catalog-bulk__categoryselect { width: auto; padding: .5rem .7rem; font-size: .82rem; }

/* Review Management (Reviews.razor) */
.reviews-header { margin-bottom: 1.25rem; }
.reviews-header__row { display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem; }
.reviews-filter-bar { display: flex; align-items: flex-end; gap: 1rem; flex-wrap: wrap; background: #fff; border: 1px solid var(--line); border-radius: 16px; padding: 1rem 1.25rem; margin-bottom: 1.25rem; }
.reviews-filter-bar .lbl { min-width: 140px; }
.reviews-filter-bar__actions { display: flex; align-items: center; gap: .75rem; margin-left: auto; }

.reviews-date-range { display: flex; align-items: center; gap: .4rem; color: var(--ink-soft); }
.reviews-date-range .tf { width: auto; }
.reviews-date-range span { flex: none; }

.reviews-tabs { margin-bottom: 1.25rem; }
.reviews-tab__count { opacity: .75; font-weight: 500; margin-left: .2rem; }


.reviews-cell__name { font-size: .88rem; font-weight: 600; color: var(--ink-heading); margin: 0; }
.reviews-cell__sub { font-size: .78rem; color: var(--ink-soft); margin: .15rem 0 0; }

.review-stars { color: var(--turmeric); letter-spacing: 1px; white-space: nowrap; }
.review-stars--lg { font-size: 1.2rem; }
.review-comment-preview { color: var(--ink-soft); max-width: 260px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.status-pill--pending { background: #FBF3E0; color: #8a6d1f; }
.status-pill--submitted { background: #FBF3E0; color: #8a6d1f; }
/* Catering "Flags" column: each flag is its own small amber pill. */
.row-cell.catering-flags { display: flex; flex-wrap: wrap; gap: .3rem; align-items: center; }
.flag-pill { display: inline-block; padding: .2rem .55rem; border-radius: 999px; background: #FBF3E0; color: #8a6d1f; font-size: .72rem; font-weight: 600; white-space: nowrap; }
.status-pill--responded { background: #E3ECE7; color: #8A9A5B; }
.status-pill--mod-approved { background: #E3ECE7; color: #8A9A5B; }
.status-pill--mod-rejected { background: #FBEAE5; color: #b3261e; }
.status-pill--mod-flagged { background: #FBF0DC; color: #8A6425; }
.status-pill--mod-pending { background: var(--wash); color: var(--ink-soft); }

.reviews-row-open { display: block; text-decoration: none; font-size: 1rem; color: var(--ink-soft); text-align: center; }
.reviews-row-open:hover { color: var(--teal-deep); }

.reviews-pagination { margin-top: 1rem; }
.reviews-pagination__pages { display: flex; align-items: center; gap: .4rem; }
.reviews-pagination__pages .date-tab { padding: .4rem .75rem; }
.reviews-pagination__disabled { opacity: .35; pointer-events: none; }

.reviews-footnote { color: var(--ink-soft); font-size: .82rem; background: #fff; border: 1px solid var(--line); border-radius: 12px; padding: .8rem 1rem; margin-top: 1.5rem; }

.review-detail__overall p { margin: .4rem 0 0; }
.review-items-table { width: 100%; border-collapse: collapse; font-size: .85rem; }
.review-items-table th { text-align: left; font-size: .74rem; color: var(--ink-soft); text-transform: uppercase; letter-spacing: .02em; padding: .4rem .5rem; border-bottom: 1px solid var(--line); }
.review-items-table td { padding: .5rem; border-bottom: 1px solid var(--line); }

.review-reply { border-radius: 10px; padding: .8rem 1rem; margin: 1rem 0; }
.review-reply--sent { background: #E3ECE7; }
.review-reply--sent p { margin: .3rem 0 0; }
.review-reply__lbl { margin: 0; font-size: .78rem; font-weight: 600; color: #5F6B3D; }
.review-reply-form { display: flex; flex-direction: column; gap: .6rem; margin: 1rem 0; }
.review-reply-form textarea.tf, .review-moderate-form textarea.tf { resize: vertical; }

/* Slide-over detail panel — server-rendered via ?selected=<id>, no client JS (this app
   has no live Blazor circuit). Each open/close/Prev-Next is a full page navigation. */
.review-panel-backdrop { position: fixed; inset: 0; background: rgba(0,0,0,.4); z-index: 40; }
.review-panel {
    position: fixed; top: 0; right: 0; bottom: 0; width: min(480px, 100vw); z-index: 41;
    background: #fff; overflow-y: auto; box-shadow: -4px 0 24px rgba(0,0,0,.16); padding: 1.5rem;
    display: flex; flex-direction: column;
}
.review-panel__head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 1rem; }
.review-panel__nav { display: flex; gap: .4rem; }
.review-panel__navbtn {
    display: grid; place-items: center; width: 30px; height: 30px; border-radius: 8px;
    border: 1px solid var(--line); color: var(--ink); text-decoration: none;
}
.review-panel__navbtn:hover { background: var(--wash); }
.review-panel__navbtn--disabled { opacity: .35; pointer-events: none; }
.review-panel__close { display: grid; place-items: center; width: 30px; height: 30px; border-radius: 8px; color: var(--ink-soft); text-decoration: none; }
.review-panel__close:hover { background: var(--wash); color: var(--ink); }
.review-panel__title { font-family: var(--serif); font-weight: 700; font-size: 1.15rem; color: var(--ink-heading); margin-bottom: .6rem; }
.review-panel__meal { display: flex; align-items: center; gap: .6rem; margin-bottom: 1rem; font-size: .88rem; font-weight: 600; color: var(--ink-heading); }
.review-panel__mealimg { width: 44px; height: 44px; object-fit: cover; border-radius: 8px; flex: none; }

.review-moderate-form { display: flex; flex-direction: column; gap: .3rem; border-top: 1px solid var(--line); padding-top: 1rem; margin-top: .5rem; }
.review-moderate-hint { font-size: .78rem; color: var(--ink-soft); margin: .2rem 0 .3rem; }
.review-moderate-form button { margin-top: .5rem; align-self: flex-start; }

.review-history { border-top: 1px solid var(--line); padding-top: 1rem; margin-top: 1.25rem; }
.review-history__title { font-size: .88rem; margin: 0 0 .8rem; }
.review-history__entry { display: flex; flex-direction: column; align-items: flex-start; gap: .3rem; padding: .6rem 0; border-bottom: 1px solid var(--line); }
.review-history__entry:last-child { border-bottom: none; padding-bottom: 0; }
.review-history__meta { font-size: .76rem; color: var(--ink-soft); }
.review-history__notes { font-size: .84rem; color: var(--ink); margin: 0; }

/* Touch targets. The primary target for this admin is a laptop, but it's also used on an
   iPad / tablet — and an iPad very often has a keyboard case, which makes its PRIMARY
   pointer report as fine (`pointer: coarse` would then miss it). `any-pointer: coarse`
   fires whenever a touch pointer is available AT ALL, so a fingertip on the iPad screen
   still gets ~44px hit areas whether or not a trackpad is also attached. The only cost is
   a touchscreen Windows laptop also getting the roomier row actions while mousing — an
   acceptable trade for not under-sizing the tablet case. Non-touch laptops (the common
   case) are unaffected and keep the dense desktop layout. This is also the sizing a
   future dedicated mobile client should adopt for these same actions. */
@media (any-pointer: coarse) {
    .btn--sm { min-height: 44px; padding: .6rem 1rem; font-size: .88rem; }
    .act-ic { width: 18px; height: 18px; }
    .meal-row__actions { gap: .55rem; flex-wrap: wrap; }
    .catalog-table__actioncell { gap: .55rem; flex-wrap: wrap; }
    .meal-row__iconbtn { width: 44px; height: 44px; border-radius: 9px; }
    .meal-row__iconbtn svg { width: 18px; height: 18px; }
    /* Keep the terminal action from sitting flush against Archive/Restore. */
    .meal-row__actions .meal-row__inlineform:last-child { margin-left: .15rem; }
}

/* Tablet / iPad portrait. The admin is laptop-first, but on a ~768-860px screen the fixed
   240px sidebar plus 2rem page padding leave the content columns too tight. Trim the
   sidebar and page chrome, drop multi-column grids to fewer columns, and let wide tables
   scroll inside their own .catalog-table-wrap (paired with .form-card's min-width:0) rather
   than forcing the whole page to scroll sideways. iPad landscape (>=1024px) never hits
   this and stays on the full desktop layout. */
@media (max-width: 860px) {
    .admin-sidebar { width: 200px; padding: 1.25rem 0; }
    .admin-sidebar__brand { padding: 0 1rem 1.25rem; }
    .admin-nav__item, .admin-nav__group-label { padding-left: 1rem; padding-right: 1rem; }
    .admin-nav__item--sub { padding-left: 1.7rem; }
    .admin-header { padding-top: 1rem; padding-bottom: 1rem; }
}

/* Menu Category icon picker (CategoryIconPicker.razor) — a grid of real icon tiles
   standing in for a plain <select>, so an admin can see each icon before picking. */
.icon-picker { display: flex; flex-wrap: wrap; gap: .5rem; margin: .3rem 0 1rem; }
.icon-picker__option {
    display: flex; flex-direction: column; align-items: center; gap: .3rem;
    width: 4.6rem; padding: .6rem .3rem .5rem; border: 1.5px solid var(--line); border-radius: 10px;
    background: #fff; cursor: pointer; font: inherit; color: var(--ink-soft);
}
.icon-picker__option:hover { border-color: var(--teal); color: var(--ink); }
.icon-picker__option--selected { border-color: var(--teal); background: var(--wash); color: var(--teal-deep); box-shadow: 0 0 0 1px var(--teal); }
.icon-picker__ic { display: grid; place-items: center; width: 22px; height: 22px; }
.icon-picker__ic svg { width: 22px; height: 22px; }
.icon-picker__label { font-size: .68rem; text-align: center; line-height: 1.2; text-transform: capitalize; }

/* Search-input-with-filter-chip (SearchFilterBar.razor) — the one search+filter control
   for every catalog list/table. One bordered box holds the icon + text input + the
   status chip, so the chip reads as part of the search control rather than a second,
   disconnected checkbox floating nearby; the match/total summary sits under the box. */
.search-filter-bar { margin-bottom: .6rem; }
.search-filter-bar__chip {
    display: inline-flex; align-items: center; gap: .35rem; flex: none; white-space: nowrap;
    font: inherit; font-size: .78rem; font-weight: 600; color: var(--ink-soft);
    background: var(--wash); border: 1px solid var(--line); border-radius: 999px;
    padding: .3rem .55rem; cursor: pointer;
}
.search-filter-bar__chip:hover { border-color: var(--teal); color: var(--ink); }
.search-filter-bar__chipx { opacity: 0; font-size: .7rem; margin-left: -.1rem; }
.search-filter-bar__chip--on {
    background: var(--teal); border-color: var(--teal); color: #fff;
}
.search-filter-bar__chip--on .search-filter-bar__chipx { opacity: .85; }
/* Deleted chip's "on" state uses the same red as .status-pill--cancelled/--deleted rather
   than teal — permanently-deleted rows are a different severity than a routine archive, and
   the color says so at a glance before the admin even reads the label. */
.search-filter-bar__chip--on-deleted {
    background: #b3261e; border-color: #b3261e; color: #fff;
}
.search-filter-bar__chip--on-deleted .search-filter-bar__chipx { opacity: .85; }
.search-filter-bar__summary { flex: 0 0 auto; margin: 0 0 0 auto; font-size: .8rem; color: var(--ink-soft); min-height: 1.1em; }
.batch-error { background: #fdecea; color: #a12622; border: 1px solid #f5c2be; border-radius: 12px; padding: .75rem 1rem; margin: 0 0 1rem; font-weight: 600; }

/* ---- Settings editor (SettingsEditor.razor) ---- */
.settings-group { margin-bottom: 1.25rem; }
.setting { padding: 1rem 0; border-top: 1px solid var(--line); max-width: 760px; }
.setting:first-of-type { border-top: 0; padding-top: .25rem; }
.setting--saved { background: #F4F8EC; border-radius: 10px; padding-left: .75rem; padding-right: .75rem; }
.setting__head { display: flex; align-items: baseline; justify-content: space-between; gap: .75rem; flex-wrap: wrap; }
.setting__badges { display: flex; gap: .4rem; flex-wrap: wrap; }
.setting__control { display: flex; align-items: center; gap: .6rem; max-width: 480px; }
.setting__control .tf { flex: 1; }
.setting__unit { color: var(--ink-soft); font-size: .9rem; white-space: nowrap; }
.setting__code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .85rem; }
.setting__access { display: flex; gap: 1rem; flex-wrap: wrap; margin-top: .75rem; }
.setting__access .lbl { display: flex; flex-direction: column; gap: .25rem; min-width: 12rem; }
.setting__actions { display: flex; gap: .6rem; flex-wrap: wrap; margin-top: .75rem; }

/* ---- Utilities ----
   Tiny single-purpose helpers for the occasional one-off spacing tweak, so a page never
   needs an inline style="margin-..." attribute. They sit at the very END of this file on
   purpose: a component's own margin (e.g. .form-card__hint { margin: 0 0 1rem }) has the
   same specificity, so the later rule wins. (The only inline styles left are
   display:none on filter-hidden table rows - admin.js switches those on and off.) */
.u-mt-sm { margin-top: .5rem; }
.u-mt-md { margin-top: 1rem; }

/* ---- Settings -> Messages (Messages.razor) ---- */
.msg-cell-key { white-space: nowrap; vertical-align: top; }
.msg-item__key { display: block; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .82rem; color: var(--ink-heading); margin-bottom: .35rem; }
.msg-item__tags { display: flex; gap: .35rem; flex-wrap: wrap; }
.msg-edit__summary { cursor: pointer; color: var(--ink); line-height: 1.45; }
.msg-edit[open] .msg-edit__summary { font-weight: 600; }
.msg-item__form { margin-top: 1rem; padding: 1rem 0 .2rem; border-top: 1px solid var(--line); max-width: 640px; }
.msg-item__actions { display: flex; gap: .6rem; margin-top: .4rem; }
.msg-tag { font-size: .68rem; font-weight: 700; letter-spacing: .03em; text-transform: uppercase; padding: .15rem .55rem; border-radius: 999px; background: #EEF0E2; color: var(--ink-soft); }
.msg-tag--error { background: #FBEAE5; color: #B3261E; }
.msg-tag--warning { background: #FBF0D8; color: #8A6425; }
.msg-tag--custom { background: #E3ECE7; color: var(--teal-deep); }
