/* YOKU ONE's look on every page (2026-09-30, Amm). Loads after style.css -> tokens.css -> harbor.css.
   Two parts:
   1. The app shell - ported from YOKU ONE's static/yoku-one.css (sidebar, brand band + wave, nav,
      top bar, icon buttons) and static/admin.css (the user chip). Markup: templates/base.html.
      The page's own .page-header becomes the white top bar; nothing per page changes.
   2. The components every page shares (cards, buttons, status pills with a dot, tables, tabs,
      tiles, fields, alerts) in YOKU ONE's values, so a page looks the same inside or outside
      the shell. Page-specific styling stays in each page. */

/* Names YOKU ONE's rules use that this app's tokens/harbor do not define. */
:root {
  --neutral-bg: var(--surface-sunken);
  --success-bg: var(--success-tint);
  --info-bg: var(--accent-100);
  --shadow-sm: var(--shadow-card);
  --ring: var(--focus);
  --accent-bright: var(--aqua-400);   /* the wordmark's second half: One Platform aqua-400, not accent-500 (2026-10-05) */
  --sidebar-w: 244px;
  --topbar-action-size: 2.35rem;
  /* One Platform's chart colours (design system v48, 2026-10-05; the Purchasing Webapp uses the
     same): light, about 2:1 on the card in both themes, so every bar prints its numbers and names
     its series. Identity, not state; fixed order, never cycled. tokens.css (YOKU ONE's copy) does
     not carry them yet. */
  --success-solid: #15803d; --on-success-solid: #ffffff;   /* One Platform: the Approve button, both themes */
  --track: #e8edf5;
  --cat-1: #85baff; --cat-2: #ffa06d; --cat-3: #00cebb; --cat-4: #c8a8ff; --cat-5: #ff97c6; --cat-6: #6ece00;
}
[data-theme="dark"] {
  --track: #1b2942;
  --cat-1: #154b92; --cat-2: #7d3812; --cat-3: #0c5650; --cat-4: #5b1dc8; --cat-5: #93154e; --cat-6: #33550c;
}

/* ═══ 1. The shell ═══════════════════════════════════════════════════════ */
.shell {
  display: grid;
  grid-template-columns: var(--sidebar-w) minmax(0, 1fr);
  min-height: 100vh;
  background: var(--bg);
}

.app-sidebar {
  position: sticky;
  top: 0;
  align-self: start;
  height: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--card);
  border-right: 1px solid var(--border);
  z-index: 25;
}

