/*
 * iduga beta landing - interim static port of iduga-primary's frontend/apps/www beta page
 * (frontend/apps/www/app/[locale]/beta/{page.tsx,beta.css}). Deliberate deviations from that
 * source, ported for real:
 *  - No animated network-background canvas (JS engine not ported for v1 - decorative only).
 *  - German copy only for v1; English deferred.
 * ASCII hyphen-minus only.
 *
 * Theme: dark (default), light, and auto (follows the OS via prefers-color-scheme) - chosen
 * with the top-right switcher (inc/page.php's idg_topbar()), persisted in localStorage, applied
 * via a blocking inline script in <head> so the page never flashes the wrong theme. Every themed
 * color is a custom property re-declared inside :root[data-theme="light"] (and duplicated,
 * unavoidably without CSS nesting, inside the prefers-color-scheme query for
 * :root[data-theme="auto"]) - component rules below reference the token, never a literal color,
 * so the whole page repaints correctly from those two override blocks alone.
 */

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: local('Inter');
}

:root {
  --bg: #1F1F1F;
  --surface-2: #2E2E2E;
  --border: #333333;
  --border-strong: #454545;
  --text: #F3F3F3;
  --text-secondary: #C2C2C2;
  --text-muted: #A3A3A3;
  /* Quieter than body text but measured well above WCAG AA (4.5:1) against --bg:
     --text-quiet ~7.6:1 (badge/consent/trust/footer), --text-quiet-em ~8.8:1 for
     italic copy specifically - italics thin the strokes, so it needs MORE
     contrast than upright text to read as comfortably, not less. */
  --text-quiet: #B0B0B0;
  --text-quiet-em: #BDBDBD;
  --primary: #0470E8;
  --primary-hover: #1E82F5;
  --on-primary: #FFFFFF;
  --primary-accent: #5FA8FF;
  --primary-soft: rgba(4,112,232,.16);
  --focus: #5FA8FF;
  --success: #34D399;
  --danger: #F87171;
  --grad-hero: linear-gradient(120deg,#5FA8FF 0%,#22D3EE 38%,#A78BFA 100%);
  --r-md: 12px;
  --font-sans: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --font-mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, monospace;
}

/* Light theme, explicitly chosen via the switcher. --primary/--primary-hover/
   --on-primary are deliberately NOT re-declared here - the notify button keeps
   the same blue fill with white text in both themes, since that combination's
   contrast never depended on the page background. Every other token that DOES
   depend on the page background is re-derived and contrast-checked against
   the new --bg (#F6F7F9, measured luminance ~0.83): --primary-accent/--focus
   ~5.9:1, --grad-hero's weakest stop (the cyan-ish middle one) ~3.4:1 (large
   text only, matching how it's used), --success ~4.9:1, --danger ~4.6:1. */
:root[data-theme="light"] {
  --bg: #F6F7F9;
  --surface-2: #FFFFFF;
  --border: #E3E5E8;
  --border-strong: #C9CCD1;
  --text: #17181A;
  --text-secondary: #43474D;
  --text-muted: #6B6F76;
  --text-quiet: #5B5F66;
  --text-quiet-em: #4E5257;
  --primary-accent: #0A5FD1;
  --primary-soft: rgba(10,95,209,.12);
  --focus: #0A5FD1;
  --success: #128A55;
  --danger: #C23934;
  --grad-hero: linear-gradient(120deg,#0A5FD1 0%,#0B8B9C 38%,#7C4FDB 100%);
}

/* "Auto" (the default for a first-time visitor - see the blocking script in
   inc/page.php) follows the OS. This is the same token list as the light
   block above, necessarily duplicated: a bare :root selector can't be
   feature-scoped by BOTH an attribute selector and a media query at once
   without CSS nesting, and this file intentionally stays plain CSS. */
@media (prefers-color-scheme: light) {
  :root[data-theme="auto"] {
    --bg: #F6F7F9;
    --surface-2: #FFFFFF;
    --border: #E3E5E8;
    --border-strong: #C9CCD1;
    --text: #17181A;
    --text-secondary: #43474D;
    --text-muted: #6B6F76;
    --text-quiet: #5B5F66;
    --text-quiet-em: #4E5257;
    --primary-accent: #0A5FD1;
    --primary-soft: rgba(10,95,209,.12);
    --focus: #0A5FD1;
    --success: #128A55;
    --danger: #C23934;
    --grad-hero: linear-gradient(120deg,#0A5FD1 0%,#0B8B9C 38%,#7C4FDB 100%);
  }
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-sans);
  transition: background-color .2s ease, color .2s ease;
}

@media (prefers-reduced-motion: reduce) {
  html, body { transition: none; }
}

.iduga-beta {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  padding: 96px 24px;
  overflow: hidden;
}

/* ---- ambient background layers (decorative) ---- */
/*
 * Scroll performance, round 2 (2026-08-08). Round 1 (position: fixed instead
 * of absolute, background-position -> transform on the grid pan) helped but
 * didn't fully fix it - the remaining, bigger cost was `mix-blend-mode:
 * screen` on four of these five layers, plus `mask-image` on the grid layer.
 * Both force the browser off the cheap "promote to an independent GPU layer,
 * composite and done" path: a blended element's pixels depend on whatever is
 * under it, and a masked element's alpha channel has to be resolved through
 * an intermediate render target, so neither can simply sit on the compositor
 * untouched - both need extra work redone essentially every frame the
 * element (still) animates, which is continuous here (26-60s infinite
 * loops), and that ongoing cost is what was still competing with scroll for
 * compositor time even after round 1's fixes. Removed both: plain
 * background colors at the same alpha read almost identically here (there's
 * nothing colorful under them to "screen" against, just the flat --bg), and
 * the grid's mask-fade is approximated by lowering its base opacity instead
 * of radially fading it out.
 */

.iduga-beta-amb {
  position: fixed;
  inset: -16%;
  z-index: 0;
  pointer-events: none;
  will-change: transform;
}

.iduga-beta-amb-1 {
  background: radial-gradient(680px 460px at 50% 40%, rgba(76, 90, 248, 0.16), transparent 62%);
  animation: iduga-beta-drift-1 26s ease-in-out infinite alternate;
}

.iduga-beta-amb-2 {
  background: radial-gradient(560px 420px at 10% 24%, rgba(4, 112, 232, 0.12), transparent 60%);
  animation: iduga-beta-drift-2 31s ease-in-out infinite alternate;
}

.iduga-beta-amb-3 {
  background: radial-gradient(560px 420px at 90% 76%, rgba(134, 95, 248, 0.12), transparent 60%);
  animation: iduga-beta-drift-3 35s ease-in-out infinite alternate;
}

.iduga-beta-grid {
  position: fixed;
  inset: -40px;
  z-index: 0;
  /* No mask-image (see the comment above .iduga-beta-amb): a masked layer can't be composited
     untouched, so its cost recurs every animation frame. Lower flat opacity approximates the
     old radial fade-out well enough for a decorative dot grid - it's no longer literally faded
     at the edges, but at this density/alpha the difference isn't the point of the effect. */
  opacity: 0.22;
  pointer-events: none;
  background-image: radial-gradient(rgba(95, 168, 255, 0.16) 1px, transparent 1.4px);
  background-size: 38px 38px;
  animation: iduga-beta-grid-pan 60s linear infinite;
  will-change: transform;
}

.iduga-beta-sheen {
  position: fixed;
  inset: -30%;
  z-index: 0;
  opacity: 0.5;
  pointer-events: none;
  background: linear-gradient(115deg, transparent 38%, rgba(95, 168, 255, 0.08) 50%, transparent 62%);
  animation: iduga-beta-sheen 34s ease-in-out infinite;
  will-change: transform;
}

/* Confined to one viewport-tall band at the top of the page (like db-ip.com's reference
   particle background, and like the original prod placement this was ported from - a
   hero-local canvas, not a page-wide one) rather than `position: fixed; inset: 0`. Two
   mobile problems traced back to that page-wide fixed layer: (1) it never left the
   viewport as the page scrolled, so the engine's own IntersectionObserver pause-when-
   off-screen logic (see network.js's perf contract) never actually triggered - the canvas
   kept redrawing every frame for the ENTIRE scroll of a long page instead of just while
   the hero was in view; (2) `position: fixed` keeps the canvas resident through every
   scroll frame regardless, which is measurably more scroll-compositor work than a normal
   `position: absolute` layer the page just scrolls past. `100svh` (small viewport height,
   i.e. address-bar-visible) keeps the box's own height constant across a mobile browser's
   address-bar show/hide instead of following `100vh`'s dynamic value - the `100vh` line
   above it is a fallback for browsers without svh support, immediately overridden where
   svh is available. */
.iduga-beta-net {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 1;
  display: block;
  width: 100%;
  height: 100vh;
  height: 100svh;
  pointer-events: none;
}

/* ---- theme switch + language switch (every page) ---- */

/* Right-aligned within the page's own content column, not pinned to the
   browser viewport edge: centered via the same left:50%/translateX(-50%)
   mechanism as .iduga-beta-intro, capped at the same 64ch max-width, so its
   right edge lines up with the content block's right edge instead of
   floating off in the page's outer margin on wide viewports. */
.iduga-topbar {
  /* absolute, not fixed: it stays at the top of the PAGE and scrolls away
     with it, rather than staying pinned to the viewport while scrolling. */
  position: absolute;
  top: 20px;
  left: 50%;
  transform: translateX(-50%);
  width: 100%;
  max-width: 64ch;
  padding: 0 24px;
  z-index: 5;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 14px;
  pointer-events: none;
}

.iduga-topbar > * { pointer-events: auto; }

.iduga-theme-switch {
  display: flex;
  align-items: center;
  height: 36px;
  gap: 2px;
  padding: 3px;
  border-radius: 999px;
  background: var(--surface-2);
  border: 1px solid var(--border);
}

.iduga-theme-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  padding: 0;
  border: none;
  border-radius: 999px;
  background: transparent;
  color: var(--text-quiet);
  cursor: pointer;
}

.iduga-theme-btn:hover { color: var(--text-secondary); }
.iduga-theme-btn:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.iduga-theme-btn[aria-pressed="true"] {
  background: var(--primary-soft);
  color: var(--primary-accent);
}

.iduga-lang-switch {
  display: inline-flex;
  align-items: center;
  height: 36px;
  font-size: 13px;
  color: var(--text-quiet);
  text-decoration: none;
  padding: 0 12px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--surface-2);
}
.iduga-lang-switch:hover, .iduga-lang-switch:focus-visible {
  color: var(--text-secondary);
  border-color: var(--border-strong);
}

