:root {
  --ink: #17172f;
  --muted: #66677d;
  --surface: #ffffff;
  --canvas: #f3f2f8;
  --navy: #15133a;
  --purple: #5844d8;
  --purple-dark: #3e2fa8;
  --lime: #c9f36a;
  --doom-red: #ff4d4d;
  --doom-red-dark: #d92b2b;
  --direct: #e5f7e8;
  --direct-strong: #24733b;
  --playoff: #fff4c7;
  --playoff-strong: #856500;
  /* A distinct, more visible amber for the "uavgjort" fixture-circle fill
     -- --playoff-strong above is tuned as TEXT on a pale --playoff
     background (badges), where dark-on-light works; as a small solid
     13px circle on white it read as a murky brown, hard to tell apart
     from "not played yet" at a glance (Marius flagged this directly). */
  --draw-strong: #d99a00;
  --eliminated: #f0f1f4;
  --eliminated-strong: #626675;
  --focus: #1668d4;
  --border: #dedee8;
  --shadow: 0 18px 55px rgb(30 25 80 / 10%);
  font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  color: var(--ink);
  background: var(--canvas);
}

* {
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
}

html.drag-scroll-active {
  scroll-behavior: auto;
}

body {
  margin: 0;
  min-width: 320px;
  background:
    radial-gradient(circle at 85% 5%, rgb(117 96 255 / 12%), transparent 30rem),
    var(--canvas);
  color: var(--ink);
  line-height: 1.5;
}

.preview-banner {
  display: flex;
  justify-content: center;
  gap: 0.35rem;
  padding: 0.55rem 1rem;
  border-bottom: 1px solid #d6d1f5;
  background: #f0edff;
  color: #30266d;
  font-size: 0.88rem;
  text-align: center;
}

button,
input {
  font: inherit;
}

button {
  cursor: pointer;
}

button:focus-visible,
input:focus-visible,
a:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 3px;
}

.skip-link {
  position: fixed;
  z-index: 100;
  top: 0.75rem;
  left: 0.75rem;
  padding: 0.75rem 1rem;
  border-radius: 0.5rem;
  background: white;
  color: var(--navy);
  transform: translateY(-200%);
}

.skip-link:focus {
  transform: none;
}

.site-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: 72px;
  padding: 0.75rem clamp(1rem, 4vw, 4rem);
  border-bottom: 1px solid rgb(255 255 255 / 12%);
  background: var(--navy);
  color: white;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.75rem;
  min-width: 0;
  color: inherit;
  font-weight: 800;
  text-decoration: none;
}

.brand-mark {
  display: block;
  flex-shrink: 0;
  width: 42px;
  height: 42px;
  border-radius: 13px;
}

/* .brand sits in a space-between row next to .season, and its own flex
   item (.brand-mark plus this span) would otherwise happily overflow or
   wrap the label onto a second line once the header gets narrow -- the
   "min-width: 0" above is what lets a flex child shrink past its content's
   natural width at all, and this is what it shrinks *into*: one line,
   ellipsis rather than a wrap, if it ever truly runs out of room. */
