/* ==========================================================================
   Weka Roastery — base layer
   Reset, typography, layout primitives, header, footer.

   Everything here uses LOGICAL properties (inline-start/end, margin-inline)
   so the RTL layout cannot mirror between pages. This is the structural fix
   for the header drift documented in .claude/design/CONTRACT.md §3.
   ========================================================================== */

/* tokens.css is loaded as its own <link> before this file — an @import
   here would block rendering and, worse, race the cascade so that
   var(--z-scrim) resolved to nothing on a cold load. */

/* ------------------------------------------------------------- reset ---- */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--color-background);
  color: var(--color-on-background);
  font-family: var(--font-body);
  font-size: var(--text-body-md);
  line-height: var(--leading-body-md);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1, h2, h3, h4, h5, h6 { margin: 0; font-family: var(--font-headline); font-weight: var(--weight-regular); }
p, figure, blockquote { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
img, picture, video, svg { display: block; max-width: 100%; height: auto; }
button, input, select, textarea { font: inherit; color: inherit; }
button { background: none; border: 0; cursor: pointer; }
a { color: inherit; text-decoration: none; }
address { font-style: normal; }

/* Visible focus everywhere. Contract §7. */
:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.skip-link {
  position: absolute;
  inset-block-start: -100%;
  inset-inline-start: var(--space-md);
  z-index: var(--z-toast);
  padding: var(--space-sm) var(--space-md);
  background: var(--color-primary);
  color: var(--color-on-primary);
  border-radius: 0 0 var(--radius) var(--radius);
}
.skip-link:focus { inset-block-start: 0; }

/* --------------------------------------------------------- typography --- */

.t-display    { font-family: var(--font-headline); font-size: var(--text-display-lg-mobile); line-height: var(--leading-display-lg-mobile); font-weight: var(--weight-bold); letter-spacing: var(--tracking-display); }
.t-headline   { font-family: var(--font-headline); font-size: var(--text-headline-md); line-height: var(--leading-headline-md); }
.t-headline-sm{ font-family: var(--font-headline); font-size: var(--text-headline-sm); line-height: var(--leading-headline-sm); }
.t-body-lg    { font-size: var(--text-body-lg); line-height: var(--leading-body-lg); }
.t-body       { font-size: var(--text-body-md); line-height: var(--leading-body-md); }
.t-label      { font-size: var(--text-label-md); line-height: var(--leading-label-md); font-weight: var(--weight-medium); letter-spacing: var(--tracking-label); }
.t-label-sm   { font-size: var(--text-label-sm); font-weight: var(--weight-medium); }

.t-muted { color: var(--color-on-surface-variant); }

@media (min-width: 768px) {
  .t-display { font-size: var(--text-display-lg); line-height: var(--leading-display-lg); }
}

/* ------------------------------------------------------------ layout ---- */

.container {
  inline-size: 100%;
  max-inline-size: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.main-content {
  min-block-size: 60vh;
  padding-block: var(--space-md) var(--space-xl);
}

.stack > * + * { margin-block-start: var(--space-md); }
.cluster { display: flex; flex-wrap: wrap; gap: var(--space-sm); align-items: center; }

/* ============================================================== HEADER ===
   CONTRACT §3. One definition, used by every page.
   Mobile:  [menu]  ·  brand  ·  [bag]      (menu at RTL start)
   Desktop: brand · nav · [search][person][bag]
   No flex-row-reverse anywhere — direction comes from dir="rtl" alone.
   ======================================================================== */

.site-header {
  position: sticky;
  inset-block-start: 0;
  z-index: var(--z-header);
  background: var(--color-surface);
  border-block-end: 1px solid rgb(211 195 189 / 30%);
  transition: transform var(--transition-slow);
}

/* Hides on scroll-down, returns on scroll-up. Progressive: without JS the
   class is never applied and the header simply stays put. */
.site-header--hidden { transform: translateY(-100%); }

.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
  block-size: var(--header-height-mobile);
  max-inline-size: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* The brand sits at the RTL start — the right edge — on every breakpoint.
   It was previously centred on mobile with the menu at the start; the mark
   now anchors the corner the eye lands on first, and the menu moved to the
   opposite end. */
.site-brand {
  display: inline-flex;
  align-items: center;
  gap: var(--space-sm);
  flex: 0 0 auto;
  min-block-size: var(--tap-target);
  color: var(--color-on-surface);
}
.site-brand__mark {
  inline-size: 36px;
  block-size: 36px;
  flex: 0 0 auto;
  /* The asset is black ink on a WHITE square, which would sit as a white chip
     on the cream ground. multiply drops the white to nothing and keeps the
     ink, so no second asset is needed. isolation:isolate on the link stops
     the blend reaching further up the stack than intended. */
  mix-blend-mode: multiply;
}
.site-brand { isolation: isolate; }
.site-brand__text {
  display: flex;
  flex-direction: column;
  line-height: 1.15;
}
.site-brand__name {
  font-family: var(--font-headline);
  font-size: var(--text-headline-sm);
  font-weight: var(--weight-bold);
  letter-spacing: -0.01em;
  color: var(--color-primary-deep);
}
.site-brand__tagline {
  font-size: var(--text-label-sm);
  color: var(--color-on-surface-variant);
  white-space: nowrap;
  /* A nicety, not information — hidden on the narrowest phones so it can
     never compete with the action icons for room. */
  display: none;
}
@media (min-width: 420px) {
  .site-brand__tagline { display: block; }
}

/* Belt and braces: the brand must never be the reason the header cannot fit.
   min-inline-size:0 lets a flex item shrink below its content width, which
   flex items otherwise refuse to do. */
.site-brand { min-inline-size: 0; }
.site-brand__text { min-inline-size: 0; }
.site-brand__name { white-space: nowrap; }

.site-header__inner { position: relative; }

.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: var(--tap-target);
  block-size: var(--tap-target);
  border-radius: var(--radius-full);
  color: var(--color-on-surface-variant);
  transition: background var(--transition), color var(--transition);
  position: relative;
}
.icon-btn:hover { background: var(--color-surface-container-low); color: var(--color-primary); }
.icon-btn:active { transform: scale(0.95); }

.cart-badge {
  position: absolute;
  inset-block-start: 4px;
  inset-inline-end: 4px;
  /* Grid centring rather than line-height + padding: the old rule stretched
     the circle into an oval as soon as the count reached two digits, and
     line-height alone never centres a glyph exactly. */
  display: grid;
  place-items: center;
  inline-size: 18px;
  block-size: 18px;
  padding: 0;
  border-radius: var(--radius-full);
  background: var(--color-accent);
  color: var(--color-on-accent);
  font-size: 11px;
  font-weight: var(--weight-bold);
  line-height: 1;
  font-variant-numeric: proportional-nums;
}
/* Three digits and up would burst an 18px circle, so let it grow into a
   pill rather than clipping the number. */
.cart-badge:has-text, .cart-badge { min-inline-size: 18px; inline-size: auto; padding-inline: 5px; }

.site-nav { display: none; }
.site-header__actions { display: flex; align-items: center; gap: var(--space-xs); }
.site-header__menu-btn { display: inline-flex; }

/* CONTRACT §3: mobile shows exactly TWO icons — menu and bag. Search and
   account live in the drawer, where there is room to label them. Showing
   four icons crowds the 390px header and squeezes the wordmark. */
/* Mobile: the bottom nav carries account, and the drawer carries search, so
   the header keeps only the bag and the menu. */
.site-header__search-btn,
.site-header__account-btn { display: none; }

@media (min-width: 768px) {
  .site-header__inner { block-size: var(--header-height-desktop); gap: var(--space-lg); }
  .site-header__menu-btn { display: none; }
  /* Desktop restores the full action set: search, account, bag. */
  .site-header__search-btn,
  .site-header__account-btn { display: inline-flex; }
  .site-brand__mark { inline-size: 40px; block-size: 40px; }
  .site-brand__name { font-size: var(--text-headline-md); }

  .site-nav {
    display: flex;
    flex: 1;
    align-items: center;
    gap: var(--space-md);
  }
  .site-nav__link {
    padding: var(--space-base) var(--space-sm);
    color: var(--color-on-surface-variant);
    border-radius: var(--radius);
    transition: color var(--transition), background var(--transition);
  }
  .site-nav__link:hover { color: var(--color-primary); background: var(--color-surface-container-low); }
  .site-nav__link[aria-current="page"] {
    color: var(--color-primary);
    font-weight: var(--weight-medium);
    box-shadow: inset 0 -2px 0 var(--color-primary);
  }
}

/* Desktop inline search — replaces the icon-that-does-nothing. */
.header-search { display: none; }
@media (min-width: 1024px) {
  .header-search {
    display: flex;
    align-items: center;
    gap: var(--space-xs);
    inline-size: 220px;
    padding: 6px var(--space-sm);
    background: var(--color-surface-container-low);
    border: 1px solid transparent;
    border-radius: var(--radius-full);
    transition: border-color var(--transition), background var(--transition);
  }
  .header-search:focus-within { border-color: var(--color-outline-variant); background: var(--color-surface-container-lowest); }
  .header-search__input {
    inline-size: 100%;
    border: 0;
    background: none;
    font-size: var(--text-label-md);
  }
  .header-search__input:focus { outline: none; }
  .site-header__search-btn { display: none; }
}

/* ============================================================== DRAWER === */

.drawer-scrim {
  position: fixed;
  inset: 0;
  /* MUST stay below the panel it dims. When scrim and panel shared one
     z-index the scrim won on source order and swallowed every click inside
     the filter drawer, so no filter could be selected on mobile.
     The literal fallback guarantees the stacking even if the token fails
     to resolve for any reason. */
  z-index: var(--z-scrim, 59);
  background: var(--color-scrim);
  opacity: 0;
  visibility: hidden;
  /* A hidden scrim must never intercept pointer events. */
  pointer-events: none;
  transition: opacity var(--transition), visibility var(--transition);
}
.drawer-scrim[data-open="true"] { pointer-events: auto; }
.drawer-scrim[data-open="true"] { opacity: 1; visibility: visible; }

.drawer {
  position: fixed;
  inset-block: 0;
  inset-inline-start: 0;
  z-index: var(--z-drawer, 60);
  inline-size: min(320px, 85vw);
  padding: var(--space-md);
  background: var(--color-surface);
  transform: translateX(-100%);
  transition: transform var(--transition-slow);
  overflow-y: auto;
}
/* In RTL the drawer slides in from the right; the logical property flips
   the anchor, so only the translate sign needs the direction query. */
[dir="rtl"] .drawer { transform: translateX(100%); }
.drawer[data-open="true"] { transform: translateX(0); }

/* The closed drawer is translated fully outside the viewport, which EXTENDS
   the document and gives the whole page a horizontal scrollbar — in RTL that
   shows up as every page shifted sideways with its start edge cut off.
   Clipping at the root contains it. overflow-x on <html> rather than <body>
   because body-level clipping is ignored once html is the scroll container. */
html { overflow-x: hidden; }

.drawer__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-block-end: var(--space-md);
  padding-block-end: var(--space-sm);
  border-block-end: var(--border-hairline);
}
.drawer__link {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-sm) var(--space-base);
  border-radius: var(--radius);
  color: var(--color-on-surface);
  transition: background var(--transition);
}
.drawer__link:hover { background: var(--color-surface-container-low); }
.drawer__link[aria-current="page"] { color: var(--color-primary); font-weight: var(--weight-medium); }

