/* =========================================================================
   Esther's - Materials & Colour Configurator
   base.css - design tokens, resets, typography, shell chrome
   ========================================================================= */

:root {
  /* Surfaces - near-black with a cool cast, layered rather than flat */
  --bg:            #0a0b0c;
  --bg-raised:     #101214;
  --bg-panel:      #15181b;
  --bg-elevated:   #1c2024;
  --bg-hover:      #22272c;

  /* Hairlines */
  --line:          rgba(255, 255, 255, 0.09);
  --line-strong:   rgba(255, 255, 255, 0.16);
  --line-soft:     rgba(255, 255, 255, 0.05);

  /* Ink */
  /* Every ink clears 4.5:1 against the darkest surface it sits on
     (--bg-elevated). The old --ink-dim and --ink-faint measured 3.54 and
     2.06, which is why form labels and placeholders were unreadable. */
  --ink:           #f2f4f5;   /* 14.9:1 */
  --ink-muted:     #a4adb4;   /*  7.2:1 */
  --ink-dim:       #909aa1;   /*  5.7:1 */
  --ink-faint:     #808a91;   /*  4.7:1 */

  /* Accent - overridden per material at runtime */
  --accent:        #9aafc4;
  --accent-soft:   rgba(154, 175, 196, 0.14);
  --accent-line:   rgba(154, 175, 196, 0.38);

  /* Metal */
  --metal-hi:      rgba(255, 255, 255, 0.55);
  --metal-lo:      rgba(0, 0, 0, 0.45);

  /* Motion - one shared easing curve keeps the whole UI feeling like one object */
  --ease:          cubic-bezier(0.22, 0.61, 0.36, 1);
  --ease-out:      cubic-bezier(0.16, 1, 0.3, 1);
  --dur-fast:      180ms;
  --dur:           340ms;
  --dur-slow:      620ms;

  /* Metrics */
  --shell:         1440px;
  --gutter:        clamp(20px, 4vw, 64px);
  --radius:        14px;
  --radius-lg:     22px;

  --font-display: "Helvetica Neue", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --font-body:    "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --font-mono:    "SF Mono", "JetBrains Mono", ui-monospace, "Menlo", monospace;
}

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* Makes the browser render native select popups and scrollbars dark. */
  color-scheme: dark;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

img, svg { display: block; max-width: 100%; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
input  { font: inherit; color: inherit; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 4px; }

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

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

.section { padding-block: clamp(72px, 9vw, 140px); position: relative; }
.section--tight { padding-block: clamp(48px, 6vw, 88px); }
.section + .section { border-top: 1px solid var(--line-soft); }

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

.eyebrow {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-dim);
  margin: 0 0 18px;
  display: flex;
  align-items: center;
  gap: 12px;
}
.eyebrow::before {
  content: "";
  width: 28px;
  height: 1px;
  background: var(--accent);
  flex: none;
  transition: width var(--dur) var(--ease);
}

h1, h2, h3, h4 { font-family: var(--font-display); font-weight: 300; margin: 0; letter-spacing: -0.02em; }

.display {
  font-size: clamp(2.6rem, 6.4vw, 5.4rem);
  line-height: 1.02;
  letter-spacing: -0.035em;
}
.headline  { font-size: clamp(1.9rem, 3.6vw, 3.1rem); line-height: 1.1; }
.subhead   { font-size: clamp(1.25rem, 2vw, 1.65rem); line-height: 1.25; }

.lede {
  font-size: clamp(1rem, 1.4vw, 1.18rem);
  color: var(--ink-muted);
  max-width: 62ch;
  line-height: 1.66;
}

.section-head { margin-bottom: clamp(36px, 5vw, 64px); max-width: 76ch; }
.section-head .lede { margin-top: 20px; }

.mono-label {
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-dim);
}

/* ------------------------------------------------------------------- nav */

