/* ============================================================
   Bite&Brief — design system
   Primary color #065ee3 · Font: FC Sara Samkan
   ============================================================ */

/* ---------- Fonts (served from ../fonts relative to this file) ----------
   Only the weights/styles the stylesheets actually ask for are declared; the
   unused faces (100-300, 800, and the italics no rule can reach) were dropped
   along with their .woff2 files. Italic 400/700 stay because the CKEditor
   toolbar lets authors italicise article text, which renders in --font-body. */
@font-face { font-family:'FC Sara Samkan'; src:url('../fonts/FCSaraSamkan-Regular.woff2') format('woff2');         font-weight:400; font-style:normal; font-display:swap; }
@font-face { font-family:'FC Sara Samkan'; src:url('../fonts/FCSaraSamkan-Italic.woff2') format('woff2');          font-weight:400; font-style:italic; font-display:swap; }
@font-face { font-family:'FC Sara Samkan'; src:url('../fonts/FCSaraSamkan-Medium.woff2') format('woff2');          font-weight:500; font-style:normal; font-display:swap; }
@font-face { font-family:'FC Sara Samkan'; src:url('../fonts/FCSaraSamkan-SemiBold.woff2') format('woff2');        font-weight:600; font-style:normal; font-display:swap; }
@font-face { font-family:'FC Sara Samkan'; src:url('../fonts/FCSaraSamkan-Bold.woff2') format('woff2');            font-weight:700; font-style:normal; font-display:swap; }
@font-face { font-family:'FC Sara Samkan'; src:url('../fonts/FCSaraSamkan-BoldItalic.woff2') format('woff2');      font-weight:700; font-style:italic; font-display:swap; }
/* 900 is used by the oversized "404" watermark on the not-found page only. */
@font-face { font-family:'FC Sara Samkan'; src:url('../fonts/FCSaraSamkan-Black.woff2') format('woff2');           font-weight:900; font-style:normal; font-display:swap; }

/* IBM Plex Sans Thai — public pages reach exactly one face: 700, on the hero
   title (.split-hero-title) and every section heading (.section-head h2). The
   400/500/600 faces are used only by the admin chrome, so they are declared in
   admin-theme.css instead; declaring them here made every public visit parse
   three @font-face rules it could never match. */
@font-face { font-family:'IBM Plex Sans Thai'; src:url('../fonts/IBMPlexSansThai-Bold.woff2') format('woff2');       font-weight:700; font-style:normal; font-display:swap; }

/* ---------- Design tokens ---------- */
:root{
  /* color tokens live in variables.css (loaded before this file) */

  /* typography */
  --font-heading:'FC Sara Samkan','Noto Sans Thai','Sarabun',system-ui,sans-serif;
  --font-body:'FC Sara Samkan','Noto Sans Thai','Sarabun',system-ui,sans-serif;
  --fs-h1:clamp(1.9rem,3vw + 1rem,2.5rem);
  --fs-h2:clamp(1.45rem,2vw + 0.6rem,2rem);
  --fs-h3:1.5rem;
  --fs-h4:1.25rem;
  --fs-h5:1.125rem;
  --fs-body:1rem;
  --fs-caption:0.875rem;
  --lh-body:1.8;

  /* shape */
  --radius:10px;
  --radius-lg:14px;
  --shadow-sm:0 1px 2px rgba(11,18,32,.04);
  --shadow:0 6px 24px rgba(11,18,32,.08);
  --pad-x:clamp(20px,4vw,48px);
}

/* ---------- Reset / base ---------- */
*,*::before,*::after{box-sizing:border-box;}
html{ -webkit-text-size-adjust:100%; }
body{
  margin:0;
  font-family:var(--font-body);
  font-size:var(--fs-body);
  line-height:var(--lh-body);
  color:var(--color-text-primary);
  background:var(--color-bg);
  -webkit-font-smoothing:antialiased;
}
a{color:inherit;text-decoration:none;transition:opacity .15s,color .15s;}
a:hover{opacity:.82;}
img{max-width:100%;display:block;}
h1,h2,h3,h4,h5,h6{font-family:var(--font-heading);margin:0;line-height:1.3;}
button{font-family:var(--font-body);cursor:pointer;}
::placeholder{color:var(--color-text-secondary);opacity:.75;}
:focus-visible{outline:2px solid var(--color-accent);outline-offset:2px;}

/* ---------- Theme switch transition ----------
   Applied only during a manual/system theme flip (JS toggles .theme-transition
   on <html> for ~200ms) so hover/other transitions are never affected and there
   is no perpetual repaint cost. */
html.theme-transition,
html.theme-transition *,
html.theme-transition *::before,
html.theme-transition *::after{
  transition:background-color .18s ease,border-color .18s ease,color .18s ease,fill .18s ease,box-shadow .18s ease !important;
}
@media (prefers-reduced-motion:reduce){
  html.theme-transition,
  html.theme-transition *,
  html.theme-transition *::before,
  html.theme-transition *::after{transition:none !important;}
}

/* ---------- Theme toggle button (sun / moon / system) ---------- */
.theme-toggle{width:44px;height:44px;border-radius:8px;border:1px solid var(--color-border);
  background:var(--color-bg);color:var(--color-text-primary);display:inline-flex;align-items:center;
  justify-content:center;flex-shrink:0;-webkit-tap-highlight-color:transparent;
  transition:border-color .15s,color .15s,background .15s;}
.theme-toggle:hover{border-color:var(--color-accent);color:var(--color-accent);opacity:1;}
.theme-toggle svg{width:20px;height:20px;display:none;}
html[data-theme-pref="light"] .theme-toggle .theme-icon-sun{display:block;}
html[data-theme-pref="dark"] .theme-toggle .theme-icon-moon{display:block;}
html[data-theme-pref="system"] .theme-toggle .theme-icon-system{display:block;}
html:not([data-theme-pref]) .theme-toggle .theme-icon-system{display:block;}

/* ---------- Layout helpers ---------- */
.container{max-width:1120px;margin:0 auto;padding-inline:var(--pad-x);}
.section{padding-block:clamp(32px,5vw,52px);}
.page-min{min-height:100vh;display:flex;flex-direction:column;}
.grow{flex:1;}
.muted{color:var(--color-text-secondary);}
.text-accent{color:var(--color-accent);}

/* ---------- Buttons ---------- */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;
  font-weight:600;font-size:1rem;padding:12px 24px;border-radius:8px;border:1px solid transparent;
  line-height:1.2;text-align:center;transition:background .15s,opacity .15s,border-color .15s;}
.btn:hover{opacity:1;}
.btn-primary{background:var(--btn-primary-bg);color:var(--btn-primary-text);}
.btn-primary:hover{background:var(--btn-primary-hover);}
.btn-primary:active{background:var(--btn-primary-active);}
.btn-outline{background:transparent;color:var(--color-text-primary);border-color:var(--color-border);}
.btn-outline:hover{border-color:var(--color-accent);color:var(--color-accent);}
.btn-danger{background:var(--btn-danger-bg);color:var(--btn-danger-text);}
.btn-block{width:100%;}
.btn-sm{padding:9px 16px;font-size:.875rem;}

/* ---------- Skip link ---------- */
/* First tab stop on every page. Parked off-screen (not display:none, which would
   drop it from the tab order) and slides in on focus. z-index clears the sticky
   header stack (100) so it is never rendered behind the bar it skips over.
   The target <main> takes tabindex="-1" and hides the ring it would draw. */
.skip-link{position:fixed;top:0;left:50%;transform:translate(-50%,-120%);z-index:1100;
  padding:12px 22px;border-bottom-left-radius:var(--radius);border-bottom-right-radius:var(--radius);
  background:var(--color-accent);color:#fff;font-weight:600;text-decoration:none;
  box-shadow:var(--shadow);transition:transform .16s ease-out;}