/* ============================================================== FOOTER ===
   CONTRACT §4. Four columns, then divider, copyright and social row.
   ======================================================================== */

/* The footer is the brand green, with cream text — the one place on the site
   where the green is a ground rather than an accent.

   The cream is #FBF6E0, NOT the page's #F5ECC2: that measures 4.46:1 on this
   green, just under the AA floor, while this one measures 4.89:1. It is the
   same warm family, already in the palette as surface-container-low. */
.site-footer {
  margin-block-start: var(--space-xl);
  padding-block: var(--space-lg) var(--space-md);
  background: var(--color-primary);
  color: var(--color-surface-container-low);
  /* No top border: the colour change already separates it from the page. */
  border-block-start: 0;
}

.site-footer__grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-lg);
}
@media (min-width: 640px) { .site-footer__grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) { .site-footer__grid { grid-template-columns: 1.4fr 1fr 1fr 1.2fr; } }

.site-footer__title {
  margin-block-end: var(--space-sm);
  font-family: var(--font-body);
  font-size: var(--text-label-md);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-label);
  color: var(--color-surface-container-low);
}
.site-footer__list > li + li { margin-block-start: var(--space-base); }
/* Cream on this green is 4.89:1 — only just past AA, so there is no headroom
   for a dimmed variant: at 82% opacity it drops to 3.88 and fails. Every
   piece of footer text is therefore the SOLID cream, and headings separate
   from links by weight rather than by colour. */