@media (max-width: 560px) {
  .iduga-topbar { top: 12px; padding: 0 16px; gap: 8px; }
  .iduga-theme-switch { height: 32px; }
  .iduga-theme-btn { width: 26px; height: 26px; }
  .iduga-lang-switch { height: 32px; font-size: 12px; padding: 0 10px; }
}

/* ---- content ---- */

.iduga-beta-content {
  position: relative;
  /* 3, not 2: the share popover (z-index 6, but scoped inside THIS stacking context -
     see the comment on .iduga-beta-share-menu) needs to visually cover .iduga-beta-social
     and .iduga-beta-legal below it. Those are siblings of this element, not descendants,
     so the popover's z-index only matters once this container itself outranks them - at
     the old tied z-index:2, DOM order decided the tie and the later-painted social/legal
     footer covered the popover instead. Measured live 2026-08-08. */
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}

.iduga-beta-logo {
  width: 300px;
  height: auto;
  /* The wordmark path fills with currentColor (inc/logo.php) so it follows
     --text automatically - light grey on the dark theme, near-black on the
     light theme - without needing a second SVG. */
  color: var(--text);
  filter: drop-shadow(0 10px 40px rgba(4, 112, 232, 0.45));
}

.iduga-beta-h1 {
  margin: 30px 0 0;
  font-size: 40px;
  font-weight: 760;
  letter-spacing: -0.02em;
  line-height: 1.2;
  color: var(--text);
}

