/* ─────────────────────────────────────────────────────────────
   Rutis — tokens
   Brand values are the same constants the app ships with
   (src/constants/colors.js + the logo SVGs).
   ───────────────────────────────────────────────────────────── */
:root {
  --blue:      #2368E8;
  --blue-deep: #1751BE;
  --gold:      #FCC207;
  --dest:      #EA4335;
  --ink:       #0E1C36;
  --muted:     #59698A;
  --faint:     #8494B0;
  --paper:     #F2F5FA;
  --surface:   #FFFFFF;
  --sunken:    #E7EDF7;
  --rule:      #D9E2F0;
  --rule-soft: #E8EEF8;
  --shadow:    14px 40px -18px rgba(14, 28, 54, .22);
  --shadow-sm: 0 2px 10px -4px rgba(14, 28, 54, .18);

  /* Price scale — lifted verbatim from src/utils/colors.js */
  --p0:  #10b981;
  --p5:  #22c55e;
  --p10: #84cc16;
  --p15: #f59e0b;
  --p20: #f97316;
  --p30: #ef4444;
  --p31: #dc2626;

  --map-land:   #F1EEE8;
  --map-block:  #E5DFD5;
  --map-park:   #CDE7C4;
  --map-road:   #FFFFFF;
  --map-major:  #F8DC96;
  --map-water:  #7FCCEC;
  --map-label:  #7C8BA5;

  /* the dark call-to-action panel keeps its own tokens, so no component
     rule has to be scoped to a theme (and win a specificity fight it shouldn't) */
  --shell: 1080px;
  --step--1: .8125rem;
  --step-0:  1.0625rem;
  --step-1:  1.25rem;
  --step-2:  1.6rem;
  --step-3:  2.15rem;
  --step-4:  clamp(2.4rem, 1.5rem + 3.6vw, 3.9rem);
}

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

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

/* The masthead is sticky, so it still takes its place at the top of the
   document and a fragment jump parks the target underneath it. The band
   sections happened to clear it by one or two pixels, which is luck rather
   than design — their padding is 56px against a 69px header, and the hero's
   is 40px, which is why #top put the slogan behind the bar.
   68px of header, 1px of border, and a little air. */
:target, [id] { scroll-margin-top: calc(69px + 1rem); }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: "Nunito Sans", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  font-size: var(--step-0);
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 {
  font-family: Nunito, ui-rounded, "Nunito Sans", system-ui, sans-serif;
  font-weight: 900;
  letter-spacing: -.022em;
  line-height: 1.08;
  text-wrap: balance;
  overflow-wrap: break-word;
  margin: 0;
}

p { margin: 0; }

a { color: var(--blue); text-decoration-color: color-mix(in srgb, var(--blue) 35%, transparent); text-underline-offset: 3px; }
a:hover { text-decoration-color: currentColor; }

:focus-visible {
  outline: 3px solid var(--blue);
  outline-offset: 3px;
  border-radius: 4px;
}

.shell {
  width: 100%;
  max-width: var(--shell);
  margin-inline: auto;
  padding-inline: clamp(1.25rem, 4vw, 2.5rem);
}

.mono {
  font-family: "IBM Plex Mono", ui-monospace, "SFMono-Regular", Menlo, monospace;
  font-variant-numeric: tabular-nums;
}

.eyebrow {
  font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace;
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--faint);
  margin: 0;
}

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--surface);
  color: var(--ink);
  padding: .7rem 1.1rem;
  border-radius: 10px;
  z-index: 99;
}
.skip:focus { left: 1rem; top: 1rem; }

/* ── logo ───────────────────────────────────────────────────── */
.logo { display: flex; align-items: center; gap: .6rem; text-decoration: none; color: inherit; }
.logo__mark { display: block; height: 34px; width: auto; overflow: visible; }
.logo__word { display: block; height: 22px; width: auto; }
.logo__letters { fill: var(--ink); }
.logo__dot { fill: var(--blue); }
.logo__pin-hole { fill: var(--paper); }

/* ── header ─────────────────────────────────────────────────── */
.masthead {
  position: sticky;
  top: 0;
  z-index: 20;
  background: color-mix(in srgb, var(--paper) 86%, transparent);
  backdrop-filter: saturate(150%) blur(14px);
  border-bottom: 1px solid transparent;
  transition: border-color .25s ease;
}
.masthead[data-scrolled="true"] { border-bottom-color: var(--rule-soft); }

.masthead__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  min-height: 68px;
}