.site-footer__link {
  color: var(--color-surface-container-low);
  transition: color var(--transition);
}
.site-footer__link:hover {
  color: var(--color-surface-container-lowest);
  text-decoration: underline;
}

.site-footer__contact-item {
  display: flex;
  align-items: flex-start;
  gap: var(--space-base);
  color: var(--color-surface-container-low);
}
.site-footer__contact-item + .site-footer__contact-item { margin-block-start: var(--space-sm); }

/* Freshness promise — the strongest trust signal for a roastery. */
.freshness-note {
  display: flex;
  align-items: center;
  gap: var(--space-base);
  margin-block-start: var(--space-md);
  padding: var(--space-sm);
  background: var(--color-tertiary-fixed);
  color: var(--color-on-tertiary-fixed);
  border-radius: var(--radius);
  font-size: var(--text-label-md);
}

.site-footer__bottom {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
  margin-block-start: var(--space-lg);
  padding-block-start: var(--space-md);
  /* A cream rule rather than the page's hairline, which is tuned for dark
     ink on a light ground and is invisible here. */
  border-block-start: 1px solid rgb(251 246 224 / 30%);
  color: var(--color-surface-container-low);
  font-size: var(--text-label-md);
}
.site-footer__social { display: flex; gap: var(--space-xs); }