.brand-name {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.season {
  flex-shrink: 0;
  color: #c9c6e9;
  font-size: 0.9rem;
}

.page-shell {
  padding-bottom: 5rem;
}

.hero {
  /* Trimmed from clamp(2.75rem, 7vw, 6rem)/clamp(5rem, 10vw, 8rem) -- that
     gave up to 96px of empty top padding and 128px of empty bottom padding
     on a wide desktop, pushing "Ranger lagene" further down than the hero
     copy's own content needed. .ranking-card's -3.5rem (56px) margin-top
     already pulls it up over part of the bottom padding, so the visible
     gap after the deadline card is this bottom value minus 56px -- kept
     comfortably above zero rather than trimmed to the same size as the top. */
  padding: clamp(2rem, 4vw, 3rem) max(1rem, calc((100vw - 1180px) / 2));
  padding-bottom: clamp(4.25rem, 6vw, 5rem);
  background:
    linear-gradient(120deg, rgb(88 68 216 / 42%), transparent 55%),
    var(--navy);
  color: white;
}

/* The detailed circular emblem (as opposed to .brand-mark's small plain
   ball in the header) -- sits above the hero copy here, left-aligned with
   the eyebrow/heading/intro below it rather than centered, since the hero
   itself can be much wider than its text column on a large screen and a
   centered image would float away from that left-aligned text. Reused
   (with a centered override, a narrower column suits that) above the
   heading on /poengberegning -- see .scoring-emblem in scoring.css. */
/* Logo and eyebrow+heading sit on the same row on desktop so the emblem no
   longer pushes the heading down below its own full height (Marius: "samme
   linje som logoen") -- .hero-top is the flex row, .hero-heading is just the
   eyebrow+h1 stack that sits beside the logo. Reverted to a plain stacked
   column on narrow screens (see the max-width: 720px block below), where a
   fixed-width logo beside the large heading has no room to breathe. */
.hero-top {
  display: flex;
  align-items: center;
  gap: 1.25rem;
  margin: 0 0 1rem;
}

.hero-heading {
  display: flex;
  flex-direction: column;
}

.hero-emblem {
  display: block;
  flex: none;
}

.hero-emblem img {
  width: clamp(96px, 22vw, 140px);
  height: auto;
}

.eyebrow {
  margin: 0 0 0.55rem;
  color: var(--purple);
  font-size: 0.75rem;
  font-weight: 850;
  letter-spacing: 0.13em;
  text-transform: uppercase;
}

.hero .eyebrow {
  color: var(--lime);
}

h1,
h2,
p {
  overflow-wrap: anywhere;
}

h1 {
  max-width: 800px;
  margin: 0;
  font-size: clamp(2.25rem, 6vw, 4.8rem);
  line-height: 0.98;
  letter-spacing: -0.05em;
}

h2 {
  margin: 0;
  font-size: clamp(1.45rem, 3vw, 2rem);
  line-height: 1.15;
  letter-spacing: -0.025em;
}

.intro {
  max-width: 720px;
  margin: 1rem 0 0;
  color: #d9d7f0;
  font-size: clamp(1rem, 2vw, 1.2rem);
}

/* A deliberately visible pill, not a plain inline link -- this is the
   hero's one call-to-action pointing at /poengberegning, and it needs to
   read as "there's more to see here" at a glance, including to someone who
   never scrolls past the deadline card right below it. */
.scoring-teaser {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  margin-top: 1.1rem;
  padding: 0.5rem 0.9rem;
  border: 1px solid rgb(201 243 106 / 45%);
  border-radius: 999px;
  color: var(--lime);
  font-weight: 700;
  font-size: 0.92rem;
  text-decoration: none;
}

.hero-links {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin-top: 1.1rem;
}

.hero-links .scoring-teaser {
  margin-top: 0;
}

.scoring-teaser:hover {
  border-color: var(--lime);
  background: rgb(201 243 106 / 10%);
}

.deadline-card {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1.1rem;
  width: 100%;
  margin-top: 1.5rem;
  padding: 1.25rem 1.5rem;
  border: 2px solid var(--doom-red);
  border-radius: 1rem;
  background: linear-gradient(120deg, rgb(255 77 77 / 20%), rgb(255 255 255 / 6%));
  box-shadow: 0 0 0 1px rgb(255 77 77 / 25%), 0 16px 34px -14px rgb(255 77 77 / 65%);
}

.deadline-card > div {
  display: grid;
  flex: 1;
  min-width: 0;
  gap: 0.15rem;
  color: white;
  font-size: 1rem;
}

.deadline-card time {
  font-weight: 700;
  overflow-wrap: anywhere;
}

.deadline-icon {
  display: grid;
  width: 52px;
  height: 52px;
  flex: 0 0 auto;
  place-items: center;
  border-radius: 50%;
  background: var(--doom-red);
  color: white;
  font-size: 1.6rem;
  box-shadow: 0 0 0 4px rgb(255 77 77 / 25%);
}

.deadline-label {
  color: white;
  font-weight: 900;
  font-size: 1.05rem;
  letter-spacing: 0.03em;
  text-transform: uppercase;
}

.config-badge,
.countdown {
  max-width: 100%;
  padding: 0.6rem 0.9rem;
  border-radius: 999px;
  background: var(--doom-red-dark);
  color: white;
  font-size: 0.95rem;
  font-weight: 800;
  white-space: normal;
  overflow-wrap: anywhere;
}

/* The countdown pill is always this same alarming red, so on its own it
   can't signal that the deadline has become genuinely close. In the last
   24 hours (toggled from ranking.js), switch to the brighter red already
   used elsewhere for the "eliminated" zone marker, add a visible ring and a
   slow pulse -- prefers-reduced-motion above already clamps the animation
   to effectively nothing for anyone who has that preference set. */
.countdown.is-urgent {
  background: var(--doom-red);
  box-shadow: 0 0 0 3px rgb(255 77 77 / 35%);
  animation: countdown-urgent-pulse 2s ease-in-out infinite;
}

@keyframes countdown-urgent-pulse {
  0%,
  100% {
    box-shadow: 0 0 0 3px rgb(255 77 77 / 35%);
  }
  50% {
    box-shadow: 0 0 0 7px rgb(255 77 77 / 15%);
  }
}

.ranking-card,
.next-step {
  width: min(calc(100% - 2rem), 1180px);
  margin-inline: auto;
  border: 1px solid rgb(222 222 232 / 70%);
  border-radius: 1.35rem;
  background: var(--surface);
  box-shadow: var(--shadow);
}

.ranking-card {
  margin-top: -3.5rem;
  padding: clamp(1rem, 3vw, 2rem);
}

.recover-callout {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.9rem;
  margin-bottom: 1.25rem;
  padding: 0.9rem 1.1rem;
  border: 1px solid #d6d1f5;
  border-radius: 0.9rem;
  background: #f0edff;
  color: #30266d;
}

.recover-callout p {
  margin: 0;
  flex: 1 1 16rem;
  min-width: 0;
}

/* .recover-callout has exactly one of its two variant children (anonymous
   vs. an already-active session) visible at a time -- "display: contents"
   lets whichever one is showing hand its own <p>/<button> straight to the
   parent's flex layout above, unchanged, rather than needing its own
   nested flex rules; [hidden] still wins so the other variant collapses
   away completely instead of leaving an empty flex item behind. */
.recover-callout > div {
  display: contents;
}

.recover-callout > div[hidden] {
  display: none;
}

.ranking-heading {
  display: flex;
  align-items: end;
  justify-content: space-between;
  gap: 1rem;
}

.save-area {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  color: var(--muted);
  font-size: 0.82rem;
}

.save-dot {
  width: 0.55rem;
  height: 0.55rem;
  border-radius: 50%;
  background: #5faa6a;
}

.save-area.saving .save-dot {
  animation: pulse 0.8s ease-in-out infinite alternate;
}

.save-area.unsaved .save-dot {
  background: var(--muted);
}

.zone-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 0.55rem;
  margin: 1.3rem 0;
}