.masthead__nav {
  display: flex;
  align-items: center;
  gap: 1.6rem;
  font-size: var(--step--1);
  font-weight: 600;
}
/* :not(.btn) so the nav's muted link colour can't outrank .btn--primary's white label */
.masthead__nav a:not(.btn) { color: var(--muted); text-decoration: none; }
.masthead__nav a:not(.btn):hover { color: var(--ink); }
.masthead__nav a.btn { text-decoration: none; }
/* The breakpoint is where the full nav genuinely fits, not a round number.
   Measured: seven links come to 673px, and with the logo, the row gap and the
   shell padding the masthead needs 847px. At the old 720 the links came back
   1px past the query and pushed the page to 835px wide — the overflow below
   the breakpoint had a twin just above it. */
@media (max-width: 860px) { .masthead__nav .is-optional { display: none; } }

/* Dropping those four was never enough on a phone. What is left — a section
   link, the language switch and the button — still comes to 353px against the
   217px between the logo and the edge, so the button was carried off screen
   and the page was 507px wide at a 390px viewport.
   min-width:0 lets the nav shrink at all: a flex item defaults to min-width
   auto and so refuses to go below its min-content width. Below 560 the last
   section link goes too, leaving the two things a phone actually needs. */
.masthead__nav { min-width: 0; }
@media (max-width: 560px) {
  .masthead__nav a:not(.btn):not(.lang) { display: none; }
  /* 1.6rem between three items is 51px of the 187 a 360px screen leaves, and
     the button cannot wrap its label to make up the difference. */
  .masthead__nav { gap: .8rem; }
}

/* Language switch. Never .is-optional: a visitor who needs the other language
   needs it most on a phone, which is where the optional links are dropped.
   Boxed rather than plain text so it reads as a control among the links. */
.lang {
  display: inline-flex;
  align-items: center;
  gap: .3rem;
  padding: .28rem .55rem;
  border: 1px solid var(--rule);
  border-radius: 999px;
  line-height: 1;
  white-space: nowrap;
}
.masthead__nav a.lang:hover { border-color: var(--blue); color: var(--blue); }
.lang__globe { flex: none; }

/* ── buttons ────────────────────────────────────────────────── */
.btn {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  padding: .72rem 1.25rem;
  border-radius: 999px;
  border: 1px solid transparent;
  font-family: Nunito, system-ui, sans-serif;
  font-size: var(--step--1);
  font-weight: 800;
  letter-spacing: -.01em;
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
  transition: transform .16s ease, box-shadow .16s ease, background-color .16s ease;
}
.btn--primary {
  background: var(--blue);
  color: #fff;
  box-shadow: 0 8px 20px -10px color-mix(in srgb, var(--blue) 80%, transparent);
}
.btn--primary:hover { background: var(--blue-deep); transform: translateY(-1px); }
.btn--ghost {
  background: transparent;
  color: var(--ink);
  border-color: var(--rule);
}
.btn--ghost:hover { border-color: var(--blue); color: var(--blue); }
@media (prefers-reduced-motion: reduce) { .btn:hover { transform: none; } }

/* ── section rhythm ─────────────────────────────────────────── */
.band { padding-block: clamp(3.5rem, 7vw, 6rem); }
.band--tint { background: var(--surface); border-block: 1px solid var(--rule-soft); }

.band__head {
  display: flex;
  flex-direction: column;
  gap: .75rem;
  max-width: min(34rem, 100%);
  margin-bottom: clamp(2rem, 4vw, 3rem);
}
/* Marginalen finns för att skilja rubriken från innehållet under den. Står
   den ensam i sin sektion — som Problemet gör sedan before/after-rutorna togs
   bort — blir den i stället en tom remsa ovanpå sektionens egen padding. */
.band__head:only-child { margin-bottom: 0; }
.band__head h2 { font-size: var(--step-3); }
/* Drawn under the words rather than behind them: a full block of yellow at
   this size reads as a UI selection, where a band along the baseline reads as
   someone reaching for a highlighter. */
.hl {
  background: linear-gradient(transparent 58%, color-mix(in srgb, var(--gold) 72%, transparent) 58%);
  color: inherit;
  padding-inline: .1em;
  border-radius: 2px;
}
.band__head p { color: var(--muted); font-size: var(--step-1); line-height: 1.55; }
/* The three questions a driver is actually asking. They were one bold line
   with a <br> in front of it, which ran them together into a single grey
   mouthful — the thing you least want to skim is the thing the whole page is
   about. Three pills read as three questions at a glance, wrap one per line
   on a phone, and pick up the rounded chip shape the app uses in its own
   legend, so the section looks like the product it is describing.

   The mark is a real "?" rather than an icon: it is the same glyph the
   questions already end with, which is why it needs no explaining. */
