/* ============================================================
   HUNTRAIL — field instrument web
   Dark instrument panel · blaze orange · condensed display type
   ============================================================ */

:root {
  --ink: #0c0e0a;
  --ink-2: #12150f;
  --panel: #171b13;
  --panel-2: #1d2218;
  --line: rgba(232, 230, 223, 0.09);
  --line-strong: rgba(232, 230, 223, 0.18);
  --text: #e8e6df;
  --muted: #a3a498;
  --faint: #6f7166;
  --blaze: #ff6b1a;
  --blaze-hot: #ff8a45;
  --blaze-deep: #cc4a00;
  --moss: #8fa876;
  --radius: 14px;
  --font-display: "Big Shoulders", "Arial Narrow", sans-serif;
  --font-body: "Barlow", "Helvetica Neue", sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, monospace;
  /* What the fixed scroll rail costs the right edge: its own offset plus its
     width, plus enough clearance to read something up against it. Anything
     full-bleed keeps out of this. Kept with the scroll rail's own numbers
     below — change one and change this. */
  --rail-clear: 32px;
}

* { margin: 0; padding: 0; box-sizing: border-box; }

html { scroll-behavior: smooth; }

/* While the scroll rail is being dragged the page has to land exactly where
   the pointer is, on the frame it gets there. Smooth scrolling would animate
   towards each position instead, so the page would lag behind the thumb and
   keep coasting after the release. Selection is off for the same press:
   dragging across the page otherwise highlights whatever it passes. */
html.is-railing { scroll-behavior: auto; }
html.is-railing, html.is-railing body { user-select: none; -webkit-user-select: none; }

body {
  background: var(--ink);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 17px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* Topographic contour atmosphere */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  opacity: 0.55;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='600' height='600' viewBox='0 0 600 600'%3E%3Cg fill='none' stroke='%23e8e6df' stroke-opacity='0.045' stroke-width='1.2'%3E%3Cpath d='M-50 120 C 80 60, 200 180, 340 110 S 560 40, 680 130'/%3E%3Cpath d='M-50 170 C 90 105, 210 225, 350 155 S 570 85, 690 175'/%3E%3Cpath d='M-50 225 C 100 155, 225 275, 365 205 S 580 135, 700 225'/%3E%3Cpath d='M-50 290 C 70 230, 190 340, 330 280 S 555 210, 675 295'/%3E%3Cpath d='M-50 360 C 85 295, 205 405, 345 340 S 565 275, 685 360'/%3E%3Cpath d='M-50 430 C 95 370, 215 470, 355 410 S 575 350, 695 430'/%3E%3Cpath d='M-50 500 C 75 445, 195 545, 335 485 S 560 425, 680 505'/%3E%3Cpath d='M-50 560 C 90 505, 210 605, 350 545 S 570 490, 690 565'/%3E%3C/g%3E%3C/svg%3E");
  background-size: 600px 600px;
}

main, header, footer { position: relative; z-index: 1; }

::selection { background: var(--blaze); color: var(--ink); }

a { color: var(--blaze-hot); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 3px; }

.wrap { max-width: 1120px; margin: 0 auto; padding: 0 24px; }

/* ---------- top bar ---------- */

.topbar {
  border-bottom: 1px solid var(--line);
  background: color-mix(in srgb, var(--ink) 82%, transparent);
  backdrop-filter: blur(12px);
  position: sticky;
  top: 0;
  z-index: 50;
}

.topbar-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 64px;
  gap: 24px;
}

.wordmark {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 24px;
  letter-spacing: 0.06em;
  color: var(--text);
}
.wordmark:hover { text-decoration: none; }

/* The app icon itself, not a stand-in dot — shared by the topbar and the
   footer wordmark. This is the background-free cut of the artwork, cropped to
   the antlers: the boxed version reads as a black tile sitting on a dark bar,
   and its dead margin left the mark too small to make out at this size. */
.wordmark .tick {
  width: 26px;
  height: 26px;
  flex: none;
  background: url("/assets/icon-mark.png") center / contain no-repeat;
}

