/* TileProdigy — see DESIGN.md for the reasoning behind every token here. */
:root {
  --paper: #FFFFFF;
  --travertine: #F1EEEA;   /* alternate section ground, a warm stone grey (not cream) */
  --grout: #D6D1C9;
  --ink: #1D2430;
  --stone: #5B6370;
  --bronze: #8C6A3F;       /* accent + primary button; white text on it passes AA */
  --bronze-deep: #6F5332;  /* links, button hover */
  --fair-blue: #1F57A6;    /* logo only */
  --joint: 6px;
  --s1: 4px; --s2: 8px; --s3: 16px; --s4: 24px; --s5: 40px; --s6: 64px; --s7: 104px; --s8: 160px;
  --display: "Marcellus", Georgia, serif;
  --body: "Hanken Grotesk", system-ui, sans-serif;
  --mono: "IBM Plex Mono", ui-monospace, monospace;
  --ease: cubic-bezier(.2, .7, .2, 1);
  --wrap: 1240px;
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font: 400 1rem/1.65 var(--body);
}
img { display: block; max-width: 100%; height: auto; }
a { color: var(--bronze-deep); text-underline-offset: 3px; }
a:hover { color: var(--ink); }
:focus-visible { outline: 3px solid var(--bronze); outline-offset: 3px; }

h1, h2 { font-family: var(--display); font-weight: 400; line-height: 1.08; margin: 0 0 var(--s4); }
h1 { font-size: clamp(2.6rem, 6vw, 4.6rem); letter-spacing: -0.01em; }
h2 { font-size: clamp(1.9rem, 3.6vw, 2.6rem); }
h3 { font-size: 1.25rem; font-weight: 600; margin: 0 0 var(--s2); }
p { margin: 0 0 var(--s3); max-width: 62ch; }
.lead { font-size: 1.25rem; color: var(--stone); }

.wrap { max-width: var(--wrap); margin: 0 auto; padding: 0 var(--s4); }
.skip { position: absolute; left: -9999px; }
.skip:focus { left: var(--s3); top: var(--s3); background: var(--ink); color: var(--paper); padding: var(--s2) var(--s3); z-index: 10; }

/* Mono caption, set like a tile spec sheet: material · room */
.spec {
  font: 400 0.78rem/1.4 var(--mono);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--bronze);
}

/* The grout band replaces hairline dividers everywhere. */
.band { height: var(--joint); background: var(--grout); border: 0; margin: 0; }

/* Unconfirmed facts stay visibly marked until Paul supplies them.
   deploy.ps1 refuses to ship any page containing TODO-PAUL. */