.app-brand {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.65rem;
  padding: 1.1rem 1.05rem 2rem;
  --wave-fill: var(--card);
  --wave-height: 36px;
  background:
    radial-gradient(115% 130% at 100% 0%, color-mix(in srgb, var(--on-brand-deep) 42%, transparent) 0%, transparent 60%),
    var(--brand-deep);
  color: var(--on-brand-deep);
  text-decoration: none;
}
.app-brand:hover { color: var(--on-brand-deep); }
.shell-logo { flex: 0 0 auto; width: 36px; height: 36px; border-radius: var(--radius-sm); background: #fff; padding: 3px; object-fit: contain; }

.yo-wordmark { display: inline-flex; align-items: baseline; gap: 0.32em; font-family: var(--font-mitr); font-size: 1.02rem;
  font-weight: 800; letter-spacing: 0.08em; line-height: 1.1; white-space: nowrap; }
.yo-word-yoku { color: var(--on-brand-deep); }
.yo-word-one { color: var(--accent-bright); }
.yo-brand-tag { display: block; font-size: 0.72rem; font-weight: 600; color: color-mix(in srgb, var(--on-brand-deep) 78%, transparent); margin-top: 0.1rem; }

.app-nav {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  padding: 0.85rem 0.65rem 1rem;
  overflow-y: auto;
}

.app-nav-item {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.62rem 0.7rem;
  border-radius: var(--radius-sm);
  color: var(--text);
  text-decoration: none;
  font-size: 0.9rem;
  line-height: 1.35;
}
.app-nav-item:hover { background: var(--neutral-bg); color: var(--text); }
.app-nav-icon { display: inline-flex; color: var(--muted); flex: 0 0 auto; }
.app-nav-item.is-active { background: var(--primary-light); color: var(--brand-text); font-weight: 600; }
.app-nav-item.is-active .app-nav-icon { color: var(--brand-text); }
.app-nav-label { min-width: 0; }
.app-nav-badge-slot { margin-left: auto; display: inline-flex; }
.app-nav-badge {
  min-width: 1.5rem;
  padding: 0.05rem 0.45rem;
  border-radius: 999px;
  background: var(--primary);
  color: var(--on-primary);
  font-size: 0.7rem;
  font-weight: 700;
  line-height: 1.5;
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.app-nav-badge.is-danger { background: var(--danger-solid); color: var(--on-danger-solid); }

/* A group heading folds its items (a button, not <details>: /cases keeps no <details>). */
.app-nav-group {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  width: 100%;
  margin: 0.55rem 0 0.1rem;
  padding: 0.35rem 0.7rem;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--muted);
  font: inherit;
  font-size: 0.75rem;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
  transition: color 120ms ease, background-color 120ms ease;
}
.app-nav-group > span:first-child { flex: 1; }
.app-nav-group:hover { color: var(--text); background: var(--neutral-bg); }
.app-nav-sum:empty { display: none; }
.app-nav-sum { font-size: 0.7rem; font-weight: 700; color: var(--brand-text); }
.app-nav-group[aria-expanded="true"] .app-nav-sum { display: none; }   /* its items show their own */
.app-nav-chevron { display: inline-flex; transition: transform 160ms ease; }
.app-nav-group[aria-expanded="true"] .app-nav-chevron { transform: rotate(90deg); }
.app-nav-fold-items { display: flex; flex-direction: column; gap: 0.15rem; }
.app-nav-fold-items[hidden] { display: none; }

/* ── The top bar: the page's own .page-header, turned white ── */
.app-main { position: relative; min-width: 0; display: flex; flex-direction: column; }
.app-main > .page { max-width: none; width: 100%; margin: 0; min-height: 0; flex: 1; background: transparent; }
.app-main > .page > .page-header {
  position: sticky;
  top: 0;
  z-index: 20;
  padding: 0.95rem 1.6rem;
  background: color-mix(in srgb, var(--card) 94%, transparent);
  backdrop-filter: blur(8px);
  color: var(--text);
  border-bottom: 1px solid var(--border);
}
.app-main > .page > .page-header::after { display: none; }        /* the wave belongs to the sidebar */
.app-main > .page > .page-header .page-header-row { max-width: none; margin: 0; align-items: center; gap: 1rem; flex-wrap: wrap; }
.app-main > .page > .page-header h1 {
  min-width: 0;
  flex: 1 1 16rem;
  font-family: var(--font-mitr);
  font-size: 1.26rem;
  font-weight: 600;   /* Prompt everywhere: a title stands apart by weight (YOKU ONE, 2026-10-01) */
  line-height: 1.3;
  color: var(--text);
}
.app-main > .page > .page-header .subtitle { max-width: none; margin: 0.12rem 0 0; font-size: 0.85rem; color: var(--muted); opacity: 1; }
.app-main > .page > .page-header .header-logo,
.app-main > .page > .page-header .header-logout { display: none; }   /* the sidebar has the brand, the bar has ออกจากระบบ */
.app-main > .page > .page-header .header-back {
  display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto;
  width: var(--topbar-action-size); height: var(--topbar-action-size); margin-right: 0.35rem;
  border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--card);
  color: var(--muted); font-size: 1rem; text-decoration: none;
}
.app-main > .page > .page-header .header-back:hover { color: var(--brand-text); border-color: var(--primary); background: var(--primary-light); }
.app-main > .page > .page-header .header-actions { gap: 0.55rem; flex-wrap: wrap; }
.app-main > .page > .page-header .header-link,
.app-main > .page > .page-header .header-date,
.app-topbar-actions .topbar-app-link {
  display: inline-flex; align-items: center; gap: 0.35rem; min-height: var(--topbar-action-size);
  padding: 0.3rem 0.8rem; border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--card); color: var(--muted); font-size: 0.82rem; font-weight: 600; text-decoration: none; opacity: 1;
}
.app-main > .page > .page-header .header-link, .app-topbar-actions .topbar-app-link { color: var(--brand-text); }
.app-main > .page > .page-header .header-link:hover, .app-topbar-actions .topbar-app-link:hover { border-color: var(--primary); background: var(--primary-light); }
.app-main > .page > .page-header :focus-visible { outline-color: var(--focus); }

.app-topbar-actions { display: flex; align-items: center; gap: 0.55rem; margin-left: auto; }
.app-menu-foot { display: none; }   /* phone only: the open menu's foot (below) */
.app-topbar-actions:not(.is-in-row) { justify-content: flex-end; padding: 0.95rem 1.6rem; border-bottom: 1px solid var(--border); background: var(--card); }
.app-icon-btn {
  width: var(--topbar-action-size);
  height: var(--topbar-action-size);
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-sm);
  background: var(--card);
  border: 1px solid var(--border);
  color: var(--muted);
  text-decoration: none;
  padding: 0;
  cursor: pointer;
}
.app-icon-btn:hover { color: var(--brand-text); border-color: var(--primary); background: var(--primary-light); }
.app-icon-btn.theme-toggle-btn { color: var(--muted); border: 1px solid var(--border); background: var(--card); }
.theme-icon-moon, .theme-icon-sun { display: inline-flex; }
[data-theme="dark"] .theme-icon-moon, :root:not([data-theme="dark"]) .theme-icon-sun { display: none; }

.ac-admin-chip {
  display: flex; align-items: center; gap: 0.55rem; background: var(--card);
  border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 0.28rem 0.9rem 0.28rem 0.3rem;
}
.ac-avatar {
  width: 32px; height: 32px; border-radius: 50%; background: var(--primary-light); color: var(--brand-text);
  display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto;
}
.ac-admin-name { display: block; font-size: 0.85rem; font-weight: 700; line-height: 1.25; max-width: clamp(80px, 28vw, 168px);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text); }
.ac-admin-role { display: block; font-size: 0.72rem; color: var(--muted); line-height: 1.25; }

