/* ============================================================
   05-shared.css — Cross-portal state, and outlet operations

   Pieces every portal draws on — the order queue, state medallions, empty states,
   entry animations, photo widgets and toasts — followed by the outlet's operational
   controls: daily caps, the pause switch and danger zones.

   Part of an ordered set linked in fragments/head.html. The browser applies these
   in link order and THAT ORDER IS THE CASCADE — a rule here can only override one
   in an earlier file. Do not reorder the links, and do not move a rule between
   files without checking what it then stops overriding.
   ============================================================ */

/* ============================================================
   04-shared.css  (4 of 6)

   Cross-portal UI: state medallions, empty states, icons, entry animations,
   toast, page headers.

   Two outlet-only blocks also live in this range — the queue, and the menu
   photo grid with its upload widget. They are not here because they belong
   here; they are here because they sit at these line numbers in the original,
   and lifting them out would reorder the cascade.

   Part of an ordered set. The browser concatenates these in the order they
   are linked in fragments/head.html, and that order IS the cascade. Do not
   reorder the <link> tags, and do not move a rule between files without
   checking what it would then stop overriding.
   ============================================================ */

/* ============================================================
   CANTEEN QUEUE
   ============================================================ */

.live-dot {
    width: 9px;
    height: 9px;
    border-radius: 0;
    background: var(--color-success);
    display: inline-block;
    box-shadow: 0 0 0 rgba(20, 16, 13, 0.35);
    animation: live-pulse 2s infinite;
}

@keyframes live-pulse {
    0%   { box-shadow: 0 0 0 0 rgba(20, 16, 13, 0.35); }
    70%  { box-shadow: 0 0 0 8px rgba(20, 16, 13, 0.35); }
    100% { box-shadow: 0 0 0 0 rgba(20, 16, 13, 0.35); }
}

/* Brand fallback mark (shown only if the Bito image fails to load) — terracotta,
   since it stands in for the logo rather than for an action. */
.auth-mark {
    width: 48px;
    height: 48px;
    margin: 0 auto 1rem;
    border-radius: 0;
    background: var(--color-accent-strong);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: var(--font-display);
    font-weight: 700;
    font-size: 1.35rem;
}

/* ============================================================
   BRAND MARK & STATE MEDALLIONS

   Bito the mascot is gone. The identity is now an abstract six-ray
   swirl — pastel at display sizes, solid terracotta wherever it has to
   survive small (favicon, navbar, 16px).

   A mark can't perform "order ready" the way a character could, so every
   empty/error/status moment is a medallion instead: the pastel swirl as
   a soft backdrop, a cream disc on top, one icon carrying the meaning
   and a tone carrying the mood. One shape, many states.
   ============================================================ */

.brand-mark {
    flex-shrink: 0;
}

.state-medallion {
    position: relative;
    display: grid;
    place-items: center;
    animation: medallion-arrive 420ms cubic-bezier(0.34, 1.4, 0.64, 1) 120ms both;
}

/* The swirl sits behind at reduced opacity so it reads as a warm halo
   rather than competing with the icon in front of it. */
.state-medallion::before {
    content: '';
    position: absolute;
    inset: 0;
    background: url('/img/logo-mark-pastel.svg') center / contain no-repeat;
    opacity: 0.55;
}

.state-medallion__disc {
    position: relative;
}

.state-medallion__disc i {
    font-size: 2.1rem;
    line-height: 1;
    color: var(--color-accent-strong);
}

.state-medallion--good .state-medallion__disc i { color: var(--color-primary-dark); }
.state-medallion--bad  .state-medallion__disc i { color: var(--color-danger); }

/* Sad/error moments drop the spring overshoot — the same bouncy landing
   that reads as delighted is tone-deaf on a cancelled order. */
.state-medallion--bad {
    animation: medallion-arrive-somber 380ms cubic-bezier(0.22, 1, 0.36, 1) 120ms both;
}

.state-medallion--sm { width: 96px; height: 96px; }
.state-medallion--sm .state-medallion__disc { width: 54px; height: 54px; }
.state-medallion--sm .state-medallion__disc i { font-size: 1.5rem; }

@keyframes medallion-arrive {
    from { opacity: 0; transform: translateY(10px) scale(0.94); }
    to   { opacity: 1; transform: none scale(1); }
}

@keyframes medallion-arrive-somber {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
    .state-medallion, .state-medallion--bad { animation: none; }
}

