/* =============================================================================
   Base + shell: reset, header, buttons, footer, and the two zero-JS motion
   primitives the whole site leans on (cross-document view transitions and
   scroll-driven reveals).
   ============================================================================= */

@layer base {
  *, *::before, *::after { box-sizing: border-box; }

  /* --- scrollbars ----------------------------------------------------------
     The platform default bar is wide and high-contrast, and on an art-led page
     it reads as part of the UI. Slimmed down and tinted to the page's own ink so
     it recedes.

     Standard properties only, no ::-webkit-scrollbar. Chrome ignores those
     pseudo-elements entirely once scrollbar-width/scrollbar-color are set, so
     mixing the two just leaves dead rules behind.

     scrollbar-color inherits; scrollbar-width does not, hence the * selector —
     harmless, since it only has an effect on elements that actually scroll. */
  :root {
    scrollbar-color: light-dark(oklch(0.175 0.014 50 / 0.30),
                                oklch(0.975 0.004 80 / 0.26))
                     transparent;
  }

  * { scrollbar-width: thin; }

  html {
    /* Anchor links and sticky-header offset without magic numbers per page. */
    scroll-padding-top: 5rem;
    -webkit-text-size-adjust: 100%;
  }

  body {
    margin: 0;
    background: var(--paper);
    color: var(--ink);
    font-family: var(--sans);
    font-size: var(--step-0);
    line-height: 1.65;
    /* Thai has tall ascenders/descenders; a touch of extra leading keeps mixed
       Thai/Latin lines from colliding. */
    text-rendering: optimizeLegibility;
  }

  h1, h2, h3 {
    margin: 0;
    font-family: var(--display);
    font-weight: 800;
    line-height: 1.14;
    letter-spacing: -0.02em;
    text-wrap: balance;
    /* Thai has no spaces between words and Latin titles here can be long
       ("L.A.G (Unlimited Passive)"); without this a headline overflows a phone
       instead of wrapping. This was a real bug in the first draft. */
    overflow-wrap: break-word;
  }

  p { margin: 0; text-wrap: pretty; }

  a { color: inherit; text-decoration: none; }

  img { display: block; max-width: 100%; height: auto; }

  ul { margin: 0; padding: 0; list-style: none; }

  :focus-visible {
    outline: 2px solid var(--accent, var(--ink));
    outline-offset: 3px;
  }

  /* Respect the OS "reduce motion" setting for every animation on the site. */
  @media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
      animation-duration: 0.01ms !important;
      animation-iteration-count: 1 !important;
      transition-duration: 0.01ms !important;
      scroll-behavior: auto !important;
    }
  }
}

@layer motion {
  /* Cross-document view transitions: navigating between two pages that share a
     view-transition-name morphs that element instead of cutting. No JavaScript,
     no client router — this is what makes a server-rendered site feel like an app. */
  @view-transition { navigation: auto; }

  ::view-transition-group(*) {
    animation-duration: 380ms;
    animation-timing-function: cubic-bezier(0.2, 0.8, 0.2, 1);
  }

  /* Scroll-driven reveal. animation-timeline: view() ties the animation to the
     element's own position in the scrollport, so there is no observer, no JS,
     and nothing to hydrate. */
  @keyframes rise {
    from { opacity: 0; transform: translateY(1.25rem); }
    to   { opacity: 1; transform: none; }
  }

  .reveal {
    animation: rise linear both;
    animation-timeline: view();
    animation-range: entry 0% entry 62%;
  }

  /* Browsers without scroll-driven animations get the finished state, not a
     permanently invisible page. */
  @supports not (animation-timeline: view()) {
    .reveal { animation: none; opacity: 1; transform: none; }
  }
}

