/* =========================================================
   ryandennis.io
   Earth outside, paper inside.
   Base: Pantone 17-1230 Mocha Mousse (#A47864) with deep greens.
   Semantic tags only, no divs. System fonts only.

   NOTE ON CSS MATH: every + and - inside clamp()/calc() MUST be
   surrounded by whitespace. Without it the declaration silently
   fails with no console error. Verified: clamp(2.5rem,1.35rem+4.7vw,
   5.4rem) resolves to 18.88px, the spaced version to 78px.
   ========================================================= */

/* ---- Brand palette (raw values, never used directly as text) ---- */
:root {
  --mocha:      #A47864;
  --mocha-deep: #7A5341;   /* mocha whenever it is TEXT */
  --mocha-pale: #E7D6CB;
  --spruce:     #1F4622;   /* primary action */
  --moss:       #2E6B2A;
  --ground:     #0E1410;
}

/* ---- Semantic tokens: light ---- */
:root {
  color-scheme: light dark;

  --paper:   #FBF8F5;
  --paper-2: #F2EBE4;
  --ink:     #141110;
  --ink-2:   #5E5148;      /* neutral biased to mocha, not grey */
  --hair:    #E3D6CB;

  --accent-text: var(--mocha-deep);
  --action:      var(--spruce);
  --action-ink:  #FFFFFF;
  --stat:        var(--spruce);

  /* Type: two roles, deliberately not serif + sans */
  --display: "Iowan Old Style", "Palatino Linotype", Palatino, "Book Antiqua", Georgia, serif;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

  /* Six sizes, no ad-hoc clamps anywhere else. --t-label floors at ~12px,
     not 10.5px, because it is set in uppercase mono with letter-spacing. */
  --t-label:  clamp(0.75rem, 0.71rem + 0.16vw, 0.85rem);
  --t-body:   clamp(1rem, 0.95rem + 0.28vw, 1.16rem);
  --t-lede:   clamp(1.16rem, 1rem + 0.7vw, 1.5rem);
  --t-figure: clamp(1.5rem, 1.05rem + 1.35vw, 2.3rem);
  --t-h2:     clamp(1.7rem, 1.25rem + 1.9vw, 2.9rem);
  --t-h1:     clamp(2.5rem, 1.35rem + 4.4vw, 4.9rem);

  --pad-y: clamp(3.2rem, 7vw, 7rem);
  --pad-x: clamp(1.1rem, 3.4vw, 2.6rem);
  --gap:   clamp(1.4rem, 2.6vw, 2.4rem);

  --ease: cubic-bezier(0.22, 0.75, 0.24, 1);
}

/* ---- Semantic tokens: dark ---- */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --paper:   #141211;
    --paper-2: #1C1917;
    --ink:     #F3EDE6;
    --ink-2:   #A8998C;
    --hair:    #33291F;
    --mocha-pale:  #3A2C24;
    --mocha-deep:  #C79A81;
    --action:      var(--mocha);
    --action-ink:  #14100D;
    --stat:        #6FD86A;
  }
}
:root[data-theme="dark"] {
  --paper:   #141211;
  --paper-2: #1C1917;
  --ink:     #F3EDE6;
  --ink-2:   #A8998C;
  --hair:    #33291F;
  --mocha-pale:  #3A2C24;
  --mocha-deep:  #C79A81;
  --action:      var(--mocha);
  --action-ink:  #14100D;
  --stat:        #6FD86A;
}

/* ---- Base ---- */
*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; background: var(--ground); }

body {
  margin: 0;
  background: transparent;
  color: var(--ink);
  font-family: var(--display);
  font-size: var(--t-body);
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* ---- Ambient ground ---- */
#ground, #grain {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  pointer-events: none;
}
#grain {
  opacity: 0.42;
  mix-blend-mode: overlay;
  image-rendering: pixelated;
}

/* ---- The card ---- */
.card {
  position: relative;
  z-index: 1;
  width: min(1180px, calc(100% - clamp(16px, 4vw, 64px)));
  margin: clamp(14px, 3.2vw, 44px) auto;
  background: var(--paper);
  border-radius: 26px;
  /* No overflow:clip here: it would break the sticky header.
     Corner children are rounded individually instead. */
  box-shadow:
    0 0 0 1px color-mix(in srgb, var(--ink) 8%, transparent),
    0 40px 90px -40px rgba(10, 20, 12, 0.55),
    0 6px 20px -8px rgba(10, 20, 12, 0.25);
}