.asks {
  /* One per row rather than wrapped: the three come to about 780px and the
     head's measure is 34rem, so a row broke 2 + 1 and read as an accident.
     justify-items keeps each pill the width of its own question. */
  display: grid;
  justify-items: start;
  gap: .5rem;
  margin: .2rem 0 0;
  padding: 0;
  list-style: none;
}
.asks li {
  display: inline-flex;
  align-items: center;
  gap: .55rem;
  padding: .5rem 1.1rem .5rem .6rem;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 999px;
  color: var(--ink);
  font-weight: 700;
  font-size: var(--step-0);
  line-height: 1.3;
}
.band--tint .asks li { background: var(--paper); }
.asks li::before {
  content: "?";
  flex: none;
  display: grid;
  place-items: center;
  width: 1.6rem;
  height: 1.6rem;
  border-radius: 50%;
  background: var(--blue);
  color: #fff;
  font-family: Nunito, ui-rounded, system-ui, sans-serif;
  font-size: 1rem;
  font-weight: 900;
}
.band__punch {
  color: var(--ink) !important;
  font-family: Nunito, ui-rounded, system-ui, sans-serif;
  font-weight: 800;
  letter-spacing: -.01em;
}

/* ── hero ───────────────────────────────────────────────────── */
.hero { padding-block: clamp(2.5rem, 5vw, 4.5rem) clamp(3rem, 6vw, 5rem); }

.hero__grid {
  display: grid;
  gap: clamp(2.5rem, 5vw, 4rem);
  align-items: center;
  grid-template-columns: 1fr;
}
/* `1fr` is minmax(auto, 1fr) — without this a wide child widens the whole page */
.hero__grid > * { min-width: 0; }
@media (min-width: 940px) {
  .hero__grid { grid-template-columns: minmax(0, 1fr) minmax(0, 34rem); }
}

.hero__copy { display: flex; flex-direction: column; align-items: flex-start; gap: 1.4rem; max-width: 100%; }

.hero__tag {
  display: inline-flex;
  align-items: center;
  gap: .55rem;
  padding: .35rem .9rem .35rem .45rem;
  border-radius: 999px;
  background: var(--surface);
  border: 1px solid var(--rule);
  font-family: Nunito, system-ui, sans-serif;
  font-size: .95rem;
  font-weight: 900;
  letter-spacing: -.01em;
  color: var(--ink);
}
.hero__tag .dot {
  width: 1.35rem; height: 1.35rem;
  border-radius: 999px;
  background: color-mix(in srgb, var(--gold) 22%, transparent);
  display: grid; place-items: center;
}

.hero h1 { font-size: var(--step-4); max-width: min(18ch, 100%); }
.hero h1 em { font-style: normal; color: var(--blue); }

.hero__lede {
  font-size: var(--step-1);
  line-height: 1.55;
  color: var(--muted);
  max-width: min(40ch, 100%);
  margin: 0 0 .9rem;
}
.hero__actions { display: flex; flex-wrap: wrap; gap: .75rem; align-items: center; }


/* ── the app screen (hero thesis) ─────────────────────────────
   A faithful redraw of the real map view: floating search + filter
   button, priced markers, the legend card with its cost cut, the
   recommended-parking bar and the feedback button. */
/* floating search row */
.app-locate {
  position: absolute;
  right: .6rem;
  top: 3rem;
  width: 2rem; height: 2rem;
  border-radius: 999px;
  background: #fff;
  display: grid; place-items: center;
  box-shadow: 0 2px 8px rgba(14, 28, 54, .22);
}
.app-filter {
  flex: none;
  width: 2rem; height: 2rem;
  border-radius: 999px;
  background: #fff;
  display: grid; place-items: center;
  box-shadow: 0 2px 8px rgba(14, 28, 54, .22);
}

/* floating cards along the bottom */
.app-bottom {
  position: absolute;
  inset: auto .6rem .6rem .6rem;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: .4rem;
}
.app-card {
  background: #fff;
  border-radius: 14px;
  box-shadow: 0 2px 10px rgba(14, 28, 54, .2);
  padding: .55rem .7rem;
  color: #0E1C36;
}
.app-rec { display: flex; flex-direction: column; gap: .12rem; max-width: 100%; }
.app-rec__head { display: flex; align-items: center; gap: .35rem; }
.app-rec__eyebrow {
  font-size: .56rem;
  font-weight: 800;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: #59698A;
}
.app-rec__name { font-family: Nunito, sans-serif; font-weight: 900; font-size: .88rem; }
.app-rec__walk {
  display: flex; align-items: center; gap: .3rem;
  font-size: .72rem; font-weight: 700; color: #2368E8;
  font-variant-numeric: tabular-nums;
}