.topnav {
  display: flex;
  align-items: center;
  gap: 26px;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.topnav a { color: var(--muted); }
.topnav a:hover { color: var(--text); text-decoration: none; }
.topnav a.cta {
  color: var(--ink);
  background: var(--blaze);
  padding: 9px 16px;
  border-radius: 999px;
  font-weight: 600;
  transition: background 0.15s ease, transform 0.15s ease;
}
.topnav a.cta:hover { background: var(--blaze-hot); transform: translateY(-1px); }

/* ---------- scroll rail ---------- */

/* The rail stands in for the native scrollbar, so the native one goes away —
   two bars stacked against the same edge is just noise. Only the chrome is
   hidden: wheel, keys, touch, find-in-page and anchor jumps are untouched. */
html { scrollbar-width: none; }              /* Firefox */
html::-webkit-scrollbar { width: 0; height: 0; }   /* Chromium, Safari */

/* Starts below the sticky topbar rather than running the full height, so it
   reads as part of the page rather than part of the browser. */
.scrollrail {
  position: fixed;
  top: 76px;
  right: 12px;
  bottom: 14px;
  width: 11px;
  z-index: 40;
  /* A read-out you can also grab. The hit area is exactly the bar you can
     see, and the bar sits inside the 24px gutter that .wrap leaves — widening
     the target would push it over the content and start eating taps meant for
     whatever runs along the right edge. 11px is a mouse-sized target as it is.

     touch-action is off so a drag down the rail moves the thumb instead of
     being claimed by the browser as a page swipe. */
  cursor: grab;
  touch-action: none;
  background: rgba(12, 14, 10, 0.78);
  border: 1px solid var(--line-strong);
  border-radius: 999px;
}
.scrollrail[hidden] { display: none; }
.scrollrail:hover .scrollrail-thumb,
.scrollrail.is-dragging .scrollrail-thumb {
  box-shadow: 0 0 14px rgba(255, 107, 26, 0.75);
}
.scrollrail.is-dragging { cursor: grabbing; }

/* top/left/right inset must match INSET in boot.js — the script sizes the
   thumb against the track that these offsets leave behind. */
.scrollrail-thumb {
  position: absolute;
  top: 2px;
  left: 2px;
  right: 2px;
  border-radius: 999px;
  background: var(--blaze);
  box-shadow: 0 0 10px rgba(255, 107, 26, 0.4);
  will-change: transform;
}

@media (max-width: 620px) {
  .scrollrail { right: 7px; width: 8px; }
  .scrollrail-thumb { left: 1px; right: 1px; }
  /* 7 + 8, plus the same clearance. See --rail-clear at the top. */
  :root { --rail-clear: 24px; }
}

/* Hamburger, phones only. Hidden on desktop, where the links sit inline.
   The breakpoint here has to match NAV_BREAKPOINT in boot.js: the script
   closes the panel on the way back up to the desktop layout, and if the two
   disagree the panel can be left open with nothing to close it. */
.navtoggle {
  display: none;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  width: 40px;
  height: 40px;
  padding: 0;
  background: none;
  border: 1px solid var(--line-strong);
  border-radius: 10px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.navtoggle span {
  display: block;
  width: 16px;
  height: 1.5px;
  background: var(--text);
  transition: transform 0.2s ease, opacity 0.2s ease;
}
/* Bars fold into an X. 6.5px is one bar plus one gap, so the outer two land
   exactly on the middle one. */
.navtoggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(6.5px) rotate(45deg); }
.navtoggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.navtoggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-6.5px) rotate(-45deg); }

@media (max-width: 760px) {
  .navtoggle { display: flex; }

  /* Out of flow, so the bar itself stays wordmark-plus-button and the panel
     drops underneath it at full width. .topbar is sticky, which makes it the
     containing block these offsets resolve against. */
  .topnav {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding: 4px 24px 20px;
    /* Solid, not translucent. A nested backdrop-filter is a no-op here —
       .topbar already has one, and an ancestor filter becomes the backdrop
       root, so the child can only blur what is painted inside it, never the
       page behind. Left semi-transparent the hero headline read straight
       through the menu. */
    background: var(--ink-2);
    border-bottom: 1px solid var(--line);
    box-shadow: 0 18px 40px rgba(0, 0, 0, 0.45);
    visibility: hidden;
    opacity: 0;
    transform: translateY(-8px);
    transition: opacity 0.18s ease, transform 0.18s ease, visibility 0.18s;
  }
  .topnav.is-open {
    visibility: visible;
    opacity: 1;
    transform: none;
  }

  /* Generous rows: this is a touch target, not a hover target. */
  .topnav a {
    padding: 15px 0;
    border-bottom: 1px solid var(--line);
  }
  .topnav a:last-child { border-bottom: 0; }
  .topnav a.cta {
    margin-top: 18px;
    padding: 15px 16px;
    text-align: center;
  }
}

/* ---------- shared: mono label ---------- */

/* No leading rule before the text. inline-block rather than the inline-flex
   this used to be: the flex box, its gap and its cross-axis alignment existed
   only to seat that rule next to the label, and every .label on the site is a
   single run of text. */
.label {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--blaze);
  display: inline-block;
}

/* ---------- landing hero ---------- */

/* Full-bleed video hero.

   .hero is only a scroll track: it is taller than the screen, and .hero-stage
   pins to the top for the difference. That pinned stretch is the whole point
   — it is what the copy reveal and the footage drift are driven by, and it
   keeps the hero still long enough to read once the copy has arrived. */
.hero {
  position: relative;
  height: 170vh;
  height: 170svh;
  border-bottom: 1px solid var(--line);
}

.hero-stage {
  position: sticky;
  top: 64px;                       /* clears the sticky topbar */
  display: flex;
  align-items: center;
  height: calc(100vh - 64px);
  height: calc(100svh - 64px);
  /* Vertical rhythm scales with viewport height, not just width: a short
     laptop window tightens up, a tall one keeps the generous spacing. */
  padding: clamp(24px, 6vh, 72px) 0 clamp(32px, 7vh, 80px);
  padding: clamp(24px, 6svh, 72px) 0 clamp(32px, 7svh, 80px);
  overflow: hidden;
}

/* No transform on this layer, deliberately. A zoom and a lift used to live
   here to buy slack for nudging the phone crop, and between them and
   object-fit the picture's framing was being decided in two places — which is
   what made the wrap look like the shot had moved. The stylesheet crops once
   now, with object-fit below, and nothing else touches it. */
.hero-media {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity 1s ease;
}
.hero-media.is-ready { opacity: 1; }

/* The only crop there is. On a desktop stage, wider than the 16:9 footage,
   cover trims top and bottom, and the crop is anchored to the bottom of the
   frame because that is where the hiker and the trail are — centred, they
   were cut off below the fold. On a portrait viewport against the portrait
   cut the trim turns horizontal and this costs nothing. */
.hero-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 100%;
}

/* Two scrims doing separate jobs: the vertical one feathers the bottom edge
   into the page and darkens under the sticky topbar, the horizontal one
   darkens the left so the headline stays readable. The middle-right is left
   clear so the footage actually reads instead of being flattened by a
   full-surface wash. */