/* ------------------------------------------------------------ newsletter */

.newsletter__form {
  display: flex;
  gap: var(--space-base);
  margin-block-start: var(--space-sm);
}
.newsletter__input {
  flex: 1;
  min-inline-size: 0;
  padding: var(--space-base) var(--space-sm);
  background: var(--color-surface-container-lowest);
  border: 1px solid var(--color-outline-variant);
  border-radius: var(--radius);
}
.newsletter__input:focus { outline: none; border-color: var(--color-primary); }

/* ------------------------------------- footer controls on the green ---- */

/* The site's primary button is green, which on the green footer would be
   invisible. Inverted here: cream fill, green label — the same pairing read
   the other way round. */
.site-footer .btn--primary {
  background: var(--color-surface-container-low);
  color: var(--color-primary-deep);
  border-color: transparent;
}
.site-footer .btn--primary:hover {
  background: var(--color-surface-container-lowest);
  color: var(--color-primary-deep);
}

/* Secondary/ghost buttons take a cream outline instead of the dark one
   tuned for a light page. */
.site-footer .btn--secondary {
  color: var(--color-surface-container-low);
  border-color: rgb(251 246 224 / 70%);
  background: transparent;
}
.site-footer .btn--secondary:hover {
  background: rgb(251 246 224 / 12%);
  color: var(--color-surface-container-lowest);
}

/* Icon buttons — the social links. */
.site-footer .icon-btn {
  color: var(--color-surface-container-low);
}
.site-footer .icon-btn:hover {
  background: rgb(251 246 224 / 14%);
  color: var(--color-surface-container-lowest);
}

/* The newsletter field keeps a white fill so typed text stays high-contrast;
   only its border needs to work against the green. */
.site-footer .newsletter__input {
  border-color: transparent;
}
.site-footer .newsletter__input:focus {
  border-color: var(--color-surface-container-low);
}

/* Focus rings are drawn for a light page; on the green they need to be cream
   or they disappear exactly where they matter most. */
.site-footer :focus-visible {
  outline-color: var(--color-surface-container-low);
}

/* Muted text is a dark grey tuned for a light page — on the green it measures
   1.20:1 and is effectively invisible. Muted HERE means slightly-receded
   cream, not grey. */
.site-footer .t-muted {
  color: var(--color-surface-container-low);
}

/* The brand wordmark takes its colour from the header, where it is green on
   cream. In the footer that is green on green. */
.site-footer .site-header__brand {
  color: var(--color-surface-container-low);
}

/* The freshness note sits in the brand column. Its tinted background was
   tuned for a light page; on the green it becomes a translucent panel. */
.site-footer .freshness-note {
  background: rgb(251 246 224 / 14%);
  color: var(--color-surface-container-low);
}