/* ============================================================
   EMPTY / ERROR STATES
   ============================================================ */

.state-panel {
    max-width: 440px;
    margin: 0 auto;
}

.state-panel h4 {
    font-size: 1.4rem;
}

.state-panel p {
    font-size: 0.96rem;
    margin-bottom: 1.5rem;
}

/* ============================================================
   ICONS (Phosphor)
   ============================================================ */

[class^="ph-"], [class*=" ph-"] {
    vertical-align: -0.15em;
    line-height: 1;
}

.btn i[class^="ph-"], .btn i[class*=" ph-"],
.nav-link i[class^="ph-"], .nav-link i[class*=" ph-"] {
    margin-right: 0.4em;
    font-size: 1.05em;
}

.icon-circle {
    background: var(--color-accent-tint);
    color: var(--color-accent-strong);
    font-size: 1.2rem;
    flex-shrink: 0;
}

/* ============================================================
   ENTRY ANIMATIONS
   ============================================================ */

/* The end state is `transform: none`, not `translateY(0)`.
   They look identical, but they are not equivalent: these animations run with
   `fill-mode: both`, so whatever the last keyframe says stays applied for ever. Ending on
   translateY(0) leaves a computed `matrix(1,0,0,1,0,0)` on every direct child of
   .container — and *any* transform other than `none` makes that element the containing
   block for its position:fixed descendants, and a scroll container boundary for its
   sticky ones.

   That one line was breaking two things: the cart bar was being positioned against a div
   2,000px down the page instead of the viewport (it came to rest on top of the tab bar),
   and the sticky category strip on the menu never stuck at all. Ending on `none` removes
   the containing block once the animation finishes and fixes both at the source. */
@keyframes fade-up {
    from { opacity: 0; transform: translateY(10px); }
    to   { opacity: 1; transform: none; }
}

/* `backwards`, not `both`.
   `both` keeps the animation filling for ever after it ends, and a filling transform
   animation is still an *animated* transform — Chrome resolves even `transform: none`
   to the identity matrix while the fill is active. Any transform other than `none`
   makes the element a containing block for its position:fixed descendants and a
   boundary for its sticky ones, so every direct child of .container was silently
   trapping whatever it contained. `backwards` applies the opening frame before the
   animation starts and then releases the property, which looks identical and leaves
   the element transform-free once it has landed. */
.container > *,
.auth-card {
    animation: fade-up 380ms cubic-bezier(0.16, 1, 0.3, 1) backwards;
}

.row.g-3 > .col,
.row.row-cols-1 > .col,
.row.row-cols-md-2 > .col,
.row.row-cols-md-3 > .col {
    animation: fade-up 420ms cubic-bezier(0.16, 1, 0.3, 1) backwards;
}
.row > .col:nth-child(2) { animation-delay: 40ms; }
.row > .col:nth-child(3) { animation-delay: 80ms; }
.row > .col:nth-child(4) { animation-delay: 120ms; }
.row > .col:nth-child(5) { animation-delay: 160ms; }
.row > .col:nth-child(n+6) { animation-delay: 200ms; }

/* Staggered menu cards */
.menu-card { animation: fade-up 400ms cubic-bezier(0.16, 1, 0.3, 1) backwards; }
.menu-card:nth-child(2) { animation-delay: 50ms; }
.menu-card:nth-child(3) { animation-delay: 100ms; }
.menu-card:nth-child(4) { animation-delay: 150ms; }
.menu-card:nth-child(5) { animation-delay: 200ms; }
.menu-card:nth-child(n+6) { animation-delay: 250ms; }

@media (prefers-reduced-motion: reduce) {
    .container > *, .auth-card, .row > .col, .menu-card { animation: none; }
}

/* ============================================================
   CARD HOVER LIFT
   ============================================================ */

.card.hoverable {
    cursor: pointer;
}
.card.hoverable:hover {
    transform: translateY(-3px);
    border-color: var(--color-primary-tint);
}
.card.hoverable:active {
    transform: scale(0.98);
}

/* ============================================================
   MENU ITEM PHOTOS (legacy — kept for canteen management grid)
   ============================================================ */

.menu-photo-frame {
    position: relative;
    aspect-ratio: 4 / 3;
    overflow: hidden;
    background: var(--color-surface-deep);
    border-radius: var(--radius-md) var(--radius-md) 0 0;
}

.menu-photo-frame img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    transition: transform 300ms ease;
}