.hero-stage::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(90deg,
      rgba(12, 14, 10, 0.92) 0%,
      rgba(12, 14, 10, 0.62) 42%,
      rgba(12, 14, 10, 0) 78%),
    linear-gradient(180deg,
      rgba(12, 14, 10, 0.7) 0%,
      rgba(12, 14, 10, 0) 26%,
      rgba(12, 14, 10, 0) 64%,
      var(--ink) 100%);
}
@media (max-width: 860px) {
  /* Copy stacks over the full width on phones, so darken vertically instead. */
  .hero-stage::after {
    background: linear-gradient(180deg,
      rgba(12, 14, 10, 0.86) 0%,
      rgba(12, 14, 10, 0.62) 34%,
      rgba(12, 14, 10, 0.5) 70%,
      var(--ink) 100%);
  }
}

/* .wrap is a flex item here, so it would shrink to its content and the auto
   margins would centre the text. width:100% makes max-width do the work.

   Two columns on desktop rather than the device sitting under the headline:
   at one screen tall the column below the headline is about 160px, which is
   not a phone. Stacked on handsets, where it does land under the headline.

   Three areas, not two: the button block is its own grid item so phones can
   drop it under the device without touching the desktop arrangement, where
   it still reads as one column of copy beside a device spanning the lot.

   The two 1fr rows are what centre that copy against the device. A row-spanning
   item hands its slack to flexible tracks before it grows the auto ones, so
   they absorb whatever the device is taller by; without them the extra would
   open a gap between the headline and the buttons. When the copy is the taller
   side they collapse to zero and this is a plain two-row grid. */
.hero-inner {
  position: relative;
  z-index: 2;
  width: 100%;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-rows: 1fr auto auto 1fr;
  grid-template-areas:
    ".      phone"
    "copy   phone"
    "reveal phone"
    ".      phone";
  align-items: center;
  column-gap: clamp(24px, 4vw, 56px);
  /* .hero-actions already carries the space under the headline; a row gap on
     top of that would double it. Phones set their own. */
  row-gap: 0;
}
/* The device sits at the top of the stage rather than centred against the
   copy. Measured at 1440x900: the hiker walks through x 900-1080, y 660-780,
   and a centred device covers y 203-754 — most of him. There is no horizontal
   answer to that, since he is at the same x as any right-hand column, but
   there is a vertical one: the footage keeps the bottom of the frame for the
   trail, so anything that ends above it is out of his way. */
.hero-phone { align-self: start; }
.hero-copy { grid-area: copy; min-width: 0; }
.hero-reveal { grid-area: reveal; min-width: 0; }

/* Yatay cevap da varmış, iki parçalı.

   Bir: cihaz metin sütununun sağ kenarına değil, pencerenin sağ oluğuna
   yaslanıyor. Sağ boşluk ne kadarsa o kadar (50vw eksi yarım wrap), en fazla
   300px — sınırı belirleyen, çerçeveden sarkan arazi şeridinin ekranda
   kalması. Dar pencerede kendiliğinden sıfıra iniyor.

   İki: kaydırma tek başına yetmiyor. 1440'ta cihaz 937'de başlıyor, avcı
   976–1120 arasında yürüyor ve şeridi ekranda tutan azami kaydırma 176px;
   ikisi teğet geçiyor. Onun için kadraj da yardım ediyor: 16:9 klip
   16:10 bir pencerede yatayda kırpılıyor zaten, o kırpmayı ortadan sağa
   kaydırmak avcıyı sola alıyor. Yanda boşluk yoksa (çok geniş pencere)
   bu satırın hiçbir etkisi olmuyor, orada da zaten kaydırma tek başına
   yetiyor.

   transform değil margin: .rise giriş animasyonu transform'u `both` ile
   tutuyor, oraya yazılan her şey animasyonun altında kalıyor. */
@media (min-width: 861px) {
  .hero-video { object-position: 78% 100%; }
}

/* The device is drawn, not photographed: a bitmap of a phone would be soft at
   any size but the one it was cut at, and this has to hold up from 190px to
   290px wide. Every radius is a share of the width so it scales as one shape. */