.skip-link:focus-visible{transform:translate(-50%,0);outline:2px solid #fff;outline-offset:-4px;}
main:focus{outline:none;}
@media (prefers-reduced-motion:reduce){
  .skip-link{transition:none;}
}

/* ---------- Site header / nav ---------- */
/* The header and the mobile menu stick together. Sticky lives on the wrapper —
   not on .site-header — so the menu, which hangs below it, travels with it and
   opens in view no matter how far the page is scrolled. */
.header-stack{position:sticky;top:0;z-index:100;}
/* Frosted-glass bar. The declaration below is the opaque fallback (browsers
   without backdrop-filter would otherwise show page content straight through
   a 66%-alpha bar); the @supports block upgrades it to real glass. */
.site-header{display:flex;align-items:center;justify-content:space-between;gap:16px;
  padding:16px var(--pad-x);border-bottom:1px solid var(--glass-border);position:relative;z-index:1;
  background:var(--glass-bg-solid);box-shadow:var(--glass-shadow),var(--glass-highlight);}
.brand{display:flex;align-items:center;gap:10px;min-width:0;}
.brand-name{font-family:var(--font-heading);font-weight:700;font-size:1.375rem;color:var(--color-text-primary);letter-spacing:-.02em;white-space:nowrap;}
/* The logo is height-constrained, never width-constrained, so a wide wordmark
   and a square mark both sit correctly on the same baseline. Both theme
   variants are in the markup and CSS reveals one — an <img> cannot inherit the
   page's colours, and swapping in JS would flash the wrong logo on first paint. */
.brand-logo{display:block;height:34px;width:auto;max-width:min(240px,42vw);object-fit:contain;}
/* Built-in wordmark: one alpha mask tinted per theme, so the artwork is fetched
   once instead of once per theme variant. aspect-ratio stands in for the
   intrinsic size an <img> would have carried, which also pins the header height
   before the mask arrives. The -webkit- pair is what old Safari understands.
   Like the fonts, this URL carries no ?v= and .htaccess caches it immutably for
   a year, so a new logo ships as a new filename — never as an edit in place. */
.brand-logo--mask{
  aspect-ratio:560/256;
  background-color:var(--brand-logo-ink);
  -webkit-mask:url('../img/logo-mask.png') no-repeat left center/contain;
          mask:url('../img/logo-mask.png') no-repeat left center/contain;
}
.brand-logo--dark{display:none;}
[data-theme="dark"] .brand-logo--light{display:none;}
[data-theme="dark"] .brand-logo--dark{display:block;}
.brand-tag{font-size:.8125rem;color:var(--color-text-secondary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.nav{position:relative;display:flex;gap:4px;align-items:center;background:var(--color-bg-alt);
  padding:5px;border-radius:999px;border:1px solid var(--color-border);}
.nav a{position:relative;z-index:1;color:var(--color-text-secondary);font-weight:500;font-size:.9375rem;
  padding:8px 18px;border-radius:999px;line-height:1;white-space:nowrap;
  transition:color .25s ease;}
.nav a:hover{color:var(--color-text-primary);}
.nav a.active{background:var(--btn-primary-bg);color:var(--btn-primary-text);font-weight:600;}
.nav a.active:hover{color:var(--btn-primary-text);}
/* JS-driven sliding pill: once active, the moving pill supplies the background */
.nav.has-pill a.active{background:transparent;}
.nav-pill{position:absolute;left:0;top:0;height:0;background:var(--btn-primary-bg);border-radius:999px;
  z-index:0;pointer-events:none;opacity:0;
  transition:transform .3s cubic-bezier(.4,0,.2,1),width .3s cubic-bezier(.4,0,.2,1);}
.nav-pill.ready{opacity:1;}
/* Right cluster: the compact search sits in one row with the header actions.
   Natural width + the header's space-between keeps the original brand|nav|right
   balance (no forced centring, no gaps). */
.header-right{display:flex;align-items:center;gap:12px;flex-shrink:0;}
.header-actions{display:flex;gap:10px;align-items:center;flex-shrink:0;}
.header-actions .link-login{color:var(--color-text-primary);font-weight:600;font-size:.9375rem;}
.menu-toggle{width:44px;height:44px;border-radius:8px;border:1px solid var(--color-border);background:var(--color-bg);
  display:none;align-items:center;justify-content:center;flex-shrink:0;color:var(--color-text-primary);
  -webkit-tap-highlight-color:transparent;}
/* Hamburger → X. Three centred bars animate on transform/opacity only (never
   width/top/left) so the morph stays GPU-smooth and never jitters. */
.hamburger{position:relative;width:20px;height:16px;}
.menu-toggle .bar{
  position:absolute;left:0;width:20px;height:2px;border-radius:2px;
  background:currentColor;transform-origin:center;
  /* Slight overshoot easing gives a gentle, non-cartoonish bounce as it snaps to X. */
  transition:transform .34s cubic-bezier(.34,1.28,.52,1),opacity .2s ease;
}
.menu-toggle .bar-top{top:3px;}
.menu-toggle .bar-mid{top:50%;margin-top:-1px;}
.menu-toggle .bar-bot{bottom:3px;}
/* Open state: top/bottom bars rotate inward to form the X, middle fades + shrinks. */
.menu-toggle[aria-expanded="true"] .bar-top{transform:translateY(4px) rotate(45deg);}
.menu-toggle[aria-expanded="true"] .bar-mid{opacity:0;transform:scaleX(.2);transition:transform .2s cubic-bezier(.4,0,.2,1),opacity .16s ease;}
.menu-toggle[aria-expanded="true"] .bar-bot{transform:translateY(-4px) rotate(-45deg);}
/* Never display:none, so it can animate: it collapses via max-height + fade +
   slide, and expands smoothly on .open. visibility flips after the collapse so
   closed links stay unfocusable.
   Hung off .header-stack as an overlay rather than sitting in flow: in flow it
   would grow the sticky wrapper until the pinned header ate the whole viewport,
   and it would shove the page content down every time it opened. */
.mobile-menu{position:absolute;top:100%;left:0;right:0;
  display:flex;flex-direction:column;gap:4px;background:var(--color-bg);
  border-bottom:1px solid transparent;padding:0 var(--pad-x);overflow:hidden;
  max-height:0;opacity:0;transform:translateY(-8px);visibility:hidden;
  transition:max-height .34s cubic-bezier(.4,0,.2,1),padding .34s cubic-bezier(.4,0,.2,1),
    opacity .26s ease,transform .34s cubic-bezier(.4,0,.2,1),
    border-color .34s ease,visibility 0s linear .34s;}
.mobile-menu.open{max-height:560px;opacity:1;transform:translateY(0);visibility:visible;
  border-bottom-color:var(--color-border);padding:12px var(--pad-x) 20px;
  transition:max-height .4s cubic-bezier(.4,0,.2,1),padding .4s cubic-bezier(.4,0,.2,1),
    opacity .3s ease,transform .4s cubic-bezier(.4,0,.2,1),border-color .3s ease,visibility 0s;}
.mobile-menu a{padding:12px 4px;color:var(--color-text-secondary);font-weight:500;font-size:1rem;}
.mobile-menu a.active{color:var(--color-text-primary);font-weight:600;}
/* Button links (e.g. สมัครสมาชิก) keep their own text colour — the .mobile-menu a
   rule above is more specific than .btn-primary and would otherwise override it. */
.mobile-menu a.btn-primary{color:var(--btn-primary-text);}
.mobile-menu .divider{height:1px;background:var(--color-border);margin:8px 0;}
.mobile-theme-row{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:8px 4px;
  color:var(--color-text-secondary);font-weight:500;font-size:1rem;}
/* Each row fades/slides in; a small per-item delay (only while opening) gives a
   gentle cascade. Closing snaps them out together so it feels quick. */
.mobile-menu > *{opacity:0;transform:translateY(-6px);
  transition:opacity .22s ease,transform .22s cubic-bezier(.4,0,.2,1);}
.mobile-menu.open > *{opacity:1;transform:translateY(0);}
.mobile-menu.open > *:nth-child(1){transition-delay:.04s;}
.mobile-menu.open > *:nth-child(2){transition-delay:.08s;}
.mobile-menu.open > *:nth-child(3){transition-delay:.12s;}
.mobile-menu.open > *:nth-child(4){transition-delay:.16s;}
.mobile-menu.open > *:nth-child(5){transition-delay:.20s;}
.mobile-menu.open > *:nth-child(6){transition-delay:.24s;}
.mobile-menu.open > *:nth-child(7){transition-delay:.28s;}

/* ---------- Glass treatment for the header + mobile menu ----------
   Controls sitting on the glass bar drop their opaque --color-bg / --color-bg-alt
   fills for a translucent tint, otherwise they read as solid blocks punched into
   the frosted panel. Scoped to the header/menu so the same components keep their
   solid look in the admin and auth layouts. */
.site-header .nav,
.site-header .header-search__form,
.site-header .theme-toggle,
.site-header .menu-toggle,
.site-header .header-search__toggle,
.mobile-menu .theme-toggle{
  background:var(--glass-surface);
  border-color:var(--glass-border);
}
.site-header .theme-toggle:hover,
.site-header .header-search__toggle:hover,
.mobile-menu .theme-toggle:hover{background:var(--glass-surface-hover);border-color:var(--color-accent);}
.site-header .menu-toggle:hover{background:var(--glass-surface-hover);}

.mobile-menu{background:var(--glass-bg-solid);}
.mobile-menu.open{border-bottom-color:var(--glass-border);}

@supports ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .site-header,.mobile-menu{
    background:var(--glass-bg);
    -webkit-backdrop-filter:var(--glass-blur);
    backdrop-filter:var(--glass-blur);
  }
}

/* Nav collapse point. The inline nav + actions need ~1080px before they start
   crowding the brand, so the hamburger takes over from there down. Kept in sync
   with the .header-right / .header-search rules in the Responsive section. */
@media(max-width:1080px){
  .nav,.header-actions{display:none;}
  .menu-toggle{display:flex;}
}

/* ---------- Breadcrumb ---------- */
.breadcrumb{padding:16px var(--pad-x) 0;color:var(--color-text-secondary);font-size:.875rem;}
.breadcrumb a{color:var(--color-text-secondary);}
.breadcrumb a:hover{color:var(--color-accent);}

/* ---------- Hero ---------- */
.hero{padding:clamp(40px,7vw,72px) var(--pad-x) clamp(40px,6vw,56px);
  background:var(--color-bg-alt);border-bottom:1px solid var(--color-border);}
.hero-inner{max-width:660px;}
.hero h1{font-size:var(--fs-h1);font-weight:700;margin:0 0 16px;line-height:1.3;}
.hero p{font-size:1.0625rem;color:var(--color-text-secondary);line-height:1.75;margin:0 0 28px;}
.hero-cta{display:flex;gap:12px;flex-wrap:wrap;}

/* ---------- Split-Screen Hero Banner ---------- */
.split-hero{
  display:grid;
  grid-template-columns:1fr 1fr;
  align-items:stretch;
  min-height:clamp(420px,52vw,600px);
  border-bottom:1px solid var(--color-border);
  overflow:hidden;
}
/* Text panel */
.split-hero-text{
  display:flex;
  align-items:center;
  padding:clamp(32px,5vw,72px) var(--pad-x);
}
.split-hero-copy{max-width:560px;margin-inline:auto;width:100%;}
/* Keyed off the class, not the tag: only the first slide's headline is an <h1>
   (the rest are <p> so the page keeps a single top-level heading — see
   partials/_banners.php). Two classes also outrank ".split-hero-copy p" below,
   so a non-first headline never picks up the subtitle's type styles. */
.split-hero-copy .split-hero-title{
  font-family:'IBM Plex Sans Thai','Noto Sans Thai','Sarabun',system-ui,sans-serif;
  font-size:clamp(1.9rem,2.2vw + 1.2rem,3rem);
  /* 700 is the heaviest IBM Plex Sans Thai face we ship; asking for 800 only
     invited a synthesised bold that differs between browsers. */
  font-weight:700;line-height:1.25;margin:0 0 16px;letter-spacing:-.01em;
}
.split-hero-copy p{
  font-size:clamp(1rem,.4vw + .95rem,1.1875rem);
  line-height:1.75;margin:0 0 28px;
}
.split-hero-cta{display:flex;gap:12px;flex-wrap:wrap;}
/* Media panel */
.split-hero-media{
  background-position:center;background-repeat:no-repeat;
  position:relative;min-height:260px;
}
/* Image fit: whole image (no crop) vs fill area (crop overflow) */
.split-hero-media.fit-contain{background-size:contain;}
.split-hero-media.fit-cover{background-size:cover;}
.split-hero-placeholder{
  position:absolute;inset:0;
  background:var(--brand-gradient);
}
/* Text-tone variants */
.split-hero.theme-dark .split-hero-copy .split-hero-title{color:var(--color-text-primary);}
.split-hero.theme-dark .split-hero-copy p{color:var(--color-text-secondary);}
/* Full-bleed banners overlay a FIXED light scrim, so a "theme-dark" (dark-text)
   banner must keep dark text in either app theme — otherwise the theme-flipping
   token would put light text on the light scrim. Split banners keep the token
   (their text sits on the page surface, which flips with the app theme). */
.split-hero.layout-full.theme-dark .split-hero-copy .split-hero-title{color:#0f172a;}
.split-hero.layout-full.theme-dark .split-hero-copy p{color:#475569;}
.split-hero.theme-light .split-hero-copy .split-hero-title{color:#fff;}
.split-hero.theme-light .split-hero-copy p{color:rgba(255,255,255,.86);}
.split-hero.theme-light .btn-outline{color:#fff;border-color:rgba(255,255,255,.5);}
.split-hero.theme-light .btn-outline:hover{border-color:#fff;color:#fff;background:rgba(255,255,255,.12);}
/* Image on the left → media becomes the first column */
.split-hero.layout-split.image-left .split-hero-media{order:-1;}

/* Full-image layout: image fills the banner, text overlaid on top.
   The media keeps the uploaded image's own aspect ratio (via --banner-ratio),
   so the banner shrinks/grows together with the image on every screen size.
   Text content can still push it taller when needed (short/narrow screens). */
.split-hero.layout-full{grid-template-columns:1fr;min-height:0;}
.split-hero.layout-full .split-hero-media{
  display:flex;align-items:center;
  aspect-ratio:var(--banner-ratio,64/21);
  background-size:cover;               /* container ≈ image ratio → whole image shows */
  max-height:82vh;
}
.split-hero.layout-full.theme-light .split-hero-media{background-color:#0B1220;}
.split-hero.layout-full.theme-dark  .split-hero-media{background-color:#F4F7FB;}
.split-hero-scrim{position:absolute;inset:0;z-index:1;}
.split-hero.layout-full.theme-light .split-hero-scrim{
  background:linear-gradient(90deg,rgba(4,10,25,.74),rgba(4,10,25,.38) 55%,rgba(4,10,25,.12));
}
.split-hero.layout-full.theme-dark .split-hero-scrim{
  background:linear-gradient(90deg,rgba(255,255,255,.9),rgba(255,255,255,.55) 55%,rgba(255,255,255,.18));
}
.split-hero.layout-full .split-hero-text{
  position:relative;z-index:2;width:100%;max-width:1120px;margin-inline:auto;
  background:transparent;padding-block:clamp(24px,3.5vw,64px);
}
.split-hero.layout-full .split-hero-copy{margin-inline:0;}
/* Full layout overlays a short, wide banner → far less vertical room than the
   split layout, so the heading needs to scale down harder across mid widths.
   More specific than the base + breakpoint h1 rules, so it wins everywhere. */
.split-hero.layout-full .split-hero-copy .split-hero-title{
  font-size:clamp(1.35rem,.55rem + 1.85vw,2.2rem);
  line-height:1.2;margin:0 0 12px;
}
.split-hero.layout-full .split-hero-copy p{margin:0 0 20px;}

/* Tablet: keep two columns but shorter (split only — full follows its image ratio) */
@media(max-width:900px){
  .split-hero.layout-split{min-height:clamp(360px,48vw,460px);}
  .split-hero-copy .split-hero-title{font-size:clamp(1.7rem,3.5vw,2.2rem);}
}
/* Mobile: stack — image on top, text below (regardless of side) */
@media(max-width:680px){
  .split-hero{grid-template-columns:1fr;min-height:0;}
  .split-hero.layout-split .split-hero-media{min-height:220px;order:-1;}
  .split-hero.layout-full{min-height:0;}
  /* On phones a wide banner image would be too short for the overlay text,
     so fall back to a comfortable min-height with a centred crop. */
  .split-hero.layout-full .split-hero-media{aspect-ratio:auto;min-height:clamp(360px,82vw,460px);}
  .split-hero-text{padding:32px var(--pad-x);}
  .split-hero-copy{max-width:none;}
  .split-hero-cta .btn{flex:1 1 auto;text-align:center;}
}

/* ---------- Hero slider (carousel) ---------- */
.hero-slider{position:relative;overflow:hidden;border-bottom:1px solid var(--color-border);
  transition:height .5s cubic-bezier(.4,0,.2,1);}
.hero-slider .split-hero{border-bottom:none;}
/* flex-start keeps each slide at its own natural height; JS sizes the slider
   to the active slide so different-shaped banners don't stretch to match. */
.hero-slider-track{display:flex;align-items:flex-start;
  transition:transform .55s cubic-bezier(.4,0,.2,1);will-change:transform;}
.hero-slider-track > .split-hero{flex:0 0 100%;min-width:0;}
.hero-slider.is-single .hero-slider-track{transition:none;}
/* Arrows */
.hero-slider-arrow{
  position:absolute;top:50%;transform:translateY(-50%);z-index:5;
  width:46px;height:46px;border-radius:50%;border:none;
  background:rgba(255,255,255,.9);color:#0B1220;font-size:1.6rem;line-height:1;
  display:flex;align-items:center;justify-content:center;cursor:pointer;
  box-shadow:0 2px 12px rgba(11,18,32,.18);transition:background .15s,opacity .15s;
}
.hero-slider-arrow:hover{background:#fff;opacity:1;}
.hero-slider-arrow.prev{left:16px;}
.hero-slider-arrow.next{right:16px;}
/* Dots */
.hero-slider-dots{position:absolute;left:0;right:0;bottom:18px;z-index:5;
  display:flex;gap:8px;justify-content:center;padding:0 16px;}
.hero-slider-dot{width:9px;height:9px;padding:0;border:none;border-radius:50%;cursor:pointer;
  background:rgba(255,255,255,.75);box-shadow:0 0 0 1.5px rgba(11,18,32,.35);
  transition:background .2s,width .2s,box-shadow .2s;}
.hero-slider-dot.active{width:24px;border-radius:5px;background:var(--color-accent);
  box-shadow:0 0 0 1.5px rgba(255,255,255,.8);}
@media(max-width:680px){
  .hero-slider-arrow{width:38px;height:38px;font-size:1.3rem;}
  .hero-slider-arrow.prev{left:8px;}
  .hero-slider-arrow.next{right:8px;}
  .hero-slider-dots{bottom:12px;}
}

/* ---------- Admin drag-to-reorder ---------- */
.reorder-row{transition:background .12s,box-shadow .12s,transform .18s ease;position:relative;}
/* The row being dragged: lift it off the list with a shadow + subtle scale so
   it's clearly "picked up", and let neighbours slide underneath it. */
.reorder-row.dragging{opacity:.9;background:var(--color-bg);z-index:5;
  box-shadow:0 10px 28px rgba(11,18,32,.16);transform:scale(1.015);
  border-radius:10px;cursor:grabbing;}
.reorder-row.drop-target{box-shadow:inset 0 2px 0 var(--color-accent);}
/* Brief highlight flash on the row once it settles into its new place. */
.reorder-row.just-moved{animation:reorder-settle .55s ease;}
@keyframes reorder-settle{
  0%{background:var(--color-accent-soft);}
  100%{background:transparent;}
}
.drag-handle{display:inline-flex;align-items:center;justify-content:center;
  cursor:grab;color:var(--color-text-secondary);font-size:1.1rem;line-height:1;
  user-select:none;touch-action:none;transition:color .12s,transform .12s;}
.drag-handle:hover{color:var(--color-text-primary);transform:scale(1.18);}
.drag-handle:active{cursor:grabbing;}
.reorder-row.dragging .drag-handle{color:var(--color-accent);cursor:grabbing;}
/* Respect reduced-motion: keep the reorder usable without the movement. */
@media (prefers-reduced-motion:reduce){
  .reorder-row,.drag-handle{transition:none;}
  .reorder-row.dragging{transform:none;}
  .reorder-row.just-moved{animation:none;}
  /* Hamburger + mobile menu still snap between states, just without the motion. */
  .menu-toggle .bar{transition:none;}
  .mobile-menu,.mobile-menu > *{transition-duration:0s;transition-delay:0s;}
}

/* ---------- Section head ---------- */
.section-head{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:24px;flex-wrap:wrap;gap:8px;}
.section-head h2{font-family:'IBM Plex Sans Thai','Noto Sans Thai','Sarabun',system-ui,sans-serif;font-size:var(--fs-h2);font-weight:700;}
.section-head .see-all{color:var(--color-accent);font-weight:600;font-size:.875rem;}

/* ---------- Grids & cards ---------- */
.card-grid{display:grid;gap:20px;}
.card-grid.apps{grid-template-columns:repeat(auto-fill,minmax(230px,1fr));}
.card-grid.articles{grid-template-columns:repeat(auto-fill,minmax(270px,1fr));}

.app-card{border:1px solid var(--color-border);border-radius:var(--radius);padding:22px;
  display:flex;flex-direction:column;gap:12px;background:var(--color-bg);box-shadow:var(--shadow-sm);
  transition:box-shadow .18s,transform .18s,border-color .18s;}
.app-card:hover{box-shadow:var(--shadow);transform:translateY(-2px);border-color:var(--border-strong);opacity:1;}
.app-icon{width:48px;height:48px;border-radius:12px;display:flex;align-items:center;justify-content:center;font-size:1.375rem;flex-shrink:0;}
.app-card .name{font-weight:600;font-size:1.0625rem;}
.app-card .desc{color:var(--color-text-secondary);font-size:.875rem;line-height:1.6;flex:1;}
.app-card .cta{color:var(--color-accent);font-weight:600;font-size:.875rem;}

.article-card{border:1px solid var(--color-border);border-radius:var(--radius);overflow:hidden;
  display:flex;flex-direction:column;background:var(--color-bg);box-shadow:var(--shadow-sm);
  transition:box-shadow .18s,transform .18s;}
.article-card:hover{box-shadow:var(--shadow);transform:translateY(-2px);opacity:1;}
.article-cover{height:160px;background-size:cover;background-position:center;}
/* Covers render as a real <img> inside their layout box (see cover_html() in
   Views/partials/_covers.php) so they carry alt text and can be indexed. The
   box keeps owning the geometry; the image just fills it and crops like the
   background-size:cover it replaced. border-radius:inherit covers the boxes
   that round their own corners (.bite-brief-item-cover, .related-thumb).
   The background-* declarations above still serve the gradient placeholder
   used by rows with no uploaded cover. */
.cover-img{display:block;width:100%;height:100%;object-fit:cover;border-radius:inherit;}
.article-body{padding:20px;}
.article-cat{color:var(--color-accent);font-size:.8125rem;font-weight:700;letter-spacing:.02em;margin-bottom:8px;text-transform:uppercase;}
.article-title{font-weight:600;font-size:1.0625rem;margin-bottom:10px;line-height:1.5;}
.article-excerpt{color:var(--color-text-secondary);font-size:.875rem;line-height:1.6;margin-bottom:12px;}
.article-date{color:var(--color-text-secondary);font-size:.8125rem;}
.article-author{display:flex;align-items:center;gap:10px;margin-top:auto;padding-top:6px;}
.article-author-meta{display:flex;flex-direction:column;line-height:1.35;min-width:0;}
.article-author-name{font-size:.8125rem;font-weight:600;color:var(--color-text);}

/* Article card skeleton: mirrors .article-card geometry so the grid keeps
   its shape while the next infinite-scroll batch loads. */
.article-skeleton{border:1px solid var(--color-border);border-radius:var(--radius);overflow:hidden;
  display:flex;flex-direction:column;background:var(--color-bg);box-shadow:var(--shadow-sm);}
.article-skeleton .sk,.bite-brief-item-sk .sk{background:var(--color-bg-alt);border-radius:6px;
  background-image:linear-gradient(90deg,transparent,rgba(127,127,127,.12),transparent);
  background-size:200% 100%;animation:sk-shimmer 1.3s ease-in-out infinite;}
.article-skeleton .sk-cover{height:160px;border-radius:0;}
.article-skeleton .sk-body{padding:20px;display:flex;flex-direction:column;gap:10px;}
.article-skeleton .sk-cat{width:35%;height:12px;}
.article-skeleton .sk-title{width:85%;height:16px;}
.article-skeleton .sk-line{width:100%;height:12px;}
.article-skeleton .sk-line.short{width:70%;}
.article-skeleton .sk-author{display:flex;align-items:center;gap:10px;margin-top:6px;}
.article-skeleton .sk-avatar{width:36px;height:36px;border-radius:50%;flex:0 0 auto;}
.article-skeleton .sk-meta{flex:1;display:flex;flex-direction:column;gap:6px;}
.article-skeleton .sk-name{width:50%;height:11px;}
.article-skeleton .sk-date{width:30%;height:11px;}
@keyframes sk-shimmer{from{background-position:200% 0;}to{background-position:-200% 0;}}
@media (prefers-reduced-motion:reduce){
  .article-skeleton .sk,.bite-brief-item-sk .sk{animation:none;}
}

/* Author avatar (shared: cards + article byline) */
.avatar{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;
  border-radius:50%;background-size:cover;background-position:center;background-repeat:no-repeat;
  background-color:var(--color-bg-alt);overflow:hidden;}
.avatar-fallback{background:var(--color-accent);color:var(--btn-primary-text);font-weight:700;line-height:1;text-transform:uppercase;}

/* Article detail byline */
.article-byline{display:flex;align-items:center;gap:12px;margin:0 0 32px;padding-bottom:24px;border-bottom:1px solid var(--color-border);}
.article-byline-meta{display:flex;flex-direction:column;line-height:1.4;}
.article-byline-name{font-weight:600;color:var(--color-text);}

/* ---------- Chips (category filter) ---------- */
.chips{display:flex;gap:10px;flex-wrap:wrap;margin-bottom:28px;}
.chip{font-weight:500;font-size:.875rem;padding:8px 16px;border-radius:20px;background:var(--color-bg-alt);color:var(--color-text-secondary);}
.chip.active{background:var(--color-accent);color:var(--btn-primary-text);font-weight:600;}
/* Subcategory chip: same pill, marked with a tree glyph so the nesting reads
   without breaking the single-row chip flow. */
.chip-sub{display:inline-flex;align-items:center;gap:6px;padding-left:12px;}
.chip-sub .chip-sub-mark{font-family:monospace;opacity:.65;}

/* ---------- Public tags (article chips + tag cloud) ---------- */
/* Kept separate from admin .tag-chip: this is a read-only, linkable, public
   context (no remove button), so it carries its own contrast-checked colours. */
.article-tags{display:flex;flex-wrap:wrap;gap:8px;margin:32px 0 0;padding-top:24px;border-top:1px solid var(--color-border);}
.article-tag{display:inline-flex;align-items:center;font-size:.8125rem;font-weight:600;padding:5px 12px;border-radius:16px;
  background:var(--color-accent-soft);color:var(--color-accent-hover);transition:background .15s;}
.article-tag::before{content:"#";opacity:.65;margin-right:2px;}
.article-tag:hover{background:var(--color-accent-soft-strong);color:var(--color-accent-hover);}
.tag-cloud{display:flex;flex-wrap:wrap;gap:10px;align-items:baseline;}
.tag-cloud-item{display:inline-flex;align-items:baseline;gap:7px;padding:8px 16px;border-radius:20px;line-height:1.2;
  background:var(--color-bg-alt);color:var(--color-text-primary);font-weight:600;transition:background .15s,color .15s;}
.tag-cloud-item:hover{background:var(--color-accent);color:var(--btn-primary-text);}
.tag-cloud-item .count{font-size:.72em;font-weight:500;color:var(--color-text-secondary);}
.tag-cloud-item:hover .count{color:inherit;opacity:.75;}

/* ---------- Article detail ---------- */
.article-hero{aspect-ratio:758/426;width:100%;max-width:758px;margin:24px auto 0;border-radius:var(--radius-lg);overflow:hidden;background-size:cover;background-position:center;}
.prose{max-width:840px;margin:0 auto;padding:32px var(--pad-x) 64px;}
.prose .kicker{color:var(--color-accent);font-size:.875rem;font-weight:700;margin-bottom:12px;letter-spacing:.02em;}
.prose h1{font-size:var(--fs-h1);font-weight:700;margin:0 0 20px;line-height:1.3;}
.prose .meta{display:flex;gap:16px;color:var(--color-text-secondary);font-size:.875rem;margin-bottom:32px;padding-bottom:24px;border-bottom:1px solid var(--color-border);flex-wrap:wrap;}
.prose-content{font-size:1.0625rem;line-height:1.85;}
.prose-content h2{font-size:var(--fs-h3);font-weight:600;margin:32px 0 12px;}
.prose-content h3{font-size:var(--fs-h4);font-weight:600;margin:24px 0 10px;}
.prose-content p{margin:0 0 20px;}
.prose-content ul,.prose-content ol{margin:0 0 20px;padding-left:1.4em;}
.prose-content li{margin-bottom:8px;}
.prose-content a{color:var(--color-accent);text-decoration:underline;}
.prose-content code{background:var(--color-bg-alt);padding:2px 6px;border-radius:4px;font-size:.9em;}
.prose-content pre{background:var(--color-bg-alt);border-radius:10px;padding:18px 20px;overflow-x:auto;}
.prose-content img{max-width:100%;height:auto;border-radius:10px;margin:8px 0;}
.prose-content blockquote{border-left:3px solid var(--color-accent);margin:0 0 20px;padding:4px 0 4px 18px;color:var(--color-text-secondary);}
/* Alignment classes emitted by the legacy Quill editor (kept for old articles). */
.prose-content .ql-align-center{text-align:center;}
.prose-content .ql-align-right{text-align:right;}
.prose-content .ql-align-justify{text-align:justify;}
/* Image size classes set via the legacy Quill per-image toolbar. */
.prose-content img.img-size-s{width:25%;}
.prose-content img.img-size-m{width:50%;}
.prose-content img.img-size-l{width:75%;}
/* CKEditor 5 images: <figure> wrapper, caption, resize (% width) + alignment.
   The box model mirrors the editor's own .ck-content rules so an image lands on
   the page where the author saw it: figure.image is a shrink-to-fit table box
   (not a full-width block), so `margin:auto` actually centres it — which is how
   CKEditor centres both the default block image and .image-style-align-center. */
.prose-content figure.image{display:table;clear:both;text-align:center;min-width:50px;margin:20px auto;}
.prose-content figure.image img{display:block;margin:0 auto;min-width:100%;max-width:100%;height:auto;border-radius:10px;}
/* Resized (% width) images are plain blocks — the width comes from the inline
   style CKEditor writes on the figure; `margin:20px auto` above still centres. */
.prose-content figure.image.image_resized{display:block;box-sizing:border-box;max-width:100%;}
.prose-content figure.image.image_resized img{width:100%;min-width:0;}
.prose-content figure.image>figcaption{display:table-caption;caption-side:bottom;font-size:.875rem;color:var(--color-text-secondary);text-align:center;padding:8px 4px 0;}
.prose-content figure.image.image_resized>figcaption{display:block;}
.prose-content img.image_resized{height:auto;}
/* Inline images (imageStyle:inline) sit in the text flow, not on their own line. */
.prose-content .image-inline{display:inline-flex;max-width:100%;align-items:flex-start;}
.prose-content .image-inline img{max-width:100%;height:auto;}
.prose-content .image-style-align-left{float:left;clear:none;max-width:50%;margin:6px 22px 14px 0;}
.prose-content .image-style-align-right{float:right;clear:none;max-width:50%;margin:6px 0 14px 22px;}
.prose-content .image-style-block-align-left{margin-left:0;margin-right:auto;max-width:calc(100% - 22px);}
.prose-content .image-style-block-align-right{margin-left:auto;margin-right:0;max-width:calc(100% - 22px);}
.prose-content .image-style-align-center,.prose-content .image-style-block{margin-left:auto;margin-right:auto;}
.prose-content .image-style-side{float:right;max-width:45%;margin:6px 0 14px 22px;}
/* Text alignment classes emitted by the CKEditor 5 alignment plugin. */
.prose-content .text-align-left{text-align:left;}
.prose-content .text-align-center{text-align:center;}
.prose-content .text-align-right{text-align:right;}
.prose-content .text-align-justify{text-align:justify;}

/* CKEditor 5 tables. The editor emits <figure class="table"><table>…; on narrow
   screens a wide table would push the whole page sideways, so the figure is the
   horizontal scroll container instead. Borders live here (not as inline style)
   because the sanitiser only lets structural table markup through. */
.prose-content figure.table{margin:0 0 20px;max-width:100%;overflow-x:auto;}
.prose-content table{border-collapse:collapse;width:100%;font-size:.9375rem;}
.prose-content th,.prose-content td{border:1px solid var(--color-border);padding:9px 12px;text-align:left;vertical-align:top;}
.prose-content thead th,.prose-content th{background:var(--color-bg-alt);font-weight:600;}
.prose-content figure.table>figcaption,.prose-content table caption{caption-side:bottom;font-size:.875rem;color:var(--color-text-secondary);text-align:center;padding:8px 4px 0;}
/* Column widths come from <col style="width:%"> written by TableColumnResize. */
.prose-content table.ck-table-resized{table-layout:fixed;}

/* CKEditor Highlight plugin: markers (background) and pens (coloured text).
   Class names match the highlight config in admin/articles.php. */
.prose-content mark{background:transparent;color:inherit;}
.prose-content mark.marker-yellow{background:#fdfd77;color:#000;}
.prose-content mark.marker-green{background:#63f963;color:#000;}
.prose-content mark.marker-pink{background:#fc7999;color:#000;}
.prose-content mark.marker-blue{background:#72cdfd;color:#000;}
.prose-content mark.pen-red{color:#e71313;background:transparent;}
.prose-content mark.pen-green{color:#128a00;background:transparent;}
/* Contain floated (left/right-aligned) images within the article body. */
.prose-content::after{content:"";display:block;clear:both;}

/* ---------- Article detail layout: content + related-news rail ---------- */
/* Desktop = บทความ + แถบข่าวที่เกี่ยวข้อง. When the article has no related items
   the wrapper carries .is-solo, which restores the original single-column
   widths exactly, so a lone article never renders beside an empty column. */
.article-layout{max-width:1180px;margin:0 auto;padding-inline:var(--pad-x);
  display:grid;grid-template-columns:minmax(0,1fr) 300px;gap:clamp(28px,3.2vw,56px);align-items:start;}
.article-main{min-width:0;}
.article-layout .article-hero{max-width:none;margin:24px 0 0;}
.article-layout .prose{max-width:none;margin:0;padding:32px 0 64px;}
.article-layout.is-solo{display:block;max-width:1120px;}
.article-layout.is-solo .article-hero{max-width:758px;margin-inline:auto;}
.article-layout.is-solo .prose{max-width:840px;margin-inline:auto;}
/* The rail follows the reader down a long article; top clears the sticky header. */
.article-aside{position:sticky;top:84px;min-width:0;padding:40px 0 64px;}

/* ---------- Related news rail ---------- */
.related-news-heading{display:inline-block;margin:0;padding-bottom:10px;font-size:1.0625rem;font-weight:700;
  border-bottom:2px solid var(--color-accent);}
.related-news-note{margin:10px 0 2px;font-size:.75rem;line-height:1.5;color:var(--color-text-secondary);}
.related-list{list-style:none;margin:0;padding:0;}
.related-item{border-bottom:1px solid var(--color-border);}
.related-item:last-child{border-bottom:0;}
.related-link{display:grid;grid-template-columns:minmax(0,1fr) 88px;gap:12px;align-items:start;padding:14px 0;color:inherit;}
.related-link:focus-visible{outline:2px solid var(--color-accent);outline-offset:3px;border-radius:6px;}
/* Clamp to 3 lines so every row keeps roughly the thumbnail's height. */
.related-title{display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden;
  font-size:.9375rem;font-weight:600;line-height:1.55;transition:color .15s;}
.related-link:hover .related-title{color:var(--color-accent);}
.related-meta{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin-top:6px;
  font-size:.75rem;color:var(--color-text-secondary);}
/* Why this article was picked (same tag / same category / trending). */
.related-reason{padding:1px 8px;border-radius:10px;font-weight:600;
  background:var(--color-accent-soft);color:var(--color-accent-hover);}
.related-thumb{aspect-ratio:4/3;border-radius:8px;background-color:var(--color-bg-alt);
  background-size:cover;background-position:center;transition:filter .15s;}
.related-link:hover .related-thumb{filter:brightness(1.06);}

@media(max-width:1080px){
  /* Rail drops below the article and widens with it. */
  .article-layout{grid-template-columns:minmax(0,1fr);}
  .article-layout .article-hero{max-width:758px;margin-inline:auto;}
  .article-layout .prose{max-width:840px;margin-inline:auto;padding-bottom:24px;}
  /* width:100% is load-bearing — on a grid item an auto inline margin makes the
     box shrink-to-fit its content, which left the rail narrower than the article. */
  .article-aside{position:static;width:100%;max-width:840px;margin-inline:auto;
    padding:24px 0 56px;border-top:1px solid var(--color-border);}
  .related-link{grid-template-columns:minmax(0,1fr) 120px;}
}

/* ---------- Share bar ---------- */
.share-divider{border:0;height:1px;background:var(--color-border);margin:40px 0 0;}
.share-bar{display:flex;align-items:center;flex-wrap:wrap;gap:16px;margin-top:24px;}
.share-bar-label{font-weight:600;font-size:.9375rem;color:var(--color-text-primary);}
.share-bar-buttons{display:flex;gap:10px;flex-wrap:wrap;align-items:center;}
.share-btn{display:inline-flex;align-items:center;justify-content:center;cursor:pointer;font-family:inherit;transition:transform .14s ease,box-shadow .14s ease,filter .14s ease,background .15s ease,color .15s ease,border-color .15s ease;}
.share-btn:focus-visible{outline:2px solid var(--color-accent);outline-offset:2px;}
/* Social icons: solid brand-coloured circles */
.share-social{width:44px;height:44px;border-radius:50%;border:0;color:#fff;box-shadow:0 1px 2px rgba(11,18,32,.12);}
.share-social:hover{transform:translateY(-2px);box-shadow:0 6px 16px rgba(11,18,32,.18);filter:brightness(1.05);}
.share-social:active{transform:translateY(0);}
.share-fb{background:#1877F2;}
.share-x{background:#000;}
.share-line{background:#06C755;}
/* Copy: outlined pill */
.share-copy{gap:8px;height:44px;padding:0 18px;border-radius:999px;border:1px solid var(--color-border);background:var(--color-bg);color:var(--color-text-primary);font-weight:600;font-size:.875rem;}
.share-copy:hover{background:var(--color-bg-alt);border-color:var(--color-text-secondary);transform:translateY(-2px);}
.share-copy.is-copied{background:#06C755;border-color:#06C755;color:#fff;}
.share-copy-text{white-space:nowrap;}

/* ---------- CKEditor 5 rich-text editor (admin) ---------- */
/* The editor ships its own theme via ckeditor5.css; only a few overrides here. */
.ck.ck-editor{width:100%;}
.ck-editor__editable_inline{min-height:300px;}
.ck.ck-editor__editable.ck-focused{border-color:var(--color-accent);}
.ck.ck-content{font-size:1rem;line-height:1.8;}
/* Alignment is configured to emit these classes, so the editing view needs
   matching rules (CKEditor's default text-align CSS keys off other classes). */
.ck-content .text-align-left{text-align:left;}
.ck-content .text-align-center{text-align:center;}
.ck-content .text-align-right{text-align:right;}
.ck-content .text-align-justify{text-align:justify;}
/* Dark mode: CKEditor ships a fixed light theme. The bulk of the retint is done
   by overriding its --ck-color-* custom properties (see variables.css); these
   element rules cover the editable surface + toolbar directly as a safety net. */
[data-theme="dark"] .ck.ck-editor__main>.ck-editor__editable,
[data-theme="dark"] .ck.ck-content{background:var(--bg-elevated);color:var(--text-primary);}
[data-theme="dark"] .ck.ck-toolbar{background:var(--bg-subtle);border-color:var(--border-default);}
[data-theme="dark"] .ck.ck-editor__editable.ck-focused{border-color:var(--color-accent);}

/* ---------- App detail ---------- */
.app-detail{max-width:900px;margin:0 auto;padding:32px var(--pad-x) 64px;}
.app-detail-head{display:flex;gap:20px;align-items:center;margin-bottom:28px;flex-wrap:wrap;}
.app-detail-head .app-icon{width:72px;height:72px;border-radius:16px;font-size:2rem;}
.app-meta-grid{margin-top:36px;display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:16px;}
.meta-box{border:1px solid var(--color-border);border-radius:var(--radius);padding:18px;}
.meta-box .label{color:var(--color-text-secondary);font-size:.875rem;margin-bottom:6px;}
.meta-box .value{font-weight:600;font-size:.9375rem;}
.divider-line{height:1px;background:var(--color-border);margin:32px 0;}

/* ---------- Downloads table ---------- */
.table-wrap{border:1px solid var(--color-border);border-radius:var(--radius);max-width:980px;overflow-x:auto;-webkit-overflow-scrolling:touch;}
.dl-table-wrap{border:1px solid var(--color-border);border-radius:var(--radius);overflow:hidden;max-width:980px;}
.dl-row{display:grid;grid-template-columns:1fr 130px 100px 130px;padding:16px 20px;align-items:center;border-top:1px solid var(--color-border);}
.dl-head{background:var(--color-bg-alt);color:var(--color-text-secondary);font-size:.875rem;font-weight:600;border-top:none;}
.dl-name{font-weight:600;font-size:.9375rem;}
.dl-sub{color:var(--color-text-secondary);font-size:.875rem;}
.dl-link{color:var(--color-accent);font-weight:600;font-size:.875rem;}
.dl-cards{display:none;flex-direction:column;gap:12px;}
.dl-card{border:1px solid var(--color-border);border-radius:var(--radius);padding:16px;display:flex;flex-direction:column;gap:10px;}
.dl-card .row{display:flex;justify-content:space-between;align-items:center;}
@media(max-width:720px){
  .dl-table-wrap{display:none;}
  .dl-cards{display:flex;}
}

/* ---------- Footer ---------- */
.site-footer{border-top:1px solid var(--color-border);padding:24px var(--pad-x);display:flex;
  justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap;color:var(--color-text-secondary);font-size:.875rem;margin-top:auto;}
.site-footer a{color:var(--color-text-secondary);}
.footer-links{display:flex;gap:20px;}

/* ---------- Badges ---------- */
.badge{font-size:.75rem;font-weight:600;padding:3px 10px;border-radius:12px;width:fit-content;display:inline-block;}
.badge-green{background:var(--badge-success-bg);color:var(--badge-success-text);}
.badge-amber{background:var(--badge-warning-bg);color:var(--badge-warning-text);}
.badge-blue{background:var(--badge-info-bg);color:var(--badge-info-text);}

/* ---------- Forms ---------- */
.form-group{margin-bottom:18px;}
.form-label{display:block;font-size:.875rem;font-weight:600;margin-bottom:6px;}
.input,.textarea,.select{width:100%;box-sizing:border-box;padding:12px 14px;border:1px solid var(--color-border);
  border-radius:8px;font-size:1rem;font-family:var(--font-body);color:var(--color-text-primary);background:var(--color-bg);}
.input:focus,.textarea:focus,.select:focus{border-color:var(--color-accent);outline:none;box-shadow:0 0 0 3px var(--color-accent-soft);}
.textarea{resize:vertical;min-height:120px;line-height:1.7;}
.field-error{color:var(--color-danger);font-size:.8125rem;margin-top:5px;}
.input.has-error,.textarea.has-error{border-color:var(--color-danger);}
.form-hint{color:var(--color-text-secondary);font-size:.8125rem;margin-top:-10px;margin-bottom:16px;}

/* ---------- Auth cards ---------- */
.auth-wrap{background:var(--color-bg-alt);min-height:100vh;display:flex;flex-direction:column;}
.auth-header{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:24px var(--pad-x);background:var(--color-bg);}
.auth-body{flex:1;display:flex;align-items:center;justify-content:center;padding:clamp(32px,6vw,48px) 20px;}
.auth-card{width:100%;max-width:430px;background:var(--color-bg);border:1px solid var(--color-border);border-radius:var(--radius-lg);padding:clamp(28px,5vw,40px);box-shadow:var(--shadow);}
.auth-card h1{font-size:1.75rem;font-weight:700;margin:0 0 6px;text-align:center;}
.auth-card .subtitle{color:var(--color-text-secondary);font-size:.875rem;text-align:center;line-height:1.6;margin:0 0 28px;}
.auth-alt{text-align:center;font-size:.875rem;color:var(--color-text-secondary);margin-top:20px;}
.auth-alt a{color:var(--color-accent);font-weight:600;}
.label-row{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:6px;flex-wrap:wrap;gap:4px;}
.label-row a{color:var(--color-accent);font-size:.875rem;font-weight:600;}

/* ---------- Flash messages ---------- */
.flash-stack{display:flex;flex-direction:column;gap:10px;margin-bottom:18px;}
.flash{padding:12px 16px;border-radius:8px;font-size:.9375rem;font-weight:500;border:1px solid;}
.flash-success{background:var(--flash-success-bg);border-color:var(--flash-success-border);color:var(--flash-success-text);}
.flash-error{background:var(--flash-error-bg);border-color:var(--flash-error-border);color:var(--flash-error-text);}
.flash-info{background:var(--flash-info-bg);border-color:var(--flash-info-border);color:var(--flash-info-text);}

/* ---------- Status banners (flash → info/success/warning/error) ---------- */
.banner-stack{display:flex;flex-direction:column;gap:12px;margin-bottom:18px;}
.banner{display:flex;align-items:flex-start;gap:12px;padding:14px 16px;border-radius:12px;border:1px solid transparent;
  background:var(--banner-bg);border-color:var(--banner-border);color:var(--banner-text);}
.banner-icon{flex-shrink:0;display:flex;align-items:center;justify-content:center;line-height:0;margin-top:1px;color:var(--banner-icon);}
.banner-body{flex:1;min-width:0;}
.banner-title{margin:0;font-size:.9375rem;font-weight:700;line-height:1.45;}
.banner-desc{margin:2px 0 0;font-size:.875rem;font-weight:400;line-height:1.5;opacity:.9;}
.banner-close{flex-shrink:0;display:flex;align-items:center;justify-content:center;width:26px;height:26px;padding:0;margin:-2px -4px 0 0;
  border:0;background:transparent;color:inherit;border-radius:7px;cursor:pointer;opacity:.65;transition:opacity .15s,background .15s;}
.banner-close:hover{opacity:1;background:rgba(0,0,0,.06);}
.banner-info{--banner-bg:var(--flash-info-bg);--banner-border:var(--flash-info-border);--banner-text:var(--flash-info-text);--banner-icon:var(--flash-info-text);}
.banner-success{--banner-bg:var(--flash-success-bg);--banner-border:var(--flash-success-border);--banner-text:var(--flash-success-text);--banner-icon:var(--flash-success-text);}
.banner-warning{--banner-bg:var(--flash-warning-bg);--banner-border:var(--flash-warning-border);--banner-text:var(--flash-warning-text);--banner-icon:var(--flash-warning-text);}
.banner-error{--banner-bg:var(--flash-error-bg);--banner-border:var(--flash-error-border);--banner-text:var(--flash-error-text);--banner-icon:var(--flash-error-text);}

/* ---------- Member profile ---------- */
.profile-header{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:16px var(--pad-x);
  border-bottom:1px solid var(--color-border);background:var(--color-bg);flex-wrap:wrap;}
.profile-user{display:flex;align-items:center;gap:14px;}
.profile-user .avatar{width:36px;height:36px;border-radius:50%;background:var(--color-accent);color:var(--btn-primary-text);display:flex;
  align-items:center;justify-content:center;font-weight:700;font-size:.9375rem;flex-shrink:0;}
.profile-body{max-width:720px;margin:0 auto;padding:clamp(32px,6vw,48px) var(--pad-x);}
.panel{background:var(--color-bg);border:1px solid var(--color-border);border-radius:12px;padding:clamp(20px,4vw,32px);margin-bottom:24px;}
.panel h2{font-size:1.125rem;font-weight:600;margin:0 0 20px;}

/* ---------- Empty state ---------- */
.empty{text-align:center;padding:48px 20px;color:var(--color-text-secondary);}

/* ============================================================
   ADMIN (light theme)
   ============================================================ */
.admin{--panel:var(--bg-elevated);--panel-2:var(--bg-subtle);--bg:var(--bg-subtle);
  background:var(--bg-subtle);min-height:100vh;display:flex;color:var(--color-text-primary);}
.admin a{color:inherit;}
.admin-sidebar{width:240px;background:var(--panel);border-right:1px solid var(--color-border);
  padding:24px 16px;display:flex;flex-direction:column;gap:4px;flex-shrink:0;}
.admin-brand{padding:0 10px 24px;}
.admin-brand .name{font-family:var(--font-heading);font-weight:700;font-size:1.25rem;}
.admin-brand .tag{display:block;color:var(--color-text-secondary);font-size:.75rem;letter-spacing:.08em;margin-top:2px;}
.admin-nav a{padding:10px 14px;border-radius:8px;color:var(--color-text-secondary);font-weight:500;font-size:1rem;display:block;}
.admin-nav a.active{background:var(--panel-2);color:var(--color-text-primary);font-weight:600;}
.admin-nav .divider{height:1px;background:var(--color-border);margin:16px 10px;}
.admin-nav a.small{font-size:.875rem;}
.admin-nav form{margin:0;}
.admin-nav button.linklike{background:none;border:none;padding:10px 14px;border-radius:8px;color:var(--color-text-secondary);
  font-weight:500;font-size:.875rem;text-align:left;width:100%;display:block;}
.admin-role-tag{padding:2px 14px 10px;color:var(--color-text-secondary);font-size:.8125rem;line-height:1.4;}
.admin-theme-row{display:flex;align-items:center;justify-content:space-between;gap:10px;
  padding:6px 14px;color:var(--color-text-secondary);font-size:.875rem;font-weight:500;}
.admin-main{flex:1;padding:32px clamp(20px,3vw,40px);min-width:0;max-width:1200px;}
.admin-topbar{display:flex;justify-content:space-between;align-items:center;margin-bottom:28px;gap:16px;flex-wrap:wrap;}
.admin-topbar h1{font-size:1.75rem;font-weight:700;}
.admin-user{display:flex;align-items:center;gap:10px;}
.admin-user .avatar{background:var(--color-accent);color:var(--btn-primary-text);width:34px;height:34px;font-size:.875rem;}

.stat-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;margin-bottom:32px;}
.stat-card{background:var(--panel);border:1px solid var(--color-border);border-radius:12px;padding:22px;box-shadow:var(--shadow-sm);}
.stat-card .label{color:var(--color-text-secondary);font-size:.875rem;margin-bottom:8px;}
.stat-card .value{font-size:1.75rem;font-weight:700;}

.admin-cols{display:grid;grid-template-columns:1fr 1fr;gap:16px;}
.admin-panel{background:var(--panel);border:1px solid var(--color-border);border-radius:12px;padding:24px;box-shadow:var(--shadow-sm);}
.admin-panel h2{font-size:1.0625rem;font-weight:600;margin:0 0 16px;}
.admin-list .item{display:flex;justify-content:space-between;gap:12px;padding:10px 0;border-bottom:1px solid var(--color-border);}
.admin-list .item:last-child{border-bottom:none;}

.admin-table{background:var(--panel);border:1px solid var(--color-border);border-radius:12px;overflow:hidden;box-shadow:var(--shadow-sm);}
.admin-table .thead,.admin-table .trow{display:grid;padding:13px 18px;align-items:center;gap:18px;}
.admin-table .thead{background:var(--panel-2);color:var(--color-text-secondary);font-size:.8125rem;font-weight:600;letter-spacing:.01em;}
.admin-table .trow{border-top:1px solid var(--color-border);font-size:.9375rem;transition:background .12s;}
.admin-table .trow:hover:not(.dragging){background:var(--panel-2);}
.admin-table .primary{font-weight:600;}
.admin-table .sub{color:var(--color-text-secondary);font-size:.8125rem;margin-top:2px;}
/* Let every grid cell shrink so long values ellipsize instead of stretching the
   column — keeps metadata tightly grouped and the row scannable at a glance. */
.admin-table .thead>*,.admin-table .trow>*{min-width:0;}
.admin-table .primary,.admin-table .sub,.admin-table .muted{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
/* ---------- Category manager ----------
   Categories are a two-level tree, not a flat table, so they get their own
   component instead of .admin-table: one card per หมวดหมู่หลัก, its
   subcategories nested inside and collapsible. That keeps a long list
   scannable at the top level — the row-per-category table stopped working
   once every parent could carry children of its own. */
.cat-bar{display:flex;align-items:center;justify-content:space-between;gap:12px;
  flex-wrap:wrap;margin-bottom:10px;}
.cat-bar strong{color:var(--color-text-primary);font-weight:700;}
.cat-bar-dot{opacity:.5;margin:0 2px;}
.cat-hint{font-size:.8125rem;margin:0 0 14px;}
.cat-hint-glyph{font-family:monospace;}

.cat-manager{display:flex;flex-direction:column;gap:10px;}
.cat-empty{background:var(--panel);border:1px solid var(--color-border);border-radius:12px;
  padding:28px;text-align:center;box-shadow:var(--shadow-sm);}

/* --- One group = one top-level category --- */
.cat-group{background:var(--panel);border:1px solid var(--color-border);border-radius:12px;
  box-shadow:var(--shadow-sm);overflow:hidden;}
/* The group currently loaded in the form below, so it's obvious what you're editing. */
.cat-group.is-editing,.cat-kid.is-editing{box-shadow:inset 0 0 0 2px var(--color-accent);}

.cat-head{display:flex;align-items:center;gap:10px;padding:13px 16px;}
.cat-head-main{flex:1 1 auto;min-width:0;}
.cat-title{display:flex;align-items:center;gap:8px;min-width:0;}
.cat-name{font-weight:700;font-size:1rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.cat-pill{flex:0 0 auto;font-size:.75rem;font-weight:600;color:var(--color-accent-hover);
  background:var(--color-accent-soft);border-radius:10px;padding:1px 8px;}
.cat-meta{display:flex;align-items:center;gap:6px;min-width:0;margin-top:3px;
  color:var(--color-text-secondary);font-size:.8125rem;}
.cat-slug{flex:0 0 auto;font-family:monospace;}
.cat-desc{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.cat-dot{opacity:.5;}

/* Fold/unfold caret. Sized to match the drag handle so the two align in a column. */
.cat-toggle{flex:0 0 auto;width:26px;height:26px;display:inline-flex;align-items:center;
  justify-content:center;background:none;border:none;border-radius:6px;cursor:pointer;
  color:var(--color-text-secondary);padding:0;}
.cat-toggle:hover{background:var(--panel-2);color:var(--color-text-primary);}
.cat-toggle--empty{cursor:default;}
.cat-caret{display:block;font-size:.8125rem;line-height:1;transition:transform .15s ease;}
.cat-group.is-folded .cat-caret{transform:rotate(-90deg);}
.cat-group.is-folded .cat-kids{display:none;}
/* The folded group still names its children, so folding hides rows, not information. */
.cat-peek{display:none;margin-top:4px;color:var(--color-text-secondary);font-size:.8125rem;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.cat-group.is-folded .cat-peek{display:block;}

.cat-head-side{display:flex;align-items:center;gap:14px;flex:0 0 auto;}
.cat-actions{display:flex;align-items:center;gap:12px;white-space:nowrap;}
.cat-actions form{display:inline;}
.cat-act{background:none;border:none;padding:0;font:inherit;font-size:.875rem;font-weight:600;
  color:var(--color-accent);cursor:pointer;}
.cat-act:hover{text-decoration:underline;}
.cat-act--del{color:var(--color-danger);}

/* --- Subcategory rows, indented inside the card --- */
.cat-kids{background:var(--panel-2);border-top:1px solid var(--color-border);}
.cat-kid{display:flex;align-items:center;gap:10px;padding:9px 16px 9px 30px;
  border-top:1px solid var(--color-border);font-size:.9375rem;}
.cat-kid:first-child{border-top:none;}
/* An "L" drawn with two borders — the tree connector, without an icon font. */
.cat-kid-mark{flex:0 0 auto;width:14px;height:14px;margin:-6px 2px 0 0;
  border-left:1px solid var(--color-border);border-bottom:1px solid var(--color-border);
  border-bottom-left-radius:4px;}
.cat-kid-main{flex:1 1 auto;min-width:0;display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;}
.cat-kid-name{font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.cat-kid-main .cat-meta{margin-top:0;}
.cat-kid-add{display:block;padding:9px 16px 9px 46px;border-top:1px solid var(--color-border);
  color:var(--color-text-secondary);font-size:.8125rem;font-weight:600;text-decoration:none;}
.cat-kid-add:hover{color:var(--color-accent);background:var(--color-accent-soft);}

@media(max-width:860px){
  .cat-head{flex-wrap:wrap;}
  .cat-head-main{flex:1 1 100%;order:2;}
  .cat-head-side{flex:1 1 100%;order:3;justify-content:space-between;
    padding-top:10px;margin-top:2px;border-top:1px solid var(--color-border);}
  .cat-kid{flex-wrap:wrap;padding-left:16px;}
  .cat-kid-mark{display:none;}
  .cat-kid-main{flex:1 1 100%;}
  .cat-kid .cat-head-side{border-top:none;padding-top:6px;}
}

.admin-actions{display:flex;gap:12px;}
.admin-actions a,.admin-actions button{font-size:.875rem;font-weight:600;background:none;border:none;padding:0;}
.admin-actions .edit{color:var(--color-accent);}
.admin-actions .del{color:var(--color-danger);}
.badge-admin-green{background:var(--badge-success-bg);color:var(--badge-success-text);}
.badge-admin-amber{background:var(--badge-warning-bg);color:var(--badge-warning-text);}
.badge-admin-blue{background:var(--badge-info-bg);color:var(--badge-info-text);}

/* --- Tag picker (article form): chips + autocomplete --- */
.tag-picker{position:relative;}
.tag-picker-box{display:flex;flex-wrap:wrap;gap:6px;align-items:center;padding:7px 8px;border:1px solid var(--color-border);border-radius:8px;background:var(--panel);min-height:44px;cursor:text;}
.tag-picker-box.is-focus{border-color:var(--color-accent);box-shadow:0 0 0 3px var(--color-accent-soft);}
.tag-chip{display:inline-flex;align-items:center;gap:6px;background:var(--color-accent-soft);color:var(--color-accent-hover);font-size:.875rem;font-weight:600;padding:3px 6px 3px 10px;border-radius:14px;}
.tag-chip button{background:none;border:none;color:var(--color-accent-hover);font-size:1rem;line-height:1;cursor:pointer;padding:0 2px;border-radius:50%;}
.tag-chip button:hover{background:rgba(6,73,180,.16);}
.tag-picker-input{flex:1 1 120px;min-width:120px;border:none;outline:none;background:none;font:inherit;color:var(--color-text-primary);padding:4px 2px;}
.tag-suggestions{position:absolute;left:0;right:0;top:calc(100% + 4px);z-index:20;background:var(--panel);border:1px solid var(--color-border);border-radius:8px;box-shadow:0 6px 20px rgba(11,18,32,.12);max-height:240px;overflow-y:auto;padding:4px;}
.tag-suggestions[hidden]{display:none;}
.tag-suggestion{display:block;width:100%;text-align:left;background:none;border:none;font:inherit;color:var(--color-text-primary);padding:8px 10px;border-radius:6px;cursor:pointer;}
.tag-suggestion:hover,.tag-suggestion.is-active{background:var(--color-accent-soft);}
.tag-suggestion .new-hint{color:var(--color-text-secondary);font-size:.8125rem;font-weight:600;}

.admin-toolbar{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin-bottom:14px;}
.admin-toolbar .input{flex:1 1 260px;min-width:200px;}
.admin-toolbar .select{flex:0 0 auto;width:auto;min-width:140px;}
.admin-result-info{font-size:.875rem;margin-bottom:12px;}

.admin-pagination{display:flex;gap:6px;align-items:center;flex-wrap:wrap;margin-top:20px;}
.admin-pagination .page-link{display:inline-flex;align-items:center;justify-content:center;min-width:38px;height:38px;padding:0 12px;border:1px solid var(--color-border);border-radius:8px;background:var(--panel);color:var(--color-text-primary);font-size:.9375rem;font-weight:600;text-decoration:none;}
.admin-pagination a.page-link:hover{border-color:var(--color-accent);color:var(--color-accent);}
.admin-pagination .page-link.active{background:var(--color-accent);border-color:var(--color-accent);color:var(--btn-primary-text);}
.admin-pagination .page-link.disabled{opacity:.45;pointer-events:none;}
.admin-pagination .page-ellipsis{padding:0 4px;color:var(--color-text-secondary);}

.admin-form{margin-top:32px;background:var(--panel);border:1px solid var(--color-border);border-radius:12px;padding:28px;max-width:660px;box-shadow:var(--shadow-sm);}
.admin-form h2{font-size:1.0625rem;font-weight:600;margin:0 0 20px;}
.admin .form-label{color:var(--color-text-primary);}
.admin .input,.admin .textarea,.admin .select{background:var(--bg-elevated);color:var(--color-text-primary);border-color:var(--color-border);}
.admin .input:focus,.admin .textarea:focus,.admin .select:focus{border-color:var(--color-accent);box-shadow:0 0 0 3px var(--color-accent-soft);}
.admin .btn-primary{background:var(--btn-primary-bg);color:var(--btn-primary-text);font-weight:700;}
.admin .btn-primary:hover{background:var(--btn-primary-hover);}
.admin .btn-outline{color:var(--color-text-secondary);border-color:var(--color-border);}
.grid-2{display:grid;grid-template-columns:1fr 1fr;gap:16px;}
.grid-3{display:grid;grid-template-columns:1fr 1fr 1fr;gap:16px;}

/* ---------- Confirmation modal (admin) ---------- */
.confirm-modal{position:fixed;inset:0;z-index:1000;display:none;align-items:center;justify-content:center;padding:20px;}
.confirm-modal.open{display:flex;}
.confirm-modal-backdrop{position:absolute;inset:0;background:rgba(11,18,32,.45);}
.confirm-modal-box{position:relative;background:var(--bg-elevated);border-radius:var(--radius-lg);box-shadow:var(--shadow);padding:24px;max-width:360px;width:100%;}
.confirm-modal-msg{margin:0 0 20px;font-size:1rem;color:var(--color-text-primary);line-height:1.6;}
.confirm-modal-actions{display:flex;justify-content:flex-end;gap:10px;}
.upload-drop{border:1px dashed var(--color-border);border-radius:8px;padding:22px;text-align:center;color:var(--color-text-secondary);font-size:.875rem;}

/* admin login */
.admin-auth{background:var(--bg-subtle);min-height:100vh;display:flex;align-items:center;justify-content:center;padding:20px;color:var(--text-primary);}
.admin-auth-card{width:100%;max-width:390px;background:var(--bg-elevated);border:1px solid var(--border-default);border-radius:14px;padding:40px;box-shadow:var(--shadow);}
.admin-auth-card .brandline{text-align:center;margin-bottom:8px;}
.admin-auth-card .brandline .name{font-family:var(--font-heading);font-weight:700;font-size:1.375rem;}
.admin-auth-card .brandline .tag{display:block;color:var(--text-secondary);font-size:.875rem;margin-top:4px;letter-spacing:.08em;}
.admin-auth-card .subtitle{color:var(--text-secondary);font-size:.875rem;text-align:center;margin:0 0 28px;}
.admin-auth-card .form-label{color:var(--text-primary);}
.admin-auth-card .input{background:var(--bg-elevated);color:var(--text-primary);border-color:var(--border-default);}
.admin-auth-card .btn-primary{background:var(--btn-primary-bg);color:var(--btn-primary-text);font-weight:700;}

/* Cloudflare Turnstile ("ยืนยันว่าไม่ใช่บอท"). The widget is a fixed 300x65
   iframe, so the min-height reserves its space and the form does not jump when
   it finishes loading. */
.turnstile-field{margin:4px 0 8px;min-height:65px;display:flex;justify-content:center;}
.turnstile-field .cf-turnstile{max-width:100%;}

/* ---------- Modal announcements (public popup) ---------- */
.badge-admin-gray{background:var(--badge-neutral-bg);color:var(--badge-neutral-text);}
body.ann-modal-open{overflow:hidden;}
.ann-modal{position:fixed;inset:0;z-index:1200;display:flex;align-items:center;justify-content:center;padding:20px;}
.ann-modal[hidden]{display:none;}
.ann-modal-backdrop{position:absolute;inset:0;background:rgba(0,0,0,.5);
  -webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);animation:annFade .22s ease-out;}
.ann-modal-box{position:relative;background:var(--bg-elevated);color:var(--color-text-primary);border-radius:var(--radius-lg);
  box-shadow:var(--shadow);width:100%;max-height:calc(100vh - 40px);overflow-y:auto;
  animation:annPop .22s ease-out;transform-origin:center;}
.ann-modal-box:focus{outline:none;}
.ann-size-small{max-width:400px;}
.ann-size-medium{max-width:560px;}
.ann-size-large{max-width:780px;}
.ann-size-fullscreen{max-width:none;width:calc(100vw - 40px);min-height:calc(100vh - 40px);}
.ann-modal-close{position:absolute;top:12px;right:12px;z-index:2;width:34px;height:34px;border-radius:999px;border:none;
  background:var(--bg-subtle);color:var(--color-text-primary);font-size:1.375rem;line-height:1;display:flex;
  align-items:center;justify-content:center;transition:background .15s ease,transform .15s ease,opacity .15s ease;opacity:.85;}
.ann-modal-close:hover{background:var(--border-default);opacity:1;transform:scale(1.08);}
/* Fixed 16:9 media band so images never stretch/squash regardless of source ratio. */
.ann-modal-media{aspect-ratio:16/9;overflow:hidden;border-radius:var(--radius-lg) var(--radius-lg) 0 0;background:var(--color-bg-alt);}
.ann-modal-media img{display:block;width:100%;height:100%;object-fit:cover;}
.ann-modal-body{padding:28px 30px 30px;}
.ann-modal-title{font-family:var(--font-heading);font-size:1.5rem;font-weight:700;margin:0 0 14px;line-height:1.3;}
.ann-modal-content{color:var(--color-text-secondary);line-height:var(--lh-body);font-size:1rem;}
.ann-modal-content img{max-width:100%;height:auto;border-radius:8px;}
.ann-modal-actions{margin-top:24px;display:flex;gap:10px;flex-wrap:wrap;}
.ann-modal-actions .btn{min-width:128px;justify-content:center;}
@keyframes annFade{from{opacity:0;}to{opacity:1;}}
@keyframes annPop{from{opacity:0;transform:scale(.95);}to{opacity:1;transform:scale(1);}}
@media(max-width:640px){
  .ann-modal{padding:12px;}
  .ann-modal-body{padding:22px 20px 24px;}
  .ann-modal-title{font-size:1.3rem;}
  .ann-modal-actions .btn{flex:1 1 auto;}
  .ann-size-fullscreen{width:calc(100vw - 24px);min-height:calc(100vh - 24px);}
}
@media(prefers-reduced-motion:reduce){
  .ann-modal-backdrop,.ann-modal-box{animation:none;}
  .ann-modal-close{transition:none;}
}

@media(max-width:860px){
  .stat-grid{grid-template-columns:repeat(2,1fr);}
  .admin-cols{grid-template-columns:1fr;}
  .grid-3{grid-template-columns:1fr;}
  .admin-sidebar{width:200px;}
}
@media(max-width:640px){
  .admin{flex-direction:column;}
  .admin-sidebar{width:100%;flex-direction:row;flex-wrap:wrap;border-right:none;border-bottom:1px solid var(--color-border);}
  .admin-brand{width:100%;padding-bottom:12px;}
  .admin-table .thead{display:none;}
  .admin-table .trow{grid-template-columns:1fr !important;gap:6px;}
  /* Single-column stack on mobile: let values wrap instead of truncating. */
  .admin-table .primary,.admin-table .sub,.admin-table .muted{white-space:normal;}
}

/* ============================================================
   404 / not found
   ------------------------------------------------------------
   One .container column (shared 1120px) holding a vertical stack
   of sections that all share the same left/right edges. Spacing
   uses the site scale (12 / 16 / 20 / 24 / 32 / 48 / 64); the
   section rhythm is one gap on the flex stack, so every gap is
   identical. Interactive pieces reuse the global .btn / .input /
   .article-card so the page stays inside one design system.
   ============================================================ */

/* Visually hidden but still read by screen readers. */
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0,0,0,0);white-space:nowrap;border:0;}

.notfound{position:relative;isolation:isolate;overflow:hidden;
  padding-block:clamp(32px,6vw,64px) clamp(56px,8vw,88px);
  background:
    radial-gradient(circle at 8% 8%,var(--color-accent-soft) 0,transparent 24rem),
    linear-gradient(180deg,var(--color-bg-alt) 0,transparent 30rem);}
/* The stack: equal vertical rhythm between every section. */
.notfound-inner{display:flex;flex-direction:column;gap:clamp(40px,6vw,64px);}

/* Hero and recovery tools form one continuous surface. This is the page's
   primary task area, so it receives the strongest visual weight. */
.nf-primary{position:relative;overflow:hidden;background:var(--color-bg);
  border:1px solid var(--color-border);border-radius:clamp(18px,3vw,28px);
  box-shadow:0 20px 60px rgba(11,18,32,.09);}

/* ---- 1. Hero ---- */
.nf-hero{display:grid;grid-template-columns:minmax(0,1.4fr) minmax(0,.6fr);
  gap:clamp(28px,5vw,56px);align-items:center;
  padding:clamp(28px,5vw,56px) clamp(24px,5vw,56px) clamp(24px,4vw,40px);}
.nf-hero-text{min-width:0;}
.nf-badge{display:inline-flex;align-items:center;font-family:var(--font-heading);
  font-weight:700;font-size:.8125rem;letter-spacing:.12em;text-transform:uppercase;
  color:var(--color-accent);background:var(--color-accent-soft);
  border:1px solid var(--color-accent-soft-strong);border-radius:999px;padding:6px 14px;}
.nf-title{font-family:var(--font-heading);font-size:clamp(2rem,4vw,3.25rem);font-weight:700;
  letter-spacing:-.025em;line-height:1.16;margin:18px 0 14px;}
.nf-lead{color:var(--color-text-secondary);font-size:1.0625rem;line-height:1.7;
  max-width:48ch;margin:0;}
.nf-path{margin:16px 0 0;font-size:.875rem;color:var(--color-text-secondary);}
.nf-path code{background:var(--color-bg-alt);border:1px solid var(--color-border);
  border-radius:6px;padding:3px 8px;font-size:.8125rem;word-break:break-all;
  color:var(--color-text-primary);}

/* Animated eyes: pupils track the pointer (offsets set by main.js as --px/--py
   in %) and blink on a loop. Sized in em off one font-size so the whole face
   scales together. */
.nf-hero-visual{position:relative;display:flex;align-items:center;justify-content:center;
  min-height:220px;border:1px solid var(--color-accent-soft-strong);border-radius:22px;
  background:linear-gradient(145deg,var(--color-accent-soft),var(--color-bg-alt));overflow:hidden;}
.nf-hero-visual::before{content:"404";position:absolute;inset:auto 50% 50%;
  transform:translate(50%,50%);font-family:var(--font-heading);font-size:clamp(7rem,15vw,11rem);
  font-weight:900;line-height:1;color:var(--color-accent);opacity:.08;letter-spacing:-.08em;}
.nf-eyes{display:flex;gap:clamp(12px,2.5vw,24px);font-size:clamp(40px,7vw,72px);
  position:relative;z-index:1;width:max-content;}
.nf-eye{position:relative;width:1.5em;height:1.5em;border-radius:50%;
  background:var(--color-bg);border:.14em solid var(--color-text-primary);
  overflow:hidden;animation:nf-blink 5s ease-in-out infinite;}
.nf-eye:last-child{animation-delay:.12s;}
.nf-pupil{position:absolute;top:50%;left:50%;width:.6em;height:.6em;border-radius:50%;
  background:var(--color-text-primary);
  transform:translate(-50%,-50%) translate(var(--px,0),var(--py,0));
  transition:transform .12s ease-out;}
/* Idle drift when the pointer isn't driving --px/--py (also the no-JS state). */
.nf-eyes:not(.is-tracking) .nf-pupil{animation:nf-glance 6s ease-in-out infinite;}
.nf-eyes:not(.is-tracking) .nf-eye:last-child .nf-pupil{animation-delay:-3s;}
@keyframes nf-blink{0%,92%,100%{transform:scaleY(1);}95%{transform:scaleY(.08);}}
@keyframes nf-glance{
  0%,100%{transform:translate(-50%,-50%) translate(-30%,-12%);}
  25%{transform:translate(-50%,-50%) translate(32%,-12%);}
  50%{transform:translate(-50%,-50%) translate(32%,20%);}
  75%{transform:translate(-50%,-50%) translate(-28%,20%);}
}

/* ---- 2. Search + actions card ---- */
.nf-search-card{background:var(--color-bg-alt);border:0;border-top:1px solid var(--color-border);
  border-radius:0;padding:clamp(22px,3vw,30px) clamp(24px,5vw,56px);}
.notfound-search{margin:0;}
.nf-field-label{display:block;font-weight:600;font-size:.9375rem;
  color:var(--color-text-primary);margin-bottom:10px;}
.nf-search-row{display:flex;gap:12px;}
.nf-search-row .input{flex:1 1 auto;min-width:0;}
/* One shared control height across input + every button on this page. */
.nf-search-row .input,.nf-btn{height:48px;}
.nf-search-row .nf-btn{flex:0 0 auto;}
.nf-actions{display:flex;gap:12px;flex-wrap:wrap;margin-top:20px;padding-top:20px;
  border-top:1px solid var(--color-border);}
.nf-actions .nf-btn{flex:0 0 auto;}

/* ---- Section heading (shared by recommend + popular) ---- */
.nf-section{margin:0;}
.nf-section-head{display:flex;align-items:center;gap:16px;font-family:var(--font-heading);
  font-size:1.5rem;font-weight:700;line-height:1.3;margin:0 0 22px;}
.nf-section-head::after{content:"";height:1px;flex:1 1 auto;background:var(--color-border);}

/* ---- 3. Recommended articles (reuses global .article-card) ---- */
.nf-reco-grid{grid-template-columns:repeat(3,minmax(0,1fr));gap:24px;}
/* Equal-height cards: uniform 16:9 cover, body flexes so the byline pins to the
   bottom, and title/excerpt are clamped so no card runs away in height. */
.nf-reco-grid .article-cover{height:auto;aspect-ratio:16/9;}
.nf-reco-grid .article-body{display:flex;flex-direction:column;flex:1 1 auto;}
.nf-reco-grid .article-title,
.nf-reco-grid .article-excerpt{display:-webkit-box;-webkit-box-orient:vertical;overflow:hidden;}
.nf-reco-grid .article-title{-webkit-line-clamp:2;}
.nf-reco-grid .article-excerpt{-webkit-line-clamp:2;}
/* Gentle staggered entrance — nothing abrupt. */
.nf-reco-grid .article-card{animation:nf-card-in .45s ease both;}
.nf-reco-grid .article-card:nth-child(2){animation-delay:.08s;}
.nf-reco-grid .article-card:nth-child(3){animation-delay:.16s;}
@keyframes nf-card-in{from{opacity:0;transform:translateY(12px);}to{opacity:1;transform:none;}}

/* ---- 4. Popular pages as navigation cards ---- */
.nf-nav-grid{list-style:none;margin:0;padding:0;display:grid;
  grid-template-columns:repeat(4,minmax(0,1fr));gap:16px;}
.nf-nav-card{display:flex;flex-direction:column;gap:10px;height:100%;box-sizing:border-box;
  padding:22px;border:1px solid var(--color-border);border-radius:16px;
  background:var(--color-bg);box-shadow:var(--shadow-sm);
  transition:border-color .15s,transform .15s,box-shadow .15s;}
.nf-nav-card:hover{border-color:var(--color-accent);transform:translateY(-3px);box-shadow:var(--shadow);}
.nf-nav-card:focus-visible{outline:2px solid var(--color-accent);outline-offset:2px;}
.nf-nav-icon{display:inline-flex;align-items:center;justify-content:center;
  width:40px;height:40px;border-radius:10px;flex:0 0 auto;
  background:var(--color-accent-soft);color:var(--color-accent);}
.nf-nav-icon svg{width:22px;height:22px;}
.nf-nav-title{display:flex;align-items:center;justify-content:space-between;gap:8px;
  font-weight:600;color:var(--color-text-primary);}
.nf-nav-arrow{color:var(--color-text-secondary);transition:transform .15s,color .15s;}
.nf-nav-card:hover .nf-nav-title{color:var(--color-accent);}
.nf-nav-card:hover .nf-nav-arrow{transform:translateX(3px);color:var(--color-accent);}
.nf-nav-desc{font-size:.875rem;line-height:1.55;color:var(--color-text-secondary);}

/* ---- Responsive ---- */
@media(max-width:1024px){
  .nf-reco-grid{grid-template-columns:repeat(2,minmax(0,1fr));}
  .nf-nav-grid{grid-template-columns:repeat(2,minmax(0,1fr));}
}
@media(max-width:880px){
  /* Stack the hero; eyes ride above the text, still left-anchored with it. */
  .nf-hero{grid-template-columns:1fr;gap:24px;}
  .nf-hero-visual{order:-1;min-height:160px;}
}
@media(max-width:640px){
  .notfound{padding-top:20px;}
  .nf-primary{border-radius:18px;}
  .nf-hero{padding:22px 20px 24px;}
  .nf-hero-visual{min-height:132px;border-radius:14px;}
  .nf-title{font-size:clamp(1.8rem,9vw,2.35rem);}
  .nf-search-card{padding:20px;}
  .nf-reco-grid{grid-template-columns:1fr;}
  .nf-nav-grid{grid-template-columns:1fr;}
  /* Field above button; both go full width so nothing gets squeezed. */
  .nf-search-row{flex-direction:column;}
  .nf-search-row .nf-btn{width:100%;}
  .nf-actions .nf-btn{flex:1 1 auto;}
}
@media(prefers-reduced-motion:reduce){
  .nf-nav-card,.nf-nav-arrow{transition:none;}
  .nf-nav-card:hover{transform:none;}
  /* Eyes stay open and still; pupils centre; cards render in place. */
  .nf-eye,.nf-pupil{animation:none;}
  .nf-pupil{transition:none;}
  .nf-reco-grid .article-card{animation:none;}
}

/* ============================================================
   Global search — header combobox, results page, filters
   ============================================================ */

/* ---------- Shared: type badges + match highlight ---------- */
.badge-type{display:inline-block;font-size:.68rem;font-weight:700;padding:2px 8px;border-radius:999px;
  letter-spacing:.02em;white-space:nowrap;line-height:1.5;}
.badge-type--app{background:var(--badge-info-bg);color:var(--badge-info-text);}
.badge-type--article{background:var(--badge-success-bg);color:var(--badge-success-text);}
.badge-type--download{background:var(--badge-warning-bg);color:var(--badge-warning-text);}
.badge-type--page,.badge-type--category,.badge-type--tag{background:var(--badge-neutral-bg);color:var(--badge-neutral-text);}
/* Highlighted matches: accent-coloured text (no yellow block) works in both themes */
.header-search mark,.search-results mark,.search-empty mark{background:transparent;color:var(--color-accent);font-weight:700;padding:0;}

/* ---------- Header search combobox ---------- */
/* Compact box sitting inline with the header actions (right cluster). Can shrink
   under pressure on tighter widths so the row never overflows. */
.header-search{position:relative;flex:0 1 200px;max-width:220px;min-width:0;}
.header-search__form{display:flex;align-items:center;gap:8px;height:44px;padding:0 10px 0 14px;
  background:var(--color-bg-alt);border:1px solid var(--color-border);border-radius:999px;
  transition:border-color .15s,box-shadow .15s;}
.header-search__form:focus-within{border-color:var(--color-accent);box-shadow:0 0 0 3px var(--color-accent-soft);}
.header-search__icon{width:18px;height:18px;color:var(--color-text-secondary);flex-shrink:0;}
.header-search__input{flex:1;min-width:0;height:100%;border:none;background:transparent;outline:none;
  font-family:var(--font-body);font-size:.9375rem;color:var(--color-text-primary);}
.header-search__input::-webkit-search-cancel-button{display:none;}
.header-search__clear{flex-shrink:0;width:26px;height:26px;border:none;border-radius:50%;background:transparent;
  color:var(--color-text-secondary);display:flex;align-items:center;justify-content:center;font-size:.75rem;}
.header-search__clear:hover{background:var(--color-border);color:var(--color-text-primary);opacity:1;}
/* The display:flex above is an author style, so it beats the UA [hidden] rule —
   without this the JS-driven `clearBtn.hidden` never hides anything. */
.header-search__clear[hidden]{display:none;}
/* Collapsed trigger — only rendered visible by the <=450px block below. */
.header-search__toggle{display:none;width:44px;height:44px;border-radius:8px;flex-shrink:0;
  border:1px solid var(--color-border);background:var(--color-bg);color:var(--color-text-primary);
  align-items:center;justify-content:center;-webkit-tap-highlight-color:transparent;
  transition:border-color .15s,color .15s,background .15s;}
.header-search__toggle:hover{border-color:var(--color-accent);color:var(--color-accent);}
.header-search__toggle svg{width:20px;height:20px;}

/* The dropdown is far wider than the compact field it hangs off: anchored to the
   field's right edge, at least as wide as the field, and clamped to the viewport
   so it never pushes the page sideways. */
.header-search__panel{position:absolute;top:calc(100% + 8px);left:auto;right:0;z-index:200;
  width:max(100%,min(440px,calc(100vw - 2 * var(--pad-x))));
  display:flex;flex-direction:column;max-height:min(72vh,540px);overflow:hidden;
  background:var(--bg-elevated);border:1px solid var(--color-border);border-radius:14px;box-shadow:var(--shadow);}
.header-search__panel[hidden]{display:none;}
/* overflow-x:hidden — long titles must ellipsis, never scroll the list sideways. */
.header-search__list{overflow-y:auto;overflow-x:hidden;overscroll-behavior:contain;padding-top:4px;}
.header-search__group-label{padding:10px 16px 4px;font-size:.72rem;font-weight:700;letter-spacing:.04em;
  text-transform:uppercase;color:var(--color-text-secondary);}
.search-option{display:flex;gap:12px;align-items:center;width:100%;padding:9px 16px;min-height:48px;
  border:none;background:transparent;text-align:left;cursor:pointer;color:inherit;scroll-margin:8px;}
.search-option:hover,.search-option.is-active{background:var(--color-accent-soft);opacity:1;}
.search-option__thumb{width:40px;height:40px;flex-shrink:0;border-radius:8px;overflow:hidden;
  display:flex;align-items:center;justify-content:center;background:var(--color-bg-alt);font-size:1.1rem;}
.search-option__thumb img{width:100%;height:100%;object-fit:cover;}
/* Must be a flex column: the title/desc spans are inline otherwise, and
   text-overflow:ellipsis is silently ignored on inline boxes (text then runs
   under the badge instead of truncating). */
.search-option__body{min-width:0;flex:1 1 auto;display:flex;flex-direction:column;gap:2px;}
.search-option__title{font-size:.9rem;font-weight:600;line-height:1.35;color:var(--color-text-primary);
  max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.search-option__desc{font-size:.8rem;line-height:1.4;color:var(--color-text-secondary);
  max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.search-option__badge{flex:0 0 auto;align-self:center;}
.search-option.search-see-all{justify-content:center;gap:6px;font-weight:600;font-size:.875rem;
  color:var(--color-accent);border-top:1px solid var(--color-border);position:sticky;bottom:0;
  background:var(--bg-elevated);min-height:48px;}
.search-option.search-see-all:hover,.search-option.search-see-all.is-active{background:var(--color-accent-soft);}
.header-search__status{padding:16px;text-align:center;font-size:.875rem;color:var(--color-text-secondary);}
.header-search__status--error{color:var(--color-danger);}
/* Autocomplete loading skeleton rows */
.hs-sk-row{display:flex;gap:12px;align-items:center;padding:9px 16px;min-height:48px;}
.hs-sk{background:linear-gradient(90deg,var(--color-bg-alt) 25%,var(--color-border) 37%,var(--color-bg-alt) 63%);
  background-size:400% 100%;border-radius:6px;animation:hs-shimmer 1.3s ease infinite;}
.hs-sk-thumb{width:40px;height:40px;border-radius:8px;flex-shrink:0;}
.hs-sk-lines{flex:1;display:flex;flex-direction:column;gap:6px;}
.hs-sk-line{height:11px;} .hs-sk-line.short{width:55%;}
@keyframes hs-shimmer{from{background-position:100% 0;}to{background-position:0 0;}}

/* ---------- Results page ---------- */
.search-page{padding-top:8px;padding-bottom:64px;}
.search-hero{display:flex;gap:12px;align-items:stretch;margin:24px 0 4px;}
.search-hero__field{flex:1;min-width:0;display:flex;align-items:center;gap:10px;padding:0 16px;
  background:var(--color-bg-alt);border:1px solid var(--color-border);border-radius:12px;
  transition:border-color .15s,box-shadow .15s;}
.search-hero__field:focus-within{border-color:var(--color-accent);box-shadow:0 0 0 3px var(--color-accent-soft);}
.search-hero__icon{width:20px;height:20px;color:var(--color-text-secondary);flex-shrink:0;}
.search-hero__input{flex:1;min-width:0;height:52px;border:none;background:transparent;outline:none;
  font-family:var(--font-body);font-size:1.05rem;color:var(--color-text-primary);}
.search-hero__input::-webkit-search-cancel-button{display:none;}
.search-hero .btn{flex-shrink:0;}
.search-summary{margin:18px 0;}
.search-summary h1{font-size:var(--fs-h2);font-weight:700;line-height:1.35;}
.search-summary__q{color:var(--color-accent);}

/* Toolbar: tabs + sort/category controls */
.search-toolbar{display:flex;justify-content:space-between;align-items:flex-end;gap:16px;flex-wrap:wrap;margin-bottom:18px;}
.search-tabs{display:flex;gap:6px;flex-wrap:wrap;}
.search-tab{display:inline-flex;align-items:center;gap:7px;padding:8px 14px;min-height:40px;border-radius:999px;
  font-size:.875rem;font-weight:500;color:var(--color-text-secondary);background:var(--color-bg-alt);
  border:1px solid transparent;transition:background .15s,color .15s;}
.search-tab:hover{color:var(--color-text-primary);opacity:1;}
.search-tab.is-active{background:var(--btn-primary-bg);color:var(--btn-primary-text);font-weight:600;}
.search-tab__count{font-size:.72rem;padding:1px 7px;border-radius:999px;background:rgba(120,120,120,.16);}
.search-tab.is-active .search-tab__count{background:rgba(255,255,255,.22);}
.search-controls{display:flex;gap:12px;align-items:flex-end;flex-wrap:wrap;}
.search-control{display:flex;flex-direction:column;gap:3px;font-size:.75rem;color:var(--color-text-secondary);}
.search-control .select{min-width:160px;padding:9px 12px;font-size:.9rem;}

/* Compact result list */
.search-results{display:flex;flex-direction:column;border:1px solid var(--color-border);border-radius:14px;
  overflow:hidden;background:var(--bg-elevated);}
.search-results--static{margin-top:4px;}
.search-result{display:flex;gap:14px;align-items:center;padding:14px 16px;color:inherit;
  border-bottom:1px solid var(--color-border);transition:background .12s;}
.search-result:last-child{border-bottom:none;}
.search-result:hover{background:var(--color-accent-soft);opacity:1;}
.search-result__thumb{width:56px;height:56px;flex-shrink:0;border-radius:10px;overflow:hidden;
  display:flex;align-items:center;justify-content:center;background:var(--color-bg-alt);font-size:1.5rem;}
.search-result__thumb img{width:100%;height:100%;object-fit:cover;}
.search-result__body{min-width:0;flex:1;}
.search-result__type{margin-bottom:5px;}
.search-result__title{font-weight:600;font-size:1rem;line-height:1.4;color:var(--color-text-primary);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.search-result__desc{font-size:.875rem;line-height:1.55;color:var(--color-text-secondary);margin-top:3px;
  display:-webkit-box;-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;}
.search-result__meta{display:flex;flex-wrap:wrap;align-items:center;gap:6px 12px;margin-top:7px;
  font-size:.75rem;color:var(--color-text-secondary);}
.search-result__cat{font-weight:600;color:var(--color-text-primary);}
.search-result__go{flex-shrink:0;color:var(--color-text-secondary);font-size:1.1rem;}
.search-more{display:flex;justify-content:center;padding:24px 0;}
.search-results[aria-busy="true"]{opacity:.7;}

/* Empty / landing state */
.search-empty{padding:8px 0 32px;}
.search-empty__block{margin-bottom:28px;}
.search-empty__block h2{font-size:1.05rem;font-weight:600;margin-bottom:12px;}
.search-chips{display:flex;flex-wrap:wrap;gap:10px;}
.search-chip{padding:8px 16px;min-height:40px;display:inline-flex;align-items:center;border-radius:999px;
  font-size:.875rem;font-weight:500;color:var(--color-text-secondary);background:var(--color-bg-alt);
  border:1px solid var(--color-border);}
.search-chip:hover{border-color:var(--color-accent);color:var(--color-accent);opacity:1;}
.search-chip--suggest{color:var(--color-accent);border-color:var(--color-accent);background:var(--color-accent-soft);font-weight:600;}
.search-empty__cta{display:flex;gap:12px;flex-wrap:wrap;margin:24px 0 28px;}

/* Mobile filter button + bottom sheet */
.search-filter-btn{display:none;}
.filter-sheet{position:fixed;inset:0;z-index:300;display:flex;align-items:flex-end;}
.filter-sheet[hidden]{display:none;}
.filter-sheet__backdrop{position:absolute;inset:0;background:rgba(11,18,32,.5);}
.filter-sheet__panel{position:relative;width:100%;max-height:82vh;overflow-y:auto;padding:4px 20px 28px;
  background:var(--bg-elevated);border-radius:20px 20px 0 0;animation:sheet-up .28s cubic-bezier(.4,0,.2,1);}
.filter-sheet__head{position:sticky;top:0;z-index:1;display:flex;justify-content:space-between;align-items:center;
  padding:14px 0;background:var(--bg-elevated);}
.filter-sheet__head h2{font-size:1.05rem;font-weight:700;}
.filter-sheet__close{width:44px;height:44px;border:none;background:transparent;color:var(--color-text-secondary);font-size:1.1rem;}
.filter-sheet .search-tabs{flex-direction:column;}
.filter-sheet .search-tab{justify-content:space-between;}
.filter-sheet .search-controls{flex-direction:column;align-items:stretch;margin-top:18px;}
.filter-sheet .search-control{width:100%;}
.filter-sheet .search-control .select{width:100%;}
@keyframes sheet-up{from{transform:translateY(100%);}to{transform:translateY(0);}}

/* ---------- Admin: synonym rows ---------- */
.syn-row{display:flex;gap:10px;align-items:center;flex-wrap:wrap;}
.syn-row .input{flex:1 1 220px;min-width:0;}
.syn-row .input[name="label"]{flex:0 1 180px;}
.syn-row .select{flex:0 0 auto;width:auto;min-width:110px;}
.syn-row--new{padding:16px;border:1px dashed var(--color-border);border-radius:12px;margin-bottom:18px;}
.syn-list{display:flex;flex-direction:column;gap:10px;}
.syn-item{display:flex;gap:10px;align-items:center;flex-wrap:wrap;padding:12px;border:1px solid var(--color-border);border-radius:12px;}
.syn-item form:first-child{flex:1;min-width:0;}

/* ---------- Responsive ---------- */
/* Mid widths: the nav has collapsed into the hamburger (see the nav collapse
   point in the header section), so the brand tagline goes and the right cluster
   takes over the freed row. */
@media(max-width:1080px){
  .brand-tag{display:none;}
  .brand-logo{height:30px;}
  .header-right{flex:1 1 auto;gap:8px;justify-content:flex-end;}
  .header-search{flex:1 1 auto;max-width:none;}
}
@media(max-width:880px){
  .search-toolbar{display:none;}          /* replaced by the bottom-sheet on mobile */
  .search-filter-btn{display:flex;width:100%;justify-content:space-between;margin-bottom:16px;}
  .search-filter-btn__summary{color:var(--color-text-secondary);font-weight:500;}
}
@media(min-width:881px){
  .filter-sheet{display:none !important;}  /* the sheet is a mobile-only surface */
}
@media(max-width:560px){
  .search-hero{flex-direction:column;}
  .search-hero .btn{width:100%;}
  .search-result__thumb{width:46px;height:46px;font-size:1.25rem;}
}
/* Narrow phones: the search field eats the whole row and squeezes the brand, so
   it collapses to a magnifier button. Tapping it (JS adds .is-open) lays the
   field over the header row — .header-search goes static so both the field and
   its suggestion panel are positioned against .site-header and can span the
   full width instead of the button's 44px. */
@media(max-width:450px){
  .brand-logo{height:26px;}
  .header-search{position:static;flex:0 0 auto;max-width:none;}
  .header-search__toggle{display:inline-flex;}
  .header-search__form{display:none;}
  /* Opaque fill (not the glass tint) — the field sits over the brand here. */
  .site-header .header-search.is-open .header-search__form{
    display:flex;position:absolute;top:16px;left:var(--pad-x);right:var(--pad-x);z-index:2;
    background:var(--bg-elevated);border-color:var(--color-border);}
  /* width:auto so the left/right pair (not the desktop min-width) sizes it. */
  .header-search.is-open .header-search__panel{left:var(--pad-x);right:var(--pad-x);width:auto;}
  /* While open the field covers the toggle; the ✕ becomes the way back out. */
  .header-search.is-open .header-search__clear{display:flex;}
}
@media(prefers-reduced-motion:reduce){
  .filter-sheet__panel{animation:none;}
  .hs-sk{animation:none;}
  .search-result,.search-tab,.header-search__form,.search-hero__field{transition:none;}
}

/* ============================================================
   Cookie consent (PDPA) — banner, modal, toggle switch
   ============================================================ */
.consent-banner{position:fixed;left:0;right:0;bottom:0;z-index:1200;
  background:var(--bg-elevated,var(--color-bg));border-top:1px solid var(--color-border);
  box-shadow:0 -6px 24px rgba(11,18,32,.10);padding:16px clamp(14px,4vw,28px);
  animation:consent-rise .28s ease;}
@media(prefers-reduced-motion:reduce){.consent-banner{animation:none;}}
@keyframes consent-rise{from{transform:translateY(100%);}to{transform:translateY(0);}}
.consent-banner__inner{max-width:1080px;margin:0 auto;display:flex;gap:18px;align-items:center;
  flex-wrap:wrap;justify-content:space-between;}
.consent-banner__text{flex:1 1 380px;min-width:0;font-size:.9rem;line-height:1.6;color:var(--color-text);}
.consent-banner__title{font-weight:700;margin:0 0 4px;font-size:.95rem;}
.consent-banner__text p{margin:0;}
.consent-banner__link{color:var(--link-color);text-decoration:underline;white-space:nowrap;}
.consent-banner__actions{display:flex;gap:8px;flex-wrap:wrap;align-items:center;}
.consent-banner__actions .btn{flex:0 0 auto;}
@media(max-width:560px){
  .consent-banner__actions{width:100%;}
  .consent-banner__actions .btn{flex:1 1 auto;justify-content:center;}
}

/* consent settings modal */
.consent-modal{position:fixed;inset:0;z-index:1300;display:flex;align-items:center;justify-content:center;padding:16px;}
.consent-modal[hidden]{display:none;}
.consent-modal__backdrop{position:absolute;inset:0;background:rgba(11,18,32,.55);backdrop-filter:blur(2px);}
.consent-modal__box{position:relative;background:var(--bg-elevated,var(--color-bg));color:var(--color-text);
  border-radius:var(--radius-lg);box-shadow:var(--shadow);width:100%;max-width:520px;
  max-height:calc(100vh - 40px);overflow-y:auto;padding:26px;animation:consent-pop .2s ease;}
@media(prefers-reduced-motion:reduce){.consent-modal__box{animation:none;}}
@keyframes consent-pop{from{opacity:0;transform:scale(.97);}to{opacity:1;transform:scale(1);}}
.consent-modal__close{position:absolute;top:12px;right:14px;background:none;border:0;font-size:1.6rem;
  line-height:1;cursor:pointer;color:var(--color-text-secondary);}
.consent-modal__title{margin:0 0 6px;font-size:1.2rem;}
.consent-modal__lead{margin:0 0 18px;font-size:.875rem;}
.consent-modal__actions{display:flex;gap:10px;justify-content:flex-end;margin-top:20px;flex-wrap:wrap;}
.consent-modal__note{margin:14px 0 0;font-size:.8125rem;}
.consent-cat{border:1px solid var(--color-border);border-radius:var(--radius);padding:14px 16px;margin-bottom:12px;background:var(--color-bg);}
.consent-cat__head{display:flex;align-items:center;justify-content:space-between;gap:12px;}
.consent-cat__name{font-weight:600;font-size:.95rem;}
.consent-cat__always{font-size:.75rem;color:var(--color-success,#16794b);font-weight:600;}
.consent-cat__desc{margin:8px 0 0;font-size:.8125rem;line-height:1.55;}

/* toggle switch */
.consent-switch{display:inline-flex;align-items:center;gap:8px;cursor:pointer;}
.consent-switch input{position:absolute;opacity:0;width:0;height:0;}
.consent-switch__slider{width:42px;height:24px;border-radius:999px;background:var(--color-border);
  position:relative;transition:background .2s;flex:0 0 auto;}
.consent-switch__slider::after{content:"";position:absolute;top:2px;left:2px;width:20px;height:20px;
  border-radius:50%;background:#fff;box-shadow:0 1px 3px rgba(0,0,0,.25);transition:transform .2s;}
.consent-switch input:checked + .consent-switch__slider{background:var(--color-primary-500);}
.consent-switch input:checked + .consent-switch__slider::after{transform:translateX(18px);}
.consent-switch input:focus-visible + .consent-switch__slider{outline:2px solid var(--color-primary-500);outline-offset:2px;}
.consent-switch__label{font-size:.8125rem;color:var(--color-text-secondary);min-width:22px;}

/* footer "manage cookies" button */
.footer-linklike{background:none;border:0;padding:0;color:inherit;cursor:pointer;font:inherit;text-decoration:underline;}
.footer-linklike:hover{color:var(--link-hover,var(--color-primary-600));}

/* ============================================================
   Privacy admin (tabs, cards, switch rows, live preview)
   ============================================================ */
.admin-tabs{display:flex;gap:6px;flex-wrap:wrap;margin:20px 0 16px;border-bottom:1px solid var(--color-border);}
.admin-tab{background:none;border:0;border-bottom:2px solid transparent;padding:10px 14px;cursor:pointer;
  font:inherit;font-weight:600;color:var(--color-text-secondary);margin-bottom:-1px;}
.admin-tab:hover{color:var(--color-text);}
.admin-tab.active{color:var(--color-primary-600);border-bottom-color:var(--color-primary-500);}
.admin-card{background:var(--panel,var(--color-bg));border:1px solid var(--color-border);border-radius:12px;
  padding:20px 22px;box-shadow:var(--shadow-sm);}
.switch-row{display:inline-flex;align-items:center;gap:10px;cursor:pointer;font-weight:600;}
.switch-row input{width:18px;height:18px;}
.privacy-panel{padding-top:4px;}
.privacy-preview{margin-top:22px;border:1px dashed var(--color-border);border-radius:12px;padding:16px;background:var(--color-bg-alt,transparent);}
.privacy-preview__label{font-size:.75rem;font-weight:700;letter-spacing:.03em;text-transform:uppercase;
  color:var(--color-text-secondary);margin-bottom:12px;}
.consent-banner--preview{position:static;box-shadow:none;border:1px solid var(--color-border);border-radius:10px;animation:none;}
.privacy-actions{display:flex;gap:16px;align-items:flex-end;flex-wrap:wrap;margin-top:24px;
  background:var(--panel,var(--color-bg));border:1px solid var(--color-border);border-radius:12px;
  padding:18px 22px;box-shadow:var(--shadow-sm);}
.privacy-actions__btns{display:flex;gap:10px;flex-wrap:wrap;}
.admin-form .privacy-panel .admin-card + .admin-card{margin-top:16px;}

/* public privacy policy page */
.privacy-policy .privacy-intro{font-size:1.05rem;color:var(--color-text-secondary);}
.privacy-policy .privacy-dates{font-size:.85rem;margin-top:-6px;}

/* ============================================================
   BiteBrief Recommend — Featured article hero section
   ============================================================ */
/* แถบพื้นหลังไล่สีเต็มความกว้าง (full-bleed) ครอบ .container ของ section นี้
   ใช้สีหลัก --color-primary-500 ไล่จากเข้มด้านบนไปจนโปร่งใสด้านล่าง
   (โทนสีเปลี่ยนตามธีมเองเพราะ token ตัวนี้มีค่าทั้ง light/dark) */
.bite-brief-section{
  --bb-tint:var(--color-primary-500);
  width:100%;
  background-image:linear-gradient(180.16deg,
    color-mix(in srgb,var(--bb-tint) 20%,transparent) 10.84%,
    color-mix(in srgb,var(--bb-tint) 12%,transparent) 40.31%,
    color-mix(in srgb,var(--bb-tint) 5%,transparent) 68.69%,
    transparent 96.82%);
  background-repeat:no-repeat;
  margin-top:0;
  padding-top:16px;
  margin-bottom:clamp(16px,3vw,24px);
  overflow:hidden;
}
/* ธีมมืดใช้สีหลักเวอร์ชันสว่าง (#5b93ec) — ลดความเข้มลงไม่ให้แถบสีเด่นกว่าเนื้อหา */
[data-theme="dark"] .bite-brief-section{
  background-image:linear-gradient(180.16deg,
    color-mix(in srgb,var(--bb-tint) 13%,transparent) 10.84%,
    color-mix(in srgb,var(--bb-tint) 8%,transparent) 40.31%,
    color-mix(in srgb,var(--bb-tint) 4%,transparent) 68.69%,
    transparent 96.82%);
}
/* section นี้มี padding-top ของแถบสีอยู่แล้ว — ไม่ต้องเว้นบนซ้ำอีกชั้น */
.bite-brief-section > .container.section{padding-top:clamp(24px,4vw,36px);}

.bite-brief-hero{
  display:grid;
  grid-template-columns:1fr 340px;
  gap:28px;
  align-items:start;
}

/* Featured article carousel (สูงสุด 5 สไลด์ — ใช้ตัวเลื่อนตัวเดียวกับ hero banner ใน main.js) */
.bite-brief-carousel{
  position:relative;
  overflow:hidden;
  border-radius:var(--radius-lg);
  transition:height .5s cubic-bezier(.4,0,.2,1);
}
/* flex-start = แต่ละสไลด์สูงตามเนื้อหาตัวเอง แล้ว main.js ปรับความสูงของ carousel
   ให้เท่าสไลด์ที่กำลังแสดง (แบบเดียวกับ hero banner) จึงไม่มีที่ว่างใต้การ์ดสั้น */
.bite-brief-carousel-track{
  display:flex;
  align-items:flex-start;
  transition:transform .55s cubic-bezier(.4,0,.2,1);
  will-change:transform;
}
.bite-brief-carousel-track > .bite-brief-main-card{flex:0 0 100%;min-width:0;}
.bite-brief-carousel.is-single .bite-brief-carousel-track{transition:none;}

/* กล่องนี้ทับพอดีกับรูปปกของสไลด์ (top:0 + aspect-ratio เท่ากับ .bite-brief-main-cover) */
.bite-brief-carousel-nav{
  position:absolute;
  inset:0 0 auto 0;
  aspect-ratio:16/9;
  z-index:5;
  display:flex;
  align-items:center;
  justify-content:space-between;
  padding:0 14px;
  pointer-events:none;
}
.bite-brief-arrow{
  pointer-events:auto;
  width:44px;height:44px;border-radius:50%;border:none;
  background:rgba(255,255,255,.9);color:#0B1220;font-size:1.55rem;line-height:1;
  display:flex;align-items:center;justify-content:center;cursor:pointer;
  box-shadow:0 2px 12px rgba(11,18,32,.18);transition:background .15s;
}
.bite-brief-arrow:hover{background:#fff;}
.bite-brief-dots{
  pointer-events:auto;
  position:absolute;left:0;right:0;bottom:14px;
  display:flex;gap:8px;justify-content:center;padding:0 16px;
}
.bite-brief-dot{
  width:9px;height:9px;padding:0;border:none;border-radius:50%;cursor:pointer;
  background:rgba(255,255,255,.75);box-shadow:0 0 0 1.5px rgba(11,18,32,.35);
  transition:background .2s,width .2s,box-shadow .2s;
}
.bite-brief-dot.active{
  width:24px;border-radius:5px;background:var(--color-accent);
  box-shadow:0 0 0 1.5px rgba(255,255,255,.8);
}

.bite-brief-main-card{
  display:grid;
  grid-template-columns:1fr;
  grid-template-rows:auto 1fr;
  border-radius:var(--radius-lg);
  overflow:hidden;
  border:1px solid var(--color-border);
  background:var(--color-bg);
  box-shadow:var(--shadow-sm);
  transition:box-shadow .15s,border-color .15s;
  height:100%;
}
.bite-brief-main-card:hover{
  box-shadow:var(--shadow);
  border-color:var(--color-accent);
}

.bite-brief-main-cover{
  width:100%;
  aspect-ratio:16/9;
  background-size:cover;
  background-position:center;
  background-color:var(--color-bg-secondary);
}

.bite-brief-main-body{
  display:flex;
  flex-direction:column;
  gap:14px;
  padding:24px;
}

.bite-brief-cat{
  font-size:.75rem;
  font-weight:700;
  letter-spacing:.03em;
  text-transform:uppercase;
  color:var(--color-accent);
  margin:0;
}

.bite-brief-title{
  font-size:var(--fs-h3);
  font-weight:700;
  line-height:1.4;
  margin:0;
  color:var(--color-text-primary);
  flex-grow:1;
}

.bite-brief-excerpt{
  font-size:.95rem;
  color:var(--color-text-secondary);
  margin:0;
  line-height:1.6;
  display:-webkit-box;
  -webkit-line-clamp:2;
  -webkit-box-orient:vertical;
  overflow:hidden;
}

.bite-brief-meta{
  display:flex;
  gap:12px;
  align-items:center;
  margin-top:auto;
  padding-top:12px;
  border-top:1px solid var(--color-border);
}

.bite-brief-meta-text{
  display:flex;
  flex-direction:column;
  gap:2px;
  min-width:0;
}

.bite-brief-author{
  font-size:.8125rem;
  font-weight:600;
  color:var(--color-text-primary);
  white-space:nowrap;
  overflow:hidden;
  text-overflow:ellipsis;
}

.bite-brief-date{
  font-size:.75rem;
  color:var(--color-text-secondary);
}

/* "ข่าวล่าสุด" sidebar */
.bite-brief-sidebar{
  display:flex;
  flex-direction:column;
  gap:12px;
  min-width:0;
}

.bite-brief-sidebar-title{
  font-weight:700;
  font-size:1.3rem;
  color:var(--color-text-secondary);
  text-transform:uppercase;
  letter-spacing:.02em;
  padding:0 4px;
}

/* รายการเลื่อนในกล่องตัวเอง: สูงพอ ๆ กับการ์ดใหญ่ข้าง ๆ แล้วให้ที่เหลือเลื่อนดู
   ชุดถัดไปโหลดตอนเลื่อนใกล้ท้ายรายการเท่านั้น (initLatestFeed ใน main.js) */
.bite-brief-related{
  display:flex;
  flex-direction:column;
  gap:12px;
  max-height:clamp(360px,54vh,450px);
  overflow-y:auto;
  overscroll-behavior:contain;
  padding-right:6px;              /* กันสกอลบาร์ทับขอบการ์ด */
  scrollbar-width:thin;
  scrollbar-color:var(--color-border) transparent;
}
.bite-brief-related::-webkit-scrollbar{width:6px;}
.bite-brief-related::-webkit-scrollbar-track{background:transparent;}
.bite-brief-related::-webkit-scrollbar-thumb{background:var(--color-border);border-radius:3px;}
.bite-brief-related:hover::-webkit-scrollbar-thumb{background:var(--color-text-secondary);}

/* จุดสังเกตท้ายรายการ (ตัวกระตุ้นโหลดชุดถัดไป) — กินที่เท่าเส้นเดียว
   grid-column: เผื่อเลย์เอาต์กริดของจอเล็ก จะได้ไม่ไปกินช่องการ์ด */
.bite-brief-feed-sentinel{height:1px;flex:0 0 auto;grid-column:1/-1;}

/* โครงการ์ดระหว่างรอชุดถัดไป — รูปทรงเดียวกับ .bite-brief-item เพื่อไม่ให้รายการกระตุก */
.bite-brief-item-sk{
  display:flex;
  align-items:center;
  gap:12px;
  flex:0 0 auto;
  grid-column:1/-1;
  padding:10px;
  border:1px solid var(--color-border);
  border-radius:var(--radius);
  background:var(--color-bg);
}
.bite-brief-item-sk .sk-thumb{width:80px;height:60px;flex:0 0 auto;}
.bite-brief-item-sk .sk-lines{flex:1;display:flex;flex-direction:column;gap:8px;min-width:0;}
.bite-brief-item-sk .sk-line{height:12px;width:100%;}
.bite-brief-item-sk .sk-line.short{width:45%;}

.bite-brief-item{
  display:flex;
  flex:0 0 auto;                  /* กล่องเลื่อนเต็ม = ห้ามบีบการ์ดให้เตี้ยลง */
  gap:12px;
  border-radius:var(--radius);
  border:1px solid var(--color-border);
  overflow:hidden;
  background:var(--color-bg);
  padding:10px;
  transition:border-color .15s,box-shadow .15s;
}
.bite-brief-item:hover{
  border-color:var(--color-accent);
  box-shadow:var(--shadow-sm);
}

.bite-brief-item-cover{
  width:80px;
  height:60px;
  flex-shrink:0;
  border-radius:var(--radius);
  background-size:cover;
  background-position:center;
  background-color:var(--color-bg-secondary);
}

.bite-brief-item-body{
  display:flex;
  flex-direction:column;
  gap:6px;
  min-width:0;
  flex:1;
  justify-content:center;
}

.bite-brief-item-title{
  font-size:.875rem;
  font-weight:600;
  color:var(--color-text-primary);
  line-height:1.4;
  display:-webkit-box;
  -webkit-line-clamp:2;
  -webkit-box-orient:vertical;
  overflow:hidden;
}

.bite-brief-item-date{
  font-size:.75rem;
  color:var(--color-text-secondary);
  white-space:nowrap;
}

/* Responsive: stack sidebar below main on mobile
   พอ sidebar ลงมาอยู่ใต้การ์ดหลัก มันได้ความกว้างเต็มแถวแล้ว — ลิสต์รูปเล็ก 80×60
   จะดูจิ๋วเกินไป จึงสลับเป็นการ์ดแนวตั้ง 2 คอลัมน์ (รูป 16/9 เต็มความกว้างการ์ด) */
@media(max-width:1024px){
  .bite-brief-hero{
    grid-template-columns:1fr;
    gap:24px;
  }

  /* จอเล็ก/แท็บเล็ต: sidebar ลงมาต่อท้ายการ์ดใหญ่เต็มความกว้าง แล้วกลายเป็น "รางเลื่อนแนวนอน"
     การ์ดเรียงข้างกันแล้วปัดนิ้วดูต่อ — snap ทีละใบ และ initLatestRail ใน main.js จะค่อย ๆ
     เลื่อนให้เองช้า ๆ (หยุดทันทีที่ผู้อ่านแตะ/เลื่อนเอง) ชุดถัดไปโหลดตอนเลื่อนใกล้ท้ายราง */
  .bite-brief-related{
    display:flex;
    flex-direction:row;
    gap:14px;
    max-height:none;
    overflow-x:auto;
    overflow-y:hidden;
    scroll-snap-type:x mandatory;
    -webkit-overflow-scrolling:touch;
    padding:2px 2px 10px;
    scroll-padding-left:2px;
    /* สกอลบาร์แนวนอนดูรก — ซ่อนไว้ ขอบการ์ดใบถัดไปที่โผล่มาเป็นตัวบอกว่ายังเลื่อนได้ */
    scrollbar-width:none;
  }
  .bite-brief-related::-webkit-scrollbar{height:0;width:0;}

  .bite-brief-item{
    flex-direction:column;
    gap:0;
    padding:0;
    flex:0 0 42%;                 /* แท็บเล็ต: เห็นราว 2 ใบครึ่ง เป็นสัญญาณว่ายังเลื่อนต่อได้ */
    scroll-snap-align:start;
  }

  .bite-brief-item-sk{padding:0;flex-direction:column;align-items:stretch;gap:0;flex:0 0 42%;}
  .bite-brief-item-sk .sk-thumb{width:100%;height:auto;aspect-ratio:16/9;border-radius:0;}
  .bite-brief-item-sk .sk-lines{padding:12px 14px 14px;}

  /* sentinel ย้ายไปอยู่ท้ายราง — เป็นเส้นบางแนวตั้ง จะได้ไม่ดันความกว้างราง */
  .bite-brief-feed-sentinel{width:1px;height:auto;align-self:stretch;flex:0 0 1px;}

  /* จอเล็ก: รางการ์ดต่อจากการ์ดใหญ่อยู่แล้ว หัวข้อ "Feature" จึงเป็นบรรทัดเกิน — ซ่อนไว้
     (เดสก์ท็อปยังต้องใช้ เพราะรายการอยู่ข้าง ๆ ไม่มีอะไรบอกว่ามันคือส่วนอะไร) */
  .bite-brief-sidebar-title{display:none;}

  .bite-brief-item-cover{
    width:100%;
    height:auto;
    aspect-ratio:16/9;
    border-radius:0;
  }

  .bite-brief-item-body{
    justify-content:flex-start;
    padding:12px 14px 14px;
    gap:8px;
  }

  .bite-brief-item-title{
    font-size:1rem;
    -webkit-line-clamp:3;
  }
}

@media(max-width:640px){
  .bite-brief-main-body{
    padding:16px;
    gap:12px;
  }

  .bite-brief-title{
    font-size:1.25rem;
  }

  .bite-brief-excerpt{
    font-size:.9rem;
  }

  .bite-brief-sidebar{
    gap:10px;
  }

  .bite-brief-related{
    gap:12px;
  }

  /* มือถือ: หนึ่งใบเกือบเต็มจอ เหลือขอบให้เห็นใบถัดไปนิดหน่อย */
  .bite-brief-item,
  .bite-brief-item-sk{
    flex-basis:78%;
  }

  .bite-brief-item-body{
    padding:10px 12px 12px;
    gap:6px;
  }

  .bite-brief-item-title{
    font-size:.9375rem;
  }

  .bite-brief-carousel-nav{padding:0 8px;}

  .bite-brief-arrow{width:36px;height:36px;font-size:1.25rem;}

  .bite-brief-dots{bottom:10px;}
}

/* จอแคบมาก (มือถือเล็ก) — การ์ดกว้างขึ้นอีกนิด รูปจะได้ไม่จิ๋ว */
@media(max-width:480px){
  .bite-brief-item,
  .bite-brief-item-sk{
    flex-basis:84%;
  }

  .bite-brief-item-title{
    font-size:1rem;
    -webkit-line-clamp:2;
  }
}
.privacy-section{margin-top:26px;}
.privacy-section h2{font-size:1.2rem;margin-bottom:8px;}

/* external embed placeholder */
.embed-placeholder{border:1px solid var(--color-border);border-radius:12px;background:var(--color-bg-alt,var(--color-bg));
  aspect-ratio:16/9;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;
  text-align:center;padding:20px;color:var(--color-text-secondary);}

/* ============================================================
   Back-to-top button (shared across public / admin / auth)
   ------------------------------------------------------------
   Fixed bottom-right; hidden until JS toggles .is-visible on scroll.
   z-index sits above the sticky header (100) but below modals and the
   consent banner (1000+), so overlays always win.
   ============================================================ */
.back-to-top{position:fixed;right:clamp(16px,3vw,28px);bottom:clamp(32px,6vw,56px);z-index:900;
  width:46px;height:46px;display:flex;align-items:center;justify-content:center;
  border:1px solid var(--color-border);border-radius:999px;
  background:var(--btn-primary-bg);color:var(--btn-primary-text);cursor:pointer;
  box-shadow:0 6px 20px rgba(11,18,32,.18);
  opacity:0;transform:translateY(12px);pointer-events:none;
  transition:opacity .22s ease,transform .22s ease,background .15s ease;}
.back-to-top[hidden]{display:none;}
.back-to-top.is-visible{opacity:1;transform:translateY(0);pointer-events:auto;}
.back-to-top:hover{background:var(--btn-primary-hover);}
.back-to-top:focus-visible{outline:2px solid var(--color-accent);outline-offset:2px;}
.back-to-top svg{display:block;}
@media(prefers-reduced-motion:reduce){
  .back-to-top{transition:opacity .22s ease;transform:none;}
  .back-to-top.is-visible{transform:none;}
}

/* ============================================================
   Site controls (Admin › จัดการเว็บไซต์) + the public pages they drive:
   the maintenance notice and the welcome/splash gate.
   ============================================================ */

/* ---------- Admin: control centre ---------- */
.site-control{border:1px solid var(--color-border);border-radius:12px;overflow:hidden;
  background:var(--panel,var(--color-bg));box-shadow:var(--shadow-sm);}
.site-control__row{display:flex;align-items:center;gap:20px;flex-wrap:wrap;padding:18px 22px;}
.site-control__row + .site-control__row{border-top:1px solid var(--color-border);}
.site-control__info{flex:1 1 320px;min-width:0;}
.site-control__name{display:flex;align-items:center;gap:10px;flex-wrap:wrap;font-weight:700;margin-bottom:6px;}
.site-control__desc{color:var(--color-text-secondary);font-size:.8125rem;line-height:1.7;}
.site-control__actions{display:flex;align-items:center;gap:10px;flex-wrap:wrap;}
.site-token{display:flex;gap:10px;align-items:center;flex-wrap:wrap;}
.site-token .input{flex:1 1 320px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.8125rem;}
.site-thumb{margin-top:10px;display:flex;align-items:center;gap:14px;flex-wrap:wrap;}
.site-thumb img{width:150px;height:84px;object-fit:cover;border-radius:8px;border:1px solid var(--color-border);}

/* ---------- Admin: brand artwork preview ----------
   Logos are usually transparent, so each preview sits on an explicit light or
   dark plate: that is the only way to judge, before saving, whether the file
   will actually be visible against the header it will end up on. */
.brand-preview{display:flex;gap:14px;flex-wrap:wrap;margin-top:10px;}
.brand-preview__plate{display:grid;place-items:center;min-width:170px;min-height:78px;padding:12px 16px;
  border-radius:10px;border:1px solid var(--color-border);}
.brand-preview__plate--light{background:#fff;}
.brand-preview__plate--dark{background:#0f141c;border-color:#2a3446;}
.brand-preview__plate img{max-height:44px;max-width:190px;width:auto;height:auto;object-fit:contain;}
.brand-preview__plate--icon img{max-height:56px;max-width:56px;}
.brand-preview__label{display:block;margin-top:8px;text-align:center;font-size:.6875rem;letter-spacing:.06em;
  text-transform:uppercase;color:var(--color-text-secondary);}
.brand-current{color:var(--color-text-secondary);font-size:.8125rem;margin:10px 0 0;word-break:break-all;}

/* ---------- Public: "you are browsing through maintenance" strip ---------- */
.maint-bar{display:flex;align-items:center;justify-content:center;gap:10px;flex-wrap:wrap;
  padding:9px 16px;font-size:.8125rem;text-align:center;
  background:var(--badge-warning-bg,#fef3c7);color:var(--badge-warning-text,#92400e);
  border-bottom:1px solid var(--color-border);}
.maint-bar a{color:inherit;text-decoration:underline;font-weight:600;}
.maint-bar__dot{width:8px;height:8px;border-radius:50%;background:currentColor;flex-shrink:0;}

/* ---------- Public: maintenance notice (503) ---------- */
.maint{min-height:100vh;display:flex;align-items:center;justify-content:center;padding:40px 20px;}
.maint__card{width:100%;max-width:560px;text-align:center;}
.maint__icon{width:72px;height:72px;margin:0 auto 24px;border-radius:50%;
  display:flex;align-items:center;justify-content:center;
  background:var(--color-accent-soft,rgba(37,99,235,.1));color:var(--color-accent);}
.maint__icon svg{width:34px;height:34px;}
.maint__title{font-family:var(--font-heading);font-size:clamp(1.5rem,4vw,2rem);line-height:1.35;margin:0 0 14px;}
.maint__message{color:var(--color-text-secondary);line-height:1.85;margin-bottom:26px;}
.maint__message p:last-child{margin-bottom:0;}
.maint__eta{border:1px dashed var(--color-border);border-radius:12px;padding:16px 18px;margin-bottom:26px;
  display:flex;flex-direction:column;gap:4px;}
.maint__eta-label{font-size:.75rem;letter-spacing:.03em;text-transform:uppercase;font-weight:700;
  color:var(--color-text-secondary);}
.maint__eta-time{font-size:1.0625rem;}
.maint__countdown{font-size:.875rem;color:var(--color-text-secondary);}
.maint__actions{display:flex;gap:10px;justify-content:center;flex-wrap:wrap;margin-bottom:32px;}
.maint__foot{font-size:.8125rem;color:var(--color-text-secondary);margin:0;}
.maint__preview-bar{padding:10px 16px;text-align:center;font-size:.875rem;
  background:var(--badge-warning-bg,#fef3c7);color:var(--badge-warning-text,#92400e);}

/* ---------- Public: welcome / splash gate ---------- */
.splash-body{margin:0;}
.splash{min-height:100vh;display:flex;align-items:center;justify-content:center;
  padding:48px 20px;background:var(--color-bg);}
.splash__card{width:100%;max-width:620px;text-align:center;}
.splash__title{font-family:var(--font-heading);font-size:clamp(1.75rem,5vw,2.75rem);line-height:1.3;margin:0 0 16px;}
.splash__subtitle{font-size:clamp(1rem,2.2vw,1.1875rem);line-height:1.75;margin:0 0 20px;
  color:var(--color-text-secondary);}
.splash__message{line-height:1.85;margin-bottom:12px;color:var(--color-text-secondary);}
.splash__message p:last-child{margin-bottom:0;}
.splash__actions{margin-top:32px;}
.splash__enter{min-width:220px;padding:14px 32px;font-size:1.0625rem;}
.splash__auto{margin:18px 0 0;font-size:.875rem;color:var(--color-text-secondary);}
.splash__cancel{background:none;border:0;padding:0;font:inherit;color:inherit;
  text-decoration:underline;cursor:pointer;}
.splash__cancel:hover{color:var(--color-accent);}

/* ---------- Image lightbox (คลิกรูปในเนื้อหาเพื่อดูขนาดใหญ่) ---------- */
/* .is-zoomable is stamped by initImageLightbox() in main.js — with scripting off
   nothing is marked, so content images keep behaving exactly as they do now.
   The overlay is dark in both themes on purpose: a photo reads best against a
   neutral surround, so it does not follow --color-bg. */
.prose-content img.is-zoomable,.article-hero img.is-zoomable{cursor:zoom-in;}
.is-zoomable:focus-visible{outline:2px solid var(--color-accent);outline-offset:3px;}

.img-lightbox{position:fixed;inset:0;z-index:1500;}
.img-lightbox[hidden]{display:none;}
.img-lightbox__backdrop{position:absolute;inset:0;background:rgba(8,10,14,.93);
  -webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);animation:lb-fade .18s ease;}
/* The stage is the scroll container: at fit size nothing overflows, and zoomed
   in it is what lets the reader pan around the full-resolution image. */
.img-lightbox__stage{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
  overflow:auto;overscroll-behavior:contain;padding:64px 16px 76px;}
.img-lightbox__figure{margin:0;display:flex;flex-direction:column;align-items:center;gap:14px;
  max-width:100%;animation:lb-pop .2s ease;}
.img-lightbox__img{display:block;width:auto;height:auto;max-width:100%;
  max-height:calc(100vh - 150px);max-height:calc(100dvh - 150px);
  border-radius:10px;box-shadow:0 24px 70px rgba(0,0,0,.6);transition:opacity .18s ease;}
.img-lightbox__img.can-zoom{cursor:zoom-in;}
.img-lightbox.is-loading .img-lightbox__img{opacity:0;}
/* Zoomed = natural size. flex-start + margin:auto keeps an image that is still
   smaller than the viewport centred, while a larger one scrolls from its corner. */
.img-lightbox.is-zoomed .img-lightbox__stage{align-items:flex-start;justify-content:flex-start;}
.img-lightbox.is-zoomed .img-lightbox__figure{margin:auto;}
.img-lightbox.is-zoomed .img-lightbox__img{max-width:none;max-height:none;cursor:zoom-out;}
.img-lightbox__caption{color:rgba(255,255,255,.82);font-size:.9375rem;line-height:1.6;
  text-align:center;max-width:820px;}
.img-lightbox__caption[hidden]{display:none;}

.img-lightbox__btn{position:absolute;z-index:2;display:flex;align-items:center;justify-content:center;
  border:0;padding:0;border-radius:999px;color:#fff;background:rgba(255,255,255,.14);cursor:pointer;
  -webkit-tap-highlight-color:transparent;transition:background .15s ease,transform .15s ease;}
.img-lightbox__btn:hover{background:rgba(255,255,255,.28);transform:scale(1.06);}
.img-lightbox__btn:focus-visible{outline:2px solid #fff;outline-offset:2px;}
.img-lightbox__close{top:14px;right:14px;width:44px;height:44px;}
.img-lightbox__nav{top:50%;margin-top:-24px;width:48px;height:48px;}
.img-lightbox__nav[hidden]{display:none;}
.img-lightbox__prev{left:14px;}
.img-lightbox__next{right:14px;}
.img-lightbox__counter{position:absolute;left:50%;bottom:20px;transform:translateX(-50%);z-index:2;
  padding:5px 14px;border-radius:999px;background:rgba(0,0,0,.4);
  color:rgba(255,255,255,.78);font-size:.8125rem;letter-spacing:.04em;}
.img-lightbox__counter:empty{display:none;}
.img-lightbox__spinner{position:absolute;top:50%;left:50%;width:36px;height:36px;margin:-18px 0 0 -18px;
  border-radius:50%;border:3px solid rgba(255,255,255,.25);border-top-color:#fff;
  opacity:0;pointer-events:none;animation:lb-spin .7s linear infinite;}
.img-lightbox.is-loading .img-lightbox__spinner{opacity:1;}

@keyframes lb-fade{from{opacity:0;}}
@keyframes lb-pop{from{opacity:0;transform:scale(.96);}}
@keyframes lb-spin{to{transform:rotate(360deg);}}

@media (max-width:600px){
  .img-lightbox__stage{padding:56px 8px 66px;}
  .img-lightbox__img{max-height:calc(100vh - 128px);max-height:calc(100dvh - 128px);}
  .img-lightbox__nav{width:40px;height:40px;margin-top:-20px;}
  .img-lightbox__prev{left:6px;}
  .img-lightbox__next{right:6px;}
}

@media (prefers-reduced-motion:reduce){
  .img-lightbox__backdrop,.img-lightbox__figure{animation:none;}
  .img-lightbox__btn,.img-lightbox__img{transition:none;}
  .img-lightbox__spinner{animation-duration:1.6s;}
}

/* ---------- Cover fallback (รูปปกโหลดไม่ได้) ----------
   เมื่อรูปปกบทความโหลดไม่สำเร็จ — ไฟล์หายจากเซิร์ฟเวอร์, URL เสีย, เน็ตหลุด —
   main.js จะซ่อน <img> ที่พังแล้วติดคลาสนี้ให้กล่องเดิม กล่องจึงกลายเป็นแผ่นพื้น
   เรียบ ๆ พร้อมโลโก้ Bite&Brief ตรงกลาง แทนไอคอนรูปแตกของเบราว์เซอร์
   Geometry ทั้งหมดยังเป็นของกล่องเดิม (.article-cover, .article-hero,
   .bite-brief-*-cover, .related-thumb, .search-result__thumb) — เลย์เอาต์จึง
   ไม่ขยับแม้รูปจะหายไป
   โลโก้ใช้ mask ใบเดียวกับ .brand-logo--mask (แคชไว้แล้วตั้งแต่ header) ทาสีด้วย
   --brand-logo-ink จึงสลับขาว/ดำตามธีมเองโดยไม่ต้องมีไฟล์ที่สอง
   บล็อกนี้อยู่ท้ายไฟล์เพราะต้องทับ background ที่กล่องแต่ละแบบตั้งไว้เอง */
.cover-fallback{
  position:relative;
  background-color:var(--cover-fallback-bg);
  /* ความกว้างโลโก้: ~40% ของกล่อง แต่ไม่เล็กจนอ่านไม่ออกในทัมบ์เนล 56px
     และไม่ใหญ่เกินงามบนปกใหญ่ */
  --cover-fallback-mark:clamp(44px,40%,420px);
}
.cover-fallback::after{
  content:"";
  position:absolute;
  inset:0;
  background-color:var(--brand-logo-ink);
  -webkit-mask:url('../img/logo-mask.png') no-repeat center/var(--cover-fallback-mark) auto;
          mask:url('../img/logo-mask.png') no-repeat center/var(--cover-fallback-mark) auto;
}
/* ตัว <img> ที่พังถูกซ่อน ไม่ให้เบราว์เซอร์วาดไอคอนรูปแตก/alt text ทับโลโก้ */
.is-cover-broken{display:none;}