.topbar {
  position: sticky;
  top: 0;
  z-index: 60;
  background: rgba(10, 11, 12, 0.78);
  backdrop-filter: saturate(150%) blur(18px);
  -webkit-backdrop-filter: saturate(150%) blur(18px);
  border-bottom: 1px solid var(--line-soft);
}

.topbar__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  height: 66px;
}

/* ------------------------------------------------------------------ logo */

/* The hero and the footer carry the supplied stacked lockup. It was drawn on
   white and its files carry a 0.60 gamma so the metal still reads against
   this background - logo-master.webp is that original, untouched.

   The top bar does NOT use artwork. See below. */

/* --- in the top bar --- */

/* Set in live text rather than the supplied wordmark raster.

   The artwork is 1898px wide and the bar shows it 24px tall. Downscaling by
   that much softens every stem no matter how good the resampling is, and the
   metallic shading in the original only made the mush more visible. Type
   solves it outright: one weight change and one colour change carry the same
   hierarchy the artwork was drawing, and it stays exact at any zoom, on any
   display density.

   Two flat colours, nothing else. No gradient, no glow, no shadow, no
   texture - anything of that kind at 17px is what made it look fuzzy in the
   first place. */
.logo--nav {
  display: inline-flex;
  align-items: baseline;
  gap: 9px;
  color: inherit;
  text-decoration: none;
  white-space: nowrap;
  /* Never let the bar squeeze the pair onto two lines. */
  flex: none;
  transition: opacity var(--dur) var(--ease);
}

.logo--nav__name {
  font-family: var(--font-display);
  font-size: clamp(16px, 1.3vw, 17px);
  font-weight: 700;
  letter-spacing: 0.1em;
  line-height: 1;
  /* Solid white, brighter than --ink, so the name is the one thing in the bar
     at full strength. */
  color: #ffffff;
}

.logo--nav__sub {
  font-family: var(--font-body);
  font-size: clamp(11.5px, 0.95vw, 12.5px);
  font-weight: 400;
  letter-spacing: 0.115em;
  line-height: 1;
  /* Deliberately lighter than the name but well clear of the nav links, so it
     reads as part of the mark rather than as another menu item. 12.7:1. */
  color: #c9d1d7;
}

.logo--nav:hover,
.logo--nav:focus-visible { opacity: 0.78; }

/* Under 640px the bar cannot spare the width for the second line, so the name
   runs on its own - the same thing the artwork's phone variant used to do. */
@media (max-width: 639px) {
  .logo--nav__sub { display: none; }
}
/* The narrowest phones need one more step down to keep the quote button on
   the same row. */
@media (max-width: 400px) {
  .logo--nav__name { font-size: 15px; letter-spacing: 0.08em; }
}

/* --- closing the page --- */

/* The footer has the room for the artwork whole. */
.logo--foot {
  display: block;
  width: min(230px, 62vw);
  height: auto;
}

.topnav { display: flex; align-items: center; gap: 4px; }
.topnav a {
  color: var(--ink-muted);
  text-decoration: none;
  font-size: 13px;
  padding: 8px 13px;
  border-radius: 999px;
  transition: color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
  white-space: nowrap;
}
.topnav a:hover { color: var(--ink); background: var(--bg-elevated); }

.topbar__meta {
  display: flex;
  align-items: center;
  gap: 14px;
}