/* ── The content column ── */
.app-main > .page > .page-content { max-width: 1340px; margin: 0; padding: 1.4rem 1.6rem 3rem; width: 100%; }

/* ── The rail and the phone menu (2026-10-05): YOKU ONE's shell as of origin/master 2026-10-04
   (its yoku-one.css), adapted to this app's .shell grid. One breakpoint, 980px. ── */
.app-burger, .app-rail-row { display: none; }

/* Count badges: digits centred in the pill (Prompt's line box leaves room for Thai below the
   baseline), and over 99 the full number in the sidebar, 99+ in the rail. */
@supports (text-box: trim-both cap alphabetic) {
  .app-nav-badge { display: inline-block; line-height: 1; text-box: trim-both cap alphabetic; padding: 0.33rem 0.45rem; }
}
.app-nav-badge-short { display: none; }

/* Desktop: |< at the top of the menu folds the sidebar to a 76px icon rail. Two classes on
   <html>: .nav-collapsed is the WIDTH, .nav-compact the CONTENT (labels screen-reader-only, each
   link has a title for the hover name; badges on the icon's corner; a group heading becomes a
   thin divider with its chevron). The icons sit at the same x in both states. */
@media (min-width: 981px) {
  .app-rail-row { display: flex; justify-content: flex-end; padding: 0.5rem 0.65rem 0; }
  .app-rail-row + .app-nav { padding-top: 0.35rem; }
  html.nav-collapsed .app-rail-toggle { transform: translateX(calc(0.65rem - 21px)); }
  .app-rail-toggle {
    display: inline-grid; place-items: center; width: 34px; height: 34px; padding: 0; border: 0;
    border-radius: var(--radius-sm); background: transparent; color: var(--ink-secondary); cursor: pointer;
    transition: color 120ms ease, background-color 120ms ease;
  }
  .app-rail-toggle:hover { background: var(--neutral-bg); color: var(--ink); }
  .app-rail-collapse, .app-rail-expand { display: inline-flex; }
  .app-rail-expand, html.nav-collapsed .app-rail-collapse { display: none; }
  html.nav-collapsed .app-rail-expand { display: inline-flex; }

  html.nav-collapsed { --sidebar-w: 76px; }
  /* 0.65rem nav padding + 0.85rem item padding = 28px: the icon sits on the rail's centre */
  .app-nav-item { position: relative; padding-left: 0.85rem; min-height: calc(0.9rem * 1.35 + 1.24rem); }

  html.nav-compact .app-brand > span { display: none; }
  html.nav-compact .app-brand { min-height: var(--brand-h, auto); }   /* the wave stays on its line */
  html.nav-compact .app-nav-label,
  html.nav-compact .app-nav-fold-label {
    position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap;
  }
  html.nav-compact .app-nav-sum { display: none; }
  html.nav-compact .app-nav-badge-full { display: none; }
  html.nav-compact .app-nav-badge-short { display: inline; }
  html.nav-compact .app-nav-badge-slot { position: absolute; top: 2px; left: 27px; margin: 0; }
  html.nav-compact .app-nav-badge { min-width: 1.15rem; margin: 0; padding: 0 0.3rem; font-size: 0.62rem; }
  @supports (text-box: trim-both cap alphabetic) {
    html.nav-compact .app-nav-badge { padding: 0.27rem 0.3rem; }
  }
  html.nav-compact .app-nav-fold-head {
    justify-content: center; margin: 0.45rem 0 0.1rem; padding: 0.4rem 0;
    border-top: 1px solid var(--border); border-radius: 0;
  }
  html.nav-compact .app-nav-chevron { margin-left: 0; }
}
@media (min-width: 981px) and (prefers-reduced-motion: no-preference) {
  html:has(.app-nav-ready) .shell { transition: grid-template-columns 200ms ease; }
  html:has(.app-nav-ready) .app-rail-toggle { transition: color 120ms ease, background-color 120ms ease, transform 200ms ease; }
  html.nav-reveal :is(.app-nav-label, .app-nav-fold-label, .app-nav-badge, .app-nav-chevron, .app-brand > span) {
    animation: nav-reveal 160ms ease both;
  }
  @keyframes nav-reveal { from { opacity: 0; } }
}

/* Phone (980px and below): the menu folds under a burger in the band's top-right corner; open,
   the whole sidebar is a full-screen menu over the page (the band on top, the same vertical
   list as the desktop sidebar, group headings and all) and the page behind stops scrolling. */