.legend-item {
  display: inline-flex;
  align-items: center;
  gap: 0.38rem;
  min-height: 32px;
  padding: 0.35rem 0.65rem;
  border-radius: 999px;
  font-size: 0.75rem;
  font-weight: 720;
}

.legend-direct {
  background: var(--direct);
  color: var(--direct-strong);
}

.legend-playoff {
  background: var(--playoff);
  color: var(--playoff-strong);
}

.legend-eliminated {
  background: var(--eliminated);
  color: var(--eliminated-strong);
}

/* This 721-999px column layout (also used by .club-row below) narrowed
   the identity column from 260px to 180px and the data column from 324px
   to 240px -- a pre-existing bug, unrelated to the row redesign: at these
   widths the old columns' combined minimum (826px) was already wider than
   the viewport itself, causing horizontal overflow that
   tests/test_browser.py's scrollWidth assertion caught. Not visually
   checked in a real browser (no browser available in this environment) --
   verify with a screenshot at ~820px before trusting this fully. */
.table-labels {
  display: grid;
  grid-template-columns: 64px minmax(180px, 1fr) 240px 142px;
  gap: 0.75rem;
  padding: 0 0.85rem 0.45rem;
  color: var(--muted);
  font-size: 0.68rem;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.table-labels span:last-child {
  text-align: center;
}

/* Hidden until the fixture-circles column itself appears -- see the
   min-width: 1000px override below, which also widens grid-template-columns
   to make room for it. Kept out of the base 4-column grid entirely
   (display: none removes it from grid flow) rather than given its own base
   column, so nothing here needs to change for any narrower breakpoint. */
.table-labels-fixtures {
  display: none;
  text-align: center;
}

.table-labels-data {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.6rem;
}

/* Child combinator (>), not a descendant selector -- ".table-labels-data
   span:first-child" looked like it only targeted the "Seedingspott" label
   span, but :first-child matches relative to EACH element's own parent,
   and the nested .fixtures-info icon inside that label happens to be the
   only (so also first-child) element child of ITS OWN parent. The old,
   more general selector matched both spans, and its higher specificity
   than plain ".fixtures-info" silently overrode the icon's own
   "display: inline-grid; place-items: center" with "inline-flex" --
   under flex layout, place-items/justify-items has no effect, so the "i"
   glyph sat jammed at the flex-start (left) edge of the circle instead of
   centered. Confirmed live: forcing the icon back to inline-grid centers
   it exactly (measured equal 6.8px gaps on both sides). The child
   combinator scopes this rule to .table-labels-data's own direct
   children only, so it can no longer reach the nested icon. */
.table-labels-data > span:first-child {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  white-space: nowrap;
}

.fixtures-info {
  display: inline-grid;
  place-items: center;
  width: 18px;
  height: 18px;
  min-width: 18px;
  min-height: 18px;
  border: 1px solid #c8c6dc;
  border-radius: 50%;
  color: var(--purple-dark);
  font-size: 0.6rem;
  font-weight: 850;
  cursor: help;
  background: transparent;
}

.club-list {
  display: grid;
  gap: 0.48rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.club-row {
  position: relative;
  display: grid;
  grid-template-columns: 64px minmax(180px, 1fr) 240px 142px;
  align-items: center;
  gap: 0.75rem;
  min-height: 78px;
  padding: 0.62rem 0.75rem;
  border: 1px solid var(--border);
  border-left: 5px solid transparent;
  border-radius: 0.9rem;
  transition: transform 140ms ease, box-shadow 140ms ease, background-color 140ms ease, opacity 180ms ease;
}

.club-row.zone-direct {
  border-left-color: var(--direct-strong);
  background: linear-gradient(90deg, var(--direct), white 24%);
}

.club-row.zone-playoff {
  border-left-color: #c39b18;
  background: linear-gradient(90deg, var(--playoff), white 24%);
}

.club-row.zone-eliminated {
  border-left-color: #a5a7b0;
  background: linear-gradient(90deg, var(--eliminated), white 24%);
}

.club-row.zone-start {
  margin-top: 2rem;
}

.zone-label {
  display: none;
}

.club-row.zone-start .zone-label {
  position: absolute;
  bottom: calc(100% + 0.35rem);
  left: 0.25rem;
  display: block;
  color: var(--muted);
  font-size: 0.68rem;
  font-weight: 850;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.club-row.drag-origin {
  opacity: 0.38;
}

.drag-preview {
  position: fixed;
  z-index: 50;
  margin: 0;
  pointer-events: none;
  transform: scale(1);
  box-shadow: 0 2px 6px rgb(25 21 70 / 14%);
  transition: transform 180ms cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 180ms ease;
}

.drag-preview.lifted {
  transform: scale(1.035) rotate(1.1deg);
  box-shadow: 0 20px 42px rgb(25 21 70 / 32%);
}

.drop-marker {
  position: relative;
  height: 12px;
  margin: -0.15rem 0;
  border-radius: 999px;
  background: var(--purple);
  box-shadow: 0 0 0 3px white, 0 0 0 5px rgb(88 68 216 / 28%);
}

.drop-marker::after {
  position: absolute;
  top: 50%;
  right: 0.4rem;
  padding: 0.08rem 0.4rem;
  border-radius: 999px;
  background: var(--navy);
  color: white;
  content: "Plass " attr(data-position);
  font-size: 0.65rem;
  font-weight: 800;
  transform: translateY(-50%);
}

.position-button,
.drag-handle,
.move-button {
  display: grid;
  min-width: 44px;
  min-height: 44px;
  place-items: center;
  border: 1px solid transparent;
  border-radius: 0.75rem;
  background: transparent;
  color: inherit;
}

.position-button {
  width: 52px;
  background: rgb(255 255 255 / 75%);
  color: var(--navy);
  font-size: 1.1rem;
  font-weight: 900;
}

.position-button:hover,
.move-button:hover,
.drag-handle:hover {
  border-color: #cbc8e8;
  background: white;
}

.club-row.zone-direct .position-button {
  background: var(--direct-strong);
  color: white;
}

.club-row.zone-playoff .position-button {
  background: var(--playoff-strong);
  color: white;
}

.club-row.zone-eliminated .position-button {
  background: var(--doom-red-dark);
  color: white;
}

/* Overrides the shared white-on-hover rule just above for the
   tier-colored badge specifically -- turning solid green/amber/red white
   on hover would read as a glitch, not a hover state. */
.position-button:hover {
  border-color: transparent;
  filter: brightness(0.93);
}

.club-identity {
  display: flex;
  min-width: 0;
  align-items: center;
  gap: 0.65rem;
}

.club-emblem {
  position: relative;
  display: grid;
  width: 56px;
  height: 56px;
  flex: 0 0 auto;
  place-items: center;
  border: 1px solid #e0dfea;
  border-radius: 14px;
  background: rgb(255 255 255 / 82%);
  cursor: help;
}

.club-emblem img {
  width: 48px;
  height: 48px;
  object-fit: contain;
}

.emblem-fallback {
  display: none;
  color: var(--muted);
  font-size: 1.5rem;
}

.club-emblem.emblem-load-error .emblem-fallback {
  display: block;
}

/* Wraps .flag so it can carry the shared [data-tooltip] custom tooltip --
   the same reason .club-emblem wraps its own <img> instead of putting
   data-tooltip on the image directly: browsers do not reliably render
   generated ::before/::after content on replaced elements like <img>. */
.flag-wrap {
  position: relative;
  display: inline-flex;
  flex: 0 0 auto;
}

.flag {
  width: 28px;
  height: 21px;
  object-fit: cover;
  border-radius: 2px;
  /* A thin inset line so a flag with a white edge (e.g. England's) still
     reads as a distinct rectangle against the row's own white background. */
  box-shadow: inset 0 0 0 1px rgb(0 0 0 / 14%);
  cursor: help;
}

.club-names {
  display: grid;
  min-width: 0;
}

.club-names strong {
  font-size: 0.98rem;
  line-height: 1.2;
}

/* Sizing/positioning/tooltip host only -- no clip-path here. clip-path on
   an element clips everything painted inside it, including an
   absolutely-positioned ::after, so putting the star's clip-path directly
   on .titles was silently clipping away its own [data-tooltip] popover.
   See .titles-shape below for the actual star shape. */
.titles {
  display: inline-grid;
  width: 32px;
  height: 32px;
  min-width: 32px;
  min-height: 32px;
  flex: 0 0 auto;
  cursor: help;
}

.titles-shape {
  display: grid;
  width: 100%;
  height: 100%;
  place-items: center;
  background: #ffe16b;
  clip-path: polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%);
}

/* Shared custom tooltip for anything carrying data-tooltip (the titles
   star, and the club emblem's qualification-route note), replacing the
   plain OS title attribute. It opens upward so it paints over the row
   above rather than under the row below -- rows share no explicit
   z-index, so later rows in the list already paint on top of earlier
   ones by DOM order. Anchors need position: relative; .titles and
   .club-emblem already have (or gain here) that.

   :hover and :focus-visible alone leave touch devices with no way to ever
   see this content -- there's no hover, and a plain <span> isn't reachable
   by tapping without tabindex. Both elements get tabindex="0" in the
   template, and ranking.js toggles the ".tooltip-open" class below on tap,
   dismissing it on an outside tap/click or Escape -- :focus-visible on its
   own was not reliable enough across mobile browsers to depend on alone. */
[data-tooltip] {
  position: relative;
}

[data-tooltip]::after,
[data-tooltip]::before {
  opacity: 0;
  pointer-events: none;
  transition: opacity 120ms ease, transform 120ms ease;
}

/* ::after's content is "none" (no generated box at all) until the tooltip
   is actually shown, rather than always-generated-but-invisible. A closed
   [data-tooltip]::after used to still occupy its full max-width box even
   at opacity:0 -- that box still counts toward the PAGE's scrollWidth, for
   every single tooltip trigger on the page, all the time, whether anyone
   is hovering it or not. With 36 rows each carrying several tooltips
   (emblem, flag, titles, fixtures), one sitting close enough to a page
   edge was consistently pushing the whole page's scrollWidth past the
   viewport width -- confirmed directly: forcing every tooltip's content
   to "none" brought scrollWidth back to exactly innerWidth at 390px,
   820px and 1440px, with nothing else changed. The "content 0s 150ms"
   delay below keeps the real text in place through the closing fade
   (120ms), only removing the box just after; opening switches back to
   real content immediately ("content 0s" with no delay, in the
   :hover/:focus-visible/.tooltip-open rule further down). Not visually
   checked in a real browser -- no browser is available in this
   environment -- so please confirm tooltips still look right (appear
   promptly, don't look broken) before trusting this fully. */
[data-tooltip]::after {
  content: none;
  position: absolute;
  bottom: calc(100% + 0.6rem);
  left: 50%;
  z-index: 20;
  width: max-content;
  max-width: min(240px, calc(100vw - 2rem));
  padding: 0.55rem 0.75rem;
  border-radius: 0.65rem;
  background: var(--navy);
  color: #fff;
  font-size: 0.78rem;
  font-weight: 600;
  /* Explicit, not inherited: .fixtures-info's ancestor (.table-labels)
     sets text-transform: uppercase for its own header-row labels, and
     that inherits into this tooltip bubble's generated content same as
     white-space did above -- shouting the seeding-pot explanation in
     capitals was never intended. Reset it here so every [data-tooltip]
     bubble reads in normal case regardless of what its trigger element
     happens to sit inside. */
  text-transform: none;
  line-height: 1.4;
  text-align: center;
  /* white-space is inherited, so without setting it explicitly here this
     tooltip silently picks up "nowrap" whenever its trigger sits inside an
     ancestor that sets white-space: nowrap for unrelated layout reasons --
     exactly what happened with the Seedingspott icon after
     ".table-labels-data > span:first-child" gained "white-space: nowrap"
     (to keep that label + icon on one line). The tooltip text then refused
     to wrap inside its max-width box, so long tooltips (like this one)
     rendered on a single line that ran off the right edge of the navy box
     in white text on the page's white background -- invisible, not just
     clipped. Confirmed live: forcing white-space back to normal here makes
     the same tooltip wrap correctly inside its box. Setting it explicitly
     on every [data-tooltip]::after breaks that inheritance chain for good,
     regardless of what white-space value any future ancestor sets. */
  white-space: normal;
  box-shadow: var(--shadow);
  /* --tooltip-shift is set from ranking.js right before the tooltip opens,
     nudging it sideways just enough to stay fully on-screen when the
     trigger sits close to the left/right edge of a narrow viewport --
     centering alone (the plain -50%) can run the box off-screen there.
     The arrow below stays centered on the trigger (no shift) so it still
     visibly points at what it describes. */
  transform: translate(calc(-50% + var(--tooltip-shift, 0px)), 4px);
  transition: opacity 120ms ease, transform 120ms ease, content 0s linear 150ms;
}

[data-tooltip]::before {
  content: "";
  position: absolute;
  bottom: calc(100% + 0.25rem);
  left: 50%;
  z-index: 20;
  border: 6px solid transparent;
  border-top-color: var(--navy);
  transform: translateX(-50%);
}

[data-tooltip]:hover::after,
[data-tooltip]:focus-visible::after,
[data-tooltip].tooltip-open::after {
  content: attr(data-tooltip);
  opacity: 1;
  transform: translate(calc(-50% + var(--tooltip-shift, 0px)), 0);
  transition: opacity 120ms ease, transform 120ms ease, content 0s;
}

[data-tooltip]:hover::before,
[data-tooltip]:focus-visible::before,
[data-tooltip].tooltip-open::before {
  opacity: 1;
}

.titles-count {
  margin-top: 0.3em;
  color: #2a2558;
  font-size: 0.7rem;
  font-weight: 800;
}

/* The "biathlon target": one circle per league-phase matchday (8 total),
   hidden below the min-width: 1000px breakpoint where it gets its own grid
   column (see .club-row/.table-labels there) -- there simply is not room
   for 8 more circles on a laptop-narrow-or-smaller layout, and Marius asked
   for desktop/laptop only. Hidden the same way .table-labels-fixtures is:
   display: none removes it from grid flow so club-data/row-actions still
   land in the base 4-column grid exactly as before this feature existed. */
.fixture-circles {
  display: none;
  align-items: center;
  justify-content: center;
  gap: 4px;
}

.fixture-circle {
  width: 13px;
  height: 13px;
  flex: 0 0 auto;
  border: 1.5px solid var(--border);
  border-radius: 50%;
  background: #fff;
  cursor: help;
}

.fixture-circle.is-played {
  border-color: transparent;
}

.fixture-circle.outcome-win {
  background: var(--direct-strong);
}

.fixture-circle.outcome-loss {
  background: var(--doom-red-dark);
}

.fixture-circle.outcome-draw {
  background: var(--draw-strong);
}

.club-data {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.6rem;
  color: var(--muted);
  font-size: 0.78rem;
}

/* Plain display values, not inputs -- but with no cursor set at all here,
   the browser falls back to its default text-selection (I-beam) cursor on
   hover, since these spans hold ordinary selectable text. That reads as
   "click here to type", same complaint Marius raised about these two
   columns. Every other hoverable bit on this row (emblem, flag, titles,
   fixture dots) already sets an explicit cursor (help, for its tooltip) --
   these two were the only ones left on the implicit default. "default"
   here means the plain arrow, not "leave as browser default". */
.club-data > span {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  /* Centers both data columns (seedingspott, CL siden 92/93) under their
     centered header labels in .table-labels-data. Previously only set on
     :last-child, so the seedingspott number sat at its flex-start default
     (left-pinned) instead of centered under "SEEDINGSPOTT" above it --
     invisible for the second column only because "CL siden 92/93"'s
     values are wide two-digit numbers that visually fill more of the
     column regardless, while seedingspott's single-digit 1-4 made the
     mismatch obvious. Reported live by Marius. */
  justify-content: center;
  gap: 0.25rem;
  cursor: default;
}

.club-data strong {
  color: var(--ink);
}

.data-label {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
}

.row-actions {
  display: flex;
  justify-content: center;
  gap: 0.25rem;
}

.drag-handle {
  touch-action: none;
  border-color: #cbc8e8;
  background: rgb(255 255 255 / 70%);
  box-shadow: 0 1px 2px rgb(25 21 70 / 10%);
  color: var(--purple-dark);
  font-size: 1.25rem;
  cursor: grab;
}

.site-footer {
  width: min(calc(100% - 2rem), 900px);
  margin: 2.5rem auto 0;
  color: var(--muted);
  font-size: 0.72rem;
  text-align: center;
}

.drag-handle:active {
  transform: scale(0.94);
  cursor: grabbing;
}

.move-button {
  border-color: #cbc8e8;
  background: rgb(255 255 255 / 70%);
  box-shadow: 0 1px 2px rgb(25 21 70 / 10%);
  color: var(--muted);
}

.move-button:not(:disabled):active {
  transform: scale(0.94);
}

.move-button:disabled {
  cursor: not-allowed;
  opacity: 0.35;
  box-shadow: none;
}

.jump-top,
.jump-bottom {
  display: none;
}

@media (min-width: 1000px) {
  .jump-top,
  .jump-bottom {
    display: grid;
    min-width: 36px;
    min-height: 36px;
    font-size: 0.95rem;
  }

  .club-row,
  .table-labels {
    grid-template-columns: 64px minmax(260px, 1fr) 324px 236px;
  }

  .row-actions {
    gap: 0.2rem;
  }

  .two-col-form {
    grid-template-columns: minmax(0, 420px) 1fr;
    column-gap: 1.5rem;
  }

  .two-col-form .auth-form-fields {
    grid-column: 1;
    grid-row: 1;
  }

  .two-col-form .auth-form-aside {
    grid-column: 2;
    grid-row: 1;
    align-self: start;
  }

  .two-col-form .auth-form-actions,
  .two-col-form .form-error {
    grid-column: 1 / -1;
  }
}

/* A separate, wider breakpoint from the one above: the fixture-circles
   column needs ~150px of its own room, which the 1000px layout doesn't
   have to spare (verified with Playwright -- adding it there overflowed
   the row at 1000-1099px). "Desktop/laptop" per Marius's own request, so
   gating it further out than the general desktop polish above is the
   right tradeoff rather than shrinking other columns to fit. */
@media (min-width: 1180px) {
  .club-row,
  .table-labels {
    grid-template-columns: 64px minmax(240px, 1fr) 150px 324px 236px;
  }

  .fixture-circles {
    display: flex;
  }

  .table-labels-fixtures {
    display: block;
  }
}

.ranking-footer {
  display: flex;
  justify-content: space-between;
  gap: 0.75rem;
  margin-top: 2rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--border);
}

/* Groups "Nullstill alfabetisk" with the two new "Ranger etter ..."
   buttons on the left of the footer, so .ranking-footer's own
   space-between still just balances two things: this group, and
   #continue-button on the right -- exactly as when there was only one
   button on the left. flex-wrap keeps three buttons from overflowing a
   narrow desktop window before the column layout below kicks in. */
.ranking-footer-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
}

