/* Oppefra — one stylesheet, self-hosted, no webfont from a third party (no cookies, no banner).
 *
 * This is the OPP-35 design system. The design calls for three families — Sora for anything the
 * interface says in its own voice, Source Serif 4 for prose a human reads, IBM Plex Mono for
 * machine output — and loads them from Google Fonts. We cannot: `style-src 'self'` in
 * public/_headers forbids it and check.mjs fails the build on any third-party subresource. All
 * three are SIL OFL 1.1, so they are served from /fonts instead, licences alongside them. The
 * design survives intact and the page still sets no cookie.
 *
 * The design is dark-only. The old stylesheet shipped a light default with a
 * prefers-color-scheme override; there is no light half to the new system, so the override is
 * gone rather than left to rot into a second, undesigned theme.
 */

/* --- Faces. Sora and Source Serif 4 arrive as variable fonts, so one file each covers every
   weight the design asks for; IBM Plex Mono is static and needs its two. --- */
@font-face {
  font-family: Sora;
  src: url("/fonts/sora-variable.woff2") format("woff2");
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Source Serif 4";
  src: url("/fonts/source-serif-4-variable.woff2") format("woff2");
  font-weight: 400 600;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "IBM Plex Mono";
  src: url("/fonts/ibm-plex-mono-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "IBM Plex Mono";
  src: url("/fonts/ibm-plex-mono-500.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

:root {
  /* Surfaces, darkest to lightest. --bg is the page, --surface a card that sits on it,
     --surface-2 a panel that needs to read as inset rather than raised. */
  --bg: #141614;
  --surface: #1f221f;
  --surface-2: #1a1d1a;
  --photo-bg: #0e100e;

  /* Two borders, not one. --line is the quiet divider between things that belong together;
     --line-strong is the one an input or a dashed "nothing here yet" box draws around itself,
     because those have to be findable without being loud. */
  --line: #2f332f;
  --line-strong: #6b716b;

  --ink: #edeee8;
  --muted: #a3a8a2;

  /* The yellow is the brand's one loud colour: primary buttons, focus rings, the square bullet,
     the marker on a photo. --accent-ink is the only text colour legible on it. */
  --accent: #f2c12e;
  --accent-hover: #dcae22;
  --accent-ink: #1a1d1a;

  /* The olive is the quiet accent — link hover, and one of the tile colours. It is never used
     for a focus ring, because at 3:1 against the page it is not a reliable focus indicator. */
  --accent-2: #a3b06f;

  /* Tile colours from the design's "four ways to use the pictures" grid. Each is a light
     surface, so anything on one takes --accent-ink. */
  --sand: #e4d3b0;
  --clay: #b08a62;

  /* A caution colour that still belongs to this palette: the roadmap note, the [TBD] marker. */
  --note: #e4d3b0;

  --radius: 8px;
  --radius-sm: 4px;
  --maxw: 1200px;

  --font-ui: Sora, "Helvetica Neue", Arial, sans-serif;
  --font-prose: "Source Serif 4", Georgia, "Times New Roman", serif;
  --font-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  /* Tells the browser the page is dark, so form controls, scrollbars and the space outside the
     page paint to match instead of flashing white. */
  color-scheme: dark;
}

* { box-sizing: border-box; }

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

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 400 17px/26px var(--font-ui);
}

/* Prose is the serif. Headings, navigation, buttons and anything the interface says about
   itself stay in Sora — the split is what makes the design read as edited rather than themed. */
p, dd, li, blockquote, figcaption { font-family: var(--font-prose); }
h1, h2, h3, h4, th, label, button, .button, .footer-col h2, .dateline {
  font-family: var(--font-ui);
}
/* A list of links is navigation, not prose, wherever it appears. The rule above puts every
   <li> in the serif; these are the lists that are the interface talking, so they are named
   back into Sora. Setting it on the <li> as well as the <a> matters — the anchor inherits
   from its item, so styling only the anchor leaves any bare text in the item mismatched. */
.site-nav li, .site-nav a,
.footer-col li, .footer-col a,
.linklist li, .linklist a,
.card-link a { font-family: var(--font-ui); }

a { color: var(--ink); }
a:hover { color: var(--accent-2); }

.skip {
  position: absolute;
  left: -9999px;
}
.skip:focus {
  left: 0.5rem;
  top: 0.5rem;
  z-index: 10;
  background: var(--surface);
  color: var(--ink);
  padding: 0.6rem 0.9rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--line-strong);
}

/* --- Header. The phone number is here on every page, ahead of the CTA, and does not
   collapse into a menu on mobile: we are a phone-first company (OPP-3 6.1). --- */
.site-header {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 16px 32px;
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 24px 32px;
  background: var(--bg);
}
/* The wordmark carries the design's zoom motif: a square inside a square inside a square, the
   same shape as the viewport rectangle drawn on the photos. */
.brand {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  font-weight: 700;
  font-size: 19px;
  letter-spacing: -0.03em;
  text-decoration: none;
  color: var(--ink);
}
.brand:hover { color: var(--ink); }
.brand-mark { display: block; flex: none; }
.brand-mark-dot { fill: var(--accent); }

.site-nav { flex: 1 1 20rem; order: 3; }
.site-nav ul {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 8px 32px;
  margin: 0;
  padding: 0;
}
.site-nav a {
  text-decoration: none;
  color: var(--ink);
  font-size: 15px;
  font-weight: 600;
  padding: 2px 0;
}
.site-nav a:hover { color: var(--accent-2); }
/* The current page is marked with the brand's square, not a colour change, so it survives
   being the same colour as its neighbours. */
.site-nav a[aria-current="page"] {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.site-nav a[aria-current="page"]::before {
  content: "";
  width: 10px;
  height: 10px;
  background: var(--accent);
  border-radius: 1px;
}

.header-actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px 20px;
  margin: 0;
  margin-left: auto;
}
.header-actions .phone {
  text-decoration: none;
  color: var(--ink);
  font-family: var(--font-ui);
  font-size: 15px;
  white-space: nowrap;
}
.header-actions .phone strong { font-size: 17px; font-weight: 600; }

main {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 48px 32px 64px;
}

/* --- Hero --- */
.hero { margin: 0 0 64px; max-width: 46rem; }
.hero h1 {
  font-size: clamp(44px, 2rem + 3.4vw, 72px);
  line-height: 1.08;
  letter-spacing: -0.045em;
  font-weight: 700;
  margin: 0 0 24px;
}
.lede {
  font-family: var(--font-prose);
  font-size: 21px;
  line-height: 32px;
  color: var(--muted);
  max-width: 34ch;
  margin: 0 0 24px;
}
.hero-cta { display: flex; flex-wrap: wrap; gap: 8px; }
/* An article's dateline: where it sits and when it was written, above the headline, because a
   reader judging whether a technical answer is still true needs the date first. */
.dateline {
  margin: 0 0 12px;
  color: var(--muted);
  font-size: 13px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
/* What the API actually serves today, one line under a headline that sells the whole zoom ladder. */
.hero-note {
  margin: 16px 0 0;
  font-family: var(--font-prose);
  color: var(--muted);
  font-size: 17px;
  line-height: 26px;
  max-width: 44rem;
}
/* The roadmap caveat inside a zoom-level card. */
.cards .qualifier { margin: 8px 0 0; color: var(--muted); font-size: 15px; line-height: 23px; }

section { margin: 0 0 64px; max-width: 70ch; }
section h2 {
  font-size: 40px;
  line-height: 46px;
  font-weight: 700;
  letter-spacing: -0.03em;
  margin: 0 0 24px;
}
section h3 {
  font-size: 23px;
  line-height: 29px;
  font-weight: 600;
  letter-spacing: -0.02em;
  margin: 0 0 8px;
}
section p { margin: 0 0 16px; font-size: 19px; line-height: 30px; }
section:has(.cards),
section:has(.tiers),
section:has(.jobs),
section:has(.table-wrap),
section:has(.sources),
section:has(.cta-band) { max-width: var(--maxw); }

@media (max-width: 900px) {
  section h2 { font-size: 30px; line-height: 36px; }
}

/* The one section that speaks in the brand's own colour. The design uses the yellow panel for
   the single thing it most wants asked ("Hvor skal vi kigge?"), so highlight gets it too. */
section.highlight {
  max-width: var(--maxw);
  background: var(--accent);
  color: var(--accent-ink);
  border: 0;
  border-radius: var(--radius);
  padding: 40px 48px;
}
section.highlight h2 { color: var(--accent-ink); }
section.highlight a { color: var(--accent-ink); }
section.highlight p:first-of-type { font-size: 21px; line-height: 32px; font-weight: 600; }

/* --- Buttons. 48px tall, because the design sizes them to a thumb and the header puts one
   beside a phone number on a phone. --- */
.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 48px;
  background: var(--accent);
  color: var(--accent-ink);
  text-decoration: none;
  font-family: var(--font-ui);
  font-size: 15px;
  font-weight: 600;
  padding: 0 24px;
  border-radius: var(--radius-sm);
}
.button:hover { background: var(--accent-hover); color: var(--accent-ink); }
.button.ghost {
  background: transparent;
  color: var(--ink);
  border: 1px solid var(--line-strong);
}
.button.ghost:hover { background: transparent; border-color: var(--ink); color: var(--ink); }
/* Inside the yellow panel the primary button would vanish, so it inverts. */
section.highlight .button { background: var(--accent-ink); color: var(--accent); }
section.highlight .button:hover { background: #000; color: var(--accent); }
section.highlight .button.ghost {
  background: transparent;
  color: var(--accent-ink);
  border-color: var(--accent-ink);
}

/* --- Grids --- */
.cards, .tiers, .jobs {
  list-style: none;
  margin: 24px 0 0;
  padding: 0;
  display: grid;
  gap: 8px;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}
/* Direct children only. A tier card contains its own <ul> of features, and an unscoped
   `.tiers li` turns every one of those into a second card — boxed, padded and, inside the
   featured tier, dark-on-yellow and unreadable. */
.cards > li, .tiers > li, .jobs > li {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 24px;
}
.cards > li p, .tiers > li p, .jobs > li p { font-size: 16px; line-height: 24px; }
/* The featured tier takes the brand colour rather than a heavier border — the design's way of
   saying "this one" is a filled block, not a thicker outline. */
.tiers > li.featured {
  background: var(--accent);
  color: var(--accent-ink);
  border-color: var(--accent);
}
.tiers > li.featured a { color: var(--accent-ink); }
.tiers > li.featured .price span,
.tiers > li.featured ul { color: var(--accent-ink); }
/* A price is a display numeral, not prose: Sora, tight, the same voice as a heading. */
.tiers .price {
  font-family: var(--font-ui);
  font-size: 32px;
  line-height: 38px;
  font-weight: 700;
  letter-spacing: -0.03em;
  margin: 0 0 12px;
}
.tiers .price span {
  display: block;
  font-family: var(--font-prose);
  font-size: 15px;
  line-height: 23px;
  font-weight: 400;
  letter-spacing: 0;
  color: var(--muted);
}
.tiers > li ul { padding-left: 1.1rem; margin: 0; color: var(--muted); font-size: 16px; line-height: 26px; }
.card-link a { font-family: var(--font-ui); font-size: 15px; font-weight: 600; }
.jobs .job-price { font-family: var(--font-ui); font-size: 23px; font-weight: 700; letter-spacing: -0.02em; margin: 0 0 4px; }
.jobs .job-when { color: var(--muted); font-size: 15px; line-height: 23px; margin: 0 0 12px; }

.linklist { list-style: none; margin: 24px 0 0; padding: 0; display: grid; gap: 16px; }
.linklist a { text-decoration: none; font-family: var(--font-ui); font-weight: 600; }
.linklist span { display: block; font-family: var(--font-prose); font-weight: 400; color: var(--muted); }

.steps { margin: 24px 0 0; padding-left: 1.2rem; display: grid; gap: 32px; }
.steps h3 { margin-bottom: 8px; }
.process { margin: 24px 0 0; padding-left: 1.2rem; display: grid; gap: 8px; }

.faq { margin: 24px 0 0; }
.faq dt { font-family: var(--font-ui); font-weight: 600; font-size: 19px; margin: 32px 0 8px; }
.faq dd { margin: 0; color: var(--muted); font-size: 17px; line-height: 27px; }

.panel {
  max-width: var(--maxw);
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 24px;
}
.panel h3 { margin-top: 0; }
.panel p:last-child { margin-bottom: 0; }
/* The roadmap panel says what the API does NOT serve yet. It is deliberately as legible as the
   rest of the page rather than tucked away as fine print — the design's dashed box, which it
   uses for "nothing here yet", is exactly the right shape for it. */
.panel.roadmap { background: transparent; border: 2px dashed var(--line-strong); }

.sources { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.sources li {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 24px;
}
.sources dl {
  margin: 0;
  display: grid;
  grid-template-columns: minmax(8rem, max-content) 1fr;
  gap: 8px 24px;
}
.sources dt { font-family: var(--font-ui); color: var(--muted); font-size: 14px; font-weight: 600; }
.sources dd { margin: 0; font-size: 16px; line-height: 24px; overflow-wrap: anywhere; }
.attribution-string { font-weight: 600; }

.cta-band {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 16px 24px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 32px;
}
.cta-band .note { flex: 1 1 100%; margin: 0; }

/* --- Tables --- */
.table-wrap { overflow-x: auto; }
.table-wrap table {
  border-collapse: collapse;
  width: 100%;
  min-width: 34rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.table-wrap caption {
  text-align: left;
  font-family: var(--font-prose);
  color: var(--muted);
  padding: 0 0 12px;
}
.table-wrap th, .table-wrap td {
  text-align: left;
  vertical-align: top;
  padding: 12px 16px;
  border-bottom: 1px solid var(--line);
  font-family: var(--font-prose);
  font-size: 16px;
  line-height: 24px;
}
.table-wrap thead th {
  font-family: var(--font-ui);
  font-size: 13px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
}
.table-wrap tbody tr:last-child th,
.table-wrap tbody tr:last-child td { border-bottom: 0; }
.contact-details table { min-width: 0; max-width: 32rem; }
.contact-details thead { display: none; }

/* A note is a caution, not an error: sand on the dark page, the same colour the design gives
   its "not sure what you need?" box. */
.note {
  color: var(--note);
  font-size: 16px;
  line-height: 24px;
}
.pending { color: var(--note); }

.code {
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 16px;
  overflow-x: auto;
  color: var(--ink);
  font: 400 13px/20px var(--font-mono);
}

/* The demo command beside the image it returns. Side by side where there is room, stacked on a
   phone, with the command first either way — the command is the thing a developer copies. */
.demo-pair {
  display: grid;
  gap: 16px;
  align-items: start;
}
@media (min-width: 52rem) {
  .demo-pair { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
}
.demo-pair .code { margin: 0; }
.demo-image { margin: 0; }
.demo-image img {
  display: block;
  width: 100%;
  height: auto;
  background: var(--photo-bg);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
/* Where a picture is promised and not here yet, the design's dashed box holds the space and
   says so, rather than collapsing and leaving the page a size it will not be later. */
.demo-image-pending {
  display: grid;
  place-items: center;
  min-height: 14rem;
  border: 2px dashed var(--line-strong);
  border-radius: var(--radius);
  background: var(--photo-bg);
  color: var(--muted);
  font-family: var(--font-prose);
  text-align: center;
  padding: 24px;
}
/* Coordinates and scale read as machine output on the design's photos, so the caption under
   one does too. */
.demo-image figcaption {
  margin-top: 12px;
  color: var(--muted);
  font: 500 12px/16px var(--font-mono);
}

.map-demo {
  display: grid;
  place-items: center;
  min-height: 16rem;
  border: 2px dashed var(--line-strong);
  border-radius: var(--radius);
  background: var(--photo-bg);
  color: var(--muted);
  font-family: var(--font-prose);
  text-align: center;
  padding: 24px;
}

/* --- Forms --- */
.lead-form { max-width: 38rem; }
.field { margin: 0 0 24px; }
.field label { display: block; font-size: 15px; font-weight: 600; margin-bottom: 8px; }
.field .optional { font-weight: 400; color: var(--muted); }
.field input[type="text"],
.field input[type="email"],
.field input[type="tel"],
.field select,
.field textarea {
  width: 100%;
  font: 400 17px/24px var(--font-ui);
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  padding: 11px 16px;
}
.field input::placeholder, .field textarea::placeholder { color: var(--muted); }

/* The marketing checkbox is set off by a rule and whitespace, never crammed against the
   submit button — the two decisions must not look like one (OPP-3 6.10). */
.consent-block {
  margin: 40px 0 32px;
  padding-top: 32px;
  border-top: 1px solid var(--line);
}
.field.consent { margin-bottom: 12px; }
.field.consent label {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  font-family: var(--font-prose);
  font-size: 16px;
  line-height: 24px;
  font-weight: 400;
}
.field.consent input { margin-top: 5px; flex: 0 0 auto; accent-color: var(--accent); }
.consent-note { color: var(--muted); font-size: 15px; line-height: 23px; margin: 0 0 8px; }

.field button {
  font: 600 15px/1 var(--font-ui);
  min-height: 48px;
  background: var(--accent);
  color: var(--accent-ink);
  border: 0;
  border-radius: var(--radius-sm);
  padding: 0 24px;
  cursor: pointer;
}
.field button:hover { background: var(--accent-hover); }
.field button[disabled] { opacity: 0.6; cursor: progress; }

.form-status:not(:empty) {
  border-left: 3px solid var(--accent);
  padding-left: 16px;
}
/* Error keeps the one non-palette colour on the site. A failed submission has to read as wrong
   at a glance, and nothing in a yellow-and-olive palette does that. */
.form-status[data-state="error"],
.form-status[data-state="invalid"] { border-color: #e2674a; }
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* --- Footer --- */
.site-footer {
  border-top: 1px solid var(--line);
  background: var(--bg);
  margin-top: 64px;
}
.footer-grid, .legal-strip {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 48px 32px 0;
}
.footer-grid {
  display: grid;
  gap: 32px;
  grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
}
.footer-col h2 {
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 600;
  color: var(--muted);
  margin: 0 0 16px;
}
.footer-col ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.footer-col li { font-family: var(--font-prose); font-size: 16px; line-height: 24px; }
.footer-col a { text-decoration: none; }

.legal-strip {
  padding-top: 32px;
  padding-bottom: 48px;
  margin-top: 48px;
  border-top: 1px solid var(--line);
  color: var(--muted);
  font-family: var(--font-prose);
  font-size: 16px;
  line-height: 24px;
}
.legal-strip p { margin: 0 0 8px; }
.legal-line { font-style: normal; display: grid; gap: 4px; margin: 0 0 12px; }
.attribution-line { color: var(--ink); }