@media (max-width: 980px) {
  .shell { grid-template-columns: minmax(0, 1fr); }
  .app-sidebar { position: relative; height: auto; border-right: 0; border-bottom: 1px solid var(--border); }
  .app-brand { padding: 0.85rem 1rem 1.4rem; padding-right: calc(1rem + 34px + 0.5rem); }
  .app-nav { display: none; padding: 0.5rem 0.75rem 0.85rem; }
  html.nav-open .app-nav { display: flex; padding: 0.75rem 1rem 2.5rem; gap: 0.25rem; }
  .app-nav-item { padding: 0.7rem 0.75rem; }
  html.nav-open .app-nav-item { padding: 0.85rem 0.9rem; font-size: 1rem; }
  html.nav-open .app-sidebar {
    position: fixed; inset: 0; z-index: 60; height: 100dvh; overflow-y: auto; overscroll-behavior: contain; border-bottom: 0;
  }
  html.nav-open, html.nav-open body { overflow: hidden; }
  html { scrollbar-gutter: stable; }   /* locking the page must not widen it */
  /* the sidebar is out of flow while the menu is up; the page keeps its place */
  html.nav-open .app-main { margin-top: var(--closed-side-h, 0px); }
  html.nav-exit .app-nav { animation: none; }

  .app-burger {
    display: inline-flex; align-items: center; justify-content: center;
    position: absolute; top: 0.85rem; right: 1rem; z-index: 2; width: 34px; height: 34px; padding: 0;
    border-radius: var(--radius-sm); color: var(--on-brand-deep); cursor: pointer;
    background: color-mix(in srgb, var(--on-brand-deep) 14%, transparent);
    border: 1px solid color-mix(in srgb, var(--on-brand-deep) 22%, transparent);
  }
  .app-burger:hover { background: color-mix(in srgb, var(--on-brand-deep) 24%, transparent); }
  .app-burger:focus-visible { outline-color: var(--on-brand-deep); }
  .app-burger-open, .app-burger-close { display: inline-flex; line-height: 0; }
  .app-burger-close, html.nav-open .app-burger-open { display: none; }
  html.nav-open .app-burger-close { display: inline-flex; }

  .app-main > .page > .page-header { position: static; padding: 0.9rem 1rem; backdrop-filter: none; background: var(--card); }
  .app-main > .page > .page-content { padding: 1.1rem 1rem 2.5rem; }

  /* As YOKU ONE (2026-10-16, Amm): the top bar is just the page's title; who, ตารางงาน, sign out
     and dark mode are the foot of the open menu. The desktop top bar is untouched. */
  .app-topbar-actions { display: none; }
  /* the whole sheet scrolls, list then foot: no scroll box of its own cutting a row in half */
  html.nav-open .app-nav { padding-bottom: 1rem; flex-shrink: 0; overflow-y: visible; }
  html.nav-open .app-menu-foot {
    display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem;
    margin: 0 1rem; padding: 1rem 0 2.5rem; border-top: 1px solid var(--border);
  }
  .app-menu-foot .btn { flex: 1 1 8rem; width: auto; min-height: 44px; padding: 0.6rem 0.5rem; font-size: 0.95rem; white-space: nowrap; }
  .app-menu-foot .app-menu-who { flex: 1 1 100%; }
  .app-menu-foot .ac-admin-name { max-width: none; white-space: normal; line-height: 1.4; }   /* a long name wraps here */
}
/* The full-screen menu appears solid at once; only its list eases in. On the page it opens,
   the menu rolls up into the band while its list fades, revealing the page. */
@media (max-width: 980px) and (prefers-reduced-motion: no-preference) {
  html.nav-open .app-nav { animation: nav-list-in 160ms ease both; }
  @keyframes nav-list-in { from { opacity: 0; transform: translateY(-6px); } }
  html.nav-exit-go .app-sidebar { animation: nav-sheet-up 160ms cubic-bezier(0.4, 0, 0.2, 1) forwards; }
  html.nav-exit-go .app-nav { animation: nav-list-out 110ms ease forwards; }
  @keyframes nav-sheet-up {
    from { clip-path: inset(0 0 0 0); }
    to { clip-path: inset(0 0 calc(100% - var(--closed-side-h, 0px)) 0); }
  }
  @keyframes nav-list-out { to { opacity: 0; } }
}
@media (max-width: 560px) {
  .app-main > .page > .page-header h1 { font-size: 1.12rem; flex-basis: 100%; }
}

/* ═══ 2. Shared components, YOKU ONE's values ═══════════════════════════ */
.card { border: 1px solid var(--border); box-shadow: var(--shadow-card); border-radius: var(--radius-md); }
.section-title { font-family: var(--font-mitr); font-weight: 600; color: var(--text); }

/* buttons */
.btn { border-radius: var(--radius-sm); line-height: 1.2; font-family: inherit; }
.btn-primary { border: 1.5px solid transparent; color: var(--on-primary); }
.btn-outline { border-width: 1.5px; border-color: var(--border-strong); background: var(--card); color: var(--brand-text); }
.btn-outline:hover { background: var(--primary-light); }
.btn-sm { border-radius: var(--radius-lg); }
.btn-danger { background: var(--danger-solid); color: var(--on-danger-solid); border: 0; }
.btn-danger:hover { background: color-mix(in srgb, var(--danger-solid) 80%, black); }

/* status pills: the word, a dot, a soft tint */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  border-radius: var(--radius-sm);
  padding: 0.18rem 0.62rem;
  font-size: 0.75rem;
  font-weight: 700;
  white-space: nowrap;
  line-height: 1.5;
}
.badge::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex: 0 0 auto; }

/* tabs: outline pills, the current one tinted */
.tab { border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--card); color: var(--muted); }
.tab:hover { border-color: var(--border-strong); color: var(--text); }
.tab.is-active { background: var(--primary-light); border-color: var(--primary); color: var(--brand-text); }
.tab-count { background: var(--neutral-bg); color: var(--text); }
.tab.is-active .tab-count { background: var(--primary); color: var(--on-primary); }

