/* ==========================================================================
   LUSTRE / base.css                                            [agent-02]

   Reset, self-hosted type, the type scale, and layout primitives.
   Loaded AFTER tokens.css and BEFORE components.css and pages.css, so
   anything here can be overridden by agent-03 (components) and 05/06/07
   (pages) without !important.

   RULES THIS FILE OBEYS
   - Mobile first. Every media query is min-width, with one exception:
     @media print.
   - Tokens only. Not one hardcoded colour, font family or type size.
     (The @font-face block names the family it defines; that is the
     definition, not a usage.)
   - No component styles. No .btn, .card, .badge, .field, .price, .site-*,
     .drawer. Those are agent-03's. The only mentions of component class
     names in this file are inside @media print, where chrome is hidden.
   - No horizontal body scroll, ever.

   Provenance tags [M] measured on analuisa.com / [I] inferred: see
   tokens.css and DESIGN.md.
   ========================================================================== */


/* ==========================================================================
   1. TYPE, SELF HOSTED
   Mulish, SIL Open Font License 1.1 (licence text at
   public/fonts/Mulish-OFL.txt). Variable, wght axis 200..1000, so two
   files cover the whole site. No external font request is ever made.
   views/layout.js preloads public/fonts/mulish-body-latin.woff2 at boot.
   ========================================================================== */

