/* Flightrax — modern glass UI over a full-screen dark map. */

:root {
  /* C1 type scale — seven sizes, three weights (650/700/800; .strike's
     400 is the ONE exception: struck old time stays light against the bold
     new one, that contrast IS the Flighty pattern). Display sizes
     (36/46/58/72) live outside the scale on purpose. */
  --fs-micro: 10px;
  --fs-caption: 11.5px;
  --fs-body: 13px;
  --fs-lead: 15px;
  --fs-title: 17px;
  --fs-big: 21px;
  --fs-hero: 26px;
  /* SVART, ikke marineblatt. Brage 7/8: «Jeg liker det svarte, for det ser
     ut som del av 2 ulike apper» — den native detaljflaten sto pa Apples
     systemGroupedBackground (ren #000) mens webflatene sto pa Flightrax-
     navy, og da er det to apper. Verdiene under ER Apples dark mode, ikke
     en tilnaerming: bakgrunn #000, gruppeplate #1C1C1E, nestet #2C2C2E,
     etiketter nøytralt hvite (235,235,245) — ikke blatonet. */
  --bg: #000000;
  /* RGB triplets back every tinted rgba() below — a theme block re-tints the
     whole UI by overriding tokens alone, no per-component rules. */
  --bg-rgb: 0, 0, 0;
  /* Arket over kartet: samme svarte familie, fortsatt gjennomskinnelig sa
     verden under leses. */
  --glass: rgba(12, 12, 14, 0.78);
  --glass-strong: rgba(12, 12, 14, 0.94);
  --line: rgba(var(--muted-rgb), 0.16);
  /* Den lyse alfa-KANTEN (border/inset-rim) på kort, chips og felt. Egen
     token fordi hvit-alfa forsvinner på en hvit plate: lyst tema snur den
     til mørk-alfa. Default = nøyaktig den gamle verdien (mørk uendret). */
  --rim: rgba(255, 255, 255, .14);
  /* Gruppeplaten er en EGEN token, ikke en hvit-alfa pa flaks: den ma
     treffe #1C1C1E over svart, og temaene skal kunne tone den. */
  --plate: #1c1c1e;
  --plate-2: #2c2c2e;
  --fg: #ffffff;
  --muted: #8e8e93;
  --muted-rgb: 235, 235, 245;
  --accent: #5b9dff;
  --accent-rgb: 91, 157, 255;
  /* Logo gradient's second stop: COLD cyan, never violet — «mindre lilla»
     (Brage 6/8). Pro themes keep their own duos. */
  --accent2: #46d4ff;
  --good: #4ade80;
  --warn: #fbbf24;
  --bad: #f87171;
  /* Two trackings only: caps for inline uppercase data, kicker for section
     labels. Every uppercase rule uses one of them — no ad-hoc px values. */
  --track-caps: .08em;
  --track-kicker: .14em;
  /* One radius scale. Floating surfaces --r-lg, cards/inputs/rows --r-md,
     small nested things --r-sm; chips/pills/dots are 999px/50%. Nested
     surfaces step DOWN (card in sheet: sheet lg, card md) — same radius
     inside same radius looks stamped, not built. */
  --r-lg: 22px;
  --r-md: 14px;
  --r-sm: 10px;
  --sat: env(safe-area-inset-top, 0px);
  --sab: env(safe-area-inset-bottom, 0px);
  /* Bevegelse (design-uplift 5/9): ÉN utgangskurve og ÉN fjær for hele
     flaten, så faneskift, toast og innlogging deler samme håndlag. */
  --ease-out: cubic-bezier(.2, .8, .2, 1);
  --ease-spring: cubic-bezier(.32, 1.5, .48, 1);
  /* ---- Liquid-glass material ---- */
  --glass-blur: blur(30px) saturate(1.7) brightness(1.02);
  --glass-blur-strong: blur(34px) saturate(1.9) brightness(1.03);
  /* The layered EDGE is what reads as a real glass slab instead of a flat
     frosted panel: a bright specular top rim, faint side rims that catch
     light, a dark bottom that gives the glass thickness, and a soft inner
     body glow. Compose it with an outer float shadow per-surface. */
  --glass-edge:
    inset 0 1.5px 0 rgba(255, 255, 255, .55),
    inset 1px 0 0 rgba(255, 255, 255, .10),
    inset -1px 0 0 rgba(255, 255, 255, .06),
    inset 0 -1.5px 1px rgba(0, 0, 0, .38),
    inset 0 0 26px rgba(255, 255, 255, .045);
}

/* ---------- Pro themes ----------
   Token-only skins: each block swaps the palette, nothing else. Status
   colours (--good/--warn/--bad) keep their meaning in every theme — only
   stealth's chrome goes mono, never its semantics. Kartet tones per tema i
   app.js (tintBasemap) — på basiskartets EGNE lag, aldri på lerretet, for
   lerretet inneholder også flyene og liveryen deres er betydning. */

/* Nordlys — aurora over dark fjord: blue-green base, cold green-cyan accent.
   --good stays the CTA green; the accent is clearly cyan so they never blur. */
:root[data-theme="nordlys"] {
  --bg: #000000;
  --bg-rgb: 0, 0, 0;
  --glass: rgba(9, 20, 17, 0.76);
  --glass-strong: rgba(9, 20, 17, 0.93);
  --plate: #131e1a;
  --plate-2: #1e2c27;
  --fg: #f2fbf7;
  --muted: #8ba79b;
  --muted-rgb: 200, 232, 220;
  --accent: #3ddcc0;
  --accent-rgb: 61, 220, 192;
  --accent2: #9c7bff; /* logo runs teal → violet */
}


/* Midnattssol — warm arctic night: deep plum base, low-sun gold accent,
   rose-tinted hairlines (--line derives from the warmed --muted-rgb). */
:root[data-theme="midnattssol"] {
  --bg: #000000;
  --bg-rgb: 0, 0, 0;
  --glass: rgba(24, 14, 20, 0.76);
  --glass-strong: rgba(24, 14, 20, 0.93);
  --line: rgba(232, 173, 160, 0.18);
  --plate: #1f1519;
  --plate-2: #2e2226;
  --fg: #fdf6f0;
  --muted: #a79088;
  --muted-rgb: 240, 214, 205;
  --accent: #f0b35c;
  --accent-rgb: 240, 179, 92;
  --accent2: #ee7d96; /* logo runs gold → rose */
}


/* Stealth — graphite mono: near-black neutral base, cool steel accent.
   Chrome is desaturated on purpose; the status colours stay saturated so
   green/amber/red still carry meaning against the grayscale. */
:root[data-theme="stealth"] {
  --bg: #000000;
  --bg-rgb: 0, 0, 0;
  --glass: rgba(14, 15, 17, 0.76);
  --glass-strong: rgba(14, 15, 17, 0.93);
  --line: rgba(190, 198, 210, 0.14);
  /* MÅLT 7/8: #1a1b1d lå ΔE 0,8 fra Flightrax-platen — under terskelen
     for hva øyet i det hele tatt kan se. Stealth er et PRO-tema; det kan
     ikke være pikselidentisk med gratistemaet på hver eneste flate.
     #141416 gir ΔE 4,0 og er dessuten riktig i innhold: stealth SKAL
     ligge lavere, nærmere svart, med mindre kontrast — den er dempet, ikke
     bare avfarget. Flightrax-platen står urørt på Apples #1C1C1E, for den
     native SwiftUI-lista bruker nøyaktig den verdien. */
  --plate: #141416;
  --plate-2: #232326;
  --fg: #f4f5f7;
  --muted: #8d949e;
  --muted-rgb: 216, 220, 226;
  --accent: #9aa7b8;
  --accent-rgb: 154, 167, 184;
  --accent2: #67717e; /* logo runs steel → gunmetal */
}

/* Lys — Apples light mode. GRATIS (tilgjengelighet, Brage 1/9): for de som
   ikke liker mørk modus — ikke et Pro-skin. Her snus HELE polariteten, så
   blokka overstyrer MER enn Pro-temaene: bakgrunn, tekst, plate — OG
   --muted-rgb (hårstrekene --line må bli MØRKE på lyst; med den lyse
   tripletten var de hvite-på-hvitt), --rim (de hvite alfa-kantene som
   forsvinner på hvitt) og --glass-edge (den mørke er bygget for mørkt glass:
   spekulær hvit topp + mørk bunn ser feil ut på lys plate). Kartet bytter
   til en LYS basisstil i app.js (pickStyle, ved last) — toning gjør ikke et
   mørkt kart lyst. Statusfargene beholder betydningen, justert for kontrast
   på hvitt (Apples light-verdier). */
:root[data-theme="lys"] {
  --bg: #f2f2f7;
  --bg-rgb: 242, 242, 247;
  --glass: rgba(255, 255, 255, 0.72);
  --glass-strong: rgba(255, 255, 255, 0.92);
  --plate: #ffffff;
  --plate-2: #f2f2f7;
  --fg: #1c1c1e;
  --muted: #6c6c70;
  --muted-rgb: 60, 60, 67;
  --line: rgba(60, 60, 67, 0.18);
  --rim: rgba(0, 0, 0, 0.10);
  --accent: #0a84ff;
  --accent-rgb: 10, 132, 255;
  --accent2: #32ade6;
  --good: #34c759;
  --warn: #ff9500;
  --bad: #ff3b30;
  --glass-edge:
    inset 0 1.5px 0 rgba(255, 255, 255, .70),
    inset 1px 0 0 rgba(0, 0, 0, .04),
    inset -1px 0 0 rgba(0, 0, 0, .04),
    inset 0 -1.5px 1px rgba(0, 0, 0, .08),
    inset 0 0 26px rgba(0, 0, 0, .015);
}
/* De to flatene som bar en HARDKODET mørk marineblå gradient (ikke token):
   dokken og foto-plassholderen. På lyst ble de mørke klatter. */
:root[data-theme="lys"] nav.tabs {
  background: linear-gradient(180deg, rgba(255, 255, 255, .78), rgba(242, 242, 247, .86));
}
:root[data-theme="lys"] .pop-photo.placeholder {
  background: linear-gradient(155deg, rgba(226, 232, 244, .9), rgba(206, 214, 230, .95));
}


/* A slow specular highlight that drifts across the glass — the "living glass"
   motion, distinct from the tap ripple. Applied to floating chrome via ::before. */
@keyframes glass-sheen {
  0%, 100% { background-position: 130% 0; }
  50%      { background-position: -30% 0; }
}
.glass-sheen::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  border-radius: inherit; z-index: 0; mix-blend-mode: screen;
  background: linear-gradient(115deg,
    transparent 22%, rgba(255, 255, 255, .09) 42%, rgba(255, 255, 255, .17) 50%,
    rgba(255, 255, 255, .09) 58%, transparent 78%);
  background-size: 260% 100%;
  animation: glass-sheen 7.5s ease-in-out infinite;
}
/* A specular highlight that slowly ORBITS the glass rim — the signature
   "living glass" edge, light travelling around the bezel. Masked to a hairline
   ring so only the border lights up. */
@property --rim-a { syntax: "<angle>"; initial-value: 0deg; inherits: false; }
@keyframes rim-orbit { to { --rim-a: 360deg; } }
/* Guarded: without mask-composite the ring can't be cut out, so skip the rim
   entirely rather than washing the panel with a conic fill. */
@supports ((-webkit-mask-composite: xor) or (mask-composite: exclude)) {
  .glass-sheen::after {
    content: ""; position: absolute; inset: 0; pointer-events: none;
    border-radius: inherit; z-index: 2; padding: 1.5px;
    background: conic-gradient(from var(--rim-a),
      transparent 0%, rgba(255, 255, 255, .55) 10%, transparent 26%,
      transparent 54%, rgba(150, 190, 255, .45) 68%, transparent 86%, transparent 100%);
    -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    -webkit-mask-composite: xor; mask-composite: exclude;
    animation: rim-orbit 7s linear infinite;
  }
}
@media (prefers-reduced-motion: reduce) {
  .glass-sheen::before, .glass-sheen::after { animation: none; }
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
html, body { height: 100%; }
body {
  margin: 0;
  font: 14px/1.45 -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Inter, Roboto, sans-serif;
  background: var(--bg);
  color: var(--fg);
  overflow: hidden;
  overscroll-behavior: none;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* Type scale — the whole UI hangs on these six steps.
   Numbers are the heroes (big + heavy + tight); labels step far down
   (small + wide caps + muted) so hierarchy reads at a glance. */
.t-display { font-size: 46px; font-weight: 800; letter-spacing: -.03em; line-height: 1; font-variant-numeric: tabular-nums; }
.t-big     { font-size: var(--fs-hero); font-weight: 800; letter-spacing: -.02em; line-height: 1.1; font-variant-numeric: tabular-nums; }
.t-label   { font-size: var(--fs-micro); font-weight: 650; letter-spacing: var(--track-kicker); text-transform: uppercase; color: var(--muted); }

#map { position: fixed; inset: 0; background: var(--bg); }
#map canvas { outline: none; }

.hidden { display: none !important; }
.muted { color: var(--muted); }

/* ---------- header ---------- */
header.appbar {
  position: fixed; top: 0; left: 0; right: 0; z-index: 30;
  padding: calc(10px + var(--sat)) 16px 10px;
  display: flex; align-items: center; gap: 10px;
  background: linear-gradient(to bottom, rgba(var(--bg-rgb),.85), rgba(var(--bg-rgb),0));
  pointer-events: none;
}
header.appbar > * { pointer-events: auto; }
.logo {
  font-weight: 800; font-size: var(--fs-title); letter-spacing: 1.5px;
  background: linear-gradient(120deg, var(--accent), var(--accent2), var(--accent));
  background-size: 220% 100%;
  -webkit-background-clip: text; background-clip: text; color: transparent;
  display: flex; align-items: center; gap: 7px;
  animation: logo-flow 1.8s ease-out 1;
}
@keyframes logo-flow { 0%, 100% { background-position: 0% 0; } 50% { background-position: 100% 0; } }
/* The wordmark's glyph is the sprite plane — sized in em so the login card's
   bigger logo scales it along. Explicit colour: the wordmark's own text
   colour is transparent (gradient clip), which a stroke must not inherit. */
.logo .glyph { display: flex; color: var(--accent); }
.logo .glyph svg { width: 1.05em; height: 1.05em; }
/* Connection state is a quiet dot, not a counter — the flight/radar counts
   it used to print were internals (board-row totals) that read as noise. */
#connState {
  width: 8px; height: 8px; border-radius: 50%; flex: none;
  background: var(--muted); opacity: .5;
}
#connState.live {
  background: var(--good); opacity: 1;
  animation: live-pulse 2.4s ease-in-out infinite;
}
@keyframes live-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(74, 222, 128, .0); }
  50%      { box-shadow: 0 0 0 4px rgba(74, 222, 128, .12), 0 0 12px rgba(74, 222, 128, .18); }
}
@media (prefers-reduced-motion: reduce) {
  .logo, #connState.live { animation: none; }
}
.spacer { flex: 1; }
.chip {
  font: inherit; font-size: var(--fs-caption); font-weight: 700; padding: 3px 10px;
  border-radius: 999px; border: 1px solid var(--rim); color: var(--muted);
  background: var(--glass); backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .28), 0 4px 14px rgba(0, 0, 0, .28);
  cursor: pointer; transition: all .15s;
}
.chip svg { width: 15px; height: 15px; display: block; margin: 2px 0; }
.chip.active { color: var(--accent); border-color: rgba(var(--accent-rgb),.45); }
/* The pill is 24 px tall because that is the size it should LOOK; a thumb
   needs 44. An invisible strip centred on it takes the taps without moving
   a pixel of the design. Vertical only, so neighbours are never covered. */
.chip { position: relative; }
.chip::after {
  content: ""; position: absolute; left: 0; right: 0; top: 50%;
  height: 44px; transform: translateY(-50%);
}

/* ---------- panel / sheet ---------- */
.sheet {
  position: fixed; z-index: 20; left: 0; right: 0; bottom: 0;
  height: 46vh; display: flex; flex-direction: column;
  background: var(--glass);
  backdrop-filter: var(--glass-blur-strong);
  -webkit-backdrop-filter: var(--glass-blur-strong);
  border-top: 1px solid var(--rim);
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  transition: height .28s cubic-bezier(.4, 0, .2, 1);
  box-shadow: 0 -20px 60px rgba(0, 0, 0, .5), var(--glass-edge);
}
/* A crisp specular arc along the sheet's rounded top lip — light pooling on
   the leading glass edge. Kept to the very top so it never washes out the
   dense content below. */