/* Cards, per YOKU ONE's DESIGN.md (Cards / Containers, Elevation):
   16px radius, Card White, 1px Hairline, 1.25rem inside, the card rest shadow - and shadows never
   stack (the Two-Level rule), so a card inside a card takes the border alone. */
.card .card, .card .stat-tile, .card .stat-card { box-shadow: none; }

/* Stat cards - every number tile on every page (.stat-tile, and the older .stat-card on the
   overview): a muted label, the figure (2rem / 800), what it means, a Link Blue action line
   pinned to the bottom, and an optional 96px icon at 16% in the bottom-right corner. Labels come
   first on screen whatever the markup order (flex order), so pages keep their markup. No rails. */
.stat-tile, .stat-card {
  position: relative;
  z-index: 0;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  padding: 1.1rem 1.2rem 1rem;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
  color: var(--text);
  text-decoration: none;
  text-align: left;
  transition: border-color 0.15s ease;
}
a.stat-tile:hover, a.stat-card:hover { border-color: var(--primary); color: var(--text); }
/* Labels are the card's title, dark and set apart by weight (2026-10-05, Amm: like the dashboard,
   not YOKU ONE's muted caption) - as big as the dashboard's when the row has room for it (four
   cards or fewer), one step smaller on a busier row. */
.stat-tile-label, .stat-label { order: 0; margin: 0; font-family: var(--font-mitr); font-size: 1rem; font-weight: 600; line-height: 1.3; color: var(--text); }
:is(.stat-tiles, .stat-grid):not(:has(> :nth-child(5))) :is(.stat-tile-label, .stat-label) { font-size: 1.25rem; }
.stat-tile-count, .stat-value { order: 1; margin: 0.3rem 0 0.1rem; font-size: 2rem; font-weight: 800; line-height: 1.15;
  letter-spacing: -0.02em; font-variant-numeric: tabular-nums; color: var(--text); }
.stat-tile-sub { order: 2; font-size: 0.8rem; color: var(--muted); }
.stat-tile-split { order: 3; padding-top: 0.25rem; font-size: 0.75rem; color: var(--ink-tertiary); }
.stat-tile-action { order: 4; margin-top: auto; padding-top: 0.6rem; font-size: 0.82rem; color: var(--brand-text); }
.stat-tile-icon { position: absolute; right: -0.6rem; bottom: -0.6rem; z-index: -1; width: 96px; height: 96px;
  opacity: 0.16; color: var(--muted); pointer-events: none; }
.stat-tile-icon svg { width: 100%; height: 100%; }
.stat-tile.is-active { background: var(--primary-50); border-color: var(--primary); box-shadow: 0 0 0 1px var(--primary), var(--shadow-card); }
/* Number cards share the row's full width, however many there are (Amm 2026-09-30: "fit their
   friend's width") - one row on a desk, two across on a phone. The QC status filter tiles (ten
   small ones) keep their own wrapping grid. */
.stat-tiles:not(.qc-status-tiles), .stat-grid { display: grid; gap: 1rem; grid-template-columns: repeat(2, minmax(0, 1fr)); }
/* an odd card out on a phone takes the whole row rather than half of it */
:is(.stat-tiles:not(.qc-status-tiles), .stat-grid) > :last-child:nth-child(odd) { grid-column: 1 / -1; }
@media (min-width: 981px) {
  .stat-tiles:not(.qc-status-tiles), .stat-grid { grid-template-columns: none; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); }
  :is(.stat-tiles:not(.qc-status-tiles), .stat-grid) > :last-child:nth-child(odd) { grid-column: auto; }
}
/* The figures of one row sit on one line (2026-10-05, Amm: "it's jumpy because some texts are
   longer"): every card in a row shares the row's five lines (label, figure, sub, split, action)
   through subgrid, so a one-line label keeps the room a two-line label next to it takes. The
   action stays at the bottom. A browser without subgrid keeps the flex column above. */
@supports (grid-template-rows: subgrid) {
  :is(.stat-tiles:not(.qc-status-tiles), .stat-grid) > * { grid-row: span 5; }
  :is(.stat-tiles:not(.qc-status-tiles), .stat-grid) > :is(.stat-tile, .stat-card) {
    display: grid; grid-template-rows: subgrid; row-gap: 0; align-content: start;
  }
  :is(.stat-tiles, .stat-grid) :is(.stat-tile-label, .stat-label) { grid-row: 1; }
  :is(.stat-tiles, .stat-grid) :is(.stat-tile-count, .stat-value) { grid-row: 2; align-self: start; }
  :is(.stat-tiles, .stat-grid) .stat-tile-sub { grid-row: 3; }
  :is(.stat-tiles, .stat-grid) .stat-tile-split { grid-row: 4; }
  :is(.stat-tiles, .stat-grid) .stat-tile-action { grid-row: 5; align-self: end; }
}