@font-face {
  font-family: "Mulish";
  font-style: normal;
  font-weight: 200 1000;
  font-display: swap;
  src: url("/fonts/mulish-body-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
                 U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F,
                 U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF,
                 U+FFFD;
}

@font-face {
  font-family: "Mulish";
  font-style: normal;
  font-weight: 200 1000;
  font-display: swap;
  src: url("/fonts/mulish-body-latinext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
                 U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF,
                 U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB,
                 U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Metric matched fallback so the swap from system sans to Mulish does not
   move a single line. Numbers read out of the shipped woff2 with fontTools:
   unitsPerEm 1000, hhea ascender 1005, descender -250, lineGap 0. [M, font]
   Vertical CLS goes to zero. Horizontal reflow is under 2 percent
   (Mulish average lowercase advance .5067em vs Arial .5107em). */
@font-face {
  font-family: "Mulish Fallback";
  font-style: normal;
  font-weight: 200 1000;
  src: local("Segoe UI"), local("Helvetica Neue"), local("Arial"),
       local("Roboto"), local("DejaVu Sans");
  ascent-override: 100.5%;
  descent-override: 25%;
  line-gap-override: 0%;
}


/* ==========================================================================
   2. RESET
   ========================================================================== */

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

* { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* clip, not hidden: hidden on the root kills position:sticky, and the
     site header is sticky. */
  overflow-x: clip;
  tab-size: 4;
}

body {
  min-height: 100vh;
  min-height: 100dvh;
  background-color: var(--bg);
  color: var(--fg);
  font-family: var(--font-body);
  font-size: var(--fs-md);
  font-weight: var(--fw-regular);
  line-height: var(--lh-body);
  overflow-x: clip;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-synthesis-weight: none;
}

/* Media. height:auto is deliberately NOT applied to svg: agent-03's icons
   carry explicit width/height attributes and would collapse. */
img,
video,
canvas,
audio,
iframe,
embed,
object {
  display: block;
  max-width: 100%;
}

img,
video,
canvas { height: auto; }

svg {
  max-width: 100%;
  flex-shrink: 0;
}

picture,
figure { display: block; }

/* Author declared lists keep their markers. Lists marked role="list" are
   navigation or grids and lose them. */
ul[role="list"],
ol[role="list"] {
  list-style: none;
  padding: 0;
}

hr {
  border: 0;
  border-top: 1px solid var(--line);
  height: 0;
}

[hidden] { display: none !important; }

/* Anchor targets clear the sticky header. */
:target { scroll-margin-top: calc(var(--header-h) + var(--sp-4)); }

::selection {
  background-color: var(--fg);
  color: var(--fg-inverse);
}


/* ==========================================================================
   3. TYPE SCALE
   The measured Ana Luisa ladder, element by element.
     h1  typo-h1     24 -> 40   [M]
     h2  typo-h2     18 -> 24   [M]
     h3  typo-h3     15 -> 20   [M]
     h4  typo-body   13 -> 15   [M]
     h5  fine print  12 -> 14   [M]
     h6  typo-micro-bold-caps 10 -> 12, uppercase, +.05em  [M]
   Everything is weight 700 and line-height 1.25, which is what every
   typo-* class on their site resolves to. The ONE exception is .display,
   below: past about 28px Ana Luisa drops to weight 400 and pulls the
   tracking in. Follow that or big headings read as a SaaS landing page.
   ========================================================================== */

h1, h2, h3, h4, h5, h6 {
  font-family: var(--font-display);
  font-weight: var(--fw-bold);
  line-height: var(--lh-tight);
  text-wrap: balance;
}

h1 { font-size: var(--fs-3xl); }
h2 { font-size: var(--fs-xl); }
h3 { font-size: var(--fs-lg); }
h4 { font-size: var(--fs-md); }
h5 { font-size: var(--fs-sm); }

h6 {
  font-size: var(--fs-xs);
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
}

/* Editorial display. Weight 400, tight tracking, near solid leading.
   Measured from their .h1 module: 28px / weight 400 / -.01em / 100%. [M]
   Use for at most one thing per page. */
.display {
  font-family: var(--font-display);
  font-size: var(--fs-4xl);
  font-weight: var(--fw-regular);
  line-height: var(--lh-display);
  letter-spacing: var(--ls-tight);
  text-wrap: balance;
}

.display--bold { font-weight: var(--fw-bold); }

/* The bridge step between h1 and .display. */
.title {
  font-family: var(--font-display);
  font-size: var(--fs-2xl);
  font-weight: var(--fw-regular);
  line-height: var(--lh-display);
  letter-spacing: var(--ls-tight);
}

/* Micro caps label. Their single most recognisable text treatment:
   brand eyebrows, section kickers, "FREE SHIPPING", filter headings. */
.eyebrow {
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  line-height: var(--lh-tight);
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
}

.eyebrow--wide { letter-spacing: var(--ls-caps-wide); }

/* Standfirst under a heading. */
.lead {
  font-size: var(--fs-prose);
  line-height: var(--lh-relaxed);
  color: var(--fg-muted);
  max-width: var(--container-narrow);
}

p { text-wrap: pretty; }

p + p { margin-top: var(--sp-4); }

small,
.text-sm { font-size: var(--fs-sm); }

.text-xs { font-size: var(--fs-xs); }

strong, b { font-weight: var(--fw-bold); }

em, i { font-style: italic; }

s, del { text-decoration-thickness: 1px; }

mark {
  background-color: var(--bg-soft);
  color: inherit;
}

abbr[title] {
  text-decoration: underline dotted;
  cursor: help;
}

sub, sup {
  font-size: 0.7em;
  line-height: 0;
  position: relative;
  vertical-align: baseline;
}
sub { bottom: -0.25em; }
sup { top: -0.5em; }

code, kbd, samp, pre {
  font-family: var(--font-mono);
  font-size: 0.9em;
}

pre {
  overflow-x: auto;
  max-width: 100%;
}

blockquote {
  font-size: var(--fs-lg);
  line-height: var(--lh-tight);
  padding-left: var(--sp-5);
  border-left: 1px solid var(--line-strong);
}

/* Colour helpers. Kept here because they are part of the type system,
   not components. */
.text-muted  { color: var(--fg-muted); }
.text-subtle { color: var(--fg-subtle); }
.text-sale   { color: var(--sale); }
.text-center { text-align: center; }
.uppercase   { text-transform: uppercase; letter-spacing: var(--ls-caps); }
.nowrap      { white-space: nowrap; }

.truncate {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Product titles are long in this catalogue. Clamp instead of truncating
   to one line, so the second half of the name is still readable. */
.clamp-2,
.clamp-3 {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.clamp-2 { -webkit-line-clamp: 2; line-clamp: 2; }
.clamp-3 { -webkit-line-clamp: 3; line-clamp: 3; }


/* ==========================================================================
   4. LINKS
   Ana Luisa does not underline links in chrome, navigation or on cards:
   they are identified by position. Inside running prose they ARE
   underlined, which is what .prose does further down. Do not "fix" this
   by underlining everything, it wrecks the grid.
   ========================================================================== */

a {
  color: inherit;
  text-decoration: none;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
  transition: color var(--dur-instant) var(--ease),
              opacity var(--dur-instant) var(--ease);
}

a:hover { text-decoration: underline; }

a:active { opacity: 0.7; }

/* An underlined link that stays underlined. Use for text links that sit
   in a block of copy but are not inside .prose. */
.link {
  text-decoration: underline;
  text-underline-offset: 0.2em;
}
.link:hover { text-decoration-color: var(--fg-muted); }

.link-quiet { color: var(--fg-muted); }
.link-quiet:hover { color: var(--fg); }


/* ==========================================================================
   5. FOCUS
   The ring is non negotiable. Never set outline:none without replacing it.
   Ana Luisa's own focus style is box-shadow 0 0 0 5px rgba(168,19,10,.1),
   kept as --ring, but that is a 1.1:1 halo and fails WCAG 2.4.11. We use
   a real outline in --focus (deep teal, ~9:1 on white) instead, and flip
   it to white inside anything marked .on-inverse or [data-inverse].
   ========================================================================== */

:focus { outline: none; }

:focus-visible {
  outline: var(--ring-w) solid var(--focus);
  outline-offset: var(--ring-off);
}

/* Keyboard focus inside dark sections (footer, full bleed bands). Put
   .on-inverse or data-inverse on the dark container. */
.on-inverse :focus-visible,
[data-inverse] :focus-visible { outline-color: var(--fg-inverse); }

/* Safety net only. Agent-03 owns .skip-link in components.css and that
   will win, this just guarantees the link is never visible-by-default if
   components.css is missing. */
.skip-link {
  position: absolute;
  left: var(--sp-2);
  top: calc(var(--sp-2) * -12);
  z-index: var(--z-toast);
  padding: var(--sp-2) var(--sp-4);
  background-color: var(--bg);
  color: var(--fg);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
}
.skip-link:focus { top: var(--sp-2); }

.sr-only,
.visually-hidden {
  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;
}

.sr-only-focusable:not(:focus):not(:focus-within) {
  position: absolute !important;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}


/* ==========================================================================
   6. LAYOUT PRIMITIVES
   ========================================================================== */

/* The page container. --container 1440px [M, their --screen-xl], gutter
   20px mobile / 50px at 770 / 80px at 1280. 1440 minus two 80px gutters
   is 1280, which is the max-width they put on hero titles. */
.container,
.wrap {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.container--narrow { max-width: calc(var(--container-narrow) + var(--gutter) * 2); }
.container--prose  { max-width: calc(var(--container-prose)  + var(--gutter) * 2); }
.container--wide   { max-width: var(--container-wide); }
.container--flush  { padding-inline: 0; }

/* Full bleed escape hatch for a child of a .container. */
.bleed {
  width: 100vw;
  max-width: 100vw;
  margin-inline: calc(50% - 50vw);
}

/* Vertical section rhythm. 48 mobile / 80 at 770 / 120 at 1280. */
.section { padding-block: var(--section-y); }
.section--tight { padding-block: calc(var(--section-y) / 2); }
.section--soft  { background-color: var(--bg-soft); }
.section--cream { background-color: var(--bg-editorial); }
.section--inverse {
  background-color: var(--bg-inverse);
  color: var(--fg-inverse);
}
.section + .section { padding-top: 0; }

/* --------------------------------------------------------------------------
   The grid.
   MEASURED, verbatim off the live collection page:
     grid grid-cols-4 gap-x-[25px] gap-y-[50px]
     mobile:grid-cols-2 mobile:gap-x-[15px] mobile:gap-y-[30px]
   So: 2 up below 770px, 4 up at 770px and above, with the gaps above.
   Those numbers live in --grid-cols / --grid-gap / --grid-gap-row, so
   plain .grid follows Ana Luisa automatically.
   minmax(0,1fr) is theirs too and it matters: it stops a long product
   title from blowing the column out and forcing a body scrollbar.

   Override the count per instance with --cols, or pin it with the
   modifiers. Agent-03 owns .grid--products and may override any of this.
   -------------------------------------------------------------------------- */
.grid {
  display: grid;
  grid-template-columns: repeat(var(--cols, var(--grid-cols)), minmax(0, 1fr));
  column-gap: var(--grid-gap);
  row-gap: var(--grid-gap-row);
}

.grid--1 { --cols: 1; }
.grid--2 { --cols: 2; }
.grid--3 { --cols: 3; }
.grid--4 { --cols: 4; }
.grid--6 { --cols: 6; }
.grid--flush { column-gap: 0; row-gap: 0; }
.grid--tight { row-gap: var(--grid-gap); }

/* Anything wide gets its own scroller. Never the body. */
.scroll-x {
  overflow-x: auto;
  overflow-y: hidden;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
  overscroll-behavior-x: contain;
}

.rail {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(150px, 45%);
  column-gap: var(--grid-gap);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
}
.rail::-webkit-scrollbar { display: none; }
.rail > * { scroll-snap-align: start; }

/* Aspect ratio helpers. --card-ratio is 4/5, measured off their product
   media (aspect-ratio:.8, and an explicit 266/333 next to it). */
.ratio-card   { aspect-ratio: var(--card-ratio); }
.ratio-square { aspect-ratio: var(--square); }
.ratio-hero   { aspect-ratio: var(--hero-ratio); }

.ratio-card > img,
.ratio-square > img,
.ratio-hero > img,
.ratio-card > picture > img,
.ratio-square > picture > img,
.ratio-hero > picture > img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

@media (min-width: 770px) {
  .rail { grid-auto-columns: minmax(200px, 24%); }
}


/* ==========================================================================
   7. FORMS
   Element level normalisation only. .field / .field__input / .btn are
   agent-03's; nothing here targets those class names. The point of this
   block is that a bare <input> or <select> that never gets a component
   class is still legible, tappable and reachable by keyboard.
   ========================================================================== */

button,
input,
optgroup,
select,
textarea {
  font: inherit;
  font-family: inherit;
  color: inherit;
  letter-spacing: inherit;
  margin: 0;
}

button,
[type="button"],
[type="reset"],
[type="submit"] {
  -webkit-appearance: none;
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  text-align: inherit;
  color: inherit;
  touch-action: manipulation;
}

button:disabled,
input:disabled,
select:disabled,
textarea:disabled,
[aria-disabled="true"] {
  cursor: not-allowed;
  color: var(--fg-subtle);
}

/* :where() zeroes this chain's specificity. Unwrapped it computes to (0,7,1),
   which outranked every component class in components.css and pages.css and
   was overriding styles it was never meant to touch (search icon sat under the
   placeholder, qty input stretched to 205px). :where() gives exactly the
   element-default behaviour the rest of this block describes. */
:where(input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]):not([type="submit"]):not([type="button"]):not([type="reset"])),
:where(select),
:where(textarea) {
  width: 100%;
  min-height: var(--input-h);
  padding: 0 var(--input-px);
  background-color: var(--bg);
  color: var(--fg);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  /* 1rem, not --fs-md. Anything under 16px makes iOS Safari zoom the page
     on focus, which is a layout shift we are not allowed to ship. Lifted
     back onto the scale at 770px, where touch keyboards stop mattering. */
  font-size: 1rem;
  line-height: var(--lh-tight);
  -webkit-appearance: none;
  appearance: none;
  transition: border-color var(--dur-instant) var(--ease);
}

textarea {
  min-height: calc(var(--input-h) * 2.5);
  padding-block: var(--sp-3);
  line-height: var(--lh-body);
  resize: vertical;
}

select {
  padding-right: calc(var(--input-px) * 2);
  cursor: pointer;
}

/* Ana Luisa's focused field goes to a solid black hairline. [M] */
input:focus,
select:focus,
textarea:focus { border-color: var(--line-strong); }

::placeholder {
  color: var(--fg-subtle);
  opacity: 1;
}

@media (min-width: 770px) {
  input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]),
  select,
  textarea { font-size: var(--fs-md); }
}

label { display: inline-block; }

fieldset {
  border: 0;
  padding: 0;
  min-width: 0;
}

legend { padding: 0; }

[type="checkbox"],
[type="radio"] {
  width: var(--sp-5);
  height: var(--sp-5);
  accent-color: var(--accent);
  cursor: pointer;
  flex-shrink: 0;
}

[type="search"] { -webkit-appearance: none; appearance: none; }
[type="search"]::-webkit-search-decoration,
[type="search"]::-webkit-search-cancel-button { -webkit-appearance: none; }

::-webkit-file-upload-button {
  -webkit-appearance: button;
  font: inherit;
}

input[type="number"] { -moz-appearance: textfield; appearance: textfield; }
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

summary { cursor: pointer; list-style: none; }
summary::-webkit-details-marker { display: none; }

table {
  border-collapse: collapse;
  width: 100%;
}
th { text-align: left; font-weight: var(--fw-bold); }
th, td {
  padding: var(--sp-3) var(--sp-3) var(--sp-3) 0;
  border-bottom: 1px solid var(--line);
  vertical-align: top;
}


/* ==========================================================================
   8. PROSE
   Wrapper for HTML we did not write: product descriptionHtml, howToUse,
   ingredients and the static pages. The catalogue ships arbitrary markup
   including tables, so everything wide is boxed into its own scroller and
   the body never scrolls sideways.
   Prose runs at --fs-prose (14 -> 16), which is a step ABOVE UI text
   (13 -> 15). That gap is measured: Ana Luisa's .text is 14/22 mobile and
   16/24 desktop while typo-body is 12/15.
   ========================================================================== */

.prose {
  font-size: var(--fs-prose);
  line-height: var(--lh-relaxed);
  max-width: var(--container-narrow);
}

.prose > * + * { margin-top: var(--sp-4); }

.prose h2,
.prose h3,
.prose h4 { margin-top: var(--sp-7); }

.prose h2:first-child,
.prose h3:first-child,
.prose h4:first-child { margin-top: 0; }

.prose ul,
.prose ol { padding-left: var(--sp-5); }

.prose li + li { margin-top: var(--sp-2); }

.prose a {
  text-decoration: underline;
  text-underline-offset: 0.2em;
}
.prose a:hover { color: var(--fg-muted); }

.prose img { margin-block: var(--sp-5); }

.prose table {
  display: block;
  max-width: 100%;
  overflow-x: auto;
  font-size: var(--fs-sm);
}

.prose blockquote { margin-block: var(--sp-6); }

.prose :where(h2, h3, h4) + * { margin-top: var(--sp-3); }

@media (min-width: 770px) {
  .prose { line-height: var(--lh-body); }
}


/* ==========================================================================
   9. MOTION
   Ana Luisa's durations: .15s colour, .2s opacity, .3s default, .6s big
   transforms. All [M]. Anyone who has asked their OS to calm down gets a
   still page, including the smooth scroll and any autoplaying media.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  :root {
    --dur-instant: 1ms;
    --dur-fast: 1ms;
    --dur: 1ms;
    --dur-slow: 1ms;
  }

  html { scroll-behavior: auto; }

  *,
  *::before,
  *::after {
    animation-duration: 1ms !important;
    animation-delay: 0ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 1ms !important;
    transition-delay: 0ms !important;
    scroll-behavior: auto !important;
    background-attachment: initial !important;
  }

  .rail { scroll-snap-type: none; }
}

@media (prefers-contrast: more) {
  :root {
    --line: var(--fg-muted);
    --fg-muted: var(--fg);
  }
}


/* ==========================================================================
   10. PRINT
   ========================================================================== */

@media print {
  body { background: none; color: var(--fg); }
  .site-header, .site-footer, .drawer, .skip-link, .announce { display: none !important; }
  a[href^="http"]::after { content: " (" attr(href) ")"; font-size: 0.8em; }
  .container, .wrap { max-width: none; padding-inline: 0; }
}