@layer shell {
  /* The page column. Its width IS the margin — the leftover 2/12 (or 1/12 on a
     phone) is what the auto margins take. No padding-inline here, otherwise the
     content box would be the share minus the gutter and no longer 10/12. */
  .wrap {
    width: var(--page-width);
    margin-inline: auto;
  }

  /* --- header --------------------------------------------------------------- */
  .site-header {
    position: sticky;
    top: 0;
    z-index: 50;
    background: color-mix(in oklab, var(--paper) 78%, transparent);
    backdrop-filter: blur(14px) saturate(1.4);
    border-bottom: 1px solid transparent;
    /* The hairline fades in only once the page has actually scrolled — a header
       that is flat at rest and defined in use. Driven by the root scroller. */
    animation: header-line linear both;
    animation-timeline: scroll(root block);
    animation-range: 0 96px;
  }

  @keyframes header-line {
    to { border-bottom-color: var(--hairline); }
  }

  .site-header__inner {
    display: flex;
    align-items: center;
    gap: clamp(0.5rem, 2vw, 2rem);
    min-height: 3.5rem;
    /* Every flex child needs to be allowed to shrink, or one long item pushes
       the rest off the side of a phone — which is exactly what happened to the
       theme switch in the first draft. */
    min-width: 0;
  }

  .site-header__inner > * { min-width: 0; }

  .wordmark {
    font-family: var(--display);
    font-weight: 800;
    font-size: var(--step-1);
    letter-spacing: -0.03em;
    white-space: nowrap;
    flex: 0 0 auto;
  }

  .wordmark span { color: var(--ink-3); font-weight: 600; }

  .site-nav {
    display: flex;
    gap: 0.25rem;
    margin-inline-start: auto;
  }

  .site-nav a {
    padding: 0.4rem 0.7rem;
    border-radius: var(--radius);
    color: var(--ink-2);
    font-size: var(--step--1);
    font-weight: 600;
  }

  .site-nav a:hover { color: var(--ink); background: var(--accent-wash, var(--paper-sunk)); }

  .stars {
    display: inline-flex;
    align-items: baseline;
    gap: 0.3rem;
    font-variant-numeric: tabular-nums;
    font-weight: 700;
    font-size: var(--step--1);
    color: var(--gold);
    white-space: nowrap;
  }

  /* --- session (balance + who you are, or the way in) ----------------------
     One wrapper that is always present whether or not anyone is signed in, so
     the header keeps the same shape and the narrow-screen rule below has a
     single element to hang the auto margin on. */
  .session {
    display: flex;
    align-items: center;
    gap: clamp(0.4rem, 1.5vw, 0.9rem);
    flex: 0 1 auto;
    min-width: 0;
  }

  .session__form {
    display: flex;
    align-items: center;
    gap: 0.45rem;
    min-width: 0;
  }

  .session__name {
    color: var(--ink-2);
    font-size: var(--step--1);
    font-weight: 600;
    /* A long display name must not push the theme switch off a phone. */
    max-width: 9ch;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .session__out,
  .session__in {
    appearance: none;
    padding: 0.34rem 0.7rem;
    border: 1px solid var(--hairline);
    border-radius: 999px;
    background: none;
    color: var(--ink-2);
    font: inherit;
    font-size: var(--step--1);
    font-weight: 600;
    white-space: nowrap;
    cursor: pointer;
    transition: color 140ms ease, background-color 140ms ease;
  }

  .session__out:hover,
  .session__in:hover {
    color: var(--ink);
    background: var(--paper-rise);
  }

  /* --- theme switch --------------------------------------------------------
     One icon button that flips the theme. A real <form>, so it works with
     JavaScript disabled, and because the server renders the resulting
     color-scheme there is no flash on the way back.

     Only two states are offered — dark and light — per the owner's spec. There
     is no "system" button, but following the OS is still the state a visitor
     starts in: with no cookie the page renders from `color-scheme: light dark`,
     and the first click is what pins an explicit choice. (Clearing site cookies
     returns to following the OS.)

     Both buttons are rendered and CSS chooses which one is visible, because the
     server cannot know a first-time visitor's OS preference and would therefore
     have to guess which single icon to draw — a guess that is wrong half the
     time and makes the first click look like it did nothing. */
  .mode-switch {
    display: flex;
    flex: 0 0 auto;
  }

  .mode-switch__btn {
    display: grid;
    place-items: center;
    width: 2.1rem;
    height: 2.1rem;
    appearance: none;
    padding: 0;
    border: 1px solid var(--hairline);
    border-radius: 999px;
    background: none;
    color: var(--ink-2);
    cursor: pointer;
    transition: color 140ms ease, background-color 140ms ease, rotate 260ms cubic-bezier(0.2, 0.8, 0.2, 1);
  }

  .mode-switch__btn svg {
    width: 1.1rem;
    height: 1.1rem;
  }

  .mode-switch__btn:hover {
    color: var(--ink);
    background: var(--paper-rise);
    rotate: -18deg;
  }

  /* Which icon shows. Base case is "page is light, offer dark" (the moon).
     These overrides MUST stay below the base rules — a media query adds no
     specificity of its own. */
  .mode-switch__btn--to-light { display: none; }

  @media (prefers-color-scheme: dark) {
    :root[data-mode="auto"] .mode-switch__btn--to-dark { display: none; }
    :root[data-mode="auto"] .mode-switch__btn--to-light { display: grid; }
  }

  :root[data-mode="dark"] .mode-switch__btn--to-dark { display: none; }
  :root[data-mode="dark"] .mode-switch__btn--to-light { display: grid; }

  :root[data-mode="light"] .mode-switch__btn--to-dark { display: grid; }
  :root[data-mode="light"] .mode-switch__btn--to-light { display: none; }

  /* --- narrow screens -----------------------------------------------------
     MUST come after the rules it overrides. A media query adds no specificity,
     so when this block sat above `.site-nav { display: flex }` the later rule
     simply won, the nav never hid, the header stayed wider than the viewport,
     and the whole page scrolled sideways with the theme switch and the headline
     cut off at the edge. That was the real cause of "not mobile friendly".

     Below this width there is no room for wordmark + nav + balance + switch.
     The star balance and the theme switch cannot be dropped, so the nav is what goes.

     🔴 The original justification written here — "the nav links only jump to sections further
     down this same page" — stopped being true on 2026-09-10, when those links were fixed to
     point at the real /manga and /novel pages (they had been dead anchors). Dropping them below
     34rem now costs a phone reader the only header route to the two catalogue pages; the landing
     page still reaches them through its "ดูทั้งหมด →" links, but /privacy and the detail pages
     do not. Left as-is on purpose rather than changed blind: whether two short Thai words fit
     next to the wordmark at 375px is a layout question the owner has to see, not one to guess. */
  @media (width < 34rem) {
    .wordmark span { display: none; }

    .site-nav { display: none; }

    /* The nav normally carries the auto margin that right-aligns everything
       after it; with the nav hidden the session block has to take that job over.
       It is the session block and not .stars because .stars is absent when no one
       is signed in — the way in would then sit against the wordmark. */
    .session { margin-inline-start: auto; }

    .mode-switch__btn { width: 1.95rem; height: 1.95rem; }
  }

  /* --- buttons ------------------------------------------------------------- */
  .btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    padding: 0.72rem 1.4rem;
    border: 1px solid transparent;
    border-radius: 999px;
    font-family: var(--display);
    font-weight: 600;
    font-size: var(--step-0);
    cursor: pointer;
    transition: translate 140ms ease, background-color 140ms ease, color 140ms ease;
  }

  .btn:hover { translate: 0 -1px; }

  /* Plain ink-on-paper inverse rather than the title's accent. Deriving the
     primary button from the cover hue looked wrong the moment a title's hue
     landed on orange — the button shouted over the artwork. The accent still
     appears, but only on small type (see .eyebrow). */
  .btn--primary {
    background: var(--ink);
    color: var(--paper);
  }

  .btn--primary:hover { background: color-mix(in oklab, var(--ink) 88%, var(--accent, transparent)); }

  /* Secondary buttons can sit on top of cover artwork, so they need their own
     surface — a plain border disappears against a busy image. */
  .btn--ghost {
    border-color: var(--hairline);
    background: color-mix(in oklab, var(--paper) 55%, transparent);
    backdrop-filter: blur(10px);
    color: var(--ink);
  }

  .btn--ghost:hover { background: color-mix(in oklab, var(--paper) 82%, transparent); }

  /* --- section headings ---------------------------------------------------- */
  .section-head {
    display: flex;
    align-items: baseline;
    gap: 0.9rem;
    margin-block: clamp(2.5rem, 6vw, 4.5rem) 1.25rem;
  }

  .section-head h2 { font-size: var(--step-2); min-width: 0; }

  .section-head__count {
    color: var(--ink-3);
    font-size: var(--step--1);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
  }

  .section-head__more { margin-inline-start: auto; font-size: var(--step--1); font-weight: 700; color: var(--ink-2); }

  /* --- footer -------------------------------------------------------------- */
  .site-footer {
    margin-top: clamp(4rem, 10vw, 8rem);
    border-top: 1px solid var(--hairline);
    padding-block: 2.5rem 3.5rem;
    color: var(--ink-3);
    font-size: var(--step--1);
  }

  .site-footer__inner { display: flex; flex-wrap: wrap; gap: 1rem 2rem; align-items: baseline; }

  .site-footer__link { color: var(--ink-3); text-decoration: underline; text-underline-offset: 2px; }
  .site-footer__link:hover { color: var(--ink); }
}