/* ---- Type ---- */
h1, h2, h3 { font-weight: 600; text-wrap: balance; margin: 0 0 0.5em; }
h1 {
  font-size: var(--t-h1);
  line-height: 0.99;
  letter-spacing: -0.033em;
  margin: 0;
}
h2 { font-size: var(--t-h2); line-height: 1.06; letter-spacing: -0.022em; }
h3 { font-size: var(--t-lede); line-height: 1.15; letter-spacing: -0.012em; }
p  { margin: 0 0 1rem; max-width: 62ch; }

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

/* Mono is reserved for short labels: nav, eyebrows, buttons, captions of a
   few words. It never sets a sentence. Multi-line descriptive text stays
   serif, which is what makes it readable at length. */
.eyebrow, .nav-links a, .nav-cta, .theme-toggle, .press-kicker,
.foot-meta, .stop-label, .altitude-control label, .altitude-control output,
.btn-primary, .btn-quiet, .ticks li {
  font-family: var(--mono);
}

:focus-visible {
  outline: 2px solid var(--mocha-deep);
  outline-offset: 3px;
  border-radius: 3px;
}

/* ---- Header / nav ---- */
.site-head {
  position: sticky;
  top: 0;
  z-index: 20;
  border-radius: 26px 26px 0 0;
  background: color-mix(in srgb, var(--paper) 82%, transparent);
  transition: padding 0.5s var(--ease), background 0.5s var(--ease);
}
.site-head.is-condensed {
  backdrop-filter: blur(14px) saturate(1.5);
  -webkit-backdrop-filter: blur(14px) saturate(1.5);
  box-shadow: inset 0 -1px 0 var(--hair);
}
.site-nav {
  display: flex;
  align-items: center;
  gap: clamp(0.8rem, 2vw, 1.8rem);
  padding: 1.15rem var(--pad-x);
  transition: padding 0.5s var(--ease);
}
.site-head.is-condensed .site-nav { padding-block: 0.62rem; }

.wordmark {
  font-family: var(--display);
  font-size: var(--t-lede);
  font-weight: 600;
  letter-spacing: -0.02em;
  text-decoration: none;
  margin-right: auto;
}
/* The items read as one run-on sentence unless the space BETWEEN items is
   clearly larger than the space between words inside an item. Wide gap,
   tighter letter-spacing, and a hairline separator settle it. */
.nav-links {
  display: flex;
  align-items: center;
  gap: clamp(1.5rem, 2.6vw, 2.6rem);
  margin: 0;
  padding: 0;
  list-style: none;
}
.nav-links li { position: relative; }
.nav-links li + li::before {
  content: "";
  position: absolute;
  left: calc(clamp(1.5rem, 2.6vw, 2.6rem) / -2);
  top: 50%;
  width: 1px;
  height: 0.85em;
  transform: translateY(-50%);
  background: var(--hair);
}
.nav-links a {
  font-size: var(--t-label);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--ink-2);
  text-decoration: none;
  position: relative;
  padding-bottom: 2px;
}
/* Underline wipe follows the cursor: in from the right, out to the left. */
.nav-links a::after, .mastheads a::after, .foot-meta a::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: right;
  transition: transform 0.4s var(--ease);
}
.nav-links a:hover { color: var(--ink); }
.nav-links a:hover::after, .mastheads a:hover::after, .foot-meta a:hover::after {
  transform: scaleX(1);
  transform-origin: left;
}

.nav-cta, .theme-toggle {
  font-size: var(--t-label);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-decoration: none;
  padding: 0.5rem 0.9rem;
  border: 1px solid var(--hair);
  border-radius: 999px;
  white-space: nowrap;
  background: transparent;
  color: var(--ink);
  cursor: pointer;
  transition: border-color 0.3s var(--ease), color 0.3s var(--ease), background 0.3s var(--ease);
}
.nav-cta:hover, .theme-toggle:hover {
  border-color: var(--mocha);
  color: var(--mocha-deep);
}

/* ---- Sections ---- */
main > section { padding: var(--pad-y) var(--pad-x); }
.section-lede {
  color: var(--ink-2);
  font-size: var(--t-lede);
  max-width: 44ch;
  margin-bottom: var(--gap);
}

/* ---- Hero ---- */
#hero {
  display: grid;
  grid-template-columns: 1.32fr 0.68fr;
  align-items: center;
  gap: clamp(1.6rem, 4vw, 3.6rem);
  padding-top: clamp(2.4rem, 5vw, 4.4rem);
}
.eyebrow {
  font-size: var(--t-label);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--mocha-deep);
  margin: 0 0 1.5rem;
}
.hero-copy .lede { font-size: var(--t-lede); max-width: 44ch; margin-top: 1.4rem; }
.hero-copy .proof { color: var(--ink-2); font-size: var(--t-body); max-width: 52ch; }
.hero-copy .proof strong { color: var(--ink); font-weight: 600; }