.primary-button,
.secondary-button,
.danger-neutral-button {
  min-height: 48px;
  padding: 0.7rem 1rem;
  border: 1px solid transparent;
  border-radius: 0.75rem;
  font-weight: 800;
}

.primary-button {
  background: var(--purple);
  color: white;
}

.primary-button:hover {
  background: var(--purple-dark);
}

.secondary-button,
.danger-neutral-button {
  border-color: #cbc9db;
  background: white;
  color: var(--ink);
}

.secondary-button:hover,
.danger-neutral-button:hover {
  background: #f1f0f7;
}

.next-step {
  margin-top: 1.5rem;
  padding: clamp(1.5rem, 5vw, 3.25rem);
}

.not-submitted {
  padding: 0.8rem 1rem;
  border-radius: 0.75rem;
  background: var(--playoff);
  color: #664e00;
  font-weight: 700;
}

.entry-status {
  padding: 0.8rem 1rem;
  border-radius: 0.75rem;
  background: var(--playoff);
  color: #664e00;
  font-weight: 700;
}

.entry-status.is-delivered {
  background: var(--direct);
  color: var(--direct-strong);
}

.recovered-banner {
  margin: 0 0 0.9rem;
  padding: 0.8rem 1rem;
  border-radius: 0.75rem;
  background: #ece9fb;
  color: var(--purple-dark);
  font-weight: 700;
}

