@import url("./fonts/fonts.css");

/* ===========================================================================
   Pan D Endz base stylesheet.

   Load order on every page is: tokens.css, then this file. Nothing else.
   This file imports the self-hosted webfaces on its first line, so a page never
   has to know that fonts/ exists.

   Rules of the house:
   - Every colour comes from a variable in tokens.css. Never a raw hex here.
   - Only transform, opacity, filter and clip-path are animated.
   - Two worlds: [data-tone="dark"] (the film and the footer) and
     [data-tone="light"] (menu, grocery, reviews, visit).
   =========================================================================== */

/* ---------------------------------------------------------------------------
   1. Reset. Small and modern: only the things that actually get in the way.
   --------------------------------------------------------------------------- */

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

/* Kill the default margins. Every gap on this site is deliberate. */
* {
  margin: 0;
}

html {
  background: var(--char);
  color: var(--cream);
  font-family: var(--font-body);
  font-size: 100%;

  /* Lenis drives the scrolling, so native smooth scroll must stay off.
     The one exception is a visitor who asked for reduced motion: they get no
     Lenis at all, and the browser's own instant jumps are correct for them. */
  scroll-behavior: auto;

  /* A jump target is never allowed to hide under the fixed top bar. */
  scroll-padding-top: 6rem;

  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;

  /* Nothing on this site may cause a sideways scrollbar: the dish marquee, the
     grocery run and several film stages are deliberately wider than the
     screen. This sits on the root rather than on body on purpose. `clip` is
     not `hidden`: it clips without turning the element into a scroll
     container, so every `position: sticky` film stage still sticks to the
     viewport. Putting it on body instead would hand the viewport a scrollport
     it does not want. */
  overflow-x: clip;
}

body {
  min-height: 100vh;
  min-height: 100dvh;
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* Media is block level and never wider than its box. */
img,
picture,
video,
canvas,
svg {
  display: block;
  max-width: 100%;
}

/* Form controls do not inherit type by default. Make them. */
input,
button,
textarea,
select {
  font: inherit;
  color: inherit;
}

/* Long words break rather than push the layout sideways. */
p,
h1,
h2,
h3,
h4,
li,
figcaption {
  overflow-wrap: break-word;
}

/* Headings inherit their size from a utility class, never from the tag. */
h1,
h2,
h3,
h4 {
  font-size: inherit;
  font-weight: inherit;
}

ul[role="list"],
ol[role="list"] {
  list-style: none;
  padding: 0;
}

a {
  color: inherit;
}

/* Body copy is left aligned and capped at a comfortable measure. Paragraphs on
   this site are never centred. */
p {
  max-width: var(--measure);
  text-align: left;
}

/* ---------------------------------------------------------------------------
   2. The two worlds.

   A section declares which world it is in with data-tone. The top bar and the
   thermometer watch these attributes and flip their own colours to match.
   --font-* and --focus are set here so everything inside inherits the right
   accent without any extra classes.
   --------------------------------------------------------------------------- */

:root {
  --focus: var(--gold); /* focus ring colour, overridden per tone below */
}

[data-tone="dark"] {
  background: var(--char);
  color: var(--cream);
  --focus: var(--gold);
  --tone-muted: var(--cream-muted);
  --tone-accent: var(--gold);
  --tone-rule: var(--cork);
}

[data-tone="light"] {
  background: var(--paper);
  color: var(--ink);
  --focus: var(--green);
  --tone-muted: var(--ink-muted);
  --tone-accent: var(--green);
  --tone-rule: var(--rule);
}

/* ---------------------------------------------------------------------------
   3. Type utilities. These names are fixed by the design system. Do not rename.
   --------------------------------------------------------------------------- */

/* A Bungee label: the shop-sign voice. Used small and rarely. Never sentences. */
.kicker {
  display: block;
  font-family: var(--font-sign);
  font-weight: 400;
  font-size: var(--fs-label);
  line-height: 1.2;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--tone-accent, var(--gold));
}

/* The three display sizes. Fraunces at weight 500 with the soft axis on.
   A word or two per headline is wrapped in <em> to get the wonky italic. */