.sheet::before {
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: 46px;
  border-radius: var(--r-lg) var(--r-lg) 0 0; pointer-events: none;
  background: linear-gradient(180deg, rgba(255, 255, 255, .12), rgba(255, 255, 255, 0));
}
/* three-mode switch where the drag handle used to live */
.sheet-modes { display: flex; gap: 8px; justify-content: center; margin: 7px auto 4px; flex: none; }
.sheet-modes button {
  position: relative;
  display: flex; align-items: flex-end; justify-content: center;
  width: 44px; height: 26px; padding: 0 0 5px;
  background: rgba(var(--muted-rgb),.10); border: 1px solid var(--line);
  border-radius: 999px; cursor: pointer; color: rgba(var(--muted-rgb),.55);
}
/* Same invisible thumb-sized strip as the header chips (see .chip::after). */
.sheet-modes button::after {
  content: ""; position: absolute; left: 0; right: 0; top: 50%;
  height: 44px; transform: translateY(-50%);
}
.sheet-modes button i { display: block; width: 16px; background: currentColor; border-radius: 2px; }
.sheet-modes button:nth-child(1) i { height: 3px; }
.sheet-modes button:nth-child(2) i { height: 8px; }
.sheet-modes button:nth-child(3) i { height: 13px; }
.sheet-modes button.active {
  color: var(--accent); background: rgba(var(--accent-rgb),.16); border-color: rgba(var(--accent-rgb),.45);
}
/* Selected-plane info: its own glass block floating ABOVE the sheet, with a
   gap between them, equal insets left and right (its bottom is anchored to
   the sheet's current top by JS). */
.plane-panel.squeezed { opacity: 0; pointer-events: none; }
.plane-panel {
  /* 16px side inset — the same rail as the dock and the sheet's content, so
     the floating chrome and the cards below sit on ONE line. */
  position: fixed; z-index: 21; left: 16px; right: 16px;
  display: flex; align-items: center; gap: 9px;
  padding: 8px 30px 8px 12px;
  /* Panelet GLIR med rullegardinens snap (samme kurve som sheeten);
     under selve draget nulles transition inline saa det foelger haanden. */
  transition: bottom .28s cubic-bezier(.4, 0, .2, 1), opacity .2s ease;
  background: var(--glass-strong);
  backdrop-filter: var(--glass-blur-strong);
  -webkit-backdrop-filter: var(--glass-blur-strong);
  border: 1px solid var(--rim); border-radius: var(--r-lg);
  overflow: hidden;
  box-shadow: 0 16px 40px rgba(0, 0, 0, .48), var(--glass-edge);
  animation: panel-in .26s cubic-bezier(.34, 1.4, .5, 1);
  /* Same curve/duration as the sheet's height transition, so the panel rides
     up and down attached to the sheet's top when you switch min/medium/full. */
  transition: bottom .28s cubic-bezier(.4, 0, .2, 1), opacity .2s ease;
}
@keyframes panel-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
/* The panel's close is the same .icon-x component as every other close —
   this class only pins it to the corner. */
.icon-x.plane-panel-x { position: absolute; top: 7px; right: 7px; margin: 0; }
/* Content rides above the drifting sheen (::before, z:0). */
.plane-panel-body, .plane-panel-x, .plane-spinner, #planePanelPhoto { position: relative; z-index: 1; }
.plane-panel-body { flex: 1; min-width: 0; }
/* fr24-style aircraft photo: thumbnail on the left, photographer credit in the
   corner (planespotters requires attribution). Its own element next to the
   text so live route/ETA updates never wipe it. */
.pop-photo {
  flex: none; width: 116px; height: 74px; border-radius: var(--r-sm); overflow: hidden;
  display: block; text-decoration: none;
  box-shadow: 0 3px 12px rgba(0, 0, 0, .4), inset 0 0 0 1px rgba(255, 255, 255, .12);
  animation: photo-in .3s ease both;
}
.pop-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Fallback when the aircraft has no photo: a muted type silhouette so the box
   stays consistent whether or not planespotters has an image. */
