/* ------------------------------------------------------------------ */
/*  heycirql homepage: the atlas page                                  */
/*  The words over a fixed dusk map. Scrolling is the journey: each     */
/*  section reached adds a stop to the map. Motion only follows the    */
/*  reader's own scroll; reduced-motion gets the finished map at once.  */
/* ------------------------------------------------------------------ */

:root {
  --bg:      #120b1c;
  --panel:   rgba(18, 11, 28, 0.62);
  --line:    rgba(241, 236, 249, 0.12);
  --dim:     #6d5f80;
  --text:    #c4b9d6;
  --text-h:  #f1ecf9;
  --ember:   #f0a36b;
  --accent:  #c4b5fd;
  --accent-ink: #140d1f;
  --serif: 'Instrument Serif', Georgia, serif;
  --sans:  'DM Sans', system-ui, sans-serif;
  color-scheme: dark;
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  font-family: var(--sans);
  font-size: 17px;
  line-height: 1.6;
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
}
h1, h2 { margin: 0; color: var(--text-h); }
p { margin: 0; }
strong { color: var(--text-h); font-weight: 600; }

/* ------------------------------------------------------------------ */
/*  The map, fixed behind everything                                   */
/* ------------------------------------------------------------------ */

.atlas { position: fixed; inset: 0; z-index: 0; overflow: hidden; background: #2b1a3f; }
.atlas svg { width: 100%; height: 100%; display: block; overflow: visible; }

.land { fill: #f1ecf9; fill-opacity: 0.045; }
.land-2 { fill-opacity: 0.03; }

.route { fill: none; stroke: var(--text-h); stroke-opacity: 0.55; stroke-width: 1.5; stroke-linecap: round; }
.thread { fill: none; stroke: var(--ember); stroke-width: 2.2; stroke-linecap: round; }

/* Anything with a stop is hidden until the reader reaches it. */
#map [data-stop] { opacity: 0; transition: opacity 500ms ease; }
#map [data-stop].is-on { opacity: 1; }

.thread-label rect { fill: var(--ember); }
.thread-label text { font-family: var(--serif); font-size: 20px; fill: #2b1508; }

.city .pin { fill: var(--text-h); stroke: var(--bg); stroke-width: 3; }
.city-name { font-family: var(--serif); font-size: 34px; fill: var(--text-h); paint-order: stroke; stroke: rgba(18,11,28,0.6); stroke-width: 6px; stroke-linejoin: round; }
.city.far .pin { fill: var(--dim); }
.city.far .city-name { font-size: 20px; fill: var(--dim); }

.chip .face { fill: #2a2138; stroke: var(--dim); stroke-width: 1.2; }
.chip .init { font-family: var(--serif); font-size: 13px; fill: var(--dim); }
.chip .who { font-family: var(--sans); font-size: 13px; fill: var(--dim); paint-order: stroke; stroke: rgba(18,11,28,0.7); stroke-width: 5px; stroke-linejoin: round; }
.chip.lit .face { fill: var(--t, #b8a1c9); stroke: var(--ember); stroke-width: 2; }
.chip.lit .init { fill: #1a1124; font-size: 15px; }
.chip.lit .who { fill: var(--text-h); font-weight: 500; font-size: 14px; }

.tagpill rect { fill: #1b1226; stroke: var(--dim); }
.tagpill text { font-family: var(--sans); font-size: 12.5px; font-weight: 500; fill: var(--text); }

/* ------------------------------------------------------------------ */
/*  Copy over the map                                                   */
/* ------------------------------------------------------------------ */

.mark-hey { opacity: 0.55; }

/* Full width, so its clip is the screen's own edge: the hero's soft pool
   (inset -70% -90%) reaches past it on every screen but the widest. The
   padding keeps the 1120px column. A clip on body is handed to the
   viewport as hidden, which scripts and focus can still scroll. */
.journey { position: relative; z-index: 1; padding: 0 max(24px, calc((100% - 1072px) / 2)); overflow-x: clip; }

.stop {
  min-height: 100vh;
  display: flex;
  align-items: center;
}
/* The panel sits away from the city its stop just added: Lisbon is west, so
   the hero reads on the east; Bucharest and Tbilisi are east, so their copy
   reads on the west. */
/* On desktop each stop's panel is seated by home.js in whichever corner
   covers the fewest map elements, weighting the ones that stop just added.
   The slots are the four corners of the stop, inside the journey column. */
@media (min-width: 721px) {
  .stop:not(.stop-intro) { position: relative; }
  .stop:not(.stop-intro) .panel { position: absolute; }
  .stop.slot-tl .panel { top: 8vh; left: 0; }
  .stop.slot-tr .panel { top: 8vh; right: 0; }
  .stop.slot-bl .panel { bottom: 8vh; left: 0; }
  .stop.slot-br .panel { bottom: 8vh; right: 0; }
  /* The close sits at mid-height: the journey has ended and the offer is the centre. */
  .stop.slot-mc .panel { top: 50%; left: 50%; transform: translate(-50%, -50%); }
}

.stop-close { min-height: 100vh; }

.panel {
  width: min(520px, 100%);
  padding: 32px;
  border-radius: 20px;
  background: var(--panel);
  border: 1px solid var(--line);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  opacity: 0.55;
  transition: opacity 400ms ease;
}
.stop.is-active .panel { opacity: 1; }

.price { margin-top: 14px; font-size: 15px; }
.scroll-cue { margin-top: 28px; font-size: 14px; color: var(--text); }
.scroll-cue::before { content: '↓ '; }

.step-no { font-family: var(--serif); font-size: 22px; color: var(--ember); margin-bottom: 8px; }
h2 {
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(30px, 4vw, 44px);
  line-height: 1.08;
  margin-bottom: 14px;
}
.panel > p:last-child { font-size: 18px; }

.price-list { list-style: none; margin: 0 0 28px; padding: 0; display: grid; gap: 6px; font-size: 18px; }
.made { margin-top: 24px; font-size: 15px; }
.made a { color: var(--text-h); }
.stop-close footer { margin-top: 28px; padding-top: 16px; border-top: 1px solid var(--line); display: flex; gap: 20px; font-size: 14px; }
.stop-close footer a { color: var(--text); }

/* ------------------------------------------------------------------ */
/*  The one button, and the sticky bar                                 */
/* ------------------------------------------------------------------ */

.cta {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  padding: 18px 30px;
  background: var(--accent);
  color: var(--accent-ink);
  font-weight: 600;
  font-size: 17px;
  text-decoration: none;
  border-radius: 12px;
}
.cta:hover { filter: brightness(1.06); }
.cta:focus-visible { outline: 3px solid var(--text-h); outline-offset: 3px; }
.cta .g { width: 20px; height: 20px; fill: currentColor; }


/* ------------------------------------------------------------------ */
/*  Responsive, reduced motion                                         */
/* ------------------------------------------------------------------ */

@media (max-width: 720px) {
  .journey { padding: 0 16px; }
  /* On a phone the panel moves up or down instead: away from the city its
     stop just added. Lisbon and the tags sit high on the portrait map, so
     their copy sits low; Bucharest and Tbilisi sit low, so theirs sits high. */
  .stop { align-items: flex-end; padding-bottom: 12vh; justify-content: flex-start; }
  .stop[data-stop="1"], .stop[data-stop="3"], .stop[data-stop="4"] { align-items: flex-start; padding-top: 10vh; padding-bottom: 0; }
  .panel { padding: 24px; }
  .cta { width: 100%; justify-content: center; }
  .city-name { font-size: 40px; }
  .chip .who { font-size: 16px; }
  .chip.lit .who { font-size: 17px; }
}

@media (prefers-reduced-motion: reduce) {
  #map [data-stop] { opacity: 1; transition: none; }
  .panel { opacity: 1; transition: none; }
  .route { stroke-dasharray: 6 8 !important; stroke-dashoffset: 0 !important; }
  .thread { stroke-dasharray: none !important; stroke-dashoffset: 0 !important; }
}

/* ------------------------------------------------------------------ */
/*  Spokes: city to the people and venues met there                    */
/* ------------------------------------------------------------------ */

.spoke { stroke: rgba(241, 236, 249, 0.28); stroke-width: 1.2; stroke-linecap: round; }

/* ------------------------------------------------------------------ */
/*  The intro: three seconds on load, scroll locked                    */
/* ------------------------------------------------------------------ */

body.is-intro { overflow: hidden; }

/* The land arrives as a wave: it travels in from the left, rising and
   settling, rather than fading in place. */
.land { transform: translate(-320px, 70px); opacity: 0; }
/* One motion, easing out into place: no midpoints, so nothing stutters. */
#map.wave-in .land { animation: wave-in 1300ms cubic-bezier(0.16, 1, 0.3, 1) both; }
#map.wave-in .land-2 { animation-delay: 120ms; animation-duration: 1500ms; }
@keyframes wave-in {
  from { transform: translate(-320px, 70px); opacity: 0; }
  to   { transform: translate(0, 0); opacity: 1; }
}

/* Cities and chips spring in from nothing, with a bounce, at their own
   point. Labels fade in behind them. */
.pop { transform-box: fill-box; transform-origin: center; transform: scale(0); transition: transform 650ms cubic-bezier(0.34, 1.6, 0.5, 1); }
[data-stop].is-on .pop { transform: scale(1); }
.label { opacity: 0; transition: opacity 400ms ease 250ms; }
[data-stop].is-on .label { opacity: 1; }

/* During the intro, Lisbon's spokes draw out before their targets spring
   in; home.js sets the dash values and the timing. */
#map.lines-in .spoke.drawing { transition: stroke-dashoffset 380ms cubic-bezier(0.2, 0.8, 0.2, 1); }

/* The intro: the name alone in the sky above the coast; the line, the
   button, the price and the terms centred in the warm lower band over a
   soft pool, with no box and no bar. The scroll cue sits at the foot of
   the viewport, where scrolling happens. */
.stop.stop-intro { position: relative; flex-direction: column; justify-content: center; align-items: center; padding-top: 0; padding-bottom: 34vh; }
.intro {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  width: 100%;
}
.intro-mark {
  font-family: var(--serif);
  font-style: italic;
  font-weight: 400;
  font-size: clamp(80px, 13vw, 176px);
  line-height: 1;
  letter-spacing: -0.015em;
  color: var(--text-h);
  min-height: 1em;
  margin: 0;
  text-shadow: 0 2px 24px rgba(18, 11, 28, 0.8);
}
/* Translucent "hey" paints with the positioned panel, in document order.
   The heading comes first, so lift it over the panel's soft pool, where it
   sat when the panel came first. */
.intro-mark .typed .mark-hey { opacity: 0.55; position: relative; z-index: 1; }
.caret {
  display: inline-block;
  width: 3px;
  height: 0.85em;
  margin-left: 0.06em;
  vertical-align: -0.08em;
  background: var(--text-h);
  opacity: 0;
}
.intro.typing .caret { opacity: 1; animation: blink 800ms steps(2) infinite; }
.intro.typing .caret.is-done { opacity: 0; animation: none; }
@keyframes blink { to { opacity: 0; } }

/* Hidden, not just transparent, until it shows: an invisible link must not
   take focus during the intro. Its own stacking context keeps the soft
   pool below it over the name, once no transform makes one on a phone. */
.intro-panel {
  position: absolute;
  left: 50%; bottom: 10vh;
  width: min(520px, calc(100% - 48px));
  padding: 0;
  text-align: center;
  background: none; border: none; backdrop-filter: none; -webkit-backdrop-filter: none;
  isolation: isolate;
  visibility: hidden;
  opacity: 0;
  transform: translate(-50%, 18px);
  transition: opacity 500ms ease, transform 650ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
/* A wide, very soft darkening so the type reads over the warm glow without
   ever showing an edge. */
.intro-panel::before {
  content: '';
  position: absolute;
  inset: -70% -90%;
  z-index: -1;
  background: radial-gradient(ellipse at center, rgba(18, 11, 28, 0.55) 0%, rgba(18, 11, 28, 0.38) 22%, rgba(18, 11, 28, 0.18) 45%, rgba(18, 11, 28, 0.05) 65%, transparent 80%);
  pointer-events: none;
}
.intro.show-panel .intro-panel { visibility: visible; opacity: 1; transform: translate(-50%, 0); }
.intro-line {
  font-family: var(--serif);
  font-size: clamp(22px, 2.3vw, 30px);
  line-height: 1.2;
  color: var(--text-h);
  margin: 0 auto 22px;
  max-width: 22ch;
}
.intro-panel .price { margin-top: 14px; color: var(--text-h); opacity: 0.85; }
@media (min-width: 1101px) { .intro-panel .price { white-space: nowrap; } }

.intro-cue {
  position: absolute;
  left: 0; right: 0;
  bottom: 4vh;
  margin: 0;
  text-align: center;
  opacity: 0;
  transition: opacity 500ms ease;
}
.stop-intro.show-cue .intro-cue { opacity: 1; }
body.has-scrolled .stop-intro .intro-cue { opacity: 0; }

/* On a phone the hero is one column in the page's flow: the name, then the
   line, the button, the price and the terms, so none can overlap another.
   The space above the name is all that gives way on a short screen; on a
   tall one the name keeps its place at 40vh and the column's foot sits
   11vh above the bottom, as the pinned panel's did. */
@media (max-width: 720px) {
  .stop.stop-intro { height: 100vh; justify-content: flex-start; padding-top: 0; padding-bottom: 11vh; }
  .intro { flex: 1; min-height: 0; }
  .intro::before { content: ''; flex: 0 1 40vh; }
  .intro-mark { font-size: 76px; margin-bottom: 24px; }
  .intro-panel { position: relative; left: auto; bottom: auto; margin-top: auto; transform: translateY(18px); }
  .intro.show-panel .intro-panel { transform: none; }
  .intro-cue { bottom: 3vh; }
}

@media (prefers-reduced-motion: reduce) {
  .land { animation: none !important; transform: none !important; opacity: 1 !important; }
  .pop, .label, .intro-cue { transition: none !important; transform: none !important; opacity: 1 !important; }
  /* The panel is centred by its transform: drop only the rise. */
  .intro-panel { transition: none !important; opacity: 1 !important; }
  .caret { animation: none !important; }
}

/* ------------------------------------------------------------------ */
/*  The bloom: at the close, connections spread out across the map     */
/* ------------------------------------------------------------------ */

.bloom { transition: opacity 900ms ease; }
.bloom.is-away { opacity: 0; }
.bloom .b-line { stroke: rgba(241, 236, 249, 0.22); stroke-width: 1; stroke-linecap: round; }
.bloom .b-line.b-hot { stroke: var(--ember); stroke-opacity: 0.7; stroke-width: 1.4; }
.bloom .b-line.drawing { transition: stroke-dashoffset 420ms cubic-bezier(0.2, 0.8, 0.2, 1); }
.bloom .b-pop { transform-box: fill-box; transform-origin: center; transform: scale(0); transition: transform 650ms cubic-bezier(0.34, 1.6, 0.5, 1); }
.bloom .b-node.is-on .b-pop { transform: scale(1); }
.bloom .b-node.b-person circle { fill: #2a2138; stroke: var(--dim); stroke-width: 1.2; }
.bloom .b-node.b-person.b-lit circle { fill: var(--t, #b8a1c9); stroke: var(--ember); stroke-width: 1.6; }
.bloom .b-node.b-place circle { fill: var(--text-h); stroke: var(--bg); stroke-width: 2; }
.bloom .b-node.b-tag rect { fill: #1b1226; stroke: var(--dim); }
.bloom .b-node text { font-family: var(--sans); font-size: 11px; fill: var(--dim); paint-order: stroke; stroke: rgba(18,11,28,0.7); stroke-width: 4px; stroke-linejoin: round; }
.bloom .b-node.b-place text { font-family: var(--serif); font-size: 15px; fill: var(--text); }
.bloom .b-node.b-tag text { font-weight: 500; fill: var(--text); stroke: none; }
.bloom .b-node.b-lit text { fill: var(--text-h); }
@media (prefers-reduced-motion: reduce) {
  .bloom, .bloom .b-pop, .bloom .b-line { transition: none !important; }
  .bloom .b-pop { transform: none !important; }
}

/* ------------------------------------------------------------------ */
/*  Privacy and Terms, the deletion notice, and phones                 */
/* ------------------------------------------------------------------ */

/* Under each sign-in button: the terms sit where they start to apply.
   p.consent inside .panel outranks `.panel > p:last-child` (18px). */
.consent, .panel > p.consent { margin-top: 10px; font-size: 13px; line-height: 1.55; color: var(--text); opacity: 0.92; text-wrap: balance; }
.intro-panel .consent { margin: 10px auto 0; }
.stop-close .consent { margin-top: 14px; }

.consent a, .stop-close footer a {
  position: relative;
  color: var(--text);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  text-decoration-color: rgba(196, 185, 214, 0.4);
  transition: color 160ms ease, text-decoration-color 160ms ease;
}
.consent a:hover, .stop-close footer a:hover { color: var(--text-h); text-decoration-color: var(--text-h); }
.consent a:focus-visible, .stop-close footer a:focus-visible, .made a:focus-visible {
  outline: 2px solid var(--text-h);
  outline-offset: 4px;
  border-radius: 3px;
}

/* The app sends a deleted account to /?deleted=1. Say it worked at once,
   above the intro rather than inside its timeline. */
.deleted-notice {
  position: absolute;
  top: 24px;
  left: 50%;
  z-index: 2;
  transform: translateX(-50%);
  width: max-content;
  max-width: calc(100% - 32px);
  margin: 0;
  padding: 10px 18px;
  font-family: var(--serif);
  font-style: italic;
  font-size: 17px;
  line-height: 1.35;
  color: var(--text-h);
  text-align: center;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 999px;
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
}
.deleted-notice[hidden] { display: none; }

/* Phones: every legal link is a 44px tap target. */
@media (max-width: 720px) {
  .consent a::after { content: ''; position: absolute; inset: -14px -4px; }
  .stop-close footer { gap: 8px; }
  .stop-close footer a { display: inline-flex; align-items: center; min-height: 44px; padding: 0 8px; }
  .stop-close footer a:first-child { margin-left: -8px; }
  .deleted-notice { top: 16px; border-radius: 16px; }
}