#participant-panel > form,
#participant-panel > div {
  margin-top: 1.25rem;
}

.auth-form {
  display: grid;
  gap: 0.15rem;
  padding: 1.25rem;
  border: 1px solid var(--border);
  border-radius: 1rem;
  background: var(--canvas);
}

.auth-form label {
  margin-top: 0.65rem;
  font-weight: 750;
}

.auth-form input {
  min-height: 48px;
  padding: 0.65rem 0.75rem;
  border: 1px solid #aaa8bc;
  border-radius: 0.65rem;
  font: inherit;
}

/* The one-time code is always exactly 6 digits -- a field that stretches to
   the full form width (like the e-mail/name inputs above it) is misleading
   about how much you're meant to type. Narrow it to roughly fit 6 digits
   with generous spacing, and enlarge it for readability, while keeping it
   left-aligned like the rest of the form.

   The width below is deliberately NOT "9ch": `ch` measures a single "0"
   glyph, but letter-spacing adds extra space after every character
   (including the last one, in every engine tested), and that extra space
   was never accounted for -- so the 9ch box was reliably too narrow for 6
   digits at this font-size/letter-spacing and clipped part of the code.
   16ch plus a small fixed buffer comfortably fits 6 digits *and* their
   letter-spacing with margin to spare (measured directly: 6 digits at
   1.5rem/0.4em never exceed ~15ch of actual scrollWidth), verified stable
   across zoom levels 75%-200%. */