.fav-count {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  color: var(--ink-muted);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 6px 13px;
  transition: border-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.fav-count:hover { border-color: var(--line-strong); color: var(--ink); }
.fav-count[data-empty="true"] { opacity: 0.5; }
.fav-count svg { width: 13px; height: 13px; }

@media (max-width: 1080px) { .topnav { display: none; } }

/* ------------------------------------------------------------------ hero */

/* Layout lives in home.css; this file keeps only the ambient wash and the
   gradient treatment on the emphasised words. */

/* Slow-drifting radial wash - reads as raking light across metal */
.hero::before {
  content: "";
  position: absolute;
  inset: -30% -10% auto -10%;
  height: 130%;
  background:
    radial-gradient(58% 48% at 72% 26%, rgba(154, 175, 196, 0.16), transparent 68%),
    radial-gradient(42% 40% at 22% 74%, rgba(184, 112, 54, 0.10), transparent 70%);
  filter: blur(6px);
  animation: heroDrift 26s var(--ease) infinite alternate;
  pointer-events: none;
}

@keyframes heroDrift {
  from { transform: translate3d(-2%, 0, 0) scale(1); }
  to   { transform: translate3d(3%, -3%, 0) scale(1.09); }
}

.hero .display em {
  font-style: normal;
  background: linear-gradient(96deg, #ffffff 0%, var(--accent) 52%, #7d8a96 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}


/* --------------------------------------------------------------- buttons */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 11px;
  padding: 14px 27px;
  border-radius: 999px;
  font-size: 14px;
  letter-spacing: 0.01em;
  border: 1px solid var(--line-strong);
  background: var(--bg-elevated);
  color: var(--ink);
  /* .btn is used on <a> as well as <button> */
  text-decoration: none;
  transition: transform var(--dur-fast) var(--ease),
              background var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease),
              box-shadow var(--dur) var(--ease);
  white-space: nowrap;
}
.btn:hover { background: var(--bg-hover); border-color: var(--accent-line); transform: translateY(-1px); }
.btn:active { transform: translateY(0); }

.btn--primary {
  background: var(--ink);
  color: #0a0b0c;
  border-color: transparent;
  font-weight: 500;
}
.btn--primary:hover {
  background: #fff;
  box-shadow: 0 12px 38px -14px rgba(255, 255, 255, 0.5);
}

.btn--accent {
  background: var(--accent-soft);
  border-color: var(--accent-line);
  color: var(--ink);
}
.btn--accent:hover { background: var(--accent); color: #0a0b0c; border-color: transparent; }

/* Compact variant for the topbar CTA */
.btn--nav {
  padding: 9px 18px;
  font-size: 13px;
  text-decoration: none;
}

.btn--ghost { background: transparent; border-color: var(--line); color: var(--ink-muted); }
.btn--ghost:hover { color: var(--ink); border-color: var(--line-strong); background: var(--bg-raised); }

.btn__arrow { transition: transform var(--dur) var(--ease-out); }
.btn:hover .btn__arrow { transform: translateX(4px); }

/* ------------------------------------------------------- scrolling rails */

/* Shared controls for any row that scrolls sideways - the Recent
   fabrication gallery on a phone, and the materials rail at every width.

   The problem this solves: a row that scrolls looks exactly like a row that
   does not. A partly visible card at the edge is a hint, but it is easy to
   miss, and on a desktop with a mouse there is no obvious way to move a
   horizontal row at all. So the state is stated outright - how many there
   are, which one you are on, and a control that moves it.

   Built by railAffordance() in app.js, and only for rails that actually
   overflow. A row that fits shows nothing. */

.rail-nav {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  margin-top: 16px;
}

/* The count, spelled out. "1 / 6" removes all doubt about whether there is
   more, which a row of dots alone can leave ambiguous at a glance. */
.rail-nav__count {
  font-family: var(--font-mono);
  font-size: 11.5px;
  letter-spacing: 0.08em;
  color: var(--ink-dim);
  font-variant-numeric: tabular-nums;
  min-width: 4.5em;
  text-align: center;
}

.rail-nav__arrow {
  flex: none;
  width: 40px;
  height: 40px;
  display: grid;
  place-items: center;
  border-radius: 999px;
  border: 1px solid var(--line-strong);
  background: var(--bg-elevated);
  color: var(--ink);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease),
              opacity var(--dur-fast) var(--ease);
}
.rail-nav__arrow:hover { background: var(--bg-hover); }
.rail-nav__arrow:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.rail-nav__arrow svg { width: 17px; height: 17px; }

/* Dimmed rather than removed at the ends: a control that disappears makes
   the row jump, and its absence is easy to read as a fault. disabled also
   takes it out of the tab order, so a keyboard user is not offered a button
   that does nothing. */
.rail-nav__arrow[disabled] {
  opacity: 0.3;
  cursor: default;
  background: transparent;
}

/* Touch targets. 40px is comfortable with a mouse; a thumb wants 44. */
@media (max-width: 999px) {
  .rail-nav__arrow { width: 44px; height: 44px; }
}

.rail-nav__dots {
  display: flex;
  align-items: center;
  gap: 7px;
}

/* Each dot is a real button that jumps to its card, not a decoration. That
   gives a keyboard and screen-reader user the same shortcut a touch user
   gets by swiping, and it makes the count tappable. */
.rail-nav__dot {
  width: 22px;
  height: 22px;
  padding: 0;
  display: grid;
  place-items: center;
  background: none;
  border: 0;
  cursor: pointer;
  border-radius: 999px;
}
.rail-nav__dot::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 999px;
  background: var(--line-strong);
  transition: background var(--dur-fast) var(--ease),
              width var(--dur-fast) var(--ease);
}
.rail-nav__dot:hover::before { background: var(--ink-dim); }
.rail-nav__dot:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* The active dot stretches into a short bar. Shape as well as brightness,
   so it still reads for anyone who cannot separate the two by colour. */
