*, *::before, *::after { box-sizing: border-box; }
/* overscroll-behavior on both html AND body: browsers disagree on which
   element is the "root scroller", so setting only one leaves Chrome's
   native pull-to-refresh (and other browsers' rubber-band bounce) free to
   trigger on the other, dragging the whole page — and our fixed header/tab
   bar with it — instead of just scrolling content inside a .cl-scroll panel. */
html, body { height: 100%; margin: 0; overscroll-behavior: none; }
body { background: #f7f9fb; font-family: Inter, system-ui, sans-serif; }
a { color: #006a65; }
a:hover { color: #001629; }
input, select, textarea, button { font-family: Inter, system-ui, sans-serif; }
[hidden] { display: none !important; }

.ms {
  font-family: 'Material Symbols Outlined';
  font-weight: 400; font-style: normal; line-height: 1; letter-spacing: normal;
  text-transform: none; display: inline-block; white-space: nowrap; direction: ltr;
  -webkit-font-feature-settings: 'liga'; -webkit-font-smoothing: antialiased;
  font-variation-settings: 'FILL' 0;
}
.msf { font-variation-settings: 'FILL' 1; }

.cl-scroll::-webkit-scrollbar { width: 0; height: 0; }
/* none (not contain): contain only stops the overscroll from chaining up to
   the page — it still lets the panel itself rubber-band/bounce at its own
   top/bottom edge, which is the "jump" a fast swipe on Profil triggers.
   none removes that local bounce too, and is the standard way to fully kill
   Chrome's pull-to-refresh, which no screen in this app needs. */
.cl-scroll { overscroll-behavior-y: none; -webkit-overflow-scrolling: touch; }

/* position:fixed (not absolute) so the bar is pinned to the real visual
   viewport and kept stable by Safari's compositor while its address bar
   animates open/closed on scroll — an absolute bar tied to #app's own
   (layout-level) box visibly glides during that animation even though svh
   keeps the layout itself from jumping. Desktop's centered phone mockup
   switches back to absolute below so the bar stays inside that box instead
   of pinning to the real browser window.
   `bottom:0` on iOS Safari is still anchored to the *layout* viewport (which
   behaves as if the toolbar is always hidden) — when the toolbar actually
   re-expands, it reclaims screen space the bar doesn't know about, so the
   bar appears to slide down behind it. app.js corrects this live with a
   translateY driven by the Visual Viewport API, which does reflect the
   toolbar's real state; will-change just keeps that transform on its own
   compositing layer. */
#tabBar { position: fixed; left: 0; right: 0; bottom: 0; will-change: transform; }
@media (min-width: 768px) {
  #tabBar { position: absolute; transform: none !important; }
}

/* Same reasoning as #tabBar above, mirrored for the top bar: still in-flow
   content drifts with the rest of the page during Safari's toolbar
   animation, so it's pinned with position:fixed and corrected live via
   visualViewport too. Pulling it out of #app's flex flow leaves a gap where
   it used to sit, so #app gets that same height back as padding-top —
   guarded to mobile only, since desktop keeps the header in normal flow. */
#chromeHeader { position: fixed; top: 0; left: 0; right: 0; will-change: transform; }
@media (min-width: 768px) {
  #chromeHeader { position: static; transform: none !important; }
}

/* #chromeHeader/#tabBar being position:fixed pulls them into the root
   stacking context (viewport-relative), separate from these overlays'
   old position:absolute (which stacked inside #app's own context). Mixed
   fixed/absolute siblings aren't guaranteed to compare z-index the way
   their numbers suggest, and in practice the fixed header/tab bar rendered
   on top of these regardless of z-index. Making the overlays fixed too puts
   everything back in the same stacking arena, where z-index behaves as
   expected again. Desktop keeps them absolute so they stay inside the
   phone-mockup box instead of covering the real browser window. */
#saveConfirm, #missingFieldsModal, #deleteConfirmModal, #mapPickerModal, #menuOverlay, #biteInfoModal, #seaForecastModal, #pressureForecastModal, #windForecastModal, #rainForecastModal, #tempForecastModal, #kartaDetailPanel {
  position: fixed;
  inset: 0;
}
@media (min-width: 768px) {
  #saveConfirm, #missingFieldsModal, #deleteConfirmModal, #mapPickerModal, #menuOverlay, #biteInfoModal, #seaForecastModal, #pressureForecastModal, #windForecastModal, #rainForecastModal, #tempForecastModal, #kartaDetailPanel {
    position: absolute;
  }
}
@media (max-width: 767px) {
  #app { padding-top: calc(52px + env(safe-area-inset-top)); }
}

@keyframes clUp { from { transform: translateY(14px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }
@keyframes clFade { from { opacity: 0; } to { opacity: 1; } }

/* svh (not dvh) on purpose: dvh re-measures live as Safari's address bar
   collapses/expands on scroll, and #app resizing mid-gesture visibly jumps
   the absolute-positioned #tabBar with it. svh always assumes the toolbar
   is visible, so the shell's height never changes during a scroll gesture. */
#app {
  height: 100vh;
  height: 100svh;
  width: 100%;
  display: flex;
  flex-direction: column;
  background: #f7f9fb;
  color: #191c1e;
  position: relative;
  overflow: hidden;
}

/* On phones the app fills the whole viewport edge-to-edge (real app feel).
   On wider screens it's presented as a centered phone mockup instead of
   stretching a mobile layout across the whole window. */
@media (min-width: 768px) {
  body {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    padding: 40px 20px;
    background:
      radial-gradient(circle at 15% 15%, rgba(168, 202, 239, .35), transparent 45%),
      radial-gradient(circle at 85% 85%, rgba(89, 218, 209, .28), transparent 45%),
      #e6eaee;
  }
  #app {
    width: 430px;
    max-width: 430px;
    height: min(900px, calc(100vh - 80px));
    border-radius: 32px;
    box-shadow: 0 30px 70px rgba(0, 43, 73, .25);
    border: 1px solid rgba(0, 43, 73, .08);
  }
}

select.cl-select,
#screen-logga select {
  appearance: none;
  -webkit-appearance: none;
  background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" stroke="%2342474d" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 9l6 6 6-6"/></svg>');
  background-repeat: no-repeat;
  background-position: right 12px center;
}