#participant-code {
  width: calc(16ch + 4px);
  max-width: 100%;
  font-size: 1.5rem;
  font-weight: 700;
  letter-spacing: 0.4em;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

.auth-form-fields {
  display: grid;
  gap: 0.15rem;
}

.form-hint {
  margin: 0.5rem 0 0;
  color: var(--muted);
  font-size: 0.85rem;
}

.form-error {
  margin: 0.9rem 0 0;
  padding: 0.65rem 0.85rem;
  border-radius: 0.65rem;
  background: #fbe4e1;
  color: #8a2a1e;
  font-size: 0.85rem;
  font-weight: 700;
}

.auth-form-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin-top: 1.1rem;
}

.primary-button:disabled,
.secondary-button:disabled {
  cursor: not-allowed;
  opacity: 0.55;
}

.link-button {
  border: 0;
  padding: 0;
  background: none;
  color: var(--purple-dark);
  font: inherit;
  font-weight: 750;
  text-decoration: underline;
}

.payment-callout {
  /* No max-width here (there used to be a 720px reading-width cap, to
     match paragraphs elsewhere in this section that had the same cap) --
     every paragraph that cap applied to has since been trimmed down to
     one short sentence, so the cap no longer helped readability and only
     left a visible empty strip next to the box. Let it fill the section's
     width like the other status boxes (.entry-status, .recovered-banner)
     already implicitly do. */
  margin: 1rem 0;
  padding: 1rem 1.15rem;
  border: 1px solid #f0d585;
  border-radius: 0.9rem;
  background: var(--playoff);
  color: #664e00;
}

