/* ------------------------------------------------------------------
   iamtracy.com
   Palette sampled from the paintings themselves.
   ------------------------------------------------------------------ */

:root {
  --paper:      #ECEDE9;
  --paper-2:    #E4E5E0;
  --ink:        #241F2E;
  --ink-soft:   #5E5769;
  --ink-faint:  #8B8595;
  --violet:     #5B45A8;
  --rose:       #A0577F;
  --aurora:     #7FA85C;
  --night:      #12102F;

  /* Cards sit cooler and lighter than the paper, so they lift off it by
     hue as well as brightness — the paper is warm, the card is violet. */
  --card:       #F3F2F7;
  --card-edge:  rgba(91, 69, 168, 0.28);
  --card-lift:  0 1px 2px rgba(36, 31, 46, 0.04), 0 10px 26px -14px rgba(36, 31, 46, 0.16);

  /* Deep enough to clear 3:1 on paper at display size; the logo's own
     teal is far too light to set type in. */
  --teal:       #1F7A82;

  --display: "Fraunces", Georgia, "Times New Roman", serif;
  --body: "Karla", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  /* Script, for section titles only — it echoes the logo's "Tracy". */
  --script: "Great Vibes", "Snell Roundhand", "Apple Chancery", cursive;

  --gutter: clamp(1.5rem, 5vw, 5.5rem);
  --section: clamp(6rem, 14vw, 11rem);
  --measure: 34rem;
}

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

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

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--body);
  font-weight: 300;
  font-size: clamp(1rem, 0.96rem + 0.2vw, 1.125rem);
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* Paper fibre. Very low opacity — felt, not seen. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 100;
  opacity: 0.035;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='240' height='240' filter='url(%23n)'/%3E%3C/svg%3E");
}

img { max-width: 100%; height: auto; display: block; }

a { color: inherit; }

:focus-visible {
  outline: 2px solid var(--violet);
  outline-offset: 4px;
}