.d1,
.d2,
.d3 {
  font-family: var(--font-display);
  font-weight: 500;
  font-style: normal;
  font-variation-settings: var(--fraunces-soft);
  letter-spacing: var(--track-display);
  line-height: var(--lh-display);
  text-wrap: balance;
}

.d1 {
  font-size: var(--fs-d1);
  line-height: 0.92;
}

.d2 {
  font-size: var(--fs-d2);
  line-height: 0.98;
}

.d3 {
  font-size: var(--fs-d3);
  line-height: 1.05;
}

.d1 em,
.d2 em,
.d3 em {
  font-style: italic;
  font-variation-settings: var(--fraunces-wonk);
}

/* The wordmark. Only ever the words "Pan D Endz". */
.mega {
  font-family: var(--font-display);
  font-weight: 600;
  font-style: normal;
  font-variation-settings: var(--fraunces-soft);
  font-size: var(--fs-mega);
  line-height: 0.85;
  letter-spacing: var(--track-display);
}

/* ---------------------------------------------------------------------------
   4. Buttons. One pill shape, three skins. Minimum 44px tall everywhere so a
   thumb can hit it.
   --------------------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-1);
  min-height: 44px;
  padding: 0.75rem 1.5rem;
  border: 0;
  border-radius: var(--r-pill);

  font-family: var(--font-sign);
  font-weight: 400;
  font-size: var(--fs-label);
  line-height: 1;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;

  background: transparent;
  color: inherit;
  cursor: pointer;

  /* Only transform and opacity move. --t-med drops to 1ms under reduced
     motion, which tokens.css handles, so no extra rule is needed here. */
  transition: transform var(--t-med) var(--ease-smoke),
    opacity var(--t-med) var(--ease-smoke);
}

.btn:hover {
  transform: translateY(-2px);
}

.btn:active {
  transform: translateY(0);
}

/* Primary on dark: gold fill, char text. 12.4:1. */
.btn--gold {
  background: var(--gold);
  color: var(--char);
}

/* Primary on paper: green fill, cream text. 5.9:1. */
.btn--green {
  background: var(--green);
  color: var(--cream);
}

/* Secondary anywhere: a hairline in whatever colour the text already is, so the
   same button works in both worlds with no extra class. */
.btn--ghost {
  background: transparent;
  color: inherit;
  box-shadow: inset 0 0 0 1px currentColor;
}

.btn--ghost:hover {
  /* A whisper of fill rather than a colour change, so contrast never moves. */
  background: color-mix(in srgb, currentColor 10%, transparent);
}

/* ---------------------------------------------------------------------------
   5. Focus, skip link, and the visually hidden helper.
   Design system section 10: 2px gold on dark, 2px green on paper, 3px offset.
   --------------------------------------------------------------------------- */

:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 3px;
  border-radius: 2px;
}

/* Drop the ring only for a mouse click, and only in browsers that understand
   :focus-visible. Written this way round on purpose: an old browser that does
   not know the selector ignores both rules and keeps its own focus ring,
   rather than being left with no visible focus at all. */
:focus:not(:focus-visible) {
  outline: none;
}

/* The skip link sits off screen until it is focused, then it lands top left. */
.skip {
  position: fixed;
  top: var(--s-2);
  left: var(--s-2);
  z-index: 999;

  padding: 0.75rem 1.25rem;
  border-radius: var(--r-pill);
  background: var(--gold);
  color: var(--char);

  font-family: var(--font-sign);
  font-size: var(--fs-label);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  text-decoration: none;

  /* Moved out of sight with a transform so it keeps its size and stays
     focusable, unlike display:none. */
  transform: translateY(-200%);
  transition: transform var(--t-fast) var(--ease-snap);
}

.skip:focus-visible {
  transform: translateY(0);
}