.iduga-beta-h1-grad {
  /* Solid fallback, applied whenever background-clip: text is unsupported - the
     @supports-gated rule below is what makes the gradient/transparent-text
     treatment conditional, so an unsupporting browser keeps this readable
     color instead of invisible (transparent) text. Measured ~6.7:1 against
     --bg, and every stop of --grad-hero individually clears 4.5:1 too (the
     weakest stop, #A78BFA, measures ~6.1:1) so the gradient itself is safe. */
  color: var(--primary-accent);
  white-space: nowrap;
}

@supports (background-clip: text) or (-webkit-background-clip: text) {
  .iduga-beta-h1-grad {
    background: var(--grad-hero);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
  }
}

.iduga-beta-status {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-top: 20px;
  margin-bottom: 36px;
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--text-quiet);
}

/* The status line belongs visually to the headline, not to the body copy
   below it - its own margin-bottom above already sets that gap, so the
   first paragraph must not add a second one on top. */
.iduga-beta-status + .iduga-beta-intro {
  margin-top: 0;
}

.iduga-beta-status-badge { color: var(--primary-accent); }

.iduga-beta-status-sep {
  width: 1px;
  height: 16px;
  background: var(--border);
}

/* Left-aligned (not centered like the rest of the page): with multi-line
   paragraphs a ragged left edge forces the eye to re-find the line start on
   every wrap, which measurably slows reading. The block as a whole still
   sits centered on the page via the auto side margins on this max-width box
   inside the flex-centered .iduga-beta-content parent. Individual
   paragraphs that should stay centered (the payoff line, the closing line)
   override text-align back to center below. */