/* ---------------------------------------------------------- utilities --- */

.icon { font-size: 24px; line-height: 1; user-select: none; }
.icon--sm { font-size: 20px; }

/* ==========================================================================
   Heading rhythm — CONTRACT §2.1/§2.2

   Spacing beneath a heading scales with its size, so the whole site keeps one
   vertical rhythm without any component inventing its own. Set once here; a
   component only overrides it when the design genuinely differs.
   ========================================================================== */

h1, .t-display      { margin-block-end: var(--space-md); }
h2, .t-headline     { margin-block-end: var(--space-sm); }
h3, .t-headline-sm  { margin-block-end: var(--space-base); }
h4, h5, h6          { margin-block-end: var(--space-xs); }

/* A heading immediately followed by its lead paragraph is one unit: the
   paragraph carries the gap to whatever comes next, not the heading. */
h1 + p, h2 + p, h3 + p,
.t-display + p, .t-headline + p, .t-headline-sm + p { margin-block-start: 0; }

/* Body copy inside a prose/flow context keeps an even measure. */
p + p { margin-block-start: var(--space-sm); }

/* A heading that follows content needs air above it, proportional to size. */
* + h2, * + .t-headline    { margin-block-start: var(--space-lg); }
* + h3, * + .t-headline-sm { margin-block-start: var(--space-md); }

/* …but never when it is the first child of its container, or inside a
   layout component that already owns its own spacing. */
h2:first-child, h3:first-child,
.section__head h2, .section__head h3,
.card__body > h2:first-child, .card__body > h3:first-child,
.drawer__header *, .site-footer__title { margin-block-start: 0; }

/* Drawer search — mobile's search entry point (CONTRACT §3). */
.drawer__search {
  display: flex;
  align-items: center;
  gap: var(--space-base);
  margin-block-end: var(--space-md);
  padding: var(--space-base) var(--space-sm);
  background: var(--color-surface-container-low);
  border: 1px solid transparent;
  border-radius: var(--radius);
  color: var(--color-on-surface-variant);
}
.drawer__search:focus-within { border-color: var(--color-outline-variant); background: var(--color-surface-container-lowest); }
.drawer__search-input {
  inline-size: 100%;
  min-inline-size: 0;
  border: 0;
  background: none;
  font-size: var(--text-body-md);
}
.drawer__search-input:focus { outline: none; }

/* ========================================================= BOTTOM NAV ====
   Phone-only navigation for the four destinations of the buying journey.
   Hidden from 768px up, where the header nav already carries them.
   ======================================================================== */

.bottom-nav {
  position: fixed;
  inset-block-end: 0;
  inset-inline: 0;
  z-index: var(--z-header);
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  background: var(--color-surface-container-lowest);
  border-block-start: 1px solid var(--color-outline-variant);
  /* Clear the iOS home indicator. */
  padding-block-end: env(safe-area-inset-bottom, 0px);
}

.bottom-nav__item {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  min-block-size: 56px;
  padding-block: var(--space-base);
  color: var(--color-on-surface-variant);
  text-decoration: none;
  transition: color var(--transition);
}
.bottom-nav__item:hover { color: var(--color-primary-deep); }

/* The active item carries BOTH colour and a rule above it: colour alone is
   the only signal for someone who cannot distinguish it. */
.bottom-nav__item.is-active {
  color: var(--color-primary-deep);
  box-shadow: inset 0 2px 0 var(--color-primary);
}
.bottom-nav__item.is-active .bottom-nav__icon {
  font-variation-settings: "FILL" 1;
}

.bottom-nav__icon { font-size: 24px; }
.bottom-nav__label {
  font-size: var(--text-label-sm);
  font-weight: var(--weight-medium);
  line-height: 1;
}

/* The cart badge is positioned against this wrapper, not the whole item —
   anchored to the item it would sit beside the label instead of the icon. */
.bottom-nav__icon-wrap {
  position: relative;
  display: inline-flex;
}

/* Reserve room so the last section of a page is never hidden behind the bar.
   Applied to <body>, and removed with the bar itself at 768px. */
.has-bottom-nav {
  padding-block-end: calc(56px + env(safe-area-inset-bottom, 0px));
}

@media (min-width: 768px) {
  .bottom-nav { display: none; }
  .has-bottom-nav { padding-block-end: 0; }
}