/* Hidden from the screen, still read by a screen reader. */
.vh {
  position: absolute !important;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ---------------------------------------------------------------------------
   5b. The film, before it knows which film it is.

   The home page plays one of three scroll films and does not know which until
   shared/js/film-loader.js has read the design settings and that film's own
   stylesheet has arrived. Until then #film is written into the page as an empty
   stage carrying data-film-state="loading", and these two rules are what make
   the wait invisible: one dark screen exactly one viewport tall, with nothing
   inside it shown. So no wrong film can flash, and nothing below the film moves
   when the real film appears, because by then the film's own stylesheet is in
   place and it is the one saying how tall the film is.

   The loader writes data-film-state="ready" once every layer is posed, and
   "none" in the one case where no film would load at all: then the film gets
   out of the way and the page below it, which is the whole point of the site,
   still builds. These states are here in base.css rather than in a film's own
   stylesheet because they have to work before any film's stylesheet exists.
   --------------------------------------------------------------------------- */

.film[data-film-state="loading"] {
  min-height: 100vh;
  background: var(--char);
}

.film[data-film-state="loading"] * {
  visibility: hidden;
}

.film[data-film-state="none"] {
  display: none;
}

/* ---------------------------------------------------------------------------
   6. Scrims. A dark gradient laid over a photograph so the text on top keeps
   its contrast ratio no matter what the picture is doing underneath.

   The design system asks for rgba(18,10,5,0.85), which is exactly --char at 85
   percent. color-mix gets there from the token, so a theme change carries.
   The parent must be position: relative.
   --------------------------------------------------------------------------- */

.scrim-b,
.scrim-t {
  position: absolute;
  left: 0;
  right: 0;
  height: 55%;
  pointer-events: none;
  z-index: 1;
}

.scrim-b {
  bottom: 0;
  background: linear-gradient(
    to top,
    color-mix(in srgb, var(--char) 85%, transparent) 0%,
    color-mix(in srgb, var(--char) 55%, transparent) 38%,
    color-mix(in srgb, var(--char) 18%, transparent) 70%,
    transparent 100%
  );
}

.scrim-t {
  top: 0;
  background: linear-gradient(
    to bottom,
    color-mix(in srgb, var(--char) 85%, transparent) 0%,
    color-mix(in srgb, var(--char) 55%, transparent) 38%,
    color-mix(in srgb, var(--char) 18%, transparent) 70%,
    transparent 100%
  );
}

/* ---------------------------------------------------------------------------
   7. The burn-in headline (design system section 5).

   PDE.burn.split(el) wraps every word of the headline in <span class="w">,
   numbers them with --i, moves the real text onto an aria-label on the parent
   and hides the spans from assistive tech. PDE.burn.observe(root) adds .is-in
   when the headline scrolls into view, and these transitions do the rest:
   each word rises 0.4em, comes out of a 12px blur, and its gold glow shrinks
   to nothing. 900ms per word, 60ms apart.
   --------------------------------------------------------------------------- */

.burn .w {
  display: inline-block;
  opacity: 0;
  transform: translateY(0.4em);
  filter: blur(12px);

  /* The glow is a text-shadow whose blur radius, not colour, is animated. That
     keeps the interpolation clean and never flashes a stray colour. */
  text-shadow: 0 0 0.55em var(--glow);

  will-change: opacity, transform, filter;

  transition: opacity var(--t-slow) var(--ease-smoke),
    transform var(--t-slow) var(--ease-smoke),
    filter var(--t-slow) var(--ease-smoke),
    text-shadow var(--t-slow) var(--ease-smoke);

  /* --i is the word index, set by PDE.burn.split. */
  transition-delay: calc(var(--i, 0) * 60ms);
}

.burn.is-in .w {
  opacity: 1;
  transform: translateY(0);
  filter: blur(0);
  text-shadow: 0 0 0 var(--glow);
}

/* ---------------------------------------------------------------------------
   8. Reduced motion. Nothing moves, everything is still readable and reachable.
   tokens.css already collapses the duration variables to 1ms. This block kills
   the transitions outright and shows burn-in headlines in their finished state
   whether or not the observer ever fires.
   --------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    transition-delay: 0ms !important;
    scroll-behavior: auto !important;
  }

  .burn .w {
    opacity: 1;
    transform: none;
    filter: none;
    text-shadow: none;
    will-change: auto;
  }

  .btn:hover {
    transform: none;
  }
}