.iduga-beta-intro {
  margin: 22px auto 0;
  max-width: 64ch;
  text-align: left;
  font-size: 16px;
  line-height: 1.7;
  color: var(--text-secondary);
}

.iduga-beta-intro strong { color: var(--text); font-weight: 700; }

.iduga-beta-accent { color: var(--primary-accent); }

/* A short beat between the opening scenario and the rest of the copy -
   italic + a dedicated quieter-but-legible color carry that, not extra
   margin: the paragraph inherits the standard rhythm above so the gap
   before AND after reads the same as every other paragraph pair. */
.iduga-beta-intro-pause {
  font-style: italic;
  color: var(--text-quiet-em);
}

/* The emotional payoff line - the section's second focal point (the
   accent-colored "iduga" mention in paragraph 4 is the first). Bold, a
   touch larger than body copy, deliberately left uncolored, and centered
   like the headline rather than left-aligned like the paragraphs above it.
   `text-wrap: balance` is a belt-and-suspenders assist in browsers that
   support it; the explicit <br> in the copy is what guarantees the second
   line is never a single orphaned word at any viewport width. */
.iduga-beta-intro-payoff {
  margin-top: 32px;
  font-size: 1.2em;
  text-align: center;
  text-wrap: balance;
  color: var(--text);
}

/* Also centered, and pulled close to the invite line right after it so the
   two read as one continuous thought. */
.iduga-beta-intro-closing {
  margin-top: 22px;
  text-align: center;
}
.iduga-beta-intro-closing + .iduga-beta-invite { margin-top: 6px; }

.iduga-beta-invite {
  margin: 16px 0 0;
  font-size: 17px;
  font-weight: 640;
  letter-spacing: -0.01em;
  color: var(--text);
}

/* ---- notify form ---- */

.iduga-beta-form {
  display: flex;
  align-items: stretch;
  gap: 8px;
  margin-top: 16px;
}

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

.iduga-beta-input {
  width: 280px;
  padding: 13px 16px;
  font-family: var(--font-sans);
  font-size: 15px;
  color: var(--text);
  background: var(--surface-2);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-md);
  outline: none;
}

.iduga-beta-input::placeholder { color: var(--text-muted); }

.iduga-beta-input:focus-visible {
  border-color: var(--focus);
  box-shadow: 0 0 0 3px var(--primary-soft);
}

.iduga-beta-input[aria-invalid="true"] { border-color: var(--danger); }

.iduga-beta-button {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 13px 20px;
  font-family: var(--font-sans);
  font-size: 15px;
  font-weight: 600;
  color: var(--on-primary);
  background: var(--primary);
  /* A transparent border, same width as the input's, so both share the
     exact same box height (padding + border, under border-box) rather than
     the button reading a hair shorter for having no border at all. */
  border: 1px solid transparent;
  border-radius: var(--r-md);
  cursor: pointer;
  white-space: nowrap;
}

.iduga-beta-button:hover { background: var(--primary-hover); }
.iduga-beta-button:focus-visible { outline: 3px solid var(--primary-soft); outline-offset: 2px; }

.iduga-beta-bell { flex: 0 0 auto; }