.portrait {
  margin: 0;
  aspect-ratio: 620 / 774;
  border-radius: 14px;
  overflow: hidden;
  background: var(--paper-2);
  box-shadow:
    0 0 0 1px color-mix(in srgb, var(--mocha) 30%, transparent),
    0 24px 50px -30px rgba(10, 20, 12, 0.5);
}
.portrait img {
  width: 100%;
  height: 112%;                    /* travel room for the parallax */
  object-fit: cover;
  transform: translateY(var(--py, -5%));
  will-change: transform;
}

.hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  align-items: center;
  margin-top: 1.8rem;
  max-width: none;
}
.btn-primary {
  display: inline-block;
  background: var(--action);
  color: var(--action-ink);
  text-decoration: none;
  font-size: var(--t-label);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 0.85rem 1.5rem;
  border-radius: 999px;
  transition: transform 0.4s var(--ease), filter 0.3s var(--ease);
}
.btn-primary:hover { filter: brightness(1.12); transform: translateY(-2px); }
.btn-quiet {
  font-size: var(--t-label);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--ink-2);
  border-bottom: 1px solid var(--mocha);
  padding-bottom: 3px;
  transition: color 0.3s var(--ease);
}
.btn-quiet:hover { color: var(--ink); }

@media (max-width: 860px) {
  #hero { grid-template-columns: 1fr; }
  /* Headline leads on small screens. The portrait must never outrank the
     sentence that is the entire hook. Centred once the column is single. */
  .portrait { max-width: 17rem; margin-inline: auto; }
}

/* ---- Press band ---- */
#press {
  padding-block: clamp(1.5rem, 3vw, 2.3rem);
  padding-inline: var(--pad-x);
  background: var(--paper-2);
  border-block: 1px solid var(--hair);
}
.press-kicker {
  font-size: var(--t-label);
  font-weight: 400;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--mocha-deep);
  text-align: center;
  margin: 0 0 1.4rem;
}
.mastheads {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: baseline;
  gap: clamp(0.9rem, 1rem + 1.4vw, 2.3rem);
  margin: 0;
  padding: 0;
  list-style: none;
}
/* Equal weight for every outlet. Linkedness must never imply prestige,
   or the unlinked top-tier names read as the weakest on the page. */
.mastheads li {
  font-size: var(--t-lede);
  font-weight: 600;
  color: var(--ink);
  opacity: 0.66;
  white-space: nowrap;
  transition: opacity 0.3s var(--ease);
}
.mastheads li:hover { opacity: 1; }
.mastheads a { text-decoration: none; position: relative; padding-bottom: 2px; }

/* ---- The rewrite demo ---- */
.rewrite-demo {
  margin: 0 0 var(--gap);
  padding: clamp(1.4rem, 2.6vw, 2rem);
  background: var(--paper-2);
  border: 1px solid var(--hair);
  border-left: 3px solid var(--mocha);
  border-radius: 16px;
}
.altitude {
  margin: 0 0 1rem;
  max-width: 58ch;
  font-size: var(--t-lede);
  line-height: 1.5;
}
.altitude:last-child { margin-bottom: 0; }
.stop-label {
  display: block;
  font-size: var(--t-label);
  font-weight: 400;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--mocha-deep);
  margin-bottom: 0.6rem;
}
/* With JS the three stops share one grid cell and crossfade, so the box
   never changes height and nothing on the page shifts. */
body.js-on .rewrite-demo { display: grid; }
/* The outgoing line leaves quickly, the incoming one eases in slightly
   later. Equal-duration crossfades dip through a washed-out middle, which
   is what read as jumpy. Blur rather than translate keeps it unhurried. */
body.js-on .altitude {
  grid-area: 1 / 1;
  margin: 0;
  opacity: 0;
  filter: blur(5px);
  transition: opacity 0.42s var(--ease), filter 0.42s var(--ease);
  pointer-events: none;
}
body.js-on .altitude.is-active {
  opacity: 1;
  filter: blur(0);
  pointer-events: auto;
  transition-duration: 0.78s;
  transition-delay: 0.1s;
}
body.js-on .stop-label { display: none; }