.hero-phone {
  grid-area: phone;
  /* Positioned because the popout hangs off it. */
  position: relative;
  /* Room for the strip that hangs off the right edge. It is absolutely
     positioned, so nothing reserves that width on its own, and the stage
     clips: between 1120 and 1250px of viewport the wrap ends close enough to
     the edge that the terrain would be cut in half. */
  margin-right: calc(var(--phone-w) * 0.33);
  /* Width drives everything, including the radii, but the constraint that
     actually binds is height: the frame is 2.12x as tall as it is wide, so a
     width picked off vw alone overshoots a one-screen stage. The vh term is
     the real cap; vw only takes over on tall narrow windows.

     29vh, not 34: with the device pinned to the top of the stage (see
     align-self above) this is what keeps its bottom edge above the hiker, who
     walks at about three quarters of the way down the frame whatever the
     window height. On anything tall the 260px ceiling binds first and this
     does not come into it. */
  --phone-w: clamp(150px, min(21vw, 29vh), 240px);
  --phone-w: clamp(150px, min(21vw, 29svh), 240px);
  width: var(--phone-w);
  /* Not 9/19.5. Both screenshots are 530x1157, and inside a bezel of 0.028w
     on every side that is the outer ratio which makes the screen box match
     them exactly: cover then crops nothing, and the popout hanging outside
     the frame can be placed off the same --phone-w the screens are scaled by.
     It works out at 9:19.05, the same phone to the eye. */
  aspect-ratio: 1 / 2.1168;
  padding: calc(var(--phone-w) * 0.028);
  border-radius: calc(var(--phone-w) * 0.155);
  background:
    linear-gradient(148deg,
      #454a40 0%, #191d14 18%, #0c0f08 52%, #171b12 82%, #3c4137 100%);
  box-shadow:
    0 32px 70px rgba(0, 0, 0, 0.62),
    0 0 0 1px rgba(232, 230, 223, 0.10);
}

/* Cihazı sağ oluğa yaslayan kaydırma. .hero-phone'un kendi margin-right'ından
   sonra gelmek zorunda: aynı özgüllükteki iki kuraldan sonraki kazanıyor. */
@media (min-width: 861px) {
  .hero-phone {
    /* 544 değil 564: cihaz her genişlikte 20px sola geldi. Sınır avcı —
       1440'ta arkasındaki adam 931 ile 1075 arasında yürüyor, cihazın sol
       kenarı 1093'te başlıyor, yani 18px payla açıkta kalıyor. Bu sayıyı
       daha da küçültmek adamı yeniden kapatır. */
    margin-right: calc(var(--phone-w) * 0.33 - clamp(0px, 50vw - 564px, 280px));
    margin-top: -22px;
  }
}

.hero-phone-screen {
  position: relative;
  width: 100%;
  height: 100%;
  border-radius: calc(var(--phone-w) * 0.128);
  overflow: hidden;
  background: var(--ink);
}

/* Stacked, all of them: the swap is a crossfade, so both frames have to be
   painted at once.

   cover, and it crops nothing: both screens were redrawn at 530x1157, which
   is the screen box's own ratio. That exactness is what the popout depends
   on — a screen scaled by anything other than box width / 530 would put the
   seam in the wrong place. Keep any replacement screenshot at this size. */
.hero-phone-screen img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 50%;
  opacity: 0;
  transition: opacity 0.45s ease;
}
.hero-phone-screen img.is-on { opacity: 1; }

/* The terrain leaving the frame.

   The map screenshot is 530px wide across a screen box of 0.944 --phone-w, so
   one source pixel is --phone-w * 0.0017811, and every number below is that
   scale applied to where this strip was cut from. Its left edge lands on the
   screen's right edge, which is what makes the mass read as one piece. Height
   is left to the file's own ratio rather than stated, so it cannot drift from
   the width.

   It slides out from under the frame rather than just fading in: coming from
   behind the device is what sells it as leaving the screen. */
.hero-popout {
  position: absolute;
  left: calc(var(--phone-w) * 0.972);
  top: calc(var(--phone-w) * 0.0934);
  width: calc(var(--phone-w) * 0.3491);
  height: auto;
  pointer-events: none;
  filter: drop-shadow(0 18px 34px rgba(0, 0, 0, 0.55));
  opacity: 0;
  transform: translate3d(calc(var(--phone-w) * -0.06), 0, 0) scale(0.97);
  transform-origin: left center;
  transition: opacity 0.45s ease, transform 0.7s cubic-bezier(0.2, 0.7, 0.2, 1);
}
.hero-popout.is-on { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .hero-popout { transform: none; transition: opacity 0.45s ease; }
}

@media (max-width: 860px) {
  /* Device between the headline and the buttons. The button is the thing
     being asked for, so it goes last where a thumb reaches it, and the device
     takes the middle, where it has room to be a device and not a thumbnail. */
  .hero-inner {
    grid-template-columns: 1fr;
    grid-template-rows: auto auto auto;
    grid-template-areas:
      "copy"
      "phone"
      "reveal";
    justify-items: start;
    row-gap: clamp(16px, 3vh, 28px);
    row-gap: clamp(16px, 3svh, 28px);
  }

  /* justify-items: start is for the device — it keeps the frame its own width
     and puts it at the left edge rather than stretching it across the column.
     The two text items must not take it: a shrink-to-fit .hero-copy is as
     wide as whatever headline is in it, and fitTitles measures the headline's
     own box to find the column. It would read 808px under "Offline, hunting
     maps." and 546px under "Plan your hunt.", size the type to each, and so
     change it every time the two swapped. Full width and the figure is the
     column's, the same whatever the words are — which is also what lets the
     headline be set to fill it. */
  .hero-copy,
  .hero-reveal { width: 100%; }

  /* Chip above the button. Stacked, the button wants to be the last thing
     before the fold — that is where the thumb is — and the readout reads as
     a caption on the device it sits under. Order rather than markup so the
     desktop column, where the chips are a footer under the buttons, keeps
     both the arrangement and the source order it had. */
  .hero-reveal {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
  }
  /* Stretched, alone among the items here. The container packs to the start
     so the buttons stay the width of their own labels — but this one holds
     the season rail, and a flex item packed to the start takes its width from
     its content, which is 4286px of track. Stretch and it is the column's,
     which is what the rail's own width: 100% is then a share of. */
  .hero-extra {
    order: -1;
    align-self: stretch;
    min-width: 0;
  }

  /* One size for every stacked width. The height term is what binds on a
     handset and it is set so a 393x852 iPhone lands at 179px, which is the
     proportion the whole block was tuned around; the vw term only takes over
     on something unusually short and wide. Everything else in the stack is a
     share of vh too, so a smaller screen gets the same hero, smaller —
     apart from the button and the chip, which are type and cannot scale. */
  .hero-phone {
    --phone-w: clamp(132px, min(48vw, 21vh), 300px);
    --phone-w: clamp(132px, min(48vw, 21svh), 300px);
  }
}