.iduga-beta-success, .iduga-beta-error {
  margin: 28px 0 0;
  max-width: 56ch;
  font-size: 16px;
  line-height: 1.6;
}

.iduga-beta-success { color: var(--success); }
.iduga-beta-error { color: var(--danger); }

/* ---- trust line under the signup block ---- */

.iduga-beta-trust {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  margin-top: 20px;
  font-size: 12px;
  color: var(--text-quiet);
}

.iduga-beta-trust svg { flex: 0 0 auto; opacity: 0.8; }

/* ---- invite / share card ---- */
/* The page's second CTA, after the beta signup form: not styled as a plain secondary
   link, but as its own small card so it reads as a deliberate second action rather than
   an afterthought under the fold. The share button itself carries the brand gradient
   (--grad-hero) as a border, echoing the h1 accent and badge rather than introducing a
   new color - "the same brand, a quieter moment" instead of a second competing accent. */

.iduga-beta-invite-card {
  /* Capped at the same measure as .iduga-beta-intro so this card can never grow wider
     than the rest of the content column - without a max-width, the flex column's
     align-items: center lets it size to the invite line's full unwrapped width, which
     made it flare out past the page's own text measure. */
  box-sizing: border-box;
  width: 100%;
  max-width: 64ch;
  margin: 28px auto 0;
  padding: 18px 20px;
  border-radius: 16px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
}

.iduga-beta-invite-line {
  margin: 0;
  font-size: 14px;
  color: var(--text-secondary);
  text-align: center;
}

.iduga-beta-share {
  position: relative;
  display: inline-block;
}

.iduga-beta-share-btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 10px 22px;
  font: inherit;
  font-size: 14px;
  font-weight: 700;
  color: var(--text);
  background:
    linear-gradient(var(--bg), var(--bg)) padding-box,
    var(--grad-hero) border-box;
  border: 1.5px solid transparent;
  border-radius: 999px;
  cursor: pointer;
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.iduga-beta-invite-card .iduga-beta-share-btn {
  background:
    linear-gradient(var(--surface-2), var(--surface-2)) padding-box,
    var(--grad-hero) border-box;
}
.iduga-beta-share-btn:hover, .iduga-beta-share-btn:focus-visible {
  transform: translateY(-1px);
  box-shadow: 0 6px 20px rgba(95, 168, 255, 0.25);
}
.iduga-beta-share-btn svg { flex: 0 0 auto; }

/* [hidden] must win over the display:flex below - author CSS otherwise beats the UA
   stylesheet's [hidden]{display:none} at equal specificity, which left the menu visible
   on load until JS explicitly opened it. Measured live in the first local render. */
.iduga-beta-share-menu[hidden] {
  display: none;
}

.iduga-beta-share-menu {
  position: absolute;
  z-index: 6;
  top: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  min-width: 180px;
  padding: 6px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.28);
}

.iduga-beta-share-opt {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  color: var(--text-secondary);
  background: none;
  border: none;
  border-radius: 8px;
  text-decoration: none;
  cursor: pointer;
  text-align: left;
}
.iduga-beta-share-opt:hover, .iduga-beta-share-opt:focus-visible {
  color: var(--text);
  background: var(--primary-soft);
}
.iduga-beta-share-opt svg { flex: 0 0 auto; opacity: 0.85; }

/* ---- social links ---- */
/* Deliberately plain - no pill/border chrome, unlike the topbar switcher: this
   sits in the same visual register as .iduga-beta-trust right above it (icon +
   quiet text), not as a second set of button controls competing with the one
   at the top of the page. */

.iduga-beta-social {
  position: relative;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  margin-top: 18px;
  font-size: 13px;
  color: var(--text-quiet);
}

.iduga-beta-social-handle {
  font-family: var(--font-mono);
}

.iduga-beta-social a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--text-quiet);
  line-height: 0;
}
.iduga-beta-social a:hover, .iduga-beta-social a:focus-visible {
  color: var(--primary-accent);
}

/* ---- legal footer ---- */

.iduga-beta-legal {
  position: relative;
  z-index: 2;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 8px 20px;
  margin-top: 28px;
  font-size: 13px;
  color: var(--text-quiet);
}