/* tables: a header band, row lines, hover */
:is(.perf-table, .kpi-table, .team-table, .alias-table, .sync-table, .detail-table, .checklist-table, .report-table) {
  border-collapse: separate;
  border-spacing: 0;
  font-size: 0.865rem;
}
:is(.perf-table, .kpi-table, .team-table, .alias-table, .sync-table, .detail-table, .checklist-table, .report-table) th {
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--text);
  background: var(--neutral-bg);
  border-bottom: 1px solid var(--border);
}
:is(.perf-table, .kpi-table, .team-table, .alias-table, .sync-table, .detail-table, .checklist-table, .report-table) td { border-bottom: 1px solid var(--border); }
:is(.perf-table, .kpi-table, .team-table, .alias-table, .sync-table, .detail-table, .checklist-table, .report-table) tbody tr:hover td { background: var(--neutral-bg); }

/* filter bars and plain fields */
.filterbar input[type="search"], .filterbar input[type="text"], .filterbar input[type="date"], .filterbar select {
  border: 1.5px solid var(--border-blend);
  border-radius: var(--radius-sm);
  background: var(--card);
  color: var(--text);
}
.filterbar input:focus, .filterbar select:focus { border-color: var(--primary); }
.filter-panel { background: var(--card); border: 1px solid var(--border); border-radius: var(--radius-md); box-shadow: var(--shadow-card); padding: 0.8rem 1rem 1rem; }

/* alerts */
.alert { border-radius: var(--radius-sm); line-height: 1.55; }
.alert-success { background: var(--success-bg); color: var(--on-success); border: 1px solid color-mix(in srgb, var(--success) 45%, transparent); }
.alert-danger { border-color: color-mix(in srgb, var(--danger) 45%, transparent); }
.alert-warning { background: var(--warning-tint); color: var(--on-warning); border: 1px solid color-mix(in srgb, var(--warning) 35%, transparent); }

.empty-state { background: var(--card); border: 1px dashed var(--border-strong); border-radius: var(--radius-md); color: var(--muted); }

@media (prefers-reduced-motion: reduce) { .app-nav-chevron, .app-nav-group { transition: none; } }

/* No rails (One Platform's No-Rail rule, Amm 2026-09-30): no coloured left or top edge on any card
   or row. A case's age shows as its age text instead; a quote or a pending row is a full outline. */
.case-card, .case-card-v2 { border-left: 1px solid var(--border); }
.case-card.age-amber, .case-card.age-red, .case-card.qc-waiting-cx,
.case-card-v2.age-amber, .case-card-v2.age-red { border-left-color: var(--border); }
.case-card-v2.age-amber .case-card-v2-age { color: var(--warning); font-weight: 600; }
.case-card-v2.age-red .case-card-v2-age { color: var(--danger); font-weight: 600; }
.timeline-original-body { border: 1px solid var(--border); border-radius: var(--radius-sm); }
.timeline-item-pending { border-left: 0; outline: 1px dashed var(--primary); outline-offset: 2px; border-radius: var(--radius-sm); }

/* A popup's title row (the new-case picker and forms, 2026-09-30): plain, on the popup's own
   surface - no brand band, no logo, no ออกจากระบบ; the popup's × closes it. Right padding keeps
   the title clear of that ×. */
.dialog-head {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 1.1rem 3.6rem 1rem 1.4rem;
  border-bottom: 1px solid var(--border);
  background: var(--card);
}
.dialog-title { flex: 1; min-width: 0; margin: 0; font-family: var(--font-mitr); font-size: 1.26rem; font-weight: 600; line-height: 1.3; color: var(--text); }
.dialog-back {
  display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto;
  width: var(--topbar-action-size); height: var(--topbar-action-size);
  border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--card);
  color: var(--muted); text-decoration: none;
}
.dialog-back:hover { color: var(--brand-text); border-color: var(--primary); background: var(--primary-light); }
.dialog-logout { flex: 0 0 auto; font-size: 0.85rem; color: var(--muted); }
.dialog-logout:hover { color: var(--danger); }

/* Urgency (2026-09-30, Amm): ปกติ neutral, ด่วน amber, ด่วนมาก red - the pill, and on the case
   list the whole card takes YOKU ONE's ด่วน / ด่วนมาก row tint (a full border, no rail). The tier
   comes from templates/_urgency.html. On a tinted card its pills and inner box turn white, as in
   YOKU ONE's triage cards, so they stay readable. */
.badge.urgency-low { background: var(--neutral-bg); color: var(--muted); }
.badge.urgency-mid { background: var(--warning-tint); color: var(--on-warning); }
.badge.urgency-mid::before { background: var(--warning-dot); }
.badge.urgency-high { background: var(--danger-tint); color: var(--danger); }
.badge.urgency-high::before { background: var(--danger-dot); }
a.case-card-v2.urgency-high { background: var(--danger-tint); border-color: color-mix(in srgb, var(--danger) 40%, transparent); }
a.case-card-v2.urgency-high:hover { background: var(--danger-tint-hover); border-color: var(--danger); }
a.case-card-v2.urgency-mid { background: var(--warning-tint); border-color: color-mix(in srgb, var(--warning) 40%, transparent); }
a.case-card-v2.urgency-mid:hover { background: var(--warning-tint-hover); border-color: var(--warning); }
a.case-card-v2:is(.urgency-high, .urgency-mid) .priority-badge { background: var(--card); }