/* Short handsets. A phone in portrait reports roughly 660px of svh with the
   browser chrome showing, not the 844 its screen suggests. The type in the
   block under the device does not shrink with the screen, so the device is
   what pays for it: a slightly flatter vh coefficient, not a different look. */
@media (max-width: 860px) and (max-height: 720px) {
  .hero-inner { row-gap: 14px; }
  .hero-phone {
    --phone-w: clamp(110px, min(44vw, 19vh), 180px);
    --phone-w: clamp(110px, min(44vw, 19svh), 180px);
  }
}

/* The block that arrives on the first scroll — the readout, and nothing else.
   The download button used to be in here and is not any more: it is what the
   page is asking for, and it now sits above this, visible on arrival.

   It keeps its layout space at all times, so the button sits at its final
   height from the start and nothing jumps when the readout appears.

   Visible by default on purpose: if boot.js never runs, everything is still
   there. The staged state is opt-in, and the script is what opts in. */
.hero.is-staged .hero-extra {
  opacity: 0;
  transform: translate3d(0, 26px, 0);
}

/* The headline is swapped as the device changes screens. It goes out on the
   same short move it comes back on, so the words read as changing rather than
   as a piece of the page leaving. The timing pairs with the 160ms wait in
   boot.js: long enough to be gone before the text is rewritten, short enough
   that the line is never missing for a beat. */
.hero h1[data-hero-title] {
  transition: opacity 0.16s ease, transform 0.16s ease;
}
.hero h1.is-swapping {
  opacity: 0;
  transform: translate3d(0, 10px, 0);
}
@media (prefers-reduced-motion: reduce) {
  .hero h1[data-hero-title] { transition: none; }
  .hero h1.is-swapping { opacity: 1; transform: none; }
}

.hero h1 {
  font-family: var(--font-display);
  font-weight: 900;
  /* min() against 17vh so two 0.92-leading lines cannot eat the fold on a
     short window; the vw term still drives it on anything tall enough.

     14vw, not 11vw with a 64px floor: at 390px that floor forced 64px, which
     needs 382px to set "Your season," on one line against 342px of column, so
     the headline broke to three lines and pushed the copy past the stage. The
     vh term is what binds on every desktop size, so this reads identically
     there and only changes what phones get. */
     svh rather than vh, here and everywhere else in the hero: on a phone vh
     follows the browser's own chrome collapsing as you scroll, so every
     measurement in this block moved on the first flick. svh is the viewport
     with the chrome showing, which is the conservative figure the fold was
     budgeted against anyway, and it does not move. The vh line before each
     stays for engines that do not know the unit. */
  font-size: clamp(40px, min(14vw, 17vh), 148px);
  font-size: clamp(40px, min(14vw, 17svh), 148px);
  line-height: 0.92;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  margin: clamp(12px, 2.2vh, 22px) 0 clamp(16px, 2.8vh, 28px);
  margin: clamp(12px, 2.2svh, 22px) 0 clamp(16px, 2.8svh, 28px);
}
.hero h1 .accent { color: var(--blaze); }

.hero-actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 18px;
  margin-top: clamp(22px, 4vh, 40px);
  margin-top: clamp(22px, 4svh, 40px);
}

.btn-store {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  background: var(--text);
  color: var(--ink);
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 16px;
  padding: 14px 26px;
  border-radius: 12px;
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.btn-store:hover {
  text-decoration: none;
  transform: translateY(-2px);
  box-shadow: 0 12px 32px rgba(255, 107, 26, 0.18);
}
.btn-store svg { flex: none; }

.btn-ghost {
  font-family: var(--font-mono);
  font-size: 13px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  border: 1px solid var(--line-strong);
  padding: 13px 22px;
  border-radius: 12px;
}
.btn-ghost:hover { color: var(--text); border-color: var(--faint); text-decoration: none; }

.hero-coords {
  margin-top: clamp(26px, 5.5vh, 64px);
  margin-top: clamp(26px, 5.5svh, 64px);
  margin-top: clamp(26px, 5.5svh, 64px);
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--muted);
  letter-spacing: 0.08em;
}
.hero-coords span {
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  padding: 6px 14px;
}
.hero-coords .live { color: var(--blaze); border-color: rgba(255, 107, 26, 0.35); }
/* tabular figures stop the chip from resizing as the distance counts up */
.hero-coords .live b { font-weight: 500; font-variant-numeric: tabular-nums; }

/* Phone trims. The mono faces carry wide tracking that reads well across a
   desktop column and simply runs out of room on a handset: the hero label is
   39 characters, which at 12px/0.22em wants 384px against 342px of column. */
/* The rest of the stacked hero's spacing. It lives down here rather than with
   the layout block above because these properties are set by the desktop
   rules in between: at equal specificity the last one wins, and a copy of
   this next to the grid definition would be dead code.

   The numbers are the fold budget at 393x852: the stage is one screen minus
   the topbar, and copy + device + this block has to fit inside it or the
   headline slides out the top. They are shares of vh so the same hero comes
   out smaller on a smaller screen instead of a different one. Retune them
   together, and check 393x852 and 375x667 after. */
@media (max-width: 860px) {
  .hero-stage {
    padding: clamp(16px, 3vh, 32px) 0 clamp(20px, 4vh, 44px);
    padding: clamp(16px, 3svh, 32px) 0 clamp(20px, 4svh, 44px);
  }
  .hero h1 {
    margin: clamp(10px, 1.6vh, 18px) 0 clamp(12px, 2vh, 22px);
    margin: clamp(10px, 1.6svh, 18px) 0 clamp(12px, 2svh, 22px);
  }
  /* The grid's row gap is the space under the device now. */
  .hero-coords { margin-top: 0; }
  .hero-actions {
    margin-top: clamp(12px, 1.8vh, 18px);
    margin-top: clamp(12px, 1.8svh, 18px);
  }
}