.card:hover .menu-photo-frame img {
    transform: scale(1.04);
}

.menu-photo-frame.is-placeholder {
    display: flex;
    align-items: center;
    justify-content: center;
}

.menu-photo-frame.menu-photo-inline {
    width: 92px;
    aspect-ratio: 1;
    border-radius: var(--radius-md) 0 0 var(--radius-md);
    flex-shrink: 0;
}

.menu-photo-frame.is-placeholder img {
    width: 46%;
    height: 46%;
    object-fit: contain;
    opacity: 0.9;
    transition: none;
}

.menu-photo-frame .badge-unavailable {
    position: absolute;
    inset: 0;
    background: rgba(31, 27, 22, 0.55);
    display: flex;
    align-items: center;
    justify-content: center;
    color: #fff;
    font-family: var(--font-display);
    font-weight: 700;
    font-size: 0.85rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

/* ============================================================
   PHOTO UPLOAD WIDGET (canteen menu item form)
   ============================================================ */

.photo-dropzone {
    position: relative;
    border: 1.5px dashed var(--color-border);
    border-radius: var(--radius-md);
    padding: 1.25rem;
    display: flex;
    align-items: center;
    gap: 1rem;
    cursor: pointer;
    transition: border-color 150ms ease, background-color 150ms ease;
    background: var(--color-field);
}
.photo-dropzone:hover, .photo-dropzone.is-dragover {
    border-color: var(--color-primary);
    background: var(--color-primary-tint);
}
.photo-dropzone input[type="file"] {
    position: absolute;
    inset: 0;
    opacity: 0;
    cursor: pointer;
}

.photo-dropzone-preview {
    width: 64px;
    height: 64px;
    border-radius: var(--radius-sm);
    overflow: hidden;
    flex-shrink: 0;
    background: var(--color-surface-deep);
    display: flex;
    align-items: center;
    justify-content: center;
}
.photo-dropzone-preview img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}
.photo-dropzone-preview img.is-placeholder-icon {
    width: 60%;
    height: 60%;
    object-fit: contain;
    opacity: 0.85;
}

.photo-dropzone-text strong {
    display: block;
    font-size: 0.9rem;
    color: var(--color-ink);
}
.photo-dropzone-text span {
    font-size: 0.8rem;
    color: var(--color-ink-muted);
}

/* ============================================================
   TOAST NOTIFICATION (added to cart, etc.)
   ============================================================ */

.toast-container {
    position: fixed;
    bottom: 90px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 1100;
    pointer-events: none;
}

@media (max-width: 767.98px) {
    .toast-container {
        bottom: calc(var(--bottom-nav-height) + var(--safe-area-bottom) + 20px);
    }
}

.toast-msg {
    background: var(--color-ink);
    color: #fff;
    padding: 12px 24px;
    border-radius: var(--radius-pill);
    font-size: 0.9rem;
    font-weight: 700;
    font-family: var(--font-display);
    white-space: nowrap;
    box-shadow: var(--shadow-md);
    display: flex;
    align-items: center;
    gap: 8px;
    animation: toast-in 320ms cubic-bezier(0.34, 1.56, 0.64, 1), toast-out 300ms ease 2.2s forwards;
    pointer-events: auto;
}

@keyframes toast-in {
    from { opacity: 0; transform: translateY(24px) scale(0.9); }
    to   { opacity: 1; transform: none scale(1); }
}

@keyframes toast-out {
    from { opacity: 1; transform: translateY(0); }
    to   { opacity: 0; transform: translateY(-12px); }
}

/* ============================================================
   PAGE SECTION HEADERS & FOOCHI WAVE DECOR
   ============================================================ */

.page-header {
    margin-bottom: 24px;
}

.page-header-decor {
    position: absolute;
    top: -20px;
    right: -20px;
    width: 220px;
    height: 140px;
    background-image: url('/img/blob-sage-1.svg');
    background-size: contain;
    background-repeat: no-repeat;
    opacity: 0.8;
    pointer-events: none;
    z-index: 0;
}

.page-header-content {
    position: relative;
    z-index: 1;
}

.page-header-label {
    font-family: var(--font-display);
    font-size: 1.05rem;
    font-weight: 700;
    color: var(--color-accent-strong);
    margin-bottom: 2px;
    letter-spacing: 0.01em;
}


@media (min-width: 768px) {
}