.btn-hover-light:hover { background: #f2f4f6; }
.btn-hover-ghost:hover { background: #e6e8ea; color: #006a65; }

/* Klickbara rader i spot-popupens fångstlista (Karta) — tryckfeedback. */
.spot-row { transition: background .12s ease; }
.spot-row:hover, .spot-row:active { background: #e0e2e4; }

/* Inner wrapper used by screens that need a flex column to stack cards with
   a `gap` (Detalj, Profil). The flex column lives on this wrapper, never on
   the scrolling section itself — combining `display:flex` and
   `overflow-y:auto` on the same element causes Safari/WebKit to visibly
   jump the sibling bottom tab bar during momentum scroll. Logga/Hem/Topplista
   avoid this by using plain block flow directly inside their scroll container.
   Separately, without flex-shrink:0 here, any child with its own
   `overflow:hidden` (e.g. a rounded photo/avatar) gets an automatic flex
   min-height of 0 and can collapse to 0px when content exceeds the view. */
.screen-flex-col > * {
  flex-shrink: 0;
}

/* Huggprognos-ringen på Hem — dashoffset/stroke sätts i JS när poängen är
   klar; transition ger en mjuk fyllnadsanimation istället för ett hopp. */
#biteScoreArc {
  transition: stroke-dashoffset .6s ease, stroke .3s ease;
}

/* Karta
   z-index:1 (inte bara position:absolute) är avsiktligt: det gör att
   #catchMap får sin egen stacking context, så Leaflets interna lager
   (kart-rutor/markörer/popup-panes, z-index 200-700) stängs in därinne
   istället för att läcka ut och konkurrera med syskon som filterraden
   (z-index:30) i #kartaMapWrap. Utan detta ligger filtret av misstag
   BAKOM kartan hela tiden — osynligt så länge kartrutorna täcker hela
   ytan, men syns genom kanterna så fort Leaflet krymper dem visuellt
   under en nyp-zoom-gest. */
#catchMap { position: absolute; inset: 0; z-index: 1; }
/* Leaflet 1.9 sätter mix-blend-mode: plus-lighter på kartrutorna för att
   dölja glapp i Chrome. I nuvarande Chrome ger det i stället ljusa linjer
   mellan rutorna så fort sidan renderas i en skala som inte är ett heltal
   (webbläsarzoom, Android-telefoner med t.ex. 2,625 i pixeltäthet), eftersom
   kanterna överlappar och adderas. Normal blandning ger varken linjer eller
   glapp. Gäller både Spots-kartan och kartväljaren. */
.leaflet-container img.leaflet-tile { mix-blend-mode: normal; }
.my-location-marker {
  width: 16px; height: 16px; border-radius: 9999px;
  background: #006a65; border: 3px solid #fff;
  box-shadow: 0 0 0 6px rgba(0,106,101,.22);
}

/* Ligger sist i filen så att det vinner över grundregeln för #app.
   Mobil: själva dokumentet rullar, inte de inre .cl-scroll-panelerna.
   Safari krymper bara adressfältet när sidan rullar — med en fast hög #app
   och inre rullpaneler ser Safari aldrig någon rullning och fältet ligger
   kvar i full storlek. Header och flikrad är redan position:fixed (se #tabBar/#chromeHeader ovan),
   så de står kvar medan innehållet rullar under dem.
   !important behövs eftersom skärmarnas overflow/flex sätts som inline-stil
   i index.html (som datorvyn med telefonramen fortfarande använder).
   Spots (#screen-karta) och inloggningen rörs inte: de behåller flex:1 och
   fyller exakt en skärmhöjd, så där finns inget att rulla. */
@media (max-width: 767px) {
  html, body { height: auto; min-height: 100%; }
  #app {
    height: auto;
    min-height: 100vh;
    min-height: 100svh;
    overflow: visible;
  }
  #screen-hem, #screen-logga, #screen-detalj, #screen-topplistan, #screen-profil, #screen-om {
    flex: none !important;
    overflow: visible !important;
  }
  /* Var position:absolute i #app — skulle annars hamna längst ner i ett
     långt dokument i stället för ovanför flikraden. */
  #toast { position: fixed !important; }
  /* Lås bakgrunden när en dialog/overlay är öppen, annars rullar sidan
     bakom den när man drar på den tonade ytan. */
  /* iOS Safari rullar roten (html), så låset måste sitta där — enbart
     body räcker inte, det visade test i simulatorn. */
  html:has(#saveConfirm:not([hidden]), #missingFieldsModal:not([hidden]), #deleteConfirmModal:not([hidden]), #mapPickerModal:not([hidden]), #menuOverlay:not([hidden]), #biteInfoModal:not([hidden]), #seaForecastModal:not([hidden]), #pressureForecastModal:not([hidden]), #windForecastModal:not([hidden]), #rainForecastModal:not([hidden]), #tempForecastModal:not([hidden]), #kartaDetailPanel:not([hidden])),
  body:has(#saveConfirm:not([hidden]), #missingFieldsModal:not([hidden]), #deleteConfirmModal:not([hidden]), #mapPickerModal:not([hidden]), #menuOverlay:not([hidden]), #biteInfoModal:not([hidden]), #seaForecastModal:not([hidden]), #pressureForecastModal:not([hidden]), #windForecastModal:not([hidden]), #rainForecastModal:not([hidden]), #tempForecastModal:not([hidden]), #kartaDetailPanel:not([hidden])) {
    overflow: hidden;
  }
}