/* Prompt is wider than Inter (2026-10-01): on a phone the จาก / ถึง date pair no longer fits one
   line, so each date takes its own full-width line instead of pushing the page sideways. */
@media (max-width: 560px) {
  .filter-date-group { flex: 1 1 100%; flex-wrap: wrap; min-width: 0; }
  .filter-date-label { flex: 1 1 100%; min-width: 0; }
  .filter-date-label input { flex: 1; min-width: 0; }
}

/* A link to hand to someone, with its copy button (2026-10-05): the field takes the row, the
   button keeps its own width - never half the card. The field reads as a field (YOKU ONE's
   console control) even outside a .field wrapper. */
.copy-link { display: flex; align-items: center; gap: 0.6rem; margin: 0.6rem 0 0.5rem; }
.copy-link input {
  flex: 1;
  min-width: 0;
  padding: 0.55rem 0.8rem;
  border: 1.5px solid var(--border-blend);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 0.9rem;
}
.copy-link input:focus { outline: none; border-color: var(--primary); }
.copy-link .btn { flex: 0 0 auto; width: auto; white-space: nowrap; }


/* ── A magnifier in front of every search box (YOKU ONE, 2026-10-02). Markup:
   <span class="filter-search">{{ icon("search", 16) }}<input type="search" ...></span>. The
   left padding is !important on purpose: each page sizes its own search box with a padding
   shorthand, and the icon's room must win over all of them. ── */
.filter-search { position: relative; display: flex; align-items: stretch; flex: 1 1 240px; min-width: 0; }
.filter-search .ic { position: absolute; left: 0.7rem; top: 50%; transform: translateY(-50%); color: var(--muted); pointer-events: none; }
.filter-search > input { flex: 1; width: 100%; max-width: none; min-width: 0; padding-left: 2.25rem !important; }

/* ── The Pill Contrast Rule (YOKU ONE tokens.css, 2026-10-02): a short bold status word on its
   own tint takes the vivid --pill-* colour (2.5:1 floor); sentences keep 4.5:1. And the No
   Tint-on-Tint Rule: a pill on a tinted card is Card White. ── */
.badge.urgency-mid { color: var(--pill-warning); }
.badge.urgency-high { color: var(--pill-danger); }
/* (the Delivery checklist and report pills set theirs in their own pages) */

/* The open phone menu covers everything, the floating สคริปต์ button included (YOKU ONE puts its
   menu over its chat bubble the same way). */
html.nav-open #saved-replies { visibility: hidden; }

/* The menu scrolls when it is taller than the window, but never sideways, and shows no
   scrollbar - in the 76px rail one pushed every icon off the rail's centre, and in the wide
   sidebar it fits better hidden too (Amm, 2026-10-05). The wheel, the trackpad, a finger and the
   keyboard still scroll it. */
.app-nav { overflow-x: hidden; scrollbar-width: none; }
.app-nav::-webkit-scrollbar { display: none; }

/* ── Page transitions (2026-10-05): YOKU ONE's page arrival (DESIGN.md, Page transitions). On a
   page change only the content column moves: the old one fades out over 0.14s, the new one fades
   up 10px over 0.3s, while the sidebar and the top bar hold still. Only the page's own column is
   named - the case popup has a .page-content of its own, and two elements with one name would
   cancel the transition. Off under reduced motion (the loading bar is already a still band
   there, style.css). ── */
@media (prefers-reduced-motion: no-preference) {
  @view-transition { navigation: auto; }
  .app-main > .page > .page-content,
  body > .page > .page-content { view-transition-name: page-content; }
  /* under the open phone menu the page is hidden: never lift it into the page-change snapshot */
  html.nav-open .app-main > .page > .page-content { view-transition-name: none; }
  ::view-transition-old(root),
  ::view-transition-new(root) { animation-duration: 0.18s; }
  ::view-transition-old(page-content) { animation: page-out 0.14s ease-in both; }
  ::view-transition-new(page-content) { animation: page-in 0.3s cubic-bezier(0.2, 0.7, 0.2, 1) both; }
}
@keyframes page-out { to { opacity: 0; } }
@keyframes page-in { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }

/* Card 1 of a case: its title row, and the CS handling form behind ⋯ (2026-10-05, CS via Amm: they
   use it only to correct a mislabelled case, so it no longer takes half the row). */
.case-card-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 0.75rem; }
.case-menu { position: relative; flex: none; }
.case-menu > summary { list-style: none; cursor: pointer; display: flex; align-items: center; justify-content: center;
  width: 2.25rem; height: 2.25rem; border: 1px solid var(--border); border-radius: var(--radius-sm);
  color: var(--ink-secondary); font-size: 1.25rem; line-height: 1; background: var(--card); }
.case-menu > summary::-webkit-details-marker { display: none; }
.case-menu > summary:hover, .case-menu[open] > summary { border-color: var(--primary); background: var(--primary-100); color: var(--brand-text); }
.case-menu-body { position: absolute; right: 0; top: calc(100% + 6px); z-index: 30; width: min(24rem, calc(100vw - 3rem));
  padding: 1rem 1.1rem 1.1rem; background: var(--card); border: 1px solid var(--border); border-radius: var(--radius-md);
  box-shadow: 0 12px 32px rgba(15, 27, 45, 0.18); }