@media (max-width: 620px) {
  /* 39 characters at 10.5px/0.14em want 384px and a 375px handset has 327px
     of column, so the label broke to two lines on the smaller phones. */
  .label {
    font-size: 9.8px;
    letter-spacing: 0.1em;
  }
  .hero-coords {
    font-size: 11px;
    gap: 8px;
    letter-spacing: 0.06em;
  }
  .hero-coords span { padding: 5px 11px; }

  /* The two buttons cannot share a row at this width, so the secondary one
     costs a full 70px stacked under the primary. It only jumps to #features,
     which the menu also does, so the fold buys back the space. */
  .hero-actions .btn-ghost { display: none; }
}

/* staggered reveal */
@keyframes rise {
  from { opacity: 0; transform: translateY(18px); }
  to { opacity: 1; transform: none; }
}
.rise { animation: rise 0.7s cubic-bezier(0.2, 0.7, 0.2, 1) both; }
.d1 { animation-delay: 0.05s; }
.d2 { animation-delay: 0.15s; }
.d3 { animation-delay: 0.25s; }
.d4 { animation-delay: 0.35s; }
.d5 { animation-delay: 0.45s; }
@media (prefers-reduced-motion: reduce) {
  .rise { animation: none; }
}

/* ---------- feature grid ---------- */

.features { padding: 40px 0 90px; }

.features-head { margin-bottom: 44px; }
.features-head h2,
.pro h2 {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(38px, 5.5vw, 64px);
  text-transform: uppercase;
  line-height: 1;
  margin-top: 16px;
}

/* Telefonda da iki sütun. Tek sütunda altı kart, altı ekran boyu scroll
   demekti; kimse özellik listesini o kadar kovalamıyor. Kartın içi buna göre
   kısaldı: başlık, ekran görüntüsü, tek cümle. */
.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
}
@media (max-width: 900px) { .grid { grid-template-columns: repeat(2, 1fr); gap: 12px; } }

.card {
  background: linear-gradient(160deg, var(--panel-2), var(--panel));
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 26px 24px 28px;
  position: relative;
  overflow: hidden;
  transition: transform 0.2s ease, border-color 0.2s ease;
  /* Görsel ve cümle aynı hizada bitsin: kartlar farklı uzunlukta metin
     taşıyor, ızgara satırındaki en uzunu diğerlerinin boyunu belirliyor. */
  display: flex;
  flex-direction: column;
}
.card:hover { transform: translateY(-3px); border-color: rgba(255, 107, 26, 0.4); }

.card .num {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.2em;
  color: var(--faint);
}

.card h3 {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 26px;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  margin: 12px 0 10px;
}

.card p { color: var(--muted); font-size: 15.5px; margin-top: auto; }

/* ---------- kart içindeki uygulama ekranı ----------
   Kutu sabit 4:3, ızgara satır satır hizalı kalsın diye. Ekran görüntüleri
   ise 0.89 ile 1.99 arasında değişen oranlarda geliyor — telefondan alınmış,
   her biri kendi panelinin boyunda. Onun için cover değil contain: kırpmak
   birinde haritayı, ötekinde "end hunt" düğmesini kesiyordu. Görüntülerin
   zemini zaten uygulamanın koyusu, kenarda kalan boşluk kutunun zeminiyle
   birleşiyor. */
.shot {
  margin: 0 0 16px;
  aspect-ratio: 16 / 9;
  border-radius: 10px;
  overflow: hidden;
  border: 1px solid var(--line);
  background: var(--ink);
}

/* Üstten hizalı doldurma. Sığdırmak (contain) denendi: dikey ekran
   görüntüleri kutunun üçte ikisine düşüyor ve iki sütunlu telefon
   ızgarasında haritadaki yazı okunmuyordu. Her ekran görüntüsünün anlamı
   üst tarafında — başlık, kart, ilk satır — o yüzden kırpma alttan. */
.shot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 0;
  display: block;
}

/* Pul kartı hazır bir görsel, telefondan çekilmiş bir ekran değil: dört pul
   zaten çerçevesine göre kadrajlanmış, kırpınca alt sıranın adları gidiyor.
   Tek istisna olarak sığdırılıyor. */
.shot img[src*="feat-stamps"] { object-fit: contain; }

/* Telefonda kart iki sütuna sığmak zorunda: iç boşluk, başlık ve gövde
   küçülüyor, ama ekran görüntüsü oranını koruyor. */
@media (max-width: 900px) {
  .card { padding: 16px 14px 18px; }
  .card h3 { font-size: 20px; margin: 8px 0 10px; }
  .card p { font-size: 13.5px; line-height: 1.45; }
  .card .num { font-size: 10px; }
  .shot { margin-bottom: 12px; border-radius: 8px; }
}

.card::after {
  content: "";
  position: absolute;
  top: 0;
  left: 24px;
  right: 24px;
  height: 2px;
  background: linear-gradient(90deg, var(--blaze), transparent 70%);
  opacity: 0;
  transition: opacity 0.2s ease;
}
.card:hover::after { opacity: 1; }

/* ---------- pro band ---------- */

/* The band's own bottom edge is the season rail's, so the padding stops at
   the copy and the rail brings its own. */
.pro {
  position: relative;
  overflow: hidden;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  background:
    radial-gradient(700px 300px at 85% 20%, rgba(255, 107, 26, 0.09), transparent 65%),
    var(--ink-2);
  padding: 84px 0 0;
}