.app-legend { width: calc(100% - 2.5rem); display: flex; flex-direction: column; gap: .3rem; }
.app-legend__cut {
  font-size: .66rem;
  font-weight: 800;
  text-align: center;
  color: #0E1C36;
  /* centred on the cost cut, 60% along a linear 0-30 kr/h scale */
  margin-inline-start: min(60%, calc(100% - 3.6rem));
  translate: -50% 0;
  width: max-content;
}
.app-scale {
  position: relative;
  height: 6px;
  /* Linear in kronor, matching the app: the stops are generated from the same
     bucket boundaries and colour ramp the app draws with, so the two can't
     drift. Grey from the cost cut onwards — 18 kr/h on a 0-30 axis is 60%. */
  background: linear-gradient(90deg,
    #10b981 0%, #1cc16a 5%, #43c746 10%, #43c746 15%, #84cc16 20%,
    #84cc16 25%, #cfad0f 30%, #cfad0f 35%, #f6900f 40%, #f6900f 45%,
    #f6900f 50%, #f97316 55%, #f97316 60%,
    /* hard stop at the cost cut — everything dearer is greyed, as in the app */
    #E2E8F0 60%, #E2E8F0 100%);
  border-radius: 3px;
  overflow: hidden;
}
.app-scale__cut {
  position: absolute;
  top: -2px; bottom: -2px;
  left: 60%;
  width: 2px;
  background: #0E1C36;
  border-radius: 1px;
}
.app-ticks {
  display: flex;
  justify-content: space-between;
  font-size: .58rem;
  font-weight: 700;
  color: #59698A;
  font-variant-numeric: tabular-nums;
}
/* Past the cut at 18 kr/h, so the 20 and 30+ labels read as excluded. */
.app-ticks span:nth-child(n+3) { color: #B4BFD0; }
.app-chips { display: flex; gap: .3rem; flex-wrap: wrap; margin-top: .1rem; }
.app-chip {
  display: inline-flex; align-items: center; gap: .25rem;
  padding: .18rem .42rem;
  border-radius: 999px;
  background: #EFF6FF;
  color: #1E3A8A;
  font-size: .62rem;
  font-weight: 700;
}
.app-chip[data-muted="true"] { background: #F1F5F9; color: #64748B; }

.star-badge {
  flex: none;
  width: 14px; height: 14px;
  border-radius: 999px;
  background: var(--gold);
  display: grid; place-items: center;
}

.app-fab {
  position: absolute;
  right: .55rem;
  bottom: .9rem;
  width: 2.1rem; height: 2.1rem;
  border-radius: 999px;
  background: #fff;
  display: grid; place-items: center;
  box-shadow: 0 2px 10px rgba(14, 28, 54, .22);
}

/* map animation — mirrors the app's own splash order */
.m-road, .m-block, .m-water { transform-origin: center; }
/* ── steps ──────────────────────────────────────────────────── */
.steps {
  display: grid;
  gap: 1px;
  background: var(--rule-soft);
  border: 1px solid var(--rule-soft);
  border-radius: 20px;
  overflow: hidden;
}
@media (min-width: 800px) { .steps { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1120px) { .steps { grid-template-columns: repeat(4, 1fr); } }

.step {
  background: var(--paper);
  padding: clamp(1.5rem, 3vw, 2.1rem);
  display: flex;
  flex-direction: column;
  gap: .7rem;
}
.band--tint .step { background: var(--surface); }

.step__n {
  font-family: "IBM Plex Mono", monospace;
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .1em;
  color: var(--blue);
}
.step h3 { font-size: var(--step-1); }
.step p { color: var(--muted); font-size: .95rem; line-height: 1.6; }

/* ── the walkthrough ────────────────────────────────────────── */
/* Inspelningen är en telefon i stående format, ramen inräknad, så den ska ta
   en telefons plats på sidan — inte en bandbredd som den liggande gjorde.
   Samma bredd som skärmbilderna bredvid, av samma skäl: en inspelning som är
   bredare än bilderna läser som en annan sorts innehåll. */
/* Bakgrunden ligger här, inte bara på bandet, för videons skull: under
   inslagsanimeringen har .reveal en transform och en opacitet under ett, och
   då blandas videon mot den här rutan i stället för mot sidan. Utan den
   blinkar det vita till innan animeringen är klar. */
.walkthrough {
  margin: clamp(2rem, 4vw, 3rem) auto 0;
  max-width: min(21rem, 100%);
  background: var(--paper);
}
.walkthrough video {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 720 / 1470;
  /* Inspelningen har telefonen mot vitt, och mp4 bär ingen alfakanal, så
     vitheten sitter i bildpunkterna och går inte att ta bort med enbart en
     bakgrund. Multiplikation löser det: vitt gånger sidans färg är sidans
     färg, så rektangeln kring telefonen försvinner helt.

     Priset är att allt i videon multipliceras med #F2F5FA, alltså mörknar
     knappt fem procent i rödkanalen. Det syns inte på telefonens ram, men
     appens vita ytor blir en aning ljusgrå jämfört med skärmbilderna bredvid.
     Den rena lösningen är att exportera om videon beskuren till telefonen. */
  mix-blend-mode: multiply;
}
.walkthrough figcaption {
  margin: .8rem 0 0;
  font-size: .85rem;
  color: var(--faint);
  text-align: center;
}

/* ── filters ────────────────────────────────────────────────── */
.split {
  display: grid;
  gap: clamp(2rem, 4vw, 3.5rem);
  align-items: start;
  grid-template-columns: 1fr;
}
.split > * { min-width: 0; }
@media (min-width: 940px) { .split { grid-template-columns: minmax(0, 1fr) minmax(0, 24rem); } }
@media (min-width: 940px) { .split--shot { grid-template-columns: minmax(0, 1fr) minmax(0, 32rem); align-items: center; } }

.shot { margin: 0; max-width: min(20rem, 100%); margin-inline: auto; }
.shot--hero { max-width: min(22rem, 100%); }

/* Annotations that stand outside the screenshot and reach into it.

   Written on the frame rather than on the picture: a label lying on the map
   competes with the map, and the whole point of the map is that it is legible.
   Out in the margin the words are just words, and the arrow does the pointing.

   The figure carries a band of space above and below for them to live in, and
   the arrow's length is set per annotation so it starts in that band and ends
   on whatever it means. */
.shot__frame { position: relative; }
.shot--noted { padding-block: 4.2rem; }

/* --top and --h are percentages of the frame, which has a definite height
   from the image, so they resolve where a percentage on an auto-height parent
   would not. --top is where the annotation begins — negative puts it above the
   picture — and --h how far it reaches, so the tip lands at --top + --h. */
.mark {
  position: absolute;
  left: var(--x);
  top: var(--top);
  height: var(--h);
  /* Bredden måste komma från texten, inte från platsen. Ett absolutpositionerat
     element med bara `left` krymps till utrymmet mellan `left` och högerkanten
     — men translate flyttar sedan tillbaka halva bredden, så etiketten står
     inte där layouten trodde. Ju längre högerut målet ligger, desto smalare
     blev rutan: vid 70 % räckte den inte till "Rutis förslag", som bröts till
     två rader och lade den andra över telefonens överkant. max-content gör
     bredden oberoende av --x; radbrytningen styrs av max-width på texten. */
  width: max-content;
  max-width: 100%;
  translate: -50% 0;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  color: var(--ink);
  pointer-events: none;
}
.mark[data-dir="up"] { flex-direction: column-reverse; }

.mark__text {
  font-family: Caveat, "Segoe Script", cursive;
  font-weight: 700;
  font-size: 1.65rem;
  line-height: 1.1;
  text-align: center;
  /* Wraps rather than running past the picture's edge, which nowrap did on a
     narrow screen. The measure keeps it to two lines at most. */
  max-width: 12rem;
  text-wrap: balance;
  rotate: -3deg;
}

/* The shaft takes whatever height is left and stretches to fill it; the head
   is drawn at a fixed size so it stays an arrowhead however far it travels. */
.mark__arrow { position: relative; flex: 1; width: var(--aw, 2.4rem); min-height: 1.5rem; }
.mark__shaft { position: absolute; inset: 0; width: 100%; height: 100%; }
.mark[data-dir="up"] .mark__arrow { scale: 1 -1; }

/* From tablet width up there is room beside the picture, and a label in the
   margin beats one hanging over it: the arrow crosses the edge instead of the
   whole map. Which side alternates, so three figures down the page don't read
   as a column of repeated furniture.

   --frame-w is the picture's own width, read by both the frame and the label
   so they can't disagree about where the edge is. --ty is the target's height,
   used only here; --top and --h drive the narrow-screen layout and are ignored.

   The columns holding these widen to match, or the picture would shrink to
   make room for the margin instead of the margin being added beside it. */
/* 940, not 700: this is where .split goes two-column and the figure gets a
   column of its own to hang a label beside. Between 700 and 940 the figure is
   centred and full-width, and the label reached past the viewport — at 721 it
   pushed the page to 755. Below this the annotation sits above the picture,
   which cannot overflow. */
@media (min-width: 940px) {
  .shot--noted { --frame-w: 20rem; max-width: min(calc(var(--frame-w) + 11.5rem), 100%); padding-block: 1.6rem; }
  .shot--hero.shot--noted { --frame-w: 22rem; }
  .shot--noted .shot__frame { width: min(var(--frame-w), 100%); }
  .shot--side-right .shot__frame { margin-right: auto; }
  .shot--side-left  .shot__frame { margin-left: auto; }

  /* The shaft has to reach the marker, not stop at the edge of the picture.
     --tx is how far across the frame the target sits, as a fraction, and the
     arrow is sized from it: the label sits out in the margin and the line runs
     from there in to the thing it means. The side chosen for each figure is
     whichever edge its target is nearer, so no arrow crosses more of the map
     than it has to.

     Watch out when retuning these: --tx and --ty are NOT where the arrow
     points. The shaft ends at (38, 97) of a 40x100 viewBox, not at the corner,
     and the box is centred on --ty — so the tip lands (0.97 - 0.5) x 2.2rem =
     1.03rem BELOW --ty, and a twentieth of the arrow's own length to the side.
     Both values are therefore set slightly short of the real target. That
     unnoticed rem is why the arrows read as pointing just under the thing they
     mean; set --ty to the target itself and they will do it again. */
  .mark {
    top: var(--ty);
    height: auto;
    /* Här ska etiketten tvärtom sticka ut utanför bilden, ut i marginalen, så
       ingen av begränsningarna ovan gäller. */
    width: auto;
    max-width: none;
    translate: 0 -50%;
    flex-direction: row;
    align-items: center;
    gap: .1rem;
  }
  .shot--side-right .mark {
    left: calc(var(--frame-w) * var(--tx));
    right: auto;
    flex-direction: row-reverse;
  }
  .shot--side-left .mark {
    right: calc(var(--frame-w) * (1 - var(--tx)));
    left: auto;
  }

  /* The same curve squashed wide. Mirrored where the label is on the right, so
     the head lands at the near end — it is pinned to the path and follows. */
  .mark__arrow { height: 2.2rem; flex: none; }
  .shot--side-right .mark__arrow {
    width: calc(var(--frame-w) * (1 - var(--tx)) + .4rem);
    scale: -1 1;
  }
  .shot--side-left .mark__arrow {
    width: calc(var(--frame-w) * var(--tx) + .4rem);
  }
  .mark__text { max-width: 9rem; font-size: 1.6rem; }
}

/* Ingen mindre padding på små skärmar längre: etiketten står ovanför bilden
   och behöver hela bandet på 4.2rem för att rymmas nu när texten är större.
   Den längsta, "varje markör visar priset", bryts till två rader och tar
   3.9rem — den satt utanför en padding på 3.6rem. */
@media (max-width: 520px) {
  .mark__text { font-size: 1.4rem; }
}

/* Skärmbilderna är ramade mockuper: telefonen, dess kant och dess rundade
   hörn ligger i själva bilden. Därför ingen kantlinje, ingen rundning och
   ingen skugga här — de skulle ritas kring bildens rektangel, inte kring
   telefonen, och alltså lägga en ruta runt en rundad sak.

   Ytan utanför telefonen är redan lagd mot sektionens bakgrundsfärg när
   bilden byggdes (se tools/prep-skarmbild.mjs), vilket är vad som gör att de
   kan vara JPEG i stället för PNG. Flyttas en figur till ett band med annan
   bakgrund måste bilden byggas om med den färgen. */
.shot img {
  display: block;
  width: 100%;
  height: auto;
}
.shot .shot__note {
  text-align: left;
  margin-top: 1.2rem;
  display: flex;
  flex-direction: column;
  gap: .55rem;
  font-size: inherit;
}
.shot__note h3 { font-size: var(--step-0); letter-spacing: -.01em; color: var(--blue); }
.shot__note p { font-size: .9rem; line-height: 1.55; color: var(--muted); margin: 0; }
.shot__note-punch { color: var(--ink) !important; font-weight: 700; }

.shot figcaption {
  margin: .85rem .4rem 0;
  font-size: .85rem;
  color: var(--faint);
  text-align: center;
}



/* ── price legend ───────────────────────────────────────────── */
.legend { display: flex; flex-direction: column; gap: 1.1rem; }
.legend__bar {
  height: 14px;
  border-radius: 999px;
  /* Samma ramp som appen ritar med — jämn övergång, inga block. */
  background: linear-gradient(90deg,
    #10b981 0%, #22c55e 16.7%, #84cc16 33.3%, #f59e0b 50%,
    #f97316 66.7%, #ef4444 83.3%, #b91c1c 100%);
}
.legend__ends {
  display: flex;
  justify-content: space-between;
  font-size: .85rem;
  color: var(--muted);
  margin-top: -.5rem;
}
.legend__key .mono { color: var(--muted); }

/* ── data / facts ───────────────────────────────────────────── */
.facts {
  display: grid;
  gap: 1px;
  background: var(--rule-soft);
  border: 1px solid var(--rule-soft);
  border-radius: 20px;
  overflow: hidden;
  grid-template-columns: 1fr;
}

/* Tre kort går inte jämnt upp på två kolumner, och auto-fit valde två mellan
   457 och 680 px fönsterbredd. Hålet syntes dessutom extra tydligt, eftersom
   gapet här är bakgrunden som lyser igenom: den fjärde platsen blev en grå
   fyrkant snarare än tomrum.
   Så: en kolumn tills två får plats, då lägger sig det sista kortet över hela
   bredden, och först när tre ryms delas de lika. Gränserna är satta där
   kolumnerna faktiskt byter, inte på jämna tal.
   Villkoret gäller udda antal kort, så det håller även om raden växer. */
@media (min-width: 29rem) {
  .facts { grid-template-columns: repeat(2, 1fr); }
  .fact:last-child:nth-child(odd) { grid-column: 1 / -1; }
}
@media (min-width: 43rem) {
  .facts { grid-template-columns: repeat(3, 1fr); }
  .fact:last-child:nth-child(odd) { grid-column: auto; }
}
.fact {
  background: var(--paper);
  padding: 1.4rem clamp(1.2rem, 2.5vw, 1.6rem);
  display: flex; flex-direction: column; gap: .3rem;
}
.band--tint .fact { background: var(--surface); }

/* .compare och .need har egen fyllning i stället för att följa bandet. När
   sektionerna bytte ton hamnade de vitt-på-vitt, så de får paper här. */
.band--tint .compare {
  max-width: 26rem; background: var(--paper); }
.fact__k {
  font-family: Nunito, system-ui, sans-serif;
  font-weight: 900;
  font-size: var(--step-2);
  letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
}
.fact__v { font-size: .88rem; color: var(--muted); line-height: 1.5; }
/* A clarification, not a claim, so it sits under the grid rather than as a
   fourth card competing with three things Rutis actually does. */
.facts__aside {
  margin: 1.1rem 0 0;
  font-size: .85rem;
  color: var(--faint);
  max-width: min(52ch, 100%);
}




/* ── footer ─────────────────────────────────────────────────── */
.foot {
  border-top: 1px solid var(--rule-soft);
  padding-block: 2.5rem 3rem;
}
.foot__row {
  display: flex; flex-wrap: wrap; gap: 1.5rem;
  align-items: center; justify-content: space-between;
}
.foot__tag {
  font-family: Nunito, ui-rounded, system-ui, sans-serif;
  font-weight: 800;
  font-size: .92rem;
  color: var(--muted);
  letter-spacing: -.01em;
}
.foot__links { display: flex; flex-wrap: wrap; gap: 1.4rem; font-size: var(--step--1); }
.foot__links a { color: var(--muted); text-decoration: none; font-weight: 600; }
.foot__links a:hover { color: var(--blue); }
.foot__fine { font-size: .8rem; color: var(--faint); margin-top: 1.75rem; max-width: min(60ch, 100%); line-height: 1.6; }

/* ── scroll reveal ──────────────────────────────────────────────
   Hidden only once the inline script has flagged that JS runs, so a
   failed or blocked script leaves the page fully readable instead of blank. */
.js .reveal { opacity: 0; transform: translateY(14px); transition: opacity .6s ease, transform .6s ease; }
.js .reveal.is-in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .js .reveal { opacity: 1; transform: none; transition: none; }
}

/* ── hero microcopy ─────────────────────────────────────────── */
.hero__micro {
  font-size: .88rem;
  color: var(--faint);
  margin: .9rem 0 0;
  display: flex;
  align-items: center;
  gap: .5rem;
  flex-wrap: wrap;
}
.hero__micro b { color: var(--muted); font-weight: 600; }

/* Kvar från before/after-rutorna: Rekommendationen använder raden. */
.ba__last { margin: .9rem 0 0; font-weight: 700; color: var(--ink); }

/* Sits under the download button rather than in the footer: someone on an
   Android phone is looking at the button when the question occurs to them. */
.close__note {
  margin: .9rem 0 0;
  color: var(--faint);
  font-size: var(--step--1);
}

/* ── price line: the aha, spelled out ───────────────────────── */
.priceline {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  margin: 1.4rem 0 0;
}
.priceline span {
  font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace;
  font-weight: 600;
  font-size: .95rem;
  color: #fff;
  padding: .34rem .7rem;
  border-radius: 999px;
  white-space: nowrap;
}

/* ── the comparison Rutis makes for you ─────────────────────── */
.compare {
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 18px;
  padding: 1.2rem;
  box-shadow: var(--shadow-sm);
}
.band__head + .compare { margin-top: clamp(1.5rem, 3vw, 2rem); }
.compare__dest {
  display: flex;
  align-items: center;
  gap: .5rem;
  font-weight: 700;
  color: var(--ink);
  padding: 0 .4rem .9rem;
  border-bottom: 1px solid var(--rule-soft);
  margin-bottom: .5rem;
}
.opt {
  display: grid;
  grid-template-columns: 1fr auto auto;
  align-items: center;
  gap: .8rem;
  padding: .72rem .8rem;
  border-radius: 12px;
}
.opt--best {
  background: color-mix(in srgb, var(--gold) 16%, var(--surface));
  outline: 1.5px solid color-mix(in srgb, var(--gold) 55%, transparent);
}
.opt__name { font-weight: 600; color: var(--muted); font-size: .95rem; }
.opt--best .opt__name { color: var(--ink); font-weight: 800; }
.opt__price, .opt__walk {
  font-family: "IBM Plex Mono", ui-monospace, Menlo, monospace;
  font-size: .92rem;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
  white-space: nowrap;
}
.opt--best .opt__price, .opt--best .opt__walk { color: var(--ink); font-weight: 600; }
.compare__note {
  margin: .9rem .4rem 0;
  font-size: .86rem;
  color: var(--faint);
}

/* ── what matters to you ────────────────────────────────────── */
/* ── free parking, given room ───────────────────────────────── */
.free {
  background: linear-gradient(135deg,
    color-mix(in srgb, var(--p0) 16%, var(--surface)),
    var(--surface) 70%);
  border: 1px solid color-mix(in srgb, var(--p0) 38%, var(--rule));
  border-radius: 20px;
  padding: clamp(1.6rem, 4vw, 2.6rem);
}
.free p { max-width: 56ch; }
.free__punch {
  font-family: Nunito, ui-rounded, system-ui, sans-serif;
  font-weight: 900;
  font-size: clamp(1.25rem, 3.4vw, 1.85rem);
  line-height: 1.25;
  color: var(--p0);
  letter-spacing: -.015em;
  margin-top: 1.5rem;
  max-width: 24ch;
}

/* ── the founder, in her own voice ──────────────────────────── */
.founder {
  max-width: min(38rem, 100%);
  display: flex;
  flex-direction: column;
  gap: .9rem;
}
.founder h2 { font-size: var(--step-3); max-width: 20ch; margin-bottom: .4rem; }
/* :where() så styckenas grundvärden inte bär någon egen vikt.
   Skriven som ".founder p" är regeln (0,0,1,1) — en klass plus ett element —
   och slår därmed varje modifierarklass som sätts på samma stycken, som bara
   är (0,0,1,0). Det var därför de två nedan tvingades ropa med !important för
   att höras. Med :where() räknas bara .founder, så alla tre väger lika och
   det är källordningen som avgör: modifierarna står senare och vinner.
   Matchningen är oförändrad, bara vikten. */
.founder :where(p) { color: var(--muted); font-size: var(--step-0); line-height: 1.65; margin: 0; }
.founder__think { font-style: italic; color: var(--ink); }
.founder__punch {
  color: var(--ink);
  font-family: Nunito, ui-rounded, system-ui, sans-serif;
  font-weight: 800;
  font-size: var(--step-1);
  letter-spacing: -.01em;
}
/* The same hand that draws the arrows on the screenshots signs off here. */
.founder__sign {
  font-family: Caveat, "Segoe Script", cursive;
  font-weight: 700;
  font-size: 1.5rem;
  color: var(--ink);
  line-height: 1;
  margin-top: .6rem;
  rotate: -3deg;
  transform-origin: left center;
}

/* ── closing call ───────────────────────────────────────────── */
.close {
  text-align: center;
  padding: clamp(2rem, 6vw, 3.4rem) 1.2rem;
}
.close h2 { max-width: 22ch; margin-inline: auto; }
.close__tag {
  font-family: Nunito, ui-rounded, system-ui, sans-serif;
  font-weight: 900;
  font-size: clamp(1.15rem, 3vw, 1.5rem);
  color: var(--blue);
  letter-spacing: -.015em;
  margin: 1.4rem auto .4rem;
}
.close p { max-width: 46ch; margin-inline: auto; }