.iduga-beta-legal a { color: var(--text-quiet); text-decoration: none; }
.iduga-beta-legal a:hover, .iduga-beta-legal a:focus-visible {
  color: var(--text-secondary);
  text-decoration: underline;
}

/* ---- consent line under the notify form ---- */

.iduga-beta-consent {
  font-size: 13px;
  color: var(--text-quiet);
  margin-top: 12px;
}
.iduga-beta-consent a,
.iduga-beta-consent a:visited {
  color: var(--primary-accent);
  text-decoration: underline;
}
.iduga-beta-consent a:hover,
.iduga-beta-consent a:focus-visible {
  color: var(--text-secondary);
}

/* ---- responsive ---- */

@media (max-width: 820px) {
  .iduga-beta { padding: 72px 20px; }
  .iduga-beta-logo { width: 240px; }
  .iduga-beta-h1 { margin-top: 24px; font-size: 34px; }
  .iduga-beta-status { margin-top: 16px; margin-bottom: 28px; }
}

@media (max-width: 560px) {
  .iduga-beta { padding: 56px 16px; }
  .iduga-beta-logo { width: 190px; }
  .iduga-beta-h1 { margin-top: 20px; font-size: 28px; }
  .iduga-beta-status { margin-top: 14px; margin-bottom: 24px; gap: 10px; font-size: 12px; }
  /* #iduga-beta-form-wrap is otherwise unstyled - it shrinks to fit its content
     under the flex-column parent's default sizing. Giving the FORM a
     percentage width with no explicitly-sized ancestor to resolve it against
     is a circular reference browsers settle inconsistently, which is what
     threw the button/input off-center here. Size the wrapper explicitly
     first, then the form's 100% has something real to resolve against. */
  #iduga-beta-form-wrap { width: 100%; max-width: 320px; }
  .iduga-beta-form { flex-direction: column; align-items: stretch; width: 100%; max-width: 320px; }
  .iduga-beta-input { width: 100%; }
  .iduga-beta-button { justify-content: center; }
}

/* ---- keyframes ---- */

@keyframes iduga-beta-drift-1 {
  from { transform: translate3d(0, 0, 0) scale(1); }
  to { transform: translate3d(2%, -2%, 0) scale(1.06); }
}
@keyframes iduga-beta-drift-2 {
  from { transform: translate3d(0, 0, 0) scale(1.04); }
  to { transform: translate3d(-3%, 2%, 0) scale(1); }
}
@keyframes iduga-beta-drift-3 {
  from { transform: translate3d(0, 0, 0) scale(1); }
  to { transform: translate3d(2%, 3%, 0) scale(1.05); }
}
@keyframes iduga-beta-grid-pan {
  /* transform, not background-position: the dot pattern tiles at exactly
     38px (background-size above), so translating the whole layer by one
     tile is visually identical to panning the background - but stays on
     the compositor thread instead of forcing a full repaint every frame. */
  from { transform: translate3d(0, 0, 0); }
  to { transform: translate3d(38px, 38px, 0); }
}
@keyframes iduga-beta-sheen {
  0%, 100% { transform: translate3d(-8%, 0, 0); }
  50% { transform: translate3d(8%, 0, 0); }
}

@media (prefers-reduced-motion: reduce) {
  .iduga-beta-amb, .iduga-beta-grid, .iduga-beta-sheen { animation: none; }
}

/* ---- legal pages (impressum/datenschutz) ---- */

.iduga-legal {
  max-width: 720px;
  margin: 0 auto;
  padding: 64px 24px 96px;
  line-height: 1.6;
}

.iduga-legal h1 { font-size: 28px; margin-bottom: 8px; }
.iduga-legal h2 { font-size: 18px; margin-top: 40px; color: var(--primary-accent); }
.iduga-legal dt { color: var(--text-muted); font-size: 13px; margin-top: 12px; }
.iduga-legal dd { margin: 2px 0 0; }
.iduga-legal a { color: var(--primary-accent); }
.iduga-legal .iduga-back { display: inline-block; margin-top: 40px; color: var(--text-muted); }
.iduga-legal .iduga-placeholder-warning {
  margin: 24px 0;
  padding: 12px 16px;
  border: 1px solid var(--danger);
  border-radius: var(--r-md);
  color: var(--danger);
  font-size: 14px;
}