.case-menu-title { margin-bottom: 0.6rem; font-family: var(--font-mitr); font-size: 1rem; font-weight: 600; color: var(--ink); }
.case-menu-body .btn { width: 100%; }
.case-menu-body .delete-form { margin-top: 0.6rem; }
/* On a phone the panel spans the card, anchored to the card rather than to the ⋯, so it never
   runs off the popup's left edge. */
@media (max-width: 640px) {
  .form-col-main:has(> .case-card-head .case-menu) { position: relative; }
  .case-menu { position: static; }
  .case-menu-body { left: 0.6rem; right: 0.6rem; top: 4rem; width: auto; }
}

/* A Timeline status chip in the colour of who the case waits on (2026-10-05, CS via Amm), from the
   status's group (status_tone in main.py): with CS blue, the customer orange, the franchise teal,
   an outside party violet, closed green. A dot, then the word - the Pill Contrast Rule's vivid
   colour for a short bold status word on its own tint. Anything else stays neutral. */
.timeline-chip[class*="tone-"] { display: inline-flex; align-items: center; gap: 0.35rem; }
.timeline-chip[class*="tone-"]::before { content: ""; width: 0.45rem; height: 0.45rem; border-radius: 50%; background: currentColor; }
.timeline-chip.tone-open { background: var(--primary-100); color: var(--brand-text); }
.timeline-chip.tone-waiting_customer { background: var(--warning-tint); color: var(--pill-warning); }
.timeline-chip.tone-waiting_franchise { background: var(--accent-100); color: var(--pill-info); }
.timeline-chip.tone-external { background: var(--violet-tint); color: var(--on-violet); }
.timeline-chip.tone-closed { background: var(--success-tint); color: var(--pill-success); }
.timeline-chip.tone-cancelled { background: var(--surface-sunken); color: var(--ink-secondary); }   /* ยกเลิก: closed, not a success */

/* YOKU ONE's Brand Colours (its DESIGN.md, 2026-10-02): a brand's name chip in its logo colour, the
   same in both themes as a logo is - and nothing else (never a state, never a button). */
.badge.brand-yoguruto { background: var(--brand-yoguruto); color: var(--on-brand-yoguruto); }
.badge.brand-beluga { background: var(--brand-beluga); color: var(--on-brand-beluga); }
.badge.brand-nuts { background: var(--brand-nuts); color: var(--on-brand-nuts); }

/* The case popup's ✕ sits in the panel's top-right corner (.modal-close, absolute); the header
   row's date and ส่งออก PDF stop short of it instead of running under it (2026-10-06, Amm). */
.modal-panel .page-header-row { padding-right: 2.9rem; flex-wrap: wrap; align-items: center; }
.modal-panel .page-header .header-actions { flex-wrap: wrap; flex-shrink: 1; min-width: 0; }   /* on a phone they drop under the title */

/* The Unstyled Field Rule (2026-10-06, after the same bug the third time: a number box, then a tel
   box, fell through `.field input[type="text"], …` lists). Every text-like control gets One
   Platform's field look from this one :where() rule - zero specificity, so any page or component
   rule still wins; it only catches a control nothing else styled. Checked by
   Documents/YokuOne/scripts/check-unstyled-fields.mjs. */
:where(input:not([type="checkbox"], [type="radio"], [type="hidden"], [type="file"], [type="range"],
  [type="color"], [type="submit"], [type="button"], [type="reset"], [type="image"]), select, textarea) {
  font-family: inherit;   /* not the `font` shorthand: that also resets line-height */
  font-size: inherit;
  color: var(--ink);
  background: var(--surface-raised);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  padding: 0.55rem 0.8rem;
}
:where(input, select, textarea):focus-visible { outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 25%, transparent); }

/* The menu's search (2026-10-06, Amm): in the row with |< on a desk, at the top of the open menu on
   a phone; gone in the icon rail, which has no room for words. */
.app-nav-search { position: relative; display: flex; flex: 1; min-width: 0; align-items: center; }
.app-nav-search .ic { position: absolute; left: 0.6rem; color: var(--muted); pointer-events: none; }
.app-nav-search input { width: 100%; min-width: 0; padding: 0.45rem 0.6rem 0.45rem 2rem; font-size: 0.88rem;
  border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface-raised); color: var(--ink); }
.app-nav-search input:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 20%, transparent); }
.app-nav-empty { margin: 0.6rem 0.85rem; font-size: 0.85rem; color: var(--muted); }
@media (min-width: 981px) {
  .app-rail-row { justify-content: space-between; align-items: center; gap: 0.4rem; }
  html.nav-compact .app-nav-search { display: none; }
  html.nav-collapsed .app-rail-row { justify-content: flex-end; }
}
@media (max-width: 980px) {
  html.nav-open .app-rail-row { display: flex; padding: 0.75rem 1rem 0; }
  .app-rail-toggle { display: none; }
  .app-nav-search input { font-size: 1rem; padding-top: 0.6rem; padding-bottom: 0.6rem; }
}
/* the search hides with [hidden]; a menu item's own display: flex would otherwise keep it on screen */
.app-nav-item[hidden], .app-nav-fold[hidden] { display: none; }