/* Centred so it reads as the interactive part of the section. */
.altitude-control {
  display: grid;
  justify-items: center;
  gap: 0.85rem;
  width: min(34rem, 100%);
  margin-inline: auto;
  text-align: center;
}
.altitude-control label,
.altitude-control output {
  font-size: var(--t-label);
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.altitude-control label { color: var(--ink-2); }
.altitude-control output { color: var(--mocha-deep); font-weight: 700; }

.ticks {
  display: flex;
  justify-content: space-between;
  width: 100%;
  margin: -0.35rem 0 0;
  padding: 0;
  list-style: none;
}
.ticks li {
  font-family: var(--mono);
  font-size: var(--t-label);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-2);
  opacity: 0.5;
  transition: opacity 0.4s var(--ease), color 0.4s var(--ease);
}
.ticks li.is-active { opacity: 1; color: var(--mocha-deep); }
.ticks li:first-child { transform: translateX(-0.15em); }
.ticks li:last-child { transform: translateX(0.15em); }
#altitude {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  background: transparent;
  cursor: pointer;
}
#altitude::-webkit-slider-runnable-track { height: 3px; border-radius: 99px; background: var(--hair); }
#altitude::-moz-range-track { height: 3px; border-radius: 99px; background: var(--hair); }
#altitude::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 24px; height: 24px;
  margin-top: -10px;
  border-radius: 50%;
  background: var(--action);
  border: 3px solid var(--paper);
  box-shadow: 0 2px 8px rgba(10, 20, 12, 0.3);
  transition: transform 0.2s var(--ease);
}
#altitude::-moz-range-thumb {
  width: 18px; height: 18px;
  border-radius: 50%;
  background: var(--action);
  border: 3px solid var(--paper);
}
#altitude:active::-webkit-slider-thumb { transform: scale(1.12); }
#altitude:focus-visible::-webkit-slider-thumb {
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--mocha) 55%, transparent);
}

/* ---- Panels: pillars, stats, offers ---- */
#value, .stats, .offers {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
  gap: clamp(0.8rem, 1.7vw, 1.15rem);
}
#value > h2, #value > .section-lede { grid-column: 1 / -1; }
.stats, .offers { margin: 0; padding: 0; list-style: none; }

.pillar, .stats li, .offers li {
  position: relative;
  padding: clamp(1.4rem, 2.6vw, 2rem);
  background: var(--paper-2);
  border: 1px solid var(--hair);
  border-radius: 16px;
  transition: transform 0.4s var(--ease), border-color 0.4s var(--ease);
}
/* Cursor spotlight, gated to fine pointers in the media query below. */
.pillar::before, .stats li::before, .offers li::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  opacity: 0;
  transition: opacity 0.4s var(--ease);
  pointer-events: none;
  background: radial-gradient(420px circle at var(--gx, 50%) var(--gy, 50%),
              color-mix(in srgb, var(--mocha) 22%, transparent), transparent 70%);
}
@media (pointer: fine) {
  .pillar:hover, .stats li:hover, .offers li:hover {
    transform: translateY(-3px);
    border-color: color-mix(in srgb, var(--mocha) 45%, var(--hair));
  }
  .pillar:hover::before, .stats li:hover::before, .offers li:hover::before { opacity: 1; }
}
.pillar h3 { margin-bottom: 0.5rem; }
.pillar p { color: var(--ink-2); margin-bottom: 0; font-size: var(--t-body); }

.stats li, .offers li { display: flex; flex-direction: column; gap: 0.6rem; }
/* Stat figures get their own scale, not --t-h2. At h2 size the longest
   figure overflowed its column and was visibly clipped on desktop. */
.stats strong {
  font-size: var(--t-figure);
  font-weight: 600;
  color: var(--stat);
  letter-spacing: -0.02em;
  line-height: 1.08;
  font-variant-numeric: tabular-nums;
  text-wrap: balance;
}
.offers strong { font-size: var(--t-lede); font-weight: 600; }
.stats span, .offers span {
  color: var(--ink-2);
  font-size: var(--t-body);
  line-height: 1.55;
  letter-spacing: 0;
}

/* ---- About ---- */
#about p { font-size: var(--t-lede); max-width: 60ch; }
.credential {
  margin: 2rem 0 0;
  padding: clamp(1.2rem, 2.2vw, 1.6rem);
  border-left: 3px solid var(--mocha);
  background: var(--paper-2);
  border-radius: 0 16px 16px 0;
}
.credential p { margin: 0; color: var(--ink-2); font-size: var(--t-body); max-width: none; }
.credential strong { color: var(--ink); }

/* ---- Work ---- */
#work .offers { margin-bottom: var(--gap); }
.closing { font-size: var(--t-lede); max-width: 44ch; margin-bottom: 2.4rem; }

/* ---- Booking ----
   Same visual grammar as the CV card below it, opened up to full width so
   the calendar has room. The embed iframe sizes itself; min-height only
   reserves space so the page doesn't jump while it loads. */