/* Navbar with scroll shadow */
.navbar.has-scrolled {
    box-shadow: none;
}

/* Category header section accent */
.menu-category-section h5 {
    color: var(--color-ink);
}

/* ============================================================
   05-outlet.css  (5 of 6)

   Outlet and admin operational controls: per-item daily caps, the pause switch,
   and the delete danger zones.

   Part of an ordered set. The browser concatenates these in the order they
   are linked in fragments/head.html, and that order IS the cascade. Do not
   reorder the <link> tags, and do not move a rule between files without
   checking what it would then stop overriding.
   ============================================================ */

/* ============================================================
   OPERATIONAL CONTROLS — daily caps, outlet pause, danger zones.

   These belong to the outlet and admin portals, which share this
   stylesheet with the student app but deliberately keep the denser,
   flatter treatment (see the --color-surface note at the top). The
   student-facing piece here is .stock-note, which is why that one
   alone is built as a soft pill rather than a table-ish row.
   ============================================================ */

/* --- Per-item daily cap, on the outlet's own menu screen --- */
.daily-limit-row {
    margin-top: 0.75rem;
}

.daily-limit-bar {
    height: 6px;
    border-radius: 0;
    background: var(--color-field);
    overflow: hidden;
}

.daily-limit-bar > span {
    display: block;
    height: 100%;
    border-radius: 0;
    background: var(--color-primary);
    transition: width 200ms ease;
}

.daily-limit-text {
    display: block;
    margin-top: 0.35rem;
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--color-ink-muted);
}

.daily-limit-text.is-low { color: var(--color-warning); }
.daily-limit-text.is-out { color: var(--color-danger); }

/* --- "Taking orders / paused" switch on the queue screen --- */
.outlet-switch {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
    padding: 0.85rem 1.1rem;
    margin-bottom: 1.25rem;
    border-radius: var(--radius-sm);
    background: var(--color-primary-tint);
    /* Left rule rather than a full border: the state is the point, and a coloured
       edge reads at a glance from across a kitchen without shouting on the page. */
    border-left: 4px solid var(--color-primary);
}

.outlet-switch.is-paused {
    background: var(--color-warning-tint);
    border-left-color: var(--color-warning);
}

.outlet-switch__text strong {
    display: block;
    font-family: var(--font-display);
    font-size: 1.02rem;
    color: var(--color-ink);
}

.outlet-switch__text span {
    font-size: 0.85rem;
    color: var(--color-ink-muted);
}

/* --- Disclosure panels: cancel-with-reason, and outlet deletion --- */
.cancel-panel,
.danger-zone {
    margin-top: 0.5rem;
}

.cancel-panel > summary,
.danger-zone > summary {
    cursor: pointer;
    list-style: none;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
    padding: 0.4rem 0.7rem;
    border-radius: var(--radius-sm);
    border: 1px solid var(--color-danger);
    color: var(--color-danger);
    font-size: 0.85rem;
    font-weight: 600;
    background: transparent;
    transition: background-color 150ms ease;
}

/* Safari still paints its own disclosure triangle without this. */
.cancel-panel > summary::-webkit-details-marker,
.danger-zone > summary::-webkit-details-marker { display: none; }

.cancel-panel > summary:hover,
.danger-zone > summary:hover { background: var(--color-danger-tint); }

.cancel-panel[open] > summary,
.danger-zone[open] > summary { margin-bottom: 0.6rem; }

.cancel-panel form,
.danger-zone form {
    padding: 0.75rem;
    border-radius: var(--radius-sm);
    background: var(--color-danger-tint);
}

.danger-zone__warning {
    font-size: 0.82rem;
    line-height: 1.5;
    color: var(--color-ink-muted);
    margin-bottom: 0.6rem;
}

/* --- One canteen's row in the admin's tenant screen --- */
.outlet-admin-card {
    padding: 0.9rem;
    margin-bottom: 0.85rem;
    border-radius: var(--radius-sm);
    border: 1px solid var(--color-border);
    background: var(--color-surface);
}

.outlet-admin-card__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    margin-bottom: 0.6rem;
}

.outlet-admin-card__name {
    font-family: var(--font-display);
    font-weight: 700;
    color: var(--color-ink);
}

/* --- Student-facing stock notes on a menu card --- */


/* --- Why an order in the history list was cancelled --- */
.order-card-reason {
    display: flex;
    align-items: center;
    gap: 0.35rem;
    margin-top: 0.5rem;
}