.pop-photo.placeholder {
  display: flex; align-items: center; justify-content: center;
  background: linear-gradient(155deg, rgba(40, 54, 82, .55), rgba(18, 26, 42, .65));
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .08);
}
.pop-photo.placeholder svg { width: 48px; height: 48px; fill: rgba(var(--muted-rgb),.42); margin-bottom: 6px; }
.pop-photo.placeholder .ph-type {
  position: absolute; left: 0; right: 0; bottom: 5px; text-align: center;
  font-size: var(--fs-micro); font-weight: 800; letter-spacing: var(--track-caps); text-transform: uppercase;
  color: rgba(var(--muted-rgb),.72);
}
.pop-photo .pop-cred {
  position: absolute; left: 0; right: 0; bottom: 0; padding: 1px 5px;
  font-size: var(--fs-micro); line-height: 1.5; letter-spacing: .02em; text-align: right;
  color: rgba(255, 255, 255, .9); background: linear-gradient(to top, rgba(0, 0, 0, .62), transparent);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
@keyframes photo-in { from { opacity: 0; transform: scale(.9); } }
/* Water ripple on press — the glass behaves like liquid where you touch it.
   (Positioning is set per-element in JS so it never clobbers an absolutely
   positioned control like the panel's close ✕.) */
.ripple {
  position: absolute; border-radius: 50%; pointer-events: none;
  background: radial-gradient(circle,
    rgba(160, 200, 255, .5) 0%, rgba(160, 200, 255, .22) 38%, rgba(160, 200, 255, 0) 68%);
  transform: scale(0); opacity: .9; mix-blend-mode: screen;
  animation: ripple .62s cubic-bezier(.22, .7, .3, 1) forwards;
}
@keyframes ripple {
  0% { transform: scale(0); opacity: .9; }
  55% { opacity: .55; }
  100% { transform: scale(1); opacity: 0; }
}

/* Lasteindikatoren ER ✕-knappens egen ramme. Brage 7/8: «om rammen rundt
   kryss-knappen kunne vært loading-spinneren, og ikke en egen spinner?
   Det ser mye renere ut.» Han har rett — to runde ting side om side i et
   hjørne som allerede er trangt er én for mye, og den ekstra sirkelen
   betydde ikke noe utover «vent». Nå snurrer rammen som allerede er der:
   én ring, to tilstander.

   `border-top-color` alene ville forsvunnet i den grå rammen, så den
   løftes til aksenten og resten dempes — ellers leser den som et hjul med
   fire like eiker, som ikke ser ut til å snurre i det hele tatt. */
.icon-x.is-loading {
  border-color: rgba(var(--muted-rgb), .22);
  border-top-color: var(--accent);
  border-width: 2px;
  animation: plane-spin .7s linear infinite;
}
/* ✕-tegnet ville snurret med ringen. Det skjules mens den laster — knappen
   er fortsatt der og fortsatt trykkbar (44px-flaten er uendret).
   `color: transparent`, ikke `> * { visibility:hidden }`: ✕ er en TEKST-
   node i knappen, ikke et element, så barneselektoren traff den aldri
   (målt). Fargen treffer den. */
.icon-x.is-loading { color: transparent; }
.icon-x.is-loading:active { transform: scale(.9); }
/* Den frittstående spinneren er borte fra panelet; regelen står igjen for
   andre flater som fortsatt bruker den. */
.plane-spinner {
  position: absolute; right: 34px; top: 9px; width: 15px; height: 15px;
  border: 2px solid rgba(var(--muted-rgb),.28); border-top-color: var(--accent);
  border-radius: 50%; animation: plane-spin .7s linear infinite;
}
@keyframes plane-spin { to { transform: rotate(360deg); } }

/* 16px side padding IS the sheet's rail: search-wrap, section titles, card
   edges, board rows and empty states all align on it (children carry no
   horizontal margins of their own). */
.sheet-content { flex: 1; overflow-y: auto; padding: 4px 16px calc(92px + var(--sab)); -webkit-overflow-scrolling: touch; scrollbar-width: none;
  /* C2: soft exit — content dissolves at the bottom edge instead of being
     guillotined by the dock/tab-bar overlap. */
  -webkit-mask-image: linear-gradient(180deg, #000 0, #000 calc(100% - 26px), transparent);
  mask-image: linear-gradient(180deg, #000 0, #000 calc(100% - 26px), transparent);
}
/* C4: empty-state art — one 40px currentColor glyph at 30 %, never a color. */
.empty-art { width: 40px; height: 40px; opacity: .3; display: block; margin: 0 auto 12px; }
/* C3: the follow-chip and a toast both live at the top edge — stack, don't
   overprint. Sibling order in index.html: followChip before toast. */
#followChip:not(.hidden) ~ #toast { top: calc(114px + var(--sat)); }
.sheet-content::-webkit-scrollbar { display: none; }

@media (min-width: 900px) {
  .sheet {
    left: auto; right: 16px; top: calc(74px + var(--sat)); bottom: 16px;
    width: 430px; height: auto !important; border-radius: var(--r-lg);
    border: 1px solid var(--line);
  }
  .sheet-modes { display: none; }
  .sheet-content { padding-bottom: 76px; }
}

/* ---------- tab bar: floating dock ---------- */
/* Liquid-glass capsule dock: translucent enough that sheet content visibly
   slides behind it, heavy blur+saturation, specular top edge. */
nav.tabs {
  /* `10px + --sab` stacked our own margin on top of the whole safe area —
     44 px of dead space under the dock on any home-indicator phone, which is
     what made it read as floating high up the screen.
     Padding stays EVEN: buying clearance with extra bottom padding left the
     labels riding high in the capsule with a dead band of glass beneath
     them, which looks exactly as wrong as it sounds. The clearance comes
     from where the capsule SITS instead. What must be avoided is the home
     indicator's own strip (~20 px), not the full 34 px inset — the inset is
     a margin hint, and treating it as forbidden space is what pushed the
     dock up the screen in the first place. */
  position: absolute; bottom: max(12px, calc(var(--sab) - 12px));
  left: 16px; right: 16px; z-index: 25;
  display: flex; gap: 4px; padding: 7px;
  background: linear-gradient(180deg, rgba(var(--muted-rgb),.16), rgba(30, 42, 66, .26) 40%, rgba(12, 18, 30, .34));
  border: 1px solid var(--rim);
  border-radius: 999px;
  backdrop-filter: var(--glass-blur-strong);
  -webkit-backdrop-filter: var(--glass-blur-strong);
  box-shadow: 0 18px 46px rgba(0, 0, 0, .55), var(--glass-edge);
}
nav.tabs button {
  position: relative; z-index: 1;
  flex: 1; border: 0; background: transparent; color: var(--muted);
  font: inherit; font-size: var(--fs-caption); font-weight: 700; cursor: pointer;
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  padding: 7px 0 6px; border-radius: 999px; transition: all .18s;
  /* The tap ripple uses mix-blend-mode inside a backdrop-filter parent — a
     combination WebKit composites OUTSIDE the overflow clip, so droplets
     leaked past the capsule edge. isolation pins the blend to the button
     and clip-path clips in the compositor where overflow:hidden can't. */
  isolation: isolate;
  clip-path: inset(0 round 999px);
}
/* Stroke glyphs inherit currentColor: muted at rest, accent on the active
   tab — the only colour a resting dock carries. */
nav.tabs button .ico { width: 22px; height: 22px; display: block; }
nav.tabs button.active .ico { color: var(--accent); }
nav.tabs button.active {
  color: var(--fg);
  background: rgba(255, 255, 255, .10);
  box-shadow: inset 0 0 0 1px var(--rim),
              inset 0 1px 0 rgba(255, 255, 255, .20);
}

/* ---------- native-app embed (?embed=app) ---------- */
/* The iOS shell brings its own Liquid Glass tab bar — ONE dock on screen. */
html.embed nav.tabs { display: none; }
/* The three mode buttons give way to the draggable grabber (rullegardin). */
html.embed .sheet-modes { display: none; }
.sheet-grabber {
  flex: 0 0 auto; height: 26px; position: relative; cursor: grab;
  /* The drag owns vertical gestures on this strip — without this iOS
     scrolls the page instead of delivering pointermove. */
  touch-action: none;
}
.sheet-grabber::after {
  content: ""; position: absolute; left: 50%; top: 9px;
  width: 44px; height: 5px; margin-left: -22px;
  border-radius: 999px; background: rgba(var(--muted-rgb), .38);
}
/* At minimum the sheet reads as a docked search bar: the teaser is the
   visible «search lives here»-affordance. Hidden from half height up — the
   real search field takes over there. */
.sheet-search-teaser { display: none; }
html.embed .sheet.min .sheet-search-teaser {
  /* gap 8, ikke 11: målt 3 px tekst-drift mot den ekte radens input. */
  display: flex; align-items: center; gap: 8px;
  margin: 4px 16px 0; padding: 0 18px; height: 50px; flex: 0 0 auto;
  border-radius: 999px;
  /* Facelift 6/8: the whole search row wears the date-chip's dark INSET
     glass — a light white pill next to a dark chip read as two different
     materials («mer dark mode»). */
  background: rgba(0, 0, 0, .22);
  border: 1px solid var(--rim);
  box-shadow: inset 0 1.5px 3px rgba(0, 0, 0, .30);
  color: var(--muted); font-size: var(--fs-lead); font-weight: 650;
  cursor: pointer;
}
.sheet-search-teaser .ico { width: 20px; height: 20px; flex: 0 0 auto; }
/* The REAL search field wears the same clothes in-app as the teaser pill —
   otherwise the drag from minimum reads as swapping between two different
   search bars instead of one continuous element. */
/* Strimlen og den EKTE søkeraden må stå på nøyaktig samme sted, ellers
   hopper baren i det du rører gardinen: `pointerdown` fjerner `.min`, og
   da byttes `.sheet-search-teaser` (margin-top 4px) mot `.search-wrap`
   (margin-top 12px). Åtte piksler — «marginalt ned», Brage 7/8. Begge er
   50px høye i embed, så det var kun toppmargen som skilte dem — og margen
   skal være NULL, ikke 4px: `.sheet-content` har allerede 4px topp-padding
   som strimlen (et direkte barn av arket) ikke får. Målt til 0,0 px hopp. */
html.embed .search-wrap { margin-top: 0; margin-bottom: 0; }

html.embed .search-box {
  border-radius: 999px;
  background: rgba(0, 0, 0, .22);
  border-color: var(--rim);
  box-shadow: inset 0 1.5px 3px rgba(0, 0, 0, .30);
  min-height: 50px; padding: 0 18px;
}
html.embed .search-box .search-ico svg { width: 20px; height: 20px; }
html.embed .search-box input { font-size: var(--fs-lead); }
/* Teksten er den SAMME i min (teaser-span) og halv/fokus (placeholder) —
   tegn for tegn OG klær for klær. Målt 10/8 19:31 (Brage: «marginalt
   forskjellig … det liker jeg ikke»): placeholderen arvet globalens
   rgba(--muted-rgb,.78) på vekt 400 mot teaserens solide --muted på 650,
   og lupen lå på fg×.55 mot teaserens muted×1. Chip og geometri var alt
   identiske (1.95.2). Samme klær, én kilde: */
html.embed .search-box input::placeholder {
  color: var(--muted); opacity: 1; font-weight: 650;
}
html.embed .search-box .search-ico { opacity: 1; color: var(--muted); }
html.embed .date-chip { border-radius: 999px; }
/* In-app the NATIVE profile owns sign-out and delete — the native session
   (URLSession cookies + APNs device token) must be cleared with it, and the
   web buttons would only kill the webview's copy and desync the two. */
html.embed .logout-btn { display: none; }
/* The board STAYS in the app's map sheet: «Mine flyvninger» is the sheet's
   default content, and its Kart action is the radar join (locate my plane).
   Hiding all of #boardDefault (1.31.1) left an empty void and read as data
   loss. Only the stats line goes: it exists to open the Pass WEB tab via
   the nav — which is hidden in embed, so the click dead-ends inside the
   sheet. In-app the native Fly tab's stats strip is that doorway. */
html.embed #myStats { display: none; }
/* Themes are web-only skins; the native tabs can't follow them (yet). An
   in-app switch would leave a half-aurora, half-navy app — hide the picker
   until FxColor is theme-aware. The boot also ignores any stored choice in
   embed (app.js). */
html.embed #themeSection { display: none; }
/* Varsler er WEB PUSH — mekanismen for nettleseren og PWA-en på
   hjemskjermen. Inne i appen finnes ikke PushManager i WKWebView i det
   hele tatt, så raden kunne aldri bli noe annet enn «Ikke støttet her»,
   og hjelpeteksten ba deg legge appen på hjemskjermen mens du STO i
   appen (Brage 7/8, IMG_7222: «Den der varsel greia er jo feil? Det er
   fra det var en webapp?»). Han har rett: i appen går varsler over APNs,
   som den native siden eier — PushManager + PermissionNag. Seksjonen blir
   stående for ekte webbrukere, der den er den ENESTE veien til varsler. */
html.embed #pushSection { display: none; }
/* …men adminflaten skal fungere i appen: den pinnede profilen har en egen
   inngang, og fanen selv må kunne vises når den åpnes derfra. */
html.embed-pin #tab-admin:not(.hidden) { display: block; }
/* …men er ADMIN selv den pinnede fanen, eier fanelinja navigasjonen og
   «‹ Profil» ville sendt deg til en flate som ikke er der. */
html[data-pin="admin"] .nav-back-row { display: none; }

/* iOS-vane: tilbake er en pil med navnet på stedet du kom fra, oppe til
   venstre — ikke en knapp i knappefarge. Den skal lese som navigasjon. */
.nav-back-row { padding: 2px 0 6px; }
.nav-back {
  display: inline-flex; align-items: center; gap: 3px;
  background: none; border: 0; padding: 6px 6px 6px 0;
  font: inherit; font-size: 16px; font-weight: 600;
  color: var(--accent); cursor: pointer; margin-left: -4px;
}
.nav-back svg { width: 18px; height: 18px; }
.nav-back:active { opacity: .55; }
/* Pinned tab (native Venner/Pass): no appbar, no sheet modes — the sheet IS
   the whole view, near full height under the status bar. */
html.embed-pin .appbar { display: none; }
html.embed-pin .sheet-modes { display: none; }
html.embed-pin .sheet {
  top: calc(8px + var(--sat));
  height: auto !important;
  border-radius: var(--r-lg) var(--r-lg) 0 0;
}
/* En innstillingsflate har ikke et kart bak seg. Brage 7/8: «Jeg liker det
   svarte … det ser ut som del av 2 ulike apper.» Den blåaktige tonen i
   Venner/Pass/Profil var IKKE en farge — det var basiskartet som skinte
   gjennom det gjennomskinnelige arket. I en pinnet fane er arket hele
   flaten, så glasset blir ugjennomsiktig svart og kartet slås av: samme
   svart som den native detaljflaten, og én GPU-tegnet kartflate mindre å
   holde i live på en fane som aldri viser den. */
html.embed-pin .sheet {
  background: var(--bg);
  backdrop-filter: none; -webkit-backdrop-filter: none;
  box-shadow: none;
}
html.embed-pin .sheet::before { display: none; }
html.embed-pin #map { display: none; }

/* ---------- cards ---------- */
/* RESTING surface: cards sit IN the sheet's glass, not on it — flat fill,
   one hairline, no blur, no outer shadow. Only floating chrome gets glass. */
.card {
  /* Plate, ikke glass-med-ramme. Rammen var web-vanen som skilte
     webflatene fra de native (Brage IMG_7219: «ser ikke ut som samme
     app») — iOS definerer en gruppe med FLATE, ikke med kontur. */
  background: var(--plate);
  border: 0;
  border-radius: var(--r-md);
  padding: 14px 16px;
  margin: 10px 0;
  animation: rise .25s ease both;
}
@keyframes rise { from { opacity: 0; transform: translateY(8px); } }
.card .row1 { display: flex; align-items: center; gap: 9px; }
.card .fno { font-size: var(--fs-lead); font-weight: 700; letter-spacing: -.01em; cursor: pointer; font-variant-numeric: tabular-nums; }
.card .route {
  color: var(--muted); font-weight: 650; font-size: var(--fs-caption);
  letter-spacing: var(--track-caps); text-transform: uppercase;
}
.card .row2 { display: flex; align-items: center; gap: 8px; margin-top: 10px; flex-wrap: wrap; }
.card .times { font-variant-numeric: tabular-nums; font-weight: 800; font-size: var(--fs-big); letter-spacing: -.02em; }
.card .strike { text-decoration: line-through; color: var(--muted); font-weight: 400; font-size: var(--fs-body); margin-right: 6px; letter-spacing: 0; }

/* One card per tracked flight (0.28.0): identity, headline route, live rows,
   actions. The old layout wrapped a card inside a card and printed the flight
   number and route twice — two boxes of the same thing is not information. */
.fcard { padding: 16px 16px 12px; }
.fcard .fc-head { display: flex; align-items: center; gap: 8px; }
.fcard .fno { font-size: var(--fs-lead); font-weight: 700; }
.fc-sub {
  color: var(--muted); font-size: var(--fs-caption); font-weight: 650;
  letter-spacing: var(--track-caps); text-transform: uppercase; white-space: nowrap;
}
/* THE close component — every ✕ in the app (card, plane-panel, modal, live
   view) is this one control; siblings only position it. */
.icon-x {
  position: relative;
  width: 27px; height: 27px; flex: none; margin-left: 4px; padding: 0;
  border-radius: 999px; border: 1px solid var(--line);
  background: rgba(var(--muted-rgb), .06); color: var(--muted);
  font: inherit; font-size: var(--fs-caption); line-height: 1; display: grid; place-items: center;
  cursor: pointer;
  transition: transform .12s ease, color .12s ease, border-color .12s ease;
}
.icon-x:hover { color: var(--bad); border-color: rgba(248, 113, 113, .45); }
.icon-x.is-loading:hover { color: transparent; border-top-color: var(--accent); }
.icon-x:active { transform: scale(.9); }
/* Finger-sized (44px) invisible strip centred on the 27px control. */
.icon-x::after {
  content: ""; position: absolute; left: 50%; top: 50%;
  width: 44px; height: 44px; transform: translate(-50%, -50%);
}
.icon-x.modal-x { float: right; }
.icon-x.live-x { position: absolute; right: 0; top: 0; margin: 0; }
/* One hero per card: the time pair. The route steps down to a subhead. */
.fc-route {
  display: flex; align-items: center; gap: 8px; margin: 12px 0 2px;
  font-size: var(--fs-title); font-weight: 650; letter-spacing: -.025em; line-height: 1.1;
}
.fc-route .arw { color: var(--muted); font-size: var(--fs-lead); font-weight: 650; }
.fcard .times { font-size: var(--fs-hero); font-variant-numeric: tabular-nums; letter-spacing: -.025em; }
/* Ankomsten ved siden av avgangen — dempet, destinasjonens klokke. */
.fcard .fc-arr {
  font-size: var(--fs-lead); font-weight: 700; color: var(--muted);
  font-variant-numeric: tabular-nums; margin-left: 8px; align-self: baseline;
}
.fcard .fc-line { align-items: baseline; }
.fc-line { display: flex; align-items: baseline; gap: 8px; margin-top: 3px; }
/* "Flyet ditt" — where the airframe operating this departure is right now.
   Grayscale on purpose: it's information, not a call to action. */
.fc-line.rot {
  align-items: center; gap: 8px; margin-top: 8px; padding: 6px 9px;
  border: 1px solid var(--line); border-radius: var(--r-sm);
  font-size: var(--fs-body); color: var(--muted);
}
.fc-line.rot.tappable { cursor: pointer; }
.fc-line.rot.tappable:active { transform: scale(.985); }
.fc-line.rot .rot-ico { font-size: var(--fs-body); opacity: .8; }
.fc-line.rot .rot-txt { flex: 1; min-width: 0; }
/* The one blue in the row: "Se på kart ›" is the tap affordance, the rest is data. */
.fc-line.rot .rot-go { font-weight: 700; font-size: var(--fs-caption); white-space: nowrap; color: var(--accent); }
/* Badge under the plane panel: this aircraft flies YOUR flight next. */
.rot-note {
  margin-top: 6px; padding-top: 5px; border-top: 1px solid var(--line);
  font-size: var(--fs-caption); color: var(--muted);
}
.fc-chips { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 8px; }
.fc-chips:empty { display: none; }
/* ONE chip: every pill-shaped token shares this geometry; the variants below
   change colour only. Uppercase + tracking is reserved for the status pill. */
.pill, .boardchip, .chipbtn, .delaychip, .pp-chip {
  display: inline-flex; align-items: center; height: 24px; padding: 0 10px;
  border-radius: 999px; font-size: var(--fs-caption); font-weight: 700; white-space: nowrap;
  border: 1px solid var(--line); background: rgba(var(--muted-rgb), .05); color: var(--muted);
}
.chipbtn { transition: transform .12s ease; cursor: pointer; }
.chipbtn:active { transform: scale(.94); }
/* The aircraft type opens the cabin layout — hint it without shouting. */
.cabin-link {
  color: var(--fg); border-bottom: 1px dotted rgba(var(--muted-rgb),.55);
  cursor: pointer; padding-bottom: 1px;
}
.cabin-link:active { opacity: .6; }
.fc-actions {
  display: flex; gap: 8px; margin-top: 10px; padding-top: 10px;
  border-top: 1px solid var(--line);
}
.fc-act {
  flex: 1; min-height: 38px; border-radius: var(--r-sm);
  border: 1px solid var(--line); background: rgba(var(--muted-rgb), .06);
  color: var(--fg); font-size: var(--fs-body); font-weight: 700; letter-spacing: .05em;
  display: flex; align-items: center; justify-content: center; gap: 7px;
  transition: transform .12s ease, background .18s ease;
}
.fc-act:active { transform: scale(.97); }
.fc-act.live {
  color: var(--accent); border-color: rgba(var(--accent-rgb),.4);
  background: rgba(var(--accent-rgb),.10);
}
.fc-act .dot {
  width: 6px; height: 6px; border-radius: 50%; background: var(--accent);
  animation: fc-dot 2.4s ease-in-out infinite;
}
@keyframes fc-dot {
  0%, 100% { box-shadow: 0 0 0 2px rgba(var(--accent-rgb),.10); }
  50%      { box-shadow: 0 0 0 5px rgba(var(--accent-rgb),.20), 0 0 10px rgba(var(--accent-rgb),.35); }
}
@media (prefers-reduced-motion: reduce) { .fc-act .dot { animation: none; } }
.fcard .journey:not(:empty) { margin-top: 12px; }
/* The meta line is ONE sentence, not flex items — as a flex container every
   text fragment around a <b> became its own item and inherited the 14px gap,
   which is why "Gate  E28  · Term  A" read so airy. */
.fcard .meta { display: block; margin-top: 8px; line-height: 1.5; }
.fcard .meta b { color: var(--fg); font-weight: 700; margin-left: 2px; }
.pill {
  font-size: var(--fs-micro); font-weight: 800;
  letter-spacing: var(--track-caps); text-transform: uppercase;
}
.pill.s-expected, .pill.s-boarding, .pill.s-gate_closing, .pill.s-go_to_gate, .pill.s-gate_closed { color: var(--warn); border-color: rgba(251,191,36,.30); background: none; }
.pill.s-cancelled, .pill.s-diverted { color: var(--bad); border-color: rgba(248,113,113,.32); background: none; }
.pill.s-landed, .pill.s-departed, .pill.s-en_route, .pill.s-baggage, .pill.s-baggage_delivered { color: var(--good); border-color: rgba(74,222,128,.30); background: none; }
.pill.track { color: var(--accent); border-color: rgba(var(--accent-rgb),.4); background: rgba(var(--accent-rgb),.08); }
.pill.ghost { opacity: .6; }
.delaychip {
  color: var(--warn); font-weight: 800; font-variant-numeric: tabular-nums;
  border-color: rgba(251, 191, 36, .35); background: rgba(251, 191, 36, .08);
}
/* Grayscale base: a countdown is data, not an affordance. Colour arrives
   only with meaning (.late/.off/.good). */
.countdown { font-size: var(--fs-body); font-weight: 800; color: var(--fg);
  white-space: nowrap; font-variant-numeric: tabular-nums; }
.countdown.late { color: var(--warn); }
.countdown.off { color: var(--bad); }
.countdown.good { color: var(--good); }
.lv-good { color: var(--good); font-weight: 700; }
.lv-late { color: var(--warn); font-weight: 700; }

/* history + stats */
.section-title.collapsible { cursor: pointer; user-select: none; }
.section-title .chev { opacity: .6; }
.past-card { padding: 10px 14px; opacity: .8; }
.fno-plain { font-weight: 800; font-size: var(--fs-lead); }
.times.muted, .muted { color: var(--muted); }
.btn.ghosted { opacity: .55; padding: 2px 8px; }
/* One-line muted teaser under "Mine flyvninger" — taps through to Pass. */
.stats-line {
  margin: 0 0 10px; font-size: var(--fs-caption); font-weight: 650; color: var(--muted);
  cursor: pointer; font-variant-numeric: tabular-nums;
}
.lb-row { display: flex; align-items: center; gap: 10px; padding: 8px 0; border-bottom: 1px solid var(--line); }
.lb-row:last-child { border-bottom: 0; }
.lb-rank { width: 26px; text-align: center; font-weight: 800; color: var(--muted); }
/* "Me" is emphasis, not interaction — weight + a whisper of surface, no blue. */
.lb-row.me { background: rgba(var(--muted-rgb), .07); }
.lb-row.me .lb-rank, .lb-row.me .lb-name { color: var(--fg); font-weight: 800; }
.lb-name { font-weight: 700; flex: 1; }
.lb-count { font-weight: 800; }
.lb-sub { font-size: var(--fs-caption); color: var(--muted); }
.hist-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.hist-grid input { width: 100%; }

/* passport */
.pp-head { display: flex; align-items: center; justify-content: space-between; margin: 20px 0 10px; }
.pp-head /* Fanens NAVN. I appen er hver webfane pinnet som sin egen skjerm, og en
   skjerm apner med stor tittel — det er vanen de native fanene (Fly,
   Venner) alltid har hatt. Brage 7/8: «ha harmoni i appen. Nå er det jo
   ulikt ulike steder.» Utenfor den pinnede fanen finnes docken, som
   allerede sier hvor du er, og tittelen blir en vanlig seksjon. */
/* …men IKKE i appen: der ligger hver pinnet fane under en ekte native
   navigasjonslinje som allerede tegner tittelen (`.navigationBarTitle-
   DisplayMode(.large)`). To titler oppa hverandre er ikke harmoni. La
   systemet eie den — da kollapser den ved scroll slik iOS gjor, og en
   web-etterligning kan aldri det. */
html.embed-pin .tab-title { display: none; }

.section-title { margin: 0; }
.pp-years { display: flex; gap: 6px; }
.pp-chip { background: rgba(var(--muted-rgb),.10); cursor: pointer; }
.pp-chip.active { color: var(--accent); background: rgba(var(--accent-rgb),.16); border-color: rgba(var(--accent-rgb),.45); }
.pp-card { border-radius: var(--r-md); padding: 18px 16px; margin-bottom: 10px; }
.pp-hero { background: linear-gradient(135deg, #16277c 0%, #1b3a8f 55%, #0d1440 100%); }
.pp-delay { background: linear-gradient(135deg, #6e1622 0%, #40101a 100%); }
/* Helten og forsinkelseskortet er farga PLATER: teksten er hvit i ALLE
   temaer. Målt 5/9: i Lys arvet tallene --fg (#1c1c1e) og sto svarte på
   mørkeblått — uleselig. Etikettene er hvit-alfa, aldri --muted. */
.pp-hero, .pp-delay { color: #fff; }
.pp-hero .pp-lab, .pp-delay .pp-lab { color: rgba(255, 255, 255, .72); }
.pp-hero .pp-kicker, .pp-delay .pp-kicker { opacity: .78; }
.pp-light { background: linear-gradient(135deg, #d9e3f8 0%, #eef3fd 100%); }
.pp-kicker { font-size: var(--fs-micro); font-weight: 700; letter-spacing: var(--track-kicker); text-transform: uppercase; opacity: .65; margin-bottom: 14px; }
.pp-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 18px 12px; }
.pp-num { font-size: 36px; font-weight: 800; letter-spacing: -.03em; line-height: 1; font-variant-numeric: tabular-nums; }
.pp-num.xl { font-size: 58px; }
.pp-unit { font-size: var(--fs-title); font-weight: 700; opacity: .75; letter-spacing: 0; }
.pp-lab { font-size: var(--fs-micro); color: var(--muted); margin-top: 5px; text-transform: uppercase; letter-spacing: var(--track-caps); font-weight: 650; }
.pp-sub { font-size: var(--fs-body); margin-top: 8px; opacity: .8; }
/* Empty passport: the SAME hero, waiting to be filled. Dimmed rather than
   absent, so a new user sees the shape of what they are collecting instead
   of one grey sentence in an empty pane. */
.pp-blank { filter: saturate(.55); }
.pp-blank .pp-num { opacity: .38; }
.pp-blank .pp-lab { opacity: .5; }
.pp-onboard { padding: 4px 2px 0; }
.ppo-title { font-size: var(--fs-lead); font-weight: 800; margin: 16px 0 12px; }
.ppo-steps { list-style: none; display: flex; flex-direction: column; gap: 11px; margin: 0 0 16px; padding: 0; }
.ppo-steps li { display: flex; align-items: flex-start; gap: 11px; font-size: var(--fs-body); line-height: 1.45; color: var(--muted); }
/* --text has never existed; the palette token is --fg. An invalid var() on an
   inherited property falls back to inherit, so these <b>s picked up --muted from
   the parent li — bleaching exactly the two words that point a first-time user at
   the "Fly" tab and the "+" button. */
.ppo-steps b { color: var(--fg); font-weight: 700; }
/* Tabular step number: a filled accent disc, same 22 px rhythm as the rows. */
.ppo-n {
  flex: none; width: 22px; height: 22px; border-radius: 999px;
  display: grid; place-items: center; margin-top: 1px;
  background: rgba(var(--accent-rgb),.16); color: var(--accent);
  border: 1px solid rgba(var(--accent-rgb),.42);
  font-size: var(--fs-caption); font-weight: 800; font-variant-numeric: tabular-nums;
}
.pp-onboard .btn { width: 100%; }
.ppo-note { font-size: var(--fs-caption); color: var(--muted); margin-top: 12px; line-height: 1.45; }
.pp-lab.dark, .pp-sub.dark { color: #37456b; opacity: 1; }
.pp-light .pp-topname { color: #101a35; }
.pp-topline { display: flex; align-items: center; gap: 10px; margin-top: 8px; }
.pp-topname { font-size: var(--fs-big); font-weight: 800; }
.pp-topcount { margin-left: auto; font-weight: 800; color: #37456b; }
.pp-row { display: flex; align-items: center; gap: 10px; padding: 9px 0; border-bottom: 1px solid var(--line); font-size: var(--fs-lead); }
.pp-row:last-child { border-bottom: 0; }
.pp-fno { font-weight: 800; min-width: 62px; }
.pp-route { color: var(--muted); flex: 1; }
.pp-date { color: var(--muted); font-size: var(--fs-body); }
.al-badge {
  position: relative; overflow: hidden;
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: 50%; flex: none;
  font-size: var(--fs-caption); font-weight: 800; border: 1.5px solid; letter-spacing: .02em;
}
/* Real logo over the lettered fallback. Brand tiles come edge-to-edge, so
   cover + inherit the badge's radius; a hairline ring keeps a white tile from
   glaring against the dark card. */
.al-logo {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: contain; border-radius: inherit; padding: 3px;
  /* Every mark sits on a light tile: brand tiles cover it, and logos that
     ship transparent (dark ink meant for a white page) stay legible instead
     of disappearing into the card. */
  background: #f2f6fb;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .10);
  animation: logo-in .22s ease both;
}
@keyframes logo-in { from { opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .al-logo { animation: none; } }

/* live "you are here" dot */
.me-dot {
  width: 14px; height: 14px; border-radius: 50%;
  background: var(--accent); border: 2.5px solid #fff;
  box-shadow: 0 0 0 0 rgba(var(--accent-rgb),.55);
  animation: me-pulse 2.4s ease-out infinite;
}
@keyframes me-pulse {
  0% { box-shadow: 0 0 0 0 rgba(var(--accent-rgb),.55); }
  70% { box-shadow: 0 0 0 16px rgba(var(--accent-rgb),0); }
  100% { box-shadow: 0 0 0 0 rgba(var(--accent-rgb),0); }
}
/* Neutral by default: only a real call to action (go to gate / boarding)
   earns colour. The security-queue and info chips are grey. */
.boardchip { background: none; }
.boardchip.go { background: rgba(74,222,128,.10); color: var(--good); border-color: rgba(74,222,128,.38); }
.boardchip.warn { background: none; color: var(--warn); border-color: rgba(251,191,36,.34); }

.meta { display: flex; gap: 14px; margin-top: 9px; font-size: var(--fs-caption); color: var(--muted); flex-wrap: wrap; font-weight: 650; }
.meta b { color: var(--fg); font-weight: 700; font-variant-numeric: tabular-nums; }

/* Brage 7/8 (IMG_7219): den native detaljflaten «ser veldig iphone aktig
   ut», webflatene «ser ikke ut som samme app». Fasiten er Apples inset-
   grouped liste — og der ROPER ikke gruppeoverskriftene. De er rolige,
   grå og i vanlig setningsform («Avgang BGO · avinor»), ikke sperret
   versal. Teksten i markupen er allerede setningsform; det var kun
   text-transform som skrek. */
.section-title {
  font-size: 15px; font-weight: 600; letter-spacing: 0;
  color: var(--muted); margin: 22px 0 7px;
}
/* Section title → first card is ALWAYS 6px (kill the card's own 10px top
   margin so block-margin collapse can't stretch the gap). #myFlights is
   listed separately: the hidden stats teaser sits between it and the title,
   and a display:none sibling still defeats the adjacent-sibling selector. */
.section-title + .card, .section-title + div > .card:first-child,
#myFlights > .card:first-child { margin-top: 0; }
.empty { text-align: center; color: var(--muted); padding: 26px 10px; font-size: var(--fs-body); }
/* Et tomt rom som står FOR en liste skal lese som listas eneste rad —
   samme plate og samme radius som radene den erstatter. Venner-fanen
   viste hvorfor: «Toppliste» lå på plate (den er et .card), mens
   «Venner» og «Venners flyvninger» hang som løs tekst i svart. Tre
   seksjoner, tre ulike svar på samme tilstand.
   Ligger den alt INNE i et kort, arver den kortets flate — da ingen ny. */
:not(.card) > .empty:not(.tall) {
  background: var(--plate); border-radius: var(--r-md); margin: 10px 0;
}
/* Empty states that own a whole view get room to breathe.
   MÅLT 21/8: `min-height: 42vh` sentrerte teksten i en boks som var
   HØYERE enn plassen som faktisk var synlig — på 932 px lå den 7 px inn
   under den flytende docken, på 700 px hele 75 px. Et tomt rom som
   klipper sin egen forklaring er verre enn ingen forklaring. Blokka er
   nå toppforankret med luft rundt: samme ro på høye skjermer, lesbar på
   lave. */
.empty.tall { padding: 42px 10px 48px; display: grid; place-items: center; }
/* Lave skjermer har ikke luften å gi bort — MÅLT: 42 px topp-padding la
   teksten 14 px inn under docken på en 700 px høy flate. */
@media (max-height: 780px) { .empty.tall { padding: 20px 10px 26px; } }

/* ---------- inputs & buttons ---------- */
/* INSET wells: fields are sunk INTO the surface — darker fill + a top inset
   shadow, never a raised or glassed box. */
input, select {
  /* Et felt inne i en gruppe stepper OPP (--plate-2), ikke ned i en mørk
     brønn. Den innfelte skyggen var en gravert-i-metall-effekt fra da
     bakgrunnen var lys nok til å kaste den; på ren svart lager den bare
     et hull. Apple bruker ett hakk lysere fyll og ingen skygge. */
  background: var(--plate-2); border: 1px solid transparent; color: var(--fg);
  border-radius: var(--r-md); padding: 11px 13px; font: inherit; width: 100%;
  outline: none; transition: border-color .15s, box-shadow .15s;
}
/* Fokus er en mykt lysende ring, ikke bare en streksfarge — feltet du
   skriver i skal FØLES aktivt (design-uplift 5/9). */
input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px rgba(var(--accent-rgb), .18); }
/* A well inside a resting card steps the radius down one notch. */
.card input, .card select { border-radius: var(--r-sm); }
input::placeholder { color: rgba(var(--muted-rgb),.78); }
/* The default .btn is the view's ONE true submit — green, nothing else.
   Everything that merely navigates or assists is .secondary glass. */
.btn {
  /* Husregel A2: ÉN ekte CTA per flate, og den er grønn. Alt annet er
     stille glass — før 1.57.0 var HVER knapp grønn (Lagre, «Ikke støttet
     her», feeder-nøkkel …), og da betyr fargen ingenting. */
  border: 1px solid var(--line); border-radius: var(--r-md);
  padding: 11px 16px; font: inherit; font-weight: 700;
  cursor: pointer; color: var(--fg);
  background: rgba(var(--muted-rgb), .09);
  transition: transform .12s, opacity .15s, background .15s;
}
.btn:active { transform: scale(.97); }
/* CTA-en bærer AKSENTEN, ikke grønt. Brage 7/8: «de grønne knappene …
   matcher ikke tema». Han hadde rett på et dypere plan enn fargesmak:
   grønn betyr «i rute» overalt ellers i appen (status, kort, brett). Når
   den SAMME grønnfargen også betyr «trykk her», slutter den å bety noe
   av delene — og på innloggingsskjermen sto en knallgrønn knapp under en
   blå logo. Aksenten følger dessuten temaet, så CTA-en blir riktig i
   Nordlys og Midnattssol også; det gjorde aldri grønn. */
.btn.cta { background: var(--accent); color: #071019; border-color: transparent; }
.btn[disabled], .btn.is-off {
  opacity: .5; cursor: default; background: rgba(var(--muted-rgb), .06);
}
.btn.secondary { background: rgba(var(--muted-rgb),.14); color: var(--fg); border: 1px solid var(--line); }
.btn.danger { background: rgba(248,113,113,.15); color: var(--bad); border: 1px solid rgba(248,113,113,.35); }
.btn.small { padding: 7px 14px; font-size: var(--fs-body); }
/* Rhythm values live here, not as inline styles in the markup. */
.card > .btn { margin-top: 10px; }
#feederSetup .btn { margin-top: 10px; }
.add-hist { margin: 10px 0 2px; }
/* Logg ut: rød tekst på en rolig plate (iOS-vanen); sletting: stille
   tekstlenke under — det irreversible skal ikke være det mest synlige. */
.logout-btn { margin: 16px 0 4px; width: 100%; background: var(--plate); border: 0; color: var(--bad); }
.delete-btn { width: 100%; margin: 0 0 6px; background: none; border: 0; color: var(--bad); opacity: .7; font-weight: 600; font-size: var(--fs-body); }
.delete-btn:active { opacity: 1; }
.lead-input { max-width: 110px; }
.hint.below { margin: 0 0 8px; }
.hint.sep { margin-top: 12px; }
:is(button, a, [role="button"]):focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.inline-form { display: flex; gap: 8px; margin: 10px 0; }
.inline-form input { flex: 1; }
/* ---------- gruppert liste (iOS Settings-vanen) ----------
   Brage 7/8: «liker at det ser litt mer iphone app aktig ut». Et stablet
   web-skjema (etikett OVER felt, ramme rundt hvert felt) leser som en
   nettside. iOS grupperer i stedet: ett kort, én hårstrek mellom radene,
   etikett venstre og verdi høyre — og fotnoten UTENFOR kortet. */
.card.list {
  padding: 0 0 0 16px; overflow: hidden;
  /* Ingen ramme: iOS-gruppa er en plate ett hakk lysere enn bakgrunnen.
     Rammen rundt glass er web-vane og er nettopp det som gjorde at de to
     flatene ikke leste som samme app. */
  border: 0; background: var(--plate);
}
.lrow {
  display: flex; align-items: center; gap: 12px;
  min-height: 46px; padding: 8px 16px 8px 0; width: 100%;
  /* border: 0 FØRST — <button>-radene arver ellers UA-rammen sin, som
     tegner en loddrett strek i venstrekanten av gruppa. */
  border: 0; border-top: 1px solid var(--line);
  background: none; font: inherit; color: var(--fg); text-align: left;
}
.lrow:first-child { border-top: 0; }
.lrow.head { padding-top: 14px; padding-bottom: 14px; }
.lrow-l { flex: 0 0 auto; font-weight: 400; color: var(--muted); }
/* Verdien er høyrestilt og dempet til du redigerer den — samme signal som
   iOS: teksten til høyre er DIN, etiketten til venstre er appens. */
.lrow-v {
  flex: 1 1 auto; min-width: 0; text-align: right;
  background: none; border: 0; padding: 0; font: inherit;
  color: var(--fg); font-weight: 700; border-radius: 0;
}
.lrow-v:focus { outline: 0; }
.lrow-v::placeholder { color: var(--muted); font-weight: 400; }
.lrow-x { margin-left: auto; color: var(--muted); font-variant-numeric: tabular-nums; }
/* Hårstreken starter der teksten starter (venstre-innrykk), som i iOS. */
.card.list .lrow + .lrow, .card.list .feeder-row + .feeder-row { border-top-color: var(--line); }
.lrow.nav, .lrow.act { cursor: pointer; }
/* Grå er for ETIKETTEN i en verdirad. En navigasjonsrad har ingen verdi
   til venstre — den har en TITTEL, og den er hvit (iOS Settings). Grå
   tittel leser som deaktivert. */
.lrow.nav .lrow-l { color: var(--fg); }
.lrow.nav:active, .lrow.act:active { background: rgba(var(--muted-rgb), .08); }
.lrow.act { color: var(--accent); font-weight: 400; }
.lrow.act .lr-ico { width: 20px; height: 20px; flex: 0 0 auto; }
.chev { width: 17px; height: 17px; color: var(--muted); opacity: .7; flex: 0 0 auto; }
/* Avskrudd er en TILSTAND, ikke en handling — den mister aksentfargen. */
.lrow.act[disabled] { color: var(--muted); cursor: default; }
.lrow.act[disabled]:active { background: none; }
/* Rader som JS fyller inn (mottakere, oppsett) arver samme geometri. */
.card.list .feeder-row { margin: 0; padding: 10px 16px 10px 0; border-top: 1px solid var(--line); }
.card.list .feeder-row:first-child { border-top: 0; }
.card.list #feederSetup:not(.hidden) { padding: 12px 16px 12px 0; border-top: 1px solid var(--line); }
/* Tom statusboks er ikke en rad: uten dette tegner knappen en løs strek
   i toppen av gruppa når ingen mottaker finnes. */
.card.list #feederStatus:empty + .lrow { border-top: 0; }
.lrow.nav .lrow-x + .chev { margin-left: 6px; }
/* Fotnoten hører til GRUPPA, ikke til en rad — derfor utenfor kortet. */
.card-note {
  font-size: var(--fs-caption); color: var(--muted);
  line-height: 1.45; margin: -2px 4px 14px; padding: 0 12px;
}

.field { margin: 12px 0; }
.field label { display: block; font-size: var(--fs-caption); font-weight: 700; color: var(--muted); margin: 0 2px 5px; }
.hint { font-size: var(--fs-caption); color: var(--muted); margin-top: 5px; line-height: 1.5; }

/* ---------- login ---------- */
#loginView {
  position: fixed; inset: 0; z-index: 40; display: flex; align-items: center; justify-content: center;
  /* Ingen farget gløde i hjørnet. Et blått radial-skjær bak et mørkt kort
     er selve standardlooken Brage kalte «generisk ai kode» — og på ren
     svart leser det som en flekk, ikke som atmosfære. Apple lar svart
     være svart og lar innholdet bære skjermen. */
  background: var(--bg);
  padding: 20px;
}
.login-card {
  width: 100%; max-width: 380px; padding: 30px 26px;
  /* Samme plate som alle andre grupper — ikke glass med kontur. Det er
     ingenting bak den å se gjennom her. */
  background: var(--plate); border: 0; border-radius: var(--r-lg);
}
.login-art { display: block; width: 96px; height: 96px; margin: 0 auto 14px; border-radius: 22px;
  box-shadow: 0 14px 34px rgba(0, 0, 0, .45), inset 0 0 0 1px rgba(255, 255, 255, .06); }
.login-card .logo { font-size: var(--fs-hero); justify-content: center; margin-bottom: 4px; }
.login-card .tag { text-align: center; color: var(--muted); font-size: var(--fs-body); margin-bottom: 20px; text-wrap: balance; }
/* Kortet ANKOMMER (design-uplift 5/9): en rolig rise+fade, ingen glød og
   ingen «generisk» radial bak — svart er svart, bevegelsen bærer det. */
.login-card { animation: login-in .5s var(--ease-out) both; }
@keyframes login-in { from { opacity: 0; transform: translateY(14px) scale(.985); } }
@media (prefers-reduced-motion: reduce) { .login-card { animation: none; } }
.login-card .btn { width: 100%; margin-top: 6px; }
.google-btn {
  width: 100%; margin-top: 10px; display: flex; align-items: center; justify-content: center; gap: 9px;
  background: #fff; color: #1f2937; border-radius: var(--r-md); padding: 11px; font-weight: 700;
  text-decoration: none; font-size: var(--fs-lead);
}
.login-alt { text-align: center; margin-top: 15px; font-size: var(--fs-body); color: var(--muted); }
.login-alt a { color: var(--accent); cursor: pointer; text-decoration: none; font-weight: 700; }
.form-error { color: var(--bad); font-size: var(--fs-body); margin-top: 9px; text-align: center; min-height: 17px; }

/* ---------- friends ---------- */
/* Sections are flex items so the "Finn venner" block can move to the top
   while the user has no friends yet — DOM order untouched. */
#tab-friends { display: flex; flex-direction: column; }
#tab-friends.friendless .fsec-find { order: -1; }
.friend-row { display: flex; align-items: center; gap: 11px; }
.avatar {
  width: 40px; height: 40px; border-radius: 50%; flex: none;
  position: relative;
  display: flex; align-items: center; justify-content: center;
  font-weight: 800; font-size: var(--fs-lead); color: #061224;
  background: var(--accent);
}
.avatar.sm { width: 30px; height: 30px; font-size: var(--fs-body); }

/* ---------- prestige frames (game badge art) + level ----------
   The frame is EARNED (airlines flown — the 18-step ladder in
   avatarRing(), mirrored in native FxPrestige). PNG art from
   scripts/gen_prestige.py; geometry contract: avatar disc = 54% of the
   frame, number plate baked at (0.5, 0.815) of it. */
.avatar .av-frame {
  position: absolute; left: 50%; top: 50%;
  width: 185.2%; height: 185.2%;
  transform: translate(-50%, -50%);
  max-width: none; pointer-events: none; z-index: 1;
}
.av-lv {
  position: absolute; right: -6px; bottom: -4px; z-index: 2;
  min-width: 17px; height: 17px; padding: 0 4px; border-radius: 999px;
  display: flex; align-items: center; justify-content: center;
  font-size: 10px; font-weight: 800; font-variant-numeric: tabular-nums;
  color: var(--fg); background: var(--bg);
  border: 1px solid rgba(var(--muted-rgb), .35);
}
.avatar.sm .av-lv { right: -7px; bottom: -5px; }
/* On a framed avatar the number sits ON the baked plate: plate centre is
   (0.815-0.5)·frame = 58.3% of the avatar below its centre. */
.avatar.framed .av-lv {
  right: auto; bottom: auto; left: 50%; top: 108.3%;
  transform: translate(-50%, -50%);
  background: transparent; border: 0; padding: 0; min-width: 0; height: auto;
  font-size: 11px; line-height: 1;
}
.avatar.sm.framed .av-lv { font-size: 8.5px; }
/* Frames overhang the row — give them air instead of clipping. */
.friend-row .avatar.framed, .lb-row .avatar.framed { margin: 8px 6px; }

.friend-row .who { flex: 1; min-width: 0; }
.friend-row .who .dn { font-weight: 700; }
.friend-row .who .un { font-size: var(--fs-caption); color: var(--muted); }

/* Follow: the ✕'s corner-sibling — same .icon-x chrome, opposite corner.
   Green is the meaning (THE CTA on this surface); followed = quiet check. */
.icon-x.pp-follow {
  position: absolute; right: 6px; bottom: 6px; margin: 0; z-index: 1;
  width: 24px; height: 24px;
  color: var(--good); border-color: rgba(74, 222, 128, .45);
  font-size: 15px; font-weight: 800;
}
.icon-x.pp-follow:hover { color: var(--good); border-color: var(--good); }
.icon-x.pp-follow.on {
  background: rgba(74, 222, 128, .14); color: var(--good);
  border-color: rgba(74, 222, 128, .30); cursor: default;
}
.icon-x.pp-follow:disabled { opacity: .55; cursor: default; }
/* Room so the meta text never runs under the corner button. */
.plane-panel-body { padding-bottom: 2px; }
/* Only when the follow-+ is actually showing does the body reserve the
   corner — an always-on pad wasted a text line for unresolved planes. */
.plane-panel:has(.pp-follow:not(.hidden)) .plane-panel-body { padding-bottom: 20px; }

/* ---------- journey (cross-referenced sources) ---------- */
.journey:not(:empty) { margin-top: 10px; border-top: 1px solid var(--line); padding-top: 8px; }
.jrow { display: flex; gap: 10px; padding: 5px 0; font-size: var(--fs-body); align-items: baseline; }
/* Labels whisper, values speak. */
.jrow .jlab {
  flex: none; width: 96px; font-size: var(--fs-caption); font-weight: 650;
  text-transform: uppercase; letter-spacing: var(--track-kicker); color: var(--muted);
}
.jrow .jlab + span { font-size: var(--fs-lead); font-weight: 700; color: var(--fg); }
.jrow .jsrc { display: block; font-size: var(--fs-micro); font-weight: 650; color: var(--muted); text-transform: uppercase; letter-spacing: var(--track-caps); }
.jcheck { font-size: var(--fs-body); padding: 7px 10px; border-radius: var(--r-sm); margin-top: 6px; }
.jcheck.warn { background: rgba(251,191,36,.1); border: 1px solid rgba(251,191,36,.3); color: var(--warn); }
.jcheck.ok { background: rgba(74,222,128,.08); border: 1px solid rgba(74,222,128,.25); color: var(--good); }
.jcheck.info { background: rgba(var(--muted-rgb),.08); border: 1px solid var(--line); color: var(--muted); }

/* ---------- feed ---------- */
.feed-item { font-size: var(--fs-body); padding: 7px 0; border-bottom: 1px dashed var(--line); color: var(--muted); }
.feed-item b { color: var(--fg); }

/* ---------- seat map modal ---------- */
.overlay {
  position: fixed; inset: 0; z-index: 50; display: none;
  align-items: flex-end; justify-content: center;
  background: rgba(4, 7, 12, .6); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
}
@media (min-width: 700px) { .overlay { align-items: center; } }
.overlay.open { display: flex; }
.modal {
  background: var(--glass-strong); border: 1px solid var(--line);
  border-radius: var(--r-lg) var(--r-lg) 0 0; width: 100%; max-width: 560px;
  max-height: 88vh; overflow-y: auto; padding: 18px 18px calc(18px + var(--sab));
  backdrop-filter: blur(24px); -webkit-backdrop-filter: blur(24px);
  animation: slideup .28s cubic-bezier(.4,0,.2,1) both;
}
@media (min-width: 700px) { .modal { border-radius: var(--r-lg); } }
@keyframes slideup { from { transform: translateY(40px); opacity: 0; } }
.modal h2 { margin: 0 0 2px; font-size: var(--fs-title); }
.modal .sub { color: var(--muted); font-size: var(--fs-body); margin-bottom: 10px; }
.modal .approx { color: var(--warn); font-size: var(--fs-caption); }
.seat-input { display: flex; gap: 8px; margin: 10px 0; }
.seat-input input { width: 90px; flex: none; text-transform: uppercase; }
.cabin-section { margin: 12px 0; }
.cabin-label { font-size: var(--fs-caption); text-transform: uppercase; letter-spacing: var(--track-caps); color: var(--muted); margin-bottom: 5px; font-weight: 800; }
.seat-row { display: flex; align-items: center; gap: 4px; margin: 2.5px 0; }
.seat-row .rownum { width: 24px; text-align: right; color: var(--muted); font-size: var(--fs-caption); flex: none; }
.seat {
  width: 24px; height: 24px; border-radius: 6px 6px 4px 4px; border: 1px solid var(--line);
  display: flex; align-items: center; justify-content: center; font-size: var(--fs-micro); font-weight: 700;
  color: var(--muted); background: var(--plate-2); cursor: pointer; transition: transform .1s;
}
.seat:active { transform: scale(.9); }
.seat.aisle-gap { background: transparent; border: 0; cursor: default; }
.seat.business, .legend .k-business { background: rgba(251, 191, 36, .18); border-color: rgba(251, 191, 36, .45); }
.seat.premium, .legend .k-premium { background: rgba(var(--accent-rgb), .16); border-color: rgba(var(--accent-rgb), .45); }
.legend .k-exit { background: var(--plate-2); box-shadow: inset 0 0 0 1.5px var(--good); }
.legend .k-mine { background: var(--accent); }
.seat.exit { box-shadow: inset 0 0 0 1.5px var(--good); }
.seat.mine { background: var(--accent); color: #061224; border-color: transparent; }
.legend { display: flex; gap: 13px; flex-wrap: wrap; font-size: var(--fs-caption); color: var(--muted); margin-top: 10px; }
.legend span { display: inline-flex; align-items: center; gap: 5px; }
.legend i { width: 13px; height: 13px; border-radius: 4px; display: inline-block; }

/* ---------- map markers ---------- */
/* Sprite plane (points north; JS rotates it to the true track). */
.plane-marker { width: 26px; height: 26px; color: var(--accent); cursor: pointer;
  filter: drop-shadow(0 0 6px rgba(var(--accent-rgb),.9)); will-change: transform; }
.plane-marker svg { width: 100%; height: 100%; display: block; }
/* Muted "last seen here"-marker for a plane that is no longer transmitting. */
.plane-marker { width: 26px; height: 26px; color: var(--accent);
  filter: drop-shadow(0 1px 3px rgba(0,0,0,.6)); }
.plane-marker svg { width: 100%; height: 100%; display: block; }
.lastseen-dot { width: 22px; height: 22px; color: var(--muted); opacity: .85; }
.lastseen-dot svg { width: 100%; height: 100%; display: block; }
.maplibregl-ctrl-attrib { background: rgba(var(--bg-rgb),.55) !important; }
/* Sammenslått er attribusjonen en BRIKKE som resten av kartchromen —
   ikke MapLibres hvite skive med svart ⓘ, som var det klareste elementet
   på hele kartet. Åpen står innholdet som før. */
.maplibregl-ctrl-attrib.maplibregl-compact:not(.maplibregl-compact-show) {
  background: rgba(var(--bg-rgb), .55) !important;
  border-radius: 999px; backdrop-filter: blur(12px) saturate(1.4);
  -webkit-backdrop-filter: blur(12px) saturate(1.4);
  box-shadow: inset 0 0 0 1px var(--line);
}
.maplibregl-ctrl-attrib-button {
  background-color: transparent !important;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%238e8e93' stroke-width='2' stroke-linecap='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M12 11v5'/%3E%3Cpath d='M12 7.6v.1'/%3E%3C/svg%3E") !important;
  background-size: 15px 15px !important;
  background-position: center !important;
  background-repeat: no-repeat !important;
  opacity: .85;
}
/* MapLibre farger bare LENKENE; ren tekst arver bibliotekets svarte —
   «Data from» sto usynlig i svart på svart når panelet åpnes. */
.maplibregl-ctrl-attrib,
.maplibregl-ctrl-attrib-inner { color: var(--muted) !important; }
.maplibregl-ctrl-attrib a { color: var(--muted) !important; }
.maplibregl-ctrl-attrib-button:focus-visible {
  outline: 2px solid rgba(var(--accent-rgb), .6); outline-offset: 1px;
}
/* OSM attribution must stay readable — lift it clear of the floating dock. */
/* The sheet has THREE snap heights and covers the whole bottom — no bottom
   placement survives them all (the ⓘ peeked half-hidden from behind the
   sheet corner). Park the attribution top-right, under the header chips. */
.maplibregl-ctrl-bottom-left, .maplibregl-ctrl-bottom-right {
  top: calc(var(--sat) + 54px); bottom: auto;
}
.maplibregl-ctrl-bottom-right { right: 6px; }

/* ---------- live flight view ---------- */
#liveView {
  position: fixed; inset: 0; z-index: 45; display: flex; flex-direction: column;
  padding: calc(20px + var(--sat)) 22px calc(24px + var(--sab));
  background: radial-gradient(130% 80% at 50% -10%, rgba(var(--accent-rgb),.14), transparent 62%),
              rgba(var(--bg-rgb),.93);
  backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px);
  animation: livein .3s cubic-bezier(.4,0,.2,1) both;
}
@keyframes livein { from { opacity: 0; transform: scale(1.04); } }
.live-top { text-align: center; position: relative; }
.live-fno {
  font-size: 36px; font-weight: 800; letter-spacing: -.03em; margin-top: 16px;
  color: var(--fg); font-variant-numeric: tabular-nums;
}
.live-route { color: var(--muted); font-weight: 650; font-size: var(--fs-lead); margin-top: 3px; letter-spacing: .01em; }
.live-status { margin-top: 10px; }
.live-status .pill { font-size: var(--fs-body); height: 28px; padding: 0 14px; }
.live-top .map-btn { margin-top: 12px; }

/* follow-cam chip — floats over the map while the camera rides a flight */
#followChip {
  position: fixed; top: calc(66px + var(--sat)); left: 50%; transform: translateX(-50%);
  z-index: 55; background: var(--glass-strong); border: 1px solid rgba(var(--accent-rgb),.45);
  color: var(--accent); padding: 8px 16px; border-radius: 999px;
  font-size: var(--fs-body); font-weight: 800; letter-spacing: .2px;
  display: inline-flex; align-items: center; gap: 7px;
  backdrop-filter: blur(18px); -webkit-backdrop-filter: blur(18px);
  box-shadow: 0 4px 18px rgba(0,0,0,.35); cursor: pointer;
}
#followChip svg { width: 15px; height: 15px; flex: none; }
.live-count { text-align: center; margin: auto 0; }
.live-count-label { color: var(--muted); font-size: var(--fs-caption); font-weight: 650; text-transform: uppercase; letter-spacing: var(--track-kicker); }
.live-count-num {
  font-size: 72px; font-weight: 800; font-variant-numeric: tabular-nums;
  letter-spacing: -.03em; margin: 8px 0 4px; line-height: 1;
  text-shadow: 0 0 40px rgba(var(--accent-rgb),.26);
}
/* A recorded landing time is a fact, not a live countdown — no hero size. */
.live-count-num.t-big { font-size: var(--fs-hero); text-shadow: none; }
.live-eta { color: var(--muted); font-size: var(--fs-body); font-weight: 650; line-height: 1.6; }
/* Frosne tall skal SI at de er frosne — banneret eies av refreshLive alene
   (tikkeren skriver aldri hit) og forsvinner når det er tomt. */
.live-stale { color: var(--warn, #e6a23c); font-size: var(--fs-caption); font-weight: 650; margin-top: 10px; }
.live-stale:empty { display: none; }
.live-progress { display: flex; align-items: center; gap: 12px; margin: 26px 0 8px; }
.live-ap { font-weight: 800; font-size: var(--fs-lead); flex: none; }
.live-bar { position: relative; flex: 1; height: 5px; border-radius: 999px; background: rgba(var(--muted-rgb),.18); }
.live-bar-fill {
  height: 100%; border-radius: 999px; width: 0%;
  background: var(--accent);
  box-shadow: 0 0 14px rgba(var(--accent-rgb),.7); transition: width 1s linear;
}
/* Sprite plane points north; quarter-turn sends it along the bar. */
.live-bar-plane {
  position: absolute; top: 50%; left: 0%; transform: translate(-50%, -50%) rotate(90deg);
  transition: left 1s linear; filter: drop-shadow(0 0 8px rgba(var(--accent-rgb),.9));
}
.live-bar-plane svg { width: 20px; height: 20px; display: block; }
.live-stats { display: flex; justify-content: center; gap: 26px; margin-top: 18px; flex-wrap: wrap; }
.live-stats .st { text-align: center; }
.live-stats .st .v { font-size: var(--fs-title); font-weight: 800; font-variant-numeric: tabular-nums; }
.live-stats .st .k { font-size: var(--fs-caption); color: var(--muted); text-transform: uppercase; letter-spacing: var(--track-caps); font-weight: 700; }
.live-checks { margin-top: 16px; }
.live-checks .jcheck { text-align: center; }
/* ---- the whole round, for a flight number that flies several hops ---- */
.live-stops { margin-top: 22px; }
.live-stops .lr-h {
  font-size: var(--fs-caption); color: var(--muted); text-transform: uppercase;
  letter-spacing: var(--track-caps); font-weight: 700; margin-bottom: 10px;
}
.live-stops ol { list-style: none; margin: 0; padding: 0; }
.live-stops li {
  position: relative; display: flex; align-items: baseline; gap: 10px;
  padding: 0 0 14px 20px; font-size: var(--fs-lead);
}
.live-stops li:last-child { padding-bottom: 0; }
/* The dot, and the line joining it to the next stop. */
.live-stops li::before {
  content: ""; position: absolute; left: 0; top: 6px;
  width: 9px; height: 9px; border-radius: 50%;
  border: 2px solid var(--muted); background: var(--bg);
}
.live-stops li:not(:last-child)::after {
  content: ""; position: absolute; left: 4px; top: 17px; bottom: 1px;
  width: 1px; background: var(--line);
}
/* Flown already: filled dot, dimmed text. */
.live-stops li.done::before { background: var(--muted); }
.live-stops li.done .lr-ap, .live-stops li.done .lr-t { opacity: .55; }
/* The stop the user boards at — the one fact we actually know about them. */
.live-stops li.mine::before {
  border-color: var(--accent); background: var(--accent);
  box-shadow: 0 0 10px rgba(var(--accent-rgb),.7);
}
.live-stops li.mine .lr-ap { color: var(--accent); }
.live-stops .lr-ap { font-weight: 800; flex: none; }
.live-stops .lr-name { color: var(--muted); font-size: var(--fs-body); flex: 1; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.live-stops .lr-t { font-variant-numeric: tabular-nums; color: var(--muted);
  font-size: var(--fs-body); flex: none; }
.live-stops .lr-t b { color: var(--fg); font-weight: 700; }
.live-stops .lr-you { font-size: var(--fs-caption); font-weight: 700; color: var(--accent);
  letter-spacing: var(--track-caps); text-transform: uppercase; flex: none; }
.delay-banner {
  margin-top: 10px; text-align: center; padding: 9px 14px; border-radius: var(--r-md);
  background: rgba(251,191,36,.12); border: 1px solid rgba(251,191,36,.35);
  color: var(--warn); font-weight: 800; font-size: var(--fs-lead);
}

.rebook-banner {
  margin-top: 10px; padding: 9px 12px; border-radius: var(--r-md);
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  background: rgba(248,113,113,.10); border: 1px solid rgba(248,113,113,.35);
  color: var(--bad); font-weight: 800; font-size: var(--fs-lead);
}
.rebook-banner .btn { flex: none; }

/* ---------- search / add flight (Flighty-style) ---------- */
.search-wrap { display: flex; gap: 8px; margin: 12px 0 4px; align-items: stretch; }
.search-box {
  flex: 1; display: flex; align-items: center; gap: 8px; min-width: 0;
  background: rgba(0, 0, 0, .18); border: 1px solid var(--line); border-radius: var(--r-md); padding: 0 12px;
  box-shadow: inset 0 1.5px 3px rgba(0, 0, 0, .30);
  transition: border-color .15s;
}
.search-box:focus-within { border-color: var(--accent); }
.search-box .search-ico { opacity: .55; flex: none; display: flex; }
.search-box .search-ico svg { width: 16px; height: 16px; }
.search-box input { border: 0; background: transparent; box-shadow: none; padding: 12px 0; border-radius: 0; text-transform: uppercase; }
.search-box input::placeholder { text-transform: none; }
.search-box input:focus { border: 0; }
.search-clear {
  position: relative; border: 0; background: transparent; color: var(--muted);
  font-size: var(--fs-lead); cursor: pointer; padding: 4px 2px; flex: none;
}
/* Same invisible thumb-strip as the header chips — vertical only, so it
   never covers taps meant for the input beside it. */
.search-clear::after {
  content: ""; position: absolute; left: 0; right: 0; top: 50%;
  height: 44px; transform: translateY(-50%);
}
.date-chip {
  flex: none; border: 1px solid var(--line); background: rgba(0, 0, 0, .18); color: var(--fg);
  border-radius: var(--r-md); padding: 0 14px; font: inherit; font-weight: 700; font-size: var(--fs-body);
  box-shadow: inset 0 1.5px 3px rgba(0, 0, 0, .30);
  cursor: pointer; white-space: nowrap; transition: transform .12s, border-color .15s;
}
.date-chip:active { transform: scale(.96); }
.date-chip.custom { border-color: rgba(var(--accent-rgb),.5); color: var(--accent); }
/* The native date input sits invisibly ON TOP of the chip so a real tap
   lands on it directly and iOS opens the picker natively — showPicker()/
   click() on a 1px hidden input silently failed on iOS. */
.date-wrap { position: relative; display: flex; }
#dateInput {
  /* PARKED at 0×0, never overlaying the chip: iOS composites the date
     input's own white UA chrome above an opacity-0 overlay — that was the
     mystery white «I dag» pill (computed styles said dark all along).
     The chip opens the picker with showPicker() instead. */
  position: absolute; width: 0; height: 0; opacity: 0;
  margin: 0; padding: 0; border: 0; pointer-events: none;
  -webkit-appearance: none; appearance: none;
}

.results-head { display: flex; align-items: baseline; gap: 8px; margin: 12px 0 2px; }
.results-head .rh-route { font-size: var(--fs-lead); font-weight: 800; }
.results-head .rh-date { color: var(--muted); font-size: var(--fs-body); font-weight: 700; }

.fresult {
  display: flex; gap: 13px; padding: 13px 0; border-bottom: 1px solid var(--line);
  cursor: pointer; align-items: center; transition: background .12s; border-radius: var(--r-md);
  touch-action: manipulation;
}
.fresult:active { background: rgba(var(--accent-rgb),.09); }
.fresult .fr-when { flex: none; width: 66px; }
.fresult .fr-when b { display: block; font-size: var(--fs-big); font-weight: 800; line-height: 1.05; letter-spacing: -.5px; font-variant-numeric: tabular-nums; }
.fresult .fr-when span { font-size: var(--fs-micro); color: var(--muted); text-transform: uppercase; letter-spacing: var(--track-caps); font-weight: 700; }
.fresult .fr-main { flex: 1; min-width: 0; }
.fr-top { display: flex; align-items: center; gap: 8px; }
.fr-no { font-size: var(--fs-body); font-weight: 800; letter-spacing: .3px; color: var(--muted); }
.fr-status { margin-left: auto; font-size: var(--fs-caption); font-weight: 700; color: var(--muted); white-space: nowrap; }
.fr-status.ontime { color: var(--good); }
.fr-status.delay { color: var(--warn); }
.fr-status.bad { color: var(--bad); }
.fr-cities { font-size: var(--fs-lead); font-weight: 800; margin: 3px 0 5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.fr-cities .arrow { color: var(--muted); margin: 0 5px; font-weight: 650; }
/* Route search: the header already names the route, so the flight number +
   airline is the row's primary line and the times step up. */
.fr-flight { font-size: var(--fs-lead); font-weight: 800; letter-spacing: -.01em; }
.fresult.route .fr-times { font-size: var(--fs-lead); margin-top: 4px; }
.fr-times { display: flex; gap: 16px; font-size: var(--fs-body); }
.fr-leg { display: flex; align-items: center; gap: 6px; }
.fr-dot { width: 17px; height: 17px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; font-size: var(--fs-micro); font-weight: 800; flex: none; }
/* The ↗/↘ glyphs already encode direction — times are data, not status.
   Green in a row belongs to .fr-status.ontime alone. */
.fr-dot.dep, .fr-dot.arr { background: rgba(var(--muted-rgb),.14); color: var(--muted); }
.fr-leg .fr-code { color: var(--muted); font-weight: 700; }
.fr-leg b { color: var(--fg); font-variant-numeric: tabular-nums; font-weight: 800; }
.fresult.added { opacity: .48; }
/* Finger-sized (44px HIG) tap target; negative margins eat the row padding
   so the visual + stays where it was. */
.fresult .fr-add {
  flex: none; color: var(--accent); font-weight: 800; font-size: var(--fs-big);
  width: 44px; align-self: stretch; margin: -13px -12px -13px -6px;
  display: flex; align-items: center; justify-content: center;
}
.fresult.added .fr-add { color: var(--good); }

.show-gone {
  display: block; width: 100%; margin: 12px 0 4px; padding: 12px;
  background: none; border: 1px dashed var(--line); border-radius: var(--r-md);
  color: var(--muted); font-size: var(--fs-body); font-weight: 700; letter-spacing: .2px;
  cursor: pointer; touch-action: manipulation;
}
.show-gone:active { background: rgba(var(--accent-rgb),.07); }

/* ---- airport search results + board ---- */
.apresult {
  display: flex; align-items: center; gap: 12px; padding: 12px 0;
  border-bottom: 1px solid var(--line); cursor: pointer; border-radius: var(--r-md);
  transition: background .12s; touch-action: manipulation;
}
.apresult:active { background: rgba(var(--accent-rgb),.09); }
.apresult .ap-pin { flex: none; width: 24px; display: flex; justify-content: center; color: var(--muted); }
.apresult .ap-pin svg { width: 18px; height: 18px; }
.apresult .ap-main { flex: 1; min-width: 0; }
.apresult .ap-name { font-size: var(--fs-lead); font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.apresult .ap-code { font-size: var(--fs-caption); font-weight: 800; color: var(--muted); letter-spacing: .6px; margin-top: 1px; }
.apresult .ap-time { flex: none; font-size: var(--fs-body); font-weight: 700; color: var(--muted); font-variant-numeric: tabular-nums; }

.apboard-head { position: relative; margin: 10px 0 2px; }
/* Back sits on the LEFT — where "back" lives everywhere else on the phone. */
.apboard-back {
  position: absolute; left: 0; top: 0; background: none; border: 0;
  color: var(--accent); font-weight: 700; font-size: var(--fs-body); padding: 2px 2px 2px 0; cursor: pointer;
  touch-action: manipulation;
}
.apb-title { font-size: var(--fs-title); font-weight: 800; letter-spacing: -.3px; padding-left: 58px; }
.apb-title .apb-code { font-size: var(--fs-body); font-weight: 800; color: var(--muted); letter-spacing: .6px; margin-left: 7px; }
.apb-sub { font-size: var(--fs-caption); color: var(--muted); font-weight: 650; margin-top: 2px; }
/* Pulsen («OSL nå: …»): tom slot fra første tegning så linja aldri
   dytter brettet når svaret lander. */
.apb-pulse { font-size: var(--fs-caption); color: var(--muted); margin-top: 3px; min-height: 0; }
.apb-pulse:empty { display: none; }
.apboard-tabs { display: flex; gap: 7px; margin: 11px 0 2px; }
.apboard-tabs button {
  flex: 1; background: rgba(var(--muted-rgb),.06); border: 1px solid var(--line);
  color: var(--muted); border-radius: 999px; padding: 6px 12px; font-weight: 700; font-size: var(--fs-body);
  cursor: pointer; touch-action: manipulation;
}
.apboard-tabs button.active { background: rgba(var(--accent-rgb),.15); color: var(--accent); border-color: rgba(var(--accent-rgb),.5); }
.apbrow .fr-cities { font-size: var(--fs-lead); margin: 2px 0 0; }
.apbrow .fr-cities .fr-code { color: var(--muted); font-weight: 800; font-size: var(--fs-caption); margin-left: 7px; }
.apbrow .fr-when b { font-size: var(--fs-title); font-weight: 800; font-variant-numeric: tabular-nums; }
/* Radar contact, not a bookable flight: the identifier is a broadcast callsign
   that the aircraft re-files on its next rotation, so there is nothing to save.
   Sits where the "+" would be, so the row still lines up. */
.fresult .fr-radar {
  flex: none; align-self: center; font-size: var(--fs-micro); font-weight: 800;
  letter-spacing: var(--track-caps); text-transform: uppercase;
  color: var(--muted); opacity: .7; border: 1px solid var(--line);
  border-radius: 999px; padding: 3px 6px; white-space: nowrap;
}
/* A time borrowed from the OTHER end of the leg, in the OTHER airport's clock.
   It is the only number we have for an outstation we don't poll, so it stays —
   but it must not read as this airport's departure. Muted, lighter, and
   labelled with the direction and airport it actually belongs to. */
.apbrow .fr-when b.borrowed { color: var(--muted); font-weight: 700; }
.apbrow .fr-when .fr-borrowed {
  display: block; font-size: var(--fs-micro); color: var(--muted); font-weight: 700;
  text-transform: uppercase; letter-spacing: var(--track-caps); opacity: .85;
  white-space: nowrap; margin-top: 1px;
}
.apbrow .apb-meta { font-size: var(--fs-caption); color: var(--muted); font-weight: 650; margin-top: 3px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.apbrow .apb-meta .apb-gate { color: var(--fg); font-weight: 800; }
/* Billettnumrene raden slukte. Dempet med vilje: det er en fotnote for
   den som leter etter SITT nummer, ikke informasjon brettet roper. */
.apbrow .apb-meta .apb-cs { opacity: .72; cursor: help;
  border-bottom: 1px dotted currentColor; }

.pp-row .pp-del {
  background: none; border: none; color: var(--muted); opacity: .55;
  font-size: var(--fs-lead); padding: 6px 2px 6px 10px; cursor: pointer; flex: none;
  touch-action: manipulation;
}
.pp-row .pp-del:active { opacity: 1; color: var(--bad); }

.sheet.min .sheet-content { visibility: hidden; }

.countdown .cd-arr { color: var(--muted); font-weight: 700; }

/* The version lives in Profil, not the header — chrome carries no internals. */
.ver-note { margin: 8px 2px 0; text-align: center; font-size: var(--fs-caption); color: var(--muted); }

/* ---------- restyled map popup ---------- */
.maplibregl-popup { max-width: 250px !important; }
.maplibregl-popup-content {
  background: var(--glass-strong) !important; color: var(--fg) !important;
  border: 1px solid var(--line); border-radius: var(--r-md) !important; padding: 11px 14px !important;
  font: inherit; font-size: var(--fs-body); line-height: 1.5;
  backdrop-filter: blur(18px); -webkit-backdrop-filter: blur(18px);
  box-shadow: 0 12px 40px rgba(0, 0, 0, .55) !important;
}
.maplibregl-popup-content b { color: var(--fg); }
.maplibregl-popup-close-button { color: var(--muted) !important; font-size: var(--fs-title) !important; padding: 2px 8px !important; right: 2px !important; }
.maplibregl-popup-anchor-top .maplibregl-popup-tip,
.maplibregl-popup-anchor-top-left .maplibregl-popup-tip,
.maplibregl-popup-anchor-top-right .maplibregl-popup-tip { border-bottom-color: var(--glass-strong) !important; }
.maplibregl-popup-anchor-bottom .maplibregl-popup-tip,
.maplibregl-popup-anchor-bottom-left .maplibregl-popup-tip,
.maplibregl-popup-anchor-bottom-right .maplibregl-popup-tip { border-top-color: var(--glass-strong) !important; }
.maplibregl-popup-anchor-left .maplibregl-popup-tip { border-right-color: var(--glass-strong) !important; }
.maplibregl-popup-anchor-right .maplibregl-popup-tip { border-left-color: var(--glass-strong) !important; }
/* Compact & inline: callsign · route · ETA on one row, meta on the next. */
.pop .pop-top { display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 8px; }
/* Redesignet panelhode (6/8): identitet venstre, ETA som chip HØYRE på
   SAMME linje — aldri en foreldreløs linje to. Rom for ✕ i hjørnet. */
.pop .pop-head {
  display: flex; align-items: baseline; gap: 7px; flex-wrap: nowrap;
  padding-right: 30px; min-width: 0;   /* plass til ✕ i hjørnet */
}
.pop .pop-head .pop-no { font-size: var(--fs-body); font-weight: 800; flex: none; }
.pop .pop-head .pop-route {
  /* Ruta kuttes ALDRI — den er panelets viktigste fakta. Full bredde,
     ingen ellipse; ETA-chipen viker til hoyre. */
  color: var(--fg); font-weight: 700; font-size: var(--fs-caption);
  white-space: nowrap; flex: 0 0 auto;
}
.pop .pop-head .pop-eta {
  flex: none; margin-left: auto;
  font-size: var(--fs-micro); font-weight: 800; font-variant-numeric: tabular-nums;
  color: var(--accent);
  padding: 2px 8px; border-radius: 999px;
  background: rgba(var(--accent-rgb), .12);
}
.pop .pop-head .pop-eta:empty { display: none; }
/* Én dempet identitetslinje: selskap · type · reg. */
.pop .pop-ident {
  color: var(--muted); font-size: var(--fs-micro); margin-top: 1px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Tallene som grid — etikett hvisker, verdi snakker. Tre kolonner. */
.pop .pop-grid {
  /* auto-fit: 2 kolonner paa mobil (smalt ved foto), 4-6 paa brede flater. */
  /* To skarpe kolonner — trenden bor i setningslinja under, ikke i cella
     (den presset gridet til aa stable seg, maalt i 390px). */
  display: grid; grid-template-columns: repeat(auto-fit, minmax(78px, 1fr));
  gap: 3px 10px; margin-top: 4px;
}
.pop .pg { min-width: 0; }
.pop .pg-l {
  display: block; font-size: 9px; font-weight: 650;
  letter-spacing: .1em; text-transform: uppercase;
  color: var(--muted); margin-bottom: 0;
}
.pop .pg-v {
  display: block; font-size: var(--fs-lead); font-weight: 700;
  font-variant-numeric: tabular-nums; line-height: 1.25; white-space: nowrap;
}
.pop .pg-s {
  display: block; font-size: var(--fs-micro); font-weight: 650;
  color: var(--muted); font-variant-numeric: tabular-nums;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.pop .pg-s:empty { display: none; }
.pop .pop-no { font-size: var(--fs-lead); font-weight: 800; }
.pop .pop-route { color: var(--fg); font-weight: 700; }
.pop .pop-eta { color: var(--fg); font-weight: 700; font-size: var(--fs-body); margin-left: auto; }
.pop .pop-meta { color: var(--muted); font-size: var(--fs-caption); margin-top: 4px; }

/* ---------- feeder program ---------- */
.pro-badge { display: inline-block; margin-left: 6px; padding: 1px 7px; border-radius: 999px;
  font-size: var(--fs-micro); font-weight: 800; letter-spacing: var(--track-caps); color: var(--good);
  border: 1px solid rgba(74, 222, 128, .30); background: rgba(74, 222, 128, .08); vertical-align: middle; }
.feeder-row { display: flex; align-items: center; gap: 7px; margin-top: 8px; font-size: var(--fs-body); }
.fx-dot { width: 8px; height: 8px; border-radius: 50%; flex: none; }
.fx-dot.on { background: var(--good); box-shadow: 0 0 8px var(--good); }
.fx-dot.off { background: var(--muted); opacity: .5; }
.feeder-del { margin-left: auto; flex: none; border: none; background: transparent;
  color: var(--muted); font-size: var(--fs-body); line-height: 1; padding: 4px 8px;
  border-radius: var(--r-sm); cursor: pointer; -webkit-tap-highlight-color: transparent; }
.feeder-del:hover { color: var(--fg); background: rgba(var(--muted-rgb), .10); }
.feeder-del.armed { color: var(--bad); background: rgba(248,113,113,.14); font-size: var(--fs-caption); font-weight: 700; }
.feeder-key { font-family: ui-monospace, Menlo, monospace; font-size: var(--fs-body); word-break: break-all;
  background: rgba(0,0,0,.18); box-shadow: inset 0 1.5px 3px rgba(0,0,0,.30);
  border: 1px solid var(--line); border-radius: var(--r-sm); padding: 9px 11px; margin-top: 5px; }
.feeder-cmd { font-family: ui-monospace, Menlo, monospace; font-size: var(--fs-caption); word-break: break-all;
  background: rgba(0,0,0,.18); box-shadow: inset 0 1.5px 3px rgba(0,0,0,.30);
  border: 1px solid var(--line); border-radius: var(--r-sm); padding: 9px 11px; margin-top: 5px; color: var(--fg); }

/* ---------- theme picker (Profil → Tema) ---------- */
/* Each chip is a miniature of its theme, painted by inline --sw-* vars:
   the swatch is the theme's bg, the stripe its accent→accent2 duo. The
   active one wears a ring in the CURRENT theme's accent. */
/* minmax(0,1fr): «Midnattssol» skal ikke få gjøre sin kolonne bredere enn
   de andre (1fr = minmax(auto,1fr) lot min-content vinne, målt 5/9). */
.theme-grid { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 8px; }
.theme-chip {
  position: relative; border: 0; background: none; padding: 0; margin: 0;
  display: flex; flex-direction: column; align-items: center; gap: 7px;
  font: inherit; color: var(--muted); cursor: pointer;
  transition: transform .12s ease;
}
.theme-chip:active { transform: scale(.95); }
.theme-chip .sw {
  position: relative; width: 100%; aspect-ratio: 5 / 4;
  border-radius: var(--r-sm); border: 1px solid var(--line);
  background: var(--sw-bg); overflow: hidden;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .07);
  transition: box-shadow .15s ease, border-color .15s ease;
}
/* The theme's own accent glow, same corner as the login card's radial. */
.theme-chip .sw::before {
  content: ""; position: absolute; inset: 0;
  background: radial-gradient(130% 110% at 22% -8%, var(--sw-glow), transparent 58%);
}
/* Accent → accent2 duo stripe: the swatch's "logo gradient" signature. */
.theme-chip .sw::after {
  content: ""; position: absolute; left: 9px; right: 9px; bottom: 8px; height: 5px;
  border-radius: 999px; background: linear-gradient(90deg, var(--sw-a), var(--sw-b));
}
.theme-chip .nm { font-size: 10px; font-weight: 700; letter-spacing: 0; white-space: nowrap; }
.theme-chip.active { color: var(--fg); }
.theme-chip.active .sw {
  border-color: transparent;
  box-shadow: 0 0 0 2px var(--bg), 0 0 0 3.5px var(--accent);
}
/* Pro-locked: dimmed swatch + a small lock; the tap toasts instead of applying. */
.theme-chip .lock {
  display: none; position: absolute; top: 6px; right: 6px;
  width: 13px; height: 13px; color: rgba(255, 255, 255, .75);
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, .6));
}
.theme-chip.locked .sw { opacity: .5; }
.theme-chip.locked .nm { opacity: .55; }
.theme-chip.locked .lock { display: block; }

/* ---------- sheet drag ---------- */
.sheet.dragging { transition: none !important; }

/* toast */
#toast {
  position: fixed; top: calc(66px + var(--sat)); left: 50%; transform: translateX(-50%) translateY(-8px);
  /* Plate-2 + rim + fall-skygge, ikke det svarte glasset: MÅLT 5/9 i riggen
     — glass-strong (12,12,14 @ .94) over svart kart ga luminans 30 mot 24
     ved siden av. En toast som knapt skiller seg fra bakgrunnen leses
     ikke; den skal LØFTE seg fra flaten. */
  z-index: 60; background: var(--plate-2); border: 1px solid var(--rim);
  box-shadow: 0 12px 32px rgba(0, 0, 0, .45), inset 0 1px 0 rgba(255, 255, 255, .10);
  padding: 10px 18px; border-radius: var(--r-md); font-size: var(--fs-body); font-weight: 650;
  opacity: 0; pointer-events: none;
  transition: opacity .2s ease, transform .34s var(--ease-spring);
  backdrop-filter: blur(18px); -webkit-backdrop-filter: blur(18px); max-width: 88vw;
  text-align: center; text-wrap: balance;
}
#toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
@media (prefers-reduced-motion: reduce) { #toast { transition: opacity .2s ease; } }

/* Broadcast flight data under the panel's meta line: vertical rate, the level
   the autopilot is flying to, Mach, OAT, wind. Dimmer than the meta row — it
   is detail, not identity. */
.pop .pop-live {
  color: var(--muted); font-size: var(--fs-micro); margin-top: 3px; opacity: .85;
  /* Setningen faar bryte til to linjer — den er panelets forklaring. */
  line-height: 1.35;
}
/* 7500/7600/7700. Rare, and the one thing on this panel worth interrupting for. */
.pop .pop-alert {
  margin-top: 5px; padding: 3px 8px; border-radius: 6px; font-size: var(--fs-caption);
  font-weight: 700; color: #fff; background: rgba(225, 29, 72, .92);
  display: inline-block;
}
/* Why the flown line starts out at sea: a statement of fact about coverage,
   not a warning — muted, one line, never competing with the emergency badge. */
.pop .pop-gap { margin-top: 4px; font-size: var(--fs-caption); color: var(--muted); line-height: 1.35; }

/* Panelfoto krymper paa smale flater saa gridet faar puste. */
@media (max-width: 430px) {
  .pop-photo { width: 96px; height: 46px; }
}

/* ---------- admin ---------- */
/* Adminraden bruker kortets eget språk; ingen ny visuell dialekt. */
/* Den ENE destruktive handlingen i adminlista bærer bad-fargen — den er
   ikke en handling blant likemenn. */
.fc-act.danger { color: var(--bad); }
.fc-act.danger[data-armed="1"] { background: rgba(248, 113, 113, .16); }
.adm-row .fc-actions { margin-top: 10px; padding-top: 10px; }
.adm-row .fc-act { font-size: var(--fs-caption); letter-spacing: 0; }
.adm-row .who .dn { display: flex; align-items: center; gap: 7px; }
.adm-row .pill { flex: none; }
#admSummary { margin: 2px 0 10px; }

/* «Tallene er X min gamle» — gult betyr avvik i dette huset, og en flate
   som står med gamle data er nettopp et avvik. Ikke rødt: ingenting er
   ødelagt, vi vet bare ikke om det stemmer lenger. */
.stale-note {
  margin: 0 0 8px; padding: 8px 12px; border-radius: 10px;
  background: rgba(255, 209, 102, .12);
  color: var(--warn); font-size: 13px; font-weight: 600;
}

/* ---------- admin: rene rader + liquid-glass-alert ---------- */
/* Raden er BARE identitet (Brage 8/8): avatar, navn, status-piller, meta —
   handlingene bor i popupen. Hele raden er tappbar. Lista er iOS-gruppert
   plate; ALERTEN er husets liquid-glass — kontrasten er hierarkiet. */
.adm-row { cursor: pointer;
  transition: background .12s, transform .12s;
  /* backwards, ikke both: en ferdig fylt animasjon eier transform og
     ville slukt pressefysikken under. */
  animation: rise .3s ease backwards;
}
.adm-row:active { background: var(--plate-2); transform: scale(.985); }
.adm-row .friend-row { padding: 2px 0; }
.adm-row .chev { margin-left: auto; flex: none; color: var(--muted);
  opacity: .5; padding-left: 8px; }
.adm-row .chev svg { width: 8px; height: 14px; display: block; }
.adm-row .un { font-variant-numeric: tabular-nums; }
#admSummary { text-transform: uppercase; font-size: var(--fs-micro);
  font-weight: 800; letter-spacing: var(--track-kicker);
  color: var(--muted); font-variant-numeric: tabular-nums; }

/* Monogram-gradienter som iOS Kontakter: duo per bruker (hash av navnet),
   lysere topp mot dypere bunn, hvit glyf. Identiteten følger fra rad til
   popup — samme duo begge steder. */
.avatar.mono { color: #fff;
  text-shadow: 0 1px 2px rgba(0, 0, 0, .18);
  box-shadow: inset 0 -8px 14px rgba(0, 0, 0, .16),
              inset 0 1px 0 rgba(255, 255, 255, .35); }
.avatar.mono-0 { background: linear-gradient(180deg, #6ab2ff, #3d7bf5); }
.avatar.mono-1 { background: linear-gradient(180deg, #fc8a7a, #f0503f); }
.avatar.mono-2 { background: linear-gradient(180deg, #ffb06b, #f0813a); }
.avatar.mono-3 { background: linear-gradient(180deg, #7fdc8b, #38b757); }
.avatar.mono-4 { background: linear-gradient(180deg, #6fd7dc, #2fa9c0); }
.avatar.mono-5 { background: linear-gradient(180deg, #8f9bff, #5d60e6); }
.avatar.mono-6 { background: linear-gradient(180deg, #c78fff, #9450ea); }
.avatar.mono-7 { background: linear-gradient(180deg, #ff8fb1, #ef4f88); }

.adm-modal {
  position: fixed; inset: 0; z-index: 60;
  display: flex; align-items: center; justify-content: center;
  background: rgba(4, 6, 10, .48);
  backdrop-filter: blur(14px) saturate(1.3);
  -webkit-backdrop-filter: blur(14px) saturate(1.3);
  animation: admfade .2s ease-out both;
}
@keyframes admfade { from { opacity: 0; } }
.adm-modal-card {
  width: min(320px, 86vw);
  display: flex; flex-direction: column; gap: 8px;
  animation: admpop .32s cubic-bezier(.32, 1.5, .48, 1) both;
}
/* Glasset er EKTE: husets materiale + den lagdelte kanten. Hodet+handlingene
   er ÉN flate, Avbryt en EGEN under — action-sheet-rytmen letter slabben. */
.adm-glass {
  /* .86, ikke husets .78: alerten ligger 8px over TEKST (søkefelt/rader),
     og bokstaver spøker gjennom tynnere glass enn kartet gjør. */
  /* Tokenglass, ikke rgba(12,12,14): i Lys sto en svart slab over en hvit
     side (Brage 6/9: «admin-fanen — når man klikker på en bruker — matcher
     ikke temaene»). --glass-strong er hvit i Lys og mørk ellers. */
  background: var(--glass-strong);
  backdrop-filter: var(--glass-blur-strong);
  -webkit-backdrop-filter: var(--glass-blur-strong);
  border-radius: var(--r-lg); overflow: hidden;
  box-shadow: var(--glass-edge), 0 28px 80px rgba(0, 0, 0, .55);
}
@keyframes admpop {
  from { opacity: 0; transform: scale(.86) translateY(14px); } }
/* Lukking er også en bevegelse — alerter som bare forsvinner føles som
   crash. closeAdmModal setter .closing og venter animasjonen ut. */
.adm-modal.closing { animation: admfade .15s ease-in reverse both; }
.adm-modal.closing .adm-modal-card { animation: admout .15s ease-in both; }
@keyframes admout { to { opacity: 0; transform: scale(.94); } }
.adm-modal-head { text-align: center; padding: 22px 16px 14px; }
.adm-modal-head .avatar { margin: 0 auto 10px;
  width: 56px; height: 56px; font-size: 24px; }
/* Duo-farget aura under det store monogrammet — identiteten LYSER svakt. */
.adm-modal-head .avatar.mono-0 { box-shadow: 0 10px 32px -6px rgba(61, 123, 245, .55); }
.adm-modal-head .avatar.mono-1 { box-shadow: 0 10px 32px -6px rgba(240, 80, 63, .5); }
.adm-modal-head .avatar.mono-2 { box-shadow: 0 10px 32px -6px rgba(240, 129, 58, .5); }
.adm-modal-head .avatar.mono-3 { box-shadow: 0 10px 32px -6px rgba(56, 183, 87, .5); }
.adm-modal-head .avatar.mono-4 { box-shadow: 0 10px 32px -6px rgba(47, 169, 192, .55); }
.adm-modal-head .avatar.mono-5 { box-shadow: 0 10px 32px -6px rgba(93, 96, 230, .55); }
.adm-modal-head .avatar.mono-6 { box-shadow: 0 10px 32px -6px rgba(148, 80, 234, .55); }
.adm-modal-head .avatar.mono-7 { box-shadow: 0 10px 32px -6px rgba(239, 79, 136, .5); }
.adm-modal-name { font-size: var(--fs-title); font-weight: 800;
  letter-spacing: -.01em; color: var(--fg); }
.adm-modal-pills { margin-top: 6px; display: flex; gap: 6px;
  justify-content: center; flex-wrap: wrap; }
.adm-modal-meta { color: var(--muted); font-size: var(--fs-caption);
  margin-top: 7px; line-height: 1.6; font-variant-numeric: tabular-nums; }
/* Handlingsrader som UIAlertController: 17pt, hårstrek over, aksent for
   handling, rød for destruktivt, halvfet Avbryt. */
.adm-act {
  display: block; width: 100%; text-align: center;
  min-height: 44px; padding: 11px 14px;
  font-size: var(--fs-title); font-weight: 650;
  color: var(--accent); background: none; border: 0;
  border-top: 1px solid var(--line); cursor: pointer;
  transition: background .1s;
}
.adm-act:active { background: rgba(var(--muted-rgb), .10); }

/* ---- tilbakemeldinger ----
   Skrivebordet bor i den samme sentrerte alerten som admin-handlingene, så
   det er ÉN modal-form i appen — ikke to som ser nesten like ut. */
.fb-pad { padding: 0 14px 14px; text-align: left; }
.fb-kinds { margin: 0 0 10px; }
.fb-kinds button { font-size: var(--fs-caption); padding: 6px 8px; white-space: nowrap; }
.fb-text {
  width: 100%; box-sizing: border-box; resize: vertical; min-height: 108px;
  background: rgba(var(--muted-rgb), .07); color: var(--fg);
  border: 1px solid var(--line); border-radius: 12px; padding: 10px 12px;
  font: inherit; font-size: var(--fs-body); line-height: 1.4;
}
.fb-text:focus { outline: none; border-color: rgba(var(--accent-rgb), .55); }
.fb-stars { display: flex; align-items: center; gap: 2px; margin-top: 8px; }
.fb-stars button {
  background: none; border: 0; cursor: pointer; padding: 2px 3px;
  font-size: 20px; line-height: 1; color: rgba(var(--muted-rgb), .45);
  transition: color .12s, transform .12s;
}
.fb-stars button.on { color: var(--warn); transform: scale(1.06); }
.fb-hint { margin-left: 8px; color: var(--muted); font-size: var(--fs-caption); }
.fb-stars-inline { color: var(--warn); font-size: var(--fs-caption); }
/* Egne piller: `.s-landed`/`.s-cancelled` BETYR flystatus. Å låne dem hit
   gjorde «ros» til en landing og «feil» til en kansellering — samme farge,
   feil språk. Tre roller, tre tokens, ingen nye farger. */
.fb-kind {
  font-size: var(--fs-micro); font-weight: 800; text-transform: uppercase;
  letter-spacing: var(--track-caps); padding: 2px 7px; border-radius: 999px;
  background: rgba(var(--muted-rgb), .10); color: var(--muted);
}
.fb-kind.k-bug { background: rgba(248, 113, 113, .16); color: var(--bad); }
.fb-kind.k-idea { background: rgba(var(--accent-rgb), .16); color: var(--accent); }
.fb-kind.k-praise { background: rgba(74, 222, 128, .16); color: var(--good); }
.fb-kind.k-done { background: rgba(var(--muted-rgb), .10); color: var(--muted); }
/* Lista viser NOK til å prioritere: to linjer av teksten, ikke én. */
.fb-row .fb-body-1 {
  margin: 3px 0 2px; font-size: var(--fs-body); line-height: 1.35;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.fb-full { white-space: pre-wrap; font-size: var(--fs-body); line-height: 1.45;
  max-height: 46vh; overflow: auto; }

.adm-act.danger { color: #ff6b5e; position: relative; }
/* Armert sletting: rød fylling + en nedtellingslinje som RENNER UT i takt
   med de-arm-timeren (4 s i openAdmUser) — brukeren SER vinduet lukke seg. */
.adm-act.danger[data-armed="1"] { background: rgba(248, 113, 113, .13);
  font-weight: 700; }
.adm-act.danger[data-armed="1"]::after {
  content: ""; position: absolute; left: 0; bottom: 0; height: 2px;
  width: 100%; background: var(--bad); transform-origin: left;
  animation: admdisarm 4s linear forwards;
}
@keyframes admdisarm { to { transform: scaleX(0); } }
.adm-act.cancel { font-weight: 750; color: var(--fg); border-top: 0; }
.adm-act[disabled] { opacity: .45; }

/* Folden (10/8): fremtidige flyvninger som kompakte rader. Samme kort-
   plate, mindre luft; chevronen hinter «det finnes mer». */
.fcard.mini { padding: 11px 16px; cursor: pointer; }
.fcard.mini .fc-head { gap: 8px; }
.fcard.mini:active { background: var(--plate-2); }
.mini-chev { flex: none; color: var(--muted); opacity: .55; padding-left: 2px; }
.mini-chev svg { width: 12px; height: 7px; display: block; }
.fc-head.foldable { cursor: pointer; }
.mini-chev.up { padding-left: 6px; }

/* Teaserens chip-spøkelse: SAMME mål som den ekte .date-chip i embed, så
   min→halv bytter element for element uten at noe flytter seg. Ikke
   interaktiv — hele teaseren åpner søket. */
.sheet-search-teaser .teaser-chip {
  /* MÅLT mot den ekte raden (min vs halv skal være 0 px drift):
     -18px kansellerer teaserens indre padding så spøkelsets høyrekant
     treffer der #dateChip faktisk står; stretch gir samme høyde. */
  margin: 0 -18px 0 auto; flex: none;
  align-self: stretch; display: flex; align-items: center;
  border: 1px solid var(--line); border-radius: 999px;
  padding: 0 14px; font-weight: 700; font-size: var(--fs-body);
  color: var(--fg); background: rgba(0, 0, 0, .18);
  box-shadow: inset 0 1.5px 3px rgba(0, 0, 0, .30);
}

/* App-krom er ikke dokument: ordmerke, faner, chips og etiketter skal ikke
   kunne tekst-markeres — de blå håndtakene + loupen over FLIGHTRAX leste
   som en bug (IMG_7258). Ekte inndatafelt beholder seleksjon. */
body {
  -webkit-user-select: none; user-select: none;
  -webkit-touch-callout: none;
}
input, textarea, [contenteditable] {
  -webkit-user-select: text; user-select: text;
}


/* Skjelett-plassholder (23/8): startmarkupen PÅSTO ting («Legg til
   venner…») som var feil for brukere med venner, synlig til første svar
   landet — målt som førstegangs-flash i flimmerriggen. Rolige striper
   til dataen kommer; påstandstekster settes kun av et LASTET svar. */
.empty.skel { display: grid; gap: 8px; padding: 14px 12px; }
.empty.skel i {
  display: block; height: 12px; border-radius: 6px;
  background: linear-gradient(90deg,
    rgba(var(--muted-rgb), 0.10) 25%,
    rgba(var(--muted-rgb), 0.22) 50%,
    rgba(var(--muted-rgb), 0.10) 75%);
  background-size: 200% 100%;
  animation: fx-skel 1.3s ease-in-out infinite;
}
.empty.skel i:nth-child(2) { width: 72%; animation-delay: 0.12s; }
.empty.skel i:nth-child(3) { width: 45%; animation-delay: 0.24s; }
@keyframes fx-skel { 0% { background-position: 180% 0; } 100% { background-position: -20% 0; } }
@media (prefers-reduced-motion: reduce) {
  .empty.skel i { animation: none; background: rgba(var(--muted-rgb), 0.14); }
}


/* Bryter-rad i grupperte lister (varselvalg, 24/8). Samme høyde og
   hårstrek som .lrow ellers; selve boksen er skjult og <i> tegner
   sporet + knotten, så treffområdet er hele raden. */
.lrow.sw { display: flex; align-items: center; justify-content: space-between;
           gap: 12px; cursor: pointer; }
.lrow.sw input { position: absolute; opacity: 0; pointer-events: none; }
.lrow.sw i {
  flex: none; width: 46px; height: 28px; border-radius: 999px;
  background: rgba(var(--muted-rgb), 0.20); position: relative;
  transition: background .18s ease;
}
.lrow.sw i::after {
  content: ""; position: absolute; top: 3px; left: 3px;
  width: 22px; height: 22px; border-radius: 50%; background: #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, .35);
  transition: transform .18s ease;
}
.lrow.sw input:checked + i { background: var(--accent); }
.lrow.sw input:checked + i::after { transform: translateX(18px); }
.lrow.sw input:focus-visible + i { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) {
  .lrow.sw i, .lrow.sw i::after { transition: none; }
}


/* Været i begge ender (24/8). Egen rad over statistikken, stille i
   uttrykket: dette er kontekst, ikke en av flyvningens fakta. */
.live-weather { display: flex; gap: 14px; justify-content: center;
                flex-wrap: wrap; margin: 2px 0 10px; }
.live-weather:empty { display: none; }
.live-weather .wx { font-size: var(--fs-caption); color: var(--muted);
                    letter-spacing: .01em; }
.live-weather .wx b { color: var(--fg); font-weight: 600; margin-right: 2px; }

/* Året på kart (24/8): delbar parade i passet. Canvasen er 1080×1350
   (Instagram-story-forhold) og skaleres ned i visning — bildet som
   deles skal være skarpt, ikke skjermoppløsning. */
.pp-review { display: grid; gap: 10px; justify-items: center; }
.pp-review.hidden { display: none; }
.pp-review canvas {
  width: 100%; max-width: 420px; height: auto; border-radius: 14px;
  background: var(--bg); box-shadow: 0 6px 22px rgba(0, 0, 0, .35);
}

/* ---------- Pro-låser (docs/PRO-SPLIT.md) ----------
   Ett kort per låst flate: hva Pro gir, og én knapp. I appen åpner
   knappen den native paywallen (fxPaywall), i nettleseren Pro-arket. */
/* .card har border: 0 — kanten må være en inset-skygge for å finnes. */
.pro-lock { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 38%, transparent); }
.pro-lock-title { font-weight: 800; font-size: var(--fs-lead); margin-bottom: 4px; display: flex; align-items: center; gap: 8px; }
.pl-ico { width: 18px; height: 18px; color: var(--accent); flex: none; }
.src-ico { width: 13px; height: 13px; vertical-align: -2px; margin-left: 1px; opacity: .8; }
.pro-lock-why { color: var(--muted); font-size: var(--fs-body); margin-bottom: 10px; }
.pro-perks { margin: 0 0 12px; padding-left: 18px; font-size: var(--fs-body); }
.pro-perks li { margin: 3px 0; }
.pro-lock .btn { width: 100%; display: block; text-align: center; box-sizing: border-box; text-decoration: none; }
.pro-sheet .card-note { margin: 8px 0 0; }
.pro-sheet .card { padding: 16px; }
#tab-friends.pro-locked .fsec { display: none; }
.pro-locked .card.list, .pro-locked .card-note { opacity: .45; pointer-events: none; }
.pro-row { display: flex; align-items: center; gap: 12px; padding: 14px 16px; margin-bottom: 14px; }
.pro-row.cta { background: var(--accent); color: #071019; border-color: transparent; cursor: pointer; }
.pro-row .pr-ico { display: flex; flex: none; }
.pro-row .pr-ico svg { width: 22px; height: 22px; }
.pro-row .pr-ico.good { color: var(--good); }
.pro-row .pr-t { font-weight: 800; font-size: var(--fs-body); }
.pro-row .pr-s { font-size: var(--fs-caption); opacity: .85; }
html.embed #proRow { display: none; }

/* ---------- design-uplift 5/9: faneskift, lys-felt, tekstbryting ----------
   Brage: «font, harmoni, flickering, loading — high end». Hver regel her er
   målt mot et skjermbilde i riggen FØR og ETTER; ingen «pussing». */
/* Et faneskift toner den nye flaten inn (6 px rise). Klassen settes av
   fane-handleren KUN ved ekte bytte, aldri ved re-render — ingen flimmer. */
.tab-in { animation: tab-in .24s var(--ease-out) both; }
@keyframes tab-in { from { opacity: 0; transform: translateY(6px); } }
@media (prefers-reduced-motion: reduce) { .tab-in { animation: none; } }
/* Lys: søkefeltet, datochipen og teaseren var mørke, tunge brønner
   (rgba(0,0,0,.18–.22) + innfelt skygge) på hvit plate. På lyst er
   brønnen Apples systemGray6: nesten ingenting, ingen skygge. */
:root[data-theme="lys"] .search-box,
:root[data-theme="lys"] .date-chip,
:root[data-theme="lys"] .sheet-search-teaser,
:root[data-theme="lys"] .sheet-search-teaser .teaser-chip,
:root[data-theme="lys"] .feeder-key,
:root[data-theme="lys"] .feeder-cmd {
  background: rgba(0, 0, 0, .045);
  box-shadow: inset 0 1px 1.5px rgba(0, 0, 0, .05);
}
:root[data-theme="lys"] nav.tabs button.active {
  background: rgba(0, 0, 0, .06);
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .06);
}
/* Tekst som skal leses som ÉN tanke brekkes balansert; fotnoter unngår
   enslige ord på siste linje. Ignoreres av eldre motorer. */
.empty, .ppo-title, .pro-lock-why, .pro-row .pr-s { text-wrap: balance; }
.card-note, .hint, .ppo-note, .pro-lock-why { text-wrap: pretty; }
/* Glyfen i et tomt rom: samme 40 px currentColor-regel (C4) — nå BRUKT. */
.empty .empty-art { margin-bottom: 10px; }
.empty.tall .empty-art { margin: 0 auto 12px; }
/* Lukk/fjern-kryssene er strekglyfer nå (i-close) — samme størrelse
   overalt, currentColor, så .is-loading (color: transparent) skjuler dem
   nøyaktig som tekst-krysset før. */
.icon-x svg, .search-clear svg, .pp-del svg, .feeder-del svg { width: 12px; height: 12px; display: block; }
.search-clear svg { width: 14px; height: 14px; }
#followChip .fc-x { width: 12px; height: 12px; margin-left: 2px; }
/* Selskapets merke i brett- og søkeradene (design-uplift r3): Flightys rad
   har alltid identiteten først. Liten variant, samme logo/monogram-regel. */
.al-badge.sm { width: 22px; height: 22px; font-size: 8.5px; border-width: 1px; letter-spacing: 0; }
.al-badge.sm .al-logo { padding: 2px; }
/* Lys: dimmingen bak alerter/ark er lettere — iOS dimmer lyst svakt. */
:root[data-theme="lys"] .adm-modal { background: rgba(60, 60, 67, .28); }
:root[data-theme="lys"] .overlay { background: rgba(60, 60, 67, .32); }
.legend i { border: 1px solid var(--line); }

/* Trakten i admin (vekst 6/9): seks tall i tre kolonner, samme plate og
   hårstrek som resten av gruppene — tåler embed-bredden. */
.funnel { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; margin: 0 0 12px; }
.funnel .fn { background: var(--plate); border-radius: var(--r-md); padding: 10px 10px 8px;
  box-shadow: inset 0 0 0 1px var(--line); min-width: 0; }
.funnel .fn b { display: block; font-size: var(--fs-big); font-weight: 800; letter-spacing: -.01em;
  font-variant-numeric: tabular-nums; line-height: 1.1; }
.funnel .fn span { display: block; font-size: var(--fs-caption); color: var(--fg); font-weight: 650; margin-top: 2px; }
.funnel .fn i { display: block; font-style: normal; font-size: var(--fs-micro); color: var(--muted); margin-top: 2px; }

/* Del-knappen (vekst 6/9) er et ikon, ikke et ord: fem ord i handlingsraden
   brakk «Vis mindre» over to linjer. Samme høyde og hårstrek som naboene. */
.fc-act.ico { flex: 0 0 auto; padding: 0 12px; }
.fc-act.ico svg { width: 17px; height: 17px; display: block; }
.fc-act.ico svg[viewBox="0 0 12 7"] { width: 14px; height: 8px; }