/* Two glows on one element, drifting and breathing on a cycle long enough
   that nothing about it reads as a loop — you notice the band has depth, not
   that something is moving in it. Inset past every edge so the corners are
   never uncovered at the extremes of the travel. */
.pro-drift {
  position: absolute;
  inset: -25% -15%;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(460px 240px at 22% 34%, rgba(255, 107, 26, 0.10), transparent 62%),
    radial-gradient(400px 220px at 78% 68%, rgba(143, 168, 118, 0.08), transparent 60%);
  animation: proDrift 34s ease-in-out infinite alternate;
}
@keyframes proDrift {
  from { transform: translate3d(-3%, -2%, 0) scale(1); }
  to   { transform: translate3d(3%, 2%, 0) scale(1.08); }
}

.pro-inner {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: 1.1fr 1fr;
  gap: 56px;
  align-items: center;
}
@media (max-width: 820px) { .pro-inner { grid-template-columns: 1fr; } }

.pro p.sub { color: var(--muted); margin-top: 18px; max-width: 480px; }

.pro-list { list-style: none; margin-top: 8px; }
.pro-list li {
  padding: 14px 0;
  border-bottom: 1px solid var(--line);
  display: flex;
  gap: 14px;
  align-items: baseline;
  color: var(--text);
  font-size: 16px;
}
.pro-list li:last-child { border-bottom: none; }
.pro-list .m {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--blaze);
  letter-spacing: 0.15em;
  flex: none;
  width: 36px;
  /* Lit in turn down the list, one row every 1.4s of an 8s cycle, so most of
     the cycle is the list sitting still. A sweep that never rests would pull
     the eye off the words it is supposed to be pointing at. */
  animation: proTick 8s ease-in-out infinite;
}
.pro-list li:nth-child(2) .m { animation-delay: 1.4s; }
.pro-list li:nth-child(3) .m { animation-delay: 2.8s; }
.pro-list li:nth-child(4) .m { animation-delay: 4.2s; }
@keyframes proTick {
  0%, 14%, 100% { color: var(--blaze); text-shadow: none; }
  5% { color: var(--blaze-hot); text-shadow: 0 0 16px rgba(255, 138, 69, 0.6); }
}

.pro-note { font-size: 13px; color: var(--faint); margin-top: 18px; font-family: var(--font-mono); letter-spacing: 0.04em; }

/* ---------- season rail ---------- */

/* Full bleed in the band on purpose: it sits outside .wrap so the year runs
   the width of the window and reads as passing through the band rather than
   sitting in a box in the middle of it.

   width, stated: in the hero this is a block in a flex column that packs to
   the start, so it took its width from its content — and its content is a
   4286px track. The box grew to match, overflow clipped nothing, and the mask
   put its fades at 10% and 90% of 4286px, both of them off screen. So the
   months ran the full width of the phone and straight under the scroll rail,
   where they were unreadable. */
.seasonrail {
  position: relative;
  z-index: 1;
  width: 100%;
  padding: 15px 0;
  border-top: 1px solid var(--line);
}

/* The moving part. The clip and the fade live here rather than on .seasonrail
   so the hairline above the months is not masked with them — it stays a
   hairline all the way across whatever the months are doing.

   The fade at both ends is also what hides the one place the loop could show:
   items entering and leaving mid-word. */
.seasonrail-view {
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 10%, #000 90%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 10%, #000 90%, transparent);
}

/* The full-bleed rails reach the window's right edge, and the scroll rail is
   fixed over it. The months stop at its left instead of passing behind it.
   The hairline still runs the whole width — it is the band's edge, and it
   reads as a line under the bar rather than as text behind it. */
.pro .seasonrail-view,
.seasonband .seasonrail-view { margin-right: var(--rail-clear); }

/* boot.js has not filled it, or never ran. It is ornament either way, so it
   goes rather than leaving an empty ruled strip across the page. */
.seasonrail:empty { display: none; }

/* In the Pro band the rail is the band's own base — .pro's bottom border
   closes it, and the gap above is what separates it from the copy. */
.pro .seasonrail { margin-top: 64px; }

/* In the hero it is one more instrument ticking over under the readout, not
   a second band: smaller type, one hairline above it, nothing else. It keeps
   the mask, which is what makes the loop's seam invisible, and it arrives
   with the readout it sits under — but nothing about it answers to the
   scroll after that. */
.hero .seasonrail {
  margin-top: 16px;
  padding: 12px 0 0;
}
.hero .seasonrail-item {
  padding: 0 20px;
  font-size: 11px;
  letter-spacing: 0.13em;
}
/* Every item keeps the same padding, first one included: the track's two
   halves have to be identical widths or the -50% snap-back lands off by
   whatever the difference is, and the seam shows. */

/* On the stacked hero too, and the five fixed chips that used to sit beside
   the tracking readout are what paid for it: they wrapped to three rows on a
   handset, the rail is one. Tighter than the desktop's, and the left edge is
   flush — the mask's fade is what the year comes out of on the way in. */
@media (max-width: 860px) {
  .hero .seasonrail {
    margin-top: 12px;
    padding: 10px 0 0;
  }
  .hero .seasonrail-item { padding: 0 15px; font-size: 10px; letter-spacing: 0.1em; }
  .hero .seasonrail-track { animation-duration: 32s; }
}

/* On the legal pages there is no section for it to close, so it brings the
   band with it: the dark panel behind the year and the line under it. */