.booking {
  position: relative;
  margin: 0 0 2.4rem;
  padding: clamp(1.8rem, 1.2rem + 2.6vw, 3rem) clamp(1.4rem, 1rem + 2.4vw, 2.8rem);
  background: var(--paper-2);
  border: 1px solid var(--hair);
  border-radius: 18px;
}
.booking::before {
  content: "";
  position: absolute;
  top: -1px;
  left: clamp(1.4rem, 1rem + 2.4vw, 2.8rem);
  right: clamp(1.4rem, 1rem + 2.4vw, 2.8rem);
  height: 2px;
  border-radius: 2px;
  background: linear-gradient(90deg, var(--mocha), color-mix(in srgb, var(--mocha) 12%, transparent));
}
.booking-eyebrow {
  font-family: var(--mono);
  font-size: var(--t-label);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--mocha-deep);
  margin: 0 0 0.9rem;
}
.booking h3 { margin: 0 0 0.6rem; }
.booking-note {
  color: var(--ink-2);
  font-size: var(--t-body);
  max-width: 52ch;
  margin: 0 0 1.8rem;
}
#cal-embed {
  width: 100%;
  min-height: clamp(24rem, 60vh, 38rem);
  overflow: auto;
  border-radius: 12px;
}
.booking-fallback {
  font-family: var(--mono);
  font-size: var(--t-label);
  letter-spacing: 0.08em;
  color: var(--ink-2);
  margin: 1.2rem 0 0;
  max-width: none;
}
.booking-fallback a { color: var(--mocha-deep); }

/* Buttons that used to be anchors: strip UA chrome, keep the class look. */
button.nav-cta { font: inherit; font-family: var(--mono); font-size: var(--t-label); }
button.btn-primary { border: 0; cursor: pointer; font-family: var(--mono); }

/* ---- Footer ---- */
.site-foot {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
  padding: 2.2rem var(--pad-x) 2.6rem;
  border-top: 1px solid var(--hair);
  border-radius: 0 0 26px 26px;
}
.foot-name { font-size: var(--t-lede); font-weight: 600; margin: 0; }
.foot-meta {
  display: flex;
  gap: 0.7rem;
  align-items: center;
  color: var(--ink-2);
  font-size: var(--t-label);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  margin: 0;
}
.foot-meta a { text-decoration: none; position: relative; padding-bottom: 2px; }
.foot-meta a:hover { color: var(--ink); }

/* ---- Headline word reveal ----
   Only opacity / filter / transform animate. Font size never changes
   mid-flight, so the heading never re-wraps. */
h1 span {
  display: inline-block;
  animation: word-in 0.85s var(--ease) both;
  animation-delay: calc(var(--i) * 52ms);
}
@keyframes word-in {
  from { opacity: 0; filter: blur(10px); transform: translateY(0.26em); }
  to   { opacity: 1; filter: blur(0);    transform: none; }
}

/* ---- Scroll reveals ---- */
@supports (animation-timeline: view()) {
  .reveal {
    animation: reveal-in 0.9s var(--ease) both;
    animation-timeline: view();
    animation-range: entry 6% cover 26%;
  }
}
@supports not (animation-timeline: view()) {
  body.js-on .reveal { opacity: 0; transform: translateY(14px); }
  body.js-on .reveal.is-in {
    opacity: 1;
    transform: none;
    transition: opacity 0.9s var(--ease), transform 0.9s var(--ease);
  }
}
@keyframes reveal-in {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}

/* ---- Mobile: navigation stays visible. Never hide the links. ---- */
@media (max-width: 720px) {
  .site-nav { flex-wrap: wrap; gap: 0.7rem; }
  .wordmark { margin-right: auto; }
  .nav-cta { display: none; }
  .nav-links {
    order: 3;
    width: 100%;
    justify-content: space-between;
    gap: 1rem;
  }
  .nav-links li + li::before { left: -0.5rem; }
}
@media (max-width: 420px) {
  .nav-links { gap: 0.7rem; }
  .nav-links li + li::before { left: -0.35rem; }
}

/* ---- Motion preferences ---- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  h1 span { animation: none; opacity: 1; filter: none; transform: none; }
  .reveal { animation: none !important; opacity: 1 !important; transform: none !important; }
  body.js-on .reveal { opacity: 1; transform: none; }
  .btn-primary:hover { transform: none; }
  .portrait img { transform: none; }
  .pillar:hover, .stats li:hover, .offers li:hover { transform: none; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    transition-duration: 0.001ms !important;
  }
}