.todo { background: #FFE9A8; color: var(--ink); padding: 0 4px; font: 600 0.85em var(--mono); }

/* ---------- header ---------- */
.site-head { padding: var(--s3) 0; position: relative; z-index: 2; }
.site-head .wrap { display: flex; align-items: center; justify-content: space-between; gap: var(--s4); flex-wrap: wrap; }
.brand { display: flex; align-items: center; gap: var(--s3); text-decoration: none; color: var(--ink); }
.brand img { width: 48px; height: 48px; border-radius: 50%; object-fit: cover; }
.brand-name { font: 400 1.5rem/1 var(--display); letter-spacing: 0.02em; }
.brand-sub { display: block; font: 400 0.72rem/1.4 var(--mono); letter-spacing: 0.08em; text-transform: uppercase; color: var(--stone); margin-top: 4px; }
.nav { display: flex; gap: var(--s4); flex-wrap: wrap; }
.nav a { color: inherit; text-decoration: none; font-weight: 600; padding: var(--s1) 0; border-bottom: 2px solid transparent; transition: border-color 200ms ease; }
.nav a:hover, .nav a[aria-current="page"] { border-bottom-color: currentColor; }
.site-head { color: var(--ink); }

/* Home only: the header sits on top of the full-screen photo */
.site-head.over { position: absolute; top: 0; left: 0; right: 0; color: #fff; }
.site-head.over .brand { color: #fff; }
.site-head.over .brand-sub { color: rgba(255,255,255,.75); }

/* ---------- buttons ---------- */
.btn {
  display: inline-block;
  font: 600 1rem/1 var(--body);
  padding: 16px 24px;
  border-radius: 2px;
  border: 2px solid var(--bronze);
  background: var(--bronze);
  color: #fff;
  text-decoration: none;
  cursor: pointer;
  transition: background-color 200ms ease, border-color 200ms ease, color 200ms ease;
}
.btn:hover { background: var(--bronze-deep); border-color: var(--bronze-deep); color: #fff; }
.btn:active { transform: translateY(1px); }
.btn-line { background: transparent; color: var(--ink); border-color: var(--ink); }
.btn-line:hover { background: var(--ink); border-color: var(--ink); color: #fff; }
.btn-ghost { background: transparent; color: #fff; border-color: rgba(255,255,255,.8); }
.btn-ghost:hover { background: #fff; border-color: #fff; color: var(--ink); }
.btn-row { display: flex; gap: var(--s3); flex-wrap: wrap; }

/* ---------- full-screen hero (home) ---------- */
.hero {
  position: relative;
  min-height: min(94vh, 860px);
  display: flex;
  align-items: flex-end;
  color: #fff;
  overflow: hidden;
  background: var(--ink);
}
.hero-photo { position: absolute; inset: 0; margin: 0; }
.hero-photo img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 40%; }
/* Tonal ink shade so white type reads on any photo; also hides some softness
   in the low-res mockup images. Single hue, not a decorative gradient. */
.hero-photo::after {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(20,24,32,.6) 0, rgba(20,24,32,0) 180px),   /* keeps the white nav readable over light parts of the photo */
              linear-gradient(90deg, rgba(20,24,32,.86) 0%, rgba(20,24,32,.55) 50%, rgba(20,24,32,.2) 100%),
              linear-gradient(0deg, rgba(20,24,32,.6) 0%, rgba(20,24,32,0) 40%);
}
.hero-copy { position: relative; padding-bottom: var(--s7); padding-top: var(--s8); }
.hero h1 {
  font-size: clamp(2.8rem, 7.4vw, 6.4rem);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  line-height: 1.02;
  max-width: 11ch;
  margin-bottom: var(--s4);
}
.hero h1 em { font-style: normal; color: #D9BE93; }
/* Small label above the headline naming the specialty */
.hero-kicker { font: 400 .8rem/1.4 var(--mono); letter-spacing: .16em; text-transform: uppercase; color: #D9BE93; margin: 0 0 var(--s3); }   /* the brand word, in a light bronze */
.hero p { font-size: 1.2rem; color: rgba(255,255,255,.86); max-width: 46ch; margin-bottom: var(--s5); }
.scroll-cue { position: absolute; right: var(--s4); bottom: var(--s5); font: 400 .72rem/1 var(--mono); letter-spacing: .16em; text-transform: uppercase; color: rgba(255,255,255,.7); writing-mode: vertical-rl; }

/* ---------- "the hard ones" showcase ---------- */
.intro { max-width: 760px; }
.intro h2 { font-size: clamp(2.2rem, 4.6vw, 3.6rem); }
.showcase { display: grid; grid-template-columns: 7fr 5fr; gap: var(--s6); align-items: center; padding: var(--s6) 0; }
.showcase.flip { grid-template-columns: 5fr 7fr; }
.showcase.flip .frame { order: 2; }
.frame { margin: 0; background: var(--grout); padding: var(--joint); border-radius: 2px; overflow: hidden; }
.frame img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; border-radius: 2px; }
.frame.portrait img { aspect-ratio: 4 / 5; max-height: min(78vh, 680px); }
/* Third-party photos carry a watermark at the edge: never crop them. .natural shows the
   whole photo at its own shape; .contain letterboxes it inside a gallery cell. */
.frame.natural { max-width: 440px; justify-self: center; }
.frame.natural img { aspect-ratio: auto; max-height: none; }
.gallery img.contain { object-fit: contain; background: var(--paper); }
.showcase h3 { font: 400 clamp(1.8rem, 3vw, 2.4rem)/1.1 var(--display); margin: var(--s2) 0 var(--s3); }
.showcase p { color: var(--stone); font-size: 1.1rem; }
.showcase .credit { font: 400 .78rem/1.4 var(--mono); letter-spacing: .04em; color: var(--stone); margin-top: var(--s3); }

/* ---------- mosaic (the signature: photos set in grout joints) ---------- */
.ground { background: var(--travertine); }
.mosaic {
  display: grid;
  gap: var(--joint);
  background: var(--grout);
  padding: var(--joint);
  grid-template-columns: 1.15fr 0.85fr 1fr 0.9fr;
  grid-template-rows: 300px 300px;
  grid-template-areas:
    "say say m1 m2"
    "m3  m4  m1 m5";
}
.mosaic > * { margin: 0; overflow: hidden; border-radius: 2px; }
.mosaic img { width: 100%; height: 100%; object-fit: cover; }
.mosaic .say {
  grid-area: say;
  background: var(--ink);
  color: #fff;
  padding: var(--s5);
  display: flex; flex-direction: column; justify-content: flex-end; align-items: flex-start;
}
.mosaic .say h2 { margin-bottom: var(--s2); }
.mosaic .say p { color: #C8CDD6; }
.m1 { grid-area: m1; } .m2 { grid-area: m2; } .m3 { grid-area: m3; } .m4 { grid-area: m4; } .m5 { grid-area: m5; }

/* ---------- sections ---------- */
.section { padding: var(--s7) 0; }
.split { display: grid; grid-template-columns: 5fr 7fr; gap: var(--s6); align-items: start; }
.split.center { align-items: center; }

/* Typeset list with grout rules, used instead of cards */
.ruled { list-style: none; margin: 0; padding: 0; border-top: var(--joint) solid var(--grout); }
.ruled li { padding: var(--s4) 0; border-bottom: 1px solid var(--grout); transition: border-color 300ms ease; }
.ruled li:hover { border-bottom-color: var(--bronze); }
.ruled li p { margin: var(--s2) 0 0; color: var(--stone); }
.ruled h3 { margin: 0; font: 400 1.75rem/1.15 var(--display); transition: transform 300ms var(--ease); }
.ruled li:hover h3 { transform: translateX(6px); }
.ruled h3 a { color: var(--ink); text-decoration: none; }
.ruled h3 a:hover { color: var(--bronze-deep); }

/* A real sequence, so the numbers carry meaning here */
.steps { counter-reset: step; list-style: none; padding: 0; margin: 0; border-top: var(--joint) solid var(--grout); }
.steps li { counter-increment: step; display: grid; grid-template-columns: 3.5rem 1fr; padding: var(--s4) 0; border-bottom: 1px solid var(--grout); }
.steps li::before { content: counter(step); font: 400 2rem/1 var(--display); color: var(--bronze); }
.steps p { margin: 0; color: var(--stone); }

.quote { margin: 0; }
.quote blockquote { margin: 0 0 var(--s3); font: 400 clamp(1.8rem, 3.4vw, 2.6rem)/1.2 var(--display); }
.quote figcaption { color: var(--stone); }
.quote-note { margin-top: var(--s4); color: var(--stone); }
.photo-frame { background: var(--grout); padding: var(--joint); border-radius: 2px; margin: 0; overflow: hidden; }
.photo-frame img { border-radius: 2px; width: 100%; aspect-ratio: 4 / 5; object-fit: cover; }
/* Caption sits on white, not grout: bronze on grout was only ~3.3:1 contrast */
.photo-frame figcaption { padding: var(--s2) var(--s2); margin-top: var(--joint); background: var(--paper); }

.cta { background: var(--ink); color: #fff; padding: var(--s6) 0; }
.cta .wrap { display: flex; justify-content: space-between; align-items: center; gap: var(--s4); flex-wrap: wrap; }
.cta h2 { margin: 0; }
.cta p { color: #C8CDD6; margin: var(--s2) 0 0; }

/* ---------- gallery ---------- */
.page-head { padding: var(--s6) 0 var(--s5); }
.jump { display: flex; gap: var(--s4); flex-wrap: wrap; margin-top: var(--s4); font-weight: 600; }
.gallery {
  display: grid;
  gap: var(--joint);
  background: var(--grout);
  padding: var(--joint);
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: 280px;
  grid-auto-flow: dense;   /* backfill: without it a wide photo that can't fit leaves an empty grout cell at tablet width */
  border-radius: 2px;
}
.gallery.two { grid-template-columns: 1fr 1fr; grid-auto-rows: 560px; }
.gallery figure { margin: 0; overflow: hidden; background: var(--paper); display: flex; flex-direction: column; }
.gallery img { width: 100%; flex: 1; min-height: 0; object-fit: cover; }
.gallery figcaption { padding: var(--s2) var(--s3); position: relative; z-index: 1; background: var(--paper); }
.gallery .tall { grid-row: span 2; }
.gallery .wide { grid-column: span 2; }
.gallery-group { scroll-margin-top: var(--s4); padding-bottom: var(--s6); }
.gallery-group h2 { margin-bottom: var(--s2); }
.gallery-group > p { color: var(--stone); margin-bottom: var(--s4); }

/* ---------- form ---------- */
.form { display: grid; gap: var(--s4); max-width: 620px; }
.form label { display: block; font-weight: 600; margin-bottom: var(--s2); }
.form .hint { font-weight: 400; color: var(--stone); }
.form input, .form select, .form textarea {
  width: 100%;
  font: 400 1rem/1.4 var(--body);
  color: var(--ink);
  background: #fff;
  border: 2px solid var(--grout);
  border-radius: 2px;
  padding: 12px 14px;
  transition: border-color 200ms ease;
}
.form input:focus, .form select:focus, .form textarea:focus { outline: none; border-color: var(--ink); }
/* :user-invalid only fires after the visitor has touched the field,
   so an empty form does not open covered in red. */
.form :user-invalid { border-color: #A33A2B; }
.form :user-invalid + .err, .form :user-invalid ~ .err { display: block; }
.err { display: none; color: #A33A2B; font-size: 0.9rem; margin-top: var(--s1); }
.form textarea { min-height: 150px; resize: vertical; }
.form .row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s4); }
.hp { position: absolute; left: -9999px; }
/* Contact: big tap-to-call / email rows */
.reach { border-top: var(--joint) solid var(--grout); }
.reach-row { display: block; padding: var(--s4) 0; border-bottom: 1px solid var(--grout); color: var(--ink); text-decoration: none; transition: border-color 300ms ease; }
.reach-row:hover { border-bottom-color: var(--bronze); color: var(--bronze-deep); }
.reach-row .spec { display: block; margin-bottom: var(--s1); }
.reach-big { font: 400 clamp(1.6rem, 4vw, 2.4rem)/1.15 var(--display); overflow-wrap: anywhere; }
.contact-list { list-style: none; padding: 0; margin: 0; border-top: var(--joint) solid var(--grout); }
.contact-list li { padding: var(--s3) 0; border-bottom: 1px solid var(--grout); }
.contact-list .spec { display: block; margin-bottom: 2px; }

/* ---------- footer ---------- */
.site-foot { padding: var(--s5) 0; color: var(--stone); font-size: 0.9rem; }
.site-foot .wrap { display: flex; justify-content: space-between; gap: var(--s4); flex-wrap: wrap; }
.site-foot a { color: var(--stone); }

/* ================= MOTION =================
   Everything below is opt-in. Hidden start states apply only under html.motion,
   which js/motion.js adds when it runs AND the visitor has not asked for reduced
   motion. No script, no motion, nothing hidden. Only transform, opacity and
   clip-path move, so phones stay smooth. */
@media (prefers-reduced-motion: no-preference) {
  /* Opening shot: the photo drifts in slowly, like a film's first frame.
     If this never runs, the photo simply sits slightly larger. Still visible. */
  .hero-photo img { animation: push 16s var(--ease) both; }
  @keyframes push { from { transform: scale(1.12); } to { transform: scale(1.02); } }

  /* Look closer: photos zoom a touch inside their grout joint */
  .gallery img, .frame img, .photo-frame img, .mosaic img { transition: transform 900ms var(--ease); }
  .gallery figure:hover img, .frame:hover img, .photo-frame:hover img, .mosaic > figure:hover img { transform: scale(1.045); }
}

/* Each element waits its turn: --i is its position among its siblings (set by the script) */
.motion [data-reveal] { transition: opacity 900ms var(--ease), transform 900ms var(--ease), clip-path 1300ms var(--ease); transition-delay: calc(var(--i, 0) * 90ms); }
/* The rule above replaces .ruled li's own transition; put the hover colour fade back (no delay) */
.motion .ruled li[data-reveal] { transition: opacity 900ms var(--ease) calc(var(--i, 0) * 90ms), transform 900ms var(--ease) calc(var(--i, 0) * 90ms), border-color 300ms ease 0s; }

/* "set": pressed into place, like a tile into thinset */
.motion [data-reveal="set"]:not(.is-in) { opacity: 0; transform: translateY(18px) scale(.985); }

/* "rise": text, a short lift with no scale */
.motion [data-reveal="rise"]:not(.is-in) { opacity: 0; transform: translateY(14px); }

/* "peel": the photo uncovers left to right, like protective film coming off new stone */
.motion [data-reveal="peel"] img { clip-path: inset(0 0 0 0); transition: clip-path 1400ms var(--ease), transform 900ms var(--ease); }
.motion [data-reveal="peel"]:not(.is-in) img { clip-path: inset(0 100% 0 0); }

/* "draw": a grout line drawn across. Uses clip-path, not scaleX(0): a scaled-to-zero
   element has zero size, so the scroll observer never sees it and it never draws. */
.motion [data-reveal="draw"] { clip-path: inset(0 0 0 0); }
.motion [data-reveal="draw"]:not(.is-in) { clip-path: inset(0 100% 0 0); }

/* ---------- phones ---------- */
@media (max-width: 860px) {
  /* Side padding only: shorthand here would wipe the vertical padding of
     .page-head / .section / .hero-copy, which share elements with .wrap. */
  .wrap { padding-left: var(--s3); padding-right: var(--s3); }
  /* Thumb-sized tap targets for the small text links */
  .jump a, .site-foot a, .contact-list a { display: inline-block; padding: 10px 0; }
  /* In 2 columns the kitchen group adds up to an odd number of cells, leaving a hole;
     making its plain photos tall evens it out. */
  .gallery:not(.two) > figure:not(.tall):not(.wide) { grid-row: span 2; }
  /* .wide-desk is wide on desktop only; in 2 columns it goes tall so the rows pair up */
  .gallery:not(.two) > figure.wide-desk { grid-column: auto; grid-row: span 2; }
  .hero { min-height: min(88vh, 760px); }
  .hero-copy { padding-bottom: var(--s6); }
  .hero-photo::after { background: linear-gradient(0deg, rgba(20,24,32,.9) 0%, rgba(20,24,32,.55) 55%, rgba(20,24,32,.35) 100%); }
  .scroll-cue { display: none; }
  .showcase, .showcase.flip { grid-template-columns: 1fr; gap: var(--s4); padding: var(--s5) 0; }
  .showcase.flip .frame { order: 0; }
  .mosaic {
    grid-template-columns: 1fr 1fr;
    grid-template-rows: auto 220px 220px;
    grid-template-areas: "say say" "m1 m2" "m3 m4";
  }
  .mosaic .say { padding: var(--s4); }
  .m5 { display: none; }
  .split { grid-template-columns: 1fr; gap: var(--s5); }
  .section { padding: var(--s6) 0; }
  .gallery { grid-template-columns: 1fr 1fr; grid-auto-rows: 220px; }
  .gallery.two { grid-auto-rows: 400px; }
  .form .row { grid-template-columns: 1fr; }
  .nav { gap: var(--s3); }
}
/* Printing a page before scrolling it: show everything */
@media print { .motion [data-reveal], .motion [data-reveal] img { opacity: 1 !important; transform: none !important; clip-path: none !important; } }

@media (max-width: 480px) {
  .gallery, .gallery.two { grid-template-columns: 1fr; grid-auto-rows: 340px; }
  .gallery .wide, .gallery .tall { grid-column: auto; grid-row: auto; }
}