.wrap {
  max-width: 78rem;
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.skip {
  position: absolute;
  left: -9999px;
}
.skip:focus {
  left: var(--gutter);
  top: 1rem;
  z-index: 200;
  background: var(--paper);
  padding: 0.6rem 1rem;
  font-size: 0.875rem;
}

/* ---------------- header ---------------- */

/* The header is the one band that is not paper. The tints are the
   logo's own violet and teal mixed down into the paper, so the band
   reads as the wordmark's ground rather than a separate colour. */
.masthead {
  position: sticky;
  top: 0;
  /* Under the fixed paper-fibre overlay (z-index 100), above content. */
  z-index: 50;
  background: linear-gradient(100deg, #D8D6E0 0%, #D4DDE1 55%, #CFE3E0 100%);
  border-bottom: 1px solid rgba(91, 69, 168, 0.22);
}

.masthead-inner {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.75rem 2rem;
  /* Slimmer than the old header: it is on screen the whole time now. */
  padding-block: clamp(0.85rem, 2vw, 1.15rem);
}

/* A sticky header on a short screen costs more than it gives, and the
   nav wraps to a second line down here. */
@media (max-width: 40rem) {
  .masthead { position: static; }

  /* The wordmark takes ~35-40% of a phone's width, and the hero logo
     directly below says the same thing more than twice as large. Two
     wordmarks in one screenful is one too many, so the header keeps the
     nav and the hero keeps the identity. Nothing is lost: the link only
     pointed at "/" on a single-page site. */
  .mark { display: none; }
  .masthead-inner { justify-content: center; }
  .nav { justify-content: center; }
}

/* Anchor targets must clear the sticky band. */
.section, .hero { scroll-margin-top: 6rem; }

/* The wordmark stands in for the site name. Its box runs tall because
   of the "y" swash, so it reads smaller than its height suggests. */
.mark {
  display: block;
  width: 9.5rem;
  text-decoration: none;
  /* Nudge the optical baseline: the masthead aligns on `baseline`, and
     the swash hangs well below the lettering. */
  margin-bottom: -0.35rem;
}

@media (max-width: 26rem) {
  .mark { width: 8rem; }
}

.nav {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem clamp(1.1rem, 3vw, 2.5rem);
  font-size: 0.8125rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

@media (max-width: 26rem) {
  .nav { font-size: 0.75rem; letter-spacing: 0.1em; }
}

.nav a {
  text-decoration: none;
  color: var(--ink-soft);
  padding-block: 0.25rem;
  border-bottom: 1px solid transparent;
  transition: color 0.3s ease, border-color 0.3s ease;
}
.nav a:hover {
  color: var(--ink);
  border-bottom-color: var(--aurora);
}

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

.hero {
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(2.5rem, 6vw, 4rem);
  align-items: center;
  /* The header no longer sits inside this wrap, so the hero opens the
     page and needs its own breathing room at the top. */
  padding-block: clamp(2.5rem, 6vw, 4.5rem) var(--section);
}

/* Keep the sheet a believable size — a painting, not a billboard. */
.hero-art .plate {
  max-width: min(100%, 26rem);
  margin-inline: auto;
}

@media (min-width: 60rem) {
  .hero {
    grid-template-columns: 1fr 0.9fr;
    gap: clamp(3rem, 7vw, 7rem);
  }
}

/* The logo carries the h1. It is artwork, not type, so it gets a
   max-width rather than a font-size — and no drop shadow or frame:
   it already arrives on transparent ground with its own glow. */
.hero-logo {
  margin: 0;
  max-width: min(100%, 26rem);
}

@media (min-width: 60rem) {
  .hero-logo { max-width: 34rem; }
}

.hero-line {
  max-width: 24rem;
  margin: clamp(1.75rem, 4vw, 2.5rem) 0 0;
  color: var(--ink-soft);
  font-size: 1.0625rem;
}

/* Credential: the caps treatment used elsewhere for practical detail,
   but larger and properly bold — it is the one claim that establishes
   she is qualified, so it should not read as fine print. The Karla
   request was widened to 700 so this is a real weight, not a browser's
   synthesised faux-bold. Kept at --ink-soft rather than full --ink so
   it supports the logo above instead of competing with it. */
.hero-cred {
  margin: 1.5rem 0 0;
  font-size: 0.875rem;
  font-weight: 700;
  /* Bold caps need a touch less tracking than light caps to stay even. */
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

/* ---------------- pigment plate ---------------- */

/* The signature: work sits on the page as a wash. Feathered deckle
   edge, and the piece's own dominant pigment blooming behind it. */
.plate {
  position: relative;
  isolation: isolate;
}

/* Pigment soaking outward, not a drop shadow: wide, pale, very diffuse. */
.plate::before {
  content: "";
  position: absolute;
  inset: -22%;
  z-index: 0;
  border-radius: 50%;
  background: radial-gradient(54% 42% at 50% 50%, var(--bleed, var(--violet)) 0%, transparent 78%);
  filter: blur(85px);
  opacity: 0;
  transition: opacity 1.8s ease 0.25s;
}

.plate img {
  position: relative;
  z-index: 1;
  width: 100%;
  -webkit-mask-image:
    linear-gradient(to right, transparent 0, #000 2.6%, #000 97.4%, transparent 100%),
    linear-gradient(to bottom, transparent 0, #000 1.5%, #000 98.5%, transparent 100%);
  mask-image:
    linear-gradient(to right, transparent 0, #000 2.6%, #000 97.4%, transparent 100%),
    linear-gradient(to bottom, transparent 0, #000 1.5%, #000 98.5%, transparent 100%);
  -webkit-mask-composite: source-in;
  mask-composite: intersect;
}

.reveal.is-in .plate::before,
.hero-art .plate::before { opacity: 0.42; }

/* ---------------- sections ---------------- */

.section { padding-block: var(--section); }

/* Section titles are set in the script face, sweeping violet to teal the
   way the logo does. Script faces carry a small x-height, so this needs
   a much larger size than a text face would to read at the same weight. */
.section-title {
  font-family: var(--script);
  font-weight: 400;
  font-size: clamp(2.25rem, 5.5vw, 3.5rem);
  line-height: 1.15;
  letter-spacing: 0.005em;
  text-transform: none;
  margin: 0 0 clamp(2.5rem, 6vw, 4rem);
  /* Script descenders and flourishes drop well below the baseline. */
  padding-bottom: 0.45rem;
  border-bottom: 1px solid rgba(91, 69, 168, 0.18);

  /* Fallback first: if the text cannot be clipped to the gradient, this
     solid violet is what shows, never transparent-on-paper. */
  color: var(--violet);
  background-image: linear-gradient(95deg, var(--violet) 0%, var(--violet) 35%, var(--teal) 100%);
}

@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .section-title {
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;
  }
}

/* Forced-colours mode strips the gradient; hand the text back a colour. */
@media (forced-colors: active) {
  .section-title {
    color: CanvasText;
    -webkit-text-fill-color: CanvasText;
  }
}

/* ---------------- services ---------------- */

/* Three abreast on wide screens, stacked below. Each service is a card:
   its own edge does the separating, so the gaps are tighter than they
   were when the columns were bare text. */
.services {
  display: grid;
  gap: clamp(1.25rem, 3vw, 1.75rem);
}

@media (min-width: 52rem) {
  .services { grid-template-columns: repeat(3, 1fr); }
}

/* Column layout so the price can be pushed to the foot of the card.
   Grid stretches items to the row height, so the prices line up across
   a row even when the descriptions run to different lengths. */
.service {
  display: flex;
  flex-direction: column;
  background: var(--card);
  border: 1px solid var(--card-edge);
  border-radius: 3px;
  box-shadow: var(--card-lift);
  padding: clamp(1.5rem, 3vw, 1.9rem);
}

.service p { margin: 0; color: var(--ink-soft); }

.service .service-price {
  margin-top: auto;
  padding-top: 1.25rem;
  font-family: var(--display);
  font-variation-settings: "SOFT" 40, "WONK" 1;
  font-weight: 400;
  font-size: 1.125rem;
  color: var(--ink);
}

.service-name {
  font-family: var(--display);
  font-variation-settings: "SOFT" 45, "WONK" 1;
  font-weight: 300;
  font-size: clamp(1.4rem, 3vw, 1.75rem);
  line-height: 1.25;
  letter-spacing: -0.012em;
  color: var(--ink);
  margin: 0 0 0.6rem;
  padding-bottom: 0.85rem;
  border-bottom: 1px solid rgba(36, 31, 46, 0.16);
}

/* Session name and duration: the practical line, set apart from prose.
   Deliberately --ink-soft, not --ink-faint: faint fails contrast on the
   card ground, and durations are information, not decoration. The caps
   and letterspacing already mark it as secondary. */
.service .service-session {
  font-size: 0.75rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-bottom: 0.9rem;
}

/* The middot separator, wherever it is used to join short facts. */
.service-session .dot,
.hero-cred .dot { padding-inline: 0.35em; }

.services-note {
  max-width: var(--measure);
  margin: clamp(3rem, 7vw, 4.5rem) 0 0;
  padding-top: 1.25rem;
  border-top: 1px solid rgba(36, 31, 46, 0.14);
  font-size: 0.9375rem;
  color: var(--ink-faint);
}

/* ---------------- sky ---------------- */

/* Register change: paper gives way to sky. Photographs keep hard
   edges — they are not washes. */
.sky {
  background: var(--night);
  color: #C9CBE4;
  margin-top: var(--section);
}

.sky-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 22rem));
  gap: clamp(1.5rem, 4vw, 3rem);
  justify-content: center;
}

.shot { margin: 0; }

.shot img { width: 100%; }

/* The precepts, carried on the night band. Centred and given room —
   this is the one place on the page that asks you to stop reading fast. */
.quote {
  max-width: 44rem;
  margin: 0 auto clamp(4rem, 9vw, 6rem);
  text-align: center;
}

.quote blockquote { margin: 0; }

.quote blockquote p {
  font-family: var(--display);
  font-optical-sizing: none;
  font-variation-settings: "opsz" 30, "SOFT" 60, "WONK" 1;
  font-weight: 300;
  font-size: clamp(1.35rem, 3.4vw, 2rem);
  line-height: 1.4;
  letter-spacing: -0.014em;
  margin: 0 0 1.1rem;
  color: #DFE0F0;
}

.quote blockquote p:last-child {
  font-size: clamp(1.05rem, 2.2vw, 1.3rem);
  color: rgba(201, 203, 228, 0.82);
}

.quote figcaption {
  margin-top: 2rem;
  font-size: 0.75rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: rgba(201, 203, 228, 0.6);
}

/* ---------------- about ---------------- */

.about {
  display: grid;
  gap: clamp(2.5rem, 6vw, 5rem);
  align-items: center;
}

/* Decoration, so keep it a believable sheet size. Centred whenever it
   is stacked rather than sitting in a column of its own. */
.about-art .plate {
  max-width: min(100%, 20rem);
  margin-inline: auto;
}

@media (min-width: 52rem) {
  .about { grid-template-columns: 20rem 1fr; }
  /* Not enough width for three columns yet: the painting drops to its
     own row under the portrait and copy. */
  .about-art { grid-column: 1 / -1; }
}

/* Wide enough for the hero's composition to repeat here: portrait,
   words, painting. */
@media (min-width: 68rem) {
  .about {
    grid-template-columns: 17rem 1fr 15rem;
    gap: clamp(2rem, 4vw, 3.5rem);
  }
  .about-art { grid-column: auto; }
  .about-art .plate { max-width: 100%; margin-inline: 0; }
}

.portrait img {
  width: 100%;
  filter: saturate(0.92);
}

.about-body { max-width: var(--measure); }

.about-body p {
  margin: 0 0 1.25rem;
}

.about-body p:first-child {
  font-family: var(--display);
  font-variation-settings: "SOFT" 45, "WONK" 1;
  font-weight: 300;
  font-size: clamp(1.5rem, 3.2vw, 2.1rem);
  line-height: 1.32;
  letter-spacing: -0.012em;
  color: var(--ink);
}

/* ---------------- testimonials ---------------- */

/* Quiet lead-in under the section eyebrow. Centred with the slider
   below it, so the whole section reads as one centred block. */
.words-lead {
  max-width: var(--measure);
  margin: 0 auto clamp(2rem, 5vw, 3rem);
  text-align: center;
  color: var(--ink-soft);
}

.slider { position: relative; }

/* The carousel is native scroll-snap, not a transform track: swipe,
   trackpad, and keyboard scrolling all work before any JS loads. */
.slider-viewport {
  display: flex;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  /* Hide the scrollbar without hiding the ability to scroll. */
  scrollbar-width: none;
  -ms-overflow-style: none;
  /* Room for the card's drop shadow, which the scroll container would
     otherwise clip. Vertical only — padding counts toward clientWidth,
     and the carousel's index maths depends on that staying exact. */
  padding-block: 0 0.75rem;
}
.slider-viewport::-webkit-scrollbar { display: none; }

.slider-viewport:focus-visible {
  outline: 2px solid var(--violet);
  outline-offset: 6px;
}

/* Each quote occupies the full viewport width, so scrollLeft divides
   evenly by clientWidth and the active index is exact. */
.slide {
  flex: 0 0 100%;
  scroll-snap-align: start;
  margin: 0;
  padding-right: 1px; /* keeps sub-pixel rounding from bleeding the next slide */
}

/* The card lives inside the slide rather than on it: .slide must stay
   exactly one viewport wide for the carousel's index arithmetic. */
.slide-card {
  max-width: 42rem;
  /* Centred in the slide. The slide itself stays a full viewport wide,
     so the carousel's index arithmetic is untouched. */
  margin-inline: auto;
  background: var(--card);
  border: 1px solid var(--card-edge);
  border-radius: 3px;
  box-shadow: var(--card-lift);
  padding: clamp(1.75rem, 4vw, 2.5rem);
}

.slide p {
  font-family: var(--display);
  font-variation-settings: "SOFT" 50, "WONK" 1;
  font-weight: 300;
  font-size: clamp(1.25rem, 2.8vw, 1.6rem);
  line-height: 1.4;
  letter-spacing: -0.01em;
  margin: 0 0 1.25rem;
}

.slide cite {
  font-style: normal;
  font-size: 0.75rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

/* ---- controls ---- */

.slider-controls { display: none; }

.js .slider-controls {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1.25rem;
  margin-top: clamp(2rem, 5vw, 3rem);
}

.arrow {
  appearance: none;
  background: none;
  border: 1px solid rgba(36, 31, 46, 0.18);
  border-radius: 50%;
  width: 2.75rem;
  height: 2.75rem;
  display: grid;
  place-items: center;
  padding: 0;
  color: var(--ink-soft);
  cursor: pointer;
  transition: color 0.3s ease, border-color 0.3s ease;
}
.arrow:hover {
  color: var(--ink);
  border-color: var(--aurora);
}
.arrow svg { width: 1.15rem; height: 1.15rem; }

.slider-dots {
  display: flex;
  gap: 0.6rem;
}

.slider-dot {
  appearance: none;
  padding: 0;
  cursor: pointer;
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  border: 1px solid var(--ink-faint);
  background: none;
  transition: background-color 0.4s ease, border-color 0.4s ease;
}

.slider-dot[aria-current="true"] {
  background: var(--violet);
  border-color: var(--violet);
}

/* ---------------- contact ---------------- */

.contact-lead {
  max-width: var(--measure);
  margin: 0 0 1.5rem;
  color: var(--ink-soft);
}

.contact {
  border-top: 1px solid rgba(36, 31, 46, 0.14);
  padding-block: var(--section) clamp(3rem, 7vw, 5rem);
}

.contact-link {
  font-family: var(--display);
  font-variation-settings: "SOFT" 50, "WONK" 1;
  font-weight: 300;
  font-size: clamp(1.75rem, 6vw, 3.75rem);
  letter-spacing: -0.02em;
  text-decoration: none;
  line-height: 1.1;
  display: inline-block;
  background-image: linear-gradient(var(--rose), var(--rose));
  background-size: 0% 1px;
  background-position: 0 100%;
  background-repeat: no-repeat;
  transition: background-size 0.5s cubic-bezier(0.22, 1, 0.36, 1);
}
.contact-link:hover { background-size: 100% 1px; }

.foot {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem 2rem;
  justify-content: space-between;
  padding-bottom: clamp(2rem, 5vw, 3.5rem);
  font-size: 0.75rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

/* ---------------- motion ---------------- */

/* Hero arrives on page load — CSS only, never gated behind JS or a
   scroll observer, because it is the first thing anyone sees. */
@keyframes rise {
  from { opacity: 0; transform: translateY(1.75rem); }
  to   { opacity: 1; transform: none; }
}

.hero-intro { animation: rise 1.2s cubic-bezier(0.22, 1, 0.36, 1) both; }
.hero-art   { animation: rise 1.4s cubic-bezier(0.22, 1, 0.36, 1) 0.15s both; }

/* Below the fold, reveal on scroll — but only once JS has confirmed it
   can undo it. Without JS the content is simply visible. */
.js .reveal {
  opacity: 0;
  transform: translateY(1.5rem);
  transition: opacity 1.1s cubic-bezier(0.22, 1, 0.36, 1),
              transform 1.1s cubic-bezier(0.22, 1, 0.36, 1);
}
.js .reveal.is-in {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-delay: 0ms !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .js .reveal { opacity: 1; transform: none; }
  .plate::before { opacity: 0.42; }
}