.payment-callout p {
  margin: 0.3rem 0 0;
}

.payment-callout p:first-child {
  margin-top: 0;
}

.modal {
  width: min(calc(100% - 2rem), 520px);
  padding: 0;
  border: 0;
  border-radius: 1rem;
  box-shadow: 0 24px 80px rgb(12 10 40 / 30%);
}

.modal::backdrop {
  background: rgb(15 13 45 / 62%);
  backdrop-filter: blur(3px);
}

.modal form {
  position: relative;
  padding: 1.5rem;
}

.modal h2 {
  padding-right: 2.5rem;
}

.modal label {
  display: block;
  margin: 1.2rem 0 0.35rem;
  font-weight: 750;
}

.modal input {
  width: 100%;
  min-height: 48px;
  padding: 0.65rem 0.75rem;
  border: 1px solid #aaa8bc;
  border-radius: 0.65rem;
}

.modal-close {
  position: absolute;
  top: 0.85rem;
  right: 0.85rem;
  display: grid;
  width: 44px;
  height: 44px;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: #f0eff5;
  font-size: 1.5rem;
}

.modal-actions {
  display: flex;
  justify-content: end;
  gap: 0.65rem;
  margin-top: 1.4rem;
}

.source-note {
  padding: 0.75rem;
  border-radius: 0.65rem;
  background: #f1f0f7;
  color: var(--muted);
  font-size: 0.85rem;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

[hidden] {
  display: none !important;
}

@keyframes pulse {
  to { opacity: 0.25; }
}

@media (max-width: 720px) {
  .hero {
    padding-bottom: 4rem;
  }

  .hero-top {
    flex-direction: column;
    align-items: flex-start;
    gap: 1rem;
  }

  .ranking-card {
    padding-inline: 0.75rem;
  }

  .ranking-heading {
    align-items: start;
    flex-direction: column;
  }

  .table-labels {
    display: none;
  }

  .club-list {
    gap: 0.42rem;
  }

  .club-row {
    grid-template-columns: 44px minmax(0, 1fr) 60px;
    gap: 0.35rem;
    min-height: 64px;
    padding: 0.3rem 0.35rem;
    border-left-width: 4px;
    border-radius: 0.7rem;
  }

  .position-button {
    width: 44px;
  }

  .club-identity {
    gap: 0.4rem;
  }

  .club-data {
    display: none;
  }

  .row-actions {
    justify-content: end;
    gap: 0;
  }

  .move-up,
  .move-down {
    display: none;
  }

  .club-emblem {
    width: 44px;
    height: 44px;
    border-radius: 11px;
  }

  .club-emblem img {
    width: 38px;
    height: 38px;
  }

  .club-names strong {
    display: -webkit-box;
    overflow: hidden;
    font-size: 0.88rem;
    line-height: 1.15;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
  }

  .titles {
    width: 24px;
    height: 24px;
    min-width: 24px;
    min-height: 24px;
  }

  .titles-count {
    font-size: 0.6rem;
  }

  .club-row.zone-start {
    margin-top: 1.65rem;
  }

  .club-row.zone-start .zone-label {
    bottom: calc(100% + 0.2rem);
    font-size: 0.6rem;
  }

  .ranking-footer {
    align-items: stretch;
    flex-direction: column-reverse;
  }

  .ranking-footer-actions {
    flex-direction: column;
    width: 100%;
  }

  .ranking-footer button {
    width: 100%;
  }
}

@media (max-width: 480px) {
  .preview-banner {
    align-items: center;
    flex-direction: column;
    gap: 0.05rem;
    padding-block: 0.45rem;
  }

  .site-header {
    min-height: 64px;
    padding-inline: 0.85rem;
  }

  .hero {
    padding-inline: 1rem;
  }

  .deadline-card {
    align-items: start;
    /* On a narrow phone the icon plus the fixed-width label column left too
       little room for the date, which wrapped across many short lines while
       the countdown pill floated to the right of just the first one. Drop
       the icon and stack countdown-then-date as two full-width lines
       instead -- there is no room to spare here for pure decoration. */
    flex-direction: column;
  }

  .deadline-icon {
    display: none;
  }

  .deadline-card > div {
    order: 1;
    width: 100%;
  }

  .config-badge,
  .countdown {
    order: 0;
    width: 100%;
    margin-left: 0;
  }

  .ranking-card,
  .next-step {
    width: calc(100% - 1rem);
    border-radius: 1rem;
  }

  .zone-legend {
    align-items: stretch;
    flex-direction: column;
  }

  .club-row {
    grid-template-columns: 44px minmax(0, 1fr) 60px;
  }

  .modal-actions {
    flex-direction: column-reverse;
  }

  .auth-form-actions {
    flex-direction: column;
  }

  .auth-form-actions button {
    width: 100%;
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    scroll-behavior: auto !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* ------------------------------------------------------------------
   Locked state: the deadline has passed (data-locked="true" on
   .ranking-card, set by league_phase.html). The table stays visible
   so a bong fetched via "Hent bong" can be looked at, but everything
   that edits or delivers is gone. Written after all the breakpoints
   above on purpose: these selectors are more specific than the plain
   .club-row/.table-labels ones, and among themselves the later
   breakpoint must win, so they are listed narrow-to-wide.
   ------------------------------------------------------------------ */
.locked-notice {
  margin-bottom: 1.25rem;
  padding: 0.9rem 1.1rem;
  border: 1px solid #f1b9b9;
  border-radius: 0.9rem;
  background: #fff0f0;
  color: #6f1d1d;
}

.ranking-card[data-locked="true"] .row-actions,
.ranking-card[data-locked="true"] .ranking-footer,
.ranking-card[data-locked="true"] .table-labels > span:last-child {
  display: none;
}

/* The badge is a plain number now, not a button. */
.ranking-card[data-locked="true"] .position-button,
.ranking-card[data-locked="true"] .position-button:hover {
  cursor: default;
  filter: none;
}

/* With the actions column gone, drop its grid track too instead of
   leaving an empty gutter on the right of every row. */
.ranking-card[data-locked="true"] .club-row,
.ranking-card[data-locked="true"] .table-labels {
  grid-template-columns: 64px minmax(180px, 1fr) 240px;
}

@media (min-width: 1000px) {
  .ranking-card[data-locked="true"] .club-row,
  .ranking-card[data-locked="true"] .table-labels {
    grid-template-columns: 64px minmax(260px, 1fr) 324px;
  }
}

@media (min-width: 1180px) {
  .ranking-card[data-locked="true"] .club-row,
  .ranking-card[data-locked="true"] .table-labels {
    grid-template-columns: 64px minmax(240px, 1fr) 150px 324px;
  }
}

@media (max-width: 720px) {
  .ranking-card[data-locked="true"] .club-row {
    grid-template-columns: 44px minmax(0, 1fr);
  }
}