.rail-nav__dot[aria-current="true"]::before {
  width: 20px;
  background: var(--ink);
}

@media (prefers-reduced-motion: reduce) {
  .rail-nav__dot::before, .rail-nav__arrow { transition: none; }
}

/* Narrow phones: the dots go, the count stays.
   Eight dots plus two 44px arrows plus the count is wider than a 320px
   screen, and a strip that overflows would push the whole page sideways -
   which is exactly the fault this control exists to prevent. The count
   says the same thing in less room: "3 / 8" needs no counting and is what
   a screen reader announces anyway.

   This block sits at the END of the rail styles on purpose. It competes
   with `.rail-nav__dots { display: flex }` at equal specificity, so source
   order is what decides it - placed any earlier, the flex rule wins and
   the dots never hide. */
@media (max-width: 559px) {
  .rail-nav { gap: 10px; }
  .rail-nav__dots { display: none; }
  .rail-nav__count { min-width: 5em; font-size: 12px; }
}

/* ---------------------------------------------------------------- footer */

.site-footer {
  border-top: 1px solid var(--line);
  padding-block: 60px 44px;
  background: var(--bg-raised);
}
.site-footer__grid {
  display: grid;
  grid-template-columns: 1.4fr repeat(2, minmax(0, 1fr));
  gap: clamp(28px, 5vw, 72px);
  margin-bottom: 44px;
}
.site-footer h4 { font-size: 13px; letter-spacing: 0.02em; margin-bottom: 16px; font-weight: 500; }
.site-footer ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 9px; }
.site-footer li { font-size: 13.5px; color: var(--ink-dim); }
.site-footer__legal {
  border-top: 1px solid var(--line-soft);
  padding-top: 26px;
  display: flex;
  flex-wrap: wrap;
  gap: 16px 32px;
  justify-content: space-between;
  font-size: 12px;
  color: var(--ink-faint);
}
@media (max-width: 760px) { .site-footer__grid { grid-template-columns: 1fr; } }

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

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

.reveal {
  opacity: 0;
  transform: translateY(24px);
  transition: opacity var(--dur-slow) var(--ease-out), transform var(--dur-slow) var(--ease-out);
}
.reveal.is-in { opacity: 1; transform: none; }

/* Respect reduced-motion: kill transforms and long transitions, keep colour
   feedback so the configurator still communicates state changes. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  .reveal { opacity: 1; transform: none; }
  .hero::before { animation: none; }
}