.seasonband {
  position: relative;
  z-index: 1;
  margin-top: 78px;
  background: var(--ink-2);
  border-bottom: 1px solid var(--line);
}

/* max-content, not a percentage: the track has to be as wide as its twenty-four
   items actually are, because the -50% below is measured against that width and
   is what makes the seam land on identical pixels. */
.seasonrail-track {
  display: flex;
  width: max-content;
  animation: seasonRoll 44s linear infinite;
}
/* Stop it where it is so a month that caught the eye can be read. */
.seasonrail:hover .seasonrail-track { animation-play-state: paused; }

@keyframes seasonRoll {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-50%, 0, 0); }
}

.seasonrail-item {
  position: relative;
  flex: none;
  padding: 0 26px;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--faint);
  white-space: nowrap;
}
.seasonrail-item b { font-weight: 500; color: var(--blaze); margin-right: 8px; }

/* The tick between months. On the item rather than between them, so the
   markup stays twelve spans a copy. */
.seasonrail-item::after {
  content: "";
  position: absolute;
  right: -2px;
  top: 50%;
  width: 4px;
  height: 4px;
  margin-top: -2px;
  background: var(--line-strong);
  transform: rotate(45deg);
}

@media (max-width: 640px) {
  .seasonrail { margin-top: 48px; }
  .seasonrail-item { padding: 0 18px; font-size: 11px; letter-spacing: 0.12em; }
  .seasonrail-track { animation-duration: 32s; }
}

/* Every loop in the band is ambience, so none of it survives the preference —
   the drift stops on its own gradients, the rail stops mid-year showing real
   months, and the list keeps the blaze the labels already had. */
@media (prefers-reduced-motion: reduce) {
  .pro-drift { animation: none; }
  .pro-list .m { animation: none; }
  .seasonrail-track { animation: none; }
}

/* ---------- footer ---------- */

footer {
  padding: 56px 0 40px;
  border-top: 1px solid var(--line);
  margin-top: 0;
}

.foot-grid {
  display: flex;
  justify-content: space-between;
  gap: 40px;
  flex-wrap: wrap;
}

.foot-brand { max-width: 340px; }
.foot-brand .wordmark { font-size: 20px; margin-bottom: 12px; }
.foot-brand p { color: var(--faint); font-size: 13.5px; }

.foot-col h4 {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--faint);
  margin-bottom: 14px;
  font-weight: 500;
}
.foot-col ul { list-style: none; }
.foot-col li { margin-bottom: 9px; }
.foot-col a { color: var(--muted); font-size: 14.5px; }
.foot-col a:hover { color: var(--text); }

.foot-legal {
  margin-top: 48px;
  padding-top: 22px;
  border-top: 1px solid var(--line);
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 12px;
  font-family: var(--font-mono);
  font-size: 11.5px;
  letter-spacing: 0.06em;
  color: var(--faint);
}

/* ---------- legal document pages ---------- */

.doc { padding: 72px 0 100px; }

.doc-head { max-width: 760px; margin-bottom: 56px; }
.doc-head h1 {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(44px, 7vw, 80px);
  text-transform: uppercase;
  line-height: 0.98;
  margin: 18px 0 20px;
}
.doc-head .meta {
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--faint);
  letter-spacing: 0.08em;
}

.doc-body { max-width: 760px; }

.doc-body h2 {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 30px;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  margin: 56px 0 16px;
  padding-top: 24px;
  border-top: 1px solid var(--line);
  scroll-margin-top: 90px;
}
.doc-body h2 .no {
  color: var(--blaze);
  font-family: var(--font-mono);
  font-size: 14px;
  letter-spacing: 0.15em;
  display: block;
  margin-bottom: 6px;
}

.doc-body h3 {
  font-size: 18px;
  font-weight: 600;
  margin: 28px 0 10px;
}

.doc-body p { color: var(--muted); margin-bottom: 16px; }
.doc-body strong { color: var(--text); font-weight: 600; }

.doc-body ul, .doc-body ol { margin: 0 0 18px 22px; color: var(--muted); }
.doc-body li { margin-bottom: 9px; }
.doc-body li::marker { color: var(--blaze); }

.doc-body .callout {
  background: var(--panel);
  border: 1px solid var(--line-strong);
  border-left: 3px solid var(--blaze);
  border-radius: 10px;
  padding: 18px 20px;
  margin: 22px 0;
  color: var(--text);
  font-size: 15.5px;
}

.doc-body table {
  width: 100%;
  border-collapse: collapse;
  margin: 20px 0 26px;
  font-size: 14.5px;
}
.doc-body th {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--faint);
  text-align: left;
  padding: 10px 14px 10px 0;
  border-bottom: 1px solid var(--line-strong);
  font-weight: 500;
}
.doc-body td {
  padding: 12px 14px 12px 0;
  border-bottom: 1px solid var(--line);
  color: var(--muted);
  vertical-align: top;
}
.doc-body td:first-child { color: var(--text); font-weight: 500; }
.table-scroll { overflow-x: auto; }

.doc-toc {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 22px 24px;
  margin-bottom: 20px;
}
.doc-toc h4 {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--faint);
  margin-bottom: 12px;
  font-weight: 500;
}
.doc-toc ol {
  list-style: none;
  columns: 2;
  gap: 32px;
  font-size: 14px;
}
@media (max-width: 620px) { .doc-toc ol { columns: 1; } }
.doc-toc li { margin-bottom: 8px; break-inside: avoid; }
.doc-toc a { color: var(--muted); }
.doc-toc a:hover { color: var(--blaze-hot); }
.doc-toc .n {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--blaze);
  margin-right: 8px;
}
